s3:passdb: convert pdb_tdb to use dbwrap wrapper functions.
[obnox/samba/samba-obnox.git] / source3 / passdb / pdb_tdb.c
1 /*
2  * Unix SMB/CIFS implementation. 
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell   1992-1998
5  * Copyright (C) Simo Sorce        2000-2003
6  * Copyright (C) Gerald Carter     2000-2006
7  * Copyright (C) Jeremy Allison    2001-2009
8  * Copyright (C) Andrew Bartlett   2002
9  * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005
10  * 
11  * This program is free software; you can redistribute it and/or modify it under
12  * the terms of the GNU General Public License as published by the Free
13  * Software Foundation; either version 3 of the License, or (at your option)
14  * any later version.
15  * 
16  * This program is distributed in the hope that it will be useful, but WITHOUT
17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
19  * more details.
20  * 
21  * You should have received a copy of the GNU General Public License along with
22  * this program; if not, see <http://www.gnu.org/licenses/>.
23  */
24
25 #include "includes.h"
26 #include "system/filesys.h"
27 #include "passdb.h"
28 #include "dbwrap/dbwrap.h"
29 #include "dbwrap/dbwrap_open.h"
30 #include "../libcli/security/security.h"
31 #include "util_tdb.h"
32 #include "passdb/pdb_tdb.h"
33
34 #if 0 /* when made a module use this */
35
36 static int tdbsam_debug_level = DBGC_ALL;
37 #undef DBGC_CLASS
38 #define DBGC_CLASS tdbsam_debug_level
39
40 #else
41
42 #undef DBGC_CLASS
43 #define DBGC_CLASS DBGC_PASSDB
44
45 #endif
46
47 #define TDBSAM_VERSION  4       /* Most recent TDBSAM version */
48 #define TDBSAM_MINOR_VERSION    0       /* Most recent TDBSAM minor version */
49 #define TDBSAM_VERSION_STRING   "INFO/version"
50 #define TDBSAM_MINOR_VERSION_STRING     "INFO/minor_version"
51 #define PASSDB_FILE_NAME        "passdb.tdb"
52 #define USERPREFIX              "USER_"
53 #define USERPREFIX_LEN          5
54 #define RIDPREFIX               "RID_"
55 #define PRIVPREFIX              "PRIV_"
56 #define NEXT_RID_STRING         "NEXT_RID"
57
58 /* GLOBAL TDB SAM CONTEXT */
59
60 static struct db_context *db_sam;
61 static char *tdbsam_filename;
62
63 struct tdbsam_convert_state {
64         int32_t from;
65         bool success;
66 };
67
68 static int tdbsam_convert_one(struct db_record *rec, void *priv)
69 {
70         struct tdbsam_convert_state *state =
71                 (struct tdbsam_convert_state *)priv;
72         struct samu *user;
73         TDB_DATA data;
74         NTSTATUS status;
75         bool ret;
76         TDB_DATA key;
77         TDB_DATA value;
78
79         key = dbwrap_record_get_key(rec);
80
81         if (key.dsize < USERPREFIX_LEN) {
82                 return 0;
83         }
84         if (strncmp((char *)key.dptr, USERPREFIX, USERPREFIX_LEN) != 0) {
85                 return 0;
86         }
87
88         user = samu_new(talloc_tos());
89         if (user == NULL) {
90                 DEBUG(0,("tdbsam_convert: samu_new() failed!\n"));
91                 state->success = false;
92                 return -1;
93         }
94
95         DEBUG(10,("tdbsam_convert: Try unpacking a record with (key:%s) "
96                   "(version:%d)\n", (char *)key.dptr, state->from));
97
98         value = dbwrap_record_get_value(rec);
99
100         switch (state->from) {
101         case 0:
102                 ret = init_samu_from_buffer(user, SAMU_BUFFER_V0,
103                                             (uint8 *)value.dptr,
104                                             value.dsize);
105                 break;
106         case 1:
107                 ret = init_samu_from_buffer(user, SAMU_BUFFER_V1,
108                                             (uint8 *)value.dptr,
109                                             value.dsize);
110                 break;
111         case 2:
112                 ret = init_samu_from_buffer(user, SAMU_BUFFER_V2,
113                                             (uint8 *)value.dptr,
114                                             value.dsize);
115                 break;
116         case 3:
117                 ret = init_samu_from_buffer(user, SAMU_BUFFER_V3,
118                                             (uint8 *)value.dptr,
119                                             value.dsize);
120                 break;
121         case 4:
122                 ret = init_samu_from_buffer(user, SAMU_BUFFER_V4,
123                                             (uint8 *)value.dptr,
124                                             value.dsize);
125                 break;
126         default:
127                 /* unknown tdbsam version */
128                 ret = False;
129         }
130         if (!ret) {
131                 DEBUG(0,("tdbsam_convert: Bad struct samu entry returned "
132                          "from TDB (key:%s) (version:%d)\n", (char *)key.dptr,
133                          state->from));
134                 TALLOC_FREE(user);
135                 state->success = false;
136                 return -1;
137         }
138
139         data.dsize = init_buffer_from_samu(&data.dptr, user, false);
140         TALLOC_FREE(user);
141
142         if (data.dsize == -1) {
143                 DEBUG(0,("tdbsam_convert: cannot pack the struct samu into "
144                          "the new format\n"));
145                 state->success = false;
146                 return -1;
147         }
148
149         status = dbwrap_record_store(rec, data, TDB_MODIFY);
150         if (!NT_STATUS_IS_OK(status)) {
151                 DEBUG(0, ("Could not store the new record: %s\n",
152                           nt_errstr(status)));
153                 state->success = false;
154                 return -1;
155         }
156
157         return 0;
158 }
159
160 /**********************************************************************
161  Struct and function to backup an old record.
162  *********************************************************************/
163
164 struct tdbsam_backup_state {
165         struct db_context *new_db;
166         bool success;
167 };
168
169 static int backup_copy_fn(struct db_record *orig_rec, void *state)
170 {
171         struct tdbsam_backup_state *bs = (struct tdbsam_backup_state *)state;
172         struct db_record *new_rec;
173         NTSTATUS status;
174         TDB_DATA key;
175         TDB_DATA value;
176
177         key = dbwrap_record_get_key(orig_rec);
178
179         new_rec = dbwrap_fetch_locked(bs->new_db, talloc_tos(), key);
180         if (new_rec == NULL) {
181                 bs->success = false;
182                 return 1;
183         }
184
185         value = dbwrap_record_get_value(orig_rec);
186
187         status = dbwrap_record_store(new_rec, value, TDB_INSERT);
188
189         TALLOC_FREE(new_rec);
190
191         if (!NT_STATUS_IS_OK(status)) {
192                 bs->success = false;
193                 return 1;
194         }
195         return 0;
196 }
197
198 /**********************************************************************
199  Make a backup of an old passdb and replace the new one with it. We
200  have to do this as between 3.0.x and 3.2.x the hash function changed
201  by mistake (used unsigned char * instead of char *). This means the
202  previous simple update code will fail due to not being able to find
203  existing records to replace in the tdbsam_convert_one() function. JRA.
204  *********************************************************************/
205
206 static bool tdbsam_convert_backup(const char *dbname, struct db_context **pp_db)
207 {
208         TALLOC_CTX *frame = talloc_stackframe();
209         const char *tmp_fname = NULL;
210         struct db_context *tmp_db = NULL;
211         struct db_context *orig_db = *pp_db;
212         struct tdbsam_backup_state bs;
213         NTSTATUS status;
214
215         tmp_fname = talloc_asprintf(frame, "%s.tmp", dbname);
216         if (!tmp_fname) {
217                 TALLOC_FREE(frame);
218                 return false;
219         }
220
221         unlink(tmp_fname);
222
223         /* Remember to open this on the NULL context. We need
224          * it to stay around after we return from here. */
225
226         tmp_db = db_open(NULL, tmp_fname, 0,
227                                 TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
228         if (tmp_db == NULL) {
229                 DEBUG(0, ("tdbsam_convert_backup: Failed to create backup TDB passwd "
230                           "[%s]\n", tmp_fname));
231                 TALLOC_FREE(frame);
232                 return false;
233         }
234
235         if (dbwrap_transaction_start(orig_db) != 0) {
236                 DEBUG(0, ("tdbsam_convert_backup: Could not start transaction (1)\n"));
237                 unlink(tmp_fname);
238                 TALLOC_FREE(tmp_db);
239                 TALLOC_FREE(frame);
240                 return false;
241         }
242         if (dbwrap_transaction_start(tmp_db) != 0) {
243                 DEBUG(0, ("tdbsam_convert_backup: Could not start transaction (2)\n"));
244                 dbwrap_transaction_cancel(orig_db);
245                 unlink(tmp_fname);
246                 TALLOC_FREE(tmp_db);
247                 TALLOC_FREE(frame);
248                 return false;
249         }
250
251         bs.new_db = tmp_db;
252         bs.success = true;
253
254         status = dbwrap_traverse(orig_db, backup_copy_fn, (void *)&bs, NULL);
255         if (!NT_STATUS_IS_OK(status)) {
256                 DEBUG(0, ("tdbsam_convert_backup: traverse failed\n"));
257                 goto cancel;
258         }
259
260         if (!bs.success) {
261                 DEBUG(0, ("tdbsam_convert_backup: Rewriting records failed\n"));
262                 goto cancel;
263         }
264
265         if (dbwrap_transaction_commit(orig_db) != 0) {
266                 smb_panic("tdbsam_convert_backup: orig commit failed\n");
267         }
268         if (dbwrap_transaction_commit(tmp_db) != 0) {
269                 smb_panic("tdbsam_convert_backup: orig commit failed\n");
270         }
271
272         /* be sure to close the DBs _before_ renaming the file */
273
274         TALLOC_FREE(orig_db);
275         TALLOC_FREE(tmp_db);
276
277         /* This is safe from other users as we know we're
278          * under a mutex here. */
279
280         if (rename(tmp_fname, dbname) == -1) {
281                 DEBUG(0, ("tdbsam_convert_backup: rename of %s to %s failed %s\n",
282                         tmp_fname,
283                         dbname,
284                         strerror(errno)));
285                 smb_panic("tdbsam_convert_backup: replace passdb failed\n");
286         }
287
288         TALLOC_FREE(frame);
289
290         /* re-open the converted TDB */
291
292         orig_db = db_open(NULL, dbname, 0,
293                           TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
294         if (orig_db == NULL) {
295                 DEBUG(0, ("tdbsam_convert_backup: Failed to re-open "
296                           "converted passdb TDB [%s]\n", dbname));
297                 return false;
298         }
299
300         DEBUG(1, ("tdbsam_convert_backup: updated %s file.\n",
301                 dbname ));
302
303         /* Replace the global db pointer. */
304         *pp_db = orig_db;
305         return true;
306
307   cancel:
308
309         if (dbwrap_transaction_cancel(orig_db) != 0) {
310                 smb_panic("tdbsam_convert: transaction_cancel failed");
311         }
312
313         if (dbwrap_transaction_cancel(tmp_db) != 0) {
314                 smb_panic("tdbsam_convert: transaction_cancel failed");
315         }
316
317         unlink(tmp_fname);
318         TALLOC_FREE(tmp_db);
319         TALLOC_FREE(frame);
320         return false;
321 }
322
323 static bool tdbsam_upgrade_next_rid(struct db_context *db)
324 {
325         TDB_CONTEXT *tdb;
326         uint32 rid;
327         bool ok = false;
328
329         ok = dbwrap_fetch_uint32(db, NEXT_RID_STRING, &rid);
330         if (ok) {
331                 return true;
332         }
333
334         tdb = tdb_open_log(state_path("winbindd_idmap.tdb"), 0,
335                            TDB_DEFAULT, O_RDONLY, 0644);
336
337         if (tdb) {
338                 ok = tdb_fetch_uint32(tdb, "RID_COUNTER", &rid);
339                 if (!ok) {
340                         rid = BASE_RID;
341                 }
342                 tdb_close(tdb);
343         } else {
344                 rid = BASE_RID;
345         }
346
347         if (dbwrap_store_uint32(db, NEXT_RID_STRING, rid) != 0) {
348                 return false;
349         }
350
351         return true;
352 }
353
354 static bool tdbsam_convert(struct db_context **pp_db, const char *name, int32 from)
355 {
356         struct tdbsam_convert_state state;
357         struct db_context *db = NULL;
358         NTSTATUS status;
359
360         /* We only need the update backup for local db's. */
361         if (db_is_local(name) && !tdbsam_convert_backup(name, pp_db)) {
362                 DEBUG(0, ("tdbsam_convert: Could not backup %s\n", name));
363                 return false;
364         }
365
366         db = *pp_db;
367         state.from = from;
368         state.success = true;
369
370         if (dbwrap_transaction_start(db) != 0) {
371                 DEBUG(0, ("tdbsam_convert: Could not start transaction\n"));
372                 return false;
373         }
374
375         if (!tdbsam_upgrade_next_rid(db)) {
376                 DEBUG(0, ("tdbsam_convert: tdbsam_upgrade_next_rid failed\n"));
377                 goto cancel;
378         }
379
380         status = dbwrap_traverse(db, tdbsam_convert_one, &state, NULL);
381         if (!NT_STATUS_IS_OK(status)) {
382                 DEBUG(0, ("tdbsam_convert: traverse failed\n"));
383                 goto cancel;
384         }
385
386         if (!state.success) {
387                 DEBUG(0, ("tdbsam_convert: Converting records failed\n"));
388                 goto cancel;
389         }
390
391         if (dbwrap_store_int32(db, TDBSAM_VERSION_STRING,
392                                TDBSAM_VERSION) != 0) {
393                 DEBUG(0, ("tdbsam_convert: Could not store tdbsam version\n"));
394                 goto cancel;
395         }
396
397         if (dbwrap_store_int32(db, TDBSAM_MINOR_VERSION_STRING,
398                                TDBSAM_MINOR_VERSION) != 0) {
399                 DEBUG(0, ("tdbsam_convert: Could not store tdbsam minor version\n"));
400                 goto cancel;
401         }
402
403         if (dbwrap_transaction_commit(db) != 0) {
404                 DEBUG(0, ("tdbsam_convert: Could not commit transaction\n"));
405                 return false;
406         }
407
408         return true;
409
410  cancel:
411         if (dbwrap_transaction_cancel(db) != 0) {
412                 smb_panic("tdbsam_convert: transaction_cancel failed");
413         }
414
415         return false;
416 }
417
418 /*********************************************************************
419  Open the tdbsam file based on the absolute path specified.
420  Uses a reference count to allow multiple open calls.
421 *********************************************************************/
422
423 static bool tdbsam_open( const char *name )
424 {
425         int32   version;
426         int32   minor_version;
427
428         /* check if we are already open */
429
430         if ( db_sam ) {
431                 return true;
432         }
433
434         /* Try to open tdb passwd.  Create a new one if necessary */
435
436         db_sam = db_open(NULL, name, 0, TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
437         if (db_sam == NULL) {
438                 DEBUG(0, ("tdbsam_open: Failed to open/create TDB passwd "
439                           "[%s]\n", name));
440                 return false;
441         }
442
443         /* Check the version */
444         version = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING);
445         if (version == -1) {
446                 version = 0;    /* Version not found, assume version 0 */
447         }
448
449         /* Get the minor version */
450         minor_version = dbwrap_fetch_int32(db_sam, TDBSAM_MINOR_VERSION_STRING);
451         if (minor_version == -1) {
452                 minor_version = 0; /* Minor version not found, assume 0 */
453         }
454
455         /* Compare the version */
456         if (version > TDBSAM_VERSION) {
457                 /* Version more recent than the latest known */
458                 DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
459                 TALLOC_FREE(db_sam);
460                 return false;
461         }
462
463         if ( version < TDBSAM_VERSION ||
464                         (version == TDBSAM_VERSION &&
465                          minor_version < TDBSAM_MINOR_VERSION) ) {
466                 /*
467                  * Ok - we think we're going to have to convert.
468                  * Due to the backup process we now must do to
469                  * upgrade we have to get a mutex and re-check
470                  * the version. Someone else may have upgraded
471                  * whilst we were checking.
472                  */
473
474                 struct named_mutex *mtx = grab_named_mutex(NULL,
475                                                 "tdbsam_upgrade_mutex",
476                                                 600);
477
478                 if (!mtx) {
479                         DEBUG(0, ("tdbsam_open: failed to grab mutex.\n"));
480                         TALLOC_FREE(db_sam);
481                         return false;
482                 }
483
484                 /* Re-check the version */
485                 version = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING);
486                 if (version == -1) {
487                         version = 0;    /* Version not found, assume version 0 */
488                 }
489
490                 /* Re-check the minor version */
491                 minor_version = dbwrap_fetch_int32(db_sam, TDBSAM_MINOR_VERSION_STRING);
492                 if (minor_version == -1) {
493                         minor_version = 0; /* Minor version not found, assume 0 */
494                 }
495
496                 /* Compare the version */
497                 if (version > TDBSAM_VERSION) {
498                         /* Version more recent than the latest known */
499                         DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
500                         TALLOC_FREE(db_sam);
501                         TALLOC_FREE(mtx);
502                         return false;
503                 }
504
505                 if ( version < TDBSAM_VERSION ||
506                                 (version == TDBSAM_VERSION &&
507                                  minor_version < TDBSAM_MINOR_VERSION) ) {
508                         /*
509                          * Note that minor versions we read that are greater
510                          * than the current minor version we have hard coded
511                          * are assumed to be compatible if they have the same
512                          * major version. That allows previous versions of the
513                          * passdb code that don't know about minor versions to
514                          * still use this database. JRA.
515                          */
516
517                         DEBUG(1, ("tdbsam_open: Converting version %d.%d database to "
518                                   "version %d.%d.\n",
519                                         version,
520                                         minor_version,
521                                         TDBSAM_VERSION,
522                                         TDBSAM_MINOR_VERSION));
523
524                         if ( !tdbsam_convert(&db_sam, name, version) ) {
525                                 DEBUG(0, ("tdbsam_open: Error when trying to convert "
526                                           "tdbsam [%s]\n",name));
527                                 TALLOC_FREE(db_sam);
528                                 TALLOC_FREE(mtx);
529                                 return false;
530                         }
531
532                         DEBUG(3, ("TDBSAM converted successfully.\n"));
533                 }
534                 TALLOC_FREE(mtx);
535         }
536
537         DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));
538
539         return true;
540 }
541
542 /******************************************************************
543  Lookup a name in the SAM TDB
544 ******************************************************************/
545
546 static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods,
547                                     struct samu *user, const char *sname)
548 {
549         TDB_DATA        data;
550         fstring         keystr;
551         fstring         name;
552         NTSTATUS status;
553
554         if ( !user ) {
555                 DEBUG(0,("pdb_getsampwnam: struct samu is NULL.\n"));
556                 return NT_STATUS_NO_MEMORY;
557         }
558
559         /* Data is stored in all lower-case */
560         fstrcpy(name, sname);
561         strlower_m(name);
562
563         /* set search key */
564         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
565
566         /* open the database */
567
568         if ( !tdbsam_open( tdbsam_filename ) ) {
569                 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
570                 return NT_STATUS_ACCESS_DENIED;
571         }
572
573         /* get the record */
574
575         status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
576         if (!NT_STATUS_IS_OK(status)) {
577                 DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
578                 DEBUGADD(5, (" Key: %s\n", keystr));
579                 return NT_STATUS_NO_SUCH_USER;
580         }
581
582         /* unpack the buffer */
583
584         if (!init_samu_from_buffer(user, SAMU_BUFFER_LATEST, data.dptr, data.dsize)) {
585                 DEBUG(0,("pdb_getsampwent: Bad struct samu entry returned from TDB!\n"));
586                 SAFE_FREE(data.dptr);
587                 return NT_STATUS_NO_MEMORY;
588         }
589
590         /* success */
591
592         TALLOC_FREE(data.dptr);
593
594         return NT_STATUS_OK;
595 }
596
597 /***************************************************************************
598  Search by rid
599  **************************************************************************/
600
601 static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods,
602                                     struct samu *user, uint32 rid)
603 {
604         NTSTATUS                nt_status = NT_STATUS_UNSUCCESSFUL;
605         TDB_DATA                data;
606         fstring                 keystr;
607         fstring                 name;
608
609         if ( !user ) {
610                 DEBUG(0,("pdb_getsampwrid: struct samu is NULL.\n"));
611                 return nt_status;
612         }
613
614         /* set search key */
615
616         slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
617
618         /* open the database */
619
620         if ( !tdbsam_open( tdbsam_filename ) ) {
621                 DEBUG(0,("tdbsam_getsampwrid: failed to open %s!\n", tdbsam_filename));
622                 return NT_STATUS_ACCESS_DENIED;
623         }
624
625         /* get the record */
626
627         nt_status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
628         if (!NT_STATUS_IS_OK(nt_status)) {
629                 DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
630                 return nt_status;
631         }
632
633         fstrcpy(name, (const char *)data.dptr);
634         TALLOC_FREE(data.dptr);
635
636         return tdbsam_getsampwnam (my_methods, user, name);
637 }
638
639 static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods,
640                                    struct samu * user, const struct dom_sid *sid)
641 {
642         uint32 rid;
643
644         if ( !sid_peek_check_rid(get_global_sam_sid(), sid, &rid) )
645                 return NT_STATUS_UNSUCCESSFUL;
646
647         return tdbsam_getsampwrid(my_methods, user, rid);
648 }
649
650 static bool tdb_delete_samacct_only( struct samu *sam_pass )
651 {
652         fstring         keystr;
653         fstring         name;
654         NTSTATUS status;
655
656         fstrcpy(name, pdb_get_username(sam_pass));
657         strlower_m(name);
658
659         /* set the search key */
660
661         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
662
663         /* it's outaa here!  8^) */
664         if ( !tdbsam_open( tdbsam_filename ) ) {
665                 DEBUG(0,("tdb_delete_samacct_only: failed to open %s!\n",
666                          tdbsam_filename));
667                 return false;
668         }
669
670         status = dbwrap_delete_bystring(db_sam, keystr);
671         if (!NT_STATUS_IS_OK(status)) {
672                 DEBUG(5, ("Error deleting entry from tdb passwd "
673                           "database: %s!\n", nt_errstr(status)));
674                 return false;
675         }
676
677         return true;
678 }
679
680 /***************************************************************************
681  Delete a struct samu records for the username and RID key
682 ****************************************************************************/
683
684 static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods,
685                                           struct samu *sam_pass)
686 {
687         NTSTATUS        nt_status = NT_STATUS_UNSUCCESSFUL;
688         fstring         keystr;
689         uint32          rid;
690         fstring         name;
691
692         /* open the database */
693
694         if ( !tdbsam_open( tdbsam_filename ) ) {
695                 DEBUG(0,("tdbsam_delete_sam_account: failed to open %s!\n",
696                          tdbsam_filename));
697                 return NT_STATUS_ACCESS_DENIED;
698         }
699
700         fstrcpy(name, pdb_get_username(sam_pass));
701         strlower_m(name);
702
703         /* set the search key */
704
705         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
706
707         rid = pdb_get_user_rid(sam_pass);
708
709         /* it's outaa here!  8^) */
710
711         if (dbwrap_transaction_start(db_sam) != 0) {
712                 DEBUG(0, ("Could not start transaction\n"));
713                 return NT_STATUS_UNSUCCESSFUL;
714         }
715
716         nt_status = dbwrap_delete_bystring(db_sam, keystr);
717         if (!NT_STATUS_IS_OK(nt_status)) {
718                 DEBUG(5, ("Error deleting entry from tdb passwd "
719                           "database: %s!\n", nt_errstr(nt_status)));
720                 goto cancel;
721         }
722
723         /* set the search key */
724
725         slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
726
727         /* it's outaa here!  8^) */
728
729         nt_status = dbwrap_delete_bystring(db_sam, keystr);
730         if (!NT_STATUS_IS_OK(nt_status)) {
731                 DEBUG(5, ("Error deleting entry from tdb rid "
732                           "database: %s!\n", nt_errstr(nt_status)));
733                 goto cancel;
734         }
735
736         if (dbwrap_transaction_commit(db_sam) != 0) {
737                 DEBUG(0, ("Could not commit transaction\n"));
738                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
739         }
740
741         return NT_STATUS_OK;
742
743  cancel:
744         if (dbwrap_transaction_cancel(db_sam) != 0) {
745                 smb_panic("transaction_cancel failed");
746         }
747
748         return nt_status;
749 }
750
751
752 /***************************************************************************
753  Update the TDB SAM account record only
754  Assumes that the tdbsam is already open 
755 ****************************************************************************/
756 static bool tdb_update_samacct_only( struct samu* newpwd, int flag )
757 {
758         TDB_DATA        data;
759         uint8           *buf = NULL;
760         fstring         keystr;
761         fstring         name;
762         bool            ret = false;
763         NTSTATUS status;
764
765         /* copy the struct samu struct into a BYTE buffer for storage */
766
767         if ( (data.dsize=init_buffer_from_samu(&buf, newpwd, False)) == -1 ) {
768                 DEBUG(0,("tdb_update_sam: ERROR - Unable to copy struct samu info BYTE buffer!\n"));
769                 goto done;
770         }
771         data.dptr = buf;
772
773         fstrcpy(name, pdb_get_username(newpwd));
774         strlower_m(name);
775
776         DEBUG(5, ("Storing %saccount %s with RID %d\n",
777                   flag == TDB_INSERT ? "(new) " : "", name,
778                   pdb_get_user_rid(newpwd)));
779
780         /* setup the USER index key */
781         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
782
783         /* add the account */
784
785         status = dbwrap_store_bystring(db_sam, keystr, data, flag);
786         if (!NT_STATUS_IS_OK(status)) {
787                 DEBUG(0, ("Unable to modify passwd TDB: %s!",
788                           nt_errstr(status)));
789                 goto done;
790         }
791
792         ret = true;
793
794 done:
795         /* cleanup */
796         SAFE_FREE(buf);
797         return ret;
798 }
799
800 /***************************************************************************
801  Update the TDB SAM RID record only
802  Assumes that the tdbsam is already open
803 ****************************************************************************/
804 static bool tdb_update_ridrec_only( struct samu* newpwd, int flag )
805 {
806         TDB_DATA        data;
807         fstring         keystr;
808         fstring         name;
809         NTSTATUS status;
810
811         fstrcpy(name, pdb_get_username(newpwd));
812         strlower_m(name);
813
814         /* setup RID data */
815         data = string_term_tdb_data(name);
816
817         /* setup the RID index key */
818         slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX,
819                  pdb_get_user_rid(newpwd));
820
821         /* add the reference */
822         status = dbwrap_store_bystring(db_sam, keystr, data, flag);
823         if (!NT_STATUS_IS_OK(status)) {
824                 DEBUG(0, ("Unable to modify TDB passwd: %s!\n",
825                           nt_errstr(status)));
826                 return false;
827         }
828
829         return true;
830
831 }
832
833 /***************************************************************************
834  Update the TDB SAM
835 ****************************************************************************/
836
837 static bool tdb_update_sam(struct pdb_methods *my_methods, struct samu* newpwd,
838                            int flag)
839 {
840         uint32_t oldrid;
841         uint32_t newrid;
842
843         if (!(newrid = pdb_get_user_rid(newpwd))) {
844                 DEBUG(0,("tdb_update_sam: struct samu (%s) with no RID!\n",
845                          pdb_get_username(newpwd)));
846                 return False;
847         }
848
849         oldrid = newrid;
850
851         /* open the database */
852
853         if ( !tdbsam_open( tdbsam_filename ) ) {
854                 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
855                 return False;
856         }
857
858         if (dbwrap_transaction_start(db_sam) != 0) {
859                 DEBUG(0, ("Could not start transaction\n"));
860                 return false;
861         }
862
863         /* If we are updating, we may be changing this users RID. Retrieve the old RID
864            so we can check. */
865
866         if (flag == TDB_MODIFY) {
867                 struct samu *account = samu_new(talloc_tos());
868                 if (account == NULL) {
869                         DEBUG(0,("tdb_update_sam: samu_new() failed\n"));
870                         goto cancel;
871                 }
872                 if (!NT_STATUS_IS_OK(tdbsam_getsampwnam(my_methods, account, pdb_get_username(newpwd)))) {
873                         DEBUG(0,("tdb_update_sam: tdbsam_getsampwnam() for %s failed\n",
874                                 pdb_get_username(newpwd)));
875                         TALLOC_FREE(account);
876                         goto cancel;
877                 }
878                 if (!(oldrid = pdb_get_user_rid(account))) {
879                         DEBUG(0,("tdb_update_sam: pdb_get_user_rid() failed\n"));
880                         TALLOC_FREE(account);
881                         goto cancel;
882                 }
883                 TALLOC_FREE(account);
884         }
885
886         /* Update the new samu entry. */
887         if (!tdb_update_samacct_only(newpwd, flag)) {
888                 goto cancel;
889         }
890
891         /* Now take care of the case where the RID changed. We need
892          * to delete the old RID key and add the new. */
893
894         if (flag == TDB_MODIFY && newrid != oldrid) { 
895                 fstring keystr;
896
897                 /* Delete old RID key */
898                 DEBUG(10, ("tdb_update_sam: Deleting key for RID %u\n", oldrid));
899                 slprintf(keystr, sizeof(keystr) - 1, "%s%.8x", RIDPREFIX, oldrid);
900                 if (!NT_STATUS_IS_OK(dbwrap_delete_bystring(db_sam, keystr))) {
901                         DEBUG(0, ("tdb_update_sam: Can't delete %s\n", keystr));
902                         goto cancel;
903                 }
904                 /* Insert new RID key */
905                 DEBUG(10, ("tdb_update_sam: Inserting key for RID %u\n", newrid));
906                 if (!tdb_update_ridrec_only(newpwd, TDB_INSERT)) {
907                         goto cancel;
908                 }
909         } else {
910                 DEBUG(10, ("tdb_update_sam: %s key for RID %u\n",
911                         flag == TDB_MODIFY ? "Updating" : "Inserting", newrid));
912                 if (!tdb_update_ridrec_only(newpwd, flag)) {
913                         goto cancel;
914                 }
915         }
916
917         if (dbwrap_transaction_commit(db_sam) != 0) {
918                 DEBUG(0, ("Could not commit transaction\n"));
919                 return false;
920         }
921
922         return true;
923
924  cancel:
925         if (dbwrap_transaction_cancel(db_sam) != 0) {
926                 smb_panic("transaction_cancel failed");
927         }
928         return false;
929 }
930
931 /***************************************************************************
932  Modifies an existing struct samu
933 ****************************************************************************/
934
935 static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
936 {
937         if ( !tdb_update_sam(my_methods, newpwd, TDB_MODIFY) )
938                 return NT_STATUS_UNSUCCESSFUL;
939         
940         return NT_STATUS_OK;
941 }
942
943 /***************************************************************************
944  Adds an existing struct samu
945 ****************************************************************************/
946
947 static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
948 {
949         if ( !tdb_update_sam(my_methods, newpwd, TDB_INSERT) )
950                 return NT_STATUS_UNSUCCESSFUL;
951                 
952         return NT_STATUS_OK;
953 }
954
955 /***************************************************************************
956  Renames a struct samu
957  - check for the posix user/rename user script
958  - Add and lock the new user record
959  - rename the posix user
960  - rewrite the rid->username record
961  - delete the old user
962  - unlock the new user record
963 ***************************************************************************/
964 static NTSTATUS tdbsam_rename_sam_account(struct pdb_methods *my_methods,
965                                           struct samu *old_acct,
966                                           const char *newname)
967 {
968         struct samu      *new_acct = NULL;
969         char *rename_script = NULL;
970         int              rename_ret;
971         fstring          oldname_lower;
972         fstring          newname_lower;
973
974         /* can't do anything without an external script */
975
976         if ( !(new_acct = samu_new( talloc_tos() )) ) {
977                 return NT_STATUS_NO_MEMORY;
978         }
979
980         rename_script = talloc_strdup(new_acct, lp_renameuser_script());
981         if (!rename_script) {
982                 TALLOC_FREE(new_acct);
983                 return NT_STATUS_NO_MEMORY;
984         }
985         if (!*rename_script) {
986                 TALLOC_FREE(new_acct);
987                 return NT_STATUS_ACCESS_DENIED;
988         }
989
990         if ( !pdb_copy_sam_account(new_acct, old_acct)
991                 || !pdb_set_username(new_acct, newname, PDB_CHANGED))
992         {
993                 TALLOC_FREE(new_acct);
994                 return NT_STATUS_NO_MEMORY;
995         }
996
997         /* open the database */
998         if ( !tdbsam_open( tdbsam_filename ) ) {
999                 DEBUG(0, ("tdbsam_getsampwnam: failed to open %s!\n",
1000                           tdbsam_filename));
1001                 TALLOC_FREE(new_acct);
1002                 return NT_STATUS_ACCESS_DENIED;
1003         }
1004
1005         if (dbwrap_transaction_start(db_sam) != 0) {
1006                 DEBUG(0, ("Could not start transaction\n"));
1007                 TALLOC_FREE(new_acct);
1008                 return NT_STATUS_ACCESS_DENIED;
1009
1010         }
1011
1012         /* add the new account and lock it */
1013         if ( !tdb_update_samacct_only(new_acct, TDB_INSERT) ) {
1014                 goto cancel;
1015         }
1016
1017         /* Rename the posix user.  Follow the semantics of _samr_create_user()
1018            so that we lower case the posix name but preserve the case in passdb */
1019
1020         fstrcpy( oldname_lower, pdb_get_username(old_acct) );
1021         strlower_m( oldname_lower );
1022
1023         fstrcpy( newname_lower, newname );
1024         strlower_m( newname_lower );
1025
1026         rename_script = talloc_string_sub2(new_acct,
1027                                 rename_script,
1028                                 "%unew",
1029                                 newname_lower,
1030                                 true,
1031                                 false,
1032                                 true);
1033         if (!rename_script) {
1034                 goto cancel;
1035         }
1036         rename_script = talloc_string_sub2(new_acct,
1037                                 rename_script,
1038                                 "%uold",
1039                                 oldname_lower,
1040                                 true,
1041                                 false,
1042                                 true);
1043         if (!rename_script) {
1044                 goto cancel;
1045         }
1046         rename_ret = smbrun(rename_script, NULL);
1047
1048         DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n",
1049                                 rename_script, rename_ret));
1050
1051         if (rename_ret != 0) {
1052                 goto cancel;
1053         }
1054
1055         smb_nscd_flush_user_cache();
1056
1057         /* rewrite the rid->username record */
1058
1059         if ( !tdb_update_ridrec_only( new_acct, TDB_MODIFY) ) {
1060                 goto cancel;
1061         }
1062
1063         tdb_delete_samacct_only( old_acct );
1064
1065         if (dbwrap_transaction_commit(db_sam) != 0) {
1066                 /*
1067                  * Ok, we're screwed. We've changed the posix account, but
1068                  * could not adapt passdb.tdb. Shall we change the posix
1069                  * account back?
1070                  */
1071                 DEBUG(0, ("transaction_commit failed\n"));
1072                 TALLOC_FREE(new_acct);
1073                 return NT_STATUS_INTERNAL_DB_CORRUPTION;        
1074         }
1075
1076         TALLOC_FREE(new_acct );
1077         return NT_STATUS_OK;
1078
1079  cancel:
1080         if (dbwrap_transaction_cancel(db_sam) != 0) {
1081                 smb_panic("transaction_cancel failed");
1082         }
1083
1084         TALLOC_FREE(new_acct);
1085
1086         return NT_STATUS_ACCESS_DENIED; 
1087 }
1088
1089 static uint32_t tdbsam_capabilities(struct pdb_methods *methods)
1090 {
1091         return PDB_CAP_STORE_RIDS;
1092 }
1093
1094 static bool tdbsam_new_rid(struct pdb_methods *methods, uint32 *prid)
1095 {
1096         uint32 rid;
1097         NTSTATUS status;
1098
1099         rid = BASE_RID;         /* Default if not set */
1100
1101         if (!tdbsam_open(tdbsam_filename)) {
1102                 DEBUG(0,("tdbsam_new_rid: failed to open %s!\n",
1103                         tdbsam_filename));
1104                 return false;
1105         }
1106
1107         status = dbwrap_trans_change_uint32_atomic(db_sam, NEXT_RID_STRING,
1108                                                    &rid, 1);
1109         if (!NT_STATUS_IS_OK(status)) {
1110                 DEBUG(3, ("tdbsam_new_rid: Failed to increase %s: %s\n",
1111                         NEXT_RID_STRING, nt_errstr(status)));
1112                 return false;
1113         }
1114
1115         *prid = rid;
1116
1117         return true;
1118 }
1119
1120 struct tdbsam_search_state {
1121         struct pdb_methods *methods;
1122         uint32_t acct_flags;
1123
1124         uint32_t *rids;
1125         uint32_t num_rids;
1126         ssize_t array_size;
1127         uint32_t current;
1128 };
1129
1130 static int tdbsam_collect_rids(struct db_record *rec, void *private_data)
1131 {
1132         struct tdbsam_search_state *state = talloc_get_type_abort(
1133                 private_data, struct tdbsam_search_state);
1134         size_t prefixlen = strlen(RIDPREFIX);
1135         uint32 rid;
1136         TDB_DATA key;
1137
1138         key = dbwrap_record_get_key(rec);
1139
1140         if ((key.dsize < prefixlen)
1141             || (strncmp((char *)key.dptr, RIDPREFIX, prefixlen))) {
1142                 return 0;
1143         }
1144
1145         rid = strtoul((char *)key.dptr+prefixlen, NULL, 16);
1146
1147         ADD_TO_LARGE_ARRAY(state, uint32, rid, &state->rids, &state->num_rids,
1148                            &state->array_size);
1149
1150         return 0;
1151 }
1152
1153 static void tdbsam_search_end(struct pdb_search *search)
1154 {
1155         struct tdbsam_search_state *state = talloc_get_type_abort(
1156                 search->private_data, struct tdbsam_search_state);
1157         TALLOC_FREE(state);
1158 }
1159
1160 static bool tdbsam_search_next_entry(struct pdb_search *search,
1161                                      struct samr_displayentry *entry)
1162 {
1163         struct tdbsam_search_state *state = talloc_get_type_abort(
1164                 search->private_data, struct tdbsam_search_state);
1165         struct samu *user = NULL;
1166         NTSTATUS status;
1167         uint32_t rid;
1168
1169  again:
1170         TALLOC_FREE(user);
1171         user = samu_new(talloc_tos());
1172         if (user == NULL) {
1173                 DEBUG(0, ("samu_new failed\n"));
1174                 return false;
1175         }
1176
1177         if (state->current == state->num_rids) {
1178                 return false;
1179         }
1180
1181         rid = state->rids[state->current++];
1182
1183         status = tdbsam_getsampwrid(state->methods, user, rid);
1184
1185         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1186                 /*
1187                  * Someone has deleted that user since we listed the RIDs
1188                  */
1189                 goto again;
1190         }
1191
1192         if (!NT_STATUS_IS_OK(status)) {
1193                 DEBUG(10, ("tdbsam_getsampwrid failed: %s\n",
1194                            nt_errstr(status)));
1195                 TALLOC_FREE(user);
1196                 return false;
1197         }
1198
1199         if ((state->acct_flags != 0) &&
1200             ((state->acct_flags & pdb_get_acct_ctrl(user)) == 0)) {
1201                 goto again;
1202         }
1203
1204         entry->acct_flags = pdb_get_acct_ctrl(user);
1205         entry->rid = rid;
1206         entry->account_name = talloc_strdup(search, pdb_get_username(user));
1207         entry->fullname = talloc_strdup(search, pdb_get_fullname(user));
1208         entry->description = talloc_strdup(search, pdb_get_acct_desc(user));
1209
1210         TALLOC_FREE(user);
1211
1212         if ((entry->account_name == NULL) || (entry->fullname == NULL)
1213             || (entry->description == NULL)) {
1214                 DEBUG(0, ("talloc_strdup failed\n"));
1215                 return false;
1216         }
1217
1218         return true;
1219 }
1220
1221 static bool tdbsam_search_users(struct pdb_methods *methods,
1222                                 struct pdb_search *search,
1223                                 uint32 acct_flags)
1224 {
1225         struct tdbsam_search_state *state;
1226
1227         if (!tdbsam_open(tdbsam_filename)) {
1228                 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n",
1229                          tdbsam_filename));
1230                 return false;
1231         }
1232
1233         state = talloc_zero(search, struct tdbsam_search_state);
1234         if (state == NULL) {
1235                 DEBUG(0, ("talloc failed\n"));
1236                 return false;
1237         }
1238         state->acct_flags = acct_flags;
1239         state->methods = methods;
1240
1241         dbwrap_traverse_read(db_sam, tdbsam_collect_rids, state, NULL);
1242
1243         search->private_data = state;
1244         search->next_entry = tdbsam_search_next_entry;
1245         search->search_end = tdbsam_search_end;
1246
1247         return true;
1248 }
1249
1250 /*********************************************************************
1251  Initialize the tdb sam backend.  Setup the dispath table of methods,
1252  open the tdb, etc...
1253 *********************************************************************/
1254
1255 static NTSTATUS pdb_init_tdbsam(struct pdb_methods **pdb_method, const char *location)
1256 {
1257         NTSTATUS nt_status;
1258         char *tdbfile = NULL;
1259         const char *pfile = location;
1260
1261         if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) {
1262                 return nt_status;
1263         }
1264
1265         (*pdb_method)->name = "tdbsam";
1266
1267         (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
1268         (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
1269         (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
1270         (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
1271         (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
1272         (*pdb_method)->rename_sam_account = tdbsam_rename_sam_account;
1273         (*pdb_method)->search_users = tdbsam_search_users;
1274
1275         (*pdb_method)->capabilities = tdbsam_capabilities;
1276         (*pdb_method)->new_rid = tdbsam_new_rid;
1277
1278         /* save the path for later */
1279
1280         if (!location) {
1281                 if (asprintf(&tdbfile, "%s/%s", lp_private_dir(),
1282                              PASSDB_FILE_NAME) < 0) {
1283                         return NT_STATUS_NO_MEMORY;
1284                 }
1285                 pfile = tdbfile;
1286         }
1287         tdbsam_filename = SMB_STRDUP(pfile);
1288         if (!tdbsam_filename) {
1289                 return NT_STATUS_NO_MEMORY;
1290         }
1291         SAFE_FREE(tdbfile);
1292
1293         /* no private data */
1294
1295         (*pdb_method)->private_data      = NULL;
1296         (*pdb_method)->free_private_data = NULL;
1297
1298         return NT_STATUS_OK;
1299 }
1300
1301 NTSTATUS pdb_tdbsam_init(void)
1302 {
1303         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);
1304 }