s3:dbwrap: change dbwrap_fetch_uint32() to NTSTATUS return type (instead of bool)
[Samba/vl.git] / source3 / passdb / pdb_tdb.c
blob80a4b49f9dd9e351d20e2fe4e0d54117d1bb0c1c
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 if (dbwrap_store_uint32(db, NEXT_RID_STRING, rid) != 0) {
349 return false;
352 return true;
355 static bool tdbsam_convert(struct db_context **pp_db, const char *name, int32 from)
357 struct tdbsam_convert_state state;
358 struct db_context *db = NULL;
359 NTSTATUS status;
361 /* We only need the update backup for local db's. */
362 if (db_is_local(name) && !tdbsam_convert_backup(name, pp_db)) {
363 DEBUG(0, ("tdbsam_convert: Could not backup %s\n", name));
364 return false;
367 db = *pp_db;
368 state.from = from;
369 state.success = true;
371 if (dbwrap_transaction_start(db) != 0) {
372 DEBUG(0, ("tdbsam_convert: Could not start transaction\n"));
373 return false;
376 if (!tdbsam_upgrade_next_rid(db)) {
377 DEBUG(0, ("tdbsam_convert: tdbsam_upgrade_next_rid failed\n"));
378 goto cancel;
381 status = dbwrap_traverse(db, tdbsam_convert_one, &state, NULL);
382 if (!NT_STATUS_IS_OK(status)) {
383 DEBUG(0, ("tdbsam_convert: traverse failed\n"));
384 goto cancel;
387 if (!state.success) {
388 DEBUG(0, ("tdbsam_convert: Converting records failed\n"));
389 goto cancel;
392 if (dbwrap_store_int32(db, TDBSAM_VERSION_STRING,
393 TDBSAM_VERSION) != 0) {
394 DEBUG(0, ("tdbsam_convert: Could not store tdbsam version\n"));
395 goto cancel;
398 if (dbwrap_store_int32(db, TDBSAM_MINOR_VERSION_STRING,
399 TDBSAM_MINOR_VERSION) != 0) {
400 DEBUG(0, ("tdbsam_convert: Could not store tdbsam minor version\n"));
401 goto cancel;
404 if (dbwrap_transaction_commit(db) != 0) {
405 DEBUG(0, ("tdbsam_convert: Could not commit transaction\n"));
406 return false;
409 return true;
411 cancel:
412 if (dbwrap_transaction_cancel(db) != 0) {
413 smb_panic("tdbsam_convert: transaction_cancel failed");
416 return false;
419 /*********************************************************************
420 Open the tdbsam file based on the absolute path specified.
421 Uses a reference count to allow multiple open calls.
422 *********************************************************************/
424 static bool tdbsam_open( const char *name )
426 int32 version;
427 int32 minor_version;
428 NTSTATUS status;
430 /* check if we are already open */
432 if ( db_sam ) {
433 return true;
436 /* Try to open tdb passwd. Create a new one if necessary */
438 db_sam = db_open(NULL, name, 0, TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
439 if (db_sam == NULL) {
440 DEBUG(0, ("tdbsam_open: Failed to open/create TDB passwd "
441 "[%s]\n", name));
442 return false;
445 /* Check the version */
446 status = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING, &version);
447 if (!NT_STATUS_IS_OK(status)) {
448 version = 0; /* Version not found, assume version 0 */
451 /* Get the minor version */
452 status = dbwrap_fetch_int32(db_sam, TDBSAM_MINOR_VERSION_STRING,
453 &minor_version);
454 if (!NT_STATUS_IS_OK(status)) {
455 minor_version = 0; /* Minor version not found, assume 0 */
458 /* Compare the version */
459 if (version > TDBSAM_VERSION) {
460 /* Version more recent than the latest known */
461 DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
462 TALLOC_FREE(db_sam);
463 return false;
466 if ( version < TDBSAM_VERSION ||
467 (version == TDBSAM_VERSION &&
468 minor_version < TDBSAM_MINOR_VERSION) ) {
470 * Ok - we think we're going to have to convert.
471 * Due to the backup process we now must do to
472 * upgrade we have to get a mutex and re-check
473 * the version. Someone else may have upgraded
474 * whilst we were checking.
477 struct named_mutex *mtx = grab_named_mutex(NULL,
478 "tdbsam_upgrade_mutex",
479 600);
481 if (!mtx) {
482 DEBUG(0, ("tdbsam_open: failed to grab mutex.\n"));
483 TALLOC_FREE(db_sam);
484 return false;
487 /* Re-check the version */
488 status = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING,
489 &version);
490 if (!NT_STATUS_IS_OK(status)) {
491 version = 0; /* Version not found, assume version 0 */
494 /* Re-check the minor version */
495 status = dbwrap_fetch_int32(db_sam, TDBSAM_MINOR_VERSION_STRING,
496 &minor_version);
497 if (!NT_STATUS_IS_OK(status)) {
498 minor_version = 0; /* Minor version not found, assume 0 */
501 /* Compare the version */
502 if (version > TDBSAM_VERSION) {
503 /* Version more recent than the latest known */
504 DEBUG(0, ("tdbsam_open: unknown version => %d\n", version));
505 TALLOC_FREE(db_sam);
506 TALLOC_FREE(mtx);
507 return false;
510 if ( version < TDBSAM_VERSION ||
511 (version == TDBSAM_VERSION &&
512 minor_version < TDBSAM_MINOR_VERSION) ) {
514 * Note that minor versions we read that are greater
515 * than the current minor version we have hard coded
516 * are assumed to be compatible if they have the same
517 * major version. That allows previous versions of the
518 * passdb code that don't know about minor versions to
519 * still use this database. JRA.
522 DEBUG(1, ("tdbsam_open: Converting version %d.%d database to "
523 "version %d.%d.\n",
524 version,
525 minor_version,
526 TDBSAM_VERSION,
527 TDBSAM_MINOR_VERSION));
529 if ( !tdbsam_convert(&db_sam, name, version) ) {
530 DEBUG(0, ("tdbsam_open: Error when trying to convert "
531 "tdbsam [%s]\n",name));
532 TALLOC_FREE(db_sam);
533 TALLOC_FREE(mtx);
534 return false;
537 DEBUG(3, ("TDBSAM converted successfully.\n"));
539 TALLOC_FREE(mtx);
542 DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));
544 return true;
547 /******************************************************************
548 Lookup a name in the SAM TDB
549 ******************************************************************/
551 static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods,
552 struct samu *user, const char *sname)
554 TDB_DATA data;
555 fstring keystr;
556 fstring name;
557 NTSTATUS status;
559 if ( !user ) {
560 DEBUG(0,("pdb_getsampwnam: struct samu is NULL.\n"));
561 return NT_STATUS_NO_MEMORY;
564 /* Data is stored in all lower-case */
565 fstrcpy(name, sname);
566 strlower_m(name);
568 /* set search key */
569 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
571 /* open the database */
573 if ( !tdbsam_open( tdbsam_filename ) ) {
574 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
575 return NT_STATUS_ACCESS_DENIED;
578 /* get the record */
580 status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
581 if (!NT_STATUS_IS_OK(status)) {
582 DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n"));
583 DEBUGADD(5, (" Key: %s\n", keystr));
584 return NT_STATUS_NO_SUCH_USER;
587 /* unpack the buffer */
589 if (!init_samu_from_buffer(user, SAMU_BUFFER_LATEST, data.dptr, data.dsize)) {
590 DEBUG(0,("pdb_getsampwent: Bad struct samu entry returned from TDB!\n"));
591 SAFE_FREE(data.dptr);
592 return NT_STATUS_NO_MEMORY;
595 /* success */
597 TALLOC_FREE(data.dptr);
599 return NT_STATUS_OK;
602 /***************************************************************************
603 Search by rid
604 **************************************************************************/
606 static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods,
607 struct samu *user, uint32 rid)
609 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
610 TDB_DATA data;
611 fstring keystr;
612 fstring name;
614 if ( !user ) {
615 DEBUG(0,("pdb_getsampwrid: struct samu is NULL.\n"));
616 return nt_status;
619 /* set search key */
621 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
623 /* open the database */
625 if ( !tdbsam_open( tdbsam_filename ) ) {
626 DEBUG(0,("tdbsam_getsampwrid: failed to open %s!\n", tdbsam_filename));
627 return NT_STATUS_ACCESS_DENIED;
630 /* get the record */
632 nt_status = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr, &data);
633 if (!NT_STATUS_IS_OK(nt_status)) {
634 DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr));
635 return nt_status;
638 fstrcpy(name, (const char *)data.dptr);
639 TALLOC_FREE(data.dptr);
641 return tdbsam_getsampwnam (my_methods, user, name);
644 static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods,
645 struct samu * user, const struct dom_sid *sid)
647 uint32 rid;
649 if ( !sid_peek_check_rid(get_global_sam_sid(), sid, &rid) )
650 return NT_STATUS_UNSUCCESSFUL;
652 return tdbsam_getsampwrid(my_methods, user, rid);
655 static bool tdb_delete_samacct_only( struct samu *sam_pass )
657 fstring keystr;
658 fstring name;
659 NTSTATUS status;
661 fstrcpy(name, pdb_get_username(sam_pass));
662 strlower_m(name);
664 /* set the search key */
666 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
668 /* it's outaa here! 8^) */
669 if ( !tdbsam_open( tdbsam_filename ) ) {
670 DEBUG(0,("tdb_delete_samacct_only: failed to open %s!\n",
671 tdbsam_filename));
672 return false;
675 status = dbwrap_delete_bystring(db_sam, keystr);
676 if (!NT_STATUS_IS_OK(status)) {
677 DEBUG(5, ("Error deleting entry from tdb passwd "
678 "database: %s!\n", nt_errstr(status)));
679 return false;
682 return true;
685 /***************************************************************************
686 Delete a struct samu records for the username and RID key
687 ****************************************************************************/
689 static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods,
690 struct samu *sam_pass)
692 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
693 fstring keystr;
694 uint32 rid;
695 fstring name;
697 /* open the database */
699 if ( !tdbsam_open( tdbsam_filename ) ) {
700 DEBUG(0,("tdbsam_delete_sam_account: failed to open %s!\n",
701 tdbsam_filename));
702 return NT_STATUS_ACCESS_DENIED;
705 fstrcpy(name, pdb_get_username(sam_pass));
706 strlower_m(name);
708 /* set the search key */
710 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
712 rid = pdb_get_user_rid(sam_pass);
714 /* it's outaa here! 8^) */
716 if (dbwrap_transaction_start(db_sam) != 0) {
717 DEBUG(0, ("Could not start transaction\n"));
718 return NT_STATUS_UNSUCCESSFUL;
721 nt_status = dbwrap_delete_bystring(db_sam, keystr);
722 if (!NT_STATUS_IS_OK(nt_status)) {
723 DEBUG(5, ("Error deleting entry from tdb passwd "
724 "database: %s!\n", nt_errstr(nt_status)));
725 goto cancel;
728 /* set the search key */
730 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid);
732 /* it's outaa here! 8^) */
734 nt_status = dbwrap_delete_bystring(db_sam, keystr);
735 if (!NT_STATUS_IS_OK(nt_status)) {
736 DEBUG(5, ("Error deleting entry from tdb rid "
737 "database: %s!\n", nt_errstr(nt_status)));
738 goto cancel;
741 if (dbwrap_transaction_commit(db_sam) != 0) {
742 DEBUG(0, ("Could not commit transaction\n"));
743 return NT_STATUS_INTERNAL_DB_CORRUPTION;
746 return NT_STATUS_OK;
748 cancel:
749 if (dbwrap_transaction_cancel(db_sam) != 0) {
750 smb_panic("transaction_cancel failed");
753 return nt_status;
757 /***************************************************************************
758 Update the TDB SAM account record only
759 Assumes that the tdbsam is already open
760 ****************************************************************************/
761 static bool tdb_update_samacct_only( struct samu* newpwd, int flag )
763 TDB_DATA data;
764 uint8 *buf = NULL;
765 fstring keystr;
766 fstring name;
767 bool ret = false;
768 NTSTATUS status;
770 /* copy the struct samu struct into a BYTE buffer for storage */
772 if ( (data.dsize=init_buffer_from_samu(&buf, newpwd, False)) == -1 ) {
773 DEBUG(0,("tdb_update_sam: ERROR - Unable to copy struct samu info BYTE buffer!\n"));
774 goto done;
776 data.dptr = buf;
778 fstrcpy(name, pdb_get_username(newpwd));
779 strlower_m(name);
781 DEBUG(5, ("Storing %saccount %s with RID %d\n",
782 flag == TDB_INSERT ? "(new) " : "", name,
783 pdb_get_user_rid(newpwd)));
785 /* setup the USER index key */
786 slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name);
788 /* add the account */
790 status = dbwrap_store_bystring(db_sam, keystr, data, flag);
791 if (!NT_STATUS_IS_OK(status)) {
792 DEBUG(0, ("Unable to modify passwd TDB: %s!",
793 nt_errstr(status)));
794 goto done;
797 ret = true;
799 done:
800 /* cleanup */
801 SAFE_FREE(buf);
802 return ret;
805 /***************************************************************************
806 Update the TDB SAM RID record only
807 Assumes that the tdbsam is already open
808 ****************************************************************************/
809 static bool tdb_update_ridrec_only( struct samu* newpwd, int flag )
811 TDB_DATA data;
812 fstring keystr;
813 fstring name;
814 NTSTATUS status;
816 fstrcpy(name, pdb_get_username(newpwd));
817 strlower_m(name);
819 /* setup RID data */
820 data = string_term_tdb_data(name);
822 /* setup the RID index key */
823 slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX,
824 pdb_get_user_rid(newpwd));
826 /* add the reference */
827 status = dbwrap_store_bystring(db_sam, keystr, data, flag);
828 if (!NT_STATUS_IS_OK(status)) {
829 DEBUG(0, ("Unable to modify TDB passwd: %s!\n",
830 nt_errstr(status)));
831 return false;
834 return true;
838 /***************************************************************************
839 Update the TDB SAM
840 ****************************************************************************/
842 static bool tdb_update_sam(struct pdb_methods *my_methods, struct samu* newpwd,
843 int flag)
845 uint32_t oldrid;
846 uint32_t newrid;
848 if (!(newrid = pdb_get_user_rid(newpwd))) {
849 DEBUG(0,("tdb_update_sam: struct samu (%s) with no RID!\n",
850 pdb_get_username(newpwd)));
851 return False;
854 oldrid = newrid;
856 /* open the database */
858 if ( !tdbsam_open( tdbsam_filename ) ) {
859 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename));
860 return False;
863 if (dbwrap_transaction_start(db_sam) != 0) {
864 DEBUG(0, ("Could not start transaction\n"));
865 return false;
868 /* If we are updating, we may be changing this users RID. Retrieve the old RID
869 so we can check. */
871 if (flag == TDB_MODIFY) {
872 struct samu *account = samu_new(talloc_tos());
873 if (account == NULL) {
874 DEBUG(0,("tdb_update_sam: samu_new() failed\n"));
875 goto cancel;
877 if (!NT_STATUS_IS_OK(tdbsam_getsampwnam(my_methods, account, pdb_get_username(newpwd)))) {
878 DEBUG(0,("tdb_update_sam: tdbsam_getsampwnam() for %s failed\n",
879 pdb_get_username(newpwd)));
880 TALLOC_FREE(account);
881 goto cancel;
883 if (!(oldrid = pdb_get_user_rid(account))) {
884 DEBUG(0,("tdb_update_sam: pdb_get_user_rid() failed\n"));
885 TALLOC_FREE(account);
886 goto cancel;
888 TALLOC_FREE(account);
891 /* Update the new samu entry. */
892 if (!tdb_update_samacct_only(newpwd, flag)) {
893 goto cancel;
896 /* Now take care of the case where the RID changed. We need
897 * to delete the old RID key and add the new. */
899 if (flag == TDB_MODIFY && newrid != oldrid) {
900 fstring keystr;
902 /* Delete old RID key */
903 DEBUG(10, ("tdb_update_sam: Deleting key for RID %u\n", oldrid));
904 slprintf(keystr, sizeof(keystr) - 1, "%s%.8x", RIDPREFIX, oldrid);
905 if (!NT_STATUS_IS_OK(dbwrap_delete_bystring(db_sam, keystr))) {
906 DEBUG(0, ("tdb_update_sam: Can't delete %s\n", keystr));
907 goto cancel;
909 /* Insert new RID key */
910 DEBUG(10, ("tdb_update_sam: Inserting key for RID %u\n", newrid));
911 if (!tdb_update_ridrec_only(newpwd, TDB_INSERT)) {
912 goto cancel;
914 } else {
915 DEBUG(10, ("tdb_update_sam: %s key for RID %u\n",
916 flag == TDB_MODIFY ? "Updating" : "Inserting", newrid));
917 if (!tdb_update_ridrec_only(newpwd, flag)) {
918 goto cancel;
922 if (dbwrap_transaction_commit(db_sam) != 0) {
923 DEBUG(0, ("Could not commit transaction\n"));
924 return false;
927 return true;
929 cancel:
930 if (dbwrap_transaction_cancel(db_sam) != 0) {
931 smb_panic("transaction_cancel failed");
933 return false;
936 /***************************************************************************
937 Modifies an existing struct samu
938 ****************************************************************************/
940 static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
942 if ( !tdb_update_sam(my_methods, newpwd, TDB_MODIFY) )
943 return NT_STATUS_UNSUCCESSFUL;
945 return NT_STATUS_OK;
948 /***************************************************************************
949 Adds an existing struct samu
950 ****************************************************************************/
952 static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, struct samu *newpwd)
954 if ( !tdb_update_sam(my_methods, newpwd, TDB_INSERT) )
955 return NT_STATUS_UNSUCCESSFUL;
957 return NT_STATUS_OK;
960 /***************************************************************************
961 Renames a struct samu
962 - check for the posix user/rename user script
963 - Add and lock the new user record
964 - rename the posix user
965 - rewrite the rid->username record
966 - delete the old user
967 - unlock the new user record
968 ***************************************************************************/
969 static NTSTATUS tdbsam_rename_sam_account(struct pdb_methods *my_methods,
970 struct samu *old_acct,
971 const char *newname)
973 struct samu *new_acct = NULL;
974 char *rename_script = NULL;
975 int rename_ret;
976 fstring oldname_lower;
977 fstring newname_lower;
979 /* can't do anything without an external script */
981 if ( !(new_acct = samu_new( talloc_tos() )) ) {
982 return NT_STATUS_NO_MEMORY;
985 rename_script = talloc_strdup(new_acct, lp_renameuser_script());
986 if (!rename_script) {
987 TALLOC_FREE(new_acct);
988 return NT_STATUS_NO_MEMORY;
990 if (!*rename_script) {
991 TALLOC_FREE(new_acct);
992 return NT_STATUS_ACCESS_DENIED;
995 if ( !pdb_copy_sam_account(new_acct, old_acct)
996 || !pdb_set_username(new_acct, newname, PDB_CHANGED))
998 TALLOC_FREE(new_acct);
999 return NT_STATUS_NO_MEMORY;
1002 /* open the database */
1003 if ( !tdbsam_open( tdbsam_filename ) ) {
1004 DEBUG(0, ("tdbsam_getsampwnam: failed to open %s!\n",
1005 tdbsam_filename));
1006 TALLOC_FREE(new_acct);
1007 return NT_STATUS_ACCESS_DENIED;
1010 if (dbwrap_transaction_start(db_sam) != 0) {
1011 DEBUG(0, ("Could not start transaction\n"));
1012 TALLOC_FREE(new_acct);
1013 return NT_STATUS_ACCESS_DENIED;
1017 /* add the new account and lock it */
1018 if ( !tdb_update_samacct_only(new_acct, TDB_INSERT) ) {
1019 goto cancel;
1022 /* Rename the posix user. Follow the semantics of _samr_create_user()
1023 so that we lower case the posix name but preserve the case in passdb */
1025 fstrcpy( oldname_lower, pdb_get_username(old_acct) );
1026 strlower_m( oldname_lower );
1028 fstrcpy( newname_lower, newname );
1029 strlower_m( newname_lower );
1031 rename_script = talloc_string_sub2(new_acct,
1032 rename_script,
1033 "%unew",
1034 newname_lower,
1035 true,
1036 false,
1037 true);
1038 if (!rename_script) {
1039 goto cancel;
1041 rename_script = talloc_string_sub2(new_acct,
1042 rename_script,
1043 "%uold",
1044 oldname_lower,
1045 true,
1046 false,
1047 true);
1048 if (!rename_script) {
1049 goto cancel;
1051 rename_ret = smbrun(rename_script, NULL);
1053 DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n",
1054 rename_script, rename_ret));
1056 if (rename_ret != 0) {
1057 goto cancel;
1060 smb_nscd_flush_user_cache();
1062 /* rewrite the rid->username record */
1064 if ( !tdb_update_ridrec_only( new_acct, TDB_MODIFY) ) {
1065 goto cancel;
1068 tdb_delete_samacct_only( old_acct );
1070 if (dbwrap_transaction_commit(db_sam) != 0) {
1072 * Ok, we're screwed. We've changed the posix account, but
1073 * could not adapt passdb.tdb. Shall we change the posix
1074 * account back?
1076 DEBUG(0, ("transaction_commit failed\n"));
1077 TALLOC_FREE(new_acct);
1078 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1081 TALLOC_FREE(new_acct );
1082 return NT_STATUS_OK;
1084 cancel:
1085 if (dbwrap_transaction_cancel(db_sam) != 0) {
1086 smb_panic("transaction_cancel failed");
1089 TALLOC_FREE(new_acct);
1091 return NT_STATUS_ACCESS_DENIED;
1094 static uint32_t tdbsam_capabilities(struct pdb_methods *methods)
1096 return PDB_CAP_STORE_RIDS;
1099 static bool tdbsam_new_rid(struct pdb_methods *methods, uint32 *prid)
1101 uint32 rid;
1102 NTSTATUS status;
1104 rid = BASE_RID; /* Default if not set */
1106 if (!tdbsam_open(tdbsam_filename)) {
1107 DEBUG(0,("tdbsam_new_rid: failed to open %s!\n",
1108 tdbsam_filename));
1109 return false;
1112 status = dbwrap_trans_change_uint32_atomic(db_sam, NEXT_RID_STRING,
1113 &rid, 1);
1114 if (!NT_STATUS_IS_OK(status)) {
1115 DEBUG(3, ("tdbsam_new_rid: Failed to increase %s: %s\n",
1116 NEXT_RID_STRING, nt_errstr(status)));
1117 return false;
1120 *prid = rid;
1122 return true;
1125 struct tdbsam_search_state {
1126 struct pdb_methods *methods;
1127 uint32_t acct_flags;
1129 uint32_t *rids;
1130 uint32_t num_rids;
1131 ssize_t array_size;
1132 uint32_t current;
1135 static int tdbsam_collect_rids(struct db_record *rec, void *private_data)
1137 struct tdbsam_search_state *state = talloc_get_type_abort(
1138 private_data, struct tdbsam_search_state);
1139 size_t prefixlen = strlen(RIDPREFIX);
1140 uint32 rid;
1141 TDB_DATA key;
1143 key = dbwrap_record_get_key(rec);
1145 if ((key.dsize < prefixlen)
1146 || (strncmp((char *)key.dptr, RIDPREFIX, prefixlen))) {
1147 return 0;
1150 rid = strtoul((char *)key.dptr+prefixlen, NULL, 16);
1152 ADD_TO_LARGE_ARRAY(state, uint32, rid, &state->rids, &state->num_rids,
1153 &state->array_size);
1155 return 0;
1158 static void tdbsam_search_end(struct pdb_search *search)
1160 struct tdbsam_search_state *state = talloc_get_type_abort(
1161 search->private_data, struct tdbsam_search_state);
1162 TALLOC_FREE(state);
1165 static bool tdbsam_search_next_entry(struct pdb_search *search,
1166 struct samr_displayentry *entry)
1168 struct tdbsam_search_state *state = talloc_get_type_abort(
1169 search->private_data, struct tdbsam_search_state);
1170 struct samu *user = NULL;
1171 NTSTATUS status;
1172 uint32_t rid;
1174 again:
1175 TALLOC_FREE(user);
1176 user = samu_new(talloc_tos());
1177 if (user == NULL) {
1178 DEBUG(0, ("samu_new failed\n"));
1179 return false;
1182 if (state->current == state->num_rids) {
1183 return false;
1186 rid = state->rids[state->current++];
1188 status = tdbsam_getsampwrid(state->methods, user, rid);
1190 if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
1192 * Someone has deleted that user since we listed the RIDs
1194 goto again;
1197 if (!NT_STATUS_IS_OK(status)) {
1198 DEBUG(10, ("tdbsam_getsampwrid failed: %s\n",
1199 nt_errstr(status)));
1200 TALLOC_FREE(user);
1201 return false;
1204 if ((state->acct_flags != 0) &&
1205 ((state->acct_flags & pdb_get_acct_ctrl(user)) == 0)) {
1206 goto again;
1209 entry->acct_flags = pdb_get_acct_ctrl(user);
1210 entry->rid = rid;
1211 entry->account_name = talloc_strdup(search, pdb_get_username(user));
1212 entry->fullname = talloc_strdup(search, pdb_get_fullname(user));
1213 entry->description = talloc_strdup(search, pdb_get_acct_desc(user));
1215 TALLOC_FREE(user);
1217 if ((entry->account_name == NULL) || (entry->fullname == NULL)
1218 || (entry->description == NULL)) {
1219 DEBUG(0, ("talloc_strdup failed\n"));
1220 return false;
1223 return true;
1226 static bool tdbsam_search_users(struct pdb_methods *methods,
1227 struct pdb_search *search,
1228 uint32 acct_flags)
1230 struct tdbsam_search_state *state;
1232 if (!tdbsam_open(tdbsam_filename)) {
1233 DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n",
1234 tdbsam_filename));
1235 return false;
1238 state = talloc_zero(search, struct tdbsam_search_state);
1239 if (state == NULL) {
1240 DEBUG(0, ("talloc failed\n"));
1241 return false;
1243 state->acct_flags = acct_flags;
1244 state->methods = methods;
1246 dbwrap_traverse_read(db_sam, tdbsam_collect_rids, state, NULL);
1248 search->private_data = state;
1249 search->next_entry = tdbsam_search_next_entry;
1250 search->search_end = tdbsam_search_end;
1252 return true;
1255 /*********************************************************************
1256 Initialize the tdb sam backend. Setup the dispath table of methods,
1257 open the tdb, etc...
1258 *********************************************************************/
1260 static NTSTATUS pdb_init_tdbsam(struct pdb_methods **pdb_method, const char *location)
1262 NTSTATUS nt_status;
1263 char *tdbfile = NULL;
1264 const char *pfile = location;
1266 if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) {
1267 return nt_status;
1270 (*pdb_method)->name = "tdbsam";
1272 (*pdb_method)->getsampwnam = tdbsam_getsampwnam;
1273 (*pdb_method)->getsampwsid = tdbsam_getsampwsid;
1274 (*pdb_method)->add_sam_account = tdbsam_add_sam_account;
1275 (*pdb_method)->update_sam_account = tdbsam_update_sam_account;
1276 (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account;
1277 (*pdb_method)->rename_sam_account = tdbsam_rename_sam_account;
1278 (*pdb_method)->search_users = tdbsam_search_users;
1280 (*pdb_method)->capabilities = tdbsam_capabilities;
1281 (*pdb_method)->new_rid = tdbsam_new_rid;
1283 /* save the path for later */
1285 if (!location) {
1286 if (asprintf(&tdbfile, "%s/%s", lp_private_dir(),
1287 PASSDB_FILE_NAME) < 0) {
1288 return NT_STATUS_NO_MEMORY;
1290 pfile = tdbfile;
1292 tdbsam_filename = SMB_STRDUP(pfile);
1293 if (!tdbsam_filename) {
1294 return NT_STATUS_NO_MEMORY;
1296 SAFE_FREE(tdbfile);
1298 /* no private data */
1300 (*pdb_method)->private_data = NULL;
1301 (*pdb_method)->free_private_data = NULL;
1303 return NT_STATUS_OK;
1306 NTSTATUS pdb_tdbsam_init(void)
1308 return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam);