* Makefile.in, configure.in, adm_rw.c, kadm5_defs.h, main.c,
authorMarc Horowitz <marc@mit.edu>
Thu, 25 Jul 1996 22:30:02 +0000 (22:30 +0000)
committerMarc Horowitz <marc@mit.edu>
Thu, 25 Jul 1996 22:30:02 +0000 (22:30 +0000)
        proto_serv.c, srv_net.c: combined pieces of the old v5server and
        kadm libraries with calls to the new kadm5srv library to produce a
        daemon which handles the v5b6 "simple" password changing protocol.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8825 dc483132-0cff-0310-8789-dd5450dbe970

src/kadmin/v5passwdd/ChangeLog [new file with mode: 0644]
src/kadmin/v5passwdd/Makefile.in [new file with mode: 0644]
src/kadmin/v5passwdd/adm_rw.c [new file with mode: 0644]
src/kadmin/v5passwdd/configure.in [new file with mode: 0644]
src/kadmin/v5passwdd/kadm5_defs.h [new file with mode: 0644]
src/kadmin/v5passwdd/main.c [new file with mode: 0644]
src/kadmin/v5passwdd/proto_serv.c [new file with mode: 0644]
src/kadmin/v5passwdd/srv_net.c [new file with mode: 0644]

diff --git a/src/kadmin/v5passwdd/ChangeLog b/src/kadmin/v5passwdd/ChangeLog
new file mode 100644 (file)
index 0000000..425781a
--- /dev/null
@@ -0,0 +1,6 @@
+Thu Jul 25 17:01:32 1996  Marc Horowitz  <marc@mit.edu>
+
+       * Makefile.in, configure.in, adm_rw.c, kadm5_defs.h, main.c,
+       proto_serv.c, srv_net.c: combined pieces of the old v5server and
+       kadm libraries with calls to the new kadm5srv library to produce a
+       daemon which handles the v5b6 "simple" password changing protocol.
diff --git a/src/kadmin/v5passwdd/Makefile.in b/src/kadmin/v5passwdd/Makefile.in
new file mode 100644 (file)
index 0000000..a46a326
--- /dev/null
@@ -0,0 +1,18 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+SRCS = main.c srv_net.c proto_serv.c adm_rw.c
+OBJS = main.o srv_net.o proto_serv.o adm_rw.o
+
+PROG = v5passwdd
+
+all:: $(PROG)
+
+$(PROG): $(OBJS) $(DEPLIBS)
+       $(LD) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install::
+       $(INSTALL_PROGRAM) $(C)$(PROG) ${DESTDIR}$(SERVER_BINDIR)$(S)$(PROG)
+
+clean::
+       $(RM) $(PROG)
+
diff --git a/src/kadmin/v5passwdd/adm_rw.c b/src/kadmin/v5passwdd/adm_rw.c
new file mode 100644 (file)
index 0000000..bed4e4b
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * lib/kadm/adm_rw.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * Routines to engage in the administrative (password changing) protocol.
+ */
+#define NEED_SOCKETS
+#include "k5-int.h"
+#include "adm_proto.h"
+
+/*
+ * Local prototypes (needed or else the PC will pass fail).
+ */
+static void kadm_copyin_int32 PROTOTYPE((char *, krb5_int32 *));
+static void kadm_copyout_int32 PROTOTYPE((krb5_int32, char *));
+
+/*
+ * Routines to [de]serialize integers.
+ *
+ * kadm_copyin_int32   - Move a 32-bit integer fron network byte order to
+ *                       host byte order.
+ * kadm_copyout_int32  - Move a 32-bit integer from host byte order to
+ *                       network byte order.
+ */
+static void
+kadm_copyin_int32(cp, ip)
+    char       *cp;
+    krb5_int32 *ip;
+{
+    *ip = (((krb5_int32) ((unsigned char) cp[0]) << 24) +
+          ((krb5_int32) ((unsigned char) cp[1]) << 16) +
+          ((krb5_int32) ((unsigned char) cp[2]) << 8) +
+          ((krb5_int32) ((unsigned char) cp[3])));
+}
+
+static void
+kadm_copyout_int32(outint, cp)
+    krb5_int32 outint;
+    char       *cp;
+{
+    cp[0] = (char) ((outint >> 24) & 0xff);
+    cp[1] = (char) ((outint >> 16) & 0xff);
+    cp[2] = (char) ((outint >> 8) & 0xff);
+    cp[3] = (char) (outint & 0xff);
+}
+\f
+/*
+ * krb5_free_adm_data()        - Free data blocks allocated by read_adm... routines.
+ */
+void INTERFACE
+krb5_free_adm_data(kcontext, ncomp, datap)
+    krb5_context       kcontext;
+    krb5_int32         ncomp;
+    krb5_data          *datap;
+{
+    int i;
+    
+    if (datap) {
+       for (i=0; i<ncomp; i++)
+           if (datap[i].data && (datap[i].length > 0))
+               krb5_xfree(datap[i].data);
+
+       krb5_xfree(datap);
+    }
+}
+\f
+/*
+ * krb5_send_adm_cmd() - Send an administrative command.
+ *
+ * Send a list of data in a KRB_PRIV message.  Data takes the format:
+ *     nargs (4 octets in network order)
+ *             arg size 1 (4 octets in network order)
+ *             arg data 1 ("arg size 1" octets)
+ *             .
+ *             .
+ *             .
+ */
+krb5_error_code INTERFACE
+krb5_send_adm_cmd(kcontext, sock, ctx, nargs, arglist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to write to   (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32                 nargs;          /* Number of arguments  (In ) */
+    krb5_data          *arglist;       /* Components to write  (In ) */
+{
+    int        writebufsize;
+    int i;
+    char *writebuf;
+    krb5_error_code ret;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    ret = 0;
+    /* Calculate write buffer size */
+    writebufsize = sizeof(krb5_int32);
+    for (i=0; i<nargs; i++) {
+       writebufsize += sizeof(krb5_int32);     /* for argument size */
+       writebufsize += arglist[i].length;      /* for actual arg */
+    }
+
+    if (writebuf = (char *) malloc(writebufsize)) {
+       char                    *curr;
+       krb5_data               write_data, out_data;
+       krb5_replay_data        replay_data;
+
+       /* Serialize into write buffer - first number of arguments */
+       curr = writebuf;
+       kadm_copyout_int32(nargs, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Then the arguments */
+       for (i=0; i<nargs; i++) {
+           kadm_copyout_int32(arglist[i].length, curr);
+           curr += sizeof(krb5_int32);
+           memcpy(curr, arglist[i].data, arglist[i].length);
+           curr += arglist[i].length;
+       }
+
+       /* Set up the message */
+       write_data.length = writebufsize;
+       write_data.data = writebuf;
+
+       /* Generate the message */
+       if (!(ret = krb5_mk_priv(kcontext,
+                                ctx,
+                                &write_data,
+                                &out_data,
+                                &replay_data))) {
+           /* Write the message */
+           if (ret = krb5_write_message(kcontext, sock, &out_data))
+               goto cleanup;
+           krb5_xfree(out_data.data);
+       }
+
+    cleanup:
+       /* Paranoia */
+       memset(writebuf, 0, writebufsize);
+       free(writebuf);
+    }
+    else {
+       /* error */
+       ret = ENOMEM;
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_send_adm_reply()       - Send an administrative reply.
+ *
+ * Send a reply in a KRB_PRIV message.  Data takes the format:
+ *     status (4 octets in network order)
+ *     ncomps (4 octets in network order)
+ *             comp size 1 (4 octets in network order)
+ *             comp data 1 ("comp size 1" octets)
+ *             .
+ *             .
+ *             .
+ */
+krb5_error_code
+krb5_send_adm_reply(kcontext, sock, ctx, cmd_stat, ncomps, complist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to write to   (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32         cmd_stat;       /* Command status       (In ) */
+    krb5_int32                 ncomps;         /* Number of arguments  (In ) */
+    krb5_data          *complist;      /* Components to write  (In ) */
+{
+    int        writebufsize;
+    int i;
+    char *writebuf;
+    krb5_error_code ret;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    ret = 0;
+    /* Calculate write buffer size */
+    writebufsize = 2 * sizeof(krb5_int32);
+    for (i=0; i<ncomps; i++) {
+       writebufsize += sizeof(krb5_int32);     /* for argument size */
+       writebufsize += complist[i].length;     /* for actual arg */
+    }
+
+    if (writebuf = (char *) malloc(writebufsize)) {
+       char                    *curr;
+       krb5_data               write_data, out_data;
+       krb5_replay_data        replay_data;
+
+       /* Serialize into write buffer - first command status */
+       curr = writebuf;
+       kadm_copyout_int32(cmd_stat, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Now number of reply components */
+       kadm_copyout_int32(ncomps, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Then the arguments */
+       for (i=0; i<ncomps; i++) {
+           kadm_copyout_int32(complist[i].length, curr);
+           curr += sizeof(krb5_int32);
+           memcpy(curr, complist[i].data, complist[i].length);
+           curr += complist[i].length;
+       }
+
+       /* Set up the message */
+       write_data.length = writebufsize;
+       write_data.data = writebuf;
+
+       /* Generate the message */
+       if (!(ret = krb5_mk_priv(kcontext,
+                                ctx,
+                                &write_data,
+                                &out_data,
+                                &replay_data))) {
+           /* Write the message */
+           if (ret = krb5_write_message(kcontext, sock, &out_data))
+               goto cleanup;
+           krb5_xfree(out_data.data);
+       }
+
+    cleanup:
+       /* Paranoia */
+       memset(writebuf, 0, writebufsize);
+       free(writebuf);
+    }
+    else {
+       /* error */
+       ret = ENOMEM;
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_read_adm_cmd() - Read an administrative protocol command.
+ *
+ * Read an administrative command from the socket.  Expect data in the
+ * same format as send_adm_cmd shoots them out in.
+ *
+ * It is the caller's responsibility to free the memory allocated for
+ * the read in argument list.
+ */
+krb5_error_code
+krb5_read_adm_cmd(kcontext, sock, ctx, nargs, arglist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to read from  (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32         *nargs;         /* Number of arguments  (Out) */
+    krb5_data          **arglist;      /* List of arguments    (Out) */
+{
+    krb5_data          read_data;
+    krb5_error_code    ret;
+    krb5_data          msg_data;
+    krb5_replay_data   replay_data;
+    krb5_int32         ac_flags;
+    krb5_int32         len32;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    if (!(ret = krb5_read_message(kcontext, sock, &read_data))) {
+       if (!(ret = krb5_rd_priv(kcontext,
+                                ctx,
+                                &read_data,
+                                &msg_data,
+                                &replay_data))) {
+           char *curr;
+           int replyok;
+           int i;
+
+           replyok = 0;
+           /* We'd better have at least one reply component */
+           if (msg_data.length >= sizeof(krb5_int32)) {
+               curr = msg_data.data;
+               kadm_copyin_int32(curr, nargs);
+               curr += sizeof(krb5_int32);
+
+               /* Are there any components to copy? */
+               if (*nargs > 0) {
+
+                   /* Get the memory for the list */
+                   if (*arglist = (krb5_data *)
+                       malloc((size_t) (*nargs) * sizeof(krb5_data))) {
+                       krb5_data *xarglist;
+
+                       xarglist = *arglist;
+                       memset((char *) (xarglist), 0,
+                               (size_t) (*nargs) * sizeof(krb5_data));
+
+                       replyok = 1;
+                       /* Copy out each list entry */
+                       for (i=0; i<*nargs; i++) {
+
+                           /* First get the length of the reply component */
+                           if (curr + sizeof(krb5_int32) - msg_data.data <=
+                               msg_data.length) {
+
+                               kadm_copyin_int32(curr, &len32);
+                               xarglist[i].length = (int) len32;
+                               curr += sizeof(krb5_int32);
+
+                               /* Then get the memory for the actual data */
+                               if ((curr + xarglist[i].length -
+                                    msg_data.data <= msg_data.length) &&
+                                   (xarglist[i].data = (char *)
+                                    malloc(xarglist[i].length+1))) {
+
+                                   /* Then copy it out */
+                                   memcpy(xarglist[i].data,
+                                          curr,
+                                          xarglist[i].length);
+                                   curr += xarglist[i].length;
+
+                                   /* Null terminate for convenience */
+                                   xarglist[i].data[xarglist[i].length] 
+                                           = '\0';
+                               }
+                               else {
+                                   /* Not enough remaining data. */
+                                   replyok = 0;
+                                   break;
+                               }
+                           }
+                           else {
+                               /* Not enough remaining data */
+                               replyok = 0;
+                               break;
+                           }
+                       }
+                       if (!replyok)
+                           krb5_free_adm_data(kcontext, *nargs, *arglist);
+                   }
+               }
+               else {
+                   if (*nargs == 0) {
+                       *arglist = (krb5_data *) NULL;
+                       replyok = 1;
+                   }
+               }
+           }
+           if (!replyok) {
+               ret = KRB5KRB_AP_ERR_MSG_TYPE;  /* syntax error */
+           }
+           memset(msg_data.data, 0, msg_data.length);
+           krb5_xfree(msg_data.data);
+       }
+       krb5_xfree(read_data.data);
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_read_adm_reply()       - Read an administrative protocol response.
+ *
+ * Expect to read them out in the same format as send_adm_reply shoots them
+ * in.
+ *
+ * It is the caller's responsibility to free the memory allocated for
+ * the read in component list.
+ */
+krb5_error_code INTERFACE
+krb5_read_adm_reply(kcontext, sock, ctx, cmd_stat, ncomps, complist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to read from  (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32         *cmd_stat;      /* Command status       (Out) */
+    krb5_int32         *ncomps;        /* # of reply components(Out) */
+    krb5_data          **complist;     /* List of components   (Out) */
+{
+    krb5_data          read_data;
+    krb5_error_code    ret;
+    krb5_data          msg_data;
+    krb5_replay_data   replay_data;
+    krb5_int32         ac_flags;
+    krb5_int32         len32;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    if (!(ret = krb5_read_message(kcontext, sock, &read_data))) {
+       if (!(ret = krb5_rd_priv(kcontext,
+                                ctx,
+                                &read_data,
+                                &msg_data,
+                                &replay_data))) {
+           char *curr;
+           int replyok;
+           int i;
+
+           replyok = 0;
+           /* We'd better have at least two reply components */
+           if (msg_data.length >= (2*sizeof(krb5_int32))) {
+               curr = msg_data.data;
+               kadm_copyin_int32(curr, cmd_stat);
+               curr += sizeof(krb5_int32);
+               kadm_copyin_int32(curr, ncomps);
+               curr += sizeof(krb5_int32);
+
+               /* Are there any components to copy? */
+               if (*ncomps > 0) {
+
+                   /* Get the memory for the list */
+                   if (*complist = (krb5_data *)
+                       malloc((size_t) ((*ncomps) * sizeof(krb5_data)))) {
+                       krb5_data *xcomplist;
+
+                       xcomplist = *complist;
+                       memset((char *) (xcomplist), 0, 
+                              (size_t) ((*ncomps) * sizeof(krb5_data)));
+
+                       replyok = 1;
+                       /* Copy out each list entry */
+                       for (i=0; i<*ncomps; i++) {
+
+                           /* First get the length of the reply component */
+                           if (curr + sizeof(krb5_int32) - msg_data.data <=
+                               msg_data.length) {
+                               kadm_copyin_int32(curr, &len32);
+                               xcomplist[i].length = (int) len32;
+                               curr += sizeof(krb5_int32);
+
+                               /* Then get the memory for the actual data */
+                               if ((curr + xcomplist[i].length -
+                                    msg_data.data <= msg_data.length) &&
+                                   (xcomplist[i].data = (char *)
+                                    malloc(xcomplist[i].length+1))) {
+
+                                   /* Then copy it out */
+                                   memcpy(xcomplist[i].data,
+                                          curr,
+                                          xcomplist[i].length);
+                                   curr += xcomplist[i].length;
+
+                                   /* Null terminate for convenience */
+                                   xcomplist[i].data[xcomplist[i].length] 
+                                           = '\0';
+                               }
+                               else {
+                                   /* Not enough remaining data. */
+                                   replyok = 0;
+                                   break;
+                               }
+                           }
+                           else {
+                               /* Not enough remaining data */
+                               replyok = 0;
+                               break;
+                           }
+                       }
+                       if (!replyok)
+                           krb5_free_adm_data(kcontext, *ncomps, *complist);
+                   }
+               }
+               else {
+                   if (*ncomps == 0) {
+                       *complist = (krb5_data *) NULL;
+                       replyok = 1;
+                   }
+               }
+           }
+           if (!replyok) {
+               ret = KRB5KRB_AP_ERR_MSG_TYPE;  /* syntax error */
+           }
+           memset(msg_data.data, 0, msg_data.length);
+           krb5_xfree(msg_data.data);
+       }
+       krb5_xfree(read_data.data);
+    }
+    return(ret);
+}
diff --git a/src/kadmin/v5passwdd/configure.in b/src/kadmin/v5passwdd/configure.in
new file mode 100644 (file)
index 0000000..ce43d2f
--- /dev/null
@@ -0,0 +1,17 @@
+AC_INIT(proto_serv.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_FUNC_CHECK(waitpid,AC_DEFINE(HAVE_WAITPID))
+AC_FUNC_CHECK(vsprintf,AC_DEFINE(HAVE_VSPRINTF))
+AC_CHECK_HEADERS(sys/select.h)
+CHECK_SIGNALS
+CHECK_SETJMP
+CHECK_WAIT_TYPE
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_KDB5_LIBRARY
+USE_DYN_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/v5passwdd/kadm5_defs.h b/src/kadmin/v5passwdd/kadm5_defs.h
new file mode 100644 (file)
index 0000000..67339bc
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * kadmin/v5server/kadm5_defs.h
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * kadmind5
+ * Version 5 administrative daemon.
+ */
+#ifndef        KADM5_DEFS_H__
+#define        KADM5_DEFS_H__
+
+/*
+ * Debug definitions.
+ */
+#define        DEBUG_SPROC     1
+#define        DEBUG_OPERATION 2
+#define        DEBUG_HOST      4
+#define        DEBUG_REALM     8
+#define        DEBUG_REQUESTS  16
+#define        DEBUG_ACL       32
+#define        DEBUG_PROTO     64
+#define        DEBUG_CALLS     128
+#define        DEBUG_NOSLAVES  256
+#ifdef DEBUG
+#define        DPRINT(l1, cl, al)      if ((cl & l1) != 0) xprintf al
+#else  /* DEBUG */
+#define        DPRINT(l1, cl, al)
+#endif /* DEBUG */
+#define        DLOG(l1, cl, msg)       if ((cl & l1) != 0)     \
+                                       com_err(programname, 0, msg)
+
+/*
+ * Access control bits.
+ */
+#define        ACL_ADD_PRINCIPAL       1
+#define        ACL_DELETE_PRINCIPAL    2
+#define        ACL_MODIFY_PRINCIPAL    4
+#define        ACL_CHANGEPW            8
+#define        ACL_CHANGE_OWN_PW       16
+#define        ACL_INQUIRE             32
+#define        ACL_EXTRACT             64
+#define        ACL_RENAME_PRINCIPAL    (ACL_ADD_PRINCIPAL+ACL_DELETE_PRINCIPAL)
+
+#define        ACL_PRINCIPAL_MASK      (ACL_ADD_PRINCIPAL|ACL_DELETE_PRINCIPAL|\
+                                ACL_MODIFY_PRINCIPAL)
+#define        ACL_PASSWD_MASK         (ACL_CHANGEPW|ACL_CHANGE_OWN_PW)
+#define        ACL_ALL_MASK            (ACL_ADD_PRINCIPAL      | \
+                                ACL_DELETE_PRINCIPAL   | \
+                                ACL_MODIFY_PRINCIPAL   | \
+                                ACL_CHANGEPW           | \
+                                ACL_CHANGE_OWN_PW      | \
+                                ACL_INQUIRE            | \
+                                ACL_EXTRACT)
+/*
+ * Subcodes.
+ */
+#define        KADM_BAD_ARGS           10
+#define        KADM_BAD_CMD            11
+#define        KADM_NO_CMD             12
+#define        KADM_BAD_PRINC          20
+#define        KADM_PWD_TOO_SHORT      21
+#define        KADM_PWD_WEAK           22
+#define        KADM_NOT_ALLOWED        100
+
+/*
+ * Reply status values.
+ */
+#define        KRB5_ADM_SUCCESS                0
+#define        KRB5_ADM_CMD_UNKNOWN            1
+#define        KRB5_ADM_PW_UNACCEPT            2
+#define        KRB5_ADM_BAD_PW                 3
+#define        KRB5_ADM_NOT_IN_TKT             4
+#define        KRB5_ADM_CANT_CHANGE            5
+#define        KRB5_ADM_LANG_NOT_SUPPORTED     6
+
+#define        KRB5_ADM_P_ALREADY_EXISTS       64
+#define        KRB5_ADM_P_DOES_NOT_EXIST       65
+#define        KRB5_ADM_NOT_AUTHORIZED         66
+#define        KRB5_ADM_BAD_OPTION             67
+#define        KRB5_ADM_VALUE_REQUIRED         68
+#define        KRB5_ADM_SYSTEM_ERROR           69
+#define        KRB5_ADM_KEY_DOES_NOT_EXIST     70
+#define        KRB5_ADM_KEY_ALREADY_EXISTS     71
+#define KRB5_ADM_BAD_DESKEY            72
+
+/*
+ * Inter-module function prototypes
+ */
+
+krb5_keytab key_keytab_id();
+krb5_error_code key_open_db KRB5_PROTOTYPE((krb5_context));
+krb5_error_code key_close_db KRB5_PROTOTYPE((krb5_context));
+
+#if 0
+/* srv_key.c */
+krb5_error_code key_init
+       KRB5_PROTOTYPE((krb5_context,
+                       int,
+                       int,
+                       char *,
+                       int,
+                       char *,
+                       char *,
+                       char *,
+                       char *,
+                       krb5_int32,
+                       krb5_key_salt_tuple *));
+void key_finish
+       KRB5_PROTOTYPE((krb5_context,
+                       int));
+krb5_error_code key_string_to_keys
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_db_entry *,
+                       krb5_data *,
+                       krb5_int32,
+                       krb5_key_salt_tuple *,
+                       krb5_int32 *,
+                       krb5_key_data **));
+krb5_error_code key_random_key
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_db_entry *,
+                       krb5_int32 *,
+                       krb5_key_data **));
+krb5_error_code key_encrypt_keys
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_db_entry *,
+                       krb5_int32 *,
+                       krb5_key_data *,
+                       krb5_key_data **));
+krb5_error_code key_decrypt_keys
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_db_entry *,
+                       krb5_int32 *,
+                       krb5_key_data *,
+                       krb5_key_data **));
+krb5_boolean key_pwd_is_weak
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_db_entry *,
+                       krb5_data *));
+krb5_db_entry *key_master_entry();
+char *key_master_realm();
+krb5_keyblock *key_admin_key();
+krb5_encrypt_block *key_master_encblock();
+void key_free_key_data KRB5_PROTOTYPE((krb5_key_data *,
+                                      krb5_int32));
+krb5_error_code key_dbent_to_keysalts
+       KRB5_PROTOTYPE((krb5_db_entry *,
+                       krb5_int32 *,
+                       krb5_key_salt_tuple **));
+krb5_error_code key_update_tl_attrs
+       KRB5_PROTOTYPE((krb5_context,
+                       krb5_db_entry *,
+                       krb5_principal,
+                       krb5_boolean));
+
+/* srv_acl.c */
+krb5_error_code acl_init
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  char *));
+void acl_finish
+       KRB5_PROTOTYPE((krb5_context,
+                  int));
+krb5_boolean acl_op_permitted
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_principal,
+                  krb5_int32,
+                  char *));
+
+#endif
+
+/* srv_output.c */
+krb5_error_code output_init
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  char *,
+                  krb5_boolean));
+void output_finish
+       KRB5_PROTOTYPE((krb5_context,
+                  int));
+krb5_boolean output_lang_supported
+       KRB5_PROTOTYPE((char *));
+char *output_krb5_errmsg
+       KRB5_PROTOTYPE((char *,
+                  krb5_boolean,
+                  krb5_int32));
+char *output_adm_error
+       KRB5_PROTOTYPE((char *,
+                  krb5_boolean,
+                  krb5_int32,
+                  krb5_int32,
+                  krb5_int32,
+                  krb5_data *));
+
+/* srv_net.c */
+krb5_error_code net_init
+       KRB5_PROTOTYPE((krb5_context,
+                  char *,
+                  int,
+                  krb5_int32));
+void net_finish
+       KRB5_PROTOTYPE((krb5_context,
+                  int));
+krb5_error_code net_dispatch
+       KRB5_PROTOTYPE((krb5_context, int));
+krb5_principal net_server_princ();
+
+/* proto_serv.c */
+krb5_error_code proto_init
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  int));
+void proto_finish
+       KRB5_PROTOTYPE((krb5_context,
+                  int));
+krb5_error_code proto_serv
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_int32,
+                  int,
+                  void *,
+                  void *));
+
+krb5_int32 passwd_change
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_auth_context,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_data *,
+                  krb5_int32 *));
+
+krb5_int32 pwd_change
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_auth_context,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_data *,
+                  char []));
+
+#if 0
+
+/* passwd.c */
+krb5_int32 passwd_check
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_auth_context,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_int32 *));
+krb5_int32 passwd_change
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_auth_context,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_data *,
+                  krb5_int32 *));
+krb5_boolean passwd_check_npass_ok
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_principal,
+                  krb5_db_entry *,
+                  krb5_data *,
+                  krb5_int32 *));
+krb5_boolean passwd_check_opass_ok
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_principal,
+                  krb5_db_entry *,
+                  krb5_data *));
+
+/* admin.c */
+krb5_error_code admin_add_principal
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_int32,
+                  krb5_data *));
+krb5_error_code admin_delete_principal
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_data *));
+krb5_error_code admin_rename_principal
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_data *));
+krb5_error_code admin_modify_principal
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_int32,
+                  krb5_data *));
+krb5_error_code admin_change_opw
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_data *));
+krb5_error_code admin_change_orandpw
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_data *));
+krb5_error_code admin_inquire
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_int32 *,
+                  krb5_data **));
+krb5_error_code admin_extract_key
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_data *,
+                  krb5_int32 *,
+                  krb5_data **));
+krb5_error_code admin_add_key
+       KRB5_PROTOTYPE((krb5_context,
+                       int,
+                       krb5_ticket *,
+                       krb5_int32,
+                       krb5_data *));
+krb5_error_code admin_delete_key
+       KRB5_PROTOTYPE((krb5_context,
+                       int,
+                       krb5_ticket *,
+                       krb5_int32,
+                       krb5_data *));
+void admin_init KRB5_PROTOTYPE((krb5_deltat,
+                               krb5_deltat,
+                               krb5_boolean,
+                               krb5_timestamp,
+                               krb5_boolean,
+                               krb5_flags));
+#endif
+
+#endif /* KADM5_DEFS_H__ */
diff --git a/src/kadmin/v5passwdd/main.c b/src/kadmin/v5passwdd/main.c
new file mode 100644 (file)
index 0000000..a37e67b
--- /dev/null
@@ -0,0 +1,289 @@
+#include    <stdio.h>
+#include    <signal.h>
+#include    <syslog.h>
+#include    <unistd.h>
+#include    <string.h>
+#include    <setjmp.h>
+
+#include <gssapi/gssapi_krb5.h>
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include "kadm5_defs.h"
+
+static krb5_keytab keytab;
+char *programname;
+kadm5_config_params params;
+void *global_server_handle;
+#if    POSIX_SETJMP
+static sigjmp_buf      terminal_jmp;
+#else  /* POSIX_SETJMP */
+static jmp_buf         terminal_jmp;
+#endif /* POSIX_SETJMP */
+
+krb5_keytab key_keytab_id()
+{
+    return(keytab);
+}
+
+static krb5_sigtype
+unhandled_signal(signo)
+    int signo;
+{
+#if    POSIX_SETJMP
+    siglongjmp(terminal_jmp, signo);
+#else  /* POSIX_SETJMP */
+    longjmp(terminal_jmp, signo);
+#endif /* POSIX_SETJMP */
+    /* NOTREACHED */
+}
+
+void usage()
+{
+     fprintf(stderr, "Usage: kadmind [-r realm] [-m] [-nofork] "
+            "[-port port-number]\n");
+     exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+     int ret, rlen, nofork, oldnames = 0;
+     krb5_error_code code;
+     int debug_level = 0;
+#if    POSIX_SIGNALS
+     struct sigaction s_action;
+#endif /* POSIX_SIGNALS */
+     krb5_context context;
+
+     programname = argv[0];
+
+     nofork = 0;
+
+     memset((char *) &params, 0, sizeof(params));
+     
+     argc--; argv++;
+     while (argc) {
+         if (strcmp(*argv, "-r") == 0) {
+              argc--; argv++;
+              if (!argc)
+                   usage();
+              params.realm = *argv;
+              params.mask |= KADM5_CONFIG_REALM;
+              argc--; argv++;
+              continue;
+         } else if (strcmp(*argv, "-m") == 0) {
+              params.mkey_from_kbd = 1;
+              params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
+         } else if (strcmp(*argv, "-nofork") == 0) {
+              nofork = 1;
+         } else if(strcmp(*argv, "-port") == 0) {
+           argc--; argv++;
+           if(!argc)
+             usage();
+           params.kadmind_port = atoi(*argv);
+           params.mask |= KADM5_CONFIG_KADMIND_PORT;
+         } else
+              break;
+         argc--; argv++;
+     }
+     
+     if (argc != 0)
+         usage();
+
+     if (ret = krb5_init_context(&context)) {
+         fprintf(stderr, "%s: %s while initializing context, aborting\n",
+                 programname, error_message(ret));
+         exit(1);
+     }
+
+     krb5_klog_init(context, "admin_server", programname, 1);
+
+     if (ret = kadm5_get_config_params(context, NULL, NULL, &params,
+                                      &params)) {
+         krb5_klog_syslog(LOG_ERR, "%s: %s while initializing, aborting\n",
+                          programname, error_message(ret));
+         fprintf(stderr, "%s: %s while initializing, aborting\n",
+                 programname, error_message(ret));
+         krb5_klog_close();
+         exit(1);
+     }
+
+#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE | \
+                        KADM5_CONFIG_ADMIN_KEYTAB)
+
+     if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
+         krb5_klog_syslog(LOG_ERR, "%s: Missing required configuration values "
+                          "(%x) while initializing, aborting\n", programname,
+                          (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
+         fprintf(stderr, "%s: Missing required configuration values "
+                 "(%x) while initializing, aborting\n", programname,
+                 (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
+         krb5_klog_close();
+         exit(1);
+     }
+
+     if ((code = krb5_kt_resolve(context, params.admin_keytab, &keytab))) {
+        fprintf(stderr, "%s: cannot resolve keytab %s (%s).\n",
+                programname, params.admin_keytab, error_message(code));
+        exit(1);
+     }
+
+     if (!nofork &&
+        daemon(0, ((params.mask&KADM5_CONFIG_MKEY_FROM_KBD)?
+                   params.mkey_from_kbd:0))) {
+         fprintf(stderr, "%s: cannot spawn and detach.\n", argv[0]);
+         perror(argv[0]);
+         return(2);
+     }
+
+#if    POSIX_SIGNALS
+     (void) sigemptyset(&s_action.sa_mask);
+     s_action.sa_flags = 0;
+     s_action.sa_handler = unhandled_signal;
+     (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
+     (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
+     (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
+     (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
+     (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
+     (void) sigaction(SIGALRM, &s_action, (struct sigaction *) NULL);
+     (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
+#else  /* POSIX_SIGNALS */
+     signal(SIGINT, unhandled_signal);
+     signal(SIGTERM, unhandled_signal);
+     signal(SIGHUP, unhandled_signal);
+     signal(SIGQUIT, unhandled_signal);
+     signal(SIGPIPE, unhandled_signal);
+     signal(SIGALRM, unhandled_signal);
+     signal(SIGCHLD, unhandled_signal);
+#endif /* POSIX_SIGNALS */
+
+     krb5_klog_syslog(LOG_INFO, "starting");
+
+     if (code = net_init(context, params.realm, debug_level,
+                        (params.mask&KADM5_CONFIG_KADMIND_PORT)?
+                        params.kadmind_port:0)) {
+       krb5_klog_syslog(LOG_ERR, "%s: %s while initializing network",
+                        programname, error_message(code));
+       fprintf(stderr, "%s: %s while initializing network\n",
+               programname, error_message(code));
+
+       exit(1);
+     }
+
+     if (
+#if    POSIX_SETJMP
+        sigsetjmp(terminal_jmp, 1) == 0
+#else  /* POSIX_SETJMP */
+        setjmp(terminal_jmp) == 0
+#endif /* POSIX_SETJMP */
+        )
+        {
+            if (code = net_dispatch(context, !nofork)) {
+                krb5_klog_syslog(LOG_ERR, "%s: %s while dispatching requests",
+                                 programname, error_message(code));
+                fprintf(stderr, "%s: %s while dispatching requests\n",
+                        programname, error_message(code));
+                
+                exit(1);
+            }
+        }
+
+     net_finish(context, debug_level);
+        
+     krb5_klog_syslog(LOG_INFO, "finished, exiting");
+     krb5_klog_close();
+     exit(2);
+}
+
+krb5_error_code key_open_db(krb5_context context)
+{
+     return(kadm5_init("kadmind", NULL,
+                      NULL, &params,
+                      KADM5_STRUCT_VERSION,
+                      KADM5_API_VERSION_2,
+                      &global_server_handle));
+}
+
+krb5_error_code key_close_db(krb5_context context)
+{
+     kadm5_destroy(global_server_handle);
+     return(0);
+}
+
+krb5_int32
+pwd_change(kcontext, debug_level, auth_context, ticket,
+             olddata, newdata, err_str)
+    krb5_context       kcontext;
+    int                        debug_level;
+    krb5_auth_context  auth_context;
+    krb5_ticket                *ticket;
+    krb5_data          *olddata;
+    krb5_data          *newdata;
+    char               err_str[];
+{
+     kadm5_ret_t ret;
+     krb5_int32                        now;
+     kadm5_policy_ent_rec      pol;
+     kadm5_principal_ent_rec   princ;
+     krb5_principal            principal;
+
+     /* Make sure the ticket is initial, otherwise don't trust it */
+     if ((ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
+         return(KRB5_ADM_NOT_IN_TKT);
+     }
+
+     /* a principal can always change its own password, so there's no
+       acl check to do here */
+
+     principal = ticket->enc_part2->client;
+
+     /* check to see if the min_time has passed.  this is stolen
+       from chpass_principal_wrapper */
+
+    if (ret = krb5_timeofday(kcontext, &now)) {
+        sprintf(err_str, error_message(ret));
+        return(KRB5_ADM_SYSTEM_ERROR);
+    }
+
+    if((ret = kadm5_get_principal(global_server_handle, principal,
+                                 &princ,
+                                 KADM5_PRINCIPAL_NORMAL_MASK)) !=
+       KADM5_OK) {
+        sprintf(err_str, error_message(ret));
+        return(KRB5_ADM_SYSTEM_ERROR);
+    }
+    if(princ.aux_attributes & KADM5_POLICY) {
+       if((ret=kadm5_get_policy(global_server_handle,
+                                princ.policy, &pol)) != KADM5_OK) {
+           (void) kadm5_free_principal_ent(global_server_handle, &princ);
+           sprintf(err_str, error_message(ret));
+           return(KRB5_ADM_SYSTEM_ERROR);
+       }
+       if((now - princ.last_pwd_change) < pol.pw_min_life &&
+          !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+           (void) kadm5_free_policy_ent(global_server_handle, &pol);
+           (void) kadm5_free_principal_ent(global_server_handle, &princ);
+           sprintf(err_str, error_message(ret));
+           return(KRB5_ADM_PW_UNACCEPT);
+       }
+       if (ret = kadm5_free_policy_ent(global_server_handle, &pol)) {
+           (void) kadm5_free_principal_ent(global_server_handle, &princ);
+           sprintf(err_str, error_message(ret));
+           return(KRB5_ADM_SYSTEM_ERROR);
+        }
+    }
+    if (ret = kadm5_free_principal_ent(global_server_handle, &princ)) {
+        sprintf(err_str, error_message(ret));
+        return(KRB5_ADM_SYSTEM_ERROR);
+    }
+
+    /* ok, it's not too early to change the password. change it. */
+
+    if (ret = kadm5_chpass_principal_util(global_server_handle,
+                                         principal,
+                                         newdata->data,
+                                         NULL,
+                                         err_str))
+        return(KRB5_ADM_PW_UNACCEPT);
+
+    return(KRB5_ADM_SUCCESS);
+}
diff --git a/src/kadmin/v5passwdd/proto_serv.c b/src/kadmin/v5passwdd/proto_serv.c
new file mode 100644 (file)
index 0000000..d87a71a
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * kadmin/v5server/proto_serv.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * proto_serv.c - Engage in protocol.  This module reflects the connection
+ *     protocol as implemented in lib/krb5/os/adm_conn.c.  Any changes
+ *     in one module must be reflected in the other.
+ */
+#include <signal.h>
+#define        NEED_SOCKETS
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+#include "adm.h"
+#include "adm_proto.h"
+#include <setjmp.h>
+
+static const char *proto_addrs_msg = "\004%d: cannot get memory for addresses";
+static const char *proto_rcache_msg = "\004%d: cannot get replay cache";
+static const char *proto_ap_req_msg = "\004%d: error reading AP_REQ message";
+static const char *proto_auth_con_msg = "\004%d: cannot get authorization context";
+static const char *proto_rd_req_msg = "\004%d: cannot decode AP_REQ message";
+static const char *proto_mk_rep_msg = "\004%d: cannot generate AP_REP message";
+static const char *proto_wr_rep_msg = "\004%d: cannot write AP_REP message";
+static const char *proto_conn_abort_msg = "\007%d: connection destroyed by client";
+static const char *proto_seq_err_msg = "\004%d: protocol sequence violation";
+static const char *proto_rd_cmd_msg = "\004%d: cannot read administrative protocol command";
+static const char *proto_db_open_msg = "\004%d: cannot open database";
+static const char *proto_db_close_msg = "\004%d: cannot close database";
+static const char *proto_wr_reply_msg = "\004%d: cannot write administrative protocol reply";
+static const char *proto_fmt_reply_msg = "\004%d: cannot format administrative protocol reply";
+extern char *programname;
+
+static int     proto_proto_timeout = -1;
+static int     proto_debug_level = 0;
+#if    POSIX_SETJMP
+static sigjmp_buf      timeout_jmp;
+#else  /* POSIX_SETJMP */
+static jmp_buf         timeout_jmp;
+#endif /* POSIX_SETJMP */
+
+static krb5_sigtype
+proto_alarmclock(signo)
+    int signo;
+{
+#if    POSIX_SETJMP
+    siglongjmp(timeout_jmp, 1);
+#else  /* POSIX_SETJMP */
+    longjmp(timeout_jmp, 1);
+#endif /* POSIX_SETJMP */
+    /* NOTREACHED */
+}
+
+krb5_error_code
+proto_init(kcontext, debug_level, timeo)
+    krb5_context       kcontext;
+    int                        debug_level;
+    int                        timeo;
+{
+    krb5_error_code    kret;
+
+    proto_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, proto_debug_level,
+          ("* proto_init(timeo=%d)\n", timeo));
+    kret = 0;
+    proto_proto_timeout = timeo;
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_init() = %d\n", kret));
+    return(kret);
+}
+
+void
+proto_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("* proto_finish()\n"));
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_finish()\n"));
+}
+
+krb5_error_code
+proto_serv(kcontext, my_id, cl_sock, sv_p, cl_p)
+    krb5_context       kcontext;
+    krb5_int32         my_id;
+    int                        cl_sock;
+    void               *sv_p;
+    void               *cl_p;
+{
+    krb5_error_code    kret;
+    struct sockaddr_in *cl_addr;
+    struct sockaddr_in *sv_addr;
+
+    krb5_data          in_data;
+    krb5_data          out_data;
+    krb5_rcache                rcache;
+    krb5_auth_context  auth_context;
+    krb5_flags         ap_options;
+    krb5_ticket                *ticket;
+    krb5_address       *local;
+    krb5_address       *remote;
+
+#if    POSIX_SIGNALS
+    struct sigaction   s_action;
+#endif /* POSIX_SIGNALS */
+
+    char               *curr_lang = (char *) NULL;
+    krb5_boolean       mime_setting = 0;
+
+    krb5_int32         num_args;
+    krb5_data          *arglist;
+
+    krb5_boolean       db_opened;
+
+    cl_addr = (struct sockaddr_in *) cl_p;
+    sv_addr = (struct sockaddr_in *) sv_p;
+    DPRINT(DEBUG_CALLS, proto_debug_level,
+          ("* proto_serv(id=%d, sock=%d, local=%x, remote=%x)\n",
+           my_id, cl_sock,
+           ntohl(sv_addr->sin_addr.s_addr),
+           ntohl(cl_addr->sin_addr.s_addr)));
+
+    /* Initialize */
+    memset((char *) &in_data, 0, sizeof(in_data));
+    memset((char *) &out_data, 0, sizeof(out_data));
+    num_args = 0;
+    local = (krb5_address *) NULL;
+    remote = (krb5_address *) NULL;
+    ticket = (krb5_ticket *) NULL;
+    rcache = (krb5_rcache) NULL;
+#if    POSIX_SIGNALS
+    (void) sigemptyset(&s_action.sa_mask);
+    s_action.sa_flags = 0;
+#endif /* POSIX_SIGNALS */
+    db_opened = 0;
+
+    /* Get memory for addresses */
+    local = (krb5_address *) malloc(sizeof(krb5_address));
+    remote = (krb5_address *) malloc(sizeof(krb5_address));
+    if (!local || !remote) {
+       kret = ENOMEM;
+       com_err(programname, kret, proto_addrs_msg, my_id);
+       goto cleanup;
+    }
+
+    local->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    remote->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    if (!local->contents || !remote->contents) {
+       kret = ENOMEM;
+       com_err(programname, kret, proto_addrs_msg, my_id);
+       goto cleanup;
+    }
+
+    /*
+     * First setup the replay cache.
+     */
+    if (kret = krb5_get_server_rcache(kcontext,
+                                     krb5_princ_component(kcontext,
+                                                          net_server_princ(),
+                                                          0),
+                                     &rcache)) {
+       com_err(programname, kret, proto_rcache_msg, my_id);
+       goto cleanup;
+    }
+
+    /* Initialize the auth context */
+    if (kret = krb5_auth_con_init(kcontext, &auth_context)) {
+       com_err(programname, kret, proto_auth_con_msg, my_id);
+       goto cleanup;
+    }
+
+    krb5_auth_con_setrcache(kcontext, auth_context, rcache);
+
+    /*
+     * Set up addresses.
+     */
+    local->addrtype = remote->addrtype = ADDRTYPE_INET;
+    local->length = remote->length = sizeof(struct in_addr);
+    memcpy((char *) local->contents,
+          (char *) &sv_addr->sin_addr,
+          sizeof(struct in_addr));
+    memcpy((char *) remote->contents,
+          (char *) &cl_addr->sin_addr,
+          sizeof(struct in_addr));
+    krb5_auth_con_setflags(kcontext, auth_context,
+                          KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                          KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+    krb5_auth_con_setaddrs(kcontext, auth_context, local, remote);
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:read message(local=%x, remote=%x)\n",
+           my_id,
+           ntohl(sv_addr->sin_addr.s_addr),
+           ntohl(cl_addr->sin_addr.s_addr)));
+    /* Now, read in the AP_REQ message and decode it. */
+    if (kret = krb5_read_message(kcontext,
+                                (krb5_pointer) &cl_sock,
+                                &in_data)) {
+       com_err(programname, kret, proto_ap_req_msg, my_id);
+       goto cleanup;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:parse message(%d bytes)\n", my_id, in_data.length));
+
+    /* Parse the AP_REQ message */
+    if (kret = krb5_rd_req(kcontext,
+                          &auth_context,
+                          &in_data,
+                          net_server_princ(),
+                          key_keytab_id(),
+                          &ap_options,
+                          &ticket)) {
+       com_err(programname, kret, proto_rd_req_msg, my_id);
+       goto err_reply;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:check AP_REQ(options are %x)\n", my_id, ap_options));
+    /* Check our options */
+    if ((ap_options & AP_OPTS_MUTUAL_REQUIRED) == 0) {
+       kret = KRB5KRB_AP_ERR_MSG_TYPE;
+       goto err_reply;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:make AP_REP\n", my_id));
+    if (kret = krb5_mk_rep(kcontext, auth_context, &out_data)) {
+       com_err(programname, kret, proto_mk_rep_msg, my_id);
+       goto cleanup;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:write AP_REP(%d bytes)\n", my_id, out_data.length));
+    if (kret = krb5_write_message(kcontext,
+                                 (krb5_pointer) &cl_sock,
+                                 &out_data)) {
+       com_err(programname, kret, proto_wr_rep_msg, my_id);
+       goto cleanup;
+    }
+
+    /*
+     * Initialization is now complete.
+     *
+     * If enabled, the protocol times out after proto_proto_timeout seconds.
+     */
+    if (
+#if    POSIX_SETJMP
+       sigsetjmp(timeout_jmp, 1) == 0
+#else  /* POSIX_SETJMP */
+       setjmp(timeout_jmp) == 0
+#endif /* POSIX_SETJMP */
+       ) {
+       if (proto_proto_timeout > 0) {
+#if    POSIX_SIGNALS
+           s_action.sa_handler = proto_alarmclock;
+           (void) sigaction(SIGALRM, &s_action, (struct sigaction *) NULL);
+#else  /* POSIX_SIGNALS */
+           signal(SIGALRM, proto_alarmclock);
+#endif /* POSIX_SIGNALS */
+       }
+       /*
+        * Loop forever - or until somebody puts us out of our misery.
+        */
+       while (1) {
+           krb5_int32  cmd_error;
+           char        err_str[1024];
+           krb5_int32  cmd_repl_ncomps;
+           krb5_data   *cmd_repl_complist;
+           int         do_quit;
+
+           /*
+            * Read a command and figure out what to do.
+            */
+           if (proto_proto_timeout > 0)
+               alarm(proto_proto_timeout);
+           num_args = 0;
+           DPRINT(DEBUG_PROTO, proto_debug_level,
+                  ("= %d:waiting for command\n", my_id));
+           kret = krb5_read_adm_cmd(kcontext,
+                                    (krb5_pointer) &cl_sock,
+                                    auth_context,
+                                    &num_args,
+                                    &arglist);
+           if (proto_proto_timeout > 0)
+               alarm(0);
+           if (kret) {
+               /*
+                * It's OK to have connections abort here.
+                */
+               if (kret == ECONNABORTED) {
+                   com_err(programname, kret, proto_conn_abort_msg, my_id);
+                   kret = 0;
+               }
+               else if (kret == KRB5KRB_AP_ERR_BADORDER) {
+                   com_err(programname, kret, proto_seq_err_msg, my_id);
+                   kret = 0;
+               }
+               else
+                   com_err(programname, kret, proto_rd_cmd_msg, my_id);
+               goto cleanup;
+           }
+
+           cmd_error = KRB5_ADM_SUCCESS;
+           do_quit = 0;
+
+           /*
+            * First open the database.  We only have it open for the
+            * lifetime of a command so that we are sure to close it after
+            * performing an update.  This also reduces the likelihood
+            * that somebody'll have stale data lying around since we're
+            * most likely going to change something here.
+            */
+           if ((kret = key_open_db(kcontext))) {
+               com_err(programname, kret, proto_db_open_msg, my_id);
+               goto cleanup;
+           }
+           else
+               db_opened = 1;
+
+           /*
+            * Now check our arguments.
+            */
+           DPRINT(DEBUG_PROTO, proto_debug_level,
+                  ("= %d:parse command\n", my_id));
+           cmd_repl_ncomps = 0;
+           cmd_repl_complist = (krb5_data *) NULL;
+           if (num_args > 0) {
+               if (!strcasecmp(arglist[0].data, KRB5_ADM_QUIT_CMD)) {
+                   /*
+                    * QUIT command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:QUIT command\n", my_id));
+                   /* QUIT takes no arguments */
+                   if (num_args == 1) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:QUIT command syntax OK\n", my_id));
+                       do_quit = 1;
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:QUIT command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       strcpy(err_str,
+                              "Bad argument list format for quit command.");
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data, KRB5_ADM_CHECKPW_CMD)) {
+                   /*
+                    * CHECKPW command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:CHECKPW command\n", my_id));
+                   if (num_args == 2) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHECKPW command syntax OK\n", my_id));
+                       cmd_error = 0;
+                       err_str[0] = '\0';
+#if 0
+                       cmd_error = pwd_check(kcontext,
+                                             proto_debug_level,
+                                             auth_context,
+                                             ticket,
+                                             &arglist[1],
+                                             &err_str);
+#endif
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHECKPW command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+
+                       strcpy(err_str,
+                              "Bad argument list format for checkpw command.");
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data, KRB5_ADM_CHANGEPW_CMD)) {
+                   /*
+                    * CHANGEPW command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:CHANGEPW command\n", my_id));
+                   if (num_args == 3) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHANGEPW command syntax OK\n", my_id));
+                       cmd_error = pwd_change(kcontext,
+                                              proto_debug_level,
+                                              auth_context,
+                                              ticket,
+                                              &arglist[1],
+                                              &arglist[2],
+                                              &err_str);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHANGEPW command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN; 
+                       strcpy(err_str,
+                              "Bad argument list format for changepw command.");
+                   }
+               }
+#if 0
+#ifdef MOTD_SUPPORTED
+               else if (!strcasecmp(arglist[0].data, KRB5_ADM_MOTD_CMD)) {
+                   /*
+                    * MOTD command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:MOTD command\n", my_id));
+                   if (num_args <= 2) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MOTD command syntax OK\n", my_id));
+                       printf("@@@ motd command ");
+                       if (num_args == 2)
+                           printf("context is %s", arglist[2].data);
+                       printf("\n");
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MOTD command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+#endif /* MOTD_SUPPORTED */
+#ifdef MIME_SUPPORTED
+               else if (!strcasecmp(arglist[0].data, KRB5_ADM_MIME_CMD)) {
+                   /*
+                    * MIME command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:MIME command\n", my_id));
+                   if (num_args == 1) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MIME command syntax OK\n", my_id));
+                       mime_setting = 1;
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MIME command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+#endif /* MIME_SUPPORTED */
+#ifdef LANGUAGES_SUPPORTED
+               else if (!strcasecmp(arglist[0].data, KRB5_ADM_LANGUAGE_CMD)) {
+                   /*
+                    * LANGUAGE command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:LANGUAGE command\n", my_id));
+                   if (num_args == 2) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:LANGUAGE command syntax OK\n", my_id));
+                       if (output_lang_supported(arglist[1].data)) {
+                           if (curr_lang)
+                               free(curr_lang);
+                           curr_lang = (char *)
+                               malloc(strlen(arglist[1].data));
+                           if (curr_lang)
+                               strcpy(curr_lang, arglist[1].data);
+                       }
+                       else
+                           cmd_error = KRB5_ADM_LANG_NOT_SUPPORTED;
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:LANGUAGE command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+#endif /* LANGUAGES_SUPPORTED */
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_ADD_PRINC_CMD)) {
+                   /*
+                    * ADD PRINCIPAL command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:ADD PRINCIPAL command\n", my_id));
+                   /* At least one argument */
+                   if (num_args > 1) {
+                       cmd_error = admin_add_principal(kcontext,
+                                                       proto_debug_level,
+                                                       ticket,
+                                                       num_args-1,
+                                                       &arglist[1]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:ADD PRINCIPAL command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_DEL_PRINC_CMD)) {
+                   /*
+                    * DELETE PRINCIPAL command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:DELETE PRINCIPAL command\n", my_id));
+                   /* Only one argument */
+                   if (num_args == 2) {
+                       cmd_error = admin_delete_principal(kcontext,
+                                                          proto_debug_level,
+                                                          ticket,
+                                                          &arglist[1]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:DELETE PRINCIPAL command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_REN_PRINC_CMD)) {
+                   /*
+                    * RENAME PRINCIPAL command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:RENAME PRINCIPAL command\n", my_id));
+                   /* Two arguments */
+                   if (num_args == 3) {
+                       cmd_error = admin_rename_principal(kcontext,
+                                                          proto_debug_level,
+                                                          ticket,
+                                                          &arglist[1],
+                                                          &arglist[2]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:RENAME PRINCIPAL command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_MOD_PRINC_CMD)) {
+                   /*
+                    * MODIFY PRINCIPAL command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:MODIFY PRINCIPAL command\n", my_id));
+                   /* At least one argument */
+                   if (num_args > 1) {
+                       cmd_error = admin_modify_principal(kcontext,
+                                                          proto_debug_level,
+                                                          ticket,
+                                                          num_args-1,
+                                                          &arglist[1]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MODIFY PRINCIPAL command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_CHG_OPW_CMD)) {
+                   /*
+                    * CHANGE OTHER'S PASSWORD command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:CHANGE OTHER'S PASSWORD command\n", my_id));
+                   /* Two arguments */
+                   if (num_args == 3) {
+                       cmd_error = admin_change_opwd(kcontext,
+                                                     proto_debug_level,
+                                                     ticket,
+                                                     &arglist[1],
+                                                     &arglist[2]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHANGE OTHER'S PASSWORD command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_CHG_ORPW_CMD)) {
+                   /*
+                    * CHANGE OTHER'S RANDOM PASSWORD command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:CHANGE OTHER'S RANDOM PASSWORD command\n", my_id));
+                   /* One argument */
+                   if (num_args == 2) {
+                       cmd_error = admin_change_orandpwd(kcontext,
+                                                         proto_debug_level,
+                                                         ticket,
+                                                         &arglist[1]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHANGE OTHER'S RANDOM PASSWORD command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_INQ_PRINC_CMD)) {
+                   /*
+                    * INQUIRE PRINCIPAL command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:INQUIRE PRINCIPAL command\n", my_id));
+                   /* One argument */
+                   if (num_args == 2) {
+                       cmd_error = admin_inquire(kcontext,
+                                                 proto_debug_level,
+                                                 ticket,
+                                                 &arglist[1],
+                                                 &cmd_repl_ncomps,
+                                                 &cmd_repl_complist);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:INQUIRE PRINCIPAL command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_EXT_KEY_CMD)) {
+                   /*
+                    * EXTRACT KEY command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:EXTRACT KEY command\n", my_id));
+                   /* Two arguments */
+                   if (num_args == 3) {
+                       cmd_error = admin_extract_key(kcontext,
+                                                     proto_debug_level,
+                                                     ticket,
+                                                     &arglist[1],
+                                                     &arglist[2],
+                                                     &cmd_repl_ncomps,
+                                                     &cmd_repl_complist);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:EXTRACT KEY command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_ADD_KEY_CMD)) {
+                   /*
+                    * ADD KEY command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:ADD KEY command\n", my_id));
+                   /* Must have at least three arguments */
+                   if (num_args > 3) {
+                       cmd_error = admin_add_key(kcontext,
+                                                 proto_debug_level,
+                                                 ticket,
+                                                 num_args-1,
+                                                 &arglist[1]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:ADD KEY command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcasecmp(arglist[0].data,
+                                    KRB5_ADM_DEL_KEY_CMD)) {
+                   /*
+                    * DELETE KEY command handling here.
+                    */
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:DELETE KEY command\n", my_id));
+                   /* At least three arguments */
+                   if (num_args > 3) {
+                       cmd_error = admin_delete_key(kcontext,
+                                                    proto_debug_level,
+                                                    ticket,
+                                                    num_args-1,
+                                                    &arglist[1]);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:DELETE KEY command syntax BAD\n",
+                               my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KADM_BAD_ARGS;
+                   }
+               }
+#endif /* 0 */
+               else {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:UNKNOWN command %s\n", my_id,
+                         arglist[0].data));
+                   cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                   sprintf(err_str, "Command %s not supported.", arglist[0]);
+               }
+           }
+           else {
+               DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                      ("> %d:NO command!\n", my_id));
+               cmd_error = KRB5_ADM_CMD_UNKNOWN;
+               strcpy(err_str, "No command in message.");
+           }
+
+           /*
+            * Close the database.
+            */
+           if ((kret = key_close_db(kcontext))) {
+               com_err(programname, kret, proto_db_close_msg, my_id);
+               goto cleanup;
+           }
+           else
+               db_opened = 0;
+
+           /*
+            * Now make the reply.
+            */
+           DPRINT(DEBUG_PROTO, proto_debug_level,
+                  ("= %d:sending reply(stat=%d)\n", my_id, cmd_error));
+           if (cmd_error == KRB5_ADM_SUCCESS) {
+               kret = krb5_send_adm_reply(kcontext,
+                                          (krb5_pointer) &cl_sock,
+                                          auth_context,
+                                          cmd_error,
+                                          cmd_repl_ncomps,
+                                          cmd_repl_complist);
+               if (kret) {
+                   com_err(programname, kret, proto_wr_reply_msg, my_id);
+                   goto cleanup;
+               }
+           }
+           else {
+               char            *adm_errmsg;
+               krb5_data       reply_comps;
+
+               reply_comps.data = err_str;
+               reply_comps.length = strlen(err_str);
+               kret = krb5_send_adm_reply(kcontext,
+                                          (krb5_pointer) &cl_sock,
+                                          auth_context,
+                                          cmd_error,
+                                          1,
+                                          &reply_comps);
+               if (kret) {
+                   com_err(programname, kret, proto_wr_reply_msg, my_id);
+                   goto cleanup;
+               }
+           }
+           if (cmd_repl_ncomps > 0)
+               krb5_free_adm_data(kcontext,
+                                  cmd_repl_ncomps,
+                                  cmd_repl_complist);
+
+           if (do_quit)
+               break;
+           krb5_free_adm_data(kcontext, num_args, arglist);
+       }
+    }
+    else {
+       DPRINT(DEBUG_REQUESTS, proto_debug_level, ("connection timed out"));
+    }
+
+    
+ err_reply:
+    if (kret) {
+       krb5_error_code er_kret;
+       krb5_error      errbuf;
+       char            *errmsg;
+       krb5_data       errout;
+
+       memset((char *) &errbuf, 0, sizeof(errbuf));
+       krb5_us_timeofday(kcontext, &errbuf.stime, &errbuf.susec);
+       errbuf.server = net_server_princ();
+       errbuf.error = kret - ERROR_TABLE_BASE_krb5;
+       if (errbuf.error > 127)
+           errbuf.error = KRB5KRB_ERR_GENERIC;
+       /* Format the error message in our language */
+       errmsg = error_message(kret);
+       errbuf.text.length = strlen(errmsg);
+       errbuf.text.data = errmsg;
+       er_kret = krb5_mk_error(kcontext, &errbuf, &errout);
+       if (!er_kret)
+           krb5_write_message(kcontext, (krb5_pointer) &cl_sock, &errout);
+       free(errbuf.text.data);
+       krb5_xfree(errout.data);
+    }
+
+ cleanup:
+    /* If the alarm was set, make sure it's cancelled */
+    if (proto_proto_timeout > 0)
+       alarm(0);
+    if (ticket)
+       krb5_free_ticket(kcontext, ticket);
+    /*
+     * Don't need to close the replay cache because it's attached to the
+     * auth context.
+     */
+    if (auth_context)
+       krb5_auth_con_free(kcontext, auth_context);
+    if (curr_lang)
+       free(curr_lang);
+    if (num_args)
+       krb5_free_adm_data(kcontext, num_args, arglist);
+    if (in_data.data)
+       krb5_xfree(in_data.data);
+    if (out_data.data)
+       krb5_xfree(out_data.data);
+    if (local && local->contents)
+       free(local->contents);
+    if (remote && remote->contents)
+       free(remote->contents);
+    if (local)
+       free(local);
+    if (remote)
+       free(remote);
+    if (db_opened)
+       key_close_db(kcontext);
+    close(cl_sock);
+
+ done:
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_serv() = %d\n", kret));
+    return(kret);
+}
diff --git a/src/kadmin/v5passwdd/srv_net.c b/src/kadmin/v5passwdd/srv_net.c
new file mode 100644 (file)
index 0000000..eefc898
--- /dev/null
@@ -0,0 +1,856 @@
+/*
+ * kadmin/v5server/srv_net.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * srv_net.c - handle networking functions of the administrative server.
+ */
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/wait.h>
+#include <signal.h>
+#ifdef USE_PTHREADS
+#include <pthread.h>
+#endif /* USE_PTHREADS */
+
+#define        NEED_SOCKETS
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+#include "adm.h"
+
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+/* linux doesn't have SOMAXCONN */
+#ifndef SOMAXCONN
+#define SOMAXCONN 5
+#endif
+
+/*
+ * This module can use the pthreads library.  To do so, define USE_PTHREADS.
+ * You'll need to find out what else pthreads requires (e.g. -lmach -lc_r
+ * under OSF/1).
+ */
+#ifdef USE_PTHREADS
+#define        net_slave_type  pthread_t *
+#ifndef        MAX_SLAVES
+#define        MAX_SLAVES      2*SOMAXCONN
+#endif /* MAX_SLAVES */
+#else  /* USE_PTHREADS */
+#define        net_slave_type  pid_t
+#ifndef        MAX_SLAVES
+#define        MAX_SLAVES      2*SOMAXCONN
+#endif /* MAX_SLAVES */
+#endif /* USE_PTHREADS */
+#define        NET_SLAVE_FULL_SLEEP    2       /* seconds */
+
+/*
+ * Slave information storage.
+ */
+typedef struct _net_slave_info {
+    int                                sl_inuse;
+    net_slave_type             sl_id;
+    krb5_context               sl_context;
+    int                                sl_socket;
+    struct sockaddr_in         sl_local_addr;  /* local address */
+    struct sockaddr_in         sl_remote_addr; /* remote address */
+} net_slave_info;
+
+/*
+ * Error messages.
+ */
+static const char *net_waiterr_msg = "\004child wait failed - cannot reap children";
+static const char *net_def_realm_fmt = "%s: cannot get default realm (%s).\n";
+static const char *net_no_mem_fmt = "%s: cannot get memory.\n";
+static const char *net_parse_srv_fmt = "%s: cannot parse server name %s (%s).\n";
+static const char *net_no_hostname_fmt = "%s: cannot get our host name (%s).\n";
+static const char *net_no_hostent_fmt = "%s: cannot get our host entry (%s).\n";
+static const char *net_no_servent_fmt = "%s: cannot get service entry for %s (%s).\n";
+static const char *net_sockerr_fmt = "%s: cannot open network socket (%s).\n";
+static const char *net_soerr_fmt = "%s: cannot set socket options (%s).\n";
+static const char *net_binderr_fmt = "%s: cannot bind to network address (%s).\n";
+
+static const char *net_select_fmt = "\004select failed";
+static const char *net_cl_disp_fmt = "\004client dispatch failed";
+static const char *net_not_ready_fmt = "\004select error - no socket to read";
+static const char *net_dispatch_msg = "network dispatch";
+
+static int net_debug_level = 0;
+
+static char            *net_service_name = (char *) NULL;
+static int             net_service_princ_init = 0;
+static krb5_principal  net_service_principal = (krb5_principal) NULL;
+static int             net_server_addr_init = 0;
+static struct sockaddr_in      net_server_addr;
+static int             net_listen_socket = -1;
+static int             net_slaves_active = 0;
+static int             net_max_slaves = 0;
+static net_slave_info  *net_slave_table = (net_slave_info *) NULL;
+
+#if    POSIX_SETJMP
+static sigjmp_buf      shutdown_jmp;
+#else  /* POSIX_SETJMP */
+static jmp_buf         shutdown_jmp;
+#endif /* POSIX_SETJMP */
+
+extern char *programname;
+\f
+/*
+ * net_find_free_entry()       - Find a free entry in the slave table.
+ */
+static net_slave_info *
+net_find_free_entry()
+{
+    int i, found;
+
+    /* Find a table entry */
+    while (1) {
+       found = 0;
+       for (i=0; i<net_max_slaves; i++) {
+           if (!net_slave_table[i].sl_inuse) {
+               net_slave_table[i].sl_inuse = 1;
+               found = 1;
+               break;
+           }
+       }
+       if (found)
+           break;
+       sleep(NET_SLAVE_FULL_SLEEP);
+    }
+    return(&net_slave_table[i]);
+}
+\f
+/*
+ * net_find_slave()    - Find a slave entry by its identity.
+ */
+static net_slave_info *
+net_find_slave(id)
+    net_slave_type     id;
+{
+    int i, found;
+
+    for (i=0; i<net_max_slaves; i++) {
+       if (net_slave_table[i].sl_inuse &&
+           (net_slave_table[i].sl_id == id)) {
+           found = 1;
+           break;
+       }
+    }
+    if (found)
+       return(&net_slave_table[i]);
+    else
+       return((net_slave_info *) NULL);
+}
+
+/*
+ * net_free_slave_entry()      - Mark an entry as free.
+ */
+static void
+net_free_slave_entry(entp)
+    net_slave_info *entp;
+{
+    entp->sl_inuse = 0;
+}
+\f
+/*
+ * net_shutdown()      - Destroy all slaves on signal reception
+ */
+static krb5_sigtype
+net_shutdown(signo)
+    int signo;
+{
+    int i;
+
+    /* Loop through all slaves */
+    for (i=0; i<net_max_slaves; i++) {
+       if (net_slave_table[i].sl_inuse){
+#ifdef DEBUG
+           /* If not us (see net_dispatch_client) */
+           if (net_slave_table[i].sl_id != (net_slave_type) getpid()) {
+#endif /* DEBUG */
+#if    USE_PTHREADS
+               pthread_cancel(*net_slave_table[i].sl_id);
+#else  /* USE_PTHREADS */
+               kill(net_slave_table[i].sl_id, SIGKILL);
+#endif /* USE_PTHREADS */
+#ifdef DEBUG
+           }
+#endif /* DEBUG */
+       }
+    }
+    sleep(5);          /* to allow children to die and be reaped */
+#if    POSIX_SETJMP
+    siglongjmp(shutdown_jmp, 1);
+#else  /* POSIX_SETJMP */
+    longjmp(shutdown_jmp, 1);
+#endif /* POSIX_SETJMP */
+    /* NOTREACHED */
+}
+\f
+#if    !USE_PTHREADS
+/*
+ * net_reaper()        - Child process termination handler.
+ */
+static krb5_sigtype
+net_reaper(signo)
+    int signo;
+{
+#ifdef WAIT_USES_INT
+    int                child_exit;
+#else  /* WAIT_USES_INT */
+    union wait         child_exit;
+#endif /* WAIT_USES_INT */
+    pid_t              deadmeat;
+    net_slave_info     *slent;
+
+    /* Reap everybody we can */
+    while (
+          (
+#ifdef HAVE_WAITPID
+           deadmeat = waitpid((pid_t) -1, &child_exit, WNOHANG)
+#else  /* HAVE_WAITPID */
+           deadmeat = wait3(&child_exit, WNOHANG, (struct rusage *) NULL)
+#endif /* HAVE_WAITPID */
+           ) > 0) {
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| process %d finished with %d\n", deadmeat, child_exit));
+       slent = net_find_slave(deadmeat);
+       if (slent) {
+           net_free_slave_entry(slent);
+       }
+       else {
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| cannot find slave entry for %d\n", deadmeat));
+       }
+    }
+    if ((deadmeat == -1) && (errno != ECHILD))
+       com_err(programname, errno, net_waiterr_msg);
+}
+#endif /* USE_PTHREADS */
+\f
+#if    USE_PTHREADS
+/*
+ * net_slave_proto()   - pthread main routine.
+ */
+static krb5_error_code
+net_slave_proto(stent)
+    net_slave_info     *stent;
+{
+    krb5_error_code    kret;
+
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("* net_slave_proto()\n"));
+    DPRINT(DEBUG_SPROC, net_debug_level,
+          ("| thread %d starting\n", stent->sl_id));
+    kret = proto_serv(stent->sl_context,
+                     (krb5_int32) stent->sl_id,
+                     stent->sl_socket,
+                     &stent->sl_local_addr,
+                     &stent->sl_remote_addr);
+    DPRINT(DEBUG_SPROC, net_debug_level,
+          ("| thread %d finished with %d\n", stent->sl_id, kret));
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("* net_slave_proto() = %d\n", kret));
+    net_free_slave_entry(stent);
+    return(kret);
+}
+#endif /* USE_PTHREADS */
+\f
+/*
+ * net_dispatch_client()       - Handle client dispatch.
+ */
+static krb5_error_code
+net_dispatch_client(kcontext, listen_sock, conn_sock, client_addr)
+    krb5_context       kcontext;
+    int                        listen_sock;
+    int                        conn_sock;
+    struct sockaddr_in *client_addr;
+{
+    krb5_error_code    kret;
+    net_slave_info     *slent;
+
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("* net_dispatch_client(listen=%d)\n", listen_sock));
+    kret = 0;
+
+    /* Find a free entry */
+    slent = net_find_free_entry();
+
+    /* Initialize the slave entry */
+    slent->sl_context = kcontext;
+    slent->sl_socket = conn_sock;
+    memcpy((char *) &slent->sl_remote_addr,
+          (char *) client_addr,
+          sizeof(struct sockaddr_in));
+    memcpy((char *) &slent->sl_local_addr,
+          (char *) &net_server_addr,
+          sizeof(struct sockaddr_in));
+
+#ifdef DEBUG
+    if ((net_debug_level & DEBUG_NOSLAVES) == 0) {
+#endif /* DEBUG */
+       /* Do a real slave creation */
+#if    USE_PTHREADS
+       if (!slent->sl_id)
+           slent->sl_id = (pthread_t *) malloc(sizeof(pthread_t));
+       if (slent->sl_id == (pthread_t *) NULL) {
+           kret = ENOMEM;
+           goto done;
+       }
+       if (kret = pthread_create(slent->sl_id,
+                                 pthread_attr_default,
+                                 (pthread_startroutine_t) net_slave_proto,
+                                 (pthread_addr_t) slent)) {
+           kret = errno;
+           goto done;
+       }
+       if (pthread_detach(slent->sl_id)) {
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| (%d) child thread %d detach failed (%d)\n",
+                   getpid(), slent->sl_id, errno));
+       }
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| (%d) created child thread %d\n",
+               getpid(), slent->sl_id));
+#else  /* USE_PTHREADS */
+       slent->sl_id = fork();
+       if (slent->sl_id < 0) {
+           kret = errno;
+           slent->sl_inuse = 0;
+           goto done;
+       }
+
+       if (slent->sl_id > 0) {
+           /* parent */
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| (%d) created child process %d\n",
+                   getpid(), slent->sl_id));
+           close(conn_sock);
+           kret = 0;
+           goto done;
+       }
+       else {
+#if    POSIX_SIGNALS
+           struct sigaction s_action;
+#endif /* POSIX_SIGNALS */
+
+           /* child */
+#if    POSIX_SIGNALS
+           (void) sigemptyset(&s_action.sa_mask);
+           s_action.sa_flags = 0;
+           /* Ignore SIGINT, SIGTERM, SIGHUP, SIGQUIT and SIGPIPE */
+           s_action.sa_handler = SIG_IGN;
+           (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
+           (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
+           (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
+           (void) sigaction(SIGQUIT, &s_action, (struct sigaction *) NULL);
+           (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
+           /* Restore to default SIGCHLD */
+           s_action.sa_handler = SIG_DFL;
+           (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
+#else  /* POSIX_SIGNALS */
+           signal(SIGINT, SIG_IGN);    /* Ignore SIGINT */
+           signal(SIGTERM, SIG_IGN);   /* Ignore SIGTERM */
+           signal(SIGHUP, SIG_IGN);    /* Ignore SIGHUP */
+           signal(SIGQUIT, SIG_IGN);   /* Ignore SIGQUIT */
+           signal(SIGPIPE, SIG_IGN);   /* Ignore SIGPIPE */
+           signal(SIGCHLD, SIG_DFL);   /* restore SIGCHLD handling */
+#endif /* POSIX_SIGNALS */
+           close(listen_sock);
+           slent->sl_id = getpid();
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| process %d starting\n", slent->sl_id));
+           kret = proto_serv(slent->sl_context,
+                             (krb5_int32) slent->sl_id,
+                             slent->sl_socket,
+                             &slent->sl_local_addr,
+                             &slent->sl_remote_addr);
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| process %d exiting with %d\n", getpid(), kret));
+           exit(kret);
+       }
+#endif /* USE_PTHREADS */
+#ifdef DEBUG
+    }
+    else {
+       net_slave_info *sl1;
+#if    POSIX_SIGNALS
+       struct sigaction s_action;
+#endif /* POSIX_SIGNALS */
+
+       /*
+        * Ignore SIGPIPE.
+        */
+#if    POSIX_SIGNALS
+       (void) sigemptyset(&s_action.sa_mask);
+       s_action.sa_flags = 0;
+       s_action.sa_handler = SIG_IGN;
+       (void) sigaction(SIGPIPE, &s_action, (struct sigaction *) NULL);
+#else  /* POSIX_SIGNALS */
+       signal(SIGPIPE, SIG_IGN);       /* Ignore SIGPIPE */
+#endif /* POSIX_SIGNALS */
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| (%d) not doing child creation\n", getpid()));
+       slent->sl_id = (net_slave_type) getpid();
+       kret = proto_serv(slent->sl_context,
+                         (krb5_int32) slent->sl_id,
+                         slent->sl_socket,
+                         &slent->sl_local_addr,
+                         &slent->sl_remote_addr);
+       sl1 = net_find_slave(slent->sl_id);
+       if (sl1)
+           net_free_slave_entry(sl1);
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| (%d) returned with %d\n", getpid(), kret));
+       kret = 0;
+    }
+#endif /* DEBUG */
+ done:
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("X net_dispatch_client() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * net_init()  - Initialize network context.
+ */
+krb5_error_code
+net_init(kcontext, realm, debug_level, port)
+    krb5_context       kcontext;
+    char *             realm;
+    int                        debug_level;
+    krb5_int32         port;
+{
+    krb5_error_code    kret;
+    char               our_host_name[MAXHOSTNAMELEN];
+    struct hostent     *our_hostent;
+    struct servent     *our_servent;
+
+    net_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, net_debug_level, ("* net_init(port=%d)\n", port));
+
+    /* Allocate the slave table */
+    net_slave_table = (net_slave_info *)
+       malloc((size_t) (MAX_SLAVES * sizeof(net_slave_info)));
+    /* Make our service name */
+    net_service_name = (char *) malloc(strlen(realm) +
+                                      strlen(KRB5_ADM_SERVICE_INSTANCE) + 2);
+    if ((net_service_name == (char *) NULL) ||
+       (net_slave_table == (net_slave_info *) NULL)) {
+       kret = ENOMEM;
+       fprintf(stderr, net_no_mem_fmt, programname);
+       goto done;
+    }
+    (void) sprintf(net_service_name, "%s%s%s",
+                  KRB5_ADM_SERVICE_INSTANCE, "/", realm);
+    memset((char *) net_slave_table, 0,
+          (size_t) (MAX_SLAVES * sizeof(net_slave_info)));
+    net_max_slaves = MAX_SLAVES;
+    DPRINT(DEBUG_HOST, net_debug_level,
+          ("- name of service is %s\n", net_service_name));
+
+    /* Now formulate the principal name */
+    if (kret = krb5_parse_name(kcontext,
+                              net_service_name,
+                              &net_service_principal)) {
+       fprintf(stderr, net_parse_srv_fmt, programname, net_service_name,
+               error_message(kret));
+       goto done;
+    }
+    net_service_princ_init = 1;
+
+#ifdef KRB5_USE_INET
+    /* Now get our host name/entry */
+    if (gethostname(our_host_name, sizeof(our_host_name))) {
+       kret = errno;
+       fprintf(stderr, net_no_hostname_fmt, programname, error_message(kret));
+       goto done;
+    }
+    if (!(our_hostent = gethostbyname(our_host_name))) {
+       kret = KRB5_ERR_BAD_HOSTNAME; /* perhaps put h_errno in the msg */
+       fprintf(stderr, net_no_hostent_fmt, programname, error_message(kret));
+       goto done;
+    }
+    DPRINT(DEBUG_HOST, net_debug_level,
+          ("- name of host is %s\n", our_hostent->h_name));
+
+    /* Now initialize our network address */
+    net_server_addr.sin_family = AF_INET;
+    memcpy((char *) &net_server_addr.sin_addr,
+          (char *) our_hostent->h_addr,
+          sizeof(net_server_addr.sin_addr));
+    DPRINT(DEBUG_HOST, net_debug_level,
+          ("- address of host is %x\n",
+           ntohl(net_server_addr.sin_addr.s_addr)));
+
+    /*
+     * Fill in the port address.
+     * If the port is supplied by the invoker, then use that one.
+     * If not, then try the profile, and if all fails, then use the service
+     *         entry.
+     */
+    if (port > 0) {
+       net_server_addr.sin_port = htons(port);
+       DPRINT(DEBUG_HOST, net_debug_level,
+              ("- service name (%s) is on port %d from options\n",
+               KRB5_ADM_SERVICE_NAME,
+               ntohs(net_server_addr.sin_port)));
+    }
+    else {
+       char            **admin_hostlist;
+       const char      *realm_admin_names[4];  /* XXX */
+       krb5_boolean    found;
+
+       /*
+        * Try to get the service entry out of the profile.
+        */
+       admin_hostlist = (char **) NULL;
+       realm_admin_names[0] = "realms";
+       realm_admin_names[1] = realm;
+       realm_admin_names[2] = "admin_server";
+       realm_admin_names[3] = (char *) NULL;
+       found = 0;
+#ifndef        OLD_CONFIG_FILES
+       if (!(kret = profile_get_values(kcontext->profile,
+                                       realm_admin_names,
+                                       &admin_hostlist))) {
+           int         hi;
+           char        *cport;
+           char        *cp;
+           krb5_int32  pport;
+           int         ai;
+
+           cport = (char *) NULL;
+           pport = KRB5_ADM_DEFAULT_PORT;
+           for (hi=0; admin_hostlist[hi]; hi++) {
+               /*
+                * This knows a little too much about the format of profile
+                * entries.  Shouldn't it just be some sort of tuple?
+                *
+                * The form is assumed to be:
+                *      admin_server = <hostname>[:<portname>[<whitespace>]]
+                */
+               cp = strchr(admin_hostlist[hi], ' ');
+               if (cp)
+                   *cp = '\0';
+               cp = strchr(admin_hostlist[hi], '\t');
+               if (cp)
+                   *cp = '\0';
+               cport = strchr(admin_hostlist[hi], ':');
+               if (cport) {
+                   *cport = '\0';
+                   cport++;
+                   if (sscanf(cport, "%d", &pport) != 1) {
+                       DPRINT(DEBUG_HOST, net_debug_level,
+                              ("- profile entry for %s has bad port %s\n",
+                               admin_hostlist[hi],
+                               cport));
+                       pport = KRB5_ADM_DEFAULT_PORT;
+                   }
+               }
+               /*
+                * We've stripped away the crud.  Now check to see if the
+                * profile entry matches our hostname.  If so, then this
+                * is the one to use.  Additionally, check the host alias
+                * list.
+                */
+               if (!strcmp(admin_hostlist[hi], our_hostent->h_name)) {
+                   net_server_addr.sin_port = ntohs((u_short) pport);
+                   DPRINT(DEBUG_HOST, net_debug_level,
+                          ("- service name (%s) is on port %d from profile\n",
+                           KRB5_ADM_SERVICE_NAME,
+                           pport));
+                   found = 1;
+               }
+               else {
+                   for (ai=0; our_hostent->h_aliases[ai]; ai++) {
+                       if (!strcmp(admin_hostlist[hi],
+                                   our_hostent->h_aliases[ai])) {
+                           net_server_addr.sin_port = ntohs(pport);
+                           DPRINT(DEBUG_HOST, net_debug_level,
+                                  ("- service name (%s) is on port %d from profile and alias\n",
+                                   KRB5_ADM_SERVICE_NAME,
+                                   pport));
+                           found = 1;
+                           break;
+                       }
+                   }
+               }
+           }
+           krb5_xfree(admin_hostlist);
+       }
+#endif /* OLD_CONFIG_FILES */
+
+       /*
+        * If we didn't find an entry in the profile, then as a last gasp
+        * effort, attempt to find it in /etc/services.
+        */
+       if (!found) {
+           /* Get the service entry out of /etc/services */
+           if (!(our_servent = getservbyname(KRB5_ADM_SERVICE_NAME, "tcp"))) {
+               kret = errno;
+               fprintf(stderr, net_no_servent_fmt, programname,
+                       KRB5_ADM_SERVICE_NAME, error_message(kret));
+               goto done;
+           }
+           net_server_addr.sin_port = our_servent->s_port;
+           DPRINT(DEBUG_HOST, net_debug_level,
+                  ("- service name (%s) is on port %d from services\n",
+                   our_servent->s_name,
+                   ntohs(our_servent->s_port)));
+       }
+    }
+    net_server_addr_init = 1;
+
+    /* Now open the listen socket */
+    net_listen_socket = socket(AF_INET, SOCK_STREAM, 0);
+    if (net_listen_socket < 0) {
+       kret = errno;
+       fprintf(stderr, net_sockerr_fmt, programname, error_message(kret));
+       goto done;
+    }
+
+    /* If we have a non-default port number, then allow reuse of address */
+    if (net_server_addr.sin_port != htons(KRB5_ADM_DEFAULT_PORT)) {
+       int     allowed;
+
+       allowed = 1;
+       if (setsockopt(net_listen_socket,
+                      SOL_SOCKET,
+                      SO_REUSEADDR,
+                      (char *) &allowed,
+                      sizeof(allowed)) < 0) {
+           kret = errno;
+           fprintf(stderr, net_soerr_fmt, programname, error_message(kret));
+           goto done;
+       }
+    }
+
+    /* Bind socket */
+    if (bind(net_listen_socket,
+            (struct sockaddr *) &net_server_addr,
+            sizeof(net_server_addr)) < 0) {
+       kret = errno;
+       fprintf(stderr, net_binderr_fmt, programname, error_message(kret));
+       goto done;
+    }
+    else {
+       DPRINT(DEBUG_HOST, net_debug_level,
+              ("- bound socket %d on port\n", net_listen_socket));
+       kret = 0;
+    }
+#else  /* KRB5_USE_INET */
+    /* Don't know how to do anything else. */
+    kret = ENOENT;
+#endif /* KRB5_USE_INET */
+
+ done:
+    DPRINT(DEBUG_CALLS, net_debug_level, ("X net_init() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * net_finish()        - Finish network context.
+ */
+void
+net_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, net_debug_level, ("* net_finish()\n"));
+    if (net_max_slaves) {
+       net_max_slaves = 0;
+       free(net_slave_table);
+    }
+    if (net_listen_socket >= 0)
+       close(net_listen_socket);
+    if (net_service_princ_init)
+       krb5_free_principal(kcontext, net_service_principal);
+    if (net_service_name)
+       free(net_service_name);
+    DPRINT(DEBUG_CALLS, net_debug_level, ("X net_finish()\n"));
+}
+\f
+/*
+ * net_dispatch()      - Listen and dispatch request.
+ *
+ * Loop forever selecting on the listen socket.  When an incoming connection
+ * comes in, dispatch to net_client_connect().
+ */
+krb5_error_code
+net_dispatch(kcontext, detached)
+    krb5_context       kcontext;
+    int                        detached;
+{
+    krb5_error_code    kret;
+    fd_set             mask, readfds;
+    int                        nready;
+#if    POSIX_SIGNALS
+    struct sigaction   s_action;
+#endif /* POSIX_SIGNALS */
+
+    DPRINT(DEBUG_CALLS, net_debug_level, ("* net_dispatch()\n"));
+
+    kret = 0;
+
+    /* Set up the fdset mask */
+    FD_ZERO(&mask);
+    FD_SET(net_listen_socket, &mask);
+
+#if    POSIX_SIGNALS
+    (void) sigemptyset(&s_action.sa_mask);
+    s_action.sa_flags = 0;
+    s_action.sa_handler = net_shutdown;
+    (void) sigaction(SIGTERM, &s_action, (struct sigaction *) NULL);
+#ifdef DEBUG
+    (void) sigaction(SIGINT, &s_action, (struct sigaction *) NULL);
+#endif /* DEBUG */
+    if (!detached)
+      (void) sigaction(SIGHUP, &s_action, (struct sigaction *) NULL);
+#else  /* POSIX_SIGNALS */
+    /*
+     * SIGTERM (or SIGINT, if debug, or SIGHUP if not detached) shuts us down.
+     */
+    signal(SIGTERM, net_shutdown);
+#ifdef DEBUG
+    signal(SIGINT, net_shutdown);
+#endif /* DEBUG */
+    if (!detached)
+      signal(SIGHUP, net_shutdown);
+#endif /* POSIX_SIGNALS */
+
+#if    !USE_PTHREADS
+#if    POSIX_SIGNALS
+    s_action.sa_handler = net_reaper;
+    (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
+#else  /* POSIX_SIGNALS */
+    /*
+     * SIGCHILD indicates end of child process life.
+     */
+    signal(SIGCHLD, net_reaper);
+#endif /* POSIX_SIGNALS */
+#endif /* !USE_PTHREADS */
+
+    /* Receive connections on the socket */
+    DPRINT(DEBUG_OPERATION, net_debug_level, ("+ listening on socket\n"));
+    if (
+#if    POSIX_SETJMP
+       sigsetjmp(shutdown_jmp, 1) == 0
+#else  /* POSIX_SETJMP */
+       setjmp(shutdown_jmp) == 0
+#endif /* POSIX_SETJMP */
+       ) {
+       if (listen(net_listen_socket, SOMAXCONN) < 0)
+           kret = errno;
+    }
+    else
+       kret = EINTR;
+    DPRINT(DEBUG_OPERATION, net_debug_level, ("+ listen done\n"));
+
+    while (kret == 0) {
+       /*
+        * Prepare to catch signals.
+        */
+       if (
+#if    POSIX_SETJMP
+           sigsetjmp(shutdown_jmp, 1) == 0
+#else  /* POSIX_SETJMP */
+           setjmp(shutdown_jmp) == 0
+#endif /* POSIX_SETJMP */
+           ) {
+           readfds = mask;
+           DPRINT(DEBUG_OPERATION, net_debug_level, ("+ doing select\n"));
+           if ((nready = select(net_listen_socket+1,
+                                &readfds,
+                                (fd_set *) NULL,
+                                (fd_set *) NULL,
+                                (struct timeval *) NULL)) == 0) {
+               DPRINT(DEBUG_OPERATION, net_debug_level, ("+ nobody ready\n"));
+               continue;       /* Nobody ready */
+           }
+
+           if ((nready < 0) && (errno != EINTR)) {
+               com_err(net_dispatch_msg, errno, net_select_fmt);
+               continue;
+           }
+
+           if (FD_ISSET(net_listen_socket, &readfds)) {
+               struct sockaddr_in      client_addr;
+               int                     addrlen;
+               int                     conn_sock;
+
+               addrlen = sizeof(client_addr);
+               DPRINT(DEBUG_OPERATION, net_debug_level,
+                      ("+ accept connection\n"));
+               while (((conn_sock = accept(net_listen_socket,
+                                           (struct sockaddr *) &client_addr,
+                                           &addrlen)) < 0) &&
+                      (errno == EINTR));
+
+               if (conn_sock < 0)  {
+                   kret = errno;
+                   break;
+               }
+               DPRINT(DEBUG_OPERATION, net_debug_level,
+                      ("+ accepted connection\n"));
+               kret = net_dispatch_client(kcontext,
+                                          net_listen_socket,
+                                          conn_sock,
+                                          &client_addr);
+               if (kret) {
+                   com_err(net_dispatch_msg, kret, net_cl_disp_fmt);
+                   continue;
+               }
+               DPRINT(DEBUG_OPERATION, net_debug_level,
+                      ("+ dispatch done\n"));
+           }
+           else {
+               com_err(net_dispatch_msg, 0, net_not_ready_fmt);
+               kret = EIO;
+           }
+       }
+       else {
+           DPRINT(DEBUG_OPERATION, net_debug_level,
+                  ("+ dispatch interrupted by SIGTERM\n"));
+           kret = 0;
+           break;
+       }
+    }
+
+    DPRINT(DEBUG_CALLS, net_debug_level, ("X net_dispatch() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * Return our service principal.
+ */
+krb5_principal
+net_server_princ()
+{
+    if (net_service_princ_init)
+       return(net_service_principal);
+    else
+       return((krb5_principal) NULL);
+}