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 143 kdb5_create(int argc, char *argv[]) 144 { 145 int optchar; 146 147 krb5_error_code retval; 148 char *pw_str = 0; 149 unsigned int pw_size = 0; 150 int do_stash = 0; 151 krb5_data pwd, seed; 152 kdb_log_context *log_ctx; 153 krb5_kvno mkey_kvno; 154 155 while ((optchar = getopt(argc, argv, "sW")) != -1) { 156 switch(optchar) { 157 case 's': 158 do_stash++; 159 break; 160 case 'W': 161 /* Ignore (deprecated weak random option). */ 162 break; 163 case '?': 164 default: 165 usage(); 166 return; 167 } 168 } 169 170 rblock.max_life = global_params.max_life; 171 rblock.max_rlife = global_params.max_rlife; 172 rblock.expiration = global_params.expiration; 173 rblock.flags = global_params.flags; 174 rblock.nkslist = global_params.num_keysalts; 175 rblock.kslist = global_params.keysalts; 176 177 log_ctx = util_context->kdblog_context; 178 179 /* assemble & parse the master key name */ 180 181 if ((retval = krb5_db_setup_mkey_name(util_context, 182 global_params.mkey_name, 183 global_params.realm, 184 &mkey_fullname, &master_princ))) { 185 com_err(progname, retval, _("while setting up master key name")); 186 exit_status++; return; 187 } 188 189 krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm); 190 krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm)); 191 krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm); 192 krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm)); 193 krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm; 194 krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm); 195 196 printf(_("Initializing database '%s' for realm '%s',\n" 197 "master key name '%s'\n"), 198 global_params.dbname, global_params.realm, mkey_fullname); 199 200 if (!mkey_password) { 201 printf(_("You will be prompted for the database Master Password.\n")); 202 printf(_("It is important that you NOT FORGET this password.\n")); 203 fflush(stdout); 204 205 pw_size = 1024; 206 pw_str = malloc(pw_size); 207 if (pw_str == NULL) { 208 com_err(progname, ENOMEM, _("while creating new master key")); 209 exit_status++; return; 210 } 211 212 retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, 213 pw_str, &pw_size); 214 if (retval) { 215 com_err(progname, retval, 216 _("while reading master key from keyboard")); 217 exit_status++; return; 218 } 219 mkey_password = pw_str; 220 } 221 222 pwd.data = mkey_password; 223 pwd.length = strlen(mkey_password); 224 retval = krb5_principal2salt(util_context, master_princ, &master_salt); 225 if (retval) { 226 com_err(progname, retval, _("while calculating master key salt")); 227 exit_status++; return; 228 } 229 230 retval = krb5_c_string_to_key(util_context, master_keyblock.enctype, 231 &pwd, &master_salt, &master_keyblock); 232 if (retval) { 233 com_err(progname, retval, 234 _("while transforming master key from password")); 235 exit_status++; return; 236 } 237 238 rblock.key = &master_keyblock; 239 240 seed = make_data(master_keyblock.contents, master_keyblock.length); 241 242 if ((retval = krb5_c_random_seed(util_context, &seed))) { 243 com_err(progname, retval, 244 _("while initializing random key generator")); 245 exit_status++; return; 246 } 247 if ((retval = krb5_db_create(util_context, 248 db5util_db_args))) { 249 com_err(progname, retval, _("while creating database '%s'"), 250 global_params.dbname); 251 exit_status++; return; 252 } 253 /* if ((retval = krb5_db_fini(util_context))) { */ 254 /* com_err(progname, retval, "while closing current database"); */ 255 /* exit_status++; return; */ 256 /* } */ 257 /* if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW))) { */ 258 /* com_err(progname, retval, "while initializing the database '%s'", */ 259 /* global_params.dbname); */ 260 /* exit_status++; return; */ 261 /* } */ 262 263 if (log_ctx && log_ctx->iproprole) { 264 retval = ulog_map(util_context, global_params.iprop_logfile, 265 global_params.iprop_ulogsize); 266 if (retval) { 267 com_err(argv[0], retval, _("while creating update log")); 268 exit_status++; 269 return; 270 } 271 272 /* 273 * We're reinitializing the update log in case one already 274 * existed, but this should never happen. 275 */ 276 retval = ulog_init_header(util_context); 277 if (retval) { 278 com_err(argv[0], retval, _("while initializing update log")); 279 exit_status++; 280 return; 281 } 282 283 /* 284 * Since we're creating a new db we shouldn't worry about 285 * adding the initial principals since any replica might as 286 * well do full resyncs from this newly created db. 287 */ 288 log_ctx->iproprole = IPROP_NULL; 289 } 290 291 if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) || 292 (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) { 293 com_err(progname, retval, _("while adding entries to the database")); 294 exit_status++; return; 295 } 296 297 298 299 /* 300 * Always stash the master key so kadm5_create does not prompt for 301 * it; delete the file below if it was not requested. DO NOT EXIT 302 * BEFORE DELETING THE KEYFILE if do_stash is not set. 303 */ 304 305 /* 306 * Determine the kvno to use, it must be that used to create the master key 307 * princ. 308 */ 309 if (global_params.mask & KADM5_CONFIG_KVNO) 310 mkey_kvno = global_params.kvno; /* user specified */ 311 else 312 mkey_kvno = 1; /* Default */ 313 314 retval = krb5_db_store_master_key(util_context, 315 global_params.stash_file, 316 master_princ, 317 mkey_kvno, 318 &master_keyblock, 319 mkey_password); 320 if (retval) { 321 com_err(progname, retval, _("while storing key")); 322 printf(_("Warning: couldn't stash master key.\n")); 323 } 324 /* clean up */ 325 zapfree(pw_str, pw_size); 326 free(master_salt.data); 327 328 if (kadm5_create(&global_params)) { 329 if (!do_stash) unlink(global_params.stash_file); 330 exit_status++; 331 return; 332 } 333 if (!do_stash) unlink(global_params.stash_file); 334 335 return; 336 } 337 338 static krb5_error_code 339 tgt_keysalt_iterate(krb5_key_salt_tuple *ksent, krb5_pointer ptr) 340 { 341 krb5_context context; 342 krb5_error_code kret; 343 struct iterate_args *iargs; 344 krb5_keyblock key; 345 krb5_int32 ind; 346 krb5_data pwd; 347 348 iargs = (struct iterate_args *) ptr; 349 kret = 0; 350 351 context = iargs->ctx; 352 353 /* 354 * Convert the master key password into a key for this particular 355 * encryption system. 356 */ 357 pwd.data = mkey_password; 358 pwd.length = strlen(mkey_password); 359 kret = krb5_c_random_seed(context, &pwd); 360 if (kret) 361 return kret; 362 363 if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { 364 ind = iargs->dbentp->n_key_data-1; 365 if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype, 366 &key))) { 367 kret = krb5_dbe_encrypt_key_data(context, iargs->rblock->key, 368 &key, NULL, 1, 369 &iargs->dbentp->key_data[ind]); 370 krb5_free_keyblock_contents(context, &key); 371 } 372 } 373 374 return(kret); 375 } 376 377 static krb5_error_code 378 add_principal(krb5_context context, krb5_principal princ, enum ap_op op, 379 struct realm_info *pblock) 380 { 381 krb5_error_code retval; 382 krb5_db_entry *entry = NULL; 383 krb5_kvno mkey_kvno; 384 krb5_timestamp now; 385 struct iterate_args iargs; 386 krb5_actkvno_node actkvno; 387 388 entry = calloc(1, sizeof(*entry)); 389 if (entry == NULL) 390 return ENOMEM; 391 392 entry->len = KRB5_KDB_V1_BASE_LENGTH; 393 entry->attributes = pblock->flags; 394 entry->max_life = pblock->max_life; 395 entry->max_renewable_life = pblock->max_rlife; 396 entry->expiration = pblock->expiration; 397 398 if ((retval = krb5_copy_principal(context, princ, &entry->princ))) 399 goto cleanup; 400 401 if ((retval = krb5_timeofday(context, &now))) 402 goto cleanup; 403 404 if ((retval = krb5_dbe_update_mod_princ_data(context, entry, 405 now, &db_create_princ))) 406 goto cleanup; 407 408 switch (op) { 409 case MASTER_KEY: 410 if ((entry->key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) 411 == NULL) 412 goto cleanup; 413 memset(entry->key_data, 0, sizeof(krb5_key_data)); 414 entry->n_key_data = 1; 415 416 if (global_params.mask & KADM5_CONFIG_KVNO) 417 mkey_kvno = global_params.kvno; /* user specified */ 418 else 419 mkey_kvno = 1; /* Default */ 420 entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 421 if ((retval = krb5_dbe_encrypt_key_data(context, pblock->key, 422 &master_keyblock, NULL, 423 mkey_kvno, entry->key_data))) 424 goto cleanup; 425 /* 426 * There should always be at least one "active" mkey so creating the 427 * KRB5_TL_ACTKVNO entry now so the initial mkey is active. 428 */ 429 actkvno.next = NULL; 430 actkvno.act_kvno = mkey_kvno; 431 /* earliest possible time in case system clock is set back */ 432 actkvno.act_time = 0; 433 if ((retval = krb5_dbe_update_actkvno(context, entry, &actkvno))) 434 goto cleanup; 435 436 /* so getprinc shows the right kvno */ 437 if ((retval = krb5_dbe_update_mkvno(context, entry, mkey_kvno))) 438 goto cleanup; 439 440 break; 441 case TGT_KEY: 442 iargs.ctx = context; 443 iargs.rblock = pblock; 444 iargs.dbentp = entry; 445 /* 446 * Iterate through the key/salt list, ignoring salt types. 447 */ 448 if ((retval = krb5_keysalt_iterate(pblock->kslist, 449 pblock->nkslist, 450 1, 451 tgt_keysalt_iterate, 452 (krb5_pointer) &iargs))) 453 goto cleanup; 454 break; 455 case NULL_KEY: 456 retval = EOPNOTSUPP; 457 goto cleanup; 458 default: 459 break; 460 } 461 462 entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | 463 KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA | 464 KADM5_PRINC_EXPIRE_TIME); 465 entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS; 466 467 retval = krb5_db_put_principal(context, entry); 468 469 cleanup: 470 krb5_db_free_principal(context, entry); 471 return retval; 472 } 473