Put the V4 kadmin server in its new place
authorTheodore Tso <tytso@mit.edu>
Tue, 25 Apr 1995 01:25:10 +0000 (01:25 +0000)
committerTheodore Tso <tytso@mit.edu>
Tue, 25 Apr 1995 01:25:10 +0000 (01:25 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@5469 dc483132-0cff-0310-8789-dd5450dbe970

15 files changed:
src/kadmin.v4/server/.Sanitize [new file with mode: 0644]
src/kadmin.v4/server/.cvsignore [new file with mode: 0644]
src/kadmin.v4/server/ChangeLog [new file with mode: 0644]
src/kadmin.v4/server/Makefile.in [new file with mode: 0644]
src/kadmin.v4/server/acl_files.c [new file with mode: 0644]
src/kadmin.v4/server/acl_files.doc [new file with mode: 0644]
src/kadmin.v4/server/admin_server.c [new file with mode: 0644]
src/kadmin.v4/server/configure.in [new file with mode: 0644]
src/kadmin.v4/server/kadm_err.et [new file with mode: 0644]
src/kadmin.v4/server/kadm_funcs.c [new file with mode: 0644]
src/kadmin.v4/server/kadm_ser_wrap.c [new file with mode: 0644]
src/kadmin.v4/server/kadm_server.c [new file with mode: 0644]
src/kadmin.v4/server/kadm_server.h [new file with mode: 0644]
src/kadmin.v4/server/kadm_stream.c [new file with mode: 0644]
src/kadmin.v4/server/kadm_supp.c [new file with mode: 0644]

diff --git a/src/kadmin.v4/server/.Sanitize b/src/kadmin.v4/server/.Sanitize
new file mode 100644 (file)
index 0000000..7568d4d
--- /dev/null
@@ -0,0 +1,46 @@
+# Sanitize.in for Kerberos V5
+
+# Each directory to survive it's way into a release will need a file
+# like this one called "./.Sanitize".  All keyword lines must exist,
+# and must exist in the order specified by this file.  Each directory
+# in the tree will be processed, top down, in the following order.
+
+# Hash started lines like this one are comments and will be deleted
+# before anything else is done.  Blank lines will also be squashed
+# out.
+
+# The lines between the "Do-first:" line and the "Things-to-keep:"
+# line are executed as a /bin/sh shell script before anything else is
+# done in this 
+
+Do-first:
+
+# All files listed between the "Things-to-keep:" line and the
+# "Files-to-sed:" line will be kept.  All other files will be removed.
+# Directories listed in this section will have their own Sanitize
+# called.  Directories not listed will be removed in their entirety
+# with rm -rf.
+
+Things-to-keep:
+
+.cvsignore
+ChangeLog
+Makefile.in
+acl_files.c
+acl_files.doc
+admin_server.c
+configure
+configure.in
+kadm_err.et
+kadm_funcs.c
+kadm_ser_wrap.c
+kadm_server.c
+kadm_server.h
+kadm_stream.c
+kadm_supp.c
+
+Things-to-lose:
+
+Do-last:
+
+# End of file.
diff --git a/src/kadmin.v4/server/.cvsignore b/src/kadmin.v4/server/.cvsignore
new file mode 100644 (file)
index 0000000..e8c05a6
--- /dev/null
@@ -0,0 +1 @@
+configure
diff --git a/src/kadmin.v4/server/ChangeLog b/src/kadmin.v4/server/ChangeLog
new file mode 100644 (file)
index 0000000..8b62939
--- /dev/null
@@ -0,0 +1,113 @@
+Thu Apr 20 23:21:42 1995  Theodore Y. Ts'o  (tytso@dcl)
+
+       * kadm_funcs.c: Don't #include <ndbm.h>, since that's
+               automatically included by k5-config.h
+
+Thu Apr 20 15:26:48 1995  Ezra Peisach  (epeisach@kangaroo.mit.edu)
+
+       * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): krb_int32 should be
+       krb5_int32. 
+
+       * acl_files.c: Declare acl_abort as static at top of file.
+
+Sun Apr 16 19:10:17 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): use krb_int32, not
+       long, for network 4 byte quantities. Should get rid of the
+       use of memcpy at some point.
+
+Sat Mar 25 16:59:55 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * kadm_funcs.c (kadm_entry2princ): pass kadm_context in to
+       krb5_524_conv_principal.
+
+Tue Mar 14 16:45:18 1995    <tytso@rsx-11.mit.edu>
+
+       * Makefile.in: Don't link in the V4 DES library; use the des425
+               library to avoid linking the DES code in twice.
+
+Thu Mar  2 12:25:13 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * Makefile.in (ISODELIB): Remove reference to $(ISODELIB).
+
+Wed Mar  1 16:30:08 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * kadm_server.c: Remove declataions of malloc(); should be done by
+               header files.
+
+       * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket
+               and -lnsl with WITH_NETLIB check.
+
+Tue Feb 28 02:24:56 1995  John Gilmore  (gnu at toad.com)
+
+       * admin_server.c, kadm_server.c, kadm-server.h:  Avoid 
+       <krb5/...> includes.
+
+Tue Feb  7 16:42:54 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * kadm_funcs.c (kadm_del_entry): fixed call to db_delete_principal.
+
+Wed Jan 25 18:42:42 1995  Mark Eichin  (eichin@tweedledumber.cygnus.com)
+
+       * kadm_server.h (DEL_ACL_FILE): new define, acl file for V4 delete
+       function.
+       * kadm_server.c (kadm_ser_add): new function, wrapper for V4 delete.
+       * kadm_funcs.c (check_access): declare int; add DEL.
+       (kadm_del_entry): new function, V4 delete from CNS.
+       (failadd): fix spelling error in log entry.
+
+Mon Dec 12 13:21:48 1994  Mark Eichin  (eichin@cygnus.com)
+
+       * kadm_funcs.c (kadm_entry2princ, kadm_princ2entry,
+       kadm_chg_srvtab): V4 and V5 max_life are in *different units* so
+       use the 60*5 conversion factor.
+
+Fri Nov 18 15:51:11 1994  Theodore Y. Ts'o  (tytso@dcl)
+
+       * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change): Add
+               magic numbers of keyblock structre.
+
+Fri Nov 18 01:11:58 1994  Mark Eichin  <eichin@cygnus.com>
+
+       * configure.in: use CHECK_SIGNALS instead of expansion (from
+       epeisach).
+
+Wed Oct 19 18:53:45 1994  Theodore Y. Ts'o  (tytso@dcl)
+
+       * kadm_ser_wrap.c (kadm_ser_init): Use krb5_use_cstype() to
+               initialize the master_encblock structure.
+
+Thu Sep 29 22:41:20 1994  Theodore Y. Ts'o  (tytso@dcl)
+
+       * Makefile.in: relink executable if libraries change
+
+Thu Sep 15 10:53:37 1994  Theodore Y. Ts'o  (tytso@dcl)
+
+       * admin_server.c (close_syslog, byebye): Move these two functions
+       before main(), so that they get declared properly.  Otherwise
+       suncc will refuse to compile the file.
+
+       * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change,
+       kadm_chg_srvtab): use krb5_timeofday instead of time(0).
+
+Thu Aug  4 16:37:33 1994  Tom Yu  (tlyu@dragons-lair)
+
+       * admin_server.c: pick up <sys/time.h> (needed to get FD_SET,
+       etc.)
+
+Sat Jul 16 09:21:22 1994  Tom Yu  (tlyu at dragons-lair)
+
+       * Makefile.in: no longer trying to install v4kadmind as krb5kdc
+       :-)
+       * configure.in: another try at making dbm libs dtrt
+
+Wed Jun 29 00:24:28 1994  Tom Yu  (tlyu at dragons-lair)
+
+       * admin_server.c: fixed calls that should have invoked
+       krb5_init_ets
+
+Sat Jun 25 09:07:48 1994  Tom Yu  (tlyu at dragons-lair)
+
+       * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to
+       krb5_build_principal
+
diff --git a/src/kadmin.v4/server/Makefile.in b/src/kadmin.v4/server/Makefile.in
new file mode 100644 (file)
index 0000000..44a1a1d
--- /dev/null
@@ -0,0 +1,56 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+LDFLAGS = -g
+
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+DBMLIB=
+KDBLIB=$(TOPLIBD)/libkdb5.a
+DEPKDBLIB=$(TOPLIBD)/libkdb5.a
+
+KRB4LIB = $(KRB4)/lib/libkrb.a $(TOPLIBD)/libdes425.a
+
+KLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(COMERRLIB) $(DBMLIB)
+DEPKLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(COMERRLIB) $(DBMLIB)
+
+DEPLIBS = $(DEPKDBLIB) $(DEPKLIB)
+
+LOCALINCLUDE=-I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I.
+
+SRCS = \
+       $(srcdir)/kadm_server.c \
+       $(srcdir)/kadm_funcs.c \
+       $(srcdir)/admin_server.c \
+       $(srcdir)/kadm_ser_wrap.c \
+       $(srcdir)/kadm_stream.c \
+       $(srcdir)/kadm_supp.c \
+       $(srcdir)/kadm_err.c \
+       $(srcdir)/acl_files.c
+OBJS = \
+       kadm_server.o \
+       kadm_funcs.o \
+       admin_server.o \
+       kadm_ser_wrap.o \
+       kadm_stream.o \
+       kadm_supp.o \
+       kadm_err.o \
+       acl_files.o
+
+all:: kadm_err.h v4kadmind
+
+depend:: kadm_err.c
+
+kadm_err.c: kadm_err.et
+
+kadm_err.h: kadm_err.et
+
+v4kadmind: $(OBJS) $(DEPLIBS)
+       $(CC) $(CFLAGS) -o v4kadmind $(OBJS) $(KDBLIB) $(KRB4LIB) $(KLIB) $(LIBS)
+
+install::
+       $(INSTALL_PROGRAM) v4kadmind ${DESTDIR}$(SERVER_BINDIR)/v4kadmind
+
+clean::
+       $(RM) kadm_err.h kadm_err.c
+
+clean::
+       $(RM) v4kadmind
+
diff --git a/src/kadmin.v4/server/acl_files.c b/src/kadmin.v4/server/acl_files.c
new file mode 100644 (file)
index 0000000..318c80c
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ *     kadmin/v4server/acl_files.c
+ *
+ * Copyright 1987,1989 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ */
+
+
+/*** Routines for manipulating access control list files ***/
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <ctype.h>
+#ifdef NEED_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#include "krb.h"
+
+#ifndef KRB_REALM
+#define KRB_REALM      "ATHENA.MIT.EDU"
+#endif
+
+/* "aname.inst@realm" */
+#define MAX_PRINCIPAL_SIZE  (ANAME_SZ + INST_SZ + REALM_SZ + 3)
+#define INST_SEP '.'
+#define REALM_SEP '@'
+
+#define LINESIZE 2048          /* Maximum line length in an acl file */
+
+#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */
+#define WAIT_TIME 300          /* Maximum time allowed write acl file */
+
+#define CACHED_ACLS 8          /* How many acls to cache */
+                               /* Each acl costs 1 open file descriptor */
+#define ACL_LEN 16             /* Twice a reasonable acl length */
+
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#define COR(a,b) ((a!=NULL)?(a):(b))
+
+extern int errno;
+
+extern char *malloc(), *calloc();
+extern time_t time();
+
+static int acl_abort();
+
+/* Canonicalize a principal name */
+/* If instance is missing, it becomes "" */
+/* If realm is missing, it becomes the local realm */
+/* Canonicalized form is put in canon, which must be big enough to hold
+   MAX_PRINCIPAL_SIZE characters */
+void acl_canonicalize_principal(principal, canon)
+char *principal;
+char *canon;
+{
+    char *dot, *atsign, *end;
+    int len;
+
+    dot = strchr(principal, INST_SEP);
+    atsign = strchr(principal, REALM_SEP);
+
+    /* Maybe we're done already */
+    if(dot != NULL && atsign != NULL) {
+       if(dot < atsign) {
+           /* It's for real */
+           /* Copy into canon */
+           strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
+           canon[MAX_PRINCIPAL_SIZE-1] = '\0';
+           return;
+       } else {
+           /* Nope, it's part of the realm */
+           dot = NULL;
+       }
+    }
+    
+    /* No such luck */
+    end = principal + strlen(principal);
+
+    /* Get the principal name */
+    len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
+    strncpy(canon, principal, len);
+    canon += len;
+
+    /* Add INST_SEP */
+    *canon++ = INST_SEP;
+
+    /* Get the instance, if it exists */
+    if(dot != NULL) {
+       ++dot;
+       len = MIN(INST_SZ, COR(atsign, end) - dot);
+       strncpy(canon, dot, len);
+       canon += len;
+    }
+
+    /* Add REALM_SEP */
+    *canon++ = REALM_SEP;
+
+    /* Get the realm, if it exists */
+    /* Otherwise, default to local realm */
+    if(atsign != NULL) {
+       ++atsign;
+       len = MIN(REALM_SZ, end - atsign);
+       strncpy(canon, atsign, len);
+       canon += len;
+       *canon++ = '\0';
+    } else if(krb_get_lrealm(canon, 1) != KSUCCESS) {
+       strcpy(canon, KRB_REALM);
+    }
+}
+           
+/* Get a lock to modify acl_file */
+/* Return new FILE pointer */
+/* or NULL if file cannot be modified */
+/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */
+static FILE *acl_lock_file(acl_file)
+char *acl_file;
+{
+    struct stat s;
+    char new[LINESIZE];
+    int nfd;
+    FILE *nf;
+    int mode;
+
+    if(stat(acl_file, &s) < 0) return(NULL);
+    mode = s.st_mode;
+    sprintf(new, NEW_FILE, acl_file);
+    for(;;) {
+       /* Open the new file */
+       if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
+           if(errno == EEXIST) {
+               /* Maybe somebody got here already, maybe it's just old */
+               if(stat(new, &s) < 0) return(NULL);
+               if(time(0) - s.st_ctime > WAIT_TIME) {
+                   /* File is stale, kill it */
+                   unlink(new);
+                   continue;
+               } else {
+                   /* Wait and try again */
+                   sleep(1);
+                   continue;
+               }
+           } else {
+               /* Some other error, we lose */
+               return(NULL);
+           }
+       }
+
+       /* If we got to here, the lock file is ours and ok */
+       /* Reopen it under stdio */
+       if((nf = fdopen(nfd, "w")) == NULL) {
+           /* Oops, clean up */
+           unlink(new);
+       }
+       return(nf);
+    }
+}
+
+/* Commit changes to acl_file written onto FILE *f */
+/* Returns zero if successful */
+/* Returns > 0 if lock was broken */
+/* Returns < 0 if some other error occurs */
+/* Closes f */
+static int acl_commit(acl_file, f)
+char *acl_file;
+FILE *f;     
+{
+    char new[LINESIZE];
+    int ret;
+    struct stat s;
+
+    sprintf(new, NEW_FILE, acl_file);
+    if(fflush(f) < 0
+       || fstat(fileno(f), &s) < 0
+       || s.st_nlink == 0) {
+       acl_abort(acl_file, f);
+       return(-1);
+    }
+
+    ret = rename(new, acl_file);
+    fclose(f);
+    return(ret);
+}
+
+/* Abort changes to acl_file written onto FILE *f */
+/* Returns 0 if successful, < 0 otherwise */
+/* Closes f */
+static int acl_abort(acl_file, f)
+char *acl_file;
+FILE *f;     
+{
+    char new[LINESIZE];
+    int ret;
+    struct stat s;
+
+    /* make sure we aren't nuking someone else's file */
+    if(fstat(fileno(f), &s) < 0
+       || s.st_nlink == 0) {
+          fclose(f);
+          return(-1);
+       } else {
+          sprintf(new, NEW_FILE, acl_file);
+          ret = unlink(new);
+          fclose(f);
+          return(ret);
+       }
+}
+
+/* Initialize an acl_file */
+/* Creates the file with permissions perm if it does not exist */
+/* Erases it if it does */
+/* Returns return value of acl_commit */
+int acl_initialize(acl_file, perm)
+char *acl_file;
+int perm;
+{
+    FILE *new;
+    int fd;
+
+    /* Check if the file exists already */
+    if((new = acl_lock_file(acl_file)) != NULL) {
+       return(acl_commit(acl_file, new));
+    } else {
+       /* File must be readable and writable by owner */
+       if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) {
+           return(-1);
+       } else {
+           close(fd);
+           return(0);
+       }
+    }
+}
+
+/* Eliminate all whitespace character in buf */
+/* Modifies its argument */
+static nuke_whitespace(buf)
+char *buf;
+{
+    register char *pin, *pout;
+
+    for(pin = pout = buf; *pin != '\0'; pin++)
+       if(!isspace(*pin)) *pout++ = *pin;
+    *pout = '\0';              /* Terminate the string */
+}
+
+/* Hash table stuff */
+
+struct hashtbl {
+    int size;                  /* Max number of entries */
+    int entries;               /* Actual number of entries */
+    char **tbl;                        /* Pointer to start of table */
+};
+
+/* Make an empty hash table of size s */
+static struct hashtbl *make_hash(size)
+int size;
+{
+    struct hashtbl *h;
+
+    if(size < 1) size = 1;
+    h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
+    h->size = size;
+    h->entries = 0;
+    h->tbl = (char **) calloc(size, sizeof(char *));
+    return(h);
+}
+
+/* Destroy a hash table */
+static destroy_hash(h)
+struct hashtbl *h;
+{
+    int i;
+
+    for(i = 0; i < h->size; i++) {
+       if(h->tbl[i] != NULL) free(h->tbl[i]);
+    }
+    free(h->tbl);
+    free(h);
+}
+
+/* Compute hash value for a string */
+static unsigned hashval(s)
+register char *s;
+{
+    register unsigned hv;
+
+    for(hv = 0; *s != '\0'; s++) {
+       hv ^= ((hv << 3) ^ *s);
+    }
+    return(hv);
+}
+
+/* Add an element to a hash table */
+static add_hash(h, el)
+struct hashtbl *h;
+char *el;
+{
+    unsigned hv;
+    char *s;
+    char **old;
+    int i;
+
+    /* Make space if it isn't there already */
+    if(h->entries + 1 > (h->size >> 1)) {
+       old = h->tbl;
+       h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
+       for(i = 0; i < h->size; i++) {
+           if(old[i] != NULL) {
+               hv = hashval(old[i]) % (h->size << 1);
+               while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
+               h->tbl[hv] = old[i];
+           }
+       }
+       h->size = h->size << 1;
+       free(old);
+    }
+
+    hv = hashval(el) % h->size;
+    while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
+    s = malloc(strlen(el)+1);
+    strcpy(s, el);
+    h->tbl[hv] = s;
+    h->entries++;
+}
+
+/* Returns nonzero if el is in h */
+static check_hash(h, el)
+struct hashtbl *h;
+char *el;
+{
+    unsigned hv;
+
+    for(hv = hashval(el) % h->size;
+       h->tbl[hv] != NULL;
+       hv = (hv + 1) % h->size) {
+           if(!strcmp(h->tbl[hv], el)) return(1);
+       }
+    return(0);
+}
+
+struct acl {
+    char filename[LINESIZE];   /* Name of acl file */
+    int fd;                    /* File descriptor for acl file */
+    struct stat status;                /* File status at last read */
+    struct hashtbl *acl;       /* Acl entries */
+};
+
+static struct acl acl_cache[CACHED_ACLS];
+
+static int acl_cache_count = 0;
+static int acl_cache_next = 0;
+
+/* Returns < 0 if unsuccessful in loading acl */
+/* Returns index into acl_cache otherwise */
+/* Note that if acl is already loaded, this is just a lookup */
+static int acl_load(name)
+char *name;
+{
+    int i;
+    FILE *f;
+    struct stat s;
+    char buf[MAX_PRINCIPAL_SIZE];
+    char canon[MAX_PRINCIPAL_SIZE];
+
+    /* See if it's there already */
+    for(i = 0; i < acl_cache_count; i++) {
+       if(!strcmp(acl_cache[i].filename, name)
+          && acl_cache[i].fd >= 0) goto got_it;
+    }
+
+    /* It isn't, load it in */
+    /* maybe there's still room */
+    if(acl_cache_count < CACHED_ACLS) {
+       i = acl_cache_count++;
+    } else {
+       /* No room, clean one out */
+       i = acl_cache_next;
+       acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
+       close(acl_cache[i].fd);
+       if(acl_cache[i].acl) {
+           destroy_hash(acl_cache[i].acl);
+           acl_cache[i].acl = (struct hashtbl *) 0;
+       }
+    }
+
+    /* Set up the acl */
+    strcpy(acl_cache[i].filename, name);
+    if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
+    /* Force reload */
+    acl_cache[i].acl = (struct hashtbl *) 0;
+
+ got_it:
+    /*
+     * See if the stat matches
+     *
+     * Use stat(), not fstat(), as the file may have been re-created by
+     * acl_add or acl_delete.  If this happens, the old inode will have
+     * no changes in the mod-time and the following test will fail.
+     */
+    if(stat(acl_cache[i].filename, &s) < 0) return(-1);
+    if(acl_cache[i].acl == (struct hashtbl *) 0
+       || s.st_nlink != acl_cache[i].status.st_nlink
+       || s.st_mtime != acl_cache[i].status.st_mtime
+       || s.st_ctime != acl_cache[i].status.st_ctime) {
+          /* Gotta reload */
+          if(acl_cache[i].fd >= 0) close(acl_cache[i].fd);
+          if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
+          if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1);
+          if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
+          acl_cache[i].acl = make_hash(ACL_LEN);
+          while(fgets(buf, sizeof(buf), f) != NULL) {
+              nuke_whitespace(buf);
+              acl_canonicalize_principal(buf, canon);
+              add_hash(acl_cache[i].acl, canon);
+          }
+          fclose(f);
+          acl_cache[i].status = s;
+       }
+    return(i);
+}
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Principal is not canonicalized, and no wildcarding is done */
+acl_exact_match(acl, principal)
+char *acl;
+char *principal;
+{
+    int idx;
+
+    return((idx = acl_load(acl)) >= 0
+          && check_hash(acl_cache[idx].acl, principal));
+}
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Recognizes wildcards in acl of the form
+   name.*@realm, *.*@realm, and *.*@* */
+acl_check(acl, principal)
+char *acl;
+char *principal;
+{
+    char buf[MAX_PRINCIPAL_SIZE];
+    char canon[MAX_PRINCIPAL_SIZE];
+    char *realm, *tmp;
+
+    acl_canonicalize_principal(principal, canon);
+
+    /* Is it there? */
+    if(acl_exact_match(acl, canon)) return(1);
+
+    /* Try the wildcards */
+    realm = strchr(canon, REALM_SEP);
+    tmp = strchr(canon, INST_SEP);
+    *tmp = '\0';                       /* Chuck the instance */
+
+    sprintf(buf, "%s.*%s", canon, realm);
+    if(acl_exact_match(acl, buf)) return(1);
+
+    sprintf(buf, "*.*%s", realm);
+    if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1);
+       
+    return(0);
+}
+
+/* Adds principal to acl */
+/* Wildcards are interpreted literally */
+acl_add(acl, principal)
+char *acl;
+char *principal;
+{
+    int idx;
+    int i;
+    FILE *new;
+    char canon[MAX_PRINCIPAL_SIZE];
+
+    acl_canonicalize_principal(principal, canon);
+
+    if((new = acl_lock_file(acl)) == NULL) return(-1);
+    if((acl_exact_match(acl, canon))
+       || (idx = acl_load(acl)) < 0) {
+          acl_abort(acl, new);
+          return(-1);
+       }
+    /* It isn't there yet, copy the file and put it in */
+    for(i = 0; i < acl_cache[idx].acl->size; i++) {
+       if(acl_cache[idx].acl->tbl[i] != NULL) {
+           if(fputs(acl_cache[idx].acl->tbl[i], new) == NULL
+              || putc('\n', new) != '\n') {
+                  acl_abort(acl, new);
+                  return(-1);
+              }
+       }
+    }
+    fputs(canon, new);
+    putc('\n', new);
+    return(acl_commit(acl, new));
+}
+
+/* Removes principal from acl */
+/* Wildcards are interpreted literally */
+acl_delete(acl, principal)
+char *acl;
+char *principal;
+{
+    int idx;
+    int i;
+    FILE *new;
+    char canon[MAX_PRINCIPAL_SIZE];
+
+    acl_canonicalize_principal(principal, canon);
+
+    if((new = acl_lock_file(acl)) == NULL) return(-1);
+    if((!acl_exact_match(acl, canon))
+       || (idx = acl_load(acl)) < 0) {
+          acl_abort(acl, new);
+          return(-1);
+       }
+    /* It isn't there yet, copy the file and put it in */
+    for(i = 0; i < acl_cache[idx].acl->size; i++) {
+       if(acl_cache[idx].acl->tbl[i] != NULL
+          && strcmp(acl_cache[idx].acl->tbl[i], canon)) {
+              fputs(acl_cache[idx].acl->tbl[i], new);
+              putc('\n', new);
+       }
+    }
+    return(acl_commit(acl, new));
+}
+
diff --git a/src/kadmin.v4/server/acl_files.doc b/src/kadmin.v4/server/acl_files.doc
new file mode 100644 (file)
index 0000000..78c448a
--- /dev/null
@@ -0,0 +1,107 @@
+PROTOTYPE ACL LIBRARY
+
+Introduction
+       
+An access control list (ACL) is a list of principals, where each
+principal is is represented by a text string which cannot contain
+whitespace.  The library allows application programs to refer to named
+access control lists to test membership and to atomically add and
+delete principals using a natural and intuitive interface.  At
+present, the names of access control lists are required to be Unix
+filenames, and refer to human-readable Unix files; in the future, when
+a networked ACL server is implemented, the names may refer to a
+different namespace specific to the ACL service.
+
+
+Usage
+
+cc <files> -lacl -lkrb.
+
+
+
+Principal Names
+
+Principal names have the form
+
+<name>[.<instance>][@<realm>]
+
+e.g.
+
+asp
+asp.root
+asp@ATHENA.MIT.EDU
+asp.@ATHENA.MIT.EDU
+asp.root@ATHENA.MIT.EDU
+
+It is possible for principals to be underspecified.  If instance is
+missing, it is assumed to be "".  If realm is missing, it is assumed
+to be local_realm.  The canonical form contains all of name, instance,
+and realm; the acl_add and acl_delete routines will always
+leave the file in that form.  Note that the canonical form of
+asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU.
+
+
+Routines
+
+acl_canonicalize_principal(principal, buf)
+char *principal;
+char *buf;     /*RETVAL*/
+
+Store the canonical form of principal in buf.  Buf must contain enough
+space to store a principal, given the limits on the sizes of name,
+instance, and realm specified in /usr/include/krb.h.
+
+acl_check(acl, principal)
+char *acl;
+char *principal;
+
+Returns nonzero if principal appears in acl.  Returns 0 if principal
+does not appear in acl, or if an error occurs.  Canonicalizes
+principal before checking, and allows the ACL to contain wildcards.
+
+acl_exact_match(acl, principal)
+char *acl;
+char *principal;
+
+Like acl_check, but does no canonicalization or wildcarding.
+
+acl_add(acl, principal)
+char *acl;
+char *principal;
+
+Atomically adds principal to acl.  Returns 0 if successful, nonzero
+otherwise.  It is considered a failure if principal is already in acl.
+This routine will canonicalize principal, but will treat wildcards
+literally.
+
+acl_delete(acl, principal)
+char *acl;
+char *principal;
+
+Atomically deletes principal from acl.  Returns 0 if successful,
+nonzero otherwise.  It is consider a failure if principal is not
+already in acl.  This routine will canonicalize principal, but will
+treat wildcards literally.
+
+acl_initialize(acl, mode)
+char *acl;
+int mode;
+
+Initialize acl.  If acl file does not exist, creates it with mode
+mode.  If acl exists, removes all members.  Returns 0 if successful,
+nonzero otherwise.  WARNING: Mode argument is likely to change with
+the eventual introduction of an ACL service.  
+
+
+Known problems
+
+In the presence of concurrency, there is a very small chance that
+acl_add or acl_delete could report success even though it would have
+had no effect.  This is a necessary side effect of using lock files
+for concurrency control rather than flock(2), which is not supported
+by NFS.
+
+The current implementation caches ACLs in memory in a hash-table
+format for increased efficiency in checking membership; one effect of
+the caching scheme is that one file descriptor will be kept open for
+each ACL cached, up to a maximum of 8.
diff --git a/src/kadmin.v4/server/admin_server.c b/src/kadmin.v4/server/admin_server.c
new file mode 100644 (file)
index 0000000..4542572
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * kadmin/v4server/admin_server.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Top-level loop of the kerberos Administration server
+ */
+
+#include <mit-copyright.h>
+/*
+  admin_server.c
+  this holds the main loop and initialization and cleanup code for the server
+*/
+
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifndef POSIX_SIGNALS
+#ifndef sigmask
+#define sigmask(m)     (1 <<((m)-1))
+#endif
+#endif /* POSIX_SIGNALS */
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "k5-int.h"
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.h>
+#include "com_err.h"
+#include "kadm_server.h"
+
+/* Almost all procs and such need this, so it is global */
+admin_params prm;              /* The command line parameters struct */
+
+char prog[32];                 /* WHY IS THIS NEEDED??????? */
+char *progname = prog;
+char *acldir = DEFAULT_ACL_DIR;
+char krbrlm[REALM_SZ];
+extern Kadm_Server server_parm;
+krb5_context kadm_context;
+
+/* close the system log file */
+void close_syslog()
+{
+   syslog(LOG_INFO, "Shutting down V4 admin server");
+}
+
+void byebye()                  /* say goodnight gracie */
+{
+   printf("Admin Server (kadm server) has completed operation.\n");
+}
+
+/*
+** Main does the logical thing, it sets up the database and RPC interface,
+**  as well as handling the creation and maintenance of the syslog file...
+*/
+main(argc, argv)               /* admin_server main routine */
+int argc;
+char *argv[];
+{
+    int errval;
+    int c;
+    char *lrealm;
+    extern char *optarg;
+    extern int fascist_cpw;
+
+    krb5_init_context(&kadm_context);
+    krb5_init_ets(kadm_context);
+    initialize_kadm_error_table();
+    prog[sizeof(prog)-1]='\0';         /* Terminate... */
+    (void) strncpy(prog, argv[0], sizeof(prog)-1);
+
+    /* initialize the admin_params structure */
+    prm.sysfile = KADM_SYSLOG;         /* default file name */
+    prm.inter = 1;
+
+    memset(krbrlm, 0, sizeof(krbrlm));
+
+    fascist_cpw = 1;           /* by default, enable fascist mode */
+    while ((c = getopt(argc, argv, "f:hnd:a:r:FN")) != EOF)
+       switch(c) {
+       case 'f':                       /* Syslog file name change */
+           prm.sysfile = optarg;
+           break;
+       case 'n':
+           prm.inter = 0;
+           break;
+       case 'a':                       /* new acl directory */
+           acldir = optarg;
+           break;
+       case 'd':
+           if (errval = krb5_db_set_name(kadm_context, optarg)) {
+               com_err(argv[0], errval, "while setting dbname");
+               exit(1);
+           }
+           break;
+        case 'F':
+           fascist_cpw++;
+           break;
+        case 'N':
+           fascist_cpw = 0;
+           break;
+       case 'r':
+           (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1);
+           break;
+       case 'h':                       /* get help on using admin_server */
+       default:
+           printf("Usage: admin_server [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n");
+           exit(-1);                   /* failure */
+       }
+
+    if (krbrlm[0] == 0) {
+       if (errval = krb5_get_default_realm(kadm_context, &lrealm)) {
+           com_err(argv[0], errval, "while attempting to get local realm");
+           exit(1);
+       }
+       (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1);
+    }
+    printf("KADM Server %s initializing\n",KADM_VERSTR);
+    printf("Please do not use 'kill -9' to kill this job, use a\n");
+    printf("regular kill instead\n\n");
+
+    printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST");
+    
+    openlog(argv[0], LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */
+    syslog(LOG_INFO, "V4 admin server starting");
+
+    errval = krb5_db_init(kadm_context);  /* Open the Kerberos database */
+    if (errval) {
+       fprintf(stderr, "error: krb5_db_init() failed");
+       close_syslog();
+       byebye();
+       exit(1);
+    }
+    if (errval = krb5_db_set_lockmode(kadm_context, TRUE)) {
+       com_err(argv[0], errval, "while setting db to nonblocking");
+       close_syslog();
+       byebye();
+       exit(1);
+    }
+    /* set up the server_parm struct */
+    if ((errval = kadm_ser_init(prm.inter, krbrlm))==KADM_SUCCESS) {
+       krb5_db_fini(kadm_context);     /* Close the Kerberos database--
+                                          will re-open later */
+       errval = kadm_listen();         /* listen for calls to server from
+                                          clients */
+    }
+    if (errval != KADM_SUCCESS) {
+       fprintf(stderr,"error:  %s\n",error_message(errval));
+       krb5_db_fini(kadm_context);     /* Close if error */
+    }
+    close_syslog();                    /* Close syslog file, print
+                                          closing note */
+    byebye();                          /* Say bye bye on the terminal
+                                          in use */
+    return 0;
+}                                      /* procedure main */
+
+
+static void clear_secrets()
+{
+    krb5_finish_key(kadm_context, &server_parm.master_encblock);
+    memset((char *)&server_parm.master_encblock, 0,
+          sizeof (server_parm.master_encblock));
+    memset((char *)server_parm.master_keyblock.contents, 0,
+          server_parm.master_keyblock.length);
+    server_parm.mkvno = 0L;
+    return;
+}
+
+static exit_now = 0;
+
+krb5_sigtype
+doexit(sig)
+       int sig;
+{
+    exit_now = 1;
+#ifdef POSIX_SIGNALS
+    return;
+#else /* !POSIX */
+    return(0);
+#endif /* POSIX */
+}
+   
+unsigned pidarraysize = 0;
+int *pidarray = (int *)0;
+int unknown_child = 0;
+
+/*
+kadm_listen
+listen on the admin servers port for a request
+*/
+kadm_listen()
+{
+    extern int errno;
+    int found;
+    int admin_fd;
+    int peer_fd;
+    fd_set mask, readfds;
+    struct sockaddr_in peer;
+    int addrlen;
+    void process_client(), kill_children();
+    int pid;
+    krb5_sigtype do_child();
+
+    (void) signal(SIGINT, doexit);
+    (void) signal(SIGTERM, doexit);
+    (void) signal(SIGHUP, doexit);
+    (void) signal(SIGQUIT, doexit);
+    (void) signal(SIGPIPE, SIG_IGN); /* get errors on write() */
+    (void) signal(SIGALRM, doexit);
+    (void) signal(SIGCHLD, do_child);
+
+    if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+       return KADM_NO_SOCK;
+    if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr,
+            sizeof(struct sockaddr_in)) < 0)
+       return KADM_NO_BIND;
+    (void) listen(admin_fd, 1);
+    FD_ZERO(&mask);
+    FD_SET(admin_fd, &mask);
+
+    for (;;) {                         /* loop nearly forever */
+       if (exit_now) {
+           clear_secrets();
+           kill_children();
+           return(0);
+       }
+       readfds = mask;
+       if ((found = select(admin_fd+1,&readfds,(fd_set *)0,
+                           (fd_set *)0, (struct timeval *)0)) == 0)
+           continue;                   /* no things read */
+       if (found < 0) {
+           if (errno != EINTR)
+               syslog(LOG_ERR, "select: %s", error_message(errno));
+           continue;
+       }      
+       if (FD_ISSET(admin_fd, &readfds)) {
+           /* accept the conn */
+           addrlen = sizeof(peer);
+           if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer,
+                                 &addrlen)) < 0) {
+               syslog(LOG_ERR, "accept: %s", error_message(errno));
+               continue;
+           }
+#ifndef DEBUG
+           /* if you want a sep daemon for each server */
+           if (pid = fork()) {
+               /* parent */
+               if (pid < 0) {
+                   syslog(LOG_ERR, "fork: %s", error_message(errno));
+                   (void) close(peer_fd);
+                   continue;
+               }
+               /* fork succeeded: keep tabs on child */
+               (void) close(peer_fd);
+               if (unknown_child != pid) {
+                   if (pidarray) {
+                       pidarray = (int *)realloc((char *)pidarray, 
+                                       (++pidarraysize * sizeof(int)));
+                       pidarray[pidarraysize-1] = pid;
+                   } else {
+                       pidarray = (int *)malloc((pidarraysize = 1) * sizeof(int));
+                       pidarray[0] = pid;
+                   }
+               }       /* End if unknown_child != pid.*/
+           } else {
+               /* child */
+               (void) close(admin_fd);
+#endif /* DEBUG */
+               /* do stuff */
+               process_client (peer_fd, &peer);
+#ifndef DEBUG
+           }
+#endif
+       } else {
+           syslog(LOG_ERR, "something else woke me up!");
+           return(0);
+       }
+    }
+    /*NOTREACHED*/
+}
+
+#ifdef DEBUG
+#define cleanexit(code) {krb5_db_fini(kadm_context); return;}
+#endif
+
+void
+process_client(fd, who)
+int fd;
+struct sockaddr_in *who;
+{
+    u_char *dat;
+    int dat_len;
+    u_short dlen;
+    int retval;
+    int on = 1;
+    int nentries = 1;
+    krb5_db_entry sprinc_entries;
+    krb5_boolean more;
+    krb5_keyblock cpw_skey;
+    int status;
+
+#ifndef NOENCRYPTION
+    /* Must do it here, since this is after the fork() call */
+    des_init_random_number_generator(server_parm.master_keyblock.contents);
+#endif /* NOENCRYPTION */
+    
+    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0)
+       syslog(LOG_ERR, "setsockopt keepalive: %d", errno);
+
+    server_parm.recv_addr = *who;
+
+    if (krb5_db_init(kadm_context)) {  /* Open as client */
+       syslog(LOG_ERR, "can't open krb db");
+       cleanexit(1);
+    }
+    /* need to set service key to changepw.KRB_MASTER */
+
+    status = krb5_db_get_principal(kadm_context, server_parm.sprinc,
+                                  &sprinc_entries,
+                                  &nentries, &more);
+    /* ugh... clean this up later */
+    if (status == KRB5_KDB_DB_INUSE) {
+       /* db locked */
+       u_long retcode = KADM_DB_INUSE;
+       char *pdat;
+       
+       dat_len = KADM_VERSIZE + sizeof(u_long);
+       dat = (u_char *) malloc((unsigned)dat_len);
+       pdat = (char *) dat;
+       retcode = htonl((u_long) KADM_DB_INUSE);
+       (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+       memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(u_long));
+       goto out;
+    } else if (!nentries) {
+       syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst);
+       cleanexit(2);
+    } else if (status) {
+       syslog(LOG_ERR, error_message(status));
+       cleanexit(2);
+    }
+
+    status = krb5_kdb_decrypt_key(kadm_context, &server_parm.master_encblock,
+                                 &sprinc_entries.key,
+                                 &cpw_skey);
+    if (status) {
+       syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status));
+       cleanexit(1);
+    }
+    /* if error, will show up when rd_req fails */
+    (void) krb_set_key((char *)cpw_skey.contents, 0); 
+    while (1) {
+       if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) !=
+           sizeof(u_short)) {
+           if (retval < 0)
+               syslog(LOG_ERR, "dlen read: %s", error_message(errno));
+           else if (retval)
+               syslog(LOG_ERR, "short dlen read: %d", retval);
+           (void) close(fd);
+           cleanexit(retval ? 3 : 0);
+       }
+       if (exit_now) {
+           cleanexit(0);
+       }
+       dat_len = (int) ntohs(dlen);
+       dat = (u_char *) malloc((unsigned)dat_len);
+       if (!dat) {
+           syslog(LOG_ERR, "malloc: No memory");
+           (void) close(fd);
+           cleanexit(4);
+       }
+       if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) {
+           if (retval < 0)
+               syslog(LOG_ERR, "data read: %s", error_message(errno));
+           else
+               syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval);
+           (void) close(fd);
+           cleanexit(5);
+       }
+       if (exit_now) {
+           cleanexit(0);
+       }
+       if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS)
+           syslog(LOG_ERR, "processing request: %s", error_message(retval));
+    
+       /* kadm_ser_in did the processing and returned stuff in
+          dat & dat_len , return the appropriate data */
+    
+    out:
+       dlen = (u_short) dat_len;
+
+       if (dat_len != (int)dlen) {
+           clear_secrets();
+           abort();                    /* XXX */
+       }
+       dlen = htons(dlen);
+    
+       if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) {
+           syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno));
+           (void) close(fd);
+           cleanexit(6);
+       }
+    
+       if (krb_net_write(fd, (char *)dat, dat_len) < 0) {
+           syslog(LOG_ERR, "writing to client: %s", error_message(errno));
+           (void) close(fd);
+           cleanexit(7);
+       }
+       free((char *)dat);
+    }
+    /*NOTREACHED*/
+}
+
+krb5_sigtype
+do_child(sig)
+       int sig;
+{
+    /* SIGCHLD brings us here */
+    int pid;
+    register int i, j;
+
+#ifdef WAIT_USES_INT
+    int status;
+#else
+    union wait status;
+#endif
+
+    pid = wait(&status);
+
+    for (i = 0; i < pidarraysize; i++)
+       if (pidarray[i] == pid) {
+           /* found it */
+           for (j = i; j < pidarraysize-1; j++)
+               /* copy others down */
+               pidarray[j] = pidarray[j+1];
+           pidarraysize--;
+#ifdef WAIT_USES_INT
+           if (WIFEXITED(status) || WIFSIGNALED(status))
+               syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid,
+                      WTERMSIG(status), WEXITSTATUS(status));
+
+#else
+           if (status.w_retcode || status.w_coredump || status.w_termsig)
+               syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d",
+                      pid, status.w_termsig, status.w_coredump, status.w_retcode);
+
+#endif
+#ifdef POSIX_SIGNALS
+           return;
+#else /* !POSIX */
+           return(0);
+#endif /* POSIX */
+       }
+    unknown_child = pid;
+#ifdef WAIT_USES_INT
+    syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid,
+          WTERMSIG(status), WEXITSTATUS(status));
+
+#else
+    syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d",
+          pid, status.w_termsig, status.w_coredump, status.w_retcode);
+
+#endif
+#ifdef POSIX_SIGNALS
+    return;
+#else /* !POSIX */
+    return(0);
+#endif /* POSIX */
+}
+
+#ifndef DEBUG
+cleanexit(val)
+{
+    krb5_db_fini(kadm_context);
+    clear_secrets();
+    exit(val);
+}
+#endif
+
+void
+kill_children()
+{
+    register int i;
+#ifdef POSIX_SIGNALS
+    sigset_t oldmask, igmask;
+#else
+    int osigmask;
+#endif
+
+#ifdef POSIX_SIGNALS
+    sigemptyset(&igmask);
+    sigaddset(&igmask, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &igmask, &oldmask);
+#else
+    osigmask = sigblock(sigmask(SIGCHLD));
+#endif
+
+    for (i = 0; i < pidarraysize; i++) {
+       kill(pidarray[i], SIGINT);
+       syslog(LOG_ERR, "killing child %d", pidarray[i]);
+    }
+#ifdef POSIX_SIGNALS
+    sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0);
+#else
+    sigsetmask(osigmask);
+#endif
+    return;
+}
diff --git a/src/kadmin.v4/server/configure.in b/src/kadmin.v4/server/configure.in
new file mode 100644 (file)
index 0000000..009b866
--- /dev/null
@@ -0,0 +1,17 @@
+AC_INIT(admin_server.c)
+WITH_CCOPTS
+CONFIG_RULES
+AC_SET_BUILDTOP
+AC_PROG_INSTALL
+WITH_NETLIB
+AC_CHECK_LIB(ndbm,main)
+AC_CHECK_LIB(dbm,main)
+AC_HAVE_HEADERS(unistd.h)
+CHECK_WAIT_TYPE
+CHECK_FCNTL
+CHECK_SIGNALS
+ET_RULES
+KRB_INCLUDE
+WITH_KRB4
+WITH_KRB5ROOT
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin.v4/server/kadm_err.et b/src/kadmin.v4/server/kadm_err.et
new file mode 100644 (file)
index 0000000..71c6c3b
--- /dev/null
@@ -0,0 +1,58 @@
+#      $Source$
+#      $Author$
+#      $Header$
+# Copyright 1988 by the Massachusetts Institute of Technology.
+#
+# For copying and distribution information, please see the file
+# <mit-copyright.h>.
+#
+# Kerberos administration server error table
+#
+       et      kadm
+
+# KADM_SUCCESS, as all success codes should be, is zero
+
+ec KADM_RCSID,         "$Header$"
+# /* Building and unbuilding the packet errors */
+ec KADM_NO_REALM,      "Cannot fetch local realm"
+ec KADM_NO_CRED,       "Unable to fetch credentials"
+ec KADM_BAD_KEY,       "Bad key supplied"
+ec KADM_NO_ENCRYPT,    "Can't encrypt data"
+ec KADM_NO_AUTH,       "Cannot encode/decode authentication info"
+ec KADM_WRONG_REALM,   "Principal attemping change is in wrong realm"
+ec KADM_NO_ROOM,       "Packet is too large"
+ec KADM_BAD_VER,       "Version number is incorrect"
+ec KADM_BAD_CHK,       "Checksum does not match"
+ec KADM_NO_READ,       "Unsealing private data failed"
+ec KADM_NO_OPCODE,     "Unsupported operation"
+ec KADM_NO_HOST,       "Could not find administrating host"
+ec KADM_UNK_HOST,      "Administrating host name is unknown"
+ec KADM_NO_SERV,       "Could not find service name in services database"
+ec KADM_NO_SOCK,       "Could not create socket"
+ec KADM_NO_CONN,       "Could not connect to server"
+ec KADM_NO_HERE,       "Could not fetch local socket address"
+ec KADM_NO_MAST,       "Could not fetch master key"
+ec KADM_NO_VERI,       "Could not verify master key"
+
+# /* From the server side routines */
+ec KADM_INUSE,         "Entry already exists in database"
+ec KADM_UK_SERROR,     "Database store error"
+ec KADM_UK_RERROR,     "Database read error"
+ec KADM_UNAUTH,                "Insufficient access to perform requested operation"
+# KADM_DATA isn't really an error, but...
+ec KADM_DATA,          "Data is available for return to client"
+ec KADM_NOENTRY,       "No such entry in the database"
+
+ec KADM_NOMEM,         "Memory exhausted"
+ec KADM_NO_HOSTNAME,   "Could not fetch system hostname"
+ec KADM_NO_BIND,       "Could not bind port"
+ec KADM_LENGTH_ERROR,  "Length mismatch problem"
+ec KADM_ILL_WILDCARD,  "Illegal use of wildcard"
+
+ec KADM_DB_INUSE,      "Database locked or in use"
+
+ec KADM_INSECURE_PW,   "Insecure password rejected"
+ec KADM_PW_MISMATCH,   "Cleartext password and DES key did not match"
+
+ec KADM_NOT_SERV_PRINC,        "Invalid principal for change srvtab request"
+end
diff --git a/src/kadmin.v4/server/kadm_funcs.c b/src/kadmin.v4/server/kadm_funcs.c
new file mode 100644 (file)
index 0000000..ac97b8a
--- /dev/null
@@ -0,0 +1,970 @@
+/*
+ * kadmin/v4server/kadm_funcs.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side database manipulation routines
+ */
+
+
+#include <mit-copyright.h>
+/*
+kadm_funcs.c
+the actual database manipulation code
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+/* #include <ndbm.h> Gotten by kadmin_server.h */
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.h>
+#include <syslog.h>
+#ifdef NEED_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#else
+#include <time.h>
+#endif
+
+#include "kadm_server.h"
+
+extern Kadm_Server server_parm;
+
+krb5_error_code
+kadm_entry2princ(entry, princ)
+     krb5_db_entry entry;
+     Principal *princ;
+{
+  char realm[REALM_SZ];                /* dummy values only */
+  krb5_error_code retval;
+
+  /* NOTE: does not convert the key */
+  memset(princ, 0, sizeof (*princ));
+  retval = krb5_524_conv_principal(kadm_context, entry.principal,
+                                  princ->name, princ->instance, realm);
+  if (retval)
+    return retval;
+  princ->exp_date = entry.expiration;
+  strncpy(ctime(&entry.expiration), princ->exp_date_txt,
+         DATE_SZ);
+  princ->mod_date = entry.mod_date;
+  strncpy(ctime(&entry.mod_date), princ->mod_date_txt,
+         DATE_SZ);
+  princ->attributes = entry.attributes;
+  princ->max_life = entry.max_life / (60 * 5);
+  princ->kdc_key_ver = entry.mkvno;
+  princ->key_version = entry.kvno;
+  retval = krb5_524_conv_principal(kadm_context, entry.mod_name,
+                                  princ->mod_name, princ->mod_instance,
+                                  realm);
+  if (retval)
+    return retval;
+  return 0;
+}
+
+krb5_error_code
+kadm_princ2entry(princ, entry)
+     Principal princ;
+     krb5_db_entry *entry;
+{
+  krb5_error_code retval;
+
+  /* NOTE: does not convert the key */
+  memset(entry, 0, sizeof (*entry));
+  /* yeah yeah stupid v4 database doesn't store realm names */
+  retval = krb5_425_conv_principal(kadm_context, princ.name, princ.instance,
+                                  server_parm.krbrlm, &entry->principal);
+  if (retval)
+    return retval;
+  entry->kvno = princ.key_version;
+  entry->max_life = princ.max_life * (60 * 5);
+  entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */
+  entry->mkvno = server_parm.mkvno; /* XXX */
+  entry->expiration = princ.exp_date;
+  retval = krb5_425_conv_principal(kadm_context, princ.mod_name,
+                                  princ.mod_instance,
+                                  server_parm.krbrlm, &entry->mod_name);
+  if (retval)
+    return retval;
+  entry->mod_date = princ.mod_date;
+  entry->attributes = princ.attributes;
+  entry->salt_type = KRB5_KDB_SALTTYPE_V4;
+}
+
+int
+check_access(pname, pinst, prealm, acltype)
+char *pname;
+char *pinst;
+char *prealm;
+enum acl_types acltype;
+{
+    char checkname[MAX_K_NAME_SZ];
+    char filename[MAXPATHLEN];
+    extern char *acldir;
+
+    (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm);
+    
+    switch (acltype) {
+    case ADDACL:
+       (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE);
+       break;
+    case GETACL:
+       (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE);
+       break;
+    case MODACL:
+       (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE);
+       break;
+    case DELACL:
+       (void) sprintf(filename, "%s%s", acldir, DEL_ACL_FILE);
+       break;
+    case STABACL:
+       (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE);
+       break;
+    }
+    return(acl_check(filename, checkname));
+}
+
+int
+wildcard(str)
+char *str;
+{
+    if (!strcmp(str, WILDCARD_STR))
+       return(1);
+    return(0);
+}
+
+#define failadd(code) {  (void) syslog(LOG_ERR, "FAILED adding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_add_entry (rname, rinstance, rrealm, valsin, valsout)
+char *rname;                           /* requestors name */
+char *rinstance;                       /* requestors instance */
+char *rrealm;                          /* requestors realm */
+Kadm_vals *valsin;
+Kadm_vals *valsout;
+{
+  Principal data_i, data_o;            /* temporary principal */
+  u_char flags[4];
+  krb5_principal default_princ;
+  krb5_error_code retval;
+  krb5_db_entry newentry, tmpentry;
+  krb5_boolean more;
+  krb5_keyblock newpw;
+  krb5_encrypted_keyblock encpw;
+  int numfound;
+
+  if (!check_access(rname, rinstance, rrealm, ADDACL)) {
+    syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'",
+          rname, rinstance, rrealm, valsin->name, valsin->instance);
+    return KADM_UNAUTH;
+  }
+
+  /* Need to check here for "legal" name and instance */
+  if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+      failadd(KADM_ILL_WILDCARD);
+  }
+
+  syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'",
+        valsin->name, valsin->instance, rname, rinstance, rrealm);
+
+  kadm_vals_to_prin(valsin->fields, &data_i, valsin);
+  (void) strncpy(data_i.name, valsin->name, ANAME_SZ);
+  (void) strncpy(data_i.instance, valsin->instance, INST_SZ);
+
+  if (!IS_FIELD(KADM_EXPDATE,valsin->fields))
+    data_i.exp_date = server_parm.expiration;
+  if (!IS_FIELD(KADM_ATTR,valsin->fields))
+    data_i.attributes = server_parm.flags;
+  if (!IS_FIELD(KADM_MAXLIFE,valsin->fields))
+    data_i.max_life = server_parm.max_life;
+
+  newpw.magic = KV5M_KEYBLOCK;
+  newpw.etype = ETYPE_UNKNOWN;
+  if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL)
+    failadd(KADM_NOMEM);
+  data_i.key_low = ntohl(data_i.key_low);
+  data_i.key_high = ntohl(data_i.key_high);
+  memcpy(newpw.contents, &data_i.key_low, 4);
+  memcpy((char *)(((long *) newpw.contents) + 1), &data_i.key_high, 4);
+  newpw.length = 8;
+  newpw.keytype = KEYTYPE_DES;
+  /* encrypt new key in master key */
+  retval = krb5_kdb_encrypt_key(kadm_context, &server_parm.master_encblock,
+                               &newpw, &encpw);
+  memset((char *)newpw.contents, 0, newpw.length);
+  free(newpw.contents);
+  if (retval) {
+    failadd(retval);
+  }
+  data_o = data_i;
+
+  retval = kadm_princ2entry(data_i, &newentry);
+  if (retval) {
+    memset((char *)encpw.contents, 0, encpw.length);
+    free(encpw.contents);
+    krb5_db_free_principal(kadm_context, &newentry, 1);
+    failadd(retval);
+  }
+
+  newentry.key = encpw;
+  numfound = 1;
+  retval = krb5_db_get_principal(kadm_context, newentry.principal,
+                                &tmpentry, &numfound, &more);
+
+  if (retval) {
+    krb5_db_free_principal(kadm_context, &newentry, 1);
+    failadd(retval);
+  }
+  krb5_db_free_principal(kadm_context, &tmpentry, numfound);
+  if (numfound) {
+    krb5_db_free_principal(kadm_context, &newentry, 1);
+    failadd(KADM_INUSE);
+  } else {
+    newentry.kvno = ++data_i.key_version;
+    if (retval = krb5_timeofday(kadm_context, &newentry.mod_date)) {
+       krb5_db_free_principal(kadm_context, &newentry, 1);
+       failadd(retval);
+    }
+    if (newentry.mod_name)
+      krb5_free_principal(kadm_context, newentry.mod_name);
+    newentry.mod_name = NULL;  /* in case the following breaks */
+    retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm,
+                                    &newentry.mod_name);
+    if (retval) {
+      krb5_db_free_principal(kadm_context, &newentry, 1);
+      failadd(retval);
+    }
+
+    numfound = 1;
+    retval = krb5_db_put_principal(kadm_context, &newentry, &numfound);
+    if (retval) {
+      krb5_db_free_principal(kadm_context, &newentry, 1);
+      failadd(retval);
+    }
+    if (!numfound) {
+      krb5_db_free_principal(kadm_context, &newentry, 1);
+      failadd(KADM_UK_SERROR);
+    } else {
+      numfound = 1;
+      retval = krb5_db_get_principal(kadm_context, newentry.principal,
+                                    &tmpentry,
+                                    &numfound, &more);
+      krb5_db_free_principal(kadm_context, &newentry, 1);
+      if (retval) {
+       failadd(retval);
+      } else if (numfound != 1 || more) {
+       krb5_db_free_principal(kadm_context, &tmpentry, numfound);
+       failadd(KADM_UK_RERROR);
+      }
+      kadm_entry2princ(tmpentry, &data_o);
+      krb5_db_free_principal(kadm_context, &tmpentry, numfound);
+      memset((char *)flags, 0, sizeof(flags));
+      SET_FIELD(KADM_NAME,flags);
+      SET_FIELD(KADM_INST,flags);
+      SET_FIELD(KADM_EXPDATE,flags);
+      SET_FIELD(KADM_ATTR,flags);
+      SET_FIELD(KADM_MAXLIFE,flags);
+      kadm_prin_to_vals(flags, valsout, &data_o);
+      syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance);
+      return KADM_DATA;                /* Set all the appropriate fields */
+    }
+  }
+}
+#undef failadd
+
+#define faildel(code) {  (void) syslog(LOG_ERR, "FAILED deleting '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_del_entry (rname, rinstance, rrealm, valsin, valsout)
+char *rname;                           /* requestors name */
+char *rinstance;                       /* requestors instance */
+char *rrealm;                          /* requestors realm */
+Kadm_vals *valsin;
+Kadm_vals *valsout;
+{
+  int numfound;                        /* check how many we get written */
+  krb5_boolean more;                   /* pointer to more grabbed records */
+  Principal data_i, data_o;            /* temporary principal */
+  u_char flags[4];
+  krb5_db_entry entry, odata;
+  krb5_error_code retval;
+  krb5_principal inprinc;
+
+  if (!check_access(rname, rinstance, rrealm, DELACL)) {
+    (void) syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to delete an entry for '%s.%s'",
+              rname, rinstance, rrealm, valsin->name, valsin->instance);
+    return KADM_UNAUTH;
+  }
+  
+  /* Need to check here for "legal" name and instance */
+  if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+      faildel(KADM_ILL_WILDCARD);
+  }
+
+  syslog(LOG_INFO, "request to delete an entry for '%s.%s' from '%s.%s@%s'",
+        valsin->name, valsin->instance, rname, rinstance, rrealm);
+  
+  retval = krb5_425_conv_principal(kadm_context, valsin->name,
+                                  valsin->instance,
+                                  server_parm.krbrlm, &inprinc);
+  if (retval)
+    faildel(retval);
+
+  numfound = 1;
+  retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound,
+                                &more);
+
+  if (retval) {
+    krb5_db_free_principal(kadm_context, &entry, numfound);
+    faildel(retval);
+  } else if (!numfound || more) {
+    faildel(KADM_NOENTRY);
+  }
+
+  retval = krb5_db_delete_principal(kadm_context, inprinc, &numfound);
+  if (retval) {
+    krb5_db_free_principal(kadm_context, &entry, numfound);
+    faildel(retval);
+  }
+  if (!numfound) {
+    krb5_db_free_principal(kadm_context, &entry, numfound);
+    faildel(KADM_UK_SERROR);
+  } else {
+    if (retval) {
+      faildel(retval);
+    } else if (numfound != 1 || more) {
+      krb5_db_free_principal(kadm_context, &entry, numfound);
+      faildel(KADM_UK_RERROR);
+    }
+    kadm_entry2princ(entry, &data_o);
+    krb5_db_free_principal(kadm_context, &entry, numfound);
+    memset((char *)flags, 0, sizeof(flags));
+    SET_FIELD(KADM_NAME,flags);
+    SET_FIELD(KADM_INST,flags);
+    SET_FIELD(KADM_EXPDATE,flags);
+    SET_FIELD(KADM_ATTR,flags);
+    SET_FIELD(KADM_MAXLIFE,flags);
+    kadm_prin_to_vals(flags, valsout, &data_o);
+    syslog(LOG_INFO, "'%s.%s' deleted.", valsin->name, valsin->instance);
+    return KADM_DATA;          /* Set all the appropriate fields */
+  }
+}
+#undef faildel
+
+#define failget(code) {  (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout)
+char *rname;                           /* requestors name */
+char *rinstance;                       /* requestors instance */
+char *rrealm;                          /* requestors realm */
+Kadm_vals *valsin;                     /* what they wannt to get */
+u_char *flags;                         /* which fields we want */
+Kadm_vals *valsout;                    /* what data is there */
+{
+  int numfound;                        /* check how many were returned */
+  krb5_boolean more;           /* To point to more name.instances */
+  Principal data_o;            /* Data object to hold Principal */
+  krb5_principal inprinc;
+  krb5_db_entry entry;
+  krb5_error_code retval;
+
+  if (!check_access(rname, rinstance, rrealm, GETACL)) {
+    syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry",
+           rname, rinstance, rrealm, valsin->name, valsin->instance);
+    return KADM_UNAUTH;
+  }
+
+  if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+      failget(KADM_ILL_WILDCARD);
+  }
+
+  syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'",
+            valsin->name, valsin->instance, rname, rinstance, rrealm);
+
+  retval = krb5_425_conv_principal(kadm_context, valsin->name,
+                                  valsin->instance,
+                                  server_parm.krbrlm, &inprinc);
+  if (retval)
+    failget(retval);
+  /* Look up the record in the database */
+  numfound = 1;
+  retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound,
+                                &more);
+  krb5_free_principal(kadm_context, inprinc);
+  if (retval) {
+    failget(retval);
+  } else if (!numfound || more) {
+    failget(KADM_NOENTRY);
+  }
+  retval = kadm_entry2princ(entry, &data_o);
+  krb5_db_free_principal(kadm_context, &entry, 1);
+  if (retval) {
+    failget(retval);
+  }
+  kadm_prin_to_vals(flags, valsout, &data_o);
+  syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance);
+  return KADM_DATA;            /* Set all the appropriate fields */
+}
+#undef failget
+
+#define failmod(code) {  (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; }
+
+kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout)
+char *rname;                           /* requestors name */
+char *rinstance;                       /* requestors instance */
+char *rrealm;                          /* requestors realm */
+Kadm_vals *valsin1, *valsin2;          /* holds the parameters being
+                                          passed in */
+Kadm_vals *valsout;            /* the actual record which is returned */
+{
+  int numfound;
+  krb5_boolean more;
+  Principal data_o, temp_key;
+  u_char fields[4];
+  krb5_keyblock newpw;
+  krb5_encrypted_keyblock encpw;
+  krb5_error_code retval;
+  krb5_principal theprinc;
+  krb5_db_entry newentry, odata;
+
+  if (wildcard(valsin1->name) || wildcard(valsin1->instance)) {
+      failmod(KADM_ILL_WILDCARD);
+  }
+
+  if (!check_access(rname, rinstance, rrealm, MODACL)) {
+    syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry",
+              rname, rinstance, rrealm, valsin1->name, valsin1->instance);
+    return KADM_UNAUTH;
+  }
+
+  syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ",
+            valsin1->name, valsin1->instance, rname, rinstance, rrealm);
+  krb5_425_conv_principal(kadm_context, valsin1->name, valsin1->instance,
+                         server_parm.krbrlm, &theprinc);
+  if (retval)
+    failmod(retval);
+  numfound = 1;
+  retval = krb5_db_get_principal(kadm_context, theprinc, &newentry,
+                                &numfound, &more);
+  if (retval) {
+    krb5_free_principal(kadm_context, theprinc);
+    failmod(retval);
+  } else if (numfound == 1) {
+    kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2);
+    krb5_free_principal(kadm_context, newentry.principal);
+    newentry.principal = theprinc;
+    if (IS_FIELD(KADM_EXPDATE,valsin2->fields))
+      newentry.expiration = temp_key.exp_date;
+    if (IS_FIELD(KADM_ATTR,valsin2->fields))
+      newentry.attributes = temp_key.attributes;
+    if (IS_FIELD(KADM_MAXLIFE,valsin2->fields))
+      newentry.max_life = temp_key.max_life; 
+    if (IS_FIELD(KADM_DESKEY,valsin2->fields)) {
+      newentry.kvno++;
+      newentry.mkvno = server_parm.mkvno;
+      if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) {
+       krb5_db_free_principal(kadm_context, &newentry, 1);
+       memset((char *)&temp_key, 0, sizeof (temp_key));
+       failmod(KADM_NOMEM);
+      }
+      newpw.magic = KV5M_KEYBLOCK;
+      newpw.etype = ETYPE_UNKNOWN;
+      newpw.length = 8;
+      newpw.keytype = KEYTYPE_DES;
+      temp_key.key_low = ntohl(temp_key.key_low);
+      temp_key.key_high = ntohl(temp_key.key_high);
+      memcpy(newpw.contents, &temp_key.key_low, 4);
+      memcpy(newpw.contents + 4, &temp_key.key_high, 4);
+      /* encrypt new key in master key */
+      retval = krb5_kdb_encrypt_key(kadm_context, &server_parm.master_encblock,
+                                   &newpw, &encpw);
+      memset(newpw.contents, 0, newpw.length);
+      free(newpw.contents);
+      memset((char *)&temp_key, 0, sizeof(temp_key));
+      if (retval) {
+       krb5_db_free_principal(kadm_context, &newentry, 1);
+       failmod(retval);
+      }
+      if (newentry.key.contents) {
+       memset((char *)newentry.key.contents, 0, newentry.key.length);
+       free(newentry.key.contents);
+      }
+      newentry.key = encpw;
+    }
+    if (retval = krb5_timeofday(kadm_context, &newentry.mod_date)) {
+       krb5_db_free_principal(kadm_context, &newentry, 1);
+       failmod(retval);
+    }
+    retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm,
+                                    &newentry.mod_name);
+    if (retval) {
+      krb5_db_free_principal(kadm_context, &newentry, 1);
+      failmod(retval);
+    }
+    numfound = 1;
+    retval = krb5_db_put_principal(kadm_context, &newentry, &numfound);
+    memset((char *)&data_o, 0, sizeof(data_o));
+    if (retval) {
+      krb5_db_free_principal(kadm_context, &newentry, 1);
+      failmod(retval);
+    } else {
+      numfound = 1;
+      retval = krb5_db_get_principal(kadm_context, newentry.principal, &odata,
+                                    &numfound, &more);
+      krb5_db_free_principal(kadm_context, &newentry, 1);
+      if (retval) {
+       failmod(retval);
+      } else if (numfound != 1 || more) {
+       krb5_db_free_principal(kadm_context, &odata, numfound);
+       failmod(KADM_UK_RERROR);
+      }
+      retval = kadm_entry2princ(odata, &data_o);
+      krb5_db_free_principal(kadm_context, &odata, 1);
+      if (retval)
+       failmod(retval);
+      memset((char *) fields, 0, sizeof(fields));
+      SET_FIELD(KADM_NAME,fields);
+      SET_FIELD(KADM_INST,fields);
+      SET_FIELD(KADM_EXPDATE,fields);
+      SET_FIELD(KADM_ATTR,fields);
+      SET_FIELD(KADM_MAXLIFE,fields);
+      kadm_prin_to_vals(fields, valsout, &data_o);
+      syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance);
+      return KADM_DATA;                /* Set all the appropriate fields */
+    }
+  } else {
+    failmod(KADM_NOENTRY);
+  }
+}
+#undef failmod
+
+#define failchange(code) {  syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; }
+
+kadm_change (rname, rinstance, rrealm, newpw)
+char *rname;
+char *rinstance;
+char *rrealm;
+des_cblock newpw;
+{
+  int numfound;
+  krb5_boolean more;
+  krb5_principal rprinc;
+  krb5_error_code retval;
+  krb5_keyblock localpw;
+  krb5_encrypted_keyblock encpw;
+  krb5_db_entry odata;
+
+  if (strcmp(server_parm.krbrlm, rrealm)) {
+      syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n",
+                rname, rinstance, rrealm);
+      return(KADM_WRONG_REALM);
+  }
+
+  if (wildcard(rname) || wildcard(rinstance)) {
+      failchange(KADM_ILL_WILDCARD);
+  }
+  syslog(LOG_INFO, "'%s.%s@%s' wants to change its password",
+            rname, rinstance, rrealm);
+  retval = krb5_425_conv_principal(kadm_context, rname, rinstance,
+                                  server_parm.krbrlm, &rprinc);
+  if (retval)
+    failchange(retval);
+  if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL)
+    failchange(KADM_NOMEM);
+  memcpy(localpw.contents, newpw, 8);
+  localpw.magic = KV5M_KEYBLOCK;
+  localpw.etype = ETYPE_UNKNOWN;
+  localpw.keytype = KEYTYPE_DES;
+  localpw.length = 8;
+  /* encrypt new key in master key */
+  retval = krb5_kdb_encrypt_key(kadm_context, &server_parm.master_encblock,
+                               &localpw, &encpw);
+  memset((char *)localpw.contents, 0, 8);
+  free(localpw.contents);
+  if (retval) {
+    krb5_free_principal(kadm_context, rprinc);
+    failchange(retval);
+  }
+  numfound = 1;
+  retval = krb5_db_get_principal(kadm_context, rprinc, &odata,
+                                &numfound, &more);
+  krb5_free_principal(kadm_context, rprinc);
+  if (retval) {
+    failchange(retval);
+  } else if (numfound == 1) {
+    odata.key = encpw;
+    odata.kvno++;
+    odata.mkvno = server_parm.mkvno;
+    if (retval = krb5_timeofday(kadm_context, &odata.mod_date)) {
+       krb5_db_free_principal(kadm_context, &odata, 1);
+       failchange(retval);
+    }
+    krb5_425_conv_principal(kadm_context, rname, rinstance,
+                           server_parm.krbrlm, &odata.mod_name);
+    if (retval) {
+      krb5_db_free_principal(kadm_context, &odata, 1);
+      failchange(retval);
+    }
+    numfound = 1;
+    retval = krb5_db_put_principal(kadm_context, &odata, &numfound);
+    krb5_db_free_principal(kadm_context, &odata, 1);
+    if (retval) {
+      failchange(retval);
+    } else if (more) {
+      failchange(KADM_UK_SERROR);
+    } else {
+      syslog(LOG_INFO,
+            "'%s.%s@%s' password changed.", rname, rinstance, rrealm);
+      return KADM_SUCCESS;
+    }
+  }
+  else {
+    failchange(KADM_NOENTRY);
+  }
+}
+#undef failchange
+
+check_pw(newpw, checkstr)
+       des_cblock      newpw;
+       char            *checkstr;
+{
+#ifdef NOENCRYPTION
+       return 0;
+#else /* !NOENCRYPTION */
+       des_cblock      checkdes;
+
+       (void) des_string_to_key(checkstr, checkdes);
+       return(!memcmp(checkdes, newpw, sizeof(des_cblock)));
+#endif /* NOENCRYPTION */
+}
+
+char *reverse(str)
+       char    *str;
+{
+       static char newstr[80];
+       char    *p, *q;
+       int     i;
+
+       i = strlen(str);
+       if (i >= sizeof(newstr))
+               i = sizeof(newstr)-1;
+       p = str+i-1;
+       q = newstr;
+       q[i]='\0';
+       for(; i > 0; i--) 
+               *q++ = *p--;
+       
+       return(newstr);
+}
+
+int lower(str)
+       char    *str;
+{
+       register char   *cp;
+       int     effect=0;
+
+       for (cp = str; *cp; cp++) {
+               if (isupper(*cp)) {
+                       *cp = tolower(*cp);
+                       effect++;
+               }
+       }
+       return(effect);
+}
+
+des_check_gecos(gecos, newpw)
+       char    *gecos;
+       des_cblock newpw;
+{
+       char            *cp, *ncp, *tcp;
+       
+       for (cp = gecos; *cp; ) {
+               /* Skip past punctuation */
+               for (; *cp; cp++)
+                       if (isalnum(*cp))
+                               break;
+               /* Skip to the end of the word */
+               for (ncp = cp; *ncp; ncp++)
+                       if (!isalnum(*ncp) && *ncp != '\'')
+                               break;
+               /* Delimit end of word */
+               if (*ncp)
+                       *ncp++ = '\0';
+               /* Check word to see if it's the password */
+               if (*cp) {
+                       if (check_pw(newpw, cp))
+                               return(KADM_INSECURE_PW);
+                       tcp = reverse(cp);
+                       if (check_pw(newpw, tcp))
+                               return(KADM_INSECURE_PW);
+                       if (lower(cp)) {
+                               if (check_pw(newpw, cp))
+                                       return(KADM_INSECURE_PW);
+                               tcp = reverse(cp);
+                               if (check_pw(newpw, tcp))
+                                       return(KADM_INSECURE_PW);
+                       }
+                       cp = ncp;                               
+               } else
+                       break;
+       }
+       return(0);
+}
+
+str_check_gecos(gecos, pwstr)
+       char    *gecos;
+       char    *pwstr;
+{
+       char            *cp, *ncp, *tcp;
+       
+       for (cp = gecos; *cp; ) {
+               /* Skip past punctuation */
+               for (; *cp; cp++)
+                       if (isalnum(*cp))
+                               break;
+               /* Skip to the end of the word */
+               for (ncp = cp; *ncp; ncp++)
+                       if (!isalnum(*ncp) && *ncp != '\'')
+                               break;
+               /* Delimit end of word */
+               if (*ncp)
+                       *ncp++ = '\0';
+               /* Check word to see if it's the password */
+               if (*cp) {
+                       if (!strcasecmp(pwstr, cp))
+                               return(KADM_INSECURE_PW);
+                       tcp = reverse(cp);
+                       if (!strcasecmp(pwstr, tcp))
+                               return(KADM_INSECURE_PW);
+                       cp = ncp;                               
+               } else
+                       break;
+       }
+       return(0);
+}
+
+
+kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring)
+char *rname;
+char *rinstance;
+char *rrealm;
+des_cblock newpw;
+char *pwstring;
+{
+       static DBM *pwfile = NULL;
+       int             retval;
+       datum           passwd, entry;
+       struct passwd   *ent;
+#ifdef HESIOD
+       extern struct passwd *hes_getpwnam();
+#endif
+       
+       if (pwstring && !check_pw(newpw, pwstring))
+               /*
+                * Someone's trying to toy with us....
+                */
+               return(KADM_PW_MISMATCH);
+       if (pwstring && (strlen(pwstring) < 5))
+               return(KADM_INSECURE_PW);
+       if (!pwfile) {
+               pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644);
+       }
+       if (pwfile) {
+               passwd.dptr = (char *) newpw;
+               passwd.dsize = 8;
+               entry = dbm_fetch(pwfile, passwd);
+               if (entry.dptr)
+                       return(KADM_INSECURE_PW);
+       }
+       if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname)))
+               return(KADM_INSECURE_PW);
+#ifdef HESIOD
+       ent = hes_getpwnam(rname);
+#else
+       ent = getpwnam(rname);
+#endif
+       if (ent && ent->pw_gecos) {
+               if (pwstring)
+                       retval = str_check_gecos(ent->pw_gecos, pwstring);
+               else
+                       retval = des_check_gecos(ent->pw_gecos, newpw);
+               if (retval)
+                       return(retval);
+       }
+       return(0);
+}
+
+/*
+ * This routine checks to see if a principal should be considered an
+ * allowable service name which can be changed by kadm_change_srvtab.
+ *
+ * We do this check by using the ACL library.  This makes the
+ * (relatively) reasonable assumption that both the name and the
+ * instance will  not contain '.' or '@'. 
+ */
+kadm_check_srvtab(name, instance)
+       char    *name;
+       char    *instance;
+{
+       char filename[MAXPATHLEN];
+       extern char *acldir;
+
+       (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE);
+       if (!acl_check(filename, name))
+               return(KADM_NOT_SERV_PRINC);
+
+       (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE);
+       if (acl_check(filename, instance))
+               return(KADM_NOT_SERV_PRINC);
+       return 0;
+}
+
+/*
+ * Routine to allow some people to change the key of a srvtab
+ * principal to a random key, which the admin server will return to
+ * the client.
+ */
+#define failsrvtab(code) {  syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; }
+
+kadm_chg_srvtab(rname, rinstance, rrealm, values)
+     char *rname;              /* requestors name */
+     char *rinstance;          /* requestors instance */
+     char *rrealm;             /* requestors realm */
+     Kadm_vals *values;
+{
+  int numfound, ret, isnew = 0;
+  des_cblock new_key;
+  krb5_principal inprinc;
+  krb5_error_code retval;
+  krb5_db_entry odata;
+  krb5_boolean more;
+  krb5_keyblock newpw;
+  krb5_encrypted_keyblock encpw;
+
+  if (!check_access(rname, rinstance, rrealm, STABACL))
+    failsrvtab(KADM_UNAUTH);
+  if (wildcard(rname) || wildcard(rinstance))
+    failsrvtab(KADM_ILL_WILDCARD);
+  if (ret = kadm_check_srvtab(values->name, values->instance))
+    failsrvtab(ret);
+
+  retval = krb5_425_conv_principal(kadm_context, values->name,
+                                  values->instance,
+                                  server_parm.krbrlm, &inprinc);
+  if (retval)
+    failsrvtab(retval);
+  /*
+   * OK, get the entry
+   */
+  numfound = 1;
+  retval = krb5_db_get_principal(kadm_context, inprinc, &odata,
+                                &numfound, &more);
+  if (retval) {
+    krb5_free_principal(kadm_context, inprinc);
+    failsrvtab(retval);
+  } else if (numfound) {
+    odata.kvno++;
+  } else {
+    /*
+     * This is a new srvtab entry that we're creating
+     */
+    isnew = 1;
+    memset((char *)&odata, 0, sizeof (odata));
+    odata.principal = inprinc;
+    odata.kvno = 1;
+    odata.max_life = server_parm.max_life;
+    odata.max_renewable_life = server_parm.max_rlife;
+    odata.mkvno = server_parm.mkvno;
+    odata.expiration = server_parm.expiration;
+    odata.attributes = 0;
+  }
+
+#ifdef NOENCRYPTION
+  memset(new_key, 0, sizeof(new_key));
+  new_key[0] = 127;
+#else
+  des_new_random_key(new_key);
+#endif
+  /*
+   * Store the new key in the return structure; also fill in the
+   * rest of the fields.
+   */
+  if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) {
+    krb5_db_free_principal(kadm_context, &odata, 1);
+    failsrvtab(KADM_NOMEM);
+  }
+  newpw.keytype = KEYTYPE_DES;
+  newpw.length = 8;
+  memcpy((char *)newpw.contents, new_key, 8);
+  memset((char *)new_key, 0, sizeof (new_key));
+  memcpy((char *)&values->key_low, newpw.contents, 4);
+  memcpy((char *)&values->key_high, newpw.contents + 4, 4);
+  values->key_low = htonl(values->key_low);
+  values->key_high = htonl(values->key_high);
+  values->max_life = odata.max_life / (60 * 5);
+  values->exp_date = odata.expiration;
+  values->attributes = odata.attributes;
+  memset(values->fields, 0, sizeof(values->fields));
+  SET_FIELD(KADM_NAME, values->fields);
+  SET_FIELD(KADM_INST, values->fields);
+  SET_FIELD(KADM_EXPDATE, values->fields);
+  SET_FIELD(KADM_ATTR, values->fields);
+  SET_FIELD(KADM_MAXLIFE, values->fields);
+  SET_FIELD(KADM_DESKEY, values->fields);
+
+  /*
+   * Encrypt the new key with the master key, and then update
+   * the database record
+   */
+  retval = krb5_kdb_encrypt_key(kadm_context, &server_parm.master_encblock,
+                               &newpw, &encpw);
+  memset((char *)newpw.contents, 0, 8);
+  free(newpw.contents);
+  if (odata.key.contents) {
+    memset((char *)odata.key.contents, 0, odata.key.length);
+    free(odata.key.contents);
+  }
+  odata.key = encpw;
+  if (retval) {
+    krb5_db_free_principal(kadm_context, &odata, 1);
+    failsrvtab(retval);
+  }
+  if (retval = krb5_timeofday(kadm_context, &odata.mod_date)) {
+      krb5_db_free_principal(kadm_context, &odata, 1);
+      failsrvtab(retval);
+  }
+  retval = krb5_425_conv_principal(kadm_context, rname, rinstance,
+                                  server_parm.krbrlm, &odata.mod_name);
+  if (retval) {
+    krb5_db_free_principal(kadm_context, &odata, 1);
+    failsrvtab(retval);
+  }
+  numfound = 1;
+  retval = krb5_db_put_principal(kadm_context, &odata, &numfound);
+  krb5_db_free_principal(kadm_context, &odata, 1);
+  if (retval) {
+    failsrvtab(retval);
+  }
+  else if (!numfound) {
+    failsrvtab(KADM_UK_SERROR);
+  } else {
+    syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.",
+          values->name, values->instance,
+          numfound ? "changed" : "created",
+          rname, rinstance, rrealm);
+    return KADM_DATA;
+  }
+}
+
+#undef failsrvtab
diff --git a/src/kadmin.v4/server/kadm_ser_wrap.c b/src/kadmin.v4/server/kadm_ser_wrap.c
new file mode 100644 (file)
index 0000000..abe9ee1
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * kadmin/v4server/kadm_ser_wrap.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side support functions
+ */
+
+
+#include <mit-copyright.h>
+/* 
+kadm_ser_wrap.c
+unwraps wrapped packets and calls the appropriate server subroutine
+*/
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_err.h>
+#include <syslog.h>
+#include "kadm_server.h"
+
+Kadm_Server server_parm;
+
+/* 
+kadm_ser_init
+set up the server_parm structure
+*/
+kadm_ser_init(inter, realm)
+    int inter;                 /* interactive or from file */
+    char realm[];
+{
+    struct servent *sep;
+    struct hostent *hp;
+    char hostname[MAXHOSTNAMELEN];
+    char *mkey_name;
+    krb5_error_code retval;
+    int numfound = 1;
+    krb5_boolean more;
+    krb5_db_entry master_entry;
+    krb5_enctype kdc_etype = DEFAULT_KDC_ETYPE;
+    
+    if (gethostname(hostname, sizeof(hostname)))
+       return KADM_NO_HOSTNAME;
+    
+    (void) strcpy(server_parm.sname, PWSERV_NAME);
+    (void) strcpy(server_parm.sinst, KRB_MASTER);
+    (void) strcpy(server_parm.krbrlm, realm);
+    if (krb5_build_principal(kadm_context,
+                            &server_parm.sprinc,
+                            strlen(realm),
+                            realm,
+                            PWSERV_NAME,
+                            KRB_MASTER, 0))
+       return KADM_NO_MAST;
+    server_parm.admin_fd = -1;
+    /* setting up the addrs */
+    if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL)
+       return KADM_NO_SERV;
+    memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr));
+    server_parm.admin_addr.sin_family = AF_INET;
+    if ((hp = gethostbyname(hostname)) == NULL)
+       return KADM_NO_HOSTNAME;
+    memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr,
+          hp->h_length);
+    server_parm.admin_addr.sin_port = sep->s_port;
+    /* setting up the database */
+    mkey_name = KRB5_KDB_M_NAME;
+    server_parm.master_keyblock.keytype = KEYTYPE_DES;
+    
+    if (!valid_etype(kdc_etype))
+       return KRB5_PROG_ETYPE_NOSUPP;
+    krb5_use_cstype(kadm_context, &server_parm.master_encblock, kdc_etype);
+    
+    retval = krb5_db_setup_mkey_name(kadm_context, mkey_name, realm,
+                                    (char **) 0,
+                                    &server_parm.master_princ);
+    if (retval)
+       return KADM_NO_MAST;
+    krb5_db_fetch_mkey(kadm_context, server_parm.master_princ,
+                      &server_parm.master_encblock,
+                      (inter == 1), FALSE, NULL,
+                      &server_parm.master_keyblock);
+    if (retval)
+       return KADM_NO_MAST;
+    retval = krb5_db_verify_master_key(kadm_context, server_parm.master_princ,
+                                      &server_parm.master_keyblock,
+                                      &server_parm.master_encblock);
+    if (retval)
+       return KADM_NO_VERI;
+    retval = krb5_process_key(kadm_context, &server_parm.master_encblock,
+                             &server_parm.master_keyblock);
+    if (retval)
+       return KADM_NO_VERI;
+    retval = krb5_db_get_principal(kadm_context, server_parm.master_princ,
+                                  &master_entry, &numfound, &more);
+    if (retval || more || !numfound)
+       return KADM_NO_VERI;
+    server_parm.max_life = master_entry.max_life;
+    server_parm.max_rlife = master_entry.max_renewable_life;
+    server_parm.expiration = master_entry.expiration;
+    server_parm.mkvno = master_entry.kvno;
+    /* don't set flags, as master has some extra restrictions
+       (??? quoted from kdb_edit.c) */
+    krb5_db_free_principal(kadm_context, &master_entry, numfound);
+    return KADM_SUCCESS;
+}
+
+
+static void errpkt(dat, dat_len, code)
+u_char **dat;
+int *dat_len;
+int code;
+{
+    u_long retcode;
+    char *pdat;
+
+    free((char *)*dat);                        /* free up req */
+    *dat_len = KADM_VERSIZE + sizeof(u_long);
+    *dat = (u_char *) malloc((unsigned)*dat_len);
+    if (!(*dat)) {
+       syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len);
+       abort();
+    }
+    pdat = (char *) *dat;
+    retcode = htonl((u_long) code);
+    (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+    memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(u_long));
+    return;
+}
+
+/*
+kadm_ser_in
+unwrap the data stored in dat, process, and return it.
+*/
+kadm_ser_in(dat,dat_len)
+u_char **dat;
+int *dat_len;
+{
+    u_char *in_st;                     /* pointer into the sent packet */
+    int in_len,retc;                   /* where in packet we are, for
+                                          returns */
+    u_long r_len;                      /* length of the actual packet */
+    KTEXT_ST authent;                  /* the authenticator */
+    AUTH_DAT ad;                       /* who is this, klink */
+    u_long ncksum;                     /* checksum of encrypted data */
+    des_key_schedule sess_sched;       /* our schedule */
+    MSG_DAT msg_st;
+    u_char *retdat, *tmpdat;
+    int retval, retlen;
+
+    if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) {
+       errpkt(dat, dat_len, KADM_BAD_VER);
+       return KADM_BAD_VER;
+    }
+    in_len = KADM_VERSIZE;
+    /* get the length */
+    if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0)
+       return KADM_LENGTH_ERROR;
+    in_len += retc;
+    authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(u_long);
+    memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length);
+    authent.mbz = 0;
+    /* service key should be set before here */
+    if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst,
+                         server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0))
+    {
+       errpkt(dat, dat_len,retc + krb_err_base);
+       return retc + krb_err_base;
+    }
+
+#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));}
+
+    in_st = *dat + *dat_len - r_len;
+#ifdef NOENCRYPTION
+    ncksum = 0;
+#else
+    ncksum = quad_cksum(in_st, (u_long *)0, (long) r_len, 0, ad.session);
+#endif
+    if (ncksum!=ad.checksum) {         /* yow, are we correct yet */
+       clr_cli_secrets();
+       errpkt(dat, dat_len,KADM_BAD_CHK);
+       return KADM_BAD_CHK;
+    }
+#ifdef NOENCRYPTION
+    memset(sess_sched, 0, sizeof(sess_sched));
+#else
+    des_key_sched(ad.session, sess_sched);
+#endif
+    if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session, 
+                                &server_parm.recv_addr,
+                                &server_parm.admin_addr, &msg_st)) {
+       clr_cli_secrets();
+       errpkt(dat, dat_len,retc + krb_err_base);
+       return retc + krb_err_base;
+    }
+    switch (msg_st.app_data[0]) {
+    case CHANGE_PW:
+       retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+                             &retdat, &retlen);
+       break;
+    case ADD_ENT:
+       retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+                             &retdat, &retlen);
+       break;
+    case DEL_ENT:
+       retval = kadm_ser_del(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+                             &retdat, &retlen);
+       break;
+    case GET_ENT:
+       retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+                             &retdat, &retlen);
+       break;
+    case MOD_ENT:
+       retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+                             &retdat, &retlen);
+       break;
+    case CHECK_PW:
+       retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+                              &retdat, &retlen);
+       break;
+    case CHG_STAB:
+       retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+                              &retdat, &retlen);
+       break;
+    default:
+       clr_cli_secrets();
+       errpkt(dat, dat_len, KADM_NO_OPCODE);
+       return KADM_NO_OPCODE;
+    }
+    /* Now seal the response back into a priv msg */
+    free((char *)*dat);
+    tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE +
+                                         sizeof(u_long)));
+    if (!tmpdat) {
+       clr_cli_secrets();
+       syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!",
+           retlen + KADM_VERSIZE + sizeof(u_long));
+       errpkt(dat, dat_len, KADM_NOMEM);
+       return KADM_NOMEM;
+    }
+    (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE);
+    retval = htonl((u_long)retval);
+    memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(u_long));
+    if (retlen) {
+       memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(u_long), (char *)retdat,
+              retlen);
+       free((char *)retdat);
+    }
+    /* slop for mk_priv stuff */
+    *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE +
+                                        sizeof(u_long) + 200));
+    if ((*dat_len = krb_mk_priv(tmpdat, *dat,
+                               (u_long) (retlen + KADM_VERSIZE +
+                                         sizeof(u_long)),
+                               sess_sched,
+                               ad.session, &server_parm.admin_addr,
+                               &server_parm.recv_addr)) < 0) {
+       clr_cli_secrets();
+       errpkt(dat, dat_len, KADM_NO_ENCRYPT);
+       return KADM_NO_ENCRYPT;
+    }
+    clr_cli_secrets();
+    return KADM_SUCCESS;
+}
diff --git a/src/kadmin.v4/server/kadm_server.c b/src/kadmin.v4/server/kadm_server.c
new file mode 100644 (file)
index 0000000..42b0ebf
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ * kadmin/v4server/kadm_server.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side subroutines
+ */
+
+
+#include <mit-copyright.h>
+
+#include "k5-int.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#else
+#include <time.h>
+#endif
+
+#include <kadm.h>
+#include <kadm_err.h>
+
+int fascist_cpw = 0;           /* Be fascist about insecure passwords? */
+
+char bad_pw_err[] =
+       "\007\007\007ERROR: Insecure password not accepted.  Please choose another.\n\n";
+
+char bad_pw_warn[] =
+       "\007\007\007WARNING: You have chosen an insecure password.  You may wish to\nchoose a better password.\n\n";
+
+char check_pw_msg[] =
+       "You have entered an insecure password.  You should choose another.\n\n";
+
+char pw_blurb[] =
+"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary.  Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation.  A good password can be:\n\n   -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n   -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n   -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems.  Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n";
+
+
+/* from V4 month_sname.c --  was not part of API */
+/*
+ * Given an integer 1-12, month_sname() returns a string
+ * containing the first three letters of the corresponding
+ * month.  Returns 0 if the argument is out of range.
+ */
+
+static char *month_sname(n)
+    int n;
+{
+    static char *name[] = {
+        "Jan","Feb","Mar","Apr","May","Jun",
+        "Jul","Aug","Sep","Oct","Nov","Dec"
+    };
+    return((n < 1 || n > 12) ? 0 : name [n-1]);
+}
+
+/* from V4 log.c --  was not part of API */
+
+/*
+ * krb_log() is used to add entries to the logfile (see krb_set_logfile()
+ * below).  Note that it is probably not portable since it makes
+ * assumptions about what the compiler will do when it is called
+ * with less than the correct number of arguments which is the
+ * way it is usually called.
+ *
+ * The log entry consists of a timestamp and the given arguments
+ * printed according to the given "format".
+ *
+ * The log file is opened and closed for each log entry.
+ *
+ * The return value is undefined.
+ */
+
+/* static char *log_name = KRBLOG; */
+/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */
+static char *log_name = KADM_SYSLOG; 
+
+static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0)
+    char *format;
+    char *a1,*a2,*a3,*a4,*a5,*a6,*a7,*a8,*a9,*a0;
+{
+    FILE *logfile;
+    time_t now;
+    struct tm *tm;
+
+    if ((logfile = fopen(log_name,"a")) == NULL)
+        return;
+
+    (void) time(&now);
+    tm = localtime(&now);
+
+    fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday,
+            month_sname(tm->tm_mon + 1),tm->tm_year,
+            tm->tm_hour, tm->tm_min, tm->tm_sec);
+    fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0);
+    fprintf(logfile,"\n");
+    (void) fclose(logfile);
+    return;
+}
+
+
+/* 
+kadm_ser_cpw - the server side of the change_password routine
+  recieves    : KTEXT, {key}
+  returns     : CKSUM, RETCODE
+  acl         : caller can change only own password
+
+Replaces the password (i.e. des key) of the caller with that specified in key.
+Returns no actual data from the master server, since this is called by a user
+*/
+kadm_ser_cpw(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+    krb5_int32 keylow, keyhigh;
+    char pword[MAX_KPW_LEN];
+    int no_pword = 0;
+    des_cblock newkey;
+    int status, stvlen = 0;
+    int        retval;
+    extern int kadm_approve_pw();
+
+    /* take key off the stream, and change the database */
+
+    if ((status = stv_long(dat, &keyhigh, 0, len)) < 0)
+       return(KADM_LENGTH_ERROR);
+    stvlen += status;
+    if ((status = stv_long(dat, &keylow, stvlen, len)) < 0)
+       return(KADM_LENGTH_ERROR);
+    stvlen += status;
+    if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) {
+       no_pword++;
+       pword[0]='\0';
+    }
+    stvlen += status;
+
+    keylow = ntohl(keylow);
+    keyhigh = ntohl(keyhigh);
+    memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4);
+    memcpy((char *)newkey, (char *)&keylow, 4);
+    if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm,
+                       newkey, no_pword ? 0 : pword)) {
+           if (retval == KADM_PW_MISMATCH) {
+                   /*
+                    * Very strange!!!  This means that the cleartext
+                    * password which was sent and the DES cblock
+                    * didn't match!
+                    */
+                   (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?",
+                                  ad->pname, ad->pinst, ad->prealm);
+                   return(retval);
+           }
+           if (fascist_cpw) {
+                   *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1;
+                   if (*datout = (u_char *) malloc(*outlen)) {
+                           strcpy(*datout, bad_pw_err);
+                           strcat(*datout, pw_blurb);
+                   } else
+                           *outlen = 0;
+                   (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw",
+                                  ad->pname, ad->pinst, ad->prealm);
+#ifdef notdef
+                   /* For debugging only, probably a bad idea */
+                   if (!no_pword)
+                           (void) krb_log("The password was %s\n", pword);
+#endif
+                   return(retval);
+           } else {
+                   *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1;
+                   if (*datout = (u_char *) malloc(*outlen)) {
+                           strcpy(*datout, bad_pw_warn);
+                           strcat(*datout, pw_blurb);
+                   } else
+                           *outlen = 0;
+                   (void) krb_log("'%s.%s@%s' used an insecure password in changepw",
+                                  ad->pname, ad->pinst, ad->prealm);
+           }
+    } else {
+           *datout = 0;
+           *outlen = 0;
+    }
+
+    retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey);
+    keylow = keyhigh = 0;
+    memset(newkey, 0, sizeof(newkey));
+    return retval;
+}
+
+/*
+kadm_ser_add - the server side of the add_entry routine
+  recieves    : KTEXT, {values}
+  returns     : CKSUM, RETCODE, {values}
+  acl         : su, sms (as alloc)
+
+Adds and entry containing values to the database
+returns the values of the entry, so if you leave certain fields blank you will
+   be able to determine the default values they are set to
+*/
+int
+kadm_ser_add(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+  Kadm_vals values, retvals;
+  int status;
+
+  if ((status = stream_to_vals(dat, &values, len)) < 0)
+      return(KADM_LENGTH_ERROR);
+  if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm,
+                             &values, &retvals)) == KADM_DATA) {
+      *outlen = vals_to_stream(&retvals,datout);
+      retvals.key_low = retvals.key_high = 0;
+      return KADM_SUCCESS;
+  } else {
+      *outlen = 0;
+      return status;
+  }
+}
+
+/*
+kadm_ser_del - the server side of the del_entry routine
+  recieves    : KTEXT, {values}
+  returns     : CKSUM, RETCODE, {values}
+  acl         : su, sms (as alloc)
+
+Deletes an entry containing values to the database
+returns the values of the entry, so if you leave certain fields blank you will
+   be able to determine the default values they are set to
+*/
+kadm_ser_del(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+  Kadm_vals values, retvals;
+  int status;
+
+  if ((status = stream_to_vals(dat, &values, len)) < 0)
+      return(KADM_LENGTH_ERROR);
+  if ((status = kadm_del_entry(ad->pname, ad->pinst, ad->prealm,
+                             &values, &retvals)) == KADM_DATA) {
+      *outlen = vals_to_stream(&retvals,datout);
+      retvals.key_low = retvals.key_high = 0;
+      return KADM_SUCCESS;
+  } else {
+      *outlen = 0;
+      return status;
+  }
+}
+
+/*
+kadm_ser_mod - the server side of the mod_entry routine
+  recieves    : KTEXT, {values, values}
+  returns     : CKSUM, RETCODE, {values}
+  acl         : su, sms (as register or dealloc)
+
+Modifies all entries corresponding to the first values so they match the
+   second values.
+returns the values for the changed entries
+*/
+kadm_ser_mod(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+  Kadm_vals vals1, vals2, retvals;
+  int wh;
+  int status;
+
+  if ((wh = stream_to_vals(dat, &vals1, len)) < 0)
+      return KADM_LENGTH_ERROR;
+  if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0)
+      return KADM_LENGTH_ERROR;
+  if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1,
+                              &vals2, &retvals)) == KADM_DATA) {
+      *outlen = vals_to_stream(&retvals,datout);
+      retvals.key_low = retvals.key_high = 0;
+      return KADM_SUCCESS;
+  } else {
+      *outlen = 0;
+      return status;
+  }
+}
+
+/*
+kadm_ser_get
+  recieves   : KTEXT, {values, flags}
+  returns    : CKSUM, RETCODE, {count, values, values, values}
+  acl        : su
+
+gets the fields requested by flags from all entries matching values
+returns this data for each matching recipient, after a count of how many such
+  matches there were
+*/
+kadm_ser_get(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+  Kadm_vals values, retvals;
+  u_char fl[FLDSZ];
+  int loop,wh;
+  int status;
+
+  if ((wh = stream_to_vals(dat, &values, len)) < 0)
+      return KADM_LENGTH_ERROR;
+  if (wh + FLDSZ > len)
+      return KADM_LENGTH_ERROR;
+  for (loop=FLDSZ-1; loop>=0; loop--)
+    fl[loop] = dat[wh++];
+  if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm,
+                             &values, fl, &retvals)) == KADM_DATA) {
+      *outlen = vals_to_stream(&retvals,datout);
+      retvals.key_low = retvals.key_high = 0;
+      return KADM_SUCCESS;
+  } else {
+      *outlen = 0;
+      return status;
+  }
+}
+
+/* 
+kadm_ser_ckpw - the server side of the check_password routine
+  recieves    : KTEXT, {key}
+  returns     : CKSUM, RETCODE
+  acl         : none
+
+Checks to see if the des key passed from the caller is a "secure" password.
+*/
+kadm_ser_ckpw(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+    unsigned long keylow, keyhigh;
+    char pword[MAX_KPW_LEN];
+    int no_pword = 0;
+    des_cblock newkey;
+    int stvlen = 0,status;
+    int        retval;
+    extern int kadm_approve_pw();
+
+    /* take key off the stream, and check it */
+
+    if ((status = stv_long(dat, &keyhigh, 0, len)) < 0)
+       return(KADM_LENGTH_ERROR);
+    stvlen += status;
+    if ((status = stv_long(dat, &keylow, stvlen, len)) < 0)
+       return(KADM_LENGTH_ERROR);
+    stvlen += status;
+    if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) {
+       no_pword++;
+       pword[0]='\0';
+    }
+    stvlen += status;
+
+    keylow = ntohl(keylow);
+    keyhigh = ntohl(keyhigh);
+    memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4);
+    memcpy((char *)newkey, (char *)&keylow, 4);
+    keylow = keyhigh = 0;
+    retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey,
+                       no_pword ? 0 : pword);
+    memset(newkey, 0, sizeof(newkey));
+    if (retval) {
+           *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1;
+           if (*datout = (u_char *) malloc(*outlen)) {
+                   strcpy(*datout, check_pw_msg);
+                   strcat(*datout, pw_blurb);
+           } else
+                   *outlen = 0;
+           (void) krb_log("'%s.%s@%s' sent an insecure password to be checked",
+                          ad->pname, ad->pinst, ad->prealm);
+           return(retval);
+    } else {
+           *datout = 0;
+           *outlen = 0;
+           (void) krb_log("'%s.%s@%s' sent a secure password to be checked",
+                          ad->pname, ad->pinst, ad->prealm);
+    }
+    return(0);
+}
+
+/*
+kadm_ser_stab - the server side of the change_srvtab routine
+  recieves    : KTEXT, {values}
+  returns     : CKSUM, RETCODE, {values}
+  acl         : su, sms (as register or dealloc)
+
+Creates or modifies the specified service principal to have a random
+key, which is sent back to the client.  The key version is returned in
+the max_life field of the values structure.  It's a hack, but it's a
+backwards compatible hack....
+*/
+kadm_ser_stab(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+  Kadm_vals values;
+  int status;
+
+  if ((status = stream_to_vals(dat, &values, len)) < 0)
+         return KADM_LENGTH_ERROR;
+  status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values);
+  if (status == KADM_DATA) {
+      *outlen = vals_to_stream(&values,datout);
+      values.key_low = values.key_high = 0;
+      return KADM_SUCCESS;
+  } else {
+      *outlen = 0;
+      return status;
+  }
+}
diff --git a/src/kadmin.v4/server/kadm_server.h b/src/kadmin.v4/server/kadm_server.h
new file mode 100644 (file)
index 0000000..d852bca
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * kadmin/v4server/kadm_server.h
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Definitions for Kerberos administration server & client
+ */
+
+#ifndef KADM_SERVER_DEFS
+#define KADM_SERVER_DEFS
+
+#include <mit-copyright.h>
+/*
+ * kadm_server.h
+ * Header file for the fourth attempt at an admin server
+ * Doug Church, December 28, 1989, MIT Project Athena
+ *    ps. Yes that means this code belongs to athena etc...
+ *        as part of our ongoing attempt to copyright all greek names
+ */
+
+#include <sys/types.h>
+#include <krb.h>
+#include <des.h>
+#include "k5-int.h"
+
+typedef struct {
+  struct sockaddr_in admin_addr;
+  struct sockaddr_in recv_addr;
+  int recv_addr_len;
+  int admin_fd;                        /* our link to clients */
+  char sname[ANAME_SZ];
+  char sinst[INST_SZ];
+  char krbrlm[REALM_SZ];
+  krb5_principal sprinc;
+  krb5_encrypt_block master_encblock;
+  krb5_principal master_princ;
+  krb5_keyblock master_keyblock;
+  krb5_deltat max_life;
+  krb5_deltat max_rlife;
+  krb5_timestamp expiration;
+  krb5_flags flags;
+  krb5_kvno mkvno;
+} Kadm_Server;
+
+#define        ADD_ACL_FILE    "/v4acl.add"
+#define        GET_ACL_FILE    "/v4acl.get"
+#define        MOD_ACL_FILE    "/v4acl.mod"
+#define        DEL_ACL_FILE    "/v4acl.del"
+#define STAB_ACL_FILE  "/v4acl.srvtab"
+#define STAB_SERVICES_FILE     "/v4stab_services"
+#define STAB_HOSTS_FILE                "/v4stab_bad_hosts"
+
+krb5_context kadm_context;
+
+#endif /* KADM_SERVER_DEFS */
diff --git a/src/kadmin.v4/server/kadm_stream.c b/src/kadmin.v4/server/kadm_stream.c
new file mode 100644 (file)
index 0000000..f09222a
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * kadmin/v4server/kadm_stream.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Stream conversion functions for Kerberos administration server
+ */
+
+
+#include <mit-copyright.h>
+#include <string.h>
+/*
+  kadm_stream.c
+  this holds the stream support routines for the kerberos administration server
+
+    vals_to_stream: converts a vals struct to a stream for transmission
+       internals build_field_header, vts_[string, char, long, short]
+    stream_to_vals: converts a stream to a vals struct
+       internals check_field_header, stv_[string, char, long, short]
+    error: prints out a kadm error message, returns
+    fatal: prints out a kadm fatal error message, exits
+*/
+
+#include "kadm.h"
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/* 
+vals_to_stream
+  recieves    : kadm_vals *, u_char *
+  returns     : a realloced and filled in u_char *
+     
+this function creates a byte-stream representation of the kadm_vals structure
+*/
+vals_to_stream(dt_in, dt_out)
+Kadm_vals *dt_in;
+u_char **dt_out;
+{
+  int vsloop, stsize;          /* loop counter, stream size */
+
+  stsize = build_field_header(dt_in->fields, dt_out);
+  for (vsloop=31; vsloop>=0; vsloop--)
+    if (IS_FIELD(vsloop,dt_in->fields)) {
+      switch (vsloop) {
+      case KADM_NAME:
+         stsize+=vts_string(dt_in->name, dt_out, stsize);
+         break;
+      case KADM_INST:
+         stsize+=vts_string(dt_in->instance, dt_out, stsize);
+         break;
+      case KADM_EXPDATE:
+         stsize+=vts_long(dt_in->exp_date, dt_out, stsize);
+         break;
+      case KADM_ATTR:
+         stsize+=vts_short(dt_in->attributes, dt_out, stsize);
+         break;
+      case KADM_MAXLIFE:
+         stsize+=vts_char(dt_in->max_life, dt_out, stsize);
+         break;
+      case KADM_DESKEY: 
+         stsize+=vts_long(dt_in->key_high, dt_out, stsize); 
+         stsize+=vts_long(dt_in->key_low, dt_out, stsize); 
+         break;
+      default:
+         break;
+      }
+}
+  return(stsize);
+}  
+
+build_field_header(cont, st)
+u_char *cont;                  /* container for fields data */
+u_char **st;                   /* stream */
+{
+  *st = (u_char *) malloc (4);
+  memcpy((char *) *st, (char *) cont, 4);
+  return 4;                    /* return pointer to current stream location */
+}
+
+vts_string(dat, st, loc)
+char *dat;                     /* a string to put on the stream */
+u_char **st;                   /* base pointer to the stream */
+int loc;                       /* offset into the stream for current data */
+{
+  *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1));
+  memcpy((char *)(*st + loc), dat, strlen(dat)+1);
+  return strlen(dat)+1;
+}
+
+vts_short(dat, st, loc)
+u_short dat;                   /* the attributes field */
+u_char **st;                   /* a base pointer to the stream */
+int loc;                       /* offset into the stream for current data */
+{
+  u_short temp;                        /* to hold the net order short */
+
+  temp = htons(dat);           /* convert to network order */
+  *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short)));
+  memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short));
+  return sizeof(u_short);
+}
+
+vts_long(dat, st, loc)
+u_long dat;                    /* the attributes field */
+u_char **st;                   /* a base pointer to the stream */
+int loc;                       /* offset into the stream for current data */
+{
+  u_long temp;                 /* to hold the net order short */
+
+  temp = htonl(dat);           /* convert to network order */
+  *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_long)));
+  memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_long));
+  return sizeof(u_long);
+}
+
+    
+vts_char(dat, st, loc)
+u_char dat;                    /* the attributes field */
+u_char **st;                   /* a base pointer to the stream */
+int loc;                       /* offset into the stream for current data */
+{
+  *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char)));
+  (*st)[loc] = (u_char) dat;
+  return 1;
+}
+    
+/* 
+stream_to_vals
+  recieves    : u_char *, kadm_vals *
+  returns     : a kadm_vals filled in according to u_char *
+     
+this decodes a byte stream represntation of a vals struct into kadm_vals
+*/
+stream_to_vals(dt_in, dt_out, maxlen)
+u_char *dt_in;
+Kadm_vals *dt_out;
+int maxlen;                            /* max length to use */
+{
+  register int vsloop, stsize;         /* loop counter, stream size */
+  register int status;
+
+  memset((char *) dt_out, 0, sizeof(*dt_out));
+
+  stsize = check_field_header(dt_in, dt_out->fields, maxlen);
+  if (stsize < 0)
+      return(-1);
+  for (vsloop=31; vsloop>=0; vsloop--)
+    if (IS_FIELD(vsloop,dt_out->fields))
+      switch (vsloop) {
+      case KADM_NAME:
+         if ((status = stv_string(dt_in, dt_out->name, stsize,
+                                  sizeof(dt_out->name), maxlen)) < 0)
+             return(-1);
+         stsize += status;
+         break;
+      case KADM_INST:
+         if ((status = stv_string(dt_in, dt_out->instance, stsize,
+                                  sizeof(dt_out->instance), maxlen)) < 0)
+             return(-1);
+         stsize += status;
+         break;
+      case KADM_EXPDATE:
+         if ((status = stv_long(dt_in, &dt_out->exp_date, stsize,
+                                maxlen)) < 0)
+             return(-1);
+         stsize += status;
+         break;
+      case KADM_ATTR:
+         if ((status = stv_short(dt_in, &dt_out->attributes, stsize,
+                                 maxlen)) < 0)
+             return(-1);
+         stsize += status;
+         break;
+      case KADM_MAXLIFE:
+         if ((status = stv_char(dt_in, &dt_out->max_life, stsize,
+                                maxlen)) < 0)
+             return(-1);
+         stsize += status;
+         break;
+      case KADM_DESKEY:
+         if ((status = stv_long(dt_in, &dt_out->key_high, stsize,
+                                   maxlen)) < 0)
+             return(-1);
+         stsize += status;
+         if ((status = stv_long(dt_in, &dt_out->key_low, stsize,
+                                   maxlen)) < 0)
+             return(-1);
+         stsize += status;
+         break;
+      default:
+         break;
+      }
+  return stsize;
+}  
+
+check_field_header(st, cont, maxlen)
+u_char *st;                    /* stream */
+u_char *cont;                  /* container for fields data */
+int maxlen;
+{
+  if (4 > maxlen)
+      return(-1);
+  memcpy((char *) cont, (char *) st, 4);
+  return 4;                    /* return pointer to current stream location */
+}
+
+stv_string(st, dat, loc, stlen, maxlen)
+register u_char *st;           /* base pointer to the stream */
+char *dat;                     /* a string to read from the stream */
+register int loc;              /* offset into the stream for current data */
+int stlen;                     /* max length of string to copy in */
+int maxlen;                    /* max length of input stream */
+{
+  int maxcount;                                /* max count of chars to copy */
+
+  maxcount = min(maxlen - loc, stlen);
+
+  (void) strncpy(dat, (char *)st + loc, maxcount);
+
+  if (dat[maxcount-1]) /* not null-term --> not enuf room */
+      return(-1);
+  return strlen(dat)+1;
+}
+
+stv_short(st, dat, loc, maxlen)
+u_char *st;                    /* a base pointer to the stream */
+u_short *dat;                  /* the attributes field */
+int loc;                       /* offset into the stream for current data */
+int maxlen;
+{
+  u_short temp;                        /* to hold the net order short */
+
+  if (loc + sizeof(u_short) > maxlen)
+      return(-1);
+  memcpy((char *) &temp, (char *)((u_long)st+(u_long)loc), sizeof(u_short));
+  *dat = ntohs(temp);          /* convert to network order */
+  return sizeof(u_short);
+}
+
+stv_long(st, dat, loc, maxlen)
+u_char *st;                    /* a base pointer to the stream */
+u_long *dat;                   /* the attributes field */
+int loc;                       /* offset into the stream for current data */
+int maxlen;                    /* maximum length of st */
+{
+  u_long temp;                 /* to hold the net order short */
+
+  if (loc + sizeof(u_long) > maxlen)
+      return(-1);
+  memcpy((char *) &temp, (char *)((u_long)st+(u_long)loc), sizeof(u_long));
+  *dat = ntohl(temp);          /* convert to network order */
+  return sizeof(u_long);
+}
+    
+stv_char(st, dat, loc, maxlen)
+u_char *st;                    /* a base pointer to the stream */
+u_char *dat;                   /* the attributes field */
+int loc;                       /* offset into the stream for current data */
+int maxlen;
+{
+  if (loc + 1 > maxlen)
+      return(-1);
+  *dat = *(st + loc);
+  return 1;
+}
+
diff --git a/src/kadmin.v4/server/kadm_supp.c b/src/kadmin.v4/server/kadm_supp.c
new file mode 100644 (file)
index 0000000..9d2f8de
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * kadmin/v4server/kadm_supp.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Support functions for Kerberos administration server & clients
+ */
+
+
+#include <mit-copyright.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+/*
+  kadm_supp.c
+  this holds the support routines for the kerberos administration server
+
+    error: prints out a kadm error message, returns
+    fatal: prints out a kadm fatal error message, exits
+    prin_vals: prints out data associated with a Principal in the vals
+           structure
+*/
+
+#include "kadm.h"
+#include "krb_db.h"
+    
+/*
+prin_vals:
+  recieves    : a vals structure
+*/
+void prin_vals(vals)
+Kadm_vals *vals;
+{
+   printf("Info in Database for %s.%s:\n", vals->name, vals->instance);
+   printf("   Max Life: %d   Exp Date: %s\n",vals->max_life, 
+         asctime(localtime((long *)&vals->exp_date)));
+   printf("   Attribs: %.2x  key: %u %u\n",vals->attributes,
+         vals->key_low, vals->key_high);
+}
+
+#ifdef notdef
+nierror(s)
+int s;
+{
+    extern char *error_message();
+    printf("Kerberos admin server loses..... %s\n",error_message(s));
+    return(s);
+}
+#endif
+
+/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal,
+   it copies the fields in Principal specified by fields into Kadm_vals, 
+   i.e from old to new */
+
+kadm_prin_to_vals(fields, new, old)
+u_char fields[FLDSZ];
+Kadm_vals *new;
+Principal *old;
+{
+  memset((char *)new, 0, sizeof(*new));
+  if (IS_FIELD(KADM_NAME,fields)) {
+      (void) strncpy(new->name, old->name, ANAME_SZ); 
+      SET_FIELD(KADM_NAME, new->fields);
+  }
+  if (IS_FIELD(KADM_INST,fields)) {
+      (void) strncpy(new->instance, old->instance, INST_SZ); 
+      SET_FIELD(KADM_INST, new->fields);
+  }      
+  if (IS_FIELD(KADM_EXPDATE,fields)) {
+      new->exp_date   = old->exp_date; 
+      SET_FIELD(KADM_EXPDATE, new->fields);
+  }      
+  if (IS_FIELD(KADM_ATTR,fields)) {
+    new->attributes = old->attributes; 
+      SET_FIELD(KADM_MAXLIFE, new->fields);
+  }      
+  if (IS_FIELD(KADM_MAXLIFE,fields)) {
+    new->max_life   = old->max_life; 
+      SET_FIELD(KADM_MAXLIFE, new->fields);
+  }      
+  if (IS_FIELD(KADM_DESKEY,fields)) {
+    new->key_low    = old->key_low; 
+    new->key_high   = old->key_high; 
+    SET_FIELD(KADM_DESKEY, new->fields);
+  }
+}
+
+kadm_vals_to_prin(fields, new, old)
+u_char fields[FLDSZ];
+Principal *new;
+Kadm_vals *old;
+{
+
+  memset((char *)new, 0, sizeof(*new));
+  if (IS_FIELD(KADM_NAME,fields))
+    (void) strncpy(new->name, old->name, ANAME_SZ); 
+  if (IS_FIELD(KADM_INST,fields))
+    (void) strncpy(new->instance, old->instance, INST_SZ); 
+  if (IS_FIELD(KADM_EXPDATE,fields))
+    new->exp_date   = old->exp_date; 
+  if (IS_FIELD(KADM_ATTR,fields))
+    new->attributes = old->attributes; 
+  if (IS_FIELD(KADM_MAXLIFE,fields))
+    new->max_life   = old->max_life; 
+  if (IS_FIELD(KADM_DESKEY,fields)) {
+    new->key_low    = old->key_low; 
+    new->key_high   = old->key_high; 
+  }
+}