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