639 lines
17 KiB
C
639 lines
17 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.50 1999/11/13 06:33:59 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, "open %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, "open %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] [-u]");
|
|
fprintf(stderr, "[-c AFS cellname] ");
|
|
fprintf(stderr, "{list | change | add | get | delete}\n");
|
|
fprintf(stderr, " -i causes the program to ask for "
|
|
"confirmation before changing keys.\n");
|
|
fprintf(stderr, " -k causes the key to printed for list or change.\n");
|
|
fprintf(stderr, " -u creates one keyfile for each principal "
|
|
"(only used with `get')\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 unique_filename = FALSE;
|
|
int add = FALSE;
|
|
int delete = 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)
|
|
strlcpy(local_realm,
|
|
KRB_REALM,
|
|
sizeof(local_realm));
|
|
|
|
while((c = getopt(argc, argv, "ikc:f:p:r:u")) != -1) {
|
|
switch (c) {
|
|
case 'i':
|
|
interactive++;
|
|
break;
|
|
case 'k':
|
|
key++;
|
|
break;
|
|
case 'c':
|
|
strlcpy(cellname, optarg, sizeof(cellname));
|
|
break;
|
|
case 'f':
|
|
strlcpy(keyfile, optarg, sizeof(keyfile));
|
|
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':
|
|
strlcpy(u_realm, optarg, sizeof(u_realm));
|
|
break;
|
|
case 'u':
|
|
unique_filename = 1;
|
|
break;
|
|
case '?':
|
|
usage();
|
|
}
|
|
}
|
|
if (optind >= argc)
|
|
usage();
|
|
if (*u_realm == '\0')
|
|
strlcpy (u_realm, local_realm, sizeof(u_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 if (strcmp(argv[optind], "delete") == 0) {
|
|
if (arg_entered)
|
|
usage();
|
|
else {
|
|
arg_entered++;
|
|
delete++;
|
|
}
|
|
}
|
|
else
|
|
usage();
|
|
++optind;
|
|
|
|
if (!arg_entered)
|
|
usage();
|
|
|
|
if(unique_filename && !get)
|
|
warnx("`-u' flag is only used with `get'");
|
|
|
|
if (!keyfile[0])
|
|
strlcpy(keyfile, KEYFILE, sizeof(keyfile));
|
|
|
|
strlcpy(work_keyfile, keyfile, sizeof(work_keyfile));
|
|
strlcpy(backup_keyfile, keyfile, sizeof(backup_keyfile));
|
|
|
|
if (change || add || (get && !unique_filename) || delete) {
|
|
snprintf(work_keyfile, sizeof(work_keyfile), "%s.work", keyfile);
|
|
snprintf(backup_keyfile, sizeof(backup_keyfile), "%s.old", keyfile);
|
|
copy_keyfile(keyfile, backup_keyfile);
|
|
}
|
|
|
|
if (add || (get && !unique_filename))
|
|
copy_keyfile(backup_keyfile, work_keyfile);
|
|
|
|
keyfile_mode = get_mode(keyfile);
|
|
|
|
if (change || list || delete)
|
|
if ((backup_keyfile_fd = open(backup_keyfile, O_RDONLY, 0)) < 0)
|
|
err (1, "open %s", backup_keyfile);
|
|
|
|
if (change || delete) {
|
|
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 && !unique_filename) {
|
|
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 || delete) {
|
|
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), "%s_ksrvutil.%u",
|
|
TKT_ROOT, (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
|
|
change_this_key = 1;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
} else if(delete) {
|
|
int delete_this_key;
|
|
printf("\nPrincipal: ");
|
|
print_name(sname, sinst, srealm);
|
|
printf("; version %d\n", key_vno);
|
|
delete_this_key = yn("Delete this key?");
|
|
|
|
if (delete_this_key)
|
|
printf("Deleting this key.\n");
|
|
|
|
if (!delete_this_key) {
|
|
append_srvtab(work_keyfile, work_keyfile_fd,
|
|
sname, sinst, srealm, key_vno, old_key);
|
|
}
|
|
}
|
|
memset(old_key, 0, sizeof(des_cblock));
|
|
memset(new_key, 0, sizeof(des_cblock));
|
|
}
|
|
}
|
|
else if (add) {
|
|
do {
|
|
do {
|
|
char *p;
|
|
|
|
safe_read_stdin("Name: ", databuf, sizeof(databuf));
|
|
p = strchr(databuf, '.');
|
|
if (p != NULL) {
|
|
*p++ = '\0';
|
|
strlcpy (sname, databuf, sizeof(sname));
|
|
strlcpy (sinst, p, sizeof(sinst));
|
|
} else {
|
|
strlcpy (sname, databuf, sizeof(sname));
|
|
safe_read_stdin("Instance: ", databuf, sizeof(databuf));
|
|
strlcpy (sinst, databuf, sizeof(databuf));
|
|
}
|
|
|
|
safe_read_stdin("Realm: ", databuf, sizeof(databuf));
|
|
if (databuf[0] != '\0')
|
|
strlcpy (srealm, databuf, sizeof(srealm));
|
|
else
|
|
strlcpy (srealm, local_realm, sizeof(srealm));
|
|
|
|
safe_read_stdin("Version number: ", databuf, sizeof(databuf));
|
|
key_vno = atoi(databuf);
|
|
if (!srealm[0])
|
|
strlcpy(srealm, local_realm, sizeof(srealm));
|
|
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(unique_filename, work_keyfile_fd, work_keyfile,
|
|
argc - optind, argv + optind);
|
|
}
|
|
|
|
if (change || list || delete)
|
|
if (close(backup_keyfile_fd) < 0)
|
|
warn ("close %s", backup_keyfile);
|
|
|
|
if (change || add || (get && !unique_filename) || delete) {
|
|
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;
|
|
}
|