* t_kdb.c: Reflect changes in the API, mostly db_create
authorTom Yu <tlyu@mit.edu>
Fri, 25 Jul 1997 19:34:42 +0000 (19:34 +0000)
committerTom Yu <tlyu@mit.edu>
Fri, 25 Jul 1997 19:34:42 +0000 (19:34 +0000)
* Makefile.in: Bump version due to major reworking.

* kdb_db2.h:
* kdb_db2.c: Add Berkely DB backend.

* keytab.c: Add support for new kdb API; delete dead arguments.

* kdb_xdr.c: Remove dependencies on dbm; encode things to
krb5_datas rather than datums.

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

src/lib/kdb/ChangeLog
src/lib/kdb/Makefile.in
src/lib/kdb/kdb_db2.c [new file with mode: 0644]
src/lib/kdb/kdb_db2.h [new file with mode: 0644]
src/lib/kdb/kdb_xdr.c
src/lib/kdb/keytab.c
src/lib/kdb/t_kdb.c

index fa3a680930349b32eef3e5cda7605f689f5c606f..05497b130e5dce7ad2f973ed09ce008698489d4d 100644 (file)
@@ -1,3 +1,17 @@
+Fri Jul 25 15:29:03 1997  Tom Yu  <tlyu@mit.edu>
+
+       * t_kdb.c: Reflect changes in the API, mostly db_create.
+
+       * Makefile.in: Bump version due to major reworking.
+
+       * kdb_db2.h:
+       * kdb_db2.c: Add Berkely DB backend.
+
+       * keytab.c: Add support for new kdb API; delete dead arguments.
+
+       * kdb_xdr.c: Remove dependencies on dbm; encode things to
+       krb5_datas rather than datums.
+
 Mon Mar 24 12:19:03 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
 
        * t_kdb.c (do_testing): Clean up error handling for krb5_init_context.
index b842f95242d6089e99437a840ec8dcc22e055e7a..370aa428e135335becbb687cae9e2d8f5332d114 100644 (file)
@@ -5,7 +5,7 @@ PROG_LIBPATH=-L$(TOPLIBD)
 PROG_RPATH=$(KRB5_LIBDIR)
 
 LIB=kdb5
-LIBMAJOR=1
+LIBMAJOR=2
 LIBMINOR=0
 RELDIR=kdb
 # Depends on libcrypto and libkrb5
@@ -24,7 +24,7 @@ SRCS= \
        $(srcdir)/encrypt_key.c \
        $(srcdir)/decrypt_key.c \
        $(srcdir)/kdb_cpw.c \
-       $(srcdir)/kdb_dbm.c \
+       $(srcdir)/kdb_db2.c \
        $(srcdir)/kdb_xdr.c \
        $(srcdir)/verify_mky.c \
        $(srcdir)/fetch_mkey.c \
@@ -37,7 +37,7 @@ STLIBOBJS= \
        encrypt_key.o \
        decrypt_key.o \
        kdb_cpw.o \
-       kdb_dbm.o \
+       kdb_db2.o \
        kdb_xdr.o \
        verify_mky.o \
        fetch_mkey.o \
diff --git a/src/lib/kdb/kdb_db2.c b/src/lib/kdb/kdb_db2.c
new file mode 100644 (file)
index 0000000..703c11d
--- /dev/null
@@ -0,0 +1,1360 @@
+/*
+ * 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.  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.
+ * 
+ */
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include "k5-int.h"
+#include <db.h>
+#include <stdio.h>
+#include <errno.h>
+#include <utime.h>
+
+#define OLD_COMPAT_VERSION_1
+
+#ifdef OLD_COMPAT_VERSION_1
+#include "kdb_compat.h"
+#endif
+
+#include "kdb_db2.h"
+
+static char *gen_dbsuffix 
+       PROTOTYPE((char *, char * ));
+static krb5_error_code krb5_db2_db_start_update 
+       PROTOTYPE((krb5_context));
+static krb5_error_code krb5_db2_db_end_update 
+       PROTOTYPE((krb5_context));
+static krb5_error_code krb5_db2_db_set_hashfirst
+       PROTOTYPE((krb5_context, int));
+
+static char default_db_name[] = DEFAULT_KDB_FILE;
+
+/*
+ * 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 &&  \
+                        ((krb5_db2_context *) c->db_context)->db_inited)
+
+/*
+ * 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;
+
+    if (context->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);
+           context->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 = 1024;          /* ??? */
+    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;
+
+    if (k5db2_inited(context))
+       return KRB5_KDB_DBNOTINITED;
+    dbc = (krb5_db2_context *) 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;
+
+    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);
+
+    db_ctx = context->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;
+
+    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;
+
+    db_ctx = (krb5_db2_context *) context->db_context;
+
+    if (k5db2_inited(context)) {
+       if (close(db_ctx->db_lf_file))
+           retval = errno;
+       else
+           retval = 0;
+    }
+    if (db_ctx) {
+       k5db2_clear_context(db_ctx);
+       free(context->db_context);
+       context->db_context = NULL;
+    }
+    return retval;
+}
+
+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;
+}
+
+/*
+ * Set/Get the master key associated with the database
+ */
+krb5_error_code
+krb5_db2_db_set_mkey(context, eblock)
+    krb5_context context;
+    krb5_encrypt_block *eblock;
+{
+    krb5_db2_context *db_ctx;
+
+    if (!k5db2_inited(context))
+       return(KRB5_KDB_DBNOTINITED);
+
+    db_ctx = context->db_context;
+    db_ctx->db_master_key = eblock;
+    return 0;
+}
+
+krb5_error_code
+krb5_db2_db_get_mkey(context, eblock)
+    krb5_context context;
+    krb5_encrypt_block **eblock;
+{
+    krb5_db2_context *db_ctx;
+
+    if (!k5db2_inited(context))
+       return(KRB5_KDB_DBNOTINITED);
+
+    db_ctx = context->db_context;
+    *eblock = 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;
+
+    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;
+
+    db_ctx = context->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;
+    struct stat st;
+
+    if (!k5db2_inited(context))
+       return(KRB5_KDB_DBNOTINITED);
+    db_ctx = (krb5_db2_context *) context->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;
+    struct stat st;
+    time_t now;
+    struct utimbuf utbuf;
+
+    if (!k5db2_inited(context))
+       return(KRB5_KDB_DBNOTINITED);
+
+    retval = 0;
+    db_ctx = context->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, mode)
+    krb5_context         context;
+    int                  mode;
+{
+    krb5_db2_context *db_ctx;
+    int krb5_lock_mode;
+    DB *db;
+    krb5_error_code retval;
+    time_t mod_time;
+
+    if (!k5db2_inited(context))
+       return KRB5_KDB_DBNOTINITED;
+
+    db_ctx = (krb5_db2_context *) context->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++;
+       return(0);
+    }
+
+    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++;
+    return 0;
+
+lock_error:;
+    db_ctx->db_lock_mode = 0;
+    db_ctx->db_locks_held = 0;
+    (void) krb5_db2_db_unlock(context);
+    return retval;
+}
+
+krb5_error_code
+krb5_db2_db_unlock(context)
+    krb5_context context;
+{
+    krb5_db2_context *db_ctx;
+    DB *db;
+    krb5_error_code retval;
+
+    if (!k5db2_inited(context))
+       return KRB5_KDB_DBNOTINITED;
+
+    db_ctx = (krb5_db2_context *) context->db_context;
+    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;
+    char *okname;
+    int fd;
+    krb5_db2_context *db_ctx;
+    DB *db;
+
+    if ((retval = k5db2_init_context(context)))
+       return(retval);
+
+    db_ctx = (krb5_db2_context *) context->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);
+       }
+    }
+    return retval;
+}
+
+/*
+ * Destroy the database.  Zero's out all of the files, just to be sure.
+ */
+krb5_error_code
+destroy_file_suffix(dbname, suffix)
+    char *dbname;
+    char *suffix;
+{
+    char *filename;
+    struct stat statb;
+    int nb,fd,i,j;
+    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);
+    i = 0;
+    while (i < 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;
+           }
+       }
+       if (dowrite) {
+           lseek(fd, i, SEEK_SET);
+           nb = write(fd, zbuf, nb);
+           if (nb < 0) {
+               int retval = errno;
+               free(filename);
+               return retval;
+           }
+       }
+       i += 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;
+
+    tmpcontext = 0;
+    if (!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 *) context->db_context);
+       free(context->db_context);
+       context->db_context = NULL;
+    }
+
+    if (retval1 || retval2)
+       return (retval1 ? retval1 : retval2);
+    else
+       return 0;
+}
+
+/*
+ * "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;
+    krb5_db2_context *s_context, *db_ctx;
+
+    s_context = context->db_context;
+    context->db_context = NULL;
+    if ((retval = k5db2_init_context(context)))
+       return retval;
+    db_ctx = (krb5_db2_context *) 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 (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 *) context->db_context);
+       free(context->db_context);
+    }
+
+    context->db_context = s_context;
+    (void) krb5_db2_db_unlock(context);        /* unlock saved context db */
+
+    return retval;
+}
+
+/*
+ * 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_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;
+
+    *more = FALSE;
+    *nentries = 0;
+
+    if (!k5db2_inited(context))
+       return KRB5_KDB_DBNOTINITED;
+
+    db_ctx = (krb5_db2_context *) context->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.
+ */
+void
+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;
+}
+
+/*
+  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)
+    krb5_context context;
+    krb5_db_entry *entries;
+    register int *nentries;            /* number of entry structs to update */
+{
+    int i, n, dbret;
+    DB *db;
+    DBT key, contents;
+    krb5_data contdata, keydata;
+    krb5_error_code retval;
+    krb5_db2_context *db_ctx;
+
+    n = *nentries;
+    *nentries = 0;
+    if (!k5db2_inited(context))
+       return KRB5_KDB_DBNOTINITED;
+
+    db_ctx = (krb5_db2_context *) context->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_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;
+
+    if (!k5db2_inited(context))
+       return KRB5_KDB_DBNOTINITED;
+
+    db_ctx = (krb5_db2_context *) context->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, 
+                  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 (context, func, func_arg)
+    krb5_context context;
+    krb5_error_code (*func) PROTOTYPE((krb5_pointer, krb5_db_entry *));
+    krb5_pointer func_arg;
+{
+    krb5_db2_context *db_ctx;
+    DB *db;
+    DBT key, contents;
+    krb5_data contdata;
+    krb5_db_entry entries;
+    krb5_error_code retval;
+    int dbret;
+    
+    if (!k5db2_inited(context))
+       return KRB5_KDB_DBNOTINITED;
+
+    db_ctx = (krb5_db2_context *) context->db_context;
+    retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED);
+    if (retval)
+       return retval;
+
+    db = db_ctx->db;
+    dbret = (*db->seq)(db, &key, &contents, R_FIRST);
+    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;
+       dbret = (*db->seq)(db, &key, &contents, R_NEXT);
+    }
+    switch (dbret) {
+    case 1:
+    case 0:
+       break;
+    case -1:
+    default:
+       retval = errno;
+    }
+    (void) krb5_db2_db_unlock(context);
+    return retval;
+}
+
+krb5_boolean
+krb5_db2_db_set_lockmode(context, mode)
+    krb5_context context;
+    krb5_boolean mode;
+{
+    krb5_boolean old;
+    krb5_db2_context *db_ctx;
+
+    old = mode;
+    if ((db_ctx = (krb5_db2_context *) context->db_context)) {
+       old = db_ctx->db_nb_locks;
+       db_ctx->db_nb_locks = mode;
+    }
+    return old;
+}
+
+/*
+ * 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);
+#ifdef notdef
+       if (dbctx->db_inited && dbctx->db_dispatch && dbctx->db_name)
+           required += strlen(dbctx->db_name);
+#endif
+       kret = 0;
+       *sizep += required;
+    }
+    return(kret);
+}
+\f
+/*
+ * 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);
+}
+\f
+/*
+ * 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;
+    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_db_lock(tmpctx, lockmode);
+                   if (!kret && lockmode)
+                       dbctx->db_locks_held = lockcount;
+                   (void) krb5_db2_db_set_lockmode(tmpctx, nb_lockmode);
+               }
+               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));
+}
diff --git a/src/lib/kdb/kdb_db2.h b/src/lib/kdb/kdb_db2.h
new file mode 100644 (file)
index 0000000..2c02219
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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.  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
+
+/* renaming kludge */
+#define krb5_db2_db_set_name           krb5_db_set_name
+#define krb5_db2_db_set_nonblocking    krb5_db_set_nonblocking
+#define krb5_db2_db_init               krb5_db_init
+#define krb5_db2_db_fini               krb5_db_fini
+#define krb5_db2_db_get_age            krb5_db_get_age
+#define krb5_db2_db_create             krb5_db_create
+#define krb5_db2_db_destroy            kdb5_db_destroy
+#define krb5_db2_db_rename             krb5_db_rename
+#define krb5_db2_db_get_principal      krb5_db_get_principal
+#define krb5_db2_db_free_principal     krb5_db_free_principal
+#define krb5_db2_db_put_principal      krb5_db_put_principal
+#define krb5_db2_db_delete_principal   krb5_db_delete_principal
+#define krb5_db2_db_iterate            krb5_db_iterate
+#define krb5_db2_db_lock               krb5_db_lock
+#define krb5_db2_db_unlock             krb5_db_unlock
+#define krb5_db2_db_set_lockmode       krb5_db_set_lockmode
+#define krb5_db2_db_close_database     krb5_db_close_database
+#define krb5_db2_db_open_database      krb5_db_open_database
+#define krb5_db2_db_set_mkey           krb5_db_set_mkey
+#define krb5_db2_db_get_mkey           krb5_db_get_mkey
+
+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_encrypt_block *db_master_key;  /* Master key of database       */
+} krb5_db2_context;
+
+#define KRB5_DB2_MAX_RETRY 5
+
+#define KDB2_LOCK_EXT ".ok"
+
+krb5_error_code krb5_db2_db_set_name 
+       KRB5_PROTOTYPE((krb5_context,
+                  char * ));
+krb5_error_code krb5_db2_db_init 
+       KRB5_PROTOTYPE((krb5_context));
+krb5_error_code krb5_db2_db_fini 
+       KRB5_PROTOTYPE((krb5_context));
+krb5_error_code krb5_db2_db_get_age 
+       KRB5_PROTOTYPE((krb5_context,
+                  char *,
+                  time_t * ));
+krb5_error_code krb5_db2_db_create 
+       KRB5_PROTOTYPE((krb5_context,
+                  char *,
+                  krb5_int32));
+krb5_error_code krb5_db2_db_destroy 
+       KRB5_PROTOTYPE((krb5_context,
+                  char * ));
+krb5_error_code krb5_db2_db_rename 
+       KRB5_PROTOTYPE((krb5_context,
+                  char *,
+                  char * ));
+krb5_error_code krb5_db2_db_get_principal 
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_principal,
+                  krb5_db_entry *,
+                  int *,
+                  krb5_boolean * ));
+void krb5_db2_db_free_principal 
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_db_entry *,
+                  int ));
+krb5_error_code krb5_db2_db_put_principal 
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_db_entry *,
+                  int * ));
+krb5_error_code krb5_db2_db_iterate
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_error_code (*) KRB5_PROTOTYPE((krb5_pointer,
+                                                 krb5_db_entry *)),
+                  krb5_pointer ));
+krb5_error_code krb5_db2_db_set_nonblocking 
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_boolean,
+                  krb5_boolean * ));
+krb5_boolean krb5_db2_db_set_lockmode
+       KRB5_PROTOTYPE((krb5_context,
+                  krb5_boolean ));
+krb5_error_code krb5_db2_db_open_database 
+       KRB5_PROTOTYPE((krb5_context));
+krb5_error_code krb5_db2_db_close_database 
+       KRB5_PROTOTYPE((krb5_context));
+
+#endif /* KRB5_KDB_DB2_H */
index 044ce4c7feada80910e341a011bb93616c652dc9..03a359636a6aa6f6b5e4e2d588c48cb0509fed10 100644 (file)
@@ -243,9 +243,9 @@ krb5_dbe_lookup_mod_princ_data(context, entry, mod_time, mod_princ)
 }
 
 krb5_error_code
-krb5_encode_princ_dbmkey(context, key, principal)
+krb5_encode_princ_dbkey(context, key, principal)
     krb5_context context;
-    datum  *key;
+    krb5_data  *key;
     krb5_principal principal;
 {
     char *princ_name;
@@ -253,27 +253,24 @@ krb5_encode_princ_dbmkey(context, key, principal)
 
     if (!(retval = krb5_unparse_name(context, principal, &princ_name))) {
         /* need to store the NULL for decoding */
-        key->dsize = strlen(princ_name)+1;     
-        key->dptr = princ_name;
+        key->length = strlen(princ_name)+1;    
+        key->data = princ_name;
     }
     return(retval);
 }
 
 void
-krb5_free_princ_dbmkey(context, key)
+krb5_free_princ_dbkey(context, key)
     krb5_context context;
-    datum  *key;
+    krb5_data  *key;
 {
-    (void) free(key->dptr);
-    key->dsize = 0;
-    key->dptr = 0;
-    return;
+    (void) krb5_free_data_contents(context, key);
 }
 
 krb5_error_code
 krb5_encode_princ_contents(context, content, entry)
     krb5_context         context;
-    datum              * content;
+    krb5_data                  * content;
     krb5_db_entry      * entry;
 {
     int                  unparse_princ_size, i, j;
@@ -301,20 +298,20 @@ krb5_encode_princ_contents(context, content, entry)
      * 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->dsize = entry->len + entry->e_length;
+    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->dsize += unparse_princ_size;
-    content->dsize += 2;               
+    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->dsize += tl_data->tl_data_length;
-       content->dsize += 4; /* type, length */
+       content->length += tl_data->tl_data_length;
+       content->length += 4; /* type, length */
        i++;
     }
 
@@ -325,14 +322,14 @@ krb5_encode_princ_contents(context, content, entry)
 
     /* key_data is an array */
     for (i = 0; i < entry->n_key_data; i++) {
-       content->dsize += 4; /* Version, KVNO */
+       content->length += 4; /* Version, KVNO */
        for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
-           content->dsize += entry->key_data[i].key_data_length[j];
-           content->dsize += 4; /* type + length */
+           content->length += entry->key_data[i].key_data_length[j];
+           content->length += 4; /* type + length */
        }
     }
        
-    if ((content->dptr = malloc(content->dsize)) == NULL) {
+    if ((content->data = malloc(content->length)) == NULL) {
        retval = ENOMEM;
        goto epc_error;
     }
@@ -341,7 +338,7 @@ krb5_encode_princ_contents(context, content, entry)
      * Now we go through entry again, this time copying data 
      * These first entries are always saved regaurdless of version
      */
-    nextloc = content->dptr;
+    nextloc = content->data;
 
        /* Base Length */
     krb5_kdb_encode_int16(entry->len, nextloc);
@@ -450,18 +447,16 @@ epc_error:;
 void
 krb5_free_princ_contents(context, contents)
     krb5_context         context;
-    datum *contents;
+    krb5_data *contents;
 {
-    free(contents->dptr);
-    contents->dsize = 0;
-    contents->dptr = 0;
+    krb5_free_data_contents(context, contents);
     return;
 }
 
 krb5_error_code
 krb5_decode_princ_contents(context, content, entry)
     krb5_context         context;
-    datum              * content;
+    krb5_data                  * content;
     krb5_db_entry      * entry;
 {
     int                          sizeleft, i;
@@ -484,8 +479,8 @@ krb5_decode_princ_contents(context, content, entry)
      */
 
     /* First do the easy stuff */
-    nextloc = content->dptr;
-    sizeleft = content->dsize;
+    nextloc = content->data;
+    sizeleft = content->length;
     if ((sizeleft -= KRB5_KDB_V1_BASE_LENGTH) < 0) 
        return KRB5_KDB_TRUNCATED_RECORD;
 
index 82ed08e33c18c2a0382ea375ab7d300c4cca4f56..c114946ea031961e99f0928d19d56262cd729112 100644 (file)
@@ -23,7 +23,7 @@
  */
 
 #include "k5-int.h"
-#include "kdb_dbc.h"
+#include "kdb_kt.h"
 
 krb5_error_code krb5_ktkdb_close KRB5_PROTOTYPE((krb5_context, krb5_keytab));
 
@@ -50,23 +50,12 @@ typedef struct krb5_ktkdb_data {
 } krb5_ktkdb_data;
 
 krb5_error_code
-krb5_ktkdb_resolve(context, kdb, id)
+krb5_ktkdb_resolve(context, id)
     krb5_context         context;
-    krb5_db_context    * kdb;
     krb5_keytab                * id;
 {
-    krb5_db_context    * data;
-
     if ((*id = (krb5_keytab) malloc(sizeof(**id))) == NULL)
         return(ENOMEM);
-
-    if ((data = (krb5_db_context *)malloc(sizeof(krb5_db_context))) == NULL) {
-        krb5_xfree(*id);
-        return(ENOMEM);
-    }
-
-    memcpy(data, kdb, sizeof(krb5_db_context)); 
-    (*id)->data = (krb5_pointer)data;
     (*id)->ops = &krb5_kt_kdb_ops;
     (*id)->magic = KV5M_KEYTAB;
     return(0);
@@ -85,8 +74,7 @@ krb5_ktkdb_close(context, kt)
    * This routine should undo anything done by krb5_ktkdb_resolve().
    */
 
-  krb5_xfree(kt->data);
-  kt->ops = 0;
+  kt->ops = NULL;
   krb5_xfree(kt);
 
   return 0;
@@ -109,25 +97,24 @@ krb5_ktkdb_get_entry(context, id, principal, kvno, enctype, entry)
     int                  n = 0;
 
     /* Open database */
-    /* krb5_dbm_db_init(context); */
-    if ((kerror = krb5_dbm_db_open_database(context)))
+    /* krb5_db_init(context); */
+    if ((kerror = krb5_db_open_database(context)))
         return(kerror);
 
     /* get_principal */
-    kerror = krb5_dbm_db_get_principal(context, principal, &
+    kerror = krb5_db_get_principal(context, principal, &
                                       db_entry, &n, &more);
     if (kerror) {
-        krb5_dbm_db_close_database(context);
+        krb5_db_close_database(context);
         return(kerror);
     }
     if (n != 1) {
-       krb5_dbm_db_close_database(context);
+       krb5_db_close_database(context);
        return KRB5_KT_NOTFOUND;
     }
 
     /* match key */
-    /* WTF??? 2nd arg to dbm_db_get_mkey appears to be unused! -tlyu */
-    kerror = krb5_dbm_db_get_mkey(context, id->ops, &master_key);
+    kerror = krb5_db_get_mkey(context, &master_key);
     if (kerror)
        goto error;
 
@@ -148,6 +135,6 @@ krb5_ktkdb_get_entry(context, id, principal, kvno, enctype, entry)
     /* Close database */
   error:
     krb5_dbe_free_contents(context, &db_entry);
-    krb5_dbm_db_close_database(context);
+    krb5_db_close_database(context);
     return(kerror);
 }
index 799df31a1fac5fa9f6ed2e8864513df1299a8591..3502edac4ea3a97b9873baff4a40862ebcbd349b 100644 (file)
@@ -445,7 +445,7 @@ delete_principal(kcontext, principal)
 
 static int
 do_testing(db, passes, verbose, timing, rcases, check, save_db, dontclean,
-          ptest)
+          ptest, hash)
     char       *db;
     int                passes;
     int                verbose;
@@ -455,6 +455,7 @@ do_testing(db, passes, verbose, timing, rcases, check, save_db, dontclean,
     int                save_db;
     int                dontclean;
     int                ptest;
+    int                hash;
 {
     krb5_error_code    kret;
     krb5_context       kcontext;
@@ -475,6 +476,7 @@ do_testing(db, passes, verbose, timing, rcases, check, save_db, dontclean,
     char               *pname;
     float              elapsed;
     krb5_keyblock      stat_kb;
+    krb5_int32         crflags;
 
     mkey_name = "master/key";
     realm = master_princ_data.realm.data;
@@ -485,6 +487,7 @@ do_testing(db, passes, verbose, timing, rcases, check, save_db, dontclean,
     db_created = 0;
     linkage = "";
     oparg = "";
+    crflags = hash ? KRB5_KDB_CREATE_HASH : KRB5_KDB_CREATE_BTREE;
 
     /* Set up some initial context */
     op = "initializing krb5";
@@ -542,7 +545,7 @@ do_testing(db, passes, verbose, timing, rcases, check, save_db, dontclean,
 
     /* Create database */
     op = "creating database";
-    if ((kret = krb5_db_create(kcontext, db)))
+    if ((kret = krb5_db_create(kcontext, db, crflags)))
        goto goodbye;
 
     db_created = 1;
@@ -956,7 +959,7 @@ do_testing(db, passes, verbose, timing, rcases, check, save_db, dontclean,
        (void) krb5_db_fini(kcontext);
     if (db_created) {
        if (!kret && !save_db) {
-           kdb5_db_destroy(kcontext, db);
+           krb5_db_destroy(kcontext, db);
            krb5_db_fini(kcontext);
        } else {
            if (kret && verbose)
@@ -987,7 +990,7 @@ main(argc, argv)
     extern char        *optarg;
 
     int                do_time, do_random, num_passes, check_cont, verbose, error;
-    int                save_db, dont_clean, do_ptest;
+    int                save_db, dont_clean, do_ptest, hash;
     char       *db_name;
 
     programname = argv[0];
@@ -1006,9 +1009,10 @@ main(argc, argv)
     dont_clean = 0;
     error = 0;
     do_ptest = 0;
+    hash = 0;
 
     /* Parse argument list */
-    while ((option = getopt(argc, argv, "cd:n:prstvD")) != EOF) {
+    while ((option = getopt(argc, argv, "cd:n:prstvDh")) != EOF) {
        switch (option) {
        case 'c':
            check_cont = 1;
@@ -1041,6 +1045,9 @@ main(argc, argv)
        case 'D':
            dont_clean = 1;
            break;
+       case 'h':
+           hash = 1;
+           break;
        default:
            error++;
            break;
@@ -1058,7 +1065,8 @@ main(argc, argv)
                           check_cont,
                           save_db,
                           dont_clean,
-                          do_ptest);
+                          do_ptest,
+                          hash);
     return(error);
 }