1
0
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:
Jeremy Allison 2009-03-18 15:44:13 -07:00
parent 531af136f9
commit f942cb616e
2 changed files with 206 additions and 19 deletions

View File

@ -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@ \

View File

@ -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 ));