Home | History | Annotate | Download | only in dbutil
      1 /*
      2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
      3  * Use is subject to license terms.
      4  */
      5 
      6 /*
      7  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
      8  *
      9  *	Openvision retains the copyright to derivative works of
     10  *	this source code.  Do *NOT* create a derivative of this
     11  *	source code before consulting with your legal department.
     12  *	Do *NOT* integrate *ANY* of this source code into another
     13  *	product before consulting with your legal department.
     14  *
     15  *	For further information, read the top-level Openvision
     16  *	copyright which is contained in the top-level MIT Kerberos
     17  *	copyright.
     18  *
     19  * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
     20  *
     21  */
     22 
     23 
     24 /*
     25  * admin/create/kdb5_create.c
     26  *
     27  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
     28  * All Rights Reserved.
     29  *
     30  * Export of this software from the United States of America may
     31  *   require a specific license from the United States Government.
     32  *   It is the responsibility of any person or organization contemplating
     33  *   export to obtain such a license before exporting.
     34  *
     35  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
     36  * distribute this software and its documentation for any purpose and
     37  * without fee is hereby granted, provided that the above copyright
     38  * notice appear in all copies and that both that copyright notice and
     39  * this permission notice appear in supporting documentation, and that
     40  * the name of M.I.T. not be used in advertising or publicity pertaining
     41  * to distribution of the software without specific, written prior
     42  * permission.  Furthermore if you modify this software you must label
     43  * your software as modified software and not distribute it in such a
     44  * fashion that it might be confused with the original M.I.T. software.
     45  * M.I.T. makes no representations about the suitability of
     46  * this software for any purpose.  It is provided "as is" without express
     47  * or implied warranty.
     48  *
     49  *
     50  * Generate (from scratch) a Kerberos KDC database.
     51  */
     52 
     53 /*
     54  *  Yes, I know this is a hack, but we need admin.h without including the
     55  *  rpc.h header. Additionally, our rpc.h header brings in
     56  *  a des.h header which causes other problems.
     57  */
     58 #define	_RPC_RPC_H
     59 
     60 #include <stdio.h>
     61 #include <k5-int.h>
     62 #include <krb5/kdb.h>
     63 #include <kadm5/server_internal.h>
     64 #include <kadm5/admin.h>
     65 #include <rpc/types.h>
     66 #include <rpc/xdr.h>
     67 #include <libintl.h>
     68 #include "kdb5_util.h"
     69 
     70 enum ap_op {
     71     NULL_KEY,				/* setup null keys */
     72     MASTER_KEY,				/* use master key as new key */
     73     TGT_KEY				/* special handling for tgt key */
     74 };
     75 
     76 krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL };
     77 
     78 struct realm_info {
     79     krb5_deltat max_life;
     80     krb5_deltat max_rlife;
     81     krb5_timestamp expiration;
     82     krb5_flags flags;
     83     krb5_keyblock *key;
     84     krb5_int32 nkslist;
     85     krb5_key_salt_tuple *kslist;
     86 } rblock = { /* XXX */
     87     KRB5_KDB_MAX_LIFE,
     88     KRB5_KDB_MAX_RLIFE,
     89     KRB5_KDB_EXPIRATION,
     90     KRB5_KDB_DEF_FLAGS,
     91     (krb5_keyblock *) NULL,
     92     1,
     93     &def_kslist
     94 };
     95 
     96 struct iterate_args {
     97     krb5_context	ctx;
     98     struct realm_info	*rblock;
     99     krb5_db_entry	*dbentp;
    100 };
    101 
    102 static krb5_error_code add_principal
    103 	(krb5_context,
    104 	 krb5_principal,
    105 	 enum ap_op,
    106 	 struct realm_info *,
    107 		krb5_keyblock *);
    108 
    109 /*
    110  * Steps in creating a database:
    111  *
    112  * 1) use the db calls to open/create a new database
    113  *
    114  * 2) get a realm name for the new db
    115  *
    116  * 3) get a master password for the new db; convert to an encryption key.
    117  *
    118  * 4) create various required entries in the database
    119  *
    120  * 5) close & exit
    121  */
    122 
    123 extern krb5_principal master_princ;
    124 
    125 krb5_data tgt_princ_entries[] = {
    126 	{0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
    127 	{0, 0, 0} };
    128 
    129 krb5_data db_creator_entries[] = {
    130 	{0, sizeof("db_creation")-1, "db_creation"} };
    131 
    132 /* XXX knows about contents of krb5_principal, and that tgt names
    133  are of form TGT/REALM@REALM */
    134 krb5_principal_data tgt_princ = {
    135         0,					/* magic number */
    136 	{0, 0, 0},				/* krb5_data realm */
    137 	tgt_princ_entries,			/* krb5_data *data */
    138 	2,					/* int length */
    139 	KRB5_NT_SRV_INST			/* int type */
    140 };
    141 
    142 krb5_principal_data db_create_princ = {
    143         0,					/* magic number */
    144 	{0, 0, 0},				/* krb5_data realm */
    145 	db_creator_entries,			/* krb5_data *data */
    146 	1,					/* int length */
    147 	KRB5_NT_SRV_INST			/* int type */
    148 };
    149 
    150 extern char *mkey_password;
    151 
    152 extern char *progname;
    153 extern int exit_status;
    154 extern kadm5_config_params global_params;
    155 extern krb5_context util_context;
    156 
    157 void kdb5_create(argc, argv)
    158    int argc;
    159    char *argv[];
    160 {
    161     int optchar;
    162 
    163     krb5_error_code retval;
    164     char *mkey_fullname;
    165     char *pw_str = 0;
    166     unsigned int pw_size = 0;
    167     int do_stash = 0;
    168     krb5_data pwd, seed;
    169     kdb_log_context *log_ctx;
    170     krb5_keyblock mkey;
    171     krb5_data master_salt = { 0, NULL };
    172 
    173     /* Solaris Kerberos */
    174     (void) memset(&mkey, 0, sizeof (mkey));
    175 
    176 /* Solaris Kerberos */
    177 #if 0
    178     if (strrchr(argv[0], '/'))
    179 	argv[0] = strrchr(argv[0], '/')+1;
    180 #endif
    181     while ((optchar = getopt(argc, argv, "s")) != -1) {
    182 	switch(optchar) {
    183 	case 's':
    184 	    do_stash++;
    185 	    break;
    186 	case 'h':
    187 	    if (!add_db_arg("hash=true")) {
    188 		com_err(progname, ENOMEM, "while parsing command arguments\n");
    189 		exit(1);
    190 	    }
    191 	    break;
    192 	case '?':
    193 	default:
    194 	    usage();
    195 	    return;
    196 	}
    197     }
    198 
    199     rblock.max_life = global_params.max_life;
    200     rblock.max_rlife = global_params.max_rlife;
    201     rblock.expiration = global_params.expiration;
    202     rblock.flags = global_params.flags;
    203     rblock.nkslist = global_params.num_keysalts;
    204     rblock.kslist = global_params.keysalts;
    205 
    206     log_ctx = util_context->kdblog_context;
    207 
    208 /* SUNW14resync XXX */
    209 #if 0
    210     printf ("Loading random data\n");
    211     retval = krb5_c_random_os_entropy (util_context, 1, NULL);
    212     if (retval) {
    213       /* Solaris Kerberos */
    214       com_err (progname, retval, "Loading random data");
    215       exit_status++; return;
    216     }
    217 #endif
    218     /* assemble & parse the master key name */
    219 
    220     if ((retval = krb5_db_setup_mkey_name(util_context,
    221 					  global_params.mkey_name,
    222 					  global_params.realm,
    223 					  &mkey_fullname, &master_princ))) {
    224 	/* Solaris Kerberos */
    225 	com_err(progname, retval,
    226 			gettext("while setting up master key name"));
    227 	exit_status++; return;
    228     }
    229 
    230     krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm);
    231     krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm));
    232     krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm);
    233     krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm));
    234     krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm;
    235     krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm);
    236 
    237 	printf(gettext("Initializing database '%s' for realm '%s',\n"
    238 			"master key name '%s'\n"),
    239 	   global_params.dbname, global_params.realm, mkey_fullname);
    240 
    241     if (!mkey_password) {
    242 	printf(gettext("You will be prompted for the "
    243 			"database Master Password.\n"));
    244 	printf(gettext("It is important that you NOT FORGET this password.\n"));
    245 	fflush(stdout);
    246 
    247 	pw_size = 1024;
    248 	pw_str = malloc(pw_size);
    249 
    250 	retval = krb5_read_password(util_context,
    251 			    gettext("Enter KDC database master key"),
    252 			    gettext("Re-enter KDC database "
    253 				    "master key to verify"),
    254 			    pw_str, &pw_size);
    255 	if (retval) {
    256 	    /* Solaris Kerberos */
    257 	    com_err(progname, retval,
    258 		    gettext("while reading master key from keyboard"));
    259 	    exit_status++; return;
    260 	}
    261 	mkey_password = pw_str;
    262     }
    263 
    264     pwd.data = mkey_password;
    265     pwd.length = strlen(mkey_password);
    266     retval = krb5_principal2salt(util_context, master_princ, &master_salt);
    267     if (retval) {
    268 	/* Solaris Kerberos */
    269 	com_err(progname, retval,
    270 		gettext("while calculated master key salt"));
    271 	exit_status++;
    272 	goto cleanup;
    273     }
    274 
    275     retval = krb5_c_string_to_key(util_context, global_params.enctype,
    276 				  &pwd, &master_salt, &mkey);
    277     if (retval) {
    278 	/* Solaris Kerberos */
    279 	com_err(progname, retval,
    280 	    gettext("while transforming master key from password"));
    281 	exit_status++;
    282 	goto cleanup;
    283     }
    284 
    285     retval = krb5_copy_keyblock(util_context, &mkey, &rblock.key);
    286     if (retval) {
    287 	/* Solaris Kerberos */
    288 	com_err(progname, retval, gettext("while copying master key"));
    289 	exit_status++;
    290 	goto cleanup;
    291     }
    292 
    293     seed.length = mkey.length;
    294     seed.data = (char *)mkey.contents;
    295 
    296     if ((retval = krb5_c_random_seed(util_context, &seed))) {
    297 	/* Solaris Kerberos */
    298 	com_err(progname, retval,
    299 		gettext("while initializing random key generator"));
    300 	exit_status++;
    301 	goto cleanup;
    302     }
    303     if ((retval = krb5_db_create(util_context, db5util_db_args))) {
    304 	/* Solaris Kerberos */
    305 	com_err(progname, retval,
    306 		gettext("while creating database '%s'"),
    307 		global_params.dbname);
    308 	exit_status++;
    309 	goto cleanup;
    310     }
    311 #if 0 /************** Begin IFDEF'ed OUT *******************************/
    312     if (retval = krb5_db_fini(util_context)) {
    313 	/* Solaris Kerberos */
    314 	com_err(progname, retval,
    315 		gettext("while closing current database"));
    316 	exit_status++;
    317 	goto cleanup;
    318     }
    319     if ((retval = krb5_db_set_name(util_context, global_params.dbname))) {
    320 	/* Solaris Kerberos */
    321 	com_err(progname, retval,
    322 		gettext("while setting active database to '%s'"),
    323                global_params.dbname);
    324 	exit_status++;
    325 	goto cleanup;
    326     }
    327     if ((retval = krb5_db_init(util_context))) {
    328 	com_err(progname, retval,
    329 		gettext("while initializing the database '%s'"),
    330 	global_params.dbname);
    331 	exit_status++;
    332 	goto cleanup;
    333     }
    334 #endif /**************** END IFDEF'ed OUT *******************************/
    335 
    336     /* Solaris Kerberos: for iprop */
    337     if (log_ctx && log_ctx->iproprole) {
    338 	if (retval = ulog_map(util_context, &global_params, FKCOMMAND)) {
    339 		/* Solaris Kerberos */
    340 		com_err(progname, retval,
    341 			gettext("while creating update log"));
    342 		exit_status++;
    343 		goto cleanup;
    344 	}
    345 
    346 	/*
    347 	 * We're reinitializing the update log in case one already
    348 	 * existed, but this should never happen.
    349 	 */
    350 	(void) memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t));
    351 
    352 	log_ctx->ulog->kdb_hmagic = KDB_HMAGIC;
    353 	log_ctx->ulog->db_version_num = KDB_VERSION;
    354 	log_ctx->ulog->kdb_state = KDB_STABLE;
    355 	log_ctx->ulog->kdb_block = ULOG_BLOCK;
    356 
    357 	/*
    358 	 * Since we're creating a new db we shouldn't worry about
    359 	 * adding the initial principals since any slave might as well
    360 	 * do full resyncs from this newly created db.
    361 	 */
    362 	log_ctx->iproprole = IPROP_NULL;
    363     }
    364 
    365     if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock, &mkey)) ||
    366 	(retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock, &mkey))) {
    367 	(void) krb5_db_fini(util_context);
    368 	/* Solaris Kerberos */
    369 	com_err(progname, retval, gettext("while adding entries to the database"));
    370 	exit_status++;
    371 	goto cleanup;
    372     }
    373     /*
    374      * Always stash the master key so kadm5_create does not prompt for
    375      * it; delete the file below if it was not requested.  DO NOT EXIT
    376      * BEFORE DELETING THE KEYFILE if do_stash is not set.
    377      */
    378     retval = krb5_db_store_master_key(util_context,
    379 				      global_params.stash_file,
    380 				      master_princ,
    381 				      &mkey,
    382 				      mkey_password);
    383 
    384     if (retval) {
    385 	/* Solaris Kerberos */
    386 	com_err(progname, errno, gettext("while storing key"));
    387 	printf(gettext("Warning: couldn't stash master key.\n"));
    388     }
    389 
    390     if (pw_str)
    391 	memset(pw_str, 0, pw_size);
    392 
    393     if (kadm5_create(&global_params)) {
    394 	 if (!do_stash) unlink(global_params.stash_file);
    395 	 exit_status++;
    396 	 goto cleanup;
    397     }
    398     if (!do_stash) unlink(global_params.stash_file);
    399 
    400 /* Solaris Kerberos: deal with master_keyblock in better way */
    401 cleanup:
    402     if (pw_str) {
    403 	if (mkey_password == pw_str)
    404 		mkey_password = NULL;
    405 	free(pw_str);
    406     }
    407     if (master_salt.data)
    408 	free(master_salt.data);
    409     krb5_free_keyblock(util_context, rblock.key);
    410     krb5_free_keyblock_contents(util_context, &mkey);
    411     (void) krb5_db_fini(util_context);
    412 
    413     return;
    414 }
    415 
    416 static krb5_error_code
    417 tgt_keysalt_iterate(ksent, ptr)
    418     krb5_key_salt_tuple	*ksent;
    419     krb5_pointer	ptr;
    420 {
    421     krb5_context	context;
    422     krb5_error_code	kret;
    423     struct iterate_args	*iargs;
    424     krb5_keyblock	key;
    425     krb5_int32		ind;
    426     krb5_data	pwd;
    427 
    428     iargs = (struct iterate_args *) ptr;
    429     kret = 0;
    430 
    431     context = iargs->ctx;
    432 
    433     /*
    434      * Convert the master key password into a key for this particular
    435      * encryption system.
    436      */
    437     pwd.data = mkey_password;
    438     pwd.length = strlen(mkey_password);
    439     kret = krb5_c_random_seed(context, &pwd);
    440     if (kret)
    441 	return kret;
    442 
    443     if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
    444 	ind = iargs->dbentp->n_key_data-1;
    445 	if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype,
    446 					    &key))) {
    447 	    kret = krb5_dbekd_encrypt_key_data(context,
    448 					       iargs->rblock->key,
    449 					       &key,
    450 					       NULL,
    451 					       1,
    452 					       &iargs->dbentp->key_data[ind]);
    453 	    krb5_free_keyblock_contents(context, &key);
    454 	}
    455     }
    456 
    457     return(kret);
    458 }
    459 
    460 static krb5_error_code
    461 add_principal(context, princ, op, pblock, mkey)
    462     krb5_context context;
    463     krb5_principal princ;
    464     enum ap_op op;
    465     struct realm_info *pblock;
    466     krb5_keyblock *mkey;
    467 {
    468     krb5_error_code 	  retval;
    469     krb5_db_entry 	  entry;
    470 
    471     krb5_timestamp	  now;
    472     struct iterate_args	  iargs;
    473 
    474     int			  nentries = 1;
    475 
    476     memset((char *) &entry, 0, sizeof(entry));
    477 
    478     entry.len = KRB5_KDB_V1_BASE_LENGTH;
    479     entry.attributes = pblock->flags;
    480     entry.max_life = pblock->max_life;
    481     entry.max_renewable_life = pblock->max_rlife;
    482     entry.expiration = pblock->expiration;
    483 
    484     if ((retval = krb5_copy_principal(context, princ, &entry.princ)))
    485 	goto error_out;
    486 
    487     if ((retval = krb5_timeofday(context, &now)))
    488 	goto error_out;
    489 
    490     if ((retval = krb5_dbe_update_mod_princ_data(context, &entry,
    491 						 now, &db_create_princ)))
    492 	goto error_out;
    493 
    494     switch (op) {
    495     case MASTER_KEY:
    496 	if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))
    497 	    == NULL)
    498 	    goto error_out;
    499 	memset((char *) entry.key_data, 0, sizeof(krb5_key_data));
    500 	entry.n_key_data = 1;
    501 
    502 	entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
    503 	if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->key,
    504 						  mkey, NULL,
    505 						  1, entry.key_data)))
    506 	    goto error_out;
    507 	break;
    508     case TGT_KEY:
    509 	iargs.ctx = context;
    510 	iargs.rblock = pblock;
    511 	iargs.dbentp = &entry;
    512 	/*
    513 	 * Iterate through the key/salt list, ignoring salt types.
    514 	 */
    515 	if ((retval = krb5_keysalt_iterate(pblock->kslist,
    516 					   pblock->nkslist,
    517 					   1,
    518 					   tgt_keysalt_iterate,
    519 					   (krb5_pointer) &iargs)))
    520 	    return retval;
    521 	break;
    522     case NULL_KEY:
    523 	return EOPNOTSUPP;
    524     default:
    525 	break;
    526     }
    527 
    528     entry.mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
    529 	KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA |
    530 	KADM5_PRINC_EXPIRE_TIME);
    531 
    532     retval = krb5_db_put_principal(context, &entry, &nentries);
    533 
    534 error_out:;
    535     krb5_db_free_principal(context, &entry, 1);
    536     return retval;
    537 }
    538