1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* 3 * Copyright 1994, 2008 by the Massachusetts Institute of Technology. 4 * All Rights Reserved. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 /* 26 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 /* Base functions for a kadmin command line interface using the OVSecure 31 * library */ 32 33 /* for "_" macro */ 34 #include "k5-int.h" 35 #include <kadm5/admin.h> 36 #include <adm_proto.h> 37 #include <errno.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <ctype.h> 41 #include <sys/types.h> 42 #include <math.h> 43 #include <unistd.h> 44 #include <pwd.h> 45 /* #include <sys/timeb.h> */ 46 #include <time.h> 47 #include "kadmin.h" 48 49 static krb5_boolean script_mode = FALSE; 50 int exit_status = 0; 51 char *def_realm = NULL; 52 char *whoami = NULL; 53 54 void *handle = NULL; 55 krb5_context context; 56 char *ccache_name = NULL; 57 58 int locked = 0; 59 60 static void 61 info(const char *fmt, ...) 62 #if !defined(__cplusplus) && (__GNUC__ > 2) 63 __attribute__((__format__(__printf__, 1, 2))) 64 #endif 65 ; 66 67 static void 68 error(const char *fmt, ...) 69 #if !defined(__cplusplus) && (__GNUC__ > 2) 70 __attribute__((__format__(__printf__, 1, 2))) 71 #endif 72 ; 73 74 /* Like printf, but suppressed if script_mode is set. */ 75 static void 76 info(const char *fmt, ...) 77 { 78 va_list ap; 79 80 if (script_mode) 81 return; 82 va_start(ap, fmt); 83 vprintf(fmt, ap); 84 va_end(ap); 85 } 86 87 /* Like fprintf to stderr; also set exit_status if script_mode is set. */ 88 static void 89 error(const char *fmt, ...) 90 { 91 va_list ap; 92 93 if (script_mode) 94 exit_status = 1; 95 va_start(ap, fmt); 96 vfprintf(stderr, fmt, ap); 97 va_end(ap); 98 } 99 100 static void 101 usage() 102 { 103 error(_("Usage: %s [-r realm] [-p principal] [-q query] " 104 "[clnt|local args]\n" 105 " [command args...]\n" 106 "\tclnt args: [-s admin_server[:port]] " 107 "[[-c ccache]|[-k [-t keytab]]]|[-n] [-O | -N]\n" 108 "\tlocal args: [-x db_args]* [-d dbname] " 109 "[-e \"enc:salt ...\"] [-m] [-w password] " 110 "where,\n\t[-x db_args]* - any number of database specific " 111 "arguments.\n" 112 "\t\t\tLook at each database documentation for supported " 113 "arguments\n"), whoami); 114 exit(1); 115 } 116 117 static char * 118 strdur(time_t duration) 119 { 120 static char out[50]; 121 int neg, days, hours, minutes, seconds; 122 123 if (duration < 0) { 124 duration *= -1; 125 neg = 1; 126 } else 127 neg = 0; 128 days = duration / (24 * 3600); 129 duration %= 24 * 3600; 130 hours = duration / 3600; 131 duration %= 3600; 132 minutes = duration / 60; 133 duration %= 60; 134 seconds = duration; 135 snprintf(out, sizeof(out), "%s%d %s %02d:%02d:%02d", neg ? "-" : "", 136 days, days == 1 ? "day" : "days", 137 hours, minutes, seconds); 138 return out; 139 } 140 141 static const char * 142 strdate(krb5_timestamp when) 143 { 144 struct tm *tm; 145 static char out[40]; 146 time_t lcltim = ts2tt(when); 147 148 tm = localtime(&lcltim); 149 if (tm == NULL || 150 strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm) == 0) 151 strlcpy(out, "(error)", sizeof(out)); 152 return out; 153 } 154 155 /* Parse a date string using getdate.y. On failure, output an error message 156 * and return (time_t)-1. */ 157 static time_t 158 parse_date(char *str, time_t now) 159 { 160 time_t date; 161 162 date = get_date_rel(str, now); 163 if (date == (time_t)-1) 164 error(_("Invalid date specification \"%s\".\n"), str); 165 return date; 166 } 167 168 /* 169 * Parse a time interval. Use krb5_string_to_deltat() if it works; otherwise 170 * use getdate.y and subtract now, with sanity checks. On failure, output an 171 * error message and return (time_t)-1. 172 */ 173 static time_t 174 parse_interval(char *str, time_t now) 175 { 176 time_t date; 177 krb5_deltat delta; 178 179 if (krb5_string_to_deltat(str, &delta) == 0) 180 return delta; 181 182 date = parse_date(str, now); 183 if (date == (time_t)-1) 184 return date; 185 186 /* Interpret an absolute time of 0 (e.g. "never") as an interval of 0. */ 187 if (date == 0) 188 return 0; 189 190 /* Don't return a negative interval if the date is in the past. */ 191 if (date < now) { 192 error(_("Interval specification \"%s\" is in the past.\n"), str); 193 return (time_t)-1; 194 } 195 196 return date - now; 197 } 198 199 /* this is a wrapper to go around krb5_parse_principal so we can set 200 the default realm up properly */ 201 static krb5_error_code 202 kadmin_parse_name(char *name, krb5_principal *principal) 203 { 204 char *cp, *fullname; 205 krb5_error_code retval; 206 int result; 207 208 /* assumes def_realm is initialized! */ 209 cp = strchr(name, '@'); 210 while (cp) { 211 if (cp - name && *(cp - 1) != '\\') 212 break; 213 else 214 cp = strchr(cp + 1, '@'); 215 } 216 if (cp == NULL) 217 result = asprintf(&fullname, "%s@%s", name, def_realm); 218 else 219 result = asprintf(&fullname, "%s", name); 220 if (result < 0) 221 return ENOMEM; 222 retval = krb5_parse_name(context, fullname, principal); 223 free(fullname); 224 return retval; 225 } 226 227 static void 228 extended_com_err_fn(const char *myprog, errcode_t code, 229 const char *fmt, va_list args) 230 { 231 const char *emsg; 232 233 if (code) { 234 emsg = krb5_get_error_message(context, code); 235 error("%s: %s ", myprog, emsg); 236 krb5_free_error_message(context, emsg); 237 } else { 238 error("%s: ", myprog); 239 } 240 vfprintf(stderr, fmt, args); 241 error("\n"); 242 } 243 244 /* Create a principal using the oldest appropriate kadm5 API. */ 245 static krb5_error_code 246 create_princ(kadm5_principal_ent_rec *princ, long mask, int n_ks, 247 krb5_key_salt_tuple *ks, char *pass) 248 { 249 if (ks) 250 return kadm5_create_principal_3(handle, princ, mask, n_ks, ks, pass); 251 else 252 return kadm5_create_principal(handle, princ, mask, pass); 253 } 254 255 /* Randomize a principal's password using the appropriate kadm5 API. */ 256 krb5_error_code 257 randkey_princ(void *lhandle, krb5_principal princ, krb5_boolean keepold, 258 int n_ks, krb5_key_salt_tuple *ks, krb5_keyblock **key, 259 int *n_keys) 260 { 261 krb5_error_code ret; 262 263 /* Try the newer API first, because the Solaris kadmind only creates DES 264 * keys when the old API is used. */ 265 ret = kadm5_randkey_principal_3(lhandle, princ, keepold, n_ks, ks, key, 266 n_keys); 267 268 /* Fall back to the old version if we get an error and aren't using any new 269 * parameters. */ 270 if (ret == KADM5_RPC_ERROR && !keepold && ks == NULL) 271 ret = kadm5_randkey_principal(lhandle, princ, key, n_keys); 272 273 return ret; 274 } 275 276 static krb5_boolean 277 policy_exists(const char *name) 278 { 279 kadm5_policy_ent_rec pol; 280 281 if (kadm5_get_policy(handle, (char *)name, &pol) != 0) 282 return FALSE; 283 kadm5_free_policy_ent(handle, &pol); 284 return TRUE; 285 } 286 287 void 288 kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out) 289 { 290 extern char *optarg; 291 char *princstr = NULL, *keytab_name = NULL, *query = NULL; 292 char *password = NULL; 293 char *luser, *canon, *cp; 294 int optchar, freeprinc = 0, use_keytab = 0, use_anonymous = 0; 295 struct passwd *pw; 296 kadm5_ret_t retval; 297 krb5_ccache cc; 298 krb5_principal princ; 299 kadm5_config_params params; 300 char **db_args = NULL; 301 int db_args_size = 0; 302 char *db_name = NULL; 303 char *svcname, *realm; 304 305 memset(¶ms, 0, sizeof(params)); 306 307 set_com_err_hook(extended_com_err_fn); 308 309 retval = kadm5_init_krb5_context(&context); 310 if (retval) { 311 com_err(whoami, retval, _("while initializing krb5 library")); 312 exit(1); 313 } 314 315 while ((optchar = getopt(argc, argv, 316 "+x:r:p:knq:w:d:s:mc:t:e:ON")) != EOF) { 317 switch (optchar) { 318 case 'x': 319 db_args_size++; 320 db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1)); 321 if (db_args == NULL) { 322 error(_("%s: Cannot initialize. Not enough memory\n"), whoami); 323 exit(1); 324 } 325 db_args[db_args_size - 1] = optarg; 326 db_args[db_args_size] = NULL; 327 break; 328 329 case 'r': 330 def_realm = optarg; 331 break; 332 case 'p': 333 princstr = optarg; 334 break; 335 case 'c': 336 ccache_name = optarg; 337 break; 338 case 'k': 339 use_keytab++; 340 break; 341 case 'n': 342 use_anonymous++; 343 break; 344 case 't': 345 keytab_name = optarg; 346 break; 347 case 'w': 348 password = optarg; 349 break; 350 case 'q': 351 query = optarg; 352 break; 353 case 'd': 354 /* db_name has to be passed as part of the db_args. */ 355 free(db_name); 356 asprintf(&db_name, "dbname=%s", optarg); 357 358 db_args_size++; 359 db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1)); 360 if (db_args == NULL) { 361 error(_("%s: Cannot initialize. Not enough memory\n"), whoami); 362 exit(1); 363 } 364 db_args[db_args_size - 1] = db_name; 365 db_args[db_args_size] = NULL; 366 break; 367 case 's': 368 params.admin_server = optarg; 369 params.mask |= KADM5_CONFIG_ADMIN_SERVER; 370 break; 371 case 'm': 372 params.mkey_from_kbd = 1; 373 params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 374 break; 375 case 'e': 376 retval = krb5_string_to_keysalts(optarg, NULL, NULL, 0, 377 ¶ms.keysalts, 378 ¶ms.num_keysalts); 379 if (retval) { 380 com_err(whoami, retval, _("while parsing keysalts %s"), 381 optarg); 382 exit(1); 383 } 384 params.mask |= KADM5_CONFIG_ENCTYPES; 385 break; 386 case 'O': 387 params.mask |= KADM5_CONFIG_OLD_AUTH_GSSAPI; 388 break; 389 case 'N': 390 params.mask |= KADM5_CONFIG_AUTH_NOFALLBACK; 391 break; 392 default: 393 usage(); 394 } 395 } 396 if ((ccache_name && use_keytab) || 397 (keytab_name && !use_keytab) || 398 (ccache_name && use_anonymous) || 399 (use_anonymous && use_keytab)) 400 usage(); 401 402 if (query != NULL && argv[optind] != NULL) { 403 error(_("%s: -q is exclusive with command-line query"), whoami); 404 usage(); 405 } 406 407 if (argv[optind] != NULL) 408 script_mode = TRUE; 409 410 if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { 411 error(_("%s: unable to get default realm\n"), whoami); 412 exit(1); 413 } 414 415 params.mask |= KADM5_CONFIG_REALM; 416 params.realm = def_realm; 417 418 if (params.mask & KADM5_CONFIG_OLD_AUTH_GSSAPI) 419 svcname = KADM5_ADMIN_SERVICE; 420 else 421 svcname = NULL; 422 423 /* 424 * Set cc to an open credentials cache, either specified by the -c 425 * argument or the default. 426 */ 427 if (ccache_name == NULL) { 428 retval = krb5_cc_default(context, &cc); 429 if (retval) { 430 com_err(whoami, retval, 431 _("while opening default credentials cache")); 432 exit(1); 433 } 434 } else { 435 retval = krb5_cc_resolve(context, ccache_name, &cc); 436 if (retval) { 437 com_err(whoami, retval, _("while opening credentials cache %s"), 438 ccache_name); 439 exit(1); 440 } 441 } 442 443 /* 444 * If no principal name is specified: If authenticating anonymously, use 445 * the anonymous principal for the local realm, else if a ccache was 446 * specified and its primary principal name can be read, it is used, else 447 * if a keytab was specified, the principal name is host/hostname, 448 * otherwise append "/admin" to the primary name of the default ccache, 449 * $USER, or pw_name. 450 * 451 * Gee, 100+ lines to figure out the client principal name. This 452 * should be compressed... 453 */ 454 455 if (princstr == NULL) { 456 if (use_anonymous) { 457 if (asprintf(&princstr, "%s/%s@%s", KRB5_WELLKNOWN_NAMESTR, 458 KRB5_ANONYMOUS_PRINCSTR, def_realm) < 0) { 459 error(_("%s: out of memory\n"), whoami); 460 exit(1); 461 } 462 freeprinc++; 463 } else if (ccache_name != NULL && 464 !krb5_cc_get_principal(context, cc, &princ)) { 465 retval = krb5_unparse_name(context, princ, &princstr); 466 if (retval) { 467 com_err(whoami, retval, 468 _("while canonicalizing principal name")); 469 exit(1); 470 } 471 krb5_free_principal(context, princ); 472 freeprinc++; 473 } else if (use_keytab != 0) { 474 retval = krb5_sname_to_principal(context, NULL, "host", 475 KRB5_NT_SRV_HST, &princ); 476 if (retval) { 477 com_err(whoami, retval, _("creating host service principal")); 478 exit(1); 479 } 480 retval = krb5_unparse_name(context, princ, &princstr); 481 if (retval) { 482 com_err(whoami, retval, 483 _("while canonicalizing principal name")); 484 exit(1); 485 } 486 krb5_free_principal(context, princ); 487 freeprinc++; 488 } else if (!krb5_cc_get_principal(context, cc, &princ)) { 489 if (krb5_unparse_name(context, princ, &canon)) { 490 error(_("%s: unable to canonicalize principal\n"), whoami); 491 exit(1); 492 } 493 /* Strip out realm of principal if it's there. */ 494 realm = strchr(canon, '@'); 495 while (realm) { 496 if (realm > canon && *(realm - 1) != '\\') 497 break; 498 realm = strchr(realm + 1, '@'); 499 } 500 if (realm) 501 *realm++ = '\0'; 502 cp = strchr(canon, '/'); 503 while (cp) { 504 if (cp > canon && *(cp - 1) != '\\') 505 break; 506 cp = strchr(cp + 1, '/'); 507 } 508 if (cp != NULL) 509 *cp = '\0'; 510 if (asprintf(&princstr, "%s/admin%s%s", canon, 511 (realm) ? "@" : "", 512 (realm) ? realm : "") < 0) { 513 error(_("%s: out of memory\n"), whoami); 514 exit(1); 515 } 516 free(canon); 517 krb5_free_principal(context, princ); 518 freeprinc++; 519 } else if ((luser = getenv("USER"))) { 520 if (asprintf(&princstr, "%s/admin@%s", luser, def_realm) < 0) { 521 error(_("%s: out of memory\n"), whoami); 522 exit(1); 523 } 524 freeprinc++; 525 } else if ((pw = getpwuid(getuid()))) { 526 if (asprintf(&princstr, "%s/admin@%s", pw->pw_name, 527 def_realm) < 0) { 528 error(_("%s: out of memory\n"), whoami); 529 exit(1); 530 } 531 freeprinc++; 532 } else { 533 error(_("%s: unable to figure out a principal name\n"), whoami); 534 exit(1); 535 } 536 } 537 538 retval = krb5_klog_init(context, "admin_server", whoami, 0); 539 if (retval) { 540 com_err(whoami, retval, _("while setting up logging")); 541 exit(1); 542 } 543 544 /* 545 * Initialize the kadm5 connection. If we were given a ccache, 546 * use it. Otherwise, use/prompt for the password. 547 */ 548 if (ccache_name) { 549 info(_("Authenticating as principal %s with existing " 550 "credentials.\n"), princstr); 551 retval = kadm5_init_with_creds(context, princstr, cc, svcname, ¶ms, 552 KADM5_STRUCT_VERSION, 553 KADM5_API_VERSION_4, db_args, &handle); 554 } else if (use_anonymous) { 555 info(_("Authenticating as principal %s with password; " 556 "anonymous requested.\n"), princstr); 557 retval = kadm5_init_anonymous(context, princstr, svcname, ¶ms, 558 KADM5_STRUCT_VERSION, 559 KADM5_API_VERSION_4, db_args, &handle); 560 } else if (use_keytab) { 561 if (keytab_name != NULL) { 562 info(_("Authenticating as principal %s with keytab %s.\n"), 563 princstr, keytab_name); 564 } else { 565 info(_("Authenticating as principal %s with default keytab.\n"), 566 princstr); 567 } 568 retval = kadm5_init_with_skey(context, princstr, keytab_name, svcname, 569 ¶ms, KADM5_STRUCT_VERSION, 570 KADM5_API_VERSION_4, db_args, &handle); 571 } else { 572 info(_("Authenticating as principal %s with password.\n"), 573 princstr); 574 retval = kadm5_init_with_password(context, princstr, password, svcname, 575 ¶ms, KADM5_STRUCT_VERSION, 576 KADM5_API_VERSION_4, db_args, 577 &handle); 578 } 579 if (retval) { 580 com_err(whoami, retval, _("while initializing %s interface"), whoami); 581 if (retval == KADM5_BAD_CLIENT_PARAMS || 582 retval == KADM5_BAD_SERVER_PARAMS) 583 usage(); 584 exit(1); 585 } 586 if (freeprinc) 587 free(princstr); 588 589 free(params.keysalts); 590 free(db_name); 591 free(db_args); 592 593 retval = krb5_cc_close(context, cc); 594 if (retval) { 595 com_err(whoami, retval, _("while closing ccache %s"), ccache_name); 596 exit(1); 597 } 598 599 retval = kadm5_init_iprop(handle, 0); 600 if (retval) { 601 com_err(whoami, retval, _("while mapping update log")); 602 exit(1); 603 } 604 605 *request_out = query; 606 *args_out = argv + optind; 607 } 608 609 int 610 quit() 611 { 612 kadm5_ret_t retval; 613 614 if (locked) { 615 retval = kadm5_unlock(handle); 616 if (retval) { 617 com_err("quit", retval, _("while unlocking locked database")); 618 return 1; 619 } 620 locked = 0; 621 } 622 623 kadm5_destroy(handle); 624 if (ccache_name != NULL && !script_mode) { 625 fprintf(stderr, "\n\a\a\a%s", 626 _("Administration credentials NOT DESTROYED.\n")); 627 } 628 629 /* insert more random cleanup here */ 630 krb5_klog_close(context); 631 krb5_free_context(context); 632 return 0; 633 } 634 635 void 636 kadmin_lock(int argc, char *argv[]) 637 { 638 kadm5_ret_t retval; 639 640 if (locked) 641 return; 642 retval = kadm5_lock(handle); 643 if (retval) { 644 com_err("lock", retval, ""); 645 return; 646 } 647 locked = 1; 648 } 649 650 void 651 kadmin_unlock(int argc, char *argv[]) 652 { 653 kadm5_ret_t retval; 654 655 if (!locked) 656 return; 657 retval = kadm5_unlock(handle); 658 if (retval) { 659 com_err("unlock", retval, ""); 660 return; 661 } 662 locked = 0; 663 } 664 665 void 666 kadmin_delprinc(int argc, char *argv[]) 667 { 668 kadm5_ret_t retval; 669 krb5_principal princ = NULL; 670 char *canon = NULL; 671 char reply[5]; 672 673 if (! (argc == 2 || 674 (argc == 3 && !strcmp("-force", argv[1])))) { 675 error(_("usage: delete_principal [-force] principal\n")); 676 return; 677 } 678 retval = kadmin_parse_name(argv[argc - 1], &princ); 679 if (retval) { 680 com_err("delete_principal", retval, _("while parsing principal name")); 681 return; 682 } 683 retval = krb5_unparse_name(context, princ, &canon); 684 if (retval) { 685 com_err("delete_principal", retval, 686 _("while canonicalizing principal")); 687 goto cleanup; 688 } 689 if (argc == 2 && !script_mode) { 690 printf(_("Are you sure you want to delete the principal \"%s\"? " 691 "(yes/no): "), canon); 692 fgets(reply, sizeof (reply), stdin); 693 if (strcmp("yes\n", reply)) { 694 fprintf(stderr, _("Principal \"%s\" not deleted\n"), canon); 695 goto cleanup; 696 } 697 } 698 retval = kadm5_delete_principal(handle, princ); 699 if (retval) { 700 com_err("delete_principal", retval, 701 _("while deleting principal \"%s\""), canon); 702 goto cleanup; 703 } 704 info(_("Principal \"%s\" deleted.\n"), canon); 705 info(_("Make sure that you have removed this principal from all ACLs " 706 "before reusing.\n")); 707 708 cleanup: 709 krb5_free_principal(context, princ); 710 free(canon); 711 } 712 713 void 714 kadmin_renameprinc(int argc, char *argv[]) 715 { 716 kadm5_ret_t retval; 717 krb5_principal oprinc = NULL, nprinc = NULL; 718 char *ocanon = NULL, *ncanon = NULL; 719 char reply[5]; 720 721 if (!(argc == 3 || (argc == 4 && !strcmp("-force", argv[1])))) { 722 error(_("usage: rename_principal [-force] old_principal " 723 "new_principal\n")); 724 return; 725 } 726 retval = kadmin_parse_name(argv[argc - 2], &oprinc); 727 if (retval) { 728 com_err("rename_principal", retval, 729 _("while parsing old principal name")); 730 goto cleanup; 731 } 732 retval = kadmin_parse_name(argv[argc - 1], &nprinc); 733 if (retval) { 734 com_err("rename_principal", retval, 735 _("while parsing new principal name")); 736 goto cleanup; 737 } 738 retval = krb5_unparse_name(context, oprinc, &ocanon); 739 if (retval) { 740 com_err("rename_principal", retval, 741 _("while canonicalizing old principal")); 742 goto cleanup; 743 } 744 retval = krb5_unparse_name(context, nprinc, &ncanon); 745 if (retval) { 746 com_err("rename_principal", retval, 747 _("while canonicalizing new principal")); 748 goto cleanup; 749 } 750 if (argc == 3 && !script_mode) { 751 printf(_("Are you sure you want to rename the principal \"%s\" " 752 "to \"%s\"? (yes/no): "), ocanon, ncanon); 753 fgets(reply, sizeof(reply), stdin); 754 if (strcmp("yes\n", reply)) { 755 fprintf(stderr, _("Principal \"%s\" not renamed\n"), ocanon); 756 goto cleanup; 757 } 758 } 759 retval = kadm5_rename_principal(handle, oprinc, nprinc); 760 if (retval) { 761 com_err("rename_principal", retval, 762 _("while renaming principal \"%s\" to \"%s\""), 763 ocanon, ncanon); 764 goto cleanup; 765 } 766 info(_("Principal \"%s\" renamed to \"%s\".\n"), ocanon, ncanon); 767 info(_("Make sure that you have removed the old principal from all ACLs " 768 "before reusing.\n")); 769 770 cleanup: 771 krb5_free_principal(context, nprinc); 772 krb5_free_principal(context, oprinc); 773 free(ncanon); 774 free(ocanon); 775 } 776 777 static void 778 cpw_usage(const char *str) 779 { 780 if (str) 781 error("%s\n", str); 782 error(_("usage: change_password [-randkey] [-keepold] " 783 "[-e keysaltlist] [-pw password] principal\n")); 784 } 785 786 void 787 kadmin_cpw(int argc, char *argv[]) 788 { 789 kadm5_ret_t retval; 790 static char newpw[1024]; 791 static char prompt1[1024], prompt2[1024]; 792 char *canon = NULL, *pwarg = NULL; 793 int n_ks_tuple = 0, randkey = 0; 794 krb5_boolean keepold = FALSE; 795 krb5_key_salt_tuple *ks_tuple = NULL; 796 krb5_principal princ = NULL; 797 char **db_args = NULL; 798 int db_args_size = 0; 799 800 if (argc < 1) { 801 cpw_usage(NULL); 802 return; 803 } 804 for (argv++, argc--; argc > 0 && **argv == '-'; argc--, argv++) { 805 if (!strcmp("-x", *argv)) { 806 argc--; 807 if (argc < 1) { 808 cpw_usage(_("change_password: missing db argument")); 809 goto cleanup; 810 } 811 db_args_size++; 812 db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1)); 813 if (db_args == NULL) { 814 error(_("change_password: Not enough memory\n")); 815 exit(1); 816 } 817 db_args[db_args_size - 1] = *++argv; 818 db_args[db_args_size] = NULL; 819 } else if (!strcmp("-pw", *argv)) { 820 argc--; 821 if (argc < 1) { 822 cpw_usage(_("change_password: missing password arg")); 823 goto cleanup; 824 } 825 pwarg = *++argv; 826 } else if (!strcmp("-randkey", *argv)) { 827 randkey++; 828 } else if (!strcmp("-keepold", *argv)) { 829 keepold = TRUE; 830 } else if (!strcmp("-e", *argv)) { 831 argc--; 832 if (argc < 1) { 833 cpw_usage(_("change_password: missing keysaltlist arg")); 834 goto cleanup; 835 } 836 retval = krb5_string_to_keysalts(*++argv, NULL, NULL, 0, 837 &ks_tuple, &n_ks_tuple); 838 if (retval) { 839 com_err("change_password", retval, 840 _("while parsing keysalts %s"), *argv); 841 goto cleanup; 842 } 843 } else { 844 com_err("change_password", 0, _("unrecognized option %s"), *argv); 845 cpw_usage(NULL); 846 goto cleanup; 847 } 848 } 849 if (argc != 1) { 850 if (argc < 1) 851 com_err("change_password", 0, _("missing principal name")); 852 else 853 com_err("change_password", 0, _("too many arguments")); 854 cpw_usage(NULL); 855 goto cleanup; 856 } 857 retval = kadmin_parse_name(*argv, &princ); 858 if (retval) { 859 com_err("change_password", retval, _("while parsing principal name")); 860 goto cleanup; 861 } 862 retval = krb5_unparse_name(context, princ, &canon); 863 if (retval) { 864 com_err("change_password", retval, 865 _("while canonicalizing principal")); 866 goto cleanup; 867 } 868 if (pwarg != NULL) { 869 if (keepold || ks_tuple != NULL) { 870 retval = kadm5_chpass_principal_3(handle, princ, keepold, 871 n_ks_tuple, ks_tuple, pwarg); 872 } else { 873 retval = kadm5_chpass_principal(handle, princ, pwarg); 874 } 875 if (retval) { 876 com_err("change_password", retval, 877 _("while changing password for \"%s\"."), canon); 878 goto cleanup; 879 } 880 info(_("Password for \"%s\" changed.\n"), canon); 881 } else if (randkey) { 882 retval = randkey_princ(handle, princ, keepold, n_ks_tuple, ks_tuple, 883 NULL, NULL); 884 if (retval) { 885 com_err("change_password", retval, 886 _("while randomizing key for \"%s\"."), canon); 887 goto cleanup; 888 } 889 info(_("Key for \"%s\" randomized.\n"), canon); 890 } else { 891 unsigned int i = sizeof (newpw) - 1; 892 893 snprintf(prompt1, sizeof(prompt1), 894 _("Enter password for principal \"%s\""), canon); 895 snprintf(prompt2, sizeof(prompt2), 896 _("Re-enter password for principal \"%s\""), canon); 897 retval = krb5_read_password(context, prompt1, prompt2, 898 newpw, &i); 899 if (retval) { 900 com_err("change_password", retval, 901 _("while reading password for \"%s\"."), canon); 902 goto cleanup; 903 } 904 if (keepold || ks_tuple != NULL) { 905 retval = kadm5_chpass_principal_3(handle, princ, keepold, 906 n_ks_tuple, ks_tuple, 907 newpw); 908 } else { 909 retval = kadm5_chpass_principal(handle, princ, newpw); 910 } 911 memset(newpw, 0, sizeof (newpw)); 912 if (retval) { 913 com_err("change_password", retval, 914 _("while changing password for \"%s\"."), canon); 915 goto cleanup; 916 } 917 info(_("Password for \"%s\" changed.\n"), canon); 918 } 919 cleanup: 920 free(canon); 921 free(db_args); 922 krb5_free_principal(context, princ); 923 free(ks_tuple); 924 } 925 926 static void 927 kadmin_free_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap) 928 { 929 krb5_tl_data *tl_data = *tl_datap, *next; 930 int n_tl_data = *n_tl_datap; 931 int i; 932 933 *n_tl_datap = 0; 934 *tl_datap = NULL; 935 936 for (i = 0; tl_data && (i < n_tl_data); i++) { 937 next = tl_data->tl_data_next; 938 free(tl_data->tl_data_contents); 939 free(tl_data); 940 tl_data = next; 941 } 942 } 943 944 /* Construct a tl_data element and add it to the tail of *tl_datap. */ 945 static void 946 add_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap, 947 krb5_int16 tl_type, krb5_ui_2 len, krb5_octet *contents) 948 { 949 krb5_tl_data *tl_data; 950 krb5_octet *copy; 951 952 copy = malloc(len); 953 tl_data = calloc(1, sizeof(*tl_data)); 954 if (copy == NULL || tl_data == NULL) { 955 error(_("Not enough memory\n")); 956 exit(1); 957 } 958 memcpy(copy, contents, len); 959 960 tl_data->tl_data_type = tl_type; 961 tl_data->tl_data_length = len; 962 tl_data->tl_data_contents = copy; 963 tl_data->tl_data_next = NULL; 964 965 for (; *tl_datap != NULL; tl_datap = &(*tl_datap)->tl_data_next); 966 *tl_datap = tl_data; 967 (*n_tl_datap)++; 968 } 969 970 static void 971 unlock_princ(kadm5_principal_ent_t princ, long *mask, const char *caller) 972 { 973 krb5_error_code retval; 974 krb5_timestamp now; 975 krb5_octet timebuf[4]; 976 977 /* Zero out the failed auth count. */ 978 princ->fail_auth_count = 0; 979 *mask |= KADM5_FAIL_AUTH_COUNT; 980 981 /* Record the timestamp of this unlock operation so that replica KDCs will 982 * see it, since fail_auth_count is unreplicated. */ 983 retval = krb5_timeofday(context, &now); 984 if (retval) { 985 com_err(caller, retval, _("while getting time")); 986 exit(1); 987 } 988 store_32_le((krb5_int32)now, timebuf); 989 add_tl_data(&princ->n_tl_data, &princ->tl_data, 990 KRB5_TL_LAST_ADMIN_UNLOCK, 4, timebuf); 991 *mask |= KADM5_TL_DATA; 992 } 993 994 /* 995 * Parse addprinc or modprinc arguments. Some output fields may be 996 * filled in on error. 997 */ 998 static int 999 kadmin_parse_princ_args(int argc, char *argv[], kadm5_principal_ent_t oprinc, 1000 long *mask, char **pass, krb5_boolean *randkey, 1001 krb5_boolean *nokey, krb5_key_salt_tuple **ks_tuple, 1002 int *n_ks_tuple, char *caller) 1003 { 1004 int i; 1005 time_t now, date, interval; 1006 krb5_error_code retval; 1007 1008 *mask = 0; 1009 *pass = NULL; 1010 *n_ks_tuple = 0; 1011 *ks_tuple = NULL; 1012 time(&now); 1013 *randkey = FALSE; 1014 *nokey = FALSE; 1015 for (i = 1; i < argc - 1; i++) { 1016 if (!strcmp("-x",argv[i])) { 1017 if (++i > argc - 2) 1018 return -1; 1019 1020 add_tl_data(&oprinc->n_tl_data, &oprinc->tl_data, 1021 KRB5_TL_DB_ARGS, strlen(argv[i]) + 1, 1022 (krb5_octet *)argv[i]); 1023 *mask |= KADM5_TL_DATA; 1024 continue; 1025 } 1026 if (!strcmp("-expire", argv[i])) { 1027 if (++i > argc - 2) 1028 return -1; 1029 date = parse_date(argv[i], now); 1030 if (date == (time_t)-1) 1031 return -1; 1032 oprinc->princ_expire_time = date; 1033 *mask |= KADM5_PRINC_EXPIRE_TIME; 1034 continue; 1035 } 1036 if (!strcmp("-pwexpire", argv[i])) { 1037 if (++i > argc - 2) 1038 return -1; 1039 date = parse_date(argv[i], now); 1040 if (date == (time_t)-1) 1041 return -1; 1042 oprinc->pw_expiration = date; 1043 *mask |= KADM5_PW_EXPIRATION; 1044 continue; 1045 } 1046 if (!strcmp("-maxlife", argv[i])) { 1047 if (++i > argc - 2) 1048 return -1; 1049 interval = parse_interval(argv[i], now); 1050 if (interval == (time_t)-1) 1051 return -1; 1052 oprinc->max_life = interval; 1053 *mask |= KADM5_MAX_LIFE; 1054 continue; 1055 } 1056 if (!strcmp("-maxrenewlife", argv[i])) { 1057 if (++i > argc - 2) 1058 return -1; 1059 interval = parse_interval(argv[i], now); 1060 if (interval == (time_t)-1) 1061 return -1; 1062 oprinc->max_renewable_life = interval; 1063 *mask |= KADM5_MAX_RLIFE; 1064 continue; 1065 } 1066 if (!strcmp("-kvno", argv[i])) { 1067 if (++i > argc - 2) 1068 return -1; 1069 oprinc->kvno = atoi(argv[i]); 1070 *mask |= KADM5_KVNO; 1071 continue; 1072 } 1073 if (!strcmp("-policy", argv[i])) { 1074 if (++i > argc - 2) 1075 return -1; 1076 oprinc->policy = argv[i]; 1077 *mask |= KADM5_POLICY; 1078 continue; 1079 } 1080 if (!strcmp("-clearpolicy", argv[i])) { 1081 oprinc->policy = NULL; 1082 *mask |= KADM5_POLICY_CLR; 1083 continue; 1084 } 1085 if (!strcmp("-pw", argv[i])) { 1086 if (++i > argc - 2) 1087 return -1; 1088 *pass = argv[i]; 1089 continue; 1090 } 1091 if (!strcmp("-randkey", argv[i])) { 1092 *randkey = TRUE; 1093 continue; 1094 } 1095 if (!strcmp("-nokey", argv[i])) { 1096 *nokey = TRUE; 1097 continue; 1098 } 1099 if (!strcmp("-unlock", argv[i])) { 1100 unlock_princ(oprinc, mask, caller); 1101 continue; 1102 } 1103 if (!strcmp("-e", argv[i])) { 1104 if (++i > argc - 2) 1105 return -1; 1106 retval = krb5_string_to_keysalts(argv[i], NULL, NULL, 0, 1107 ks_tuple, n_ks_tuple); 1108 if (retval) { 1109 com_err(caller, retval, _("while parsing keysalts %s"), 1110 argv[i]); 1111 return -1; 1112 } 1113 continue; 1114 } 1115 retval = krb5_flagspec_to_mask(argv[i], &oprinc->attributes, 1116 &oprinc->attributes); 1117 if (retval) 1118 return -1; 1119 else 1120 *mask |= KADM5_ATTRIBUTES; 1121 } 1122 if (i != argc - 1) 1123 return -1; 1124 retval = kadmin_parse_name(argv[i], &oprinc->principal); 1125 if (retval) { 1126 com_err(caller, retval, _("while parsing principal")); 1127 return -1; 1128 } 1129 return 0; 1130 } 1131 1132 static void 1133 kadmin_addprinc_usage() 1134 { 1135 error(_("usage: add_principal [options] principal\n")); 1136 error(_("\toptions are:\n")); 1137 error(_("\t\t[-randkey|-nokey] [-x db_princ_args]* [-expire expdate] " 1138 "[-pwexpire pwexpdate] [-maxlife maxtixlife]\n" 1139 "\t\t[-kvno kvno] [-policy policy] [-clearpolicy]\n" 1140 "\t\t[-pw password] [-maxrenewlife maxrenewlife]\n" 1141 "\t\t[-e keysaltlist]\n\t\t[{+|-}attribute]\n")); 1142 error(_("\tattributes are:\n")); 1143 error(_("\t\tallow_postdated allow_forwardable allow_tgs_req " 1144 "allow_renewable\n" 1145 "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n" 1146 "\t\trequires_hwauth needchange allow_svr " 1147 "password_changing_service\n" 1148 "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n" 1149 "\t\tlockdown_keys\n" 1150 "\nwhere,\n\t[-x db_princ_args]* - any number of database " 1151 "specific arguments.\n" 1152 "\t\t\tLook at each database documentation for supported " 1153 "arguments\n")); 1154 } 1155 1156 static void 1157 kadmin_modprinc_usage() 1158 { 1159 error(_("usage: modify_principal [options] principal\n")); 1160 error(_("\toptions are:\n")); 1161 error(_("\t\t[-x db_princ_args]* [-expire expdate] " 1162 "[-pwexpire pwexpdate] [-maxlife maxtixlife]\n" 1163 "\t\t[-kvno kvno] [-policy policy] [-clearpolicy]\n" 1164 "\t\t[-maxrenewlife maxrenewlife] [-unlock] [{+|-}attribute]\n")); 1165 error(_("\tattributes are:\n")); 1166 error(_("\t\tallow_postdated allow_forwardable allow_tgs_req " 1167 "allow_renewable\n" 1168 "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n" 1169 "\t\trequires_hwauth needchange allow_svr " 1170 "password_changing_service\n" 1171 "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n" 1172 "\t\tlockdown_keys\n" 1173 "\nwhere,\n\t[-x db_princ_args]* - any number of database " 1174 "specific arguments.\n" 1175 "\t\t\tLook at each database documentation for supported " 1176 "arguments\n")); 1177 } 1178 1179 /* Create a dummy password for old-style (pre-1.8) randkey creation. */ 1180 static void 1181 prepare_dummy_password(char *buf, size_t sz) 1182 { 1183 size_t i; 1184 1185 /* Must try to pass any password policy in place, and be valid UTF-8. */ 1186 strlcpy(buf, "6F a[", sz); 1187 for (i = strlen(buf); i < sz - 1; i++) 1188 buf[i] = 'a' + (i % 26); 1189 buf[sz - 1] = '\0'; 1190 } 1191 1192 void 1193 kadmin_addprinc(int argc, char *argv[]) 1194 { 1195 kadm5_principal_ent_rec princ; 1196 long mask; 1197 krb5_boolean randkey = FALSE, nokey = FALSE, old_style_randkey = FALSE; 1198 int n_ks_tuple; 1199 krb5_key_salt_tuple *ks_tuple = NULL; 1200 char *pass, *canon = NULL; 1201 krb5_error_code retval; 1202 char newpw[1024], dummybuf[256]; 1203 static char prompt1[1024], prompt2[1024]; 1204 1205 /* Zero all fields in request structure */ 1206 memset(&princ, 0, sizeof(princ)); 1207 1208 princ.attributes = 0; 1209 if (kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey, 1210 &nokey, &ks_tuple, &n_ks_tuple, 1211 "add_principal")) { 1212 kadmin_addprinc_usage(); 1213 goto cleanup; 1214 } 1215 1216 retval = krb5_unparse_name(context, princ.principal, &canon); 1217 if (retval) { 1218 com_err("add_principal", retval, _("while canonicalizing principal")); 1219 goto cleanup; 1220 } 1221 1222 if (mask & KADM5_POLICY) { 1223 /* Warn if the specified policy does not exist. */ 1224 if (!script_mode && !policy_exists(princ.policy)) { 1225 fprintf(stderr, _("WARNING: policy \"%s\" does not exist\n"), 1226 princ.policy); 1227 } 1228 } else if (!(mask & KADM5_POLICY_CLR)) { 1229 /* If the policy "default" exists, assign it. */ 1230 if (policy_exists("default")) { 1231 if (!script_mode) { 1232 fprintf(stderr, _("No policy specified for %s; " 1233 "assigning \"default\"\n"), canon); 1234 } 1235 princ.policy = "default"; 1236 mask |= KADM5_POLICY; 1237 } else if (!script_mode) { 1238 fprintf(stderr, _("No policy specified for %s; " 1239 "defaulting to no policy\n"), canon); 1240 } 1241 } 1242 /* Don't send KADM5_POLICY_CLR to the server. */ 1243 mask &= ~KADM5_POLICY_CLR; 1244 1245 if (nokey) { 1246 pass = NULL; 1247 mask |= KADM5_KEY_DATA; 1248 } else if (randkey) { 1249 pass = NULL; 1250 } else if (pass == NULL) { 1251 unsigned int sz = sizeof(newpw) - 1; 1252 1253 snprintf(prompt1, sizeof(prompt1), 1254 _("Enter password for principal \"%s\""), canon); 1255 snprintf(prompt2, sizeof(prompt2), 1256 _("Re-enter password for principal \"%s\""), canon); 1257 retval = krb5_read_password(context, prompt1, prompt2, newpw, &sz); 1258 if (retval) { 1259 com_err("add_principal", retval, 1260 _("while reading password for \"%s\"."), canon); 1261 goto cleanup; 1262 } 1263 pass = newpw; 1264 } 1265 mask |= KADM5_PRINCIPAL; 1266 retval = create_princ(&princ, mask, n_ks_tuple, ks_tuple, pass); 1267 if (retval == EINVAL && randkey) { 1268 /* 1269 * The server doesn't support randkey creation. Create the principal 1270 * with a dummy password and disallow tickets. 1271 */ 1272 prepare_dummy_password(dummybuf, sizeof(dummybuf)); 1273 princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; 1274 mask |= KADM5_ATTRIBUTES; 1275 pass = dummybuf; 1276 retval = create_princ(&princ, mask, n_ks_tuple, ks_tuple, pass); 1277 old_style_randkey = 1; 1278 } 1279 if (retval == KADM5_BAD_MASK && nokey) { 1280 error(_("Admin server does not support -nokey while creating " 1281 "\"%s\"\n"), canon); 1282 goto cleanup; 1283 } 1284 if (retval) { 1285 com_err("add_principal", retval, "while creating \"%s\".", canon); 1286 goto cleanup; 1287 } 1288 if (old_style_randkey) { 1289 /* Randomize the password and re-enable tickets. */ 1290 retval = randkey_princ(handle, princ.principal, FALSE, n_ks_tuple, 1291 ks_tuple, NULL, NULL); 1292 if (retval) { 1293 com_err("add_principal", retval, 1294 _("while randomizing key for \"%s\"."), canon); 1295 goto cleanup; 1296 } 1297 princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; /* clear notix */ 1298 mask = KADM5_ATTRIBUTES; 1299 retval = kadm5_modify_principal(handle, &princ, mask); 1300 if (retval) { 1301 com_err("add_principal", retval, 1302 _("while clearing DISALLOW_ALL_TIX for \"%s\"."), canon); 1303 goto cleanup; 1304 } 1305 } 1306 info("Principal \"%s\" created.\n", canon); 1307 1308 cleanup: 1309 krb5_free_principal(context, princ.principal); 1310 free(ks_tuple); 1311 free(canon); 1312 kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data); 1313 } 1314 1315 void 1316 kadmin_modprinc(int argc, char *argv[]) 1317 { 1318 kadm5_principal_ent_rec princ, oldprinc; 1319 krb5_principal kprinc = NULL; 1320 long mask; 1321 krb5_error_code retval; 1322 char *pass, *canon = NULL; 1323 krb5_boolean randkey = FALSE, nokey = FALSE; 1324 int n_ks_tuple = 0; 1325 krb5_key_salt_tuple *ks_tuple = NULL; 1326 1327 if (argc < 2) { 1328 kadmin_modprinc_usage(); 1329 return; 1330 } 1331 1332 memset(&oldprinc, 0, sizeof(oldprinc)); 1333 memset(&princ, 0, sizeof(princ)); 1334 1335 retval = kadmin_parse_name(argv[argc - 1], &kprinc); 1336 if (retval) { 1337 com_err("modify_principal", retval, _("while parsing principal")); 1338 return; 1339 } 1340 retval = krb5_unparse_name(context, kprinc, &canon); 1341 if (retval) { 1342 com_err("modify_principal", retval, 1343 _("while canonicalizing principal")); 1344 goto cleanup; 1345 } 1346 retval = kadm5_get_principal(handle, kprinc, &oldprinc, 1347 KADM5_PRINCIPAL_NORMAL_MASK); 1348 if (retval) { 1349 com_err("modify_principal", retval, _("while getting \"%s\"."), canon); 1350 goto cleanup; 1351 } 1352 princ.attributes = oldprinc.attributes; 1353 kadm5_free_principal_ent(handle, &oldprinc); 1354 retval = kadmin_parse_princ_args(argc, argv, 1355 &princ, &mask, 1356 &pass, &randkey, &nokey, 1357 &ks_tuple, &n_ks_tuple, 1358 "modify_principal"); 1359 if (retval || ks_tuple != NULL || randkey || nokey || pass) { 1360 kadmin_modprinc_usage(); 1361 goto cleanup; 1362 } 1363 if (mask & KADM5_POLICY) { 1364 /* Warn if the specified policy does not exist. */ 1365 if (!script_mode && !policy_exists(princ.policy)) { 1366 fprintf(stderr, _("WARNING: policy \"%s\" does not exist\n"), 1367 princ.policy); 1368 } 1369 } 1370 if (mask) { 1371 /* Skip this if all we're doing is setting certhash. */ 1372 retval = kadm5_modify_principal(handle, &princ, mask); 1373 } 1374 if (retval) { 1375 com_err("modify_principal", retval, _("while modifying \"%s\"."), 1376 canon); 1377 goto cleanup; 1378 } 1379 info(_("Principal \"%s\" modified.\n"), canon); 1380 cleanup: 1381 krb5_free_principal(context, kprinc); 1382 krb5_free_principal(context, princ.principal); 1383 kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data); 1384 free(canon); 1385 free(ks_tuple); 1386 } 1387 1388 void 1389 kadmin_getprinc(int argc, char *argv[]) 1390 { 1391 kadm5_principal_ent_rec dprinc; 1392 krb5_principal princ = NULL; 1393 krb5_error_code retval; 1394 const char *polname, *noexist; 1395 char *canon = NULL, *princstr = NULL, *modprincstr = NULL; 1396 char **sp = NULL, **attrstrs = NULL; 1397 int i; 1398 1399 if (!(argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) { 1400 error(_("usage: get_principal [-terse] principal\n")); 1401 return; 1402 } 1403 1404 memset(&dprinc, 0, sizeof(dprinc)); 1405 1406 retval = kadmin_parse_name(argv[argc - 1], &princ); 1407 if (retval) { 1408 com_err("get_principal", retval, _("while parsing principal")); 1409 return; 1410 } 1411 retval = krb5_unparse_name(context, princ, &canon); 1412 if (retval) { 1413 com_err("get_principal", retval, _("while canonicalizing principal")); 1414 goto cleanup; 1415 } 1416 retval = kadm5_get_principal(handle, princ, &dprinc, 1417 KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA); 1418 if (retval) { 1419 com_err("get_principal", retval, _("while retrieving \"%s\"."), canon); 1420 goto cleanup; 1421 } 1422 retval = krb5_unparse_name(context, dprinc.principal, &princstr); 1423 if (retval) { 1424 com_err("get_principal", retval, _("while unparsing principal")); 1425 goto cleanup; 1426 } 1427 retval = krb5_unparse_name(context, dprinc.mod_name, &modprincstr); 1428 if (retval) { 1429 com_err("get_principal", retval, _("while unparsing principal")); 1430 goto cleanup; 1431 } 1432 if (argc == 2) { 1433 printf(_("Principal: %s\n"), princstr); 1434 printf(_("Expiration date: %s\n"), dprinc.princ_expire_time ? 1435 strdate(dprinc.princ_expire_time) : _("[never]")); 1436 printf(_("Last password change: %s\n"), dprinc.last_pwd_change ? 1437 strdate(dprinc.last_pwd_change) : _("[never]")); 1438 printf(_("Password expiration date: %s\n"), 1439 dprinc.pw_expiration ? 1440 strdate(dprinc.pw_expiration) : _("[never]")); 1441 printf(_("Maximum ticket life: %s\n"), strdur(dprinc.max_life)); 1442 printf(_("Maximum renewable life: %s\n"), 1443 strdur(dprinc.max_renewable_life)); 1444 printf(_("Last modified: %s (%s)\n"), strdate(dprinc.mod_date), 1445 modprincstr); 1446 printf(_("Last successful authentication: %s\n"), 1447 dprinc.last_success ? strdate(dprinc.last_success) : 1448 _("[never]")); 1449 printf("Last failed authentication: %s\n", 1450 dprinc.last_failed ? strdate(dprinc.last_failed) : 1451 "[never]"); 1452 printf(_("Failed password attempts: %d\n"), 1453 dprinc.fail_auth_count); 1454 printf(_("Number of keys: %d\n"), dprinc.n_key_data); 1455 for (i = 0; i < dprinc.n_key_data; i++) { 1456 krb5_key_data *key_data = &dprinc.key_data[i]; 1457 char enctype[BUFSIZ], salttype[BUFSIZ]; 1458 char *deprecated = ""; 1459 1460 if (krb5_enctype_to_name(key_data->key_data_type[0], FALSE, 1461 enctype, sizeof(enctype))) 1462 snprintf(enctype, sizeof(enctype), _("<Encryption type 0x%x>"), 1463 key_data->key_data_type[0]); 1464 if (!krb5_c_valid_enctype(key_data->key_data_type[0])) 1465 deprecated = "UNSUPPORTED:"; 1466 else if (krb5int_c_deprecated_enctype(key_data->key_data_type[0])) 1467 deprecated = "DEPRECATED:"; 1468 printf("Key: vno %d, %s%s", key_data->key_data_kvno, deprecated, 1469 enctype); 1470 if (key_data->key_data_ver > 1 && 1471 key_data->key_data_type[1] != KRB5_KDB_SALTTYPE_NORMAL) { 1472 if (krb5_salttype_to_string(key_data->key_data_type[1], 1473 salttype, sizeof(salttype))) 1474 snprintf(salttype, sizeof(salttype), _("<Salt type 0x%x>"), 1475 key_data->key_data_type[1]); 1476 printf(":%s", salttype); 1477 } 1478 printf("\n"); 1479 } 1480 printf(_("MKey: vno %d\n"), dprinc.mkvno); 1481 1482 printf(_("Attributes:")); 1483 retval = krb5_flags_to_strings(dprinc.attributes, &attrstrs); 1484 if (retval) { 1485 com_err("get_principal", retval, _("while printing flags")); 1486 return; 1487 } 1488 for (sp = attrstrs; sp != NULL && *sp != NULL; sp++) { 1489 printf(" %s", *sp); 1490 free(*sp); 1491 } 1492 free(attrstrs); 1493 printf("\n"); 1494 polname = (dprinc.policy != NULL) ? dprinc.policy : _("[none]"); 1495 noexist = (dprinc.policy != NULL && !policy_exists(dprinc.policy)) ? 1496 _(" [does not exist]") : ""; 1497 printf(_("Policy: %s%s\n"), polname, noexist); 1498 } else { 1499 printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"" 1500 "\t%d\t%d\t%d\t%d\t%d", 1501 princstr, dprinc.princ_expire_time, dprinc.last_pwd_change, 1502 dprinc.pw_expiration, dprinc.max_life, modprincstr, 1503 dprinc.mod_date, dprinc.attributes, dprinc.kvno, 1504 dprinc.mkvno, dprinc.policy ? dprinc.policy : "[none]", 1505 dprinc.max_renewable_life, dprinc.last_success, 1506 dprinc.last_failed, dprinc.fail_auth_count, 1507 dprinc.n_key_data); 1508 for (i = 0; i < dprinc.n_key_data; i++) 1509 printf("\t%d\t%d\t%d\t%d", 1510 dprinc.key_data[i].key_data_ver, 1511 dprinc.key_data[i].key_data_kvno, 1512 dprinc.key_data[i].key_data_type[0], 1513 dprinc.key_data[i].key_data_type[1]); 1514 printf("\n"); 1515 } 1516 cleanup: 1517 krb5_free_principal(context, princ); 1518 kadm5_free_principal_ent(handle, &dprinc); 1519 free(canon); 1520 free(princstr); 1521 free(modprincstr); 1522 } 1523 1524 void 1525 kadmin_getprincs(int argc, char *argv[]) 1526 { 1527 krb5_error_code retval; 1528 char *expr, **names; 1529 int i, count; 1530 1531 expr = NULL; 1532 if (!(argc == 1 || (argc == 2 && (expr = argv[1])))) { 1533 error(_("usage: get_principals [expression]\n")); 1534 return; 1535 } 1536 retval = kadm5_get_principals(handle, expr, &names, &count); 1537 if (retval) { 1538 com_err("get_principals", retval, _("while retrieving list.")); 1539 return; 1540 } 1541 for (i = 0; i < count; i++) 1542 printf("%s\n", names[i]); 1543 kadm5_free_name_list(handle, names, count); 1544 } 1545 1546 static int 1547 kadmin_parse_policy_args(int argc, char *argv[], kadm5_policy_ent_t policy, 1548 long *mask, char *caller) 1549 { 1550 krb5_error_code retval; 1551 int i; 1552 time_t now, interval; 1553 1554 time(&now); 1555 *mask = 0; 1556 for (i = 1; i < argc - 1; i++) { 1557 if (!strcmp(argv[i], "-maxlife")) { 1558 if (++i > argc -2) 1559 return -1; 1560 interval = parse_interval(argv[i], now); 1561 if (interval == (time_t)-1) 1562 return -1; 1563 policy->pw_max_life = interval; 1564 *mask |= KADM5_PW_MAX_LIFE; 1565 continue; 1566 } else if (!strcmp(argv[i], "-minlife")) { 1567 if (++i > argc - 2) 1568 return -1; 1569 interval = parse_interval(argv[i], now); 1570 if (interval == (time_t)-1) 1571 return -1; 1572 policy->pw_min_life = interval; 1573 *mask |= KADM5_PW_MIN_LIFE; 1574 continue; 1575 } else if (!strcmp(argv[i], "-minlength")) { 1576 if (++i > argc - 2) 1577 return -1; 1578 policy->pw_min_length = atoi(argv[i]); 1579 *mask |= KADM5_PW_MIN_LENGTH; 1580 continue; 1581 } else if (!strcmp(argv[i], "-minclasses")) { 1582 if (++i > argc - 2) 1583 return -1; 1584 policy->pw_min_classes = atoi(argv[i]); 1585 *mask |= KADM5_PW_MIN_CLASSES; 1586 continue; 1587 } else if (!strcmp(argv[i], "-history")) { 1588 if (++i > argc - 2) 1589 return -1; 1590 policy->pw_history_num = atoi(argv[i]); 1591 *mask |= KADM5_PW_HISTORY_NUM; 1592 continue; 1593 } else if (strlen(argv[i]) == 11 && 1594 !strcmp(argv[i], "-maxfailure")) { 1595 if (++i > argc - 2) 1596 return -1; 1597 policy->pw_max_fail = atoi(argv[i]); 1598 *mask |= KADM5_PW_MAX_FAILURE; 1599 continue; 1600 } else if (strlen(argv[i]) == 21 && 1601 !strcmp(argv[i], "-failurecountinterval")) { 1602 if (++i > argc - 2) 1603 return -1; 1604 interval = parse_interval(argv[i], now); 1605 if (interval == (time_t)-1) 1606 return -1; 1607 policy->pw_failcnt_interval = interval; 1608 *mask |= KADM5_PW_FAILURE_COUNT_INTERVAL; 1609 continue; 1610 } else if (strlen(argv[i]) == 16 && 1611 !strcmp(argv[i], "-lockoutduration")) { 1612 if (++i > argc - 2) 1613 return -1; 1614 interval = parse_interval(argv[i], now); 1615 if (interval == (time_t)-1) 1616 return -1; 1617 policy->pw_lockout_duration = interval; 1618 *mask |= KADM5_PW_LOCKOUT_DURATION; 1619 continue; 1620 } else if (!strcmp(argv[i], "-allowedkeysalts")) { 1621 krb5_key_salt_tuple *ks_tuple = NULL; 1622 int n_ks_tuple = 0; 1623 1624 if (++i > argc - 2) 1625 return -1; 1626 if (strcmp(argv[i], "-")) { 1627 retval = krb5_string_to_keysalts(argv[i], ",", NULL, 0, 1628 &ks_tuple, &n_ks_tuple); 1629 if (retval) { 1630 com_err(caller, retval, _("while parsing keysalts %s"), 1631 argv[i]); 1632 return -1; 1633 } 1634 free(ks_tuple); 1635 policy->allowed_keysalts = argv[i]; 1636 } 1637 *mask |= KADM5_POLICY_ALLOWED_KEYSALTS; 1638 continue; 1639 } else 1640 return -1; 1641 } 1642 if (i != argc -1) { 1643 error(_("%s: parser lost count!\n"), caller); 1644 return -1; 1645 } else 1646 return 0; 1647 } 1648 1649 static void 1650 kadmin_addmodpol_usage(char *func) 1651 { 1652 error(_("usage; %s [options] policy\n"), func); 1653 error(_("\toptions are:\n")); 1654 error(_("\t\t[-maxlife time] [-minlife time] [-minlength length]\n" 1655 "\t\t[-minclasses number] [-history number]\n" 1656 "\t\t[-maxfailure number] [-failurecountinterval time]\n" 1657 "\t\t[-allowedkeysalts keysalts]\n")); 1658 error(_("\t\t[-lockoutduration time]\n")); 1659 } 1660 1661 void 1662 kadmin_addpol(int argc, char *argv[]) 1663 { 1664 krb5_error_code retval; 1665 long mask; 1666 kadm5_policy_ent_rec policy; 1667 1668 memset(&policy, 0, sizeof(policy)); 1669 if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) { 1670 kadmin_addmodpol_usage("add_policy"); 1671 return; 1672 } 1673 policy.policy = argv[argc - 1]; 1674 mask |= KADM5_POLICY; 1675 retval = kadm5_create_policy(handle, &policy, mask); 1676 if (retval) { 1677 com_err("add_policy", retval, _("while creating policy \"%s\"."), 1678 policy.policy); 1679 } 1680 } 1681 1682 void 1683 kadmin_modpol(int argc, char *argv[]) 1684 { 1685 krb5_error_code retval; 1686 long mask; 1687 kadm5_policy_ent_rec policy; 1688 1689 memset(&policy, 0, sizeof(policy)); 1690 if (kadmin_parse_policy_args(argc, argv, &policy, &mask, 1691 "modify_policy")) { 1692 kadmin_addmodpol_usage("modify_policy"); 1693 return; 1694 } 1695 policy.policy = argv[argc - 1]; 1696 retval = kadm5_modify_policy(handle, &policy, mask); 1697 if (retval) { 1698 com_err("modify_policy", retval, _("while modifying policy \"%s\"."), 1699 policy.policy); 1700 } 1701 } 1702 1703 void 1704 kadmin_delpol(int argc, char *argv[]) 1705 { 1706 krb5_error_code retval; 1707 char reply[5]; 1708 1709 if (!(argc == 2 || (argc == 3 && !strcmp("-force", argv[1])))) { 1710 error(_("usage: delete_policy [-force] policy\n")); 1711 return; 1712 } 1713 if (argc == 2 && !script_mode) { 1714 printf(_("Are you sure you want to delete the policy \"%s\"? " 1715 "(yes/no): "), argv[1]); 1716 fgets(reply, sizeof(reply), stdin); 1717 if (strcmp("yes\n", reply)) { 1718 fprintf(stderr, _("Policy \"%s\" not deleted.\n"), argv[1]); 1719 return; 1720 } 1721 } 1722 retval = kadm5_delete_policy(handle, argv[argc - 1]); 1723 if (retval) { 1724 com_err("delete_policy:", retval, _("while deleting policy \"%s\""), 1725 argv[argc - 1]); 1726 } 1727 } 1728 1729 void 1730 kadmin_getpol(int argc, char *argv[]) 1731 { 1732 krb5_error_code retval; 1733 kadm5_policy_ent_rec policy; 1734 1735 if (!(argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) { 1736 error(_("usage: get_policy [-terse] policy\n")); 1737 return; 1738 } 1739 retval = kadm5_get_policy(handle, argv[argc - 1], &policy); 1740 if (retval) { 1741 com_err("get_policy", retval, _("while retrieving policy \"%s\"."), 1742 argv[argc - 1]); 1743 return; 1744 } 1745 if (argc == 2) { 1746 printf(_("Policy: %s\n"), policy.policy); 1747 printf(_("Maximum password life: %s\n"), strdur(policy.pw_max_life)); 1748 printf(_("Minimum password life: %s\n"), strdur(policy.pw_min_life)); 1749 printf(_("Minimum password length: %ld\n"), policy.pw_min_length); 1750 printf(_("Minimum number of password character classes: %ld\n"), 1751 policy.pw_min_classes); 1752 printf(_("Number of old keys kept: %ld\n"), policy.pw_history_num); 1753 printf(_("Maximum password failures before lockout: %lu\n"), 1754 (unsigned long)policy.pw_max_fail); 1755 printf(_("Password failure count reset interval: %s\n"), 1756 strdur(policy.pw_failcnt_interval)); 1757 printf(_("Password lockout duration: %s\n"), 1758 strdur(policy.pw_lockout_duration)); 1759 if (policy.allowed_keysalts != NULL) 1760 printf(_("Allowed key/salt types: %s\n"), policy.allowed_keysalts); 1761 } else { 1762 /* Output 0 where we used to output policy_refcnt. */ 1763 printf("\"%s\"\t%ld\t%ld\t%ld\t%ld\t%ld\t0\t%lu\t%ld\t%ld\t%s\n", 1764 policy.policy, policy.pw_max_life, policy.pw_min_life, 1765 policy.pw_min_length, policy.pw_min_classes, 1766 policy.pw_history_num, (unsigned long)policy.pw_max_fail, 1767 (long)policy.pw_failcnt_interval, 1768 (long)policy.pw_lockout_duration, 1769 (policy.allowed_keysalts == NULL) ? "-" : 1770 policy.allowed_keysalts); 1771 } 1772 kadm5_free_policy_ent(handle, &policy); 1773 } 1774 1775 void 1776 kadmin_getpols(int argc, char *argv[]) 1777 { 1778 krb5_error_code retval; 1779 char *expr, **names; 1780 int i, count; 1781 1782 expr = NULL; 1783 if (!(argc == 1 || (argc == 2 && (expr = argv[1])))) { 1784 error(_("usage: get_policies [expression]\n")); 1785 return; 1786 } 1787 retval = kadm5_get_policies(handle, expr, &names, &count); 1788 if (retval) { 1789 com_err("get_policies", retval, _("while retrieving list.")); 1790 return; 1791 } 1792 for (i = 0; i < count; i++) 1793 printf("%s\n", names[i]); 1794 kadm5_free_name_list(handle, names, count); 1795 } 1796 1797 void 1798 kadmin_getprivs(int argc, char *argv[]) 1799 { 1800 static char *privs[] = {"INQUIRE", "ADD", "MODIFY", "DELETE"}; 1801 krb5_error_code retval; 1802 size_t i; 1803 long plist; 1804 1805 if (argc != 1) { 1806 error(_("usage: get_privs\n")); 1807 return; 1808 } 1809 retval = kadm5_get_privs(handle, &plist); 1810 if (retval) { 1811 com_err("get_privs", retval, _("while retrieving privileges")); 1812 return; 1813 } 1814 printf(_("current privileges:")); 1815 for (i = 0; i < sizeof (privs) / sizeof (char *); i++) { 1816 if (plist & 1 << i) 1817 printf(" %s", privs[i]); 1818 } 1819 printf("\n"); 1820 } 1821 1822 void 1823 kadmin_purgekeys(int argc, char *argv[]) 1824 { 1825 kadm5_ret_t retval; 1826 int keepkvno = -1; 1827 char *pname = NULL, *canon = NULL; 1828 krb5_principal princ; 1829 1830 if (argc == 4 && strcmp(argv[1], "-keepkvno") == 0) { 1831 keepkvno = atoi(argv[2]); 1832 pname = argv[3]; 1833 } else if (argc == 3 && strcmp(argv[1], "-all") == 0) { 1834 keepkvno = KRB5_INT32_MAX; 1835 pname = argv[2]; 1836 } else if (argc == 2) { 1837 pname = argv[1]; 1838 } 1839 if (pname == NULL) { 1840 error(_("usage: purgekeys [-all|-keepkvno oldest_kvno_to_keep] " 1841 "principal\n")); 1842 return; 1843 } 1844 1845 retval = kadmin_parse_name(pname, &princ); 1846 if (retval) { 1847 com_err("purgekeys", retval, _("while parsing principal")); 1848 return; 1849 } 1850 1851 retval = krb5_unparse_name(context, princ, &canon); 1852 if (retval) { 1853 com_err("purgekeys", retval, _("while canonicalizing principal")); 1854 goto cleanup; 1855 } 1856 1857 retval = kadm5_purgekeys(handle, princ, keepkvno); 1858 if (retval) { 1859 com_err("purgekeys", retval, 1860 _("while purging keys for principal \"%s\""), canon); 1861 goto cleanup; 1862 } 1863 1864 if (keepkvno == KRB5_INT32_MAX) 1865 info(_("All keys for principal \"%s\" removed.\n"), canon); 1866 else 1867 info(_("Old keys for principal \"%s\" purged.\n"), canon); 1868 cleanup: 1869 krb5_free_principal(context, princ); 1870 free(canon); 1871 return; 1872 } 1873 1874 void 1875 kadmin_getstrings(int argc, char *argv[]) 1876 { 1877 kadm5_ret_t retval; 1878 char *pname, *canon = NULL; 1879 krb5_principal princ = NULL; 1880 krb5_string_attr *strings = NULL; 1881 int count, i; 1882 1883 if (argc != 2) { 1884 error(_("usage: get_strings principal\n")); 1885 return; 1886 } 1887 pname = argv[1]; 1888 1889 retval = kadmin_parse_name(pname, &princ); 1890 if (retval) { 1891 com_err("get_strings", retval, _("while parsing principal")); 1892 return; 1893 } 1894 1895 retval = krb5_unparse_name(context, princ, &canon); 1896 if (retval) { 1897 com_err("get_strings", retval, _("while canonicalizing principal")); 1898 goto cleanup; 1899 } 1900 1901 retval = kadm5_get_strings(handle, princ, &strings, &count); 1902 if (retval) { 1903 com_err("get_strings", retval, 1904 _("while getting attributes for principal \"%s\""), canon); 1905 goto cleanup; 1906 } 1907 1908 if (count == 0) 1909 printf(_("(No string attributes.)\n")); 1910 for (i = 0; i < count; i++) 1911 printf("%s: %s\n", strings[i].key, strings[i].value); 1912 kadm5_free_strings(handle, strings, count); 1913 1914 cleanup: 1915 krb5_free_principal(context, princ); 1916 free(canon); 1917 return; 1918 } 1919 1920 void 1921 kadmin_setstring(int argc, char *argv[]) 1922 { 1923 kadm5_ret_t retval; 1924 char *pname, *canon = NULL, *key, *value; 1925 krb5_principal princ = NULL; 1926 1927 if (argc != 4) { 1928 error(_("usage: set_string principal key value\n")); 1929 return; 1930 } 1931 pname = argv[1]; 1932 key = argv[2]; 1933 value = argv[3]; 1934 1935 retval = kadmin_parse_name(pname, &princ); 1936 if (retval) { 1937 com_err("set_string", retval, _("while parsing principal")); 1938 return; 1939 } 1940 1941 retval = krb5_unparse_name(context, princ, &canon); 1942 if (retval) { 1943 com_err("set_string", retval, _("while canonicalizing principal")); 1944 goto cleanup; 1945 } 1946 1947 retval = kadm5_set_string(handle, princ, key, value); 1948 if (retval) { 1949 com_err("set_string", retval, 1950 _("while setting attribute on principal \"%s\""), canon); 1951 goto cleanup; 1952 } 1953 1954 info(_("Attribute set for principal \"%s\".\n"), canon); 1955 cleanup: 1956 krb5_free_principal(context, princ); 1957 free(canon); 1958 return; 1959 } 1960 1961 void 1962 kadmin_delstring(int argc, char *argv[]) 1963 { 1964 kadm5_ret_t retval; 1965 char *pname, *canon = NULL, *key; 1966 krb5_principal princ = NULL; 1967 1968 if (argc != 3) { 1969 error(_("usage: del_string principal key\n")); 1970 return; 1971 } 1972 pname = argv[1]; 1973 key = argv[2]; 1974 1975 retval = kadmin_parse_name(pname, &princ); 1976 if (retval) { 1977 com_err("delstring", retval, _("while parsing principal")); 1978 return; 1979 } 1980 1981 retval = krb5_unparse_name(context, princ, &canon); 1982 if (retval) { 1983 com_err("del_string", retval, _("while canonicalizing principal")); 1984 goto cleanup; 1985 } 1986 1987 retval = kadm5_set_string(handle, princ, key, NULL); 1988 if (retval) { 1989 com_err("del_string", retval, 1990 _("while deleting attribute from principal \"%s\""), canon); 1991 goto cleanup; 1992 } 1993 1994 info(_("Attribute removed from principal \"%s\".\n"), canon); 1995 cleanup: 1996 krb5_free_principal(context, princ); 1997 free(canon); 1998 return; 1999 } 2000