1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 4 * Use is subject to license terms. 5 */ 6 7 #include <k5-int.h> 8 #include <kdb.h> 9 #include <kadm5/server_internal.h> 10 #include <kadm5/admin.h> 11 #include <adm_proto.h> 12 #include "kdb5_util.h" 13 #include <time.h> 14 #include "k5-regex.h" 15 16 extern krb5_keyblock master_keyblock; /* current mkey */ 17 extern krb5_kvno master_kvno; 18 extern krb5_principal master_princ; 19 extern krb5_data master_salt; 20 extern char *mkey_fullname; 21 extern char *mkey_password; 22 extern char *progname; 23 extern int exit_status; 24 extern kadm5_config_params global_params; 25 extern krb5_context util_context; 26 extern time_t get_date(char *); 27 28 static const char * 29 strdate(krb5_timestamp when) 30 { 31 struct tm *tm; 32 static char out[40]; 33 time_t lcltim = ts2tt(when); 34 35 tm = localtime(&lcltim); 36 if (tm == NULL || 37 strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm) == 0) 38 strlcpy(out, "(error)", sizeof(out)); 39 return out; 40 } 41 42 krb5_kvno 43 get_next_kvno(krb5_context context, krb5_db_entry *entry) 44 { 45 krb5_kvno new_kvno; 46 47 new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data, 48 entry->key_data); 49 new_kvno++; 50 /* deal with wrapping */ 51 if (new_kvno == 0) 52 new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */ 53 54 return (new_kvno); 55 } 56 57 krb5_error_code 58 add_new_mkey(krb5_context context, krb5_db_entry *master_entry, 59 krb5_keyblock *new_mkey, krb5_kvno use_mkvno) 60 { 61 krb5_error_code retval = 0; 62 int old_key_data_count, i; 63 krb5_kvno new_mkey_kvno; 64 krb5_key_data tmp_key_data; 65 krb5_mkey_aux_node *mkey_aux_data_head = NULL, **mkey_aux_data; 66 krb5_keylist_node *keylist_node; 67 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(context); 68 69 /* do this before modifying master_entry key_data */ 70 new_mkey_kvno = get_next_kvno(context, master_entry); 71 /* verify the requested mkvno if not 0 is the one that would be used here. */ 72 if (use_mkvno != 0 && new_mkey_kvno != use_mkvno) 73 return (KRB5_KDB_KVNONOMATCH); 74 75 old_key_data_count = master_entry->n_key_data; 76 77 /* alloc enough space to hold new and existing key_data */ 78 /* 79 * The encrypted key is malloc'ed by krb5_dbe_encrypt_key_data and 80 * krb5_key_data key_data_contents is a pointer to this key. Using some 81 * logic from master_key_convert(). 82 */ 83 for (i = 0; i < master_entry->n_key_data; i++) 84 krb5_free_key_data_contents(context, &master_entry->key_data[i]); 85 free(master_entry->key_data); 86 master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * 87 (old_key_data_count + 1)); 88 if (master_entry->key_data == NULL) 89 return (ENOMEM); 90 91 memset(master_entry->key_data, 0, 92 sizeof(krb5_key_data) * (old_key_data_count + 1)); 93 master_entry->n_key_data = old_key_data_count + 1; 94 95 /* Note, mkey does not have salt */ 96 /* add new mkey encrypted with itself to mkey princ entry */ 97 if ((retval = krb5_dbe_encrypt_key_data(context, new_mkey, new_mkey, NULL, 98 (int) new_mkey_kvno, 99 &master_entry->key_data[0]))) { 100 return (retval); 101 } 102 /* the mvkno should be that of the newest mkey */ 103 if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) { 104 krb5_free_key_data_contents(context, &master_entry->key_data[0]); 105 return (retval); 106 } 107 /* 108 * Need to decrypt old keys with the current mkey which is in the global 109 * master_keyblock and encrypt those keys with the latest mkey. And while 110 * the old keys are being decrypted, use those to create the 111 * KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of 112 * the older mkeys. 113 * 114 * The new mkey is followed by existing keys. 115 * 116 * First, set up for creating a krb5_mkey_aux_node list which will be used 117 * to update the mkey aux data for the mkey princ entry. 118 */ 119 mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node)); 120 if (mkey_aux_data_head == NULL) { 121 retval = ENOMEM; 122 goto clean_n_exit; 123 } 124 memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node)); 125 mkey_aux_data = &mkey_aux_data_head; 126 127 for (keylist_node = master_keylist, i = 1; keylist_node != NULL; 128 keylist_node = keylist_node->next, i++) { 129 130 /* 131 * Create a list of krb5_mkey_aux_node nodes. One node contains the new 132 * mkey encrypted by an old mkey and the old mkey's kvno (one node per 133 * old mkey). 134 */ 135 if (*mkey_aux_data == NULL) { 136 /* *mkey_aux_data points to next field of previous node */ 137 *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node)); 138 if (*mkey_aux_data == NULL) { 139 retval = ENOMEM; 140 goto clean_n_exit; 141 } 142 memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node)); 143 } 144 145 memset(&tmp_key_data, 0, sizeof(tmp_key_data)); 146 /* encrypt the new mkey with the older mkey */ 147 retval = krb5_dbe_encrypt_key_data(context, &keylist_node->keyblock, 148 new_mkey, NULL, (int) new_mkey_kvno, 149 &tmp_key_data); 150 if (retval) 151 goto clean_n_exit; 152 153 (*mkey_aux_data)->latest_mkey = tmp_key_data; 154 (*mkey_aux_data)->mkey_kvno = keylist_node->kvno; 155 mkey_aux_data = &((*mkey_aux_data)->next); 156 157 /* 158 * Store old key in master_entry keydata past the new mkey 159 */ 160 retval = krb5_dbe_encrypt_key_data(context, new_mkey, 161 &keylist_node->keyblock, 162 NULL, (int) keylist_node->kvno, 163 &master_entry->key_data[i]); 164 if (retval) 165 goto clean_n_exit; 166 } 167 assert(i == old_key_data_count + 1); 168 169 if ((retval = krb5_dbe_update_mkey_aux(context, master_entry, 170 mkey_aux_data_head))) { 171 goto clean_n_exit; 172 } 173 master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA; 174 175 clean_n_exit: 176 krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head); 177 return (retval); 178 } 179 180 void 181 kdb5_add_mkey(int argc, char *argv[]) 182 { 183 int optchar; 184 krb5_error_code retval; 185 char *pw_str = 0; 186 unsigned int pw_size = 0; 187 int do_stash = 0; 188 krb5_data pwd; 189 krb5_kvno new_mkey_kvno; 190 krb5_keyblock new_mkeyblock; 191 krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN; 192 char *new_mkey_password; 193 krb5_db_entry *master_entry = NULL; 194 krb5_timestamp now; 195 196 /* 197 * The command table entry for this command causes open_db_and_mkey() to be 198 * called first to open the KDB and get the current mkey. 199 */ 200 201 memset(&new_mkeyblock, 0, sizeof(new_mkeyblock)); 202 master_salt.data = NULL; 203 204 while ((optchar = getopt(argc, argv, "e:s")) != -1) { 205 switch(optchar) { 206 case 'e': 207 if (krb5_string_to_enctype(optarg, &new_master_enctype)) { 208 com_err(progname, EINVAL, _("%s is an invalid enctype"), 209 optarg); 210 exit_status++; 211 return; 212 } 213 break; 214 case 's': 215 do_stash++; 216 break; 217 case '?': 218 default: 219 usage(); 220 return; 221 } 222 } 223 224 if (new_master_enctype == ENCTYPE_UNKNOWN) 225 new_master_enctype = global_params.enctype; 226 227 retval = krb5_db_get_principal(util_context, master_princ, 0, 228 &master_entry); 229 if (retval != 0) { 230 com_err(progname, retval, _("while getting master key principal %s"), 231 mkey_fullname); 232 exit_status++; 233 goto cleanup_return; 234 } 235 236 printf(_("Creating new master key for master key principal '%s'\n"), 237 mkey_fullname); 238 239 printf(_("You will be prompted for a new database Master Password.\n")); 240 printf(_("It is important that you NOT FORGET this password.\n")); 241 fflush(stdout); 242 243 pw_size = 1024; 244 pw_str = malloc(pw_size); 245 if (pw_str == NULL) { 246 com_err(progname, ENOMEM, _("while creating new master key")); 247 exit_status++; 248 goto cleanup_return; 249 } 250 251 retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, 252 pw_str, &pw_size); 253 if (retval) { 254 com_err(progname, retval, 255 _("while reading new master key from keyboard")); 256 exit_status++; 257 goto cleanup_return; 258 } 259 new_mkey_password = pw_str; 260 261 pwd.data = new_mkey_password; 262 pwd.length = strlen(new_mkey_password); 263 retval = krb5_principal2salt(util_context, master_princ, &master_salt); 264 if (retval) { 265 com_err(progname, retval, _("while calculating master key salt")); 266 exit_status++; 267 goto cleanup_return; 268 } 269 270 retval = krb5_c_string_to_key(util_context, new_master_enctype, 271 &pwd, &master_salt, &new_mkeyblock); 272 if (retval) { 273 com_err(progname, retval, 274 _("while transforming master key from password")); 275 exit_status++; 276 goto cleanup_return; 277 } 278 279 new_mkey_kvno = get_next_kvno(util_context, master_entry); 280 retval = add_new_mkey(util_context, master_entry, &new_mkeyblock, 281 new_mkey_kvno); 282 if (retval) { 283 com_err(progname, retval, 284 _("adding new master key to master principal")); 285 exit_status++; 286 goto cleanup_return; 287 } 288 289 if ((retval = krb5_timeofday(util_context, &now))) { 290 com_err(progname, retval, _("while getting current time")); 291 exit_status++; 292 goto cleanup_return; 293 } 294 295 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry, 296 now, master_princ))) { 297 com_err(progname, retval, _("while updating the master key principal " 298 "modification time")); 299 exit_status++; 300 goto cleanup_return; 301 } 302 303 if ((retval = krb5_db_put_principal(util_context, master_entry))) { 304 com_err(progname, retval, _("while adding master key entry to the " 305 "database")); 306 exit_status++; 307 goto cleanup_return; 308 } 309 310 if (do_stash) { 311 retval = krb5_db_store_master_key(util_context, 312 global_params.stash_file, 313 master_princ, 314 new_mkey_kvno, 315 &new_mkeyblock, 316 mkey_password); 317 if (retval) { 318 com_err(progname, retval, _("while storing key")); 319 printf(_("Warning: couldn't stash master key.\n")); 320 } 321 } 322 323 cleanup_return: 324 /* clean up */ 325 krb5_db_free_principal(util_context, master_entry); 326 zap((char *)new_mkeyblock.contents, new_mkeyblock.length); 327 free(new_mkeyblock.contents); 328 if (pw_str) { 329 zap(pw_str, pw_size); 330 free(pw_str); 331 } 332 free(master_salt.data); 333 return; 334 } 335 336 void 337 kdb5_use_mkey(int argc, char *argv[]) 338 { 339 krb5_error_code retval; 340 krb5_kvno use_kvno; 341 krb5_timestamp now, start_time; 342 krb5_actkvno_node *actkvno_list = NULL, *new_actkvno = NULL, 343 *prev_actkvno, *cur_actkvno; 344 krb5_db_entry *master_entry = NULL; 345 krb5_keylist_node *keylist_node; 346 krb5_boolean inserted = FALSE; 347 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context); 348 349 if (argc < 2 || argc > 3) { 350 /* usage calls exit */ 351 usage(); 352 } 353 354 use_kvno = atoi(argv[1]); 355 if (use_kvno == 0) { 356 com_err(progname, EINVAL, _("0 is an invalid KVNO value")); 357 exit_status++; 358 return; 359 } else { 360 /* verify use_kvno is valid */ 361 for (keylist_node = master_keylist; keylist_node != NULL; 362 keylist_node = keylist_node->next) { 363 if (use_kvno == keylist_node->kvno) 364 break; 365 } 366 if (!keylist_node) { 367 com_err(progname, EINVAL, _("%d is an invalid KVNO value"), 368 use_kvno); 369 exit_status++; 370 return; 371 } 372 } 373 374 if ((retval = krb5_timeofday(util_context, &now))) { 375 com_err(progname, retval, _("while getting current time")); 376 exit_status++; 377 return; 378 } 379 380 if (argc == 3) { 381 time_t t = get_date(argv[2]); 382 if (t == -1) { 383 com_err(progname, 0, _("could not parse date-time string '%s'"), 384 argv[2]); 385 exit_status++; 386 return; 387 } else 388 start_time = (krb5_timestamp) t; 389 } else { 390 start_time = now; 391 } 392 393 /* 394 * Need to: 395 * 396 * 1. get mkey princ 397 * 2. get krb5_actkvno_node list 398 * 3. add use_kvno to actkvno list (sorted in right spot) 399 * 4. update mkey princ's tl data 400 * 5. put mkey princ. 401 */ 402 403 retval = krb5_db_get_principal(util_context, master_princ, 0, 404 &master_entry); 405 if (retval != 0) { 406 com_err(progname, retval, _("while getting master key principal %s"), 407 mkey_fullname); 408 exit_status++; 409 goto cleanup_return; 410 } 411 412 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); 413 if (retval != 0) { 414 com_err(progname, retval, 415 _("while looking up active version of master key")); 416 exit_status++; 417 goto cleanup_return; 418 } 419 420 /* 421 * If an entry already exists with the same kvno either delete it or if it's 422 * the only entry, just set its active time. 423 */ 424 for (prev_actkvno = NULL, cur_actkvno = actkvno_list; 425 cur_actkvno != NULL; 426 prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) { 427 428 if (cur_actkvno->act_kvno == use_kvno) { 429 /* delete it */ 430 if (prev_actkvno) { 431 prev_actkvno->next = cur_actkvno->next; 432 cur_actkvno->next = NULL; 433 krb5_dbe_free_actkvno_list(util_context, cur_actkvno); 434 } else { 435 if (cur_actkvno->next) { 436 /* delete it from front of list */ 437 actkvno_list = cur_actkvno->next; 438 cur_actkvno->next = NULL; 439 krb5_dbe_free_actkvno_list(util_context, cur_actkvno); 440 } else { 441 /* There's only one entry, go ahead and change the time */ 442 cur_actkvno->act_time = start_time; 443 inserted = TRUE; 444 } 445 } 446 break; 447 } 448 } 449 450 if (!inserted) { 451 /* alloc enough space to hold new and existing key_data */ 452 new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node)); 453 if (new_actkvno == NULL) { 454 com_err(progname, ENOMEM, _("while adding new master key")); 455 exit_status++; 456 goto cleanup_return; 457 } 458 memset(new_actkvno, 0, sizeof(krb5_actkvno_node)); 459 new_actkvno->act_kvno = use_kvno; 460 new_actkvno->act_time = start_time; 461 462 /* insert new act kvno node */ 463 464 if (actkvno_list == NULL) { 465 /* new actkvno is the list */ 466 actkvno_list = new_actkvno; 467 } else { 468 for (prev_actkvno = NULL, cur_actkvno = actkvno_list; 469 cur_actkvno != NULL; 470 prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) { 471 472 if (ts_after(cur_actkvno->act_time, new_actkvno->act_time)) { 473 if (prev_actkvno) { 474 prev_actkvno->next = new_actkvno; 475 new_actkvno->next = cur_actkvno; 476 } else { 477 new_actkvno->next = actkvno_list; 478 actkvno_list = new_actkvno; 479 } 480 break; 481 } else if (cur_actkvno->next == NULL) { 482 /* end of line, just add new node to end of list */ 483 cur_actkvno->next = new_actkvno; 484 break; 485 } 486 } 487 } 488 } 489 490 if (ts_after(actkvno_list->act_time, now)) { 491 com_err(progname, EINVAL, 492 _("there must be one master key currently active")); 493 exit_status++; 494 goto cleanup_return; 495 } 496 497 if ((retval = krb5_dbe_update_actkvno(util_context, master_entry, 498 actkvno_list))) { 499 com_err(progname, retval, 500 _("while updating actkvno data for master principal entry")); 501 exit_status++; 502 goto cleanup_return; 503 } 504 505 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry, 506 now, master_princ))) { 507 com_err(progname, retval, _("while updating the master key principal " 508 "modification time")); 509 exit_status++; 510 goto cleanup_return; 511 } 512 513 master_entry->mask |= KADM5_TL_DATA; 514 515 if ((retval = krb5_db_put_principal(util_context, master_entry))) { 516 com_err(progname, retval, 517 _("while adding master key entry to the database")); 518 exit_status++; 519 goto cleanup_return; 520 } 521 522 cleanup_return: 523 /* clean up */ 524 krb5_db_free_principal(util_context, master_entry); 525 krb5_dbe_free_actkvno_list(util_context, actkvno_list); 526 return; 527 } 528 529 void 530 kdb5_list_mkeys(int argc, char *argv[]) 531 { 532 krb5_error_code retval; 533 char *output_str = NULL, enctype[BUFSIZ]; 534 krb5_kvno act_kvno; 535 krb5_timestamp act_time; 536 krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno; 537 krb5_db_entry *master_entry = NULL; 538 krb5_keylist_node *cur_kb_node; 539 krb5_keyblock *act_mkey; 540 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context); 541 542 if (master_keylist == NULL) { 543 com_err(progname, 0, _("master keylist not initialized")); 544 exit_status++; 545 return; 546 } 547 548 retval = krb5_db_get_principal(util_context, master_princ, 0, 549 &master_entry); 550 if (retval != 0) { 551 com_err(progname, retval, _("while getting master key principal %s"), 552 mkey_fullname); 553 exit_status++; 554 goto cleanup_return; 555 } 556 557 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); 558 if (retval != 0) { 559 com_err(progname, retval, _("while looking up active kvno list")); 560 exit_status++; 561 goto cleanup_return; 562 } 563 564 retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &act_kvno, 565 &act_mkey); 566 if (retval != 0) { 567 com_err(progname, retval, _("while looking up active master key")); 568 exit_status++; 569 goto cleanup_return; 570 } 571 572 printf("Master keys for Principal: %s\n", mkey_fullname); 573 574 for (cur_kb_node = master_keylist; cur_kb_node != NULL; 575 cur_kb_node = cur_kb_node->next) { 576 577 if ((retval = krb5_enctype_to_name(cur_kb_node->keyblock.enctype, 578 FALSE, enctype, sizeof(enctype)))) { 579 com_err(progname, retval, _("while getting enctype description")); 580 exit_status++; 581 goto cleanup_return; 582 } 583 584 act_time = -1; /* assume actkvno entry not found */ 585 for (cur_actkvno = actkvno_list; cur_actkvno != NULL; 586 cur_actkvno = cur_actkvno->next) { 587 if (cur_actkvno->act_kvno == cur_kb_node->kvno) { 588 act_time = cur_actkvno->act_time; 589 break; 590 } 591 } 592 593 if (cur_kb_node->kvno == act_kvno) { 594 /* * indicates kvno is currently active */ 595 retval = asprintf(&output_str, 596 _("KVNO: %d, Enctype: %s, Active on: %s *\n"), 597 cur_kb_node->kvno, enctype, strdate(act_time)); 598 } else { 599 if (act_time != -1) { 600 retval = asprintf(&output_str, 601 _("KVNO: %d, Enctype: %s, Active on: %s\n"), 602 cur_kb_node->kvno, enctype, strdate(act_time)); 603 } else { 604 retval = asprintf(&output_str, 605 _("KVNO: %d, Enctype: %s, No activate time " 606 "set\n"), cur_kb_node->kvno, enctype); 607 } 608 } 609 if (retval == -1) { 610 com_err(progname, ENOMEM, _("asprintf could not allocate enough " 611 "memory to hold output")); 612 exit_status++; 613 goto cleanup_return; 614 } 615 printf("%s", output_str); 616 free(output_str); 617 output_str = NULL; 618 } 619 620 cleanup_return: 621 /* clean up */ 622 krb5_db_free_principal(util_context, master_entry); 623 free(output_str); 624 krb5_dbe_free_actkvno_list(util_context, actkvno_list); 625 return; 626 } 627 628 struct update_enc_mkvno { 629 unsigned int re_match_count; 630 unsigned int already_current; 631 unsigned int updated; 632 unsigned int dry_run : 1; 633 unsigned int verbose : 1; 634 regex_t preg; 635 }; 636 637 /* XXX Duplicated in libkadm5srv! */ 638 /* 639 * Function: glob_to_regexp 640 * 641 * Arguments: 642 * 643 * glob (r) the shell-style glob (?*[]) to convert 644 * realm (r) the default realm to append, or NULL 645 * regexp (w) the ed-style regexp created from glob 646 * 647 * Effects: 648 * 649 * regexp is filled in with allocated memory containing a regular 650 * expression that matches what the shell-style glob would match. 651 * If glob does not contain an "@" character and realm is not 652 * NULL, "@*" is appended to the regexp. 653 * 654 * Conversion algorithm: 655 * 656 * quoted characters are copied quoted 657 * ? is converted to . 658 * * is converted to .* 659 * active characters are quoted: ^, $, . 660 * [ and ] are active but supported and have the same meaning, so 661 * they are copied 662 * other characters are copied 663 * regexp is anchored with ^ and $ 664 */ 665 static int glob_to_regexp(char *glob, char *realm, char **regexp) 666 { 667 int append_realm; 668 char *p; 669 670 /* validate the glob */ 671 if (glob[strlen(glob)-1] == '\\') 672 return EINVAL; 673 674 /* A character of glob can turn into two in regexp, plus ^ and $ */ 675 /* and trailing null. If glob has no @, also allocate space for */ 676 /* the realm. */ 677 append_realm = (realm != NULL) && (strchr(glob, '@') == NULL); 678 p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0)); 679 if (p == NULL) 680 return ENOMEM; 681 *regexp = p; 682 683 *p++ = '^'; 684 while (*glob) { 685 switch (*glob) { 686 case '?': 687 *p++ = '.'; 688 break; 689 case '*': 690 *p++ = '.'; 691 *p++ = '*'; 692 break; 693 case '.': 694 case '^': 695 case '$': 696 *p++ = '\\'; 697 *p++ = *glob; 698 break; 699 case '\\': 700 *p++ = '\\'; 701 *p++ = *++glob; 702 break; 703 default: 704 *p++ = *glob; 705 break; 706 } 707 glob++; 708 } 709 710 if (append_realm) { 711 *p++ = '@'; 712 *p++ = '.'; 713 *p++ = '*'; 714 } 715 716 *p++ = '$'; 717 *p++ = '\0'; 718 return 0; 719 } 720 721 static int 722 update_princ_encryption_1(void *cb, krb5_db_entry *ent) 723 { 724 struct update_enc_mkvno *p = cb; 725 char *pname = 0; 726 krb5_error_code retval; 727 krb5_timestamp now; 728 int result; 729 krb5_kvno old_mkvno; 730 731 retval = krb5_unparse_name(util_context, ent->princ, &pname); 732 if (retval) { 733 com_err(progname, retval, 734 _("getting string representation of principal name")); 735 goto fail; 736 } 737 738 if (krb5_principal_compare(util_context, ent->princ, master_princ)) { 739 goto skip; 740 } 741 742 if (regexec(&p->preg, pname, 0, NULL, 0) != 0) 743 goto skip; 744 p->re_match_count++; 745 retval = krb5_dbe_get_mkvno(util_context, ent, &old_mkvno); 746 if (retval) { 747 com_err(progname, retval, 748 _("determining master key used for principal '%s'"), pname); 749 goto fail; 750 } 751 /* Line up "skip" and "update" messages for viewing. */ 752 if (old_mkvno == new_mkvno) { 753 if (p->dry_run && p->verbose) 754 printf(_("would skip: %s\n"), pname); 755 else if (p->verbose) 756 printf(_("skipping: %s\n"), pname); 757 p->already_current++; 758 goto skip; 759 } 760 if (p->dry_run) { 761 if (p->verbose) 762 printf(_("would update: %s\n"), pname); 763 p->updated++; 764 goto skip; 765 } else if (p->verbose) 766 printf(_("updating: %s\n"), pname); 767 retval = master_key_convert (util_context, ent); 768 if (retval) { 769 com_err(progname, retval, 770 _("error re-encrypting key for principal '%s'"), pname); 771 goto fail; 772 } 773 if ((retval = krb5_timeofday(util_context, &now))) { 774 com_err(progname, retval, _("while getting current time")); 775 goto fail; 776 } 777 778 if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent, 779 now, master_princ))) { 780 com_err(progname, retval, 781 _("while updating principal '%s' modification time"), pname); 782 goto fail; 783 } 784 785 ent->mask |= KADM5_KEY_DATA | KADM5_TL_DATA; 786 787 if ((retval = krb5_db_put_principal(util_context, ent))) { 788 com_err(progname, retval, _("while updating principal '%s' key data " 789 "in the database"), pname); 790 goto fail; 791 } 792 p->updated++; 793 skip: 794 result = 0; 795 goto egress; 796 fail: 797 exit_status++; 798 result = 1; 799 egress: 800 if (pname) 801 krb5_free_unparsed_name(util_context, pname); 802 return result; 803 } 804 805 extern int are_you_sure (const char *, ...) 806 #if !defined(__cplusplus) && (__GNUC__ > 2) 807 __attribute__((__format__(__printf__, 1, 2))) 808 #endif 809 ; 810 811 int 812 are_you_sure (const char *format, ...) 813 { 814 va_list va; 815 char ansbuf[100]; 816 817 va_start(va, format); 818 vprintf(format, va); 819 va_end(va); 820 printf(_("\n(type 'yes' to confirm)? ")); 821 fflush(stdout); 822 if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL) 823 return 0; 824 if (strcmp(ansbuf, "yes\n")) 825 return 0; 826 return 1; 827 } 828 829 void 830 kdb5_update_princ_encryption(int argc, char *argv[]) 831 { 832 struct update_enc_mkvno data = { 0 }; 833 char *name_pattern = NULL; 834 int force = 0; 835 int optchar; 836 krb5_error_code retval; 837 krb5_actkvno_node *actkvno_list = 0; 838 krb5_db_entry *master_entry = NULL; 839 char *regexp = NULL; 840 krb5_keyblock *act_mkey; 841 krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context); 842 krb5_flags iterflags = 0; 843 844 while ((optchar = getopt(argc, argv, "fnv")) != -1) { 845 switch (optchar) { 846 case 'f': 847 force = 1; 848 break; 849 case 'n': 850 data.dry_run = 1; 851 break; 852 case 'v': 853 data.verbose = 1; 854 break; 855 case '?': 856 case ':': 857 default: 858 usage(); 859 } 860 } 861 if (argv[optind] != NULL) { 862 name_pattern = argv[optind]; 863 if (argv[optind+1] != NULL) 864 usage(); 865 } 866 867 if (master_keylist == NULL) { 868 com_err(progname, 0, _("master keylist not initialized")); 869 exit_status++; 870 goto cleanup; 871 } 872 873 /* The glob_to_regexp code only cares if the "realm" parameter is 874 NULL or not; the string data is irrelevant. */ 875 if (name_pattern == NULL) 876 name_pattern = "*"; 877 if (glob_to_regexp(name_pattern, "hi", ®exp) != 0) { 878 com_err(progname, ENOMEM, 879 _("converting glob pattern '%s' to regular expression"), 880 name_pattern); 881 exit_status++; 882 goto cleanup; 883 } 884 885 if (regcomp(&data.preg, regexp, REG_NOSUB) != 0) { 886 /* XXX syslog msg or regerr(regerrno) */ 887 com_err(progname, 0, _("error compiling converted regexp '%s'"), 888 regexp); 889 exit_status++; 890 goto cleanup; 891 } 892 893 retval = krb5_db_get_principal(util_context, master_princ, 0, 894 &master_entry); 895 if (retval != 0) { 896 com_err(progname, retval, _("while getting master key principal %s"), 897 mkey_fullname); 898 exit_status++; 899 goto cleanup; 900 } 901 902 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); 903 if (retval != 0) { 904 com_err(progname, retval, _("while looking up active kvno list")); 905 exit_status++; 906 goto cleanup; 907 } 908 909 retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &new_mkvno, 910 &act_mkey); 911 if (retval) { 912 com_err(progname, retval, _("while looking up active master key")); 913 exit_status++; 914 goto cleanup; 915 } 916 new_master_keyblock = *act_mkey; 917 918 if (!force && 919 !data.dry_run && 920 !are_you_sure(_("Re-encrypt all keys not using master key vno %u?"), 921 new_mkvno)) { 922 printf(_("OK, doing nothing.\n")); 923 exit_status++; 924 goto cleanup; 925 } 926 if (data.verbose) { 927 if (data.dry_run) { 928 printf(_("Principals whose keys WOULD BE re-encrypted to master " 929 "key vno %u:\n"), new_mkvno); 930 } else { 931 printf(_("Principals whose keys are being re-encrypted to master " 932 "key vno %u if necessary:\n"), new_mkvno); 933 } 934 } 935 936 if (!data.dry_run) { 937 /* Grab a write lock so we don't have to upgrade to a write lock and 938 * reopen the DB while iterating. */ 939 iterflags = KRB5_DB_ITER_WRITE; 940 } 941 942 retval = krb5_db_iterate(util_context, name_pattern, 943 update_princ_encryption_1, &data, iterflags); 944 /* If exit_status is set, then update_princ_encryption_1 already 945 printed a message. */ 946 if (retval != 0 && exit_status == 0) { 947 com_err(progname, retval, _("trying to process principal database")); 948 exit_status++; 949 } 950 if (data.dry_run) { 951 printf(_("%u principals processed: %u would be updated, %u already " 952 "current\n"), 953 data.re_match_count, data.updated, data.already_current); 954 } else { 955 printf(_("%u principals processed: %u updated, %u already current\n"), 956 data.re_match_count, data.updated, data.already_current); 957 } 958 959 cleanup: 960 krb5_db_free_principal(util_context, master_entry); 961 free(regexp); 962 regfree(&data.preg); 963 memset(&new_master_keyblock, 0, sizeof(new_master_keyblock)); 964 krb5_dbe_free_actkvno_list(util_context, actkvno_list); 965 } 966 967 struct kvnos_in_use { 968 krb5_kvno kvno; 969 unsigned int use_count; 970 }; 971 972 struct purge_args { 973 krb5_context kcontext; 974 struct kvnos_in_use *kvnos; 975 unsigned int num_kvnos; 976 }; 977 978 static krb5_error_code 979 find_mkvnos_in_use(krb5_pointer ptr, 980 krb5_db_entry *entry) 981 { 982 krb5_error_code retval; 983 struct purge_args * args; 984 unsigned int i; 985 krb5_kvno mkvno; 986 987 args = (struct purge_args *) ptr; 988 989 retval = krb5_dbe_get_mkvno(args->kcontext, entry, &mkvno); 990 if (retval) 991 return (retval); 992 993 for (i = 0; i < args->num_kvnos; i++) { 994 if (args->kvnos[i].kvno == mkvno) { 995 /* XXX do I need to worry about use_count wrapping? */ 996 args->kvnos[i].use_count++; 997 break; 998 } 999 } 1000 return 0; 1001 } 1002 1003 void 1004 kdb5_purge_mkeys(int argc, char *argv[]) 1005 { 1006 int optchar; 1007 krb5_error_code retval; 1008 krb5_timestamp now; 1009 krb5_db_entry *master_entry = NULL; 1010 krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE; 1011 struct purge_args args; 1012 char buf[5]; 1013 unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged; 1014 unsigned int old_key_data_count; 1015 krb5_actkvno_node *actkvno_list = NULL, *actkvno_entry, *prev_actkvno_entry; 1016 krb5_mkey_aux_node *mkey_aux_list = NULL, *mkey_aux_entry, *prev_mkey_aux_entry; 1017 krb5_key_data *old_key_data; 1018 1019 /* 1020 * Verify that the master key list has been initialized before doing 1021 * anything else. 1022 */ 1023 if (krb5_db_mkey_list_alias(util_context) == NULL) { 1024 com_err(progname, KRB5_KDB_DBNOTINITED, 1025 _("master keylist not initialized")); 1026 exit_status++; 1027 return; 1028 } 1029 1030 memset(&args, 0, sizeof(args)); 1031 1032 optind = 1; 1033 while ((optchar = getopt(argc, argv, "fnv")) != -1) { 1034 switch(optchar) { 1035 case 'f': 1036 force = TRUE; 1037 break; 1038 case 'n': 1039 dry_run = TRUE; /* mkey princ will not be modified */ 1040 force = TRUE; /* implied */ 1041 break; 1042 case 'v': 1043 verbose = TRUE; 1044 break; 1045 case '?': 1046 default: 1047 usage(); 1048 return; 1049 } 1050 } 1051 1052 retval = krb5_db_get_principal(util_context, master_princ, 0, 1053 &master_entry); 1054 if (retval != 0) { 1055 com_err(progname, retval, _("while getting master key principal %s"), 1056 mkey_fullname); 1057 exit_status++; 1058 goto cleanup_return; 1059 } 1060 1061 if (!force) { 1062 printf(_("Will purge all unused master keys stored in the '%s' " 1063 "principal, are you sure?\n"), mkey_fullname); 1064 printf(_("(type 'yes' to confirm)? ")); 1065 if (fgets(buf, sizeof(buf), stdin) == NULL) { 1066 exit_status++; 1067 goto cleanup_return; 1068 } 1069 if (strcmp(buf, "yes\n")) { 1070 exit_status++; 1071 goto cleanup_return; 1072 } 1073 printf(_("OK, purging unused master keys from '%s'...\n"), 1074 mkey_fullname); 1075 } 1076 1077 /* save the old keydata */ 1078 old_key_data_count = master_entry->n_key_data; 1079 if (old_key_data_count == 1) { 1080 if (verbose) 1081 printf(_("There is only one master key which can not be " 1082 "purged.\n")); 1083 goto cleanup_return; 1084 } 1085 old_key_data = master_entry->key_data; 1086 1087 args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count); 1088 if (args.kvnos == NULL) { 1089 retval = ENOMEM; 1090 com_err(progname, ENOMEM, _("while allocating args.kvnos")); 1091 exit_status++; 1092 goto cleanup_return; 1093 } 1094 memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count); 1095 args.num_kvnos = old_key_data_count; 1096 args.kcontext = util_context; 1097 1098 /* populate the kvnos array with all the current mkvnos */ 1099 for (i = 0; i < old_key_data_count; i++) 1100 args.kvnos[i].kvno = master_entry->key_data[i].key_data_kvno; 1101 1102 if ((retval = krb5_db_iterate(util_context, 1103 NULL, 1104 find_mkvnos_in_use, 1105 (krb5_pointer) &args, 0))) { 1106 com_err(progname, retval, _("while finding master keys in use")); 1107 exit_status++; 1108 goto cleanup_return; 1109 } 1110 /* 1111 * args.kvnos has been marked with the mkvno's that are currently protecting 1112 * princ entries 1113 */ 1114 if (dry_run) { 1115 printf(_("Would purge the following master key(s) from %s:\n"), 1116 mkey_fullname); 1117 } else { 1118 printf(_("Purging the following master key(s) from %s:\n"), 1119 mkey_fullname); 1120 } 1121 1122 /* find # of keys still in use or print out verbose info */ 1123 for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) { 1124 if (args.kvnos[i].use_count > 0) { 1125 num_kvnos_inuse++; 1126 } else { 1127 /* this key would be deleted */ 1128 if (args.kvnos[i].kvno == master_kvno) { 1129 com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT, 1130 _("master key stash file needs updating, command " 1131 "aborting")); 1132 exit_status++; 1133 goto cleanup_return; 1134 } 1135 num_kvnos_purged++; 1136 printf(_("KVNO: %d\n"), args.kvnos[i].kvno); 1137 } 1138 } 1139 /* didn't find any keys to purge */ 1140 if (num_kvnos_inuse == args.num_kvnos) { 1141 printf(_("All keys in use, nothing purged.\n")); 1142 goto cleanup_return; 1143 } 1144 if (dry_run) { 1145 /* bail before doing anything else */ 1146 printf(_("%d key(s) would be purged.\n"), num_kvnos_purged); 1147 goto cleanup_return; 1148 } 1149 1150 retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); 1151 if (retval != 0) { 1152 com_err(progname, retval, _("while looking up active kvno list")); 1153 exit_status++; 1154 goto cleanup_return; 1155 } 1156 1157 retval = krb5_dbe_lookup_mkey_aux(util_context, master_entry, &mkey_aux_list); 1158 if (retval != 0) { 1159 com_err(progname, retval, _("while looking up mkey aux data list")); 1160 exit_status++; 1161 goto cleanup_return; 1162 } 1163 1164 master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse); 1165 if (master_entry->key_data == NULL) { 1166 retval = ENOMEM; 1167 com_err(progname, ENOMEM, _("while allocating key_data")); 1168 exit_status++; 1169 goto cleanup_return; 1170 } 1171 memset(master_entry->key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse); 1172 master_entry->n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */ 1173 1174 /* 1175 * Assuming that the latest mkey will not be purged because it will always 1176 * be "in use" so this code will not bother with encrypting keys again. 1177 */ 1178 for (i = k = 0; i < old_key_data_count; i++) { 1179 for (j = 0; j < args.num_kvnos; j++) { 1180 if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) { 1181 if (args.kvnos[j].use_count != 0) { 1182 master_entry->key_data[k++] = old_key_data[i]; 1183 memset(&old_key_data[i], 0, sizeof(old_key_data[i])); 1184 break; 1185 } else { 1186 /* remove unused mkey */ 1187 /* adjust the actkno data */ 1188 for (prev_actkvno_entry = actkvno_entry = actkvno_list; 1189 actkvno_entry != NULL; 1190 actkvno_entry = actkvno_entry->next) { 1191 1192 if (actkvno_entry->act_kvno == args.kvnos[j].kvno) { 1193 if (actkvno_entry == actkvno_list) { 1194 /* remove from head */ 1195 actkvno_list = actkvno_entry->next; 1196 } else if (actkvno_entry->next == NULL) { 1197 /* remove from tail */ 1198 prev_actkvno_entry->next = NULL; 1199 } else { 1200 /* remove in between */ 1201 prev_actkvno_entry->next = actkvno_entry->next; 1202 } 1203 actkvno_entry->next = NULL; 1204 krb5_dbe_free_actkvno_list(util_context, actkvno_entry); 1205 break; /* deleted entry, no need to loop further */ 1206 } else { 1207 prev_actkvno_entry = actkvno_entry; 1208 } 1209 } 1210 /* adjust the mkey aux data */ 1211 for (prev_mkey_aux_entry = mkey_aux_entry = mkey_aux_list; 1212 mkey_aux_entry != NULL; 1213 mkey_aux_entry = mkey_aux_entry->next) { 1214 1215 if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) { 1216 if (mkey_aux_entry == mkey_aux_list) { 1217 mkey_aux_list = mkey_aux_entry->next; 1218 } else if (mkey_aux_entry->next == NULL) { 1219 prev_mkey_aux_entry->next = NULL; 1220 } else { 1221 prev_mkey_aux_entry->next = mkey_aux_entry->next; 1222 } 1223 mkey_aux_entry->next = NULL; 1224 krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_entry); 1225 break; /* deleted entry, no need to loop further */ 1226 } else { 1227 prev_mkey_aux_entry = mkey_aux_entry; 1228 } 1229 } 1230 } 1231 } 1232 } 1233 } 1234 assert(k == num_kvnos_inuse); 1235 1236 /* Free any key data entries we did not consume in the loop above. */ 1237 for (i = 0; i < old_key_data_count; i++) 1238 krb5_dbe_free_key_data_contents(util_context, &old_key_data[i]); 1239 free(old_key_data); 1240 1241 if ((retval = krb5_dbe_update_actkvno(util_context, master_entry, 1242 actkvno_list))) { 1243 com_err(progname, retval, 1244 _("while updating actkvno data for master principal entry")); 1245 exit_status++; 1246 goto cleanup_return; 1247 } 1248 1249 if ((retval = krb5_dbe_update_mkey_aux(util_context, master_entry, 1250 mkey_aux_list))) { 1251 com_err(progname, retval, 1252 _("while updating mkey_aux data for master principal entry")); 1253 exit_status++; 1254 goto cleanup_return; 1255 } 1256 1257 if ((retval = krb5_timeofday(util_context, &now))) { 1258 com_err(progname, retval, _("while getting current time")); 1259 exit_status++; 1260 goto cleanup_return; 1261 } 1262 1263 if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry, 1264 now, master_princ))) { 1265 com_err(progname, retval, _("while updating the master key principal " 1266 "modification time")); 1267 exit_status++; 1268 goto cleanup_return; 1269 } 1270 1271 master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA; 1272 1273 if ((retval = krb5_db_put_principal(util_context, master_entry))) { 1274 com_err(progname, retval, 1275 _("while adding master key entry to the database")); 1276 exit_status++; 1277 goto cleanup_return; 1278 } 1279 printf(_("%d key(s) purged.\n"), num_kvnos_purged); 1280 1281 cleanup_return: 1282 krb5_db_free_principal(util_context, master_entry); 1283 free(args.kvnos); 1284 krb5_dbe_free_actkvno_list(util_context, actkvno_list); 1285 krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_list); 1286 return; 1287 } 1288