507 lines
13 KiB
C
507 lines
13 KiB
C
|
/*
|
||
|
* Copyright 1987, 1988 by the Massachusetts Institute of Technology.
|
||
|
* For copying and distribution information, please see the file
|
||
|
* <Copyright.MIT>.
|
||
|
*
|
||
|
* Kerberos database manipulation utility. This program allows you to
|
||
|
* dump a kerberos database to an ascii readable file and load this
|
||
|
* file into the database. Read locking of the database is done during a
|
||
|
* dump operation. NO LOCKING is done during a load operation. Loads
|
||
|
* should happen with other processes shutdown.
|
||
|
*
|
||
|
* Written July 9, 1987 by Jeffrey I. Schiller
|
||
|
*
|
||
|
* from: kdb_util.c,v 4.4 90/01/09 15:57:20 raeburn Exp $
|
||
|
* $Id: kdb_util.c,v 1.3 1994/09/24 14:04:21 g89r4222 Exp $
|
||
|
*/
|
||
|
|
||
|
#ifndef lint
|
||
|
static char rcsid[] =
|
||
|
"$Id: kdb_util.c,v 1.3 1994/09/24 14:04:21 g89r4222 Exp $";
|
||
|
#endif lint
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <netinet/in.h>
|
||
|
#include "time.h"
|
||
|
#include <strings.h>
|
||
|
#include <des.h>
|
||
|
#include <krb.h>
|
||
|
#include <sys/file.h>
|
||
|
#include <krb_db.h>
|
||
|
|
||
|
#define TRUE 1
|
||
|
|
||
|
Principal aprinc;
|
||
|
|
||
|
static des_cblock master_key, new_master_key;
|
||
|
static des_key_schedule master_key_schedule, new_master_key_schedule;
|
||
|
|
||
|
#define zaptime(foo) bzero((char *)(foo), sizeof(*(foo)))
|
||
|
|
||
|
extern long kdb_get_master_key(), kdb_verify_master_key();
|
||
|
extern char *malloc();
|
||
|
extern int errno;
|
||
|
|
||
|
char * progname;
|
||
|
|
||
|
main(argc, argv)
|
||
|
int argc;
|
||
|
char **argv;
|
||
|
{
|
||
|
FILE *file;
|
||
|
enum {
|
||
|
OP_LOAD,
|
||
|
OP_DUMP,
|
||
|
OP_SLAVE_DUMP,
|
||
|
OP_NEW_MASTER,
|
||
|
OP_CONVERT_OLD_DB,
|
||
|
} op;
|
||
|
char *file_name;
|
||
|
char *prog = argv[0];
|
||
|
char *db_name;
|
||
|
|
||
|
progname = prog;
|
||
|
|
||
|
if (argc != 3 && argc != 4) {
|
||
|
fprintf(stderr, "Usage: %s operation file-name [database name].\n",
|
||
|
argv[0]);
|
||
|
exit(1);
|
||
|
}
|
||
|
if (argc == 3)
|
||
|
db_name = DBM_FILE;
|
||
|
else
|
||
|
db_name = argv[3];
|
||
|
|
||
|
if (kerb_db_set_name (db_name) != 0) {
|
||
|
perror("Can't open database");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
if (!strcmp(argv[1], "load"))
|
||
|
op = OP_LOAD;
|
||
|
else if (!strcmp(argv[1], "dump"))
|
||
|
op = OP_DUMP;
|
||
|
else if (!strcmp(argv[1], "slave_dump"))
|
||
|
op = OP_SLAVE_DUMP;
|
||
|
else if (!strcmp(argv[1], "new_master_key"))
|
||
|
op = OP_NEW_MASTER;
|
||
|
else if (!strcmp(argv[1], "convert_old_db"))
|
||
|
op = OP_CONVERT_OLD_DB;
|
||
|
else {
|
||
|
fprintf(stderr,
|
||
|
"%s: %s is an invalid operation.\n", prog, argv[1]);
|
||
|
fprintf(stderr,
|
||
|
"%s: Valid operations are \"dump\", \"slave_dump\",", argv[0]);
|
||
|
fprintf(stderr,
|
||
|
"\"load\", \"new_master_key\", and \"convert_old_db\".\n");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
file_name = argv[2];
|
||
|
file = fopen(file_name, op == OP_LOAD ? "r" : "w");
|
||
|
if (file == NULL) {
|
||
|
fprintf(stderr, "%s: Unable to open %s\n", prog, argv[2]);
|
||
|
(void) fflush(stderr);
|
||
|
perror("open");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
switch (op) {
|
||
|
case OP_DUMP:
|
||
|
if ((dump_db (db_name, file, (void (*)()) 0) == EOF) ||
|
||
|
(fclose(file) == EOF)) {
|
||
|
fprintf(stderr, "error on file %s:", file_name);
|
||
|
perror("");
|
||
|
exit(1);
|
||
|
}
|
||
|
break;
|
||
|
case OP_SLAVE_DUMP:
|
||
|
if ((dump_db (db_name, file, (void (*)()) 0) == EOF) ||
|
||
|
(fclose(file) == EOF)) {
|
||
|
fprintf(stderr, "error on file %s:", file_name);
|
||
|
perror("");
|
||
|
exit(1);
|
||
|
}
|
||
|
update_ok_file (file_name);
|
||
|
break;
|
||
|
case OP_LOAD:
|
||
|
load_db (db_name, file);
|
||
|
break;
|
||
|
case OP_NEW_MASTER:
|
||
|
convert_new_master_key (db_name, file);
|
||
|
printf("Don't forget to do a `kdb_util load %s' to reload the database!\n", file_name);
|
||
|
break;
|
||
|
case OP_CONVERT_OLD_DB:
|
||
|
convert_old_format_db (db_name, file);
|
||
|
printf("Don't forget to do a `kdb_util load %s' to reload the database!\n", file_name);
|
||
|
break;
|
||
|
}
|
||
|
exit(0);
|
||
|
}
|
||
|
|
||
|
clear_secrets ()
|
||
|
{
|
||
|
bzero((char *)master_key, sizeof (des_cblock));
|
||
|
bzero((char *)master_key_schedule, sizeof (Key_schedule));
|
||
|
bzero((char *)new_master_key, sizeof (des_cblock));
|
||
|
bzero((char *)new_master_key_schedule, sizeof (Key_schedule));
|
||
|
}
|
||
|
|
||
|
/* cv_key is a procedure which takes a principle and changes its key,
|
||
|
either for a new method of encrypting the keys, or a new master key.
|
||
|
if cv_key is null no transformation of key is done (other than net byte
|
||
|
order). */
|
||
|
|
||
|
struct callback_args {
|
||
|
void (*cv_key)();
|
||
|
FILE *output_file;
|
||
|
};
|
||
|
|
||
|
static int dump_db_1(arg, principal)
|
||
|
char *arg;
|
||
|
Principal *principal;
|
||
|
{ /* replace null strings with "*" */
|
||
|
struct callback_args *a = (struct callback_args *)arg;
|
||
|
|
||
|
if (principal->instance[0] == '\0') {
|
||
|
principal->instance[0] = '*';
|
||
|
principal->instance[1] = '\0';
|
||
|
}
|
||
|
if (principal->mod_name[0] == '\0') {
|
||
|
principal->mod_name[0] = '*';
|
||
|
principal->mod_name[1] = '\0';
|
||
|
}
|
||
|
if (principal->mod_instance[0] == '\0') {
|
||
|
principal->mod_instance[0] = '*';
|
||
|
principal->mod_instance[1] = '\0';
|
||
|
}
|
||
|
if (a->cv_key != NULL) {
|
||
|
(*a->cv_key) (principal);
|
||
|
}
|
||
|
fprintf(a->output_file, "%s %s %d %d %d %d %x %x",
|
||
|
principal->name,
|
||
|
principal->instance,
|
||
|
principal->max_life,
|
||
|
principal->kdc_key_ver,
|
||
|
principal->key_version,
|
||
|
principal->attributes,
|
||
|
htonl (principal->key_low),
|
||
|
htonl (principal->key_high));
|
||
|
print_time(a->output_file, principal->exp_date);
|
||
|
print_time(a->output_file, principal->mod_date);
|
||
|
fprintf(a->output_file, " %s %s\n",
|
||
|
principal->mod_name,
|
||
|
principal->mod_instance);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
dump_db (db_file, output_file, cv_key)
|
||
|
char *db_file;
|
||
|
FILE *output_file;
|
||
|
void (*cv_key)();
|
||
|
{
|
||
|
struct callback_args a;
|
||
|
|
||
|
a.cv_key = cv_key;
|
||
|
a.output_file = output_file;
|
||
|
|
||
|
kerb_db_iterate (dump_db_1, (char *)&a);
|
||
|
return fflush(output_file);
|
||
|
}
|
||
|
|
||
|
load_db (db_file, input_file)
|
||
|
char *db_file;
|
||
|
FILE *input_file;
|
||
|
{
|
||
|
char exp_date_str[50];
|
||
|
char mod_date_str[50];
|
||
|
int temp1, temp2, temp3;
|
||
|
long time_explode();
|
||
|
int code;
|
||
|
char *temp_db_file;
|
||
|
temp1 = strlen(db_file+2);
|
||
|
temp_db_file = malloc (temp1);
|
||
|
strcpy(temp_db_file, db_file);
|
||
|
strcat(temp_db_file, "~");
|
||
|
|
||
|
/* Create the database */
|
||
|
if ((code = kerb_db_create(temp_db_file)) != 0) {
|
||
|
fprintf(stderr, "Couldn't create temp database %s: %s\n",
|
||
|
temp_db_file, sys_errlist[code]);
|
||
|
exit(1);
|
||
|
}
|
||
|
kerb_db_set_name(temp_db_file);
|
||
|
for (;;) { /* explicit break on eof from fscanf */
|
||
|
bzero((char *)&aprinc, sizeof(aprinc));
|
||
|
if (fscanf(input_file,
|
||
|
"%s %s %d %d %d %hd %x %x %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) == EOF)
|
||
|
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);
|
||
|
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';
|
||
|
if (kerb_db_put_principal(&aprinc, 1) != 1) {
|
||
|
fprintf(stderr, "Couldn't store %s.%s: %s; load aborted\n",
|
||
|
aprinc.name, aprinc.instance,
|
||
|
sys_errlist[errno]);
|
||
|
exit(1);
|
||
|
};
|
||
|
}
|
||
|
if ((code = kerb_db_rename(temp_db_file, db_file)) != 0)
|
||
|
perror("database rename failed");
|
||
|
(void) fclose(input_file);
|
||
|
free(temp_db_file);
|
||
|
}
|
||
|
|
||
|
print_time(file, timeval)
|
||
|
FILE *file;
|
||
|
unsigned long timeval;
|
||
|
{
|
||
|
struct tm *tm;
|
||
|
struct tm *gmtime();
|
||
|
tm = gmtime((long *)&timeval);
|
||
|
fprintf(file, " %04d%02d%02d%02d%02d",
|
||
|
tm->tm_year < 1900 ? tm->tm_year + 1900: tm->tm_year,
|
||
|
tm->tm_mon + 1,
|
||
|
tm->tm_mday,
|
||
|
tm->tm_hour,
|
||
|
tm->tm_min);
|
||
|
}
|
||
|
|
||
|
/*ARGSUSED*/
|
||
|
update_ok_file (file_name)
|
||
|
char *file_name;
|
||
|
{
|
||
|
/* handle slave locking/failure stuff */
|
||
|
char *file_ok;
|
||
|
int fd;
|
||
|
static char ok[]=".dump_ok";
|
||
|
|
||
|
if ((file_ok = (char *)malloc(strlen(file_name) + strlen(ok) + 1))
|
||
|
== NULL) {
|
||
|
fprintf(stderr, "kdb_util: out of memory.\n");
|
||
|
(void) fflush (stderr);
|
||
|
perror ("malloc");
|
||
|
exit (1);
|
||
|
}
|
||
|
strcpy(file_ok, file_name);
|
||
|
strcat(file_ok, ok);
|
||
|
if ((fd = open(file_ok, O_WRONLY|O_CREAT|O_TRUNC, 0400)) < 0) {
|
||
|
fprintf(stderr, "Error creating 'ok' file, '%s'", file_ok);
|
||
|
perror("");
|
||
|
(void) fflush (stderr);
|
||
|
exit (1);
|
||
|
}
|
||
|
free(file_ok);
|
||
|
close(fd);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
convert_key_new_master (p)
|
||
|
Principal *p;
|
||
|
{
|
||
|
des_cblock key;
|
||
|
|
||
|
/* leave null keys alone */
|
||
|
if ((p->key_low == 0) && (p->key_high == 0)) return;
|
||
|
|
||
|
/* move current key to des_cblock for encryption, special case master key
|
||
|
since that's changing */
|
||
|
if ((strncmp (p->name, KERB_M_NAME, ANAME_SZ) == 0) &&
|
||
|
(strncmp (p->instance, KERB_M_INST, INST_SZ) == 0)) {
|
||
|
bcopy((char *)new_master_key, (char *) key, sizeof (des_cblock));
|
||
|
(p->key_version)++;
|
||
|
} else {
|
||
|
bcopy((char *)&(p->key_low), (char *)key, 4);
|
||
|
bcopy((char *)&(p->key_high), (char *) (((long *) key) + 1), 4);
|
||
|
kdb_encrypt_key (key, key, master_key, master_key_schedule, DECRYPT);
|
||
|
}
|
||
|
|
||
|
kdb_encrypt_key (key, key, new_master_key, new_master_key_schedule, ENCRYPT);
|
||
|
|
||
|
bcopy((char *)key, (char *)&(p->key_low), 4);
|
||
|
bcopy((char *)(((long *) key) + 1), (char *)&(p->key_high), 4);
|
||
|
bzero((char *)key, sizeof (key)); /* a little paranoia ... */
|
||
|
|
||
|
(p->kdc_key_ver)++;
|
||
|
}
|
||
|
|
||
|
convert_new_master_key (db_file, out)
|
||
|
char *db_file;
|
||
|
FILE *out;
|
||
|
{
|
||
|
|
||
|
printf ("\n\nEnter the CURRENT master key.");
|
||
|
if (kdb_get_master_key (TRUE, master_key, master_key_schedule) != 0) {
|
||
|
fprintf (stderr, "%s: Couldn't get master key.\n");
|
||
|
clear_secrets ();
|
||
|
exit (-1);
|
||
|
}
|
||
|
|
||
|
if (kdb_verify_master_key (master_key, master_key_schedule, stderr) < 0) {
|
||
|
clear_secrets ();
|
||
|
exit (-1);
|
||
|
}
|
||
|
|
||
|
printf ("\n\nNow enter the NEW master key. Do not forget it!!");
|
||
|
if (kdb_get_master_key (TRUE, new_master_key, new_master_key_schedule) != 0) {
|
||
|
fprintf (stderr, "%s: Couldn't get new master key.\n");
|
||
|
clear_secrets ();
|
||
|
exit (-1);
|
||
|
}
|
||
|
|
||
|
dump_db (db_file, out, convert_key_new_master);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
convert_key_old_db (p)
|
||
|
Principal *p;
|
||
|
{
|
||
|
des_cblock key;
|
||
|
|
||
|
/* leave null keys alone */
|
||
|
if ((p->key_low == 0) && (p->key_high == 0)) return;
|
||
|
|
||
|
bcopy((char *)&(p->key_low), (char *)key, 4);
|
||
|
bcopy((char *)&(p->key_high), (char *)(((long *) key) + 1), 4);
|
||
|
|
||
|
#ifndef NOENCRYPTION
|
||
|
des_pcbc_encrypt((des_cblock *)key,(des_cblock *)key,
|
||
|
(long)sizeof(des_cblock),master_key_schedule,
|
||
|
(des_cblock *)master_key_schedule,DECRYPT);
|
||
|
#endif
|
||
|
|
||
|
/* make new key, new style */
|
||
|
kdb_encrypt_key (key, key, master_key, master_key_schedule, ENCRYPT);
|
||
|
|
||
|
bcopy((char *)key, (char *)&(p->key_low), 4);
|
||
|
bcopy((char *)(((long *) key) + 1), (char *)&(p->key_high), 4);
|
||
|
bzero((char *)key, sizeof (key)); /* a little paranoia ... */
|
||
|
}
|
||
|
|
||
|
convert_old_format_db (db_file, out)
|
||
|
char *db_file;
|
||
|
FILE *out;
|
||
|
{
|
||
|
des_cblock key_from_db;
|
||
|
Principal principal_data[1];
|
||
|
int n, more;
|
||
|
|
||
|
if (kdb_get_master_key (TRUE, master_key, master_key_schedule) != 0L) {
|
||
|
fprintf (stderr, "%s: Couldn't get master key.\n");
|
||
|
clear_secrets();
|
||
|
exit (-1);
|
||
|
}
|
||
|
|
||
|
/* can't call kdb_verify_master_key because this is an old style db */
|
||
|
/* lookup the master key version */
|
||
|
n = kerb_get_principal(KERB_M_NAME, KERB_M_INST, principal_data,
|
||
|
1 /* only one please */, &more);
|
||
|
if ((n != 1) || more) {
|
||
|
fprintf(stderr, "verify_master_key: ",
|
||
|
"Kerberos error on master key lookup, %d found.\n",
|
||
|
n);
|
||
|
exit (-1);
|
||
|
}
|
||
|
|
||
|
/* set up the master key */
|
||
|
fprintf(stderr, "Current Kerberos master key version is %d.\n",
|
||
|
principal_data[0].kdc_key_ver);
|
||
|
|
||
|
/*
|
||
|
* now use the master key to decrypt (old style) the key in the db, had better
|
||
|
* be the same!
|
||
|
*/
|
||
|
bcopy((char *)&principal_data[0].key_low, (char *)key_from_db, 4);
|
||
|
bcopy((char *)&principal_data[0].key_high,
|
||
|
(char *)(((long *) key_from_db) + 1), 4);
|
||
|
#ifndef NOENCRYPTION
|
||
|
des_pcbc_encrypt(key_from_db,key_from_db,(long)sizeof(key_from_db),
|
||
|
master_key_schedule,(des_cblock *)master_key_schedule,DECRYPT);
|
||
|
#endif
|
||
|
/* the decrypted database key had better equal the master key */
|
||
|
n = bcmp((char *) master_key, (char *) key_from_db,
|
||
|
sizeof(master_key));
|
||
|
bzero((char *)key_from_db, sizeof(key_from_db));
|
||
|
|
||
|
if (n) {
|
||
|
fprintf(stderr, "\n\07\07%verify_master_key: Invalid master key, ");
|
||
|
fprintf(stderr, "does not match database.\n");
|
||
|
exit (-1);
|
||
|
}
|
||
|
|
||
|
fprintf(stderr, "Master key verified.\n");
|
||
|
(void) fflush(stderr);
|
||
|
|
||
|
dump_db (db_file, out, convert_key_old_db);
|
||
|
}
|
||
|
|
||
|
long
|
||
|
time_explode(cp)
|
||
|
register char *cp;
|
||
|
{
|
||
|
char wbuf[5];
|
||
|
struct tm tp;
|
||
|
long maketime();
|
||
|
int local;
|
||
|
|
||
|
zaptime(&tp); /* clear out the struct */
|
||
|
|
||
|
if (strlen(cp) > 10) { /* new format */
|
||
|
(void) strncpy(wbuf, cp, 4);
|
||
|
wbuf[4] = 0;
|
||
|
tp.tm_year = atoi(wbuf);
|
||
|
cp += 4; /* step over the year */
|
||
|
local = 0; /* GMT */
|
||
|
} else { /* old format: local time,
|
||
|
year is 2 digits, assuming 19xx */
|
||
|
wbuf[0] = *cp++;
|
||
|
wbuf[1] = *cp++;
|
||
|
wbuf[2] = 0;
|
||
|
tp.tm_year = 1900 + atoi(wbuf);
|
||
|
local = 1; /* local */
|
||
|
}
|
||
|
|
||
|
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));
|
||
|
}
|