12958623036ef17be56dadb39750fd024b063480
[kai/samba.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                          DBWRAP_LOCK_ORDER_1);
229         if (tmp_db == NULL) {
230                 DEBUG(0, ("tdbsam_convert_backup: Failed to create backup TDB passwd "
231                           "[%s]\n", tmp_fname));
232                 TALLOC_FREE(frame);
233                 return false;
234         }
235
236         if (dbwrap_transaction_start(orig_db) != 0) {
237                 DEBUG(0, ("tdbsam_convert_backup: Could not start transaction (1)\n"));
238                 unlink(tmp_fname);
239                 TALLOC_FREE(tmp_db);
240                 TALLOC_FREE(frame);
241                 return false;
242         }
243         if (dbwrap_transaction_start(tmp_db) != 0) {
244                 DEBUG(0, ("tdbsam_convert_backup: Could not start transaction (2)\n"));
245                 dbwrap_transaction_cancel(orig_db);
246                 unlink(tmp_fname);
247                 TALLOC_FREE(tmp_db);
248                 TALLOC_FREE(frame);
249                 return false;
250         }
251
252         bs.new_db = tmp_db;
253         bs.success = true;
254
255         status = dbwrap_traverse(orig_db, backup_copy_fn, (void *)&bs, NULL);
256         if (!NT_STATUS_IS_OK(status)) {
257                 DEBUG(0, ("tdbsam_convert_backup: traverse failed\n"));
258                 goto cancel;
259         }
260
261         if (!bs.success) {
262                 DEBUG(0, ("tdbsam_convert_backup: Rewriting records failed\n"));
263                 goto cancel;
264         }
265
266         if (dbwrap_transaction_commit(orig_db) != 0) {
267                 smb_panic("tdbsam_convert_backup: orig commit failed\n");
268         }
269         if (dbwrap_transaction_commit(tmp_db) != 0) {
270                 smb_panic("tdbsam_convert_backup: orig commit failed\n");
271         }
272
273         /* be sure to close the DBs _before_ renaming the file */
274
275         TALLOC_FREE(orig_db);
276         TALLOC_FREE(tmp_db);
277
278         /* This is safe from other users as we know we're
279          * under a mutex here. */
280
281         if (rename(tmp_fname, dbname) == -1) {
282                 DEBUG(0, ("tdbsam_convert_backup: rename of %s to %s failed %s\n",
283                         tmp_fname,
284                         dbname,
285                         strerror(errno)));
286                 smb_panic("tdbsam_convert_backup: replace passdb failed\n");
287         }
288
289         TALLOC_FREE(frame);
290
291         /* re-open the converted TDB */
292
293         orig_db = db_open(NULL, dbname, 0,
294                           TDB_DEFAULT, O_CREAT|O_RDWR, 0600,
295                           DBWRAP_LOCK_ORDER_1);
296         if (orig_db == NULL) {
297                 DEBUG(0, ("tdbsam_convert_backup: Failed to re-open "
298                           "converted passdb TDB [%s]\n", dbname));
299                 return false;
300         }
301
302         DEBUG(1, ("tdbsam_convert_backup: updated %s file.\n",
303                 dbname ));
304
305         /* Replace the global db pointer. */
306         *pp_db = orig_db;
307         return true;
308
309   cancel:
310
311         if (dbwrap_transaction_cancel(orig_db) != 0) {
312                 smb_panic("tdbsam_convert: transaction_cancel failed");
313         }
314
315         if (dbwrap_transaction_cancel(tmp_db) != 0) {
316                 smb_panic("tdbsam_convert: transaction_cancel failed");
317         }
318
319         unlink(tmp_fname);
320         TALLOC_FREE(tmp_db);
321         TALLOC_FREE(frame);
322         return false;
323 }
324
325 static bool tdbsam_upgrade_next_rid(struct db_context *db)
326 {
327         TDB_CONTEXT *tdb;
328         uint32 rid;
329         bool ok = false;
330         NTSTATUS status;
331
332         status = dbwrap_fetch_uint32_bystring(db, NEXT_RID_STRING, &rid);
333         if (NT_STATUS_IS_OK(status)) {
334                 return true;
335         }
336
337         tdb = tdb_open_log(state_path("winbindd_idmap.tdb"), 0,
338                            TDB_DEFAULT, O_RDONLY, 0644);
339
340         if (tdb) {
341                 ok = tdb_fetch_uint32(tdb, "RID_COUNTER", &rid);
342                 if (!ok) {
343                         rid = BASE_RID;
344                 }
345                 tdb_close(tdb);
346         } else {
347                 rid = BASE_RID;
348         }
349
350         status = dbwrap_store_uint32_bystring(db, NEXT_RID_STRING, rid);
351         if (!NT_STATUS_IS_OK(status)) {
352                 return false;
353         }
354
355         return true;
356 }
357
358 static bool tdbsam_convert(struct db_context **pp_db, const char *name, int32 from)
359 {
360         struct tdbsam_convert_state state;
361         struct db_context *db = NULL;
362         NTSTATUS status;
363
364         /* We only need the update backup for local db's. */
365         if (db_is_local(name) && !tdbsam_convert_backup(name, pp_db)) {
366                 DEBUG(0, ("tdbsam_convert: Could not backup %s\n", name));
367                 return false;
368         }
369
370         db = *pp_db;
371         state.from = from;
372         state.success = true;
373
374         if (dbwrap_transaction_start(db) != 0) {
375                 DEBUG(0, ("tdbsam_convert: Could not start transaction\n"));
376                 return false;
377         }
378
379         if (!tdbsam_upgrade_next_rid(db)) {
380                 DEBUG(0, ("tdbsam_convert: tdbsam_upgrade_next_rid failed\n"));
381                 goto cancel;
382         }
383
384         status = dbwrap_traverse(db, tdbsam_convert_one, &state, NULL);
385         if (!NT_STATUS_IS_OK(status)) {
386                 DEBUG(0, ("tdbsam_convert: traverse failed\n"));
387                 goto cancel;
388         }
389
390         if (!state.success) {
391                 DEBUG(0, ("tdbsam_convert: Converting records failed\n"));
392                 goto cancel;
393         }
394
395         status = dbwrap_store_int32_bystring(db, TDBSAM_VERSION_STRING,
396                                              TDBSAM_VERSION);
397         if (!NT_STATUS_IS_OK(status)) {
398                 DEBUG(0, ("tdbsam_convert: Could not store tdbsam version: "
399                           "%s\n", nt_errstr(status)));
400                 goto cancel;
401         }
402
403         status = dbwrap_store_int32_bystring(db, TDBSAM_MINOR_VERSION_STRING,
404                                              TDBSAM_MINOR_VERSION);
405         if (!NT_STATUS_IS_OK(status)) {
406                 DEBUG(0, ("tdbsam_convert: Could not store tdbsam minor "
407                           "version: %s\n", nt_errstr(status)));
408                 goto cancel;
409         }
410
411         if (dbwrap_transaction_commit(db) != 0) {
412                 DEBUG(0, ("tdbsam_convert: Could not commit transaction\n"));
413                 return false;
414         }
415
416         return true;
417
418  cancel:
419         if (dbwrap_transaction_cancel(db) != 0) {
420                 smb_panic("tdbsam_convert: transaction_cancel failed");
421         }
422
423         return false;
424 }
425
426 /*********************************************************************
427  Open the tdbsam file based on the absolute path specified.
428  Uses a reference count to allow multiple open calls.
429 *********************************************************************/
430
431 static bool tdbsam_open( const char *name )
432 {
433         int32   version;
434         int32   minor_version;
435         NTSTATUS status;
436
437         /* check if we are already open */
438
439         if ( db_sam ) {
440                 return true;
441         }
442
443         /* Try to open tdb passwd.  Create a new one if necessary */
444
445         db_sam = db_open(NULL, name, 0, TDB_DEFAULT, O_CREAT|O_RDWR, 0600,
446                          DBWRAP_LOCK_ORDER_1);
447         if (db_sam == NULL) {
448                 DEBUG(0, ("tdbsam_open: Failed to open/create TDB passwd "
449                           "[%s]\n", name));
450                 return false;
451         }
452
453         /* Check the version */
454         status = dbwrap_fetch_int32_bystring(db_sam, TDBSAM_VERSION_STRING,
455                                              &version);
456         if (!NT_STATUS_IS_OK(status)) {
457                 version = 0;    /* Version not found, assume version 0 */
458         }
459
460         /* Get the minor version */
461         status = dbwrap_fetch_int32_bystring(
462                 db_sam, TDBSAM_MINOR_VERSION_STRING, &minor_version);
463         if (!NT_STATUS_IS_OK(status)) {
464                 minor_version = 0; /* Minor version not found, assume 0 */
465         }
466
467         /* Compare the version */
468         if (version > TDBSAM_VERSION) {
469                 /* Version more recent than the latest known */
470                 DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
471                 TALLOC_FREE(db_sam);
472                 return false;
473         }
474
475         if ( version < TDBSAM_VERSION ||
476                         (version == TDBSAM_VERSION &&
477                          minor_version < TDBSAM_MINOR_VERSION) ) {
478                 /*
479                  * Ok - we think we're going to have to convert.
480                  * Due to the backup process we now must do to
481                  * upgrade we have to get a mutex and re-check
482                  * the version. Someone else may have upgraded
483                  * whilst we were checking.
484                  */
485
486                 struct named_mutex *mtx = grab_named_mutex(NULL,
487                                                 "tdbsam_upgrade_mutex",
488                                                 600);
489
490                 if (!mtx) {
491                         DEBUG(0, ("tdbsam_open: failed to grab mutex.\n"));
492                         TALLOC_FREE(db_sam);
493                         return false;
494                 }
495
496                 /* Re-check the version */
497                 status = dbwrap_fetch_int32_bystring(
498                         db_sam, TDBSAM_VERSION_STRING, &version);
499                 if (!NT_STATUS_IS_OK(status)) {
500                         version = 0;    /* Version not found, assume version 0 */
501                 }
502
503                 /* Re-check the minor version */
504                 status = dbwrap_fetch_int32_bystring(
505                         db_sam, TDBSAM_MINOR_VERSION_STRING, &minor_version);
506                 if (!NT_STATUS_IS_OK(status)) {
507                         minor_version = 0; /* Minor version not found, assume 0 */
508                 }
509
510                 /* Compare the version */
511                 if (version > TDBSAM_VERSION) {
512                         /* Version more recent than the latest known */
513                         DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
514                         TALLOC_FREE(db_sam);
515                         TALLOC_FREE(mtx);
516                         return false;
517                 }
518
519                 if ( version < TDBSAM_VERSION ||
520                                 (version == TDBSAM_VERSION &&
521                                  minor_version < TDBSAM_MINOR_VERSION) ) {
522                         /*
523                          * Note that minor versions we read that are greater
524                          * than the current minor version we have hard coded
525                          * are assumed to be compatible if they have the same
526                          * major version. That allows previous versions of the
527                          * passdb code that don't know about minor versions to
528                          * still use this database. JRA.
529                          */
530
531                         DEBUG(1, ("tdbsam_open: Converting version %d.%d database to "
532                                   "version %d.%d.\n",
533                                         version,
534                                         minor_version,
535                                         TDBSAM_VERSION,
536                                         TDBSAM_MINOR_VERSION));
537
538                         if ( !tdbsam_convert(&db_sam, name, version) ) {
539                                 DEBUG(0, ("tdbsam_open: Error when trying to convert "
540                                           "tdbsam [%s]\n",name));
541                                 TALLOC_FREE(db_sam);
542                                 TALLOC_FREE(mtx);
543                                 return false;
544                         }
545
546                         DEBUG(3, ("TDBSAM converted successfully.\n"));
547                 }
548                 TALLOC_FREE(mtx);
549         }
550
551         DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));
552
553         return true;
554 }
555
556 /******************************************************************
557  Lookup a name in the SAM TDB
558 ******************************************************************/
559
560 static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods,
561                                     struct samu *user, const char *sname)
562 {
563         TDB_DATA        data;
564         fstring         keystr;
565         fstring         name;
566         NTSTATUS status;
567
568         if ( !user ) {
569                 DEBUG(0,("pdb_getsampwnam: struct samu is NULL.\n"));
570                 return NT_STATUS_NO_MEMORY;
571         }
572
573         /* Data is stored in all lower-case */
574         fstrcpy(name, sname);
575         strlower_m(name);
576
577         /* set search key */
578         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
579
580         /* open the database */
581
582         if ( !tdbsam_open( tdbsam_filename ) ) {
583                 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
584                 return NT_STATUS_ACCESS_DENIED;
585         }
586
587         /* get the record */
588
589         status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
590         if (!NT_STATUS_IS_OK(status)) {
591                 DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
592                 DEBUGADD(5, (" Key: %s\n", keystr));
593                 return NT_STATUS_NO_SUCH_USER;
594         }
595
596         /* unpack the buffer */
597
598         if (!init_samu_from_buffer(user, SAMU_BUFFER_LATEST, data.dptr, data.dsize)) {
599                 DEBUG(0,("pdb_getsampwent: Bad struct samu entry returned from TDB!\n"));
600                 SAFE_FREE(data.dptr);
601                 return NT_STATUS_NO_MEMORY;
602         }
603
604         /* success */
605
606         TALLOC_FREE(data.dptr);
607
608         return NT_STATUS_OK;
609 }
610
611 /***************************************************************************
612  Search by rid
613  **************************************************************************/
614
615 static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods,
616                                     struct samu *user, uint32 rid)
617 {
618         NTSTATUS                nt_status = NT_STATUS_UNSUCCESSFUL;
619         TDB_DATA                data;
620         fstring                 keystr;
621         fstring                 name;
622
623         if ( !user ) {
624                 DEBUG(0,("pdb_getsampwrid: struct samu is NULL.\n"));
625                 return nt_status;
626         }
627
628         /* set search key */
629
630         slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
631
632         /* open the database */
633
634         if ( !tdbsam_open( tdbsam_filename ) ) {
635                 DEBUG(0,("tdbsam_getsampwrid: failed to open %s!\n", tdbsam_filename));
636                 return NT_STATUS_ACCESS_DENIED;
637         }
638
639         /* get the record */
640
641         nt_status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
642         if (!NT_STATUS_IS_OK(nt_status)) {
643                 DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
644                 return nt_status;
645         }
646
647         fstrcpy(name, (const char *)data.dptr);
648         TALLOC_FREE(data.dptr);
649
650         return tdbsam_getsampwnam (my_methods, user, name);
651 }
652
653 static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods,
654                                    struct samu * user, const struct dom_sid *sid)
655 {
656         uint32 rid;
657
658         if ( !sid_peek_check_rid(get_global_sam_sid(), sid, &rid) )
659                 return NT_STATUS_UNSUCCESSFUL;
660
661         return tdbsam_getsampwrid(my_methods, user, rid);
662 }
663
664 static bool tdb_delete_samacct_only( struct samu *sam_pass )
665 {
666         fstring         keystr;
667         fstring         name;
668         NTSTATUS status;
669
670         fstrcpy(name, pdb_get_username(sam_pass));
671         strlower_m(name);
672
673         /* set the search key */
674
675         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
676
677         /* it's outaa here!  8^) */
678         if ( !tdbsam_open( tdbsam_filename ) ) {
679                 DEBUG(0,("tdb_delete_samacct_only: failed to open %s!\n",
680                          tdbsam_filename));
681                 return false;
682         }
683
684         status = dbwrap_delete_bystring(db_sam, keystr);
685         if (!NT_STATUS_IS_OK(status)) {
686                 DEBUG(5, ("Error deleting entry from tdb passwd "
687                           "database: %s!\n", nt_errstr(status)));
688                 return false;
689         }
690
691         return true;
692 }
693
694 /***************************************************************************
695  Delete a struct samu records for the username and RID key
696 ****************************************************************************/
697
698 static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods,
699                                           struct samu *sam_pass)
700 {
701         NTSTATUS        nt_status = NT_STATUS_UNSUCCESSFUL;
702         fstring         keystr;
703         uint32          rid;
704         fstring         name;
705
706         /* open the database */
707
708         if ( !tdbsam_open( tdbsam_filename ) ) {
709                 DEBUG(0,("tdbsam_delete_sam_account: failed to open %s!\n",
710                          tdbsam_filename));
711                 return NT_STATUS_ACCESS_DENIED;
712         }
713
714         fstrcpy(name, pdb_get_username(sam_pass));
715         strlower_m(name);
716
717         /* set the search key */
718
719         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
720
721         rid = pdb_get_user_rid(sam_pass);
722
723         /* it's outaa here!  8^) */
724
725         if (dbwrap_transaction_start(db_sam) != 0) {
726                 DEBUG(0, ("Could not start transaction\n"));
727                 return NT_STATUS_UNSUCCESSFUL;
728         }
729
730         nt_status = dbwrap_delete_bystring(db_sam, keystr);
731         if (!NT_STATUS_IS_OK(nt_status)) {
732                 DEBUG(5, ("Error deleting entry from tdb passwd "
733                           "database: %s!\n", nt_errstr(nt_status)));
734                 goto cancel;
735         }
736
737         /* set the search key */
738
739         slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
740
741         /* it's outaa here!  8^) */
742
743         nt_status = dbwrap_delete_bystring(db_sam, keystr);
744         if (!NT_STATUS_IS_OK(nt_status)) {
745                 DEBUG(5, ("Error deleting entry from tdb rid "
746                           "database: %s!\n", nt_errstr(nt_status)));
747                 goto cancel;
748         }
749
750         if (dbwrap_transaction_commit(db_sam) != 0) {
751                 DEBUG(0, ("Could not commit transaction\n"));
752                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
753         }
754
755         return NT_STATUS_OK;
756
757  cancel:
758         if (dbwrap_transaction_cancel(db_sam) != 0) {
759                 smb_panic("transaction_cancel failed");
760         }
761
762         return nt_status;
763 }
764
765
766 /***************************************************************************
767  Update the TDB SAM account record only
768  Assumes that the tdbsam is already open 
769 ****************************************************************************/
770 static bool tdb_update_samacct_only( struct samu* newpwd, int flag )
771 {
772         TDB_DATA        data;
773         uint8           *buf = NULL;
774         fstring         keystr;
775         fstring         name;
776         bool            ret = false;
777         NTSTATUS status;
778
779         /* copy the struct samu struct into a BYTE buffer for storage */
780
781         if ( (data.dsize=init_buffer_from_samu(&buf, newpwd, False)) == -1 ) {
782                 DEBUG(0,("tdb_update_sam: ERROR - Unable to copy struct samu info BYTE buffer!\n"));
783                 goto done;
784         }
785         data.dptr = buf;
786
787         fstrcpy(name, pdb_get_username(newpwd));
788         strlower_m(name);
789
790         DEBUG(5, ("Storing %saccount %s with RID %d\n",
791                   flag == TDB_INSERT ? "(new) " : "", name,
792                   pdb_get_user_rid(newpwd)));
793
794         /* setup the USER index key */
795         slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
796
797         /* add the account */
798
799         status = dbwrap_store_bystring(db_sam, keystr, data, flag);
800         if (!NT_STATUS_IS_OK(status)) {
801                 DEBUG(0, ("Unable to modify passwd TDB: %s!",
802                           nt_errstr(status)));
803                 goto done;
804         }
805
806         ret = true;
807
808 done:
809         /* cleanup */
810         SAFE_FREE(buf);
811         return ret;
812 }
813
814 /***************************************************************************
815  Update the TDB SAM RID record only
816  Assumes that the tdbsam is already open
817 ****************************************************************************/
818 static bool tdb_update_ridrec_only( struct samu* newpwd, int flag )
819 {
820         TDB_DATA        data;
821         fstring         keystr;
822         fstring         name;
823         NTSTATUS status;
824
825         fstrcpy(name, pdb_get_username(newpwd));
826         strlower_m(name);
827
828         /* setup RID data */
829         data = string_term_tdb_data(name);
830
831         /* setup the RID index key */
832         slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX,
833                  pdb_get_user_rid(newpwd));
834
835         /* add the reference */
836         status = dbwrap_store_bystring(db_sam, keystr, data, flag);
837         if (!NT_STATUS_IS_OK(status)) {
838                 DEBUG(0, ("Unable to modify TDB passwd: %s!\n",
839                           nt_errstr(status)));
840                 return false;
841         }
842
843         return true;
844
845 }
846
847 /***************************************************************************
848  Update the TDB SAM
849 ****************************************************************************/
850
851 static bool tdb_update_sam(struct pdb_methods *my_methods, struct samu* newpwd,
852                            int flag)
853 {
854         uint32_t oldrid;
855         uint32_t newrid;
856
857         if (!(newrid = pdb_get_user_rid(newpwd))) {
858                 DEBUG(0,("tdb_update_sam: struct samu (%s) with no RID!\n",
859                          pdb_get_username(newpwd)));
860                 return False;
861         }
862
863         oldrid = newrid;
864
865         /* open the database */
866
867         if ( !tdbsam_open( tdbsam_filename ) ) {
868                 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
869                 return False;
870         }
871
872         if (dbwrap_transaction_start(db_sam) != 0) {
873                 DEBUG(0, ("Could not start transaction\n"));
874                 return false;
875         }
876
877         /* If we are updating, we may be changing this users RID. Retrieve the old RID
878            so we can check. */
879
880         if (flag == TDB_MODIFY) {
881                 struct samu *account = samu_new(talloc_tos());
882                 if (account == NULL) {
883                         DEBUG(0,("tdb_update_sam: samu_new() failed\n"));
884                         goto cancel;
885                 }
886                 if (!NT_STATUS_IS_OK(tdbsam_getsampwnam(my_methods, account, pdb_get_username(newpwd)))) {
887                         DEBUG(0,("tdb_update_sam: tdbsam_getsampwnam() for %s failed\n",
888                                 pdb_get_username(newpwd)));
889                         TALLOC_FREE(account);
890                         goto cancel;
891                 }
892                 if (!(oldrid = pdb_get_user_rid(account))) {
893                         DEBUG(0,("tdb_update_sam: pdb_get_user_rid() failed\n"));
894                         TALLOC_FREE(account);
895                         goto cancel;
896                 }
897                 TALLOC_FREE(account);
898         }
899
900         /* Update the new samu entry. */
901         if (!tdb_update_samacct_only(newpwd, flag)) {
902                 goto cancel;
903         }
904
905         /* Now take care of the case where the RID changed. We need
906          * to delete the old RID key and add the new. */
907
908         if (flag == TDB_MODIFY && newrid != oldrid) { 
909                 fstring keystr;
910
911                 /* Delete old RID key */
912                 DEBUG(10, ("tdb_update_sam: Deleting key for RID %u\n", oldrid));
913                 slprintf(keystr, sizeof(keystr) - 1, "%s%.8x", RIDPREFIX, oldrid);
914                 if (!NT_STATUS_IS_OK(dbwrap_delete_bystring(db_sam, keystr))) {
915                         DEBUG(0, ("tdb_update_sam: Can't delete %s\n", keystr));
916                         goto cancel;
917                 }
918                 /* Insert new RID key */
919                 DEBUG(10, ("tdb_update_sam: Inserting key for RID %u\n", newrid));
920                 if (!tdb_update_ridrec_only(newpwd, TDB_INSERT)) {
921                         goto cancel;
922                 }
923         } else {
924                 DEBUG(10, ("tdb_update_sam: %s key for RID %u\n",
925                         flag == TDB_MODIFY ? "Updating" : "Inserting", newrid));
926                 if (!tdb_update_ridrec_only(newpwd, flag)) {
927                         goto cancel;
928                 }
929         }
930
931         if (dbwrap_transaction_commit(db_sam) != 0) {
932                 DEBUG(0, ("Could not commit transaction\n"));
933                 return false;
934         }
935
936         return true;
937
938  cancel:
939         if (dbwrap_transaction_cancel(db_sam) != 0) {
940                 smb_panic("transaction_cancel failed");
941         }
942         return false;
943 }
944
945 /***************************************************************************
946  Modifies an existing struct samu
947 ****************************************************************************/
948
949 static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
950 {
951         if ( !tdb_update_sam(my_methods, newpwd, TDB_MODIFY) )
952                 return NT_STATUS_UNSUCCESSFUL;
953         
954         return NT_STATUS_OK;
955 }
956
957 /***************************************************************************
958  Adds an existing struct samu
959 ****************************************************************************/
960
961 static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
962 {
963         if ( !tdb_update_sam(my_methods, newpwd, TDB_INSERT) )
964                 return NT_STATUS_UNSUCCESSFUL;
965                 
966         return NT_STATUS_OK;
967 }
968
969 /***************************************************************************
970  Renames a struct samu
971  - check for the posix user/rename user script
972  - Add and lock the new user record
973  - rename the posix user
974  - rewrite the rid->username record
975  - delete the old user
976  - unlock the new user record
977 ***************************************************************************/
978 static NTSTATUS tdbsam_rename_sam_account(struct pdb_methods *my_methods,
979                                           struct samu *old_acct,
980                                           const char *newname)
981 {
982         struct samu      *new_acct = NULL;
983         char *rename_script = NULL;
984         int              rename_ret;
985         fstring          oldname_lower;
986         fstring          newname_lower;
987
988         /* can't do anything without an external script */
989
990         if ( !(new_acct = samu_new( talloc_tos() )) ) {
991                 return NT_STATUS_NO_MEMORY;
992         }
993
994         rename_script = lp_renameuser_script(new_acct);
995         if (!rename_script) {
996                 TALLOC_FREE(new_acct);
997                 return NT_STATUS_NO_MEMORY;
998         }
999         if (!*rename_script) {
1000                 TALLOC_FREE(new_acct);
1001                 return NT_STATUS_ACCESS_DENIED;
1002         }
1003
1004         if ( !pdb_copy_sam_account(new_acct, old_acct)
1005                 || !pdb_set_username(new_acct, newname, PDB_CHANGED))
1006         {
1007                 TALLOC_FREE(new_acct);
1008                 return NT_STATUS_NO_MEMORY;
1009         }
1010
1011         /* open the database */
1012         if ( !tdbsam_open( tdbsam_filename ) ) {
1013                 DEBUG(0, ("tdbsam_getsampwnam: failed to open %s!\n",
1014                           tdbsam_filename));
1015                 TALLOC_FREE(new_acct);
1016                 return NT_STATUS_ACCESS_DENIED;
1017         }
1018
1019         if (dbwrap_transaction_start(db_sam) != 0) {
1020                 DEBUG(0, ("Could not start transaction\n"));
1021                 TALLOC_FREE(new_acct);
1022                 return NT_STATUS_ACCESS_DENIED;
1023
1024         }
1025
1026         /* add the new account and lock it */
1027         if ( !tdb_update_samacct_only(new_acct, TDB_INSERT) ) {
1028                 goto cancel;
1029         }
1030
1031         /* Rename the posix user.  Follow the semantics of _samr_create_user()
1032            so that we lower case the posix name but preserve the case in passdb */
1033
1034         fstrcpy( oldname_lower, pdb_get_username(old_acct) );
1035         strlower_m( oldname_lower );
1036
1037         fstrcpy( newname_lower, newname );
1038         strlower_m( newname_lower );
1039
1040         rename_script = talloc_string_sub2(new_acct,
1041                                 rename_script,
1042                                 "%unew",
1043                                 newname_lower,
1044                                 true,
1045                                 false,
1046                                 true);
1047         if (!rename_script) {
1048                 goto cancel;
1049         }
1050         rename_script = talloc_string_sub2(new_acct,
1051                                 rename_script,
1052                                 "%uold",
1053                                 oldname_lower,
1054                                 true,
1055                                 false,
1056                                 true);
1057         if (!rename_script) {
1058                 goto cancel;
1059         }
1060         rename_ret = smbrun(rename_script, NULL);
1061
1062         DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n",
1063                                 rename_script, rename_ret));
1064
1065         if (rename_ret != 0) {
1066                 goto cancel;
1067         }
1068
1069         smb_nscd_flush_user_cache();
1070
1071         /* rewrite the rid->username record */
1072
1073         if ( !tdb_update_ridrec_only( new_acct, TDB_MODIFY) ) {
1074                 goto cancel;
1075         }
1076
1077         tdb_delete_samacct_only( old_acct );
1078
1079         if (dbwrap_transaction_commit(db_sam) != 0) {
1080                 /*
1081                  * Ok, we're screwed. We've changed the posix account, but
1082                  * could not adapt passdb.tdb. Shall we change the posix
1083                  * account back?
1084                  */
1085                 DEBUG(0, ("transaction_commit failed\n"));
1086                 TALLOC_FREE(new_acct);
1087                 return NT_STATUS_INTERNAL_DB_CORRUPTION;        
1088         }
1089
1090         TALLOC_FREE(new_acct );
1091         return NT_STATUS_OK;
1092
1093  cancel:
1094         if (dbwrap_transaction_cancel(db_sam) != 0) {
1095                 smb_panic("transaction_cancel failed");
1096         }
1097
1098         TALLOC_FREE(new_acct);
1099
1100         return NT_STATUS_ACCESS_DENIED; 
1101 }
1102
1103 static uint32_t tdbsam_capabilities(struct pdb_methods *methods)
1104 {
1105         return PDB_CAP_STORE_RIDS;
1106 }
1107
1108 static bool tdbsam_new_rid(struct pdb_methods *methods, uint32 *prid)
1109 {
1110         uint32 rid;
1111         NTSTATUS status;
1112
1113         rid = BASE_RID;         /* Default if not set */
1114
1115         if (!tdbsam_open(tdbsam_filename)) {
1116                 DEBUG(0,("tdbsam_new_rid: failed to open %s!\n",
1117                         tdbsam_filename));
1118                 return false;
1119         }
1120
1121         status = dbwrap_trans_change_uint32_atomic_bystring(
1122                 db_sam, NEXT_RID_STRING, &rid, 1);
1123         if (!NT_STATUS_IS_OK(status)) {
1124                 DEBUG(3, ("tdbsam_new_rid: Failed to increase %s: %s\n",
1125                         NEXT_RID_STRING, nt_errstr(status)));
1126                 return false;
1127         }
1128
1129         *prid = rid;
1130
1131         return true;
1132 }
1133
1134 struct tdbsam_search_state {
1135         struct pdb_methods *methods;
1136         uint32_t acct_flags;
1137
1138         uint32_t *rids;
1139         uint32_t num_rids;
1140         ssize_t array_size;
1141         uint32_t current;
1142 };
1143
1144 static int tdbsam_collect_rids(struct db_record *rec, void *private_data)
1145 {
1146         struct tdbsam_search_state *state = talloc_get_type_abort(
1147                 private_data, struct tdbsam_search_state);
1148         size_t prefixlen = strlen(RIDPREFIX);
1149         uint32 rid;
1150         TDB_DATA key;
1151
1152         key = dbwrap_record_get_key(rec);
1153
1154         if ((key.dsize < prefixlen)
1155             || (strncmp((char *)key.dptr, RIDPREFIX, prefixlen))) {
1156                 return 0;
1157         }
1158
1159         rid = strtoul((char *)key.dptr+prefixlen, NULL, 16);
1160
1161         ADD_TO_LARGE_ARRAY(state, uint32, rid, &state->rids, &state->num_rids,
1162                            &state->array_size);
1163
1164         return 0;
1165 }
1166
1167 static void tdbsam_search_end(struct pdb_search *search)
1168 {
1169         struct tdbsam_search_state *state = talloc_get_type_abort(
1170                 search->private_data, struct tdbsam_search_state);
1171         TALLOC_FREE(state);
1172 }
1173
1174 static bool tdbsam_search_next_entry(struct pdb_search *search,
1175                                      struct samr_displayentry *entry)
1176 {
1177         struct tdbsam_search_state *state = talloc_get_type_abort(
1178                 search->private_data, struct tdbsam_search_state);
1179         struct samu *user = NULL;
1180         NTSTATUS status;
1181         uint32_t rid;
1182
1183  again:
1184         TALLOC_FREE(user);
1185         user = samu_new(talloc_tos());
1186         if (user == NULL) {
1187                 DEBUG(0, ("samu_new failed\n"));
1188                 return false;
1189         }
1190
1191         if (state->current == state->num_rids) {
1192                 TALLOC_FREE(user);
1193                 return false;
1194         }
1195
1196         rid = state->rids[state->current++];
1197
1198         status = tdbsam_getsampwrid(state->methods, user, rid);
1199
1200         if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1201                 /*
1202                  * Someone has deleted that user since we listed the RIDs
1203                  */
1204                 goto again;
1205         }
1206
1207         if (!NT_STATUS_IS_OK(status)) {
1208                 DEBUG(10, ("tdbsam_getsampwrid failed: %s\n",
1209                            nt_errstr(status)));
1210                 TALLOC_FREE(user);
1211                 return false;
1212         }
1213
1214         if ((state->acct_flags != 0) &&
1215             ((state->acct_flags & pdb_get_acct_ctrl(user)) == 0)) {
1216                 goto again;
1217         }
1218
1219         entry->acct_flags = pdb_get_acct_ctrl(user);
1220         entry->rid = rid;
1221         entry->account_name = talloc_strdup(search, pdb_get_username(user));
1222         entry->fullname = talloc_strdup(search, pdb_get_fullname(user));
1223         entry->description = talloc_strdup(search, pdb_get_acct_desc(user));
1224
1225         TALLOC_FREE(user);
1226
1227         if ((entry->account_name == NULL) || (entry->fullname == NULL)
1228             || (entry->description == NULL)) {
1229                 DEBUG(0, ("talloc_strdup failed\n"));
1230                 return false;
1231         }
1232
1233         return true;
1234 }
1235
1236 static bool tdbsam_search_users(struct pdb_methods *methods,
1237                                 struct pdb_search *search,
1238                                 uint32 acct_flags)
1239 {
1240         struct tdbsam_search_state *state;
1241
1242         if (!tdbsam_open(tdbsam_filename)) {
1243                 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n",
1244                          tdbsam_filename));
1245                 return false;
1246         }
1247
1248         state = talloc_zero(search, struct tdbsam_search_state);
1249         if (state == NULL) {
1250                 DEBUG(0, ("talloc failed\n"));
1251                 return false;
1252         }
1253         state->acct_flags = acct_flags;
1254         state->methods = methods;
1255
1256         dbwrap_traverse_read(db_sam, tdbsam_collect_rids, state, NULL);
1257
1258         search->private_data = state;
1259         search->next_entry = tdbsam_search_next_entry;
1260         search->search_end = tdbsam_search_end;
1261
1262         return true;
1263 }
1264
1265 /*********************************************************************
1266  Initialize the tdb sam backend.  Setup the dispath table of methods,
1267  open the tdb, etc...
1268 *********************************************************************/
1269
1270 static NTSTATUS pdb_init_tdbsam(struct pdb_methods **pdb_method, const char *location)
1271 {
1272         NTSTATUS nt_status;
1273         char *tdbfile = NULL;
1274         const char *pfile = location;
1275
1276         if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) {
1277                 return nt_status;
1278         }
1279
1280         (*pdb_method)->name = "tdbsam";
1281
1282         (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
1283         (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
1284         (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
1285         (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
1286         (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
1287         (*pdb_method)->rename_sam_account = tdbsam_rename_sam_account;
1288         (*pdb_method)->search_users = tdbsam_search_users;
1289
1290         (*pdb_method)->capabilities = tdbsam_capabilities;
1291         (*pdb_method)->new_rid = tdbsam_new_rid;
1292
1293         /* save the path for later */
1294
1295         if (!location) {
1296                 if (asprintf(&tdbfile, "%s/%s", lp_private_dir(),
1297                              PASSDB_FILE_NAME) < 0) {
1298                         return NT_STATUS_NO_MEMORY;
1299                 }
1300                 pfile = tdbfile;
1301         }
1302         tdbsam_filename = SMB_STRDUP(pfile);
1303         if (!tdbsam_filename) {
1304                 return NT_STATUS_NO_MEMORY;
1305         }
1306         SAFE_FREE(tdbfile);
1307
1308         /* no private data */
1309
1310         (*pdb_method)->private_data      = NULL;
1311         (*pdb_method)->free_private_data = NULL;
1312
1313         return NT_STATUS_OK;
1314 }
1315
1316 NTSTATUS pdb_tdbsam_init(void)
1317 {
1318         return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);
1319 }