samba-tool: dbcheck avoid problems with deleted objects
[Samba/gebeck_regimport.git] / source3 / passdb / pdb_tdb.c
blob01c0def57fab97679bc23aadcd715aa3b66fe76f
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
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.
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.
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/>.
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"
34 #if 0 /* when made a module use this */
36 static int tdbsam_debug_level = DBGC_ALL;
37 #undef DBGC_CLASS
38 #define DBGC_CLASS tdbsam_debug_level
40 #else
42 #undef DBGC_CLASS
43 #define DBGC_CLASS DBGC_PASSDB
45 #endif
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"
58 /* GLOBAL TDB SAM CONTEXT */
60 static struct db_context *db_sam;
61 static char *tdbsam_filename;
63 struct tdbsam_convert_state {
64 int32_t from;
65 bool success;
68 static int tdbsam_convert_one(struct db_record *rec, void *priv)
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;
79 key = dbwrap_record_get_key(rec);
81 if (key.dsize < USERPREFIX_LEN) {
82 return 0;
84 if (strncmp((char *)key.dptr, USERPREFIX, USERPREFIX_LEN) != 0) {
85 return 0;
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;
95 DEBUG(10,("tdbsam_convert: Try unpacking a record with (key:%s) "
96 "(version:%d)\n", (char *)key.dptr, state->from));
98 value = dbwrap_record_get_value(rec);
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;
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;
139 data.dsize = init_buffer_from_samu(&data.dptr, user, false);
140 TALLOC_FREE(user);
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;
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;
157 return 0;
160 /**********************************************************************
161 Struct and function to backup an old record.
162 *********************************************************************/
164 struct tdbsam_backup_state {
165 struct db_context *new_db;
166 bool success;
169 static int backup_copy_fn(struct db_record *orig_rec, void *state)
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;
177 key = dbwrap_record_get_key(orig_rec);
179 new_rec = dbwrap_fetch_locked(bs->new_db, talloc_tos(), key);
180 if (new_rec == NULL) {
181 bs->success = false;
182 return 1;
185 value = dbwrap_record_get_value(orig_rec);
187 status = dbwrap_record_store(new_rec, value, TDB_INSERT);
189 TALLOC_FREE(new_rec);
191 if (!NT_STATUS_IS_OK(status)) {
192 bs->success = false;
193 return 1;
195 return 0;
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 *********************************************************************/
206 static bool tdbsam_convert_backup(const char *dbname, struct db_context **pp_db)
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;
215 tmp_fname = talloc_asprintf(frame, "%s.tmp", dbname);
216 if (!tmp_fname) {
217 TALLOC_FREE(frame);
218 return false;
221 unlink(tmp_fname);
223 /* Remember to open this on the NULL context. We need
224 * it to stay around after we return from here. */
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;
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;
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;
251 bs.new_db = tmp_db;
252 bs.success = true;
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;
260 if (!bs.success) {
261 DEBUG(0, ("tdbsam_convert_backup: Rewriting records failed\n"));
262 goto cancel;
265 if (dbwrap_transaction_commit(orig_db) != 0) {
266 smb_panic("tdbsam_convert_backup: orig commit failed\n");
268 if (dbwrap_transaction_commit(tmp_db) != 0) {
269 smb_panic("tdbsam_convert_backup: orig commit failed\n");
272 /* be sure to close the DBs _before_ renaming the file */
274 TALLOC_FREE(orig_db);
275 TALLOC_FREE(tmp_db);
277 /* This is safe from other users as we know we're
278 * under a mutex here. */
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");
288 TALLOC_FREE(frame);
290 /* re-open the converted TDB */
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;
300 DEBUG(1, ("tdbsam_convert_backup: updated %s file.\n",
301 dbname ));
303 /* Replace the global db pointer. */
304 *pp_db = orig_db;
305 return true;
307 cancel:
309 if (dbwrap_transaction_cancel(orig_db) != 0) {
310 smb_panic("tdbsam_convert: transaction_cancel failed");
313 if (dbwrap_transaction_cancel(tmp_db) != 0) {
314 smb_panic("tdbsam_convert: transaction_cancel failed");
317 unlink(tmp_fname);
318 TALLOC_FREE(tmp_db);
319 TALLOC_FREE(frame);
320 return false;
323 static bool tdbsam_upgrade_next_rid(struct db_context *db)
325 TDB_CONTEXT *tdb;
326 uint32 rid;
327 bool ok = false;
328 NTSTATUS status;
330 status = dbwrap_fetch_uint32(db, NEXT_RID_STRING, &rid);
331 if (NT_STATUS_IS_OK(status)) {
332 return true;
335 tdb = tdb_open_log(state_path("winbindd_idmap.tdb"), 0,
336 TDB_DEFAULT, O_RDONLY, 0644);
338 if (tdb) {
339 ok = tdb_fetch_uint32(tdb, "RID_COUNTER", &rid);
340 if (!ok) {
341 rid = BASE_RID;
343 tdb_close(tdb);
344 } else {
345 rid = BASE_RID;
348 status = dbwrap_store_uint32(db, NEXT_RID_STRING, rid);
349 if (!NT_STATUS_IS_OK(status)) {
350 return false;
353 return true;
356 static bool tdbsam_convert(struct db_context **pp_db, const char *name, int32 from)
358 struct tdbsam_convert_state state;
359 struct db_context *db = NULL;
360 NTSTATUS status;
362 /* We only need the update backup for local db's. */
363 if (db_is_local(name) && !tdbsam_convert_backup(name, pp_db)) {
364 DEBUG(0, ("tdbsam_convert: Could not backup %s\n", name));
365 return false;
368 db = *pp_db;
369 state.from = from;
370 state.success = true;
372 if (dbwrap_transaction_start(db) != 0) {
373 DEBUG(0, ("tdbsam_convert: Could not start transaction\n"));
374 return false;
377 if (!tdbsam_upgrade_next_rid(db)) {
378 DEBUG(0, ("tdbsam_convert: tdbsam_upgrade_next_rid failed\n"));
379 goto cancel;
382 status = dbwrap_traverse(db, tdbsam_convert_one, &state, NULL);
383 if (!NT_STATUS_IS_OK(status)) {
384 DEBUG(0, ("tdbsam_convert: traverse failed\n"));
385 goto cancel;
388 if (!state.success) {
389 DEBUG(0, ("tdbsam_convert: Converting records failed\n"));
390 goto cancel;
393 status = dbwrap_store_int32(db, TDBSAM_VERSION_STRING,
394 TDBSAM_VERSION);
395 if (!NT_STATUS_IS_OK(status)) {
396 DEBUG(0, ("tdbsam_convert: Could not store tdbsam version: "
397 "%s\n", nt_errstr(status)));
398 goto cancel;
401 status = dbwrap_store_int32(db, TDBSAM_MINOR_VERSION_STRING,
402 TDBSAM_MINOR_VERSION);
403 if (!NT_STATUS_IS_OK(status)) {
404 DEBUG(0, ("tdbsam_convert: Could not store tdbsam minor "
405 "version: %s\n", nt_errstr(status)));
406 goto cancel;
409 if (dbwrap_transaction_commit(db) != 0) {
410 DEBUG(0, ("tdbsam_convert: Could not commit transaction\n"));
411 return false;
414 return true;
416 cancel:
417 if (dbwrap_transaction_cancel(db) != 0) {
418 smb_panic("tdbsam_convert: transaction_cancel failed");
421 return false;
424 /*********************************************************************
425 Open the tdbsam file based on the absolute path specified.
426 Uses a reference count to allow multiple open calls.
427 *********************************************************************/
429 static bool tdbsam_open( const char *name )
431 int32 version;
432 int32 minor_version;
433 NTSTATUS status;
435 /* check if we are already open */
437 if ( db_sam ) {
438 return true;
441 /* Try to open tdb passwd. Create a new one if necessary */
443 db_sam = db_open(NULL, name, 0, TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
444 if (db_sam == NULL) {
445 DEBUG(0, ("tdbsam_open: Failed to open/create TDB passwd "
446 "[%s]\n", name));
447 return false;
450 /* Check the version */
451 status = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING, &version);
452 if (!NT_STATUS_IS_OK(status)) {
453 version = 0; /* Version not found, assume version 0 */
456 /* Get the minor version */
457 status = dbwrap_fetch_int32(db_sam, TDBSAM_MINOR_VERSION_STRING,
458 &minor_version);
459 if (!NT_STATUS_IS_OK(status)) {
460 minor_version = 0; /* Minor version not found, assume 0 */
463 /* Compare the version */
464 if (version > TDBSAM_VERSION) {
465 /* Version more recent than the latest known */
466 DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
467 TALLOC_FREE(db_sam);
468 return false;
471 if ( version < TDBSAM_VERSION ||
472 (version == TDBSAM_VERSION &&
473 minor_version < TDBSAM_MINOR_VERSION) ) {
475 * Ok - we think we're going to have to convert.
476 * Due to the backup process we now must do to
477 * upgrade we have to get a mutex and re-check
478 * the version. Someone else may have upgraded
479 * whilst we were checking.
482 struct named_mutex *mtx = grab_named_mutex(NULL,
483 "tdbsam_upgrade_mutex",
484 600);
486 if (!mtx) {
487 DEBUG(0, ("tdbsam_open: failed to grab mutex.\n"));
488 TALLOC_FREE(db_sam);
489 return false;
492 /* Re-check the version */
493 status = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING,
494 &version);
495 if (!NT_STATUS_IS_OK(status)) {
496 version = 0; /* Version not found, assume version 0 */
499 /* Re-check the minor version */
500 status = dbwrap_fetch_int32(db_sam, TDBSAM_MINOR_VERSION_STRING,
501 &minor_version);
502 if (!NT_STATUS_IS_OK(status)) {
503 minor_version = 0; /* Minor version not found, assume 0 */
506 /* Compare the version */
507 if (version > TDBSAM_VERSION) {
508 /* Version more recent than the latest known */
509 DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
510 TALLOC_FREE(db_sam);
511 TALLOC_FREE(mtx);
512 return false;
515 if ( version < TDBSAM_VERSION ||
516 (version == TDBSAM_VERSION &&
517 minor_version < TDBSAM_MINOR_VERSION) ) {
519 * Note that minor versions we read that are greater
520 * than the current minor version we have hard coded
521 * are assumed to be compatible if they have the same
522 * major version. That allows previous versions of the
523 * passdb code that don't know about minor versions to
524 * still use this database. JRA.
527 DEBUG(1, ("tdbsam_open: Converting version %d.%d database to "
528 "version %d.%d.\n",
529 version,
530 minor_version,
531 TDBSAM_VERSION,
532 TDBSAM_MINOR_VERSION));
534 if ( !tdbsam_convert(&db_sam, name, version) ) {
535 DEBUG(0, ("tdbsam_open: Error when trying to convert "
536 "tdbsam [%s]\n",name));
537 TALLOC_FREE(db_sam);
538 TALLOC_FREE(mtx);
539 return false;
542 DEBUG(3, ("TDBSAM converted successfully.\n"));
544 TALLOC_FREE(mtx);
547 DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));
549 return true;
552 /******************************************************************
553 Lookup a name in the SAM TDB
554 ******************************************************************/
556 static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods,
557 struct samu *user, const char *sname)
559 TDB_DATA data;
560 fstring keystr;
561 fstring name;
562 NTSTATUS status;
564 if ( !user ) {
565 DEBUG(0,("pdb_getsampwnam: struct samu is NULL.\n"));
566 return NT_STATUS_NO_MEMORY;
569 /* Data is stored in all lower-case */
570 fstrcpy(name, sname);
571 strlower_m(name);
573 /* set search key */
574 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
576 /* open the database */
578 if ( !tdbsam_open( tdbsam_filename ) ) {
579 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
580 return NT_STATUS_ACCESS_DENIED;
583 /* get the record */
585 status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
586 if (!NT_STATUS_IS_OK(status)) {
587 DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
588 DEBUGADD(5, (" Key: %s\n", keystr));
589 return NT_STATUS_NO_SUCH_USER;
592 /* unpack the buffer */
594 if (!init_samu_from_buffer(user, SAMU_BUFFER_LATEST, data.dptr, data.dsize)) {
595 DEBUG(0,("pdb_getsampwent: Bad struct samu entry returned from TDB!\n"));
596 SAFE_FREE(data.dptr);
597 return NT_STATUS_NO_MEMORY;
600 /* success */
602 TALLOC_FREE(data.dptr);
604 return NT_STATUS_OK;
607 /***************************************************************************
608 Search by rid
609 **************************************************************************/
611 static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods,
612 struct samu *user, uint32 rid)
614 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
615 TDB_DATA data;
616 fstring keystr;
617 fstring name;
619 if ( !user ) {
620 DEBUG(0,("pdb_getsampwrid: struct samu is NULL.\n"));
621 return nt_status;
624 /* set search key */
626 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
628 /* open the database */
630 if ( !tdbsam_open( tdbsam_filename ) ) {
631 DEBUG(0,("tdbsam_getsampwrid: failed to open %s!\n", tdbsam_filename));
632 return NT_STATUS_ACCESS_DENIED;
635 /* get the record */
637 nt_status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
638 if (!NT_STATUS_IS_OK(nt_status)) {
639 DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
640 return nt_status;
643 fstrcpy(name, (const char *)data.dptr);
644 TALLOC_FREE(data.dptr);
646 return tdbsam_getsampwnam (my_methods, user, name);
649 static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods,
650 struct samu * user, const struct dom_sid *sid)
652 uint32 rid;
654 if ( !sid_peek_check_rid(get_global_sam_sid(), sid, &rid) )
655 return NT_STATUS_UNSUCCESSFUL;
657 return tdbsam_getsampwrid(my_methods, user, rid);
660 static bool tdb_delete_samacct_only( struct samu *sam_pass )
662 fstring keystr;
663 fstring name;
664 NTSTATUS status;
666 fstrcpy(name, pdb_get_username(sam_pass));
667 strlower_m(name);
669 /* set the search key */
671 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
673 /* it's outaa here! 8^) */
674 if ( !tdbsam_open( tdbsam_filename ) ) {
675 DEBUG(0,("tdb_delete_samacct_only: failed to open %s!\n",
676 tdbsam_filename));
677 return false;
680 status = dbwrap_delete_bystring(db_sam, keystr);
681 if (!NT_STATUS_IS_OK(status)) {
682 DEBUG(5, ("Error deleting entry from tdb passwd "
683 "database: %s!\n", nt_errstr(status)));
684 return false;
687 return true;
690 /***************************************************************************
691 Delete a struct samu records for the username and RID key
692 ****************************************************************************/
694 static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods,
695 struct samu *sam_pass)
697 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
698 fstring keystr;
699 uint32 rid;
700 fstring name;
702 /* open the database */
704 if ( !tdbsam_open( tdbsam_filename ) ) {
705 DEBUG(0,("tdbsam_delete_sam_account: failed to open %s!\n",
706 tdbsam_filename));
707 return NT_STATUS_ACCESS_DENIED;
710 fstrcpy(name, pdb_get_username(sam_pass));
711 strlower_m(name);
713 /* set the search key */
715 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
717 rid = pdb_get_user_rid(sam_pass);
719 /* it's outaa here! 8^) */
721 if (dbwrap_transaction_start(db_sam) != 0) {
722 DEBUG(0, ("Could not start transaction\n"));
723 return NT_STATUS_UNSUCCESSFUL;
726 nt_status = dbwrap_delete_bystring(db_sam, keystr);
727 if (!NT_STATUS_IS_OK(nt_status)) {
728 DEBUG(5, ("Error deleting entry from tdb passwd "
729 "database: %s!\n", nt_errstr(nt_status)));
730 goto cancel;
733 /* set the search key */
735 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
737 /* it's outaa here! 8^) */
739 nt_status = dbwrap_delete_bystring(db_sam, keystr);
740 if (!NT_STATUS_IS_OK(nt_status)) {
741 DEBUG(5, ("Error deleting entry from tdb rid "
742 "database: %s!\n", nt_errstr(nt_status)));
743 goto cancel;
746 if (dbwrap_transaction_commit(db_sam) != 0) {
747 DEBUG(0, ("Could not commit transaction\n"));
748 return NT_STATUS_INTERNAL_DB_CORRUPTION;
751 return NT_STATUS_OK;
753 cancel:
754 if (dbwrap_transaction_cancel(db_sam) != 0) {
755 smb_panic("transaction_cancel failed");
758 return nt_status;
762 /***************************************************************************
763 Update the TDB SAM account record only
764 Assumes that the tdbsam is already open
765 ****************************************************************************/
766 static bool tdb_update_samacct_only( struct samu* newpwd, int flag )
768 TDB_DATA data;
769 uint8 *buf = NULL;
770 fstring keystr;
771 fstring name;
772 bool ret = false;
773 NTSTATUS status;
775 /* copy the struct samu struct into a BYTE buffer for storage */
777 if ( (data.dsize=init_buffer_from_samu(&buf, newpwd, False)) == -1 ) {
778 DEBUG(0,("tdb_update_sam: ERROR - Unable to copy struct samu info BYTE buffer!\n"));
779 goto done;
781 data.dptr = buf;
783 fstrcpy(name, pdb_get_username(newpwd));
784 strlower_m(name);
786 DEBUG(5, ("Storing %saccount %s with RID %d\n",
787 flag == TDB_INSERT ? "(new) " : "", name,
788 pdb_get_user_rid(newpwd)));
790 /* setup the USER index key */
791 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
793 /* add the account */
795 status = dbwrap_store_bystring(db_sam, keystr, data, flag);
796 if (!NT_STATUS_IS_OK(status)) {
797 DEBUG(0, ("Unable to modify passwd TDB: %s!",
798 nt_errstr(status)));
799 goto done;
802 ret = true;
804 done:
805 /* cleanup */
806 SAFE_FREE(buf);
807 return ret;
810 /***************************************************************************
811 Update the TDB SAM RID record only
812 Assumes that the tdbsam is already open
813 ****************************************************************************/
814 static bool tdb_update_ridrec_only( struct samu* newpwd, int flag )
816 TDB_DATA data;
817 fstring keystr;
818 fstring name;
819 NTSTATUS status;
821 fstrcpy(name, pdb_get_username(newpwd));
822 strlower_m(name);
824 /* setup RID data */
825 data = string_term_tdb_data(name);
827 /* setup the RID index key */
828 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX,
829 pdb_get_user_rid(newpwd));
831 /* add the reference */
832 status = dbwrap_store_bystring(db_sam, keystr, data, flag);
833 if (!NT_STATUS_IS_OK(status)) {
834 DEBUG(0, ("Unable to modify TDB passwd: %s!\n",
835 nt_errstr(status)));
836 return false;
839 return true;
843 /***************************************************************************
844 Update the TDB SAM
845 ****************************************************************************/
847 static bool tdb_update_sam(struct pdb_methods *my_methods, struct samu* newpwd,
848 int flag)
850 uint32_t oldrid;
851 uint32_t newrid;
853 if (!(newrid = pdb_get_user_rid(newpwd))) {
854 DEBUG(0,("tdb_update_sam: struct samu (%s) with no RID!\n",
855 pdb_get_username(newpwd)));
856 return False;
859 oldrid = newrid;
861 /* open the database */
863 if ( !tdbsam_open( tdbsam_filename ) ) {
864 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
865 return False;
868 if (dbwrap_transaction_start(db_sam) != 0) {
869 DEBUG(0, ("Could not start transaction\n"));
870 return false;
873 /* If we are updating, we may be changing this users RID. Retrieve the old RID
874 so we can check. */
876 if (flag == TDB_MODIFY) {
877 struct samu *account = samu_new(talloc_tos());
878 if (account == NULL) {
879 DEBUG(0,("tdb_update_sam: samu_new() failed\n"));
880 goto cancel;
882 if (!NT_STATUS_IS_OK(tdbsam_getsampwnam(my_methods, account, pdb_get_username(newpwd)))) {
883 DEBUG(0,("tdb_update_sam: tdbsam_getsampwnam() for %s failed\n",
884 pdb_get_username(newpwd)));
885 TALLOC_FREE(account);
886 goto cancel;
888 if (!(oldrid = pdb_get_user_rid(account))) {
889 DEBUG(0,("tdb_update_sam: pdb_get_user_rid() failed\n"));
890 TALLOC_FREE(account);
891 goto cancel;
893 TALLOC_FREE(account);
896 /* Update the new samu entry. */
897 if (!tdb_update_samacct_only(newpwd, flag)) {
898 goto cancel;
901 /* Now take care of the case where the RID changed. We need
902 * to delete the old RID key and add the new. */
904 if (flag == TDB_MODIFY && newrid != oldrid) {
905 fstring keystr;
907 /* Delete old RID key */
908 DEBUG(10, ("tdb_update_sam: Deleting key for RID %u\n", oldrid));
909 slprintf(keystr, sizeof(keystr) - 1, "%s%.8x", RIDPREFIX, oldrid);
910 if (!NT_STATUS_IS_OK(dbwrap_delete_bystring(db_sam, keystr))) {
911 DEBUG(0, ("tdb_update_sam: Can't delete %s\n", keystr));
912 goto cancel;
914 /* Insert new RID key */
915 DEBUG(10, ("tdb_update_sam: Inserting key for RID %u\n", newrid));
916 if (!tdb_update_ridrec_only(newpwd, TDB_INSERT)) {
917 goto cancel;
919 } else {
920 DEBUG(10, ("tdb_update_sam: %s key for RID %u\n",
921 flag == TDB_MODIFY ? "Updating" : "Inserting", newrid));
922 if (!tdb_update_ridrec_only(newpwd, flag)) {
923 goto cancel;
927 if (dbwrap_transaction_commit(db_sam) != 0) {
928 DEBUG(0, ("Could not commit transaction\n"));
929 return false;
932 return true;
934 cancel:
935 if (dbwrap_transaction_cancel(db_sam) != 0) {
936 smb_panic("transaction_cancel failed");
938 return false;
941 /***************************************************************************
942 Modifies an existing struct samu
943 ****************************************************************************/
945 static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
947 if ( !tdb_update_sam(my_methods, newpwd, TDB_MODIFY) )
948 return NT_STATUS_UNSUCCESSFUL;
950 return NT_STATUS_OK;
953 /***************************************************************************
954 Adds an existing struct samu
955 ****************************************************************************/
957 static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
959 if ( !tdb_update_sam(my_methods, newpwd, TDB_INSERT) )
960 return NT_STATUS_UNSUCCESSFUL;
962 return NT_STATUS_OK;
965 /***************************************************************************
966 Renames a struct samu
967 - check for the posix user/rename user script
968 - Add and lock the new user record
969 - rename the posix user
970 - rewrite the rid->username record
971 - delete the old user
972 - unlock the new user record
973 ***************************************************************************/
974 static NTSTATUS tdbsam_rename_sam_account(struct pdb_methods *my_methods,
975 struct samu *old_acct,
976 const char *newname)
978 struct samu *new_acct = NULL;
979 char *rename_script = NULL;
980 int rename_ret;
981 fstring oldname_lower;
982 fstring newname_lower;
984 /* can't do anything without an external script */
986 if ( !(new_acct = samu_new( talloc_tos() )) ) {
987 return NT_STATUS_NO_MEMORY;
990 rename_script = talloc_strdup(new_acct, lp_renameuser_script());
991 if (!rename_script) {
992 TALLOC_FREE(new_acct);
993 return NT_STATUS_NO_MEMORY;
995 if (!*rename_script) {
996 TALLOC_FREE(new_acct);
997 return NT_STATUS_ACCESS_DENIED;
1000 if ( !pdb_copy_sam_account(new_acct, old_acct)
1001 || !pdb_set_username(new_acct, newname, PDB_CHANGED))
1003 TALLOC_FREE(new_acct);
1004 return NT_STATUS_NO_MEMORY;
1007 /* open the database */
1008 if ( !tdbsam_open( tdbsam_filename ) ) {
1009 DEBUG(0, ("tdbsam_getsampwnam: failed to open %s!\n",
1010 tdbsam_filename));
1011 TALLOC_FREE(new_acct);
1012 return NT_STATUS_ACCESS_DENIED;
1015 if (dbwrap_transaction_start(db_sam) != 0) {
1016 DEBUG(0, ("Could not start transaction\n"));
1017 TALLOC_FREE(new_acct);
1018 return NT_STATUS_ACCESS_DENIED;
1022 /* add the new account and lock it */
1023 if ( !tdb_update_samacct_only(new_acct, TDB_INSERT) ) {
1024 goto cancel;
1027 /* Rename the posix user. Follow the semantics of _samr_create_user()
1028 so that we lower case the posix name but preserve the case in passdb */
1030 fstrcpy( oldname_lower, pdb_get_username(old_acct) );
1031 strlower_m( oldname_lower );
1033 fstrcpy( newname_lower, newname );
1034 strlower_m( newname_lower );
1036 rename_script = talloc_string_sub2(new_acct,
1037 rename_script,
1038 "%unew",
1039 newname_lower,
1040 true,
1041 false,
1042 true);
1043 if (!rename_script) {
1044 goto cancel;
1046 rename_script = talloc_string_sub2(new_acct,
1047 rename_script,
1048 "%uold",
1049 oldname_lower,
1050 true,
1051 false,
1052 true);
1053 if (!rename_script) {
1054 goto cancel;
1056 rename_ret = smbrun(rename_script, NULL);
1058 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n",
1059 rename_script, rename_ret));
1061 if (rename_ret != 0) {
1062 goto cancel;
1065 smb_nscd_flush_user_cache();
1067 /* rewrite the rid->username record */
1069 if ( !tdb_update_ridrec_only( new_acct, TDB_MODIFY) ) {
1070 goto cancel;
1073 tdb_delete_samacct_only( old_acct );
1075 if (dbwrap_transaction_commit(db_sam) != 0) {
1077 * Ok, we're screwed. We've changed the posix account, but
1078 * could not adapt passdb.tdb. Shall we change the posix
1079 * account back?
1081 DEBUG(0, ("transaction_commit failed\n"));
1082 TALLOC_FREE(new_acct);
1083 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1086 TALLOC_FREE(new_acct );
1087 return NT_STATUS_OK;
1089 cancel:
1090 if (dbwrap_transaction_cancel(db_sam) != 0) {
1091 smb_panic("transaction_cancel failed");
1094 TALLOC_FREE(new_acct);
1096 return NT_STATUS_ACCESS_DENIED;
1099 static uint32_t tdbsam_capabilities(struct pdb_methods *methods)
1101 return PDB_CAP_STORE_RIDS;
1104 static bool tdbsam_new_rid(struct pdb_methods *methods, uint32 *prid)
1106 uint32 rid;
1107 NTSTATUS status;
1109 rid = BASE_RID; /* Default if not set */
1111 if (!tdbsam_open(tdbsam_filename)) {
1112 DEBUG(0,("tdbsam_new_rid: failed to open %s!\n",
1113 tdbsam_filename));
1114 return false;
1117 status = dbwrap_trans_change_uint32_atomic(db_sam, NEXT_RID_STRING,
1118 &rid, 1);
1119 if (!NT_STATUS_IS_OK(status)) {
1120 DEBUG(3, ("tdbsam_new_rid: Failed to increase %s: %s\n",
1121 NEXT_RID_STRING, nt_errstr(status)));
1122 return false;
1125 *prid = rid;
1127 return true;
1130 struct tdbsam_search_state {
1131 struct pdb_methods *methods;
1132 uint32_t acct_flags;
1134 uint32_t *rids;
1135 uint32_t num_rids;
1136 ssize_t array_size;
1137 uint32_t current;
1140 static int tdbsam_collect_rids(struct db_record *rec, void *private_data)
1142 struct tdbsam_search_state *state = talloc_get_type_abort(
1143 private_data, struct tdbsam_search_state);
1144 size_t prefixlen = strlen(RIDPREFIX);
1145 uint32 rid;
1146 TDB_DATA key;
1148 key = dbwrap_record_get_key(rec);
1150 if ((key.dsize < prefixlen)
1151 || (strncmp((char *)key.dptr, RIDPREFIX, prefixlen))) {
1152 return 0;
1155 rid = strtoul((char *)key.dptr+prefixlen, NULL, 16);
1157 ADD_TO_LARGE_ARRAY(state, uint32, rid, &state->rids, &state->num_rids,
1158 &state->array_size);
1160 return 0;
1163 static void tdbsam_search_end(struct pdb_search *search)
1165 struct tdbsam_search_state *state = talloc_get_type_abort(
1166 search->private_data, struct tdbsam_search_state);
1167 TALLOC_FREE(state);
1170 static bool tdbsam_search_next_entry(struct pdb_search *search,
1171 struct samr_displayentry *entry)
1173 struct tdbsam_search_state *state = talloc_get_type_abort(
1174 search->private_data, struct tdbsam_search_state);
1175 struct samu *user = NULL;
1176 NTSTATUS status;
1177 uint32_t rid;
1179 again:
1180 TALLOC_FREE(user);
1181 user = samu_new(talloc_tos());
1182 if (user == NULL) {
1183 DEBUG(0, ("samu_new failed\n"));
1184 return false;
1187 if (state->current == state->num_rids) {
1188 return false;
1191 rid = state->rids[state->current++];
1193 status = tdbsam_getsampwrid(state->methods, user, rid);
1195 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1197 * Someone has deleted that user since we listed the RIDs
1199 goto again;
1202 if (!NT_STATUS_IS_OK(status)) {
1203 DEBUG(10, ("tdbsam_getsampwrid failed: %s\n",
1204 nt_errstr(status)));
1205 TALLOC_FREE(user);
1206 return false;
1209 if ((state->acct_flags != 0) &&
1210 ((state->acct_flags & pdb_get_acct_ctrl(user)) == 0)) {
1211 goto again;
1214 entry->acct_flags = pdb_get_acct_ctrl(user);
1215 entry->rid = rid;
1216 entry->account_name = talloc_strdup(search, pdb_get_username(user));
1217 entry->fullname = talloc_strdup(search, pdb_get_fullname(user));
1218 entry->description = talloc_strdup(search, pdb_get_acct_desc(user));
1220 TALLOC_FREE(user);
1222 if ((entry->account_name == NULL) || (entry->fullname == NULL)
1223 || (entry->description == NULL)) {
1224 DEBUG(0, ("talloc_strdup failed\n"));
1225 return false;
1228 return true;
1231 static bool tdbsam_search_users(struct pdb_methods *methods,
1232 struct pdb_search *search,
1233 uint32 acct_flags)
1235 struct tdbsam_search_state *state;
1237 if (!tdbsam_open(tdbsam_filename)) {
1238 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n",
1239 tdbsam_filename));
1240 return false;
1243 state = talloc_zero(search, struct tdbsam_search_state);
1244 if (state == NULL) {
1245 DEBUG(0, ("talloc failed\n"));
1246 return false;
1248 state->acct_flags = acct_flags;
1249 state->methods = methods;
1251 dbwrap_traverse_read(db_sam, tdbsam_collect_rids, state, NULL);
1253 search->private_data = state;
1254 search->next_entry = tdbsam_search_next_entry;
1255 search->search_end = tdbsam_search_end;
1257 return true;
1260 /*********************************************************************
1261 Initialize the tdb sam backend. Setup the dispath table of methods,
1262 open the tdb, etc...
1263 *********************************************************************/
1265 static NTSTATUS pdb_init_tdbsam(struct pdb_methods **pdb_method, const char *location)
1267 NTSTATUS nt_status;
1268 char *tdbfile = NULL;
1269 const char *pfile = location;
1271 if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) {
1272 return nt_status;
1275 (*pdb_method)->name = "tdbsam";
1277 (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
1278 (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
1279 (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
1280 (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
1281 (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
1282 (*pdb_method)->rename_sam_account = tdbsam_rename_sam_account;
1283 (*pdb_method)->search_users = tdbsam_search_users;
1285 (*pdb_method)->capabilities = tdbsam_capabilities;
1286 (*pdb_method)->new_rid = tdbsam_new_rid;
1288 /* save the path for later */
1290 if (!location) {
1291 if (asprintf(&tdbfile, "%s/%s", lp_private_dir(),
1292 PASSDB_FILE_NAME) < 0) {
1293 return NT_STATUS_NO_MEMORY;
1295 pfile = tdbfile;
1297 tdbsam_filename = SMB_STRDUP(pfile);
1298 if (!tdbsam_filename) {
1299 return NT_STATUS_NO_MEMORY;
1301 SAFE_FREE(tdbfile);
1303 /* no private data */
1305 (*pdb_method)->private_data = NULL;
1306 (*pdb_method)->free_private_data = NULL;
1308 return NT_STATUS_OK;
1311 NTSTATUS pdb_tdbsam_init(void)
1313 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);