/* * Unix SMB/CIFS implementation. * SMB parameters and setup * Copyright (C) Andrew Tridgell 1992-1998 * Copyright (C) Simo Sorce 2000-2003 * Copyright (C) Gerald Carter 2000-2006 * Copyright (C) Jeremy Allison 2001 * Copyright (C) Andrew Bartlett 2002 * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005 * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, see <http://www.gnu.org/licenses/>. */ #include "includes.h" #if 0 /* when made a module use this */ static int tdbsam_debug_level = DBGC_ALL; #undef DBGC_CLASS #define DBGC_CLASS tdbsam_debug_level #else #undef DBGC_CLASS #define DBGC_CLASS DBGC_PASSDB #endif #define TDBSAM_VERSION 4 /* Most recent TDBSAM version */ #define TDBSAM_VERSION_STRING "INFO/version" #define PASSDB_FILE_NAME "passdb.tdb" #define USERPREFIX "USER_" #define USERPREFIX_LEN 5 #define RIDPREFIX "RID_" #define PRIVPREFIX "PRIV_" #define NEXT_RID_STRING "NEXT_RID" /* GLOBAL TDB SAM CONTEXT */ static struct db_context *db_sam; static char *tdbsam_filename; struct tdbsam_convert_state { int32_t from; bool success; }; static int tdbsam_convert_one(struct db_record *rec, void *priv) { struct tdbsam_convert_state *state = (struct tdbsam_convert_state *)priv; struct samu *user; TDB_DATA data; NTSTATUS status; bool ret; if (rec->key.dsize < USERPREFIX_LEN) { return 0; } if (strncmp((char *)rec->key.dptr, USERPREFIX, USERPREFIX_LEN) != 0) { return 0; } user = samu_new(talloc_tos()); if (user == NULL) { DEBUG(0,("tdbsam_convert: samu_new() failed!\n")); state->success = false; return -1; } DEBUG(10,("tdbsam_convert: Try unpacking a record with (key:%s) " "(version:%d)\n", rec->key.dptr, state->from)); switch (state->from) { case 0: ret = init_samu_from_buffer(user, SAMU_BUFFER_V0, (uint8 *)rec->value.dptr, rec->value.dsize); break; case 1: ret = init_samu_from_buffer(user, SAMU_BUFFER_V1, (uint8 *)rec->value.dptr, rec->value.dsize); break; case 2: ret = init_samu_from_buffer(user, SAMU_BUFFER_V2, (uint8 *)rec->value.dptr, rec->value.dsize); break; case 3: ret = init_samu_from_buffer(user, SAMU_BUFFER_V3, (uint8 *)rec->value.dptr, rec->value.dsize); case 4: ret = init_samu_from_buffer(user, SAMU_BUFFER_V4, (uint8 *)rec->value.dptr, rec->value.dsize); break; default: /* unknown tdbsam version */ ret = False; } if (!ret) { DEBUG(0,("tdbsam_convert: Bad struct samu entry returned " "from TDB (key:%s) (version:%d)\n", rec->key.dptr, state->from)); TALLOC_FREE(user); state->success = false; return -1; } data.dsize = init_buffer_from_samu(&data.dptr, user, false); TALLOC_FREE(user); if (data.dsize == -1) { DEBUG(0,("tdbsam_convert: cannot pack the struct samu into " "the new format\n")); state->success = false; return -1; } status = rec->store(rec, data, TDB_MODIFY); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Could not store the new record: %s\n", nt_errstr(status))); state->success = false; return -1; } return 0; } static bool tdbsam_upgrade_next_rid(struct db_context *db) { TDB_CONTEXT *tdb; uint32 rid; bool ok = false; ok = dbwrap_fetch_uint32(db, NEXT_RID_STRING, &rid); if (ok) { return true; } tdb = tdb_open_log(state_path("winbindd_idmap.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0644); if (tdb) { ok = tdb_fetch_uint32(tdb, "RID_COUNTER", &rid); if (!ok) { rid = BASE_RID; } tdb_close(tdb); } else { rid = BASE_RID; } if (dbwrap_store_uint32(db, NEXT_RID_STRING, rid) != 0) { return false; } return true; } static bool tdbsam_convert(struct db_context *db, int32 from) { struct tdbsam_convert_state state; int ret; state.from = from; state.success = true; if (db->transaction_start(db) != 0) { DEBUG(0, ("Could not start transaction\n")); return false; } if (!tdbsam_upgrade_next_rid(db)) { DEBUG(0, ("tdbsam_upgrade_next_rid failed\n")); goto cancel; } ret = db->traverse(db, tdbsam_convert_one, &state); if (ret < 0) { DEBUG(0, ("traverse failed\n")); goto cancel; } if (!state.success) { DEBUG(0, ("Converting records failed\n")); goto cancel; } if (dbwrap_store_int32(db, TDBSAM_VERSION_STRING, TDBSAM_VERSION) != 0) { DEBUG(0, ("Could not store tdbsam version\n")); goto cancel; } if (db->transaction_commit(db) != 0) { DEBUG(0, ("Could not commit transaction\n")); return false; } return true; cancel: if (db->transaction_cancel(db) != 0) { smb_panic("transaction_cancel failed"); } return false; } /********************************************************************* Open the tdbsam file based on the absolute path specified. Uses a reference count to allow multiple open calls. *********************************************************************/ static bool tdbsam_open( const char *name ) { int32 version; /* check if we are already open */ if ( db_sam ) { return true; } /* Try to open tdb passwd. Create a new one if necessary */ db_sam = db_open(NULL, name, 0, TDB_DEFAULT, O_CREAT|O_RDWR, 0600); if (db_sam == NULL) { DEBUG(0, ("tdbsam_open: Failed to open/create TDB passwd " "[%s]\n", name)); return false; } /* Check the version */ version = dbwrap_fetch_int32(db_sam, TDBSAM_VERSION_STRING); if (version == -1) { version = 0; /* Version not found, assume version 0 */ } /* Compare the version */ if (version > TDBSAM_VERSION) { /* Version more recent than the latest known */ DEBUG(0, ("tdbsam_open: unknown version => %d\n", version)); TALLOC_FREE(db_sam); return false; } if ( version < TDBSAM_VERSION ) { DEBUG(1, ("tdbsam_open: Converting version %d database to " "version %d.\n", version, TDBSAM_VERSION)); if ( !tdbsam_convert(db_sam, version) ) { DEBUG(0, ("tdbsam_open: Error when trying to convert " "tdbsam [%s]\n",name)); TALLOC_FREE(db_sam); return false; } DEBUG(3, ("TDBSAM converted successfully.\n")); } DEBUG(4,("tdbsam_open: successfully opened %s\n", name )); return true; } /****************************************************************** Lookup a name in the SAM TDB ******************************************************************/ static NTSTATUS tdbsam_getsampwnam (struct pdb_methods *my_methods, struct samu *user, const char *sname) { TDB_DATA data; fstring keystr; fstring name; if ( !user ) { DEBUG(0,("pdb_getsampwnam: struct samu is NULL.\n")); return NT_STATUS_NO_MEMORY; } /* Data is stored in all lower-case */ fstrcpy(name, sname); strlower_m(name); /* set search key */ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); /* open the database */ if ( !tdbsam_open( tdbsam_filename ) ) { DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename)); return NT_STATUS_ACCESS_DENIED; } /* get the record */ data = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr); if (!data.dptr) { DEBUG(5,("pdb_getsampwnam (TDB): error fetching database.\n")); DEBUGADD(5, (" Key: %s\n", keystr)); return NT_STATUS_NO_SUCH_USER; } /* unpack the buffer */ if (!init_samu_from_buffer(user, SAMU_BUFFER_LATEST, data.dptr, data.dsize)) { DEBUG(0,("pdb_getsampwent: Bad struct samu entry returned from TDB!\n")); SAFE_FREE(data.dptr); return NT_STATUS_NO_MEMORY; } /* success */ TALLOC_FREE(data.dptr); return NT_STATUS_OK; } /*************************************************************************** Search by rid **************************************************************************/ static NTSTATUS tdbsam_getsampwrid (struct pdb_methods *my_methods, struct samu *user, uint32 rid) { NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; TDB_DATA data; fstring keystr; fstring name; if ( !user ) { DEBUG(0,("pdb_getsampwrid: struct samu is NULL.\n")); return nt_status; } /* set search key */ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid); /* open the database */ if ( !tdbsam_open( tdbsam_filename ) ) { DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename)); return NT_STATUS_ACCESS_DENIED; } /* get the record */ data = dbwrap_fetch_bystring(db_sam, talloc_tos(), keystr); if (!data.dptr) { DEBUG(5,("pdb_getsampwrid (TDB): error looking up RID %d by key %s.\n", rid, keystr)); return NT_STATUS_UNSUCCESSFUL; } fstrcpy(name, (const char *)data.dptr); TALLOC_FREE(data.dptr); return tdbsam_getsampwnam (my_methods, user, name); } static NTSTATUS tdbsam_getsampwsid(struct pdb_methods *my_methods, struct samu * user, const DOM_SID *sid) { uint32 rid; if ( !sid_peek_check_rid(get_global_sam_sid(), sid, &rid) ) return NT_STATUS_UNSUCCESSFUL; return tdbsam_getsampwrid(my_methods, user, rid); } static bool tdb_delete_samacct_only( struct samu *sam_pass ) { fstring keystr; fstring name; NTSTATUS status; fstrcpy(name, pdb_get_username(sam_pass)); strlower_m(name); /* set the search key */ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); /* it's outaa here! 8^) */ status = dbwrap_delete_bystring(db_sam, keystr); if (!NT_STATUS_IS_OK(status)) { DEBUG(5, ("Error deleting entry from tdb passwd " "database: %s!\n", nt_errstr(status))); return false; } return true; } /*************************************************************************** Delete a struct samu records for the username and RID key ****************************************************************************/ static NTSTATUS tdbsam_delete_sam_account(struct pdb_methods *my_methods, struct samu *sam_pass) { NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; fstring keystr; uint32 rid; fstring name; /* open the database */ if ( !tdbsam_open( tdbsam_filename ) ) { DEBUG(0,("tdbsam_delete_sam_account: failed to open %s!\n", tdbsam_filename)); return NT_STATUS_ACCESS_DENIED; } fstrcpy(name, pdb_get_username(sam_pass)); strlower_m(name); /* set the search key */ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); rid = pdb_get_user_rid(sam_pass); /* it's outaa here! 8^) */ if (db_sam->transaction_start(db_sam) != 0) { DEBUG(0, ("Could not start transaction\n")); return NT_STATUS_UNSUCCESSFUL; } nt_status = dbwrap_delete_bystring(db_sam, keystr); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(5, ("Error deleting entry from tdb passwd " "database: %s!\n", nt_errstr(nt_status))); goto cancel; } /* set the search key */ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, rid); /* it's outaa here! 8^) */ nt_status = dbwrap_delete_bystring(db_sam, keystr); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(5, ("Error deleting entry from tdb rid " "database: %s!\n", nt_errstr(nt_status))); goto cancel; } if (db_sam->transaction_commit(db_sam) != 0) { DEBUG(0, ("Could not commit transaction\n")); return NT_STATUS_INTERNAL_DB_CORRUPTION; } return NT_STATUS_OK; cancel: if (db_sam->transaction_cancel(db_sam) != 0) { smb_panic("transaction_cancel failed"); } return nt_status; } /*************************************************************************** Update the TDB SAM account record only Assumes that the tdbsam is already open ****************************************************************************/ static bool tdb_update_samacct_only( struct samu* newpwd, int flag ) { TDB_DATA data; uint8 *buf = NULL; fstring keystr; fstring name; bool ret = false; NTSTATUS status; /* copy the struct samu struct into a BYTE buffer for storage */ if ( (data.dsize=init_buffer_from_samu(&buf, newpwd, False)) == -1 ) { DEBUG(0,("tdb_update_sam: ERROR - Unable to copy struct samu info BYTE buffer!\n")); goto done; } data.dptr = buf; fstrcpy(name, pdb_get_username(newpwd)); strlower_m(name); DEBUG(5, ("Storing %saccount %s with RID %d\n", flag == TDB_INSERT ? "(new) " : "", name, pdb_get_user_rid(newpwd))); /* setup the USER index key */ slprintf(keystr, sizeof(keystr)-1, "%s%s", USERPREFIX, name); /* add the account */ status = dbwrap_store_bystring(db_sam, keystr, data, flag); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Unable to modify passwd TDB: %s!", nt_errstr(status))); goto done; } ret = true; done: /* cleanup */ SAFE_FREE(buf); return ret; } /*************************************************************************** Update the TDB SAM RID record only Assumes that the tdbsam is already open ****************************************************************************/ static bool tdb_update_ridrec_only( struct samu* newpwd, int flag ) { TDB_DATA data; fstring keystr; fstring name; NTSTATUS status; fstrcpy(name, pdb_get_username(newpwd)); strlower_m(name); /* setup RID data */ data = string_term_tdb_data(name); /* setup the RID index key */ slprintf(keystr, sizeof(keystr)-1, "%s%.8x", RIDPREFIX, pdb_get_user_rid(newpwd)); /* add the reference */ status = dbwrap_store_bystring(db_sam, keystr, data, flag); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("Unable to modify TDB passwd: %s!\n", nt_errstr(status))); return false; } return true; } /*************************************************************************** Update the TDB SAM ****************************************************************************/ static bool tdb_update_sam(struct pdb_methods *my_methods, struct samu* newpwd, int flag) { if (!pdb_get_user_rid(newpwd)) { DEBUG(0,("tdb_update_sam: struct samu (%s) with no RID!\n", pdb_get_username(newpwd))); return False; } /* open the database */ if ( !tdbsam_open( tdbsam_filename ) ) { DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename)); return False; } if (db_sam->transaction_start(db_sam) != 0) { DEBUG(0, ("Could not start transaction\n")); return false; } if (!tdb_update_samacct_only(newpwd, flag) || !tdb_update_ridrec_only(newpwd, flag)) { goto cancel; } if (db_sam->transaction_commit(db_sam) != 0) { DEBUG(0, ("Could not commit transaction\n")); return false; } return true; cancel: if (db_sam->transaction_cancel(db_sam) != 0) { smb_panic("transaction_cancel failed"); } return false; } /*************************************************************************** Modifies an existing struct samu ****************************************************************************/ static NTSTATUS tdbsam_update_sam_account (struct pdb_methods *my_methods, struct samu *newpwd) { if ( !tdb_update_sam(my_methods, newpwd, TDB_MODIFY) ) return NT_STATUS_UNSUCCESSFUL; return NT_STATUS_OK; } /*************************************************************************** Adds an existing struct samu ****************************************************************************/ static NTSTATUS tdbsam_add_sam_account (struct pdb_methods *my_methods, struct samu *newpwd) { if ( !tdb_update_sam(my_methods, newpwd, TDB_INSERT) ) return NT_STATUS_UNSUCCESSFUL; return NT_STATUS_OK; } /*************************************************************************** Renames a struct samu - check for the posix user/rename user script - Add and lock the new user record - rename the posix user - rewrite the rid->username record - delete the old user - unlock the new user record ***************************************************************************/ static NTSTATUS tdbsam_rename_sam_account(struct pdb_methods *my_methods, struct samu *old_acct, const char *newname) { struct samu *new_acct = NULL; char *rename_script = NULL; int rename_ret; fstring oldname_lower; fstring newname_lower; /* can't do anything without an external script */ if ( !(new_acct = samu_new( talloc_tos() )) ) { return NT_STATUS_NO_MEMORY; } rename_script = talloc_strdup(new_acct, lp_renameuser_script()); if (!rename_script) { TALLOC_FREE(new_acct); return NT_STATUS_NO_MEMORY; } if (!*rename_script) { TALLOC_FREE(new_acct); return NT_STATUS_ACCESS_DENIED; } if ( !pdb_copy_sam_account(new_acct, old_acct) || !pdb_set_username(new_acct, newname, PDB_CHANGED)) { TALLOC_FREE(new_acct); return NT_STATUS_NO_MEMORY; } /* open the database */ if ( !tdbsam_open( tdbsam_filename ) ) { DEBUG(0, ("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename)); TALLOC_FREE(new_acct); return NT_STATUS_ACCESS_DENIED; } if (db_sam->transaction_start(db_sam) != 0) { DEBUG(0, ("Could not start transaction\n")); TALLOC_FREE(new_acct); return NT_STATUS_ACCESS_DENIED; } /* add the new account and lock it */ if ( !tdb_update_samacct_only(new_acct, TDB_INSERT) ) { goto cancel; } /* Rename the posix user. Follow the semantics of _samr_create_user() so that we lower case the posix name but preserve the case in passdb */ fstrcpy( oldname_lower, pdb_get_username(old_acct) ); strlower_m( oldname_lower ); fstrcpy( newname_lower, newname ); strlower_m( newname_lower ); rename_script = talloc_string_sub2(new_acct, rename_script, "%unew", newname_lower, true, false, true); if (!rename_script) { goto cancel; } rename_script = talloc_string_sub2(new_acct, rename_script, "%uold", oldname_lower, true, false, true); if (!rename_script) { goto cancel; } rename_ret = smbrun(rename_script, NULL); DEBUG(rename_ret ? 0 : 3,("Running the command `%s' gave %d\n", rename_script, rename_ret)); if (rename_ret != 0) { goto cancel; } smb_nscd_flush_user_cache(); /* rewrite the rid->username record */ if ( !tdb_update_ridrec_only( new_acct, TDB_MODIFY) ) { goto cancel; } tdb_delete_samacct_only( old_acct ); if (db_sam->transaction_commit(db_sam) != 0) { /* * Ok, we're screwed. We've changed the posix account, but * could not adapt passdb.tdb. Shall we change the posix * account back? */ DEBUG(0, ("transaction_commit failed\n")); TALLOC_FREE(new_acct); return NT_STATUS_INTERNAL_DB_CORRUPTION; } TALLOC_FREE(new_acct ); return NT_STATUS_OK; cancel: if (db_sam->transaction_cancel(db_sam) != 0) { smb_panic("transaction_cancel failed"); } TALLOC_FREE(new_acct); return NT_STATUS_ACCESS_DENIED; } static bool tdbsam_rid_algorithm(struct pdb_methods *methods) { return False; } static bool tdbsam_new_rid(struct pdb_methods *methods, uint32 *prid) { uint32 rid; rid = BASE_RID; /* Default if not set */ if (dbwrap_change_uint32_atomic(db_sam, NEXT_RID_STRING, &rid, 1) != 0) { DEBUG(3, ("tdbsam_new_rid: Failed to increase %s\n", NEXT_RID_STRING)); return false; } *prid = rid; return true; } struct tdbsam_search_state { struct pdb_methods *methods; uint32_t acct_flags; uint32_t *rids; uint32_t num_rids; ssize_t array_size; uint32_t current; }; static int tdbsam_collect_rids(struct db_record *rec, void *private_data) { struct tdbsam_search_state *state = talloc_get_type_abort( private_data, struct tdbsam_search_state); size_t prefixlen = strlen(RIDPREFIX); uint32 rid; if ((rec->key.dsize < prefixlen) || (strncmp((char *)rec->key.dptr, RIDPREFIX, prefixlen))) { return 0; } rid = strtoul((char *)rec->key.dptr+prefixlen, NULL, 16); ADD_TO_LARGE_ARRAY(state, uint32, rid, &state->rids, &state->num_rids, &state->array_size); return 0; } static void tdbsam_search_end(struct pdb_search *search) { struct tdbsam_search_state *state = talloc_get_type_abort( search->private_data, struct tdbsam_search_state); TALLOC_FREE(state); } static bool tdbsam_search_next_entry(struct pdb_search *search, struct samr_displayentry *entry) { struct tdbsam_search_state *state = talloc_get_type_abort( search->private_data, struct tdbsam_search_state); struct samu *user = NULL; NTSTATUS status; uint32_t rid; again: TALLOC_FREE(user); user = samu_new(talloc_tos()); if (user == NULL) { DEBUG(0, ("samu_new failed\n")); return false; } if (state->current == state->num_rids) { return false; } rid = state->rids[state->current++]; status = tdbsam_getsampwrid(state->methods, user, rid); if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) { /* * Someone has deleted that user since we listed the RIDs */ goto again; } if (!NT_STATUS_IS_OK(status)) { DEBUG(10, ("tdbsam_getsampwrid failed: %s\n", nt_errstr(status))); TALLOC_FREE(user); return false; } if ((state->acct_flags != 0) && ((state->acct_flags & pdb_get_acct_ctrl(user)) == 0)) { goto again; } entry->acct_flags = pdb_get_acct_ctrl(user); entry->rid = rid; entry->account_name = talloc_strdup( search->mem_ctx, pdb_get_username(user)); entry->fullname = talloc_strdup( search->mem_ctx, pdb_get_fullname(user)); entry->description = talloc_strdup( search->mem_ctx, pdb_get_acct_desc(user)); TALLOC_FREE(user); if ((entry->account_name == NULL) || (entry->fullname == NULL) || (entry->description == NULL)) { DEBUG(0, ("talloc_strdup failed\n")); return false; } return true; } static bool tdbsam_search_users(struct pdb_methods *methods, struct pdb_search *search, uint32 acct_flags) { struct tdbsam_search_state *state; if (!tdbsam_open(tdbsam_filename)) { DEBUG(0,("tdbsam_getsampwnam: failed to open %s!\n", tdbsam_filename)); return false; } state = TALLOC_ZERO_P(search->mem_ctx, struct tdbsam_search_state); if (state == NULL) { DEBUG(0, ("talloc failed\n")); return false; } state->acct_flags = acct_flags; state->methods = methods; db_sam->traverse_read(db_sam, tdbsam_collect_rids, state); search->private_data = state; search->next_entry = tdbsam_search_next_entry; search->search_end = tdbsam_search_end; return true; } /********************************************************************* Initialize the tdb sam backend. Setup the dispath table of methods, open the tdb, etc... *********************************************************************/ static NTSTATUS pdb_init_tdbsam(struct pdb_methods **pdb_method, const char *location) { NTSTATUS nt_status; char *tdbfile = NULL; const char *pfile = location; if (!NT_STATUS_IS_OK(nt_status = make_pdb_method( pdb_method ))) { return nt_status; } (*pdb_method)->name = "tdbsam"; (*pdb_method)->getsampwnam = tdbsam_getsampwnam; (*pdb_method)->getsampwsid = tdbsam_getsampwsid; (*pdb_method)->add_sam_account = tdbsam_add_sam_account; (*pdb_method)->update_sam_account = tdbsam_update_sam_account; (*pdb_method)->delete_sam_account = tdbsam_delete_sam_account; (*pdb_method)->rename_sam_account = tdbsam_rename_sam_account; (*pdb_method)->search_users = tdbsam_search_users; (*pdb_method)->rid_algorithm = tdbsam_rid_algorithm; (*pdb_method)->new_rid = tdbsam_new_rid; /* save the path for later */ if (!location) { if (asprintf(&tdbfile, "%s/%s", lp_private_dir(), PASSDB_FILE_NAME) < 0) { return NT_STATUS_NO_MEMORY; } pfile = tdbfile; } tdbsam_filename = SMB_STRDUP(pfile); if (!tdbsam_filename) { return NT_STATUS_NO_MEMORY; } SAFE_FREE(tdbfile); /* no private data */ (*pdb_method)->private_data = NULL; (*pdb_method)->free_private_data = NULL; return NT_STATUS_OK; } NTSTATUS pdb_tdbsam_init(void) { return smb_register_passdb(PASSDB_INTERFACE_VERSION, "tdbsam", pdb_init_tdbsam); }