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