freebsd-dev/crypto/kerberosIV/kadmin/ksrvutil.c
Mark Murray 03656ac1b0 Initial import of KTH eBones. This has been cleaned up to only include
the "core" Kerberos functionality. The rest of the userland will get their
own changes later.
1997-09-04 06:04:33 +00:00

602 lines
16 KiB
C

/*
Copyright (C) 1989 by the Massachusetts Institute of Technology
Export of this software from the United States of America is assumed
to 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.
*/
/*
* list and update contents of srvtab files
*/
/*
* ksrvutil
* list and update the contents of srvtab files
*/
#include "kadm_locl.h"
RCSID("$Id: ksrvutil.c,v 1.39 1997/05/02 14:28:52 assar Exp $");
#include "ksrvutil.h"
#ifdef NOENCRYPTION
#define read_long_pw_string placebo_read_pw_string
#else /* NOENCRYPTION */
#define read_long_pw_string des_read_pw_string
#endif /* NOENCRYPTION */
#define SRVTAB_MODE 0600 /* rw------- */
#define PAD " "
#define VNO_HEADER "Version"
#define VNO_FORMAT "%4d "
#define KEY_HEADER " Key " /* 17 characters long */
#define PRINC_HEADER " Principal\n"
#define PRINC_FORMAT "%s"
char u_name[ANAME_SZ];
char u_inst[INST_SZ];
char u_realm[REALM_SZ];
int destroyp = FALSE; /* Should the ticket file be destroyed? */
static unsigned short
get_mode(char *filename)
{
struct stat statbuf;
unsigned short mode;
memset(&statbuf, 0, sizeof(statbuf));
if (stat(filename, &statbuf) < 0)
mode = SRVTAB_MODE;
else
mode = statbuf.st_mode;
return(mode);
}
static void
copy_keyfile(char *keyfile, char *backup_keyfile)
{
int keyfile_fd;
int backup_keyfile_fd;
int keyfile_mode;
char buf[BUFSIZ]; /* for copying keyfiles */
int rcount; /* for copying keyfiles */
int try_again;
memset(buf, 0, sizeof(buf));
do {
try_again = FALSE;
if ((keyfile_fd = open(keyfile, O_RDONLY, 0)) < 0) {
if (errno != ENOENT)
err (1, "read %s", keyfile);
else {
try_again = TRUE;
if ((keyfile_fd =
open(keyfile,
O_WRONLY | O_TRUNC | O_CREAT, SRVTAB_MODE)) < 0)
err(1, "create %s", keyfile);
else
if (close(keyfile_fd) < 0)
err (1, "close %s", keyfile);
}
}
} while(try_again);
keyfile_mode = get_mode(keyfile);
if ((backup_keyfile_fd =
open(backup_keyfile, O_WRONLY | O_TRUNC | O_CREAT,
keyfile_mode)) < 0)
err (1, "write %s", backup_keyfile);
do {
if ((rcount = read(keyfile_fd, buf, sizeof(buf))) < 0)
err (1, "read %s", keyfile);
if (rcount && (write(backup_keyfile_fd, buf, rcount) != rcount))
err (1, "write %s", backup_keyfile);
} while (rcount);
if (close(backup_keyfile_fd) < 0)
err(1, "close %s", backup_keyfile);
if (close(keyfile_fd) < 0)
err(1, "close %s", keyfile);
}
void
leave(char *str, int x)
{
if (str)
fprintf(stderr, "%s\n", str);
if (destroyp)
dest_tkt();
exit(x);
}
void
safe_read_stdin(char *prompt, char *buf, size_t size)
{
printf("%s", prompt);
fflush(stdout);
memset(buf, 0, size);
if (read(0, buf, size - 1) < 0) {
warn("read stdin");
leave(NULL, 1);
}
buf[strlen(buf)-1] = 0;
}
void
safe_write(char *filename, int fd, void *buf, size_t len)
{
if (write(fd, buf, len) != len) {
warn("write %s", filename);
close(fd);
leave("In progress srvtab in this file.", 1);
}
}
static int
yes_no(char *string, int dflt)
{
char ynbuf[5];
printf("%s (y,n) [%c]", string, dflt?'y':'n');
for (;;) {
safe_read_stdin("", ynbuf, sizeof(ynbuf));
if ((ynbuf[0] == 'n') || (ynbuf[0] == 'N'))
return(0);
else if ((ynbuf[0] == 'y') || (ynbuf[0] == 'Y'))
return(1);
else if(ynbuf[0] == 0)
return dflt;
else {
printf("Please enter 'y' or 'n': ");
fflush(stdout);
}
}
}
int yn(char *string)
{
return yes_no(string, 1);
}
int ny(char *string)
{
return yes_no(string, 0);
}
static void
append_srvtab(char *filename, int fd, char *sname, char *sinst, char *srealm, unsigned char key_vno, unsigned char *key)
{
/* Add one to append null */
safe_write(filename, fd, sname, strlen(sname) + 1);
safe_write(filename, fd, sinst, strlen(sinst) + 1);
safe_write(filename, fd, srealm, strlen(srealm) + 1);
safe_write(filename, fd, &key_vno, 1);
safe_write(filename, fd, key, sizeof(des_cblock));
fsync(fd);
}
static void
print_key(unsigned char *key)
{
int i;
for (i = 0; i < 4; i++)
printf("%02x", key[i]);
printf(" ");
for (i = 4; i < 8; i++)
printf("%02x", key[i]);
}
static void
print_name(char *name, char *inst, char *realm)
{
printf("%s", krb_unparse_name_long(name, inst, realm));
}
static int
get_svc_new_key(des_cblock *new_key, char *sname, char *sinst,
char *srealm, char *keyfile)
{
int status = KADM_SUCCESS;
if (((status = krb_get_svc_in_tkt(sname, sinst, srealm, PWSERV_NAME,
KADM_SINST, 1, keyfile)) == KSUCCESS) &&
((status = kadm_init_link(PWSERV_NAME, KRB_MASTER, srealm)) ==
KADM_SUCCESS)) {
#ifdef NOENCRYPTION
memset(new_key, 0, sizeof(des_cblock));
(*new_key)[0] = (unsigned char) 1;
#else /* NOENCRYPTION */
des_new_random_key(new_key);
#endif /* NOENCRYPTION */
return(KADM_SUCCESS);
}
return(status);
}
static void
get_key_from_password(des_cblock (*key), char *cellname)
{
char password[MAX_KPW_LEN]; /* storage for the password */
if (read_long_pw_string(password, sizeof(password)-1, "Password: ", 1))
leave("Error reading password.", 1);
#ifdef NOENCRYPTION
memset(key, 0, sizeof(des_cblock));
(*key)[0] = (unsigned char) 1;
#else /* NOENCRYPTION */
if (strlen(cellname) == 0)
des_string_to_key(password, key);
else
afs_string_to_key(password, cellname, key);
#endif /* NOENCRYPTION */
memset(password, 0, sizeof(password));
}
static void
usage(void)
{
fprintf(stderr, "Usage: ksrvutil [-f keyfile] [-i] [-k] ");
fprintf(stderr, "[-p principal] [-r realm] ");
fprintf(stderr, "[-c AFS cellname] ");
fprintf(stderr, "{list | change | add | get}\n");
fprintf(stderr, " -i causes the program to ask for ");
fprintf(stderr, "confirmation before changing keys.\n");
fprintf(stderr, " -k causes the key to printed for list or ");
fprintf(stderr, "change.\n");
exit(1);
}
int
main(int argc, char **argv)
{
char sname[ANAME_SZ]; /* name of service */
char sinst[INST_SZ]; /* instance of service */
char srealm[REALM_SZ]; /* realm of service */
unsigned char key_vno; /* key version number */
int status; /* general purpose error status */
des_cblock new_key;
des_cblock old_key;
char change_tkt[MaxPathLen]; /* Ticket to use for key change */
char keyfile[MaxPathLen]; /* Original keyfile */
char work_keyfile[MaxPathLen]; /* Working copy of keyfile */
char backup_keyfile[MaxPathLen]; /* Backup copy of keyfile */
unsigned short keyfile_mode; /* Protections on keyfile */
int work_keyfile_fd = -1; /* Initialize so that */
int backup_keyfile_fd = -1; /* compiler doesn't complain */
char local_realm[REALM_SZ]; /* local kerberos realm */
char cellname[1024]; /* AFS cell name */
int c;
int interactive = FALSE;
int list = FALSE;
int change = FALSE;
int add = FALSE;
int get = FALSE;
int key = FALSE; /* do we show keys? */
int arg_entered = FALSE;
int change_this_key = FALSE;
char databuf[BUFSIZ];
int first_printed = FALSE; /* have we printed the first item? */
memset(sname, 0, sizeof(sname));
memset(sinst, 0, sizeof(sinst));
memset(srealm, 0, sizeof(srealm));
memset(change_tkt, 0, sizeof(change_tkt));
memset(keyfile, 0, sizeof(keyfile));
memset(work_keyfile, 0, sizeof(work_keyfile));
memset(backup_keyfile, 0, sizeof(backup_keyfile));
memset(local_realm, 0, sizeof(local_realm));
memset(cellname, 0, sizeof(cellname));
set_progname (argv[0]);
if (krb_get_default_principal(u_name, u_inst, u_realm) < 0)
errx (1, "could not get default principal");
/* This is used only as a default for adding keys */
if (krb_get_lrealm(local_realm, 1) != KSUCCESS)
strcpy(local_realm, KRB_REALM);
while((c = getopt(argc, argv, "ikc:f:p:r:")) != EOF) {
switch (c) {
case 'i':
interactive++;
break;
case 'k':
key++;
break;
case 'c':
strcpy(cellname, optarg);
break;
case 'f':
strcpy(keyfile, optarg);
break;
case 'p':
if((status = kname_parse (u_name, u_inst, u_realm, optarg)) !=
KSUCCESS)
errx (1, "principal %s: %s", optarg,
krb_get_err_text(status));
break;
case 'r':
strcpy(u_realm, optarg);
break;
case '?':
usage();
}
}
if (optind >= argc)
usage();
if (*u_realm == '\0')
strcpy (u_realm, local_realm);
if (strcmp(argv[optind], "list") == 0) {
if (arg_entered)
usage();
else {
arg_entered++;
list++;
}
}
else if (strcmp(argv[optind], "change") == 0) {
if (arg_entered)
usage();
else {
arg_entered++;
change++;
}
}
else if (strcmp(argv[optind], "add") == 0) {
if (arg_entered)
usage();
else {
arg_entered++;
add++;
}
}
else if (strcmp(argv[optind], "get") == 0) {
if (arg_entered)
usage();
else {
arg_entered++;
get++;
}
}
else
usage();
++optind;
if (!arg_entered)
usage();
if (!keyfile[0])
strcpy(keyfile, KEYFILE);
strcpy(work_keyfile, keyfile);
strcpy(backup_keyfile, keyfile);
if (change || add || get) {
strcat(work_keyfile, ".work");
strcat(backup_keyfile, ".old");
copy_keyfile(keyfile, backup_keyfile);
}
if (add || get)
copy_keyfile(backup_keyfile, work_keyfile);
keyfile_mode = get_mode(keyfile);
if (change || list)
if ((backup_keyfile_fd = open(backup_keyfile, O_RDONLY, 0)) < 0)
err (1, "open %s", backup_keyfile);
if (change) {
if ((work_keyfile_fd =
open(work_keyfile, O_WRONLY | O_CREAT | O_TRUNC,
SRVTAB_MODE)) < 0)
err (1, "creat %s", work_keyfile);
}
else if (add) {
if ((work_keyfile_fd =
open(work_keyfile, O_APPEND | O_WRONLY, SRVTAB_MODE)) < 0)
err (1, "open with append %s", work_keyfile );
}
else if (get) {
if ((work_keyfile_fd =
open(work_keyfile, O_RDWR | O_CREAT, SRVTAB_MODE)) < 0)
err (1, "open for writing %s", work_keyfile);
}
if (change || list) {
while ((getst(backup_keyfile_fd, sname, SNAME_SZ) > 0) &&
(getst(backup_keyfile_fd, sinst, INST_SZ) > 0) &&
(getst(backup_keyfile_fd, srealm, REALM_SZ) > 0) &&
(read(backup_keyfile_fd, &key_vno, 1) > 0) &&
(read(backup_keyfile_fd, old_key, sizeof(old_key)) > 0)) {
if (list) {
if (!first_printed) {
printf(VNO_HEADER);
printf(PAD);
if (key) {
printf(KEY_HEADER);
printf(PAD);
}
printf(PRINC_HEADER);
first_printed = 1;
}
printf(VNO_FORMAT, key_vno);
printf(PAD);
if (key) {
print_key(old_key);
printf(PAD);
}
print_name(sname, sinst, srealm);
printf("\n");
}
else if (change) {
snprintf(change_tkt, sizeof(change_tkt),
TKT_ROOT "_ksrvutil.%u",
(unsigned)getpid());
krb_set_tkt_string(change_tkt);
destroyp = TRUE;
printf("\nPrincipal: ");
print_name(sname, sinst, srealm);
printf("; version %d\n", key_vno);
if (interactive)
change_this_key = yn("Change this key?");
else if (change)
change_this_key = 1;
else
change_this_key = 0;
if (change_this_key)
printf("Changing to version %d.\n", key_vno + 1);
else if (change)
printf("Not changing this key.\n");
if (change_this_key) {
/*
* This is not a good choice of seed when/if the
* key has been compromised so we also use a
* random sequence number!
*/
des_init_random_number_generator(&old_key);
{
des_cblock seqnum;
des_generate_random_block(&seqnum);
des_set_sequence_number((unsigned char *)&seqnum);
}
/*
* Pick a new key and determine whether or not
* it is safe to change
*/
if ((status =
get_svc_new_key(&new_key, sname, sinst,
srealm, keyfile)) == KADM_SUCCESS)
key_vno++;
else {
memcpy(new_key, old_key, sizeof(new_key));
warnx ("Key NOT changed: %s\n",
krb_get_err_text(status));
change_this_key = FALSE;
}
}
else
memcpy(new_key, old_key, sizeof(new_key));
append_srvtab(work_keyfile, work_keyfile_fd,
sname, sinst, srealm, key_vno, new_key);
if (key && change_this_key) {
printf("Old key: ");
print_key(old_key);
printf("; new key: ");
print_key(new_key);
printf("\n");
}
if (change_this_key) {
if ((status = kadm_change_pw(new_key)) == KADM_SUCCESS) {
printf("Key changed.\n");
dest_tkt();
}
else {
com_err(__progname, status,
" attempting to change password.");
dest_tkt();
/* XXX This knows the format of a keyfile */
if (lseek(work_keyfile_fd, -9, SEEK_CUR) >= 0) {
key_vno--;
safe_write(work_keyfile,
work_keyfile_fd, &key_vno, 1);
safe_write(work_keyfile, work_keyfile_fd,
old_key, sizeof(des_cblock));
fsync(work_keyfile_fd);
fprintf(stderr,"Key NOT changed.\n");
} else {
warn ("Unable to revert keyfile");
leave("", 1);
}
}
}
}
memset(old_key, 0, sizeof(des_cblock));
memset(new_key, 0, sizeof(des_cblock));
}
}
else if (add) {
do {
do {
safe_read_stdin("Name: ", databuf, sizeof(databuf));
strncpy(sname, databuf, sizeof(sname) - 1);
if (strchr(sname, '.') != 0) {
strcpy(sinst, strchr(sname, '.') + 1);
*(strchr(sname, '.')) = 0;
} else {
safe_read_stdin("Instance: ", databuf, sizeof(databuf));
strncpy(sinst, databuf, sizeof(sinst) - 1);
}
safe_read_stdin("Realm: ", databuf, sizeof(databuf));
strncpy(srealm, databuf, sizeof(srealm) - 1);
safe_read_stdin("Version number: ", databuf, sizeof(databuf));
key_vno = atoi(databuf);
if (key_vno == 0)
key_vno = 1; /* Version numbers are never 0 */
if (!srealm[0])
strcpy(srealm, local_realm);
printf("New principal: ");
print_name(sname, sinst, srealm);
printf("; version %d\n", key_vno);
} while (!yn("Is this correct?"));
get_key_from_password(&new_key, cellname);
if (key) {
printf("Key: ");
print_key(new_key);
printf("\n");
}
append_srvtab(work_keyfile, work_keyfile_fd,
sname, sinst, srealm, key_vno, new_key);
printf("Key successfully added.\n");
} while (yn("Would you like to add another key?"));
}
else if (get) {
ksrvutil_get(work_keyfile_fd, work_keyfile,
argc - optind, argv + optind);
}
if (change || list)
if (close(backup_keyfile_fd) < 0)
warn ("close %s", backup_keyfile);
if (change || add || get) {
if (close(work_keyfile_fd) < 0)
err (1, "close %s", work_keyfile);
if (rename(work_keyfile, keyfile) < 0)
err (1, "rename(%s, %s)", work_keyfile, keyfile);
chmod(backup_keyfile, keyfile_mode);
chmod(keyfile, keyfile_mode);
printf("Old keyfile in %s.\n", backup_keyfile);
}
return 0;
}