mirror of
https://github.com/samba-team/samba.git
synced 2025-01-11 05:18:09 +03:00
Fix bug #6195 - Migrating from 3.0.x to 3.3.x can fail to update passdb.tdb correctly.
This is a really nasty one to fix as in order to successfully update the passdb.tdb we must do the equivalent of a tdbbackup to move to the new hash values before we do the upgrade. Jeremy.
This commit is contained in:
parent
531af136f9
commit
f942cb616e
@ -608,7 +608,7 @@ PASSDB_OBJ = $(PASSDB_GET_SET_OBJ) passdb/passdb.o passdb/pdb_interface.o \
|
||||
passdb/util_unixsids.o passdb/lookup_sid.o \
|
||||
passdb/login_cache.o @PDB_STATIC@ \
|
||||
lib/account_pol.o $(PRIVILEGES_OBJ) \
|
||||
lib/util_nscd.o lib/winbind_util.o
|
||||
lib/util_nscd.o lib/winbind_util.o $(SERVER_MUTEX_OBJ)
|
||||
|
||||
DEVEL_HELP_WEIRD_OBJ = modules/weird.o
|
||||
CP850_OBJ = modules/CP850.o
|
||||
@ -712,7 +712,7 @@ SMBD_OBJ_SRV = smbd/files.o smbd/chgpasswd.o smbd/connection.o \
|
||||
smbd/dosmode.o smbd/filename.o smbd/open.o smbd/close.o \
|
||||
smbd/blocking.o smbd/sec_ctx.o smbd/srvstr.o \
|
||||
smbd/vfs.o smbd/perfcount.o smbd/statcache.o smbd/seal.o \
|
||||
smbd/posix_acls.o lib/sysacls.o $(SERVER_MUTEX_OBJ) \
|
||||
smbd/posix_acls.o lib/sysacls.o \
|
||||
smbd/process.o smbd/service.o smbd/error.o \
|
||||
printing/printfsp.o lib/sysquotas.o lib/sysquotas_linux.o \
|
||||
lib/sysquotas_xfs.o lib/sysquotas_4A.o \
|
||||
@ -929,7 +929,7 @@ NET_OBJ = $(NET_OBJ1) \
|
||||
$(KRBCLIENT_OBJ) $(LIB_NONSMBD_OBJ) $(LIBADDNS_OBJ0) \
|
||||
$(LIBMSRPC_OBJ) $(LIBMSRPC_GEN_OBJ) \
|
||||
$(LIBADS_OBJ) $(LIBADS_SERVER_OBJ) $(POPT_LIB_OBJ) \
|
||||
$(SMBLDAP_OBJ) $(DCUTIL_OBJ) $(SERVER_MUTEX_OBJ) \
|
||||
$(SMBLDAP_OBJ) $(DCUTIL_OBJ) \
|
||||
$(AFS_OBJ) $(AFS_SETTOKEN_OBJ) $(READLINE_OBJ) \
|
||||
$(LDB_OBJ) $(LIBGPO_OBJ) @BUILD_INIPARSER@ $(DISPLAY_SEC_OBJ) \
|
||||
$(REG_SMBCONF_OBJ) @LIBNETAPI_STATIC@ $(LIBNET_OBJ) \
|
||||
@ -1092,7 +1092,7 @@ WINBINDD_OBJ = \
|
||||
$(LIBADS_OBJ) $(KRBCLIENT_OBJ) $(POPT_LIB_OBJ) \
|
||||
$(DCUTIL_OBJ) $(IDMAP_OBJ) $(NSS_INFO_OBJ) \
|
||||
$(AFS_OBJ) $(AFS_SETTOKEN_OBJ) \
|
||||
$(LIBADS_SERVER_OBJ) $(SERVER_MUTEX_OBJ) $(LDB_OBJ) \
|
||||
$(LIBADS_SERVER_OBJ) $(LDB_OBJ) \
|
||||
$(TDB_VALIDATE_OBJ)
|
||||
|
||||
WBINFO_OBJ = ../nsswitch/wbinfo.o $(LIBSAMBA_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
|
||||
@ -1159,7 +1159,7 @@ NTLM_AUTH_OBJ1 = utils/ntlm_auth.o utils/ntlm_auth_diagnostics.o
|
||||
|
||||
NTLM_AUTH_OBJ = ${NTLM_AUTH_OBJ1} $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \
|
||||
../lib/util/asn1.o libsmb/spnego.o libsmb/clikrb5.o libads/kerberos.o \
|
||||
$(SERVER_MUTEX_OBJ) $(LIBADS_SERVER_OBJ) \
|
||||
$(LIBADS_SERVER_OBJ) \
|
||||
$(PASSDB_OBJ) $(GROUPDB_OBJ) \
|
||||
$(SMBLDAP_OBJ) $(LIBNMB_OBJ) \
|
||||
$(LDB_OBJ) $(WBCOMMON_OBJ) @LIBWBCLIENT_STATIC@ \
|
||||
|
@ -142,6 +142,149 @@ static int tdbsam_convert_one(struct db_record *rec, void *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Struct and function to backup an old record.
|
||||
*********************************************************************/
|
||||
|
||||
struct tdbsam_backup_state {
|
||||
struct db_context *new_db;
|
||||
bool success;
|
||||
};
|
||||
|
||||
static int backup_copy_fn(struct db_record *orig_rec, void *state)
|
||||
{
|
||||
struct tdbsam_backup_state *bs = (struct tdbsam_backup_state *)state;
|
||||
struct db_record *new_rec;
|
||||
NTSTATUS status;
|
||||
|
||||
new_rec = bs->new_db->fetch_locked(bs->new_db, talloc_tos(), orig_rec->key);
|
||||
if (new_rec == NULL) {
|
||||
bs->success = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
status = new_rec->store(new_rec, orig_rec->value, TDB_INSERT);
|
||||
|
||||
TALLOC_FREE(new_rec);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
bs->success = false;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
Make a backup of an old passdb and replace the new one with it. We
|
||||
have to do this as between 3.0.x and 3.2.x the hash function changed
|
||||
by mistake (used unsigned char * instead of char *). This means the
|
||||
previous simple update code will fail due to not being able to find
|
||||
existing records to replace in the tdbsam_convert_one() function. JRA.
|
||||
*********************************************************************/
|
||||
|
||||
static bool tdbsam_convert_backup(const char *dbname, struct db_context **pp_db)
|
||||
{
|
||||
TALLOC_CTX *frame = talloc_stackframe();
|
||||
const char *tmp_fname = NULL;
|
||||
struct db_context *tmp_db = NULL;
|
||||
struct db_context *orig_db = *pp_db;
|
||||
struct tdbsam_backup_state bs;
|
||||
int ret;
|
||||
|
||||
tmp_fname = talloc_asprintf(frame, "%s.tmp", dbname);
|
||||
if (!tmp_fname) {
|
||||
TALLOC_FREE(frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
unlink(tmp_fname);
|
||||
|
||||
/* Remember to open this on the NULL context. We need
|
||||
* it to stay around after we return from here. */
|
||||
|
||||
tmp_db = db_open(NULL, tmp_fname, 0,
|
||||
TDB_DEFAULT, O_CREAT|O_RDWR, 0600);
|
||||
if (tmp_db == NULL) {
|
||||
DEBUG(0, ("tdbsam_convert_backup: Failed to create backup TDB passwd "
|
||||
"[%s]\n", tmp_fname));
|
||||
TALLOC_FREE(frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (orig_db->transaction_start(orig_db) != 0) {
|
||||
DEBUG(0, ("tdbsam_convert_backup: Could not start transaction (1)\n"));
|
||||
unlink(tmp_fname);
|
||||
TALLOC_FREE(tmp_db);
|
||||
TALLOC_FREE(frame);
|
||||
return false;
|
||||
}
|
||||
if (tmp_db->transaction_start(tmp_db) != 0) {
|
||||
DEBUG(0, ("tdbsam_convert_backup: Could not start transaction (2)\n"));
|
||||
orig_db->transaction_cancel(orig_db);
|
||||
unlink(tmp_fname);
|
||||
TALLOC_FREE(tmp_db);
|
||||
TALLOC_FREE(frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
bs.new_db = tmp_db;
|
||||
bs.success = true;
|
||||
|
||||
ret = orig_db->traverse(orig_db, backup_copy_fn, (void *)&bs);
|
||||
if (ret < 0) {
|
||||
DEBUG(0, ("tdbsam_convert_backup: traverse failed\n"));
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (!bs.success) {
|
||||
DEBUG(0, ("tdbsam_convert_backup: Rewriting records failed\n"));
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (orig_db->transaction_commit(orig_db) != 0) {
|
||||
smb_panic("tdbsam_convert_backup: orig commit failed\n");
|
||||
}
|
||||
if (tmp_db->transaction_commit(tmp_db) != 0) {
|
||||
smb_panic("tdbsam_convert_backup: orig commit failed\n");
|
||||
}
|
||||
|
||||
/* This is safe from other users as we know we're
|
||||
* under a mutex here. */
|
||||
|
||||
if (rename(tmp_fname, dbname) == -1) {
|
||||
DEBUG(0, ("tdbsam_convert_backup: rename of %s to %s failed %s\n",
|
||||
tmp_fname,
|
||||
dbname,
|
||||
strerror(errno)));
|
||||
smb_panic("tdbsam_convert_backup: replace passdb failed\n");
|
||||
}
|
||||
|
||||
TALLOC_FREE(frame);
|
||||
TALLOC_FREE(orig_db);
|
||||
|
||||
DEBUG(1, ("tdbsam_convert_backup: updated %s file.\n",
|
||||
dbname ));
|
||||
|
||||
/* Replace the global db pointer. */
|
||||
*pp_db = tmp_db;
|
||||
return true;
|
||||
|
||||
cancel:
|
||||
|
||||
if (orig_db->transaction_cancel(orig_db) != 0) {
|
||||
smb_panic("tdbsam_convert: transaction_cancel failed");
|
||||
}
|
||||
|
||||
if (tmp_db->transaction_cancel(tmp_db) != 0) {
|
||||
smb_panic("tdbsam_convert: transaction_cancel failed");
|
||||
}
|
||||
|
||||
unlink(tmp_fname);
|
||||
TALLOC_FREE(tmp_db);
|
||||
TALLOC_FREE(frame);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool tdbsam_upgrade_next_rid(struct db_context *db)
|
||||
{
|
||||
TDB_CONTEXT *tdb;
|
||||
@ -173,43 +316,50 @@ static bool tdbsam_upgrade_next_rid(struct db_context *db)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool tdbsam_convert(struct db_context *db, int32 from)
|
||||
static bool tdbsam_convert(struct db_context **pp_db, const char *name, int32 from)
|
||||
{
|
||||
struct tdbsam_convert_state state;
|
||||
struct db_context *db = NULL;
|
||||
int ret;
|
||||
|
||||
if (!tdbsam_convert_backup(name, pp_db)) {
|
||||
DEBUG(0, ("tdbsam_convert: Could not backup %s\n", name));
|
||||
return false;
|
||||
}
|
||||
|
||||
db = *pp_db;
|
||||
state.from = from;
|
||||
state.success = true;
|
||||
|
||||
if (db->transaction_start(db) != 0) {
|
||||
DEBUG(0, ("Could not start transaction\n"));
|
||||
DEBUG(0, ("tdbsam_convert: Could not start transaction\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tdbsam_upgrade_next_rid(db)) {
|
||||
DEBUG(0, ("tdbsam_upgrade_next_rid failed\n"));
|
||||
DEBUG(0, ("tdbsam_convert: tdbsam_upgrade_next_rid failed\n"));
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
ret = db->traverse(db, tdbsam_convert_one, &state);
|
||||
if (ret < 0) {
|
||||
DEBUG(0, ("traverse failed\n"));
|
||||
DEBUG(0, ("tdbsam_convert: traverse failed\n"));
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (!state.success) {
|
||||
DEBUG(0, ("Converting records failed\n"));
|
||||
DEBUG(0, ("tdbsam_convert: 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"));
|
||||
DEBUG(0, ("tdbsam_convert: Could not store tdbsam version\n"));
|
||||
goto cancel;
|
||||
}
|
||||
|
||||
if (db->transaction_commit(db) != 0) {
|
||||
DEBUG(0, ("Could not commit transaction\n"));
|
||||
DEBUG(0, ("tdbsam_convert: Could not commit transaction\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -217,7 +367,7 @@ static bool tdbsam_convert(struct db_context *db, int32 from)
|
||||
|
||||
cancel:
|
||||
if (db->transaction_cancel(db) != 0) {
|
||||
smb_panic("transaction_cancel failed");
|
||||
smb_panic("tdbsam_convert: transaction_cancel failed");
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -262,17 +412,54 @@ static bool tdbsam_open( const char *name )
|
||||
}
|
||||
|
||||
if ( version < TDBSAM_VERSION ) {
|
||||
DEBUG(1, ("tdbsam_open: Converting version %d database to "
|
||||
"version %d.\n", version, TDBSAM_VERSION));
|
||||
/*
|
||||
* Ok - we think we're going to have to convert.
|
||||
* Due to the backup process we now must do to
|
||||
* upgrade we have to get a mutex and re-check
|
||||
* the version. Someone else may have upgraded
|
||||
* whilst we were checking.
|
||||
*/
|
||||
|
||||
if ( !tdbsam_convert(db_sam, version) ) {
|
||||
DEBUG(0, ("tdbsam_open: Error when trying to convert "
|
||||
"tdbsam [%s]\n",name));
|
||||
struct named_mutex *mtx = grab_named_mutex(NULL,
|
||||
"tdbsam_upgrade_mutex",
|
||||
600);
|
||||
|
||||
if (!mtx) {
|
||||
DEBUG(0, ("tdbsam_open: failed to grab mutex.\n"));
|
||||
TALLOC_FREE(db_sam);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(3, ("TDBSAM converted successfully.\n"));
|
||||
/* Re-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);
|
||||
TALLOC_FREE(mtx);
|
||||
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, name, version) ) {
|
||||
DEBUG(0, ("tdbsam_open: Error when trying to convert "
|
||||
"tdbsam [%s]\n",name));
|
||||
TALLOC_FREE(db_sam);
|
||||
TALLOC_FREE(mtx);
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG(3, ("TDBSAM converted successfully.\n"));
|
||||
}
|
||||
TALLOC_FREE(mtx);
|
||||
}
|
||||
|
||||
DEBUG(4,("tdbsam_open: successfully opened %s\n", name ));
|
||||
|
Loading…
Reference in New Issue
Block a user