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