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