From: Theodore Tso Date: Wed, 27 Feb 1991 11:25:07 +0000 (+0000) Subject: *** empty log message *** X-Git-Tag: krb5-1.0-alpha4~177 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=2eac11b1e5eb39bfdd6fe186b882928ad1a93dcf;p=krb5.git *** empty log message *** git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@1795 dc483132-0cff-0310-8789-dd5450dbe970 --- diff --git a/src/slave/Imakefile b/src/slave/Imakefile new file mode 100644 index 000000000..ec4b5e0a4 --- /dev/null +++ b/src/slave/Imakefile @@ -0,0 +1,31 @@ +# $Source$ +# $Author$ +# $Id$ +# +# Copyright 1990 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, please see the file +# . +# + + DEPLIBS = $(DEPKLIB) +LOCAL_LIBRARIES = $(KLIB) + +CLIENTSRCS= sendauth.c kprop.c +CLIENTOBJS= sendauth.o kprop.o + +SERVERSRCS= sendauth.c kpropd.c +SERVEROBJS= sendauth.o kpropd.o + +all:: kprop kpropd + +NormalProgramTarget(kprop,$(CLIENTOBJS),$(DEPLIBS),$(LOCAL_LIBRARIES),) +NormalProgramTarget(kpropd,$(SERVEROBJS),$(DEPLIBS),$(LOCAL_LIBRARIES),) +SaberProgramTarget(kprop, $(CLIENTSRCS), $(CLIENTOBJS), + $(DEPLIBS) $(LOCAL_LIBRARIES),) +SaberProgramTarget(kpropd, $(SERVERSRCS), $(SERVEROBJS), + $(DEPLIBS) $(LOCAL_LIBRARIES),) + +saber-setup: + #setopt load_flags $(CFLAGS) + #load $(LOCAL_LIBRARIES) diff --git a/src/slave/kprop.c b/src/slave/kprop.c new file mode 100644 index 000000000..98fc7e79e --- /dev/null +++ b/src/slave/kprop.c @@ -0,0 +1,654 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1990 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + */ + +#if !defined(lint) && !defined(SABER) +static char rcsid_kprop_c[] = +"$Id$"; +#endif /* !lint && !SABER */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kprop.h" + +static char *kprop_version = KPROP_PROT_VERSION; + +char *progname = 0; +int debug = 0; +char *slave_host; +char *realm = 0; +char *file = KPROP_DEFAULT_FILE; + +krb5_principal my_principal; /* The Kerberos principal we'll be */ + /* running under, initialized in */ + /* get_tickets() */ +krb5_ccache ccache; /* Credentials cache which we'll be using */ +krb5_creds my_creds; /* My credentials */ +int my_seq_num; /* Sequence number to use for connection */ +int his_seq_num; /* Remote sequence number */ +krb5_address sender_addr; +krb5_address receiver_addr; + +void PRS(); +void get_tickets(); +void usage(); +krb5_error_code open_connection(); +void kerberos_authenticate(); +int open_database(); +void xmit_database(); +void send_error(); +void update_last_prop_file(); + +static void usage() +{ + fprintf(stderr, "\nUsage: %s [-r realm] [-f file] [-d] slave_host\n\n", + progname); + exit(1); +} + +void +main(argc, argv) + int argc; + char **argv; +{ + int fd, database_fd, database_size; + krb5_error_code retval; + char Errmsg[256]; + + PRS(argv); + get_tickets(); + + database_fd = open_database(file, &database_size); + if (retval = open_connection(slave_host, &fd, Errmsg)) { + com_err(progname, retval, "%s while opening connection to %s", + Errmsg, slave_host); + exit(1); + } + if (fd < 0) { + fprintf(stderr, "%s: %s while opening connection to %s\n", + progname, Errmsg, slave_host); + exit(1); + } + kerberos_authenticate(fd, my_principal); + if (debug) { + printf("My sequence number: %d\n", my_seq_num); + printf("His sequence number: %d\n", his_seq_num); + } + xmit_database(fd, database_fd, database_size); + update_last_prop_file(slave_host, file); + printf("Database propagation to %s: SUCCEEDED\n", slave_host); + exit(0); +} + +void PRS(argv) + char **argv; +{ + register char *word, ch; + + krb5_init_ets(); + progname = *argv++; + while (word = *argv++) { + if (*word == '-') { + word++; + while (ch = *word++) { + switch(ch){ + case 'r': + if (*word) + realm = word; + else + realm = *argv++; + if (!realm) + usage(); + word = 0; + break; + case 'f': + if (*word) + file = word; + else + file = *argv++; + if (!file) + usage(); + word = 0; + break; + case 'd': + debug++; + break; + default: + usage(); + } + + } + } else { + if (slave_host) + usage(); + else + slave_host = word; + } + } + if (!slave_host) + usage(); +} + +void get_tickets() +{ + char my_host_name[MAXHOSTNAMELEN]; + char buf[BUFSIZ]; + char *cp, *strcpy(), *krb_get_phost(); + struct hostent *hp; + krb5_address **my_addresses; + krb5_error_code retval; + static char tkstring[] = "/tmp/kproptktXXXXXX"; + + /* + * Figure out what tickets we'll be using to send stuff + */ + if (gethostname (my_host_name, sizeof(my_host_name)) != 0) { + com_err(progname, errno, "while getting my hostname"); + exit(1); + } + /* get canonicalized service instance name */ + if (!(hp = gethostbyname(my_host_name))) { + fprintf(stderr, "Couldn't get my cannonicalized host name!\n"); + exit(1); + } + for (cp=hp->h_name; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + if (realm) + sprintf(buf, "rcmd/%s@%s", hp->h_name, realm); + else + sprintf(buf, "rcmd/%s", hp->h_name); + if (retval = krb5_parse_name(buf, &my_principal)) { + com_err (progname, retval, "when parsing name %s",buf); + exit(1); + } + + /* + * Initialize cache file which we're going to be using + */ + (void) mktemp(tkstring); + sprintf(buf, "FILE:%s", tkstring); + if (retval = krb5_cc_resolve(buf, &ccache)) { + com_err(progname, retval, "while opening crednetials cache %s", + buf); + exit(1); + } + if (retval = krb5_cc_initialize(ccache, my_principal)) { + com_err (progname, retval, "when initializing cache %s", + buf); + exit(1); + } + + /* + * Get the tickets we'll need. + * + * Construct the principal name for the slave host. + */ + memset((char *)&my_creds, 0, sizeof(my_creds)); + if (!(hp = gethostbyname(slave_host))) { + fprintf(stderr, + "Couldn't get cannonicalized name for slave\n"); + exit(1); + } + for (cp=hp->h_name; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + if (!(slave_host = malloc(strlen(hp->h_name) + 1))) { + com_err(progname, ENOMEM, + "while allocate space for canonicalized slave host"); + exit(1); + } + strcpy(slave_host, hp->h_name); + if (realm) + sprintf(buf, "%s/%s@%s", KPROP_SERVICE_NAME, slave_host, + realm); + else + sprintf(buf, "%s/%s", KPROP_SERVICE_NAME, hp->h_name); + if (retval = krb5_parse_name(buf, &my_creds.server)) { + com_err(progname, retval, + "while parsing slave principal name"); + exit(1); + } + /* + * Now fill in the client.... + */ + if (retval = krb5_copy_principal(my_principal, &my_creds.client)) { + com_err(progname, retval, "While copying client principal"); + exit(1); + } + /* + * Get my addresses + */ + retval = krb5_os_localaddr(&my_addresses); + if (retval != 0) { + com_err(progname, retval, + "when getting my address"); + exit(1); + } + retval = krb5_get_in_tkt_with_skey(0, my_addresses, + ETYPE_DES_CBC_CRC, + 0, ccache, &my_creds); + if (retval) { + com_err(progname, retval, "While getting initial ticket\n"); + exit(1); + } + /* + * Now destroy the cache right away --- the credentials we + * need will be in my_creds. + */ + if (retval = krb5_cc_destroy(ccache)) { + com_err(progname, retval, "while destroying ticket cache"); + exit(1); + } +} + +krb5_error_code +open_connection(host, fd, Errmsg) + char *host; + int *fd; + char *Errmsg; +{ + int s; + krb5_error_code retval; + + struct hostent *hp; + register struct servent *sp; + struct sockaddr_in sin; + int socket_length; + + hp = gethostbyname(host); + if (hp == NULL) { + (void) sprintf(Errmsg, "%s: unknown host", host); + *fd = -1; + return(0); + } + sp = getservbyname(KPROP_SERVICE, "tcp"); + if (sp == 0) { + (void) strcpy(Errmsg, KPROP_SERVICE); + (void) strcat(Errmsg, "/tcp: unknown service"); + *fd = -1; + return(0); + } + sin.sin_family = hp->h_addrtype; + bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length); + sin.sin_port = sp->s_port; + s = socket(AF_INET, SOCK_STREAM, 0); + + if (s < 0) { + (void) sprintf(Errmsg, "in call to socket"); + return(errno); + } + if (connect(s, (struct sockaddr *)&sin, sizeof sin) < 0) { + retval = errno; + close(s); + (void) sprintf(Errmsg, "in call to connect"); + return(retval); + } + *fd = s; + + /* + * Set receiver_addr and sender_addr. + */ + receiver_addr.addrtype = ADDRTYPE_INET; + receiver_addr.length = sizeof(sin.sin_addr); + receiver_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr)); + memcpy((char *) receiver_addr.contents, (char *) &sin.sin_addr, + sizeof(sin.sin_addr)); + + socket_length = sizeof(sin); + if (getpeername(s, (struct sockaddr *)&sin, &socket_length) < 0) { + retval = errno; + close(s); + (void) sprintf(Errmsg, "in call to getpeername"); + return(retval); + } + sender_addr.addrtype = ADDRTYPE_INET; + sender_addr.length = sizeof(sin.sin_addr); + sender_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr)); + memcpy((char *) sender_addr.contents, (char *) &sin.sin_addr, + sizeof(sin.sin_addr)); + + return(0); +} + + +void kerberos_authenticate(fd, me) + int fd; + krb5_principal me; +{ + krb5_error_code retval; + krb5_error *error = NULL; + krb5_ap_rep_enc_part *rep_result; + struct timeval mytime; + + /* + * Generate a random key to use as a sequence number + */ + gettimeofday(&mytime, NULL); + srandom(mytime.tv_usec ^ mytime.tv_sec ^ (9 * getpid())); + my_seq_num = random(); + + if (retval = krb5_sendauth(fd, kprop_version, me, my_creds.server, + AP_OPTS_MUTUAL_REQUIRED, my_seq_num, NULL, + NULL, &my_creds, NULL, &error, + &rep_result)) { + com_err(progname, retval, "while authenticating to server"); + if (error) { + if (error->error == KRB_ERR_GENERIC) { + if (error->text.data) + fprintf(stderr, + "Generic remote error: %s\n", + error->text.data); + } else if (error->error) { + com_err(progname, + error->error + ERROR_TABLE_BASE_krb5, + "signalled from server"); + if (error->text.data) + fprintf(stderr, + "Error text from server: %s\n", + error->text.data); + } + krb5_free_error(error); + } + exit(1); + } + his_seq_num = rep_result->seq_number; + krb5_free_ap_rep_enc_part(repl); +} + +/* + * Open the Kerberos database dump file. Takes care of locking it + * and making sure that the .ok file is more recent that the database + * dump file itself. + * + * Returns the file descriptor of the database dump file. Also fills + * in the size of the database file. + */ +int +open_database(data_fn, size) + char *data_fn; + int *size; +{ + int fd; + struct stat stbuf, stbuf_ok; + char *data_ok_fn; + static char ok[] = ".dump_ok"; + + if ((fd = open(data_fn, O_RDONLY)) < 0) { + com_err(progname, 0, "While trying to open %s", + data_fn); + exit(1); + } + if (flock(fd, LOCK_SH | LOCK_NB)) { + com_err(progname, errno, "while trying to flock %s", + data_fn); + exit(1); + } + if (stat(data_fn, &stbuf)) { + com_err(progname, errno, "while trying to stat %s", + data_fn); + exit(1); + } + if ((data_ok_fn = (char *) malloc(strlen(data_fn)+strlen(ok)+1)) + == NULL) { + com_err(progname, ENOMEM, "while trying to malloc data_ok_fn"); + exit(1); + } + strcat(strcpy(data_ok_fn, data_fn), ok); + if (stat(data_ok_fn, &stbuf_ok)) { + com_err(progname, errno, "while trying to stat %s", + data_ok_fn); + free(data_ok_fn); + exit(1); + } + free(data_ok_fn); + if (stbuf.st_mtime > stbuf_ok.st_mtime) { + com_err(progname, 0, "'%s' more recent than '%s'.", + data_fn, data_ok_fn); + exit(1); + } + *size = stbuf.st_size; + return(fd); +} + +/* + * Now we send over the database. We use the following protocol: + * Send over a KRB_SAFE message with the size. Then we send over the + * database in blocks of KPROP_BLKSIZE, encrypted using KRB_PRIV. + * Then we expect to see a KRB_SAFE message with the size sent back. + * + * At any point in the protocol, we may send a KRB_ERROR message; this + * will abort the entire operation. + */ +void +xmit_database(fd, database_fd, database_size) + int fd; + int database_fd; + int database_size; +{ + int send_size, sent_size, n, eblock_size; + krb5_data inbuf, outbuf; + char buf[KPROP_BUFSIZ]; + char *i_vector; + krb5_error_code retval; + krb5_error *error; + + /* + * Send over the size + */ + send_size = htonl(database_size); + inbuf.data = (char *) &send_size; + inbuf.length = sizeof(send_size); /* must be 4, really */ + if (retval = krb5_mk_safe(&inbuf, KPROP_CKSUMTYPE, + &my_creds.keyblock, + &sender_addr, &receiver_addr, + my_seq_num++, KRB5_PRIV_DOSEQUENCE, + &outbuf)) { + com_err(progname, retval, "while encoding database size"); + send_error(fd, "while encoding database size", retval); + exit(1); + } + if (retval = krb5_write_message(fd, &outbuf)) { + xfree(outbuf.data); + com_err(progname, retval, "while sending database size"); + exit(1); + } + xfree(outbuf.data); + /* + * Initialize the initial vector. + */ + eblock_size = krb5_keytype_array[my_creds.keyblock.keytype]-> + system->block_length; + if (!(i_vector=malloc(eblock_size))) { + com_err(progname, ENOMEM, "while allocating i_vector"); + send_error(fd, "malloc failed while allocating i_vector", + ENOMEM); + exit(1); + } + memset(i_vector, 0, eblock_size); + /* + * Send over the file, block by block.... + */ + inbuf.data = buf; + sent_size = 0; + while (n = read(database_fd, buf, sizeof(buf))) { + inbuf.length = n; + if (retval = krb5_mk_priv(&inbuf, ETYPE_DES_CBC_CRC, + &my_creds.keyblock, + &sender_addr, + &receiver_addr, + my_seq_num++, + KRB5_PRIV_DOSEQUENCE, + i_vector, + &outbuf)) { + sprintf(buf, + "while encoding database block starting at %d", + sent_size); + com_err(progname, retval, buf); + send_error(fd, buf, retval); + exit(1); + } + if (retval = krb5_write_message(fd, &outbuf)) { + xfree(outbuf.data); + com_err(progname, retval, + "while sending database block starting at %d", + sent_size); + exit(1); + } + xfree(outbuf.data); + sent_size += n; + if (debug) + printf("%d bytes sent.\n", sent_size); + } + if (sent_size != database_size) { + com_err(progname, 0, "Premature EOF found for database file!"); + send_error(fd, "Premature EOF found for database file!", + KRB5KRB_ERR_GENERIC); + exit(1); + } + /* + * OK, we've sent the database; now let's wait for a success + * indication from the remote end. + */ + if (retval = krb5_read_message(fd, &inbuf)) { + com_err(progname, retval, + "while reading response from server"); + exit(1); + } + /* + * If we got an error response back from the server, display + * the error message + */ + if (krb5_is_krb_error(&inbuf)) { + if (retval = krb5_rd_error(&inbuf, &error)) { + com_err(progname, retval, + "while decoding error response from server"); + exit(1); + } + if (error->error == KRB_ERR_GENERIC) { + if (error->text.data) + fprintf(stderr, + "Generic remote error: %s\n", + error->text.data); + } else if (error->error) { + com_err(progname, error->error + ERROR_TABLE_BASE_krb5, + "signalled from server"); + if (error->text.data) + fprintf(stderr, + "Error text from server: %s\n", + error->text.data); + } + krb5_free_error(error); + exit(1); + } + if (retval = krb5_rd_safe(&inbuf, &my_creds.keyblock, &receiver_addr, + &sender_addr, his_seq_num++, + KRB5_SAFE_DOSEQUENCE, 0, &outbuf)) { + com_err(progname, retval, + "while decoding final size packet from server"); + exit(1); + } + memcpy((char *)&send_size, outbuf.data, sizeof(send_size)); + send_size = ntohl(send_size); + if (send_size != database_size) { + com_err(progname, 0, + "Kpropd sent database size %d, expecting %d", + send_size, database_size); + exit(1); + } + free(outbuf.data); + free(inbuf.data); +} + +void +send_error(fd, err_text, err_code) + int fd; + char *err_text; + krb5_error_code err_code; +{ + krb5_error error; + const char *text; + krb5_data outbuf; + + memset((char *)&error, 0, sizeof(error)); + krb5_us_timeofday(&error.ctime, &error.cusec); + error.server = my_creds.server; + error.client = my_principal; + error.error = err_code - ERROR_TABLE_BASE_krb5; + if (error.error < 0 || error.error > 127) + error.error = KRB_ERR_GENERIC; + if (err_text) + text = err_text; + else + text = error_message(err_code); + error.text.length = strlen(text) + 1; + if (error.text.data = malloc(error.text.length)) { + strcpy(error.text.data, text); + if (!krb5_mk_error(&error, &outbuf)) { + (void) krb5_write_message(fd, &outbuf); + xfree(outbuf.data); + } + free(error.text.data); + } +} + +void update_last_prop_file(hostname, file_name) + char *hostname; + char *file_name; +{ + /* handle slave locking/failure stuff */ + char *file_last_prop; + int fd; + static char last_prop[]=".last_prop"; + + if ((file_last_prop = (char *)malloc(strlen(file_name) + + strlen(hostname) + 1 + + strlen(last_prop) + 1)) == NULL) { + com_err(progname, ENOMEM, + "while allocating filename for update_last_prop_file"); + return; + } + strcpy(file_last_prop, file_name); + strcat(file_last_prop, "."); + strcat(file_last_prop, hostname); + strcat(file_last_prop, last_prop); + if ((fd = open(file_last_prop, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { + com_err(progname, errno, + "while creating 'last_prop' file, '%s'", + file_last_prop); + free(file_last_prop); + return; + } + free(file_last_prop); + close(fd); + return; +} diff --git a/src/slave/kprop.h b/src/slave/kprop.h new file mode 100644 index 000000000..23d1777d7 --- /dev/null +++ b/src/slave/kprop.h @@ -0,0 +1,24 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1990 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + */ + +#define KPROP_SERVICE_NAME "rcmd" +#define KPROP_SRVTAB "/etc/srvtab" +#define TGT_SERVICE_NAME "krbtgt" +#define KPROP_SERVICE "krb5_prop" +#define KPROP_DEFAULT_FILE "/krb5/slave_datatrans" +#define KPROPD_DEFAULT_FILE "/krb5/from_master" +#define KPROP_CKSUMTYPE CKSUMTYPE_RSA_MD4_DES +#define KPROPD_DEFAULT_KDB5_EDIT "/krb5/bin/kdb5_edit" +#define KPROPD_DEFAULT_KRB_DB "/krb5/principal" + +#define KPROP_PROT_VERSION "kprop5_01" + +#define KPROP_BUFSIZ 32768 diff --git a/src/slave/kpropd.c b/src/slave/kpropd.c new file mode 100644 index 000000000..9dff84dc4 --- /dev/null +++ b/src/slave/kpropd.c @@ -0,0 +1,813 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1990 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * . + * + */ + +#if !defined(lint) && !defined(SABER) +static char rcsid_kpropd_c[] = +"$Id$"; +#endif /* !lint && !SABER */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kprop.h" + +#define SYSLOG_CLASS LOG_DAEMON + +static char *kprop_version = KPROP_PROT_VERSION; + +char *progname; +int debug = 0; +char *srvtab = 0; +int standalone; + +krb5_principal server; /* This is our server principal name */ +krb5_principal client; /* This is who we're talking to */ +krb5_keyblock *session_key; /* Here is the session key */ +krb5_address **server_addrs; +krb5_pointer kerb_keytab = 0; /* Use default */ +char *realm = NULL; /* Our realm */ +char *file = KPROPD_DEFAULT_FILE; +char *temp_file_name; +char *kdb5_edit = KPROPD_DEFAULT_KDB5_EDIT; +char *kerb_database = KPROPD_DEFAULT_KRB_DB; + +int database_fd; +int my_seq_num; /* Sequence number */ +int his_seq_num; /* The remote's sequence number */ +krb5_address sender_addr; +krb5_address receiver_addr; + +void PRS(); +void do_standalone(); +void doit(); +void detach_process(); +void kerberos_authenticate(); +krb5_boolean authorized_principal(); +void recv_database(); +void load_database(); +void send_error(); +void recv_error(); + +static void usage() +{ + fprintf(stderr, + "\nUsage: %s [-r realm] [-s srvtab] [-dS] [-f slave_file]\n"); + fprintf(stderr, "\t[-F kerberos_db_file ] [-p kdb5_edit_pathname]\n\n", + progname); + exit(1); +} + +void +main(argc, argv) + int argc; + char **argv; +{ + PRS(argv); + + if (standalone) + do_standalone(); + else + doit(0); + exit(0); +} + +void do_standalone() +{ + struct sockaddr_in sin, frominet; + struct servent *sp; + int finet, fromlen, s; + + finet = socket(AF_INET, SOCK_STREAM, 0); + if (finet < 0) { + com_err(progname, errno, "while obtaining socket"); + exit(1); + } + sp = getservbyname(KPROP_SERVICE, "tcp"); + if (sp == NULL) { + com_err(progname, 0, "%s/tcp: unknown service", KPROP_SERVICE); + exit(1); + } + memset((char *) &sin,0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = sp->s_port; + if (bind(finet, (struct sockaddr *) &sin, sizeof(sin)) < 0) { + perror("bind"); + com_err(progname, errno, "while binding listener socker"); + exit(1); + } + if (!debug) + detach_process(); +#ifdef PID_FILE + if ((pidfile = fopen(PID_FILE, "w")) != NULL) { + fprintf(pidfile, "%d\n", getpid()); + fclose(pidfile); + } else + com_err(progname, errno, + "while opening pid file %s for writing", PID_FILE); +#endif + if (listen(finet, 5) < 0) { + com_err(progname, errno, "in listen call"); + exit(1); + } + while (1) { + memset((char *)&frominet, 0, sizeof(frominet)); + fromlen = sizeof(frominet); + s = accept(finet, (struct sockaddr *) &frominet, &fromlen); + + if (s < 0) { + if (errno != EINTR) + com_err(progname, errno, + "from accept system call"); + continue; + } + if (debug || fork() == 0) { + (void) signal(SIGCHLD, SIG_IGN); + (void) close(finet); + + doit(s); + close(s); + exit(0); + } + close(s); + } +} + +void doit(fd) + int fd; +{ + struct sockaddr_in from; + int on = 1, fromlen; + struct hostent *hp; + krb5_error_code retval; + struct timeval my_time; + int lock_fd; + + fromlen = sizeof (from); + if (getpeername(fd, (struct sockaddr *) &from, &fromlen) < 0) { + fprintf(stderr, "%s: ", progname); + perror("getpeername"); + exit(1); + } + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (caddr_t) &on, + sizeof (on)) < 0) { + com_err(progname, errno, + "while attempting setsockopt (SO_KEEPALIVE)"); + } + + if (!(hp = gethostbyaddr((char *) &(from.sin_addr.s_addr), fromlen, + AF_INET))) { + syslog(LOG_INFO, "Connection from %s", + inet_ntoa(from.sin_addr)); + if (debug) + printf("Connection from %s\n", + inet_ntoa(from.sin_addr)); + } else { + syslog(LOG_INFO, "Connection from %s", hp->h_name); + if (debug) + printf("Connection from %s\n", hp->h_name); + } + /* + * Create a random number for my sequence number. + */ + gettimeofday(&my_time, NULL); + srandom(my_time.tv_usec ^ my_time.tv_sec ^ (9 * getpid())); + my_seq_num = random(); + /* + * Now do the authentication + */ + kerberos_authenticate(fd, &client, from); + if (!authorized_principal(client)) { + char *name; + + if (retval = krb5_unparse_name(client, &name)) { + com_err(progname, retval, + "While unparsing client name"); + exit(1); + } + syslog(LOG_WARNING, + "Rejected connection from unauthorized principal %s", + name); + free(name); + } + if (debug) { + printf("My sequence number: %d\n", my_seq_num); + printf("His sequence number: %d\n", his_seq_num); + } + if ((lock_fd = (open(temp_file_name, O_WRONLY | O_CREAT, 0600))) < 0) { + com_err(progname, errno, + "while opening database file, '%s'", + temp_file_name); + exit(1); + } +#ifdef POSIX_FILE_LOCKS + { + int lock_cmd = F_SETLK; + struct flock lock_arg; + + lock_arg.l_type = F_WRLCK; + lock_arg.l_whence = 0; + lock_arg.l_start = 0; + lock_arg.l_len = 0; + + if (fcntl(lock_fd, lock_cmd, &lock_arg) == -1) { + /* see POSIX/IEEE 1003.1-1988, 6.5.2.4 */ + if (errno == EACCES || errno == EAGAIN) + errno = EAGAIN; + com_err(progname, errno, "while trying to lock '%s'", + temp_file_name); + } + } +#else + if (flock(lock_fd, LOCK_EX | LOCK_NB)) { + com_err(progname, errno, "while trying to lock '%s'", + temp_file_name); + exit(1); + } +#endif + if ((database_fd = open(temp_file_name, + O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { + com_err(progname, errno, + "while opening database file, '%s'", + temp_file_name); + exit(1); + } + recv_database(fd, database_fd); + if (close(fd) < 0) { + com_err(progname, errno, + "while trying to close database file"); + exit(1); + } + if (rename(temp_file_name, file)) { + com_err(progname, errno, "While renaming %s to %s", + temp_file_name, file); + exit(1); + } + load_database(kdb5_edit, file); + close(lock_fd); + exit(0); +} + +static void +kpropd_com_err_proc(whoami, code, fmt, args) + const char *whoami; + long code; + const char *fmt; + va_list args; +{ + char error_buf[8096]; + + error_buf[0] = '\0'; + if (fmt) + vsprintf(error_buf, fmt, args); + syslog(LOG_ERR, "%s%s%s%s%s", whoami ? whoami : "", whoami ? ": " : "", + code ? error_message(code) : "", code ? " " : "", error_buf); +} + +void PRS(argv) + char **argv; +{ + register char *word, ch; + char *cp; + struct hostent *hp; + char my_host_name[MAXHOSTNAMELEN], buf[BUFSIZ]; + krb5_error_code retval; + static const char tmp[] = ".temp"; + + krb5_init_ets(); + + progname = *argv++; + while (word = *argv++) { + if (*word == '-') { + word++; + while (ch = *word++) { + switch(ch){ + case 'f': + if (*word) + file = word; + else + file = *argv++; + if (!file) + usage(); + word = 0; + break; + case 'F': + if (*word) + kerb_database = word; + else + kerb_database = *argv++; + if (!kerb_database) + usage(); + word = 0; + break; + case 'p': + if (*word) + kdb5_edit = word; + else + kdb5_edit = *argv++; + if (!kdb5_edit) + usage(); + word = 0; + break; + case 'r': + if (*word) + realm = word; + else + realm = *argv++; + if (!realm) + usage(); + word = 0; + break; + case 's': + if (*word) + srvtab = word; + else + srvtab = *argv++; + if (!srvtab) + usage(); + word = 0; + break; + case 'd': + debug++; + break; + case 'S': + standalone++; + break; + default: + usage(); + } + + } + } else + /* We don't take any arguments, only options */ + usage(); + } + /* + * If not in debug mode, switch com_err reporting to syslog + */ + openlog("kpropd", LOG_PID | LOG_ODELAY, SYSLOG_CLASS); + set_com_err_hook(kpropd_com_err_proc); + /* + * Get my hostname, so we can construct my service name + */ + if (gethostname (my_host_name, sizeof(my_host_name)) != 0) { + com_err(progname, errno, "while getting my hostname"); + exit(1); + } + if (!(hp = gethostbyname(my_host_name))) { + fprintf(stderr, "Couldn't get my cannonicalized host name!\n"); + exit(1); + } + for (cp=hp->h_name; *cp; cp++) + if (isupper(*cp)) + *cp = tolower(*cp); + if (realm) + sprintf(buf, "%s/%s@%s", KPROP_SERVICE_NAME, hp->h_name, + realm); + else + sprintf(buf, "%s/%s", KPROP_SERVICE_NAME, hp->h_name); + if (retval = krb5_parse_name(buf, &server)) { + com_err(progname, retval, + "While trying to parse %s for service name"); + exit(1); + } + if (retval = krb5_os_localaddr(&server_addrs)) { + com_err(progname, retval, + "While trying to get local server address"); + exit(1); + } + /* + * Construct the name of the temporary file. + */ + if ((temp_file_name = (char *) malloc(strlen(file) + + strlen(tmp) + 1)) == NULL) { + com_err(progname, ENOMEM, + "while allocating filename for temp file"); + exit(1); + } + strcpy(temp_file_name, file); + strcat(temp_file_name, tmp); +} + +void +detach_process() +{ + int n; + +#if defined(BSD) && BSD >= 199006 + daemon(1, 0); +#else + if (fork() > 0) + exit(0); + n = open("/dev/null", O_RDONLY); + (void) dup2(n, 0); + (void) dup2(n, 1); + (void) dup2(n, 2); + if (n > 2) + (void) close(n); +#ifdef SYSV + setpgrp(); +#else + { + /* + * The open below may hang on pseudo ttys if the person + * who starts named logs out before this point. Thus, + * the need for the timer. + */ + alarm(120); + n = open("/dev/tty", O_RDWR); + alarm(0); + if (n > 0) { + (void) ioctl(n, TIOCNOTTY, (char *)NULL); + (void) close(n); + } + } +#endif /* SYSV */ +#endif /* BSD > 199006 */ +} + +/* + * Figure out who's calling on the other end of the connection.... + */ +void +kerberos_authenticate(fd, clientp, sin) + int fd; + krb5_principal *clientp; + struct sockaddr_in sin; +{ + krb5_error_code retval; + krb5_ticket *ticket; + krb5_authenticator *authent; + struct sockaddr_in r_sin; + int sin_length; + + /* + * Set recv_addr and send_addr + */ + sender_addr.addrtype = ADDRTYPE_INET; + sender_addr.length = sizeof(sin.sin_addr); + sender_addr.contents = (krb5_octet *) malloc(sizeof(sin.sin_addr)); + memcpy((char *) sender_addr.contents, (char *) &sin.sin_addr, + sizeof(sin.sin_addr)); + + sin_length = sizeof(r_sin); + if (getsockname(fd, (struct sockaddr *) &r_sin, &sin_length)) { + com_err(progname, errno, "while getting local socket address"); + exit(1); + } + + receiver_addr.addrtype = ADDRTYPE_INET; + receiver_addr.length = sizeof(r_sin.sin_addr); + receiver_addr.contents = (krb5_octet *) malloc(sizeof(r_sin.sin_addr)); + memcpy((char *) receiver_addr.contents, (char *) &r_sin.sin_addr, + sizeof(r_sin.sin_addr)); + + if (retval = krb5_recvauth(fd, kprop_version, server, &sender_addr, + kerb_keytab, NULL, NULL, my_seq_num, + "dfl", clientp, &ticket, &authent)) { + syslog(LOG_ERR, "Error in krb5_recvauth: %s", + error_message(retval)); + exit(1); + } + if (debug) { + char *name; + + if (retval = krb5_unparse_name(*clientp, &name)) { + com_err(progname, retval, + "While unparsing client name"); + exit(1); + } + printf("authenticated client: %s\n", name); + free(name); + } + his_seq_num = authent->seq_number; + krb5_copy_keyblock(ticket->enc_part2->session, session_key); + krb5_free_ticket(ticket); + krb5_free_authenticator(authent); +} + +krb5_boolean +authorized_principal(p) + krb5_principal p; +{ + static char *localrealm = NULL; + char *default_realm; + krb5_error_code retval; + + if (!localrealm) { + if (realm) + localrealm = realm; + else { + if (retval = krb5_get_default_realm(&default_realm)) { + com_err(progname, retval, + "While getting default realm in authorized_boolean"); + abort(); + } + localrealm = default_realm; + } + } + /* + * The other side must be coming from the local realm! + */ + if (!p[0] || (p[0]->length != strlen(localrealm)) + || memcmp(p[0]->data, localrealm, p[0]->length)) + return(FALSE); + /* + * The client's service must be KPROP_SERVICE_NAME + */ + if (!p[1] || (p[1]->length != strlen(KPROP_SERVICE_NAME)) + || memcmp(p[1]->data, KPROP_SERVICE_NAME, p[1]->length)) + return(FALSE); + /* + * For now, it can come from any hostname. We this needs to + * be fixed to check an access control list or something. + * + * XXXX + */ + return(TRUE); +} + +void +recv_database(fd, database_fd) + int fd; + int database_fd; +{ + int database_size; + int received_size, n; + char buf[1024]; + char *i_vector; + krb5_data inbuf, outbuf; + krb5_error_code retval; + int eblock_size; + + /* + * Receive and decode size from client + */ + if (retval = krb5_read_message(fd, &inbuf)) { + send_error(fd, retval, "while reading database size"); + com_err(progname, retval, + "while reading size of database from client"); + exit(1); + } + if (krb5_is_krb_error(&inbuf)) + recv_error(&inbuf); + if (retval = krb5_rd_safe(&inbuf, session_key, &sender_addr, + &receiver_addr, his_seq_num++, + KRB5_SAFE_DOSEQUENCE, 0, &outbuf)) { + send_error(fd, retval, "while decoding database size"); + xfree(inbuf.data); + com_err(progname, retval, + "while decoding database size from client"); + exit(1); + } + memcpy((char *) &database_size, outbuf.data, sizeof(database_size)); + xfree(inbuf.data); + xfree(outbuf.data); + database_size = ntohl(database_size); + /* + * Initialize the initial vector. + */ + eblock_size = krb5_keytype_array[session_key->keytype]-> + system->block_length; + if (!(i_vector=malloc(eblock_size))) { + com_err(progname, ENOMEM, "while allocating i_vector"); + send_error(fd, ENOMEM, + "malloc failed while allocating i_vector"); + exit(1); + } + memset(i_vector, 0, eblock_size); + /* + * Now start receiving the database from the net + */ + received_size = 0; + while (received_size < database_size) { + if (retval = krb5_read_message(fd, &inbuf)) { + sprintf(buf, + "while reading database block starting at offset %d", + received_size); + com_err(progname, retval, buf); + send_error(fd, retval, buf); + exit(1); + } + if (krb5_is_krb_error(&inbuf)) + recv_error(&inbuf); + if (retval = krb5_rd_priv(&inbuf, session_key, + &sender_addr, &receiver_addr, + his_seq_num++, KRB5_PRIV_DOSEQUENCE, + i_vector, 0, &outbuf)) { + sprintf(buf, + "while decoding database block starting at offset %d", + received_size); + com_err(progname, retval, buf); + send_error(fd, retval, buf); + xfree(inbuf.data); + exit(1); + } + n = write(database_fd, outbuf.data, outbuf.length); + xfree(inbuf.data); + xfree(outbuf.data); + if (n < 0) { + sprintf(buf, + "while writing database block starting at offset %d", + received_size); + send_error(fd, errno, buf); + } else if (n != outbuf.length) { + sprintf(buf, + "incomplete write while writing database block starting at \noffset %d (%d written, %d expected)", + received_size, n, outbuf.length); + send_error(fd, KRB5KRB_ERR_GENERIC, buf); + } + received_size += outbuf.length; + } + /* + * OK, we've seen the entire file. Did we get too many bytes? + */ + if (received_size > database_size) { + sprintf(buf, + "Received %d bytes, expected %d bytes for database file", + received_size, database_size); + send_error(fd, KRB5KRB_ERR_GENERIC, buf); + } + /* + * Send over acknowledgement of number of bytes receieved. + */ + database_size = htonl(database_size); + inbuf.data = (char *) &database_size; + inbuf.length = sizeof(database_size); + if (retval = krb5_mk_safe(&inbuf, KPROP_CKSUMTYPE, + session_key, + /* Note these are reversed because */ + /* we are sending, not receiving! */ + &receiver_addr, &sender_addr, + my_seq_num++, KRB5_PRIV_DOSEQUENCE, + &outbuf)) { + com_err(progname, retval, + "while encoding # of receieved bytes"); + send_error(fd, retval, + "while encoding # of received bytes"); + exit(1); + } + if (retval = krb5_write_message(fd, &outbuf)) { + xfree(outbuf.data); + com_err(progname, retval, + "while sending # of receeived bytes"); + exit(1); + } + xfree(outbuf.data); +} + + +void +send_error(fd, err_code, err_text) + int fd; + char *err_text; + krb5_error_code err_code; +{ + krb5_error error; + const char *text; + krb5_data outbuf; + + memset((char *)&error, 0, sizeof(error)); + krb5_us_timeofday(&error.stime, &error.susec); + error.server = server; + error.client = client; + error.error = err_code - ERROR_TABLE_BASE_krb5; + if (error.error < 0 || error.error > 127) + error.error = KRB_ERR_GENERIC; + if (err_text) + text = err_text; + else + text = error_message(err_code); + error.text.length = strlen(text) + 1; + if (error.text.data = malloc(error.text.length)) { + strcpy(error.text.data, text); + if (!krb5_mk_error(&error, &outbuf)) { + (void) krb5_write_message(fd, &outbuf); + xfree(outbuf.data); + } + free(error.text.data); + } +} + +void +recv_error(inbuf) + krb5_data *inbuf; +{ + krb5_error *error; + krb5_error_code retval; + + if (retval = krb5_rd_error(inbuf, &error)) { + com_err(progname, retval, + "while decoding error packet from client"); + exit(1); + } + if (error->error == KRB_ERR_GENERIC) { + if (error->text.data) + fprintf(stderr, + "Generic remote error: %s\n", + error->text.data); + } else if (error->error) { + com_err(progname, error->error + ERROR_TABLE_BASE_krb5, + "signalled from server"); + if (error->text.data) + fprintf(stderr, + "Error text from client: %s\n", + error->text.data); + } + krb5_free_error(error); + exit(1); +} + +void +load_database(kdb5_edit, database_file_name) + char *kdb5_edit; + char *database_file_name; +{ + static char *edit_av[4]; + int error_ret, save_stderr; + union wait waitb; + char request[1024]; + krb5_error_code retval; + + if (debug) + printf("calling krb5_edit to load database\n"); + + sprintf(request, "load_db %s %s", database_file_name, kerb_database); + + edit_av[0] = kdb5_edit; + edit_av[1] = "-R"; + edit_av[2] = request; + edit_av[3] = NULL; + switch(vfork()) { + case -1: + com_err(progname, errno, "while trying to fork %s", + kdb5_edit); + exit(1); + case 0: + if (!debug) { + save_stderr = dup(2); + close(0); + close(1); + close(2); + open("/dev/null", O_RDWR); + dup(0); + dup(0); + } + + execv(kdb5_edit, edit_av); + retval = errno; + if (!debug) + dup2(save_stderr, 2); + com_err(progname, retval, "while trying to exec %s", + kdb5_edit); + exit(1); + /*NOTREACHED*/ + default: + if (wait(&waitb) < 0) { + com_err(progname, errno, "while waiting for %s", + kdb5_edit); + exit(1); + } + } + + if (error_ret = waitb.w_retcode) { + com_err(progname, 0, "%s returned a bad exit status (%d)", + kdb5_edit, error_ret); + exit(1); + } + return; +}