diff options
Diffstat (limited to 'src/kadmin/cli/dump.c')
| -rw-r--r-- | src/kadmin/cli/dump.c | 1485 |
1 files changed, 1485 insertions, 0 deletions
diff --git a/src/kadmin/cli/dump.c b/src/kadmin/cli/dump.c new file mode 100644 index 000000000..2c5e4e753 --- /dev/null +++ b/src/kadmin/cli/dump.c @@ -0,0 +1,1485 @@ +/* + * admin/edit/dump.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * Dump a KDC database. This file was originally written to be part + * of kdb5_edit but has now been adapted for kadmin. + */ + +#include <stdio.h> +#include <k5-int.h> +#include <kadm5/admin.h> + +struct dump_args { + char *programname; + FILE *ofile; + krb5_context context; + int verbose; +}; + +/* External data */ +extern int exit_status; +extern krb5_context context; +extern void *handle; + +/* Strings */ + +static const char k5beta5_dump_header[] = "kdb5_edit load_dump version 2.0\n"; +static const char k5_dump_header[] = "kdb5_edit load_dump version 3.0\n"; +static const char kadm5_dump_header[] = "kadm5 load_dump version 4.0\n"; + +static const char null_mprinc_name[] = "kdb5_dump@MISSING"; + +/* Message strings */ +static const char regex_err[] = "%s: regular expression error - %s\n"; +static const char regex_merr[] = "%s: regular expression match error - %s\n"; +static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n"; +static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n"; +static const char nokeys_err[] = "%s: cannot find any standard key for %s\n"; +static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n"; +static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n"; +static const char oflock_error[] = "%s: cannot lock %s (%s)\n"; +static const char dumprec_err[] = "%s: error performing %s dump (%s)\n"; +static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n"; +static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: "; +static const char read_name_string[] = "name string"; +static const char read_key_type[] = "key type"; +static const char read_key_data[] = "key data"; +static const char read_pr_data1[] = "first set of principal attributes"; +static const char read_mod_name[] = "modifier name"; +static const char read_pr_data2[] = "second set of principal attributes"; +static const char read_salt_data[] = "salt data"; +static const char read_akey_type[] = "alternate key type"; +static const char read_akey_data[] = "alternate key data"; +static const char read_asalt_type[] = "alternate salt type"; +static const char read_asalt_data[] = "alternate salt data"; +static const char read_exp_data[] = "expansion data"; +static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n"; +static const char add_princ_fmt[] = "%s\n"; +static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n"; +static const char read_err_fmt[] = "%s(%d): cannot read %s\n"; +static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n"; +static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n"; +static const char err_line_fmt[] = "%s: error processing line %d of %s\n"; +static const char head_bad_fmt[] = "%s: dump header bad in %s\n"; +static const char read_bytecnt[] = "record byte count"; +static const char read_encdata[] = "encoded data"; +static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n"; +static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n"; +static const char read_nint_data[] = "principal static attributes"; +static const char read_tcontents[] = "tagged data contents"; +static const char read_ttypelen[] = "tagged data type and length"; +static const char read_kcontents[] = "key data contents"; +static const char read_ktypelen[] = "key data type and length"; +static const char read_econtents[] = "extra data contents"; +static const char k5beta5_fmt_name[] = "Kerberos version 5 old format"; +static const char k5beta6_fmt_name[] = "Kerberos version 5 beta 6 format"; +static const char lusage_err_fmt[] = "%s: usage is %s [%s] [%s] [%s] filename dbname\n"; +static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n"; +static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n"; +static const char stdin_name[] = "standard input"; +static const char restfail_fmt[] = "%s: %s restore failed\n"; +static const char close_err_fmt[] = "%s: cannot close database (%s)\n"; +static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n"; +static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n"; +static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n"; +static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n"; +static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n"; +static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n"; + +static const char oldoption[] = "-old"; +static const char verboseoption[] = "-verbose"; +static const char updateoption[] = "-update"; +static const char dump_tmptrail[] = "~"; + +/* Can't use krb5_dbe_find_enctype because we have a */ +/* kadm5_principal_ent_t and not a krb5_db_entry */ +static krb5_error_code +find_enctype(dbentp, enctype, salttype, kentp) + kadm5_principal_ent_rec *dbentp; + krb5_enctype enctype; + krb5_int32 salttype; + krb5_key_data **kentp; +{ + int i; + int maxkvno; + krb5_key_data *datap; + + maxkvno = -1; + datap = (krb5_key_data *) NULL; + for (i=0; i<dbentp->n_key_data; i++) { + if ((dbentp->key_data[i].key_data_type[0] == enctype) && + ((dbentp->key_data[i].key_data_type[1] == salttype) || + (salttype < 0))) { + maxkvno = dbentp->key_data[i].key_data_kvno; + datap = &dbentp->key_data[i]; + } + } + if (maxkvno >= 0) { + *kentp = datap; + return(0); + } + return(ENOENT); +} + + +/* + * dump_k5beta5_header() - Make a dump header that is recognizable by Kerberos + * Version 5 Beta 5 and previous releases. + */ +static krb5_error_code +dump_k5beta5_header(arglist) + struct dump_args *arglist; +{ + /* The old header consists of the leading string */ + fprintf(arglist->ofile, k5beta5_dump_header); + return(0); +} + + +/* + * dump_k5beta5_iterator() - Dump an entry in a format that is usable + * by Kerberos Version 5 Beta 5 and previous + * releases. + */ +static krb5_error_code +dump_k5beta5_iterator(ptr, name, entry) + krb5_pointer ptr; + char *name; + kadm5_principal_ent_rec *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *mod_name; + krb5_tl_data *pwchg; + krb5_key_data *pkey, *akey, nullkey; + int i; + + /* Initialize */ + arg = (struct dump_args *) ptr; + mod_name = (char *) NULL; + memset(&nullkey, 0, sizeof(nullkey)); + + /* + * Deserialize the modifier record. + */ + mod_name = (char *) NULL; + pkey = akey = (krb5_key_data *) NULL; + + /* + * Flatten the modifier name. + */ + if ((retval = krb5_unparse_name(arg->context, + entry->mod_name, + &mod_name))) + fprintf(stderr, mname_unp_err, arg->programname, + error_message(retval)); + + /* + * Find the 'primary' key and the 'alternate' key. + */ + if ((retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_NORMAL, + &pkey)) && + (retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + &akey))) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + return(retval); + } + + /* If we only have one type, then ship it out as the primary. */ + if (!pkey && akey) { + pkey = akey; + akey = &nullkey; + } + else { + if (!akey) + akey = &nullkey; + } + + /* + * First put out strings representing the length of the variable + * length data in this record, then the name and the primary key type. + */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name), + strlen(mod_name), + (krb5_int32) pkey->key_data_length[0], + (krb5_int32) akey->key_data_length[0], + (krb5_int32) pkey->key_data_length[1], + (krb5_int32) akey->key_data_length[1], + name, + (krb5_int32) pkey->key_data_type[0]); + for (i=0; i<pkey->key_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]); + } + /* + * Second, print out strings representing the standard integer + * data in this record. + */ + fprintf(arg->ofile, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t", + (krb5_int32) pkey->key_data_kvno, + entry->max_life, entry->max_renewable_life, + 1 /* Fake mkvno */, entry->princ_expire_time, entry->pw_expiration, + entry->last_pwd_change, entry->last_success, entry->last_failed, + entry->fail_auth_count, mod_name, entry->mod_date, + entry->attributes, pkey->key_data_type[1]); + + /* Pound out the salt data, if present. */ + for (i=0; i<pkey->key_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]); + } + /* Pound out the alternate key type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]); + for (i=0; i<akey->key_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]); + } + /* Pound out the alternate salt type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]); + for (i=0; i<akey->key_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]); + } + /* Pound out the expansion data. (is null) */ + for (i=0; i < 8; i++) { + fprintf(arg->ofile, "\t%u", 0); + } + fprintf(arg->ofile, ";\n"); + /* If we're blabbing, do it */ + if (arg->verbose) + fprintf(stderr, "%s\n", name); + krb5_xfree(mod_name); + + return(0); +} + + +/* + * dump_k5beta6_header() - Output the k5beta6 dump header. + */ +static krb5_error_code +dump_k5beta6_header(arglist) + struct dump_args *arglist; +{ + /* The k5beta6 header consists of the leading string */ + fprintf(arglist->ofile, k5_dump_header); + return(0); +} + + +/* + * dump_k5beta6_iterator() - Output a dump record in k5beta6 format. + */ +static krb5_error_code +dump_k5beta6_iterator(ptr, name, entry) + krb5_pointer ptr; + char *name; + kadm5_principal_ent_rec *entry; +{ + krb5_error_code retval = 0; + struct dump_args *arg; + krb5_tl_data *tlp, *etl; + krb5_key_data *kdata; + int counter, i, j; + + /* Initialize */ + arg = (struct dump_args *) ptr; + + /* + * We'd like to just blast out the contents as they would appear in + * the database so that we can just suck it back in, but it doesn't + * lend itself to easy editing. + */ + + /* + * The dump format is as follows: + * len strlen(name) n_tl_data n_key_data e_length + * name + * attributes max_life max_renewable_life expiration + * pw_expiration last_success last_failed fail_auth_count + * n_tl_data*[type length <contents>] + * n_key_data*[ver kvno ver*(type length <contents>)] + * <e_data> + * Fields which are not encapsulated by angle-brackets are to appear + * verbatim. Bracketed fields absence is indicated by a -1 in its + * place + */ + + /* + * Make sure that the tagged list is reasonably correct, and find + * E_DATA while we're at it. + */ + counter = 0; + etl = NULL; + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA) + etl = tlp; + counter++; + } + + if (counter == entry->n_tl_data) { + /* Pound out header */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", + KRB5_KDB_V1_BASE_LENGTH + (etl ? etl->tl_data_length : 0), + strlen(name), + (int) entry->n_tl_data, + (int) entry->n_key_data, + etl ? etl->tl_data_length : 0, + name); + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + entry->attributes, + entry->max_life, + entry->max_renewable_life, + entry->princ_expire_time, + entry->pw_expiration, + entry->last_success, + entry->last_failed, + entry->fail_auth_count); + /* Pound out tagged data. */ + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + /* skip E_DATA since it is included later */ + if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA) + continue; + + fprintf(arg->ofile, "%d\t%d\t", + (int) tlp->tl_data_type, + (int) tlp->tl_data_length); + if (tlp->tl_data_length) + for (i=0; i<tlp->tl_data_length; i++) + fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + + /* Pound out key data */ + for (counter=0; counter<entry->n_key_data; counter++) { + kdata = &entry->key_data[counter]; + fprintf(arg->ofile, "%d\t%d\t", + (int) kdata->key_data_ver, + (int) kdata->key_data_kvno); + for (i=0; i<kdata->key_data_ver; i++) { + fprintf(arg->ofile, "%d\t%d\t", + kdata->key_data_type[i], + kdata->key_data_length[i]); + if (kdata->key_data_length[i]) + for (j=0; j<kdata->key_data_length[i]; j++) + fprintf(arg->ofile, "%02x", + kdata->key_data_contents[i][j]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + } + + /* Pound out extra data */ + if (etl && etl->tl_data_length) + for (i=0; i<etl->tl_data_length; i++) + fprintf(arg->ofile, "%02x", etl->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + fprintf(stderr, "%s\n", name); + } + else { + fprintf(stderr, sdump_tl_inc_err, + arg->programname, name, counter, (int) entry->n_tl_data); + retval = EINVAL; + } + return(retval); +} + + +/* + * usage is: + * dump_db [-old] [-verbose] [filename|- [principals...]] + */ +void dump_db(argc, argv) + int argc; + char **argv; +{ + FILE *f; + struct dump_args arglist; + int error; + char *programname; + char *ofile; + krb5_error_code kret; + krb5_error_code (*dump_iterator) PROTOTYPE((krb5_pointer, + char *, + kadm5_principal_ent_rec *)); + krb5_error_code (*dump_header) PROTOTYPE((struct dump_args *)); + const char * dump_name; + int aindex, num, i; + krb5_boolean locked; + char **princs; + kadm5_principal_ent_rec princ_ent; + krb5_principal princ; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + ofile = (char *) NULL; + error = 0; + dump_iterator = dump_k5beta6_iterator; + dump_header = dump_k5beta6_header; + dump_name = k5beta6_fmt_name; + arglist.verbose = 0; + + /* + * Parse the qualifiers. + */ + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) { + dump_iterator = dump_k5beta5_iterator; + dump_header = dump_k5beta5_header; + dump_name = k5beta5_fmt_name; + } + else if (!strcmp(argv[aindex], verboseoption)) { + arglist.verbose++; + } + else + break; + } + + if (aindex < argc) { + ofile = argv[aindex]; + aindex++; + } + + /* this works because of the way aindex and argc are used below */ + if (aindex == argc) { + argv[aindex] = "*"; + argc++; + } + + locked = 0; + if (ofile) { + /* + * Make sure that we don't open and truncate on the fopen, + * since that may hose an on-going kprop process. + * + * We could also control this by opening for read and + * write, doing an flock with LOCK_EX, and then + * truncating the file once we have gotten the lock, + * but that would involve more OS dependencies than I + * want to get into. + */ + unlink(ofile); + if (!(f = fopen(ofile, "w"))) { + fprintf(stderr, ofopen_error, + programname, ofile, error_message(errno)); + exit_status++; + goto cleanup; + } + if ((kret = krb5_lock_file(context, + fileno(f), + KRB5_LOCKMODE_EXCLUSIVE))) { + fprintf(stderr, oflock_error, + programname, ofile, error_message(kret)); + exit_status++; + goto cleanup; + } + else + locked = 1; + } else { + f = stdout; + } + + arglist.programname = programname; + arglist.ofile = f; + arglist.context = context; + + if (kret = (*dump_header)(&arglist)) { + fprintf(stderr, dumphdr_err, + programname, dump_name, error_message(kret)); + exit_status++; + goto cleanup; + } + + while (aindex < argc) { + if (kret = kadm5_get_principals(handle, argv[aindex], + &princs, &num)) { + fprintf(stderr, "%s: error retrieving principals " + "matching %s: (%s)\n", programname, + argv[aindex], error_message(kret)); + exit_status++; + goto cleanup; + } + + for (i = 0; i < num; i++) { + if (kret = krb5_parse_name(context, princs[i], + &princ)) { + com_err(programname, kret, + "while parsing principal name"); + exit_status++; + break; + } + if (kret = kadm5_get_principal(handle, princ, + &princ_ent, + KADM5_PRINCIPAL_NORMAL_MASK | + KADM5_KEY_DATA|KADM5_TL_DATA)){ + com_err(programname, kret, + "while retrieving principal entry"); + krb5_free_principal(context, princ); + exit_status++; + break; + } + if (kret = (*dump_iterator)(&arglist, princs[i], &princ_ent)) { + exit_status++; + krb5_free_principal(context, princ); + kadm5_free_principal_ent(handle, &princ_ent); + break; + } + + krb5_free_principal(context, princ); + kadm5_free_principal_ent(handle, &princ_ent); + } + + kadm5_free_name_list(handle, princs, num); + aindex++; + if (kret) + goto cleanup; + } + +cleanup: + if (ofile) + fclose(f); + + if (locked) + (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK); +} + + +/* + * Read a string of bytes while counting the number of lines passed. + */ +static int +read_string(f, buf, len, lp) + FILE *f; + char *buf; + int len; + int *lp; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; i<len; i++) { + c = (char) fgetc(f); + if (c < 0) { + retval = 1; + break; + } + if (c == '\n') + (*lp)++; + buf[i] = (char) c; + } + buf[len] = '\0'; + return(retval); +} + +/* + * Read a string of two character representations of bytes. + */ +static int +read_octet_string(f, buf, len) + FILE *f; + krb5_octet *buf; + int len; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; i<len; i++) { + if (fscanf(f, "%02x", &c) != 1) { + retval = 1; + break; + } + buf[i] = (krb5_octet) c; + } + return(retval); +} + +/* + * Find the end of an old format record. + */ +static void +find_record_end(f, fn, lineno) + FILE *f; + char *fn; + int lineno; +{ + int ch; + + if (((ch = fgetc(f)) != ';') || ((ch = fgetc(f)) != '\n')) { + fprintf(stderr, trash_end_fmt, fn, lineno); + while (ch != '\n') { + putc(ch, stderr); + ch = fgetc(f); + } + putc(ch, stderr); + } +} + +#if 0 +/* + * process_k5beta5_record() - Handle a dump record in old format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta5_record(fname, context, filep, verbose, linenop) + char *fname; + krb5_context context; + FILE *filep; + int verbose; + int *linenop; +{ + int nmatched; + int retval; + krb5_db_entry dbent; + int name_len, mod_name_len, key_len; + int alt_key_len, salt_len, alt_salt_len; + char *name; + char *mod_name; + int tmpint1, tmpint2, tmpint3; + int error; + const char *try2read; + int i; + krb5_key_data *pkey, *akey; + krb5_timestamp last_pwd_change, mod_date; + krb5_principal mod_princ; + krb5_error_code kret; + + try2read = (char *) NULL; + (*linenop)++; + retval = 1; + memset((char *)&dbent, 0, sizeof(dbent)); + + /* Make sure we've got key_data entries */ + if (krb5_dbe_create_key_data(context, &dbent) || + krb5_dbe_create_key_data(context, &dbent)) { + krb5_db_free_principal(context, &dbent, 1); + return(1); + } + pkey = &dbent.key_data[0]; + akey = &dbent.key_data[1]; + + /* + * Match the sizes. 6 tokens to match. + */ + nmatched = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t", + &name_len, &mod_name_len, &key_len, + &alt_key_len, &salt_len, &alt_salt_len); + if (nmatched == 6) { + pkey->key_data_length[0] = key_len; + akey->key_data_length[0] = alt_key_len; + pkey->key_data_length[1] = salt_len; + akey->key_data_length[1] = alt_salt_len; + name = (char *) NULL; + mod_name = (char *) NULL; + /* + * Get the memory for the variable length fields. + */ + if ((name = (char *) malloc((size_t) (name_len + 1))) && + (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) && + (!key_len || + (pkey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (key_len + 1)))) && + (!alt_key_len || + (akey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) && + (!salt_len || + (pkey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (salt_len + 1)))) && + (!alt_salt_len || + (akey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (alt_salt_len + 1)))) + ) { + error = 0; + + /* Read the principal name */ + if (read_string(filep, name, name_len, linenop)) { + try2read = read_name_string; + error++; + } + /* Read the key type */ + if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) { + try2read = read_key_type; + error++; + } + pkey->key_data_type[0] = tmpint1; + /* Read the old format key */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[0], + pkey->key_data_length[0])) { + try2read = read_key_data; + error++; + } + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((pkey->key_data_length[0] > 4) + && (pkey->key_data_contents[0][0] == 0) + && (pkey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = pkey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = pkey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + pkey->key_data_length[0] = shortlen; + pkey->key_data_contents[0] = shortcopy; + } + + /* Read principal attributes */ + if (!error && (fscanf(filep, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t", + &tmpint1, &dbent.max_life, + &dbent.max_renewable_life, + &tmpint2, &dbent.expiration, + &dbent.pw_expiration, &last_pwd_change, + &dbent.last_success, &dbent.last_failed, + &tmpint3) != 10)) { + try2read = read_pr_data1; + error++; + } + pkey->key_data_kvno = tmpint1; + dbent.fail_auth_count = tmpint3; + /* Read modifier name */ + if (!error && read_string(filep, + mod_name, + mod_name_len, + linenop)) { + try2read = read_mod_name; + error++; + } + /* Read second set of attributes */ + if (!error && (fscanf(filep, "\t%u\t%u\t%u\t", + &mod_date, &dbent.attributes, + &tmpint1) != 3)) { + try2read = read_pr_data2; + error++; + } + pkey->key_data_type[1] = tmpint1; + /* Read salt data */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[1], + pkey->key_data_length[1])) { + try2read = read_salt_data; + error++; + } + /* Read alternate key type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_akey_type; + error++; + } + akey->key_data_type[0] = tmpint1; + /* Read alternate key */ + if (!error && read_octet_string(filep, + akey->key_data_contents[0], + akey->key_data_length[0])) { + try2read = read_akey_data; + error++; + } + + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((akey->key_data_length[0] > 4) + && (akey->key_data_contents[0][0] == 0) + && (akey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = akey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = akey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + akey->key_data_length[0] = shortlen; + akey->key_data_contents[0] = shortcopy; + } + + /* Read alternate salt type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_asalt_type; + error++; + } + akey->key_data_type[1] = tmpint1; + /* Read alternate salt data */ + if (!error && read_octet_string(filep, + akey->key_data_contents[1], + akey->key_data_length[1])) { + try2read = read_asalt_data; + error++; + } + /* Read expansion data - discard it */ + if (!error) { + for (i=0; i<8; i++) { + if (fscanf(filep, "\t%u", &tmpint1) != 1) { + try2read = read_exp_data; + error++; + break; + } + } + if (!error) + find_record_end(filep, fname, *linenop); + } + + /* + * If no error, then we're done reading. Now parse the names + * and store the database dbent. + */ + if (!error) { + if (!(kret = krb5_parse_name(context, + name, + &dbent.princ))) { + if (!(kret = krb5_parse_name(context, + mod_name, + &mod_princ))) { + if (!(kret = + krb5_dbe_update_mod_princ_data(context, + &dbent, + mod_date, + mod_princ)) && + !(kret = + krb5_dbe_update_last_pwd_change(context, + &dbent, + last_pwd_change))) { + int one = 1; + + dbent.len = KRB5_KDB_V1_BASE_LENGTH; + pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ? + 2 : 1; + akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ? + 2 : 1; + if ((pkey->key_data_type[0] == + akey->key_data_type[0]) && + (pkey->key_data_type[1] == + akey->key_data_type[1])) + dbent.n_key_data--; + else if ((akey->key_data_type[0] == 0) + && (akey->key_data_length[0] == 0) + && (akey->key_data_type[1] == 0) + && (akey->key_data_length[1] == 0)) + dbent.n_key_data--; + if ((kret = krb5_db_put_principal(context, + &dbent, + &one)) || + (one != 1)) { + fprintf(stderr, store_err_fmt, + fname, *linenop, name, + error_message(kret)); + error++; + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + dbent.n_key_data = 2; + } + krb5_free_principal(context, mod_princ); + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, mod_name, + error_message(kret)); + error++; + } + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + error++; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + + krb5_db_free_principal(context, &dbent, 1); + if (mod_name) + free(mod_name); + if (name) + free(name); + } + else { + if (nmatched != EOF) + fprintf(stderr, rhead_err_fmt, fname, *linenop); + else + retval = -1; + } + return(retval); +} + + +/* + * process_k5_record() - Handle a dump record in new format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5_record(fname, context, filep, verbose, linenop) + char *fname; + krb5_context context; + FILE *filep; + int verbose; + int *linenop; +{ + int retval; + krb5_db_entry dbentry; + krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9; + int nread; + int error; + int i, j, one; + char *name; + krb5_key_data *kp, *kdatap; + krb5_tl_data **tlp, *tl; + krb5_octet *op; + krb5_error_code kret; + const char *try2read; + + try2read = (char *) NULL; + memset((char *) &dbentry, 0, sizeof(dbentry)); + (*linenop)++; + retval = 1; + name = (char *) NULL; + kp = (krb5_key_data *) NULL; + op = (krb5_octet *) NULL; + error = 0; + kret = 0; + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5); + if (nread == 5) { + /* Get memory for flattened principal name */ + if (!(name = (char *) malloc((size_t) t2 + 1))) + error++; + + /* Get memory for and form tagged data linked list */ + tlp = &dbentry.tl_data; + for (i=0; i<t3; i++) { + if ((*tlp = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) { + memset(*tlp, 0, sizeof(krb5_tl_data)); + tlp = &((*tlp)->tl_data_next); + dbentry.n_tl_data++; + } + else { + error++; + break; + } + } + + /* Get memory for key list */ + if (t4 && !(kp = (krb5_key_data *) malloc((size_t) + (t4*sizeof(krb5_key_data))))) + error++; + + /* Get memory for extra data */ + if (t5 && !(op = (krb5_octet *) malloc((size_t) t5))) + error++; + + if (!error) { + dbentry.len = t1; + dbentry.n_key_data = t4; + dbentry.e_length = t5; + if (kp) { + memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data))); + dbentry.key_data = kp; + kp = (krb5_key_data *) NULL; + } + if (op) { + memset(op, 0, (size_t) t5); + dbentry.e_data = op; + op = (krb5_octet *) NULL; + } + + /* Read in and parse the principal name */ + if (!read_string(filep, name, t2, linenop) && + !(kret = krb5_parse_name(context, name, &dbentry.princ))) { + + /* Get the fixed principal attributes */ + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9); + if (nread == 8) { + dbentry.attributes = (krb5_flags) t2; + dbentry.max_life = (krb5_deltat) t3; + dbentry.max_renewable_life = (krb5_deltat) t4; + dbentry.expiration = (krb5_timestamp) t5; + dbentry.pw_expiration = (krb5_timestamp) t6; + dbentry.last_success = (krb5_timestamp) t7; + dbentry.last_failed = (krb5_timestamp) t8; + dbentry.fail_auth_count = (krb5_kvno) t9; + } else { + try2read = read_nint_data; + error++; + } + + /* Get the tagged data */ + if (!error && dbentry.n_tl_data) { + for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + tl->tl_data_type = (krb5_int16) t1; + tl->tl_data_length = (krb5_int16) t2; + if (tl->tl_data_length) { + if (!(tl->tl_data_contents = + (krb5_octet *) malloc((size_t) t2+1)) || + read_octet_string(filep, + tl->tl_data_contents, + t2)) { + try2read = read_tcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_tcontents; + break; + } + } + } + else { + try2read = read_ttypelen; + error++; + break; + } + } + } + + /* Get the key data */ + if (!error && dbentry.n_key_data) { + for (i=0; !error && (i<dbentry.n_key_data); i++) { + kdatap = &dbentry.key_data[i]; + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + kdatap->key_data_ver = (krb5_int16) t1; + kdatap->key_data_kvno = (krb5_int16) t2; + + for (j=0; j<t1; j++) { + nread = fscanf(filep, "%d\t%d\t", &t3, &t4); + if (nread == 2) { + kdatap->key_data_type[j] = t3; + kdatap->key_data_length[j] = t4; + if (t4) { + if (!(kdatap->key_data_contents[j] = + (krb5_octet *) + malloc((size_t) t4+1)) || + read_octet_string(filep, + kdatap->key_data_contents[j], + t4)) { + try2read = read_kcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_kcontents; + break; + } + } + } + else { + try2read = read_ktypelen; + error++; + break; + } + } + } + } + } + + /* Get the extra data */ + if (!error && dbentry.e_length) { + if (read_octet_string(filep, + dbentry.e_data, + (int) dbentry.e_length)) { + try2read = read_econtents; + error++; + } + } + else { + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_econtents; + } + } + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + one = 1; + if ((kret = krb5_db_put_principal(context, + &dbentry, + &one))) { + fprintf(stderr, store_err_fmt, + fname, *linenop, + name, error_message(kret)); + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + if (kret) + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + else + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + } + else { + fprintf(stderr, rhead_err_fmt, fname, *linenop); + } + + if (op) + free(op); + if (kp) + free(kp); + if (name) + free(name); + krb5_db_free_principal(context, &dbentry, 1); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + + +/* + * restore_k5beta5_compat() - Restore the database from a K5 Beta + * format dump file. + */ +static int +restore_k5beta5_compat(programname, context, dumpfile, f, verbose) + const char *programname; + krb5_context context; + const char *dumpfile; + FILE *f; + int verbose; +{ + int error; + int lineno; + char buf[2*sizeof(k5beta5_dump_header)]; + + /* + * Get/check the header. + */ + error = 0; + fgets(buf, sizeof(buf), f); + if (!strcmp(buf, k5beta5_dump_header)) { + lineno = 1; + /* + * Process the records. + */ + while (!(error = process_k5beta5_record(dumpfile, + context, + f, + verbose, + &lineno))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + /* + * Close the input file. + */ + if (f != stdin) + fclose(f); + } + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + error++; + } + return(error); +} + + +/* + * restore_dump() - Restore the database from a standard dump file. + */ +static int +restore_dump(programname, context, dumpfile, f, verbose) + const char *programname; + krb5_context context; + const char *dumpfile; + FILE *f; + int verbose; +{ + int error; + int lineno; + char buf[2*sizeof(k5_dump_header)]; + + /* + * Get/check the header. + */ + error = 0; + fgets(buf, sizeof(buf), f); + if (!strcmp(buf, k5_dump_header)) { + lineno = 1; + /* + * Process the records. + */ + while (!(error = process_k5_record(dumpfile, + context, + f, + verbose, + &lineno))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + /* + * Close the input file. + */ + if (f != stdin) + fclose(f); + } + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + error++; + } + return(error); +} + +/* + * Usage is + * load_db [-old] [-verbose] [-update] filename dbname + */ +void +load_db(argc, argv) + int argc; + char **argv; +{ + krb5_error_code kret; + krb5_context context; + FILE *f; + extern char *optarg; + extern int optind; + const char *programname; + const char *dumpfile; + char *dbname; + char *dbname_tmp; + int (*restore_function) PROTOTYPE((const char *, + krb5_context, + const char *, + FILE *, + int)); + const char * restore_name; + int update, verbose; + int aindex; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + dumpfile = (char *) NULL; + dbname = (char *) NULL; + restore_function = restore_dump; + restore_name = standard_fmt_name; + update = 0; + verbose = 0; + exit_status = 0; + dbname_tmp = (char *) NULL; + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) { + restore_function = restore_k5beta5_compat; + restore_name = k5beta5_fmt_name; + } + else if (!strcmp(argv[aindex], verboseoption)) { + verbose = 1; + } + else if (!strcmp(argv[aindex], updateoption)) { + update = 1; + } + else + break; + } + if ((argc - aindex) != 2) { + fprintf(stderr, lusage_err_fmt, argv[0], argv[0], + oldoption, verboseoption, updateoption); + exit_status++; + return; + } + + dumpfile = argv[aindex]; + dbname = argv[aindex+1]; + if (!(dbname_tmp = (char *) malloc(strlen(dbname)+ + strlen(dump_tmptrail)+1))) { + fprintf(stderr, no_name_mem_fmt, argv[0]); + exit_status++; + return; + } + strcpy(dbname_tmp, dbname); + strcat(dbname_tmp, dump_tmptrail); + + /* + * Initialize the Kerberos context and error tables. + */ + if ((kret = krb5_init_context(&context))) { + fprintf(stderr, ctx_err_fmt, programname); + free(dbname_tmp); + exit_status++; + return; + } + krb5_init_ets(context); + + /* + * Open the dumpfile + */ + if (dumpfile) { + if ((f = fopen(dumpfile, "r+"))) { + kret = krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_SHARED); + } + } + else { + f = stdin; + } + if (f && !kret) { + /* + * Create the new database if not an update restoration. + */ + if (update || !(kret = krb5_db_create(context, dbname_tmp))) { + /* + * Point ourselves at it. + */ + if (!(kret = krb5_db_set_name(context, + (update) ? dbname : dbname_tmp))) { + /* + * Initialize the database. + */ + if (!(kret = krb5_db_init(context))) { + if ((*restore_function)(programname, + context, + (dumpfile) ? dumpfile : stdin_name, + f, + verbose)) { + fprintf(stderr, restfail_fmt, + programname, restore_name); + exit_status++; + } + if ((kret = krb5_db_fini(context))) { + fprintf(stderr, close_err_fmt, + programname, error_message(kret)); + exit_status++; + } + } + else { + fprintf(stderr, dbinit_err_fmt, + programname, error_message(kret)); + exit_status++; + } + } + else { + fprintf(stderr, dbname_err_fmt, + programname, + (update) ? dbname : dbname_tmp, error_message(kret)); + exit_status++; + } + /* + * If there was an error and this is not an update, then + * destroy the database. + */ + if (!update) { + if (exit_status) { + if ((kret = kdb5_db_destroy(context, dbname))) { + fprintf(stderr, dbdelerr_fmt, + programname, dbname_tmp, error_message(kret)); + exit_status++; + } + } + else { + if ((kret = krb5_db_rename(context, + dbname_tmp, + dbname))) { + fprintf(stderr, dbrenerr_fmt, + programname, dbname_tmp, dbname, + error_message(kret)); + exit_status++; + } + } + } + } + else { + fprintf(stderr, dbcreaterr_fmt, + programname, dbname, error_message(kret)); + exit_status++; + } + if (dumpfile) { + (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK); + fclose(f); + } + } + else { + fprintf(stderr, dfile_err_fmt, dumpfile, error_message(errno)); + exit_status++; + } + free(dbname_tmp); + krb5_free_context(context); +} +#endif |
