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