aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib/kadm5/srv/ChangeLog5
-rw-r--r--src/lib/kadm5/srv/Makefile.in92
-rw-r--r--src/lib/kadm5/srv/adb_free.c75
-rw-r--r--src/lib/kadm5/srv/adb_openclose.c338
-rw-r--r--src/lib/kadm5/srv/adb_policy.c401
-rw-r--r--src/lib/kadm5/srv/adb_principal.c412
-rw-r--r--src/lib/kadm5/srv/adb_xdr.c132
-rw-r--r--src/lib/kadm5/srv/configure.in42
-rw-r--r--src/lib/kadm5/srv/server_acl.c511
-rw-r--r--src/lib/kadm5/srv/server_acl.h81
-rw-r--r--src/lib/kadm5/srv/server_dict.c199
-rw-r--r--src/lib/kadm5/srv/server_handle.c9
-rw-r--r--src/lib/kadm5/srv/server_init.c330
-rw-r--r--src/lib/kadm5/srv/server_kdb.c424
-rw-r--r--src/lib/kadm5/srv/server_misc.c101
-rw-r--r--src/lib/kadm5/srv/svr_chpass_util.c15
-rw-r--r--src/lib/kadm5/srv/svr_iters.c248
-rw-r--r--src/lib/kadm5/srv/svr_misc_free.c37
-rw-r--r--src/lib/kadm5/srv/svr_policy.c315
-rw-r--r--src/lib/kadm5/srv/svr_principal.c1350
20 files changed, 5117 insertions, 0 deletions
diff --git a/src/lib/kadm5/srv/ChangeLog b/src/lib/kadm5/srv/ChangeLog
new file mode 100644
index 0000000..f91603f
--- /dev/null
+++ b/src/lib/kadm5/srv/ChangeLog
@@ -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
index 0000000..4308ed4
--- /dev/null
+++ b/src/lib/kadm5/srv/Makefile.in
@@ -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
index 0000000..a58af17
--- /dev/null
+++ b/src/lib/kadm5/srv/adb_free.c
@@ -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
index 0000000..627a6b4
--- /dev/null
+++ b/src/lib/kadm5/srv/adb_openclose.c
@@ -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
index 0000000..ff0117b
--- /dev/null
+++ b/src/lib/kadm5/srv/adb_policy.c
@@ -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
index 0000000..8c2e9e2
--- /dev/null
+++ b/src/lib/kadm5/srv/adb_principal.c
@@ -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
index 0000000..944fb04
--- /dev/null
+++ b/src/lib/kadm5/srv/adb_xdr.c
@@ -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
index 0000000..f17fd5c
--- /dev/null
+++ b/src/lib/kadm5/srv/configure.in
@@ -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
index 0000000..16a7f4e
--- /dev/null
+++ b/src/lib/kadm5/srv/server_acl.c
@@ -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";
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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"));
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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"));
+}
+
+/*
+ * 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
index 0000000..9dfc8da
--- /dev/null
+++ b/src/lib/kadm5/srv/server_acl.h
@@ -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
index 0000000..6c0bcef
--- /dev/null
+++ b/src/lib/kadm5/srv/server_dict.c
@@ -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
index 0000000..53abe94
--- /dev/null
+++ b/src/lib/kadm5/srv/server_handle.c
@@ -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
index 0000000..653f6d8
--- /dev/null
+++ b/src/lib/kadm5/srv/server_init.c
@@ -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
index 0000000..1a900a3
--- /dev/null
+++ b/src/lib/kadm5/srv/server_kdb.c
@@ -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
index 0000000..24f101c
--- /dev/null
+++ b/src/lib/kadm5/srv/server_misc.c
@@ -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
index 0000000..df2bf4c
--- /dev/null
+++ b/src/lib/kadm5/srv/svr_chpass_util.c
@@ -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
index 0000000..19c9000
--- /dev/null
+++ b/src/lib/kadm5/srv/svr_iters.c
@@ -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
index 0000000..5c76a1e
--- /dev/null
+++ b/src/lib/kadm5/srv/svr_misc_free.c
@@ -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
index 0000000..74e2521
--- /dev/null
+++ b/src/lib/kadm5/srv/svr_policy.c
@@ -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
index 0000000..6f9671f
--- /dev/null
+++ b/src/lib/kadm5/srv/svr_principal.c
@@ -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;
+}