#ifdef KRB5_KRB4_COMPAT
#include "k5-int.h"
#include "com_err.h"
#include <des.h>
#include <krb.h>
#include <krb_db.h>
#include <kdc.h>
static C_Block master_key;
static Key_schedule master_key_schedule;
static char *v4_mkeyfile = "/.k";
#include <kadm5/admin.h>
#include <stdio.h>
#define V4_DECLARES_STATIC
#include "kdb5_util.h"
#include "kadm5/adb.h"
#include <netinet/in.h>
#define PROGNAME argv[0]
enum ap_op {
NULL_KEY,
MASTER_KEY,
RANDOM_KEY
};
struct realm_info {
krb5_deltat max_life;
krb5_deltat max_rlife;
krb5_timestamp expiration;
krb5_flags flags;
krb5_keyblock *key;
};
static struct realm_info rblock = {
KRB5_KDB_MAX_LIFE,
KRB5_KDB_MAX_RLIFE,
KRB5_KDB_EXPIRATION,
KRB5_KDB_DEF_FLAGS,
0
};
static int verbose = 0;
static int shortlife = 0;
static krb5_error_code add_principal
(krb5_context,
krb5_principal,
enum ap_op,
struct realm_info *);
static int v4init (char *, int, char *);
static krb5_error_code enter_in_v5_db (krb5_context,
char *, Principal *);
static krb5_error_code process_v4_dump (krb5_context, char *,
char *, long);
static krb5_error_code v4_dump_find_default (krb5_context, char *,
char *, long *);
static krb5_error_code fixup_database (krb5_context, char *);
static int create_local_tgt = 0;
static krb5_keyblock master_keyblock;
static krb5_principal master_princ;
static krb5_data tgt_princ_entries[] = {
{0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
{0, 0, 0} };
static krb5_data db_creator_entries[] = {
{0, sizeof("db_creation")-1, "db_creation"} };
static krb5_principal_data tgt_princ = {
0,
{0, 0, 0},
tgt_princ_entries,
2,
KRB5_NT_SRV_INST
};
static krb5_principal_data db_create_princ = {
0,
{0, 0, 0},
db_creator_entries,
1,
KRB5_NT_SRV_INST
};
void
load_v4db(argc, argv)
int argc;
char *argv[];
{
krb5_error_code retval;
char *dbname = DEFAULT_KDB_FILE;
char *v4dumpfile = 0;
char *realm = 0;
char *mkey_name = 0;
char *mkey_fullname;
char *defrealm;
int v4manual = 0;
krb5_boolean read_mkey = 0;
int tempdb = 0;
char *tempdbname;
krb5_context context;
char *stash_file = (char *) NULL;
int persist, op_ind;
kadm5_config_params newparams;
extern kadm5_config_params global_params;
long exp_time = 0;
krb5_int32 crflags = KRB5_KDB_CREATE_BTREE;
krb5_data seed;
retval = kadm5_init_krb5_context(&context);
if (retval) {
fprintf(stderr, "%s: Could not initialize krb5 context.\n", PROGNAME);
return;
}
if (strrchr(argv[0], '/'))
argv[0] = strrchr(argv[0], '/')+1;
persist = 1;
op_ind = 1;
while (persist && (op_ind < argc)) {
if (!strcmp(argv[op_ind], "-T")) {
create_local_tgt = 1;
}
else if (!strcmp(argv[op_ind], "-t")) {
tempdb = 1;
}
else if (!strcmp(argv[op_ind], "-K")) {
read_mkey = 1;
}
else if (!strcmp(argv[op_ind], "-v")) {
verbose = 1;
}
else if (!strcmp(argv[op_ind], "-n")) {
v4manual++;
}
else if (!strcmp(argv[op_ind], "-S")) {
shortlife++;
}
else if (!strcmp(argv[op_ind], "-s")) {
if ((argc - op_ind) >= 1) {
v4_mkeyfile = argv[op_ind+1];
op_ind++;
} else {
usage();
}
}
else if (!strcmp(argv[op_ind], "-h")) {
crflags = KRB5_KDB_CREATE_HASH;
}
else if ((argc - op_ind) >= 1) {
v4dumpfile = argv[op_ind];
op_ind++;
}
else
usage();
op_ind++;
}
realm = global_params.realm;
dbname = global_params.dbname;
mkey_name = global_params.mkey_name;
master_keyblock.enctype = global_params.enctype;
if (global_params.stash_file)
stash_file = strdup(global_params.stash_file);
rblock.max_life = global_params.max_life;
rblock.max_rlife = global_params.max_rlife;
rblock.expiration = global_params.expiration;
rblock.flags = global_params.flags;
if (!v4dumpfile) {
usage();
krb5_free_context(context);
return;
}
if (!krb5_c_valid_enctype(master_keyblock.enctype)) {
com_err(PROGNAME, KRB5_PROG_KEYTYPE_NOSUPP,
"while setting up enctype %d", master_keyblock.enctype);
krb5_free_context(context);
return;
}
if (! tempdb) {
retval = krb5_db_set_name(context, dbname);
if (retval != ENOENT) {
fprintf(stderr,
"%s: The v5 database appears to already exist.\n",
PROGNAME);
krb5_free_context(context);
return;
}
tempdbname = dbname;
} else {
size_t dbnamelen = strlen(dbname);
tempdbname = malloc(dbnamelen + 2);
if (tempdbname == 0) {
com_err(PROGNAME, ENOMEM, "allocating temporary filename");
krb5_free_context(context);
return;
}
strcpy(tempdbname, dbname);
tempdbname[dbnamelen] = '~';
tempdbname[dbnamelen+1] = 0;
(void) krb5_db_destroy(context, tempdbname);
}
if (!realm) {
retval = krb5_get_default_realm(context, &defrealm);
if (retval) {
com_err(PROGNAME, retval, "while retrieving default realm name");
krb5_free_context(context);
return;
}
realm = defrealm;
}
retval = krb5_db_setup_mkey_name(context, mkey_name, realm,
&mkey_fullname, &master_princ);
if (retval) {
com_err(PROGNAME, retval, "while setting up master key name");
krb5_free_context(context);
return;
}
krb5_princ_set_realm_data(context, &db_create_princ, realm);
krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm));
krb5_princ_set_realm_data(context, &tgt_princ, realm);
krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm));
krb5_princ_component(context, &tgt_princ,1)->data = realm;
krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm);
printf("Initializing database '%s' for realm '%s',\n\
master key name '%s'\n",
dbname, realm, mkey_fullname);
if (read_mkey) {
puts("You will be prompted for the version 5 database Master Password.");
puts("It is important that you NOT FORGET this password.");
fflush(stdout);
}
retval = krb5_db_fetch_mkey(context, master_princ,
master_keyblock.enctype,
read_mkey, read_mkey, stash_file, 0,
&master_keyblock);
if (retval) {
com_err(PROGNAME, retval, "while reading master key");
krb5_free_context(context);
return;
}
rblock.key = &master_keyblock;
seed.length = master_keyblock.length;
seed.data = master_keyblock.contents;
retval = krb5_c_random_seed(context, &seed);
if (retval) {
com_err(PROGNAME, retval, "while initializing random key generator");
krb5_free_context(context);
return;
}
retval = krb5_db_create(context, tempdbname, crflags);
if (retval) {
com_err(PROGNAME, retval, "while creating %sdatabase '%s'",
tempdb ? "temporary " : "", tempdbname);
krb5_free_context(context);
return;
}
retval = krb5_db_set_name(context, tempdbname);
if (retval) {
(void) krb5_db_destroy(context, tempdbname);
com_err(PROGNAME, retval, "while setting active database to '%s'",
tempdbname);
krb5_free_context(context);
return;
}
if (v4init(PROGNAME, v4manual, v4dumpfile)) {
(void) krb5_db_destroy(context, tempdbname);
krb5_free_context(context);
return;
}
if ((retval = krb5_db_init(context)) ||
(retval = krb5_db_open_database(context))) {
(void) krb5_db_destroy(context, tempdbname);
com_err(PROGNAME, retval, "while initializing the database '%s'",
tempdbname);
krb5_free_context(context);
return;
}
retval = add_principal(context, master_princ, MASTER_KEY, &rblock);
if (retval) {
(void) krb5_db_fini(context);
(void) krb5_db_destroy(context, tempdbname);
com_err(PROGNAME, retval, "while adding K/M to the database");
krb5_free_context(context);
return;
}
if (create_local_tgt &&
(retval = add_principal(context, &tgt_princ, RANDOM_KEY, &rblock))) {
(void) krb5_db_fini(context);
(void) krb5_db_destroy(context, tempdbname);
com_err(PROGNAME, retval, "while adding TGT service to the database");
krb5_free_context(context);
return;
}
retval = v4_dump_find_default(context, v4dumpfile, realm, &exp_time);
if (retval) {
com_err(PROGNAME, retval, "warning: default entry not found");
}
retval = process_v4_dump(context, v4dumpfile, realm, exp_time);
putchar('\n');
if (retval)
com_err(PROGNAME, retval, "while translating entries to the database");
else {
retval = fixup_database(context, realm);
}
if (retval == 0) {
retval = krb5_db_fini (context);
if (retval)
com_err(PROGNAME, retval, "while shutting down database");
else if (tempdb && (retval = krb5_db_rename(context, tempdbname,
dbname)))
com_err(PROGNAME, retval, "while renaming temporary database");
} else {
(void) krb5_db_fini (context);
if (tempdb)
(void) krb5_db_destroy (context, tempdbname);
}
memset((char *)master_keyblock.contents, 0, master_keyblock.length);
newparams = global_params;
if (!tempdb && (retval = osa_adb_create_policy_db(&newparams))) {
com_err(PROGNAME, retval, "while creating policy database");
kadm5_free_config_params(context, &newparams);
krb5_free_context(context);
return;
}
retval = kadm5_create_magic_princs(&newparams, context);
if (retval) {
com_err(PROGNAME, retval, "while creating KADM5 principals");
krb5_free_context(context);
return;
}
krb5_free_context(context);
return;
}
static int
v4init(pname, manual, dumpfile)
char *pname;
int manual;
char *dumpfile;
{
int fd;
int ok = 0;
if (!manual) {
fd = open(v4_mkeyfile, O_RDONLY, 0600);
if (fd >= 0) {
if (read(fd, master_key, sizeof(master_key)) == sizeof(master_key))
ok = 1;
close(fd);
}
}
if (!ok) {
des_read_password(&master_key, "V4 Kerberos master key", 0);
printf("\n");
}
key_sched(master_key, master_key_schedule);
return 0;
}
static krb5_error_code
enter_in_v5_db(context, realm, princ)
krb5_context context;
char *realm;
Principal *princ;
{
krb5_db_entry entry;
krb5_error_code retval;
krb5_keyblock v4v5key;
int nentries = 1;
des_cblock v4key;
char *name;
krb5_timestamp mod_time;
krb5_principal mod_princ;
krb5_keysalt keysalt;
if (create_local_tgt && !strcmp(princ->name, "krbtgt") &&
!strcmp(princ->instance, realm)) {
if (verbose)
printf("\nignoring local TGT: '%s.%s' ...",
princ->name, princ->instance);
return 0;
}
if (!strcmp(princ->name, KERB_M_NAME) &&
!strcmp(princ->instance, KERB_M_INST)) {
des_cblock key_from_db;
int val;
memcpy(key_from_db, (char *)&princ->key_low, 4);
memcpy(((char *) key_from_db) + 4, (char *)&princ->key_high, 4);
pcbc_encrypt((C_Block *) &key_from_db,
(C_Block *) &key_from_db,
(long) sizeof(C_Block),
master_key_schedule,
(C_Block *) master_key,
DECRYPT);
val = memcmp((char *) master_key, (char *) key_from_db,
sizeof(master_key));
memset((char *)key_from_db, 0, sizeof(key_from_db));
if (val) {
return KRB5_KDB_BADMASTERKEY;
}
if (verbose)
printf("\nignoring '%s.%s' ...", princ->name, princ->instance);
return 0;
}
memset((char *) &entry, 0, sizeof(entry));
retval = krb5_425_conv_principal(context, princ->name, princ->instance,
realm, &entry.princ);
if (retval)
return retval;
if (verbose) {
retval = krb5_unparse_name(context, entry.princ, &name);
if (retval)
name = strdup("<not unparsable name!>");
if (verbose)
printf("\ntranslating %s...", name);
free(name);
}
retval = krb5_build_principal(context, &mod_princ,
strlen(realm), realm, princ->mod_name,
princ->mod_instance[0] ?
princ->mod_instance : NULL,
NULL);
if (retval) {
krb5_free_principal(context, entry.princ);
return retval;
}
mod_time = princ->mod_date;
if (!shortlife)
entry.max_life = krb_life_to_time(0, princ->max_life);
else
entry.max_life = princ->max_life * 60 * 5;
entry.max_renewable_life = rblock.max_rlife;
entry.len = KRB5_KDB_V1_BASE_LENGTH;
entry.expiration = princ->exp_date;
entry.attributes = rblock.flags;
memcpy((char *)v4key, (char *)&(princ->key_low), 4);
memcpy((char *) (((char *) v4key) + 4), (char *)&(princ->key_high), 4);
pcbc_encrypt((C_Block *) &v4key,
(C_Block *) &v4key,
(long) sizeof(C_Block),
master_key_schedule,
(C_Block *) master_key,
DECRYPT);
v4v5key.magic = KV5M_KEYBLOCK;
v4v5key.contents = (krb5_octet *)v4key;
v4v5key.enctype = ENCTYPE_DES_CBC_CRC;
v4v5key.length = sizeof(v4key);
retval = krb5_dbe_create_key_data(context, &entry);
if (retval) {
krb5_free_principal(context, entry.princ);
krb5_free_principal(context, mod_princ);
return retval;
}
keysalt.type = KRB5_KDB_SALTTYPE_V4;
keysalt.data.length = 0;
keysalt.data.data = (char *) NULL;
retval = krb5_dbekd_encrypt_key_data(context, rblock.key,
&v4v5key, &keysalt,
princ->key_version,
&entry.key_data[0]);
if (!retval)
retval = krb5_dbe_update_mod_princ_data(context, &entry,
mod_time, mod_princ);
if (!retval)
retval = krb5_dbe_update_last_pwd_change(context, &entry, mod_time);
if (retval) {
krb5_db_free_principal(context, &entry, 1);
krb5_free_principal(context, mod_princ);
return retval;
}
memset((char *)v4key, 0, sizeof(v4key));
retval = krb5_db_put_principal(context, &entry, &nentries);
if (!retval && !strcmp(princ->name, "krbtgt") &&
strcmp(princ->instance, realm) && princ->instance[0]) {
krb5_free_principal(context, entry.princ);
retval = krb5_build_principal(context, &entry.princ,
strlen(princ->instance),
princ->instance,
"krbtgt", realm, NULL);
if (retval)
return retval;
retval = krb5_db_put_principal(context, &entry, &nentries);
}
krb5_db_free_principal(context, &entry, 1);
krb5_free_principal(context, mod_princ);
return retval;
}
static krb5_error_code
add_principal(context, princ, op, pblock)
krb5_context context;
krb5_principal princ;
enum ap_op op;
struct realm_info *pblock;
{
krb5_db_entry entry;
krb5_error_code retval;
krb5_keyblock rkey;
int nentries = 1;
krb5_timestamp mod_time;
memset((char *) &entry, 0, sizeof(entry));
retval = krb5_copy_principal(context, princ, &entry.princ);
if (retval)
return(retval);
entry.max_life = pblock->max_life;
entry.max_renewable_life = pblock->max_rlife;
entry.len = KRB5_KDB_V1_BASE_LENGTH;
entry.expiration = pblock->expiration;
retval = krb5_timeofday(context, &mod_time);
if (retval) {
krb5_db_free_principal(context, &entry, 1);
return retval;
}
entry.attributes = pblock->flags;
retval = krb5_dbe_create_key_data(context, &entry);
if (retval) {
krb5_db_free_principal(context, &entry, 1);
return(retval);
}
switch (op) {
case MASTER_KEY:
entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
retval = krb5_dbekd_encrypt_key_data(context, pblock->key,
&master_keyblock,
(krb5_keysalt *) NULL, 1,
&entry.key_data[0]);
if (retval) {
krb5_db_free_principal(context, &entry, 1);
return retval;
}
break;
case RANDOM_KEY:
retval = krb5_c_make_random_key(context, pblock->key->enctype,
&rkey);
if (retval) {
krb5_db_free_principal(context, &entry, 1);
return retval;
}
retval = krb5_dbekd_encrypt_key_data(context, pblock->key,
&rkey, (krb5_keysalt *) NULL,
1, &entry.key_data[0]);
if (retval) {
krb5_db_free_principal(context, &entry, 1);
return(retval);
}
krb5_free_keyblock_contents(context, &rkey);
break;
case NULL_KEY:
return EOPNOTSUPP;
default:
break;
}
retval = krb5_dbe_update_mod_princ_data(context, &entry,
mod_time, &db_create_princ);
if (!retval)
retval = krb5_db_put_principal(context, &entry, &nentries);
krb5_db_free_principal(context, &entry, 1);
return retval;
}
#define daysinyear(y) (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
#define SECSPERDAY 24*60*60
#define SECSPERHOUR 60*60
#define SECSPERMIN 60
static int cumdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
365};
static int leapyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static int nonleapyear[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
static long
maketime(tp, local)
register struct tm *tp;
int local;
{
register long retval;
int foo;
int *marray;
if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
tp->tm_hour < 0 || tp->tm_hour > 23 ||
tp->tm_min < 0 || tp->tm_min > 59 ||
tp->tm_sec < 0 || tp->tm_sec > 59)
return 0;
retval = 0;
if (tp->tm_year < 1900)
foo = tp->tm_year + 1900;
else
foo = tp->tm_year;
if (foo < 1901 || foo > 2038)
return 0;
if (daysinyear(foo) == 366) {
if (tp->tm_mon > 1)
retval+= SECSPERDAY;
marray = leapyear;
} else
marray = nonleapyear;
if (tp->tm_mday < 0 || tp->tm_mday > marray[tp->tm_mon])
return 0;
while (--foo >= 1970)
retval += daysinyear(foo) * SECSPERDAY;
retval += cumdays[tp->tm_mon] * SECSPERDAY;
retval += (tp->tm_mday-1) * SECSPERDAY;
retval += tp->tm_hour * SECSPERHOUR + tp->tm_min * SECSPERMIN + tp->tm_sec;
if (local) {
struct timezone tz;
struct timeval tv;
if (gettimeofday(&tv, &tz) < 0) {
return(retval);
}
retval += tz.tz_minuteswest * SECSPERMIN;
}
return(retval);
}
static long
time_explode(cp)
register char *cp;
{
char wbuf[5];
struct tm tp;
int local;
memset((char *)&tp, 0, sizeof(tp));
if (strlen(cp) > 10) {
(void) strncpy(wbuf, cp, 4);
wbuf[4] = 0;
tp.tm_year = atoi(wbuf);
cp += 4;
local = 0;
} else {
wbuf[0] = *cp++;
wbuf[1] = *cp++;
wbuf[2] = 0;
tp.tm_year = 1900 + atoi(wbuf);
local = 1;
}
wbuf[0] = *cp++;
wbuf[1] = *cp++;
wbuf[2] = 0;
tp.tm_mon = atoi(wbuf)-1;
wbuf[0] = *cp++;
wbuf[1] = *cp++;
tp.tm_mday = atoi(wbuf);
wbuf[0] = *cp++;
wbuf[1] = *cp++;
tp.tm_hour = atoi(wbuf);
wbuf[0] = *cp++;
wbuf[1] = *cp++;
tp.tm_min = atoi(wbuf);
return(maketime(&tp, local));
}
static krb5_error_code
process_v4_dump(context, dumpfile, realm, default_exp_time)
krb5_context context;
char *dumpfile;
char *realm;
long default_exp_time;
{
krb5_error_code retval;
FILE *input_file;
Principal aprinc;
char exp_date_str[50];
char mod_date_str[50];
int temp1, temp2, temp3;
input_file = fopen(dumpfile, "r");
if (!input_file)
return errno;
for (;;) {
int nread;
memset((char *)&aprinc, 0, sizeof(aprinc));
nread = fscanf(input_file,
"%s %s %d %d %d %hd %lx %lx %s %s %s %s\n",
aprinc.name,
aprinc.instance,
&temp1,
&temp2,
&temp3,
&aprinc.attributes,
&aprinc.key_low,
&aprinc.key_high,
exp_date_str,
mod_date_str,
aprinc.mod_name,
aprinc.mod_instance);
if (nread != 12) {
retval = nread == EOF ? 0 : KRB5_KDB_DB_CORRUPT;
break;
}
aprinc.key_low = ntohl (aprinc.key_low);
aprinc.key_high = ntohl (aprinc.key_high);
aprinc.max_life = (unsigned char) temp1;
aprinc.kdc_key_ver = (unsigned char) temp2;
aprinc.key_version = (unsigned char) temp3;
aprinc.exp_date = time_explode(exp_date_str);
if (aprinc.exp_date == default_exp_time)
aprinc.exp_date = 0;
aprinc.mod_date = time_explode(mod_date_str);
if (aprinc.instance[0] == '*')
aprinc.instance[0] = '\0';
if (aprinc.mod_name[0] == '*')
aprinc.mod_name[0] = '\0';
if (aprinc.mod_instance[0] == '*')
aprinc.mod_instance[0] = '\0';
retval = enter_in_v5_db(context, realm, &aprinc);
if (retval)
break;
}
(void) fclose(input_file);
return retval;
}
static krb5_error_code
v4_dump_find_default(context, dumpfile, realm, exptime)
krb5_context context;
char *dumpfile;
char *realm;
long *exptime;
{
krb5_error_code retval = 0;
FILE *input_file;
Principal aprinc;
char exp_date_str[50];
char mod_date_str[50];
int temp1, temp2, temp3;
long foundtime, guess1, guess2;
guess1 = 946702799-59;
guess2 = 946702799+((365*10+3)*24*60*60);
input_file = fopen(dumpfile, "r");
if (!input_file)
return errno;
for (;;) {
int nread;
memset((char *)&aprinc, 0, sizeof(aprinc));
nread = fscanf(input_file,
"%s %s %d %d %d %hd %lx %lx %s %s %s %s\n",
aprinc.name,
aprinc.instance,
&temp1,
&temp2,
&temp3,
&aprinc.attributes,
&aprinc.key_low,
&aprinc.key_high,
exp_date_str,
mod_date_str,
aprinc.mod_name,
aprinc.mod_instance);
if (nread != 12) {
retval = nread == EOF ? 0 : KRB5_KDB_DB_CORRUPT;
break;
}
if (!strcmp(aprinc.name, "default")
&& !strcmp(aprinc.instance, "*")) {
foundtime = time_explode(exp_date_str);
if (foundtime == guess1 || foundtime == guess2)
*exptime = foundtime;
if (verbose) {
printf("\ndefault expiration found: ");
if (foundtime == guess1) {
printf("MIT or pre96q1 value (1999)");
} else if (foundtime == guess2) {
printf("Cygnus CNS post 96q1 value (2009)");
} else {
printf("non-default start time (%ld,%s)",
foundtime, exp_date_str);
}
}
break;
}
}
(void) fclose(input_file);
return retval;
}
static krb5_error_code fixup_database(context, realm)
krb5_context context;
char * realm;
{
return 0;
}
#else
void
load_v4db(argc, argv)
int argc;
char *argv[];
{
printf("This version of kdb5_util does not support the V4 load command.\n");
}
#endif