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