1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kadmin/dbutil/kdb5_create.c - Create a KDC database */ 3 /* 4 * Copyright 1990,1991,2001, 2002, 2008 by the Massachusetts Institute of 5 * Technology. All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 /* 27 * Copyright (C) 1998 by the FundsXpress, INC. 28 * 29 * All rights reserved. 30 * 31 * Export of this software from the United States of America may require 32 * a specific license from the United States Government. It is the 33 * responsibility of any person or organization contemplating export to 34 * obtain such a license before exporting. 35 * 36 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 37 * distribute this software and its documentation for any purpose and 38 * without fee is hereby granted, provided that the above copyright 39 * notice appear in all copies and that both that copyright notice and 40 * this permission notice appear in supporting documentation, and that 41 * the name of FundsXpress. not be used in advertising or publicity pertaining 42 * to distribution of the software without specific, written prior 43 * permission. FundsXpress makes no representations about the suitability of 44 * this software for any purpose. It is provided "as is" without express 45 * or implied warranty. 46 * 47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 50 */ 51 /* 52 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 53 * Use is subject to license terms. 54 */ 55 56 #include <k5-int.h> 57 #include <kdb.h> 58 #include <kadm5/server_internal.h> 59 #include <kadm5/admin.h> 60 #include <adm_proto.h> 61 #include "kdb5_util.h" 62 63 enum ap_op { 64 NULL_KEY, /* setup null keys */ 65 MASTER_KEY, /* use master key as new key */ 66 TGT_KEY /* special handling for tgt key */ 67 }; 68 69 struct realm_info { 70 krb5_deltat max_life; 71 krb5_deltat max_rlife; 72 krb5_timestamp expiration; 73 krb5_flags flags; 74 krb5_keyblock *key; 75 krb5_int32 nkslist; 76 krb5_key_salt_tuple *kslist; 77 } rblock; 78 79 struct iterate_args { 80 krb5_context ctx; 81 struct realm_info *rblock; 82 krb5_db_entry *dbentp; 83 }; 84 85 static krb5_error_code add_principal 86 (krb5_context, 87 krb5_principal, 88 enum ap_op, 89 struct realm_info *); 90 91 /* 92 * Steps in creating a database: 93 * 94 * 1) use the db calls to open/create a new database 95 * 96 * 2) get a realm name for the new db 97 * 98 * 3) get a master password for the new db; convert to an encryption key. 99 * 100 * 4) create various required entries in the database 101 * 102 * 5) close & exit 103 */ 104 105 extern krb5_keyblock master_keyblock; 106 extern krb5_principal master_princ; 107 extern char *mkey_fullname; 108 krb5_data master_salt; 109 110 krb5_data tgt_princ_entries[] = { 111 {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, 112 {0, 0, 0} }; 113 114 krb5_data db_creator_entries[] = { 115 {0, sizeof("db_creation")-1, "db_creation"} }; 116 117 /* XXX knows about contents of krb5_principal, and that tgt names 118 are of form TGT/REALM@REALM */ 119 krb5_principal_data tgt_princ = { 120 0, /* magic number */ 121 {0, 0, 0}, /* krb5_data realm */ 122 tgt_princ_entries, /* krb5_data *data */ 123 2, /* int length */ 124 KRB5_NT_SRV_INST /* int type */ 125 }; 126 127 krb5_principal_data db_create_princ = { 128 0, /* magic number */ 129 {0, 0, 0}, /* krb5_data realm */ 130 db_creator_entries, /* krb5_data *data */ 131 1, /* int length */ 132 KRB5_NT_SRV_INST /* int type */ 133 }; 134 135 extern char *mkey_password; 136 137 extern char *progname; 138 extern int exit_status; 139 extern kadm5_config_params global_params; 140 extern krb5_context util_context; 141 142 void kdb5_create(argc, argv) 143 int argc; 144 char *argv[]; 145 { 146 int optchar; 147 148 krb5_error_code retval; 149 char *pw_str = 0; 150 unsigned int pw_size = 0; 151 int do_stash = 0; 152 krb5_data pwd, seed; 153 kdb_log_context *log_ctx; 154 krb5_kvno mkey_kvno; 155 156 while ((optchar = getopt(argc, argv, "sW")) != -1) { 157 switch(optchar) { 158 case 's': 159 do_stash++; 160 break; 161 case 'W': 162 /* Ignore (deprecated weak random option). */ 163 break; 164 case '?': 165 default: 166 usage(); 167 return; 168 } 169 } 170 171 rblock.max_life = global_params.max_life; 172 rblock.max_rlife = global_params.max_rlife; 173 rblock.expiration = global_params.expiration; 174 rblock.flags = global_params.flags; 175 rblock.nkslist = global_params.num_keysalts; 176 rblock.kslist = global_params.keysalts; 177 178 log_ctx = util_context->kdblog_context; 179 180 /* assemble & parse the master key name */ 181 182 if ((retval = krb5_db_setup_mkey_name(util_context, 183 global_params.mkey_name, 184 global_params.realm, 185 &mkey_fullname, &master_princ))) { 186 com_err(progname, retval, _("while setting up master key name")); 187 exit_status++; return; 188 } 189 190 krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm); 191 krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm)); 192 krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm); 193 krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm)); 194 krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm; 195 krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm); 196 197 printf(_("Initializing database '%s' for realm '%s',\n" 198 "master key name '%s'\n"), 199 global_params.dbname, global_params.realm, mkey_fullname); 200 201 if (!mkey_password) { 202 printf(_("You will be prompted for the database Master Password.\n")); 203 printf(_("It is important that you NOT FORGET this password.\n")); 204 fflush(stdout); 205 206 pw_size = 1024; 207 pw_str = malloc(pw_size); 208 if (pw_str == NULL) { 209 com_err(progname, ENOMEM, _("while creating new master key")); 210 exit_status++; return; 211 } 212 213 retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, 214 pw_str, &pw_size); 215 if (retval) { 216 com_err(progname, retval, 217 _("while reading master key from keyboard")); 218 exit_status++; return; 219 } 220 mkey_password = pw_str; 221 } 222 223 pwd.data = mkey_password; 224 pwd.length = strlen(mkey_password); 225 retval = krb5_principal2salt(util_context, master_princ, &master_salt); 226 if (retval) { 227 com_err(progname, retval, _("while calculating master key salt")); 228 exit_status++; return; 229 } 230 231 retval = krb5_c_string_to_key(util_context, master_keyblock.enctype, 232 &pwd, &master_salt, &master_keyblock); 233 if (retval) { 234 com_err(progname, retval, 235 _("while transforming master key from password")); 236 exit_status++; return; 237 } 238 239 rblock.key = &master_keyblock; 240 241 seed = make_data(master_keyblock.contents, master_keyblock.length); 242 243 if ((retval = krb5_c_random_seed(util_context, &seed))) { 244 com_err(progname, retval, 245 _("while initializing random key generator")); 246 exit_status++; return; 247 } 248 if ((retval = krb5_db_create(util_context, 249 db5util_db_args))) { 250 com_err(progname, retval, _("while creating database '%s'"), 251 global_params.dbname); 252 exit_status++; return; 253 } 254 /* if ((retval = krb5_db_fini(util_context))) { */ 255 /* com_err(progname, retval, "while closing current database"); */ 256 /* exit_status++; return; */ 257 /* } */ 258 /* if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW))) { */ 259 /* com_err(progname, retval, "while initializing the database '%s'", */ 260 /* global_params.dbname); */ 261 /* exit_status++; return; */ 262 /* } */ 263 264 if (log_ctx && log_ctx->iproprole) { 265 retval = ulog_map(util_context, global_params.iprop_logfile, 266 global_params.iprop_ulogsize); 267 if (retval) { 268 com_err(argv[0], retval, _("while creating update log")); 269 exit_status++; 270 return; 271 } 272 273 /* 274 * We're reinitializing the update log in case one already 275 * existed, but this should never happen. 276 */ 277 retval = ulog_init_header(util_context); 278 if (retval) { 279 com_err(argv[0], retval, _("while initializing update log")); 280 exit_status++; 281 return; 282 } 283 284 /* 285 * Since we're creating a new db we shouldn't worry about 286 * adding the initial principals since any replica might as 287 * well do full resyncs from this newly created db. 288 */ 289 log_ctx->iproprole = IPROP_NULL; 290 } 291 292 if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) || 293 (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) { 294 com_err(progname, retval, _("while adding entries to the database")); 295 exit_status++; return; 296 } 297 298 299 300 /* 301 * Always stash the master key so kadm5_create does not prompt for 302 * it; delete the file below if it was not requested. DO NOT EXIT 303 * BEFORE DELETING THE KEYFILE if do_stash is not set. 304 */ 305 306 /* 307 * Determine the kvno to use, it must be that used to create the master key 308 * princ. 309 */ 310 if (global_params.mask & KADM5_CONFIG_KVNO) 311 mkey_kvno = global_params.kvno; /* user specified */ 312 else 313 mkey_kvno = 1; /* Default */ 314 315 retval = krb5_db_store_master_key(util_context, 316 global_params.stash_file, 317 master_princ, 318 mkey_kvno, 319 &master_keyblock, 320 mkey_password); 321 if (retval) { 322 com_err(progname, retval, _("while storing key")); 323 printf(_("Warning: couldn't stash master key.\n")); 324 } 325 /* clean up */ 326 zapfree(pw_str, pw_size); 327 free(master_salt.data); 328 329 if (kadm5_create(&global_params)) { 330 if (!do_stash) unlink(global_params.stash_file); 331 exit_status++; 332 return; 333 } 334 if (!do_stash) unlink(global_params.stash_file); 335 336 return; 337 } 338 339 static krb5_error_code 340 tgt_keysalt_iterate(ksent, ptr) 341 krb5_key_salt_tuple *ksent; 342 krb5_pointer ptr; 343 { 344 krb5_context context; 345 krb5_error_code kret; 346 struct iterate_args *iargs; 347 krb5_keyblock key; 348 krb5_int32 ind; 349 krb5_data pwd; 350 351 iargs = (struct iterate_args *) ptr; 352 kret = 0; 353 354 context = iargs->ctx; 355 356 /* 357 * Convert the master key password into a key for this particular 358 * encryption system. 359 */ 360 pwd.data = mkey_password; 361 pwd.length = strlen(mkey_password); 362 kret = krb5_c_random_seed(context, &pwd); 363 if (kret) 364 return kret; 365 366 if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { 367 ind = iargs->dbentp->n_key_data-1; 368 if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype, 369 &key))) { 370 kret = krb5_dbe_encrypt_key_data(context, iargs->rblock->key, 371 &key, NULL, 1, 372 &iargs->dbentp->key_data[ind]); 373 krb5_free_keyblock_contents(context, &key); 374 } 375 } 376 377 return(kret); 378 } 379 380 static krb5_error_code 381 add_principal(context, princ, op, pblock) 382 krb5_context context; 383 krb5_principal princ; 384 enum ap_op op; 385 struct realm_info *pblock; 386 { 387 krb5_error_code retval; 388 krb5_db_entry *entry = NULL; 389 krb5_kvno mkey_kvno; 390 krb5_timestamp now; 391 struct iterate_args iargs; 392 krb5_actkvno_node actkvno; 393 394 entry = calloc(1, sizeof(*entry)); 395 if (entry == NULL) 396 return ENOMEM; 397 398 entry->len = KRB5_KDB_V1_BASE_LENGTH; 399 entry->attributes = pblock->flags; 400 entry->max_life = pblock->max_life; 401 entry->max_renewable_life = pblock->max_rlife; 402 entry->expiration = pblock->expiration; 403 404 if ((retval = krb5_copy_principal(context, princ, &entry->princ))) 405 goto cleanup; 406 407 if ((retval = krb5_timeofday(context, &now))) 408 goto cleanup; 409 410 if ((retval = krb5_dbe_update_mod_princ_data(context, entry, 411 now, &db_create_princ))) 412 goto cleanup; 413 414 switch (op) { 415 case MASTER_KEY: 416 if ((entry->key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) 417 == NULL) 418 goto cleanup; 419 memset(entry->key_data, 0, sizeof(krb5_key_data)); 420 entry->n_key_data = 1; 421 422 if (global_params.mask & KADM5_CONFIG_KVNO) 423 mkey_kvno = global_params.kvno; /* user specified */ 424 else 425 mkey_kvno = 1; /* Default */ 426 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 427 if ((retval = krb5_dbe_encrypt_key_data(context, pblock->key, 428 &master_keyblock, NULL, 429 mkey_kvno, entry->key_data))) 430 goto cleanup; 431 /* 432 * There should always be at least one "active" mkey so creating the 433 * KRB5_TL_ACTKVNO entry now so the initial mkey is active. 434 */ 435 actkvno.next = NULL; 436 actkvno.act_kvno = mkey_kvno; 437 /* earliest possible time in case system clock is set back */ 438 actkvno.act_time = 0; 439 if ((retval = krb5_dbe_update_actkvno(context, entry, &actkvno))) 440 goto cleanup; 441 442 /* so getprinc shows the right kvno */ 443 if ((retval = krb5_dbe_update_mkvno(context, entry, mkey_kvno))) 444 goto cleanup; 445 446 break; 447 case TGT_KEY: 448 iargs.ctx = context; 449 iargs.rblock = pblock; 450 iargs.dbentp = entry; 451 /* 452 * Iterate through the key/salt list, ignoring salt types. 453 */ 454 if ((retval = krb5_keysalt_iterate(pblock->kslist, 455 pblock->nkslist, 456 1, 457 tgt_keysalt_iterate, 458 (krb5_pointer) &iargs))) 459 goto cleanup; 460 break; 461 case NULL_KEY: 462 retval = EOPNOTSUPP; 463 goto cleanup; 464 default: 465 break; 466 } 467 468 entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | 469 KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA | 470 KADM5_PRINC_EXPIRE_TIME); 471 entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS; 472 473 retval = krb5_db_put_principal(context, entry); 474 475 cleanup: 476 krb5_db_free_principal(context, entry); 477 return retval; 478 } 479