--- /dev/null
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1990 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#if !defined(lint) && !defined(SABER)
+static char rcsid_kprop_c[] =
+"$Id$";
+#endif /* !lint && !SABER */
+
+#include <krb5/copyright.h>
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/osconf.h>
+#include <krb5/kdb.h>
+#include <krb5/kdb_dbm.h>
+#include <krb5/ext-proto.h>
+#include <krb5/libos-proto.h>
+#include <com_err.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/param.h>
+#include <netdb.h>
+
+#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;
+}
--- /dev/null
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1990 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#if !defined(lint) && !defined(SABER)
+static char rcsid_kpropd_c[] =
+"$Id$";
+#endif /* !lint && !SABER */
+
+#include <krb5/copyright.h>
+#include <krb5/krb5.h>
+#include <krb5/asn1.h>
+#include <krb5/osconf.h>
+#include <krb5/kdb.h>
+#include <krb5/kdb_dbm.h>
+#include <krb5/ext-proto.h>
+#include <krb5/libos-proto.h>
+#include <com_err.h>
+#include <errno.h>
+
+#include <stdio.h>
+#include <varargs.h>
+#include <ctype.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <string.h>
+#include <sgtty.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <netdb.h>
+#include <syslog.h>
+
+#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;
+}