summaryrefslogtreecommitdiffstats
path: root/src/kadmin/create/kdb5_create.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kadmin/create/kdb5_create.c')
-rw-r--r--src/kadmin/create/kdb5_create.c536
1 files changed, 536 insertions, 0 deletions
diff --git a/src/kadmin/create/kdb5_create.c b/src/kadmin/create/kdb5_create.c
new file mode 100644
index 0000000000..8b167b6b93
--- /dev/null
+++ b/src/kadmin/create/kdb5_create.c
@@ -0,0 +1,536 @@
+/*
+ * admin/create/kdb5_create.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 KDC database.
+ */
+
+#include <stdio.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+
+enum ap_op {
+ NULL_KEY, /* setup null keys */
+ MASTER_KEY, /* use master key as new key */
+ TGT_KEY /* special handling for tgt key */
+};
+
+krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL };
+
+struct realm_info {
+ krb5_deltat max_life;
+ krb5_deltat max_rlife;
+ krb5_timestamp expiration;
+ krb5_flags flags;
+ krb5_encrypt_block *eblock;
+ krb5_pointer rseed;
+ krb5_int32 nkslist;
+ krb5_key_salt_tuple *kslist;
+} rblock = { /* XXX */
+ KRB5_KDB_MAX_LIFE,
+ KRB5_KDB_MAX_RLIFE,
+ KRB5_KDB_EXPIRATION,
+ KRB5_KDB_DEF_FLAGS,
+ (krb5_encrypt_block *) NULL,
+ (krb5_pointer) NULL,
+ 1,
+ &def_kslist
+};
+
+struct iterate_args {
+ krb5_context ctx;
+ struct realm_info *rblock;
+ krb5_db_entry *dbentp;
+};
+
+static krb5_error_code add_principal
+ PROTOTYPE((krb5_context,
+ krb5_principal,
+ enum ap_op,
+ struct realm_info *));
+
+/*
+ * Steps in creating a database:
+ *
+ * 1) use the db calls to open/create a new database
+ *
+ * 2) get a realm name for the new db
+ *
+ * 3) get a master password for the new db; convert to an encryption key.
+ *
+ * 4) create various required entries in the database
+ *
+ * 5) close & exit
+ */
+
+static void
+usage(who, status)
+char *who;
+int status;
+{
+ fprintf(stderr, "usage: %s [-d dbpathname] [-r realmname] [-k enctype]\n\
+\t[-M mkeyname]\n",
+ who);
+ exit(status);
+}
+
+krb5_keyblock master_keyblock;
+krb5_principal master_princ;
+krb5_encrypt_block master_encblock;
+krb5_data master_salt;
+
+krb5_data tgt_princ_entries[] = {
+ {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
+ {0, 0, 0} };
+
+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 */
+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 */
+};
+
+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 */
+};
+
+char *mkey_password = 0;
+char *whoami;
+
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ extern char *optarg;
+ int optchar;
+
+ krb5_error_code retval;
+ char *dbname = (char *) NULL;
+ char *realm = 0;
+ char *mkey_name = 0;
+ char *mkey_fullname;
+ char *defrealm;
+ char *pw_str = 0;
+ char *keyfile = 0;
+ int pw_size = 0;
+ int enctypedone = 0;
+ int do_stash = 0;
+ krb5_data pwd;
+ krb5_context context;
+ krb5_realm_params *rparams;
+ kadm5_config_params kadm5_params;
+
+ memset(&kadm5_params, 0, sizeof(kadm5_params));
+
+ krb5_init_context(&context);
+ krb5_init_ets(context);
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+ whoami = argv[0];
+
+ kadm5_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
+ kadm5_params.mkey_from_kbd = 1;
+
+ while ((optchar = getopt(argc, argv, "d:r:k:M:e:P:sf:")) != EOF) {
+ switch(optchar) {
+ case 'd': /* set db name */
+ kadm5_params.dbname = dbname = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_DBNAME;
+ break;
+ case 'r':
+ kadm5_params.realm = realm = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_REALM;
+ break;
+ case 'k':
+ if (!krb5_string_to_enctype(optarg, &master_keyblock.enctype)){
+ enctypedone++;
+ kadm5_params.enctype = master_keyblock.enctype;
+ kadm5_params.mask |= KADM5_CONFIG_ENCTYPE;
+ }
+ else
+ com_err(argv[0], 0, "%s is an invalid enctype", optarg);
+ break;
+ case 's':
+ do_stash++;
+ kadm5_params.mkey_from_kbd = 0;
+ break;
+ case 'f':
+ kadm5_params.stash_file = keyfile = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_STASH_FILE;
+ break;
+ case 'M': /* master key name in DB */
+ kadm5_params.mkey_name = mkey_name = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_MKEY_NAME;
+ break;
+ case 'P': /* Only used for testing!!! */
+ mkey_password = optarg;
+ break;
+ case '?':
+ default:
+ usage(argv[0], 1);
+ /*NOTREACHED*/
+ }
+ }
+
+ /*
+ * 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_enctype_valid && !enctypedone) {
+ master_keyblock.enctype = rparams->realm_enctype;
+ enctypedone++;
+ }
+
+ /* 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;
+
+ /* Get the value of the supported key/salt pairs */
+ if (rparams->realm_num_keysalts) {
+ rblock.nkslist = rparams->realm_num_keysalts;
+ rblock.kslist = rparams->realm_keysalts;
+ rparams->realm_num_keysalts = 0;
+ rparams->realm_keysalts = (krb5_key_salt_tuple *) NULL;
+ }
+
+ /* Get the value for the stash file */
+ if (rparams->realm_stash_file && !keyfile)
+ keyfile = strdup(rparams->realm_stash_file);
+
+ krb5_free_realm_params(context, rparams);
+ }
+
+ if (!dbname)
+ dbname = DEFAULT_KDB_FILE;
+
+ if (!enctypedone)
+ master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
+
+ if (!valid_enctype(master_keyblock.enctype)) {
+ char tmp[32];
+ if (krb5_enctype_to_string(master_keyblock.enctype, tmp, sizeof(tmp)))
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d", master_keyblock.enctype);
+ else
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp);
+ exit(1);
+ }
+
+ krb5_use_enctype(context, &master_encblock, master_keyblock.enctype);
+
+ retval = krb5_db_set_name(context, dbname);
+ if (!retval) retval = EEXIST;
+
+ if (retval == EEXIST || retval == EACCES || retval == EPERM) {
+ /* it exists ! */
+ com_err(argv[0], 0, "The database '%s' appears to already exist",
+ dbname);
+ exit(1);
+ }
+ if (!realm) {
+ if ((retval = krb5_get_default_realm(context, &defrealm))) {
+ com_err(argv[0], retval, "while retrieving default realm name");
+ exit(1);
+ }
+ 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(argv[0], retval, "while setting up master key name");
+ exit(1);
+ }
+
+ 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 (!mkey_password) {
+ printf("You will be prompted for the database Master Password.\n");
+ printf("It is important that you NOT FORGET this password.\n");
+ fflush(stdout);
+
+ pw_size = 1024;
+ pw_str = malloc(pw_size);
+
+ retval = krb5_read_password(context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
+ pw_str, &pw_size);
+ if (retval) {
+ com_err(argv[0], retval, "while reading master key from keyboard");
+ exit(1);
+ }
+ mkey_password = pw_str;
+ }
+
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ retval = krb5_principal2salt(context, master_princ, &master_salt);
+ if (retval) {
+ com_err(argv[0], retval, "while calculated master key salt");
+ exit(1);
+ }
+ if (retval = krb5_string_to_key(context, &master_encblock,
+ &master_keyblock, &pwd, &master_salt)) {
+ com_err(argv[0], retval, "while transforming master key from password");
+ exit(1);
+ }
+
+ if ((retval = krb5_process_key(context, &master_encblock,
+ &master_keyblock))) {
+ com_err(argv[0], retval, "while processing master key");
+ exit(1);
+ }
+
+ rblock.eblock = &master_encblock;
+ if ((retval = krb5_init_random_key(context, &master_encblock,
+ &master_keyblock, &rblock.rseed))) {
+ com_err(argv[0], retval, "while initializing random key generator");
+ (void) krb5_finish_key(context, &master_encblock);
+ exit(1);
+ }
+ if ((retval = krb5_db_create(context, dbname))) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while creating database '%s'",
+ dbname);
+ exit(1);
+ }
+ if ((retval = krb5_db_set_name(context, dbname))) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while setting active database to '%s'",
+ dbname);
+ exit(1);
+ }
+ if ((retval = krb5_db_init(context))) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while initializing the database '%s'",
+ dbname);
+ exit(1);
+ }
+
+ if ((retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) ||
+ (retval = add_principal(context, &tgt_princ, TGT_KEY, &rblock))) {
+ (void) krb5_db_fini(context);
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while adding entries to the database");
+ exit(1);
+ }
+ if (do_stash &&
+ ((retval = krb5_db_store_mkey(context, keyfile, master_princ,
+ &master_keyblock)))) {
+ com_err(argv[0], errno, "while storing key");
+ printf("Warning: couldn't stash master key.\n");
+ }
+ /* clean up */
+ (void) krb5_db_fini(context);
+ (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);
+ free(master_keyblock.contents);
+ if (pw_str) {
+ memset(pw_str, 0, pw_size);
+ free(pw_str);
+ }
+ free(master_salt.data);
+
+ kadm5_create(&kadm5_params);
+
+ exit(0);
+
+}
+
+static krb5_error_code
+tgt_keysalt_iterate(ksent, ptr)
+ krb5_key_salt_tuple *ksent;
+ krb5_pointer ptr;
+{
+ krb5_context context;
+ krb5_error_code kret;
+ struct iterate_args *iargs;
+ krb5_keyblock random_keyblock, *key;
+ krb5_int32 ind;
+ krb5_encrypt_block random_encblock;
+ krb5_pointer rseed;
+ krb5_data pwd;
+
+ iargs = (struct iterate_args *) ptr;
+ kret = 0;
+
+ context = iargs->ctx;
+
+ /*
+ * Convert the master key password into a key for this particular
+ * encryption system.
+ */
+ krb5_use_enctype(context, &random_encblock, ksent->ks_enctype);
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ if (kret = krb5_string_to_key(context, &random_encblock, &random_keyblock,
+ &pwd, &master_salt))
+ return kret;
+ if ((kret = krb5_init_random_key(context, &random_encblock,
+ &random_keyblock, &rseed)))
+ return kret;
+
+ if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
+ ind = iargs->dbentp->n_key_data-1;
+ if (!(kret = krb5_random_key(context,
+ &random_encblock, rseed,
+ &key))) {
+ kret = krb5_dbekd_encrypt_key_data(context,
+ iargs->rblock->eblock,
+ key,
+ NULL,
+ 1,
+ &iargs->dbentp->key_data[ind]);
+ krb5_free_keyblock(context, key);
+ }
+ }
+ memset((char *)random_keyblock.contents, 0, random_keyblock.length);
+ free(random_keyblock.contents);
+ (void) krb5_finish_random_key(context, &random_encblock, &rseed);
+ return(kret);
+}
+
+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_error_code retval;
+ krb5_db_entry entry;
+
+ krb5_timestamp now;
+ struct iterate_args iargs;
+
+ int nentries = 1;
+
+ memset((char *) &entry, 0, sizeof(entry));
+
+ entry.len = KRB5_KDB_V1_BASE_LENGTH;
+ entry.attributes = pblock->flags;
+ entry.max_life = pblock->max_life;
+ entry.max_renewable_life = pblock->max_rlife;
+ entry.expiration = pblock->expiration;
+
+ if ((retval = krb5_copy_principal(context, princ, &entry.princ)))
+ goto error_out;
+
+ if ((retval = krb5_timeofday(context, &now)))
+ goto error_out;
+
+ if ((retval = krb5_dbe_update_mod_princ_data(context, &entry,
+ now, &db_create_princ)))
+ goto error_out;
+
+ switch (op) {
+ case MASTER_KEY:
+ if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))
+ == NULL)
+ goto error_out;
+ memset((char *) entry.key_data, 0, sizeof(krb5_key_data));
+ entry.n_key_data = 1;
+
+ entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock,
+ &master_keyblock, NULL,
+ 1, entry.key_data)))
+ return retval;
+ break;
+ case TGT_KEY:
+ iargs.ctx = context;
+ iargs.rblock = pblock;
+ iargs.dbentp = &entry;
+ /*
+ * Iterate through the key/salt list, ignoring salt types.
+ */
+ if ((retval = krb5_keysalt_iterate(pblock->kslist,
+ pblock->nkslist,
+ 1,
+ tgt_keysalt_iterate,
+ (krb5_pointer) &iargs)))
+ return retval;
+ break;
+ case NULL_KEY:
+ return EOPNOTSUPP;
+ default:
+ break;
+ }
+
+ retval = krb5_db_put_principal(context, &entry, &nentries);
+
+error_out:;
+ krb5_dbe_free_contents(context, &entry);
+ return retval;
+}