/* $OpenBSD: ksrvutil.c,v 1.5 1997/12/17 07:50:16 art Exp $ */ /* $KTH: ksrvutil.c,v 1.39 1997/05/02 14:28:52 assar Exp $ */ /* * 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" #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]; char progname[] = "ksrvutil"; 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)); 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) { strncpy(local_realm, KRB_REALM, REALM_SZ - 1); local_realm[REALM_SZ - 1] = '\0'; } 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); #if 0 /* kth-krb has own addidtions to libdes */ { des_cblock seqnum; des_generate_random_block(&seqnum); des_set_sequence_number((unsigned char *)&seqnum); } #endif /* * 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); sname[sizeof(sname) - 1] = '\0'; if (strchr(sname, '.') != 0) { strncpy(sinst, strchr(sname, '.') + 1, sizeof(sinst) - 1); sinst[sizeof(sinst) - 1] = '\0'; *(strchr(sname, '.')) = 0; } else { safe_read_stdin("Instance: ", databuf, sizeof(databuf)); strncpy(sinst, databuf, sizeof(sinst) - 1); sinst[sizeof(sinst) - 1] = '\0'; } safe_read_stdin("Realm: ", databuf, sizeof(databuf)); strncpy(srealm, databuf, sizeof(srealm) - 1); srealm[sizeof(srealm) - 1] = '\0'; 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]) { strncpy(srealm, local_realm, sizeof(srealm) - 1); srealm[sizeof(srealm) - 1] = '\0'; } 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; }