diff options
author | Ken Raeburn <raeburn@mit.edu> | 2005-06-21 01:36:03 +0000 |
---|---|---|
committer | Ken Raeburn <raeburn@mit.edu> | 2005-06-21 01:36:03 +0000 |
commit | f72c3ffaca4600d4e75282857ce4dda11106d5e7 (patch) | |
tree | 89df6e717f00e1687994fc089fd6df9ff8a5c21c /src/lib/kdb/kdb_db2 | |
parent | f4aaa29ac68e3dd5a2ae326cd54918c7250558e9 (diff) | |
download | krb5-f72c3ffaca4600d4e75282857ce4dda11106d5e7.tar.gz krb5-f72c3ffaca4600d4e75282857ce4dda11106d5e7.tar.xz krb5-f72c3ffaca4600d4e75282857ce4dda11106d5e7.zip |
Novell Database Abstraction Layer merge.
Will probably break things.
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@17258 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/kdb/kdb_db2')
-rw-r--r-- | src/lib/kdb/kdb_db2/ChangeLog | 17 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/Makefile.in | 150 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/adb_openclose.c | 412 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/adb_policy.c | 389 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/configure.in | 22 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/db2_exp.c | 64 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/kdb_compat.h | 81 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/kdb_db2.c | 2000 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/kdb_db2.h | 216 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/kdb_xdr.c | 816 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/kdb_xdr.h | 32 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/libkdb_db2.exports | 1 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/pol_xdr.c | 88 | ||||
-rw-r--r-- | src/lib/kdb/kdb_db2/policy_db.h | 87 |
14 files changed, 4375 insertions, 0 deletions
diff --git a/src/lib/kdb/kdb_db2/ChangeLog b/src/lib/kdb/kdb_db2/ChangeLog new file mode 100644 index 000000000..6ebc2a0e8 --- /dev/null +++ b/src/lib/kdb/kdb_db2/ChangeLog @@ -0,0 +1,17 @@ +2005-06-20 Ken Raeburn <raeburn@mit.edu> + + Novell merge. + * Makefile.in: + * adb_openclose.c: + * adb_policy.c: + * configure.in: + * db2_exp.c: + * kdb_compat.h: + * kdb_db2.c: + * kdb_db2.h: + * kdb_xdr.c: + * kdb_xdr.h: + * libkdb_db2.exports: + * pol_xdr.c: + * policy_db.h: + diff --git a/src/lib/kdb/kdb_db2/Makefile.in b/src/lib/kdb/kdb_db2/Makefile.in new file mode 100644 index 000000000..ebd39f0c7 --- /dev/null +++ b/src/lib/kdb/kdb_db2/Makefile.in @@ -0,0 +1,150 @@ +thisconfigdir=. +myfulldir=lib/kdb/kdb_db2 +mydir=. +BUILDTOP=$(REL)..$(S)..$(S).. +KRB5_RUN_ENV = @KRB5_RUN_ENV@ +KRB5_CONFIG_SETUP = KRB5_CONFIG=$(SRCTOP)/config-files/krb5.conf ; export KRB5_CONFIG ; +PROG_LIBPATH=-L$(TOPLIBD) +PROG_RPATH=$(KRB5_LIBDIR) + +LOCALINCLUDES = -I.. + +LIBBASE=kdb_db2 +LIB=$(LIBBASE) +LIBMAJOR=4 +LIBMINOR=0 +RELDIR=kdb/kdb_db2 +SO_EXT=.so +# Depends on libk5crypto and libkrb5 + +SHLIB_EXPDEPS = \ + $(TOPLIBD)/libk5crypto$(SHLIBEXT) \ + $(TOPLIBD)/libkrb5$(SHLIBEXT) +SHLIB_EXPLIBS=-lkrb5 -lcom_err -lk5crypto $(KDB5_DB_LIB) $(LIBS) +SHLIB_DIRS=-L$(TOPLIBD) +SHLIB_RDIRS=$(KRB5_LIBDIR) + +DBDIR = $(BUILDTOP)/util/db2 +DBOBJLISTS = $(DBOBJLISTS-@DB_VERSION@) +DBOBJLISTS-sys = +DBOBJLISTS-k5 = $(DBDIR)/hash/OBJS.ST $(DBDIR)/btree/OBJS.ST \ + $(DBDIR)/db/OBJS.ST $(DBDIR)/mpool/OBJS.ST $(DBDIR)/recno/OBJS.ST \ + $(DBDIR)/clib/OBJS.ST + +all:: lib$(LIB)$(SO_EXT) + +SRCS= \ + $(srcdir)/kdb_xdr.c \ + $(srcdir)/adb_openclose.c \ + $(srcdir)/adb_policy.c \ + $(srcdir)/kdb_db2.c \ + $(srcdir)/pol_xdr.c \ + $(srcdir)/db2_exp.c + +STOBJLISTS=OBJS.ST $(DBOBJLISTS) +STLIBOBJS= \ + kdb_xdr.o \ + adb_openclose.o \ + adb_policy.o \ + kdb_db2.o \ + pol_xdr.o \ + db2_exp.o + +all-unix:: all-liblinks +install-unix:: install-libs +clean-unix:: clean-liblinks clean-libs clean-libobjs + +lib$(LIB)$(SO_EXT): db2_exp.o # lib$(LIB)$(STLIBEXT) + $(CC) -shared -o $@ -L$(TOPLIBD) $^ -lgssrpc -ldb $(SHLIB_EXPLIBS) + +clean:: + $(RM) lib$(LIB)$(SO_EXT) db2_exp.o + + +t_kdb: t_kdb.o $(OBJS) $(KDB5_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o t_kdb t_kdb.o $(OBJS) $(KDB5_LIBS) $(KRB5_BASE_LIBS) + +check:: t_kdb + $(RM) test_db + $(KRB5_CONFIG_SETUP) $(KRB5_RUN_ENV) ./t_kdb -tcv + $(KRB5_CONFIG_SETUP) $(KRB5_RUN_ENV) ./t_kdb -tcvr + +clean:: + $(RM) t_kdb t_kdb.o + + +adb_openclose.c adb_policy.c : ../adb_err.h + +# @lib_frag@ +# @libobj_frag@ + +# +++ Dependency line eater +++ +# +# Makefile dependencies follow. This must be the last section in +# the Makefile.in file +# +kdb_xdr.so kdb_xdr.po $(OUTPRE)kdb_xdr.$(OBJEXT): kdb_xdr.c \ + $(SRCTOP)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-platform.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-thread.h \ + $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \ + $(SRCTOP)/include/port-sockets.h $(BUILDTOP)/include/krb5/autoconf.h \ + $(SRCTOP)/include/socket-utils.h $(SRCTOP)/include/krb5/kdb.h \ + kdb_xdr.h +adb_openclose.so adb_openclose.po $(OUTPRE)adb_openclose.$(OBJEXT): \ + adb_openclose.c $(SRCTOP)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-platform.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-thread.h \ + $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \ + $(SRCTOP)/include/port-sockets.h $(BUILDTOP)/include/krb5/autoconf.h \ + $(SRCTOP)/include/socket-utils.h $(SRCTOP)/include/krb5/kdb.h \ + policy_db.h $(SRCTOP)/include/krb5/kdb.h $(DB_DEPS) \ + $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/gssrpc/rename.h \ + $(BUILDTOP)/include/gssrpc/xdr.h ../adb_err.h +adb_policy.so adb_policy.po $(OUTPRE)adb_policy.$(OBJEXT): \ + adb_policy.c policy_db.h $(SRCTOP)/include/k5-int.h \ + $(BUILDTOP)/include/krb5/osconf.h $(BUILDTOP)/include/krb5/autoconf.h \ + $(SRCTOP)/include/k5-platform.h $(BUILDTOP)/include/krb5/autoconf.h \ + $(SRCTOP)/include/k5-thread.h $(BUILDTOP)/include/krb5.h \ + $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h $(SRCTOP)/include/port-sockets.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/socket-utils.h \ + $(SRCTOP)/include/krb5/kdb.h $(SRCTOP)/include/krb5/kdb.h \ + $(DB_DEPS) $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/gssrpc/rename.h \ + $(BUILDTOP)/include/gssrpc/xdr.h ../adb_err.h +kdb_db2.so kdb_db2.po $(OUTPRE)kdb_db2.$(OBJEXT): kdb_db2.c \ + $(SRCTOP)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-platform.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-thread.h \ + $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \ + $(SRCTOP)/include/port-sockets.h $(BUILDTOP)/include/krb5/autoconf.h \ + $(SRCTOP)/include/socket-utils.h $(SRCTOP)/include/krb5/kdb.h \ + $(DB_DEPS) $(srcdir)/../kdb5.h $(SRCTOP)/include/k5-int.h \ + $(srcdir)/../err_handle.h kdb_db2.h policy_db.h $(SRCTOP)/include/krb5/kdb.h \ + $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/gssrpc/rename.h \ + $(BUILDTOP)/include/gssrpc/xdr.h ../adb_err.h kdb_xdr.h \ + kdb_compat.h +pol_xdr.so pol_xdr.po $(OUTPRE)pol_xdr.$(OBJEXT): pol_xdr.c \ + $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/gssrpc/rpc.h \ + $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/gssrpc/rename.h \ + $(BUILDTOP)/include/gssrpc/xdr.h $(BUILDTOP)/include/gssrpc/auth.h \ + $(BUILDTOP)/include/gssrpc/clnt.h $(BUILDTOP)/include/gssrpc/rpc_msg.h \ + $(BUILDTOP)/include/gssrpc/auth_unix.h $(BUILDTOP)/include/gssrpc/auth_gss.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/svc_auth.h \ + $(BUILDTOP)/include/gssrpc/svc.h $(SRCTOP)/include/krb5/kdb.h \ + policy_db.h $(SRCTOP)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-platform.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-thread.h \ + $(BUILDTOP)/include/profile.h $(SRCTOP)/include/port-sockets.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/socket-utils.h \ + $(SRCTOP)/include/krb5/kdb.h $(DB_DEPS) ../adb_err.h +db2_exp.so db2_exp.po $(OUTPRE)db2_exp.$(OBJEXT): db2_exp.c \ + $(SRCTOP)/include/k5-int.h $(BUILDTOP)/include/krb5/osconf.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-platform.h \ + $(BUILDTOP)/include/krb5/autoconf.h $(SRCTOP)/include/k5-thread.h \ + $(BUILDTOP)/include/krb5.h $(COM_ERR_DEPS) $(BUILDTOP)/include/profile.h \ + $(SRCTOP)/include/port-sockets.h $(BUILDTOP)/include/krb5/autoconf.h \ + $(SRCTOP)/include/socket-utils.h $(SRCTOP)/include/krb5/kdb.h \ + $(DB_DEPS) $(srcdir)/../kdb5.h $(SRCTOP)/include/k5-int.h \ + $(srcdir)/../err_handle.h kdb_db2.h policy_db.h $(SRCTOP)/include/krb5/kdb.h \ + $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/gssrpc/rename.h \ + $(BUILDTOP)/include/gssrpc/xdr.h ../adb_err.h kdb_xdr.h diff --git a/src/lib/kdb/kdb_db2/adb_openclose.c b/src/lib/kdb/kdb_db2/adb_openclose.c new file mode 100644 index 000000000..97ce1123b --- /dev/null +++ b/src/lib/kdb/kdb_db2/adb_openclose.c @@ -0,0 +1,412 @@ +/* + * 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 <k5-int.h> +#include "policy_db.h" +#include <stdlib.h> +#include <db.h> + +#define MAX_LOCK_TRIES 5 + +struct _locklist { + osa_adb_lock_ent lockinfo; + struct _locklist *next; +}; + +krb5_error_code osa_adb_create_db(char *filename, char *lockfilename, + int magic) +{ + int lf; + DB *db; + BTREEINFO btinfo; + + memset(&btinfo, 0, sizeof(btinfo)); + btinfo.flags = 0; + btinfo.cachesize = 0; + btinfo.psize = 4096; + btinfo.lorder = 0; + btinfo.minkeypage = 0; + btinfo.compare = NULL; + btinfo.prefix = NULL; + db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_BTREE, &btinfo); + if (db == NULL) + return errno; + if (db->close(db) < 0) + return errno; + + /* only create the lock file if we successfully created the db */ + lf = THREEPARAMOPEN(lockfilename, O_RDWR | O_CREAT | O_EXCL, 0600); + if (lf == -1) + return errno; + (void) close(lf); + + return OSA_ADB_OK; +} + +krb5_error_code 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; +} + +krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom, + char *fileto, char *lockto, int magic) +{ + osa_adb_db_t fromdb, todb; + krb5_error_code ret; + + /* make sure todb exists */ + if ((ret = osa_adb_create_db(fileto, lockto, magic)) && + ret != EEXIST) + return ret; + + if ((ret = osa_adb_init_db(&fromdb, filefrom, lockfrom, magic))) + return ret; + if ((ret = osa_adb_init_db(&todb, fileto, lockto, magic))) { + (void) osa_adb_fini_db(fromdb, magic); + return ret; + } + if ((ret = osa_adb_get_lock(fromdb, KRB5_DB_LOCKMODE_PERMANENT))) { + (void) osa_adb_fini_db(fromdb, magic); + (void) osa_adb_fini_db(todb, magic); + return ret; + } + if ((ret = osa_adb_get_lock(todb, KRB5_DB_LOCKMODE_PERMANENT))) { + (void) osa_adb_fini_db(fromdb, magic); + (void) osa_adb_fini_db(todb, magic); + return ret; + } + if ((rename(filefrom, fileto) < 0)) { + (void) osa_adb_fini_db(fromdb, magic); + (void) osa_adb_fini_db(todb, magic); + return errno; + } + /* + * Do not release the lock on fromdb because it is being renamed + * out of existence; no one can ever use it again. + */ + if ((ret = osa_adb_release_lock(todb))) { + (void) osa_adb_fini_db(fromdb, magic); + (void) osa_adb_fini_db(todb, magic); + return ret; + } + + (void) osa_adb_fini_db(fromdb, magic); + (void) osa_adb_fini_db(todb, magic); + return 0; +} + +krb5_error_code 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; + + db->btinfo.flags = 0; + db->btinfo.cachesize = 0; + db->btinfo.psize = 4096; + db->btinfo.lorder = 0; + db->btinfo.minkeypage = 0; + db->btinfo.compare = NULL; + db->btinfo.prefix = NULL; + /* + * 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((krb5_error_code) 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->opencnt = 0; + db->filename = strdup(filename); + db->magic = magic; + + *dbp = db; + + return OSA_ADB_OK; +} + +krb5_error_code 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. + */ + if (db->lock->lockmode != KRB5_DB_LOCKMODE_PERMANENT) + (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; +} + +krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode) +{ + int tries, gotlock, perm, krb5_mode, ret = 0; + + 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 KRB5_DB_LOCKMODE_PERMANENT: + perm = 1; + case KRB5_DB_LOCKMODE_EXCLUSIVE: + krb5_mode = KRB5_LOCKMODE_EXCLUSIVE; + break; + case KRB5_DB_LOCKMODE_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 == KRB5_DB_LOCKMODE_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) { + /* 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; +} + +krb5_error_code osa_adb_release_lock(osa_adb_db_t db) +{ + int ret, fd; + + if (!db->lock->lockcnt) /* lock already unlocked */ + return OSA_ADB_NOTLOCKED; + + if (--db->lock->lockcnt == 0) { + if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) { + /* now we need to create the file since it does not exist */ + fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL, + 0600); + if ((db->lock->lockfile = fdopen(fd, "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; +} + +krb5_error_code 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; + if (db->opencnt) + goto open_ok; + + db->db = dbopen(db->filename, O_RDWR, 0600, DB_BTREE, &db->btinfo); + if (db->db != NULL) + goto open_ok; + switch (errno) { +#ifdef EFTYPE + case EFTYPE: +#endif + case EINVAL: + db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info); + if (db->db != NULL) + goto open_ok; + default: + (void) osa_adb_release_lock(db); + if (errno == EINVAL) + return OSA_ADB_BAD_DB; + return errno; + } +open_ok: + db->opencnt++; + return OSA_ADB_OK; +} + +krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db) +{ + if (--db->opencnt) + return osa_adb_release_lock(db); + if(db->db != NULL && 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/kdb/kdb_db2/adb_policy.c b/src/lib/kdb/kdb_db2/adb_policy.c new file mode 100644 index 000000000..e338cbbd0 --- /dev/null +++ b/src/lib/kdb/kdb_db2/adb_policy.c @@ -0,0 +1,389 @@ +/* + * 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 "policy_db.h" +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#define OPENLOCK(db, mode) \ +{ \ + int olret; \ + if (db == NULL) \ + return EINVAL; \ + else if (db->magic != OSA_ADB_POLICY_DB_MAGIC) \ + return OSA_ADB_DBINIT; \ + else if ((olret = osa_adb_open_and_lock(db, mode)) != OSA_ADB_OK) \ + return olret; \ + } + +#define CLOSELOCK(db) \ +{ \ + int cl_ret; \ + if ((cl_ret = osa_adb_close_and_unlock(db)) != OSA_ADB_OK) \ + return cl_ret; \ +} + + +/* + * 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 success, else error code. + * + * Requires: + * entry have a valid name. + * + * Effects: + * creates the entry in the db + * + * Modifies: + * the policy db. + * + */ +krb5_error_code +osa_adb_create_policy(osa_adb_policy_t db, osa_policy_ent_t entry) +{ + DBT dbkey; + DBT dbdata; + XDR xdrs; + int ret; + + OPENLOCK(db, KRB5_DB_LOCKMODE_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 success, or error code. + * + * Requires: + * db being valid. + * name being non-null. + * Effects: + * deletes policy from db. + * + * Modifies: + * policy db. + * + */ +krb5_error_code +osa_adb_destroy_policy(osa_adb_policy_t db, char *name) +{ + DBT dbkey; + int status, ret; + + OPENLOCK(db, KRB5_DB_LOCKMODE_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 + * cnt (inout) Number of entries + * <return value> 0 on success, error code on failure. + * + * Requires: + * Effects: + * Modifies: + */ +krb5_error_code +osa_adb_get_policy(osa_adb_policy_t db, char *name, + osa_policy_ent_t *entry, int *cnt) +{ + DBT dbkey; + DBT dbdata; + XDR xdrs; + int ret; + char *aligned_data; + + OPENLOCK(db, KRB5_DB_LOCKMODE_SHARED); + + *cnt = 1; + + 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 = 0; + *cnt = 0; + 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 success error code on failure. + * + * Requires: + * [requires] + * + * Effects: + * [effects] + * + * Modifies: + * [modifies] + * + */ +krb5_error_code +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, KRB5_DB_LOCKMODE_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 success error code on failure + * + * Requires: + * Effects: + * Modifies: + */ +krb5_error_code +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, KRB5_DB_LOCKMODE_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; +} + +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/kdb/kdb_db2/configure.in b/src/lib/kdb/kdb_db2/configure.in new file mode 100644 index 000000000..dc002d878 --- /dev/null +++ b/src/lib/kdb/kdb_db2/configure.in @@ -0,0 +1,22 @@ +AC_INIT(configure.in) +CONFIG_RULES +AC_CHECK_HEADERS(unistd.h) +AC_TYPE_MODE_T +AC_TYPE_OFF_T + +AC_CHECK_FUNCS(srand48 srand srandom umask) + +dnl AIX is unusual in that it wants all symbols resolved at link time +dnl Fortunately, it will allow us to link the kdb library now, even if +dnl it is linked again later. +case $krb5_cv_host in +*-*-aix*) + LIBS="$LIBS -ldb" + ;; +esac +KRB5_RUN_FLAGS +dnl The following is for check... +KRB5_BUILD_PROGRAM +KRB5_BUILD_LIBOBJS +KRB5_BUILD_LIBRARY_WITH_DEPS +V5_AC_OUTPUT_MAKEFILE diff --git a/src/lib/kdb/kdb_db2/db2_exp.c b/src/lib/kdb/kdb_db2/db2_exp.c new file mode 100644 index 000000000..f511487f1 --- /dev/null +++ b/src/lib/kdb/kdb_db2/db2_exp.c @@ -0,0 +1,64 @@ +/********************************************************************** +* +* C %name: db2_exp.c % +* Instance: idc_sec_2 +* Description: +* %created_by: spradeep % +* %date_created: Tue Apr 5 11:44:00 2005 % +* +**********************************************************************/ +#ifndef lint +static char *_csrc = "@(#) %filespec: db2_exp.c~5 % (%full_filespec: db2_exp.c~5:csrc:idc_sec#2 %)"; +#endif + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "k5-int.h" +#include <db.h> +#include <stdio.h> +#include <errno.h> +#include <utime.h> +#include "../kdb5.h" +#include "kdb_db2.h" +#include "kdb_xdr.h" +#include "policy_db.h" + +/* + * Exposed API + */ + +kdb_vftabl krb5_db_vftabl_kdb_db2 = { + 1, /* major version number 1 */ + 0, /* minor version number 0 */ + 0, /* TBD. Not sure whether thread safe. For now, its not */ + /* init_library */ krb5_db2_lib_init, + /* fini_library */ krb5_db2_lib_cleanup, + /* init_module */ krb5_db2_open, + /* fini_module */ krb5_db2_db_fini, + /* db_create */ krb5_db2_create, + /* db_destroy */ krb5_db2_destroy, + /* db_get_age */ krb5_db2_db_get_age, + /* db_set_option */ krb5_db2_db_set_option, + /* db_lock */ krb5_db2_db_lock, + /* db_unlock */ krb5_db2_db_unlock, + /* db_get_principal */ krb5_db2_db_get_principal, + /* db_free_principal */ krb5_db2_db_free_principal, + /* db_put_principal */ krb5_db2_db_put_principal, + /* db_delete_principal */ krb5_db2_db_delete_principal, + /* db_iterate */ krb5_db2_db_iterate, + /* db_create_policy */ krb5_db2_create_policy, + /* db_get_policy */ krb5_db2_get_policy, + /* db_put_policy */ krb5_db2_put_policy, + /* db_iter_policy */ krb5_db2_iter_policy, + /* db_delete_policy */ krb5_db2_delete_policy, + /* db_free_policy */ krb5_db2_free_policy, + /* db_supported_realms */ NULL, + /* db_free_supported_realms */ NULL, + /* errcode_2_string */ NULL, + /* db_alloc */ krb5_db2_alloc, + /* db_free */ krb5_db2_free, + /* set_master_key */ krb5_db2_set_master_key_ext, + /* get_master_key */ krb5_db2_db_get_mkey +}; diff --git a/src/lib/kdb/kdb_db2/kdb_compat.h b/src/lib/kdb/kdb_db2/kdb_compat.h new file mode 100644 index 000000000..540d4a249 --- /dev/null +++ b/src/lib/kdb/kdb_db2/kdb_compat.h @@ -0,0 +1,81 @@ +/* + * lib/kdb/kdb_compat.h + * + * Copyright 1994 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + * + * + * KDC Database interface definitions. + */ + + +typedef struct _old_krb5_encrypted_keyblock { + krb5_enctype enctype; + int length; + krb5_octet *contents; +} old_krb5_encrypted_keyblock; + +typedef struct old_krb5_principal_data { + krb5_magic magic; + krb5_data realm; + krb5_data *data; /* An array of strings */ + krb5_int32 length; + krb5_int32 type; +} old_krb5_principal_data; + +typedef old_krb5_principal_data *old_krb5_principal; + + +/* + * Note --- this structure cannot be modified without changing the + * database version number in libkdb.a + */ +typedef struct _old_krb5_db_entry { + old_krb5_principal principal; + old_krb5_encrypted_keyblock key; + krb5_kvno kvno; + krb5_deltat max_life; + krb5_deltat max_renewable_life; + krb5_kvno mkvno; /* master encryption key vno */ + + krb5_timestamp expiration; /* This is when the client expires */ + krb5_timestamp pw_expiration; /* This is when its password does */ + krb5_timestamp last_pwd_change; /* Last time of password change */ + krb5_timestamp last_success; /* Last successful password */ + + krb5_timestamp last_failed; /* Last failed password attempt */ + krb5_kvno fail_auth_count; /* # of failed password attempts */ + + old_krb5_principal mod_name; + krb5_timestamp mod_date; + krb5_flags attributes; + krb5_int32 salt_type:8, + salt_length:24; + krb5_octet *salt; + old_krb5_encrypted_keyblock alt_key; + krb5_int32 alt_salt_type:8, + alt_salt_length:24; + krb5_octet *alt_salt; + + krb5_int32 expansion[8]; +} old_krb5_db_entry; + diff --git a/src/lib/kdb/kdb_db2/kdb_db2.c b/src/lib/kdb/kdb_db2/kdb_db2.c new file mode 100644 index 000000000..2ac462e89 --- /dev/null +++ b/src/lib/kdb/kdb_db2/kdb_db2.c @@ -0,0 +1,2000 @@ +/* + * lib/kdb/kdb_db2.c + * + * Copyright 1997 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + * + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * 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 FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "k5-int.h" +#include <db.h> +#include <stdio.h> +#include <errno.h> +#include <utime.h> +#include "../kdb5.h" +#include "kdb_db2.h" +#include "kdb_xdr.h" +#include "policy_db.h" + +#define KDB_DB2_DATABASE_NAME "database_name" + +#define OLD_COMPAT_VERSION_1 + +#ifdef OLD_COMPAT_VERSION_1 +#include "kdb_compat.h" +#endif + +#include "kdb_db2.h" + +static char *gen_dbsuffix + (char *, char * ); + +static krb5_error_code krb5_db2_db_start_update + (krb5_context); +static krb5_error_code krb5_db2_db_end_update + (krb5_context); + +krb5_error_code +krb5_db2_db_set_name(krb5_context,char*); + +krb5_error_code krb5_db2_db_lock +( krb5_context, int ); + +static krb5_error_code krb5_db2_db_set_hashfirst + (krb5_context, int); + +static char default_db_name[] = DEFAULT_KDB_FILE; +krb5_set_err_func_t krb5_db2_dal_err_funcp = NULL; + +/* + * Locking: + * + * There are two distinct locking protocols used. One is designed to + * lock against processes (the admin_server, for one) which make + * incremental changes to the database; the other is designed to lock + * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the + * entire database in one fell swoop. + * + * The first locking protocol is implemented using flock() in the + * krb_dbl_lock() and krb_dbl_unlock routines. + * + * The second locking protocol is necessary because DBM "files" are + * actually implemented as two separate files, and it is impossible to + * atomically rename two files simultaneously. It assumes that the + * database is replaced only very infrequently in comparison to the time + * needed to do a database read operation. + * + * A third file is used as a "version" semaphore; the modification + * time of this file is the "version number" of the database. + * At the start of a read operation, the reader checks the version + * number; at the end of the read operation, it checks again. If the + * version number changed, or if the semaphore was nonexistant at + * either time, the reader sleeps for a second to let things + * stabilize, and then tries again; if it does not succeed after + * KRB5_DBM_MAX_RETRY attempts, it gives up. + * + * On update, the semaphore file is deleted (if it exists) before any + * update takes place; at the end of the update, it is replaced, with + * a version number strictly greater than the version number which + * existed at the start of the update. + * + * If the system crashes in the middle of an update, the semaphore + * file is not automatically created on reboot; this is a feature, not + * a bug, since the database may be inconsistant. Note that the + * absence of a semaphore file does not prevent another _update_ from + * taking place later. Database replacements take place automatically + * only on slave servers; a crash in the middle of an update will be + * fixed by the next slave propagation. A crash in the middle of an + * update on the master would be somewhat more serious, but this would + * likely be noticed by an administrator, who could fix the problem and + * retry the operation. + */ + +#define free_dbsuffix(name) free(name) + +/* + * Routines to deal with context. + */ +#define k5db2_inited(c) (c && c->db_context \ + && ((kdb5_dal_handle*)c->db_context)->db_context \ + && ((krb5_db2_context *) ((kdb5_dal_handle*)c->db_context)->db_context)->db_inited) + + +static +krb5_error_code +krb5_db2_get_db_opt( char *input, char **opt, char **val ) +{ + char *pos = strchr(input, '='); + if( pos == NULL ) + { + *opt = NULL; + *val = strdup(input); + if( *val == NULL ) + { + return ENOMEM; + } + } + else + { + *opt = malloc( (pos - input) + 1 ); + *val = strdup( pos + 1 ); + if( !*opt || !*val ) + { + return ENOMEM; + } + memcpy( *opt, input, pos - input); + (*opt)[pos - input] = '\0'; + } + return (0); + +} + +/* + * Restore the default context. + */ +static void +k5db2_clear_context(dbctx) + krb5_db2_context *dbctx; +{ + /* + * Free any dynamically allocated memory. File descriptors and locks + * are the caller's problem. + */ + if (dbctx->db_lf_name) + free(dbctx->db_lf_name); + if (dbctx->db_name && (dbctx->db_name != default_db_name)) + free(dbctx->db_name); + /* + * Clear the structure and reset the defaults. + */ + memset((char *) dbctx, 0, sizeof(krb5_db2_context)); + dbctx->db_name = default_db_name; + dbctx->db_nb_locks = FALSE; +} + + +static krb5_error_code +k5db2_init_context(context) + krb5_context context; +{ + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + dal_handle = (kdb5_dal_handle*) context->db_context; + + if ( dal_handle->db_context == NULL) { + db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context)); + if (db_ctx == NULL) + return ENOMEM; + else { + memset((char *) db_ctx, 0, sizeof(krb5_db2_context)); + k5db2_clear_context((krb5_db2_context *)db_ctx); + dal_handle->db_context = (void *) db_ctx; + } + } + return(0); +} + + +/* + * Utility routine: generate name of database file. + */ + +static char * +gen_dbsuffix(db_name, sfx) + char *db_name; + char *sfx; +{ + char *dbsuffix; + + if (sfx == NULL) + return((char *) NULL); + + dbsuffix = malloc (strlen(db_name) + strlen(sfx) + 1); + if (!dbsuffix) + return(0); + (void) strcpy(dbsuffix, db_name); + (void) strcat(dbsuffix, sfx); + return dbsuffix; +} + + +static DB * +k5db2_dbopen(dbc, fname, flags, mode) + krb5_db2_context *dbc; + char *fname; + int flags; + int mode; +{ + DB *db; + BTREEINFO bti; + HASHINFO hashi; + + bti.flags = 0; + bti.cachesize = 0; + bti.psize = 4096; + bti.lorder = 0; + bti.minkeypage = 0; + bti.compare = NULL; + bti.prefix = NULL; + + hashi.bsize = 4096; + hashi.cachesize = 0; + hashi.ffactor = 40; + hashi.hash = NULL; + hashi.lorder = 0; + hashi.nelem = 1; + + db = dbopen(fname, flags, mode, + dbc->hashfirst ? DB_HASH : DB_BTREE, + dbc->hashfirst ? (void *) &hashi : (void *) &bti); + if (db != NULL) + return db; + switch (errno) { +#ifdef EFTYPE + case EFTYPE: +#endif + case EINVAL: + db = dbopen(fname, flags, mode, + dbc->hashfirst ? DB_BTREE : DB_HASH, + dbc->hashfirst ? (void *) &bti : (void *) &hashi); + if (db != NULL) + dbc->hashfirst = !dbc->hashfirst; + default: + return db; + } +} + +static krb5_error_code +krb5_db2_db_set_hashfirst(context, hashfirst) + krb5_context context; + int hashfirst; +{ + krb5_db2_context *dbc; + kdb5_dal_handle *dal_handle; + + if (k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + dal_handle = (kdb5_dal_handle*) context->db_context; + dbc = (krb5_db2_context *) dal_handle->db_context; + dbc->hashfirst = hashfirst; + return 0; +} + +/* + * initialization for data base routines. + */ + +krb5_error_code +krb5_db2_db_init(context) + krb5_context context; +{ + char *filename = NULL; + krb5_db2_context *db_ctx; + krb5_error_code retval; + kdb5_dal_handle *dal_handle; + char policy_db_name[1024], policy_lock_name[1024]; + + if (k5db2_inited(context)) + return 0; + + /* Check for presence of our context, if not present, allocate one. */ + if ((retval = k5db2_init_context(context))) + return(retval); + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = dal_handle->db_context; + db_ctx->db = NULL; + + if (!(filename = gen_dbsuffix(db_ctx->db_name, KDB2_LOCK_EXT))) + return ENOMEM; + db_ctx->db_lf_name = filename; /* so it gets freed by clear_context */ + + /* + * should be opened read/write so that write locking can work with + * POSIX systems + */ + if ((db_ctx->db_lf_file = open(filename, O_RDWR, 0666)) < 0) { + if ((db_ctx->db_lf_file = open(filename, O_RDONLY, 0666)) < 0) { + retval = errno; + goto err_out; + } + } + db_ctx->db_inited++; + + if ((retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time))) + goto err_out; + + sprintf( policy_db_name, "%s.kadm5", db_ctx->db_name ); + sprintf( policy_lock_name, "%s.lock", policy_db_name ); + + if( (retval = osa_adb_init_db(&db_ctx->policy_db, policy_db_name, + policy_lock_name, OSA_ADB_POLICY_DB_MAGIC)) ) + { + goto err_out; + } + return 0; + +err_out: + db_ctx->db = NULL; + k5db2_clear_context(db_ctx); + return (retval); +} + + +/* + * gracefully shut down database--must be called by ANY program that does + * a krb5_db2_db_init + */ +krb5_error_code +krb5_db2_db_fini(context) + krb5_context context; +{ + krb5_error_code retval = 0; + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + dal_handle = (kdb5_dal_handle*) context->db_context; + if( dal_handle == NULL ) + { + return 0; + } + + db_ctx = (krb5_db2_context *) dal_handle->db_context; + + if (k5db2_inited(context)) { + if (close(db_ctx->db_lf_file)) + retval = errno; + else + retval = 0; + } + if (db_ctx) { + if( db_ctx->policy_db ) + { + retval = osa_adb_fini_db(db_ctx->policy_db, OSA_ADB_POLICY_DB_MAGIC); + if( retval ) + return retval; + } + + k5db2_clear_context(db_ctx); + /* free(dal_handle->db_context); */ + dal_handle->db_context = NULL; + } + return retval; +} + +#if 0 // pradx + +krb5_error_code +krb5_db2_db_open_database(context) + krb5_context context; +{ + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + return 0; +} + +krb5_error_code +krb5_db2_db_close_database(context) + krb5_context context; +{ + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + return 0; +} + +#endif // 0 -pradx + +/* + * Set/Get the master key associated with the database + */ +krb5_error_code +krb5_db2_db_set_mkey(context, key) + krb5_context context; + krb5_keyblock *key; +{ + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + if (!k5db2_inited(context)) + return(KRB5_KDB_DBNOTINITED); + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = dal_handle->db_context; + db_ctx->db_master_key = key; + return 0; +} + +krb5_error_code +krb5_db2_db_get_mkey(context, key) + krb5_context context; + krb5_keyblock **key; +{ + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + if (!k5db2_inited(context)) + return(KRB5_KDB_DBNOTINITED); + + dal_handle = (kdb5_dal_handle*)context->db_context; + db_ctx = dal_handle->db_context; + *key = db_ctx->db_master_key; + + return 0; +} + +/* + * Set the "name" of the current database to some alternate value. + * + * Passing a null pointer as "name" will set back to the default. + * If the alternate database doesn't exist, nothing is changed. + * + * XXX rethink this + */ + +krb5_error_code +krb5_db2_db_set_name(context, name) + krb5_context context; + char *name; +{ + DB *db; + krb5_db2_context *db_ctx; + krb5_error_code kret; + kdb5_dal_handle *dal_handle; + + if (k5db2_inited(context)) + return KRB5_KDB_DBINITED; + + /* Check for presence of our context, if not present, allocate one. */ + if ((kret = k5db2_init_context(context))) + return(kret); + + if (name == NULL) + name = default_db_name; + + dal_handle = (kdb5_dal_handle*)context->db_context; + db_ctx = dal_handle->db_context; + db = k5db2_dbopen(db_ctx, name, O_RDONLY, 0); + if (db == NULL) + return errno; + + db_ctx->db_name = strdup(name); + (*db->close)(db); + return 0; +} + +/* + * Return the last modification time of the database. + * + * Think about using fstat. + */ + +krb5_error_code +krb5_db2_db_get_age(context, db_name, age) + krb5_context context; + char *db_name; + time_t *age; +{ + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + struct stat st; + + if (!k5db2_inited(context)) + return(KRB5_KDB_DBNOTINITED); + dal_handle = (kdb5_dal_handle *) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + + if (fstat (db_ctx->db_lf_file, &st) < 0) + *age = -1; + else + *age = st.st_mtime; + return 0; +} + +/* + * Remove the semaphore file; indicates that database is currently + * under renovation. + * + * This is only for use when moving the database out from underneath + * the server (for example, during slave updates). + */ + +static krb5_error_code +krb5_db2_db_start_update(context) + krb5_context context; +{ + return 0; +} + +static krb5_error_code +krb5_db2_db_end_update(context) + krb5_context context; +{ + krb5_error_code retval; + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + struct stat st; + time_t now; + struct utimbuf utbuf; + + if (!k5db2_inited(context)) + return(KRB5_KDB_DBNOTINITED); + + retval = 0; + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = dal_handle->db_context; + now = time((time_t *) NULL); + if (fstat(db_ctx->db_lf_file, &st) == 0) { + if (st.st_mtime >= now) { + utbuf.actime = st.st_mtime+1; + utbuf.modtime = st.st_mtime+1; + if (utime(db_ctx->db_lf_name, &utbuf)) + retval = errno; + } + else { + if (utime(db_ctx->db_lf_name, (struct utimbuf *) NULL)) + retval = errno; + } + } + else + retval = errno; + if (!retval) { + if (fstat(db_ctx->db_lf_file, &st) == 0) + db_ctx->db_lf_time = st.st_mtime; + else + retval = errno; + } + return(retval); +} + +krb5_error_code +krb5_db2_db_lock(context, in_mode) + krb5_context context; + int in_mode; +{ + krb5_db2_context *db_ctx; + int krb5_lock_mode; + DB *db; + krb5_error_code retval; + time_t mod_time; + kdb5_dal_handle *dal_handle; + int mode = in_mode & ~KRB5_DB_LOCKMODE_PERMANENT; /* permanent is not available for principal db */ + + switch( in_mode ) + { + case KRB5_DB_LOCKMODE_PERMANENT: + mode = KRB5_DB_LOCKMODE_EXCLUSIVE; + break; + case KRB5_DB_LOCKMODE_EXCLUSIVE: + mode = KRB5_LOCKMODE_EXCLUSIVE; + break; + + case KRB5_DB_LOCKMODE_SHARED: + mode = KRB5_LOCKMODE_SHARED; + break; + default: + return EINVAL; + } + + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) { + /* No need to upgrade lock, just return */ + db_ctx->db_locks_held++; + goto policy_lock; + } + + if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE)) + return KRB5_KDB_BADLOCKMODE; + + if (db_ctx->db_nb_locks) + krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK; + else + krb5_lock_mode = mode; + retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode); + switch (retval) { + case EBADF: + if (mode == KRB5_LOCKMODE_EXCLUSIVE) + return KRB5_KDB_CANTLOCK_DB; + default: + return retval; + case 0: + break; + } + + if ((retval = krb5_db2_db_get_age(context, NULL, &mod_time))) + goto lock_error; + + db = k5db2_dbopen(db_ctx, db_ctx->db_name, + mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, + 0600); + if (db) { + db_ctx->db_lf_time = mod_time; + db_ctx->db = db; + } else { + retval = errno; + db_ctx->db = NULL; + goto lock_error; + } + + db_ctx->db_lock_mode = mode; + db_ctx->db_locks_held++; + + policy_lock: + if((retval=osa_adb_get_lock(db_ctx->policy_db, in_mode))) + { + krb5_db2_db_unlock(context); + } + return retval; + +lock_error:; + db_ctx->db_lock_mode = 0; + db_ctx->db_locks_held = 0; + krb5_db2_db_unlock(context); + return retval; +} + + +krb5_error_code +krb5_db2_db_unlock(context) + krb5_context context; +{ + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + DB *db; + krb5_error_code retval; + + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + + if( (retval = osa_adb_release_lock(db_ctx->policy_db)) ) + { + return retval; + } + + if (!db_ctx->db_locks_held) /* lock already unlocked */ + return KRB5_KDB_NOTLOCKED; + db = db_ctx->db; + if (--(db_ctx->db_locks_held) == 0) { + (*db->close)(db); + db_ctx->db = NULL; + + retval = krb5_lock_file(context, db_ctx->db_lf_file, + KRB5_LOCKMODE_UNLOCK); + db_ctx->db_lock_mode = 0; + return(retval); + } + return 0; +} + +/* + * Create the database, assuming it's not there. + */ +krb5_error_code +krb5_db2_db_create(context, db_name, flags) + krb5_context context; + char *db_name; + krb5_int32 flags; +{ + register krb5_error_code retval = 0; + kdb5_dal_handle *dal_handle; + char *okname; + int fd; + krb5_db2_context *db_ctx; + DB *db; + char policy_db_name[1024], policy_lock_name[1024]; + + if ((retval = k5db2_init_context(context))) + return(retval); + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + switch (flags) { + case KRB5_KDB_CREATE_HASH: + if ((retval = krb5_db2_db_set_hashfirst(context, TRUE))) + return retval; + break; + case KRB5_KDB_CREATE_BTREE: + case 0: + if ((retval = krb5_db2_db_set_hashfirst(context, FALSE))) + return retval; + break; + default: + return KRB5_KDB_BAD_CREATEFLAGS; + } + db = k5db2_dbopen(db_ctx, db_name, O_RDWR|O_CREAT|O_EXCL, 0600); + if (db == NULL) + retval = errno; + else + (*db->close)(db); + if (retval == 0) { + okname = gen_dbsuffix(db_name, KDB2_LOCK_EXT); + if (!okname) + retval = ENOMEM; + else { + fd = open (okname, O_CREAT|O_RDWR|O_TRUNC, 0600); + if (fd < 0) + retval = errno; + else + close(fd); + free_dbsuffix(okname); + } + } + + sprintf( policy_db_name, "%s.kadm5", db_name ); + sprintf( policy_lock_name, "%s.lock", policy_db_name ); + + retval = osa_adb_create_db( policy_db_name, + policy_lock_name, OSA_ADB_POLICY_DB_MAGIC); + + + return retval; +} + +/* + * Destroy the database. Zero's out all of the files, just to be sure. + */ +static krb5_error_code +destroy_file_suffix(dbname, suffix) + char *dbname; + char *suffix; +{ + char *filename; + struct stat statb; + int nb,fd; + unsigned int j; + off_t pos; + char buf[BUFSIZ]; + char zbuf[BUFSIZ]; + int dowrite; + + filename = gen_dbsuffix(dbname, suffix); + if (filename == 0) + return ENOMEM; + if ((fd = open(filename, O_RDWR, 0)) < 0) { + free(filename); + return errno; + } + /* fstat() will probably not fail unless using a remote filesystem + (which is inappropriate for the kerberos database) so this check + is mostly paranoia. */ + if (fstat(fd, &statb) == -1) { + int retval = errno; + free(filename); + return retval; + } + /* + * Stroll through the file, reading in BUFSIZ chunks. If everything + * is zero, then we're done for that block, otherwise, zero the block. + * We would like to just blast through everything, but some DB + * implementations make holey files and writing data to the holes + * causes actual blocks to be allocated which is no good, since + * we're just about to unlink it anyways. + */ + memset(zbuf, 0, BUFSIZ); + pos = 0; + while (pos < statb.st_size) { + dowrite = 0; + nb = read(fd, buf, BUFSIZ); + if (nb < 0) { + int retval = errno; + free(filename); + return retval; + } + for (j=0; j<nb; j++) { + if (buf[j] != '\0') { + dowrite = 1; + break; + } + } + /* For signedness */ + j = nb; + if (dowrite) { + lseek(fd, pos, SEEK_SET); + nb = write(fd, zbuf, j); + if (nb < 0) { + int retval = errno; + free(filename); + return retval; + } + } + pos += nb; + } + /* ??? Is fsync really needed? I don't know of any non-networked + filesystem which will discard queued writes to disk if a file + is deleted after it is closed. --jfc */ +#ifndef NOFSYNC + fsync(fd); +#endif + close(fd); + + if (unlink(filename)) { + free(filename); + return(errno); + } + free(filename); + return(0); +} + +/* + * Since the destroy operation happens outside the init/fini bracket, we + * have some tomfoolery to undergo here. If we're operating under no + * database context, then we initialize with the default. If the caller + * wishes a different context (e.g. different dispatch table), it's their + * responsibility to call kdb5_db_set_dbops() before this call. That will + * set up the right dispatch table values (e.g. name extensions). + * + * Not quite valid due to ripping out of dbops... + */ +krb5_error_code +krb5_db2_db_destroy(context, dbname) + krb5_context context; + char *dbname; +{ + krb5_error_code retval1, retval2; + krb5_boolean tmpcontext; + char policy_db_name[1024], policy_lock_name[1024]; + + tmpcontext = 0; + if ( !context->db_context || !((kdb5_dal_handle*)context->db_context)->db_context ) { + tmpcontext = 1; + if ((retval1 = k5db2_init_context(context))) + return(retval1); + } + + retval1 = retval2 = 0; + retval1 = destroy_file_suffix(dbname, ""); + retval2 = destroy_file_suffix(dbname, KDB2_LOCK_EXT); + + if (tmpcontext) { + k5db2_clear_context((krb5_db2_context *) ((kdb5_dal_handle*)context->db_context)->db_context ); + free(((kdb5_dal_handle*)context->db_context)->db_context); + ((kdb5_dal_handle*)context->db_context)->db_context = NULL; + } + + if (retval1 || retval2) + return (retval1 ? retval1 : retval2); + + sprintf( policy_db_name, "%s.kadm5", dbname ); + sprintf( policy_lock_name, "%s.lock", policy_db_name ); + + retval1 = osa_adb_destroy_db( policy_db_name, + policy_lock_name, OSA_ADB_POLICY_DB_MAGIC); + + return retval1; +} + +#if 0 // -pradx + +/* + * "Atomically" rename the database in a way that locks out read + * access in the middle of the rename. + * + * Not perfect; if we crash in the middle of an update, we don't + * necessarily know to complete the transaction the rename, but... + * + * Since the rename operation happens outside the init/fini bracket, we + * have to go through the same stuff that we went through up in db_destroy. + */ +krb5_error_code +krb5_db2_db_rename(context, from, to) + krb5_context context; + char *from; + char *to; +{ + DB *db; + char *fromok; + krb5_error_code retval; + kdb5_dal_handle *dal_handle; + krb5_db2_context *s_context, *db_ctx; + + dal_handle = (kdb5_dal_handle*) context->db_context; + s_context = dal_handle->db_context; + dal_handle->db_context = NULL; + if ((retval = k5db2_init_context(context))) + return retval; + db_ctx = (krb5_db2_context *) ((kdb5_dal_handle*)context->db_context)->db_context; + + /* + * Create the database if it does not already exist; the + * files must exist because krb5_db2_db_lock, called below, + * will fail otherwise. + */ + db = k5db2_dbopen(db_ctx, to, O_RDWR|O_CREAT, 0600); + if (db == NULL) { + retval = errno; + goto errout; + } + else + (*db->close)(db); + /* + * Set the database to the target, so that other processes sharing + * the target will stop their activity, and notice the new database. + */ + retval = krb5_db2_db_set_name(context, to); + if (retval) + goto errout; + + db_ctx->db_lf_name = gen_dbsuffix(db_ctx->db_name, KDB2_LOCK_EXT); + if (db_ctx->db_lf_name == NULL) { + retval = ENOMEM; + goto errout; + } + db_ctx->db_lf_file = open(db_ctx->db_lf_name, O_RDWR|O_CREAT, 0600); + if (db_ctx->db_lf_file < 0) { + retval = errno; + goto errout; + } + + db_ctx->db_inited = 1; + + retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time); + if (retval) + goto errout; + + fromok = gen_dbsuffix(from, KDB2_LOCK_EXT); + if (fromok == NULL) { + retval = ENOMEM; + goto errout; + } + + if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) + goto errfromok; + + if ((retval = krb5_db2_db_start_update(context))) + goto errfromok; + + if (rename(from, to)) { + retval = errno; + goto errfromok; + } + if (unlink(fromok)) { + retval = errno; + goto errfromok; + } + retval = krb5_db2_db_end_update(context); +errfromok: + free_dbsuffix(fromok); +errout: + if ( ((kdb5_dal_handle*)context->db_context)->db_context ) { + if (db_ctx->db_lf_file >= 0) { + krb5_db2_db_unlock(context); + close(db_ctx->db_lf_file); + } + k5db2_clear_context((krb5_db2_context *) ((kdb5_dal_handle*)context->db_context)->db_context); + free(((kdb5_dal_handle*)context->db_context)->db_context); + } + + ((kdb5_dal_handle*)context->db_context)->db_context = s_context; + (void) krb5_db2_db_unlock(context); /* unlock saved context db */ + + return retval; +} + +#endif // 0 - pradx + +/* + * look up a principal in the data base. + * returns number of entries found, and whether there were + * more than requested. + */ + +krb5_error_code +krb5_db2_db_get_principal(context, searchfor, entries, nentries, more) + krb5_context context; + krb5_const_principal searchfor; + krb5_db_entry *entries; /* filled in */ + int *nentries; /* how much room/how many found */ + krb5_boolean *more; /* are there more? */ +{ + krb5_db2_context *db_ctx; + krb5_error_code retval; + DB *db; + DBT key, contents; + krb5_data keydata, contdata; + int try, dbret; + kdb5_dal_handle *dal_handle; + + *more = FALSE; + *nentries = 0; + + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + + for (try = 0; try < KRB5_DB2_MAX_RETRY; try++) { + if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) { + if (db_ctx->db_nb_locks) + return(retval); + sleep(1); + continue; + } + break; + } + if (try == KRB5_DB2_MAX_RETRY) + return KRB5_KDB_DB_INUSE; + + /* XXX deal with wildcard lookups */ + retval = krb5_encode_princ_dbkey(context, &keydata, searchfor); + if (retval) + goto cleanup; + key.data = keydata.data; + key.size = keydata.length; + + db = db_ctx->db; + dbret = (*db->get)(db, &key, &contents, 0); + retval = errno; + krb5_free_data_contents(context, &keydata); + switch (dbret) { + case 1: + retval = 0; + case -1: + default: + *nentries = 0; + goto cleanup; + case 0: + contdata.data = contents.data; + contdata.length = contents.size; + retval = krb5_decode_princ_contents(context, &contdata, entries); + if (!retval) + *nentries = 1; + break; + } + +cleanup: + (void) krb5_db2_db_unlock(context); /* unlock read lock */ + return retval; +} + +/* + Free stuff returned by krb5_db2_db_get_principal. + */ +krb5_error_code +krb5_db2_db_free_principal(context, entries, nentries) + krb5_context context; + krb5_db_entry *entries; + int nentries; +{ + register int i; + for (i = 0; i < nentries; i++) + krb5_dbe_free_contents(context, &entries[i]); + return 0; +} + +/* + Stores the *"nentries" entry structures pointed to by "entries" in the + database. + + *"nentries" is updated upon return to reflect the number of records + acutally stored; the first *"nstored" records will have been stored in the + database (even if an error occurs). + + */ + +krb5_error_code +krb5_db2_db_put_principal(context, entries, nentries, db_args) + krb5_context context; + krb5_db_entry *entries; + register int *nentries; /* number of entry structs to update */ + char **db_args; +{ + int i, n, dbret; + DB *db; + DBT key, contents; + krb5_data contdata, keydata; + krb5_error_code retval; + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + if( db_args ) + { + /* DB2 does not support db_args DB arguments for principal */ + char buf[KRB5_MAX_ERR_STR]; + sprintf(buf, "Unsupported argument \"%s\" for db2", db_args[0]); + krb5_db2_dal_err_funcp( context, krb5_err_have_str, EINVAL, buf); + return EINVAL; + } + + n = *nentries; + *nentries = 0; + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) + return retval; + + db = db_ctx->db; + if ((retval = krb5_db2_db_start_update(context))) { + (void)krb5_db2_db_unlock(context); + return retval; + } + + /* for each one, stuff temps, and do replace/append */ + for (i = 0; i < n; i++) { + retval = krb5_encode_princ_contents(context, &contdata, entries); + if (retval) + break; + contents.data = contdata.data; + contents.size = contdata.length; + retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ); + if (retval) { + krb5_free_data_contents(context, &contdata); + break; + } + + key.data = keydata.data; + key.size = keydata.length; + dbret = (*db->put)(db, &key, &contents, 0); + retval = dbret ? errno : 0; + krb5_free_data_contents(context, &keydata); + krb5_free_data_contents(context, &contdata); + if (retval) + break; + entries++; /* bump to next struct */ + } + + (void)krb5_db2_db_end_update(context); + (void)krb5_db2_db_unlock(context); /* unlock database */ + *nentries = i; + return(retval); +} + +/* + * delete a principal from the data base. + * returns number of entries removed + */ + +krb5_error_code +krb5_db2_db_delete_principal(context, searchfor, nentries) + krb5_context context; + krb5_const_principal searchfor; + int *nentries; /* how many found & deleted */ +{ + krb5_error_code retval; + krb5_db_entry entry; + krb5_db2_context *db_ctx; + DB *db; + DBT key, contents; + krb5_data keydata, contdata; + int i, dbret; + kdb5_dal_handle *dal_handle; + + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) + return(retval); + + if ((retval = krb5_db2_db_start_update(context))) { + (void) krb5_db2_db_unlock(context); /* unlock write lock */ + return(retval); + } + + if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor))) + goto cleanup; + key.data = keydata.data; + key.size = keydata.length; + + db = db_ctx->db; + dbret = (*db->get)(db, &key, &contents, 0); + retval = errno; + switch (dbret) { + case 1: + retval = KRB5_KDB_NOENTRY; + case -1: + default: + *nentries = 0; + goto cleankey; + case 0: + ; + } + memset((char *)&entry, 0, sizeof(entry)); + contdata.data = contents.data; + contdata.length = contents.size; + retval = krb5_decode_princ_contents(context, &contdata, &entry); + if (retval) + goto cleankey; + *nentries = 1; + + /* Clear encrypted key contents */ + for (i = 0; i < entry.n_key_data; i++) { + if (entry.key_data[i].key_data_length[0]) { + memset((char *)entry.key_data[i].key_data_contents[0], 0, + (unsigned) entry.key_data[i].key_data_length[0]); + } + } + + retval = krb5_encode_princ_contents(context, &contdata, &entry); + krb5_dbe_free_contents(context, &entry); + if (retval) + goto cleankey; + + contents.data = contdata.data; + contents.size = contdata.length; + dbret = (*db->put)(db, &key, &contents, 0); + retval = dbret ? errno : 0; + krb5_free_data_contents(context, &contdata); + if (retval) + goto cleankey; + dbret = (*db->del)(db, &key, 0); + retval = dbret ? errno : 0; +cleankey: + krb5_free_data_contents(context, &keydata); + +cleanup: + (void) krb5_db2_db_end_update(context); + (void) krb5_db2_db_unlock(context); /* unlock write lock */ + return retval; +} + +krb5_error_code +krb5_db2_db_iterate_ext(context, func, func_arg, backwards, recursive) + krb5_context context; + krb5_error_code (*func) (krb5_pointer, krb5_db_entry *); + krb5_pointer func_arg; + int backwards, recursive; +{ + krb5_db2_context *db_ctx; + DB *db; + DBT key, contents; + krb5_data contdata; + krb5_db_entry entries; + krb5_error_code retval; + kdb5_dal_handle *dal_handle; + int dbret; + void *cookie; + + cookie = NULL; + if (!k5db2_inited(context)) + return KRB5_KDB_DBNOTINITED; + + dal_handle = (kdb5_dal_handle*) context->db_context; + db_ctx = (krb5_db2_context *) dal_handle->db_context; + retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED); + + if (retval) + return retval; + + db = db_ctx->db; + if (recursive && db->type != DB_BTREE) { + (void)krb5_db2_db_unlock(context); + return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ + } + + if (!recursive) { + dbret = (*db->seq)(db, &key, &contents, + backwards ? R_LAST : R_FIRST); + } else { +#ifdef HAVE_BT_RSEQ + dbret = bt_rseq(db, &key, &contents, &cookie, + backwards ? R_LAST : R_FIRST); +#else + (void)krb5_db2_db_unlock(context); + return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ +#endif + } + while (dbret == 0) { + contdata.data = contents.data; + contdata.length = contents.size; + retval = krb5_decode_princ_contents(context, &contdata, &entries); + if (retval) + break; + retval = (*func)(func_arg, &entries); + krb5_dbe_free_contents(context, &entries); + if (retval) + break; + if (!recursive) { + dbret = (*db->seq)(db, &key, &contents, + backwards ? R_PREV : R_NEXT); + } else { +#ifdef HAVE_BT_RSEQ + dbret = bt_rseq(db, &key, &contents, &cookie, + backwards ? R_PREV : R_NEXT); +#else + (void)krb5_db2_db_unlock(context); + return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ +#endif + } + } + switch (dbret) { + case 1: + case 0: + break; + case -1: + default: + retval = errno; + } + (void) krb5_db2_db_unlock(context); + return retval; +} + +krb5_error_code +krb5_db2_db_iterate( krb5_context context, + char *match_expr, + krb5_error_code (*func) (krb5_pointer, krb5_db_entry *), + krb5_pointer func_arg ) +{ + return krb5_db2_db_iterate_ext(context, func, func_arg, 0, 0); +} + + +krb5_boolean +krb5_db2_db_set_lockmode(context, mode) + krb5_context context; + krb5_boolean mode; +{ + krb5_boolean old; + krb5_db2_context *db_ctx; + kdb5_dal_handle *dal_handle; + + dal_handle = (kdb5_dal_handle*) context->db_context; + old = mode; + if ( dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) { + old = db_ctx->db_nb_locks; + db_ctx->db_nb_locks = mode; + } + return old; +} + +#if 0 // -pradx +/* + * Context serialization operations. + * + * Ick, this is really gross. --- tlyu + */ + +/* + * kdb5_context_size() - Determine size required to serialize. + */ +static krb5_error_code +kdb5_context_size(kcontext, arg, sizep) + krb5_context kcontext; + krb5_pointer arg; + size_t *sizep; +{ + krb5_error_code kret; + size_t required; + krb5_db2_context *dbctx; + + /* + * The database context requires at minimum: + * krb5_int32 for KV5M_DB_CONTEXT + * krb5_int32 for db_inited + * krb5_int32 for database lockfile non-blocking flag + * krb5_int32 for database lockfile lock count + * krb5_int32 for database lockfile lock mode + * krb5_int32 for length of database name. + * krb5_int32 for KV5M_DB_CONTEXT + */ + kret = EINVAL; + if ((dbctx = (krb5_db2_context *) arg)) { + required = (sizeof(krb5_int32) * 7); + if (dbctx->db_inited && dbctx->db_name) + required += strlen(dbctx->db_name); + kret = 0; + *sizep += required; + } + return(kret); +} + +/* + * kdb5_context_externalize() - Externalize the database context. + */ +static krb5_error_code +kdb5_context_externalize(kcontext, arg, buffer, lenremain) + krb5_context kcontext; + krb5_pointer arg; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code kret; + krb5_db2_context *dbctx; + size_t required; + krb5_octet *bp; + size_t remain; + + required = 0; + bp = *buffer; + remain = *lenremain; + kret = EINVAL; + if ((dbctx = (krb5_db2_context *) arg)) { + kret = ENOMEM; + if (!kdb5_context_size(kcontext, arg, &required) && + (required <= remain)) { + /* Write magic number */ + (void) krb5_ser_pack_int32(KV5M_DB_CONTEXT, &bp, &remain); + + /* Write inited flag */ + (void) krb5_ser_pack_int32((krb5_int32) dbctx->db_inited, + &bp, &remain); + + /* Write blocking lock lockmode */ + (void) krb5_ser_pack_int32((krb5_int32) dbctx->db_nb_locks, + &bp, &remain); + + /* Write lock count */ + (void) krb5_ser_pack_int32((krb5_int32) + (dbctx->db_inited) ? + dbctx->db_locks_held : 0, + &bp, &remain); + + /* Write lock mode */ + (void) krb5_ser_pack_int32((krb5_int32) + (dbctx->db_inited) ? + dbctx->db_lock_mode : 0, + &bp, &remain); + + /* Write length of database name */ + (void) krb5_ser_pack_int32((dbctx->db_inited && dbctx->db_name) ? + (krb5_int32) strlen(dbctx->db_name) : 0, + &bp, &remain); + if (dbctx->db_inited && dbctx->db_name) + (void) krb5_ser_pack_bytes((krb5_octet *) dbctx->db_name, + strlen(dbctx->db_name), + &bp, &remain); + + /* Write trailer */ + (void) krb5_ser_pack_int32(KV5M_DB_CONTEXT, &bp, &remain); + kret = 0; + *buffer = bp; + *lenremain = remain; + } + } + return(kret); +} + +/* + * kdb5_context_internalize() - Internalize the database context. + */ +static krb5_error_code +kdb5_context_internalize(kcontext, argp, buffer, lenremain) + krb5_context kcontext; + krb5_pointer *argp; + krb5_octet **buffer; + size_t *lenremain; +{ + krb5_error_code kret; + krb5_context tmpctx; + krb5_db2_context *dbctx; + krb5_int32 ibuf; + krb5_octet *bp; + size_t remain; + krb5_int32 iflag; + krb5_int32 nb_lockmode; + krb5_int32 lockcount; + krb5_int32 lockmode; + krb5_int32 dbnamelen; + krb5_boolean nb_lock; + char *dbname; + + bp = *buffer; + remain = *lenremain; + kret = EINVAL; + dbctx = (krb5_db2_context *) NULL; + /* Read our magic number */ + if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) + ibuf = 0; + if (ibuf == KV5M_DB_CONTEXT) { + kret = ENOMEM; + + if (!(kret = krb5_ser_unpack_int32(&iflag, &bp, &remain)) && + !(kret = krb5_ser_unpack_int32(&nb_lockmode, &bp, &remain)) && + !(kret = krb5_ser_unpack_int32(&lockcount, &bp, &remain)) && + !(kret = krb5_ser_unpack_int32(&lockmode, &bp, &remain)) && + !(kret = krb5_ser_unpack_int32(&dbnamelen, &bp, &remain)) && + !(kret = krb5_init_context(&tmpctx))) { + if (iflag) { + dbname = (char *) NULL; + if (dbnamelen && + (dbname = (char *) malloc((size_t) (dbnamelen+1)))) { + kret = krb5_ser_unpack_bytes((krb5_octet *) dbname, + (size_t) dbnamelen, + &bp, &remain); + if (!kret) + dbname[dbnamelen] = '\0'; + } + if (!kret && + (!dbname || !(kret = krb5_db_set_name(tmpctx, dbname))) && + !(kret = krb5_db_init(tmpctx))) { + dbctx = (krb5_db2_context *) tmpctx->db_context; + (void) krb5_db2_db_set_lockmode(tmpctx, 0); + if (lockmode) + kret = krb5_db2_db_lock(tmpctx, lockmode); + if (!kret && lockmode) + dbctx->db_locks_held = lockcount; + nb_lock = nb_lockmode & 0xff; + (void) krb5_db2_db_set_lockmode(tmpctx, nb_lock); + } + if (dbname) + krb5_xfree(dbname); + } + if (!kret) + kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain); + if (kret || (ibuf != KV5M_DB_CONTEXT)) + kret = EINVAL; + + if (kret) { + if (dbctx) + krb5_db_fini(tmpctx); + } + else + tmpctx->db_context = NULL; + krb5_free_context(tmpctx); + } + } + if (!kret) { + *buffer = bp; + *lenremain = remain; + *argp = (krb5_pointer) dbctx; + } + return(kret); +} + +/* Dispatch entry */ +static const krb5_ser_entry kdb5_context_ser_entry = { + KV5M_DB_CONTEXT, /* Type */ + kdb5_context_size, /* Sizer routine */ + kdb5_context_externalize, /* Externalize routine */ + kdb5_context_internalize /* Externalize routine */ +}; + +/* + * Register serializer. + */ +krb5_error_code +krb5_ser_db_context_init(kcontext) + krb5_context kcontext; +{ + return(krb5_register_serializer(kcontext, &kdb5_context_ser_entry)); +} + +#endif // 0 - pradx + +/* + * DAL API functions + */ +krb5_error_code krb5_db2_lib_init(krb5_set_err_func_t set_err) +{ + krb5_db2_dal_err_funcp = set_err; + return 0; +} + +krb5_error_code krb5_db2_lib_cleanup() +{ + /* right now, no cleanup required */ + return 0; +} + +krb5_error_code krb5_db2_open( krb5_context kcontext, + char *conf_section, + char **db_args, + int mode ) +{ + krb5_error_code status = 0; + char **t_ptr = db_args; + char db_name_set = 0; + + if (k5db2_inited(kcontext)) + return 0; + + + while ( t_ptr && *t_ptr ) + { + char *opt = NULL, *val = NULL; + + krb5_db2_get_db_opt( *t_ptr, &opt, &val ); + if( opt && !strcmp( opt, "dbname" ) ) + { + status = krb5_db2_db_set_name( kcontext, val ); + if( status ) + { + free(opt); + free(val); + goto clean_n_exit; + } + db_name_set = 1; + } + /* ignore hash argument. Might have been passed from create */ + else if( !opt || strcmp( opt, "hash") ) + { + char buf[KRB5_MAX_ERR_STR]; + sprintf(buf, "Unsupported argument \"%s\" for db2", opt?opt:val); + krb5_db2_dal_err_funcp( kcontext, krb5_err_have_str, EINVAL, buf); + free(opt); + free(val); + return EINVAL; + } + + free(opt); + free(val); + t_ptr++; + } + + if( !db_name_set ) + { + char *value = NULL; + status = profile_get_string( KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, + conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ + NULL, &value ); + + + if( value == NULL ) + { + /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ + status = profile_get_string( KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, + KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ + default_db_name, &value ); + if( status ) + { + goto clean_n_exit; + } + } + + status = krb5_db2_db_set_name( kcontext, value ); + profile_release_string( value ); + if( status ) + { + goto clean_n_exit; + } + + } + + status = krb5_db2_db_init( kcontext ); + + clean_n_exit: + return status; +} + + +krb5_error_code krb5_db2_create( krb5_context kcontext, + char *conf_section, + char **db_args ) +{ + krb5_error_code status = 0; + char **t_ptr = db_args; + char db_name_set = 0; + krb5_int32 flags = KRB5_KDB_CREATE_BTREE; + char *db_name = NULL; + + if (k5db2_inited(kcontext)) + return 0; + + + while ( t_ptr && *t_ptr ) + { + char *opt = NULL, *val = NULL; + + krb5_db2_get_db_opt( *t_ptr, &opt, &val ); + if( opt && !strcmp( opt, "dbname" ) ) + { + db_name = strdup(val); + status = krb5_db2_db_set_name( kcontext, val ); + if( !status ) + { + status = EEXIST; + free(opt); + free(val); + goto clean_n_exit; + } + db_name_set = 1; + } + /* ignore hash argument. Might have been passed from create */ + else if( opt && !strcmp( opt, "hash") ) + { + flags=KRB5_KDB_CREATE_HASH; + } + else + { + char buf[KRB5_MAX_ERR_STR]; + sprintf(buf, "Unsupported argument \"%s\" for db2", opt?opt:val); + krb5_db2_dal_err_funcp( kcontext, krb5_err_have_str, EINVAL, buf); + free(opt); + free(val); + return EINVAL; + } + + free(opt); + free(val); + t_ptr++; + } + + if( !db_name_set ) + { + char *value = NULL; + status = profile_get_string( KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, + conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ + NULL, &value ); + + + if( value == NULL ) + { + /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ + status = profile_get_string( KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, + KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ + default_db_name, &value ); + if( status ) + { + goto clean_n_exit; + } + } + + db_name = strdup( value ); + status = krb5_db2_db_set_name( kcontext, value ); + profile_release_string( value ); + if( !status ) + { + status = EEXIST; + goto clean_n_exit; + } + + } + + status = krb5_db2_db_create( kcontext, db_name, flags ); + if( status ) + goto clean_n_exit; + /* db2 has a problem of needing to close and open the database again. This removes that need */ + status = krb5_db2_db_fini(kcontext); + if( status ) + goto clean_n_exit; + + status = krb5_db2_open( kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW ); + + clean_n_exit: + if( db_name ) + free( db_name ); + return status; +} + +krb5_error_code krb5_db2_destroy( krb5_context kcontext, + char *conf_section, + char **db_args ) +{ + krb5_error_code status = 0; + char **t_ptr = db_args; + char db_name_set = 0; + char *db_name = NULL; + + while ( t_ptr && *t_ptr ) + { + char *opt = NULL, *val = NULL; + + krb5_db2_get_db_opt( *t_ptr, &opt, &val ); + if( opt && !strcmp( opt, "dbname" ) ) + { + db_name = strdup(val); + status = krb5_db2_db_set_name( kcontext, val ); + if( status ) + { + free(opt); + free(val); + goto clean_n_exit; + } + db_name_set = 1; + } + /* ignore hash argument. Might have been passed from create */ + else if( !opt || strcmp( opt, "hash") ) + { + free(opt); + free(val); + return EINVAL; + } + + free(opt); + free(val); + t_ptr++; + } + + if( !db_name_set ) + { + char *value = NULL; + status = profile_get_string( KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, + conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ + NULL, &value ); + + + if( value == NULL ) + { + /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ + status = profile_get_string( KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, + KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ + default_db_name, &value ); + if( status ) + { + goto clean_n_exit; + } + } + + db_name = strdup(value); + status = krb5_db2_db_set_name( kcontext, value ); + profile_release_string( value ); + if( status ) + { + goto clean_n_exit; + } + + } + + status = krb5_db2_db_destroy( kcontext, db_name ); + + clean_n_exit: + if( db_name ) + free(db_name); + return status; +} + +krb5_error_code krb5_db2_set_master_key_ext ( krb5_context kcontext, + char *pwd, + krb5_keyblock *key) +{ + return krb5_db2_db_set_mkey( kcontext, key ); +} + +krb5_error_code krb5_db2_db_set_option ( krb5_context kcontext, int option, void *value ) +{ + krb5_error_code status = 0; + krb5_boolean oldval; + + switch(option) + { + case KRB5_KDB_OPT_SET_DB_NAME: + status = krb5_db2_db_set_name( kcontext, (char *)value); + break; + + case KRB5_KDB_OPT_SET_LOCK_MODE: + oldval = krb5_db2_db_set_lockmode( kcontext, *((krb5_boolean*)value) ); + *((krb5_boolean*)value) = oldval; + break; + + default: + status = -1; /* TBD */ + break; + } + + return status; +} + +void * krb5_db2_alloc( krb5_context kcontext, void *ptr, size_t size ) +{ + return realloc(ptr, size); +} + +void krb5_db2_free( krb5_context kcontext, void *ptr ) +{ + free(ptr); +} + + +/* policy functions */ +krb5_error_code krb5_db2_create_policy( krb5_context kcontext, + osa_policy_ent_t policy ) +{ + kdb5_dal_handle *dal_handle; + krb5_db2_context *dbc; + + dal_handle = (kdb5_dal_handle*) kcontext->db_context; + dbc = (krb5_db2_context*) dal_handle->db_context; + + return osa_adb_create_policy( dbc->policy_db, policy ); +} + +krb5_error_code krb5_db2_get_policy ( krb5_context kcontext, + char *name, + osa_policy_ent_t *policy, + int *cnt) +{ + kdb5_dal_handle *dal_handle; + krb5_db2_context *dbc; + + dal_handle = (kdb5_dal_handle*) kcontext->db_context; + dbc = (krb5_db2_context*) dal_handle->db_context; + + return osa_adb_get_policy( dbc->policy_db, name, policy, cnt ); +} + +krb5_error_code krb5_db2_put_policy ( krb5_context kcontext, + osa_policy_ent_t policy ) +{ + kdb5_dal_handle *dal_handle; + krb5_db2_context *dbc; + + dal_handle = (kdb5_dal_handle*) kcontext->db_context; + dbc = (krb5_db2_context*) dal_handle->db_context; + + return osa_adb_put_policy( dbc->policy_db, policy ); +} + +krb5_error_code krb5_db2_iter_policy ( krb5_context kcontext, + char *match_entry, + osa_adb_iter_policy_func func, + void *data ) +{ + kdb5_dal_handle *dal_handle; + krb5_db2_context *dbc; + + dal_handle = (kdb5_dal_handle*) kcontext->db_context; + dbc = (krb5_db2_context*) dal_handle->db_context; + + return osa_adb_iter_policy( dbc->policy_db, func, data ); +} + + +krb5_error_code krb5_db2_delete_policy ( krb5_context kcontext, + char *policy ) +{ + kdb5_dal_handle *dal_handle; + krb5_db2_context *dbc; + + dal_handle = (kdb5_dal_handle*) kcontext->db_context; + dbc = (krb5_db2_context*) dal_handle->db_context; + + return osa_adb_destroy_policy( dbc->policy_db, policy ); +} + + +void krb5_db2_free_policy( krb5_context kcontext, + osa_policy_ent_t entry ) +{ + osa_free_policy_ent(entry); +} + diff --git a/src/lib/kdb/kdb_db2/kdb_db2.h b/src/lib/kdb/kdb_db2/kdb_db2.h new file mode 100644 index 000000000..ba03ea36f --- /dev/null +++ b/src/lib/kdb/kdb_db2/kdb_db2.h @@ -0,0 +1,216 @@ +/* + * lib/kdb/kdb_db2.h + * + * Copyright 1997 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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + * + * + * KDC Database backend definitions for Berkely DB. + */ +#ifndef KRB5_KDB_DB2_H +#define KRB5_KDB_DB2_H + +#include "policy_db.h" + +typedef struct _krb5_db2_context { + krb5_boolean db_inited; /* Context initialized */ + char * db_name; /* Name of database */ + DB * db; /* DB handle */ + krb5_boolean hashfirst; /* Try hash database type first */ + char * db_lf_name; /* Name of lock file */ + int db_lf_file; /* File descriptor of lock file */ + time_t db_lf_time; /* Time last updated */ + int db_locks_held; /* Number of times locked */ + int db_lock_mode; /* Last lock mode, e.g. greatest*/ + krb5_boolean db_nb_locks; /* [Non]Blocking lock modes */ + krb5_keyblock *db_master_key; /* Master key of database */ + osa_adb_policy_t policy_db; +} krb5_db2_context; + +#define KRB5_DB2_MAX_RETRY 5 + +#define KDB2_LOCK_EXT ".ok" + +krb5_error_code krb5_db2_db_set_name + (krb5_context, + char * ); +krb5_error_code krb5_db2_db_init + (krb5_context); +krb5_error_code krb5_db2_db_fini + (krb5_context); +krb5_error_code krb5_db2_db_get_age + (krb5_context, + char *, + time_t * ); +krb5_error_code krb5_db2_db_create + (krb5_context, + char *, + krb5_int32); +krb5_error_code krb5_db2_db_destroy + (krb5_context, + char * ); +krb5_error_code krb5_db2_db_rename + (krb5_context, + char *, + char * ); +krb5_error_code krb5_db2_db_get_principal + (krb5_context, + krb5_const_principal, + krb5_db_entry *, + int *, + krb5_boolean * ); +krb5_error_code krb5_db2_db_free_principal + (krb5_context, + krb5_db_entry *, + int ); +krb5_error_code krb5_db2_db_put_principal + (krb5_context, + krb5_db_entry *, + int *, + char **db_args + ); +krb5_error_code krb5_db2_db_iterate_ext + (krb5_context, + krb5_error_code (*) (krb5_pointer, + krb5_db_entry *), + krb5_pointer, int, int ); +krb5_error_code krb5_db2_db_iterate +(krb5_context,char *, + krb5_error_code (*) (krb5_pointer, + krb5_db_entry *), + krb5_pointer ); +krb5_error_code krb5_db2_db_set_nonblocking + (krb5_context, + krb5_boolean, + krb5_boolean * ); +krb5_boolean krb5_db2_db_set_lockmode + (krb5_context, + krb5_boolean ); +krb5_error_code krb5_db2_db_open_database + (krb5_context); +krb5_error_code krb5_db2_db_close_database + (krb5_context); + +krb5_error_code +krb5_db2_set_master_key_ext ( krb5_context kcontext, + char *pwd, + krb5_keyblock *key); + +krb5_error_code +krb5_db2_db_set_mkey( krb5_context context, + krb5_keyblock *key); + +krb5_error_code +krb5_db2_db_get_mkey( krb5_context context, + krb5_keyblock **key); + +krb5_error_code +krb5_db2_db_put_principal( krb5_context context, + krb5_db_entry *entries, + register int *nentries, + char **db_args); + +krb5_error_code +krb5_db2_db_delete_principal(krb5_context context, + krb5_const_principal searchfor, + int *nentries); + +krb5_error_code krb5_db2_lib_init(krb5_set_err_func_t); + +krb5_error_code krb5_db2_lib_cleanup(void); + +krb5_error_code +krb5_db2_db_unlock(krb5_context); + +krb5_error_code +krb5_db2_db_set_option ( krb5_context kcontext, + int option, + void *value ); + +krb5_error_code +krb5_db2_db_lock( krb5_context context, + int in_mode); + + +krb5_error_code +krb5_db2_open( krb5_context kcontext, + char *conf_section, + char **db_args, + int mode ); + +krb5_error_code krb5_db2_create( krb5_context kcontext, + char *conf_section, + char **db_args ); + +krb5_error_code krb5_db2_destroy( krb5_context kcontext, + char *conf_section, + char **db_args ); + +const char * krb5_db2_err2str( krb5_context kcontext, + long err_code ); + +void * +krb5_db2_alloc( krb5_context kcontext, + void *ptr, + size_t size ); + +void +krb5_db2_free( krb5_context kcontext, + void *ptr ); + + + + + +/* policy management functions */ +krb5_error_code +krb5_db2_create_policy(krb5_context context, osa_policy_ent_t entry); + +krb5_error_code krb5_db2_get_policy ( krb5_context kcontext, + char *name, + osa_policy_ent_t *policy, + int *cnt); + +krb5_error_code krb5_db2_get_policy ( krb5_context kcontext, + char *name, + osa_policy_ent_t *policy, + int *cnt); + +krb5_error_code krb5_db2_put_policy ( krb5_context kcontext, + osa_policy_ent_t policy ); + +krb5_error_code krb5_db2_iter_policy ( krb5_context kcontext, + char *match_entry, + osa_adb_iter_policy_func func, + void *data ); + +krb5_error_code krb5_db2_delete_policy ( krb5_context kcontext, + char *policy ); + +void krb5_db2_free_policy( krb5_context kcontext, + osa_policy_ent_t entry ); + + + +extern krb5_set_err_func_t krb5_db2_dal_err_funcp; + +#endif /* KRB5_KDB_DB2_H */ diff --git a/src/lib/kdb/kdb_db2/kdb_xdr.c b/src/lib/kdb/kdb_db2/kdb_xdr.c new file mode 100644 index 000000000..a5332ea6d --- /dev/null +++ b/src/lib/kdb/kdb_db2/kdb_xdr.c @@ -0,0 +1,816 @@ +/* + * lib/kdb/kdb_xdr.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. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * 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. + * + */ + +#include "k5-int.h" +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include "kdb_xdr.h" + +#define safe_realloc(p,n) ((p)?(realloc(p,n)):(malloc(n))) + +krb5_error_code +krb5_dbe_create_key_data(context, entry) + krb5_context context; + krb5_db_entry * entry; +{ + if ((entry->key_data = + (krb5_key_data *) safe_realloc(entry->key_data, + (sizeof(krb5_key_data)* + (entry->n_key_data + 1)))) == NULL) + return(ENOMEM); + + + memset(entry->key_data + entry->n_key_data, 0, sizeof(krb5_key_data)); + entry->n_key_data++; + + return 0; +} + +krb5_error_code +krb5_dbe_update_tl_data(context, entry, new_tl_data) + krb5_context context; + krb5_db_entry * entry; + krb5_tl_data * new_tl_data; +{ + krb5_tl_data * tl_data; + krb5_octet * tmp; + + /* copy the new data first, so we can fail cleanly if malloc() + fails */ + + if ((tmp = (krb5_octet *) malloc(new_tl_data->tl_data_length)) == NULL) + return(ENOMEM); + + /* Find an existing entry of the specified type and point at + it, or NULL if not found */ + + for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) + if (tl_data->tl_data_type == new_tl_data->tl_data_type) + break; + + /* if necessary, chain a new record in the beginning and point at it */ + + if (!tl_data) { + if ((tl_data = (krb5_tl_data *) calloc(1, sizeof(krb5_tl_data))) + == NULL) { + free(tmp); + return(ENOMEM); + } + tl_data->tl_data_next = entry->tl_data; + entry->tl_data = tl_data; + entry->n_tl_data++; + } + + /* fill in the record */ + + if (tl_data->tl_data_contents) + free(tl_data->tl_data_contents); + + tl_data->tl_data_type = new_tl_data->tl_data_type; + tl_data->tl_data_length = new_tl_data->tl_data_length; + tl_data->tl_data_contents = tmp; + memcpy(tmp, new_tl_data->tl_data_contents, tl_data->tl_data_length); + + return(0); +} + +krb5_error_code +krb5_dbe_lookup_tl_data(context, entry, ret_tl_data) + krb5_context context; + krb5_db_entry * entry; + krb5_tl_data * ret_tl_data; +{ + krb5_tl_data *tl_data; + + for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { + if (tl_data->tl_data_type == ret_tl_data->tl_data_type) { + *ret_tl_data = *tl_data; + return(0); + } + } + + /* if the requested record isn't found, return zero bytes. + if it ever means something to have a zero-length tl_data, + this code and its callers will have to be changed */ + + ret_tl_data->tl_data_length = 0; + ret_tl_data->tl_data_contents = NULL; + return(0); +} + +krb5_error_code +krb5_dbe_update_last_pwd_change(context, entry, stamp) + krb5_context context; + krb5_db_entry * entry; + krb5_timestamp stamp; +{ + krb5_tl_data tl_data; + krb5_octet buf[4]; /* this is the encoded size of an int32 */ + + tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; + tl_data.tl_data_length = sizeof(buf); + krb5_kdb_encode_int32((krb5_int32) stamp, buf); + tl_data.tl_data_contents = buf; + + return(krb5_dbe_update_tl_data(context, entry, &tl_data)); +} + +krb5_error_code +krb5_dbe_lookup_last_pwd_change(context, entry, stamp) + krb5_context context; + krb5_db_entry * entry; + krb5_timestamp * stamp; +{ + krb5_tl_data tl_data; + krb5_error_code code; + krb5_int32 tmp; + + tl_data.tl_data_type = KRB5_TL_LAST_PWD_CHANGE; + + if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) + return(code); + + if (tl_data.tl_data_length != 4) { + *stamp = 0; + return(0); + } + + krb5_kdb_decode_int32(tl_data.tl_data_contents, tmp); + + *stamp = (krb5_timestamp) tmp; + + return(0); +} + +/* it seems odd that there's no function to remove a tl_data, but if + I need one, I'll add one */ + +krb5_error_code +krb5_dbe_update_mod_princ_data(context, entry, mod_date, mod_princ) + krb5_context context; + krb5_db_entry * entry; + krb5_timestamp mod_date; + krb5_const_principal mod_princ; +{ + krb5_tl_data tl_data; + + krb5_error_code retval = 0; + krb5_octet * nextloc = 0; + char * unparse_mod_princ = 0; + unsigned int unparse_mod_princ_size; + + if ((retval = krb5_unparse_name(context, mod_princ, + &unparse_mod_princ))) + return(retval); + + unparse_mod_princ_size = strlen(unparse_mod_princ) + 1; + + if ((nextloc = (krb5_octet *) malloc(unparse_mod_princ_size + 4)) + == NULL) { + free(unparse_mod_princ); + return(ENOMEM); + } + + tl_data.tl_data_type = KRB5_TL_MOD_PRINC; + tl_data.tl_data_length = unparse_mod_princ_size + 4; + tl_data.tl_data_contents = nextloc; + + /* Mod Date */ + krb5_kdb_encode_int32(mod_date, nextloc); + + /* Mod Princ */ + memcpy(nextloc+4, unparse_mod_princ, unparse_mod_princ_size); + + retval = krb5_dbe_update_tl_data(context, entry, &tl_data); + + free(unparse_mod_princ); + free(nextloc); + + return(retval); +} + +krb5_error_code +krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ) + krb5_context context; + krb5_db_entry * entry; + krb5_timestamp * mod_time; + krb5_principal * mod_princ; +{ + krb5_tl_data tl_data; + krb5_error_code code; + + tl_data.tl_data_type = KRB5_TL_MOD_PRINC; + + if ((code = krb5_dbe_lookup_tl_data(context, entry, &tl_data))) + return(code); + + if ((tl_data.tl_data_length < 5) || + (tl_data.tl_data_contents[tl_data.tl_data_length-1] != '\0')) + return(KRB5_KDB_TRUNCATED_RECORD); + + /* Mod Date */ + krb5_kdb_decode_int32(tl_data.tl_data_contents, *mod_time); + + /* Mod Princ */ + if ((code = krb5_parse_name(context, + (const char *) (tl_data.tl_data_contents+4), + mod_princ))) + return(code); + + return(0); +} + +krb5_error_code +krb5_encode_princ_dbkey(context, key, principal) + krb5_context context; + krb5_data *key; + krb5_const_principal principal; +{ + char *princ_name; + krb5_error_code retval; + + if (!(retval = krb5_unparse_name(context, principal, &princ_name))) { + /* need to store the NULL for decoding */ + key->length = strlen(princ_name)+1; + key->data = princ_name; + } + return(retval); +} + +void +krb5_free_princ_dbkey(context, key) + krb5_context context; + krb5_data *key; +{ + (void) krb5_free_data_contents(context, key); +} + +krb5_error_code +krb5_encode_princ_contents(context, content, entry) + krb5_context context; + krb5_data * content; + krb5_db_entry * entry; +{ + int i, j; + unsigned int unparse_princ_size; + char * unparse_princ; + char * nextloc; + krb5_tl_data * tl_data; + krb5_error_code retval; + krb5_int16 psize16; + + /* + * Generate one lump of data from the krb5_db_entry. + * This data must be independent of byte order of the machine, + * compact and extensible. + */ + + /* + * First allocate enough space for all the data. + * Need 2 bytes for the length of the base structure + * then 36 [ 8 * 4 + 2 * 2] bytes for the base information + * [ attributes, max_life, max_renewable_life, expiration, + * pw_expiration, last_success, last_failed, fail_auth_count ] + * [ n_key_data, n_tl_data ] + * then XX bytes [ e_length ] for the extra data [ e_data ] + * then XX bytes [ 2 for length + length for string ] for the principal, + * then (4 [type + length] + tl_data_length) bytes per tl_data + * then (4 + (4 + key_data_length) per key_data_contents) bytes per key_data + */ + content->length = entry->len + entry->e_length; + + if ((retval = krb5_unparse_name(context, entry->princ, &unparse_princ))) + return(retval); + + unparse_princ_size = strlen(unparse_princ) + 1; + content->length += unparse_princ_size; + content->length += 2; + + i = 0; + /* tl_data is a linked list */ + for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { + content->length += tl_data->tl_data_length; + content->length += 4; /* type, length */ + i++; + } + + if (i != entry->n_tl_data) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto epc_error; + } + + /* key_data is an array */ + for (i = 0; i < entry->n_key_data; i++) { + content->length += 4; /* Version, KVNO */ + for (j = 0; j < entry->key_data[i].key_data_ver; j++) { + content->length += entry->key_data[i].key_data_length[j]; + content->length += 4; /* type + length */ + } + } + + if ((content->data = malloc(content->length)) == NULL) { + retval = ENOMEM; + goto epc_error; + } + + /* + * Now we go through entry again, this time copying data + * These first entries are always saved regardless of version + */ + nextloc = content->data; + + /* Base Length */ + krb5_kdb_encode_int16(entry->len, nextloc); + nextloc += 2; + + /* Attributes */ + krb5_kdb_encode_int32(entry->attributes, nextloc); + nextloc += 4; + + /* Max Life */ + krb5_kdb_encode_int32(entry->max_life, nextloc); + nextloc += 4; + + /* Max Renewable Life */ + krb5_kdb_encode_int32(entry->max_renewable_life, nextloc); + nextloc += 4; + + /* When the client expires */ + krb5_kdb_encode_int32(entry->expiration, nextloc); + nextloc += 4; + + /* When its passwd expires */ + krb5_kdb_encode_int32(entry->pw_expiration, nextloc); + nextloc += 4; + + /* Last successful passwd */ + krb5_kdb_encode_int32(entry->last_success, nextloc); + nextloc += 4; + + /* Last failed passwd attempt */ + krb5_kdb_encode_int32(entry->last_failed, nextloc); + nextloc += 4; + + /* # of failed passwd attempt */ + krb5_kdb_encode_int32(entry->fail_auth_count, nextloc); + nextloc += 4; + + /* # tl_data strutures */ + krb5_kdb_encode_int16(entry->n_tl_data, nextloc); + nextloc += 2; + + /* # key_data strutures */ + krb5_kdb_encode_int16(entry->n_key_data, nextloc); + nextloc += 2; + + /* Put extended fields here */ + if (entry->len != KRB5_KDB_V1_BASE_LENGTH) + abort(); + + /* Any extra data that this version doesn't understand. */ + if (entry->e_length) { + memcpy(nextloc, entry->e_data, entry->e_length); + nextloc += entry->e_length; + } + + /* + * Now we get to the principal. + * To squeze a few extra bytes out it is always assumed to come + * after the base type. + */ + psize16 = (krb5_int16) unparse_princ_size; + krb5_kdb_encode_int16(psize16, nextloc); + nextloc += 2; + (void) memcpy(nextloc, unparse_princ, unparse_princ_size); + nextloc += unparse_princ_size; + + /* tl_data is a linked list, of type, legth, contents */ + for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) { + krb5_kdb_encode_int16(tl_data->tl_data_type, nextloc); + nextloc += 2; + krb5_kdb_encode_int16(tl_data->tl_data_length, nextloc); + nextloc += 2; + + memcpy(nextloc, tl_data->tl_data_contents, tl_data->tl_data_length); + nextloc += tl_data->tl_data_length; + } + + /* key_data is an array */ + for (i = 0; i < entry->n_key_data; i++) { + krb5_kdb_encode_int16(entry->key_data[i].key_data_ver, nextloc); + nextloc += 2; + krb5_kdb_encode_int16(entry->key_data[i].key_data_kvno, nextloc); + nextloc += 2; + + for (j = 0; j < entry->key_data[i].key_data_ver; j++) { + krb5_int16 type = entry->key_data[i].key_data_type[j]; + krb5_ui_2 length = entry->key_data[i].key_data_length[j]; + + krb5_kdb_encode_int16(type, nextloc); + nextloc += 2; + krb5_kdb_encode_int16(length, nextloc); + nextloc += 2; + + if (length) { + memcpy(nextloc, entry->key_data[i].key_data_contents[j],length); + nextloc += length; + } + } + } + +epc_error:; + free(unparse_princ); + return retval; +} + +void +krb5_free_princ_contents(context, contents) + krb5_context context; + krb5_data *contents; +{ + krb5_free_data_contents(context, contents); + return; +} + +krb5_error_code +krb5_decode_princ_contents(context, content, entry) + krb5_context context; + krb5_data * content; + krb5_db_entry * entry; +{ + int sizeleft, i; + char * nextloc; + krb5_tl_data ** tl_data; + krb5_int16 i16; + + krb5_error_code retval; + + /* Zero out entry and NULL pointers */ + memset(entry, 0, sizeof(krb5_db_entry)); + + /* + * undo the effects of encode_princ_contents. + * + * The first part is decoding the base type. If the base type is + * bigger than the original base type then the additional fields + * need to be filled in. If the base type is larger than any + * known base type the additional data goes in e_data. + */ + + /* First do the easy stuff */ + nextloc = content->data; + sizeleft = content->length; + if ((sizeleft -= KRB5_KDB_V1_BASE_LENGTH) < 0) + return KRB5_KDB_TRUNCATED_RECORD; + + /* Base Length */ + krb5_kdb_decode_int16(nextloc, entry->len); + nextloc += 2; + + /* Attributes */ + krb5_kdb_decode_int32(nextloc, entry->attributes); + nextloc += 4; + + /* Max Life */ + krb5_kdb_decode_int32(nextloc, entry->max_life); + nextloc += 4; + + /* Max Renewable Life */ + krb5_kdb_decode_int32(nextloc, entry->max_renewable_life); + nextloc += 4; + + /* When the client expires */ + krb5_kdb_decode_int32(nextloc, entry->expiration); + nextloc += 4; + + /* When its passwd expires */ + krb5_kdb_decode_int32(nextloc, entry->pw_expiration); + nextloc += 4; + + /* Last successful passwd */ + krb5_kdb_decode_int32(nextloc, entry->last_success); + nextloc += 4; + + /* Last failed passwd attempt */ + krb5_kdb_decode_int32(nextloc, entry->last_failed); + nextloc += 4; + + /* # of failed passwd attempt */ + krb5_kdb_decode_int32(nextloc, entry->fail_auth_count); + nextloc += 4; + + /* # tl_data strutures */ + krb5_kdb_decode_int16(nextloc, entry->n_tl_data); + nextloc += 2; + + if (entry->n_tl_data < 0) + return KRB5_KDB_TRUNCATED_RECORD; + + /* # key_data strutures */ + krb5_kdb_decode_int16(nextloc, entry->n_key_data); + nextloc += 2; + + if (entry->n_key_data < 0) + return KRB5_KDB_TRUNCATED_RECORD; + + /* Check for extra data */ + if (entry->len > KRB5_KDB_V1_BASE_LENGTH) { + entry->e_length = entry->len - KRB5_KDB_V1_BASE_LENGTH; + if ((entry->e_data = (krb5_octet *)malloc(entry->e_length))) { + memcpy(entry->e_data, nextloc, entry->e_length); + nextloc += entry->e_length; + } else { + return ENOMEM; + } + } + + /* + * Get the principal name for the entry + * (stored as a string which gets unparsed.) + */ + if ((sizeleft -= 2) < 0) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto error_out; + } + + i = 0; + krb5_kdb_decode_int16(nextloc, i16); + i = (int) i16; + nextloc += 2; + + if ((retval = krb5_parse_name(context, nextloc, &(entry->princ)))) + goto error_out; + if (((size_t) i != (strlen(nextloc) + 1)) || (sizeleft < i)) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto error_out; + } + sizeleft -= i; + nextloc += i; + + /* tl_data is a linked list */ + tl_data = &entry->tl_data; + for (i = 0; i < entry->n_tl_data; i++) { + if ((sizeleft -= 4) < 0) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto error_out; + } + if ((*tl_data = (krb5_tl_data *) + malloc(sizeof(krb5_tl_data))) == NULL) { + retval = ENOMEM; + goto error_out; + } + (*tl_data)->tl_data_next = NULL; + (*tl_data)->tl_data_contents = NULL; + krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_type); + nextloc += 2; + krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_length); + nextloc += 2; + + if ((sizeleft -= (*tl_data)->tl_data_length) < 0) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto error_out; + } + if (((*tl_data)->tl_data_contents = (krb5_octet *) + malloc((*tl_data)->tl_data_length)) == NULL) { + retval = ENOMEM; + goto error_out; + } + memcpy((*tl_data)->tl_data_contents,nextloc,(*tl_data)->tl_data_length); + nextloc += (*tl_data)->tl_data_length; + tl_data = &((*tl_data)->tl_data_next); + } + + /* key_data is an array */ + if (entry->n_key_data && ((entry->key_data = (krb5_key_data *) + malloc(sizeof(krb5_key_data) * entry->n_key_data)) == NULL)) { + retval = ENOMEM; + goto error_out; + } + for (i = 0; i < entry->n_key_data; i++) { + krb5_key_data * key_data; + int j; + + if ((sizeleft -= 4) < 0) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto error_out; + } + key_data = entry->key_data + i; + memset(key_data, 0, sizeof(krb5_key_data)); + krb5_kdb_decode_int16(nextloc, key_data->key_data_ver); + nextloc += 2; + krb5_kdb_decode_int16(nextloc, key_data->key_data_kvno); + nextloc += 2; + + /* key_data_ver determins number of elements and how to unparse them. */ + if (key_data->key_data_ver <= KRB5_KDB_V1_KEY_DATA_ARRAY) { + for (j = 0; j < key_data->key_data_ver; j++) { + if ((sizeleft -= 4) < 0) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto error_out; + } + krb5_kdb_decode_int16(nextloc, key_data->key_data_type[j]); + nextloc += 2; + krb5_kdb_decode_int16(nextloc, key_data->key_data_length[j]); + nextloc += 2; + + if ((sizeleft -= key_data->key_data_length[j]) < 0) { + retval = KRB5_KDB_TRUNCATED_RECORD; + goto error_out; + } + if (key_data->key_data_length[j]) { + if ((key_data->key_data_contents[j] = (krb5_octet *) + malloc(key_data->key_data_length[j])) == NULL) { + retval = ENOMEM; + goto error_out; + } + memcpy(key_data->key_data_contents[j], nextloc, + key_data->key_data_length[j]); + nextloc += key_data->key_data_length[j]; + } + } + } else { + /* This isn't right. I'll fix it later */ + abort(); + } + } + return 0; + +error_out:; + krb5_dbe_free_contents(context, entry); + return retval; +} + +void +krb5_dbe_free_contents(context, entry) + krb5_context context; + krb5_db_entry * entry; +{ + krb5_tl_data * tl_data_next; + krb5_tl_data * tl_data; + int i, j; + + if (entry->e_data) + free(entry->e_data); + if (entry->princ) + krb5_free_principal(context, entry->princ); + for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) { + tl_data_next = tl_data->tl_data_next; + if (tl_data->tl_data_contents) + free(tl_data->tl_data_contents); + free(tl_data); + } + if (entry->key_data) { + for (i = 0; i < entry->n_key_data; i++) { + for (j = 0; j < entry->key_data[i].key_data_ver; j++) { + if (entry->key_data[i].key_data_length[j]) { + if (entry->key_data[i].key_data_contents[j]) { + memset(entry->key_data[i].key_data_contents[j], + 0, + (unsigned) entry->key_data[i].key_data_length[j]); + free (entry->key_data[i].key_data_contents[j]); + } + } + entry->key_data[i].key_data_contents[j] = NULL; + entry->key_data[i].key_data_length[j] = 0; + entry->key_data[i].key_data_type[j] = 0; + } + } + free(entry->key_data); + } + memset(entry, 0, sizeof(*entry)); + return; +} + +/* + * Given a particular enctype and optional salttype and kvno, find the + * most appropriate krb5_key_data entry of the database entry. + * + * If stype or kvno is negative, it is ignored. + * If kvno is 0 get the key which is maxkvno for the princ and matches + * the other attributes. + */ +krb5_error_code +krb5_dbe_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap) + krb5_context kcontext; + krb5_db_entry *dbentp; + krb5_int32 *start; + krb5_int32 ktype; + krb5_int32 stype; + krb5_int32 kvno; + krb5_key_data **kdatap; +{ + int i, idx; + int maxkvno; + krb5_key_data *datap; + krb5_error_code ret; + + ret = 0; + if (kvno == -1 && stype == -1 && ktype == -1) + kvno = 0; + + if (kvno == 0) { + /* Get the max key version */ + for (i = 0; i < dbentp->n_key_data; i++) { + if (kvno < dbentp->key_data[i].key_data_kvno) { + kvno = dbentp->key_data[i].key_data_kvno; + } + } + } + + maxkvno = -1; + datap = (krb5_key_data *) NULL; + for (i = *start; i < dbentp->n_key_data; i++) { + krb5_boolean similar; + krb5_int32 db_stype; + + ret = 0; + if (dbentp->key_data[i].key_data_ver > 1) { + db_stype = dbentp->key_data[i].key_data_type[1]; + } else { + db_stype = KRB5_KDB_SALTTYPE_NORMAL; + } + + /* + * Filter out non-permitted enctypes. + */ + if (!krb5_is_permitted_enctype(kcontext, + dbentp->key_data[i].key_data_type[0])) { + ret = KRB5_KDB_NO_PERMITTED_KEY; + continue; + } + + + if (ktype > 0) { + if ((ret = krb5_c_enctype_compare(kcontext, (krb5_enctype) ktype, + dbentp->key_data[i].key_data_type[0], + &similar))) + + return(ret); + } + + if (((ktype <= 0) || similar) && + ((db_stype == stype) || (stype < 0))) { + if (kvno >= 0) { + if (kvno == dbentp->key_data[i].key_data_kvno) { + datap = &dbentp->key_data[i]; + idx = i; + maxkvno = kvno; + break; + } + } else { + if (dbentp->key_data[i].key_data_kvno > maxkvno) { + maxkvno = dbentp->key_data[i].key_data_kvno; + datap = &dbentp->key_data[i]; + idx = i; + } + } + } + } + if (maxkvno < 0) + return ret ? ret : KRB5_KDB_NO_MATCHING_KEY; + *kdatap = datap; + *start = idx+1; + return 0; +} + +krb5_error_code +krb5_dbe_find_enctype(kcontext, dbentp, ktype, stype, kvno, kdatap) + krb5_context kcontext; + krb5_db_entry *dbentp; + krb5_int32 ktype; + krb5_int32 stype; + krb5_int32 kvno; + krb5_key_data **kdatap; +{ + krb5_int32 start = 0; + + return krb5_dbe_search_enctype(kcontext, dbentp, &start, ktype, stype, + kvno, kdatap); +} + + diff --git a/src/lib/kdb/kdb_db2/kdb_xdr.h b/src/lib/kdb/kdb_db2/kdb_xdr.h new file mode 100644 index 000000000..2ee068fbd --- /dev/null +++ b/src/lib/kdb/kdb_db2/kdb_xdr.h @@ -0,0 +1,32 @@ +#ifndef _KDB2_XDR_H +#define _KDB2_XDR_H + +krb5_error_code +krb5_encode_princ_dbkey( krb5_context context, + krb5_data *key, + krb5_const_principal principal); + +krb5_error_code +krb5_decode_princ_contents( krb5_context context, + krb5_data * content, + krb5_db_entry * entry); + +void +krb5_dbe_free_contents( krb5_context context, + krb5_db_entry * entry); + +krb5_error_code +krb5_encode_princ_contents( krb5_context context, + krb5_data * content, + krb5_db_entry * entry); + + +void +krb5_free_princ_dbkey( krb5_context context, + krb5_data *key); + +void +krb5_free_princ_contents( krb5_context context, + krb5_data *contents); + +#endif diff --git a/src/lib/kdb/kdb_db2/libkdb_db2.exports b/src/lib/kdb/kdb_db2/libkdb_db2.exports new file mode 100644 index 000000000..b6902ebda --- /dev/null +++ b/src/lib/kdb/kdb_db2/libkdb_db2.exports @@ -0,0 +1 @@ +krb5_db_vftabl_kdb_db2 diff --git a/src/lib/kdb/kdb_db2/pol_xdr.c b/src/lib/kdb/kdb_db2/pol_xdr.c new file mode 100644 index 000000000..37761080a --- /dev/null +++ b/src/lib/kdb/kdb_db2/pol_xdr.c @@ -0,0 +1,88 @@ +#include <sys/types.h> +#include <krb5.h> +#include <gssrpc/rpc.h> +#include <krb5/kdb.h> +#include "policy_db.h" +#ifdef HAVE_MEMORY_H +#include <memory.h> +#endif + +bool_t xdr_nullstring(XDR *xdrs, char **objp) +{ + u_int size; + + if (xdrs->x_op == XDR_ENCODE) { + if (*objp == NULL) + size = 0; + else + size = strlen(*objp) + 1; + } + if (! xdr_u_int(xdrs, &size)) { + return FALSE; + } + switch (xdrs->x_op) { + case XDR_DECODE: + if (size == 0) { + *objp = NULL; + return TRUE; + } else if (*objp == NULL) { + *objp = (char *) mem_alloc(size); + if (*objp == NULL) { + errno = ENOMEM; + return FALSE; + } + } + return (xdr_opaque(xdrs, *objp, size)); + + case XDR_ENCODE: + if (size != 0) + return (xdr_opaque(xdrs, *objp, size)); + return TRUE; + + case XDR_FREE: + if (*objp != NULL) + mem_free(*objp, size); + *objp = NULL; + return TRUE; + } + + return FALSE; +} + + + +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/kdb/kdb_db2/policy_db.h b/src/lib/kdb/kdb_db2/policy_db.h new file mode 100644 index 000000000..772b24d07 --- /dev/null +++ b/src/lib/kdb/kdb_db2/policy_db.h @@ -0,0 +1,87 @@ +/* + * Data Types for policy and principal information that + * exists in the respective databases. + * + * $Header$ + * + * This file was originally created with rpcgen. + * It has been hacked up since then. + */ + +#ifndef __ADB_H__ +#define __ADB_H__ +#include <sys/types.h> +#include "k5-int.h" +#include <krb5/kdb.h> +#include <db.h> +#include <gssrpc/types.h> +#include <gssrpc/xdr.h> +#include "adb_err.h" +#include <com_err.h> + +typedef long osa_adb_ret_t; + +#define OSA_ADB_POLICY_DB_MAGIC 0x12345A00 + +#define OSA_ADB_POLICY_VERSION_MASK 0x12345D00 +#define OSA_ADB_POLICY_VERSION_1 0x12345D01 + + + +typedef struct _osa_adb_db_lock_ent_t { + FILE *lockfile; + char *filename; + int refcnt, lockmode, lockcnt; + krb5_context context; +} osa_adb_lock_ent, *osa_adb_lock_t; + +typedef struct _osa_adb_db_ent_t { + int magic; + DB *db; + HASHINFO info; + BTREEINFO btinfo; + char *filename; + osa_adb_lock_t lock; + int opencnt; +} osa_adb_db_ent, *osa_adb_db_t, *osa_adb_princ_t, *osa_adb_policy_t; + +/* + * Return Code (the rest are in adb_err.h) + */ + +#define OSA_ADB_OK 0 + +/* + * Functions + */ + +krb5_error_code osa_adb_create_db(char *filename, char *lockfile, int magic); +krb5_error_code osa_adb_destroy_db(char *filename, char *lockfile, int magic); +krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom, + char *fileto, char *lockto, int magic); +krb5_error_code osa_adb_init_db(osa_adb_db_t *dbp, char *filename, + char *lockfile, int magic); +krb5_error_code osa_adb_fini_db(osa_adb_db_t db, int magic); +krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode); +krb5_error_code osa_adb_release_lock(osa_adb_db_t db); +krb5_error_code osa_adb_open_and_lock(osa_adb_princ_t db, int locktype); +krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db); +krb5_error_code osa_adb_close_policy(osa_adb_policy_t db); +krb5_error_code osa_adb_create_policy(osa_adb_policy_t db, + osa_policy_ent_t entry); +krb5_error_code osa_adb_destroy_policy(osa_adb_policy_t db, + char * name); +krb5_error_code osa_adb_get_policy(osa_adb_policy_t db, + char * name, + osa_policy_ent_t *entry, + int *cnt); +krb5_error_code osa_adb_put_policy(osa_adb_policy_t db, + osa_policy_ent_t entry); +krb5_error_code osa_adb_iter_policy(osa_adb_policy_t db, + osa_adb_iter_policy_func func, + void * data); +void osa_free_policy_ent(osa_policy_ent_t val); + +bool_t xdr_osa_policy_ent_rec(XDR *xdrs, osa_policy_ent_t objp); + +#endif /* __ADB_H__ */ |