1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* kadmin/dbutil/dump.c - Dump a KDC database */ 3 /* 4 * Copyright 1990,1991,2001,2006,2008,2009,2013 by the Massachusetts Institute 5 * of Technology. 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 2004 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 */ 30 31 #include <k5-int.h> 32 #include <kadm5/admin.h> 33 #include <kadm5/server_internal.h> 34 #include <kdb.h> 35 #include <com_err.h> 36 #include "kdb5_util.h" 37 #include "k5-regex.h" 38 39 /* Needed for master key conversion. */ 40 static krb5_boolean mkey_convert; 41 krb5_keyblock new_master_keyblock; 42 krb5_kvno new_mkvno; 43 44 #define K5Q1(x) #x 45 #define K5Q(x) K5Q1(x) 46 #define K5CONST_WIDTH_SCANF_STR(x) "%" K5Q(x) "s" 47 48 typedef krb5_error_code (*dump_func)(krb5_context context, 49 krb5_db_entry *entry, const char *name, 50 FILE *fp, krb5_boolean verbose, 51 krb5_boolean omit_nra); 52 typedef int (*load_func)(krb5_context context, const char *dumpfile, FILE *fp, 53 krb5_boolean verbose, int *linenop); 54 55 typedef struct _dump_version { 56 char *name; 57 char *header; 58 int updateonly; 59 int iprop; 60 int ipropx; 61 dump_func dump_princ; 62 osa_adb_iter_policy_func dump_policy; 63 load_func load_record; 64 } dump_version; 65 66 struct dump_args { 67 FILE *ofile; 68 krb5_context context; 69 char **names; 70 int nnames; 71 krb5_boolean verbose; 72 krb5_boolean omit_nra; /* omit non-replicated attributes */ 73 dump_version *dump; 74 }; 75 76 /* External data */ 77 extern krb5_db_entry *master_entry; 78 79 /* 80 * Re-encrypt the key_data with the new master key... 81 */ 82 krb5_error_code 83 master_key_convert(krb5_context context, krb5_db_entry *db_entry) 84 { 85 krb5_error_code retval; 86 krb5_keyblock v5plainkey, *key_ptr, *tmp_mkey; 87 krb5_keysalt keysalt; 88 krb5_key_data new_key_data, *key_data; 89 krb5_boolean is_mkey; 90 krb5_kvno kvno; 91 int i, j; 92 93 is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ); 94 95 if (is_mkey) { 96 return add_new_mkey(context, db_entry, &new_master_keyblock, 97 new_mkvno); 98 } 99 100 for (i = 0; i < db_entry->n_key_data; i++) { 101 key_data = &db_entry->key_data[i]; 102 retval = krb5_dbe_find_mkey(context, db_entry, &tmp_mkey); 103 if (retval) 104 return retval; 105 retval = krb5_dbe_decrypt_key_data(context, tmp_mkey, key_data, 106 &v5plainkey, &keysalt); 107 if (retval) 108 return retval; 109 110 memset(&new_key_data, 0, sizeof(new_key_data)); 111 112 key_ptr = &v5plainkey; 113 kvno = key_data->key_data_kvno; 114 115 retval = krb5_dbe_encrypt_key_data(context, &new_master_keyblock, 116 key_ptr, &keysalt, kvno, 117 &new_key_data); 118 if (retval) 119 return retval; 120 krb5_free_keyblock_contents(context, &v5plainkey); 121 for (j = 0; j < key_data->key_data_ver; j++) { 122 if (key_data->key_data_length[j]) 123 free(key_data->key_data_contents[j]); 124 } 125 *key_data = new_key_data; 126 } 127 assert(new_mkvno > 0); 128 return krb5_dbe_update_mkvno(context, db_entry, new_mkvno); 129 } 130 131 /* Create temp file for new dump to be named ofile. */ 132 static FILE * 133 create_ofile(char *ofile, char **tmpname) 134 { 135 int fd = -1; 136 FILE *f; 137 138 *tmpname = NULL; 139 if (asprintf(tmpname, "%s-XXXXXX", ofile) < 0) 140 goto error; 141 142 fd = mkstemp(*tmpname); 143 if (fd == -1) 144 goto error; 145 146 f = fdopen(fd, "w+"); 147 if (f != NULL) 148 return f; 149 150 error: 151 com_err(progname, errno, _("while allocating temporary filename dump")); 152 if (fd >= 0) 153 unlink(*tmpname); 154 exit(1); 155 } 156 157 /* Rename new dump file into place. */ 158 static void 159 finish_ofile(char *ofile, char **tmpname) 160 { 161 if (rename(*tmpname, ofile) == -1) { 162 com_err(progname, errno, _("while renaming dump file into place")); 163 exit(1); 164 } 165 free(*tmpname); 166 *tmpname = NULL; 167 } 168 169 /* Create the .dump_ok file. */ 170 static krb5_boolean 171 prep_ok_file(krb5_context context, char *file_name, int *fd_out) 172 { 173 static char ok[] = ".dump_ok"; 174 krb5_error_code retval; 175 char *file_ok = NULL; 176 int fd = -1; 177 krb5_boolean success = FALSE; 178 179 *fd_out = -1; 180 181 if (asprintf(&file_ok, "%s%s", file_name, ok) < 0) { 182 com_err(progname, ENOMEM, _("while allocating dump_ok filename")); 183 goto cleanup; 184 } 185 186 fd = open(file_ok, O_WRONLY | O_CREAT | O_TRUNC, 0600); 187 if (fd == -1) { 188 com_err(progname, errno, _("while creating 'ok' file, '%s'"), file_ok); 189 goto cleanup; 190 } 191 retval = krb5_lock_file(context, fd, KRB5_LOCKMODE_EXCLUSIVE); 192 if (retval) { 193 com_err(progname, retval, _("while locking 'ok' file, '%s'"), file_ok); 194 goto cleanup; 195 } 196 197 *fd_out = fd; 198 fd = -1; 199 success = TRUE; 200 201 cleanup: 202 free(file_ok); 203 if (fd != -1) 204 close(fd); 205 if (!success) 206 exit_status++; 207 return success; 208 } 209 210 /* 211 * Update the "ok" file. 212 */ 213 static void 214 update_ok_file(krb5_context context, int fd) 215 { 216 write(fd, "", 1); 217 krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK); 218 close(fd); 219 } 220 221 /* Return true if a principal name matches a regular expression or string. */ 222 static int 223 name_matches(char *name, struct dump_args *args) 224 { 225 regex_t reg; 226 regmatch_t rmatch; 227 int st; 228 char errmsg[BUFSIZ]; 229 int i, match; 230 231 /* Check each regular expression in args. */ 232 match = args->nnames ? 0 : 1; 233 for (i = 0; i < args->nnames && !match; i++) { 234 /* Compile the regular expression. */ 235 st = regcomp(®, args->names[i], REG_EXTENDED); 236 if (st) { 237 regerror(st, ®, errmsg, sizeof(errmsg)); 238 fprintf(stderr, _("%s: regular expression error: %s\n"), progname, 239 errmsg); 240 break; 241 } 242 /* See if we have a match. */ 243 st = regexec(®, name, 1, &rmatch, 0); 244 if (st == 0) { 245 /* See if it matches the whole name. */ 246 if (rmatch.rm_so == 0 && (size_t)rmatch.rm_eo == strlen(name)) 247 match = 1; 248 } else if (st != REG_NOMATCH) { 249 regerror(st, ®, errmsg, sizeof(errmsg)); 250 fprintf(stderr, _("%s: regular expression match error: %s\n"), 251 progname, errmsg); 252 break; 253 } 254 regfree(®); 255 } 256 return match; 257 } 258 259 /* Output "-1" if len is 0; otherwise output len bytes of data in hex. */ 260 static void 261 dump_octets_or_minus1(FILE *fp, unsigned char *data, size_t len) 262 { 263 if (len > 0) { 264 for (; len > 0; len--) 265 fprintf(fp, "%02x", *data++); 266 } else { 267 fprintf(fp, "-1"); 268 } 269 } 270 271 /* 272 * Dump TL data; common to principals and policies. 273 * 274 * If filter_kadm then the KRB5_TL_KADM_DATA (where a principal's policy 275 * name is stored) is filtered out. This is for dump formats that don't 276 * support policies. 277 */ 278 static void 279 dump_tl_data(FILE *ofile, krb5_tl_data *tlp, krb5_boolean filter_kadm) 280 { 281 for (; tlp != NULL; tlp = tlp->tl_data_next) { 282 if (tlp->tl_data_type == KRB5_TL_KADM_DATA && filter_kadm) 283 continue; 284 fprintf(ofile, "\t%d\t%d\t", (int)tlp->tl_data_type, 285 (int)tlp->tl_data_length); 286 dump_octets_or_minus1(ofile, tlp->tl_data_contents, 287 tlp->tl_data_length); 288 } 289 } 290 291 /* Dump a principal entry in krb5 beta 7 format. Omit kadmin tl-data if kadm 292 * is false. */ 293 static krb5_error_code 294 k5beta7_common(krb5_context context, krb5_db_entry *entry, 295 const char *name, FILE *fp, krb5_boolean verbose, 296 krb5_boolean omit_nra, krb5_boolean kadm) 297 { 298 krb5_tl_data *tlp; 299 krb5_key_data *kdata; 300 int counter, skip, i; 301 302 /* 303 * The dump format is as follows: 304 * len strlen(name) n_tl_data n_key_data e_length 305 * name 306 * attributes max_life max_renewable_life expiration 307 * pw_expiration last_success last_failed fail_auth_count 308 * n_tl_data*[type length <contents>] 309 * n_key_data*[ver kvno ver*(type length <contents>)] 310 * <e_data> 311 * Fields which are not encapsulated by angle-brackets are to appear 312 * verbatim. A bracketed field's absence is indicated by a -1 in its 313 * place. 314 */ 315 316 /* Make sure that the tagged list is reasonably correct. */ 317 counter = skip = 0; 318 for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { 319 /* Don't dump tl data types we know aren't understood by earlier 320 * versions. */ 321 if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm) 322 skip++; 323 else 324 counter++; 325 } 326 327 if (counter + skip != entry->n_tl_data) { 328 fprintf(stderr, _("%s: tagged data list inconsistency for %s " 329 "(counted %d, stored %d)\n"), progname, name, 330 counter + skip, (int)entry->n_tl_data); 331 return EINVAL; 332 } 333 334 /* Write out header. */ 335 fprintf(fp, "princ\t%d\t%lu\t%d\t%d\t%d\t%s\t", (int)entry->len, 336 (unsigned long)strlen(name), counter, (int)entry->n_key_data, 337 (int)entry->e_length, name); 338 fprintf(fp, "%d\t%d\t%d\t%u\t%u\t%u\t%u\t%d", entry->attributes, 339 entry->max_life, entry->max_renewable_life, 340 (unsigned int)entry->expiration, 341 (unsigned int)entry->pw_expiration, 342 (unsigned int)(omit_nra ? 0 : entry->last_success), 343 (unsigned int)(omit_nra ? 0 : entry->last_failed), 344 omit_nra ? 0 : entry->fail_auth_count); 345 346 /* Write out tagged data. */ 347 dump_tl_data(fp, entry->tl_data, !kadm); 348 fprintf(fp, "\t"); 349 350 /* Write out key data. */ 351 for (counter = 0; counter < entry->n_key_data; counter++) { 352 kdata = &entry->key_data[counter]; 353 fprintf(fp, "%d\t%d\t", (int)kdata->key_data_ver, 354 (int)kdata->key_data_kvno); 355 for (i = 0; i < kdata->key_data_ver; i++) { 356 fprintf(fp, "%d\t%d\t", kdata->key_data_type[i], 357 kdata->key_data_length[i]); 358 dump_octets_or_minus1(fp, kdata->key_data_contents[i], 359 kdata->key_data_length[i]); 360 fprintf(fp, "\t"); 361 } 362 } 363 364 /* Write out extra data. */ 365 dump_octets_or_minus1(fp, entry->e_data, entry->e_length); 366 367 /* Write trailer. */ 368 fprintf(fp, ";\n"); 369 370 if (verbose) 371 fprintf(stderr, "%s\n", name); 372 373 return 0; 374 } 375 376 /* Output a dump record in krb5b7 format. */ 377 static krb5_error_code 378 dump_k5beta7_princ(krb5_context context, krb5_db_entry *entry, 379 const char *name, FILE *fp, krb5_boolean verbose, 380 krb5_boolean omit_nra) 381 { 382 return k5beta7_common(context, entry, name, fp, verbose, omit_nra, FALSE); 383 } 384 385 static krb5_error_code 386 dump_k5beta7_princ_withpolicy(krb5_context context, krb5_db_entry *entry, 387 const char *name, FILE *fp, krb5_boolean verbose, 388 krb5_boolean omit_nra) 389 { 390 return k5beta7_common(context, entry, name, fp, verbose, omit_nra, TRUE); 391 } 392 393 static void 394 dump_k5beta7_policy(void *data, osa_policy_ent_t entry) 395 { 396 struct dump_args *arg = data; 397 398 fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, 399 entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, 400 entry->pw_min_classes, entry->pw_history_num, 0); 401 } 402 403 static void 404 dump_r1_8_policy(void *data, osa_policy_ent_t entry) 405 { 406 struct dump_args *arg = data; 407 408 fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", 409 entry->name, entry->pw_min_life, entry->pw_max_life, 410 entry->pw_min_length, entry->pw_min_classes, entry->pw_history_num, 411 0, entry->pw_max_fail, entry->pw_failcnt_interval, 412 entry->pw_lockout_duration); 413 } 414 415 static void 416 dump_r1_11_policy(void *data, osa_policy_ent_t entry) 417 { 418 struct dump_args *arg = data; 419 420 fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t" 421 "%d\t%d\t%d\t%s\t%d", entry->name, entry->pw_min_life, 422 entry->pw_max_life, entry->pw_min_length, entry->pw_min_classes, 423 entry->pw_history_num, 0, entry->pw_max_fail, 424 entry->pw_failcnt_interval, entry->pw_lockout_duration, 425 entry->attributes, entry->max_life, entry->max_renewable_life, 426 entry->allowed_keysalts ? entry->allowed_keysalts : "-", 427 entry->n_tl_data); 428 429 dump_tl_data(arg->ofile, entry->tl_data, FALSE); 430 fprintf(arg->ofile, "\n"); 431 } 432 433 static krb5_error_code 434 dump_iterator(void *ptr, krb5_db_entry *entry) 435 { 436 krb5_error_code ret; 437 struct dump_args *args = ptr; 438 char *name; 439 440 ret = krb5_unparse_name(args->context, entry->princ, &name); 441 if (ret) { 442 com_err(progname, ret, _("while unparsing principal name")); 443 return ret; 444 } 445 446 /* Re-encode the keys in the new master key, if necessary. */ 447 if (mkey_convert) { 448 ret = master_key_convert(args->context, entry); 449 if (ret) { 450 com_err(progname, ret, _("while converting %s to new master key"), 451 name); 452 goto cleanup; 453 } 454 } 455 456 /* Don't dump this entry if we have match strings and it doesn't match. */ 457 if (args->nnames > 0 && !name_matches(name, args)) 458 goto cleanup; 459 460 ret = args->dump->dump_princ(args->context, entry, name, args->ofile, 461 args->verbose, args->omit_nra); 462 463 cleanup: 464 free(name); 465 return ret; 466 } 467 468 static inline void 469 load_err(const char *fname, int lineno, const char *msg) 470 { 471 fprintf(stderr, _("%s(%d): %s\n"), fname, lineno, msg); 472 } 473 474 /* Read a string of bytes. Increment *lp for each newline. Return 0 on 475 * success, 1 on failure. */ 476 static int 477 read_string(FILE *f, char *buf, int len, int *lp) 478 { 479 int c, i; 480 481 for (i = 0; i < len; i++) { 482 c = fgetc(f); 483 if (c < 0) 484 return 1; 485 if (c == '\n') 486 (*lp)++; 487 buf[i] = c; 488 } 489 buf[len] = '\0'; 490 return 0; 491 } 492 493 /* Read a string of two-character representations of bytes. */ 494 static int 495 read_octet_string(FILE *f, unsigned char *buf, int len) 496 { 497 int c, i; 498 499 for (i = 0; i < len; i++) { 500 if (fscanf(f, "%02x", &c) != 1) 501 return 1; 502 buf[i] = c; 503 } 504 return 0; 505 } 506 507 /* Read the end of a dumpfile record. */ 508 static void 509 read_record_end(FILE *f, const char *fn, int lineno) 510 { 511 int ch; 512 513 if ((ch = fgetc(f)) != ';' || (ch = fgetc(f)) != '\n') { 514 fprintf(stderr, _("%s(%d): ignoring trash at end of line: "), fn, 515 lineno); 516 while (ch != '\n') { 517 putc(ch, stderr); 518 ch = fgetc(f); 519 } 520 putc(ch, stderr); 521 } 522 } 523 524 /* Allocate and form a TL data list of a desired size. */ 525 static int 526 alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp) 527 { 528 krb5_tl_data **tlp = tldp; 529 int i; 530 531 for (i = 0; i < n_tl_data; i++) { 532 *tlp = calloc(1, sizeof(krb5_tl_data)); 533 if (*tlp == NULL) 534 return ENOMEM; /* caller cleans up */ 535 tlp = &((*tlp)->tl_data_next); 536 } 537 538 return 0; 539 } 540 541 /* If len is zero, read the string "-1" from fp. Otherwise allocate space and 542 * read len octets. Return 0 on success, 1 on failure. */ 543 static int 544 read_octets_or_minus1(FILE *fp, size_t len, unsigned char **out) 545 { 546 int ival; 547 unsigned char *buf; 548 549 *out = NULL; 550 if (len == 0) 551 return fscanf(fp, "%d", &ival) != 1 || ival != -1; 552 buf = malloc(len); 553 if (buf == NULL) 554 return 1; 555 if (read_octet_string(fp, buf, len)) { 556 free(buf); 557 return 1; 558 } 559 *out = buf; 560 return 0; 561 } 562 563 /* Read TL data for a principal or policy. Print an error and return -1 on 564 * failure. */ 565 static int 566 process_tl_data(const char *fname, FILE *filep, int lineno, 567 krb5_tl_data *tl_data) 568 { 569 krb5_tl_data *tl; 570 int nread, i1; 571 unsigned int u1; 572 573 for (tl = tl_data; tl; tl = tl->tl_data_next) { 574 nread = fscanf(filep, "%d\t%u\t", &i1, &u1); 575 if (nread != 2) { 576 load_err(fname, lineno, 577 _("cannot read tagged data type and length")); 578 return EINVAL; 579 } 580 if (i1 < INT16_MIN || i1 > INT16_MAX || u1 > UINT16_MAX) { 581 load_err(fname, lineno, _("data type or length overflowed")); 582 return EINVAL; 583 } 584 tl->tl_data_type = i1; 585 tl->tl_data_length = u1; 586 if (read_octets_or_minus1(filep, tl->tl_data_length, 587 &tl->tl_data_contents)) { 588 load_err(fname, lineno, _("cannot read tagged data contents")); 589 return EINVAL; 590 } 591 } 592 593 return 0; 594 } 595 596 /* Read a beta 7 entry and add it to the database. Return -1 for end of file, 597 * 0 for success and 1 for failure. */ 598 static int 599 process_k5beta7_princ(krb5_context context, const char *fname, FILE *filep, 600 krb5_boolean verbose, int *linenop) 601 { 602 int retval, nread, i, j; 603 krb5_db_entry *dbentry; 604 int t1, t2, t3, t4; 605 unsigned int u1, u2, u3, u4, u5; 606 char *name = NULL; 607 krb5_key_data *kp = NULL, *kd; 608 krb5_tl_data *tl; 609 krb5_error_code ret; 610 611 dbentry = calloc(1, sizeof(*dbentry)); 612 if (dbentry == NULL) 613 return 1; 614 (*linenop)++; 615 nread = fscanf(filep, "%u\t%u\t%u\t%u\t%u\t", &u1, &u2, &u3, &u4, &u5); 616 if (nread == EOF) { 617 retval = -1; 618 goto cleanup; 619 } 620 if (nread != 5) { 621 load_err(fname, *linenop, _("cannot match size tokens")); 622 goto fail; 623 } 624 625 /* Get memory for flattened principal name */ 626 if (u2 > UINT_MAX / 2) { 627 load_err(fname, *linenop, _("cannot allocate principal (too large)")); 628 goto fail; 629 } 630 name = malloc(u2 + 1); 631 if (name == NULL) 632 goto fail; 633 634 /* Get memory for and form tagged data linked list */ 635 if (u3 > UINT16_MAX) { 636 load_err(fname, *linenop, _("cannot allocate tl_data (too large)")); 637 goto fail; 638 } 639 if (alloc_tl_data(u3, &dbentry->tl_data)) 640 goto fail; 641 dbentry->n_tl_data = u3; 642 643 /* Get memory for key list */ 644 if (u4 > INT16_MAX) { 645 load_err(fname, *linenop, _("invalid key_data size")); 646 goto fail; 647 } 648 if (u4 && (kp = calloc(u4, sizeof(krb5_key_data))) == NULL) 649 goto fail; 650 651 dbentry->len = u1; 652 dbentry->n_key_data = u4; 653 654 if (u5 > UINT16_MAX) { 655 load_err(fname, *linenop, _("invalid principal extra data size")); 656 goto fail; 657 } 658 dbentry->e_length = u5; 659 660 if (kp != NULL) { 661 dbentry->key_data = kp; 662 kp = NULL; 663 } 664 665 /* Read in and parse the principal name */ 666 if (read_string(filep, name, u2, linenop)) { 667 load_err(fname, *linenop, _("cannot read name string")); 668 goto fail; 669 } 670 ret = krb5_parse_name(context, name, &dbentry->princ); 671 if (ret) { 672 com_err(progname, ret, _("while parsing name %s"), name); 673 goto fail; 674 } 675 676 /* Get the fixed principal attributes */ 677 nread = fscanf(filep, "%d\t%d\t%d\t%u\t%u\t%d\t%d\t%d\t", 678 &t1, &t2, &t3, &u1, &u2, &u3, &u4, &u5); 679 if (nread != 8) { 680 load_err(fname, *linenop, _("cannot read principal attributes")); 681 goto fail; 682 } 683 dbentry->attributes = t1; 684 dbentry->max_life = t2; 685 dbentry->max_renewable_life = t3; 686 dbentry->expiration = u1; 687 dbentry->pw_expiration = u2; 688 dbentry->last_success = u3; 689 dbentry->last_failed = u4; 690 dbentry->fail_auth_count = u5; 691 dbentry->mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | 692 KADM5_MAX_LIFE | KADM5_MAX_RLIFE | 693 KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION | KADM5_LAST_SUCCESS | 694 KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT; 695 696 /* Read tagged data. */ 697 if (dbentry->n_tl_data) { 698 if (process_tl_data(fname, filep, *linenop, dbentry->tl_data)) 699 goto fail; 700 for (tl = dbentry->tl_data; tl; tl = tl->tl_data_next) { 701 /* test to set mask fields */ 702 if (tl->tl_data_type == KRB5_TL_KADM_DATA) { 703 XDR xdrs; 704 osa_princ_ent_rec osa_princ_ent; 705 706 /* 707 * Assuming aux_attributes will always be 708 * there 709 */ 710 dbentry->mask |= KADM5_AUX_ATTRIBUTES; 711 712 /* test for an actual policy reference */ 713 memset(&osa_princ_ent, 0, sizeof(osa_princ_ent)); 714 xdrmem_create(&xdrs, (char *)tl->tl_data_contents, 715 tl->tl_data_length, XDR_DECODE); 716 if (xdr_osa_princ_ent_rec(&xdrs, &osa_princ_ent)) { 717 if ((osa_princ_ent.aux_attributes & KADM5_POLICY) && 718 osa_princ_ent.policy != NULL) 719 dbentry->mask |= KADM5_POLICY; 720 kdb_free_entry(NULL, NULL, &osa_princ_ent); 721 } 722 xdr_destroy(&xdrs); 723 } 724 } 725 dbentry->mask |= KADM5_TL_DATA; 726 } 727 728 /* Get the key data. */ 729 for (i = 0; i < dbentry->n_key_data; i++) { 730 kd = &dbentry->key_data[i]; 731 nread = fscanf(filep, "%d\t%d\t", &t1, &t2); 732 if (nread != 2) { 733 load_err(fname, *linenop, _("cannot read key size and version")); 734 goto fail; 735 } 736 if (t1 > KRB5_KDB_V1_KEY_DATA_ARRAY) { 737 load_err(fname, *linenop, _("unsupported key_data_ver version")); 738 goto fail; 739 } 740 if (t2 < 0 || t2 > UINT16_MAX) { 741 load_err(fname, *linenop, _("invalid kvno")); 742 goto fail; 743 } 744 745 kd->key_data_ver = t1; 746 kd->key_data_kvno = t2; 747 748 for (j = 0; j < t1; j++) { 749 nread = fscanf(filep, "%d\t%d\t", &t3, &t4); 750 if (nread != 2 || t4 < 0 || t4 > UINT16_MAX) { 751 load_err(fname, *linenop, 752 _("cannot read key type and length")); 753 goto fail; 754 } 755 kd->key_data_type[j] = t3; 756 kd->key_data_length[j] = t4; 757 if (read_octets_or_minus1(filep, t4, &kd->key_data_contents[j])) { 758 load_err(fname, *linenop, _("cannot read key data")); 759 goto fail; 760 } 761 } 762 } 763 if (dbentry->n_key_data) 764 dbentry->mask |= KADM5_KEY_DATA; 765 766 /* Get the extra data */ 767 if (read_octets_or_minus1(filep, dbentry->e_length, &dbentry->e_data)) { 768 load_err(fname, *linenop, _("cannot read extra data")); 769 goto fail; 770 } 771 772 /* Finally, find the end of the record. */ 773 read_record_end(filep, fname, *linenop); 774 775 ret = krb5_db_put_principal(context, dbentry); 776 if (ret) { 777 com_err(progname, ret, _("while storing %s"), name); 778 goto fail; 779 } 780 781 if (verbose) 782 fprintf(stderr, "%s\n", name); 783 retval = 0; 784 785 cleanup: 786 free(kp); 787 free(name); 788 krb5_db_free_principal(context, dbentry); 789 return retval; 790 791 fail: 792 retval = 1; 793 goto cleanup; 794 } 795 796 static int 797 process_k5beta7_policy(krb5_context context, const char *fname, FILE *filep, 798 krb5_boolean verbose, int *linenop) 799 { 800 osa_policy_ent_rec rec; 801 char namebuf[1024]; 802 unsigned int refcnt; 803 int nread, ret; 804 805 memset(&rec, 0, sizeof(rec)); 806 807 (*linenop)++; 808 rec.name = namebuf; 809 810 nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u", rec.name, 811 &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length, 812 &rec.pw_min_classes, &rec.pw_history_num, &refcnt); 813 if (nread == EOF) 814 return -1; 815 if (nread != 7) { 816 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); 817 return 1; 818 } 819 820 ret = krb5_db_create_policy(context, &rec); 821 if (ret) 822 ret = krb5_db_put_policy(context, &rec); 823 if (ret) { 824 com_err(progname, ret, _("while creating policy")); 825 return 1; 826 } 827 if (verbose) 828 fprintf(stderr, _("created policy %s\n"), rec.name); 829 830 return 0; 831 } 832 833 static int 834 process_r1_8_policy(krb5_context context, const char *fname, FILE *filep, 835 krb5_boolean verbose, int *linenop) 836 { 837 osa_policy_ent_rec rec; 838 char namebuf[1024]; 839 unsigned int refcnt; 840 int nread, ret; 841 842 memset(&rec, 0, sizeof(rec)); 843 844 (*linenop)++; 845 rec.name = namebuf; 846 847 nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u", 848 rec.name, &rec.pw_min_life, &rec.pw_max_life, 849 &rec.pw_min_length, &rec.pw_min_classes, 850 &rec.pw_history_num, &refcnt, &rec.pw_max_fail, 851 &rec.pw_failcnt_interval, &rec.pw_lockout_duration); 852 if (nread == EOF) 853 return -1; 854 if (nread != 10) { 855 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); 856 return 1; 857 } 858 859 ret = krb5_db_create_policy(context, &rec); 860 if (ret) 861 ret = krb5_db_put_policy(context, &rec); 862 if (ret) { 863 com_err(progname, ret, _("while creating policy")); 864 return 1; 865 } 866 if (verbose) 867 fprintf(stderr, "created policy %s\n", rec.name); 868 869 return 0; 870 } 871 872 static int 873 process_r1_11_policy(krb5_context context, const char *fname, FILE *filep, 874 krb5_boolean verbose, int *linenop) 875 { 876 osa_policy_ent_rec rec; 877 krb5_tl_data *tl, *tl_next; 878 char namebuf[1024]; 879 char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1]; 880 unsigned int refcnt; 881 int nread, c, ret = 0; 882 883 memset(&rec, 0, sizeof(rec)); 884 885 (*linenop)++; 886 rec.name = namebuf; 887 888 /* 889 * Due to a historical error, iprop dumps use the same version before and 890 * after the 1.11 policy extensions. So we need to accept both 1.8-format 891 * and 1.11-format policy entries. Begin by reading the 1.8 fields. 892 */ 893 nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u", 894 rec.name, &rec.pw_min_life, &rec.pw_max_life, 895 &rec.pw_min_length, &rec.pw_min_classes, 896 &rec.pw_history_num, &refcnt, &rec.pw_max_fail, 897 &rec.pw_failcnt_interval, &rec.pw_lockout_duration); 898 if (nread == EOF) 899 return -1; 900 if (nread != 10) { 901 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); 902 return 1; 903 } 904 905 /* The next character should be a newline (1.8) or a tab (1.11). */ 906 c = getc(filep); 907 if (c == EOF) 908 return -1; 909 if (c != '\n') { 910 /* Read the additional 1.11-format fields. */ 911 rec.allowed_keysalts = keysaltbuf; 912 nread = fscanf(filep, "%u\t%u\t%u\t" 913 K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN) 914 "\t%hd", &rec.attributes, &rec.max_life, 915 &rec.max_renewable_life, rec.allowed_keysalts, 916 &rec.n_tl_data); 917 if (nread == EOF) 918 return -1; 919 if (nread != 5) { 920 fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); 921 return 1; 922 } 923 924 if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-")) 925 rec.allowed_keysalts = NULL; 926 927 /* Get TL data */ 928 ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data); 929 if (ret) 930 goto cleanup; 931 932 ret = process_tl_data(fname, filep, *linenop, rec.tl_data); 933 if (ret) 934 goto cleanup; 935 } 936 937 ret = krb5_db_create_policy(context, &rec); 938 if (ret) 939 ret = krb5_db_put_policy(context, &rec); 940 if (ret) { 941 com_err(progname, ret, _("while creating policy")); 942 goto cleanup; 943 } 944 if (verbose) 945 fprintf(stderr, "created policy %s\n", rec.name); 946 947 cleanup: 948 for (tl = rec.tl_data; tl; tl = tl_next) { 949 tl_next = tl->tl_data_next; 950 free(tl->tl_data_contents); 951 free(tl); 952 } 953 return ret ? 1 : 0; 954 } 955 956 /* Read a record which is tagged with "princ" or "policy", calling princfn 957 * or policyfn as appropriate. */ 958 static int 959 process_tagged(krb5_context context, const char *fname, FILE *filep, 960 krb5_boolean verbose, int *linenop, load_func princfn, 961 load_func policyfn) 962 { 963 int nread; 964 char rectype[100]; 965 966 nread = fscanf(filep, "%99s\t", rectype); 967 if (nread == EOF) 968 return -1; 969 if (nread != 1) 970 return 1; 971 if (strcmp(rectype, "princ") == 0) 972 return (*princfn)(context, fname, filep, verbose, linenop); 973 if (strcmp(rectype, "policy") == 0) 974 return (*policyfn)(context, fname, filep, verbose, linenop); 975 if (strcmp(rectype, "End") == 0) /* Only expected for OV format */ 976 return -1; 977 978 fprintf(stderr, _("unknown record type \"%s\"\n"), rectype); 979 return 1; 980 } 981 982 static int 983 process_k5beta7_record(krb5_context context, const char *fname, FILE *filep, 984 krb5_boolean verbose, int *linenop) 985 { 986 return process_tagged(context, fname, filep, verbose, linenop, 987 process_k5beta7_princ, process_k5beta7_policy); 988 } 989 990 static int 991 process_r1_8_record(krb5_context context, const char *fname, FILE *filep, 992 krb5_boolean verbose, int *linenop) 993 { 994 return process_tagged(context, fname, filep, verbose, linenop, 995 process_k5beta7_princ, process_r1_8_policy); 996 } 997 998 static int 999 process_r1_11_record(krb5_context context, const char *fname, FILE *filep, 1000 krb5_boolean verbose, int *linenop) 1001 { 1002 return process_tagged(context, fname, filep, verbose, linenop, 1003 process_k5beta7_princ, process_r1_11_policy); 1004 } 1005 1006 dump_version beta7_version = { 1007 "Kerberos version 5", 1008 "kdb5_util load_dump version 4\n", 1009 0, 1010 0, 1011 0, 1012 dump_k5beta7_princ, 1013 dump_k5beta7_policy, 1014 process_k5beta7_record, 1015 }; 1016 dump_version r1_3_version = { 1017 "Kerberos version 5 release 1.3", 1018 "kdb5_util load_dump version 5\n", 1019 0, 1020 0, 1021 0, 1022 dump_k5beta7_princ_withpolicy, 1023 dump_k5beta7_policy, 1024 process_k5beta7_record, 1025 }; 1026 dump_version r1_8_version = { 1027 "Kerberos version 5 release 1.8", 1028 "kdb5_util load_dump version 6\n", 1029 0, 1030 0, 1031 0, 1032 dump_k5beta7_princ_withpolicy, 1033 dump_r1_8_policy, 1034 process_r1_8_record, 1035 }; 1036 dump_version r1_11_version = { 1037 "Kerberos version 5 release 1.11", 1038 "kdb5_util load_dump version 7\n", 1039 0, 1040 0, 1041 0, 1042 dump_k5beta7_princ_withpolicy, 1043 dump_r1_11_policy, 1044 process_r1_11_record, 1045 }; 1046 dump_version iprop_version = { 1047 "Kerberos iprop version", 1048 "iprop", 1049 0, 1050 1, 1051 0, 1052 dump_k5beta7_princ_withpolicy, 1053 dump_k5beta7_policy, 1054 process_k5beta7_record, 1055 }; 1056 dump_version ipropx_1_version = { 1057 "Kerberos iprop extensible version", 1058 "ipropx", 1059 0, 1060 1, 1061 1, 1062 dump_k5beta7_princ_withpolicy, 1063 dump_r1_11_policy, 1064 process_r1_11_record, 1065 }; 1066 1067 /* Read the dump header. Return 1 on success, 0 if the file is not a 1068 * recognized iprop dump format. */ 1069 static int 1070 parse_iprop_header(char *buf, dump_version **dv, kdb_last_t *last) 1071 { 1072 char head[128]; 1073 int nread; 1074 uint32_t u[4]; 1075 uint32_t *up = &u[0]; 1076 1077 nread = sscanf(buf, "%127s %u %u %u %u", head, &u[0], &u[1], &u[2], &u[3]); 1078 if (nread < 1) 1079 return 0; 1080 1081 if (!strcmp(head, ipropx_1_version.header)) { 1082 if (nread != 5) 1083 return 0; 1084 if (u[0] == IPROPX_VERSION_0) { 1085 *dv = &iprop_version; 1086 } else if (u[0] == IPROPX_VERSION_1) { 1087 *dv = &ipropx_1_version; 1088 } else { 1089 fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), progname, 1090 u[0]); 1091 return 0; 1092 } 1093 up = &u[1]; 1094 } else if (!strcmp(head, iprop_version.header)) { 1095 if (nread != 4) 1096 return 0; 1097 *dv = &iprop_version; 1098 } else { 1099 fprintf(stderr, "Invalid iprop header\n"); 1100 return 0; 1101 } 1102 1103 last->last_sno = *up++; 1104 last->last_time.seconds = *up++; 1105 last->last_time.useconds = *up++; 1106 return 1; 1107 } 1108 1109 /* Return true if the serial number and timestamp in an existing dump file is 1110 * in the ulog. */ 1111 static krb5_boolean 1112 current_dump_sno_in_ulog(krb5_context context, const char *ifile) 1113 { 1114 update_status_t status; 1115 dump_version *junk; 1116 kdb_last_t last; 1117 char buf[BUFSIZ], *r; 1118 FILE *f; 1119 1120 f = fopen(ifile, "r"); 1121 if (f == NULL) 1122 return 0; /* aliasing other errors to ENOENT here is OK */ 1123 1124 r = fgets(buf, sizeof(buf), f); 1125 fclose(f); 1126 if (r == NULL) 1127 return errno ? -1 : 0; 1128 1129 if (!parse_iprop_header(buf, &junk, &last)) 1130 return 0; 1131 1132 status = ulog_get_sno_status(context, &last); 1133 return status == UPDATE_OK || status == UPDATE_NIL; 1134 } 1135 1136 void 1137 dump_db(int argc, char **argv) 1138 { 1139 FILE *f; 1140 struct dump_args args; 1141 char *ofile = NULL, *tmpofile = NULL, *new_mkey_file = NULL; 1142 krb5_error_code ret, retval; 1143 dump_version *dump; 1144 int aindex, ok_fd = -1; 1145 bool_t dump_sno = FALSE; 1146 kdb_log_context *log_ctx; 1147 unsigned int ipropx_version = IPROPX_VERSION_0; 1148 krb5_kvno kt_kvno; 1149 krb5_boolean conditional = FALSE; 1150 kdb_last_t last; 1151 krb5_flags iterflags = 0; 1152 1153 /* Parse the arguments. */ 1154 dump = &r1_11_version; 1155 args.verbose = FALSE; 1156 args.omit_nra = FALSE; 1157 mkey_convert = FALSE; 1158 log_ctx = util_context->kdblog_context; 1159 1160 /* 1161 * Parse the qualifiers. 1162 */ 1163 for (aindex = 1; aindex < argc; aindex++) { 1164 if (!strcmp(argv[aindex], "-b7")) { 1165 dump = &beta7_version; 1166 } else if (!strcmp(argv[aindex], "-ov")) { 1167 fprintf(stderr, _("OV dump format not supported\n")); 1168 goto error; 1169 } else if (!strcmp(argv[aindex], "-r13")) { 1170 dump = &r1_3_version; 1171 } else if (!strcmp(argv[aindex], "-r18")) { 1172 dump = &r1_8_version; 1173 } else if (!strncmp(argv[aindex], "-i", 2)) { 1174 /* Intentionally undocumented - only used by kadmin. */ 1175 if (log_ctx && log_ctx->iproprole) { 1176 /* ipropx_version is the maximum version acceptable. */ 1177 ipropx_version = atoi(argv[aindex] + 2); 1178 dump = ipropx_version ? &ipropx_1_version : &iprop_version; 1179 /* 1180 * dump_sno is used to indicate if the serial number should be 1181 * populated in the output file to be used later by iprop for 1182 * updating the replica's update log when loading. 1183 */ 1184 dump_sno = TRUE; 1185 /* FLAG_OMIT_NRA is set to indicate that non-replicated 1186 * attributes should be omitted. */ 1187 args.omit_nra = TRUE; 1188 } else { 1189 fprintf(stderr, _("Iprop not enabled\n")); 1190 goto error; 1191 } 1192 } else if (!strcmp(argv[aindex], "-c")) { 1193 conditional = 1; 1194 } else if (!strcmp(argv[aindex], "-verbose")) { 1195 args.verbose = TRUE; 1196 } else if (!strcmp(argv[aindex], "-mkey_convert")) { 1197 mkey_convert = 1; 1198 } else if (!strcmp(argv[aindex], "-new_mkey_file")) { 1199 new_mkey_file = argv[++aindex]; 1200 mkey_convert = 1; 1201 } else if (!strcmp(argv[aindex], "-rev")) { 1202 iterflags |= KRB5_DB_ITER_REV; 1203 } else if (!strcmp(argv[aindex], "-recurse")) { 1204 iterflags |= KRB5_DB_ITER_RECURSE; 1205 } else { 1206 break; 1207 } 1208 } 1209 1210 args.names = NULL; 1211 args.nnames = 0; 1212 if (aindex < argc) { 1213 ofile = argv[aindex]; 1214 aindex++; 1215 if (aindex < argc) { 1216 args.names = &argv[aindex]; 1217 args.nnames = argc - aindex; 1218 } 1219 } 1220 1221 /* If a conditional ipropx dump we check if the existing dump is 1222 * good enough. */ 1223 if (ofile != NULL && conditional) { 1224 if (!dump->iprop) { 1225 com_err(progname, 0, 1226 _("Conditional dump is an undocumented option for " 1227 "use only for iprop dumps")); 1228 goto error; 1229 } 1230 if (current_dump_sno_in_ulog(util_context, ofile)) 1231 return; 1232 } 1233 1234 /* 1235 * Make sure the database is open. The policy database only has 1236 * to be opened if we try a dump that uses it. 1237 */ 1238 if (!dbactive) { 1239 com_err(progname, 0, _("Database not currently opened!")); 1240 goto error; 1241 } 1242 1243 /* 1244 * If we're doing a master key conversion, set up for it. 1245 */ 1246 if (mkey_convert) { 1247 if (!valid_master_key) { 1248 /* TRUE here means read the keyboard, but only once */ 1249 retval = krb5_db_fetch_mkey(util_context, master_princ, 1250 master_keyblock.enctype, TRUE, FALSE, 1251 NULL, NULL, NULL, &master_keyblock); 1252 if (retval) { 1253 com_err(progname, retval, _("while reading master key")); 1254 exit(1); 1255 } 1256 retval = krb5_db_fetch_mkey_list(util_context, master_princ, 1257 &master_keyblock); 1258 if (retval) { 1259 com_err(progname, retval, _("while verifying master key")); 1260 exit(1); 1261 } 1262 } 1263 new_master_keyblock.enctype = global_params.enctype; 1264 if (new_master_keyblock.enctype == ENCTYPE_UNKNOWN) 1265 new_master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; 1266 1267 if (new_mkey_file) { 1268 if (global_params.mask & KADM5_CONFIG_KVNO) 1269 kt_kvno = global_params.kvno; 1270 else 1271 kt_kvno = IGNORE_VNO; 1272 1273 retval = krb5_db_fetch_mkey(util_context, master_princ, 1274 new_master_keyblock.enctype, FALSE, 1275 FALSE, new_mkey_file, &kt_kvno, NULL, 1276 &new_master_keyblock); 1277 if (retval) { 1278 com_err(progname, retval, _("while reading new master key")); 1279 exit(1); 1280 } 1281 } else { 1282 printf(_("Please enter new master key....\n")); 1283 retval = krb5_db_fetch_mkey(util_context, master_princ, 1284 new_master_keyblock.enctype, TRUE, 1285 TRUE, NULL, NULL, NULL, 1286 &new_master_keyblock); 1287 if (retval) { 1288 com_err(progname, retval, _("while reading new master key")); 1289 exit(1); 1290 } 1291 } 1292 /* Get new master key vno that will be used to protect princs. */ 1293 new_mkvno = get_next_kvno(util_context, master_entry); 1294 } 1295 1296 ret = 0; 1297 1298 if (ofile != NULL && strcmp(ofile, "-")) { 1299 /* Discourage accidental dumping to filenames beginning with '-'. */ 1300 if (ofile[0] == '-') 1301 usage(); 1302 if (!prep_ok_file(util_context, ofile, &ok_fd)) 1303 return; /* prep_ok_file() bumps exit_status */ 1304 f = create_ofile(ofile, &tmpofile); 1305 if (f == NULL) { 1306 com_err(progname, errno, _("while opening %s for writing"), ofile); 1307 goto error; 1308 } 1309 } else { 1310 f = stdout; 1311 } 1312 1313 args.ofile = f; 1314 args.context = util_context; 1315 args.dump = dump; 1316 fprintf(args.ofile, "%s", dump->header); 1317 1318 if (dump_sno) { 1319 ret = ulog_get_last(util_context, &last); 1320 if (ret) { 1321 com_err(progname, ret, _("while reading update log header")); 1322 goto error; 1323 } 1324 if (ipropx_version) 1325 fprintf(f, " %u", IPROPX_VERSION); 1326 fprintf(f, " %u", last.last_sno); 1327 fprintf(f, " %u", last.last_time.seconds); 1328 fprintf(f, " %u", last.last_time.useconds); 1329 } 1330 1331 if (dump->header[strlen(dump->header)-1] != '\n') 1332 fputc('\n', args.ofile); 1333 1334 ret = krb5_db_iterate(util_context, NULL, dump_iterator, &args, iterflags); 1335 if (ret) { 1336 com_err(progname, ret, _("performing %s dump"), dump->name); 1337 goto error; 1338 } 1339 1340 /* Don't dump policies if specific principal entries were requested. */ 1341 if (dump->dump_policy != NULL && args.nnames == 0) { 1342 ret = krb5_db_iter_policy(util_context, "*", dump->dump_policy, &args); 1343 if (ret) { 1344 com_err(progname, ret, _("performing %s dump"), dump->name); 1345 goto error; 1346 } 1347 } 1348 1349 if (f != stdout) { 1350 fclose(f); 1351 finish_ofile(ofile, &tmpofile); 1352 update_ok_file(util_context, ok_fd); 1353 } 1354 return; 1355 1356 error: 1357 if (tmpofile != NULL) 1358 unlink(tmpofile); 1359 free(tmpofile); 1360 exit_status++; 1361 } 1362 1363 /* Restore the database from any version dump file. */ 1364 static int 1365 restore_dump(krb5_context context, char *dumpfile, FILE *f, 1366 krb5_boolean verbose, dump_version *dump) 1367 { 1368 int err = 0; 1369 int lineno = 1; 1370 1371 /* Process the records. */ 1372 while (!(err = dump->load_record(context, dumpfile, f, verbose, &lineno))); 1373 if (err != -1) { 1374 fprintf(stderr, _("%s: error processing line %d of %s\n"), progname, 1375 lineno, dumpfile); 1376 return err; 1377 } 1378 return 0; 1379 } 1380 1381 void 1382 load_db(int argc, char **argv) 1383 { 1384 krb5_error_code ret; 1385 FILE *f = NULL; 1386 char *dumpfile = NULL, *dbname, buf[BUFSIZ]; 1387 dump_version *load = NULL; 1388 int aindex; 1389 kdb_log_context *log_ctx; 1390 kdb_last_t last; 1391 krb5_boolean db_locked = FALSE, temp_db_created = FALSE; 1392 krb5_boolean verbose = FALSE, update = FALSE, iprop_load = FALSE; 1393 1394 /* Parse the arguments. */ 1395 dbname = global_params.dbname; 1396 exit_status = 0; 1397 log_ctx = util_context->kdblog_context; 1398 1399 for (aindex = 1; aindex < argc; aindex++) { 1400 if (!strcmp(argv[aindex], "-b7")){ 1401 load = &beta7_version; 1402 } else if (!strcmp(argv[aindex], "-ov")) { 1403 fprintf(stderr, _("OV dump format not supported\n")); 1404 goto error; 1405 } else if (!strcmp(argv[aindex], "-r13")) { 1406 load = &r1_3_version; 1407 } else if (!strcmp(argv[aindex], "-r18")){ 1408 load = &r1_8_version; 1409 } else if (!strcmp(argv[aindex], "-i")) { 1410 /* Intentionally undocumented - only used by kadmin. */ 1411 if (log_ctx && log_ctx->iproprole) { 1412 load = &iprop_version; 1413 iprop_load = TRUE; 1414 } else { 1415 fprintf(stderr, _("Iprop not enabled\n")); 1416 goto error; 1417 } 1418 } else if (!strcmp(argv[aindex], "-verbose")) { 1419 verbose = TRUE; 1420 } else if (!strcmp(argv[aindex], "-update")){ 1421 update = TRUE; 1422 } else if (!strcmp(argv[aindex], "-hash")) { 1423 if (!add_db_arg("hash=true")) { 1424 com_err(progname, ENOMEM, _("while parsing options")); 1425 goto error; 1426 } 1427 } else { 1428 break; 1429 } 1430 } 1431 if (argc - aindex != 1) 1432 usage(); 1433 dumpfile = argv[aindex]; 1434 1435 /* Open the dumpfile. */ 1436 if (dumpfile != NULL) { 1437 f = fopen(dumpfile, "r"); 1438 if (f == NULL) { 1439 com_err(progname, errno, _("while opening %s"), dumpfile); 1440 goto error; 1441 } 1442 } else { 1443 f = stdin; 1444 dumpfile = _("standard input"); 1445 } 1446 1447 /* Auto-detect dump version if we weren't told, or verify if we were. */ 1448 if (fgets(buf, sizeof(buf), f) == NULL) { 1449 fprintf(stderr, _("%s: can't read dump header in %s\n"), progname, 1450 dumpfile); 1451 goto error; 1452 } 1453 if (load) { 1454 /* Only check what we know; some headers only contain a prefix. 1455 * NB: this should work for ipropx even though load is iprop */ 1456 if (strncmp(buf, load->header, strlen(load->header)) != 0) { 1457 fprintf(stderr, _("%s: dump header bad in %s\n"), progname, 1458 dumpfile); 1459 goto error; 1460 } 1461 } else { 1462 if (strcmp(buf, beta7_version.header) == 0) { 1463 load = &beta7_version; 1464 } else if (strcmp(buf, r1_3_version.header) == 0) { 1465 load = &r1_3_version; 1466 } else if (strcmp(buf, r1_8_version.header) == 0) { 1467 load = &r1_8_version; 1468 } else if (strcmp(buf, r1_11_version.header) == 0) { 1469 load = &r1_11_version; 1470 } else { 1471 fprintf(stderr, _("%s: dump header bad in %s\n"), progname, 1472 dumpfile); 1473 goto error; 1474 } 1475 } 1476 1477 if (global_params.iprop_enabled && 1478 ulog_map(util_context, global_params.iprop_logfile, 1479 global_params.iprop_ulogsize)) { 1480 fprintf(stderr, _("Could not open iprop ulog\n")); 1481 goto error; 1482 } 1483 1484 if (load->updateonly && !update) { 1485 fprintf(stderr, _("%s: dump version %s can only be loaded with the " 1486 "-update flag\n"), progname, load->name); 1487 goto error; 1488 } 1489 1490 /* If we are not in update mode, we create an alternate database and then 1491 * promote it to be the live db. */ 1492 if (!update) { 1493 if (!add_db_arg("temporary")) { 1494 com_err(progname, ENOMEM, _("computing parameters for database")); 1495 goto error; 1496 } 1497 1498 if (iprop_load && !add_db_arg("merge_nra")) { 1499 com_err(progname, ENOMEM, _("computing parameters for database")); 1500 goto error; 1501 } 1502 1503 ret = krb5_db_create(util_context, db5util_db_args); 1504 if (ret) { 1505 com_err(progname, ret, _("while creating database")); 1506 goto error; 1507 } 1508 temp_db_created = TRUE; 1509 } else { 1510 /* Initialize the database. */ 1511 ret = krb5_db_open(util_context, db5util_db_args, 1512 KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); 1513 if (ret) { 1514 com_err(progname, ret, _("while opening database")); 1515 goto error; 1516 } 1517 1518 /* Make sure the db is left unusable if the update fails, if the db 1519 * supports locking. */ 1520 ret = krb5_db_lock(util_context, KRB5_DB_LOCKMODE_PERMANENT); 1521 if (ret == 0) { 1522 db_locked = TRUE; 1523 } else if (ret != KRB5_PLUGIN_OP_NOTSUPP) { 1524 com_err(progname, ret, _("while permanently locking database")); 1525 goto error; 1526 } 1527 } 1528 1529 if (log_ctx != NULL && log_ctx->iproprole && !update) { 1530 /* Don't record updates we are making to the temporary DB. We will 1531 * reinitialize or update the ulog header after promoting it. */ 1532 log_ctx->iproprole = IPROP_REPLICA; 1533 if (iprop_load) { 1534 /* Parse the iprop header information. */ 1535 if (!parse_iprop_header(buf, &load, &last)) 1536 goto error; 1537 } 1538 } 1539 1540 if (restore_dump(util_context, dumpfile ? dumpfile : _("standard input"), 1541 f, verbose, load)) { 1542 fprintf(stderr, _("%s: %s restore failed\n"), progname, load->name); 1543 goto error; 1544 } 1545 1546 if (db_locked && (ret = krb5_db_unlock(util_context))) { 1547 com_err(progname, ret, _("while unlocking database")); 1548 goto error; 1549 } 1550 1551 if (!update) { 1552 /* Initialize the ulog header before promoting so we can't leave behind 1553 * the pre-load ulog state if we are killed just after promoting. */ 1554 if (log_ctx != NULL && log_ctx->iproprole) { 1555 ret = ulog_init_header(util_context); 1556 if (ret) { 1557 com_err(progname, ret, _("while reinitializing update log")); 1558 goto error; 1559 } 1560 } 1561 1562 ret = krb5_db_promote(util_context, db5util_db_args); 1563 /* Ignore a not supported error since there is nothing to do about it 1564 * anyway. */ 1565 if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) { 1566 com_err(progname, ret, 1567 _("while making newly loaded database live")); 1568 goto error; 1569 } 1570 1571 if (log_ctx != NULL && log_ctx->iproprole) { 1572 /* Reinitialize the ulog header since we replaced the DB, and 1573 * record the iprop state if we received it. */ 1574 ret = ulog_init_header(util_context); 1575 if (ret) { 1576 com_err(progname, ret, _("while reinitializing update log")); 1577 goto error; 1578 } 1579 if (iprop_load) { 1580 ret = ulog_set_last(util_context, &last); 1581 if (ret) { 1582 com_err(progname, ret, 1583 _("while writing update log header")); 1584 goto error; 1585 } 1586 } 1587 } 1588 } 1589 1590 cleanup: 1591 /* If we created a temporary DB but didn't succeed, destroy it. */ 1592 if (exit_status && temp_db_created) { 1593 ret = krb5_db_destroy(util_context, db5util_db_args); 1594 /* Ignore a not supported error since there is nothing to do about 1595 * it anyway. */ 1596 if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) { 1597 com_err(progname, ret, _("while deleting bad database %s"), 1598 dbname); 1599 } 1600 } 1601 1602 if (f != NULL && f != stdin) 1603 fclose(f); 1604 1605 return; 1606 1607 error: 1608 exit_status++; 1609 goto cleanup; 1610 } 1611