*** empty log message ***
authorTheodore Tso <tytso@mit.edu>
Wed, 27 Feb 1991 11:25:07 +0000 (11:25 +0000)
committerTheodore Tso <tytso@mit.edu>
Wed, 27 Feb 1991 11:25:07 +0000 (11:25 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@1795 dc483132-0cff-0310-8789-dd5450dbe970

src/slave/Imakefile [new file with mode: 0644]
src/slave/kprop.c [new file with mode: 0644]
src/slave/kprop.h [new file with mode: 0644]
src/slave/kpropd.c [new file with mode: 0644]

diff --git a/src/slave/Imakefile b/src/slave/Imakefile
new file mode 100644 (file)
index 0000000..ec4b5e0
--- /dev/null
@@ -0,0 +1,31 @@
+#      $Source$
+#      $Author$
+#      $Id$
+#
+#  Copyright 1990 by the Massachusetts Institute of Technology.
+# 
+#  For copying and distribution information, please see the file
+#  <krb5/copyright.h>.
+# 
+
+        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 (file)
index 0000000..98fc7e7
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * $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;
+}
diff --git a/src/slave/kprop.h b/src/slave/kprop.h
new file mode 100644 (file)
index 0000000..23d1777
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1990 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <krb5/copyright.h>.
+ *
+ */
+
+#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 (file)
index 0000000..9dff84d
--- /dev/null
@@ -0,0 +1,813 @@
+/*
+ * $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;
+}