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() 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 *cmd_lookup(name) 147 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 int db5util_db_args_size = 0; 164 165 static void extended_com_err_fn (const char *myprog, errcode_t code, 166 const char *fmt, va_list args) 167 { 168 const char *emsg; 169 if (code) { 170 emsg = krb5_get_error_message (util_context, code); 171 fprintf (stderr, "%s: %s ", myprog, emsg); 172 krb5_free_error_message (util_context, emsg); 173 } else { 174 fprintf (stderr, "%s: ", myprog); 175 } 176 vfprintf (stderr, fmt, args); 177 fprintf (stderr, "\n"); 178 } 179 180 int add_db_arg(char *arg) 181 { 182 char **temp; 183 db5util_db_args_size++; 184 temp = realloc(db5util_db_args, 185 sizeof(char *) * (db5util_db_args_size + 1)); 186 if (temp == NULL) 187 return 0; 188 db5util_db_args = temp; 189 db5util_db_args[db5util_db_args_size-1] = arg; 190 db5util_db_args[db5util_db_args_size] = NULL; 191 return 1; 192 } 193 194 int main(argc, argv) 195 int argc; 196 char *argv[]; 197 { 198 struct _cmd_table *cmd = NULL; 199 char *koptarg, **cmd_argv; 200 char *db_name_tmp = NULL; 201 int cmd_argc; 202 krb5_error_code retval; 203 204 setlocale(LC_ALL, ""); 205 set_com_err_hook(extended_com_err_fn); 206 207 /* 208 * Ensure that "progname" is set before calling com_err. 209 */ 210 progname = (strrchr(argv[0], '/') ? 211 strrchr(argv[0], '/') + 1 : argv[0]); 212 213 retval = kadm5_init_krb5_context(&util_context); 214 if (retval) { 215 com_err (progname, retval, _("while initializing Kerberos code")); 216 exit(1); 217 } 218 219 cmd_argv = (char **) malloc(sizeof(char *)*argc); 220 if (cmd_argv == NULL) { 221 com_err(progname, ENOMEM, _("while creating sub-command arguments")); 222 exit(1); 223 } 224 memset(cmd_argv, 0, sizeof(char *)*argc); 225 cmd_argc = 0; 226 227 argv++; argc--; 228 while (*argv) { 229 if (strcmp(*argv, "-P") == 0 && ARG_VAL) { 230 mkey_password = koptarg; 231 manual_mkey = TRUE; 232 } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) { 233 global_params.dbname = koptarg; 234 global_params.mask |= KADM5_CONFIG_DBNAME; 235 236 if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0) 237 { 238 com_err(progname, ENOMEM, 239 _("while parsing command arguments")); 240 exit(1); 241 } 242 243 if (!add_db_arg(db_name_tmp)) { 244 com_err(progname, ENOMEM, 245 _("while parsing command arguments\n")); 246 exit(1); 247 } 248 249 } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) { 250 if (!add_db_arg(koptarg)) { 251 com_err(progname, ENOMEM, 252 _("while parsing command arguments\n")); 253 exit(1); 254 } 255 256 } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { 257 global_params.realm = koptarg; 258 global_params.mask |= KADM5_CONFIG_REALM; 259 /* not sure this is really necessary */ 260 if ((retval = krb5_set_default_realm(util_context, 261 global_params.realm))) { 262 com_err(progname, retval, 263 _("while setting default realm name")); 264 exit(1); 265 } 266 } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { 267 if (krb5_string_to_enctype(koptarg, &global_params.enctype)) { 268 com_err(progname, EINVAL, _(": %s is an invalid enctype"), 269 koptarg); 270 exit(1); 271 } else 272 global_params.mask |= KADM5_CONFIG_ENCTYPE; 273 } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) { 274 global_params.kvno = (krb5_kvno) atoi(koptarg); 275 if (global_params.kvno == IGNORE_VNO) { 276 com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"), 277 koptarg); 278 exit(1); 279 } else 280 global_params.mask |= KADM5_CONFIG_KVNO; 281 } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { 282 global_params.mkey_name = koptarg; 283 global_params.mask |= KADM5_CONFIG_MKEY_NAME; 284 } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) { 285 global_params.stash_file = koptarg; 286 global_params.mask |= KADM5_CONFIG_STASH_FILE; 287 } else if (strcmp(*argv, "-m") == 0) { 288 manual_mkey = TRUE; 289 global_params.mkey_from_kbd = 1; 290 global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; 291 } else { 292 cmd_argv[cmd_argc++] = *argv; 293 } 294 argv++; argc--; 295 } 296 297 if (cmd_argv[0] == NULL) 298 usage(); 299 cmd = cmd_lookup(cmd_argv[0]); 300 if (cmd == NULL) 301 usage(); 302 303 if( !util_context->default_realm ) 304 { 305 char *temp = NULL; 306 retval = krb5_get_default_realm(util_context, &temp); 307 if( retval ) 308 { 309 com_err(progname, retval, _("while getting default realm")); 310 exit(1); 311 } 312 krb5_free_default_realm(util_context, temp); 313 } 314 315 retval = kadm5_get_config_params(util_context, 1, 316 &global_params, &global_params); 317 if (retval) { 318 com_err(progname, retval, 319 _("while retrieving configuration parameters")); 320 exit(1); 321 } 322 323 /* 324 * Dump creates files which should not be world-readable. It is 325 * easiest to do a single umask call here. 326 */ 327 (void) umask(077); 328 329 master_keyblock.enctype = global_params.enctype; 330 if ((master_keyblock.enctype != ENCTYPE_UNKNOWN) && 331 (!krb5_c_valid_enctype(master_keyblock.enctype))) { 332 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, 333 "while setting up enctype %d", master_keyblock.enctype); 334 } 335 336 if (cmd->opendb && open_db_and_mkey()) 337 return exit_status; 338 339 if (global_params.iprop_enabled == TRUE) 340 ulog_set_role(util_context, IPROP_PRIMARY); 341 else 342 ulog_set_role(util_context, IPROP_NULL); 343 344 (*cmd->func)(cmd_argc, cmd_argv); 345 346 if( db_name_tmp ) 347 free( db_name_tmp ); 348 349 if( db5util_db_args ) 350 free(db5util_db_args); 351 352 quit(); 353 kadm5_free_config_params(util_context, &global_params); 354 krb5_free_context(util_context); 355 free(cmd_argv); 356 return exit_status; 357 } 358 359 /* 360 * open_db_and_mkey: Opens the KDC and policy database, and sets the 361 * global master_* variables. Sets dbactive to TRUE if the databases 362 * are opened, and valid_master_key to 1 if the global master 363 * variables are set properly. Returns 0 on success, and 1 on 364 * failure, but it is not considered a failure if the master key 365 * cannot be fetched (the master key stash file may not exist when the 366 * program is run). 367 */ 368 static int open_db_and_mkey() 369 { 370 krb5_error_code retval; 371 krb5_data scratch, pwd, seed; 372 373 dbactive = FALSE; 374 valid_master_key = 0; 375 376 if ((retval = krb5_db_open(util_context, db5util_db_args, 377 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) { 378 com_err(progname, retval, _("while initializing database")); 379 exit_status++; 380 return(1); 381 } 382 383 /* assemble & parse the master key name */ 384 385 if ((retval = krb5_db_setup_mkey_name(util_context, 386 global_params.mkey_name, 387 global_params.realm, 388 &mkey_fullname, &master_princ))) { 389 com_err(progname, retval, _("while setting up master key name")); 390 exit_status++; 391 return(1); 392 } 393 if ((retval = krb5_db_get_principal(util_context, master_princ, 0, 394 &master_entry))) { 395 com_err(progname, retval, _("while retrieving master entry")); 396 exit_status++; 397 (void) krb5_db_fini(util_context); 398 return(1); 399 } 400 401 if (global_params.mask & KADM5_CONFIG_KVNO) 402 master_kvno = global_params.kvno; /* user specified */ 403 else 404 master_kvno = IGNORE_VNO; 405 406 /* the databases are now open, and the master principal exists */ 407 dbactive = TRUE; 408 409 if (mkey_password) { 410 pwd.data = mkey_password; 411 pwd.length = strlen(mkey_password); 412 retval = krb5_principal2salt(util_context, master_princ, &scratch); 413 if (retval) { 414 com_err(progname, retval, _("while calculated master key salt")); 415 exit_status++; 416 return(1); 417 } 418 419 /* If no encryption type is set, use the default */ 420 if (master_keyblock.enctype == ENCTYPE_UNKNOWN) 421 master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; 422 if (!krb5_c_valid_enctype(master_keyblock.enctype)) 423 com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, 424 "while setting up enctype %d", 425 master_keyblock.enctype); 426 427 retval = krb5_c_string_to_key(util_context, master_keyblock.enctype, 428 &pwd, &scratch, &master_keyblock); 429 if (retval) { 430 com_err(progname, retval, 431 _("while transforming master key from password")); 432 exit_status++; 433 return(1); 434 } 435 free(scratch.data); 436 mkey_password = 0; 437 438 } else { 439 if ((retval = krb5_db_fetch_mkey(util_context, master_princ, 440 master_keyblock.enctype, 441 manual_mkey, FALSE, 442 global_params.stash_file, 443 &master_kvno, 444 0, &master_keyblock))) { 445 com_err(progname, retval, _("while reading master key")); 446 com_err(progname, 0, _("Warning: proceeding without master key")); 447 exit_status++; 448 return(0); 449 } 450 } 451 452 if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ, 453 &master_keyblock))) { 454 com_err(progname, retval, "while getting master key list"); 455 com_err(progname, 0, "Warning: proceeding without master key list"); 456 exit_status++; 457 return(0); 458 } 459 460 seed.length = master_keyblock.length; 461 seed.data = (char *) master_keyblock.contents; 462 463 if ((retval = krb5_c_random_seed(util_context, &seed))) { 464 com_err(progname, retval, _("while seeding random number generator")); 465 exit_status++; 466 memset(master_keyblock.contents, 0, master_keyblock.length); 467 krb5_free_keyblock_contents(util_context, &master_keyblock); 468 return(1); 469 } 470 471 if (global_params.iprop_enabled) { 472 if (ulog_map(util_context, global_params.iprop_logfile, 473 global_params.iprop_ulogsize)) { 474 fprintf(stderr, _("%s: Could not map log\n"), progname); 475 exit_status++; 476 return(1); 477 } 478 } 479 480 valid_master_key = 1; 481 dbactive = TRUE; 482 return 0; 483 } 484 485 #ifdef HAVE_GETCWD 486 #undef getwd 487 #endif 488 489 int 490 quit() 491 { 492 krb5_error_code retval; 493 static krb5_boolean finished = 0; 494 495 if (finished) 496 return 0; 497 ulog_fini(util_context); 498 retval = krb5_db_fini(util_context); 499 zapfree(master_keyblock.contents, master_keyblock.length); 500 krb5_free_principal(util_context, master_princ); 501 finished = TRUE; 502 if (retval && retval != KRB5_KDB_DBNOTINITED) { 503 com_err(progname, retval, _("while closing database")); 504 exit_status++; 505 return 1; 506 } 507 return 0; 508 } 509 510 static void 511 add_random_key(argc, argv) 512 int argc; 513 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 = NULL; 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 ret = krb5_db_put_principal(util_context, dbent); 604 krb5_db_free_principal(util_context, dbent); 605 if (ret) { 606 com_err(me, ret, _("while saving principal %s"), pr_str); 607 exit_status++; 608 return; 609 } 610 printf(_("%s changed\n"), pr_str); 611 } 612