selftest/flapping: more samba4.rpc.samr.large-dc.two subtests are flakey
[Samba/bb.git] / source3 / passdb / pdb_tdb.c
blobbd6e123e31cfa13a59806f68f1e4c13c8b029a4a
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 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;
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;
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;
252 bs.new_db = tmp_db;
253 bs.success = true;
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;
261 if (!bs.success) {
262 DEBUG(0, ("tdbsam_convert_backup: Rewriting records failed\n"));
263 goto cancel;
266 if (dbwrap_transaction_commit(orig_db) != 0) {
267 smb_panic("tdbsam_convert_backup: orig commit failed\n");
269 if (dbwrap_transaction_commit(tmp_db) != 0) {
270 smb_panic("tdbsam_convert_backup: orig commit failed\n");
273 /* be sure to close the DBs _before_ renaming the file */
275 TALLOC_FREE(orig_db);
276 TALLOC_FREE(tmp_db);
278 /* This is safe from other users as we know we're
279 * under a mutex here. */
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");
289 TALLOC_FREE(frame);
291 /* re-open the converted TDB */
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;
302 DEBUG(1, ("tdbsam_convert_backup: updated %s file.\n",
303 dbname ));
305 /* Replace the global db pointer. */
306 *pp_db = orig_db;
307 return true;
309 cancel:
311 if (dbwrap_transaction_cancel(orig_db) != 0) {
312 smb_panic("tdbsam_convert: transaction_cancel failed");
315 if (dbwrap_transaction_cancel(tmp_db) != 0) {
316 smb_panic("tdbsam_convert: transaction_cancel failed");
319 unlink(tmp_fname);
320 TALLOC_FREE(tmp_db);
321 TALLOC_FREE(frame);
322 return false;
325 static bool tdbsam_upgrade_next_rid(struct db_context *db)
327 TDB_CONTEXT *tdb;
328 uint32 rid;
329 bool ok = false;
330 NTSTATUS status;
332 status = dbwrap_fetch_uint32_bystring(db, NEXT_RID_STRING, &rid);
333 if (NT_STATUS_IS_OK(status)) {
334 return true;
337 tdb = tdb_open_log(state_path("winbindd_idmap.tdb"), 0,
338 TDB_DEFAULT, O_RDONLY, 0644);
340 if (tdb) {
341 ok = tdb_fetch_uint32(tdb, "RID_COUNTER", &rid);
342 if (!ok) {
343 rid = BASE_RID;
345 tdb_close(tdb);
346 } else {
347 rid = BASE_RID;
350 status = dbwrap_store_uint32_bystring(db, NEXT_RID_STRING, rid);
351 if (!NT_STATUS_IS_OK(status)) {
352 return false;
355 return true;
358 static bool tdbsam_convert(struct db_context **pp_db, const char *name, int32 from)
360 struct tdbsam_convert_state state;
361 struct db_context *db = NULL;
362 NTSTATUS status;
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;
370 db = *pp_db;
371 state.from = from;
372 state.success = true;
374 if (dbwrap_transaction_start(db) != 0) {
375 DEBUG(0, ("tdbsam_convert: Could not start transaction\n"));
376 return false;
379 if (!tdbsam_upgrade_next_rid(db)) {
380 DEBUG(0, ("tdbsam_convert: tdbsam_upgrade_next_rid failed\n"));
381 goto cancel;
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;
390 if (!state.success) {
391 DEBUG(0, ("tdbsam_convert: Converting records failed\n"));
392 goto cancel;
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;
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;
411 if (dbwrap_transaction_commit(db) != 0) {
412 DEBUG(0, ("tdbsam_convert: Could not commit transaction\n"));
413 return false;
416 return true;
418 cancel:
419 if (dbwrap_transaction_cancel(db) != 0) {
420 smb_panic("tdbsam_convert: transaction_cancel failed");
423 return false;
426 /*********************************************************************
427 Open the tdbsam file based on the absolute path specified.
428 Uses a reference count to allow multiple open calls.
429 *********************************************************************/
431 static bool tdbsam_open( const char *name )
433 int32 version;
434 int32 minor_version;
435 NTSTATUS status;
437 /* check if we are already open */
439 if ( db_sam ) {
440 return true;
443 /* Try to open tdb passwd. Create a new one if necessary */
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;
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 */
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 */
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;
475 if ( version < TDBSAM_VERSION ||
476 (version == TDBSAM_VERSION &&
477 minor_version < TDBSAM_MINOR_VERSION) ) {
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.
486 struct named_mutex *mtx = grab_named_mutex(NULL,
487 "tdbsam_upgrade_mutex",
488 600);
490 if (!mtx) {
491 DEBUG(0, ("tdbsam_open: failed to grab mutex.\n"));
492 TALLOC_FREE(db_sam);
493 return false;
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 */
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 */
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;
519 if ( version < TDBSAM_VERSION ||
520 (version == TDBSAM_VERSION &&
521 minor_version < TDBSAM_MINOR_VERSION) ) {
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.
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));
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;
546 DEBUG(3, ("TDBSAM converted successfully.\n"));
548 TALLOC_FREE(mtx);
551 DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));
553 return true;
556 /******************************************************************
557 Lookup a name in the SAM TDB
558 ******************************************************************/
560 static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods,
561 struct samu *user, const char *sname)
563 TDB_DATA data;
564 fstring keystr;
565 fstring name;
566 NTSTATUS status;
568 if ( !user ) {
569 DEBUG(0,("pdb_getsampwnam: struct samu is NULL.\n"));
570 return NT_STATUS_NO_MEMORY;
573 /* Data is stored in all lower-case */
574 fstrcpy(name, sname);
575 if (!strlower_m(name)) {
576 return NT_STATUS_INVALID_PARAMETER;
579 /* set search key */
580 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
582 /* open the database */
584 if ( !tdbsam_open( tdbsam_filename ) ) {
585 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
586 return NT_STATUS_ACCESS_DENIED;
589 /* get the record */
591 status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
592 if (!NT_STATUS_IS_OK(status)) {
593 DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
594 DEBUGADD(5, (" Key: %s\n", keystr));
595 return NT_STATUS_NO_SUCH_USER;
598 /* unpack the buffer */
600 if (!init_samu_from_buffer(user, SAMU_BUFFER_LATEST, data.dptr, data.dsize)) {
601 DEBUG(0,("pdb_getsampwent: Bad struct samu entry returned from TDB!\n"));
602 SAFE_FREE(data.dptr);
603 return NT_STATUS_NO_MEMORY;
606 /* success */
608 TALLOC_FREE(data.dptr);
610 return NT_STATUS_OK;
613 /***************************************************************************
614 Search by rid
615 **************************************************************************/
617 static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods,
618 struct samu *user, uint32 rid)
620 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
621 TDB_DATA data;
622 fstring keystr;
623 fstring name;
625 if ( !user ) {
626 DEBUG(0,("pdb_getsampwrid: struct samu is NULL.\n"));
627 return nt_status;
630 /* set search key */
632 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
634 /* open the database */
636 if ( !tdbsam_open( tdbsam_filename ) ) {
637 DEBUG(0,("tdbsam_getsampwrid: failed to open %s!\n", tdbsam_filename));
638 return NT_STATUS_ACCESS_DENIED;
641 /* get the record */
643 nt_status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
644 if (!NT_STATUS_IS_OK(nt_status)) {
645 DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
646 return nt_status;
649 fstrcpy(name, (const char *)data.dptr);
650 TALLOC_FREE(data.dptr);
652 return tdbsam_getsampwnam (my_methods, user, name);
655 static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods,
656 struct samu * user, const struct dom_sid *sid)
658 uint32 rid;
660 if ( !sid_peek_check_rid(get_global_sam_sid(), sid, &rid) )
661 return NT_STATUS_UNSUCCESSFUL;
663 return tdbsam_getsampwrid(my_methods, user, rid);
666 static bool tdb_delete_samacct_only( struct samu *sam_pass )
668 fstring keystr;
669 fstring name;
670 NTSTATUS status;
672 fstrcpy(name, pdb_get_username(sam_pass));
673 if (!strlower_m(name)) {
674 return false;
677 /* set the search key */
679 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
681 /* it's outaa here! 8^) */
682 if ( !tdbsam_open( tdbsam_filename ) ) {
683 DEBUG(0,("tdb_delete_samacct_only: failed to open %s!\n",
684 tdbsam_filename));
685 return false;
688 status = dbwrap_delete_bystring(db_sam, keystr);
689 if (!NT_STATUS_IS_OK(status)) {
690 DEBUG(5, ("Error deleting entry from tdb passwd "
691 "database: %s!\n", nt_errstr(status)));
692 return false;
695 return true;
698 /***************************************************************************
699 Delete a struct samu records for the username and RID key
700 ****************************************************************************/
702 static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods,
703 struct samu *sam_pass)
705 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
706 fstring keystr;
707 uint32 rid;
708 fstring name;
710 /* open the database */
712 if ( !tdbsam_open( tdbsam_filename ) ) {
713 DEBUG(0,("tdbsam_delete_sam_account: failed to open %s!\n",
714 tdbsam_filename));
715 return NT_STATUS_ACCESS_DENIED;
718 fstrcpy(name, pdb_get_username(sam_pass));
719 if (!strlower_m(name)) {
720 return NT_STATUS_INVALID_PARAMETER;
723 /* set the search key */
725 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
727 rid = pdb_get_user_rid(sam_pass);
729 /* it's outaa here! 8^) */
731 if (dbwrap_transaction_start(db_sam) != 0) {
732 DEBUG(0, ("Could not start transaction\n"));
733 return NT_STATUS_UNSUCCESSFUL;
736 nt_status = dbwrap_delete_bystring(db_sam, keystr);
737 if (!NT_STATUS_IS_OK(nt_status)) {
738 DEBUG(5, ("Error deleting entry from tdb passwd "
739 "database: %s!\n", nt_errstr(nt_status)));
740 goto cancel;
743 /* set the search key */
745 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
747 /* it's outaa here! 8^) */
749 nt_status = dbwrap_delete_bystring(db_sam, keystr);
750 if (!NT_STATUS_IS_OK(nt_status)) {
751 DEBUG(5, ("Error deleting entry from tdb rid "
752 "database: %s!\n", nt_errstr(nt_status)));
753 goto cancel;
756 if (dbwrap_transaction_commit(db_sam) != 0) {
757 DEBUG(0, ("Could not commit transaction\n"));
758 return NT_STATUS_INTERNAL_DB_CORRUPTION;
761 return NT_STATUS_OK;
763 cancel:
764 if (dbwrap_transaction_cancel(db_sam) != 0) {
765 smb_panic("transaction_cancel failed");
768 return nt_status;
772 /***************************************************************************
773 Update the TDB SAM account record only
774 Assumes that the tdbsam is already open
775 ****************************************************************************/
776 static bool tdb_update_samacct_only( struct samu* newpwd, int flag )
778 TDB_DATA data;
779 uint8 *buf = NULL;
780 fstring keystr;
781 fstring name;
782 bool ret = false;
783 NTSTATUS status;
785 /* copy the struct samu struct into a BYTE buffer for storage */
787 if ( (data.dsize=init_buffer_from_samu(&buf, newpwd, False)) == -1 ) {
788 DEBUG(0,("tdb_update_sam: ERROR - Unable to copy struct samu info BYTE buffer!\n"));
789 goto done;
791 data.dptr = buf;
793 fstrcpy(name, pdb_get_username(newpwd));
794 if (!strlower_m(name)) {
795 goto done;
798 DEBUG(5, ("Storing %saccount %s with RID %d\n",
799 flag == TDB_INSERT ? "(new) " : "", name,
800 pdb_get_user_rid(newpwd)));
802 /* setup the USER index key */
803 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
805 /* add the account */
807 status = dbwrap_store_bystring(db_sam, keystr, data, flag);
808 if (!NT_STATUS_IS_OK(status)) {
809 DEBUG(0, ("Unable to modify passwd TDB: %s!",
810 nt_errstr(status)));
811 goto done;
814 ret = true;
816 done:
817 /* cleanup */
818 SAFE_FREE(buf);
819 return ret;
822 /***************************************************************************
823 Update the TDB SAM RID record only
824 Assumes that the tdbsam is already open
825 ****************************************************************************/
826 static bool tdb_update_ridrec_only( struct samu* newpwd, int flag )
828 TDB_DATA data;
829 fstring keystr;
830 fstring name;
831 NTSTATUS status;
833 fstrcpy(name, pdb_get_username(newpwd));
834 if (!strlower_m(name)) {
835 return false;
838 /* setup RID data */
839 data = string_term_tdb_data(name);
841 /* setup the RID index key */
842 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX,
843 pdb_get_user_rid(newpwd));
845 /* add the reference */
846 status = dbwrap_store_bystring(db_sam, keystr, data, flag);
847 if (!NT_STATUS_IS_OK(status)) {
848 DEBUG(0, ("Unable to modify TDB passwd: %s!\n",
849 nt_errstr(status)));
850 return false;
853 return true;
857 /***************************************************************************
858 Update the TDB SAM
859 ****************************************************************************/
861 static bool tdb_update_sam(struct pdb_methods *my_methods, struct samu* newpwd,
862 int flag)
864 uint32_t oldrid;
865 uint32_t newrid;
867 if (!(newrid = pdb_get_user_rid(newpwd))) {
868 DEBUG(0,("tdb_update_sam: struct samu (%s) with no RID!\n",
869 pdb_get_username(newpwd)));
870 return False;
873 oldrid = newrid;
875 /* open the database */
877 if ( !tdbsam_open( tdbsam_filename ) ) {
878 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
879 return False;
882 if (dbwrap_transaction_start(db_sam) != 0) {
883 DEBUG(0, ("Could not start transaction\n"));
884 return false;
887 /* If we are updating, we may be changing this users RID. Retrieve the old RID
888 so we can check. */
890 if (flag == TDB_MODIFY) {
891 struct samu *account = samu_new(talloc_tos());
892 if (account == NULL) {
893 DEBUG(0,("tdb_update_sam: samu_new() failed\n"));
894 goto cancel;
896 if (!NT_STATUS_IS_OK(tdbsam_getsampwnam(my_methods, account, pdb_get_username(newpwd)))) {
897 DEBUG(0,("tdb_update_sam: tdbsam_getsampwnam() for %s failed\n",
898 pdb_get_username(newpwd)));
899 TALLOC_FREE(account);
900 goto cancel;
902 if (!(oldrid = pdb_get_user_rid(account))) {
903 DEBUG(0,("tdb_update_sam: pdb_get_user_rid() failed\n"));
904 TALLOC_FREE(account);
905 goto cancel;
907 TALLOC_FREE(account);
910 /* Update the new samu entry. */
911 if (!tdb_update_samacct_only(newpwd, flag)) {
912 goto cancel;
915 /* Now take care of the case where the RID changed. We need
916 * to delete the old RID key and add the new. */
918 if (flag == TDB_MODIFY && newrid != oldrid) {
919 fstring keystr;
921 /* Delete old RID key */
922 DEBUG(10, ("tdb_update_sam: Deleting key for RID %u\n", oldrid));
923 slprintf(keystr, sizeof(keystr) - 1, "%s%.8x", RIDPREFIX, oldrid);
924 if (!NT_STATUS_IS_OK(dbwrap_delete_bystring(db_sam, keystr))) {
925 DEBUG(0, ("tdb_update_sam: Can't delete %s\n", keystr));
926 goto cancel;
928 /* Insert new RID key */
929 DEBUG(10, ("tdb_update_sam: Inserting key for RID %u\n", newrid));
930 if (!tdb_update_ridrec_only(newpwd, TDB_INSERT)) {
931 goto cancel;
933 } else {
934 DEBUG(10, ("tdb_update_sam: %s key for RID %u\n",
935 flag == TDB_MODIFY ? "Updating" : "Inserting", newrid));
936 if (!tdb_update_ridrec_only(newpwd, flag)) {
937 goto cancel;
941 if (dbwrap_transaction_commit(db_sam) != 0) {
942 DEBUG(0, ("Could not commit transaction\n"));
943 return false;
946 return true;
948 cancel:
949 if (dbwrap_transaction_cancel(db_sam) != 0) {
950 smb_panic("transaction_cancel failed");
952 return false;
955 /***************************************************************************
956 Modifies an existing struct samu
957 ****************************************************************************/
959 static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
961 if ( !tdb_update_sam(my_methods, newpwd, TDB_MODIFY) )
962 return NT_STATUS_UNSUCCESSFUL;
964 return NT_STATUS_OK;
967 /***************************************************************************
968 Adds an existing struct samu
969 ****************************************************************************/
971 static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
973 if ( !tdb_update_sam(my_methods, newpwd, TDB_INSERT) )
974 return NT_STATUS_UNSUCCESSFUL;
976 return NT_STATUS_OK;
979 /***************************************************************************
980 Renames a struct samu
981 - check for the posix user/rename user script
982 - Add and lock the new user record
983 - rename the posix user
984 - rewrite the rid->username record
985 - delete the old user
986 - unlock the new user record
987 ***************************************************************************/
988 static NTSTATUS tdbsam_rename_sam_account(struct pdb_methods *my_methods,
989 struct samu *old_acct,
990 const char *newname)
992 struct samu *new_acct = NULL;
993 char *rename_script = NULL;
994 int rename_ret;
995 fstring oldname_lower;
996 fstring newname_lower;
998 /* can't do anything without an external script */
1000 if ( !(new_acct = samu_new( talloc_tos() )) ) {
1001 return NT_STATUS_NO_MEMORY;
1004 rename_script = lp_renameuser_script(new_acct);
1005 if (!rename_script) {
1006 TALLOC_FREE(new_acct);
1007 return NT_STATUS_NO_MEMORY;
1009 if (!*rename_script) {
1010 TALLOC_FREE(new_acct);
1011 return NT_STATUS_ACCESS_DENIED;
1014 if ( !pdb_copy_sam_account(new_acct, old_acct)
1015 || !pdb_set_username(new_acct, newname, PDB_CHANGED))
1017 TALLOC_FREE(new_acct);
1018 return NT_STATUS_NO_MEMORY;
1021 /* open the database */
1022 if ( !tdbsam_open( tdbsam_filename ) ) {
1023 DEBUG(0, ("tdbsam_getsampwnam: failed to open %s!\n",
1024 tdbsam_filename));
1025 TALLOC_FREE(new_acct);
1026 return NT_STATUS_ACCESS_DENIED;
1029 if (dbwrap_transaction_start(db_sam) != 0) {
1030 DEBUG(0, ("Could not start transaction\n"));
1031 TALLOC_FREE(new_acct);
1032 return NT_STATUS_ACCESS_DENIED;
1036 /* add the new account and lock it */
1037 if ( !tdb_update_samacct_only(new_acct, TDB_INSERT) ) {
1038 goto cancel;
1041 /* Rename the posix user. Follow the semantics of _samr_create_user()
1042 so that we lower case the posix name but preserve the case in passdb */
1044 fstrcpy( oldname_lower, pdb_get_username(old_acct) );
1045 if (!strlower_m( oldname_lower )) {
1046 goto cancel;
1049 fstrcpy( newname_lower, newname );
1050 if (!strlower_m( newname_lower )) {
1051 goto cancel;
1054 rename_script = talloc_string_sub2(new_acct,
1055 rename_script,
1056 "%unew",
1057 newname_lower,
1058 true,
1059 false,
1060 true);
1061 if (!rename_script) {
1062 goto cancel;
1064 rename_script = talloc_string_sub2(new_acct,
1065 rename_script,
1066 "%uold",
1067 oldname_lower,
1068 true,
1069 false,
1070 true);
1071 if (!rename_script) {
1072 goto cancel;
1074 rename_ret = smbrun(rename_script, NULL);
1076 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n",
1077 rename_script, rename_ret));
1079 if (rename_ret != 0) {
1080 goto cancel;
1083 smb_nscd_flush_user_cache();
1085 /* rewrite the rid->username record */
1087 if ( !tdb_update_ridrec_only( new_acct, TDB_MODIFY) ) {
1088 goto cancel;
1091 tdb_delete_samacct_only( old_acct );
1093 if (dbwrap_transaction_commit(db_sam) != 0) {
1095 * Ok, we're screwed. We've changed the posix account, but
1096 * could not adapt passdb.tdb. Shall we change the posix
1097 * account back?
1099 DEBUG(0, ("transaction_commit failed\n"));
1100 TALLOC_FREE(new_acct);
1101 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1104 TALLOC_FREE(new_acct );
1105 return NT_STATUS_OK;
1107 cancel:
1108 if (dbwrap_transaction_cancel(db_sam) != 0) {
1109 smb_panic("transaction_cancel failed");
1112 TALLOC_FREE(new_acct);
1114 return NT_STATUS_ACCESS_DENIED;
1117 static uint32_t tdbsam_capabilities(struct pdb_methods *methods)
1119 return PDB_CAP_STORE_RIDS;
1122 static bool tdbsam_new_rid(struct pdb_methods *methods, uint32 *prid)
1124 uint32 rid;
1125 NTSTATUS status;
1127 rid = BASE_RID; /* Default if not set */
1129 if (!tdbsam_open(tdbsam_filename)) {
1130 DEBUG(0,("tdbsam_new_rid: failed to open %s!\n",
1131 tdbsam_filename));
1132 return false;
1135 status = dbwrap_trans_change_uint32_atomic_bystring(
1136 db_sam, NEXT_RID_STRING, &rid, 1);
1137 if (!NT_STATUS_IS_OK(status)) {
1138 DEBUG(3, ("tdbsam_new_rid: Failed to increase %s: %s\n",
1139 NEXT_RID_STRING, nt_errstr(status)));
1140 return false;
1143 *prid = rid;
1145 return true;
1148 struct tdbsam_search_state {
1149 struct pdb_methods *methods;
1150 uint32_t acct_flags;
1152 uint32_t *rids;
1153 uint32_t num_rids;
1154 ssize_t array_size;
1155 uint32_t current;
1158 static int tdbsam_collect_rids(struct db_record *rec, void *private_data)
1160 struct tdbsam_search_state *state = talloc_get_type_abort(
1161 private_data, struct tdbsam_search_state);
1162 size_t prefixlen = strlen(RIDPREFIX);
1163 uint32 rid;
1164 TDB_DATA key;
1166 key = dbwrap_record_get_key(rec);
1168 if ((key.dsize < prefixlen)
1169 || (strncmp((char *)key.dptr, RIDPREFIX, prefixlen))) {
1170 return 0;
1173 rid = strtoul((char *)key.dptr+prefixlen, NULL, 16);
1175 ADD_TO_LARGE_ARRAY(state, uint32, rid, &state->rids, &state->num_rids,
1176 &state->array_size);
1178 return 0;
1181 static void tdbsam_search_end(struct pdb_search *search)
1183 struct tdbsam_search_state *state = talloc_get_type_abort(
1184 search->private_data, struct tdbsam_search_state);
1185 TALLOC_FREE(state);
1188 static bool tdbsam_search_next_entry(struct pdb_search *search,
1189 struct samr_displayentry *entry)
1191 struct tdbsam_search_state *state = talloc_get_type_abort(
1192 search->private_data, struct tdbsam_search_state);
1193 struct samu *user = NULL;
1194 NTSTATUS status;
1195 uint32_t rid;
1197 again:
1198 TALLOC_FREE(user);
1199 user = samu_new(talloc_tos());
1200 if (user == NULL) {
1201 DEBUG(0, ("samu_new failed\n"));
1202 return false;
1205 if (state->current == state->num_rids) {
1206 TALLOC_FREE(user);
1207 return false;
1210 rid = state->rids[state->current++];
1212 status = tdbsam_getsampwrid(state->methods, user, rid);
1214 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1216 * Someone has deleted that user since we listed the RIDs
1218 goto again;
1221 if (!NT_STATUS_IS_OK(status)) {
1222 DEBUG(10, ("tdbsam_getsampwrid failed: %s\n",
1223 nt_errstr(status)));
1224 TALLOC_FREE(user);
1225 return false;
1228 if ((state->acct_flags != 0) &&
1229 ((state->acct_flags & pdb_get_acct_ctrl(user)) == 0)) {
1230 goto again;
1233 entry->acct_flags = pdb_get_acct_ctrl(user);
1234 entry->rid = rid;
1235 entry->account_name = talloc_strdup(search, pdb_get_username(user));
1236 entry->fullname = talloc_strdup(search, pdb_get_fullname(user));
1237 entry->description = talloc_strdup(search, pdb_get_acct_desc(user));
1239 TALLOC_FREE(user);
1241 if ((entry->account_name == NULL) || (entry->fullname == NULL)
1242 || (entry->description == NULL)) {
1243 DEBUG(0, ("talloc_strdup failed\n"));
1244 return false;
1247 return true;
1250 static bool tdbsam_search_users(struct pdb_methods *methods,
1251 struct pdb_search *search,
1252 uint32 acct_flags)
1254 struct tdbsam_search_state *state;
1256 if (!tdbsam_open(tdbsam_filename)) {
1257 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n",
1258 tdbsam_filename));
1259 return false;
1262 state = talloc_zero(search, struct tdbsam_search_state);
1263 if (state == NULL) {
1264 DEBUG(0, ("talloc failed\n"));
1265 return false;
1267 state->acct_flags = acct_flags;
1268 state->methods = methods;
1270 dbwrap_traverse_read(db_sam, tdbsam_collect_rids, state, NULL);
1272 search->private_data = state;
1273 search->next_entry = tdbsam_search_next_entry;
1274 search->search_end = tdbsam_search_end;
1276 return true;
1279 /*********************************************************************
1280 Initialize the tdb sam backend. Setup the dispath table of methods,
1281 open the tdb, etc...
1282 *********************************************************************/
1284 static NTSTATUS pdb_init_tdbsam(struct pdb_methods **pdb_method, const char *location)
1286 NTSTATUS nt_status;
1287 char *tdbfile = NULL;
1288 const char *pfile = location;
1290 if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) {
1291 return nt_status;
1294 (*pdb_method)->name = "tdbsam";
1296 (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
1297 (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
1298 (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
1299 (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
1300 (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
1301 (*pdb_method)->rename_sam_account = tdbsam_rename_sam_account;
1302 (*pdb_method)->search_users = tdbsam_search_users;
1304 (*pdb_method)->capabilities = tdbsam_capabilities;
1305 (*pdb_method)->new_rid = tdbsam_new_rid;
1307 /* save the path for later */
1309 if (!location) {
1310 if (asprintf(&tdbfile, "%s/%s", lp_private_dir(),
1311 PASSDB_FILE_NAME) < 0) {
1312 return NT_STATUS_NO_MEMORY;
1314 pfile = tdbfile;
1316 tdbsam_filename = SMB_STRDUP(pfile);
1317 if (!tdbsam_filename) {
1318 return NT_STATUS_NO_MEMORY;
1320 SAFE_FREE(tdbfile);
1322 /* no private data */
1324 (*pdb_method)->private_data = NULL;
1325 (*pdb_method)->free_private_data = NULL;
1327 return NT_STATUS_OK;
1330 NTSTATUS pdb_tdbsam_init(void)
1332 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);