]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
Fix bug 6195 - Migrating from 3.0.x to 3.3.x can fail to update passdb.tdb correctly.
authorJeremy Allison <jra@samba.org>
Wed, 18 Mar 2009 21:31:01 +0000 (14:31 -0700)
committerJeremy Allison <jra@samba.org>
Wed, 18 Mar 2009 21:31:01 +0000 (14:31 -0700)
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.

source/Makefile.in
source/passdb/pdb_tdb.c

index a7ddf9ce0bd40bde7692f80f87fd830d28f24274..c01a6cc4f08e0bb7de5e3b92fa2b15f9423dfaaf 100644 (file)
@@ -547,7 +547,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
@@ -639,7 +639,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/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 \
@@ -841,7 +841,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) \
@@ -1011,7 +1011,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) 
 
 WBINFO_OBJ = nsswitch/wbinfo.o $(LIBSAMBA_OBJ) $(PARAM_OBJ) $(LIB_NONSMBD_OBJ) \
                $(POPT_LIB_OBJ) $(AFS_SETTOKEN_OBJ) \
@@ -1076,7 +1076,7 @@ NTLM_AUTH_OBJ1 = utils/ntlm_auth.o utils/ntlm_auth_diagnostics.o
 
 NTLM_AUTH_OBJ = ${NTLM_AUTH_OBJ1} $(LIBSAMBA_OBJ) $(POPT_LIB_OBJ) \
                libsmb/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@ \
index 9c8c7b85179044387e5691dbad041688e64c1bd2..9928768a2195219abe6a139551a1d550e1901f82 100644 (file)
@@ -687,7 +687,7 @@ static uint32 init_buffer_from_sam (uint8 **buf, struct samu *sampass, bool size
 }
 
 /**********************************************************************
- Intialize a BYTE buffer from a struct samu struct
+ Struct and function to update an old record.
  *********************************************************************/
 
 struct tdbsam_convert_state {
@@ -772,38 +772,188 @@ static int tdbsam_convert_one(struct db_record *rec, void *priv)
        return 0;
 }
 
-static bool tdbsam_convert(struct db_context *db, int32 from)
+/**********************************************************************
+ 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_trans(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_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;
        }
 
        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"));
                goto cancel;
        }
 
@@ -811,7 +961,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;
@@ -856,17 +1006,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 ));