* Makefile.in, configure.in: break out server lib into a
authorTom Yu <tlyu@mit.edu>
Wed, 24 Jul 1996 22:23:37 +0000 (22:23 +0000)
committerTom Yu <tlyu@mit.edu>
Wed, 24 Jul 1996 22:23:37 +0000 (22:23 +0000)
subdirectory

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

20 files changed:
src/lib/kadm5/srv/ChangeLog [new file with mode: 0644]
src/lib/kadm5/srv/Makefile.in [new file with mode: 0644]
src/lib/kadm5/srv/adb_free.c [new file with mode: 0644]
src/lib/kadm5/srv/adb_openclose.c [new file with mode: 0644]
src/lib/kadm5/srv/adb_policy.c [new file with mode: 0644]
src/lib/kadm5/srv/adb_principal.c [new file with mode: 0644]
src/lib/kadm5/srv/adb_xdr.c [new file with mode: 0644]
src/lib/kadm5/srv/configure.in [new file with mode: 0644]
src/lib/kadm5/srv/server_acl.c [new file with mode: 0644]
src/lib/kadm5/srv/server_acl.h [new file with mode: 0644]
src/lib/kadm5/srv/server_dict.c [new file with mode: 0644]
src/lib/kadm5/srv/server_handle.c [new file with mode: 0644]
src/lib/kadm5/srv/server_init.c [new file with mode: 0644]
src/lib/kadm5/srv/server_kdb.c [new file with mode: 0644]
src/lib/kadm5/srv/server_misc.c [new file with mode: 0644]
src/lib/kadm5/srv/svr_chpass_util.c [new file with mode: 0644]
src/lib/kadm5/srv/svr_iters.c [new file with mode: 0644]
src/lib/kadm5/srv/svr_misc_free.c [new file with mode: 0644]
src/lib/kadm5/srv/svr_policy.c [new file with mode: 0644]
src/lib/kadm5/srv/svr_principal.c [new file with mode: 0644]

diff --git a/src/lib/kadm5/srv/ChangeLog b/src/lib/kadm5/srv/ChangeLog
new file mode 100644 (file)
index 0000000..f91603f
--- /dev/null
@@ -0,0 +1,5 @@
+Wed Jul 24 18:21:28 1996  Tom Yu  <tlyu@voltage-multiplier.mit.edu>
+
+       * Makefile.in, configure.in: break out server lib into a
+               subdirectory
+
diff --git a/src/lib/kadm5/srv/Makefile.in b/src/lib/kadm5/srv/Makefile.in
new file mode 100644 (file)
index 0000000..4308ed4
--- /dev/null
@@ -0,0 +1,92 @@
+CFLAGS = $(CCOPTS) $(DEFS) -I$(BUILDTOP)/include/kadm5
+
+##DOSBUILDTOP = ..\..\..
+##DOSLIBNAME = libkadm5srv.lib
+
+.c.o:
+       $(CC) $(CFLAGS) -c $(srcdir)/$*.c
+@SHARED_RULE@
+
+SRCS = $(srcdir)/svr_policy.c \
+       $(srcdir)/svr_principal.c \
+       $(srcdir)/server_acl.c \
+       $(srcdir)/server_kdb.c \
+       $(srcdir)/server_misc.c \
+       $(srcdir)/server_init.c \
+       $(srcdir)/server_dict.c \
+       $(srcdir)/svr_iters.c \
+       $(srcdir)/svr_chpass_util.c \
+       $(srcdir)/adb_xdr.c \
+       $(srcdir)/adb_policy.c \
+       $(srcdir)/adb_free.c \
+       $(srcdir)/adb_openclose.c
+
+OBJS = svr_policy.$(OBJEXT) \
+       svr_principal.$(OBJEXT) \
+       server_acl.$(OBJEXT) \
+       server_kdb.$(OBJEXT) \
+       server_misc.$(OBJEXT) \
+       server_init.$(OBJEXT) \
+       server_dict.$(OBJEXT) \
+       svr_iters.$(OBJEXT) \
+       svr_chpass_util.$(OBJEXT) \
+       adb_xdr.$(OBJEXT) \
+       adb_policy.$(OBJEXT) \
+       adb_free.$(OBJEXT) \
+       adb_openclose.$(OBJEXT)
+
+LIBUPDATE=$(BUILDTOP)/util/libupdate
+
+#
+# Depends on libgssrpc, libgssapi_krb5, libkdb5, libkrb5, libcrypto,
+# libcom_err, libdyn
+#
+GSSRPC_VER=@GSSRPC_SH_VERS@
+GSSAPI_KRB5_VER=@GSSAPI_KRB5_SH_VERS@
+KDB5_VER=@KDB5_SH_VERS@
+KRB5_VER=@KRB5_SH_VERS@
+CRYPTO_VER=@CRYPTO_SH_VERS@
+COMERR_VER=@COMERR_SH_VERS@
+DYN_VER=@DYN_SH_VERS@
+DEPLIBS=$(TOPLIBD)/libgssrpc.$(SHEXT).$(GSSRPC_VER) \
+       $(TOPLIBD)/libgssapi_krb5.$(SHEXT).$(GSSAPI_KRB5_VER) \
+       $(TOPLIBD)/libkdb5.$(SHEXT).$(KDB5_VER) \
+       $(TOPLIBD)/libkrb5.$(SHEXT).$(KRB5_VER) \
+       $(TOPLIBD)/libcrypto.$(SHEXT).$(CRYPTO_VER) \
+       $(TOPLIBD)/libcom_err.$(SHEXT).$(COMERR_VER) \
+       $(TOPLIBD)/libdyn.$(SHEXT).$(DYN_VER)
+
+SHLIB_LIBS=-lgssrpc -lgssapi_krb5 -lkdb5 -ldb -lkrb5 -lcrypto -lcom_err -ldyn
+SHLIB_LDFLAGS= $(LDFLAGS) @SHLIB_RPATH_DIRS@
+SHLIB_LIBDIRS= @SHLIB_LIBDIRS@
+
+all-unix:: shared includes $(OBJS)
+all-mac:: $(OBJS)
+all-windows:: $(OBJS)
+
+LIBDONE=../DONE DONE
+LIB_SUBDIRS=.. .
+
+shared:
+       mkdir shared
+
+libkadm5srv.$(STEXT): $(LIBDONE)
+       @if test -f $@ ; then \
+               (set -x; $(LIBUPDATE) $@ DONE $(LIB_SUBDIRS)) \
+       else \
+               (set -x; $(LIBUPDATE) --force $@ DONE $(LIB_SUBDIRS)) \
+       fi
+       $(RANLIB) $@
+       touch libkadm5srv.stamp
+
+check-windows::
+
+clean-mac::
+clean-windows::
+
+clean-unix::
+       $(RM) libkadm5srv.$(STEXT) libkadm5srv.stamp
+
+install:: libkadm5srv.a
+       $(INSTALL_DATA) libkadm5srv.a $(DESTDIR)$(KRB5_LIBDIR)/libkadm5srv.a
+       $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libkadm5srv.a
diff --git a/src/lib/kadm5/srv/adb_free.c b/src/lib/kadm5/srv/adb_free.c
new file mode 100644 (file)
index 0000000..a58af17
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ * 
+ * $Log$
+ * Revision 1.1  1996/07/24 22:23:09  tlyu
+ *     * Makefile.in, configure.in: break out server lib into a
+ *             subdirectory
+ *
+ * Revision 1.8  1996/07/22 20:35:16  marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches.  This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964.  before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.7.4.1  1996/07/18 03:08:07  marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.7.2.1  1996/06/20  02:16:25  marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.7  1996/05/12  06:21:57  marc
+ * don't use <absolute paths> for "internal header files"
+ *
+ * Revision 1.6  1993/12/13  21:15:56  shanzer
+ * fixed memory leak
+ * .,
+ *
+ * Revision 1.5  1993/12/06  22:20:37  marc
+ * fixup free functions to use xdr to free the underlying struct
+ *
+ * Revision 1.4  1993/11/15  00:29:46  shanzer
+ * check to make sure pointers are somewhat vaid before freeing.
+ *
+ * Revision 1.3  1993/11/09  04:02:24  shanzer
+ * added some includefiles
+ * changed bzero to memset
+ *
+ * Revision 1.2  1993/11/04  01:54:24  shanzer
+ * added rcs header ..
+ *
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include       "adb.h"
+#include       <memory.h>
+#include       <malloc.h>
+
+void
+osa_free_princ_ent(osa_princ_ent_t val)
+{
+    XDR xdrs;
+
+    xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
+
+    xdr_osa_princ_ent_rec(&xdrs, val);
+    free(val);
+}
+
+void
+osa_free_policy_ent(osa_policy_ent_t val)
+{
+    XDR xdrs;
+
+    xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
+
+    xdr_osa_policy_ent_rec(&xdrs, val);
+    free(val);
+}
+
diff --git a/src/lib/kadm5/srv/adb_openclose.c b/src/lib/kadm5/srv/adb_openclose.c
new file mode 100644 (file)
index 0000000..627a6b4
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$ 
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include       <sys/file.h>
+#include       <fcntl.h>
+#include       <unistd.h>
+#include       "adb.h"
+#include       <stdlib.h>
+
+#define MAX_LOCK_TRIES 5
+
+struct _locklist {
+     osa_adb_lock_ent lockinfo;
+     struct _locklist *next;
+};
+
+osa_adb_ret_t osa_adb_create_db(char *filename, char *lockfilename,
+                               int magic)
+{
+     FILE *lf;
+     DB *db;
+     HASHINFO info;
+     
+     lf = fopen(lockfilename, "w+");
+     if (lf == NULL)
+         return errno;
+     (void) fclose(lf);
+
+     memset(&info, 0, sizeof(info));
+     info.hash = NULL;
+     info.bsize = 256;
+     info.ffactor = 8;
+     info.nelem = 25000;
+     info.lorder = 0;
+     db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_HASH, &info);
+     if (db == NULL)
+         return errno;
+     if (db->close(db) < 0)
+         return errno;
+     return OSA_ADB_OK;
+}
+
+osa_adb_ret_t osa_adb_destroy_db(char *filename, char *lockfilename,
+                                int magic)
+{
+     /* the admin databases do not contain security-critical data */
+     if (unlink(filename) < 0 ||
+        unlink(lockfilename) < 0)
+         return errno;
+     return OSA_ADB_OK;
+}
+
+osa_adb_ret_t osa_adb_init_db(osa_adb_db_t *dbp, char *filename,
+                             char *lockfilename, int magic)
+{
+     osa_adb_db_t db;
+     static struct _locklist *locklist = NULL;
+     struct _locklist *lockp;
+     krb5_error_code code;
+     
+     if (dbp == NULL || filename == NULL)
+         return EINVAL;
+
+     db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent));
+     if (db == NULL)
+         return ENOMEM;
+
+     memset(db, 0, sizeof(*db));
+     db->info.hash = NULL;
+     db->info.bsize = 256;
+     db->info.ffactor = 8;
+     db->info.nelem = 25000;
+     db->info.lorder = 0;
+
+     /*
+      * A process is allowed to open the same database multiple times
+      * and access it via different handles.  If the handles use
+      * distinct lockinfo structures, things get confused: lock(A),
+      * lock(B), release(B) will result in the kernel unlocking the
+      * lock file but handle A will still think the file is locked.
+      * Therefore, all handles using the same lock file must share a
+      * single lockinfo structure.
+      *
+      * It is not sufficient to have a single lockinfo structure,
+      * however, because a single process may also wish to open
+      * multiple different databases simultaneously, with different
+      * lock files.  This code used to use a single static lockinfo
+      * structure, which means that the second database opened used
+      * the first database's lock file.  This was Bad.
+      *
+      * We now maintain a linked list of lockinfo structures, keyed by
+      * lockfilename.  An entry is added when this function is called
+      * with a new lockfilename, and all subsequent calls with that
+      * lockfilename use the existing entry, updating the refcnt.
+      * When the database is closed with fini_db(), the refcnt is
+      * decremented, and when it is zero the lockinfo structure is
+      * freed and reset.  The entry in the linked list, however, is
+      * never removed; it will just be reinitialized the next time
+      * init_db is called with the right lockfilename.
+      */
+
+     /* find or create the lockinfo structure for lockfilename */
+     lockp = locklist;
+     while (lockp) {
+         if (strcmp(lockp->lockinfo.filename, lockfilename) == 0)
+              break;
+         else
+              lockp = lockp->next;
+     }
+     if (lockp == NULL) {
+         /* doesn't exist, create it, add to list */
+         lockp = (struct _locklist *) malloc(sizeof(*lockp));
+         if (lockp == NULL) {
+              free(db);
+              return ENOMEM;
+         }
+         memset(lockp, 0, sizeof(*lockp));
+         lockp->next = locklist;
+         locklist = lockp;
+     }
+
+     /* now initialize lockp->lockinfo if necessary */
+     if (lockp->lockinfo.lockfile == NULL) {
+         if (code = krb5_init_context(&lockp->lockinfo.context)) {
+              free(db);
+              return((osa_adb_ret_t) code);
+         }
+
+         /*
+          * needs be open read/write so that write locking can work with
+          * POSIX systems
+          */
+         lockp->lockinfo.filename = strdup(lockfilename);
+         if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+")) == NULL) {
+              /*
+               * maybe someone took away write permission so we could only
+               * get shared locks?
+               */
+              if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r"))
+                  == NULL) {
+                   free(db);
+                   return OSA_ADB_NOLOCKFILE;
+              }
+         }
+         lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0;
+     }
+
+     /* lockp is set, lockinfo is initialized, update the reference count */
+     db->lock = &lockp->lockinfo;
+     db->lock->refcnt++;
+
+     db->filename = strdup(filename);
+     db->magic = magic;
+
+     *dbp = db;
+     
+     return OSA_ADB_OK;
+}
+
+osa_adb_ret_t osa_adb_fini_db(osa_adb_db_t db, int magic)
+{
+     if (db->magic != magic)
+         return EINVAL;
+     if (db->lock->refcnt == 0) {
+         /* barry says this can't happen */
+         return OSA_ADB_FAILURE;
+     } else {
+         db->lock->refcnt--;
+     }
+
+     if (db->lock->refcnt == 0) {
+         /*
+          * Don't free db->lock->filename, it is used as a key to
+          * find the lockinfo entry in the linked list.  If the
+          * lockfile doesn't exist, we must be closing the database
+          * after trashing it.  This has to be allowed, so don't
+          * generate an error.
+          */
+         (void) fclose(db->lock->lockfile);
+         db->lock->lockfile = NULL;
+         krb5_free_context(db->lock->context);
+     }
+
+     db->magic = 0;
+     free(db->filename);
+     free(db);
+     return OSA_ADB_OK;
+}     
+     
+osa_adb_ret_t osa_adb_get_lock(osa_adb_db_t db, int mode)
+{
+     int tries, gotlock, perm, krb5_mode, ret;
+
+     if (db->lock->lockmode >= mode) {
+         /* No need to upgrade lock, just incr refcnt and return */
+         db->lock->lockcnt++;
+         return(OSA_ADB_OK);
+     }
+
+     perm = 0;
+     switch (mode) {
+       case OSA_ADB_PERMANENT:
+         perm = 1;
+       case OSA_ADB_EXCLUSIVE:
+         krb5_mode = KRB5_LOCKMODE_EXCLUSIVE;
+         break;
+       case OSA_ADB_SHARED:
+         krb5_mode = KRB5_LOCKMODE_SHARED;
+         break;
+       default:
+         return(EINVAL);
+     }
+
+     for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
+         if ((ret = krb5_lock_file(db->lock->context,
+                                   fileno(db->lock->lockfile),
+                                   krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) {
+              gotlock++;
+              break;
+         } else if (ret == EBADF && mode == OSA_ADB_EXCLUSIVE)
+              /* tried to exclusive-lock something we don't have */
+              /* write access to */
+              return OSA_ADB_NOEXCL_PERM;
+
+         sleep(1);
+     }
+
+     /* test for all the likely "can't get lock" error codes */
+     if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK)
+         return OSA_ADB_CANTLOCK_DB;
+     else if (ret != 0)
+         return ret;
+
+     /*
+      * If the file no longer exists, someone acquired a permanent
+      * lock.  If that process terminates its exclusive lock is lost,
+      * but if we already had the file open we can (probably) lock it
+      * even though it has been unlinked.  So we need to insist that
+      * it exist.
+      */
+     if (access(db->lock->filename, F_OK) < 0) {
+         (void) krb5_lock_file(db->lock->context,
+                               fileno(db->lock->lockfile),
+                               KRB5_LOCKMODE_UNLOCK);
+         return OSA_ADB_NOLOCKFILE;
+     }
+     
+     /* we have the shared/exclusive lock */
+     
+     if (perm) {
+         if (unlink(db->lock->filename) < 0) {
+              int ret;
+
+              /* somehow we can't delete the file, but we already */
+              /* have the lock, so release it and return */
+
+              ret = errno;
+              (void) krb5_lock_file(db->lock->context,
+                                    fileno(db->lock->lockfile),
+                                    KRB5_LOCKMODE_UNLOCK);
+              
+              /* maybe we should return CANTLOCK_DB.. but that would */
+              /* look just like the db was already locked */
+              return ret;
+         }
+
+         /* this releases our exclusive lock.. which is okay because */
+         /* now no one else can get one either */
+         (void) fclose(db->lock->lockfile);
+     }
+     
+     db->lock->lockmode = mode;
+     db->lock->lockcnt++;
+     return OSA_ADB_OK;
+}
+
+osa_adb_ret_t osa_adb_release_lock(osa_adb_db_t db)
+{
+     int ret;
+     
+     if (!db->lock->lockcnt)           /* lock already unlocked */
+         return OSA_ADB_NOTLOCKED;
+
+     if (--db->lock->lockcnt == 0) {
+         if (db->lock->lockmode == OSA_ADB_PERMANENT) {
+              /* now we need to create the file since it does not exist */
+              if ((db->lock->lockfile = fopen(db->lock->filename,
+                                              "w+")) == NULL)
+                   return OSA_ADB_NOLOCKFILE;
+         } else if (ret = krb5_lock_file(db->lock->context,
+                                         fileno(db->lock->lockfile),
+                                         KRB5_LOCKMODE_UNLOCK))
+              return ret;
+         
+         db->lock->lockmode = 0;
+     }
+     return OSA_ADB_OK;
+}
+
+osa_adb_ret_t osa_adb_open_and_lock(osa_adb_princ_t db, int locktype)
+{
+     int ret;
+
+     ret = osa_adb_get_lock(db, locktype);
+     if (ret != OSA_ADB_OK)
+         return ret;
+     
+     db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info);
+     if (db->db == NULL) {
+         (void) osa_adb_release_lock(db);
+         if(errno == EINVAL)
+              return OSA_ADB_BAD_DB;
+         return errno;
+     }
+     return OSA_ADB_OK;
+}
+
+osa_adb_ret_t osa_adb_close_and_unlock(osa_adb_princ_t db)
+{
+     int ret;
+
+     if(db->db->close(db->db) == -1) {
+         (void) osa_adb_release_lock(db);
+         return OSA_ADB_FAILURE;
+     }
+
+     db->db = NULL;
+
+     return(osa_adb_release_lock(db));
+}
+
diff --git a/src/lib/kadm5/srv/adb_policy.c b/src/lib/kadm5/srv/adb_policy.c
new file mode 100644 (file)
index 0000000..ff0117b
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include       <sys/file.h>
+#include       <fcntl.h>
+#include       "adb.h"
+#include       <stdlib.h>
+#include       <malloc.h>
+#include       <string.h>
+
+extern int errno;
+
+#define OPENLOCK(db, mode) \
+{ \
+       int ret; \
+           if (db == NULL) \
+                return EINVAL; \
+           else if (db->magic != OSA_ADB_POLICY_DB_MAGIC) \
+                return OSA_ADB_DBINIT; \
+           else if ((ret = osa_adb_open_and_lock(db, mode)) != OSA_ADB_OK) \
+                return ret; \
+           }
+
+#define CLOSELOCK(db) \
+{ \
+     int ret; \
+     if ((ret = osa_adb_close_and_unlock(db)) != OSA_ADB_OK) \
+         return ret; \
+}
+
+osa_adb_ret_t osa_adb_create_policy_db(kadm5_config_params *params)
+{
+     return osa_adb_create_db(params->admin_dbname,
+                             params->admin_lockfile,
+                             OSA_ADB_POLICY_DB_MAGIC);
+}
+
+osa_adb_ret_t osa_adb_destroy_policy_db(kadm5_config_params *params)
+{
+     return osa_adb_destroy_db(params->admin_dbname,
+                              params->admin_lockfile,
+                              OSA_ADB_POLICY_DB_MAGIC);
+}
+
+osa_adb_ret_t osa_adb_open_policy(osa_adb_princ_t *dbp,
+                                 kadm5_config_params *rparams)
+{
+     return osa_adb_init_db(dbp, rparams->admin_dbname,
+                           rparams->admin_lockfile,
+                           OSA_ADB_POLICY_DB_MAGIC);
+}
+
+osa_adb_ret_t osa_adb_close_policy(osa_adb_princ_t db)
+{
+     return osa_adb_fini_db(db, OSA_ADB_POLICY_DB_MAGIC);
+}
+
+/*
+ * Function: osa_adb_create_policy
+ * 
+ * Purpose: create a policy entry in the policy db.
+ *
+ * Arguments:
+ *     entry           (input) pointer to the entry to be added
+ *     <return value>  OSA_ADB_OK on sucsess, else error code.
+ *
+ * Requires:
+ *     entry have a valid name.
+ * 
+ * Effects:
+ *     creates the entry in the db
+ *
+ * Modifies:
+ *     the policy db.
+ * 
+ */
+osa_adb_ret_t
+osa_adb_create_policy(osa_adb_policy_t db, osa_policy_ent_t entry)
+{
+    DBT                        dbkey;
+    DBT                        dbdata;
+    XDR                        xdrs;
+    int                        ret;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE);
+
+    if(entry->name == NULL) {
+        ret = EINVAL;
+        goto error;
+    }
+    dbkey.data = entry->name;
+    dbkey.size = (strlen(entry->name) + 1);
+               
+    switch(db->db->get(db->db, &dbkey, &dbdata, 0)) {
+    case 0:
+        ret = OSA_ADB_DUP;
+        goto error;
+    case 1:
+       break;
+    default:
+        ret = errno;
+        goto error;
+    }
+    xdralloc_create(&xdrs, XDR_ENCODE);
+    if(!xdr_osa_policy_ent_rec(&xdrs, entry)) {
+       xdr_destroy(&xdrs);
+       ret = OSA_ADB_XDR_FAILURE;
+       goto error;
+    }
+    dbdata.data = xdralloc_getdata(&xdrs);
+    dbdata.size = xdr_getpos(&xdrs);
+    switch(db->db->put(db->db, &dbkey, &dbdata, R_NOOVERWRITE)) {
+    case 0:
+       if((db->db->sync(db->db, 0)) == -1)
+           ret = OSA_ADB_FAILURE;
+       ret = OSA_ADB_OK;
+       break;
+    case 1:
+       ret = OSA_ADB_DUP;
+       break;
+    default:
+       ret = OSA_ADB_FAILURE;
+       break;
+    }
+    xdr_destroy(&xdrs);
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
+
+/*
+ * Function: osa_adb_destroy_policy
+ * 
+ * Purpose: destroy a policy entry
+ *
+ * Arguments:
+ *     db              (input) database handle
+ *     name            (input) name of policy
+ *     <return value>  OSA_ADB_OK on sucsess, or error code.
+ *
+ * Requires:
+ *     db being valid.
+ *     name being non-null.
+ * Effects:
+ *     deletes policy from db.
+ *
+ * Modifies:
+ *     policy db.
+ * 
+ */
+osa_adb_ret_t
+osa_adb_destroy_policy(osa_adb_policy_t db, kadm5_policy_t name)
+{
+    DBT            dbkey;
+    int            status, ret;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE);
+    
+    if(name == NULL) {
+        ret = EINVAL;
+        goto error;
+    }
+    dbkey.data = name;
+    dbkey.size = (strlen(name) + 1);
+
+    status = db->db->del(db->db, &dbkey, 0);
+    switch(status) {
+    case 1:
+        ret = OSA_ADB_NOENT;
+        goto error;
+    case 0:
+        if ((db->db->sync(db->db, 0)) == -1) {
+             ret = OSA_ADB_FAILURE;
+             goto error;
+        }
+        ret = OSA_ADB_OK;
+        break;
+    default:
+        ret = OSA_ADB_FAILURE;
+        goto error;
+    }
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
+
+/*
+ * Function: osa_adb_get_policy
+ * 
+ * Purpose: retrieve policy
+ *
+ * Arguments:
+ *     db              (input) db handle
+ *     name            (input) name of policy
+ *     entry           (output) policy entry
+ *     <return value>  0 on sucsess, error code on failure.
+ *
+ * Requires:
+ * Effects:
+ * Modifies:
+ */
+osa_adb_ret_t
+osa_adb_get_policy(osa_adb_policy_t db, kadm5_policy_t name,
+                  osa_policy_ent_t *entry)
+{
+    DBT                        dbkey;
+    DBT                        dbdata;
+    XDR                        xdrs;
+    int                        ret;
+    char               *aligned_data;
+
+    OPENLOCK(db, OSA_ADB_SHARED);
+
+    if(name == NULL) {
+        ret = EINVAL;
+        goto error;
+    }
+    dbkey.data = name;
+    dbkey.size = (strlen(dbkey.data) + 1);
+    dbdata.data = NULL;
+    dbdata.size = 0;
+    switch((db->db->get(db->db, &dbkey, &dbdata, 0))) {
+    case 1:
+        ret = OSA_ADB_NOENT;
+        goto error;
+    case 0:
+       break;
+    default:
+        ret = OSA_ADB_FAILURE;
+        goto error;
+    }
+    if (!(*(entry) = (osa_policy_ent_t)malloc(sizeof(osa_policy_ent_rec)))) {
+        ret = ENOMEM;
+        goto error;
+    }
+    if (!(aligned_data = (char *) malloc(dbdata.size))) {
+        ret = ENOMEM;
+        goto error;
+    }
+    memcpy(aligned_data, dbdata.data, dbdata.size);    
+    memset(*entry, 0, sizeof(osa_policy_ent_rec));
+    xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE);
+    if (!xdr_osa_policy_ent_rec(&xdrs, *entry)) 
+       ret =  OSA_ADB_FAILURE;
+    else ret = OSA_ADB_OK;
+    xdr_destroy(&xdrs);
+    free(aligned_data);
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
+
+/*
+ * Function: osa_adb_put_policy
+ * 
+ * Purpose: update a policy in the dababase
+ *
+ * Arguments:
+ *     db              (input) db handle
+ *     entry           (input) policy entry
+ *     <return value>  0 on sucsess error code on failure.
+ *
+ * Requires:
+ *     [requires]
+ * 
+ * Effects:
+ *     [effects]
+ *
+ * Modifies:
+ *     [modifies]
+ * 
+ */
+osa_adb_ret_t
+osa_adb_put_policy(osa_adb_policy_t db, osa_policy_ent_t entry)
+{
+    DBT                        dbkey;
+    DBT                        dbdata;
+    DBT                        tmpdb;
+    XDR                        xdrs;
+    int                        ret;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE);
+    
+    if(entry->name == NULL) {
+        ret = EINVAL;
+        goto error;
+    }
+    dbkey.data = entry->name;
+    dbkey.size = (strlen(entry->name) + 1);
+    switch(db->db->get(db->db, &dbkey, &tmpdb, 0)) {
+    case 0:
+       break;
+    case 1:
+       ret = OSA_ADB_NOENT;
+       goto error;
+    default:
+       ret = OSA_ADB_FAILURE;
+       goto error;
+    }
+    xdralloc_create(&xdrs, XDR_ENCODE);
+    if(!xdr_osa_policy_ent_rec(&xdrs, entry)) {
+       xdr_destroy(&xdrs);
+       ret = OSA_ADB_XDR_FAILURE;
+       goto error;
+    }
+    dbdata.data = xdralloc_getdata(&xdrs);
+    dbdata.size = xdr_getpos(&xdrs);
+    switch(db->db->put(db->db, &dbkey, &dbdata, 0)) {
+    case 0:
+       if((db->db->sync(db->db, 0)) == -1)
+           ret = OSA_ADB_FAILURE;
+       ret = OSA_ADB_OK;
+       break;
+    default:
+       ret = OSA_ADB_FAILURE;
+       break;
+    }
+    xdr_destroy(&xdrs);
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
+
+/*
+ * Function: osa_adb_iter_policy
+ * 
+ * Purpose: iterate over the policy database.
+ *
+ * Arguments:
+ *     db              (input) db handle
+ *     func            (input) fucntion pointer to call
+ *     data            opaque data type
+ *     <return value>  0 on sucsess error code on failure
+ *
+ * Requires:
+ * Effects:
+ * Modifies:
+ */
+osa_adb_ret_t
+osa_adb_iter_policy(osa_adb_policy_t db, osa_adb_iter_policy_func func,
+                   void *data)
+{
+    DBT                            dbkey,
+                           dbdata;
+    XDR                            xdrs;
+    int                            ret;
+    osa_policy_ent_t       entry;
+    char                   *aligned_data;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE); /* hmmm */
+
+    if((ret = db->db->seq(db->db, &dbkey, &dbdata, R_FIRST)) == -1) {
+        ret = errno;
+        goto error;
+    }
+
+    while (ret == 0) {
+       if (!(entry = (osa_policy_ent_t) malloc(sizeof(osa_policy_ent_rec)))) {
+            ret = ENOMEM;
+            goto error;
+       }
+
+       if(!(aligned_data = (char *) malloc(dbdata.size))) {
+            ret = ENOMEM;
+            goto error;
+       }
+       memcpy(aligned_data, dbdata.data, dbdata.size);
+       
+       memset(entry, 0, sizeof(osa_policy_ent_rec));
+       xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE);
+       if(!xdr_osa_policy_ent_rec(&xdrs, entry)) {
+           xdr_destroy(&xdrs);
+           free(aligned_data);
+           ret = OSA_ADB_FAILURE;
+           goto error;
+       }
+       (*func)(data, entry);
+       xdr_destroy(&xdrs);
+       free(aligned_data);     
+       osa_free_policy_ent(entry);
+       ret = db->db->seq(db->db, &dbkey, &dbdata, R_NEXT);
+    }
+    if(ret == -1)
+        ret = errno;
+    else ret = OSA_ADB_OK;
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
diff --git a/src/lib/kadm5/srv/adb_principal.c b/src/lib/kadm5/srv/adb_principal.c
new file mode 100644 (file)
index 0000000..8c2e9e2
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ * 
+ * $Log$
+ * Revision 1.1  1996/07/24 22:23:12  tlyu
+ *     * Makefile.in, configure.in: break out server lib into a
+ *             subdirectory
+ *
+ * Revision 1.24  1996/07/22 20:35:23  marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches.  This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964.  before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.23.4.1  1996/07/18 03:08:17  marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.23.2.1  1996/06/20  02:16:30  marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.23  1996/05/16  21:44:35  bjaspan
+ * this file is no longer used, #if the whole thing out
+ *
+ * Revision 1.22  1996/05/08 20:51:44  bjaspan
+ * marc's changes
+ *
+ * Revision 1.21  1995/08/24  20:23:43  bjaspan
+ * marc is a bonehead
+ *
+ * Revision 1.20  1995/08/23  19:16:02  marc
+ * check for db == NULL in OPENLOCK()
+ *
+ * Revision 1.19  1995/08/08  18:31:30  bjaspan
+ * [secure/3394] first cut at admin db locking support
+ *
+ * Revision 1.18  1995/08/02  15:26:57  bjaspan
+ * check db==NULL in iter
+ *
+ * Revision 1.17  1994/05/09  17:52:36  shanzer
+ * fixed some include files
+ *
+ * Revision 1.16  1994/03/17  01:25:58  shanzer
+ * include fcntl.h
+ *
+ * Revision 1.15  1993/12/17  18:54:06  jik
+ * [secure-admin/1040]
+ *
+ * open_princ should return errno, rather than BAD_DB, if errno is
+ * something other than BAD_DB.
+ *
+ * Revision 1.14  1993/12/13  18:55:58  marc
+ * remove bogus free()'s
+ *
+ * Revision 1.13  1993/12/08  22:29:27  marc
+ * fixed another xdrmem alignment thing]
+ *
+ * Revision 1.12  1993/12/06  22:22:22  bjaspan
+ * fix alignment and free-memory-read bugs
+ *
+ * Revision 1.11  1993/12/05  04:15:16  shanzer
+ * removed data size hack.
+ *
+ * Revision 1.10  1993/11/15  00:29:24  shanzer
+ * added filenme to open
+ *
+ * Revision 1.9  1993/11/10  20:10:06  shanzer
+ * now uses xdralloc instead of xdrmem
+ *
+ * Revision 1.8  1993/11/09  21:43:24  shanzer
+ * added check to see if we overflowed our xdr buffer.
+ *
+ * Revision 1.7  1993/11/09  04:00:19  shanzer
+ * changed bzero to memset
+ *
+ * Revision 1.6  1993/11/05  23:16:21  shanzer
+ * return ENOMEM instead of ovsec_kadm_mem
+ *
+ * Revision 1.5  1993/11/05  22:17:03  shanzer
+ * added principal db interative function
+ *
+ * Revision 1.4  1993/11/04  23:20:24  shanzer
+ * made HASHINFO static.
+ *
+ * Revision 1.3  1993/11/04  01:52:30  shanzer
+ * Restructred some code .. fixed some bugs/leaks
+ *
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#if 0
+/* XXX THIS FILE IS NO LONGER USED, and should be deleted when we're done */
+
+#include       <sys/file.h>
+#include       <fcntl.h>
+#include       "adb.h"
+#include       <stdlib.h>
+#include       <memory.h>
+
+#define OPENLOCK(db, mode) \
+{ \
+       int ret; \
+           if (db == NULL) \
+                return EINVAL; \
+           else if (db->magic != OSA_ADB_PRINC_DB_MAGIC) \
+                return OSA_ADB_DBINIT; \
+           else if ((ret = osa_adb_open_and_lock(db, mode)) != OSA_ADB_OK) \
+                return ret; \
+           }
+
+#define CLOSELOCK(db) \
+{ \
+     int ret; \
+     if ((ret = osa_adb_close_and_unlock(db)) != OSA_ADB_OK) \
+         return ret; \
+}
+
+osa_adb_ret_t osa_adb_open_princ(osa_adb_princ_t *dbp, char *filename)
+{
+     return osa_adb_init_db(dbp, filename, OSA_ADB_PRINC_DB_MAGIC);
+}
+
+osa_adb_ret_t osa_adb_close_princ(osa_adb_princ_t db)
+{
+     return osa_adb_fini_db(db, OSA_ADB_PRINC_DB_MAGIC);
+}
+
+osa_adb_ret_t
+osa_adb_create_princ(osa_adb_princ_t db, osa_princ_ent_t entry)
+{
+
+    DBT                        dbkey;
+    DBT                        dbdata;
+    XDR                        xdrs;
+    int                        ret;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE);
+
+    if(krb5_unparse_name(db->lock->context,
+                        entry->name, (char **) &dbkey.data)) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+    if((dbkey.size = strlen(dbkey.data)) == 0) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+       
+    switch(db->db->get(db->db, &dbkey, &dbdata, 0)) {
+    case 0:
+        ret = OSA_ADB_DUP;
+        goto error;
+    case 1:
+       break;
+    default:
+        ret = OSA_ADB_FAILURE;
+        goto error;
+    }
+    xdralloc_create(&xdrs, XDR_ENCODE); 
+    if(!xdr_osa_princ_ent_rec(&xdrs, entry)) {
+       xdr_destroy(&xdrs);
+       ret = OSA_ADB_XDR_FAILURE;
+       goto error;
+    }
+    dbdata.data = xdralloc_getdata(&xdrs);
+    dbdata.size = xdr_getpos(&xdrs);
+    switch(db->db->put(db->db, &dbkey, &dbdata, R_NOOVERWRITE)) {
+    case 0:
+       if((db->db->sync(db->db, 0)) == -1)
+           ret =  OSA_ADB_FAILURE;
+       else
+            ret = OSA_ADB_OK;
+       break;
+    case 1:
+       ret = OSA_ADB_DUP;
+       break;
+    default:
+       ret = OSA_ADB_FAILURE;
+       break;
+    }
+    xdralloc_release(&xdrs);
+    free(dbkey.data);
+
+error:
+    CLOSELOCK(db);
+    
+    return ret;
+}
+       
+osa_adb_ret_t
+osa_adb_destroy_princ(osa_adb_princ_t db, ovsec_kadm_princ_t name)
+{
+    DBT            dbkey;
+    int            status;
+    int            ret;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE);
+
+    if(krb5_unparse_name(db->lock->context, name, (char **) &dbkey.data)) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+    if ((dbkey.size = strlen(dbkey.data)) == 0) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+    status = db->db->del(db->db, &dbkey, 0);
+    switch(status) {
+    case 1:
+       ret = OSA_ADB_NOENT;
+       break;
+    case 0:
+       if ((db->db->sync(db->db, 0)) == -1)
+           ret = OSA_ADB_FAILURE;
+       else 
+            ret = OSA_ADB_OK;
+       break;
+    default:
+       ret = OSA_ADB_FAILURE;
+       break;
+    }
+    free(dbkey.data);
+
+error:
+    CLOSELOCK(db);
+    
+    return ret;
+}
+
+osa_adb_ret_t
+osa_adb_get_princ(osa_adb_princ_t db, ovsec_kadm_princ_t name,
+                 osa_princ_ent_t *entry)
+{
+    DBT                        dbkey;
+    DBT                        dbdata;
+    XDR                        xdrs;
+    int                        ret = 0;
+    char               *aligned_data;
+
+    OPENLOCK(db, OSA_ADB_SHARED);
+
+    if(krb5_unparse_name(db->lock->context, name, (char **) &dbkey.data)) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+    if((dbkey.size = strlen(dbkey.data)) == 0) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+    dbdata.size = 0;
+    dbdata.data = NULL;
+    switch(db->db->get(db->db, &dbkey, &dbdata, 0)) {
+    case 1:
+       ret = OSA_ADB_NOENT;
+       break;
+    case 0:
+       break;
+    default:
+       ret = OSA_ADB_FAILURE;
+       break;
+    }
+    free(dbkey.data);
+    if (ret)
+        goto error;
+
+    if (!(*(entry) = (osa_princ_ent_t)malloc(sizeof(osa_princ_ent_rec)))) {
+        ret = ENOMEM;
+        goto error;
+    }
+
+    aligned_data = (char *) malloc(dbdata.size);
+    if (aligned_data == NULL) {
+        ret = ENOMEM;
+        goto error;
+    }
+    memcpy(aligned_data, dbdata.data, dbdata.size);
+    
+    memset(*entry, 0, sizeof(osa_princ_ent_rec));      
+    xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE);
+    if (!xdr_osa_princ_ent_rec(&xdrs, *entry)) {
+       xdr_destroy(&xdrs);
+       free(aligned_data);
+       ret = OSA_ADB_FAILURE;
+       goto error;
+    }
+    xdr_destroy(&xdrs);
+    free(aligned_data);
+    ret = OSA_ADB_OK;
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
+
+osa_adb_ret_t
+osa_adb_put_princ(osa_adb_princ_t db, osa_princ_ent_t entry)
+{
+    DBT                        dbkey;
+    DBT                        dbdata;
+    DBT                        tmpdb;
+    XDR                        xdrs;
+    int                        ret;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE);
+
+    if(krb5_unparse_name(db->lock->context,
+                        entry->name, (char **) &dbkey.data)) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+    if((dbkey.size = strlen(dbkey.data)) == 0) {
+        ret = OSA_ADB_BAD_PRINC;
+        goto error;
+    }
+       
+    switch(db->db->get(db->db, &dbkey, &tmpdb, 0)) {
+    case 0:
+       break;
+    case 1:
+       ret = OSA_ADB_NOENT;
+       goto error;
+    default:
+       ret = OSA_ADB_FAILURE;
+       goto error;
+    }
+    xdralloc_create(&xdrs, XDR_ENCODE);
+    if(!xdr_osa_princ_ent_rec(&xdrs, entry)) {
+       xdr_destroy(&xdrs);
+       ret =  OSA_ADB_XDR_FAILURE;
+       goto error;
+    }
+    dbdata.data = xdralloc_getdata(&xdrs);
+    dbdata.size = xdr_getpos(&xdrs);
+    switch(db->db->put(db->db, &dbkey, &dbdata, 0)) {
+    case 0:
+       if((db->db->sync(db->db, 0)) == -1)
+           ret =  OSA_ADB_FAILURE;
+       else 
+            ret =  OSA_ADB_OK;
+       break;
+    default:
+       ret = OSA_ADB_FAILURE;
+       break;
+    }
+    xdralloc_release(&xdrs);
+    free(dbkey.data);
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
+
+osa_adb_ret_t
+osa_adb_iter_princ(osa_adb_princ_t db, osa_adb_iter_princ_func func,
+                   void *data)
+{
+    DBT                            dbkey,
+                           dbdata;
+    XDR                            xdrs;
+    int                            ret;
+    osa_princ_ent_t        entry;
+    char                   *aligned_data;
+
+    OPENLOCK(db, OSA_ADB_EXCLUSIVE); /* hmmmm */
+    
+    if((ret = db->db->seq(db->db, &dbkey, &dbdata, R_FIRST)) == -1) {
+        ret = errno;
+        goto error;
+    }
+    while (ret == 0) {
+        if (!(entry = (osa_princ_ent_t) malloc(sizeof(osa_princ_ent_rec)))) {
+             ret = ENOMEM;
+             goto error;
+        }
+
+       aligned_data = (char *) malloc(dbdata.size);
+        if (aligned_data == NULL) {
+             ret = ENOMEM;
+             goto error;
+        }
+       memcpy(aligned_data, dbdata.data, dbdata.size);
+
+       memset(entry, 0, sizeof(osa_princ_ent_rec));
+       xdrmem_create(&xdrs, aligned_data, dbdata.size, XDR_DECODE);
+       if(!xdr_osa_princ_ent_rec(&xdrs, entry)) {
+           xdr_destroy(&xdrs);
+           free(aligned_data);
+           ret = OSA_ADB_FAILURE;
+           goto error;
+       }
+       (*func)(data, entry);
+       xdr_destroy(&xdrs);
+       free(aligned_data);
+       osa_free_princ_ent(entry);
+       ret = db->db->seq(db->db, &dbkey, &dbdata, R_NEXT);
+    }
+    if(ret == -1)
+        ret = errno;
+    else
+        ret = OSA_ADB_OK;
+
+error:
+    CLOSELOCK(db);
+    return ret;
+}
+
+#endif /* 0 */
diff --git a/src/lib/kadm5/srv/adb_xdr.c b/src/lib/kadm5/srv/adb_xdr.c
new file mode 100644 (file)
index 0000000..944fb04
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <sys/types.h>
+#include <krb5.h>
+#include <rpc/rpc.h>
+#include "adb.h"
+#include "admin_xdr.h"
+#include <memory.h>
+
+bool_t
+xdr_krb5_key_data(XDR *xdrs, krb5_key_data *objp)
+{
+    unsigned int tmp;
+
+    if (!xdr_krb5_int16(xdrs, &objp->key_data_ver))
+       return(FALSE);
+    if (!xdr_krb5_int16(xdrs, &objp->key_data_kvno))
+       return(FALSE);
+    if (!xdr_krb5_int16(xdrs, &objp->key_data_type[0]))
+       return(FALSE);
+    if (!xdr_krb5_int16(xdrs, &objp->key_data_type[1]))
+       return(FALSE);
+    if (!xdr_krb5_int16(xdrs, &objp->key_data_length[0]))
+       return(FALSE);
+    if (!xdr_krb5_int16(xdrs, &objp->key_data_length[1]))
+       return(FALSE);
+
+    tmp = (unsigned int) objp->key_data_length[0];
+    if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[0],
+                  &tmp, ~0))
+       return FALSE;
+
+    tmp = (unsigned int) objp->key_data_length[1];
+    if (!xdr_bytes(xdrs, (char **) &objp->key_data_contents[1],
+                  &tmp, ~0))
+       return FALSE;
+
+    /* don't need to copy tmp out, since key_data_length will be set
+       by the above encoding. */
+
+    return(TRUE);
+}
+
+bool_t
+xdr_osa_pw_hist_ent(XDR *xdrs, osa_pw_hist_ent *objp)
+{
+    if (!xdr_array(xdrs, (caddr_t *) &objp->key_data,
+                  (u_int *) &objp->n_key_data, ~0,
+                  sizeof(krb5_key_data),
+                  xdr_krb5_key_data))
+       return (FALSE);
+    return (TRUE);
+}
+
+bool_t
+xdr_osa_princ_ent_rec(XDR *xdrs, osa_princ_ent_t objp)
+{
+    switch (xdrs->x_op) {
+    case XDR_ENCODE:
+        objp->version = OSA_ADB_PRINC_VERSION_1;
+        /* fall through */
+    case XDR_FREE:
+        if (!xdr_int(xdrs, &objp->version))
+             return FALSE;
+        break;
+    case XDR_DECODE:
+        if (!xdr_int(xdrs, &objp->version))
+             return FALSE;
+        if (objp->version != OSA_ADB_PRINC_VERSION_1)
+             return FALSE;
+        break;
+    }
+    
+    if (!xdr_nullstring(xdrs, &objp->policy))
+       return (FALSE);
+    if (!xdr_long(xdrs, &objp->aux_attributes))
+       return (FALSE);
+    if (!xdr_u_int(xdrs, &objp->old_key_next))
+       return (FALSE);
+    if (!xdr_krb5_kvno(xdrs, &objp->admin_history_kvno))
+       return (FALSE);
+    if (!xdr_array(xdrs, (caddr_t *) &objp->old_keys,
+                  (unsigned int *) &objp->old_key_len, ~0,
+                  sizeof(osa_pw_hist_ent),
+                  xdr_osa_pw_hist_ent))
+       return (FALSE);
+    return (TRUE);
+}
+
+bool_t
+xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp)
+{
+    switch (xdrs->x_op) {
+    case XDR_ENCODE:
+        objp->version = OSA_ADB_POLICY_VERSION_1;
+        /* fall through */
+    case XDR_FREE:
+        if (!xdr_int(xdrs, &objp->version))
+             return FALSE;
+        break;
+    case XDR_DECODE:
+        if (!xdr_int(xdrs, &objp->version))
+             return FALSE;
+        if (objp->version != OSA_ADB_POLICY_VERSION_1)
+             return FALSE;
+        break;
+    }
+    
+    if(!xdr_nullstring(xdrs, &objp->name))
+       return (FALSE);
+    if (!xdr_u_int32(xdrs, &objp->pw_min_life))
+       return (FALSE);
+    if (!xdr_u_int32(xdrs, &objp->pw_max_life))
+       return (FALSE);
+    if (!xdr_u_int32(xdrs, &objp->pw_min_length))
+       return (FALSE);
+    if (!xdr_u_int32(xdrs, &objp->pw_min_classes))
+       return (FALSE);
+    if (!xdr_u_int32(xdrs, &objp->pw_history_num))
+       return (FALSE);
+    if (!xdr_u_int32(xdrs, &objp->policy_refcnt))
+       return (FALSE);
+    return (TRUE);
+}
diff --git a/src/lib/kadm5/srv/configure.in b/src/lib/kadm5/srv/configure.in
new file mode 100644 (file)
index 0000000..f17fd5c
--- /dev/null
@@ -0,0 +1,42 @@
+AC_INIT(server_kdb.c)
+CONFIG_RULES
+AC_PROG_ARCHIVE
+AC_PROG_ARCHIVE_ADD
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+AC_PROG_LEX
+AC_PROG_AWK
+
+save_LIBS="$LIBS"
+LIBS=-lgen
+AC_CHECK_FUNCS(compile step)
+if test "$ac_cv_func_compile" = true ; then
+       LIBS="$save_LIBS -lgen"
+else
+       LIBS="$save_LIBS"
+fi
+
+AC_CHECK_FUNCS(re_comp re_exec regcomp regexec)
+
+V5_SHARED_LIB_OBJS
+V5_MAKE_SHARED_LIB(libkadm5srv, 1.0, ../.., ./kadm5/srv)
+
+GSSRPC_SH_VERS=$krb5_cv_shlib_version_libgssrpc
+AC_SUBST(GSSRPC_SH_VERS)
+GSSAPI_KRB5_SH_VERS=$krb5_cv_shlib_version_libgssapi_krb5
+AC_SUBST(GSSAPI_KRB5_SH_VERS)
+KDB5_SH_VERS=$krb5_cv_shlib_version_libkdb5
+AC_SUBST(KDB5_SH_VERS)
+KRB5_SH_VERS=$krb5_cv_shlib_version_libkrb5
+AC_SUBST(KRB5_SH_VERS)
+CRYPTO_SH_VERS=$krb5_cv_shlib_version_libcrypto
+AC_SUBST(CRYPTO_SH_VERS)
+COMERR_SH_VERS=$krb5_cv_shlib_version_libcom_err
+AC_SUBST(COMERR_SH_VERS)
+DYN_SH_VERS=$krb5_cv_shlib_version_libdyn
+AC_SUBST(DYN_SH_VERS)
+
+SubdirLibraryRule([$(OBJS)])
+
+CopySrcHeader(server_acl.h,[$](BUILDTOP)/include/kadm5)
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/lib/kadm5/srv/server_acl.c b/src/lib/kadm5/srv/server_acl.c
new file mode 100644 (file)
index 0000000..16a7f4e
--- /dev/null
@@ -0,0 +1,511 @@
+/*
+ * kadmin/v5server/srv_acl.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * srv_acl.c - Handle Kerberos ACL related functions.
+ */
+#include <stdio.h>
+#include <sys/param.h>
+#include <gssapi/gssapi_generic.h>
+#include "k5-int.h"
+#include "server_acl.h"
+#include <kadm5/server_internal.h>
+
+typedef struct _acl_op_table {
+    char       ao_op;
+    krb5_int32 ao_mask;
+} aop_t;
+
+typedef struct _acl_entry {
+    struct _acl_entry  *ae_next;
+    char               *ae_name;
+    krb5_boolean       ae_name_bad;
+    krb5_principal     ae_principal;
+    krb5_int32         ae_op_allowed;
+    char               *ae_target;
+    krb5_boolean       ae_target_bad;
+    krb5_principal     ae_target_princ;
+} aent_t;
+
+static const aop_t acl_op_table[] = {
+    { 'a',     ACL_ADD },
+    { 'd',     ACL_DELETE },
+    { 'm',     ACL_MODIFY },
+    { 'c',     ACL_CHANGEPW },
+    { 'i',     ACL_INQUIRE },
+    { 'l',     ACL_LIST },
+    { 'x',     ACL_ALL_MASK },
+    { '*',     ACL_ALL_MASK },
+    { '\0',    0 }
+};
+
+static aent_t  *acl_list_head = (aent_t *) NULL;
+static aent_t  *acl_list_tail = (aent_t *) NULL;
+
+static const char *acl_acl_file = (char *) NULL;
+static int acl_inited = 0;
+static int acl_debug_level = 0;
+/*
+ * This is the catchall entry.  If nothing else appropriate is found, or in
+ * the case where the ACL file is not present, this entry controls what can
+ * be done.
+ */
+static const char *acl_catchall_entry = NULL;
+
+static const char *acl_line2long_msg = "%s: line %d too long, truncated\n";
+static const char *acl_op_bad_msg = "Unrecognized ACL operation '%c' in %s\n";
+static const char *acl_syn_err_msg = "%s: syntax error at line %d <%10s...>\n";
+static const char *acl_cantopen_msg = "\007cannot open ACL file";
+\f
+/*
+ * acl_get_line()      - Get a line from the ACL file.
+ */
+static char *
+acl_get_line(fp, lnp)
+    FILE       *fp;
+    int                *lnp;
+{
+    int                i, domore;
+    static char acl_buf[BUFSIZ];
+
+    for (domore = 1; domore && !feof(fp); ) {
+       /* Copy in the line */
+       for (i=0;
+            ((i<BUFSIZ) &&
+             (!feof(fp)) &&
+             ((acl_buf[i] = fgetc(fp)) != '\n'));
+            i++);
+
+       /* Check if we exceeded our buffer size */
+       if ((i == BUFSIZ) && (!feof(fp)) && (acl_buf[i] != '\n')) {
+           fprintf(stderr, acl_line2long_msg, acl_acl_file, *lnp);
+           while (fgetc(fp) != '\n');
+       }
+               acl_buf[i] = '\0';
+       if (acl_buf[0] == (char) EOF)   /* ptooey */
+           acl_buf[0] = '\0';
+       else
+           (*lnp)++;
+       if ((acl_buf[0] != '#') && (acl_buf[0] != '\0'))
+           domore = 0;
+    }
+    if (domore || (strlen(acl_buf) == 0))
+       return((char *) NULL);
+    else
+       return(acl_buf);
+}
+\f
+/*
+ * acl_parse_line()    - Parse the contents of an ACL line.
+ */
+static aent_t *
+acl_parse_line(lp)
+    char *lp;
+{
+    static char acle_principal[BUFSIZ];
+    static char acle_ops[BUFSIZ];
+    static char acle_object[BUFSIZ];
+    aent_t     *acle;
+    char       *op;
+    int                t, found, opok, nmatch;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("* acl_parse_line(line=%20s)\n", lp));
+    /*
+     * Format is very simple:
+     * entry ::= <whitespace> <principal> <whitespace> <opstring> <whitespace>
+     *           [<target> <whitespace>]
+     */
+    acle = (aent_t *) NULL;
+    acle_object[0] = '\0';
+    nmatch = sscanf(lp, "%s %s %s", acle_principal, acle_ops, acle_object);
+    if (nmatch >= 2) {
+       acle = (aent_t *) malloc(sizeof(aent_t));
+       if (acle) {
+           acle->ae_next = (aent_t *) NULL;
+           acle->ae_op_allowed = (krb5_int32) 0;
+           acle->ae_target =
+               (nmatch >= 3) ? strdup(acle_object) : (char *) NULL;
+           acle->ae_target_bad = 0;
+           acle->ae_target_princ = (krb5_principal) NULL;
+           opok = 1;
+           for (op=acle_ops; *op; op++) {
+               char rop;
+
+               rop = (isupper(*op)) ? tolower(*op) : *op;
+               found = 0;
+               for (t=0; acl_op_table[t].ao_op; t++) {
+                   if (rop == acl_op_table[t].ao_op) {
+                       found = 1;
+                       if (rop == *op)
+                           acle->ae_op_allowed |= acl_op_table[t].ao_mask;
+                       else
+                           acle->ae_op_allowed &= ~acl_op_table[t].ao_mask;
+                   }
+               }
+               if (!found) {
+                   fprintf(stderr, acl_op_bad_msg, *op, lp);
+                   opok = 0;
+               }
+           }
+           if (opok) {
+               acle->ae_name = (char *) malloc(strlen(acle_principal)+1);
+               if (acle->ae_name) {
+                   strcpy(acle->ae_name, acle_principal);
+                   acle->ae_principal = (krb5_principal) NULL;
+                   acle->ae_name_bad = 0;
+                   DPRINT(DEBUG_ACL, acl_debug_level,
+                          ("A ACL entry %s -> opmask %x\n",
+                           acle->ae_name, acle->ae_op_allowed));
+               }
+               else {
+                   if (acle->ae_target)
+                       free(acle->ae_target);
+                   free(acle);
+                   acle = (aent_t *) NULL;
+               }
+           }
+           else {
+               if (acle->ae_target)
+                   free(acle->ae_target);
+               free(acle);
+               acle = (aent_t *) NULL;
+           }
+       }
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("X acl_parse_line() = %x\n", (long) acle));
+    return(acle);
+}
+\f
+/*
+ * acl_free_entries()  - Free all ACL entries.
+ */
+static void
+acl_free_entries()
+{
+    aent_t     *ap;
+    aent_t     *np;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_free_entries()\n"));
+    for (ap=acl_list_head; ap; ap = np) {
+       if (ap->ae_name)
+           free(ap->ae_name);
+       if (ap->ae_principal)
+           krb5_free_principal((krb5_context) NULL, ap->ae_principal);
+       if (ap->ae_target)
+           free(ap->ae_target);
+       if (ap->ae_target_princ)
+           krb5_free_principal((krb5_context) NULL, ap->ae_target_princ);
+       np = ap->ae_next;
+       free(ap);
+    }
+    acl_list_head = acl_list_tail = (aent_t *) NULL;
+    acl_inited = 0;
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_free_entries()\n"));
+}
+\f
+/*
+ * acl_load_acl_file() - Open and parse the ACL file.
+ */
+static int
+acl_load_acl_file()
+{
+char tmpbuf[10];
+    FILE       *afp;
+    char       *alinep;
+    aent_t     **aentpp;
+    int                alineno;
+    int                retval = 1;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_load_acl_file()\n"));
+    /* Open the ACL file for read */
+    if (afp = fopen(acl_acl_file, "r")) {
+       alineno = 1;
+       aentpp = &acl_list_head;
+
+       /* Get a non-comment line */
+       while (alinep = acl_get_line(afp, &alineno)) {
+           /* Parse it */
+           *aentpp = acl_parse_line(alinep);
+           /* If syntax error, then fall out */
+           if (!*aentpp) {
+               fprintf(stderr, acl_syn_err_msg,
+                       acl_acl_file, alineno, alinep);
+               retval = 0;
+               break;
+           }
+           acl_list_tail = *aentpp;
+           aentpp = &(*aentpp)->ae_next;
+       }
+
+       if (acl_catchall_entry) {
+            strcpy(tmpbuf, acl_catchall_entry);
+            if (*aentpp = acl_parse_line(tmpbuf)) {
+                 acl_list_tail = *aentpp;
+            }
+            else {
+                 retval = 0;
+                 DPRINT(DEBUG_OPERATION, acl_debug_level,
+                        ("> catchall acl entry (%s) load failed\n",
+                         acl_catchall_entry));
+            }
+            fclose(afp);
+       }
+    }
+    else {
+       com_err(acl_acl_file, errno, acl_cantopen_msg);
+       if (acl_list_head = acl_parse_line(acl_catchall_entry)) {
+           acl_list_tail = acl_list_head;
+       }
+       else {
+           retval = 0;
+           DPRINT(DEBUG_OPERATION, acl_debug_level,
+                  ("> catchall acl entry (%s) load failed\n",
+                   acl_catchall_entry));
+       }
+    }
+
+    if (!retval) {
+       acl_free_entries();
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("X acl_load_acl_file() = %d\n", retval));
+    return(retval);
+}
+\f
+/*
+ * acl_match_data()    - See if two data entries match.
+ *
+ * Wildcarding is only supported for a whole component.
+ */
+static krb5_boolean
+acl_match_data(e1, e2)
+    krb5_data  *e1, *e2;
+{
+    krb5_boolean       retval;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, 
+          ("* acl_match_entry(%s, %s)\n", e1->data, e2->data));
+    retval = 0;
+    if (!strncmp(e1->data, "*", e1->length) ||
+       !strncmp(e2->data, "*", e2->length)) {
+       retval = 1;
+    }
+    else {
+       if ((e1->length == e2->length) &&
+           (!strncmp(e1->data, e2->data, e1->length)))
+           retval = 1;
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval));
+    return(retval);
+}
+\f
+/*
+ * acl_find_entry()    - Find a matching entry.
+ */
+static aent_t *
+acl_find_entry(kcontext, principal, dest_princ)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_principal     dest_princ;
+{
+    aent_t             *entry;
+    krb5_error_code    kret;
+    int                        i;
+    int                        matchgood;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_find_entry()\n"));
+    for (entry=acl_list_head; entry; entry = entry->ae_next) {
+       if (!strcmp(entry->ae_name, "*")) {
+           DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
+           break;
+       }
+       if (!entry->ae_principal && !entry->ae_name_bad) {
+           kret = krb5_parse_name(kcontext,
+                                  entry->ae_name,
+                                  &entry->ae_principal);
+           if (kret)
+               entry->ae_name_bad = 1;
+       }
+       if (entry->ae_name_bad) {
+           DPRINT(DEBUG_ACL, acl_debug_level,
+                  ("A Bad ACL entry %s\n", entry->ae_name));
+           continue;
+       }
+       if (entry->ae_target &&
+           !entry->ae_target_princ &&
+           !entry->ae_target_bad) {
+           kret = krb5_parse_name(kcontext,
+                                  entry->ae_target,
+                                  &entry->ae_target_princ);
+           if (kret)
+               entry->ae_target_bad = 1;
+       }
+       if (entry->ae_target_bad) {
+           DPRINT(DEBUG_ACL, acl_debug_level,
+                  ("A Bad target in an ACL entry for %s\n", entry->ae_name));
+           entry->ae_name_bad = 1;
+           continue;
+       }
+       matchgood = 0;
+       if (acl_match_data(&entry->ae_principal->realm,
+                          &principal->realm) &&
+           (entry->ae_principal->length == principal->length)) {
+           matchgood = 1;
+           for (i=0; i<principal->length; i++) {
+               if (!acl_match_data(&entry->ae_principal->data[i],
+                                   &principal->data[i])) {
+                   matchgood = 0;
+                   break;
+               }
+           }
+       }
+       if (!matchgood)
+           continue;
+
+       /* We've matched the principal.  If we have a target, then try it */
+       if (entry->ae_target && entry->ae_target_princ && dest_princ) {
+           if (acl_match_data(&entry->ae_target_princ->realm,
+                              &dest_princ->realm) &&
+               (entry->ae_target_princ->length == dest_princ->length)) {
+              for (i=0; i<dest_princ->length; i++) {
+                 if (!acl_match_data(&entry->ae_target_princ->data[i],
+                                     &dest_princ->data[i])) {
+                    matchgood = 0;
+                    break;
+                 }
+              }
+           }
+           else
+              matchgood = 0;
+       }
+
+       if (matchgood)
+           break;
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_find_entry()=%x\n",entry));
+    return(entry);
+}
+\f
+/*
+ * acl_init()  - Initialize ACL context.
+ */
+krb5_error_code
+acl_init(kcontext, debug_level, acl_file)
+    krb5_context       kcontext;
+    int                        debug_level;
+    char               *acl_file;
+{
+    krb5_error_code    kret;
+
+    kret = 0;
+    acl_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("* acl_init(afile=%s)\n",
+           ((acl_file) ? acl_file : "(null)")));
+    acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL;
+    acl_inited = acl_load_acl_file();
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_init() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * acl_finish  - Terminate ACL context.
+ */
+void
+acl_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_finish()\n"));
+    acl_free_entries();
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_finish()\n"));
+}
+\f
+/*
+ * acl_op_permitted()  - Is this operation permitted for this principal?
+ *                     this code used not to be based on gssapi.  In order
+ *                     to minimize porting hassles, I've put all the
+ *                     gssapi hair in this function.  This might not be
+ *                     the best medium-term solution.  (The best long-term
+ *                     solution is, of course, a real authorization service.)
+ */
+krb5_boolean
+acl_check(kcontext, caller, opmask, principal)
+    krb5_context       kcontext;
+    gss_name_t         caller;
+    krb5_int32         opmask;
+    krb5_principal     principal;
+{
+    krb5_boolean       retval;
+    aent_t             *aentry;
+    gss_buffer_desc    caller_buf;
+    gss_OID            caller_oid;
+    OM_uint32          emaj, emin;
+    krb5_error_code    code;
+    krb5_principal     caller_princ;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n"));
+
+    if (GSS_ERROR(emaj = gss_display_name(&emin, caller, &caller_buf,
+                                         &caller_oid)))
+       return(0);
+
+    code = krb5_parse_name(kcontext, (char *) caller_buf.value,
+                          &caller_princ);
+
+    gss_release_buffer(&emin, &caller_buf);
+
+    if (code)
+       return(code);
+
+    retval = 0;
+    if (aentry = acl_find_entry(kcontext, caller_princ, principal)) {
+       if ((aentry->ae_op_allowed & opmask) == opmask)
+           retval = 1;
+    }
+
+    krb5_free_principal(kcontext, caller_princ);
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n",
+                                         retval));
+    return(retval);
+}
+
+kadm5_ret_t kadm5_get_privs(void *server_handle, long *privs)
+{
+     kadm5_server_handle_t handle = server_handle;
+
+     CHECK_HANDLE(server_handle);
+
+     /* this is impossible to do with the current interface.  For now,
+       return all privs, which will confuse some clients, but not
+       deny any access to users of "smart" clients which try to cache */
+
+     *privs = ~0;
+
+     return KADM5_OK;
+}
diff --git a/src/lib/kadm5/srv/server_acl.h b/src/lib/kadm5/srv/server_acl.h
new file mode 100644 (file)
index 0000000..9dfc8da
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * kadmin/v5server/kadm5_defs.h
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#ifndef        SERVER_ACL_H__
+#define        SERVER_ACL_H__
+
+/*
+ * Debug definitions.
+ */
+#define        DEBUG_SPROC     1
+#define        DEBUG_OPERATION 2
+#define        DEBUG_HOST      4
+#define        DEBUG_REALM     8
+#define        DEBUG_REQUESTS  16
+#define        DEBUG_ACL       32
+#define        DEBUG_PROTO     64
+#define        DEBUG_CALLS     128
+#define        DEBUG_NOSLAVES  256
+#ifdef DEBUG
+#define        DPRINT(l1, cl, al)      if ((cl & l1) != 0) xprintf al
+#else  /* DEBUG */
+#define        DPRINT(l1, cl, al)
+#endif /* DEBUG */
+#define        DLOG(l1, cl, msg)       if ((cl & l1) != 0)     \
+                                       com_err(programname, 0, msg)
+
+/*
+ * Access control bits.
+ */
+#define        ACL_ADD                 1
+#define        ACL_DELETE              2
+#define        ACL_MODIFY              4
+#define        ACL_CHANGEPW            8
+/* #define ACL_CHANGE_OWN_PW   16 */
+#define        ACL_INQUIRE             32
+/* #define ACL_EXTRACT         64 */
+#define        ACL_LIST                128
+#define        ACL_RENAME              (ACL_ADD+ACL_DELETE)
+
+#define        ACL_ALL_MASK            (ACL_ADD        | \
+                                ACL_DELETE     | \
+                                ACL_MODIFY     | \
+                                ACL_CHANGEPW   | \
+                                ACL_INQUIRE    | \
+                                ACL_LIST)
+
+krb5_error_code acl_init
+       KRB5_PROTOTYPE((krb5_context,
+                  int,
+                  char *));
+void acl_finish
+       KRB5_PROTOTYPE((krb5_context,
+                  int));
+krb5_boolean acl_check
+       KRB5_PROTOTYPE((krb5_context,
+                  gss_name_t,
+                  krb5_int32,
+                  krb5_principal));
+
+#endif /* SERVER_ACL_H__ */
diff --git a/src/lib/kadm5/srv/server_dict.c b/src/lib/kadm5/srv/server_dict.c
new file mode 100644 (file)
index 0000000..6c0bcef
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include    <sys/types.h>
+#include    <sys/file.h>
+#include    <fcntl.h>
+#include    <sys/stat.h>
+#include    <unistd.h>
+#include    <kadm5/admin.h>
+#include    <stdlib.h>
+#include    <stdio.h>
+#include    <string.h>
+#include    <malloc.h>
+#include    <memory.h>
+#include    <syslog.h>
+#include    "server_internal.h"
+
+static char        **word_list = NULL;     /* list of word pointers */
+static char        *word_block = NULL;     /* actual word data */
+static int         word_count = 0;         /* number of words */
+extern int         errno;
+
+/*
+ * Function: word_compare
+ * 
+ * Purpose: compare two words in the dictionary.
+ *
+ * Arguments:
+ *     w1              (input) pointer to first word
+ *     w2              (input) pointer to second word
+ *     <return value>  result of strcmp
+ *
+ * Requires:
+ *     w1 and w2 to point to valid memory
+ * 
+ */
+
+static int
+word_compare(const void *s1, const void *s2)
+{
+    return (strcasecmp(*(char **)s1, *(char **)s2));
+}
+
+/*
+ * Function: init-dict
+ * 
+ * Purpose: Initialize in memory word dictionary
+ *
+ * Arguments:
+ *         none
+ *         <return value> KADM5_OK on sucsess errno on failure;
+ *                        (but success on ENOENT)
+ *
+ * Requires:
+ *     If WORDFILE exists, it must contain a list of words,
+ *     one word per-line.
+ * 
+ * Effects:
+ *     If WORDFILE exists, it is read into memory sorted for future
+ * use.  If it does not exist, it syslogs an error message and returns
+ * success.
+ *
+ * Modifies:
+ *     word_list to point to a chunck of allocated memory containing
+ *     pointers to words
+ *     word_block to contain the dictionary.
+ * 
+ */
+
+int init_dict(kadm5_config_params *params)
+{
+    int                    fd,
+                   len,
+                   i;
+    char           *p,
+                   *t;
+    struct  stat    sb;
+    
+    if(word_list != NULL && word_block != NULL)
+       return KADM5_OK;
+    if (! (params->mask & KADM5_CONFIG_DICT_FILE)) {
+        syslog(LOG_INFO, "No dictionary file specified, continuing "
+               "without one.");
+        return KADM5_OK;
+    }
+    if ((fd = open(params->dict_file, O_RDONLY)) == -1) {
+        if (errno == ENOENT) {
+             syslog(LOG_ERR, "WARNING!  Cannot find dictionary file %s, "
+                    "continuing without one.", params->dict_file);
+             return KADM5_OK;
+        } else
+             return errno;
+    }
+    if (fstat(fd, &sb) == -1) 
+       return errno;
+    if ((word_block = (char *) malloc(sb.st_size + 1)) == NULL)
+       return errno;
+    if (read(fd, word_block, sb.st_size) != sb.st_size)
+       return errno;
+    (void) close(fd);
+    word_block[sb.st_size] = '\0';
+
+    p = word_block;
+    len = sb.st_size;
+    while(len > 0 && (t = memchr(p, '\n', len)) != NULL) {
+       *t = '\0';
+       len -= t - p + 1;       
+       p = t + 1;
+       word_count++;
+    }
+    if ((word_list = (char **) malloc(word_count * sizeof(char *))) == NULL)
+       return errno;
+    p = word_block;
+    for (i = 0; i < word_count; i++) {
+       word_list[i] = p;
+       p += strlen(p) + 1;
+    }
+    qsort(word_list, word_count, sizeof(char *), word_compare);
+    return KADM5_OK;
+}
+
+/*
+ * Function: find_word
+ * 
+ * Purpose: See if the specified word exists in the in-core dictionary
+ *
+ * Arguments:
+ *     word            (input) word to search for.
+ *     <return value>  WORD_NOT_FOUND if not in dictionary,
+ *                     KADM5_OK if if found word
+ *                     errno if init needs to be called and returns an
+ *                     error
+ *
+ * Requires:
+ *     word to be a null terminated string.
+ *     That word_list and word_block besetup
+ * 
+ * Effects:
+ *     finds word in dictionary.
+ * Modifies:
+ *     nothing.
+ * 
+ */
+
+int
+find_word(const char *word)
+{
+    char    **value;
+
+    if(word_list == NULL || word_block == NULL) 
+           return WORD_NOT_FOUND;
+    if ((value = (char **) bsearch(&word, word_list, word_count, sizeof(char *),
+                                  word_compare)) == NULL)
+       return WORD_NOT_FOUND;
+    else
+       return KADM5_OK;
+}
+
+/*
+ * Function: destroy_dict
+ * 
+ * Purpose: destroy in-core copy of dictionary.
+ *
+ * Arguments:
+ *         none
+ *         <return value>  none
+ * Requires:
+ *         nothing
+ * Effects:
+ *     frees up memory occupied by word_list and word_block
+ *     sets count back to 0, and resets the pointers to NULL
+ *
+ * Modifies:
+ *     word_list, word_block, and word_count.
+ * 
+ */
+
+void
+destroy_dict(void)
+{
+    if(word_list) {
+       free(word_list);
+       word_list = NULL;
+    }
+    if(word_block) {
+       free(word_block);
+       word_block = NULL;
+    }
+    if(word_count)
+       word_count = 0;
+    return;
+}
diff --git a/src/lib/kadm5/srv/server_handle.c b/src/lib/kadm5/srv/server_handle.c
new file mode 100644 (file)
index 0000000..53abe94
--- /dev/null
@@ -0,0 +1,9 @@
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include "server_internal.h"
+
+int _kadm5_check_handle(void *handle)
+{
+     CHECK_HANDLE(handle);
+     return 0;
+}
diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c
new file mode 100644 (file)
index 0000000..653f6d8
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <com_err.h>
+#include <kadm5/admin.h>
+#include <krb5.h>
+#include "server_internal.h"
+
+/*
+ * Function check_handle
+ *
+ * Purpose: Check a server handle and return a com_err code if it is
+ * invalid or 0 if it is valid.
+ *
+ * Arguments:
+ *
+ *     handle          The server handle.
+ */
+
+static int check_handle(void *handle)
+{
+     CHECK_HANDLE(handle);
+     return 0;
+}
+
+kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass,
+                                    char *service_name,
+                                    kadm5_config_params *params,
+                                    krb5_ui_4 struct_version,
+                                    krb5_ui_4 api_version,
+                                    void **server_handle)
+{
+     return kadm5_init(client_name, pass, service_name, params,
+                      struct_version, api_version,
+                      server_handle);
+}
+
+kadm5_ret_t kadm5_init_with_creds(char *client_name,
+                                 krb5_ccache ccache,
+                                 char *service_name,
+                                 kadm5_config_params *params,
+                                 krb5_ui_4 struct_version,
+                                 krb5_ui_4 api_version,
+                                 void **server_handle)
+{
+     /*
+      * A program calling init_with_creds *never* expects to prompt the
+      * user.  Therefore, always pass a dummy password in case this is
+      * KADM5_API_VERSION_1.  If this is KADM5_API_VERSION_2 and
+      * MKEY_FROM_KBD is non-zero, return an error.
+      */
+     if (api_version == KADM5_API_VERSION_2 && params &&
+        (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) &&
+        params->mkey_from_kbd)
+         return KADM5_BAD_SERVER_PARAMS;
+     return kadm5_init(client_name, NULL, service_name, params,
+                      struct_version, api_version,
+                      server_handle);
+}
+
+
+kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab,
+                                char *service_name,
+                                kadm5_config_params *params,
+                                krb5_ui_4 struct_version,
+                                krb5_ui_4 api_version,
+                                void **server_handle)
+{
+     /*
+      * A program calling init_with_skey *never* expects to prompt the
+      * user.  Therefore, always pass a dummy password in case this is
+      * KADM5_API_VERSION_1.  If this is KADM5_API_VERSION_2 and
+      * MKEY_FROM_KBD is non-zero, return an error.
+      */
+     if (api_version == KADM5_API_VERSION_2 && params &&
+        (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) &&
+        params->mkey_from_kbd)
+         return KADM5_BAD_SERVER_PARAMS;
+     return kadm5_init(client_name, NULL, service_name, params,
+                      struct_version, api_version,
+                      server_handle);
+}
+
+kadm5_ret_t kadm5_init(char *client_name, char *pass,
+                      char *service_name,
+                      kadm5_config_params *params_in,
+                      krb5_ui_4 struct_version,
+                      krb5_ui_4 api_version,
+                      void **server_handle)
+{
+     int ret;
+     kadm5_server_handle_t handle;
+     kadm5_config_params params_local; /* for v1 compat */
+
+    if (! server_handle)
+        return EINVAL;
+
+    if (! client_name)
+        return EINVAL;
+    
+    if (! (handle = (kadm5_server_handle_t) malloc(sizeof *handle)))
+        return ENOMEM;
+    memset(handle, 0, sizeof(*handle));
+
+    if (ret = (int) krb5_init_context(&(handle->context))) {
+        free(handle);
+        return(ret);
+    }
+     
+    initialize_ovk_error_table();
+    initialize_adb_error_table();
+    initialize_ovku_error_table();
+    krb5_init_ets(handle->context);
+
+    handle->magic_number = KADM5_SERVER_HANDLE_MAGIC;
+    handle->struct_version = struct_version;
+    handle->api_version = api_version;
+
+     /*
+      * Verify the version numbers before proceeding; we can't use
+      * CHECK_HANDLE because not all fields are set yet.
+      */
+     GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION,
+                         KADM5_NEW_SERVER_API_VERSION);
+     
+     /*
+      * Acquire relevant profile entries.  In version 2, merge values
+      * in params_in with values from profile, based on
+      * params_in->mask.
+      *
+      * In version 1, we've given a realm (which may be NULL) instead
+      * of params_in.  So use that realm, make params_in contain an
+      * empty mask, and behave like version 2.
+      */
+     memset((char *) &params_local, 0, sizeof(params_local));
+     if (api_version == KADM5_API_VERSION_1) {
+         params_local.realm = (char *) params_in;
+         if (params_in)
+              params_local.mask = KADM5_CONFIG_REALM;
+         params_in = &params_local;
+     }
+
+#define ILLEGAL_PARAMS (KADM5_CONFIG_ADMIN_SERVER)
+     if (params_in && (params_in->mask & ILLEGAL_PARAMS)) {
+         krb5_free_context(handle->context);
+         free(handle);
+         return KADM5_BAD_SERVER_PARAMS;
+     }
+
+     if (ret = kadm5_get_config_params(handle->context,
+                                      (char *) NULL,
+                                      (char *) NULL,
+                                      params_in,
+                                      &handle->params)) {
+         krb5_free_context(handle->context);
+         free(handle);
+         return(ret);
+     }
+
+#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_DBNAME | \
+                        KADM5_CONFIG_ADBNAME | \
+                        KADM5_CONFIG_ADB_LOCKFILE | \
+                        KADM5_CONFIG_ENCTYPE | \
+                        KADM5_CONFIG_FLAGS | \
+                        KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \
+                        KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES) 
+
+     if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
+         krb5_free_context(handle->context);
+         free(handle);
+         return KRB5_CONFIG_BADFORMAT;
+     }
+
+    /*
+     * Set the db_name based on configuration before calling
+     * krb5_db_init, so it will get used.
+     */
+    if (ret = krb5_dbm_db_set_name(handle->context,
+                                  handle->params.dbname)) {
+        free(handle);
+        return(ret);
+    }
+
+    if (ret = krb5_db_init(handle->context)) {
+        krb5_free_context(handle->context);
+        free(handle);
+        return(ret);
+    }
+
+    if ((ret = krb5_parse_name(handle->context, client_name,
+                              &handle->current_caller))) {
+        krb5_db_fini(handle->context);
+        krb5_free_context(handle->context);
+        free(handle);
+        return ret;
+    }
+
+     if (! (handle->lhandle = malloc(sizeof(*handle)))) {
+         krb5_db_fini(handle->context);
+         krb5_free_context(handle->context);
+         free(handle);
+         return ENOMEM;
+     }
+     *handle->lhandle = *handle;
+     handle->lhandle->api_version = KADM5_API_VERSION_2;
+     handle->lhandle->struct_version = KADM5_STRUCT_VERSION;
+     handle->lhandle->lhandle = handle->lhandle;
+
+     /* can't check the handle until current_caller is set */
+    if (ret = check_handle((void *) handle)) {
+        free(handle);
+        return ret;
+    }
+
+     /*
+      * The KADM5_API_VERSION_1 spec said "If pass (or keytab) is NULL
+      * or an empty string, reads the master password from [the stash
+      * file].  Otherwise, the non-NULL password is ignored and the
+      * user is prompted for it via the tty."  However, the code was
+      * implemented the other way: when a non-NULL password was
+      * provided, the stash file was used.  This is somewhat more
+      * sensible, as then a local or remote client that provides a
+      * password does not prompt the user.  This code maintains the
+      * previous actual behavior, and not the old spec behavior,
+      * because that is how the unit tests are written.
+      *
+      * In KADM5_API_VERSION_2, this decision is controlled by
+      * params.
+      *
+      * kdb_init_master's third argument is "from_keyboard".
+      */ 
+    if (ret = kdb_init_master(handle, handle->params.realm,
+                             (handle->api_version == KADM5_API_VERSION_1 ?
+                              ((pass == NULL) || !(strlen(pass))) :
+                              ((handle->params.mask &
+                                KADM5_CONFIG_MKEY_FROM_KBD) &&
+                               handle->params.mkey_from_kbd))
+                             )) {
+        krb5_db_fini(handle->context);
+        krb5_free_context(handle->context);
+        free(handle);
+        return ret;
+    }
+
+    if ((ret = kdb_init_hist(handle, handle->params.realm))) {
+        krb5_db_fini(handle->context);
+        krb5_free_context(handle->context);
+        free(handle);
+        return ret;
+    }
+
+    if (ret = init_dict(&handle->params)) {
+        krb5_db_fini(handle->context);
+        krb5_free_principal(handle->context, handle->current_caller);
+        krb5_free_context(handle->context);
+        free(handle);
+        return ret;
+    }
+    
+    if (ret = adb_policy_init(handle)) {
+        krb5_db_fini(handle->context);
+        krb5_free_principal(handle->context, handle->current_caller);
+        krb5_free_context(handle->context);
+        free(handle);
+        return ret;
+    }
+    handle->lhandle->policy_db = handle->policy_db;
+
+    *server_handle = (void *) handle;
+    
+    return KADM5_OK;
+}
+
+kadm5_ret_t kadm5_destroy(void *server_handle)
+{
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    destroy_dict();
+
+    adb_policy_close(handle);
+    krb5_db_fini(handle->context);
+    krb5_free_principal(handle->context, handle->current_caller);
+    krb5_free_context(handle->context);
+    handle->magic_number = 0;
+    free(handle->lhandle);
+    free(handle);
+    return KADM5_OK;
+}
+
+kadm5_ret_t kadm5_flush(void *server_handle)
+{
+     kadm5_server_handle_t handle = server_handle;
+     kadm5_ret_t ret;
+
+     CHECK_HANDLE(server_handle);
+
+     if ((ret = krb5_db_fini(handle->context)) ||
+        /*
+         * Set the db_name based on configuration before calling
+         * krb5_db_init, so it will get used.
+         */
+        (ret = krb5_dbm_db_set_name(handle->context,
+                                    handle->params.dbname)) ||
+        (ret = krb5_db_init(handle->context)) ||
+        (ret = adb_policy_close(handle)) ||
+        (ret = adb_policy_init(handle))) {
+         (void) kadm5_destroy(server_handle);
+         return ret;
+     }
+     return KADM5_OK;
+}
+
+int _kadm5_check_handle(void *handle)
+{
+     CHECK_HANDLE(handle);
+     return 0;
+}
diff --git a/src/lib/kadm5/srv/server_kdb.c b/src/lib/kadm5/srv/server_kdb.c
new file mode 100644 (file)
index 0000000..1a900a3
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "k5-int.h"
+#include <kadm5/admin.h>
+#include "server_internal.h"
+
+krb5_principal     master_princ;
+krb5_encrypt_block  master_encblock;
+krb5_keyblock      master_keyblock;
+krb5_db_entry      master_db;
+
+krb5_principal     hist_princ;
+krb5_encrypt_block  hist_encblock;
+krb5_keyblock      hist_key;
+krb5_db_entry      hist_db;
+krb5_kvno          hist_kvno;
+
+/* much of this code is stolen from the kdc.  there should be some
+   library code to deal with this. */
+
+krb5_error_code kdb_init_master(kadm5_server_handle_t handle,
+                               char *r, int from_keyboard)
+{
+    int                   ret = 0;
+    char          *realm;
+    krb5_keyblock  tmk;
+
+    if (r == NULL)  {
+       if ((ret = krb5_get_default_realm(handle->context, &realm)))
+           return ret;
+    } else {
+       realm = r;
+    }
+           
+    if ((ret = krb5_db_setup_mkey_name(handle->context,
+                                      handle->params.mkey_name,
+                                      realm, NULL, &master_princ)))
+       goto done;
+
+    master_keyblock.enctype = handle->params.enctype;
+
+    krb5_use_enctype(handle->context, &master_encblock,
+                    master_keyblock.enctype);
+
+    if (ret = krb5_db_fetch_mkey(handle->context, master_princ,
+                                &master_encblock, from_keyboard,
+                                FALSE /* only prompt once */,
+                                handle->params.stash_file,
+                                NULL /* I'm not sure about this,
+                                        but it's what the kdc does --marc */,
+                                &master_keyblock))
+       goto done;
+                                
+    if ((ret = krb5_db_init(handle->context)) != KSUCCESS)
+       goto done;
+
+    if ((ret = krb5_db_verify_master_key(handle->context, master_princ,
+                                        &master_keyblock,
+                                        &master_encblock))) {
+         krb5_db_fini(handle->context);
+         return ret;
+    }
+
+    /* the kdc gets the db mkvno here.  The admin server never uses this
+       bit of information, so there's no reason to retrieve it. */
+
+    if ((ret = krb5_process_key(handle->context, &master_encblock,
+                               &master_keyblock))) {
+          krb5_db_fini(handle->context);
+         goto done;
+    }
+
+done:
+    if (r == NULL)
+       free(realm);
+
+    return(ret);
+}
+
+/*
+ * Function: kdb_init_hist
+ *
+ * Purpose: Initializes the global history variables.
+ *
+ * Arguments:
+ *
+ *     handle          (r) kadm5 api server handle
+ *     r               (r) realm of history principal to use, or NULL
+ *
+ * Effects: This function sets the value of the following global
+ * variables:
+ *
+ *     hist_princ      krb5_principal holding the history principal
+ *     hist_db         krb5_db_entry of the history principal
+ *     hist_key        krb5_keyblock holding the history principal's key
+ *     hist_encblock   krb5_encrypt_block holding the procssed hist_key
+ *     hist_kvno       the version number of the history key
+ *
+ * If the history principal does not already exist, this function
+ * attempts to create it with kadm5_create_principal.  WARNING!
+ * If the history principal is deleted and this function is executed
+ * (by kadmind, or kadmin.local, or anything else with permission),
+ * the principal will be assigned a new random key and all existing
+ * password history information will become useless.
+ */
+krb5_error_code kdb_init_hist(kadm5_server_handle_t handle, char *r)
+{
+    int            ret = 0;
+    char    *realm, *hist_name;
+    krb5_key_data *key_data;
+
+    if (r == NULL)  {
+       if ((ret = krb5_get_default_realm(handle->context, &realm)))
+           return ret;
+    } else {
+       realm = r;
+    }
+
+    if ((hist_name = (char *) malloc(strlen(KADM5_HIST_PRINCIPAL) +
+                                    strlen(realm) + 2)) == NULL)
+       goto done;
+
+    (void) sprintf(hist_name, "%s@%s", KADM5_HIST_PRINCIPAL, realm);
+
+    if ((ret = krb5_parse_name(handle->context, hist_name, &hist_princ)))
+       goto done;
+
+    if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL))) {
+       kadm5_principal_ent_rec ent;
+
+       if (ret != KADM5_UNK_PRINC)
+           goto done;
+
+       /* try to create the principal */
+
+       memset(&ent, 0, sizeof(ent));
+
+       ent.principal = hist_princ;
+       ent.max_life = KRB5_KDB_DISALLOW_ALL_TIX;
+       ent.attributes = 0;
+
+       /* this uses hist_kvno.  So we set it to 2, which will be the
+          correct value once the principal is created and randomized.
+          Of course, it doesn't make sense to keep a history for the
+          history principal, anyway. */
+
+       hist_kvno = 2;
+
+       if (ret = kadm5_create_principal(handle, &ent,
+                                             (KADM5_PRINCIPAL |
+                                              KADM5_MAX_LIFE |
+                                              KADM5_ATTRIBUTES),
+                                             "to-be-random"))
+           goto done;
+
+       /* this won't let us randomize the hist_princ.  So we cheat. */
+
+       hist_princ = NULL;
+
+       ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL);
+
+       hist_princ = ent.principal;
+
+       if (ret)
+           goto done;
+
+       /* now read the newly-created kdb record out of the
+          database. */
+
+       if ((ret = kdb_get_entry(handle, hist_princ, &hist_db, NULL)))
+           goto done;
+
+    }
+
+    if (ret = krb5_dbe_find_enctype(handle->context,
+                                   &hist_db,
+                                   handle->params.enctype,
+                                   -1,
+                                   -1,
+                                   &key_data))
+       goto done;
+
+    if (ret = krb5_dbekd_decrypt_key_data(handle->context, &master_encblock,
+                                         key_data, &hist_key, NULL))
+       goto done;
+
+    krb5_use_enctype(handle->context, &hist_encblock, hist_key.enctype);
+
+    if ((ret = krb5_process_key(handle->context, &hist_encblock,
+                               &hist_key)) != KSUCCESS)
+       goto done;
+    
+    hist_kvno = key_data->key_data_kvno;
+
+done:
+    free(hist_name);
+    if (r == NULL)
+       free(realm);
+    return ret;
+}
+
+/*
+ * Function: kdb_get_entry
+ *
+ * Purpose: Gets an entry from the kerberos database and breaks
+ * it out into a krb5_db_entry and an osa_princ_ent_t.
+ *
+ * Arguments:
+ *
+ *             handle          (r) the server_handle
+ *             principal       (r) the principal to get
+ *             kdb             (w) krb5_db_entry to fill in
+ *             adb             (w) osa_princ_ent_rec to fill in
+ *
+ * when the caller is done with kdb and adb, kdb_free_entry must be
+ * called to release them.  The adb record is filled in with the
+ * contents of the KRB5_TL_KADM_DATA record; if that record doesn't
+ * exist, an empty but valid adb record is returned.
+ */
+krb5_error_code
+kdb_get_entry(kadm5_server_handle_t handle,
+             krb5_principal principal, krb5_db_entry *kdb,
+             osa_princ_ent_rec *adb)
+{
+    krb5_error_code ret;
+    int nprincs;
+    krb5_boolean more;
+    krb5_tl_data tl_data;
+    XDR xdrs;
+
+    if (ret = krb5_db_get_principal(handle->context, principal, kdb, &nprincs,
+                                   &more))
+       return(ret);
+
+    if (more) {
+       krb5_db_free_principal(handle->context, kdb, nprincs);
+       return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
+    } else if (nprincs != 1) {
+       krb5_db_free_principal(handle->context, kdb, nprincs);
+       return(KADM5_UNK_PRINC);
+    }
+
+    if (adb) {
+       memset(adb, 0, sizeof(*adb));
+
+       tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+       /*
+        * XXX Currently, lookup_tl_data always returns zero; it sets
+        * tl_data->tl_data_length to zero if the type isn't found.
+        * This should be fixed...
+        */
+       if ((ret = krb5_dbe_lookup_tl_data(handle->context, kdb, &tl_data))
+           || (tl_data.tl_data_length == 0)) {
+           /* there's no admin data.  this can happen, if the admin
+              server is put into production after some principals
+              are created.  In this case, return valid admin
+              data (which is all zeros with the hist_kvno filled
+              in), and when the entry is written, the admin
+              data will get stored correctly. */
+
+           adb->admin_history_kvno = hist_kvno;
+
+           return(ret);
+       }
+
+       xdrmem_create(&xdrs, tl_data.tl_data_contents,
+                     tl_data.tl_data_length, XDR_DECODE);
+       if (! xdr_osa_princ_ent_rec(&xdrs, adb)) {
+          xdr_destroy(&xdrs);
+          krb5_db_free_principal(handle->context, kdb, 1);
+          return(OSA_ADB_XDR_FAILURE);
+       }
+       xdr_destroy(&xdrs);
+    }
+
+    return(0);
+}
+
+/*
+ * Function: kdb_free_entry
+ *
+ * Purpose: frees the resources allocated by kdb_get_entry
+ *
+ * Arguments:
+ *
+ *             handle          (r) the server_handle
+ *             kdb             (w) krb5_db_entry to fill in
+ *             adb             (w) osa_princ_ent_rec to fill in
+ *
+ * when the caller is done with kdb and adb, kdb_free_entry must be
+ * called to release them.
+ */
+
+krb5_error_code
+kdb_free_entry(kadm5_server_handle_t handle,
+              krb5_db_entry *kdb, osa_princ_ent_rec *adb)
+{
+    XDR xdrs;
+
+
+    if (kdb)
+       krb5_db_free_principal(handle->context, kdb, 1);
+
+    if (adb) {
+       xdrmem_create(&xdrs, NULL, 0, XDR_FREE);
+       xdr_osa_princ_ent_rec(&xdrs, adb);
+       xdr_destroy(&xdrs);
+    }
+
+    return(0);
+}
+
+/*
+ * Function: kdb_put_entry
+ *
+ * Purpose: Stores the osa_princ_ent_t and krb5_db_entry into to
+ * database.
+ *
+ * Arguments:
+ *
+ *             handle  (r) the server_handle
+ *             kdb     (r/w) the krb5_db_entry to store
+ *             adb     (r) the osa_princ_db_ent to store
+ *
+ * Effects:
+ *
+ * The last modifier field of the kdb is set to the caller at now.
+ * adb is encoded with xdr_osa_princ_ent_ret and stored in kbd as
+ * KRB5_TL_KADM_DATA.  kdb is then written to the database.
+ */
+krb5_error_code
+kdb_put_entry(kadm5_server_handle_t handle,
+             krb5_db_entry *kdb, osa_princ_ent_rec *adb)
+{
+    krb5_error_code ret;
+    krb5_int32 now;
+    XDR xdrs;
+    krb5_tl_data tl_data;
+    int one;
+
+    if (ret = krb5_timeofday(handle->context, &now))
+       return(ret);
+
+    if (ret = krb5_dbe_update_mod_princ_data(handle->context, kdb, now,
+                                            handle->current_caller))
+       return(ret);
+    
+    xdralloc_create(&xdrs, XDR_ENCODE); 
+    if(! xdr_osa_princ_ent_rec(&xdrs, adb)) {
+       xdr_destroy(&xdrs);
+       return(OSA_ADB_XDR_FAILURE);
+    }
+    tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+    tl_data.tl_data_length = xdr_getpos(&xdrs);
+    tl_data.tl_data_contents = xdralloc_getdata(&xdrs);
+
+    ret = krb5_dbe_update_tl_data(handle->context, kdb, &tl_data);
+
+    xdr_destroy(&xdrs);
+
+    if (ret)
+       return(ret);
+
+    one = 1;
+
+    if (ret = krb5_db_put_principal(handle->context, kdb, &one))
+       return(ret);
+
+    return(0);
+}
+
+krb5_error_code
+kdb_delete_entry(kadm5_server_handle_t handle, krb5_principal name)
+{
+    int one = 1;
+    krb5_error_code ret;
+    
+    ret = krb5_db_delete_principal(handle->context, name, &one);
+
+    return ret;
+}
+
+typedef struct _iter_data {
+    void (*func)(void *, krb5_principal);
+    void *data;
+} iter_data;
+
+static krb5_error_code
+kdb_iter_func(krb5_pointer data, krb5_db_entry *kdb)
+{
+    iter_data *id = (iter_data *) data;
+
+    (*(id->func))(id->data, kdb->princ);
+
+    return(0);
+}
+
+krb5_error_code
+kdb_iter_entry(kadm5_server_handle_t handle,
+              void (*iter_fct)(void *, krb5_principal), void *data)
+{
+    iter_data id;
+    krb5_error_code ret;
+
+    id.func = iter_fct;
+    id.data = data;
+
+    if (ret = krb5_db_iterate(handle->context, kdb_iter_func, &id))
+       return(ret);
+
+    return(0);
+}
+
+
diff --git a/src/lib/kadm5/srv/server_misc.c b/src/lib/kadm5/srv/server_misc.c
new file mode 100644 (file)
index 0000000..24f101c
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include    "k5-int.h"
+#include    <krb5/kdb.h>
+#include    <ctype.h>
+#include    "adb.h"
+
+/* for strcasecmp */
+#include    <string.h>
+
+#include    "server_internal.h"
+
+kadm5_ret_t
+adb_policy_init(kadm5_server_handle_t handle)
+{
+    osa_adb_ret_t   ret;
+    if(handle->policy_db == (osa_adb_policy_t) NULL)
+       if((ret = osa_adb_open_policy(&handle->policy_db,
+                                     &handle->params)) != OSA_ADB_OK)
+            return ret;
+    return KADM5_OK;
+}
+
+kadm5_ret_t
+adb_policy_close(kadm5_server_handle_t handle)
+{
+    osa_adb_ret_t   ret;
+    if(handle->policy_db != (osa_adb_policy_t) NULL)
+       if((ret = osa_adb_close_policy(handle->policy_db)) != OSA_ADB_OK)
+           return ret;
+    handle->policy_db = NULL;
+    return KADM5_OK;
+}
+
+/* some of this is stolen from gatekeeper ... */
+kadm5_ret_t
+passwd_check(kadm5_server_handle_t handle,
+            char *password, int use_policy, kadm5_policy_ent_t pol,
+            krb5_principal principal)
+{
+    int            nupper = 0,
+           nlower = 0,
+           ndigit = 0, 
+           npunct = 0,
+           nspec = 0;
+    char    c, *s;
+    
+    if(use_policy) {
+       if(strlen(password) < pol->pw_min_length)
+           return KADM5_PASS_Q_TOOSHORT;
+       s = password;
+       while ((c = *s++)) {
+           if (islower(c)) {
+               nlower = 1;
+               continue;
+           }
+           else if (isupper(c)) {
+               nupper = 1;
+               continue;
+           } else if (isdigit(c)) {
+               ndigit = 1;
+               continue;
+           } else if (ispunct(c)) {
+               npunct = 1;
+               continue;
+           } else {
+               nspec = 1;
+               continue;
+           }
+       }
+       if ((nupper + nlower + ndigit + npunct + nspec) < pol->pw_min_classes) 
+           return KADM5_PASS_Q_CLASS;
+       if((find_word(password) == KADM5_OK))
+           return KADM5_PASS_Q_DICT;
+       else { 
+           char        *cp;
+           int c, n = krb5_princ_size(handle->context, principal);
+           cp = krb5_princ_realm(handle->context, principal)->data;
+           if (strcasecmp(cp, password) == 0)
+               return KADM5_PASS_Q_DICT;
+           for (c = 0; c < n ; c++) {
+               cp = krb5_princ_component(handle->context, principal, c)->data;
+               if (strcasecmp(cp, password) == 0)
+                   return KADM5_PASS_Q_DICT;
+           }
+           return KADM5_OK;
+       }
+    } else {
+       if (strlen(password) < 1)
+           return KADM5_PASS_Q_TOOSHORT;
+    }
+    return KADM5_OK;    
+}
diff --git a/src/lib/kadm5/srv/svr_chpass_util.c b/src/lib/kadm5/srv/svr_chpass_util.c
new file mode 100644 (file)
index 0000000..df2bf4c
--- /dev/null
@@ -0,0 +1,15 @@
+#include <kadm5/admin.h>
+#include "server_internal.h"
+
+kadm5_ret_t kadm5_chpass_principal_util(void *server_handle,
+                                       krb5_principal princ,
+                                       char *new_pw, 
+                                       char **ret_pw,
+                                       char *msg_ret)
+{
+  kadm5_server_handle_t handle = server_handle;
+
+  CHECK_HANDLE(server_handle);
+  return _kadm5_chpass_principal_util(handle, handle->lhandle, princ,
+                                     new_pw, ret_pw, msg_ret);
+}
diff --git a/src/lib/kadm5/srv/svr_iters.c b/src/lib/kadm5/srv/svr_iters.c
new file mode 100644 (file)
index 0000000..19c9000
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#if defined(HAVE_COMPILE) && defined(HAVE_STEP)
+#define SOLARIS_REGEXPS
+#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
+#define POSIX_REGEXPS
+#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
+#define BSD_REGEXPS
+#else
+#error I cannot find any regexp functions
+#endif
+
+#include       <sys/types.h>
+#include       <string.h>
+#include       <kadm5/admin.h>
+#include       "adb.h"
+#include       <dyn.h>
+#ifdef SOLARIS_REGEXPS
+#include       <regexpr.h>
+#endif
+#ifdef POSIX_REGEXPS
+#include       <regex.h>
+#endif
+#include <stdlib.h>
+
+#include       "server_internal.h"
+
+struct iter_data {
+     krb5_context context;
+     DynObject matches;
+     char *exp;
+#ifdef SOLARIS_REGEXPS
+     char *expbuf;
+#endif
+#ifdef POSIX_REGEXPS
+     regex_t preg;
+#endif
+};
+
+/*
+ * Function: glob_to_regexp
+ *
+ * Arguments:
+ *
+ *     glob    (r) the shell-style glob (?*[]) to convert
+ *     realm   (r) the default realm to append, or NULL
+ *     regexp  (w) the ed-style regexp created from glob
+ *
+ * Effects:
+ *
+ * regexp is filled in with allocated memory contained a regular
+ * expression to be used with re_comp/compile that matches what the
+ * shell-style glob would match.  If glob does not contain an "@"
+ * character and realm is not NULL, "@<realm>" is appended to the regexp.
+ *
+ * Conversion algorithm:
+ *
+ *     quoted characters are copied quoted
+ *     ? is converted to .
+ *     * is converted to .*
+ *     active characters are quoted: ^, $, .
+ *     [ and ] are active but supported and have the same meaning, so
+ *             they are copied
+ *     other characters are copied
+ *     regexp is anchored with ^ and $
+ */
+kadm5_ret_t glob_to_regexp(char *glob, char *realm, char **regexp)
+{
+     int append_realm;
+     char *p;
+
+     /* validate the glob */
+     if (glob[strlen(glob)-1] == '\\')
+         return EINVAL;
+
+     /* A character of glob can turn into two in regexp, plus ^ and $ */
+     /* and trailing null.  If glob has no @, also allocate space for */
+     /* the realm. */
+     append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
+     p = (char *) malloc(strlen(glob)*2+ 3 +
+                        (append_realm ? (strlen(realm)+1) : 0));
+     if (p == NULL)
+         return ENOMEM;
+     *regexp = p;
+
+     *p++ = '^';
+     while (*glob) {
+         switch (*glob) {
+         case '?':
+              *p++ = '.';
+              break;
+         case '*':
+              *p++ = '.';
+              *p++ = '*';
+              break;
+         case '.':
+         case '^':
+         case '$':
+              *p++ = '\\';
+              *p++ = *glob;
+              break;
+         case '\\':
+              *p++ = '\\';
+              *p++ = ++*glob;
+              break;
+         default:
+              *p++ = *glob;
+              break;
+         }
+         glob++;
+     }
+
+     if (append_realm) {
+         *p++ = '@';
+         strcpy(p, realm);
+         p += strlen(realm);
+     }
+
+     *p++ = '$';
+     *p++ = '\0';
+     return KADM5_OK;
+}
+
+void get_either_iter(struct iter_data *data, char *name)
+{
+     if (
+#ifdef SOLARIS_REGEXPS
+        (step(name, data->expbuf) != 0)
+#endif
+#ifdef POSIX_REGEXPS
+        (regexec(&data->preg, name, 0, NULL, 0) == 0)
+#endif
+#ifdef BSD_REGEXPS
+        (re_exec(name) != 0)
+#endif
+        )
+     {
+         (void) DynAdd(data->matches, &name);
+     } else
+         free(name);
+}
+
+void get_pols_iter(void *data, osa_policy_ent_t entry)
+{
+     char *name;
+
+     if ((name = strdup(entry->name)) == NULL)
+         return;
+     get_either_iter(data, name);
+}
+
+void get_princs_iter(void *data, krb5_principal princ)
+{
+     struct iter_data *id = (struct iter_data *) data;
+     char *name;
+     
+     if (krb5_unparse_name(id->context, princ, &name) != 0)
+         return;
+     get_either_iter(data, name);
+}
+
+kadm5_ret_t kadm5_get_either(int princ,
+                                      void *server_handle,
+                                      char *exp,
+                                      char ***princs,
+                                      int *count)
+{
+     struct iter_data data;
+     char *msg, *regexp;
+     int ret;
+     kadm5_server_handle_t handle = server_handle;
+     
+     *count = 0;
+     if (exp == NULL)
+         exp = "*";
+
+     CHECK_HANDLE(server_handle);
+
+     if ((ret = glob_to_regexp(exp, princ ? handle->params.realm : NULL,
+                              &regexp)) != KADM5_OK)
+         return ret;
+
+     if (
+#ifdef SOLARIS_REGEXPS
+        ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
+#endif
+#ifdef POSIX_REGEXPS
+        ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
+#endif
+#ifdef BSD_REGEXPS
+        ((msg = (char *) re_comp(regexp)) != NULL)
+#endif
+        )
+     {
+         /* XXX syslog msg or regerr(regerrno) */
+         free(regexp);
+         return EINVAL;
+     }
+
+     if ((data.matches = DynCreate(sizeof(char *), -4)) == NULL) {
+         free(regexp);
+         return ENOMEM;
+     }
+
+     if (princ) {
+         data.context = handle->context;
+         ret = kdb_iter_entry(handle, get_princs_iter, (void *) &data);
+     } else {
+         ret = osa_adb_iter_policy(handle->policy_db, get_pols_iter, (void *)&data);
+     }
+     
+     if (ret != OSA_ADB_OK) {
+         free(regexp);
+         DynDestroy(data.matches);
+         return ret;
+     }
+
+     (*princs) = (char **) DynArray(data.matches);
+     *count = DynSize(data.matches);
+     DynRelease(data.matches);
+     free(regexp);
+     return KADM5_OK;
+}
+
+kadm5_ret_t kadm5_get_principals(void *server_handle,
+                                          char *exp,
+                                          char ***princs,
+                                          int *count)
+{
+     return kadm5_get_either(1, server_handle, exp, princs, count);
+}
+
+kadm5_ret_t kadm5_get_policies(void *server_handle,
+                                          char *exp,
+                                          char ***pols,
+                                          int *count)
+{
+     return kadm5_get_either(0, server_handle, exp, pols, count);
+}
+
diff --git a/src/lib/kadm5/srv/svr_misc_free.c b/src/lib/kadm5/srv/svr_misc_free.c
new file mode 100644 (file)
index 0000000..5c76a1e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ * 
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+#include       <kadm5/admin.h>
+#include       <malloc.h>
+#include       "server_internal.h"
+
+kadm5_ret_t
+kadm5_free_principal_ent(void *server_handle,
+                             kadm5_principal_ent_t val)
+{
+    kadm5_server_handle_t      handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    if(val) {
+       if(val->principal) 
+           krb5_free_principal(handle->context, val->principal);
+       if(val->mod_name)
+           krb5_free_principal(handle->context, val->mod_name);
+       if(val->policy)
+           free(val->policy);
+
+       /* XXX free key_data and tl_data */
+
+       if (handle->api_version == KADM5_API_VERSION_1)
+            free(val);
+    }
+    return KADM5_OK;
+}
diff --git a/src/lib/kadm5/srv/svr_policy.c b/src/lib/kadm5/srv/svr_policy.c
new file mode 100644 (file)
index 0000000..74e2521
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include       <sys/types.h>
+#include       <kadm5/admin.h>
+#include       "adb.h"
+#include       "server_internal.h"
+#include       <stdlib.h>
+
+#define MAX_PW_HISTORY 10
+#define MIN_PW_HISTORY 1
+#define        MIN_PW_CLASSES  1
+#define MAX_PW_CLASSES 5
+#define        MIN_PW_LENGTH   1
+
+/*
+ * Function: kadm5_create_policy
+ * 
+ * Purpose: Create Policies in the policy DB.
+ *
+ * Arguments:
+ *     entry   (input) The policy entry to be written out to the DB.
+ *     mask    (input) Specifies which fields in entry are to ge written out
+ *                     and which get default values.
+ *     <return value> 0 if sucsessfull otherwise an error code is returned.
+ *
+ * Requires:
+ *     Entry must be a valid principal entry, and mask have a valid value.
+ * 
+ * Effects:
+ *     Verifies that mask does not specify that the refcount should
+ *     be set as part of the creation, and calls
+ *     kadm5_create_policy_internal.  If the refcount *is*
+ *     specified, returns KADM5_BAD_MASK.
+ */
+
+kadm5_ret_t
+kadm5_create_policy(void *server_handle,
+                        kadm5_policy_ent_t entry, long mask)
+{
+    CHECK_HANDLE(server_handle);
+
+    if (mask & KADM5_REF_COUNT)
+       return KADM5_BAD_MASK;
+    else
+       return kadm5_create_policy_internal(server_handle, entry, mask);
+}
+
+/*
+ * Function: kadm5_create_policy_internal
+ * 
+ * Purpose: Create Policies in the policy DB.
+ *
+ * Arguments:
+ *     entry   (input) The policy entry to be written out to the DB.
+ *     mask    (input) Specifies which fields in entry are to ge written out
+ *                     and which get default values.
+ *     <return value> 0 if sucsessfull otherwise an error code is returned.
+ *
+ * Requires:
+ *     Entry must be a valid principal entry, and mask have a valid value.
+ * 
+ * Effects:
+ *     Writes the data to the database, and does a database sync if
+ *     sucsessfull.
+ *
+ */
+
+kadm5_ret_t
+kadm5_create_policy_internal(void *server_handle,
+                                 kadm5_policy_ent_t entry, long mask)
+{
+    kadm5_server_handle_t handle = server_handle;
+    osa_policy_ent_rec pent;
+    int                        ret;
+    char               *p;
+
+    CHECK_HANDLE(server_handle);
+
+    if ((entry == (kadm5_policy_ent_t) NULL) || (entry->policy == NULL))
+       return EINVAL;
+    if(strlen(entry->policy) == 0)
+       return KADM5_BAD_POLICY;
+    if (!(mask & KADM5_POLICY))
+       return KADM5_BAD_MASK;
+       
+    pent.name = entry->policy;
+    p = entry->policy;
+    while(*p != '\0') {
+       if(*p < ' ' || *p > '~')
+           return KADM5_BAD_POLICY;
+       else
+           p++;
+    }
+    if (!(mask & KADM5_PW_MAX_LIFE))
+       pent.pw_max_life = 0;
+    else
+       pent.pw_max_life = entry->pw_max_life;
+    if (!(mask & KADM5_PW_MIN_LIFE))
+       pent.pw_min_life = 0;
+    else {
+       if((mask & KADM5_PW_MAX_LIFE)) {
+           if(entry->pw_min_life > entry->pw_max_life && entry->pw_max_life != 0)
+               return KADM5_BAD_MIN_PASS_LIFE;
+       }
+       pent.pw_min_life = entry->pw_min_life;
+    }
+    if (!(mask & KADM5_PW_MIN_LENGTH))
+       pent.pw_min_length = MIN_PW_LENGTH;
+    else {
+       if(entry->pw_min_length < MIN_PW_LENGTH)
+           return KADM5_BAD_LENGTH;
+       pent.pw_min_length = entry->pw_min_length;
+    }
+    if (!(mask & KADM5_PW_MIN_CLASSES))
+       pent.pw_min_classes = MIN_PW_CLASSES;
+    else {
+       if(entry->pw_min_classes > MAX_PW_CLASSES || entry->pw_min_classes < MIN_PW_CLASSES)
+           return KADM5_BAD_CLASS;
+       pent.pw_min_classes = entry->pw_min_classes;
+    }
+    if (!(mask & KADM5_PW_HISTORY_NUM))
+       pent.pw_history_num = MIN_PW_HISTORY;
+    else {
+       if(entry->pw_history_num < MIN_PW_HISTORY ||
+          entry->pw_history_num > MAX_PW_HISTORY)
+           return KADM5_BAD_HISTORY;
+       else
+           pent.pw_history_num = entry->pw_history_num;
+    }
+    if (!(mask & KADM5_REF_COUNT))
+       pent.policy_refcnt = 0;
+    else
+       pent.policy_refcnt = entry->policy_refcnt;
+    if ((ret = osa_adb_create_policy(handle->policy_db, &pent)) == OSA_ADB_OK)
+       return KADM5_OK;
+    else
+       return ret;
+}
+         
+kadm5_ret_t
+kadm5_delete_policy(void *server_handle, kadm5_policy_t name)
+{
+    kadm5_server_handle_t handle = server_handle;
+    osa_policy_ent_t           entry;
+    int                                ret;
+
+    CHECK_HANDLE(server_handle);
+
+    if(name == (kadm5_policy_t) NULL)
+       return EINVAL;
+    if(strlen(name) == 0)
+       return KADM5_BAD_POLICY;
+    if ((ret = osa_adb_get_policy(handle->policy_db, name, &entry)) != OSA_ADB_OK)
+       return ret;
+    if(entry->policy_refcnt != 0) {
+       osa_free_policy_ent(entry);
+       return KADM5_POLICY_REF;
+    }
+    osa_free_policy_ent(entry);
+    if ((ret = osa_adb_destroy_policy(handle->policy_db, name)) == OSA_ADB_OK)
+       return KADM5_OK;
+    else
+       return ret;
+}
+
+kadm5_ret_t
+kadm5_modify_policy(void *server_handle,
+                        kadm5_policy_ent_t entry, long mask)
+{
+    CHECK_HANDLE(server_handle);
+
+    if (mask & KADM5_REF_COUNT)
+       return KADM5_BAD_MASK;
+    else
+       return kadm5_modify_policy_internal(server_handle, entry, mask);
+}
+
+kadm5_ret_t
+kadm5_modify_policy_internal(void *server_handle,
+                                 kadm5_policy_ent_t entry, long mask)
+{
+    kadm5_server_handle_t handle = server_handle;
+    osa_policy_ent_t   p;
+    int                        ret;
+
+    CHECK_HANDLE(server_handle);
+
+    if((entry == (kadm5_policy_ent_t) NULL) || (entry->policy == NULL))
+       return EINVAL;
+    if(strlen(entry->policy) == 0)
+       return KADM5_BAD_POLICY;
+    if((mask & KADM5_POLICY))
+       return KADM5_BAD_MASK;
+               
+    switch ((ret = osa_adb_get_policy(handle->policy_db, entry->policy, &p))) {
+    case OSA_ADB_OK:
+       break;
+    case OSA_ADB_NOENT:
+       return KADM5_UNK_POLICY;
+    default:
+       break;
+    }
+    if ((mask & KADM5_PW_MAX_LIFE))
+       p->pw_max_life = entry->pw_max_life;
+    if ((mask & KADM5_PW_MIN_LIFE)) {
+       if(entry->pw_min_life > p->pw_max_life && p->pw_max_life != 0)  {
+            osa_free_policy_ent(p);
+            return KADM5_BAD_MIN_PASS_LIFE;
+       }
+       p->pw_min_life = entry->pw_min_life;
+    }
+    if ((mask & KADM5_PW_MIN_LENGTH)) {
+       if(entry->pw_min_length < MIN_PW_LENGTH) {
+             osa_free_policy_ent(p);
+             return KADM5_BAD_LENGTH;
+        }
+       p->pw_min_length = entry->pw_min_length;
+    }
+    if ((mask & KADM5_PW_MIN_CLASSES)) {
+       if(entry->pw_min_classes > MAX_PW_CLASSES ||
+          entry->pw_min_classes < MIN_PW_CLASSES) {
+            osa_free_policy_ent(p);
+            return KADM5_BAD_CLASS;
+       }
+       p->pw_min_classes = entry->pw_min_classes;
+    }
+    if ((mask & KADM5_PW_HISTORY_NUM)) {
+       if(entry->pw_history_num < MIN_PW_HISTORY ||
+          entry->pw_history_num > MAX_PW_HISTORY) {
+            osa_free_policy_ent(p);
+            return KADM5_BAD_HISTORY;
+       }
+       p->pw_history_num = entry->pw_history_num;
+    }
+    if ((mask & KADM5_REF_COUNT))
+       p->policy_refcnt = entry->policy_refcnt;
+    switch ((ret = osa_adb_put_policy(handle->policy_db, p))) {
+    case OSA_ADB_OK:
+       ret = KADM5_OK;
+       break;
+    case OSA_ADB_NOENT:        /* this should not happen here ... */
+       ret = KADM5_UNK_POLICY;
+       break;
+    }
+    osa_free_policy_ent(p);
+    return ret;
+}
+
+kadm5_ret_t
+kadm5_get_policy(void *server_handle, kadm5_policy_t name,
+                kadm5_policy_ent_t entry) 
+{
+    osa_policy_ent_t           t;
+    kadm5_policy_ent_rec       entry_local, **entry_orig, *new;
+    int                                ret;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    /*
+     * In version 1, entry is a pointer to a kadm5_policy_ent_t that
+     * should be filled with allocated memory.
+     */
+    if (handle->api_version == KADM5_API_VERSION_1) {
+        entry_orig = (kadm5_policy_ent_rec **) entry;
+        *entry_orig = NULL;
+        entry = &entry_local;
+    }
+    
+    if (name == (kadm5_policy_t) NULL)
+       return EINVAL;
+    if(strlen(name) == 0)
+       return KADM5_BAD_POLICY;
+    switch((ret = osa_adb_get_policy(handle->policy_db, name, &t))) {
+    case OSA_ADB_OK:
+       break;
+    case OSA_ADB_NOENT:
+       return KADM5_UNK_POLICY;
+    default:
+       return ret;
+    }
+    if ((entry->policy = (char *) malloc(strlen(t->name) + 1)) == NULL) {
+        osa_free_policy_ent(t);
+        return ENOMEM;
+    }
+    strcpy(entry->policy, t->name);
+    entry->pw_min_life = t->pw_min_life;
+    entry->pw_max_life = t->pw_max_life;
+    entry->pw_min_length = t->pw_min_length;
+    entry->pw_min_classes = t->pw_min_classes;
+    entry->pw_history_num = t->pw_history_num;
+    entry->policy_refcnt = t->policy_refcnt;
+    osa_free_policy_ent(t);
+
+    if (handle->api_version == KADM5_API_VERSION_1) {
+        new = (kadm5_policy_ent_t) malloc(sizeof(kadm5_policy_ent_rec));
+        if (new == NULL) {
+             free(entry->policy);
+             osa_free_policy_ent(t);
+             return ENOMEM;
+        }
+        *new = *entry;
+        *entry_orig = new;
+    }
+    
+    return KADM5_OK;
+}
diff --git a/src/lib/kadm5/srv/svr_principal.c b/src/lib/kadm5/srv/svr_principal.c
new file mode 100644 (file)
index 0000000..6f9671f
--- /dev/null
@@ -0,0 +1,1350 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include       <sys/types.h>
+#include       <sys/time.h>
+#include       <kadm5/admin.h>
+#include       "adb.h"
+#include       "k5-int.h"
+#include       <krb5/kdb.h>
+#include       <stdio.h>
+#include       <string.h>
+#include       "server_internal.h"
+#include       <stdarg.h>
+#include       <stdlib.h>
+
+extern krb5_principal      master_princ;
+extern krb5_principal      hist_princ;
+extern krb5_encrypt_block  master_encblock;
+extern krb5_encrypt_block  hist_encblock;
+extern krb5_keyblock       master_keyblock;
+extern krb5_keyblock       hist_key;
+extern krb5_db_entry       master_db;
+extern krb5_db_entry       hist_db;
+extern  krb5_kvno          hist_kvno;
+
+static int decrypt_key_data(krb5_context context,
+                           int n_key_data, krb5_key_data *key_data,
+                           krb5_keyblock **keyblocks, int *n_keys);
+
+/*
+ * XXX Functions that ought to be in libkrb5.a, but aren't.
+ */
+int krb5_free_keyblock_contents(context, key)
+     krb5_context context;
+     krb5_keyblock *key;
+{
+     memset(key->contents, 0, key->length);
+     krb5_xfree(key->contents);
+     return 0;
+}
+
+kadm5_ret_t krb5_copy_key_data_contents(context, from, to)
+   krb5_context context;
+   krb5_key_data *from, *to;
+{
+     int i, idx;
+     
+     *to = *from;
+
+     idx = (from->key_data_ver == 1 ? 1 : 2);
+
+     for (i = 0; i < idx; i++) {
+         to->key_data_contents[i] = malloc(from->key_data_length[i]);
+         if (to->key_data_contents[i] == NULL) {
+              for (i = 0; i < idx; i++) {
+                   if (to->key_data_contents[i]) {
+                        memset(to->key_data_contents[i], 0,
+                               to->key_data_length[i]);
+                        free(to->key_data_contents[i]);
+                   }
+              }
+              return ENOMEM;
+         }
+         memcpy(to->key_data_contents[i], from->key_data_contents[i],
+                from->key_data_length[i]);
+     }
+     return 0;
+}
+
+static krb5_tl_data *dup_tl_data(krb5_tl_data *tl)
+{
+     krb5_tl_data *n;
+
+     n = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
+     if (n == NULL)
+         return NULL;
+     n->tl_data_contents = malloc(tl->tl_data_length);
+     if (n->tl_data_contents == NULL) {
+         free(n);
+         return NULL;
+     }
+     memcpy(n->tl_data_contents, tl->tl_data_contents, tl->tl_data_length);
+     n->tl_data_type = tl->tl_data_type;
+     n->tl_data_length = tl->tl_data_length;
+     n->tl_data_next = NULL;
+     return n;
+}
+
+kadm5_ret_t
+kadm5_create_principal(void *server_handle,
+                           kadm5_principal_ent_t entry, long mask,
+                           char *password)
+{
+    krb5_db_entry              kdb;
+    osa_princ_ent_rec          adb;
+    kadm5_policy_ent_rec       polent;
+    krb5_int32                 now;
+    krb5_tl_data               *tl_data_orig, *tl_data_tail;
+    unsigned int               ret;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    /*
+     * Argument sanity checking, and opening up the DB
+     */
+    if(!(mask & KADM5_PRINCIPAL) || (mask & KADM5_MOD_NAME) ||
+       (mask & KADM5_MOD_TIME) || (mask & KADM5_LAST_PWD_CHANGE) ||
+       (mask & KADM5_MKVNO) || (mask & KADM5_POLICY_CLR) ||
+       (mask & KADM5_AUX_ATTRIBUTES) || (mask & KADM5_KEY_DATA) ||
+       (mask & KADM5_LAST_SUCCESS) || (mask & KADM5_LAST_FAILED) ||
+       (mask & KADM5_FAIL_AUTH_COUNT))
+       return KADM5_BAD_MASK;
+    if((mask & ~ALL_PRINC_MASK))
+       return KADM5_BAD_MASK;
+    if (entry == (kadm5_principal_ent_t) NULL || password == NULL)
+       return EINVAL;
+
+    /*
+     * Check to see if the principal exists
+     */
+    ret = kdb_get_entry(handle, entry->principal, &kdb, &adb);
+
+    switch(ret) {
+    case KADM5_UNK_PRINC:
+       break;
+    case 0:
+       kdb_free_entry(handle, &kdb, &adb);
+       return KADM5_DUP;
+    default:
+       return ret;
+    }
+
+    memset(&kdb, 0, sizeof(krb5_db_entry));
+    memset(&adb, 0, sizeof(osa_princ_ent_rec));
+
+    /*
+     * If a policy was specified, load it.
+     * If we can not find the one specified return an error
+     */
+    if ((mask & KADM5_POLICY)) {
+        if ((ret = kadm5_get_policy(handle->lhandle, entry->policy,
+                                    &polent)) != KADM5_OK) {
+           if(ret == EINVAL) 
+               return KADM5_BAD_POLICY;
+           else
+               return ret;
+       }
+    }
+    if (ret = passwd_check(handle, password, (mask & KADM5_POLICY),
+                          &polent, entry->principal)) {
+       if (mask & KADM5_POLICY)
+            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+       return ret;
+    }
+    /*
+     * Start populating the various DB fields, using the
+     * "defaults" for fields that were not specified by the
+     * mask.
+     */
+    if (ret = krb5_timeofday(handle->context, &now)) {
+       if (mask & KADM5_POLICY)
+            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+       return ret;
+    }
+
+    kdb.magic = KRB5_KDB_MAGIC_NUMBER;
+    kdb.len = KRB5_KDB_V1_BASE_LENGTH; /* gag me with a chainsaw */
+
+    if ((mask & KADM5_ATTRIBUTES)) 
+       kdb.attributes = entry->attributes;
+    else
+       kdb.attributes = handle->params.flags;
+
+    if ((mask & KADM5_MAX_LIFE))
+       kdb.max_life = entry->max_life; 
+    else 
+       kdb.max_life = handle->params.max_life;
+
+    if (mask & KADM5_MAX_RLIFE)
+        kdb.max_renewable_life = entry->max_renewable_life;
+    else
+        kdb.max_renewable_life = handle->params.max_rlife;
+
+    if ((mask & KADM5_PRINC_EXPIRE_TIME))
+       kdb.expiration = entry->princ_expire_time;
+    else
+       kdb.expiration = handle->params.expiration;
+
+    kdb.pw_expiration = 0;
+    if ((mask & KADM5_POLICY)) {
+       if(polent.pw_max_life)
+           kdb.pw_expiration = now + polent.pw_max_life;
+       else
+           kdb.pw_expiration = 0;
+    }
+    if ((mask & KADM5_PW_EXPIRATION)) {
+       if(!kdb.pw_expiration)
+           kdb.pw_expiration = entry->pw_expiration;
+       else {
+           if(entry->pw_expiration != 0)
+               kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
+                   entry->pw_expiration : kdb.pw_expiration;
+       }
+    }
+
+    kdb.last_success = 0;
+    kdb.last_failed = 0;
+    kdb.fail_auth_count = 0;
+
+    /* this is kind of gross, but in order to free the tl data, I need
+       to free the entire kdb entry, and that will try to free the
+       principal. */
+
+    if (ret = krb5_copy_principal(handle->context,
+                                 entry->principal, &(kdb.princ))) {
+       if (mask & KADM5_POLICY)
+            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+       return(ret);
+    }
+
+    if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now)) {
+       krb5_dbe_free_contents(handle->context, &kdb);
+       if (mask & KADM5_POLICY)
+            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+       return(ret);
+    }
+
+    /* initialize the keys */
+
+    if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
+                          handle->params.keysalts,
+                          handle->params.num_keysalts,
+                          password,
+                          (mask & KADM5_KVNO)?entry->kvno:1, &kdb)) {
+       krb5_dbe_free_contents(handle->context, &kdb);
+       if (mask & KADM5_POLICY)
+            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+       return(ret);
+    }
+
+    /* populate the admin-server-specific fields.  In the OV server,
+       this used to be in a separate database.  Since there's already
+       marshalling code for the admin fields, to keep things simple,
+       I'm going to keep it, and make all the admin stuff occupy a
+       single tl_data record, */
+
+    adb.admin_history_kvno = hist_kvno;
+    if ((mask & KADM5_POLICY)) {
+       adb.aux_attributes = KADM5_POLICY;
+
+       /* this does *not* need to be strdup'ed, because adb is xdr */
+       /* encoded in osa_adb_create_princ, and not ever freed */
+
+       adb.policy = entry->policy;
+    }
+
+    /* increment the policy ref count, if any */
+
+    if ((mask & KADM5_POLICY)) {
+       polent.policy_refcnt++;
+       if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
+                                                   KADM5_REF_COUNT))
+           != KADM5_OK) {
+           krb5_dbe_free_contents(handle->context, &kdb);
+           if (mask & KADM5_POLICY)
+                (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+           return(ret);
+       }
+    }
+
+    if (mask & KADM5_TL_DATA) {
+        /* splice entry->tl_data onto the front of kdb.tl_data */
+        tl_data_orig = kdb.tl_data;
+        for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
+             tl_data_tail = tl_data_tail->tl_data_next)
+             ;
+        tl_data_tail->tl_data_next = kdb.tl_data;
+        kdb.tl_data = entry->tl_data;
+    }
+
+    /* store the new db entry */
+    ret = kdb_put_entry(handle, &kdb, &adb);
+
+    if (mask & KADM5_TL_DATA) {
+        /* remove entry->tl_data from the front of kdb.tl_data */
+        tl_data_tail->tl_data_next = NULL;
+        kdb.tl_data = tl_data_orig;
+    }
+
+    krb5_dbe_free_contents(handle->context, &kdb);
+
+    if (ret) {
+       if ((mask & KADM5_POLICY)) {
+           /* decrement the policy ref count */
+
+           polent.policy_refcnt--;
+           /*
+            * if this fails, there's nothing we can do anyway.  the
+            * policy refcount wil be too high.
+            */
+           (void) kadm5_modify_policy_internal(handle->lhandle, &polent,
+                                                    KADM5_REF_COUNT);
+       }
+
+       if (mask & KADM5_POLICY)
+            (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+       return(ret);
+    }
+
+    if (mask & KADM5_POLICY)
+        (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+
+    return KADM5_OK;
+}
+
+       
+kadm5_ret_t
+kadm5_delete_principal(void *server_handle, krb5_principal principal)
+{
+    unsigned int               ret;
+    kadm5_policy_ent_rec       polent;
+    krb5_db_entry              kdb;
+    osa_princ_ent_rec          adb;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    if (principal == NULL)
+       return EINVAL;
+
+    if (ret = kdb_get_entry(handle, principal, &kdb, &adb))
+       return(ret);
+
+    if ((adb.aux_attributes & KADM5_POLICY)) {
+       if ((ret = kadm5_get_policy(handle->lhandle,
+                                   adb.policy, &polent))
+           == KADM5_OK) {
+           polent.policy_refcnt--;
+           if ((ret = kadm5_modify_policy_internal(handle->lhandle, &polent,
+                                                        KADM5_REF_COUNT))
+               != KADM5_OK) {
+               (void) kadm5_free_policy_ent(handle->lhandle, &polent);
+               kdb_free_entry(handle, &kdb, &adb);
+               return(ret);
+           }
+       }
+       if (ret = kadm5_free_policy_ent(handle->lhandle, &polent)) {
+           kdb_free_entry(handle, &kdb, &adb);
+           return ret;
+       }
+    }
+
+    ret = kdb_delete_entry(handle, principal);
+
+    kdb_free_entry(handle, &kdb, &adb);
+
+    return ret;
+}
+
+kadm5_ret_t
+kadm5_modify_principal(void *server_handle,
+                           kadm5_principal_ent_t entry, long mask)
+{
+    int                            ret, ret2, i;
+    kadm5_policy_ent_rec    npol, opol;
+    int                            have_npol = 0, have_opol = 0;
+    krb5_db_entry          kdb;
+    krb5_tl_data           *tl_data_orig, *tl_data_tail;
+    osa_princ_ent_rec      adb;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    if((mask & KADM5_PRINCIPAL) || (mask & KADM5_LAST_PWD_CHANGE) ||
+       (mask & KADM5_MOD_TIME) || (mask & KADM5_MOD_NAME) ||
+       (mask & KADM5_MKVNO) || (mask & KADM5_AUX_ATTRIBUTES) ||
+       (mask & KADM5_KEY_DATA) || (mask & KADM5_LAST_SUCCESS) ||
+       (mask & KADM5_LAST_FAILED))
+       return KADM5_BAD_MASK;
+    if((mask & ~ALL_PRINC_MASK))
+       return KADM5_BAD_MASK;
+    if((mask & KADM5_POLICY) && (mask & KADM5_POLICY_CLR))
+       return KADM5_BAD_MASK;
+    if(entry == (kadm5_principal_ent_t) NULL)
+       return EINVAL;
+
+    if (ret = kdb_get_entry(handle, entry->principal, &kdb, &adb))
+       return(ret);
+
+    /*
+     * This is pretty much the same as create ...
+     */
+
+    if ((mask & KADM5_POLICY)) {
+       ret = kadm5_get_policy(handle->lhandle, entry->policy, &npol);
+       switch(ret) {
+       case EINVAL:
+           ret = KADM5_BAD_POLICY;
+           break;
+       case KADM5_UNK_POLICY:
+       case KADM5_BAD_POLICY:
+           ret =  KADM5_UNK_POLICY;
+           goto done;
+           break;
+       case KADM5_OK:
+           have_npol = 1;
+           if(adb.aux_attributes & KADM5_POLICY) {
+               if(strcmp(adb.policy, entry->policy)) {
+                   ret = kadm5_get_policy(handle->lhandle,
+                                          adb.policy, &opol);
+                   switch(ret) {
+                   case EINVAL:
+                   case KADM5_BAD_POLICY:
+                   case KADM5_UNK_POLICY:
+                       break;
+                   case KADM5_OK:
+                       have_opol = 1;
+                       opol.policy_refcnt--;
+                       break;
+                   default:
+                       goto done;
+                       break;
+                   }
+                   npol.policy_refcnt++;
+               }
+           } else npol.policy_refcnt++;
+           adb.aux_attributes |= KADM5_POLICY;
+           if (adb.policy)
+                free(adb.policy);
+           adb.policy = strdup(entry->policy);
+           if (npol.pw_max_life) {
+               if (ret =
+                   krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
+                                                   &(kdb.pw_expiration)))
+                   goto done;
+               kdb.pw_expiration += npol.pw_max_life;
+           } else {
+               kdb.pw_expiration = 0;
+           }
+           break;
+       default:
+           goto done;
+       }
+       if ((mask & KADM5_PW_EXPIRATION)) {
+           if(kdb.pw_expiration == 0)
+               kdb.pw_expiration = entry->pw_expiration;
+           else if(entry->pw_expiration != 0)
+               kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
+                                   entry->pw_expiration : kdb.pw_expiration;
+       }
+    }
+    if ((mask & KADM5_PW_EXPIRATION) && !(mask & KADM5_POLICY)) {
+           if(kdb.pw_expiration == 0)
+               kdb.pw_expiration = entry->pw_expiration;
+           else if(entry->pw_expiration != 0)
+               kdb.pw_expiration = (entry->pw_expiration < kdb.pw_expiration) ?
+                                   entry->pw_expiration : kdb.pw_expiration;
+    }
+
+    if ((mask & KADM5_POLICY_CLR)) {
+       if (adb.aux_attributes & KADM5_POLICY) {
+           adb.aux_attributes &= ~KADM5_POLICY;
+           kdb.pw_expiration = 0;
+           ret = kadm5_get_policy(handle->lhandle, adb.policy, &opol);
+           switch(ret) {
+           case EINVAL:
+           case KADM5_BAD_POLICY:
+           case KADM5_UNK_POLICY:
+               ret = KADM5_BAD_DB;
+               goto done;
+               break;
+           case KADM5_OK:
+               have_opol = 1;
+               if (adb.policy)
+                    free(adb.policy);
+               adb.policy = NULL;
+               opol.policy_refcnt--;
+               break;
+           default:
+               goto done;
+               break;
+           }
+       }
+    }
+    if (((mask & KADM5_POLICY) ||
+        (mask & KADM5_POLICY_CLR)) &&
+       (((have_opol) &&
+         (ret =
+          kadm5_modify_policy_internal(handle->lhandle, &opol,
+                                            KADM5_REF_COUNT))) ||
+        ((have_npol) &&
+         (ret =
+          kadm5_modify_policy_internal(handle->lhandle, &npol,
+                                            KADM5_REF_COUNT)))))
+       goto done;
+
+    if ((mask & KADM5_ATTRIBUTES)) 
+       kdb.attributes = entry->attributes;
+    if ((mask & KADM5_MAX_LIFE))
+       kdb.max_life = entry->max_life;
+    if ((mask & KADM5_PRINC_EXPIRE_TIME))
+       kdb.expiration = entry->princ_expire_time;
+    /* the pw_expiration logic would go here if it wasn't spread
+       all over the policy code */
+    if (mask & KADM5_MAX_RLIFE)
+        kdb.max_renewable_life = entry->max_renewable_life;
+    if (mask & KADM5_FAIL_AUTH_COUNT)
+        kdb.fail_auth_count = entry->fail_auth_count;
+    
+    if((mask & KADM5_KVNO)) {
+        for (i = 0; i < kdb.n_key_data; i++)
+             kdb.key_data[i].key_data_kvno = entry->kvno;
+    }
+
+    if (mask & KADM5_TL_DATA) {
+        /* splice entry->tl_data onto the front of kdb.tl_data */
+        tl_data_orig = kdb.tl_data;
+        for (tl_data_tail = entry->tl_data; tl_data_tail->tl_data_next;
+             tl_data_tail = tl_data_tail->tl_data_next)
+             ;
+        tl_data_tail->tl_data_next = kdb.tl_data;
+        kdb.tl_data = entry->tl_data;
+    }
+
+    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
+       goto done;
+
+    if (mask & KADM5_TL_DATA) {
+        /* remove entry->tl_data from the front of kdb.tl_data */
+        tl_data_tail->tl_data_next = NULL;
+        kdb.tl_data = tl_data_orig;
+    }
+    
+    ret = KADM5_OK;
+done:
+    if (have_opol) {
+        ret2 = kadm5_free_policy_ent(handle->lhandle, &opol);
+        ret = ret ? ret : ret2;
+    }
+    if (have_npol) {
+        ret2 = kadm5_free_policy_ent(handle->lhandle, &npol);
+        ret = ret ? ret : ret2;
+    }
+    kdb_free_entry(handle, &kdb, &adb);
+    return ret;
+}
+    
+kadm5_ret_t
+kadm5_rename_principal(void *server_handle,
+                           krb5_principal source, krb5_principal target)
+{
+    krb5_db_entry      kdb;
+    osa_princ_ent_rec  adb;
+    int                        ret, i;
+    kadm5_server_handle_t handle = server_handle;
+
+    CHECK_HANDLE(server_handle);
+
+    if (source == NULL || target == NULL)
+       return EINVAL;
+
+    if ((ret = kdb_get_entry(handle, target, &kdb, &adb)) == 0) {
+       kdb_free_entry(handle, &kdb, &adb);
+       return(KADM5_DUP);
+    }
+
+    if ((ret = kdb_get_entry(handle, source, &kdb, &adb)))
+       return ret;
+
+    /* this is kinda gross, but unavoidable */
+
+    for (i=0; i<kdb.n_key_data; i++) {
+       if ((kdb.key_data[i].key_data_ver == 1) ||
+           (kdb.key_data[i].key_data_type[1] == KRB5_KDB_SALTTYPE_NORMAL)) {
+           ret = KADM5_NO_RENAME_SALT;
+           goto done;
+       }
+    }
+
+    krb5_free_principal(handle->context, kdb.princ);
+    if (ret = krb5_copy_principal(handle->context, target, &kdb.princ)) {
+       kdb.princ = NULL; /* so freeing the dbe doesn't lose */
+       goto done;
+    }
+
+    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
+       goto done;
+
+    ret = kdb_delete_entry(handle, source);
+
+done:
+    kdb_free_entry(handle, &kdb, &adb);
+    return ret;
+}
+
+kadm5_ret_t
+kadm5_get_principal(void *server_handle, krb5_principal principal,
+                   kadm5_principal_ent_t entry,
+                   long in_mask)
+{
+    krb5_db_entry              kdb;
+    osa_princ_ent_rec          adb;
+    osa_adb_ret_t              ret = 0;
+    long                       mask;
+    int i;
+    kadm5_server_handle_t handle = server_handle;
+    kadm5_principal_ent_rec    entry_local, *entry_orig;
+
+    CHECK_HANDLE(server_handle);
+
+    /*
+     * In version 1, all the defined fields are always returned.
+     * entry is a pointer to a kadm5_principal_ent_t_v1 that should be
+     * filled with allocated memory.
+     */
+    if (handle->api_version == KADM5_API_VERSION_1) {
+        mask = KADM5_PRINCIPAL_NORMAL_MASK;
+        entry_orig = entry;
+        entry = &entry_local;
+    } else {
+        mask = in_mask;
+    }
+
+    memset((char *) entry, 0, sizeof(*entry));
+
+    if (principal == NULL)
+       return EINVAL;
+
+    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+       return ret;
+
+    if ((mask & KADM5_POLICY) &&
+       adb.policy && (adb.aux_attributes & KADM5_POLICY)) {
+       if ((entry->policy = (char *) malloc(strlen(adb.policy) + 1)) == NULL) {
+           ret = ENOMEM;
+           goto done;
+       }
+       strcpy(entry->policy, adb.policy);
+    }
+
+    if (mask & KADM5_AUX_ATTRIBUTES)
+        entry->aux_attributes = adb.aux_attributes;
+
+    if ((mask & KADM5_PRINCIPAL) &&
+       (ret = krb5_copy_principal(handle->context, principal,
+                                  &entry->principal))) { 
+       goto done;
+    }
+
+    if (mask & KADM5_PRINC_EXPIRE_TIME)
+        entry->princ_expire_time = kdb.expiration;
+
+    if ((mask & KADM5_LAST_PWD_CHANGE) &&
+       (ret = krb5_dbe_lookup_last_pwd_change(handle->context, &kdb,
+                                              &(entry->last_pwd_change)))) {
+       goto done;
+    }
+
+    if (mask & KADM5_PW_EXPIRATION)
+        entry->pw_expiration = kdb.pw_expiration;
+    if (mask & KADM5_MAX_LIFE)
+        entry->max_life = kdb.max_life;
+
+    /* this is a little non-sensical because the function returns two */
+    /* values that must be checked separately against the mask */
+    if ((mask & KADM5_MOD_NAME) || (mask & KADM5_MOD_TIME)) {
+        if (ret = krb5_dbe_lookup_mod_princ_data(handle->context, &kdb,
+                                                 &(entry->mod_date),
+                                                 &(entry->mod_name))) {
+             goto done;
+        }
+        if (! (mask & KADM5_MOD_TIME))
+             entry->mod_date = 0;
+        if (! (mask & KADM5_MOD_NAME)) {
+             krb5_free_principal(handle->context, entry->principal);
+             entry->principal = NULL;
+        }
+    }
+
+    if (mask & KADM5_ATTRIBUTES)
+        entry->attributes = kdb.attributes;
+
+    if (mask & KADM5_KVNO)
+        for (entry->kvno = 0, i=0; i<kdb.n_key_data; i++)
+             if (kdb.key_data[i].key_data_kvno > entry->kvno)
+                  entry->kvno = kdb.key_data[i].key_data_kvno;
+    
+    if (handle->api_version == KADM5_API_VERSION_2)
+        entry->mkvno = 0;
+    else {
+        /* XXX I'll be damned if I know how to deal with this one --marc */
+        entry->mkvno = 1;
+    }
+
+    /*
+     * The new fields that only exist in version 2 start here
+     */
+    if (handle->api_version == KADM5_API_VERSION_2) {
+        if (mask & KADM5_MAX_RLIFE)
+             entry->max_renewable_life = kdb.max_renewable_life;
+        if (mask & KADM5_LAST_SUCCESS)
+             entry->last_success = kdb.last_success;
+        if (mask & KADM5_LAST_FAILED)
+             entry->last_failed = kdb.last_failed;
+        if (mask & KADM5_FAIL_AUTH_COUNT)
+             entry->fail_auth_count = kdb.fail_auth_count;
+        if (mask & KADM5_TL_DATA) {
+             krb5_tl_data td, *tl, *tl2;
+
+             entry->n_tl_data = kdb.n_tl_data;
+             entry->tl_data = NULL;
+             
+             tl = kdb.tl_data;
+             while (tl) {
+                  if ((tl2 = dup_tl_data(tl)) == NULL) {
+                       ret = ENOMEM;
+                       goto done;
+                  }
+                  tl2->tl_data_next = entry->tl_data;
+                  entry->tl_data = tl2;
+
+                  tl = tl->tl_data_next;
+             }
+             
+             if (kdb.e_length) {
+                  td.tl_data_type = KRB5_TL_KADM5_E_DATA;
+                  td.tl_data_length = kdb.e_length;
+                  td.tl_data_contents = kdb.e_data;
+
+                  if ((tl = dup_tl_data(&td)) == NULL) {
+                       ret = ENOMEM;
+                       goto done;
+                  }
+                  tl->tl_data_next = entry->tl_data;
+                  entry->tl_data = tl;
+             }
+        }
+        if (mask & KADM5_KEY_DATA) {
+             entry->n_key_data = kdb.n_key_data;
+             entry->key_data = (krb5_key_data *)
+                  malloc(entry->n_key_data*sizeof(krb5_key_data));
+             if (entry->key_data == NULL) {
+                  ret = ENOMEM;
+                  goto done;
+             }
+             for (i = 0; i < entry->n_key_data; i++)
+                  if (ret = krb5_copy_key_data_contents(handle->context,
+                                                        &kdb.key_data[i],
+                                                        &entry->key_data[i]))
+                       goto done;
+        }
+    }
+
+    /*
+     * If KADM5_API_VERSION_1, we return an allocated structure, and
+     * we need to convert the new structure back into the format the
+     * caller is expecting.
+     */
+    if (handle->api_version == KADM5_API_VERSION_1) {
+        kadm5_principal_ent_t_v1 newv1;
+
+        newv1 = ((kadm5_principal_ent_t_v1) calloc(1, sizeof(*newv1)));
+        if (newv1 == NULL) {
+             ret = ENOMEM;
+             goto done;
+        }
+        
+        newv1->principal = entry->principal;
+        newv1->princ_expire_time = entry->princ_expire_time;
+        newv1->last_pwd_change = entry->last_pwd_change;
+        newv1->pw_expiration = entry->pw_expiration;
+        newv1->max_life = entry->max_life;
+        newv1->mod_name = entry->mod_name;
+        newv1->mod_date = entry->mod_date;
+        newv1->attributes = entry->attributes;
+        newv1->kvno = entry->kvno;
+        newv1->mkvno = entry->mkvno;
+        newv1->policy = entry->policy;
+        newv1->aux_attributes = entry->aux_attributes;
+
+        *((kadm5_principal_ent_t_v1 *) entry_orig) = newv1;
+    }
+
+    ret = KADM5_OK;
+
+done:
+    if (ret && entry->principal)
+        krb5_free_principal(handle->context, entry->principal);
+    kdb_free_entry(handle, &kdb, &adb);
+
+    return ret;
+}
+
+/*
+ * Function: check_pw_reuse
+ *
+ * Purpose: Check if a key appears in a list of keys, in order to
+ * enforce password history.
+ *
+ * Arguments:
+ *
+ *     context                 (r) the krb5 context
+ *     histkey_encblock        (r) the encblock that hist_key_data is
+ *                             encrypted in
+ *     n_new_key_data          (r) length of new_key_data
+ *     new_key_data            (r) keys to check against
+ *                             pw_hist_data, encrypted in histkey_encblock
+ *     n_pw_hist_data          (r) length of pw_hist_data
+ *     pw_hist_data            (r) passwords to check new_key_data against
+ *
+ * Effects:
+ * For each new_key in new_key_data:
+ *     decrypt new_key with the master_encblock
+ *     for each password in pw_hist_data:
+ *             for each hist_key in password:
+ *                     decrypt hist_key with histkey_encblock
+ *                     compare the new_key and hist_key
+ *
+ * Returns krb5 errors, KADM5_PASS_RESUSE if a key in
+ * new_key_data is the same as a key in pw_hist_data, or 0.
+ */
+static kadm5_ret_t
+check_pw_reuse(krb5_context context,
+              krb5_encrypt_block *histkey_encblock,
+              int n_new_key_data, krb5_key_data *new_key_data,
+              int n_pw_hist_data, osa_pw_hist_ent *pw_hist_data)
+{
+    int x, y, z;
+    krb5_keyblock newkey, histkey;
+    krb5_error_code ret;
+
+    for (x = 0; x < n_new_key_data; x++) {
+        if (ret = krb5_dbekd_decrypt_key_data(context,
+                                              &master_encblock,
+                                              &(new_key_data[x]),
+                                              &newkey, NULL))
+           return(ret);
+       for (y = 0; y < n_pw_hist_data; y++) {
+            for (z = 0; z < pw_hist_data[y].n_key_data; z++) {
+                 if (ret =
+                     krb5_dbekd_decrypt_key_data(context,
+                                                 histkey_encblock,
+                                                 &pw_hist_data[y].key_data[z],
+                                                 &histkey, NULL))
+                      return(ret);             
+                 
+                 if ((newkey.length == histkey.length) &&
+                     (newkey.enctype == histkey.enctype) &&
+                     (memcmp(newkey.contents, histkey.contents,
+                             histkey.length) == 0)) {
+                      krb5_free_keyblock_contents(context, &histkey);
+                      krb5_free_keyblock_contents(context, &newkey);
+                      
+                      return(KADM5_PASS_REUSE);
+                 }
+                 krb5_free_keyblock_contents(context, &histkey);
+            }
+       }
+       krb5_free_keyblock_contents(context, &newkey);
+    }
+
+    return(0);
+}
+
+/*
+ * Function: create_history_entry
+ *
+ * Purpose: Creates a password history entry from an array of
+ * key_data.
+ *
+ * Arguments:
+ *
+ *     context         (r) krb5_context to use
+ *     n_key_data      (r) number of elements in key_data
+ *     key_data        (r) keys to add to the history entry
+ *     hist            (w) history entry to fill in
+ *
+ * Effects:
+ *
+ * hist->key_data is allocated to store n_key_data key_datas.  Each
+ * element of key_data is decrypted with master_encblock, re-encrypted
+ * in hist_encblock, and added to hist->key_data.  hist->n_key_data is
+ * set to n_key_data.
+ */
+int create_history_entry(krb5_context context, int n_key_data,
+                        krb5_key_data *key_data, osa_pw_hist_ent *hist)
+{
+     int i, ret;
+     krb5_keyblock key;
+     krb5_keysalt salt;
+     
+     hist->key_data = (krb5_key_data*)malloc(n_key_data*sizeof(krb5_key_data));
+     if (hist->key_data == NULL)
+         return ENOMEM;
+     memset(hist->key_data, 0, n_key_data*sizeof(krb5_key_data));
+
+     for (i = 0; i < n_key_data; i++) {
+         if (ret = krb5_dbekd_decrypt_key_data(context,
+                                               &master_encblock,
+                                               &key_data[i],
+                                               &key, &salt))
+              return ret;
+         if (ret = krb5_dbekd_encrypt_key_data(context,
+                                               &hist_encblock,
+                                               &key, &salt,
+                                               key_data[i].key_data_kvno,
+                                               &hist->key_data[i]))
+              return ret;
+         krb5_free_keyblock_contents(context, &key);
+         /* krb5_free_keysalt(context, &salt); */
+     }
+
+     hist->n_key_data = n_key_data;
+     return 0;
+}
+
+int free_history_entry(krb5_context context, osa_pw_hist_ent *hist)
+{
+     int i;
+
+     for (i = 0; i < hist->n_key_data; i++)
+         krb5_free_key_data_contents(context, &hist->key_data[i]);
+     free(hist->key_data);
+}
+
+/*
+ * Function: add_to_history
+ *
+ * Purpose: Adds a password to a principal's password history.
+ *
+ * Arguments:
+ *
+ *     context         (r) krb5_context to use
+ *     adb             (r/w) admin principal entry to add keys to
+ *     pol             (r) adb's policy
+ *     pw              (r) keys for the password to add to adb's key history
+ *
+ * Effects:
+ *
+ * add_to_history adds a single password to adb's password history.
+ * pw contains n_key_data keys in its key_data, in storage should be
+ * allocated but not freed by the caller (XXX blech!).
+ *
+ * This function maintains adb->old_keys as a circular queue.  It
+ * starts empty, and grows each time this function is called until it
+ * is pol->pw_history_num items long.  adb->old_key_len holds the
+ * number of allocated entries in the array, and must therefore be [0,
+ * pol->pw_history_num).  adb->old_key_next is the index into the
+ * array where the next element should be written, and must be [0,
+ * adb->old_key_len).
+ */
+static kadm5_ret_t add_to_history(krb5_context context,
+                                 osa_princ_ent_t adb,
+                                 kadm5_policy_ent_t pol,
+                                 osa_pw_hist_ent *pw)
+{
+     osa_pw_hist_ent hist, *histp;
+     int ret, i;
+
+     /* A history of 1 means just check the current password */
+     if (pol->pw_history_num == 1)
+         return 0;
+
+     /* resize the adb->old_keys array if necessary */
+     if (adb->old_key_len < pol->pw_history_num-1) {
+         adb->old_keys = (osa_pw_hist_ent *)
+              realloc(adb->old_keys,
+                      (adb->old_key_len+1)*sizeof(osa_pw_hist_ent));
+         if (adb->old_keys == NULL)
+              return(ENOMEM);
+         
+         memset(&adb->old_keys[adb->old_key_len],0,sizeof(osa_pw_hist_ent)); 
+         adb->old_key_len++;
+     }
+
+     /* free the old pw history entry if it contains data */
+     histp = &adb->old_keys[adb->old_key_next];
+     for (i = 0; i < histp->n_key_data; i++)
+         krb5_free_key_data_contents(context, &histp->key_data[i]);
+     
+     /* store the new entry */
+     adb->old_keys[adb->old_key_next] = *pw;
+
+     /* update the next pointer */
+     if (++adb->old_key_next == pol->pw_history_num-1)
+              adb->old_key_next = 0;
+
+     return(0);
+}
+
+kadm5_ret_t
+kadm5_chpass_principal(void *server_handle,
+                           krb5_principal principal, char *password)
+{
+    krb5_int32                 now;
+    kadm5_policy_ent_rec       pol;
+    osa_princ_ent_rec          adb;
+    krb5_db_entry              kdb, kdb_save;
+    int                                ret, ret2, last_pwd, i, hist_added;
+    int                                have_pol = 0;
+    kadm5_server_handle_t      handle = server_handle;
+    osa_pw_hist_ent            hist;
+
+    CHECK_HANDLE(server_handle);
+
+    hist_added = 0;
+    memset(&hist, 0, sizeof(hist));
+
+    if (principal == NULL || password == NULL)
+       return EINVAL;
+    if ((krb5_principal_compare(handle->context,
+                               principal, hist_princ)) == TRUE)
+       return KADM5_PROTECT_PRINCIPAL;
+
+    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+       return(ret);
+
+    /* we are going to need the current keys after the new keys are set */
+    if ((ret = kdb_get_entry(handle, principal, &kdb_save, NULL))) {
+        kdb_free_entry(handle, &kdb, &adb);
+        return(ret);
+    }
+    
+    if ((adb.aux_attributes & KADM5_POLICY)) {
+       if ((ret = kadm5_get_policy(handle->lhandle, adb.policy, &pol)))
+            goto done;
+       have_pol = 1;
+    }
+
+    if ((ret = passwd_check(handle, password, adb.aux_attributes &
+                           KADM5_POLICY, &pol, principal)))
+        goto done;
+
+    if (ret = krb5_dbe_cpw(handle->context, &master_encblock,
+                          handle->params.keysalts,
+                          handle->params.num_keysalts,
+                          password, 0 /* increment kvno */, &kdb))
+       goto done;
+
+    kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+
+    if (ret = krb5_timeofday(handle->context, &now))
+        goto done;
+    
+    if ((adb.aux_attributes & KADM5_POLICY)) {
+       /* the policy was loaded before */
+
+       if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
+                                                 &kdb, &last_pwd))
+            goto done;
+
+#if 0
+        /*
+         * The spec says this check is overridden if the caller has
+         * modify privilege.  The admin server therefore makes this
+         * check itself (in chpass_principal_wrapper, misc.c). A
+         * local caller implicitly has all authorization bits.
+         */
+       if ((now - last_pwd) < pol.pw_min_life &&
+           !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+            ret = KADM5_PASS_TOOSOON;
+            goto done;
+       }
+#endif
+
+       if (ret = create_history_entry(handle->context,
+                                      kdb_save.n_key_data,
+                                      kdb_save.key_data, &hist))
+            goto done;
+
+       if (ret = check_pw_reuse(handle->context,
+                                &hist_encblock,
+                                kdb.n_key_data, kdb.key_data,
+                                1, &hist))
+            goto done;
+        
+       if (pol.pw_history_num > 1) {
+           if (adb.admin_history_kvno != hist_kvno) {
+               ret = KADM5_BAD_HIST_KEY;
+               goto done;
+           }
+
+           if (ret = check_pw_reuse(handle->context,
+                                    &hist_encblock,
+                                    kdb.n_key_data, kdb.key_data,
+                                    adb.old_key_len, adb.old_keys))
+               goto done;
+
+           if (ret = add_to_history(handle->context, &adb, &pol, &hist))
+                goto done;
+           hist_added = 1;
+       }
+
+       if (pol.pw_max_life)
+          kdb.pw_expiration = now + pol.pw_max_life;
+       else
+          kdb.pw_expiration = 0;
+    } else {
+       kdb.pw_expiration = 0;
+    }
+
+    if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
+       goto done;
+
+    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
+       goto done;
+
+    ret = KADM5_OK;
+done:
+    if (!hist_added && hist.key_data)
+        free_history_entry(handle->context, &hist);
+    kdb_free_entry(handle, &kdb, &adb);
+    kdb_free_entry(handle, &kdb_save, NULL);
+    krb5_dbe_free_contents(handle->context, &kdb);
+
+    if (have_pol && (ret2 = kadm5_free_policy_ent(handle->lhandle, &pol))
+       && !ret) 
+        ret = ret2;
+
+    return ret;
+}
+
+kadm5_ret_t
+kadm5_randkey_principal(void *server_handle,
+                       krb5_principal principal,
+                       krb5_keyblock **keyblocks,
+                       int *n_keys)
+{
+    krb5_db_entry              kdb;
+    osa_princ_ent_rec          adb;
+    krb5_int32                 now;
+    kadm5_policy_ent_rec       pol;
+    krb5_key_data              *key_data;
+    krb5_keyblock              *keyblock;
+    int                                ret, last_pwd, have_pol = 0;
+    kadm5_server_handle_t      handle = server_handle;
+
+    if (keyblocks)
+        *keyblocks = NULL;
+
+    CHECK_HANDLE(server_handle);
+
+    if (principal == NULL)
+       return EINVAL;
+    if (hist_princ && /* this will be NULL when initializing the databse */
+       ((krb5_principal_compare(handle->context,
+                                principal, hist_princ)) == TRUE))
+       return KADM5_PROTECT_PRINCIPAL;
+
+    if ((ret = kdb_get_entry(handle, principal, &kdb, &adb)))
+       return(ret);
+
+    if (ret = krb5_dbe_crk(handle->context, &master_encblock,
+                          handle->params.keysalts,
+                          handle->params.num_keysalts,
+                          &kdb))
+       goto done;
+
+    kdb.attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+
+    if (ret = krb5_timeofday(handle->context, &now))
+       goto done;
+
+    if ((adb.aux_attributes & KADM5_POLICY)) {
+       if ((ret = kadm5_get_policy(handle->lhandle, adb.policy,
+                                   &pol)) != KADM5_OK) 
+          goto done;
+       have_pol = 1;
+
+       if (ret = krb5_dbe_lookup_last_pwd_change(handle->context,
+                                                 &kdb, &last_pwd))
+            goto done;
+
+#if 0
+        /*
+         * The spec says this check is overridden if the caller has
+         * modify privilege.  The admin server therefore makes this
+         * check itself (in chpass_principal_wrapper, misc.c).  A
+         * local caller implicitly has all authorization bits.
+         */
+       if((now - last_pwd) < pol.pw_min_life &&
+          !(kdb.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+            ret = KADM5_PASS_TOOSOON;
+            goto done;
+       }
+#endif
+
+       if(pol.pw_history_num > 1) {
+           if(adb.admin_history_kvno != hist_kvno) {
+               ret = KADM5_BAD_HIST_KEY;
+               goto done;
+           }
+
+           if (ret = check_pw_reuse(handle->context,
+                                    &hist_encblock,
+                                    kdb.n_key_data, kdb.key_data,
+                                    adb.old_key_len, adb.old_keys))
+               goto done;
+       }
+       if (pol.pw_max_life)
+          kdb.pw_expiration = now + pol.pw_max_life;
+       else
+          kdb.pw_expiration = 0;
+    } else {
+       kdb.pw_expiration = 0;
+    }
+
+    if (ret = krb5_dbe_update_last_pwd_change(handle->context, &kdb, now))
+        goto done;
+
+    if (keyblocks) {
+        if (handle->api_version == KADM5_API_VERSION_1) {
+             /* Version 1 clients will expect to see a DES_CRC enctype. */
+             if (ret = krb5_dbe_find_enctype(handle->context, &kdb,
+                                             ENCTYPE_DES_CBC_CRC,
+                                             -1, -1, &key_data))
+                  goto done;
+
+             if (ret = decrypt_key_data(handle->context, 1, key_data,
+                                        keyblocks, NULL))
+                  goto done;
+        } else {
+             ret = decrypt_key_data(handle->context,
+                                    kdb.n_key_data, kdb.key_data,
+                                    keyblocks, n_keys);
+             if (ret)
+                  goto done;
+        }
+    }   
+    
+    if ((ret = kdb_put_entry(handle, &kdb, &adb)))
+       goto done;
+
+    ret = KADM5_OK;
+done:
+    kdb_free_entry(handle, &kdb, &adb);
+    if (have_pol)
+        kadm5_free_policy_ent(handle->lhandle, &pol);
+
+    return ret;
+}
+
+/*
+ * Allocate an array of n_key_data krb5_keyblocks, fill in each
+ * element with the results of decrypting the nth key in key_data with
+ * master_encblock, and if n_keys is not NULL fill it in with the
+ * number of keys decrypted.
+ */
+static int decrypt_key_data(krb5_context context,
+                           int n_key_data, krb5_key_data *key_data,
+                           krb5_keyblock **keyblocks, int *n_keys)
+{
+     krb5_keyblock *keys;
+     int ret, i;
+
+     keys = (krb5_keyblock *) malloc(n_key_data*sizeof(krb5_keyblock));
+     if (keys == NULL)
+         return ENOMEM;
+     memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
+
+     for (i = 0; i < n_key_data; i++) {
+         if (ret = krb5_dbekd_decrypt_key_data(context,
+                                               &master_encblock,
+                                               &key_data[i], 
+                                               &keys[i], NULL)) {
+
+              memset((char *) keys, 0, n_key_data*sizeof(krb5_keyblock));
+              free(keys);
+              return ret;
+         }
+     }
+
+     *keyblocks = keys;
+     if (n_keys)
+         *n_keys = n_key_data;
+
+     return 0;
+}
+
+/*
+ * Function: kadm5_decrypt_key
+ *
+ * Purpose: Retrieves and decrypts a principal key.
+ *
+ * Arguments:
+ *
+ *     server_handle   (r) kadm5 handle
+ *     entry           (r) principal retrieved with kadm5_get_principal
+ *     ktype           (r) enctype to search for, or -1 to ignore
+ *     stype           (r) salt type to search for, or -1 to ignore
+ *     kvno            (r) kvno to search for, -1 for max, 0 for max
+ *                     only if it also matches ktype and stype
+ *     keyblock        (w) keyblock to fill in
+ *     keysalt         (w) keysalt to fill in, or NULL
+ *     kvnop           (w) kvno to fill in, or NULL
+ *
+ * Effects: Searches the key_data array of entry, which must have been
+ * retrived with kadm5_get_principal with the KADM5_KEY_DATA mask, to
+ * find a key with a specified enctype, salt type, and kvno in a
+ * principal entry.  If not found, return ENOENT.  Otherwise, decrypt
+ * it with the master key, and return the key in keyblock, the salt
+ * in salttype, and the key version number in kvno.
+ *
+ * If ktype or stype is -1, it is ignored for the search.  If kvno is
+ * -1, ktype and stype are ignored and the key with the max kvno is
+ * returned.  If kvno is 0, only the key with the max kvno is returned
+ * and only if it matches the ktype and stype; otherwise, ENOENT is
+ * returned.
+ */
+kadm5_ret_t kadm5_decrypt_key(void *server_handle,
+                             kadm5_principal_ent_t entry, krb5_int32
+                             ktype, krb5_int32 stype, krb5_int32
+                             kvno, krb5_keyblock *keyblock,
+                             krb5_keysalt *keysalt, int *kvnop)
+{
+    kadm5_server_handle_t handle = server_handle;
+    krb5_db_entry dbent;
+    krb5_key_data *key_data;
+    int ret;
+
+    CHECK_HANDLE(server_handle);
+
+    if (entry->n_key_data == 0 || entry->key_data == NULL)
+        return EINVAL;
+
+    /* find_enctype only uses these two fields */
+    dbent.n_key_data = entry->n_key_data;
+    dbent.key_data = entry->key_data;
+    if (ret = krb5_dbe_find_enctype(handle->context, &dbent, ktype,
+                                   stype, kvno, &key_data))
+        return ret;
+
+    if (ret = krb5_dbekd_decrypt_key_data(handle->context,
+                                         &master_encblock, key_data,
+                                         keyblock, keysalt))
+        return ret;
+
+    if (kvnop)
+        *kvnop = key_data->key_data_kvno;
+
+    return KADM5_OK;
+}