summaryrefslogtreecommitdiffstats
path: root/src/kadmin/v4server
diff options
context:
space:
mode:
Diffstat (limited to 'src/kadmin/v4server')
-rw-r--r--src/kadmin/v4server/ChangeLog249
-rw-r--r--src/kadmin/v4server/Makefile.in23
-rw-r--r--src/kadmin/v4server/Makefile.ov42
-rw-r--r--src/kadmin/v4server/acl_files.c536
-rw-r--r--src/kadmin/v4server/acl_files.doc107
-rw-r--r--src/kadmin/v4server/admin_server.c684
-rw-r--r--src/kadmin/v4server/attic/ChangeLog25
-rw-r--r--src/kadmin/v4server/attic/Imakefile49
-rw-r--r--src/kadmin/v4server/attic/Makefile39
-rw-r--r--src/kadmin/v4server/attic/Makefile.in53
-rw-r--r--src/kadmin/v4server/attic/acl_files.c541
-rw-r--r--src/kadmin/v4server/attic/acl_files.doc107
-rw-r--r--src/kadmin/v4server/attic/aclocal.m43
-rw-r--r--src/kadmin/v4server/attic/admin_server.c668
-rw-r--r--src/kadmin/v4server/attic/configure.in22
-rw-r--r--src/kadmin/v4server/attic/kadm_err.et57
-rw-r--r--src/kadmin/v4server/attic/kadm_funcs.c876
-rw-r--r--src/kadmin/v4server/attic/kadm_ser_wrap.c288
-rw-r--r--src/kadmin/v4server/attic/kadm_server.c546
-rw-r--r--src/kadmin/v4server/attic/kadm_server.h64
-rw-r--r--src/kadmin/v4server/attic/kadm_stream.c276
-rw-r--r--src/kadmin/v4server/attic/kadm_supp.c114
-rw-r--r--src/kadmin/v4server/configure.in16
-rw-r--r--src/kadmin/v4server/kadm_err.et57
-rw-r--r--src/kadmin/v4server/kadm_funcs.c1066
-rw-r--r--src/kadmin/v4server/kadm_ser_wrap.c310
-rw-r--r--src/kadmin/v4server/kadm_server.c571
-rw-r--r--src/kadmin/v4server/kadm_server.h58
-rw-r--r--src/kadmin/v4server/kadm_stream.c277
-rw-r--r--src/kadmin/v4server/kadm_supp.c113
-rw-r--r--src/kadmin/v4server/unit-test/ChangeLog13
-rw-r--r--src/kadmin/v4server/unit-test/Makefile.ov19
-rw-r--r--src/kadmin/v4server/unit-test/config/ChangeLog7
-rw-r--r--src/kadmin/v4server/unit-test/config/unix.exp42
-rw-r--r--src/kadmin/v4server/unit-test/getpid.sh5
-rw-r--r--src/kadmin/v4server/unit-test/helpers.exp232
-rw-r--r--src/kadmin/v4server/unit-test/remove_changepw_perms.sh9
-rw-r--r--src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp11
-rw-r--r--src/kadmin/v4server/unit-test/v4server.1/access.exp88
-rw-r--r--src/kadmin/v4server/unit-test/v4server.1/change-password.exp59
-rw-r--r--src/kadmin/v4server/unit-test/v4server.1/usage.exp26
41 files changed, 8348 insertions, 0 deletions
diff --git a/src/kadmin/v4server/ChangeLog b/src/kadmin/v4server/ChangeLog
new file mode 100644
index 000000000..604014849
--- /dev/null
+++ b/src/kadmin/v4server/ChangeLog
@@ -0,0 +1,249 @@
+Thu Jul 18 19:46:49 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Tue Jul 9 17:18:56 1996 Marc Horowitz <marc@mit.edu>
+
+ * kadm_stream.c: rename HAS_STDLIB_H to HAVE_STDLIB_H to conform
+ to the autoconf convention
+ * configure.in: the old configure.in seemed to be written for some
+ other directory. Now it's right.
+ * admin_server.c, kadm_ser_wrap.c, kadm_server.c: renamed
+ <ovsec_admin/foo.h> to <kadm5/foo.h>
+ * Makefile.in: complete rewrite.
+
+Thu Mar 21 20:33:43 1996 Richard Basch <basch@lehman.com>
+
+ * kadm_funcs.c: new principals were being created with two keys,
+ one of which the key_data_ver=0 and had no valid data.
+
+Tue Mar 19 19:42:37 1996 Richard Basch <basch@lehman.com>
+
+ * kadm_funcs.c:
+ changed all references of des-cbc-md5 to des-cbc-crc
+ fixed uninitialized variable
+ set kvno modulo 256 in database
+
+Wed Feb 21 23:34:31 1996 Richard Basch <basch@lehman.com>
+
+ * kadm_funcs.c: Initialize the length element of the krb5_db_entry
+ structure in kadm_princ2entry (add_entry was failing).
+
+Wed Dec 13 03:51:53 1995 Chris Provenzano (proven@mit.edu)
+
+ * kadm_funcs.c : Remove mkvno for krb5_db_entry
+
+Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu)
+
+ * admin_server.c, kadm_funcs.c kadm_ser_wrap.c :
+ s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g
+
+Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu)
+
+ * admin_server.c, kadm_funcs.c, kadm_ser_wrap.c : Remove krb5_enctype
+ references, and replace with krb5_keytype where appropriate.
+
+Tue Aug 15 14:31:37 EDT 1995 Paul Park (pjpark@mit.edu)
+ * admin_server,kadm_funcs,kadm_ser_wrap.c - Replace kadm_find_keytype()
+ with krb5_dbe_find_keytype().
+
+
+Thu Aug 10 14:48:26 EDT 1995 Paul Park (pjpark@mit.edu)
+ * kadm_funcs.c - Add kadm_find_keytype() to find a particular key/salt
+ pair. Use this to find keys instead of assuming that the
+ right one's in the first slot.
+ Fix transposed arguments to strncpy().
+ Handle mod_princ_data stuff.
+ Supply saltblock to encrypt_key_data().
+ * admin_server, kadm_ser_wrap.c - Use kadm_find_keytype() to find keys.
+
+
+Mon Aug 7 13:30:46 EDT 1995 Paul Park (pjpark@mit.edu)
+ * admin_server,kadm_funcs,kadm_ser_wrap.c - Brute force substitutions
+ to get this to compile.
+
+
+Mon Jul 17 15:12:30 EDT 1995 Paul Park (pjpark@mit.edu)
+ * kadm_ser_wrap.c - Add NULL stash file argument to krb5_db_fetch_mkey.
+
+
+Fri Jul 7 16:05:11 EDT 1995 Paul Park (pjpark@mit.edu)
+ * Makefile.in - Remove all explicit library handling and LDFLAGS.
+ * configure.in - Add USE_<mumble> and KRB5_LIBRARIES.
+
+
+Tue Jun 27 16:05:27 EDT 1995 Paul Park (pjpark@mit.edu)
+ * acl_files.c - Change check for return value from fputs(3) from NULL
+ to EOF. That's what's returned on error.
+ * admin_server.c - Cast 4th argument of setsockopt(2) to be const char *
+ * kadm_funcs.c - Cast argument to ctime(3)
+ * kadm_server.c - Cast first argument to strcpy(3) and strcat(3).
+
+Tue Jun 20 14:44:54 1995 Tom Yu (tlyu@dragons-lair)
+
+ * configure.in: add tests for TIME_WITH_SYS_TIME and sys/time.h
+
+Thu Jun 15 17:52:29 EDT 1995 Paul Park (pjpark@mit.edu)
+ * Makefile.in - Change explicit library names to -l<lib> form, and
+ change target link line to use $(LD) and associated flags.
+ Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these were
+ split out.
+ * configure.in - Add shared library usage check.
+
+Fri Jun 9 19:07:25 1995 <tytso@rsx-11.mit.edu>
+
+ * configure.in: Remove standardized set of autoconf macros, which
+ are now handled by CONFIG_RULES.
+
+Fri Jun 9 06:49:36 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kadm_stream.c (vts_long, stv_long): Change u_long to krb5_ui_4
+
+ * kadm_server.c (kadm_ser_ckpw): Change u_long to krb5_ui_4
+
+ * kadm_ser_wrap.c (errpkt, kadm_ser_in): Change u_long to krb5_ui_4
+
+ * kadm_funcs.c (kadm_add_entry): Change u_long to krb5_ui_4
+
+ * admin_server.c (process_client): Change u_long to krb5_ui_4
+
+Sat May 20 22:33:58 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kadm_stream.c: Based on presence of stdlib.h, include or declare
+ malloc.
+
+ * configure.in: Check for stdlib.h
+
+Sun May 7 13:49:54 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * admin_server.c: Avoid warning of redeclaring POSIX_SIGNALS if
+ already defined.
+
+Sat Apr 29 00:34:01 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * admin_server.c (kadm_listen): Use Posix sigaction() instead of
+ signal() to set signal handlers. This allows us not to
+ worry about System V signal semantics. Make the code use
+ POSIX_SIGNALS by default.
+
+Fri Apr 28 18:08:05 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (KLIB): put KRB4_LIB inside KLIB.
+
+Thu Apr 27 13:53:41 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (v4kadmind): use KRB4_LIB directly.
+
+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/v4server/Makefile.in b/src/kadmin/v4server/Makefile.in
new file mode 100644
index 000000000..01500167b
--- /dev/null
+++ b/src/kadmin/v4server/Makefile.in
@@ -0,0 +1,23 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) \
+ -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 -DNEED_SOCKETS
+
+LOCALINCLUDE = -I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I.
+
+PROG = kadmind4
+OBJS = kadm_server.o admin_server.o kadm_ser_wrap.o \
+ kadm_stream.o kadm_supp.o acl_files.o kadm_err.o
+
+all:: $(PROG)
+
+kadm_err.c kadm_err.h: $(srcdir)/kadm_err.et
+
+$(OBJS): kadm_err.h
+
+$(PROG): $(OBJS) $(DEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install::
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean::
+ $(RM) $(PROG) $(OBJS)
diff --git a/src/kadmin/v4server/Makefile.ov b/src/kadmin/v4server/Makefile.ov
new file mode 100644
index 000000000..a365e8ea7
--- /dev/null
+++ b/src/kadmin/v4server/Makefile.ov
@@ -0,0 +1,42 @@
+TOP = ..
+include $(TOP)/config.mk/template
+
+ifdef KRB5B4
+CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS)
+endif
+
+ETABLES = kadm_err.et
+expand ErrorTables
+
+depend:: kadm_err.h
+
+PROG := ovsec_v4adm_server
+
+SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \
+ kadm_stream.c kadm_supp.c acl_files.c kadm_err.c
+
+OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \
+ kadm_stream.o kadm_supp.o acl_files.o kadm_err.o
+
+LIBS := $(LIBADMCLNT) $(LIBRPCLIB) \
+ $(LIBKADM) $(LIBKRB) $(LIBDES425) \
+ $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5_ALL) \
+ $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB)
+
+ifdef WAIT_USES_INT
+WAIT_FLAGS = -DWAIT_USES_INT
+endif
+ifdef OPEN_NEEDS_FCNTL
+FCNTL_FLAGS = -DNEED_SYS_FCNTL_H
+endif
+
+CFLAGS := -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 \
+ $(WAIT_FLAGS) $(FCNLT_FLAGS) -I. \
+ -I$(TOP)/../include/kerberosIV -I$(TOP)/../../src/include/kerberosIV \
+ $(CFLAGS)
+
+expand InstallServer
+expand Depend
+
+SUBDIRS = unit-test
+expand SubdirTarget
diff --git a/src/kadmin/v4server/acl_files.c b/src/kadmin/v4server/acl_files.c
new file mode 100644
index 000000000..ae3b0c6bf
--- /dev/null
+++ b/src/kadmin/v4server/acl_files.c
@@ -0,0 +1,536 @@
+/*
+ * 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>
+#include <fcntl.h>
+#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) == EOF
+ || 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/v4server/acl_files.doc b/src/kadmin/v4server/acl_files.doc
new file mode 100644
index 000000000..78c448a6d
--- /dev/null
+++ b/src/kadmin/v4server/acl_files.doc
@@ -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/v4server/admin_server.c b/src/kadmin/v4server/admin_server.c
new file mode 100644
index 000000000..7a207d7c5
--- /dev/null
+++ b/src/kadmin/v4server/admin_server.c
@@ -0,0 +1,684 @@
+/*
+ * 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
+
+/* define it for now */
+#ifndef POSIX_SIGNALS
+#define POSIX_SIGNALS
+#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>
+
+#ifdef OVSEC_KADM
+#include <kadm5/admin.h>
+void *ovsec_handle;
+kadm5_config_params params;
+#endif
+
+#include "k5-int.h"
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.h>
+#include "com_err.h"
+#include "kadm_server.h"
+
+#ifdef POSIX_SIGTYPE
+#define SIGNAL_RETURN return
+#else
+#define SIGNAL_RETURN return(0)
+#endif
+
+/* 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;
+int debug;
+
+/* 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;
+
+#ifdef OVSEC_KADM
+ memset(&params, 0, sizeof(params));
+#endif
+
+ 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, "Df:hnd:a:r:FN")) != EOF)
+ switch(c) {
+ case 'D':
+ debug++;
+ break;
+ 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':
+#ifdef OVSEC_KADM
+ params.dbname = optarg;
+ params.mask |= KADM5_CONFIG_DBNAME;
+#else
+ if (errval = krb5_db_set_name(kadm_context, optarg)) {
+ com_err(argv[0], errval, "while setting dbname");
+ exit(1);
+ }
+#endif
+ 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);
+ }
+
+#ifdef OVSEC_KADM
+ params.realm = krbrlm;
+ params.mask |= KADM5_CONFIG_REALM;
+
+ if (errval = kadm5_get_config_params(kadm_context, NULL, NULL,
+ &params, &params)) {
+ com_err(argv[0], errval, "while retrieving kadm5 params");
+ exit(1);
+ }
+ if (errval = krb5_db_set_name(kadm_context, params.dbname)) {
+ com_err(argv[0], errval, "while setting dbname");
+ exit(1);
+ }
+#endif /* OVSEC_KADM */
+
+ 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");
+
+#ifdef OVSEC_KADM
+ printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n",
+ debug ? "" : "parent ", getpid());
+#else
+ printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST");
+#endif
+
+ 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
+#ifdef OVSEC_KADM
+ , &params
+#endif
+ ))==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;
+ SIGNAL_RETURN;
+}
+
+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();
+#ifdef POSIX_SIGNALS
+ struct sigaction new_act;
+
+ new_act.sa_handler = doexit;
+ sigemptyset(&new_act.sa_mask);
+ sigaction(SIGINT, &new_act, 0);
+ sigaction(SIGTERM, &new_act, 0);
+ sigaction(SIGHUP, &new_act, 0);
+ sigaction(SIGQUIT, &new_act, 0);
+ sigaction(SIGALRM, &new_act, 0);
+ new_act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &new_act, 0);
+ new_act.sa_handler = do_child;
+ sigaction(SIGCHLD, &new_act, 0);
+#else
+ signal(SIGINT, doexit);
+ signal(SIGTERM, doexit);
+ signal(SIGHUP, doexit);
+ signal(SIGQUIT, doexit);
+ signal(SIGPIPE, SIG_IGN); /* get errors on write() */
+ signal(SIGALRM, doexit);
+ signal(SIGCHLD, do_child);
+#endif
+
+ if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return KADM_NO_SOCK;
+ if (debug) {
+ int one = 1;
+ if (setsockopt(admin_fd, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(int)) < 0) {
+ syslog(LOG_ERR, "setsockopt: %m");
+ return KADM_NO_BIND;
+ }
+ }
+ 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;
+ }
+
+ if (debug) {
+ process_client(peer_fd, &peer);
+ } else 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);
+ process_client(peer_fd, &peer);
+ }
+ } else {
+ syslog(LOG_ERR, "something else woke me up!");
+ return(0);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+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;
+ krb5_key_data *kdatap;
+ int status;
+
+#ifdef OVSEC_KADM
+#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab"
+ char *service_name;
+
+ service_name = (char *) malloc(strlen(server_parm.sname) +
+ strlen(server_parm.sinst) +
+ strlen(server_parm.krbrlm) + 3);
+ if (service_name == NULL) {
+ syslog(LOG_ERR, "error: out of memory allocating service name");
+ cleanexit(1);
+ }
+ sprintf(service_name, "%s/%s@%s", server_parm.sname,
+ server_parm.sinst, server_parm.krbrlm);
+
+ retval = ovsec_kadm_init_with_skey(service_name,
+ OVSEC_KADM_SRVTAB,
+ OVSEC_KADM_ADMIN_SERVICE, krbrlm,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &ovsec_handle);
+ if (retval) {
+ syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s",
+ error_message(retval));
+ cleanexit(1);
+ }
+ free(service_name);
+
+ if (retval = krb5_db_set_name(kadm_context, params.dbname)) {
+ syslog(LOG_ERR, "%s while setting dbname", error_message(retval));
+ cleanexit(1);
+ }
+#endif
+
+#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,
+ (const char *) &on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt keepalive: %d", errno);
+
+ server_parm.recv_addr = *who;
+
+ if (retval = krb5_db_init(kadm_context)) { /* Open as client */
+ syslog(LOG_ERR, "can't open krb db: %s", error_message(retval));
+ 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 */
+ krb5_ui_4 retcode = KADM_DB_INUSE;
+ char *pdat;
+
+ dat_len = KADM_VERSIZE + sizeof(krb5_ui_4);
+ dat = (u_char *) malloc((unsigned)dat_len);
+ pdat = (char *) dat;
+ retcode = htonl((krb5_ui_4) KADM_DB_INUSE);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4));
+ 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_dbe_find_enctype(kadm_context,
+ &sprinc_entries,
+ ENCTYPE_DES_CBC_MD5,
+ -1,
+ -1,
+ &kdatap);
+ if (status) {
+ syslog(LOG_ERR, "find enctype failed: %s", error_message(status));
+ cleanexit(1);
+ }
+
+ status = krb5_dbekd_decrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ kdatap,
+ &cpw_skey,
+ (krb5_keysalt *) NULL);
+ 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);
+#ifdef OVSEC_KADM
+ (void) ovsec_kadm_destroy(ovsec_handle);
+#endif
+ 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))
+ if (WTERMSIG(status) || WEXITSTATUS(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
+ SIGNAL_RETURN;
+ }
+ 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
+ SIGNAL_RETURN;
+}
+
+cleanexit(val)
+{
+ krb5_db_fini(kadm_context);
+ clear_secrets();
+ exit(val);
+}
+
+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;
+}
+
+#ifdef OVSEC_KADM
+krb5_ui_4 convert_ovsec_to_kadm(val)
+ krb5_ui_4 val;
+{
+ switch (val) {
+ case KADM5_AUTH_GET:
+ case KADM5_AUTH_ADD:
+ case KADM5_AUTH_MODIFY:
+ case KADM5_AUTH_DELETE:
+ case KADM5_AUTH_INSUFFICIENT:
+ case KADM5_AUTH_LIST:
+ case KADM5_AUTH_CHANGEPW:
+ return KADM_UNAUTH;
+ case KADM5_BAD_DB:
+ return KADM_UK_RERROR;
+ case KADM5_DUP:
+ case KADM5_POLICY_REF:
+ return KADM_INUSE;
+ case KADM5_RPC_ERROR:
+ return KADM_NO_CONN;
+ case KADM5_NO_SRV:
+ return KADM_NO_HOST;
+ case KADM5_UNK_PRINC:
+ case KADM5_UNK_POLICY:
+ return KADM_NOENTRY;
+ case KADM5_PASS_Q_TOOSHORT:
+ case KADM5_PASS_Q_CLASS:
+ case KADM5_PASS_Q_DICT:
+ case KADM5_PASS_REUSE:
+ case KADM5_PASS_TOOSOON:
+ case CHPASS_UTIL_PASSWORD_TOO_SOON:
+ return KADM_INSECURE_PW;
+ case KADM5_BAD_PASSWORD:
+ return KADM_NO_CRED;
+ case KADM5_PROTECT_PRINCIPAL:
+ return KADM_NO_OPCODE;
+ case KADM5_NOT_INIT:
+ case KADM5_BAD_HIST_KEY:
+ case KADM5_BAD_MASK:
+ case KADM5_BAD_CLASS:
+ case KADM5_BAD_LENGTH:
+ case KADM5_BAD_POLICY:
+ case KADM5_BAD_PRINCIPAL:
+ case KADM5_BAD_AUX_ATTR:
+ case KADM5_BAD_HISTORY:
+ case KADM5_BAD_MIN_PASS_LIFE:
+ return -1;
+ }
+ return val;
+}
+#endif
diff --git a/src/kadmin/v4server/attic/ChangeLog b/src/kadmin/v4server/attic/ChangeLog
new file mode 100644
index 000000000..6eefc24c7
--- /dev/null
+++ b/src/kadmin/v4server/attic/ChangeLog
@@ -0,0 +1,25 @@
+Thu Jul 18 19:47:58 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+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/v4server/attic/Imakefile b/src/kadmin/v4server/attic/Imakefile
new file mode 100644
index 000000000..e1449ef32
--- /dev/null
+++ b/src/kadmin/v4server/attic/Imakefile
@@ -0,0 +1,49 @@
+# $Source$
+# $Author$
+# $Header$
+#
+# Copyright 1989 by the Massachusetts Institute of Technology.
+#
+# For copying and distribution information,
+# please see the file <mit-copyright.h>.
+#
+# Imakefile for Kerberos admin server library.
+
+DEFINES = $(KRB4DEF)
+INCLUDES = $(KRB4INCLUDES) -I.
+SRCS = \
+ kadm_server.c \
+ kadm_funcs.c \
+ admin_server.c \
+ kadm_ser_wrap.c \
+ kadm_stream.c \
+ kadm_supp.c \
+ kadm_err.c \
+ 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
+
+ErrorTableObjectRule()
+
+all:: v4kadmind
+
+depend:: kadm_err.c
+
+kadm_err.c: kadm_err.et
+
+NormalProgramTarget(v4kadmind,$(OBJS),$(KDBDEPLIB) $(DEPKLIB), \
+ $(KDBLIB) $(KRB4LIB) $(KLIB) ,)
+
+Krb5InstallServerProgram(v4kadmind)
+
+clean::
+ $(RM) kadm_err.c kadm_err.h
+
+DependTarget()
diff --git a/src/kadmin/v4server/attic/Makefile b/src/kadmin/v4server/attic/Makefile
new file mode 100644
index 000000000..d0acac021
--- /dev/null
+++ b/src/kadmin/v4server/attic/Makefile
@@ -0,0 +1,39 @@
+TOP = ../..
+include $(TOP)/config.mk/template
+
+ifdef KRB5B4
+CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS)
+endif
+
+PROG := ovsec_v4adm_server
+
+SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \
+ kadm_stream.c kadm_supp.c acl_files.c
+
+OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \
+ kadm_stream.o kadm_supp.o acl_files.o
+
+LIBS := $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5) \
+ $(LIBKADM) $(LIBKRB) $(LIBDES425) $(LIBKDB5) \
+ $(LIBCRYPTO) $(LIBISODE) \
+ $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB)
+
+ifdef WAIT_USES_INT
+WAIT_FLAGS = -DWAIT_USES_INT
+endif
+ifdef OPEN_NEEDS_FCNTL
+FCNTL_FLAGS = -DNEED_SYS_FCNTL_H
+endif
+
+# XXX the -D's should probably be moved somewhere; in krb5.4.2 they
+# are in osconf.h
+CFLAGS := -DOVSEC_KADM \
+ -DKADM_SYSLOG="\"/krb5/admin_server.syslog\"" \
+ -DDEFAULT_ACL_DIR="\"/krb5\"" $(WAIT_FLAGS) $(FCNTL_FLAGS) \
+ $(CFLAGS)
+
+expand InstallServer
+expand Depend
+
+SUBDIRS = unit-test
+expand SubdirTarget
diff --git a/src/kadmin/v4server/attic/Makefile.in b/src/kadmin/v4server/attic/Makefile.in
new file mode 100644
index 000000000..f5206aa66
--- /dev/null
+++ b/src/kadmin/v4server/attic/Makefile.in
@@ -0,0 +1,53 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+LDFLAGS = -g
+
+ISODELIB=@ISODELIB@
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+DBMLIB=
+KDBLIB=$(TOPLIBD)/libkdb5.a
+
+KRB4LIB = $(KRB4)/lib/libkrb.a $(TOPLIBD)/libdes425.a
+
+KLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(ISODELIB) $(COMERRLIB) $(DBMLIB)
+
+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) $(KDBDEPLIB) $(DEPKLIB)
+ $(CC) $(CFLAGS) -o v4kadmind $(OBJS) $(KDBLIB) $(KLIB) $(KRB4LIB) $(LIBS) $(KRB4)/lib/libdes.a
+
+install::
+ $(INSTALL_PROGRAM) v4kadmind ${DESTDIR}$(SERVER_BINDIR)/v4kadmind
+
+clean::
+ $(RM) kadm_err.h kadm_err.c
+
+clean::
+ $(RM) v4kadmind
+
diff --git a/src/kadmin/v4server/attic/acl_files.c b/src/kadmin/v4server/attic/acl_files.c
new file mode 100644
index 000000000..81275ae26
--- /dev/null
+++ b/src/kadmin/v4server/attic/acl_files.c
@@ -0,0 +1,541 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1987,1989 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ */
+
+#ifndef lint
+static char rcsid_acl_files_c[] = "$Id$";
+#endif lint
+
+
+/*** Routines for manipulating access control list files ***/
+
+#include <stdio.h>
+#include <string.h>
+#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"
+#include <krb5/krb5.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 PROTOTYPE((char *acl_file, FILE *f));
+
+/* 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 */
+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) == EOF)
+ || (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/v4server/attic/acl_files.doc b/src/kadmin/v4server/attic/acl_files.doc
new file mode 100644
index 000000000..78c448a6d
--- /dev/null
+++ b/src/kadmin/v4server/attic/acl_files.doc
@@ -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/v4server/attic/aclocal.m4 b/src/kadmin/v4server/attic/aclocal.m4
new file mode 100644
index 000000000..70bf66a7d
--- /dev/null
+++ b/src/kadmin/v4server/attic/aclocal.m4
@@ -0,0 +1,3 @@
+sinclude([./../../aclocal.m4])dnl
+undefine([AC_BUILDTOP])dnl
+define(AC_BUILDTOP,[./../..])dnl
diff --git a/src/kadmin/v4server/attic/admin_server.c b/src/kadmin/v4server/attic/admin_server.c
new file mode 100644
index 000000000..04155bca1
--- /dev/null
+++ b/src/kadmin/v4server/attic/admin_server.c
@@ -0,0 +1,668 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * 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 <sys/types.h>
+#include <signal.h>
+#include <string.h>
+
+#ifndef POSIX_SIGNALS
+#ifndef sigmask
+#define sigmask(m) (1 <<((m)-1))
+#endif
+#endif /* POSIX_SIGNALS */
+#ifdef _AIX
+#include <sys/resource.h>
+#endif /* _AIX */
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include <krb5/krb5.h>
+#include <krb5/kdb.h>
+#include <krb5/kdb_dbm.h>
+#include <krb5/los-proto.h>
+#include <krb5/config.h>
+
+#ifdef OVSEC_KADM
+#include <ovsec_admin/admin.h>
+void *ovsec_handle;
+#endif
+
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.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;
+int des_debug; /* used by the des425 libraries */
+int debug = 0;
+
+/*
+** 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 *db_name, *lrealm;
+ extern char *optarg;
+ extern int fascist_cpw;
+
+ krb5_init_ets();
+ 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:Da:r:FN")) != EOF)
+ switch(c) {
+ case 'd':
+ if (errval = krb5_db_set_name(optarg)) {
+ com_err(argv[0], errval, "while setting dbname");
+ exit(1);
+ }
+ break;
+ case 'D':
+ debug++;
+ break;
+#ifndef OVSEC_KADM
+ case 'f': /* Syslog file name change */
+ prm.sysfile = optarg;
+ break;
+ case 'F':
+ fascist_cpw++;
+ break;
+ case 'N':
+ fascist_cpw = 0;
+ break;
+#endif
+ case 'n':
+ prm.inter = 0;
+ break;
+ case 'a': /* new acl directory */
+ acldir = optarg;
+ break;
+ case 'r':
+ (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1);
+ break;
+ case 'h': /* get help on using admin_server */
+ default:
+#ifdef OVSEC_KADM
+ fprintf(stderr, "Usage: ovsec_v4adm_server [-D] [-h] [-n] [-r realm] [-d dbname] [-a acldir]\n");
+
+#else
+ printf("Usage: admin_server [-D] [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n");
+#endif
+ exit(-1); /* failure */
+ }
+
+ if (krbrlm[0] == 0) {
+ if (errval = krb5_get_default_realm(&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");
+
+#ifdef OVSEC_KADM
+ printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n",
+ debug ? "" : "parent ", getpid());
+#else
+ printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST");
+#endif
+
+ open_syslog(argv[0], "V4 admin server (parent) starting");
+
+ errval = krb5_db_init(); /* Open the Kerberos database */
+ if (errval) {
+ fprintf(stderr, "error: krb5_db_init() failed");
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+ if (errval = krb5_db_set_lockmode(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) {
+ fprintf(stderr, "error initializing: %s\n", error_message(errval));
+ krb5_db_fini();
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+
+ /* detach from the terminal */
+ if (!debug) {
+ if (
+#ifdef KRB5B4
+ daemon(0, 0)
+#else
+ errval = krb5_detach_process()
+#endif
+ ) {
+#ifdef KRB5B4
+ errval = errno;
+#endif
+ fprintf(stderr, "error detaching from terminal: %s\n",
+ error_message(errval));
+ syslog(LOG_ERR, "error detaching from terminal: %s",
+ error_message(errval));
+ krb5_db_fini();
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+ open_syslog(argv[0], "V4 admin server (child) starting");
+ }
+
+ krb5_db_fini();
+
+ if (errval = kadm_listen()) {
+ fprintf(stderr, "error while listening for requests: %s\n",
+ error_message(errval));
+ syslog(LOG_ERR, "error while listening for requests: %s",
+ error_message(errval));
+ krb5_db_fini();
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+
+ close_syslog();
+ byebye();
+ exit(0);
+} /* procedure main */
+
+
+/* open the system log file */
+open_syslog(whoami, message)
+ char *whoami, *message;
+{
+ static int opened = 0;
+
+ if (opened) {
+ closelog();
+ }
+ openlog(whoami, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */
+ syslog(LOG_INFO, message);
+ opened++;
+}
+
+/* close the system log file */
+close_syslog()
+{
+ syslog(LOG_INFO, "Shutting down V4 admin server");
+}
+
+byebye() /* say goodnight gracie */
+{
+ printf("Admin Server (kadm server) has completed operation.\n");
+}
+
+static clear_secrets()
+{
+ krb5_finish_key(&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()
+{
+ exit_now = 1;
+}
+
+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;
+ }
+
+ if (debug) {
+ process_client(peer_fd, &peer);
+ } else 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);
+ process_client(peer_fd, &peer);
+ }
+ } else {
+ syslog(LOG_ERR, "something else woke me up!");
+ return(0);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+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;
+ Principal service;
+ des_cblock skey;
+ int nentries = 1;
+ krb5_db_entry sprinc_entries;
+ krb5_boolean more;
+ krb5_keyblock cpw_skey;
+ int status;
+
+#ifdef OVSEC_KADM
+#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab"
+ char *service_name;
+
+ service_name = (char *) malloc(strlen(server_parm.sname) +
+ strlen(server_parm.sinst) +
+ strlen(server_parm.krbrlm) + 3);
+ if (service_name == NULL) {
+ syslog(LOG_ERR, "error: out of memory allocating service name");
+ }
+ sprintf(service_name, "%s/%s@%s", server_parm.sname,
+ server_parm.sinst, server_parm.krbrlm);
+
+ retval = ovsec_kadm_init_with_skey(service_name,
+ OVSEC_KADM_SRVTAB,
+ OVSEC_KADM_ADMIN_SERVICE, krbrlm,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &ovsec_handle);
+ if (retval) {
+ syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s",
+ error_message(retval));
+ cleanexit(1);
+ }
+ free(service_name);
+
+#endif
+
+#if !defined(NOENCRYPTION)
+ /* Must do it here, since this is after the fork() call. */
+ des_init_random_number_generator(server_parm.master_keyblock.contents);
+#endif
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt keepalive: %d", errno);
+
+ server_parm.recv_addr = *who;
+
+ if (krb5_db_init()) { /* 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(server_parm.sprinc,
+ &sprinc_entries,
+ &nentries, &more);
+ /* ugh... clean this up later */
+ if (status == KRB5_KDB_DB_INUSE) {
+ /* db locked */
+ krb5_ui_4 retcode = KADM_DB_INUSE;
+ char *pdat;
+
+ dat_len = KADM_VERSIZE + sizeof(u_int);
+ dat = (u_char *) malloc((unsigned)dat_len);
+ pdat = (char *) dat;
+ /* This must be 32 bit integer due to the htonl */
+ retcode = htonl((krb5_ui_4) KADM_DB_INUSE);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4));
+ 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(&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);
+#ifdef KRB5_FREE_KEYBLOCK_CONTENTS_EXISTS
+ krb5_free_keyblock_contents(&cpw_skey);
+#else
+ memset((char*)cpw_skey.contents, 0, cpw_skey.length);
+ free(cpw_skey.contents);
+#endif
+
+ krb5_dbm_db_free_principal(&sprinc_entries, nentries);
+
+ 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);
+#ifdef OVSEC_KADM
+ (void) ovsec_kadm_destroy(ovsec_handle);
+#endif
+ if (debug)
+ return;
+ else
+ 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()
+{
+ /* 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
+ goto done; /* use goto to avoid figuring out whether to
+ return a value */
+ }
+ 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
+
+ done:
+}
+
+cleanexit(val)
+{
+ krb5_db_fini();
+ clear_secrets();
+ exit(val);
+}
+
+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;
+}
+
+#ifdef OVSEC_KADM
+krb5_ui_4 convert_ovsec_to_kadm(val)
+ krb5_ui_4 val;
+{
+ switch (val) {
+ case OVSEC_KADM_AUTH_GET:
+ case OVSEC_KADM_AUTH_ADD:
+ case OVSEC_KADM_AUTH_MODIFY:
+ case OVSEC_KADM_AUTH_DELETE:
+ case OVSEC_KADM_AUTH_INSUFFICIENT:
+ return KADM_UNAUTH;
+ case OVSEC_KADM_BAD_DB:
+ return KADM_UK_RERROR;
+ case OVSEC_KADM_DUP:
+ case OVSEC_KADM_POLICY_REF:
+ return KADM_INUSE;
+ case OVSEC_KADM_RPC_ERROR:
+ return KADM_NO_CONN;
+ case OVSEC_KADM_NO_SRV:
+ return KADM_NO_HOST;
+ case OVSEC_KADM_UNK_PRINC:
+ case OVSEC_KADM_UNK_POLICY:
+ return KADM_NOENTRY;
+ case OVSEC_KADM_PASS_Q_TOOSHORT:
+ case OVSEC_KADM_PASS_Q_CLASS:
+ case OVSEC_KADM_PASS_Q_DICT:
+ case OVSEC_KADM_PASS_REUSE:
+ case OVSEC_KADM_PASS_TOOSOON:
+ case CHPASS_UTIL_PASSWORD_TOO_SOON:
+ return KADM_INSECURE_PW;
+ case OVSEC_KADM_BAD_PASSWORD:
+ return KADM_NO_CRED;
+ case OVSEC_KADM_PROTECT_PRINCIPAL:
+ return KADM_NO_OPCODE;
+ case OVSEC_KADM_NOT_INIT:
+ case OVSEC_KADM_BAD_HIST_KEY:
+ case OVSEC_KADM_BAD_MASK:
+ case OVSEC_KADM_BAD_CLASS:
+ case OVSEC_KADM_BAD_LENGTH:
+ case OVSEC_KADM_BAD_POLICY:
+ case OVSEC_KADM_BAD_PRINCIPAL:
+ case OVSEC_KADM_BAD_AUX_ATTR:
+ case OVSEC_KADM_BAD_HISTORY:
+ case OVSEC_KADM_BAD_MIN_PASS_LIFE:
+ return -1;
+ }
+ return val;
+}
+#endif
diff --git a/src/kadmin/v4server/attic/configure.in b/src/kadmin/v4server/attic/configure.in
new file mode 100644
index 000000000..f09ba3a28
--- /dev/null
+++ b/src/kadmin/v4server/attic/configure.in
@@ -0,0 +1,22 @@
+AC_INIT(admin_server.c)
+WITH_CCOPTS
+CONFIG_RULES
+AC_SET_BUILDTOP
+AC_PROG_INSTALL
+AC_HAVE_LIBRARY(socket)
+AC_HAVE_LIBRARY(nsl)
+AC_HAVE_LIBRARY(-lndbm)
+AC_HAVE_LIBRARY(-ldbm)
+CHECK_WAIT_TYPE
+CHECK_FCNTL
+AC_FUNC_CHECK(sigprocmask,
+AC_COMPILE_CHECK([sigset_t],
+[#include <signal.h>],
+[sigset_t x],
+AC_DEFINE(POSIX_SIGNALS)))
+AC_PROG_AWK
+KRB_INCLUDE
+ISODE_INCLUDE
+WITH_KRB4
+WITH_KRB5ROOT
+AC_OUTPUT(Makefile,[EXTRA_RULES])
diff --git a/src/kadmin/v4server/attic/kadm_err.et b/src/kadmin/v4server/attic/kadm_err.et
new file mode 100644
index 000000000..a19273083
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_err.et
@@ -0,0 +1,57 @@
+# kadmin.v4/server/kadm_err.et
+#
+# 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/v4server/attic/kadm_funcs.c b/src/kadmin/v4server/attic/kadm_funcs.c
new file mode 100644
index 000000000..7ce9c7b4f
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_funcs.c
@@ -0,0 +1,876 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * 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
+ */
+
+#ifndef lint
+static char rcsid_kadm_funcs_c[] =
+"$Id$";
+#endif lint
+
+#include <mit-copyright.h>
+/*
+kadm_funcs.c
+the actual database manipulation code
+*/
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <ndbm.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
+
+#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;
+ time_t lcltim;
+
+ /* NOTE: does not convert the key */
+ memset(princ, 0, sizeof (*princ));
+ retval = krb5_524_conv_principal(entry.principal,
+ princ->name, princ->instance, realm);
+ if (retval)
+ return retval;
+ princ->exp_date = entry.expiration;
+ lcltim = entry.expiration;
+ strncpy(princ->exp_date_txt, ctime(&lcltim),
+ DATE_SZ);
+ lcltim = princ->mod_date = entry.mod_date;
+ strncpy(princ->mod_date_txt, ctime(&lcltim),
+ DATE_SZ);
+ princ->attributes = entry.attributes;
+ princ->max_life = entry.max_life;
+ princ->kdc_key_ver = entry.mkvno;
+ princ->key_version = entry.kvno;
+ retval = krb5_524_conv_principal(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(princ.name, princ.instance,
+ server_parm.krbrlm, &entry->principal);
+ if (retval)
+ return retval;
+ entry->kvno = princ.key_version;
+ entry->max_life = princ.max_life;
+ 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(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;
+}
+
+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 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 addding '%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;
+
+ 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 *)(((krb4_int32 *) 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(&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(&newentry, 1);
+ failadd(retval);
+ }
+
+ newentry.key = encpw;
+ numfound = 1;
+ retval = krb5_db_get_principal(newentry.principal,
+ &tmpentry, &numfound, &more);
+
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+ krb5_db_free_principal(&tmpentry, numfound);
+ if (numfound) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(KADM_INUSE);
+ } else {
+ newentry.kvno = ++data_i.key_version;
+ if (retval = krb5_timeofday(&newentry.mod_date)) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+ if (newentry.mod_name)
+ krb5_free_principal(newentry.mod_name);
+ newentry.mod_name = NULL; /* in case the following breaks */
+ retval = krb5_425_conv_principal(rname, rinstance, rrealm,
+ &newentry.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+
+ numfound = 1;
+ retval = krb5_db_put_principal(&newentry, &numfound);
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+ if (!numfound) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(KADM_UK_SERROR);
+ } else {
+ numfound = 1;
+ retval = krb5_db_get_principal(newentry.principal,
+ &tmpentry,
+ &numfound, &more);
+ krb5_db_free_principal(&newentry, 1);
+ if (retval) {
+ failadd(retval);
+ } else if (numfound != 1 || more) {
+ krb5_db_free_principal(&tmpentry, numfound);
+ failadd(KADM_UK_RERROR);
+ }
+ kadm_entry2princ(tmpentry, &data_o);
+ krb5_db_free_principal(&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 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(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(inprinc, &entry, &numfound, &more);
+ krb5_free_principal(inprinc);
+ if (retval) {
+ failget(retval);
+ } else if (!numfound || more) {
+ failget(KADM_NOENTRY);
+ }
+ retval = kadm_entry2princ(entry, &data_o);
+ krb5_db_free_principal(&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, rprinc;
+ 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(valsin1->name, valsin1->instance,
+ server_parm.krbrlm, &theprinc);
+ if (retval)
+ failmod(retval);
+ numfound = 1;
+ retval = krb5_db_get_principal(theprinc, &newentry, &numfound, &more);
+ if (retval) {
+ krb5_free_principal(theprinc);
+ failmod(retval);
+ } else if (numfound == 1) {
+ kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2);
+ krb5_free_principal(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(&newentry, 1);
+ memset((char *)&temp_key, 0, sizeof (temp_key));
+ failmod(KADM_NOMEM);
+ }
+ 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(&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(&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(&newentry.mod_date)) {
+ krb5_db_free_principal(&newentry, 1);
+ failmod(retval);
+ }
+ retval = krb5_425_conv_principal(rname, rinstance, rrealm,
+ &newentry.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failmod(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_put_principal(&newentry, &numfound);
+ memset((char *)&data_o, 0, sizeof(data_o));
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failmod(retval);
+ } else {
+ numfound = 1;
+ retval = krb5_db_get_principal(newentry.principal, &odata,
+ &numfound, &more);
+ krb5_db_free_principal(&newentry, 1);
+ if (retval) {
+ failmod(retval);
+ } else if (numfound != 1 || more) {
+ krb5_db_free_principal(&odata, numfound);
+ failmod(KADM_UK_RERROR);
+ }
+ retval = kadm_entry2princ(odata, &data_o);
+ krb5_db_free_principal(&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(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.keytype = KEYTYPE_DES;
+ localpw.length = 8;
+ /* encrypt new key in master key */
+ retval = krb5_kdb_encrypt_key(&server_parm.master_encblock,
+ &localpw, &encpw);
+ memset((char *)localpw.contents, 0, 8);
+ free(localpw.contents);
+ if (retval) {
+ krb5_free_principal(rprinc);
+ failchange(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_get_principal(rprinc, &odata, &numfound, &more);
+ krb5_free_principal(rprinc);
+ if (retval) {
+ failchange(retval);
+ } else if (numfound == 1) {
+ odata.key = encpw;
+ odata.kvno++;
+ odata.mkvno = server_parm.mkvno;
+ if (retval = krb5_timeofday(&odata.mod_date)) {
+ krb5_db_free_principal(&odata, 1);
+ failchange(retval);
+ }
+ krb5_425_conv_principal(rname, rinstance,
+ server_parm.krbrlm, &odata.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&odata, 1);
+ failchange(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_put_principal(&odata, &numfound);
+ krb5_db_free_principal(&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;
+{
+ FILE *f;
+ char filename[MAXPATHLEN];
+ char buf[ANAME_SZ], *cp;
+ 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;
+ Principal principal;
+ 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(values->name, values->instance,
+ server_parm.krbrlm, &inprinc);
+ if (retval)
+ failsrvtab(retval);
+ /*
+ * OK, get the entry
+ */
+ numfound = 1;
+ retval = krb5_db_get_principal(inprinc, &odata, &numfound, &more);
+ if (retval) {
+ krb5_free_principal(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(&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.kvno;
+ 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(&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(&odata, 1);
+ failsrvtab(retval);
+ }
+ if (retval = krb5_timeofday(&odata.mod_date)) {
+ krb5_db_free_principal(&odata, 1);
+ failsrvtab(retval);
+ }
+ retval = krb5_425_conv_principal(rname, rinstance,
+ server_parm.krbrlm, &odata.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&odata, 1);
+ failsrvtab(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_put_principal(&odata, &numfound);
+ krb5_db_free_principal(&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/v4server/attic/kadm_ser_wrap.c b/src/kadmin/v4server/attic/kadm_ser_wrap.c
new file mode 100644
index 000000000..bca8b8e05
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_ser_wrap.c
@@ -0,0 +1,288 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * 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
+ */
+
+#ifndef lint
+static char rcsid_module_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+/*
+kadm_ser_wrap.c
+unwraps wrapped packets and calls the appropriate server subroutine
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <krb.h>
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_err.h>
+#include <syslog.h>
+#include "kadm_server.h"
+
+#ifdef OVSEC_KADM
+#include <ovsec_admin/admin.h>
+extern void *ovsec_handle;
+#endif
+
+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;
+
+ 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(&server_parm.sprinc,
+ strlen(realm),
+ realm,
+ PWSERV_NAME,
+ KRB_MASTER, 0))
+ return KADM_NO_MAST;
+
+ /* setting up the addrs */
+ server_parm.admin_fd = -1;
+ 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;
+#ifdef PROVIDE_DES_CBC_CRC
+#ifdef KRB5B4
+ server_parm.master_encblock.crypto_entry = krb5_des_cst_entry.system;
+#else
+ server_parm.master_encblock.crypto_entry = &mit_des_cryptosystem_entry;
+#endif /* KRB5B4 */
+#else
+ error(You gotta figure out what cryptosystem to use in the KDC);
+#endif
+ retval = krb5_db_setup_mkey_name(mkey_name, realm, (char **) 0,
+ &server_parm.master_princ);
+ if (retval)
+ return KADM_NO_MAST;
+ krb5_db_fetch_mkey(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(server_parm.master_princ,
+ &server_parm.master_keyblock,
+ &server_parm.master_encblock);
+ if (retval)
+ return KADM_NO_VERI;
+ retval = krb5_process_key(&server_parm.master_encblock,
+ &server_parm.master_keyblock);
+ if (retval)
+ return KADM_NO_VERI;
+
+ retval = krb5_db_get_principal(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(&master_entry, numfound);
+ return KADM_SUCCESS;
+}
+
+
+static void errpkt(dat, dat_len, code)
+u_char **dat;
+int *dat_len;
+int code;
+{
+ krb4_uint32 retcode;
+ char *pdat;
+
+ free((char *)*dat); /* free up req */
+ *dat_len = KADM_VERSIZE + sizeof(krb4_uint32);
+ *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((krb4_uint32) code);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb4_uint32));
+ 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 */
+ krb4_uint32 r_len; /* length of the actual packet */
+ KTEXT_ST authent; /* the authenticator */
+ AUTH_DAT ad; /* who is this, klink */
+ krb4_uint32 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(krb4_uint32);
+ 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((des_cblock *)in_st, (des_cblock *)0, (krb4_int32) r_len, 0,
+ (des_cblock *)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;
+#ifndef OVSEC_KADM
+ case ADD_ENT:
+ retval = kadm_ser_add(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;
+#endif /* OVSEC_KADM */
+ 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(krb4_uint32)));
+ if (!tmpdat) {
+ clr_cli_secrets();
+ syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!",
+ retlen + KADM_VERSIZE + sizeof(krb4_uint32));
+ errpkt(dat, dat_len, KADM_NOMEM);
+ return KADM_NOMEM;
+ }
+ (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE);
+ retval = htonl((krb4_uint32)retval);
+ memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb4_uint32));
+ if (retlen) {
+ memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb4_uint32), (char *)retdat,
+ retlen);
+ free((char *)retdat);
+ }
+ /* slop for mk_priv stuff */
+ *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE +
+ sizeof(krb4_uint32) + 200));
+ if ((*dat_len = krb_mk_priv(tmpdat, *dat,
+ (krb4_uint32) (retlen + KADM_VERSIZE +
+ sizeof(krb4_uint32)),
+ sess_sched,
+ ad.session, &server_parm.admin_addr,
+ &server_parm.recv_addr)) < 0) {
+ clr_cli_secrets();
+ errpkt(dat, dat_len, KADM_NO_ENCRYPT);
+ free(tmpdat);
+ return KADM_NO_ENCRYPT;
+ }
+ clr_cli_secrets();
+ free(tmpdat);
+ return KADM_SUCCESS;
+}
diff --git a/src/kadmin/v4server/attic/kadm_server.c b/src/kadmin/v4server/attic/kadm_server.c
new file mode 100644
index 000000000..143af5d4e
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_server.c
@@ -0,0 +1,546 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * 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
+ */
+
+#ifndef lint
+static char rcsid_kadm_server_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+
+#include <krb5/osconf.h>
+#include <krb5/wordsize.h>
+
+#include <stdio.h>
+#ifdef USE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#include <string.h>
+
+#ifdef OVSEC_KADM
+#include <com_err.h>
+#include <ovsec_admin/admin.h>
+#include <ovsec_admin/chpass_util_strings.h>
+#include <krb5/kdb.h>
+extern void *ovsec_handle;
+#endif
+
+#include <kadm.h>
+#include <kadm_err.h>
+
+int fascist_cpw = 0; /* Be fascist about insecure passwords? */
+
+#ifdef OVSEC_KADM
+char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n";
+
+#else /* !OVSEC_KADM */
+
+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";
+
+#endif /* OVSEC_KADM */
+
+/* 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;
+ int a1,a2,a3,a4,a5,a6,a7,a8,a9,a0;
+{
+ FILE *logfile, *fopen();
+ 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_ui_4 keylow, keyhigh;
+ char pword[MAX_KPW_LEN];
+ int no_pword = 0;
+ des_cblock newkey;
+ int status, stvlen = 0;
+ int retval;
+ extern char *malloc();
+ extern int kadm_approve_pw();
+#ifdef OVSEC_KADM
+ ovsec_kadm_principal_ent_t princ_ent;
+ ovsec_kadm_policy_ent_t pol_ent;
+ krb5_principal user_princ;
+ char msg_ret[1024], *time_string, *ptr;
+ const char *msg_ptr;
+ krb5_int32 now;
+ time_t until;
+#endif
+
+ /* 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((((krb5_int32 *)newkey) + 1), &keyhigh, 4);
+ memcpy(newkey, &keylow, 4);
+
+#ifdef OVSEC_KADM
+ /* we don't use the client-provided key itself */
+ keylow = keyhigh = 0;
+ memset(newkey, 0, sizeof(newkey));
+
+ if (no_pword) {
+ krb_log("Old-style change password request from '%s.%s@%s'!",
+ ad->pname, ad->pinst, ad->prealm);
+ *outlen = strlen(pw_required)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, pw_required);
+ } else {
+ *outlen = 0;
+ }
+ return KADM_INSECURE_PW;
+ }
+
+ if (krb5_build_principal(&user_princ,
+ strlen(ad->prealm),
+ ad->prealm,
+ ad->pname,
+ *ad->pinst ? ad->pinst : 0, 0))
+ /* this should never happen */
+ return KADM_NOENTRY;
+
+ *outlen = 0;
+
+ if (retval = krb5_timeofday(&now)) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ retval = ovsec_kadm_get_principal(ovsec_handle, user_princ,
+ &princ_ent);
+ if (retval != 0) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /*
+ * This daemon necessarily has the modify privilege, so
+ * ovsec_kadm_chpass_principal will allow it to violate the
+ * policy's minimum lifetime. Since that's A Bad Thing, we need
+ * to enforce it ourselves. Unfortunately, this means we are
+ * duplicating code from both ovsec_adm_server and
+ * ovsec_kadm_chpass_util().
+ */
+ if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) {
+ retval = ovsec_kadm_get_policy(ovsec_handle,
+ princ_ent->policy,
+ &pol_ent);
+ if (retval != 0) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /* make "now" a boolean, true == too soon */
+ now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life);
+
+ (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent);
+
+ if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ retval = CHPASS_UTIL_PASSWORD_TOO_SOON;
+
+ until = princ_ent->last_pwd_change + pol_ent->pw_min_life;
+ time_string = ctime(&until);
+
+ if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
+ *ptr = '\0';
+
+ sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON),
+ time_string);
+ msg_ptr = msg_ret;
+
+ goto send_response;
+ }
+ }
+
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+
+ retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ,
+ pword, NULL, msg_ret);
+ msg_ptr = msg_ret;
+ (void) krb5_free_principal(user_princ);
+
+send_response:
+
+ retval = convert_ovsec_to_kadm(retval);
+
+ if (retval) {
+ /* don't send message on success because kpasswd.v4 will */
+ /* print "password changed" too */
+ *outlen = strlen(msg_ptr)+2;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, msg_ptr);
+ strcat(*datout, "\n");
+ } else
+ *outlen = 0;
+ }
+ if (retval == KADM_INSECURE_PW) {
+ krb_log("'%s.%s@%s' tried to use an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+
+#else /* !OVSEC_KADM */
+
+ 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));
+#endif /* OVSEC_KADM */
+
+ return retval;
+}
+
+/**********************************************************************/
+#ifndef OVSEC_KADM
+
+/*
+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
+*/
+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_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;
+{
+ krb5_ui_4 keylow, keyhigh;
+ char pword[MAX_KPW_LEN];
+ int no_pword = 0;
+ des_cblock newkey;
+ int stvlen = 0,status;
+ int retval;
+ extern char *malloc();
+ 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;
+ }
+}
+
+#endif /* !OVSEC_KADM */
diff --git a/src/kadmin/v4server/attic/kadm_server.h b/src/kadmin/v4server/attic/kadm_server.h
new file mode 100644
index 000000000..4e6fd8c46
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_server.h
@@ -0,0 +1,64 @@
+/*
+ * $Source$
+ * $Author$
+ * $Header$
+ *
+ * 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 <krb5/krb5.h>
+#include <krb5/kdb.h>
+#include <krb5/osconf.h>
+#include <krb5/config.h>
+#ifdef PROVIDE_DES_CBC_CRC
+#include <krb5/mit-des.h>
+#endif
+
+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 STAB_ACL_FILE "/v4acl.srvtab"
+#define STAB_SERVICES_FILE "/v4stab_services"
+#define STAB_HOSTS_FILE "/v4stab_bad_hosts"
+
+#endif /* KADM_SERVER_DEFS */
diff --git a/src/kadmin/v4server/attic/kadm_stream.c b/src/kadmin/v4server/attic/kadm_stream.c
new file mode 100644
index 000000000..83aaa295c
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_stream.c
@@ -0,0 +1,276 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * 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
+ */
+
+#ifndef lint
+static char rcsid_kadm_stream_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.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, krb4_int32, short]
+ stream_to_vals: converts a stream to a vals struct
+ internals check_field_header, stv_[string, char, krb4_int32, short]
+ error: prints out a kadm error message, returns
+ fatal: prints out a kadm fatal error message, exits
+*/
+
+#include "kadm.h"
+#include <string.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((void *) *st, (void *) 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)
+krb4_uint32 dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ krb4_uint32 temp; /* to hold the net order short */
+
+ temp = htonl(dat); /* convert to network order */
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb4_uint32)));
+ memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb4_uint32));
+ return sizeof(krb4_uint32);
+}
+
+
+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;
+ krb4_int32 lcllong;
+
+ 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, &lcllong, stsize,
+ maxlen)) < 0)
+ return(-1);
+ dt_out->exp_date = lcllong;
+ 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 *)((long)st+(krb4_uint32)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 */
+krb4_uint32 *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen; /* maximum length of st */
+{
+ krb4_uint32 temp; /* to hold the net order short */
+
+ if (loc + sizeof(krb4_uint32) > maxlen)
+ return(-1);
+ memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(krb4_uint32));
+ *dat = ntohl(temp); /* convert to network order */
+ return sizeof(krb4_uint32);
+}
+
+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/v4server/attic/kadm_supp.c b/src/kadmin/v4server/attic/kadm_supp.c
new file mode 100644
index 000000000..cf4ba40f4
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_supp.c
@@ -0,0 +1,114 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * 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
+ */
+
+#ifndef lint
+static char rcsid_kadm_supp_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+/*
+ 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
+*/
+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(&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;
+ }
+}
diff --git a/src/kadmin/v4server/configure.in b/src/kadmin/v4server/configure.in
new file mode 100644
index 000000000..9d45c1e16
--- /dev/null
+++ b/src/kadmin/v4server/configure.in
@@ -0,0 +1,16 @@
+AC_INIT(admin_server.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_CHECK_HEADERS(sys/time.h unistd.h stdlib.h)
+CHECK_SIGNALS
+CHECK_WAIT_TYPE
+AC_PROG_AWK
+USE_KADMCLNT_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+USE_KRB4_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/v4server/kadm_err.et b/src/kadmin/v4server/kadm_err.et
new file mode 100644
index 000000000..a19273083
--- /dev/null
+++ b/src/kadmin/v4server/kadm_err.et
@@ -0,0 +1,57 @@
+# kadmin.v4/server/kadm_err.et
+#
+# 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/v4server/kadm_funcs.c b/src/kadmin/v4server/kadm_funcs.c
new file mode 100644
index 000000000..5025e3acb
--- /dev/null
+++ b/src/kadmin/v4server/kadm_funcs.c
@@ -0,0 +1,1066 @@
+/*
+ * 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>
+#include <fcntl.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_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_tl_mod_princ *mprinc;
+ krb5_key_data *pkey;
+ krb5_error_code retval;
+
+ /* NOTE: does not convert the key */
+ memset(princ, 0, sizeof (*princ));
+ retval = krb5_524_conv_principal(kadm_context, entry.princ,
+ princ->name, princ->instance, realm);
+ if (retval)
+ return retval;
+ princ->exp_date = entry.expiration;
+ strncpy(princ->exp_date_txt, ctime((const time_t *) &entry.expiration),
+ DATE_SZ);
+ princ->attributes = entry.attributes;
+ princ->max_life = entry.max_life / (60 * 5);
+ princ->kdc_key_ver = 1; /* entry.mkvno; */
+ princ->key_version = entry.key_data[0].key_data_kvno;
+
+ retval = krb5_dbe_decode_mod_princ_data(kadm_context, &entry, &mprinc);
+ if (retval)
+ return retval;
+ princ->mod_date = mprinc->mod_date;
+ strncpy(princ->mod_date_txt,
+ ctime((const time_t *) &mprinc->mod_date),
+ DATE_SZ);
+ krb5_free_principal(kadm_context, mprinc->mod_princ);
+ krb5_xfree(mprinc);
+
+ /* Find the V4 key */
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey);
+ if (retval)
+ return retval;
+ princ->key_version = pkey->key_data_kvno;
+
+ return 0;
+}
+
+krb5_error_code
+kadm_princ2entry(princ, entry)
+ Principal princ;
+ krb5_db_entry *entry;
+{
+ krb5_error_code retval;
+ krb5_tl_mod_princ mprinc;
+ krb5_key_data *kdatap;
+
+ /* 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->princ);
+ if (retval)
+ return retval;
+
+ entry->len = KRB5_KDB_V1_BASE_LENGTH;
+ entry->max_life = princ.max_life * (60 * 5);
+ entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */
+ entry->expiration = princ.exp_date;
+ entry->attributes = princ.attributes;
+
+ retval = krb5_425_conv_principal(kadm_context, princ.mod_name,
+ princ.mod_instance,
+ server_parm.krbrlm, &mprinc.mod_princ);
+ if (retval)
+ return(retval);
+ mprinc.mod_date = princ.mod_date;
+
+ retval = krb5_dbe_encode_mod_princ_data(kadm_context, &mprinc, entry);
+ if (retval)
+ return(retval);
+
+ if (mprinc.mod_princ)
+ krb5_free_principal(kadm_context, mprinc.mod_princ);
+
+ if (retval = krb5_dbe_find_enctype(kadm_context,
+ entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &kdatap)) {
+ if (!(retval = krb5_dbe_create_key_data(kadm_context, entry)))
+ kdatap = &entry->key_data[entry->n_key_data-1];
+ }
+ if (kdatap) {
+ kdatap->key_data_ver = 2;
+ kdatap->key_data_type[0] = (krb5_int16) ENCTYPE_DES_CBC_CRC;
+ kdatap->key_data_type[1] = (krb5_int16) KRB5_KDB_SALTTYPE_V4;
+ kdatap->key_data_kvno = (krb5_int16) princ.key_version;
+ }
+ return(retval);
+}
+
+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_tl_mod_princ mprinc;
+ krb5_key_data *pkey;
+ krb5_keysalt sblock;
+ 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;
+
+ retval = kadm_princ2entry(data_i, &newentry);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+
+ newpw.magic = KV5M_KEYBLOCK;
+ if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL)
+ failadd(KADM_NOMEM);
+
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &newentry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey);
+ if (retval)
+ failadd(retval);
+
+ 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 *)(((krb5_int32 *) newpw.contents) + 1), &data_i.key_high, 4);
+ newpw.length = 8;
+ newpw.enctype = ENCTYPE_DES_CBC_CRC;
+ sblock.type = KRB5_KDB_SALTTYPE_V4;
+ sblock.data.length = 0;
+ sblock.data.data = (char *) NULL;
+ /* encrypt new key in master key */
+ retval = krb5_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &newpw,
+ &sblock,
+ (int) ++data_i.key_version,
+ pkey);
+ memset((char *)newpw.contents, 0, newpw.length);
+ free(newpw.contents);
+ if (retval) {
+ failadd(retval);
+ }
+ data_o = data_i;
+
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, newentry.princ,
+ &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 {
+ if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+ mprinc.mod_princ = NULL; /* in case the following breaks */
+ retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm,
+ &mprinc.mod_princ);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+
+ retval = krb5_dbe_encode_mod_princ_data(kadm_context,
+ &mprinc,
+ &newentry);
+ krb5_free_principal(kadm_context, mprinc.mod_princ);
+ 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.princ,
+ &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_error_code retval;
+ krb5_principal theprinc;
+ krb5_db_entry newentry, odata;
+ krb5_tl_mod_princ mprinc;
+ krb5_key_data *pkey;
+ krb5_keysalt sblock;
+
+ 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);
+ retval = 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.princ);
+ newentry.princ = 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)) {
+ 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.length = 8;
+ newpw.enctype = ENCTYPE_DES_CBC_CRC;
+ 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);
+ if (retval = krb5_dbe_find_enctype(kadm_context,
+ &newentry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey)) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ memset((char *)&temp_key, 0, sizeof (temp_key));
+ failmod(retval);
+ }
+ if (pkey->key_data_contents[0]) {
+ krb5_xfree(pkey->key_data_contents[0]);
+ pkey->key_data_contents[0] = (krb5_octet *) NULL;
+ }
+ /* encrypt new key in master key */
+ sblock.type = KRB5_KDB_SALTTYPE_V4;
+ sblock.data.length = 0;
+ sblock.data.data = (char *) NULL;
+ retval = krb5_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &newpw,
+ &sblock,
+ (int) pkey->key_data_kvno+1,
+ pkey);
+ 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 (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failmod(retval);
+ }
+ retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm,
+ &mprinc.mod_princ);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failmod(retval);
+ }
+
+ retval = krb5_dbe_encode_mod_princ_data(kadm_context,
+ &mprinc,
+ &newentry);
+ krb5_free_principal(kadm_context, mprinc.mod_princ);
+ 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.princ, &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_db_entry odata;
+ krb5_key_data *pkey;
+ krb5_keysalt sblock;
+
+ 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.enctype = ENCTYPE_DES_CBC_CRC;
+ localpw.length = 8;
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, rprinc, &odata,
+ &numfound, &more);
+ krb5_free_principal(kadm_context, rprinc);
+ if (retval) {
+ memset(localpw.contents, 0, localpw.length);
+ free(localpw.contents);
+ failchange(retval);
+ } else if (numfound == 1) {
+ if (retval = krb5_dbe_find_enctype(kadm_context,
+ &odata,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey)) {
+ failchange(retval);
+ }
+ pkey->key_data_kvno++;
+ pkey->key_data_kvno %= 256;
+ numfound = 1;
+ sblock.type = KRB5_KDB_SALTTYPE_V4;
+ sblock.data.length = 0;
+ sblock.data.data = (char *) NULL;
+ retval = krb5_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &localpw,
+ &sblock,
+ (int) pkey->key_data_kvno,
+ pkey);
+ memset(localpw.contents, 0, localpw.length);
+ free(localpw.contents);
+ if (retval) {
+ failchange(retval);
+ }
+ 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_key_data *pkey;
+
+ 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) {
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &odata,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey);
+ if (retval) {
+ krb5_free_principal(kadm_context, inprinc);
+ failsrvtab(retval);
+ }
+ }
+ else {
+ /*
+ * This is a new srvtab entry that we're creating
+ */
+ isnew = 1;
+ memset((char *)&odata, 0, sizeof (odata));
+ odata.princ = inprinc;
+ odata.max_life = server_parm.max_life;
+ odata.max_renewable_life = server_parm.max_rlife;
+ odata.expiration = server_parm.expiration;
+ odata.attributes = 0;
+ if (!krb5_dbe_create_key_data(kadm_context, &odata)) {
+ pkey = &odata.key_data[0];
+ memset(pkey, 0, sizeof(*pkey));
+ pkey->key_data_ver = 2;
+ pkey->key_data_type[0] = ENCTYPE_DES_CBC_CRC;
+ pkey->key_data_type[1] = KRB5_KDB_SALTTYPE_V4;
+ }
+ }
+ pkey->key_data_kvno++;
+
+#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.enctype = ENCTYPE_DES_CBC_CRC;
+ 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_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &newpw,
+ (krb5_keysalt *) NULL,
+ (int) pkey->key_data_kvno,
+ pkey);
+ memset((char *)newpw.contents, 0, 8);
+ free(newpw.contents);
+ 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/v4server/kadm_ser_wrap.c b/src/kadmin/v4server/kadm_ser_wrap.c
new file mode 100644
index 000000000..5e7f48508
--- /dev/null
+++ b/src/kadmin/v4server/kadm_ser_wrap.c
@@ -0,0 +1,310 @@
+/*
+ * 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_server.h"
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_err.h>
+#include <syslog.h>
+
+#ifdef OVSEC_KADM
+#include <kadm5/admin.h>
+extern void *ovsec_handle;
+#endif
+
+Kadm_Server server_parm;
+
+/*
+kadm_ser_init
+set up the server_parm structure
+*/
+#ifdef OVSEC_KADM
+kadm_ser_init(inter, realm, params)
+ int inter; /* interactive or from file */
+ char realm[];
+ kadm5_config_params *params;
+#else
+kadm_ser_init(inter, realm)
+ int inter; /* interactive or from file */
+ char realm[];
+#endif
+{
+ 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_key_data *kdatap;
+
+ 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;
+
+#ifdef OVSEC_KADM
+ server_parm.master_keyblock.enctype = params->enctype;
+ krb5_use_enctype(kadm_context, &server_parm.master_encblock,
+ server_parm.master_keyblock.enctype);
+#else
+ if (inter == 1) {
+ server_parm.master_keyblock.enctype = ENCTYPE_DES_CBC_MD5;
+ krb5_use_enctype(kadm_context, &server_parm.master_encblock,
+ server_parm.master_keyblock.enctype);
+ } else
+ server_parm.master_keyblock.enctype = ENCTYPE_UNKNOWN;
+#endif
+
+ 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,
+#ifdef OVSEC_KADM
+ params->stash_file,
+#else
+ (char *) NULL,
+#endif
+ 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;
+
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &master_entry,
+ -1, -1, -1,
+ &kdatap);
+ if (retval)
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+ 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 = kdatap->key_data_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;
+{
+ krb5_ui_4 retcode;
+ char *pdat;
+
+ free((char *)*dat); /* free up req */
+ *dat_len = KADM_VERSIZE + sizeof(krb5_ui_4);
+ *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((krb5_ui_4) code);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4));
+ 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 */
+ krb5_ui_4 r_len; /* length of the actual packet */
+ KTEXT_ST authent; /* the authenticator */
+ AUTH_DAT ad; /* who is this, klink */
+ krb5_ui_4 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(krb5_ui_4);
+ 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, (krb5_ui_4 *)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;
+#ifndef OVSEC_KADM
+ 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;
+#endif /* OVSEC_KADM */
+ 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(krb5_ui_4)));
+ if (!tmpdat) {
+ clr_cli_secrets();
+ syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!",
+ retlen + KADM_VERSIZE + sizeof(krb5_ui_4));
+ errpkt(dat, dat_len, KADM_NOMEM);
+ return KADM_NOMEM;
+ }
+ (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE);
+ retval = htonl((krb5_ui_4)retval);
+ memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb5_ui_4));
+ if (retlen) {
+ memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb5_ui_4), (char *)retdat,
+ retlen);
+ free((char *)retdat);
+ }
+ /* slop for mk_priv stuff */
+ *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE +
+ sizeof(krb5_ui_4) + 200));
+ if ((*dat_len = krb_mk_priv(tmpdat, *dat,
+ (u_long) (retlen + KADM_VERSIZE +
+ sizeof(krb5_ui_4)),
+ 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/v4server/kadm_server.c b/src/kadmin/v4server/kadm_server.c
new file mode 100644
index 000000000..81e43f128
--- /dev/null
+++ b/src/kadmin/v4server/kadm_server.c
@@ -0,0 +1,571 @@
+/*
+ * 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
+
+#ifdef OVSEC_KADM
+#include <com_err.h>
+#include <kadm5/admin.h>
+#include <kadm5/chpass_util_strings.h>
+#include <krb5/kdb.h>
+extern void *ovsec_handle;
+#endif
+
+#include <kadm.h>
+#include <kadm_err.h>
+
+extern krb5_context kadm_context;
+int fascist_cpw = 0; /* Be fascist about insecure passwords? */
+
+#ifdef OVSEC_KADM
+char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n";
+
+#else /* !OVSEC_KADM */
+
+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";
+
+#endif /* OVSEC_KADM */
+
+/* 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();
+#ifdef OVSEC_KADM
+ ovsec_kadm_principal_ent_t princ_ent;
+ ovsec_kadm_policy_ent_t pol_ent;
+ krb5_principal user_princ;
+ char msg_ret[1024], *time_string, *ptr;
+ const char *msg_ptr;
+ krb5_int32 now;
+ time_t until;
+#endif
+
+ /* 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);
+
+#ifdef OVSEC_KADM
+ /* we don't use the client-provided key itself */
+ keylow = keyhigh = 0;
+ memset(newkey, 0, sizeof(newkey));
+
+ if (no_pword) {
+ krb_log("Old-style change password request from '%s.%s@%s'!",
+ ad->pname, ad->pinst, ad->prealm);
+ *outlen = strlen(pw_required)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, pw_required);
+ } else {
+ *outlen = 0;
+ }
+ return KADM_INSECURE_PW;
+ }
+
+ if (krb5_build_principal(kadm_context, &user_princ,
+ strlen(ad->prealm),
+ ad->prealm,
+ ad->pname,
+ *ad->pinst ? ad->pinst : 0, 0))
+ /* this should never happen */
+ return KADM_NOENTRY;
+
+ *outlen = 0;
+
+ if (retval = krb5_timeofday(kadm_context, &now)) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ retval = ovsec_kadm_get_principal(ovsec_handle, user_princ,
+ &princ_ent);
+ if (retval != 0) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /*
+ * This daemon necessarily has the modify privilege, so
+ * ovsec_kadm_chpass_principal will allow it to violate the
+ * policy's minimum lifetime. Since that's A Bad Thing, we need
+ * to enforce it ourselves. Unfortunately, this means we are
+ * duplicating code from both ovsec_adm_server and
+ * ovsec_kadm_chpass_util().
+ */
+ if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) {
+ retval = ovsec_kadm_get_policy(ovsec_handle,
+ princ_ent->policy,
+ &pol_ent);
+ if (retval != 0) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /* make "now" a boolean, true == too soon */
+ now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life);
+
+ (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent);
+
+ if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ retval = CHPASS_UTIL_PASSWORD_TOO_SOON;
+
+ until = princ_ent->last_pwd_change + pol_ent->pw_min_life;
+ time_string = ctime(&until);
+
+ if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
+ *ptr = '\0';
+
+ sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON),
+ time_string);
+ msg_ptr = msg_ret;
+
+ goto send_response;
+ }
+ }
+
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+
+ retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ,
+ pword, NULL, msg_ret);
+ msg_ptr = msg_ret;
+ (void) krb5_free_principal(kadm_context, user_princ);
+
+send_response:
+
+ retval = convert_ovsec_to_kadm(retval);
+
+ if (retval) {
+ /* don't send message on success because kpasswd.v4 will */
+ /* print "password changed" too */
+ *outlen = strlen(msg_ptr)+2;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, msg_ptr);
+ strcat(*datout, "\n");
+ } else
+ *outlen = 0;
+ }
+ if (retval == KADM_INSECURE_PW) {
+ krb_log("'%s.%s@%s' tried to use an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+#else /* OVSEC_KADM */
+ 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((char *) *datout, bad_pw_err);
+ strcat((char *) *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((char *) *datout, bad_pw_warn);
+ strcat((char *) *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));
+#endif /* OVSEC_KADM */
+
+ return retval;
+}
+
+#ifndef OVSEC_KADM
+/*
+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;
+{
+ krb5_ui_4 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((char *) *datout, check_pw_msg);
+ strcat((char *) *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;
+ }
+}
+#endif /* !OVSEC_KADM */
diff --git a/src/kadmin/v4server/kadm_server.h b/src/kadmin/v4server/kadm_server.h
new file mode 100644
index 000000000..d852bcaab
--- /dev/null
+++ b/src/kadmin/v4server/kadm_server.h
@@ -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/v4server/kadm_stream.c b/src/kadmin/v4server/kadm_stream.c
new file mode 100644
index 000000000..86da6c64f
--- /dev/null
+++ b/src/kadmin/v4server/kadm_stream.c
@@ -0,0 +1,277 @@
+/*
+ * 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>
+#include "k5-int.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#else
+extern char *malloc(), *calloc(), *realloc();
+#endif
+
+/*
+ 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)
+krb5_ui_4 dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ krb5_ui_4 temp; /* to hold the net order short */
+
+ temp = htonl(dat); /* convert to network order */
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb5_ui_4)));
+ memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb5_ui_4));
+ return sizeof(krb5_ui_4);
+}
+
+
+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 *) st+ 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 */
+krb5_ui_4 *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen; /* maximum length of st */
+{
+ krb5_ui_4 temp; /* to hold the net order short */
+
+ if (loc + sizeof(krb5_ui_4) > maxlen)
+ return(-1);
+ memcpy((char *) &temp, (char *) st + loc, sizeof(krb5_ui_4));
+ *dat = ntohl(temp); /* convert to network order */
+ return sizeof(krb5_ui_4);
+}
+
+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/v4server/kadm_supp.c b/src/kadmin/v4server/kadm_supp.c
new file mode 100644
index 000000000..9d2f8deb2
--- /dev/null
+++ b/src/kadmin/v4server/kadm_supp.c
@@ -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;
+ }
+}
diff --git a/src/kadmin/v4server/unit-test/ChangeLog b/src/kadmin/v4server/unit-test/ChangeLog
new file mode 100644
index 000000000..93120b8a4
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/ChangeLog
@@ -0,0 +1,13 @@
+Mon Jul 15 17:15:51 1996 Marc Horowitz <marc@mit.edu>
+
+ * helpers.exp (exp_prog): the check for non-newline-terminated
+ stdout was causing failures where there weren't any. Barry
+ doesn't remember why this was here to begin with.
+ * Makefile.ov (unit-test-body), helpers.exp: some versions of
+ runtest do not like digits in command-line variable names.
+ * Makefile.ov (unit-test-body), helpers.exp: ovsec_v4adm_server
+ renamed to kadmind4
+ * getpid.sh: grep out any programs with expect or kadmind4 in
+ their names.
+
+
diff --git a/src/kadmin/v4server/unit-test/Makefile.ov b/src/kadmin/v4server/unit-test/Makefile.ov
new file mode 100644
index 000000000..3af65607e
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/Makefile.ov
@@ -0,0 +1,19 @@
+#
+# $Id$
+#
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+unit-test:: unit-test-setup unit-test-body unit-test-cleanup
+
+unit-test-setup::
+ $(START_SERVERS_LOCAL) -v4files -kdcport 750 -keysalt des-cbc-crc:v4
+ $(LOCAL_MAKE_KEYTAB) -princ changepw/kerberos /krb5/ovsec_adm.srvtab
+
+unit-test-body::
+ $(RUNTEST) VFOURSERVER=../kadmind4 --tool v4server \
+ KDBFIVE_EDIT=../../../admin/edit/kdb5_edit
+
+unit-test-cleanup::
+ $(STOP_SERVERS_LOCAL) -v4files
diff --git a/src/kadmin/v4server/unit-test/config/ChangeLog b/src/kadmin/v4server/unit-test/config/ChangeLog
new file mode 100644
index 000000000..aa01abc17
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/config/ChangeLog
@@ -0,0 +1,7 @@
+Mon Jul 15 17:18:56 1996 Marc Horowitz <marc@mit.edu>
+
+ * unix.exp: some versions of runtest do not like digits in
+ command-line variable names. ovsec_edit_keytab renamed to
+ kadm5_keytab
+
+
diff --git a/src/kadmin/v4server/unit-test/config/unix.exp b/src/kadmin/v4server/unit-test/config/unix.exp
new file mode 100644
index 000000000..874092311
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/config/unix.exp
@@ -0,0 +1,42 @@
+global env
+
+set kill /bin/kill
+
+if {[file exists /bin/sleep]} {
+ set sleep /bin/sleep
+} else {
+ set sleep /usr/bin/sleep
+}
+
+set kpasswd_v4 /usr/athena/bin/kpasswd
+set ovpasswd $env(TOP)/kpasswd/kpasswd
+set kadmin_local $env(TOP)/cli/kadmin.local
+set kdb5_edit $KDBFIVE_EDIT
+set remove_changepw_perms ./remove_changepw_perms.sh
+set getpid ./getpid.sh
+set ovsec_adm_server $env(TOP)/server/kadmind
+set ovsec_edit_keytab $env(TOP)/keytab/kadm5_keytab
+set hostname [exec hostname]
+
+# change-password.exp sends ^C to kpasswd to kill it; on HP-UX the
+# default intr character is DEL, and setting it on all platforms
+# won't hurt
+set stty_init "intr \\^c"
+
+if {[info commands exp_version] != {}} {
+ set exp_version_4 [regexp {^4} [exp_version]]
+} else {
+ set exp_version_4 [regexp {^4} [expect_version]]
+}
+
+# Backward compatibility until we're using expect 5 everywhere
+if {$exp_version_4} {
+ global wait_error_index wait_errno_index wait_status_index
+ set wait_error_index 0
+ set wait_errno_index 1
+ set wait_status_index 1
+} else {
+ set wait_error_index 2
+ set wait_errno_index 3
+ set wait_status_index 3
+}
diff --git a/src/kadmin/v4server/unit-test/getpid.sh b/src/kadmin/v4server/unit-test/getpid.sh
new file mode 100644
index 000000000..5c1b1a690
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/getpid.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# tcl sucks big fat hairy rocks
+
+$PS_ALL | awk "/$1/"' && !/awk/ && !/getpid/ && !/expect/ && !/kadmind4/ { print $2 }'
diff --git a/src/kadmin/v4server/unit-test/helpers.exp b/src/kadmin/v4server/unit-test/helpers.exp
new file mode 100644
index 000000000..7f23b65c8
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/helpers.exp
@@ -0,0 +1,232 @@
+proc server_pids { } {
+ global env
+
+ return [eval [concat exec $env(PS_ALL) | \
+ awk {{/kadmind4/ && !/awk/ && !/expect/ {printf("%d ", $2)}}}]]
+}
+
+proc server_exit { name status } {
+ global wait_error_index wait_errno_index wait_status_index
+ global server_id
+ global kill
+
+ verbose "$name: stopping V4 kpasswd server." 1
+
+ # We can't know whether the process exists or not, so we have
+ # to ignore errors. XXX will close ever time out?
+ catch {close $server_id}
+ set pids [server_pids]
+ if {$pids != {}} {
+ verbose "server_exit killing process(es) $pids"
+ catch {exec $kill $pids}
+ } else {
+ verbose "server_exit: couldn't find running server(s) to kill"
+ }
+
+ # wait hangs on AIX if the process was killed; since status == -1
+ # in that case, solve the problem by not waiting; the zombies will
+ # be cleaned up when the test finishes
+ if {$status == -1} {
+ return 1
+ }
+
+ set ret [wait -i $server_id]
+ verbose "% Exit $ret" 2
+
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail "$name: wait returned error [lindex $ret $wait_errno_index]"
+ return 0
+ } else {
+ if { [lindex $ret $wait_status_index] == $status ||
+ (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ return 0
+ }
+ }
+
+ return 1
+}
+
+proc myfail { msg } {
+ global mytest_status
+ fail "$msg"
+ set mytest_status 1
+}
+
+proc server_start { name cmdline should_listen args } {
+ global spawn_id server_id
+ global VFOURSERVER
+ global mytest_status
+ global sleep hostname
+
+ set max_tries 60
+
+ verbose "$name: starting V4 kpasswd server." 1
+
+ for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} {
+ if {$num_tries} {
+ exec $sleep 5
+ verbose "Couldn't connect to V4 kpasswd server; retrying ($num_tries so far)."
+ }
+
+ spawn $VFOURSERVER $cmdline
+ set server_id $spawn_id
+
+ foreach test $args {
+ set mytest_status 0
+ uplevel 1 "expect {
+ -i $server_id
+ $test
+ timeout { myfail \"$name: timeout\" }
+ eof { myfail \"$name: eof while expecting string\" }
+ }"
+
+ if {$mytest_status == 1} {
+ return 0
+ }
+ }
+
+ set pids [server_pids]
+
+ if {$should_listen} {
+ exec $sleep 1
+ set save_spawn_id $spawn_id
+ spawn telnet $hostname kerberos_master
+ expect {
+ {Connection refused} {
+ close -i $save_spawn_id
+ wait -i $save_spawn_id
+ close
+ wait
+ continue
+ }
+ {Connected} {
+ send "close\n"
+ close
+ wait
+ set spawn_id $save_spawn_id
+ break
+ }
+ default {
+ close -i $save_spawn_id
+ wait -i $save_spawn_id
+ catch {close}
+ wait
+ continue
+ }
+ }
+ } else {
+ break
+ }
+ }
+
+ if {$pids == {}} {
+ # Try twice to find the server processes. Not sure why,
+ # but there seems to be some sort of race condition in the OS.
+
+ verbose "server_start: couldn't find server process(es) -- trying again"
+ exec $sleep 1
+ set pids [server_pids]
+ }
+
+ if {$num_tries > $max_tries} {
+ myfail "$name: couldn't connect to V4 kpasswd server"
+ return 0
+ } else {
+ if {$pids != {}} {
+ verbose "server_start: server process ID(s) is/are $pids"
+ }
+ pass "$name"
+ return 1
+ }
+}
+
+proc exp_prog { name prog cmdline status args } {
+ global spawn_id spawn_pid
+ global mytest_status
+ global wait_error_index wait_errno_index wait_status_index
+
+ verbose "$name: spawning $prog $cmdline" 1
+
+ set spawn_pid [eval "spawn $prog $cmdline"]
+
+ # at the end, eof is success
+
+# lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } }
+ lappend args { eof {} }
+
+ foreach test $args {
+ set mytest_status 0
+ uplevel 1 "expect {
+ $test
+ timeout { close; myfail \"$name: timeout\" }
+ eof { myfail \"$name: eof while expecting string\" }
+ }"
+
+ if {$mytest_status == 1} { return 0 }
+ }
+
+ # at this point, the id is closed and we can wait on it.
+
+ set ret [wait]
+ verbose "% Exit $ret" 2
+
+ if {$status == -1} { return 1 }
+
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail "$name: wait returned error [lindex $ret $wait_errno_index]"
+ } else {
+ if { [lindex $ret $wait_status_index] == $status ||
+ (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ }
+ }
+
+ return 1
+}
+
+proc fix_salt { name fullname oldpw newpw } {
+ global kdb5_edit
+
+ exp_prog "$name: kdb5_edit" $kdb5_edit "" 0 {
+ "kdb5_edit:" { send "cpw $fullname\n" }
+ } {
+ "Enter password:" { send "$newpw\n" }
+ } {
+ "Re-enter password for verification:" { send "$newpw\n" }
+ } {
+ "kdb5_edit:" { send "quit\n" }
+ }
+}
+
+proc unexpire { name fullname } {
+ global kadmin_local
+
+ # While we're at it, make sure they aren't expired.
+ exp_prog "$name: kadmin.local" $kadmin_local "" 0 {
+ "kadmin.local:" {
+ send "modprinc -expire \"May 6, 1999\" $fullname\n"
+ }
+ } {
+ -re "Principal .* modified." { send "quit\n" }
+ }
+}
+
+proc kpasswd_v4 { name fullname status oldpw newpw args } {
+ global kpasswd_v4 s
+
+ eval [concat {
+ exp_prog $name $kpasswd_v4 "-u $fullname" $status {
+ -re "Old password for $fullname:" { send "$oldpw\n" }
+ } {
+ -re "New Password for $fullname:" { send "$newpw\n" }
+ } {
+ -re "Verifying, please re-enter New Password for $fullname:"
+ { send "$newpw\n" }
+ }
+ } $args]
+}
diff --git a/src/kadmin/v4server/unit-test/remove_changepw_perms.sh b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh
new file mode 100644
index 000000000..27d026ff3
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# tcl sucks big fat hairy rocks
+
+ed /krb5/ovsec_adm.acl <<EOF >/dev/null 2>&1
+g/changepw\/kerberos/s/^/#/
+w
+q
+EOF
diff --git a/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp
new file mode 100644
index 000000000..3c8e181b2
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp
@@ -0,0 +1,11 @@
+load_lib "helpers.exp"
+
+set timeout 10
+
+exp_prog "setup" $ovsec_edit_keytab \
+ "-k /krb5/ovsec_adm.srvtab -a -c -p admin changepw/kerberos" \
+ 0 {
+ "Enter password:" { send "admin\n" }
+} {
+ -re "Entry for principal changepw/kerberos .* added to keytab" {}
+}
diff --git a/src/kadmin/v4server/unit-test/v4server.1/access.exp b/src/kadmin/v4server/unit-test/v4server.1/access.exp
new file mode 100644
index 000000000..4d30fc9c7
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.1/access.exp
@@ -0,0 +1,88 @@
+load_lib "helpers.exp"
+
+set timeout 30
+
+# Setup: make sure the principals we will use have V4 salt
+fix_salt "A.setup" testuser notathena notathena
+unexpire "A.setup" testuser
+unexpire "A.setup" changepw/kerberos
+
+proc kill_admin_server {} {
+ global env kill getpid
+
+ set pid [exec $getpid kadmind]
+ if {$pid != ""} {
+ exec $kill $pid
+ }
+}
+
+proc start_admin_server {} {
+ global ovsec_adm_server sleep
+
+ set max_tries 60
+
+ for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} {
+ if {$num_tries} {
+ exec $sleep 5
+ verbose "$ovsec_adm_server couldn't bind; retrying ($num_tries so far)"
+ }
+ if {[catch "exec $ovsec_adm_server" msg]} {
+ if {[regexp {Address already in use} $msg]} {
+ continue
+ }
+ fail "starting $ovsec_adm_server: $msg"
+ }
+ return
+ }
+ fail "starting $ovsec_adm_server: $msg"
+}
+
+proc remove_changepw_perms {} {
+ global remove_changepw_perms
+
+ exec $remove_changepw_perms
+}
+
+proc set_changepw_perms { perms } {
+ remove_changepw_perms
+
+ exec echo "changepw/kerberos@SECURE-TEST.OV.COM $perms" \
+ >> /krb5/ovsec_adm.acl
+}
+
+# start off with a dead admin server
+kill_admin_server
+
+set_changepw_perms "i"
+start_admin_server
+server_start A.1 "-n" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+kpasswd_v4 A.1 testuser 2 notathena foobar {
+ "Operation requires ``change-password'' privilege" {}
+} {
+ "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {}
+} {
+ "Password NOT changed." {}
+}
+server_exit A.1 -1
+kill_admin_server
+
+set_changepw_perms "c"
+start_admin_server
+server_start A.2 "-n" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+kpasswd_v4 A.2 testuser 2 notathena foobar {
+ "Operation requires ``get'' privilege" {}
+} {
+ "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {}
+} {
+ "Password NOT changed." {}
+}
+server_exit A.2 -1
+kill_admin_server
+
+set_changepw_perms "ci"
+
+start_admin_server
diff --git a/src/kadmin/v4server/unit-test/v4server.1/change-password.exp b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp
new file mode 100644
index 000000000..62b9ec30a
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp
@@ -0,0 +1,59 @@
+load_lib "helpers.exp"
+
+set timeout 30
+
+spawn stty -a
+expect { eof {} }
+wait
+
+# Setup: make sure the principals we will use have V4 salt
+fix_salt "CPW.setup" testuser notathena notathena
+fix_salt "CPW.setup" pol1 pol111111 pol111111
+fix_salt "CPW.setup" pol2 pol222222 pol222222
+unexpire "CPW.setup" testuser
+unexpire "CPW.setup" pol1
+unexpire "CPW.setup" pol2
+unexpire "CPW.setup" changepw/kerberos
+
+server_start "CPW.all" "-n" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+
+kpasswd_v4 CPW.1 testuser 0 notathena foobar { "Password changed." {} }
+kpasswd_v4 CPW.1 testuser 0 foobar notathena { "Password changed." {} }
+
+kpasswd_v4 CPW.3 pol1 -1 pol111111 foo {
+ "New password is too short." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+kpasswd_v4 CPW.4 pol1 -1 pol111111 foooooooo {
+ "New password does not have enough character classes." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+kpasswd_v4 CPW.5 pol1 -1 pol111111 Abyssinia {
+ "New password was found in a dictionary" {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+kpasswd_v4 CPW.6.setup pol1 0 pol111111 polAAAAAA { "Password changed." {} }
+kpasswd_v4 CPW.6 pol1 -1 polAAAAAA pol111111 {
+ "New password was used previously." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+# this relies on the fact that kdb5_edit resets last_pwd_change, which
+# it appears to
+kpasswd_v4 CPW.7.setup pol2 0 pol222222 polBBBBBB { "Password changed." {} }
+kpasswd_v4 CPW.7 pol2 -1 polBBBBBB pol222222 {
+ "Password cannot be changed because it was changed too recently." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+server_exit "CPW.all" -1
diff --git a/src/kadmin/v4server/unit-test/v4server.1/usage.exp b/src/kadmin/v4server/unit-test/v4server.1/usage.exp
new file mode 100644
index 000000000..4d292067a
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.1/usage.exp
@@ -0,0 +1,26 @@
+load_lib "helpers.exp"
+
+set timeout 10
+
+server_start "U.1: -h" "-h" 0 {
+ -re {Usage: .*} {}
+} {
+ eof {}
+}
+server_exit "U.1: -h" 255
+
+server_start "U.4: -n" "-n" 1 {
+ "Enter KDC database master key:" {
+ myfail "unexpected password prompt"
+ }
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+
+server_exit "U.4: -n" -1
+
+server_start "U.5: no -n" "" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+} {
+ "Enter KDC database master key:" { send "mrroot\n" }
+}
+server_exit "U.5: no -n" -1