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