1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kadmin/dbutil/kdb5_util.c - Administer a KDC database */ 3 /* 4 * (C) Copyright 1990,1991, 1996, 2008, 2009 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 /* 27 * Copyright (C) 1998 by the FundsXpress, INC. 28 * 29 * All rights reserved. 30 * 31 * Export of this software from the United States of America may require 32 * a specific license from the United States Government. It is the 33 * responsibility of any person or organization contemplating export to 34 * 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 FundsXpress. not be used in advertising or publicity pertaining 42 * to distribution of the software without specific, written prior 43 * permission. FundsXpress makes no representations about the suitability of 44 * this software for any purpose. It is provided "as is" without express 45 * or implied warranty. 46 * 47 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 48 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 49 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 50 */ 51 /* 52 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 53 * Use is subject to license terms. 54 */ 55 56 #include <k5-int.h> 57 #include <kadm5/admin.h> 58 #include <locale.h> 59 #include <adm_proto.h> 60 #include <time.h> 61 #include "kdb5_util.h" 62 63 /* 64 * XXX Ick, ick, ick. These global variables shouldn't be global.... 65 */ 66 char *mkey_password = 0; 67 68 /* 69 * I can't figure out any way for this not to be global, given how ss 70 * works. 71 */ 72 73 int exit_status = 0; 74 krb5_context util_context; 75 kadm5_config_params global_params; 76 77 void usage(void) 78 { 79 fprintf(stderr, 80 _("Usage: kdb5_util [-r realm] [-d dbname] " 81 "[-k mkeytype] [-kv mkeyVNO]\n" 82 "\t [-M mkeyname] [-m] [-sf stashfilename] " 83 "[-P password]\n" 84 "\t [-x db_args]* cmd [cmd_options]\n" 85 "\tcreate [-s]\n" 86 "\tdestroy [-f]\n" 87 "\tstash [-f keyfile]\n" 88 "\tdump [-b7|-r13|-r18] [-verbose]\n" 89 "\t [-mkey_convert] [-new_mkey_file mkey_file]\n" 90 "\t [-rev] [-recurse] [filename [princs...]]\n" 91 "\tload [-b7|-r13|-r18] [-hash] [-verbose] [-update] " 92 "filename\n" 93 "\tark [-e etype_list] principal\n" 94 "\tadd_mkey [-e etype] [-s]\n" 95 "\tuse_mkey kvno [time]\n" 96 "\tlist_mkeys\n")); 97 /* avoid a string length compiler warning */ 98 fprintf(stderr, 99 _("\tupdate_princ_encryption [-f] [-n] [-v] [princ-pattern]\n" 100 "\tpurge_mkeys [-f] [-n] [-v]\n" 101 "\ttabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype\n" 102 "\nwhere,\n\t[-x db_args]* - any number of database specific " 103 "arguments.\n" 104 "\t\t\tLook at each database documentation for supported " 105 "arguments\n")); 106 exit(1); 107 } 108 109 krb5_keyblock master_keyblock; 110 krb5_kvno master_kvno; /* fetched */ 111 extern krb5_principal master_princ; 112 char *mkey_fullname; 113 krb5_db_entry *master_entry = NULL; 114 int valid_master_key = 0; 115 116 char *progname; 117 krb5_boolean manual_mkey = FALSE; 118 krb5_boolean dbactive = FALSE; 119 120 static int open_db_and_mkey(void); 121 122 static void add_random_key(int, char **); 123 124 typedef void (*cmd_func)(int, char **); 125 126 struct _cmd_table { 127 char *name; 128 cmd_func func; 129 int opendb; 130 } cmd_table[] = { 131 {"create", kdb5_create, 0}, 132 {"destroy", kdb5_destroy, 1}, /* 1 opens the kdb */ 133 {"stash", kdb5_stash, 1}, 134 {"dump", dump_db, 1}, 135 {"load", load_db, 0}, 136 {"ark", add_random_key, 1}, 137 {"add_mkey", kdb5_add_mkey, 1}, 138 {"use_mkey", kdb5_use_mkey, 1}, 139 {"list_mkeys", kdb5_list_mkeys, 1}, 140 {"update_princ_encryption", kdb5_update_princ_encryption, 1}, 141 {"purge_mkeys", kdb5_purge_mkeys, 1}, 142 {"tabdump", tabdump, 1}, 143 {NULL, NULL, 0}, 144 }; 145 146 static struct _cmd_table * 147 cmd_lookup(char *name) 148 { 149 struct _cmd_table *cmd = cmd_table; 150 while (cmd->name) { 151 if (strcmp(cmd->name, name) == 0) 152 return cmd; 153 else 154 cmd++; 155 } 156 157 return NULL; 158 } 159 160 #define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL)) 161 162 char **db5util_db_args = NULL; 163 size_t db5util_db_args_size = 0; 164 165 static void 166 extended_com_err_fn(const char *myprog, errcode_t code, const char *fmt, 167 va_list args) 168 { 169 const char *emsg; 170 if (code) { 171 emsg = krb5_get_error_message (util_context, code); 172 fprintf (stderr, "%s: %s ", myprog, emsg); 173 krb5_free_error_message (util_context, emsg); 174 } else { 175 fprintf (stderr, "%s: ", myprog); 176 } 177 vfprintf (stderr, fmt, args); 178 fprintf (stderr, "\n"); 179 } 180 181 int 182 add_db_arg(char *arg) 183 { 184 char **temp; 185 db5util_db_args_size++; 186 temp = realloc(db5util_db_args, 187 sizeof(char *) * (db5util_db_args_size + 1)); 188 if (temp == NULL) 189 return 0; 190 db5util_db_args = temp; 191 db5util_db_args[db5util_db_args_size-1] = arg; 192 db5util_db_args[db5util_db_args_size] = NULL; 193 return 1; 194 } 195 196 int 197 main(int argc, char *argv[]) 198 { 199 struct _cmd_table *cmd = NULL; 200 char *koptarg, **cmd_argv; 201 char *db_name_tmp = NULL; 202 int cmd_argc; 203 krb5_error_code retval; 204 205 setlocale(LC_ALL, ""); 206 set_com_err_hook(extended_com_err_fn); 207 208 /* 209 * Ensure that "progname" is set before calling com_err. 210 */ 211 progname = (strrchr(argv[0], '/') ? 212 strrchr(argv[0], '/') + 1 : argv[0]); 213 214 retval = kadm5_init_krb5_context(&util_context); 215 if (retval) { 216 com_err (progname, retval, _("while initializing Kerberos code")); 217 exit(1); 218 } 219 220 cmd_argv = (char **) malloc(sizeof(char *)*argc); 221 if (cmd_argv == NULL) { 222 com_err(progname, ENOMEM, _("while creating sub-command arguments")); 223 exit(1); 224 } 225 memset(cmd_argv, 0, sizeof(char *)*argc); 226 cmd_argc = 0; 227 228 argv++; argc--; 229 while (*argv) { 230 if (strcmp(*argv, "-P") == 0 && ARG_VAL) { 231 mkey_password = koptarg; 232 manual_mkey = TRUE; 233 } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) { 234 global_params.dbname = koptarg; 235 global_params.mask |= KADM5_CONFIG_DBNAME; 236 237 if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0) 238 { 239 com_err(progname, ENOMEM, 240 _("while parsing command arguments")); 241 exit(1); 242 } 243 244 if (!add_db_arg(db_name_tmp)) { 245 com_err(progname, ENOMEM, 246 _("while parsing command arguments\n")); 247 exit(1); 248 } 249 250 } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) { 251 if (!add_db_arg(koptarg)) { 252 com_err(progname, ENOMEM, 253 _("while parsing command arguments\n")); 254 exit(1); 255 } 256 257 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { 258 global_params.realm = koptarg; 259 global_params.mask |= KADM5_CONFIG_REALM; 260 /* not sure this is really necessary */ 261 if ((retval = krb5_set_default_realm(util_context, 262 global_params.realm))) { 263 com_err(progname, retval, 264 _("while setting default realm name")); 265 exit(1); 266 } 267 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { 268 if (krb5_string_to_enctype(koptarg, &global_params.enctype)) { 269 com_err(progname, EINVAL, _(": %s is an invalid enctype"), 270 koptarg); 271 exit(1); 272 } else 273 global_params.mask |= KADM5_CONFIG_ENCTYPE; 274 } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) { 275 global_params.kvno = (krb5_kvno) atoi(koptarg); 276 if (global_params.kvno == IGNORE_VNO) { 277 com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"), 278 koptarg); 279 exit(1); 280 } else 281 global_params.mask |= KADM5_CONFIG_KVNO; 282 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { 283 global_params.mkey_name = koptarg; 284 global_params.mask |= KADM5_CONFIG_MKEY_NAME; 285 } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) { 286 global_params.stash_file = koptarg; 287 global_params.mask |= KADM5_CONFIG_STASH_FILE; 288 } else if (strcmp(*argv, "-m") == 0) { 289 manual_mkey = TRUE; 290 global_params.mkey_from_kbd = 1; 291 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 292 } else { 293 cmd_argv[cmd_argc++] = *argv; 294 } 295 argv++; argc--; 296 } 297 298 if (cmd_argv[0] == NULL) 299 usage(); 300 cmd = cmd_lookup(cmd_argv[0]); 301 if (cmd == NULL) 302 usage(); 303 304 if( !util_context->default_realm ) 305 { 306 char *temp = NULL; 307 retval = krb5_get_default_realm(util_context, &temp); 308 if( retval ) 309 { 310 com_err(progname, retval, _("while getting default realm")); 311 exit(1); 312 } 313 krb5_free_default_realm(util_context, temp); 314 } 315 316 retval = kadm5_get_config_params(util_context, 1, 317 &global_params, &global_params); 318 if (retval) { 319 com_err(progname, retval, 320 _("while retrieving configuration parameters")); 321 exit(1); 322 } 323 324 /* 325 * Dump creates files which should not be world-readable. It is 326 * easiest to do a single umask call here. 327 */ 328 (void) umask(077); 329 330 master_keyblock.enctype = global_params.enctype; 331 if ((master_keyblock.enctype != ENCTYPE_UNKNOWN) && 332 (!krb5_c_valid_enctype(master_keyblock.enctype))) { 333 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, 334 "while setting up enctype %d", master_keyblock.enctype); 335 } 336 337 if (cmd->opendb && open_db_and_mkey()) 338 return exit_status; 339 340 if (global_params.iprop_enabled == TRUE) 341 ulog_set_role(util_context, IPROP_PRIMARY); 342 else 343 ulog_set_role(util_context, IPROP_NULL); 344 345 (*cmd->func)(cmd_argc, cmd_argv); 346 347 if( db_name_tmp ) 348 free( db_name_tmp ); 349 350 if( db5util_db_args ) 351 free(db5util_db_args); 352 353 quit(); 354 kadm5_free_config_params(util_context, &global_params); 355 krb5_free_context(util_context); 356 free(cmd_argv); 357 return exit_status; 358 } 359 360 /* 361 * open_db_and_mkey: Opens the KDC and policy database, and sets the 362 * global master_* variables. Sets dbactive to TRUE if the databases 363 * are opened, and valid_master_key to 1 if the global master 364 * variables are set properly. Returns 0 on success, and 1 on 365 * failure, but it is not considered a failure if the master key 366 * cannot be fetched (the master key stash file may not exist when the 367 * program is run). 368 */ 369 static int 370 open_db_and_mkey(void) 371 { 372 krb5_error_code retval; 373 krb5_data scratch, pwd, seed; 374 375 dbactive = FALSE; 376 valid_master_key = 0; 377 378 if ((retval = krb5_db_open(util_context, db5util_db_args, 379 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) { 380 com_err(progname, retval, _("while initializing database")); 381 exit_status++; 382 return(1); 383 } 384 385 /* assemble & parse the master key name */ 386 387 if ((retval = krb5_db_setup_mkey_name(util_context, 388 global_params.mkey_name, 389 global_params.realm, 390 &mkey_fullname, &master_princ))) { 391 com_err(progname, retval, _("while setting up master key name")); 392 exit_status++; 393 return(1); 394 } 395 if ((retval = krb5_db_get_principal(util_context, master_princ, 0, 396 &master_entry))) { 397 com_err(progname, retval, _("while retrieving master entry")); 398 exit_status++; 399 (void) krb5_db_fini(util_context); 400 return(1); 401 } 402 403 if (global_params.mask & KADM5_CONFIG_KVNO) 404 master_kvno = global_params.kvno; /* user specified */ 405 else 406 master_kvno = IGNORE_VNO; 407 408 /* the databases are now open, and the master principal exists */ 409 dbactive = TRUE; 410 411 if (mkey_password) { 412 pwd.data = mkey_password; 413 pwd.length = strlen(mkey_password); 414 retval = krb5_principal2salt(util_context, master_princ, &scratch); 415 if (retval) { 416 com_err(progname, retval, _("while calculated master key salt")); 417 exit_status++; 418 return(1); 419 } 420 421 /* If no encryption type is set, use the default */ 422 if (master_keyblock.enctype == ENCTYPE_UNKNOWN) 423 master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; 424 if (!krb5_c_valid_enctype(master_keyblock.enctype)) 425 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, 426 "while setting up enctype %d", 427 master_keyblock.enctype); 428 429 retval = krb5_c_string_to_key(util_context, master_keyblock.enctype, 430 &pwd, &scratch, &master_keyblock); 431 if (retval) { 432 com_err(progname, retval, 433 _("while transforming master key from password")); 434 exit_status++; 435 return(1); 436 } 437 free(scratch.data); 438 mkey_password = 0; 439 440 } else { 441 if ((retval = krb5_db_fetch_mkey(util_context, master_princ, 442 master_keyblock.enctype, 443 manual_mkey, FALSE, 444 global_params.stash_file, 445 &master_kvno, 446 0, &master_keyblock))) { 447 com_err(progname, retval, _("while reading master key")); 448 com_err(progname, 0, _("Warning: proceeding without master key")); 449 exit_status++; 450 return(0); 451 } 452 } 453 454 if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ, 455 &master_keyblock))) { 456 com_err(progname, retval, "while getting master key list"); 457 com_err(progname, 0, "Warning: proceeding without master key list"); 458 exit_status++; 459 return(0); 460 } 461 462 seed.length = master_keyblock.length; 463 seed.data = (char *) master_keyblock.contents; 464 465 if ((retval = krb5_c_random_seed(util_context, &seed))) { 466 com_err(progname, retval, _("while seeding random number generator")); 467 exit_status++; 468 memset(master_keyblock.contents, 0, master_keyblock.length); 469 krb5_free_keyblock_contents(util_context, &master_keyblock); 470 return(1); 471 } 472 473 if (global_params.iprop_enabled) { 474 if (ulog_map(util_context, global_params.iprop_logfile, 475 global_params.iprop_ulogsize)) { 476 fprintf(stderr, _("%s: Could not map log\n"), progname); 477 exit_status++; 478 return(1); 479 } 480 } 481 482 valid_master_key = 1; 483 dbactive = TRUE; 484 return 0; 485 } 486 487 #ifdef HAVE_GETCWD 488 #undef getwd 489 #endif 490 491 int 492 quit(void) 493 { 494 krb5_error_code retval; 495 static krb5_boolean finished = 0; 496 497 if (finished) 498 return 0; 499 ulog_fini(util_context); 500 retval = krb5_db_fini(util_context); 501 zapfree(master_keyblock.contents, master_keyblock.length); 502 krb5_free_principal(util_context, master_princ); 503 finished = TRUE; 504 if (retval && retval != KRB5_KDB_DBNOTINITED) { 505 com_err(progname, retval, _("while closing database")); 506 exit_status++; 507 return 1; 508 } 509 return 0; 510 } 511 512 static void 513 add_random_key(int argc, char **argv) 514 { 515 krb5_error_code ret; 516 krb5_principal princ; 517 krb5_db_entry *dbent; 518 krb5_timestamp now; 519 520 krb5_key_salt_tuple *keysalts = NULL; 521 krb5_int32 num_keysalts = 0; 522 523 int free_keysalts; 524 char *me = progname; 525 char *ks_str = ""; 526 char *pr_str; 527 krb5_keyblock *tmp_mkey; 528 529 if (argc < 2) 530 usage(); 531 for (argv++, argc--; *argv; argv++, argc--) { 532 if (!strcmp(*argv, "-e")) { 533 argv++; argc--; 534 ks_str = *argv; 535 continue; 536 } else 537 break; 538 } 539 if (argc < 1) 540 usage(); 541 pr_str = *argv; 542 ret = krb5_parse_name(util_context, pr_str, &princ); 543 if (ret) { 544 com_err(me, ret, _("while parsing principal name %s"), pr_str); 545 exit_status++; 546 return; 547 } 548 ret = krb5_db_get_principal(util_context, princ, 0, &dbent); 549 if (ret) { 550 com_err(me, ret, _("while fetching principal %s"), pr_str); 551 exit_status++; 552 return; 553 } 554 ret = krb5_string_to_keysalts(ks_str, 555 NULL, NULL, 0, 556 &keysalts, 557 &num_keysalts); 558 if (ret) { 559 com_err(me, ret, _("while parsing keysalts %s"), ks_str); 560 exit_status++; 561 return; 562 } 563 if (!num_keysalts || keysalts == NULL) { 564 num_keysalts = global_params.num_keysalts; 565 keysalts = global_params.keysalts; 566 free_keysalts = 0; 567 } else 568 free_keysalts = 1; 569 570 /* Find the mkey used to protect the existing keys */ 571 ret = krb5_dbe_find_mkey(util_context, dbent, &tmp_mkey); 572 if (ret) { 573 com_err(me, ret, _("while finding mkey")); 574 krb5_db_free_principal(util_context, dbent); 575 exit_status++; 576 return; 577 } 578 579 ret = krb5_dbe_ark(util_context, tmp_mkey, keysalts, num_keysalts, dbent); 580 if (free_keysalts) 581 free(keysalts); 582 if (ret) { 583 com_err(me, ret, "while randomizing principal %s", pr_str); 584 krb5_db_free_principal(util_context, dbent); 585 exit_status++; 586 return; 587 } 588 dbent->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; 589 ret = krb5_timeofday(util_context, &now); 590 if (ret) { 591 com_err(me, ret, _("while getting time")); 592 krb5_db_free_principal(util_context, dbent); 593 exit_status++; 594 return; 595 } 596 ret = krb5_dbe_update_last_pwd_change(util_context, dbent, now); 597 if (ret) { 598 com_err(me, ret, _("while setting changetime")); 599 krb5_db_free_principal(util_context, dbent); 600 exit_status++; 601 return; 602 } 603 604 dbent->mask |= KADM5_ATTRIBUTES | KADM5_KEY_DATA | KADM5_TL_DATA; 605 606 ret = krb5_db_put_principal(util_context, dbent); 607 krb5_db_free_principal(util_context, dbent); 608 if (ret) { 609 com_err(me, ret, _("while saving principal %s"), pr_str); 610 exit_status++; 611 return; 612 } 613 printf(_("%s changed\n"), pr_str); 614 } 615