1 /* 2 * Copyright 2004 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 = 98 {ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL}; 99 100 struct realm_info { 101 krb5_deltat max_life; 102 krb5_deltat max_rlife; 103 krb5_timestamp expiration; 104 krb5_flags flags; 105 krb5_keyblock *key; 106 krb5_int32 nkslist; 107 krb5_key_salt_tuple *kslist; 108 } rblock = { /* XXX */ 109 110 KRB5_KDB_MAX_LIFE, 111 KRB5_KDB_MAX_RLIFE, 112 KRB5_KDB_EXPIRATION, 113 KRB5_KDB_DEF_FLAGS, 114 (krb5_keyblock *) NULL, 115 1, 116 &def_kslist 117 }; 118 119 struct iterate_args { 120 krb5_context ctx; 121 struct realm_info *rblock; 122 krb5_db_entry *dbentp; 123 }; 124 125 static krb5_error_code add_principal(krb5_context, 126 krb5_principal, 127 enum ap_op, 128 struct realm_info *, 129 krb5_keyblock *); 130 131 /* 132 * Steps in creating a database: 133 * 134 * 1) use the db calls to open/create a new database 135 * 136 * 2) get a realm name for the new db 137 * 138 * 3) get a master password for the new db; convert to an encryption key. 139 * 140 * 4) create various required entries in the database 141 * 142 * 5) close & exit 143 */ 144 145 extern krb5_principal master_princ; 146 147 krb5_data tgt_princ_entries[] = { 148 {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, 149 {0, 0, 0} }; 150 151 krb5_data db_creator_entries[] = { 152 {0, sizeof("db_creation")-1, "db_creation"} }; 153 154 /* 155 * XXX knows about contents of krb5_principal, and that tgt names 156 * are of form TGT/REALM@REALM 157 */ 158 krb5_principal_data tgt_princ = { 159 0, /* magic number */ 160 {0, 0, 0}, /* krb5_data realm */ 161 tgt_princ_entries, /* krb5_data *data */ 162 2, /* int length */ 163 KRB5_NT_SRV_INST /* int type */ 164 }; 165 166 krb5_principal_data db_create_princ = { 167 0, /* magic number */ 168 {0, 0, 0}, /* krb5_data realm */ 169 db_creator_entries, /* krb5_data *data */ 170 1, /* int length */ 171 KRB5_NT_SRV_INST /* int type */ 172 }; 173 174 extern char *mkey_password; 175 176 extern char *progname; 177 extern int exit_status; 178 extern osa_adb_policy_t policy_db; 179 extern kadm5_config_params global_params; 180 extern krb5_context util_context; 181 182 void 183 kdb5_create(argc, argv) 184 int argc; 185 char *argv[]; 186 { 187 int optchar; 188 189 krb5_error_code retval; 190 char *mkey_fullname; 191 char *pw_str = 0; 192 unsigned int pw_size = 0; 193 int do_stash = 0; 194 krb5_int32 crflags = KRB5_KDB_CREATE_BTREE; 195 krb5_data pwd, seed; 196 kdb_log_context *log_ctx; 197 krb5_keyblock mkey; 198 krb5_data master_salt = { 0, NULL }; 199 200 if (strrchr(argv[0], '/')) 201 argv[0] = strrchr(argv[0], '/')+1; 202 203 while ((optchar = getopt(argc, argv, "s")) != -1) { 204 switch(optchar) { 205 case 's': 206 do_stash++; 207 break; 208 case 'h': 209 crflags = KRB5_KDB_CREATE_HASH; 210 case '?': 211 default: 212 usage(); 213 return; 214 } 215 } 216 217 rblock.max_life = global_params.max_life; 218 rblock.max_rlife = global_params.max_rlife; 219 rblock.expiration = global_params.expiration; 220 rblock.flags = global_params.flags; 221 rblock.nkslist = global_params.num_keysalts; 222 rblock.kslist = global_params.keysalts; 223 224 log_ctx = util_context->kdblog_context; 225 226 retval = krb5_db_set_name(util_context, global_params.dbname); 227 if (!retval) 228 retval = EEXIST; 229 230 if (retval == EEXIST || retval == EACCES || retval == EPERM) { 231 /* it exists ! */ 232 com_err(argv[0], 0, 233 gettext("The database '%s' appears to already exist"), 234 global_params.dbname); 235 exit_status++; 236 return; 237 } 238 /* assemble & parse the master key name */ 239 240 if ((retval = krb5_db_setup_mkey_name(util_context, 241 global_params.mkey_name, 242 global_params.realm, 243 &mkey_fullname, &master_princ))) { 244 com_err(argv[0], retval, 245 gettext("while setting up master key name")); 246 exit_status++; 247 return; 248 } 249 krb5_princ_set_realm_data(util_context, 250 &db_create_princ, global_params.realm); 251 krb5_princ_set_realm_length(util_context, 252 &db_create_princ, 253 strlen(global_params.realm)); 254 krb5_princ_set_realm_data(util_context, 255 &tgt_princ, global_params.realm); 256 krb5_princ_set_realm_length(util_context, 257 &tgt_princ, strlen(global_params.realm)); 258 krb5_princ_component(util_context, &tgt_princ, 1)->data = 259 global_params.realm; 260 krb5_princ_component(util_context, &tgt_princ, 1)->length = 261 strlen(global_params.realm); 262 263 printf(gettext("Initializing database '%s' for realm '%s',\n" 264 "master key name '%s'\n"), 265 global_params.dbname, global_params.realm, mkey_fullname); 266 267 if (!mkey_password) { 268 printf(gettext("You will be prompted for the " 269 "database Master Password.\n")); 270 printf(gettext("It is important that you NOT FORGET this password.\n")); 271 fflush(stdout); 272 273 pw_size = 1024; 274 pw_str = malloc(pw_size); 275 276 retval = krb5_read_password(util_context, 277 gettext("Enter KDC database master key:"), 278 gettext("Re-enter KDC database " 279 "master key to verify:"), 280 pw_str, &pw_size); 281 if (retval) { 282 com_err(argv[0], retval, 283 gettext("while reading master key from keyboard")); 284 exit_status++; 285 return; 286 } 287 mkey_password = pw_str; 288 } 289 290 pwd.data = mkey_password; 291 pwd.length = strlen(mkey_password); 292 293 retval = krb5_principal2salt(util_context, master_princ, &master_salt); 294 if (retval) { 295 com_err(argv[0], retval, 296 gettext("while calculated master key salt")); 297 exit_status++; 298 goto cleanup; 299 } 300 301 if (retval = krb5_c_string_to_key(util_context, global_params.enctype, 302 &pwd, &master_salt, &mkey)) { 303 com_err(argv[0], retval, 304 gettext("while transforming master key from password")); 305 exit_status++; 306 goto cleanup; 307 } 308 309 retval = krb5_copy_keyblock(util_context, &mkey, &rblock.key); 310 if (retval) { 311 com_err(argv[0], retval, gettext("while copying master key")); 312 exit_status++; 313 goto cleanup; 314 } 315 316 seed.length = mkey.length; 317 seed.data = (char *)mkey.contents; 318 319 if ((retval = krb5_c_random_seed(util_context, &seed))) { 320 com_err(argv[0], retval, 321 gettext("while initializing random key generator")); 322 exit_status++; 323 goto cleanup; 324 } 325 if ((retval = krb5_db_create(util_context, 326 global_params.dbname, crflags))) { 327 com_err(argv[0], retval, 328 gettext("while creating database '%s'"), 329 global_params.dbname); 330 exit_status++; 331 goto cleanup; 332 } 333 if (retval = krb5_db_fini(util_context)) { 334 com_err(argv[0], retval, 335 gettext("while closing current database")); 336 exit_status++; 337 goto cleanup; 338 } 339 if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { 340 com_err(argv[0], retval, 341 gettext("while setting active database to '%s'"), 342 global_params.dbname); 343 exit_status++; 344 goto cleanup; 345 } 346 if ((retval = krb5_db_init(util_context))) { 347 com_err(argv[0], retval, 348 gettext("while initializing the database '%s'"), 349 global_params.dbname); 350 exit_status++; 351 goto cleanup; 352 } 353 354 if (log_ctx && log_ctx->iproprole) { 355 if (retval = ulog_map(util_context, &global_params, FKCOMMAND)) { 356 com_err(argv[0], retval, 357 gettext("while creating update log")); 358 exit_status++; 359 goto cleanup; 360 } 361 362 /* 363 * We're reinitializing the update log in case one already 364 * existed, but this should never happen. 365 */ 366 (void) memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t)); 367 368 log_ctx->ulog->kdb_hmagic = KDB_HMAGIC; 369 log_ctx->ulog->db_version_num = KDB_VERSION; 370 log_ctx->ulog->kdb_state = KDB_STABLE; 371 log_ctx->ulog->kdb_block = ULOG_BLOCK; 372 373 /* 374 * Since we're creating a new db we shouldn't worry about 375 * adding the initial principals since any slave might as well 376 * do full resyncs from this newly created db. 377 */ 378 log_ctx->iproprole = IPROP_NULL; 379 } 380 381 if ((retval = add_principal(util_context, 382 master_princ, MASTER_KEY, &rblock, &mkey)) || 383 (retval = add_principal(util_context, 384 &tgt_princ, TGT_KEY, &rblock, &mkey))) { 385 (void) krb5_db_fini(util_context); 386 com_err(argv[0], retval, 387 gettext("while adding entries to the database")); 388 exit_status++; 389 goto cleanup; 390 } 391 /* 392 * Always stash the master key so kadm5_create does not prompt for 393 * it; delete the file below if it was not requested. DO NOT EXIT 394 * BEFORE DELETING THE KEYFILE if do_stash is not set. 395 */ 396 if (retval = krb5_db_store_mkey(util_context, 397 global_params.stash_file, 398 master_princ, 399 &mkey)) { 400 com_err(argv[0], errno, gettext("while storing key")); 401 printf(gettext("Warning: couldn't stash master key.\n")); 402 } 403 404 if (pw_str) 405 memset(pw_str, 0, pw_size); 406 407 if (kadm5_create(&global_params)) { 408 if (!do_stash) 409 unlink(global_params.stash_file); 410 exit_status++; 411 goto cleanup; 412 } 413 if (!do_stash) 414 unlink(global_params.stash_file); 415 416 cleanup: 417 if (pw_str) { 418 if (mkey_password == pw_str) 419 mkey_password = NULL; 420 free(pw_str); 421 } 422 if (master_salt.data) 423 free(master_salt.data); 424 krb5_free_keyblock_contents(util_context, rblock.key); 425 krb5_free_keyblock_contents(util_context, &mkey); 426 (void) krb5_db_fini(util_context); 427 428 return; 429 430 } 431 432 static krb5_error_code 433 tgt_keysalt_iterate(ksent, ptr) 434 krb5_key_salt_tuple *ksent; 435 krb5_pointer ptr; 436 { 437 krb5_context context; 438 krb5_error_code kret; 439 struct iterate_args *iargs; 440 krb5_keyblock key; 441 krb5_int32 ind; 442 krb5_pointer rseed; 443 krb5_data pwd; 444 445 iargs = (struct iterate_args *) ptr; 446 kret = 0; 447 448 context = iargs->ctx; 449 450 /* 451 * Convert the master key password into a key for this particular 452 * encryption system. 453 */ 454 pwd.data = mkey_password; 455 pwd.length = strlen(mkey_password); 456 if (kret = krb5_c_random_seed(context, &pwd)) 457 return kret; 458 459 if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { 460 ind = iargs->dbentp->n_key_data-1; 461 if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype, 462 &key))) { 463 kret = krb5_dbekd_encrypt_key_data(context, 464 iargs->rblock->key, 465 &key, 466 NULL, 467 1, 468 &iargs->dbentp->key_data[ind]); 469 krb5_free_keyblock_contents(context, &key); 470 } 471 } 472 473 return(kret); 474 } 475 476 static krb5_error_code 477 add_principal(krb5_context context, 478 krb5_principal princ, 479 enum ap_op op, 480 struct realm_info *pblock, 481 krb5_keyblock *mkey) 482 { 483 krb5_error_code retval; 484 krb5_db_entry entry; 485 486 krb5_timestamp now; 487 struct iterate_args iargs; 488 489 int nentries = 1; 490 491 memset((char *) &entry, 0, sizeof(entry)); 492 493 entry.len = KRB5_KDB_V1_BASE_LENGTH; 494 entry.attributes = pblock->flags; 495 entry.max_life = pblock->max_life; 496 entry.max_renewable_life = pblock->max_rlife; 497 entry.expiration = pblock->expiration; 498 499 if ((retval = krb5_copy_principal(context, princ, &entry.princ))) 500 goto error_out; 501 502 if ((retval = krb5_timeofday(context, &now))) 503 goto error_out; 504 505 if ((retval = krb5_dbe_update_mod_princ_data(context, &entry, 506 now, &db_create_princ))) 507 goto error_out; 508 509 switch (op) { 510 case MASTER_KEY: 511 entry.key_data = (krb5_key_data *) malloc(sizeof (krb5_key_data)); 512 if (entry.key_data == NULL) 513 goto error_out; 514 515 memset((char *) entry.key_data, 0, sizeof(krb5_key_data)); 516 entry.n_key_data = 1; 517 518 entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 519 if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->key, 520 mkey, NULL, 1, entry.key_data))) 521 goto error_out; 522 break; 523 case TGT_KEY: 524 iargs.ctx = context; 525 iargs.rblock = pblock; 526 iargs.dbentp = &entry; 527 /* 528 * Iterate through the key/salt list, ignoring salt types. 529 */ 530 if ((retval = krb5_keysalt_iterate(pblock->kslist, 531 pblock->nkslist, 532 1, 533 tgt_keysalt_iterate, 534 (krb5_pointer) &iargs))) 535 return (retval); 536 break; 537 case NULL_KEY: 538 return (EOPNOTSUPP); 539 default: 540 break; 541 } 542 543 retval = krb5_db_put_principal(context, &entry, &nentries); 544 545 error_out:; 546 krb5_dbe_free_contents(context, &entry); 547 return (retval); 548 } 549