1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 8 * 9 * Openvision retains the copyright to derivative works of 10 * this source code. Do *NOT* create a derivative of this 11 * source code before consulting with your legal department. 12 * Do *NOT* integrate *ANY* of this source code into another 13 * product before consulting with your legal department. 14 * 15 * For further information, read the top-level Openvision 16 * copyright which is contained in the top-level MIT Kerberos 17 * copyright. 18 * 19 * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 20 * 21 */ 22 23 24 /* 25 * admin/edit/kdb5_edit.c 26 * 27 * (C) Copyright 1990,1991, 1996 by the Massachusetts Institute of Technology. 28 * All Rights Reserved. 29 * 30 * Export of this software from the United States of America may 31 * require a specific license from the United States Government. 32 * It is the responsibility of any person or organization contemplating 33 * export to obtain such a license before exporting. 34 * 35 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 36 * distribute this software and its documentation for any purpose and 37 * without fee is hereby granted, provided that the above copyright 38 * notice appear in all copies and that both that copyright notice and 39 * this permission notice appear in supporting documentation, and that 40 * the name of M.I.T. not be used in advertising or publicity pertaining 41 * to distribution of the software without specific, written prior 42 * permission. Furthermore if you modify this software you must label 43 * your software as modified software and not distribute it in such a 44 * fashion that it might be confused with the original M.I.T. software. 45 * M.I.T. makes no representations about the suitability of 46 * this software for any purpose. It is provided "as is" without express 47 * or implied warranty. 48 * 49 * 50 * Edit a KDC database. 51 */ 52 53 /* 54 * Copyright (C) 1998 by the FundsXpress, INC. 55 * 56 * All rights reserved. 57 * 58 * Export of this software from the United States of America may require 59 * a specific license from the United States Government. It is the 60 * responsibility of any person or organization contemplating export to 61 * obtain such a license before exporting. 62 * 63 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 64 * distribute this software and its documentation for any purpose and 65 * without fee is hereby granted, provided that the above copyright 66 * notice appear in all copies and that both that copyright notice and 67 * this permission notice appear in supporting documentation, and that 68 * the name of FundsXpress. not be used in advertising or publicity pertaining 69 * to distribution of the software without specific, written prior 70 * permission. FundsXpress makes no representations about the suitability of 71 * this software for any purpose. It is provided "as is" without express 72 * or implied warranty. 73 * 74 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 75 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 76 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 77 */ 78 79 /* 80 * Yes, I know this is a hack, but we need admin.h without including the 81 * rpc.h header. Additionally, our rpc.h header brings in 82 * a des.h header which causes other problems. 83 */ 84 #define _RPC_RPC_H 85 86 #include <stdio.h> 87 #include <k5-int.h> 88 #include <kadm5/admin.h> 89 #include <rpc/types.h> 90 #include <krb5/adm_proto.h> 91 #include <rpc/xdr.h> 92 #include <time.h> 93 #include <libintl.h> 94 #include <locale.h> 95 #include "kdb5_util.h" 96 97 char *Err_no_master_msg = "Master key not entered!\n"; 98 char *Err_no_database = "Database not currently opened!\n"; 99 100 /* 101 * XXX Ick, ick, ick. These global variables shouldn't be global.... 102 */ 103 char *mkey_password = 0; 104 105 /* 106 * I can't figure out any way for this not to be global, given how ss 107 * works. 108 */ 109 110 int exit_status = 0; 111 krb5_context util_context; 112 kadm5_config_params global_params; 113 114 void usage() 115 { 116 fprintf(stderr, "%s: " 117 "kdb5_util [-x db_args]* [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname]\n" 118 "\t [-sf stashfilename] [-P password] [-m] cmd [cmd_options]\n" 119 "\tcreate [-s]\n" 120 "\tdestroy [-f]\n" 121 "\tstash [-f keyfile]\n" 122 "\tdump [-old] [-ov] [-b6] [-verbose] [filename [princs...]]\n" 123 "\t [-mkey_convert] [-new_mkey_file mkey_file]\n" 124 "\t [-rev] [-recurse] [filename [princs...]]\n" 125 "\tload [-old] [-ov] [-b6] [-verbose] [-update] filename\n" 126 "\tark [-e etype_list] principal\n" 127 "\nwhere,\n\t[-x db_args]* - any number of database specific arguments.\n" 128 "\t\t\tLook at each database documentation for supported arguments\n", 129 gettext("Usage")); 130 exit(1); 131 } 132 133 krb5_keyblock master_key; 134 extern krb5_principal master_princ; 135 krb5_db_entry master_entry; 136 int valid_master_key = 0; 137 138 char *progname; 139 krb5_boolean manual_mkey = FALSE; 140 krb5_boolean dbactive = FALSE; 141 142 static int open_db_and_mkey(void); 143 144 static void add_random_key(int, char **); 145 146 typedef void (*cmd_func)(int, char **); 147 148 struct _cmd_table { 149 char *name; 150 cmd_func func; 151 int opendb; 152 } cmd_table[] = { 153 {"create", kdb5_create, 0}, 154 {"destroy", kdb5_destroy, 1}, 155 {"stash", kdb5_stash, 1}, 156 {"dump", dump_db, 1}, 157 {"load", load_db, 0}, 158 {"ark", add_random_key, 1}, 159 {NULL, NULL, 0}, 160 }; 161 162 static struct _cmd_table *cmd_lookup(name) 163 char *name; 164 { 165 struct _cmd_table *cmd = cmd_table; 166 while (cmd->name) { 167 if (strcmp(cmd->name, name) == 0) 168 return cmd; 169 else 170 cmd++; 171 } 172 173 return NULL; 174 } 175 176 #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL)) 177 178 char **db5util_db_args = NULL; 179 int db5util_db_args_size = 0; 180 181 static void extended_com_err_fn (const char *myprog, errcode_t code, 182 const char *fmt, va_list args) 183 { 184 const char *emsg; 185 if (code) { 186 emsg = krb5_get_error_message (util_context, code); 187 fprintf (stderr, "%s: %s ", myprog, emsg); 188 krb5_free_error_message (util_context, emsg); 189 } else { 190 fprintf (stderr, "%s: ", myprog); 191 } 192 vfprintf (stderr, fmt, args); 193 fprintf (stderr, "\n"); 194 } 195 196 int add_db_arg(char *arg) 197 { 198 char **temp; 199 db5util_db_args_size++; 200 temp = realloc(db5util_db_args, 201 sizeof(char *) * (db5util_db_args_size + 1)); 202 if (temp == NULL) 203 return 0; 204 db5util_db_args = temp; 205 db5util_db_args[db5util_db_args_size-1] = arg; 206 db5util_db_args[db5util_db_args_size] = NULL; 207 return 1; 208 } 209 210 int main(argc, argv) 211 int argc; 212 char *argv[]; 213 { 214 struct _cmd_table *cmd = NULL; 215 char *koptarg, **cmd_argv; 216 char *db_name_tmp = NULL; 217 int cmd_argc; 218 krb5_error_code retval; 219 220 (void) setlocale(LC_ALL, ""); 221 set_com_err_hook(extended_com_err_fn); 222 223 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 224 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 225 #endif 226 227 (void) textdomain(TEXT_DOMAIN); 228 229 Err_no_master_msg = gettext("Master key not entered!\n"); 230 Err_no_database = gettext("Database not currently opened!\n"); 231 232 /* 233 * Solaris Kerberos: 234 * Ensure that "progname" is set before calling com_err. 235 */ 236 progname = (strrchr(argv[0], '/') ? 237 strrchr(argv[0], '/') + 1 : argv[0]); 238 239 retval = kadm5_init_krb5_context(&util_context); 240 if (retval) { 241 com_err (progname, retval, 242 gettext("while initializing Kerberos code")); 243 exit(1); 244 } 245 246 cmd_argv = (char **) malloc(sizeof(char *)*argc); 247 if (cmd_argv == NULL) { 248 com_err(progname, ENOMEM, 249 gettext("while creating sub-command arguments")); 250 exit(1); 251 } 252 memset(cmd_argv, 0, sizeof(char *)*argc); 253 cmd_argc = 1; 254 255 argv++; argc--; 256 while (*argv) { 257 if (strcmp(*argv, "-P") == 0 && ARG_VAL) { 258 mkey_password = koptarg; 259 manual_mkey = TRUE; 260 } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) { 261 global_params.dbname = koptarg; 262 global_params.mask |= KADM5_CONFIG_DBNAME; 263 264 db_name_tmp = malloc( strlen(global_params.dbname) + sizeof("dbname=")); 265 if( db_name_tmp == NULL ) 266 { 267 com_err(progname, ENOMEM, "while parsing command arguments"); 268 exit(1); 269 } 270 271 strcpy( db_name_tmp, "dbname="); 272 strcat( db_name_tmp, global_params.dbname ); 273 274 if (!add_db_arg(db_name_tmp)) { 275 com_err(progname, ENOMEM, "while parsing command arguments\n"); 276 exit(1); 277 } 278 279 } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) { 280 if (!add_db_arg(koptarg)) { 281 com_err(progname, ENOMEM, "while parsing command arguments\n"); 282 exit(1); 283 } 284 285 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { 286 global_params.realm = koptarg; 287 global_params.mask |= KADM5_CONFIG_REALM; 288 /* not sure this is really necessary */ 289 if ((retval = krb5_set_default_realm(util_context, 290 global_params.realm))) { 291 com_err(progname, retval, 292 gettext("while setting default " 293 "realm name")); 294 exit(1); 295 } 296 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { 297 if (krb5_string_to_enctype(koptarg, &global_params.enctype)) { 298 /* Solaris Kerberos */ 299 com_err(progname, 0, gettext("%s is an invalid enctype"), koptarg); 300 } 301 else 302 global_params.mask |= KADM5_CONFIG_ENCTYPE; 303 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { 304 global_params.mkey_name = koptarg; 305 global_params.mask |= KADM5_CONFIG_MKEY_NAME; 306 } else if (((strcmp(*argv, "-sf") == 0) 307 /* SUNWresync121 - carry the old -f forward too */ 308 || (strcmp(*argv, "-f") == 0)) && ARG_VAL) { 309 global_params.stash_file = koptarg; 310 global_params.mask |= KADM5_CONFIG_STASH_FILE; 311 } else if (strcmp(*argv, "-m") == 0) { 312 manual_mkey = TRUE; 313 global_params.mkey_from_kbd = 1; 314 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 315 } else if (cmd_lookup(*argv) != NULL) { 316 if (cmd_argv[0] == NULL) 317 cmd_argv[0] = *argv; 318 else 319 usage(); 320 } else { 321 cmd_argv[cmd_argc++] = *argv; 322 } 323 argv++; argc--; 324 } 325 326 if (cmd_argv[0] == NULL) 327 usage(); 328 329 if( !util_context->default_realm ) 330 { 331 char *temp = NULL; 332 retval = krb5_get_default_realm(util_context, &temp); 333 if( retval ) 334 { 335 com_err (progname, retval, "while getting default realm"); 336 exit(1); 337 } 338 util_context->default_realm = temp; 339 } 340 341 retval = kadm5_get_config_params(util_context, 1, 342 &global_params, &global_params); 343 if (retval) { 344 /* Solaris Kerberos */ 345 com_err(progname, retval, 346 gettext("while retreiving configuration parameters")); 347 exit(1); 348 } 349 350 /* 351 * Dump creates files which should not be world-readable. It is 352 * easiest to do a single umask call here. 353 */ 354 (void) umask(077); 355 356 (void) memset(&master_key, 0, sizeof (krb5_keyblock)); 357 358 if ((global_params.enctype != ENCTYPE_UNKNOWN) && 359 (!krb5_c_valid_enctype(global_params.enctype))) { 360 /* Solaris Kerberos */ 361 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, 362 gettext("while setting up enctype %d"), global_params.enctype); 363 exit(1); 364 } 365 366 cmd = cmd_lookup(cmd_argv[0]); 367 if (cmd->opendb && open_db_and_mkey()) 368 return exit_status; 369 370 if (global_params.iprop_enabled == TRUE) 371 ulog_set_role(util_context, IPROP_MASTER); 372 else 373 ulog_set_role(util_context, IPROP_NULL); 374 375 (*cmd->func)(cmd_argc, cmd_argv); 376 377 if( db_name_tmp ) 378 free( db_name_tmp ); 379 380 if( db5util_db_args ) 381 free(db5util_db_args); 382 383 kadm5_free_config_params(util_context, &global_params); 384 krb5_free_context(util_context); 385 return exit_status; 386 } 387 388 #if 0 389 /* 390 * This function is no longer used in kdb5_util (and it would no 391 * longer work, anyway). 392 */ 393 void set_dbname(argc, argv) 394 int argc; 395 char *argv[]; 396 { 397 krb5_error_code retval; 398 399 if (argc < 3) { 400 /* Solaris Kerberos */ 401 com_err(progname, 0, gettext("Too few arguments")); 402 com_err(progname, 0, gettext("Usage: %s dbpathname realmname"), 403 progname); 404 exit_status++; 405 return; 406 } 407 if (dbactive) { 408 if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) { 409 /* Solaris Kerberos */ 410 com_err(progname, retval, gettext("while closing previous database")); 411 exit_status++; 412 return; 413 } 414 if (valid_master_key) { 415 krb5_free_keyblock_contents(util_context, &master_key); 416 master_key.contents = NULL; 417 valid_master_key = 0; 418 } 419 krb5_free_principal(util_context, master_princ); 420 dbactive = FALSE; 421 } 422 423 /* Solaris Kerberos */ 424 (void) set_dbname_help(progname, argv[1]); 425 return; 426 } 427 #endif 428 429 /* 430 * open_db_and_mkey: Opens the KDC and policy database, and sets the 431 * global master_* variables. Sets dbactive to TRUE if the databases 432 * are opened, and valid_master_key to 1 if the global master 433 * variables are set properly. Returns 0 on success, and 1 on 434 * failure, but it is not considered a failure if the master key 435 * cannot be fetched (the master key stash file may not exist when the 436 * program is run). 437 */ 438 static int open_db_and_mkey() 439 { 440 krb5_error_code retval; 441 int nentries; 442 krb5_boolean more; 443 krb5_data scratch, pwd, seed; 444 445 dbactive = FALSE; 446 valid_master_key = 0; 447 448 if ((retval = krb5_db_open(util_context, db5util_db_args, 449 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) { 450 com_err(progname, retval, "while initializing database"); 451 exit_status++; 452 return(1); 453 } 454 455 /* assemble & parse the master key name */ 456 457 if ((retval = krb5_db_setup_mkey_name(util_context, 458 global_params.mkey_name, 459 global_params.realm, 460 0, &master_princ))) { 461 com_err(progname, retval, 462 gettext("while setting up master key name")); 463 exit_status++; 464 return(1); 465 } 466 nentries = 1; 467 if ((retval = krb5_db_get_principal(util_context, master_princ, 468 &master_entry, &nentries, &more))) { 469 com_err(progname, retval, 470 gettext("while retrieving master entry")); 471 exit_status++; 472 (void) krb5_db_fini(util_context); 473 return(1); 474 } else if (more) { 475 com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE, 476 gettext("while retrieving master entry")); 477 exit_status++; 478 (void) krb5_db_fini(util_context); 479 return(1); 480 } else if (!nentries) { 481 com_err(progname, KRB5_KDB_NOENTRY, 482 gettext("while retrieving master entry")); 483 exit_status++; 484 (void) krb5_db_fini(util_context); 485 return(1); 486 } 487 488 krb5_db_free_principal(util_context, &master_entry, nentries); 489 490 /* the databases are now open, and the master principal exists */ 491 dbactive = TRUE; 492 493 if (mkey_password) { 494 pwd.data = mkey_password; 495 pwd.length = strlen(mkey_password); 496 retval = krb5_principal2salt(util_context, master_princ, &scratch); 497 if (retval) { 498 com_err(progname, retval, 499 gettext("while calculated master key salt")); 500 /* Solaris Kerberos */ 501 exit_status++; 502 return(1); 503 } 504 505 /* If no encryption type is set, use the default */ 506 if (global_params.enctype == ENCTYPE_UNKNOWN) { 507 global_params.enctype = DEFAULT_KDC_ENCTYPE; 508 if (!krb5_c_valid_enctype(global_params.enctype)) 509 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, 510 gettext("while setting up enctype %d"), 511 global_params.enctype); 512 } 513 514 retval = krb5_c_string_to_key(util_context, global_params.enctype, 515 &pwd, &scratch, &master_key); 516 if (retval) { 517 com_err(progname, retval, 518 gettext("while transforming master key from password")); 519 /* Solaris Kerberos */ 520 exit_status++; 521 return(1); 522 } 523 free(scratch.data); 524 mkey_password = 0; 525 } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ, 526 global_params.enctype, 527 manual_mkey, FALSE, 528 global_params.stash_file, 529 0, &master_key))) { 530 com_err(progname, retval, 531 gettext("while reading master key")); 532 com_err(progname, 0, 533 gettext("Warning: proceeding without master key")); 534 /* 535 * Solaris Kerberos: We don't want to count as an error if for instance 536 * the stash file is not present and we are trying to automate 537 * propagation, which really doesn't need a master key to do so. 538 */ 539 if (retval != KRB5_KDB_CANTREAD_STORED) 540 exit_status++; 541 return(0); 542 } 543 if ((retval = krb5_db_verify_master_key(util_context, master_princ, 544 &master_key))) { 545 com_err(progname, retval, 546 gettext("while verifying master key")); 547 exit_status++; 548 krb5_free_keyblock_contents(util_context, &master_key); 549 return(1); 550 } 551 552 seed.length = master_key.length; 553 seed.data = (char *)master_key.contents; 554 555 if ((retval = krb5_c_random_seed(util_context, &seed))) { 556 com_err(progname, retval, 557 gettext("while initializing random key generator")); 558 exit_status++; 559 krb5_free_keyblock_contents(util_context, &master_key); 560 return(1); 561 } 562 563 valid_master_key = 1; 564 dbactive = TRUE; 565 return 0; 566 } 567 568 #ifdef HAVE_GETCWD 569 #undef getwd 570 #endif 571 572 int 573 quit() 574 { 575 krb5_error_code retval; 576 static krb5_boolean finished = 0; 577 578 if (finished) 579 return 0; 580 retval = krb5_db_fini(util_context); 581 krb5_free_keyblock_contents(util_context, &master_key); 582 finished = TRUE; 583 krb5_free_context(util_context); 584 if (retval && retval != KRB5_KDB_DBNOTINITED) { 585 com_err(progname, retval, gettext("while closing database")); 586 exit_status++; 587 return 1; 588 } 589 return 0; 590 } 591 592 static void 593 add_random_key(argc, argv) 594 int argc; 595 char **argv; 596 { 597 krb5_error_code ret; 598 krb5_principal princ; 599 krb5_db_entry dbent; 600 int n; 601 krb5_boolean more; 602 krb5_timestamp now; 603 604 krb5_key_salt_tuple *keysalts = NULL; 605 krb5_int32 num_keysalts = 0; 606 607 int free_keysalts; 608 /* Solaris Kerberos */ 609 char *me = progname; 610 char *ks_str = NULL; 611 char *pr_str; 612 613 if (argc < 2) 614 usage(); 615 for (argv++, argc--; *argv; argv++, argc--) { 616 if (!strcmp(*argv, "-e")) { 617 argv++; argc--; 618 ks_str = *argv; 619 continue; 620 } else 621 break; 622 } 623 if (argc < 1) 624 usage(); 625 pr_str = *argv; 626 ret = krb5_parse_name(util_context, pr_str, &princ); 627 if (ret) { 628 com_err(me, ret, gettext("while parsing principal name %s"), pr_str); 629 exit_status++; 630 return; 631 } 632 n = 1; 633 ret = krb5_db_get_principal(util_context, princ, &dbent, 634 &n, &more); 635 if (ret) { 636 com_err(me, ret, gettext("while fetching principal %s"), pr_str); 637 exit_status++; 638 return; 639 } 640 if (n != 1) { 641 fprintf(stderr, gettext("principal %s not found\n"), pr_str); 642 exit_status++; 643 return; 644 } 645 if (more) { 646 fprintf(stderr, gettext("principal %s not unique\n"), pr_str); 647 krb5_db_free_principal(util_context, &dbent, 1); 648 exit_status++; 649 return; 650 } 651 ret = krb5_string_to_keysalts(ks_str, 652 ", \t", ":.-", 0, 653 &keysalts, 654 &num_keysalts); 655 if (ret) { 656 com_err(me, ret, gettext("while parsing keysalts %s"), ks_str); 657 exit_status++; 658 return; 659 } 660 if (!num_keysalts || keysalts == NULL) { 661 num_keysalts = global_params.num_keysalts; 662 keysalts = global_params.keysalts; 663 free_keysalts = 0; 664 } else 665 free_keysalts = 1; 666 ret = krb5_dbe_ark(util_context, &master_key, 667 keysalts, num_keysalts, 668 &dbent); 669 if (free_keysalts) 670 free(keysalts); 671 if (ret) { 672 com_err(me, ret, gettext("while randomizing principal %s"), pr_str); 673 krb5_db_free_principal(util_context, &dbent, 1); 674 exit_status++; 675 return; 676 } 677 dbent.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 678 ret = krb5_timeofday(util_context, &now); 679 if (ret) { 680 com_err(me, ret, gettext("while getting time")); 681 krb5_db_free_principal(util_context, &dbent, 1); 682 exit_status++; 683 return; 684 } 685 ret = krb5_dbe_update_last_pwd_change(util_context, &dbent, now); 686 if (ret) { 687 com_err(me, ret, gettext("while setting changetime")); 688 krb5_db_free_principal(util_context, &dbent, 1); 689 exit_status++; 690 return; 691 } 692 ret = krb5_db_put_principal(util_context, &dbent, &n); 693 krb5_db_free_principal(util_context, &dbent, 1); 694 if (ret) { 695 com_err(me, ret, gettext("while saving principal %s"), pr_str); 696 exit_status++; 697 return; 698 } 699 printf("%s changed\n", pr_str); 700 } 701