/* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * * Openvision retains the copyright to derivative works of * this source code. Do *NOT* create a derivative of this * source code before consulting with your legal department. * Do *NOT* integrate *ANY* of this source code into another * product before consulting with your legal department. * * For further information, read the top-level Openvision * copyright which is contained in the top-level MIT Kerberos * copyright. * * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING * */ /* * 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. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * 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. */ /* * Yes, I know this is a hack, but we need admin.h without including the * rpc.h header. Additionally, our rpc.h header brings in * a des.h header which causes other problems. */ #define _RPC_RPC_H #include #define KDB5_DISPATCH #define KRB5_KDB5_DBM__ #include /* #define these to avoid an indirection function; for future implementations, these may be redirected from a dispatch table/routine */ #define krb5_dbm_db_set_name krb5_db_set_name #define krb5_dbm_db_set_nonblocking krb5_db_set_nonblocking #define krb5_dbm_db_init krb5_db_init #define krb5_dbm_db_get_age krb5_db_get_age #define krb5_dbm_db_create krb5_db_create #define krb5_dbm_db_rename krb5_db_rename #define krb5_dbm_db_get_principal krb5_db_get_principal #define krb5_dbm_db_free_principal krb5_db_free_principal #define krb5_dbm_db_put_principal krb5_db_put_principal #define krb5_dbm_db_delete_principal krb5_db_delete_principal #define krb5_dbm_db_lock krb5_db_lock #define krb5_dbm_db_unlock krb5_db_unlock #define krb5_dbm_db_set_lockmode krb5_db_set_lockmode #define krb5_dbm_db_close_database krb5_db_close_database #define krb5_dbm_db_open_database krb5_db_open_database #include #include #include #include #include #include "kdb5_util.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_keyblock *key; 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_keyblock *) NULL, 1, &def_kslist }; struct iterate_args { krb5_context ctx; struct realm_info *rblock; krb5_db_entry *dbentp; }; static krb5_error_code add_principal(krb5_context, krb5_principal, enum ap_op, struct realm_info *, krb5_keyblock *); /* * 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 */ extern krb5_principal master_princ; 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 */ }; extern char *mkey_password; extern char *progname; extern int exit_status; extern osa_adb_policy_t policy_db; extern kadm5_config_params global_params; extern krb5_context util_context; void kdb5_create(argc, argv) int argc; char *argv[]; { int optchar; krb5_error_code retval; char *mkey_fullname; char *pw_str = 0; unsigned int pw_size = 0; int do_stash = 0; krb5_int32 crflags = KRB5_KDB_CREATE_BTREE; krb5_data pwd, seed; kdb_log_context *log_ctx; krb5_keyblock mkey; krb5_data master_salt = { 0, NULL }; if (strrchr(argv[0], '/')) argv[0] = strrchr(argv[0], '/')+1; while ((optchar = getopt(argc, argv, "s")) != -1) { switch(optchar) { case 's': do_stash++; break; case 'h': crflags = KRB5_KDB_CREATE_HASH; case '?': default: usage(); return; } } rblock.max_life = global_params.max_life; rblock.max_rlife = global_params.max_rlife; rblock.expiration = global_params.expiration; rblock.flags = global_params.flags; rblock.nkslist = global_params.num_keysalts; rblock.kslist = global_params.keysalts; log_ctx = util_context->kdblog_context; retval = krb5_db_set_name(util_context, global_params.dbname); if (!retval) retval = EEXIST; if (retval == EEXIST || retval == EACCES || retval == EPERM) { /* it exists ! */ com_err(argv[0], 0, gettext("The database '%s' appears to already exist"), global_params.dbname); exit_status++; return; } /* assemble & parse the master key name */ if ((retval = krb5_db_setup_mkey_name(util_context, global_params.mkey_name, global_params.realm, &mkey_fullname, &master_princ))) { com_err(argv[0], retval, gettext("while setting up master key name")); exit_status++; return; } krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm); krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm)); krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm); krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm)); krb5_princ_component(util_context, &tgt_princ, 1)->data = global_params.realm; krb5_princ_component(util_context, &tgt_princ, 1)->length = strlen(global_params.realm); printf(gettext("Initializing database '%s' for realm '%s',\n" "master key name '%s'\n"), global_params.dbname, global_params.realm, mkey_fullname); if (!mkey_password) { printf(gettext("You will be prompted for the " "database Master Password.\n")); printf(gettext("It is important that you NOT FORGET this password.\n")); fflush(stdout); pw_size = 1024; pw_str = malloc(pw_size); retval = krb5_read_password(util_context, gettext("Enter KDC database master key"), gettext("Re-enter KDC database " "master key to verify"), pw_str, &pw_size); if (retval) { com_err(argv[0], retval, gettext("while reading master key from keyboard")); exit_status++; return; } mkey_password = pw_str; } pwd.data = mkey_password; pwd.length = strlen(mkey_password); retval = krb5_principal2salt(util_context, master_princ, &master_salt); if (retval) { com_err(argv[0], retval, gettext("while calculated master key salt")); exit_status++; goto cleanup; } if (retval = krb5_c_string_to_key(util_context, global_params.enctype, &pwd, &master_salt, &mkey)) { com_err(argv[0], retval, gettext("while transforming master key from password")); exit_status++; goto cleanup; } retval = krb5_copy_keyblock(util_context, &mkey, &rblock.key); if (retval) { com_err(argv[0], retval, gettext("while copying master key")); exit_status++; goto cleanup; } seed.length = mkey.length; seed.data = (char *)mkey.contents; if ((retval = krb5_c_random_seed(util_context, &seed))) { com_err(argv[0], retval, gettext("while initializing random key generator")); exit_status++; goto cleanup; } if ((retval = krb5_db_create(util_context, global_params.dbname, crflags))) { com_err(argv[0], retval, gettext("while creating database '%s'"), global_params.dbname); exit_status++; goto cleanup; } if (retval = krb5_db_fini(util_context)) { com_err(argv[0], retval, gettext("while closing current database")); exit_status++; goto cleanup; } if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { com_err(argv[0], retval, gettext("while setting active database to '%s'"), global_params.dbname); exit_status++; goto cleanup; } if ((retval = krb5_db_init(util_context))) { com_err(argv[0], retval, gettext("while initializing the database '%s'"), global_params.dbname); exit_status++; goto cleanup; } if (log_ctx && log_ctx->iproprole) { if (retval = ulog_map(util_context, &global_params, FKCOMMAND)) { com_err(argv[0], retval, gettext("while creating update log")); exit_status++; goto cleanup; } /* * We're reinitializing the update log in case one already * existed, but this should never happen. */ (void) memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t)); log_ctx->ulog->kdb_hmagic = KDB_HMAGIC; log_ctx->ulog->db_version_num = KDB_VERSION; log_ctx->ulog->kdb_state = KDB_STABLE; log_ctx->ulog->kdb_block = ULOG_BLOCK; /* * Since we're creating a new db we shouldn't worry about * adding the initial principals since any slave might as well * do full resyncs from this newly created db. */ log_ctx->iproprole = IPROP_NULL; } if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock, &mkey)) || (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock, &mkey))) { (void) krb5_db_fini(util_context); com_err(argv[0], retval, gettext("while adding entries to the database")); exit_status++; goto cleanup; } /* * Always stash the master key so kadm5_create does not prompt for * it; delete the file below if it was not requested. DO NOT EXIT * BEFORE DELETING THE KEYFILE if do_stash is not set. */ if (retval = krb5_db_store_mkey(util_context, global_params.stash_file, master_princ, &mkey)) { com_err(argv[0], errno, gettext("while storing key")); printf(gettext("Warning: couldn't stash master key.\n")); } if (pw_str) memset(pw_str, 0, pw_size); if (kadm5_create(&global_params)) { if (!do_stash) unlink(global_params.stash_file); exit_status++; goto cleanup; } if (!do_stash) unlink(global_params.stash_file); cleanup: if (pw_str) { if (mkey_password == pw_str) mkey_password = NULL; free(pw_str); } if (master_salt.data) free(master_salt.data); krb5_free_keyblock_contents(util_context, rblock.key); krb5_free_keyblock_contents(util_context, &mkey); (void) krb5_db_fini(util_context); return; } 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 key; krb5_int32 ind; 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. */ pwd.data = mkey_password; pwd.length = strlen(mkey_password); if (kret = krb5_c_random_seed(context, &pwd)) return kret; if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { ind = iargs->dbentp->n_key_data-1; if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype, &key))) { kret = krb5_dbekd_encrypt_key_data(context, iargs->rblock->key, &key, NULL, 1, &iargs->dbentp->key_data[ind]); krb5_free_keyblock_contents(context, &key); } } return(kret); } static krb5_error_code add_principal(krb5_context context, krb5_principal princ, enum ap_op op, struct realm_info *pblock, krb5_keyblock *mkey) { 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: entry.key_data = (krb5_key_data *) malloc(sizeof (krb5_key_data)); if (entry.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->key, mkey, NULL, 1, entry.key_data))) goto error_out; 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); }