Add v4 dump load logic from kdb5_convert
authorPaul Park <pjpark@mit.edu>
Fri, 25 Aug 1995 21:46:33 +0000 (21:46 +0000)
committerPaul Park <pjpark@mit.edu>
Fri, 25 Aug 1995 21:46:33 +0000 (21:46 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@6592 dc483132-0cff-0310-8789-dd5450dbe970

src/admin/edit/.Sanitize
src/admin/edit/ChangeLog
src/admin/edit/Makefile.in
src/admin/edit/kdb5_ed_ct.ct
src/admin/edit/loadv4.c [new file with mode: 0644]

index e77eeb0f592d7909cdcb84bc512be8999d914df4..9e105839dc7566f7c171b590104fb039dcd12c7a 100644 (file)
@@ -35,6 +35,7 @@ kdb5_ed_ct.ct
 kdb5_edit.M
 kdb5_edit.c
 kdb5_edit.h
+loadv4.c
 ss_wrapper.c
 tcl_wrapper.c
 util.c
index 486803c9c0b02919bb1464d817c96de0abef0852..80c85873987f137ebcb04aade4d40d4d2a8d4ea9 100644 (file)
@@ -1,3 +1,11 @@
+
+Fri Aug 25 17:37:33 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * dumpv4.c - Fix handle_keys().  It was trying to recreate work that
+               has already been done.
+       * Makefile.in, .Sanitize, loadv4.c, kdb5_ed_ct.ct - Add lddb4, the
+               command to load a v4 dump file.  This is basically, kdb5_
+               convert reconstituted to fit within the framework of kdb5_edit.
+
 Thu Aug 24 19:28:39 1995  Theodore Y. Ts'o  <tytso@dcl>
 
        * .Sanitize: Update file list
index dd976ffc7411baa6872f4cfd81e1ba3e0c9b47d0..3d6c6546efd340e14b1c1baff9210741d8f3c17c 100644 (file)
@@ -10,6 +10,7 @@ OBJS= kdb5_edit.o \
        util.o \
        dump.o \
        dumpv4.o \
+       loadv4.o \
        ss_wrapper.o \
        $(LIBOBJS)
 
@@ -19,7 +20,8 @@ SRCS= $(srcdir)/kdb5_edit.c \
        $(srcdir)/util.c \
        $(srcdir)/dump.c \
        $(srcdir)/ss_wrapper.c \
-       $(srcdir)/dumpv4.c
+       $(srcdir)/dumpv4.c \
+       $(srcdir)/loadv4.c
 
 all:: kdb5_edit
 
index f00af1e7625211c45e7142e9c6fa34091e28f334..84ef78ce54731e5614908ddf156855d6145ef4cc 100644 (file)
@@ -66,6 +66,9 @@ request dump_v4db, "Dump database entries to a V4 slave dump file",
 request load_db, "Load database entries from a file",
        load_db, lddb;
 
+request load_v4db, "Load database entries from a V4 slave dump file",
+       load_v4db, lddb4;
+
 request set_dbname, "Change database name",
        set_dbname, sdbn;
 
diff --git a/src/admin/edit/loadv4.c b/src/admin/edit/loadv4.c
new file mode 100644 (file)
index 0000000..6374ea5
--- /dev/null
@@ -0,0 +1,888 @@
+/*
+ * admin/edit/loadv4.c
+ *
+ * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ * 
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ * 
+ *
+ * Generate (from scratch) a Kerberos V5 KDC database, filling it in with the
+ * entries from a V4 database.
+ */
+
+#include <des.h>
+#include <krb.h>
+#include <krb_db.h>
+/* MKEYFILE is now defined in kdc.h */
+#include <kdc.h>
+
+static C_Block master_key;
+static Key_schedule master_key_schedule;
+static long master_key_version;
+
+#include "k5-int.h"
+#include "com_err.h"
+#include "adm.h"
+#include "adm_proto.h"
+#include <stdio.h>
+
+#include <netinet/in.h>                        /* ntohl */
+
+#define PROGNAME argv[0]
+
+enum ap_op {
+    NULL_KEY,                          /* setup null keys */
+    MASTER_KEY,                                /* use master key as new key */
+    RANDOM_KEY                         /* choose a random key */
+};
+
+struct realm_info {
+    krb5_deltat max_life;
+    krb5_deltat max_rlife;
+    krb5_timestamp expiration;
+    krb5_flags flags;
+    krb5_encrypt_block *eblock;
+    krb5_pointer rseed;
+};
+
+static struct realm_info rblock = { /* XXX */
+    KRB5_KDB_MAX_LIFE,
+    KRB5_KDB_MAX_RLIFE,
+    KRB5_KDB_EXPIRATION,
+    KRB5_KDB_DEF_FLAGS,
+    0
+};
+
+static int verbose = 0;
+
+static krb5_error_code add_principal 
+       PROTOTYPE((krb5_context,
+                  krb5_principal, 
+                  enum ap_op,
+                  struct realm_info *));
+
+static int v4init PROTOTYPE((char *, char *, int, char *));
+static krb5_error_code enter_in_v5_db PROTOTYPE((krb5_context,
+                                                char *, Principal *));
+static krb5_error_code process_v4_dump PROTOTYPE((krb5_context, char *,
+                                                 char *));
+static krb5_error_code fixup_database PROTOTYPE((krb5_context, char *));
+       
+static int create_local_tgt = 0;
+
+static void
+usage(who, status)
+char *who;
+int status;
+{
+    fprintf(stderr, "usage: %s [-d v5dbpathname] [-t] [-n] [-r realmname] [-K] [-k keytype]\n\
+\t[-e etype] [-M mkeyname] -f inputfile\n",
+           who);
+    return;
+}
+
+static krb5_keyblock master_keyblock;
+static krb5_principal master_princ;
+static krb5_encrypt_block master_encblock;
+
+static krb5_data tgt_princ_entries[] = {
+       {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
+       {0, 0, 0} };
+
+static krb5_data db_creator_entries[] = {
+       {0, sizeof("db_creation")-1, "db_creation"} };
+
+/* XXX knows about contents of krb5_principal, and that tgt names
+ are of form TGT/REALM@REALM */
+static krb5_principal_data tgt_princ = {
+        0,                                     /* magic number */
+       {0, 0, 0},                              /* krb5_data realm */
+       tgt_princ_entries,                      /* krb5_data *data */
+       2,                                      /* int length */
+       KRB5_NT_SRV_INST                        /* int type */
+};
+
+static krb5_principal_data db_create_princ = {
+        0,                                     /* magic number */
+       {0, 0, 0},                              /* krb5_data realm */
+       db_creator_entries,                     /* krb5_data *data */
+       1,                                      /* int length */
+       KRB5_NT_SRV_INST                        /* int type */
+};
+
+
+void
+load_v4db(argc, argv)
+int argc;
+char *argv[];
+{
+    krb5_error_code retval;
+    /* The kdb library will default to this, but it is convenient to
+       make it explicit (error reporting and temporary filename generation
+       use it).  */
+    char *dbname = DEFAULT_KDB_FILE;
+    char *v4dbname = 0;
+    char *v4dumpfile = 0;
+    char *realm = 0;
+    char *mkey_name = 0;
+    char *mkey_fullname;
+    char *defrealm;
+    int keytypedone = 0;
+    int v4manual = 0;
+    int read_mkey = 0;
+    int tempdb = 0;
+    char *tempdbname;
+    krb5_context context;
+    char *stash_file = (char *) NULL;
+    krb5_realm_params *rparams;
+    int        persist, op_ind;
+
+    krb5_enctype etype = 0xffff;
+
+    krb5_init_context(&context);
+
+    krb5_init_ets(context);
+
+    if (strrchr(argv[0], '/'))
+       argv[0] = strrchr(argv[0], '/')+1;
+
+    persist = 1;
+    op_ind = 1;
+    while (persist && (op_ind < argc)) {
+       if (!strcmp(argv[op_ind], "-d") && ((argc - op_ind) >= 2)) {
+           dbname = argv[op_ind+1];
+           op_ind++;
+       }
+       else if (!strcmp(argv[op_ind], "-T")) {
+           create_local_tgt = 1;
+       }
+       else if (!strcmp(argv[op_ind], "-t")) {
+           tempdb = 1;
+       }
+       else if (!strcmp(argv[op_ind], "-r") && ((argc - op_ind) >= 2)) {
+           realm = argv[op_ind+1];
+           op_ind++;
+       }
+       else if (!strcmp(argv[op_ind], "-K")) {
+           read_mkey = 1;
+       }
+       else if (!strcmp(argv[op_ind], "-v")) {
+           verbose = 1;
+       }
+       else if (!strcmp(argv[op_ind], "-k") && ((argc - op_ind) >= 2)) {
+           if (!krb5_string_to_keytype(argv[op_ind+1],
+                                       &master_keyblock.keytype))
+               keytypedone++;
+           else
+               com_err(argv[0], 0, "%s is an invalid keytype",
+                       argv[op_ind+1]);
+           op_ind++;
+       }
+       else if (!strcmp(argv[op_ind], "-M") && ((argc - op_ind) >= 2)) {
+           mkey_name = argv[op_ind+1];
+           op_ind++;
+       }
+       else if (!strcmp(argv[op_ind], "-e") && ((argc - op_ind) >= 2)) {
+           if (krb5_string_to_enctype(argv[op_ind+1], &etype))
+               com_err(argv[0], 0, "%s is an invalid encryption type",
+                       argv[op_ind+1]);
+           op_ind++;
+       }
+       else if (!strcmp(argv[op_ind], "-n")) {
+           v4manual++;
+       }
+       else if (!strcmp(argv[op_ind], "-f") && ((argc - op_ind) >= 2)) {
+           if (v4dbname) {
+               usage(PROGNAME, 1);
+               return;
+           }
+           v4dumpfile = argv[op_ind+1];
+           op_ind++;
+       }
+       else
+           persist = 0;
+       op_ind++;
+    }
+
+    /*
+     * Attempt to read the KDC profile.  If we do, then read appropriate values
+     * from it and augment values supplied on the command line.
+     */
+    if (!(retval = krb5_read_realm_params(context,
+                                         realm,
+                                         (char *) NULL,
+                                         (char *) NULL,
+                                         &rparams))) {
+       /* Get the value for the database */
+       if (rparams->realm_dbname && !dbname)
+           dbname = strdup(rparams->realm_dbname);
+
+       /* Get the value for the master key name */
+       if (rparams->realm_mkey_name && !mkey_name)
+           mkey_name = strdup(rparams->realm_mkey_name);
+
+       /* Get the value for the master key type */
+       if (rparams->realm_keytype_valid && !keytypedone) {
+           master_keyblock.keytype = rparams->realm_keytype;
+           keytypedone++;
+       }
+
+       /* Get the value for the encryption type */
+       if (rparams->realm_enctype_valid && (etype == 0xffff))
+           etype = rparams->realm_enctype;
+
+       /* Get the value for the stashfile */
+       if (rparams->realm_stash_file)
+           stash_file = strdup(rparams->realm_stash_file);
+
+       /* Get the value for maximum ticket lifetime. */
+       if (rparams->realm_max_life_valid)
+           rblock.max_life = rparams->realm_max_life;
+
+       /* Get the value for maximum renewable ticket lifetime. */
+       if (rparams->realm_max_rlife_valid)
+           rblock.max_rlife = rparams->realm_max_rlife;
+
+       /* Get the value for the default principal expiration */
+       if (rparams->realm_expiration_valid)
+           rblock.expiration = rparams->realm_expiration;
+
+       /* Get the value for the default principal flags */
+       if (rparams->realm_flags_valid)
+           rblock.flags = rparams->realm_flags;
+
+       krb5_free_realm_params(context, rparams);
+    }
+
+    if (!v4dumpfile) {
+       usage(PROGNAME, 1);
+       return;
+    }
+
+    if (!keytypedone)
+       master_keyblock.keytype = DEFAULT_KDC_KEYTYPE;
+
+    if (!valid_keytype(master_keyblock.keytype)) {
+       com_err(PROGNAME, KRB5_PROG_KEYTYPE_NOSUPP,
+               "while setting up keytype %d", master_keyblock.keytype);
+       return;
+    }
+
+    if (etype == 0xffff)
+       etype = DEFAULT_KDC_ETYPE;
+
+    if (!valid_etype(etype)) {
+       com_err(PROGNAME, KRB5_PROG_ETYPE_NOSUPP,
+               "while setting up etype %d", etype);
+       return;
+    }
+    krb5_use_cstype(context, &master_encblock, etype);
+
+    /* If the user has not requested locking, don't modify an existing database. */
+    if (! tempdb) {
+       retval = krb5_db_set_name(context, dbname);
+       if (retval != ENOENT) {
+           fprintf(stderr,
+                   "%s: The v5 database appears to already exist.\n",
+                   PROGNAME);
+           return;
+       }
+       tempdbname = dbname;
+    } else {
+       int dbnamelen = strlen(dbname);
+       tempdbname = malloc(dbnamelen + 2);
+       if (tempdbname == 0) {
+           com_err(PROGNAME, ENOMEM, "allocating temporary filename");
+           return;
+       }
+       strcpy(tempdbname, dbname);
+       tempdbname[dbnamelen] = '~';
+       tempdbname[dbnamelen+1] = 0;
+       (void) kdb5_db_destroy(context, tempdbname);
+    }
+       
+
+    if (!realm) {
+       if (retval = krb5_get_default_realm(context, &defrealm)) {
+           com_err(PROGNAME, retval, "while retrieving default realm name");
+           return;
+       }           
+       realm = defrealm;
+    }
+
+    /* assemble & parse the master key name */
+
+    if (retval = krb5_db_setup_mkey_name(context, mkey_name, realm,
+                                        &mkey_fullname, &master_princ)) {
+       com_err(PROGNAME, retval, "while setting up master key name");
+       return;
+    }
+
+    krb5_princ_set_realm_data(context, &db_create_princ, realm);
+    krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm));
+    krb5_princ_set_realm_data(context, &tgt_princ, realm);
+    krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm));
+    krb5_princ_component(context, &tgt_princ,1)->data = realm;
+    krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm);
+
+    printf("Initializing database '%s' for realm '%s',\n\
+master key name '%s'\n",
+          dbname, realm, mkey_fullname);
+
+    if (read_mkey) {
+       puts("You will be prompted for the version 5 database Master Password.");
+       puts("It is important that you NOT FORGET this password.");
+       fflush(stdout);
+    }
+
+    if (retval = krb5_db_fetch_mkey(context, master_princ, &master_encblock,
+                                   read_mkey, read_mkey, stash_file, 0, 
+                                   &master_keyblock)) {
+       com_err(PROGNAME, retval, "while reading master key");
+       return;
+    }
+    if (retval = krb5_process_key(context, &master_encblock, &master_keyblock)) {
+       com_err(PROGNAME, retval, "while processing master key");
+       return;
+    }
+
+    rblock.eblock = &master_encblock;
+    if (retval = krb5_init_random_key(context, &master_encblock,
+                                     &master_keyblock, &rblock.rseed)) {
+       com_err(PROGNAME, retval, "while initializing random key generator");
+       (void) krb5_finish_key(context, &master_encblock);
+       return;
+    }
+    if (retval = krb5_db_create(context, tempdbname)) {
+       (void) krb5_finish_key(context, &master_encblock);
+       (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+       (void) krb5_dbm_db_destroy(context, tempdbname);
+       com_err(PROGNAME, retval, "while creating %sdatabase '%s'",
+               tempdb ? "temporary " : "", tempdbname);
+       return;
+    }
+    if (retval = krb5_db_set_name(context, tempdbname)) {
+       (void) krb5_finish_key(context, &master_encblock);
+       (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+       (void) krb5_dbm_db_destroy(context, tempdbname);
+        com_err(PROGNAME, retval, "while setting active database to '%s'",
+                tempdbname);
+        return;
+    }
+    if (v4init(PROGNAME, v4dbname, v4manual, v4dumpfile)) {
+       (void) krb5_finish_key(context, &master_encblock);
+       (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+       (void) krb5_dbm_db_destroy(context, tempdbname);
+       return;
+    }
+    if ((retval = krb5_db_init(context)) || 
+       (retval = krb5_dbm_db_open_database(context))) {
+       (void) krb5_finish_key(context, &master_encblock);
+       (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+       (void) krb5_dbm_db_destroy(context, tempdbname);
+       com_err(PROGNAME, retval, "while initializing the database '%s'",
+               tempdbname);
+       return;
+    }
+
+    if (retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) {
+       (void) krb5_db_fini(context);
+       (void) krb5_finish_key(context, &master_encblock);
+       (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+       (void) krb5_dbm_db_destroy(context, tempdbname);
+       com_err(PROGNAME, retval, "while adding K/M to the database");
+       return;
+    }
+
+    if (create_local_tgt &&
+       (retval = add_principal(context, &tgt_princ, RANDOM_KEY, &rblock))) {
+       (void) krb5_db_fini(context);
+       (void) krb5_finish_key(context, &master_encblock);
+       (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+       (void) krb5_dbm_db_destroy(context, tempdbname);
+       com_err(PROGNAME, retval, "while adding TGT service to the database");
+       return;
+    }
+
+    retval = process_v4_dump(context, v4dumpfile, realm);
+    putchar('\n');
+    if (retval)
+       com_err(PROGNAME, retval, "while translating entries to the database");
+    else {
+       retval = fixup_database(context, realm);
+    }
+    
+    /* clean up; rename temporary database if there were no errors */
+    if (retval == 0) {
+       if (retval = krb5_db_fini (context))
+           com_err(PROGNAME, retval, "while shutting down database");
+       else if (tempdb && (retval = krb5_dbm_db_rename(context, tempdbname,
+                                                       dbname)))
+           com_err(PROGNAME, retval, "while renaming temporary database");
+    } else {
+       (void) krb5_db_fini (context);
+       if (tempdb)
+               (void) krb5_dbm_db_destroy (context, tempdbname);
+    }
+    (void) krb5_finish_key(context, &master_encblock);
+    (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+    memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+    krb5_free_context(context);
+    return;
+}
+
+static int
+v4init(pname, name, manual, dumpfile)
+char *pname, *name;
+int manual;
+char *dumpfile;
+{
+    des_read_password(master_key, "Kerberos master key: ", 1);
+    printf("\n");
+    key_sched(master_key, master_key_schedule);
+    return 0;
+}
+
+static krb5_error_code
+enter_in_v5_db(context, realm, princ)
+krb5_context context;
+char *realm;
+Principal *princ;
+{
+    krb5_db_entry entry;
+    krb5_error_code retval;
+    krb5_keyblock v4v5key;
+    int nentries = 1;
+    des_cblock v4key;
+    char *name;
+    krb5_tl_mod_princ  mod_princ;
+    krb5_keysalt       keysalt;
+
+    /* don't convert local TGT if we created a TGT already.... */
+    if (create_local_tgt && !strcmp(princ->name, "krbtgt") &&
+       !strcmp(princ->instance, realm)) {
+           if (verbose)
+                   printf("\nignoring local TGT: '%s.%s' ...",
+                          princ->name, princ->instance);
+           return 0;
+    }
+    if (!strcmp(princ->name, KERB_M_NAME) &&
+       !strcmp(princ->instance, KERB_M_INST)) {
+       des_cblock key_from_db;
+       int val;
+
+       /* here's our chance to verify the master key */
+       /*
+        * use the master key to decrypt the key in the db, had better
+        * be the same! 
+        */
+       memcpy(key_from_db, (char *)&princ->key_low, 4);
+       memcpy(((char *) key_from_db) + 4, (char *)&princ->key_high, 4);
+       pcbc_encrypt((C_Block *) &key_from_db,
+                    (C_Block *) &key_from_db,
+                    (long) sizeof(C_Block),
+                    master_key_schedule,
+                    (C_Block *) master_key,
+                    DECRYPT);
+       val = memcmp((char *) master_key, (char *) key_from_db,
+                    sizeof(master_key));
+       memset((char *)key_from_db, 0, sizeof(key_from_db));
+       if (val) {
+           return KRB5_KDB_BADMASTERKEY;
+       }
+       if (verbose)
+           printf("\nignoring '%s.%s' ...", princ->name, princ->instance);
+       return 0;
+    }
+    memset((char *) &entry, 0, sizeof(entry));
+    if (retval = krb5_425_conv_principal(context, princ->name, princ->instance,
+                                        realm, &entry.princ))
+       return retval;
+    if (verbose) {
+       if (retval = krb5_unparse_name(context, entry.princ, &name))
+          name = strdup("<not unparsable name!>");
+       if (verbose)
+           printf("\ntranslating %s...", name);
+       free(name);
+    }
+
+    if (retval = krb5_build_principal(context, &mod_princ.mod_princ,
+                                     strlen(realm),
+                                     realm, princ->mod_name,
+                                     princ->mod_instance[0] ? princ->mod_instance : 0,
+                                     0)) {
+       krb5_free_principal(context, entry.princ);
+       return retval;
+    }
+    mod_princ.mod_date = princ->mod_date;
+
+    entry.max_life = princ->max_life * 60 * 5;
+    entry.max_renewable_life = rblock.max_rlife;
+    entry.mkvno = 1;
+    entry.len = KRB5_KDB_V1_BASE_LENGTH;
+    entry.expiration = princ->exp_date;
+    entry.attributes = rblock.flags;   /* XXX is there a way to convert
+                                          the old attrs? */
+
+    memcpy((char *)v4key, (char *)&(princ->key_low), 4);
+    memcpy((char *) (((char *) v4key) + 4), (char *)&(princ->key_high), 4);
+    pcbc_encrypt((C_Block *) &v4key,
+                (C_Block *) &v4key,
+                (long) sizeof(C_Block),
+                master_key_schedule,
+                (C_Block *) master_key,
+                DECRYPT);
+
+    v4v5key.magic = KV5M_KEYBLOCK;
+    v4v5key.etype = master_keyblock.etype;
+    v4v5key.contents = (krb5_octet *)v4key;
+    v4v5key.keytype = KEYTYPE_DES;
+    v4v5key.length = sizeof(v4key);
+
+    retval = krb5_dbe_create_key_data(context, &entry);
+    if (retval) {
+       krb5_free_principal(context, entry.princ);
+       krb5_free_principal(context, mod_princ.mod_princ);
+       return retval;
+    }
+
+    keysalt.type = KRB5_KDB_SALTTYPE_V4;
+    keysalt.data.length = 0;
+    keysalt.data.data = (char *) NULL;
+    retval = krb5_dbekd_encrypt_key_data(context, rblock.eblock,
+                                        &v4v5key, &keysalt, 
+                                        princ->key_version,
+                                        &entry.key_data[0]);
+    if (!retval)
+       retval = krb5_dbe_encode_mod_princ_data(context, &mod_princ, &entry);
+    if (retval) {
+       krb5_db_free_principal(context, &entry, 1);
+       krb5_free_principal(context, mod_princ.mod_princ);
+       return retval;
+    }
+    memset((char *)v4key, 0, sizeof(v4key));
+
+    retval = krb5_db_put_principal(context, &entry, &nentries);
+
+    if (!retval && !strcmp(princ->name, "krbtgt") &&
+       strcmp(princ->instance, realm) && princ->instance[0]) {
+           krb5_free_principal(context, entry.princ);
+           if (retval = krb5_build_principal(context, &entry.princ,
+                                             strlen(princ->instance),
+                                             princ->instance,
+                                             "krbtgt", realm, 0))
+                   return retval;
+           retval = krb5_db_put_principal(context, &entry, &nentries);
+    }
+
+    krb5_db_free_principal(context, &entry, 1);
+    krb5_free_principal(context, mod_princ.mod_princ);
+
+    return retval;
+}
+
+static krb5_error_code
+add_principal(context, princ, op, pblock)
+krb5_context context;
+krb5_principal princ;
+enum ap_op op;
+struct realm_info *pblock;
+{
+    krb5_db_entry entry;
+    krb5_error_code retval;
+    krb5_keyblock *rkey;
+    int nentries = 1;
+    krb5_tl_mod_princ mod_princ;
+
+    memset((char *) &entry, 0, sizeof(entry));
+    if (retval = krb5_copy_principal(context, princ, &entry.princ))
+       return(retval);
+    entry.max_life = pblock->max_life;
+    entry.max_renewable_life = pblock->max_rlife;
+    entry.mkvno = 1;
+    entry.len = KRB5_KDB_V1_BASE_LENGTH;
+    entry.expiration = pblock->expiration;
+    if (retval = krb5_copy_principal(context, &db_create_princ,
+                                    &mod_princ.mod_princ)) {
+       krb5_free_principal(context, entry.princ);
+       return(retval);
+    }
+    
+    if ((retval = krb5_timeofday(context, &mod_princ.mod_date)) ||
+       (retval = krb5_copy_principal(context, &db_create_princ,
+                                     &mod_princ.mod_princ))) {
+       krb5_free_principal(context, mod_princ.mod_princ);
+       krb5_db_free_principal(context, &entry, 1);
+       return retval;
+    }
+    entry.attributes = pblock->flags;
+
+    if (retval = krb5_dbe_create_key_data(context, &entry)) {
+       krb5_free_principal(context, mod_princ.mod_princ);
+       krb5_db_free_principal(context, &entry, 1);
+       return(retval);
+    }
+
+    switch (op) {
+    case MASTER_KEY:
+       entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+       if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock,
+                                                &master_keyblock,
+                                                (krb5_keysalt *) NULL, 1,
+                                                &entry.key_data[0])) {
+           krb5_free_principal(context, mod_princ.mod_princ);
+           krb5_db_free_principal(context, &entry, 1);
+           return retval;
+       }
+       break;
+    case RANDOM_KEY:
+       if (retval = krb5_random_key(context, pblock->eblock, pblock->rseed,
+                                    &rkey)) {
+           krb5_free_principal(context, mod_princ.mod_princ);
+           krb5_db_free_principal(context, &entry, 1);
+           return retval;
+       }
+       if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock,
+                                                rkey,
+                                                (krb5_keysalt *) NULL, 1,
+                                                &entry.key_data[0])) {
+           krb5_free_principal(context, mod_princ.mod_princ);
+           krb5_db_free_principal(context, &entry, 1);
+           return(retval);
+       }
+       krb5_free_keyblock(context, rkey);
+       break;
+    case NULL_KEY:
+       return EOPNOTSUPP;
+    default:
+       break;
+    }
+
+    retval = krb5_dbe_encode_mod_princ_data(context, &mod_princ, &entry);
+    if (!retval)
+       retval = krb5_db_put_principal(context, &entry, &nentries);
+    krb5_db_free_principal(context, &entry, 1);
+    krb5_free_principal(context, mod_princ.mod_princ);
+    return retval;
+}
+
+/*
+ * Convert a struct tm * to a UNIX time.
+ */
+
+
+#define daysinyear(y) (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
+
+#define SECSPERDAY 24*60*60
+#define SECSPERHOUR 60*60
+#define SECSPERMIN 60
+
+static int cumdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+                            365};
+
+static int leapyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int nonleapyear[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static long
+maketime(tp, local)
+register struct tm *tp;
+int local;
+{
+    register long retval;
+    int foo;
+    int *marray;
+
+    if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
+       tp->tm_hour < 0 || tp->tm_hour > 23 ||
+       tp->tm_min < 0 || tp->tm_min > 59 ||
+       tp->tm_sec < 0 || tp->tm_sec > 59) /* out of range */
+       return 0;
+
+    retval = 0;
+    if (tp->tm_year < 1900)
+       foo = tp->tm_year + 1900;
+    else
+       foo = tp->tm_year;
+
+    if (foo < 1901 || foo > 2038)      /* year is too small/large */
+       return 0;
+
+    if (daysinyear(foo) == 366) {
+       if (tp->tm_mon > 1)
+           retval+= SECSPERDAY;        /* add leap day */
+       marray = leapyear;
+    } else
+       marray = nonleapyear;
+
+    if (tp->tm_mday < 0 || tp->tm_mday > marray[tp->tm_mon])
+       return 0;                       /* out of range */
+
+    while (--foo >= 1970)
+       retval += daysinyear(foo) * SECSPERDAY;
+
+    retval += cumdays[tp->tm_mon] * SECSPERDAY;
+    retval += (tp->tm_mday-1) * SECSPERDAY;
+    retval += tp->tm_hour * SECSPERHOUR + tp->tm_min * SECSPERMIN + tp->tm_sec;
+
+    if (local) {
+       /* need to use local time, so we retrieve timezone info */
+       struct timezone tz;
+       struct timeval tv;
+       if (gettimeofday(&tv, &tz) < 0) {
+           /* some error--give up? */
+           return(retval);
+       }
+       retval += tz.tz_minuteswest * SECSPERMIN;
+    }
+    return(retval);
+}
+
+static long
+time_explode(cp)
+register char *cp;
+{
+    char wbuf[5];
+    struct tm tp;
+    int local;
+
+    memset((char *)&tp, 0, sizeof(tp));
+    
+    if (strlen(cp) > 10) {             /* new format */
+       (void) strncpy(wbuf, cp, 4);
+       wbuf[4] = 0;
+       tp.tm_year = atoi(wbuf);
+       cp += 4;                        /* step over the year */
+       local = 0;                      /* GMT */
+    } else {                           /* old format: local time, 
+                                          year is 2 digits, assuming 19xx */
+       wbuf[0] = *cp++;
+       wbuf[1] = *cp++;
+       wbuf[2] = 0;
+       tp.tm_year = 1900 + atoi(wbuf);
+       local = 1;                      /* local */
+    }
+
+    wbuf[0] = *cp++;
+    wbuf[1] = *cp++;
+    wbuf[2] = 0;
+    tp.tm_mon = atoi(wbuf)-1;
+
+    wbuf[0] = *cp++;
+    wbuf[1] = *cp++;
+    tp.tm_mday = atoi(wbuf);
+    
+    wbuf[0] = *cp++;
+    wbuf[1] = *cp++;
+    tp.tm_hour = atoi(wbuf);
+    
+    wbuf[0] = *cp++;
+    wbuf[1] = *cp++;
+    tp.tm_min = atoi(wbuf);
+
+
+    return(maketime(&tp, local));
+}
+
+static krb5_error_code
+process_v4_dump(context, dumpfile, realm)
+krb5_context context;
+char *dumpfile;
+char *realm;
+{
+    krb5_error_code retval;
+    FILE *input_file;
+    Principal aprinc;
+    char    exp_date_str[50];
+    char    mod_date_str[50];
+    int     temp1, temp2, temp3;
+    long time_explode();
+
+    input_file = fopen(dumpfile, "r");
+    if (!input_file)
+       return errno;
+
+    for (;;) {                 /* explicit break on eof from fscanf */
+       int nread;
+
+       memset((char *)&aprinc, 0, sizeof(aprinc));
+       nread = fscanf(input_file,
+                      "%s %s %d %d %d %hd %x %x %s %s %s %s\n",
+                      aprinc.name,
+                      aprinc.instance,
+                      &temp1,
+                      &temp2,
+                      &temp3,
+                      &aprinc.attributes,
+                      &aprinc.key_low,
+                      &aprinc.key_high,
+                      exp_date_str,
+                      mod_date_str,
+                      aprinc.mod_name,
+                      aprinc.mod_instance);
+       if (nread != 12) {
+           retval = nread == EOF ? 0 : KRB5_KDB_DB_CORRUPT;
+           break;
+       }
+       aprinc.key_low = ntohl (aprinc.key_low);
+       aprinc.key_high = ntohl (aprinc.key_high);
+       aprinc.max_life = (unsigned char) temp1;
+       aprinc.kdc_key_ver = (unsigned char) temp2;
+       aprinc.key_version = (unsigned char) temp3;
+       aprinc.exp_date = time_explode(exp_date_str);
+       aprinc.mod_date = time_explode(mod_date_str);
+       if (aprinc.instance[0] == '*')
+           aprinc.instance[0] = '\0';
+       if (aprinc.mod_name[0] == '*')
+           aprinc.mod_name[0] = '\0';
+       if (aprinc.mod_instance[0] == '*')
+           aprinc.mod_instance[0] = '\0';
+       if (retval = enter_in_v5_db(context, realm, &aprinc))
+           break;
+    }
+    (void) fclose(input_file);
+    return retval;
+}
+
+static krb5_error_code fixup_database(context, realm)
+    krb5_context context;
+    char * realm;
+{
+    krb5_db_entry entry;
+    krb5_error_code retval;
+    int nprincs;
+    krb5_boolean more;
+
+    nprincs = 1;
+    if (retval = krb5_db_get_principal(context, &tgt_princ, &entry, 
+                                      &nprincs, &more))
+       return retval;
+    
+    if (nprincs == 0)
+       return 0;
+    
+    entry.attributes |= KRB5_KDB_SUPPORT_DESMD5;
+    
+    retval = krb5_db_put_principal(context, &entry, &nprincs);
+    
+    if (nprincs)
+       krb5_db_free_principal(context, &entry, nprincs);
+    
+    return retval;
+}
+
+