1 /* 2 * Copyright 2007 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 #define KDB5_DISPATCH 90 #define KRB5_KDB5_DBM__ 91 #include <k5-int.h> 92 /* #define these to avoid an indirection function; for future implementations, 93 these may be redirected from a dispatch table/routine */ 94 #define krb5_dbm_db_set_name krb5_db_set_name 95 #define krb5_dbm_db_set_nonblocking krb5_db_set_nonblocking 96 #define krb5_dbm_db_init krb5_db_init 97 #define krb5_dbm_db_get_age krb5_db_get_age 98 #define krb5_dbm_db_create krb5_db_create 99 #define krb5_dbm_db_rename krb5_db_rename 100 #define krb5_dbm_db_get_principal krb5_db_get_principal 101 #define krb5_dbm_db_free_principal krb5_db_free_principal 102 #define krb5_dbm_db_put_principal krb5_db_put_principal 103 #define krb5_dbm_db_delete_principal krb5_db_delete_principal 104 #define krb5_dbm_db_lock krb5_db_lock 105 #define krb5_dbm_db_unlock krb5_db_unlock 106 #define krb5_dbm_db_set_lockmode krb5_db_set_lockmode 107 #define krb5_dbm_db_close_database krb5_db_close_database 108 #define krb5_dbm_db_open_database krb5_db_open_database 109 110 #include <kadm5/admin.h> 111 #include <rpc/types.h> 112 #include <rpc/xdr.h> 113 #include <kadm5/adb.h> 114 #include <time.h> 115 #include <libintl.h> 116 #include <locale.h> 117 #include "kdb5_util.h" 118 119 char *Err_no_master_msg = "Master key not entered!\n"; 120 char *Err_no_database = "Database not currently opened!\n"; 121 122 /* 123 * XXX Ick, ick, ick. These global variables shouldn't be global.... 124 */ 125 char *mkey_password = 0; 126 127 /* 128 * I can't figure out any way for this not to be global, given how ss 129 * works. 130 */ 131 132 int exit_status = 0; 133 krb5_context util_context; 134 osa_adb_policy_t policy_db; 135 kadm5_config_params global_params; 136 137 void usage() 138 { 139 fprintf(stderr, "%s: " 140 "kdb5_util [-r realm] [-d dbname] [-k mkeytype] [-M mkeyname]\n" 141 "\t [-f | -sf stashfilename] [-P password] [-m] cmd [cmd_options]\n" 142 "\tcreate [-s]\n" 143 "\tdestroy [-f]\n" 144 "\tstash [-f keyfile]\n" 145 "\tdump [-old] [-ov] [-b6] [-verbose] [filename [princs...]]\n" 146 "\t [-mkey_convert] [-new_mkey_file mkey_file]\n" 147 "\t [-rev] [-recurse] [filename [princs...]]\n" 148 "\tload [-old] [-ov] [-b6] [-verbose] [-update] filename\n" 149 #ifdef SUNWOFF 150 "\tload_v4 [-t] [-n] [-v] [-K] [-s stashfile] inputfile\n" 151 #endif 152 "\tark [-e etype_list] principal\n", 153 gettext("Usage")); 154 exit(1); 155 } 156 157 krb5_keyblock master_key; 158 extern krb5_principal master_princ; 159 krb5_db_entry master_entry; 160 int valid_master_key = 0; 161 int close_policy_db = 0; 162 163 char *progname; 164 krb5_boolean manual_mkey = FALSE; 165 krb5_boolean dbactive = FALSE; 166 167 static int open_db_and_mkey(void); 168 169 static void add_random_key(int, char **); 170 171 typedef void (*cmd_func)(int, char **); 172 173 struct _cmd_table { 174 char *name; 175 cmd_func func; 176 int opendb; 177 } cmd_table[] = { 178 {"create", kdb5_create, 0}, 179 {"destroy", kdb5_destroy, 1}, 180 {"stash", kdb5_stash, 1}, 181 {"dump", dump_db, 1}, 182 {"load", load_db, 0}, 183 {"ark", add_random_key, 1}, 184 {NULL, NULL, 0}, 185 }; 186 187 static struct _cmd_table *cmd_lookup(name) 188 char *name; 189 { 190 struct _cmd_table *cmd = cmd_table; 191 while (cmd->name) { 192 if (strcmp(cmd->name, name) == 0) 193 return cmd; 194 else 195 cmd++; 196 } 197 198 return NULL; 199 } 200 201 #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL)) 202 203 int main(argc, argv) 204 int argc; 205 char *argv[]; 206 { 207 struct _cmd_table *cmd = NULL; 208 char *koptarg, **cmd_argv; 209 int cmd_argc; 210 krb5_error_code retval; 211 212 (void) setlocale(LC_ALL, ""); 213 214 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 215 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ 216 #endif 217 218 (void) textdomain(TEXT_DOMAIN); 219 220 Err_no_master_msg = gettext("Master key not entered!\n"); 221 Err_no_database = gettext("Database not currently opened!\n"); 222 223 retval = krb5_init_context(&util_context); 224 if (retval) { 225 com_err (progname, retval, 226 gettext("while initializing Kerberos code")); 227 exit(1); 228 } 229 progname = (strrchr(argv[0], '/') ? 230 strrchr(argv[0], '/') + 1 : argv[0]); 231 232 cmd_argv = (char **) malloc(sizeof(char *)*argc); 233 if (cmd_argv == NULL) { 234 com_err(progname, ENOMEM, 235 gettext("while creating sub-command arguments")); 236 exit(1); 237 } 238 memset(cmd_argv, 0, sizeof(char *)*argc); 239 cmd_argc = 1; 240 241 argv++; argc--; 242 while (*argv) { 243 if (strcmp(*argv, "-P") == 0 && ARG_VAL) { 244 mkey_password = koptarg; 245 manual_mkey = TRUE; 246 } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) { 247 global_params.dbname = koptarg; 248 global_params.mask |= KADM5_CONFIG_DBNAME; 249 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { 250 global_params.realm = koptarg; 251 global_params.mask |= KADM5_CONFIG_REALM; 252 /* not sure this is really necessary */ 253 if ((retval = krb5_set_default_realm(util_context, 254 global_params.realm))) { 255 com_err(progname, retval, 256 gettext("while setting default " 257 "realm name")); 258 exit(1); 259 } 260 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { 261 if (krb5_string_to_enctype(koptarg, 262 &global_params.enctype)) 263 com_err(argv[0], 0, 264 gettext("%s is an invalid enctype"), 265 koptarg); 266 else 267 global_params.mask |= KADM5_CONFIG_ENCTYPE; 268 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { 269 global_params.mkey_name = koptarg; 270 global_params.mask |= KADM5_CONFIG_MKEY_NAME; 271 } else if (((strcmp(*argv, "-sf") == 0) 272 /* SUNWresync121 - carry the old -f forward too */ 273 || (strcmp(*argv, "-f") == 0)) && ARG_VAL) { 274 global_params.stash_file = koptarg; 275 global_params.mask |= KADM5_CONFIG_STASH_FILE; 276 } else if (strcmp(*argv, "-m") == 0) { 277 manual_mkey = TRUE; 278 global_params.mkey_from_kbd = 1; 279 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 280 } else if (cmd_lookup(*argv) != NULL) { 281 if (cmd_argv[0] == NULL) 282 cmd_argv[0] = *argv; 283 else 284 usage(); 285 } else { 286 cmd_argv[cmd_argc++] = *argv; 287 } 288 argv++; argc--; 289 } 290 291 if (cmd_argv[0] == NULL) 292 usage(); 293 294 retval = kadm5_get_config_params(util_context, NULL, NULL, 295 &global_params, &global_params); 296 if (retval) { 297 com_err(argv[0], retval, 298 gettext("while retreiving configuration parameters")); 299 exit(1); 300 } 301 302 /* 303 * Dump creates files which should not be world-readable. It is 304 * easiest to do a single umask call here. 305 */ 306 (void) umask(077); 307 308 (void) memset(&master_key, 0, sizeof (krb5_keyblock)); 309 310 if ((global_params.enctype != ENCTYPE_UNKNOWN) && 311 (!krb5_c_valid_enctype(global_params.enctype))) { 312 com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, 313 gettext("while setting up enctype %d"), global_params.enctype); 314 } 315 316 cmd = cmd_lookup(cmd_argv[0]); 317 if (cmd->opendb && open_db_and_mkey()) 318 return exit_status; 319 320 if (global_params.iprop_enabled == TRUE) 321 ulog_set_role(util_context, IPROP_MASTER); 322 else 323 ulog_set_role(util_context, IPROP_NULL); 324 325 (*cmd->func)(cmd_argc, cmd_argv); 326 327 if(close_policy_db) { 328 (void) osa_adb_close_policy(policy_db); 329 } 330 kadm5_free_config_params(util_context, &global_params); 331 krb5_free_context(util_context); 332 return exit_status; 333 } 334 335 #if 0 336 /* 337 * This function is no longer used in kdb5_util (and it would no 338 * longer work, anyway). 339 */ 340 void set_dbname(argc, argv) 341 int argc; 342 char *argv[]; 343 { 344 krb5_error_code retval; 345 346 if (argc < 3) { 347 com_err(argv[0], 0, gettext("Too few arguments")); 348 com_err(argv[0], 0, gettext("Usage: %s dbpathname realmname"), 349 argv[0]); 350 exit_status++; 351 return; 352 } 353 if (dbactive) { 354 if ((retval = krb5_db_fini(util_context)) && 355 retval != KRB5_KDB_DBNOTINITED) { 356 com_err(argv[0], retval, 357 gettext("while closing previous database")); 358 exit_status++; 359 return; 360 } 361 if (valid_master_key) { 362 krb5_free_keyblock_contents(util_context, &master_key); 363 valid_master_key = 0; 364 } 365 krb5_free_principal(util_context, master_princ); 366 dbactive = FALSE; 367 } 368 369 (void) set_dbname_help(argv[0], argv[1]); 370 return; 371 } 372 #endif 373 374 /* 375 * open_db_and_mkey: Opens the KDC and policy database, and sets the 376 * global master_* variables. Sets dbactive to TRUE if the databases 377 * are opened, and valid_master_key to 1 if the global master 378 * variables are set properly. Returns 0 on success, and 1 on 379 * failure, but it is not considered a failure if the master key 380 * cannot be fetched (the master key stash file may not exist when the 381 * program is run). 382 */ 383 static int open_db_and_mkey() 384 { 385 krb5_error_code retval; 386 int nentries; 387 krb5_boolean more; 388 krb5_data scratch, pwd, seed; 389 390 dbactive = FALSE; 391 valid_master_key = 0; 392 393 if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { 394 com_err(progname, retval, 395 gettext("while setting active database to '%s'"), 396 global_params.dbname); 397 exit_status++; 398 return(1); 399 } 400 if ((retval = krb5_db_init(util_context))) { 401 com_err(progname, retval, 402 gettext("while initializing database")); 403 exit_status++; 404 return(1); 405 } 406 if ((retval = osa_adb_open_policy(&policy_db, &global_params))) { 407 com_err(progname, retval, 408 gettext("opening policy database")); 409 exit_status++; 410 return (1); 411 } 412 413 /* assemble & parse the master key name */ 414 415 if ((retval = krb5_db_setup_mkey_name(util_context, 416 global_params.mkey_name, 417 global_params.realm, 418 0, &master_princ))) { 419 com_err(progname, retval, 420 gettext("while setting up master key name")); 421 exit_status++; 422 return(1); 423 } 424 nentries = 1; 425 if ((retval = krb5_db_get_principal(util_context, master_princ, 426 &master_entry, &nentries, &more))) { 427 com_err(progname, retval, 428 gettext("while retrieving master entry")); 429 exit_status++; 430 (void) krb5_db_fini(util_context); 431 return(1); 432 } else if (more) { 433 com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE, 434 gettext("while retrieving master entry")); 435 exit_status++; 436 (void) krb5_db_fini(util_context); 437 return(1); 438 } else if (!nentries) { 439 com_err(progname, KRB5_KDB_NOENTRY, 440 gettext("while retrieving master entry")); 441 exit_status++; 442 (void) krb5_db_fini(util_context); 443 return(1); 444 } 445 446 krb5_db_free_principal(util_context, &master_entry, nentries); 447 448 /* the databases are now open, and the master principal exists */ 449 dbactive = TRUE; 450 451 if (mkey_password) { 452 pwd.data = mkey_password; 453 pwd.length = strlen(mkey_password); 454 retval = krb5_principal2salt(util_context, master_princ, &scratch); 455 if (retval) { 456 com_err(progname, retval, 457 gettext("while calculated master key salt")); 458 return(1); 459 } 460 461 /* If no encryption type is set, use the default */ 462 if (global_params.enctype == ENCTYPE_UNKNOWN) { 463 global_params.enctype = DEFAULT_KDC_ENCTYPE; 464 if (!krb5_c_valid_enctype(global_params.enctype)) 465 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, 466 gettext("while setting up enctype %d"), 467 global_params.enctype); 468 } 469 470 retval = krb5_c_string_to_key(util_context, 471 global_params.enctype, 472 &pwd, &scratch, &master_key); 473 if (retval) { 474 com_err(progname, retval, 475 gettext("while transforming master key from password")); 476 return(1); 477 } 478 free(scratch.data); 479 mkey_password = 0; 480 } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ, 481 global_params.enctype, 482 manual_mkey, FALSE, 483 global_params.stash_file, 484 0, &master_key))) { 485 com_err(progname, retval, 486 gettext("while reading master key")); 487 com_err(progname, 0, 488 gettext("Warning: proceeding without master key")); 489 /* 490 * Solaris Kerberos: We don't want to count as an error if for instance 491 * the stash file is not present and we are trying to automate 492 * propagation, which really doesn't need a master key to do so. 493 */ 494 if (retval != KRB5_KDB_CANTREAD_STORED) 495 exit_status++; 496 return(0); 497 } 498 if ((retval = krb5_db_verify_master_key(util_context, master_princ, 499 &master_key))) { 500 com_err(progname, retval, 501 gettext("while verifying master key")); 502 exit_status++; 503 krb5_free_keyblock_contents(util_context, &master_key); 504 return(1); 505 } 506 507 seed.length = master_key.length; 508 seed.data = (char *)master_key.contents; 509 510 if ((retval = krb5_c_random_seed(util_context, &seed))) { 511 com_err(progname, retval, 512 gettext("while initializing random key generator")); 513 exit_status++; 514 krb5_free_keyblock_contents(util_context, &master_key); 515 return(1); 516 } 517 518 valid_master_key = 1; 519 dbactive = TRUE; 520 return 0; 521 } 522 523 #ifdef HAVE_GETCWD 524 #undef getwd 525 #endif 526 527 int 528 quit() 529 { 530 krb5_error_code retval; 531 static krb5_boolean finished = 0; 532 533 if (finished) 534 return 0; 535 retval = krb5_db_fini(util_context); 536 krb5_free_keyblock_contents(util_context, &master_key); 537 finished = TRUE; 538 krb5_free_context(util_context); 539 if (retval && retval != KRB5_KDB_DBNOTINITED) { 540 com_err(progname, retval, gettext("while closing database")); 541 exit_status++; 542 return 1; 543 } 544 return 0; 545 } 546 547 static void 548 add_random_key(argc, argv) 549 int argc; 550 char **argv; 551 { 552 krb5_error_code ret; 553 krb5_principal princ; 554 krb5_db_entry dbent; 555 int n; 556 krb5_boolean more; 557 krb5_timestamp now; 558 559 krb5_key_salt_tuple *keysalts = NULL; 560 krb5_int32 num_keysalts = 0; 561 562 int free_keysalts; 563 char *me = argv[0]; 564 char *ks_str = NULL; 565 char *pr_str; 566 567 if (argc < 2) 568 usage(); 569 for (argv++, argc--; *argv; argv++, argc--) { 570 if (!strcmp(*argv, "-e")) { 571 argv++; argc--; 572 ks_str = *argv; 573 continue; 574 } else 575 break; 576 } 577 if (argc < 1) 578 usage(); 579 pr_str = *argv; 580 ret = krb5_parse_name(util_context, pr_str, &princ); 581 if (ret) { 582 com_err(me, ret, gettext("while parsing principal name %s"), pr_str); 583 exit_status++; 584 return; 585 } 586 n = 1; 587 ret = krb5_db_get_principal(util_context, princ, &dbent, 588 &n, &more); 589 if (ret) { 590 com_err(me, ret, gettext("while fetching principal %s"), pr_str); 591 exit_status++; 592 return; 593 } 594 if (n != 1) { 595 fprintf(stderr, gettext("principal %s not found\n"), pr_str); 596 exit_status++; 597 return; 598 } 599 if (more) { 600 fprintf(stderr, gettext("principal %s not unique\n"), pr_str); 601 krb5_dbe_free_contents(util_context, &dbent); 602 exit_status++; 603 return; 604 } 605 ret = krb5_string_to_keysalts(ks_str, 606 ", \t", ":.-", 0, 607 &keysalts, 608 &num_keysalts); 609 if (ret) { 610 com_err(me, ret, gettext("while parsing keysalts %s"), ks_str); 611 exit_status++; 612 return; 613 } 614 if (!num_keysalts || keysalts == NULL) { 615 num_keysalts = global_params.num_keysalts; 616 keysalts = global_params.keysalts; 617 free_keysalts = 0; 618 } else 619 free_keysalts = 1; 620 ret = krb5_dbe_ark(util_context, &master_key, 621 keysalts, num_keysalts, 622 &dbent); 623 if (free_keysalts) 624 free(keysalts); 625 if (ret) { 626 com_err(me, ret, gettext("while randomizing principal %s"), pr_str); 627 krb5_dbe_free_contents(util_context, &dbent); 628 exit_status++; 629 return; 630 } 631 dbent.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 632 ret = krb5_timeofday(util_context, &now); 633 if (ret) { 634 com_err(me, ret, gettext("while getting time")); 635 krb5_dbe_free_contents(util_context, &dbent); 636 exit_status++; 637 return; 638 } 639 ret = krb5_dbe_update_last_pwd_change(util_context, &dbent, now); 640 if (ret) { 641 com_err(me, ret, gettext("while setting changetime")); 642 krb5_dbe_free_contents(util_context, &dbent); 643 exit_status++; 644 return; 645 } 646 ret = krb5_db_put_principal(util_context, &dbent, &n); 647 krb5_dbe_free_contents(util_context, &dbent); 648 if (ret) { 649 com_err(me, ret, gettext("while saving principal %s"), pr_str); 650 exit_status++; 651 return; 652 } 653 printf("%s changed\n", pr_str); 654 } 655