Path: blob/main/crypto/krb5/src/kadmin/dbutil/dump.c
34907 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/* kadmin/dbutil/dump.c - Dump a KDC database */2/*3* Copyright 1990,1991,2001,2006,2008,2009,2013 by the Massachusetts Institute4* of Technology. All Rights Reserved.5*6* Export of this software from the United States of America may7* require a specific license from the United States Government.8* It is the responsibility of any person or organization contemplating9* export to obtain such a license before exporting.10*11* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and12* distribute this software and its documentation for any purpose and13* without fee is hereby granted, provided that the above copyright14* notice appear in all copies and that both that copyright notice and15* this permission notice appear in supporting documentation, and that16* the name of M.I.T. not be used in advertising or publicity pertaining17* to distribution of the software without specific, written prior18* permission. Furthermore if you modify this software you must label19* your software as modified software and not distribute it in such a20* fashion that it might be confused with the original M.I.T. software.21* M.I.T. makes no representations about the suitability of22* this software for any purpose. It is provided "as is" without express23* or implied warranty.24*/25/*26* Copyright 2004 Sun Microsystems, Inc. All rights reserved.27* Use is subject to license terms.28*/2930#include <k5-int.h>31#include <kadm5/admin.h>32#include <kadm5/server_internal.h>33#include <kdb.h>34#include <com_err.h>35#include "kdb5_util.h"36#include "k5-regex.h"3738/* Needed for master key conversion. */39static krb5_boolean mkey_convert;40krb5_keyblock new_master_keyblock;41krb5_kvno new_mkvno;4243#define K5Q1(x) #x44#define K5Q(x) K5Q1(x)45#define K5CONST_WIDTH_SCANF_STR(x) "%" K5Q(x) "s"4647typedef krb5_error_code (*dump_func)(krb5_context context,48krb5_db_entry *entry, const char *name,49FILE *fp, krb5_boolean verbose,50krb5_boolean omit_nra);51typedef int (*load_func)(krb5_context context, const char *dumpfile, FILE *fp,52krb5_boolean verbose, int *linenop);5354typedef struct _dump_version {55char *name;56char *header;57int updateonly;58int iprop;59int ipropx;60dump_func dump_princ;61osa_adb_iter_policy_func dump_policy;62load_func load_record;63} dump_version;6465struct dump_args {66FILE *ofile;67krb5_context context;68char **names;69int nnames;70krb5_boolean verbose;71krb5_boolean omit_nra; /* omit non-replicated attributes */72dump_version *dump;73};7475/* External data */76extern krb5_db_entry *master_entry;7778/*79* Re-encrypt the key_data with the new master key...80*/81krb5_error_code82master_key_convert(krb5_context context, krb5_db_entry *db_entry)83{84krb5_error_code retval;85krb5_keyblock v5plainkey, *key_ptr, *tmp_mkey;86krb5_keysalt keysalt;87krb5_key_data new_key_data, *key_data;88krb5_boolean is_mkey;89krb5_kvno kvno;90int i, j;9192is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ);9394if (is_mkey) {95return add_new_mkey(context, db_entry, &new_master_keyblock,96new_mkvno);97}9899for (i = 0; i < db_entry->n_key_data; i++) {100key_data = &db_entry->key_data[i];101retval = krb5_dbe_find_mkey(context, db_entry, &tmp_mkey);102if (retval)103return retval;104retval = krb5_dbe_decrypt_key_data(context, tmp_mkey, key_data,105&v5plainkey, &keysalt);106if (retval)107return retval;108109memset(&new_key_data, 0, sizeof(new_key_data));110111key_ptr = &v5plainkey;112kvno = key_data->key_data_kvno;113114retval = krb5_dbe_encrypt_key_data(context, &new_master_keyblock,115key_ptr, &keysalt, kvno,116&new_key_data);117if (retval)118return retval;119krb5_free_keyblock_contents(context, &v5plainkey);120for (j = 0; j < key_data->key_data_ver; j++) {121if (key_data->key_data_length[j])122free(key_data->key_data_contents[j]);123}124*key_data = new_key_data;125}126assert(new_mkvno > 0);127return krb5_dbe_update_mkvno(context, db_entry, new_mkvno);128}129130/* Create temp file for new dump to be named ofile. */131static FILE *132create_ofile(char *ofile, char **tmpname)133{134int fd = -1;135FILE *f;136137*tmpname = NULL;138if (asprintf(tmpname, "%s-XXXXXX", ofile) < 0)139goto error;140141fd = mkstemp(*tmpname);142if (fd == -1)143goto error;144145f = fdopen(fd, "w+");146if (f != NULL)147return f;148149error:150com_err(progname, errno, _("while allocating temporary filename dump"));151if (fd >= 0)152unlink(*tmpname);153exit(1);154}155156/* Rename new dump file into place. */157static void158finish_ofile(char *ofile, char **tmpname)159{160if (rename(*tmpname, ofile) == -1) {161com_err(progname, errno, _("while renaming dump file into place"));162exit(1);163}164free(*tmpname);165*tmpname = NULL;166}167168/* Create the .dump_ok file. */169static krb5_boolean170prep_ok_file(krb5_context context, char *file_name, int *fd_out)171{172static char ok[] = ".dump_ok";173krb5_error_code retval;174char *file_ok = NULL;175int fd = -1;176krb5_boolean success = FALSE;177178*fd_out = -1;179180if (asprintf(&file_ok, "%s%s", file_name, ok) < 0) {181com_err(progname, ENOMEM, _("while allocating dump_ok filename"));182goto cleanup;183}184185fd = open(file_ok, O_WRONLY | O_CREAT | O_TRUNC, 0600);186if (fd == -1) {187com_err(progname, errno, _("while creating 'ok' file, '%s'"), file_ok);188goto cleanup;189}190retval = krb5_lock_file(context, fd, KRB5_LOCKMODE_EXCLUSIVE);191if (retval) {192com_err(progname, retval, _("while locking 'ok' file, '%s'"), file_ok);193goto cleanup;194}195196*fd_out = fd;197fd = -1;198success = TRUE;199200cleanup:201free(file_ok);202if (fd != -1)203close(fd);204if (!success)205exit_status++;206return success;207}208209/*210* Update the "ok" file.211*/212static void213update_ok_file(krb5_context context, int fd)214{215write(fd, "", 1);216krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK);217close(fd);218}219220/* Return true if a principal name matches a regular expression or string. */221static int222name_matches(char *name, struct dump_args *args)223{224regex_t reg;225regmatch_t rmatch;226int st;227char errmsg[BUFSIZ];228int i, match;229230/* Check each regular expression in args. */231match = args->nnames ? 0 : 1;232for (i = 0; i < args->nnames && !match; i++) {233/* Compile the regular expression. */234st = regcomp(®, args->names[i], REG_EXTENDED);235if (st) {236regerror(st, ®, errmsg, sizeof(errmsg));237fprintf(stderr, _("%s: regular expression error: %s\n"), progname,238errmsg);239break;240}241/* See if we have a match. */242st = regexec(®, name, 1, &rmatch, 0);243if (st == 0) {244/* See if it matches the whole name. */245if (rmatch.rm_so == 0 && (size_t)rmatch.rm_eo == strlen(name))246match = 1;247} else if (st != REG_NOMATCH) {248regerror(st, ®, errmsg, sizeof(errmsg));249fprintf(stderr, _("%s: regular expression match error: %s\n"),250progname, errmsg);251break;252}253regfree(®);254}255return match;256}257258/* Output "-1" if len is 0; otherwise output len bytes of data in hex. */259static void260dump_octets_or_minus1(FILE *fp, unsigned char *data, size_t len)261{262if (len > 0) {263for (; len > 0; len--)264fprintf(fp, "%02x", *data++);265} else {266fprintf(fp, "-1");267}268}269270/*271* Dump TL data; common to principals and policies.272*273* If filter_kadm then the KRB5_TL_KADM_DATA (where a principal's policy274* name is stored) is filtered out. This is for dump formats that don't275* support policies.276*/277static void278dump_tl_data(FILE *ofile, krb5_tl_data *tlp, krb5_boolean filter_kadm)279{280for (; tlp != NULL; tlp = tlp->tl_data_next) {281if (tlp->tl_data_type == KRB5_TL_KADM_DATA && filter_kadm)282continue;283fprintf(ofile, "\t%d\t%d\t", (int)tlp->tl_data_type,284(int)tlp->tl_data_length);285dump_octets_or_minus1(ofile, tlp->tl_data_contents,286tlp->tl_data_length);287}288}289290/* Dump a principal entry in krb5 beta 7 format. Omit kadmin tl-data if kadm291* is false. */292static krb5_error_code293k5beta7_common(krb5_context context, krb5_db_entry *entry,294const char *name, FILE *fp, krb5_boolean verbose,295krb5_boolean omit_nra, krb5_boolean kadm)296{297krb5_tl_data *tlp;298krb5_key_data *kdata;299int counter, skip, i;300301/*302* The dump format is as follows:303* len strlen(name) n_tl_data n_key_data e_length304* name305* attributes max_life max_renewable_life expiration306* pw_expiration last_success last_failed fail_auth_count307* n_tl_data*[type length <contents>]308* n_key_data*[ver kvno ver*(type length <contents>)]309* <e_data>310* Fields which are not encapsulated by angle-brackets are to appear311* verbatim. A bracketed field's absence is indicated by a -1 in its312* place.313*/314315/* Make sure that the tagged list is reasonably correct. */316counter = skip = 0;317for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) {318/* Don't dump tl data types we know aren't understood by earlier319* versions. */320if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm)321skip++;322else323counter++;324}325326if (counter + skip != entry->n_tl_data) {327fprintf(stderr, _("%s: tagged data list inconsistency for %s "328"(counted %d, stored %d)\n"), progname, name,329counter + skip, (int)entry->n_tl_data);330return EINVAL;331}332333/* Write out header. */334fprintf(fp, "princ\t%d\t%lu\t%d\t%d\t%d\t%s\t", (int)entry->len,335(unsigned long)strlen(name), counter, (int)entry->n_key_data,336(int)entry->e_length, name);337fprintf(fp, "%d\t%d\t%d\t%u\t%u\t%u\t%u\t%d", entry->attributes,338entry->max_life, entry->max_renewable_life,339(unsigned int)entry->expiration,340(unsigned int)entry->pw_expiration,341(unsigned int)(omit_nra ? 0 : entry->last_success),342(unsigned int)(omit_nra ? 0 : entry->last_failed),343omit_nra ? 0 : entry->fail_auth_count);344345/* Write out tagged data. */346dump_tl_data(fp, entry->tl_data, !kadm);347fprintf(fp, "\t");348349/* Write out key data. */350for (counter = 0; counter < entry->n_key_data; counter++) {351kdata = &entry->key_data[counter];352fprintf(fp, "%d\t%d\t", (int)kdata->key_data_ver,353(int)kdata->key_data_kvno);354for (i = 0; i < kdata->key_data_ver; i++) {355fprintf(fp, "%d\t%d\t", kdata->key_data_type[i],356kdata->key_data_length[i]);357dump_octets_or_minus1(fp, kdata->key_data_contents[i],358kdata->key_data_length[i]);359fprintf(fp, "\t");360}361}362363/* Write out extra data. */364dump_octets_or_minus1(fp, entry->e_data, entry->e_length);365366/* Write trailer. */367fprintf(fp, ";\n");368369if (verbose)370fprintf(stderr, "%s\n", name);371372return 0;373}374375/* Output a dump record in krb5b7 format. */376static krb5_error_code377dump_k5beta7_princ(krb5_context context, krb5_db_entry *entry,378const char *name, FILE *fp, krb5_boolean verbose,379krb5_boolean omit_nra)380{381return k5beta7_common(context, entry, name, fp, verbose, omit_nra, FALSE);382}383384static krb5_error_code385dump_k5beta7_princ_withpolicy(krb5_context context, krb5_db_entry *entry,386const char *name, FILE *fp, krb5_boolean verbose,387krb5_boolean omit_nra)388{389return k5beta7_common(context, entry, name, fp, verbose, omit_nra, TRUE);390}391392static void393dump_k5beta7_policy(void *data, osa_policy_ent_t entry)394{395struct dump_args *arg = data;396397fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name,398entry->pw_min_life, entry->pw_max_life, entry->pw_min_length,399entry->pw_min_classes, entry->pw_history_num, 0);400}401402static void403dump_r1_8_policy(void *data, osa_policy_ent_t entry)404{405struct dump_args *arg = data;406407fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n",408entry->name, entry->pw_min_life, entry->pw_max_life,409entry->pw_min_length, entry->pw_min_classes, entry->pw_history_num,4100, entry->pw_max_fail, entry->pw_failcnt_interval,411entry->pw_lockout_duration);412}413414static void415dump_r1_11_policy(void *data, osa_policy_ent_t entry)416{417struct dump_args *arg = data;418419fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t"420"%d\t%d\t%d\t%s\t%d", entry->name, entry->pw_min_life,421entry->pw_max_life, entry->pw_min_length, entry->pw_min_classes,422entry->pw_history_num, 0, entry->pw_max_fail,423entry->pw_failcnt_interval, entry->pw_lockout_duration,424entry->attributes, entry->max_life, entry->max_renewable_life,425entry->allowed_keysalts ? entry->allowed_keysalts : "-",426entry->n_tl_data);427428dump_tl_data(arg->ofile, entry->tl_data, FALSE);429fprintf(arg->ofile, "\n");430}431432static krb5_error_code433dump_iterator(void *ptr, krb5_db_entry *entry)434{435krb5_error_code ret;436struct dump_args *args = ptr;437char *name;438439ret = krb5_unparse_name(args->context, entry->princ, &name);440if (ret) {441com_err(progname, ret, _("while unparsing principal name"));442return ret;443}444445/* Re-encode the keys in the new master key, if necessary. */446if (mkey_convert) {447ret = master_key_convert(args->context, entry);448if (ret) {449com_err(progname, ret, _("while converting %s to new master key"),450name);451goto cleanup;452}453}454455/* Don't dump this entry if we have match strings and it doesn't match. */456if (args->nnames > 0 && !name_matches(name, args))457goto cleanup;458459ret = args->dump->dump_princ(args->context, entry, name, args->ofile,460args->verbose, args->omit_nra);461462cleanup:463free(name);464return ret;465}466467static inline void468load_err(const char *fname, int lineno, const char *msg)469{470fprintf(stderr, _("%s(%d): %s\n"), fname, lineno, msg);471}472473/* Read a string of bytes. Increment *lp for each newline. Return 0 on474* success, 1 on failure. */475static int476read_string(FILE *f, char *buf, int len, int *lp)477{478int c, i;479480for (i = 0; i < len; i++) {481c = fgetc(f);482if (c < 0)483return 1;484if (c == '\n')485(*lp)++;486buf[i] = c;487}488buf[len] = '\0';489return 0;490}491492/* Read a string of two-character representations of bytes. */493static int494read_octet_string(FILE *f, unsigned char *buf, int len)495{496int c, i;497498for (i = 0; i < len; i++) {499if (fscanf(f, "%02x", &c) != 1)500return 1;501buf[i] = c;502}503return 0;504}505506/* Read the end of a dumpfile record. */507static void508read_record_end(FILE *f, const char *fn, int lineno)509{510int ch;511512if ((ch = fgetc(f)) != ';' || (ch = fgetc(f)) != '\n') {513fprintf(stderr, _("%s(%d): ignoring trash at end of line: "), fn,514lineno);515while (ch != '\n') {516putc(ch, stderr);517ch = fgetc(f);518}519putc(ch, stderr);520}521}522523/* Allocate and form a TL data list of a desired size. */524static int525alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp)526{527krb5_tl_data **tlp = tldp;528int i;529530for (i = 0; i < n_tl_data; i++) {531*tlp = calloc(1, sizeof(krb5_tl_data));532if (*tlp == NULL)533return ENOMEM; /* caller cleans up */534tlp = &((*tlp)->tl_data_next);535}536537return 0;538}539540/* If len is zero, read the string "-1" from fp. Otherwise allocate space and541* read len octets. Return 0 on success, 1 on failure. */542static int543read_octets_or_minus1(FILE *fp, size_t len, unsigned char **out)544{545int ival;546unsigned char *buf;547548*out = NULL;549if (len == 0)550return fscanf(fp, "%d", &ival) != 1 || ival != -1;551buf = malloc(len);552if (buf == NULL)553return 1;554if (read_octet_string(fp, buf, len)) {555free(buf);556return 1;557}558*out = buf;559return 0;560}561562/* Read TL data for a principal or policy. Print an error and return -1 on563* failure. */564static int565process_tl_data(const char *fname, FILE *filep, int lineno,566krb5_tl_data *tl_data)567{568krb5_tl_data *tl;569int nread, i1;570unsigned int u1;571572for (tl = tl_data; tl; tl = tl->tl_data_next) {573nread = fscanf(filep, "%d\t%u\t", &i1, &u1);574if (nread != 2) {575load_err(fname, lineno,576_("cannot read tagged data type and length"));577return EINVAL;578}579if (i1 < INT16_MIN || i1 > INT16_MAX || u1 > UINT16_MAX) {580load_err(fname, lineno, _("data type or length overflowed"));581return EINVAL;582}583tl->tl_data_type = i1;584tl->tl_data_length = u1;585if (read_octets_or_minus1(filep, tl->tl_data_length,586&tl->tl_data_contents)) {587load_err(fname, lineno, _("cannot read tagged data contents"));588return EINVAL;589}590}591592return 0;593}594595/* Read a beta 7 entry and add it to the database. Return -1 for end of file,596* 0 for success and 1 for failure. */597static int598process_k5beta7_princ(krb5_context context, const char *fname, FILE *filep,599krb5_boolean verbose, int *linenop)600{601int retval, nread, i, j;602krb5_db_entry *dbentry;603int t1, t2, t3, t4;604unsigned int u1, u2, u3, u4, u5;605char *name = NULL;606krb5_key_data *kp = NULL, *kd;607krb5_tl_data *tl;608krb5_error_code ret;609610dbentry = calloc(1, sizeof(*dbentry));611if (dbentry == NULL)612return 1;613(*linenop)++;614nread = fscanf(filep, "%u\t%u\t%u\t%u\t%u\t", &u1, &u2, &u3, &u4, &u5);615if (nread == EOF) {616retval = -1;617goto cleanup;618}619if (nread != 5) {620load_err(fname, *linenop, _("cannot match size tokens"));621goto fail;622}623624/* Get memory for flattened principal name */625if (u2 > UINT_MAX / 2) {626load_err(fname, *linenop, _("cannot allocate principal (too large)"));627goto fail;628}629name = malloc(u2 + 1);630if (name == NULL)631goto fail;632633/* Get memory for and form tagged data linked list */634if (u3 > UINT16_MAX) {635load_err(fname, *linenop, _("cannot allocate tl_data (too large)"));636goto fail;637}638if (alloc_tl_data(u3, &dbentry->tl_data))639goto fail;640dbentry->n_tl_data = u3;641642/* Get memory for key list */643if (u4 > INT16_MAX) {644load_err(fname, *linenop, _("invalid key_data size"));645goto fail;646}647if (u4 && (kp = calloc(u4, sizeof(krb5_key_data))) == NULL)648goto fail;649650dbentry->len = u1;651dbentry->n_key_data = u4;652653if (u5 > UINT16_MAX) {654load_err(fname, *linenop, _("invalid principal extra data size"));655goto fail;656}657dbentry->e_length = u5;658659if (kp != NULL) {660dbentry->key_data = kp;661kp = NULL;662}663664/* Read in and parse the principal name */665if (read_string(filep, name, u2, linenop)) {666load_err(fname, *linenop, _("cannot read name string"));667goto fail;668}669ret = krb5_parse_name(context, name, &dbentry->princ);670if (ret) {671com_err(progname, ret, _("while parsing name %s"), name);672goto fail;673}674675/* Get the fixed principal attributes */676nread = fscanf(filep, "%d\t%d\t%d\t%u\t%u\t%d\t%d\t%d\t",677&t1, &t2, &t3, &u1, &u2, &u3, &u4, &u5);678if (nread != 8) {679load_err(fname, *linenop, _("cannot read principal attributes"));680goto fail;681}682dbentry->attributes = t1;683dbentry->max_life = t2;684dbentry->max_renewable_life = t3;685dbentry->expiration = u1;686dbentry->pw_expiration = u2;687dbentry->last_success = u3;688dbentry->last_failed = u4;689dbentry->fail_auth_count = u5;690dbentry->mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |691KADM5_MAX_LIFE | KADM5_MAX_RLIFE |692KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION | KADM5_LAST_SUCCESS |693KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT;694695/* Read tagged data. */696if (dbentry->n_tl_data) {697if (process_tl_data(fname, filep, *linenop, dbentry->tl_data))698goto fail;699for (tl = dbentry->tl_data; tl; tl = tl->tl_data_next) {700/* test to set mask fields */701if (tl->tl_data_type == KRB5_TL_KADM_DATA) {702XDR xdrs;703osa_princ_ent_rec osa_princ_ent;704705/*706* Assuming aux_attributes will always be707* there708*/709dbentry->mask |= KADM5_AUX_ATTRIBUTES;710711/* test for an actual policy reference */712memset(&osa_princ_ent, 0, sizeof(osa_princ_ent));713xdrmem_create(&xdrs, (char *)tl->tl_data_contents,714tl->tl_data_length, XDR_DECODE);715if (xdr_osa_princ_ent_rec(&xdrs, &osa_princ_ent)) {716if ((osa_princ_ent.aux_attributes & KADM5_POLICY) &&717osa_princ_ent.policy != NULL)718dbentry->mask |= KADM5_POLICY;719kdb_free_entry(NULL, NULL, &osa_princ_ent);720}721xdr_destroy(&xdrs);722}723}724dbentry->mask |= KADM5_TL_DATA;725}726727/* Get the key data. */728for (i = 0; i < dbentry->n_key_data; i++) {729kd = &dbentry->key_data[i];730nread = fscanf(filep, "%d\t%d\t", &t1, &t2);731if (nread != 2) {732load_err(fname, *linenop, _("cannot read key size and version"));733goto fail;734}735if (t1 > KRB5_KDB_V1_KEY_DATA_ARRAY) {736load_err(fname, *linenop, _("unsupported key_data_ver version"));737goto fail;738}739if (t2 < 0 || t2 > UINT16_MAX) {740load_err(fname, *linenop, _("invalid kvno"));741goto fail;742}743744kd->key_data_ver = t1;745kd->key_data_kvno = t2;746747for (j = 0; j < t1; j++) {748nread = fscanf(filep, "%d\t%d\t", &t3, &t4);749if (nread != 2 || t4 < 0 || t4 > UINT16_MAX) {750load_err(fname, *linenop,751_("cannot read key type and length"));752goto fail;753}754kd->key_data_type[j] = t3;755kd->key_data_length[j] = t4;756if (read_octets_or_minus1(filep, t4, &kd->key_data_contents[j])) {757load_err(fname, *linenop, _("cannot read key data"));758goto fail;759}760}761}762if (dbentry->n_key_data)763dbentry->mask |= KADM5_KEY_DATA;764765/* Get the extra data */766if (read_octets_or_minus1(filep, dbentry->e_length, &dbentry->e_data)) {767load_err(fname, *linenop, _("cannot read extra data"));768goto fail;769}770771/* Finally, find the end of the record. */772read_record_end(filep, fname, *linenop);773774ret = krb5_db_put_principal(context, dbentry);775if (ret) {776com_err(progname, ret, _("while storing %s"), name);777goto fail;778}779780if (verbose)781fprintf(stderr, "%s\n", name);782retval = 0;783784cleanup:785free(kp);786free(name);787krb5_db_free_principal(context, dbentry);788return retval;789790fail:791retval = 1;792goto cleanup;793}794795static int796process_k5beta7_policy(krb5_context context, const char *fname, FILE *filep,797krb5_boolean verbose, int *linenop)798{799osa_policy_ent_rec rec;800char namebuf[1024];801unsigned int refcnt;802int nread, ret;803804memset(&rec, 0, sizeof(rec));805806(*linenop)++;807rec.name = namebuf;808809nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u", rec.name,810&rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length,811&rec.pw_min_classes, &rec.pw_history_num, &refcnt);812if (nread == EOF)813return -1;814if (nread != 7) {815fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);816return 1;817}818819ret = krb5_db_create_policy(context, &rec);820if (ret)821ret = krb5_db_put_policy(context, &rec);822if (ret) {823com_err(progname, ret, _("while creating policy"));824return 1;825}826if (verbose)827fprintf(stderr, _("created policy %s\n"), rec.name);828829return 0;830}831832static int833process_r1_8_policy(krb5_context context, const char *fname, FILE *filep,834krb5_boolean verbose, int *linenop)835{836osa_policy_ent_rec rec;837char namebuf[1024];838unsigned int refcnt;839int nread, ret;840841memset(&rec, 0, sizeof(rec));842843(*linenop)++;844rec.name = namebuf;845846nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",847rec.name, &rec.pw_min_life, &rec.pw_max_life,848&rec.pw_min_length, &rec.pw_min_classes,849&rec.pw_history_num, &refcnt, &rec.pw_max_fail,850&rec.pw_failcnt_interval, &rec.pw_lockout_duration);851if (nread == EOF)852return -1;853if (nread != 10) {854fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);855return 1;856}857858ret = krb5_db_create_policy(context, &rec);859if (ret)860ret = krb5_db_put_policy(context, &rec);861if (ret) {862com_err(progname, ret, _("while creating policy"));863return 1;864}865if (verbose)866fprintf(stderr, "created policy %s\n", rec.name);867868return 0;869}870871static int872process_r1_11_policy(krb5_context context, const char *fname, FILE *filep,873krb5_boolean verbose, int *linenop)874{875osa_policy_ent_rec rec;876krb5_tl_data *tl, *tl_next;877char namebuf[1024];878char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1];879unsigned int refcnt;880int nread, c, ret = 0;881882memset(&rec, 0, sizeof(rec));883884(*linenop)++;885rec.name = namebuf;886887/*888* Due to a historical error, iprop dumps use the same version before and889* after the 1.11 policy extensions. So we need to accept both 1.8-format890* and 1.11-format policy entries. Begin by reading the 1.8 fields.891*/892nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",893rec.name, &rec.pw_min_life, &rec.pw_max_life,894&rec.pw_min_length, &rec.pw_min_classes,895&rec.pw_history_num, &refcnt, &rec.pw_max_fail,896&rec.pw_failcnt_interval, &rec.pw_lockout_duration);897if (nread == EOF)898return -1;899if (nread != 10) {900fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);901return 1;902}903904/* The next character should be a newline (1.8) or a tab (1.11). */905c = getc(filep);906if (c == EOF)907return -1;908if (c != '\n') {909/* Read the additional 1.11-format fields. */910rec.allowed_keysalts = keysaltbuf;911nread = fscanf(filep, "%u\t%u\t%u\t"912K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN)913"\t%hd", &rec.attributes, &rec.max_life,914&rec.max_renewable_life, rec.allowed_keysalts,915&rec.n_tl_data);916if (nread == EOF)917return -1;918if (nread != 5) {919fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);920return 1;921}922923if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-"))924rec.allowed_keysalts = NULL;925926/* Get TL data */927ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data);928if (ret)929goto cleanup;930931ret = process_tl_data(fname, filep, *linenop, rec.tl_data);932if (ret)933goto cleanup;934}935936ret = krb5_db_create_policy(context, &rec);937if (ret)938ret = krb5_db_put_policy(context, &rec);939if (ret) {940com_err(progname, ret, _("while creating policy"));941goto cleanup;942}943if (verbose)944fprintf(stderr, "created policy %s\n", rec.name);945946cleanup:947for (tl = rec.tl_data; tl; tl = tl_next) {948tl_next = tl->tl_data_next;949free(tl->tl_data_contents);950free(tl);951}952return ret ? 1 : 0;953}954955/* Read a record which is tagged with "princ" or "policy", calling princfn956* or policyfn as appropriate. */957static int958process_tagged(krb5_context context, const char *fname, FILE *filep,959krb5_boolean verbose, int *linenop, load_func princfn,960load_func policyfn)961{962int nread;963char rectype[100];964965nread = fscanf(filep, "%99s\t", rectype);966if (nread == EOF)967return -1;968if (nread != 1)969return 1;970if (strcmp(rectype, "princ") == 0)971return (*princfn)(context, fname, filep, verbose, linenop);972if (strcmp(rectype, "policy") == 0)973return (*policyfn)(context, fname, filep, verbose, linenop);974if (strcmp(rectype, "End") == 0) /* Only expected for OV format */975return -1;976977fprintf(stderr, _("unknown record type \"%s\"\n"), rectype);978return 1;979}980981static int982process_k5beta7_record(krb5_context context, const char *fname, FILE *filep,983krb5_boolean verbose, int *linenop)984{985return process_tagged(context, fname, filep, verbose, linenop,986process_k5beta7_princ, process_k5beta7_policy);987}988989static int990process_r1_8_record(krb5_context context, const char *fname, FILE *filep,991krb5_boolean verbose, int *linenop)992{993return process_tagged(context, fname, filep, verbose, linenop,994process_k5beta7_princ, process_r1_8_policy);995}996997static int998process_r1_11_record(krb5_context context, const char *fname, FILE *filep,999krb5_boolean verbose, int *linenop)1000{1001return process_tagged(context, fname, filep, verbose, linenop,1002process_k5beta7_princ, process_r1_11_policy);1003}10041005dump_version beta7_version = {1006"Kerberos version 5",1007"kdb5_util load_dump version 4\n",10080,10090,10100,1011dump_k5beta7_princ,1012dump_k5beta7_policy,1013process_k5beta7_record,1014};1015dump_version r1_3_version = {1016"Kerberos version 5 release 1.3",1017"kdb5_util load_dump version 5\n",10180,10190,10200,1021dump_k5beta7_princ_withpolicy,1022dump_k5beta7_policy,1023process_k5beta7_record,1024};1025dump_version r1_8_version = {1026"Kerberos version 5 release 1.8",1027"kdb5_util load_dump version 6\n",10280,10290,10300,1031dump_k5beta7_princ_withpolicy,1032dump_r1_8_policy,1033process_r1_8_record,1034};1035dump_version r1_11_version = {1036"Kerberos version 5 release 1.11",1037"kdb5_util load_dump version 7\n",10380,10390,10400,1041dump_k5beta7_princ_withpolicy,1042dump_r1_11_policy,1043process_r1_11_record,1044};1045dump_version iprop_version = {1046"Kerberos iprop version",1047"iprop",10480,10491,10500,1051dump_k5beta7_princ_withpolicy,1052dump_k5beta7_policy,1053process_k5beta7_record,1054};1055dump_version ipropx_1_version = {1056"Kerberos iprop extensible version",1057"ipropx",10580,10591,10601,1061dump_k5beta7_princ_withpolicy,1062dump_r1_11_policy,1063process_r1_11_record,1064};10651066/* Read the dump header. Return 1 on success, 0 if the file is not a1067* recognized iprop dump format. */1068static int1069parse_iprop_header(char *buf, dump_version **dv, kdb_last_t *last)1070{1071char head[128];1072int nread;1073uint32_t u[4];1074uint32_t *up = &u[0];10751076nread = sscanf(buf, "%127s %u %u %u %u", head, &u[0], &u[1], &u[2], &u[3]);1077if (nread < 1)1078return 0;10791080if (!strcmp(head, ipropx_1_version.header)) {1081if (nread != 5)1082return 0;1083if (u[0] == IPROPX_VERSION_0) {1084*dv = &iprop_version;1085} else if (u[0] == IPROPX_VERSION_1) {1086*dv = &ipropx_1_version;1087} else {1088fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), progname,1089u[0]);1090return 0;1091}1092up = &u[1];1093} else if (!strcmp(head, iprop_version.header)) {1094if (nread != 4)1095return 0;1096*dv = &iprop_version;1097} else {1098fprintf(stderr, "Invalid iprop header\n");1099return 0;1100}11011102last->last_sno = *up++;1103last->last_time.seconds = *up++;1104last->last_time.useconds = *up++;1105return 1;1106}11071108/* Return true if the serial number and timestamp in an existing dump file is1109* in the ulog. */1110static krb5_boolean1111current_dump_sno_in_ulog(krb5_context context, const char *ifile)1112{1113update_status_t status;1114dump_version *junk;1115kdb_last_t last;1116char buf[BUFSIZ], *r;1117FILE *f;11181119f = fopen(ifile, "r");1120if (f == NULL)1121return 0; /* aliasing other errors to ENOENT here is OK */11221123r = fgets(buf, sizeof(buf), f);1124fclose(f);1125if (r == NULL)1126return errno ? -1 : 0;11271128if (!parse_iprop_header(buf, &junk, &last))1129return 0;11301131status = ulog_get_sno_status(context, &last);1132return status == UPDATE_OK || status == UPDATE_NIL;1133}11341135void1136dump_db(int argc, char **argv)1137{1138FILE *f;1139struct dump_args args;1140char *ofile = NULL, *tmpofile = NULL, *new_mkey_file = NULL;1141krb5_error_code ret, retval;1142dump_version *dump;1143int aindex, ok_fd = -1;1144bool_t dump_sno = FALSE;1145kdb_log_context *log_ctx;1146unsigned int ipropx_version = IPROPX_VERSION_0;1147krb5_kvno kt_kvno;1148krb5_boolean conditional = FALSE;1149kdb_last_t last;1150krb5_flags iterflags = 0;11511152/* Parse the arguments. */1153dump = &r1_11_version;1154args.verbose = FALSE;1155args.omit_nra = FALSE;1156mkey_convert = FALSE;1157log_ctx = util_context->kdblog_context;11581159/*1160* Parse the qualifiers.1161*/1162for (aindex = 1; aindex < argc; aindex++) {1163if (!strcmp(argv[aindex], "-b7")) {1164dump = &beta7_version;1165} else if (!strcmp(argv[aindex], "-ov")) {1166fprintf(stderr, _("OV dump format not supported\n"));1167goto error;1168} else if (!strcmp(argv[aindex], "-r13")) {1169dump = &r1_3_version;1170} else if (!strcmp(argv[aindex], "-r18")) {1171dump = &r1_8_version;1172} else if (!strncmp(argv[aindex], "-i", 2)) {1173/* Intentionally undocumented - only used by kadmin. */1174if (log_ctx && log_ctx->iproprole) {1175/* ipropx_version is the maximum version acceptable. */1176ipropx_version = atoi(argv[aindex] + 2);1177dump = ipropx_version ? &ipropx_1_version : &iprop_version;1178/*1179* dump_sno is used to indicate if the serial number should be1180* populated in the output file to be used later by iprop for1181* updating the replica's update log when loading.1182*/1183dump_sno = TRUE;1184/* FLAG_OMIT_NRA is set to indicate that non-replicated1185* attributes should be omitted. */1186args.omit_nra = TRUE;1187} else {1188fprintf(stderr, _("Iprop not enabled\n"));1189goto error;1190}1191} else if (!strcmp(argv[aindex], "-c")) {1192conditional = 1;1193} else if (!strcmp(argv[aindex], "-verbose")) {1194args.verbose = TRUE;1195} else if (!strcmp(argv[aindex], "-mkey_convert")) {1196mkey_convert = 1;1197} else if (!strcmp(argv[aindex], "-new_mkey_file")) {1198new_mkey_file = argv[++aindex];1199mkey_convert = 1;1200} else if (!strcmp(argv[aindex], "-rev")) {1201iterflags |= KRB5_DB_ITER_REV;1202} else if (!strcmp(argv[aindex], "-recurse")) {1203iterflags |= KRB5_DB_ITER_RECURSE;1204} else {1205break;1206}1207}12081209args.names = NULL;1210args.nnames = 0;1211if (aindex < argc) {1212ofile = argv[aindex];1213aindex++;1214if (aindex < argc) {1215args.names = &argv[aindex];1216args.nnames = argc - aindex;1217}1218}12191220/* If a conditional ipropx dump we check if the existing dump is1221* good enough. */1222if (ofile != NULL && conditional) {1223if (!dump->iprop) {1224com_err(progname, 0,1225_("Conditional dump is an undocumented option for "1226"use only for iprop dumps"));1227goto error;1228}1229if (current_dump_sno_in_ulog(util_context, ofile))1230return;1231}12321233/*1234* Make sure the database is open. The policy database only has1235* to be opened if we try a dump that uses it.1236*/1237if (!dbactive) {1238com_err(progname, 0, _("Database not currently opened!"));1239goto error;1240}12411242/*1243* If we're doing a master key conversion, set up for it.1244*/1245if (mkey_convert) {1246if (!valid_master_key) {1247/* TRUE here means read the keyboard, but only once */1248retval = krb5_db_fetch_mkey(util_context, master_princ,1249master_keyblock.enctype, TRUE, FALSE,1250NULL, NULL, NULL, &master_keyblock);1251if (retval) {1252com_err(progname, retval, _("while reading master key"));1253exit(1);1254}1255retval = krb5_db_fetch_mkey_list(util_context, master_princ,1256&master_keyblock);1257if (retval) {1258com_err(progname, retval, _("while verifying master key"));1259exit(1);1260}1261}1262new_master_keyblock.enctype = global_params.enctype;1263if (new_master_keyblock.enctype == ENCTYPE_UNKNOWN)1264new_master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;12651266if (new_mkey_file) {1267if (global_params.mask & KADM5_CONFIG_KVNO)1268kt_kvno = global_params.kvno;1269else1270kt_kvno = IGNORE_VNO;12711272retval = krb5_db_fetch_mkey(util_context, master_princ,1273new_master_keyblock.enctype, FALSE,1274FALSE, new_mkey_file, &kt_kvno, NULL,1275&new_master_keyblock);1276if (retval) {1277com_err(progname, retval, _("while reading new master key"));1278exit(1);1279}1280} else {1281printf(_("Please enter new master key....\n"));1282retval = krb5_db_fetch_mkey(util_context, master_princ,1283new_master_keyblock.enctype, TRUE,1284TRUE, NULL, NULL, NULL,1285&new_master_keyblock);1286if (retval) {1287com_err(progname, retval, _("while reading new master key"));1288exit(1);1289}1290}1291/* Get new master key vno that will be used to protect princs. */1292new_mkvno = get_next_kvno(util_context, master_entry);1293}12941295ret = 0;12961297if (ofile != NULL && strcmp(ofile, "-")) {1298/* Discourage accidental dumping to filenames beginning with '-'. */1299if (ofile[0] == '-')1300usage();1301if (!prep_ok_file(util_context, ofile, &ok_fd))1302return; /* prep_ok_file() bumps exit_status */1303f = create_ofile(ofile, &tmpofile);1304if (f == NULL) {1305com_err(progname, errno, _("while opening %s for writing"), ofile);1306goto error;1307}1308} else {1309f = stdout;1310}13111312args.ofile = f;1313args.context = util_context;1314args.dump = dump;1315fprintf(args.ofile, "%s", dump->header);13161317if (dump_sno) {1318ret = ulog_get_last(util_context, &last);1319if (ret) {1320com_err(progname, ret, _("while reading update log header"));1321goto error;1322}1323if (ipropx_version)1324fprintf(f, " %u", IPROPX_VERSION);1325fprintf(f, " %u", last.last_sno);1326fprintf(f, " %u", last.last_time.seconds);1327fprintf(f, " %u", last.last_time.useconds);1328}13291330if (dump->header[strlen(dump->header)-1] != '\n')1331fputc('\n', args.ofile);13321333ret = krb5_db_iterate(util_context, NULL, dump_iterator, &args, iterflags);1334if (ret) {1335com_err(progname, ret, _("performing %s dump"), dump->name);1336goto error;1337}13381339/* Don't dump policies if specific principal entries were requested. */1340if (dump->dump_policy != NULL && args.nnames == 0) {1341ret = krb5_db_iter_policy(util_context, "*", dump->dump_policy, &args);1342if (ret) {1343com_err(progname, ret, _("performing %s dump"), dump->name);1344goto error;1345}1346}13471348if (f != stdout) {1349fclose(f);1350finish_ofile(ofile, &tmpofile);1351update_ok_file(util_context, ok_fd);1352}1353return;13541355error:1356if (tmpofile != NULL)1357unlink(tmpofile);1358free(tmpofile);1359exit_status++;1360}13611362/* Restore the database from any version dump file. */1363static int1364restore_dump(krb5_context context, char *dumpfile, FILE *f,1365krb5_boolean verbose, dump_version *dump)1366{1367int err = 0;1368int lineno = 1;13691370/* Process the records. */1371while (!(err = dump->load_record(context, dumpfile, f, verbose, &lineno)));1372if (err != -1) {1373fprintf(stderr, _("%s: error processing line %d of %s\n"), progname,1374lineno, dumpfile);1375return err;1376}1377return 0;1378}13791380void1381load_db(int argc, char **argv)1382{1383krb5_error_code ret;1384FILE *f = NULL;1385char *dumpfile = NULL, *dbname, buf[BUFSIZ];1386dump_version *load = NULL;1387int aindex;1388kdb_log_context *log_ctx;1389kdb_last_t last;1390krb5_boolean db_locked = FALSE, temp_db_created = FALSE;1391krb5_boolean verbose = FALSE, update = FALSE, iprop_load = FALSE;13921393/* Parse the arguments. */1394dbname = global_params.dbname;1395exit_status = 0;1396log_ctx = util_context->kdblog_context;13971398for (aindex = 1; aindex < argc; aindex++) {1399if (!strcmp(argv[aindex], "-b7")){1400load = &beta7_version;1401} else if (!strcmp(argv[aindex], "-ov")) {1402fprintf(stderr, _("OV dump format not supported\n"));1403goto error;1404} else if (!strcmp(argv[aindex], "-r13")) {1405load = &r1_3_version;1406} else if (!strcmp(argv[aindex], "-r18")){1407load = &r1_8_version;1408} else if (!strcmp(argv[aindex], "-i")) {1409/* Intentionally undocumented - only used by kadmin. */1410if (log_ctx && log_ctx->iproprole) {1411load = &iprop_version;1412iprop_load = TRUE;1413} else {1414fprintf(stderr, _("Iprop not enabled\n"));1415goto error;1416}1417} else if (!strcmp(argv[aindex], "-verbose")) {1418verbose = TRUE;1419} else if (!strcmp(argv[aindex], "-update")){1420update = TRUE;1421} else if (!strcmp(argv[aindex], "-hash")) {1422if (!add_db_arg("hash=true")) {1423com_err(progname, ENOMEM, _("while parsing options"));1424goto error;1425}1426} else {1427break;1428}1429}1430if (argc - aindex != 1)1431usage();1432dumpfile = argv[aindex];14331434/* Open the dumpfile. */1435if (dumpfile != NULL) {1436f = fopen(dumpfile, "r");1437if (f == NULL) {1438com_err(progname, errno, _("while opening %s"), dumpfile);1439goto error;1440}1441} else {1442f = stdin;1443dumpfile = _("standard input");1444}14451446/* Auto-detect dump version if we weren't told, or verify if we were. */1447if (fgets(buf, sizeof(buf), f) == NULL) {1448fprintf(stderr, _("%s: can't read dump header in %s\n"), progname,1449dumpfile);1450goto error;1451}1452if (load) {1453/* Only check what we know; some headers only contain a prefix.1454* NB: this should work for ipropx even though load is iprop */1455if (strncmp(buf, load->header, strlen(load->header)) != 0) {1456fprintf(stderr, _("%s: dump header bad in %s\n"), progname,1457dumpfile);1458goto error;1459}1460} else {1461if (strcmp(buf, beta7_version.header) == 0) {1462load = &beta7_version;1463} else if (strcmp(buf, r1_3_version.header) == 0) {1464load = &r1_3_version;1465} else if (strcmp(buf, r1_8_version.header) == 0) {1466load = &r1_8_version;1467} else if (strcmp(buf, r1_11_version.header) == 0) {1468load = &r1_11_version;1469} else {1470fprintf(stderr, _("%s: dump header bad in %s\n"), progname,1471dumpfile);1472goto error;1473}1474}14751476if (global_params.iprop_enabled &&1477ulog_map(util_context, global_params.iprop_logfile,1478global_params.iprop_ulogsize)) {1479fprintf(stderr, _("Could not open iprop ulog\n"));1480goto error;1481}14821483if (load->updateonly && !update) {1484fprintf(stderr, _("%s: dump version %s can only be loaded with the "1485"-update flag\n"), progname, load->name);1486goto error;1487}14881489/* If we are not in update mode, we create an alternate database and then1490* promote it to be the live db. */1491if (!update) {1492if (!add_db_arg("temporary")) {1493com_err(progname, ENOMEM, _("computing parameters for database"));1494goto error;1495}14961497if (iprop_load && !add_db_arg("merge_nra")) {1498com_err(progname, ENOMEM, _("computing parameters for database"));1499goto error;1500}15011502ret = krb5_db_create(util_context, db5util_db_args);1503if (ret) {1504com_err(progname, ret, _("while creating database"));1505goto error;1506}1507temp_db_created = TRUE;1508} else {1509/* Initialize the database. */1510ret = krb5_db_open(util_context, db5util_db_args,1511KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);1512if (ret) {1513com_err(progname, ret, _("while opening database"));1514goto error;1515}15161517/* Make sure the db is left unusable if the update fails, if the db1518* supports locking. */1519ret = krb5_db_lock(util_context, KRB5_DB_LOCKMODE_PERMANENT);1520if (ret == 0) {1521db_locked = TRUE;1522} else if (ret != KRB5_PLUGIN_OP_NOTSUPP) {1523com_err(progname, ret, _("while permanently locking database"));1524goto error;1525}1526}15271528if (log_ctx != NULL && log_ctx->iproprole && !update) {1529/* Don't record updates we are making to the temporary DB. We will1530* reinitialize or update the ulog header after promoting it. */1531log_ctx->iproprole = IPROP_REPLICA;1532if (iprop_load) {1533/* Parse the iprop header information. */1534if (!parse_iprop_header(buf, &load, &last))1535goto error;1536}1537}15381539if (restore_dump(util_context, dumpfile ? dumpfile : _("standard input"),1540f, verbose, load)) {1541fprintf(stderr, _("%s: %s restore failed\n"), progname, load->name);1542goto error;1543}15441545if (db_locked && (ret = krb5_db_unlock(util_context))) {1546com_err(progname, ret, _("while unlocking database"));1547goto error;1548}15491550if (!update) {1551/* Initialize the ulog header before promoting so we can't leave behind1552* the pre-load ulog state if we are killed just after promoting. */1553if (log_ctx != NULL && log_ctx->iproprole) {1554ret = ulog_init_header(util_context);1555if (ret) {1556com_err(progname, ret, _("while reinitializing update log"));1557goto error;1558}1559}15601561ret = krb5_db_promote(util_context, db5util_db_args);1562/* Ignore a not supported error since there is nothing to do about it1563* anyway. */1564if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {1565com_err(progname, ret,1566_("while making newly loaded database live"));1567goto error;1568}15691570if (log_ctx != NULL && log_ctx->iproprole) {1571/* Reinitialize the ulog header since we replaced the DB, and1572* record the iprop state if we received it. */1573ret = ulog_init_header(util_context);1574if (ret) {1575com_err(progname, ret, _("while reinitializing update log"));1576goto error;1577}1578if (iprop_load) {1579ret = ulog_set_last(util_context, &last);1580if (ret) {1581com_err(progname, ret,1582_("while writing update log header"));1583goto error;1584}1585}1586}1587}15881589cleanup:1590/* If we created a temporary DB but didn't succeed, destroy it. */1591if (exit_status && temp_db_created) {1592ret = krb5_db_destroy(util_context, db5util_db_args);1593/* Ignore a not supported error since there is nothing to do about1594* it anyway. */1595if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {1596com_err(progname, ret, _("while deleting bad database %s"),1597dbname);1598}1599}16001601if (f != NULL && f != stdin)1602fclose(f);16031604return;16051606error:1607exit_status++;1608goto cleanup;1609}161016111612