Use new Berkeley Database code and add test
authorPaul Park <pjpark@mit.edu>
Fri, 26 May 1995 22:43:18 +0000 (22:43 +0000)
committerPaul Park <pjpark@mit.edu>
Fri, 26 May 1995 22:43:18 +0000 (22:43 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@5896 dc483132-0cff-0310-8789-dd5450dbe970

src/lib/kdb/ChangeLog
src/lib/kdb/Makefile.in
src/lib/kdb/configure.in
src/lib/kdb/kdb_dbm.c
src/lib/kdb/t_kdb.c [new file with mode: 0644]

index e3d7bf4b3ff2e0bac29cf6ac16945943babfdfa7..2438b9139f1ce1e8cf4397a7e9e8ebb4c503ed0c 100644 (file)
@@ -1,3 +1,13 @@
+
+Fri May 26 17:52:03 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * kdb_dbm.c - Change usage of dbm package or Berkeley db package
+               based on setting of BERK_DB_DBM.  Also, conditionalize
+               implicit knowledge of dbm/Berkeley db filename extensions.
+       * Makefile.in - Set BERK_DB_DBM when compiling.  Update from Berkeley
+               db build directory.
+       * configure.in - Check for random number generators.
+       * t_kdb.c - New tester for kdb code.
+
 Fri Mar 24 21:59:34 1995  Theodore Y. Ts'o  <tytso@dcl>
 
        * store_mkey.c (krb5_db_store_mkey): 
index 0f47ca8721e0731ac28d32632ed06c7b9ed421bb..73cad14da8651286ceb0db9f1a9dfd38d17771f1 100644 (file)
@@ -1,6 +1,9 @@
-CFLAGS = $(CCOPTS) $(DEFS)
+CFLAGS = $(CCOPTS) $(DEFS) -DBERK_DB_DBM
 LDFLAGS = -g
 
+LIB_SUBDIRS= $(BUILDTOP)/util/berk_db/hash
+LIBUPDATE=$(BUILDTOP)/util/libupdate
+
 all:: $(OBJS)
 
 
@@ -22,10 +25,21 @@ OBJS= \
        setup_mkey.o \
        store_mkey.o
 
-libkdb5.a: $(OBJS)
+libkdb5.a: $(OBJS) $(BUILDTOP)/util/berk_db/hash/DONE
        $(RM) $@
        $(ARADD) $@ $(OBJS)
        $(RANLIB) $@
+       if test -f $@ ; then \
+               for i in $(LIB_SUBDIRS) ; \
+               do \
+                       $(LIBUPDATE) $@ $$i/DONE $$i ; \
+               done ; \
+       else \
+               for i in $(LIB_SUBDIRS) ; \
+               do \
+                       $(LIBUPDATE) $@ --force $$i/DONE $$i ; \
+               done ; \
+       fi
        $(RM) $(TOPLIBD)/libkdb5.a
        $(CP) libkdb5.a $(BUILDTOP)/lib/libkdb5.a
        $(RANLIB) $(BUILDTOP)/lib/libkdb5.a
@@ -36,3 +50,16 @@ install:: libkdb5.a
 
 clean::
        $(RM) libkdb5.a
+
+COMERRLIB =    $(BUILDTOP)/util/et/libcom_err.a
+KDB5LIB =      libkdb5.a
+KRB5LIB =      $(TOPLIBD)/libkrb5.a
+CRYPTOLIB =    $(TOPLIBD)/libcrypto.a
+KLIBS =                $(KDB5LIB) $(KRB5LIB) $(CRYPTOLIB) $(COMERRLIB)
+
+t_kdb: t_kdb.o $(KLIBS)
+       $(LD) -o t_kdb t_kdb.o $(KLIBS) $(LIBS)
+
+check::        t_kdb
+       ./t_kdb
+
index b7d42136317b4d8e278d592f6c1d9acf3e5fffe7..4fdf651672746b6bed3e93f6f3bd1c2648835c9b 100644 (file)
@@ -6,7 +6,9 @@ AC_PROG_ARCHIVE
 AC_PROG_ARCHIVE_ADD
 AC_PROG_RANLIB
 AC_PROG_INSTALL
+WITH_NETLIB
 AC_HAVE_HEADERS(unistd.h)
+AC_HAVE_FUNCS(srand48 srand srandom)
 AppendRule([all:: libkdb5.a])
 KRB_INCLUDE
 WITH_KRB5ROOT
index 80d62cff2055b1ec1a92dc53b1b08d5d58781d99..f9022688543acd199afe9287061d9187d45b0a98 100644 (file)
@@ -109,6 +109,37 @@ static void free_decode_princ_dbmkey
        PROTOTYPE((krb5_principal ));
 #endif
 
+
+#ifdef BERK_DB_DBM
+/*
+ * This module contains all of the code which directly interfaces to
+ * the underlying representation of the Kerberos database; this
+ * implementation uses a Berkeley hashed database file to store the
+ * relations, plus a third file as a semaphore to allow the database
+ * to be replaced out from underneath the KDC server.
+ */
+extern DBM     *db_dbm_open PROTOTYPE((char *, int, int));
+extern void     db_dbm_close PROTOTYPE((DBM *));
+extern datum    db_dbm_fetch PROTOTYPE((DBM *, datum));
+extern datum    db_dbm_firstkey PROTOTYPE((DBM *));
+extern datum    db_dbm_nextkey PROTOTYPE((DBM *));
+extern int      db_dbm_delete PROTOTYPE((DBM *, datum));
+extern int      db_dbm_store PROTOTYPE((DBM *, datum, datum, int));
+extern int     db_dbm_error PROTOTYPE((DBM *));
+extern int     db_dbm_clearerr PROTOTYPE((DBM *));
+extern int     db_dbm_dirfno PROTOTYPE((DBM *));
+
+#define        KDBM_OPEN(db, fl, mo)   db_dbm_open(db, fl, mo)
+#define        KDBM_CLOSE(db)          db_dbm_close(db)
+#define        KDBM_FETCH(db, key)     db_dbm_fetch(db, key)
+#define        KDBM_FIRSTKEY(db)       db_dbm_firstkey(db)
+#define        KDBM_NEXTKEY(db)        db_dbm_nextkey(db)
+#define        KDBM_DELETE(db, key)    db_dbm_delete(db, key)
+#define        KDBM_STORE(db,key,c,f)  db_dbm_store(db, key, c, f)
+#define        KDBM_ERROR(db)          db_dbm_error(db)
+#define        KDBM_CLEARERR(db)       db_dbm_clearerr(db)
+#define        KDBM_DIRFNO(db)         db_dbm_dirfno(db)
+#else  /* BERK_DB_DBM */
 /*
  * This module contains all of the code which directly interfaces to
  * the underlying representation of the Kerberos database; this
@@ -117,6 +148,17 @@ static void free_decode_princ_dbmkey
  * third file as a semaphore to allow the database to be replaced out
  * from underneath the KDC server.
  */
+#define        KDBM_OPEN(db, fl, mo)   dbm_open(db, fl, mo)
+#define        KDBM_CLOSE(db)          dbm_close(db)
+#define        KDBM_FETCH(db, key)     dbm_fetch(db, key)
+#define        KDBM_FIRSTKEY(db)       dbm_firstkey(db)
+#define        KDBM_NEXTKEY(db)        dbm_nextkey(db)
+#define        KDBM_DELETE(db, key)    dbm_delete(db, key)
+#define        KDBM_STORE(db,key,c,f)  dbm_store(db, key, c, f)
+#define        KDBM_ERROR(db)          dbm_error(db)
+#define        KDBM_CLEARERR(db)       dbm_clearerr(db)
+#define        KDBM_DIRFNO(db)         dbm_dirfno(db)
+#endif /* BERK_DB_DBM */
 
 /*
  * Locking:
@@ -242,7 +284,7 @@ krb5_dbm_db_fini(context)
           error in close().  Possible changes to this routine: check errno
           on return from dbm_close(), call fsync on the database file
           descriptors.  */
-       dbm_close(current_db_ptr);
+       KDBM_CLOSE(current_db_ptr);
        current_db_ptr = 0;
     }
 
@@ -268,7 +310,7 @@ krb5_dbm_db_open_database(context)
   if (!inited)
     return KRB5_KDB_DBNOTINITED;
 
-  if (!(current_db_ptr = (DBM *)dbm_open(current_db_name, O_RDWR, 0600)))
+  if (!(current_db_ptr = (DBM *)KDBM_OPEN(current_db_name, O_RDWR, 0600)))
     return errno;
 
   /* It is safe to ignore errors here because all function which write
@@ -282,7 +324,7 @@ krb5_error_code
 krb5_dbm_db_close_database(context)
     krb5_context context;
 {
-  dbm_close(current_db_ptr);
+  KDBM_CLOSE(current_db_ptr);
   current_db_ptr = 0;
   (void) krb5_dbm_db_unlock(context);
   return 0;
@@ -306,10 +348,10 @@ krb5_dbm_db_set_name(context, name)
        return KRB5_KDB_DBINITED;
     if (name == NULL)
        name = default_db_name;
-    db = dbm_open(name, O_RDONLY, 0);
+    db = KDBM_OPEN(name, O_RDONLY, 0);
     if (db == NULL)
        return errno;
-    dbm_close(db);
+    KDBM_CLOSE(db);
     current_db_name = name;
     return 0;
 }
@@ -917,11 +959,11 @@ krb5_dbm_db_create(context, db_name)
 #ifndef ODBM
     DBM *db;
 
-    db = dbm_open(db_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+    db = KDBM_OPEN(db_name, O_RDWR|O_CREAT|O_EXCL, 0600);
     if (db == NULL)
        retval = errno;
     else
-       dbm_close(db);
+       KDBM_CLOSE(db);
 #else /* OLD DBM */
     char *dirname;
     char *pagname;
@@ -1030,10 +1072,15 @@ krb5_dbm_db_destroy(context, dbname)
 {
        krb5_error_code retval;
 
+#ifndef        BERK_DB_DBM
        if (retval = destroy_file_suffix(dbname, ".pag"))
                return(retval);
        if (retval = destroy_file_suffix(dbname, ".dir"))
                return(retval);
+#else  /* BERK_DB_DBM */
+       if (retval = destroy_file_suffix(dbname, ".db"))
+               return(retval);
+#endif /* BERK_DB_DBM */
        if (retval = destroy_file_suffix(dbname, ".ok"))
                return(retval);
        return(0);
@@ -1061,6 +1108,7 @@ krb5_dbm_db_rename(context, from, to)
     time_t trans;
     krb5_error_code retval;
 
+#ifndef        BERK_DB_DBM
     fromdir = gen_dbsuffix (from, ".dir");
     if (!fromdir)
        return ENOMEM;
@@ -1079,6 +1127,16 @@ krb5_dbm_db_rename(context, from, to)
        retval = ENOMEM;
        goto errout;
     }
+#else  /* BERK_DB_DBM */
+    fromdir = gen_dbsuffix (from, ".db");
+    if (!fromdir)
+       return ENOMEM;
+    todir = gen_dbsuffix (to, ".db");
+    if (!todir) {
+       retval = ENOMEM;
+       goto errout;
+    }
+#endif /* BERK_DB_DBM */
     fromok = gen_dbsuffix(from, ".ok");
     if (!fromok) {
        retval = ENOMEM;
@@ -1089,7 +1147,10 @@ krb5_dbm_db_rename(context, from, to)
        goto errout;
     
     if ((rename (fromdir, todir) == 0)
-       && (rename (frompag, topag) == 0)) {
+#ifndef        BERK_DB_DBM
+       && (rename (frompag, topag) == 0)
+#endif /* BERK_DB_DBM */
+       ) {
        (void) unlink (fromok);
        retval = 0;
     } else
@@ -1098,10 +1159,12 @@ krb5_dbm_db_rename(context, from, to)
 errout:
     if (fromok)
        free_dbsuffix (fromok);
+#ifndef        BERK_DB_DBM
     if (topag)
        free_dbsuffix (topag);
     if (frompag)
        free_dbsuffix (frompag);
+#endif /* BERK_DB_DBM */
     if (todir)
        free_dbsuffix (todir);
     if (fromdir)
@@ -1147,7 +1210,7 @@ krb5_boolean *more;                       /* are there more? */
        if (current_db_ptr)
            db = current_db_ptr;
        else {
-           db = dbm_open(current_db_name, O_RDONLY, 0600);
+           db = KDBM_OPEN(current_db_name, O_RDONLY, 0600);
            if (db == NULL) {
                retval = errno;
                (void) krb5_dbm_db_unlock(context);
@@ -1161,7 +1224,7 @@ krb5_boolean *more;                       /* are there more? */
        if (retval = encode_princ_dbmkey(context, &key, searchfor))
            goto cleanup;
 
-       contents = dbm_fetch(db, key);
+       contents = KDBM_FETCH(db, key);
        free_encode_princ_dbmkey(context, &key);
 
        if (contents.dptr == NULL)
@@ -1171,7 +1234,7 @@ krb5_boolean *more;                       /* are there more? */
        else found = 1;
 
        if (current_db_ptr == 0)
-           dbm_close(db);
+           KDBM_CLOSE(db);
        (void) krb5_dbm_db_unlock(context);     /* unlock read lock */
        if (krb5_dbm_db_end_read(context, transaction) == 0)
            break;
@@ -1188,7 +1251,7 @@ krb5_boolean *more;                       /* are there more? */
 
  cleanup:
     if (current_db_ptr == 0)
-       dbm_close(db);
+       KDBM_CLOSE(db);
     (void) krb5_dbm_db_unlock(context);        /* unlock read lock */
     return retval;
 }
@@ -1242,7 +1305,7 @@ krb5_dbm_db_put_principal(context, entries, nentries)
     if (current_db_ptr)
        db = current_db_ptr;
     else {
-       db = dbm_open(current_db_name, O_RDWR, 0600);
+       db = KDBM_OPEN(current_db_name, O_RDWR, 0600);
        if (db == NULL) {
            retval = errno;
            (void) krb5_dbm_db_unlock(context);
@@ -1262,7 +1325,7 @@ krb5_dbm_db_put_principal(context, entries, nentries)
            free_encode_princ_contents(&contents);
            break;
        }
-       if (dbm_store(db, key, contents, DBM_REPLACE))
+       if (KDBM_STORE(db, key, contents, DBM_REPLACE))
            retval = errno;
        else
            retval = 0;
@@ -1274,7 +1337,7 @@ krb5_dbm_db_put_principal(context, entries, nentries)
     }
 
     if (current_db_ptr == 0)
-       dbm_close(db);
+       KDBM_CLOSE(db);
     (void) krb5_dbm_db_unlock(context);                /* unlock database */
     *nentries = i;
     return (retval);
@@ -1306,7 +1369,7 @@ int *nentries;                            /* how many found & deleted */
     if (current_db_ptr)
        db = current_db_ptr;
     else {
-       db = dbm_open(current_db_name, O_RDWR, 0600);
+       db = KDBM_OPEN(current_db_name, O_RDWR, 0600);
        if (db == NULL) {
            retval = errno;
            (void) krb5_dbm_db_unlock(context);
@@ -1317,7 +1380,7 @@ int *nentries;                            /* how many found & deleted */
     if (retval = encode_princ_dbmkey(context, &key, searchfor))
        goto cleanup;
 
-    contents = dbm_fetch(db, key);
+    contents = KDBM_FETCH(db, key);
     if (contents.dptr == NULL) {
        found = 0;
        retval = KRB5_KDB_NOENTRY;
@@ -1329,10 +1392,10 @@ int *nentries;                          /* how many found & deleted */
        if (retval = encode_princ_contents(context, &contents2, &entry))
            goto cleancontents;
 
-       if (dbm_store(db, key, contents2, DBM_REPLACE))
+       if (KDBM_STORE(db, key, contents2, DBM_REPLACE))
            retval = errno;
        else {
-           if (dbm_delete(db, key))
+           if (KDBM_DELETE(db, key))
                retval = errno;
            else
                retval = 0;
@@ -1346,7 +1409,7 @@ int *nentries;                            /* how many found & deleted */
 
  cleanup:
     if (current_db_ptr == 0)
-       dbm_close(db);
+       KDBM_CLOSE(db);
     (void) krb5_dbm_db_unlock(context);        /* unlock write lock */
     *nentries = found;
     return retval;
@@ -1372,7 +1435,7 @@ krb5_dbm_db_iterate (context, func, func_arg)
     if (current_db_ptr)
        db = current_db_ptr;
     else {
-       db = dbm_open(current_db_name, O_RDONLY, 0600);
+       db = KDBM_OPEN(current_db_name, O_RDONLY, 0600);
        if (db == NULL) {
            retval = errno;
            (void) krb5_dbm_db_unlock(context);
@@ -1380,8 +1443,8 @@ krb5_dbm_db_iterate (context, func, func_arg)
        }
     }
 
-    for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_next(db, key)) {
-       contents = dbm_fetch (db, key);
+    for (key = KDBM_FIRSTKEY (db); key.dptr != NULL; key = KDBM_NEXTKEY(db)) {
+       contents = KDBM_FETCH (db, key);
        if (retval = decode_princ_contents(context, &contents, &entries))
            break;
        retval = (*func)(func_arg, &entries);
@@ -1390,7 +1453,7 @@ krb5_dbm_db_iterate (context, func, func_arg)
            break;
     }
     if (current_db_ptr == 0)
-       dbm_close(db);
+       KDBM_CLOSE(db);
     (void) krb5_dbm_db_unlock(context);
     return retval;
 }
diff --git a/src/lib/kdb/t_kdb.c b/src/lib/kdb/t_kdb.c
new file mode 100644 (file)
index 0000000..b14ef15
--- /dev/null
@@ -0,0 +1,865 @@
+/*
+ * lib/kdb/t_kdb.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * t_kdb.c     - Test [and optionally obtain timing information about] the
+ *               Kerberos database functions.
+ */
+
+#include "k5-int.h"
+#include <sys/time.h>
+
+#if    HAVE_SRAND48
+#define        RAND()          lrand48()
+#define        SRAND(a)        srand48(a)
+#define        RAND_TYPE       long
+#elif  HAVE_SRAND
+#define        RAND()          rand()
+#define        SRAND(a)        srand(a)
+#define        RAND_TYPE       int
+#elif  HAVE_SRANDOM
+#define        RAND()          random()
+#define        SRAND(a)        srandom(a)
+#define        RAND_TYPE       long
+#else  /* no random */
+need a random number generator
+#endif /* no random */
+
+#define        T_KDB_N_PASSES  100
+#define        T_KDB_DEF_DB    "test_db"
+#define        MAX_PNAME_LEN   1024
+#define        MAX_PRINC_COMPS 8
+#define        MAX_COMP_SIZE   32
+
+#define        RANDOM(a,b)     (a + (RAND() % (b-a)))
+
+char                   *programname = (char *) NULL;
+krb5_data              mprinc_data_entries[] = {
+    { 0, sizeof("master")-1, "master"},
+    { 0, sizeof("key")-1, "key"}
+};
+
+krb5_principal_data    master_princ_data = {
+    0,                                         /* Magic number         */
+    { 0, sizeof("test.realm")-1, "test.realm"},        /* Realm                */
+    mprinc_data_entries,                       /* Name/instance        */
+    sizeof(mprinc_data_entries)/
+       sizeof(mprinc_data_entries[0]),         /* Number               */
+    KRB5_NT_SRV_INST                           /* Type                 */
+};
+
+struct timeval tstart_time, tend_time;
+struct timezone        dontcare;
+krb5_principal *recorded_principals = (krb5_principal *) NULL;
+char           **recorded_names = (char **) NULL;
+\f
+/*
+ * Timer macros.
+ */
+#define        swatch_on()     ((void) gettimeofday(&tstart_time, &dontcare))
+#define        swatch_eltime() ((gettimeofday(&tend_time, &dontcare)) ? -1.0 : \
+                        (((float) (tend_time.tv_sec -                  \
+                                   tstart_time.tv_sec)) +              \
+                         (((float) (tend_time.tv_usec -                \
+                                    tstart_time.tv_usec))/1000000.0)))
+\f
+/*
+ * Free all principals and names in the recorded names list.
+ */
+void
+free_principals(kcontext, nentries)
+    krb5_context       kcontext;
+    int                nentries;
+{
+    int i;
+    if (recorded_principals) {
+       for (i=0; i<nentries; i++) {
+           if (recorded_principals[i])
+               krb5_free_principal(kcontext, recorded_principals[i]);
+       }
+       free(recorded_principals);
+    }
+    recorded_principals = (krb5_principal *) NULL;
+
+    if (recorded_names) {
+       for (i=0; i<nentries; i++) {
+           if (recorded_names[i])
+               free(recorded_names[i]);
+       }
+       free(recorded_names);
+    }
+    recorded_names = (char **) NULL;
+}
+
+/*
+ * Initialize the recorded names list.
+ */
+void
+init_princ_recording(kcontext, nentries)
+    krb5_context       kcontext;
+    int                nentries;
+{
+    if (recorded_principals = (krb5_principal *)
+       malloc(nentries * sizeof(krb5_principal)))
+       memset((char *) recorded_principals, 0,
+              nentries * sizeof(krb5_principal));
+    if (recorded_names = (char **) malloc(nentries * sizeof(char *)))
+       memset((char *) recorded_names, 0, nentries * sizeof(char *));
+}
+
+/*
+ * Record a principal and name.
+ */
+void
+record_principal(slotno, princ, pname)
+    int                        slotno;
+    krb5_principal     princ;
+    char               *pname;
+{
+    recorded_principals[slotno] = princ;
+    recorded_names[slotno] = (char *) malloc(strlen(pname)+1);
+    if (recorded_names[slotno])
+       strcpy(recorded_names[slotno], pname);
+}
+
+#define        playback_principal(slotno)      (recorded_principals[slotno])
+#define        playback_name(slotno)           (recorded_names[slotno])
+
+/*
+ * See if a principal already exists.
+ */
+krb5_boolean
+principal_found(nvalid, pname)
+    int                nvalid;
+    char       *pname;
+{
+    krb5_boolean       found;
+    int                        i;
+
+    found = 0;
+    for (i=0; i<nvalid; i++) {
+       if (!strcmp(recorded_names[i], pname)) {
+           found = 1;
+           break;
+       }
+    }
+    return(found);
+}
+
+/*
+ * Add a principal to the database.
+ */
+krb5_error_code
+add_principal(kcontext, principal, eblock, key, rseed)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_encrypt_block *eblock;
+    krb5_keyblock      *key;
+    krb5_pointer       rseed;
+{
+    krb5_error_code            kret;
+    krb5_db_entry              dbent;
+    krb5_keyblock              *rkey;
+    krb5_keyblock              *kp;
+    int                                nentries = 1;
+
+    memset((char *) &dbent, 0, sizeof(dbent));
+    rkey = (krb5_keyblock *) NULL;
+    dbent.principal = principal;
+    dbent.kvno = 1;
+    dbent.max_life = KRB5_KDB_MAX_LIFE;
+    dbent.max_renewable_life = KRB5_KDB_MAX_RLIFE;
+    dbent.mkvno = 1;
+    dbent.expiration = KRB5_KDB_EXPIRATION;
+    dbent.mod_name = principal;
+    dbent.attributes = KRB5_KDB_DEF_FLAGS;
+    if (kret = krb5_timeofday(kcontext, &dbent.mod_date))
+       return(kret);
+
+    if (!key) {
+       if (kret = krb5_random_key(kcontext, eblock, rseed, &rkey))
+           return(kret);
+       kp = rkey;
+    }
+    else
+       kp = key;
+
+    if (kret = krb5_kdb_encrypt_key(kcontext, eblock, kp, &dbent.key))
+       goto out;
+
+    if (rkey)
+       krb5_free_keyblock(kcontext, rkey);
+
+    dbent.salt_type = KRB5_KDB_SALTTYPE_NORMAL;
+    dbent.salt_length = 0;
+    dbent.salt = (krb5_octet *) NULL;
+    kret = krb5_db_put_principal(kcontext, &dbent, &nentries);
+    if (!kret && (nentries != 1))
+       kret = KRB5KRB_ERR_GENERIC;
+ out:
+    if (dbent.key.contents)
+       free(dbent.key.contents);
+    return(kret);
+}
+
+/*
+ * Generate a principal name.
+ */
+krb5_error_code
+gen_principal(kcontext, realm, do_rand, n, princp, namep)
+    krb5_context       kcontext;
+    char               *realm;
+    int                        do_rand;
+    int                        n;
+    krb5_principal     *princp;
+    char               **namep;
+{
+    static char pnamebuf[MAX_PNAME_LEN];
+    static char *instnames[] = {
+       "instance1", "xxx2", "whereami3", "ABCDEFG4" };
+    static char *princnames[] = {
+       "princ1", "user2", "service3", "RANDOM4" };
+
+    krb5_error_code    kret;
+    char               *instname;
+    char               *princbase;
+    int                        ncomps;
+    int                        i, complen, j;
+    char               *cp;
+
+    if (do_rand) {
+       ncomps = RANDOM(1,MAX_PRINC_COMPS);
+       cp = pnamebuf;
+       for (i=0; i<ncomps; i++) {
+           complen = RANDOM(1,MAX_COMP_SIZE);
+           for (j=0; j<complen; j++) {
+               *cp = (char) RANDOM(0,256);
+               while (!isalnum(*cp))
+                   *cp = (char) RANDOM(0,256);
+               cp++;
+           }
+           *cp = '/';
+           cp++;
+       }
+       cp[-1] = '@';
+       strcpy(cp, realm);
+    }
+    else {
+       instname = instnames[n % (sizeof(instnames)/sizeof(instnames[0]))];
+       princbase = princnames[n % (sizeof(princnames)/sizeof(princnames[0]))];
+       sprintf(pnamebuf, "%s%d/%s@%s", princbase, n, instname, realm);
+    }
+    kret = krb5_parse_name(kcontext, pnamebuf, princp);
+    *namep = (!kret) ? pnamebuf : (char *) NULL;
+    return(kret);
+}
+
+/*
+ * Find a principal in the database.
+ */
+krb5_error_code
+find_principal(kcontext, principal, docompare)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_boolean       docompare;
+{
+    krb5_error_code    kret;
+    krb5_db_entry      dbent;
+    int                        how_many;
+    krb5_boolean       more;
+
+    how_many = 1;
+    more = 0;
+    if (kret = krb5_db_get_principal(kcontext,
+                                    principal,
+                                    &dbent,
+                                    &how_many,
+                                    &more))
+       return(kret);
+    if (docompare) {
+       if ((dbent.kvno != 1) ||
+           (dbent.max_life != KRB5_KDB_MAX_LIFE) ||
+           (dbent.max_renewable_life != KRB5_KDB_MAX_RLIFE) ||
+           (dbent.mkvno != 1) ||
+           (dbent.expiration != KRB5_KDB_EXPIRATION) ||
+           (dbent.attributes != KRB5_KDB_DEF_FLAGS) ||
+           !krb5_principal_compare(kcontext, principal, dbent.principal) ||
+           !krb5_principal_compare(kcontext, principal, dbent.mod_name))
+           kret = KRB5_PRINC_NOMATCH;
+       krb5_db_free_principal(kcontext, &dbent, how_many);
+    }
+    if (!kret && how_many)
+       krb5_db_free_principal(kcontext, &dbent, how_many);
+    return(((how_many == 1) && (more == 0)) ? 0 : KRB5KRB_ERR_GENERIC);
+}
+
+/*
+ * Delete a principal.
+ */
+krb5_error_code
+delete_principal(kcontext, principal)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+{
+    krb5_error_code    kret;
+    int                        num2delete;
+
+    num2delete = 1;
+    if (kret = krb5_db_delete_principal(kcontext,
+                                       principal,
+                                       &num2delete))
+       return(kret);
+    return((num2delete == 1) ? 0 : KRB5KRB_ERR_GENERIC);
+}
+
+int
+do_testing(db, passes, verbose, timing, rcases, check, save_db)
+    char       *db;
+    int                passes;
+    int                verbose;
+    int                timing;
+    int                rcases;
+    int                check;
+    int                save_db;
+{
+    krb5_error_code    kret;
+    krb5_context       kcontext;
+    char               *op, *linkage, *oparg;
+    krb5_principal     master_princ;
+    char               *mkey_name;
+    char               *realm;
+    char               *mkey_fullname;
+    char               *master_passwd;
+    krb5_data          salt_data;
+    krb5_encrypt_block master_encblock;
+    krb5_keyblock      master_keyblock;
+    krb5_data          passwd;
+    krb5_pointer       rseed;
+    krb5_boolean       db_open, db_created;
+    int                        passno;
+    krb5_principal     principal;
+    char               *pname;
+    float              elapsed;
+    krb5_keyblock      stat_kb;
+
+    mkey_name = "master/key";
+    realm = master_princ_data.realm.data;
+    mkey_fullname = (char *) NULL;
+    master_princ = (krb5_principal) NULL;
+    master_passwd = "master_password";
+    db_open = 0;
+    db_created = 0;
+    linkage = "";
+    oparg = "";
+
+    /* Set up some initial context */
+    krb5_init_context(&kcontext);
+    krb5_init_ets(kcontext);
+
+    /* 
+     * The database had better not exist.
+     */
+    op = "making sure database doesn't exist";
+    if (!(kret = krb5_db_set_name(kcontext, db))) {
+       kret = EEXIST;
+       goto goodbye;
+    }
+
+    /* Set up the master key name */
+    op = "setting up master key name";
+    if (kret = krb5_db_setup_mkey_name(kcontext,
+                                      mkey_name,
+                                      realm,
+                                      &mkey_fullname,
+                                      &master_princ))
+       goto goodbye;
+
+    if (verbose)
+       fprintf(stdout, "%s: Initializing '%s', master key is '%s'\n",
+               programname, db, mkey_fullname);
+
+    op = "salting master key";
+    if (kret = krb5_principal2salt(kcontext, master_princ, &salt_data))
+       goto goodbye;
+
+    op = "converting master key";
+    krb5_use_cstype(kcontext, &master_encblock, DEFAULT_KDC_ETYPE);
+    master_keyblock.keytype = DEFAULT_KDC_KEYTYPE;
+    passwd.length = strlen(master_passwd);
+    passwd.data = master_passwd;
+    if (kret = krb5_string_to_key(kcontext,
+                                 &master_encblock,
+                                 master_keyblock.keytype,
+                                 &master_keyblock,
+                                 &passwd,
+                                 &salt_data))
+       goto goodbye;
+    /* Clean up */
+    free(salt_data.data);
+
+    /* Process master key */
+    op = "processing master key";
+    if (kret = krb5_process_key(kcontext, &master_encblock, &master_keyblock))
+       goto goodbye;
+
+    /* Initialize random key generator */
+    op = "initializing random key generator";
+    if (kret = krb5_init_random_key(kcontext,
+                                   &master_encblock,
+                                   &master_keyblock,
+                                   &rseed))
+       goto goodbye;
+
+    /* Create database */
+    op = "creating database";
+    if (kret = krb5_db_create(kcontext, db))
+       goto goodbye;
+
+    db_created = 1;
+
+    /* Set this database as active. */
+    op = "setting active database";
+    if (kret = krb5_db_set_name(kcontext, db))
+       goto goodbye;
+
+    /* Initialize database */
+    op = "initializing database";
+    if (kret = krb5_db_init(kcontext))
+       goto goodbye;
+
+    db_open = 1;
+    op = "adding master principal";
+    if (kret = add_principal(kcontext,
+                            master_princ,
+                            &master_encblock,
+                            &master_keyblock,
+                            rseed))
+       goto goodbye;
+
+
+    stat_kb.keytype = DEFAULT_KDC_KEYTYPE;
+    stat_kb.etype = DEFAULT_KDC_ETYPE;
+    stat_kb.length = 8;
+    stat_kb.contents = (krb5_octet *) "helpmeee";
+
+    /* We are now ready to proceed to test. */
+    if (verbose)
+       fprintf(stdout, "%s: Beginning %stest\n",
+               programname, (rcases) ? "random " : "");
+    init_princ_recording(kcontext, passes);
+    if (rcases) {
+       struct tacc {
+           float       t_time;
+           int         t_number;
+       } accumulated[3];
+       int             i, nvalid, discrim, highwater, coinflip;
+       krb5_keyblock   *kbp;
+
+       /* Generate random cases */
+       for (i=0; i<3; i++) {
+           accumulated[i].t_time = 0.0;
+           accumulated[i].t_number = 0;
+       }
+
+       /*
+        * Generate principal names.
+        */
+       if (verbose > 1)
+           fprintf(stdout, "%s: generating %d names\n",
+                   programname, passes);
+       for (passno=0; passno<passes; passno++) {
+           op = "generating principal name";
+           do {
+               if (kret = gen_principal(kcontext,
+                                        realm,
+                                        rcases,
+                                        passno,
+                                        &principal,
+                                        &pname))
+                   goto goodbye;
+           } while (principal_found(passno-1, pname));
+           record_principal(passno, principal, pname);
+       }
+
+       /* Prime the database with some number of entries */
+       nvalid = passes/4;
+       if (nvalid < 10)
+           nvalid = 10;
+       if (nvalid > passes)
+           nvalid = passes;
+
+       if (verbose > 1)
+           fprintf(stdout, "%s: priming database with %d principals\n",
+                   programname, nvalid);
+       highwater = 0;
+       for (passno=0; passno<nvalid; passno++) {
+           op = "adding principal";
+           coinflip = RANDOM(0,2);
+           kbp = (coinflip) ? &stat_kb : (krb5_keyblock *) NULL;
+           if (timing) {
+               swatch_on();
+           }
+           if (kret = add_principal(kcontext,
+                                    playback_principal(passno),
+                                    &master_encblock,
+                                    kbp,
+                                    rseed)) {
+               linkage = "initially ";
+               oparg = playback_name(passno);
+               goto cya;
+           }
+           if (timing) {
+               elapsed = swatch_eltime();
+               accumulated[0].t_time += elapsed;
+               accumulated[0].t_number++;
+           }
+           if (verbose > 4)
+               fprintf(stderr, "*A(%s)\n", playback_name(passno));
+           highwater++;
+       }
+
+       if (verbose > 1)
+           fprintf(stderr, "%s: beginning random loop\n", programname);
+       /* Loop through some number of times and pick random operations */
+       for (i=0; i<3*passes; i++) {
+           discrim = RANDOM(0,100);
+
+           /* Add a principal 25% of the time, if possible */
+           if ((discrim < 25) && (nvalid < passes)) {
+               op = "adding principal";
+               coinflip = RANDOM(0,2);
+               kbp = (coinflip) ? &stat_kb : (krb5_keyblock *) NULL;
+               if (timing) {
+                   swatch_on();
+               }
+               if (kret = add_principal(kcontext,
+                                        playback_principal(nvalid),
+                                        &master_encblock,
+                                        kbp,
+                                        rseed)) {
+                   oparg = playback_name(nvalid);
+                   goto cya;
+               }
+               if (timing) {
+                   elapsed = swatch_eltime();
+                   accumulated[0].t_time += elapsed;
+                   accumulated[0].t_number++;
+               }
+               if (verbose > 4)
+                   fprintf(stderr, "*A(%s)\n", playback_name(nvalid));
+               nvalid++;
+               if (nvalid > highwater)
+                   highwater = nvalid;
+           }
+           /* Delete a principal 15% of the time, if possible */
+           else if ((discrim > 85) && (nvalid > 10)) {
+               op = "deleting principal";
+               if (timing) {
+                   swatch_on();
+               }
+               if (kret = delete_principal(kcontext,
+                                           playback_principal(nvalid-1))) {
+                   oparg = playback_name(nvalid-1);
+                   goto cya;
+               }
+               if (timing) {
+                   elapsed = swatch_eltime();
+                   accumulated[2].t_time += elapsed;
+                   accumulated[2].t_number++;
+               }
+               if (verbose > 4)
+                   fprintf(stderr, "XD(%s)\n", playback_name(nvalid-1));
+               nvalid--;
+           }
+           /* Otherwise, find a principal */
+           else {
+               op = "looking up principal";
+               passno = RANDOM(0, nvalid);
+               if (timing) {
+                   swatch_on();
+               }
+               if (kret = find_principal(kcontext,
+                                         playback_principal(passno),
+                                         check)) {
+                   oparg = playback_name(passno);
+                   goto cya;
+               }
+               if (timing) {
+                   elapsed = swatch_eltime();
+                   accumulated[1].t_time += elapsed;
+                   accumulated[1].t_number++;
+               }
+               if (verbose > 4)
+                   fprintf(stderr, "-S(%s)\n", playback_name(passno));
+           }
+       }
+
+       /* Clean up the remaining principals */
+       if (verbose > 1)
+           fprintf(stdout, "%s: deleting remaining %d principals\n",
+                   programname, nvalid);
+       for (passno=0; passno<nvalid; passno++) {
+           op = "deleting principal";
+           if (timing) {
+               swatch_on();
+           }
+           if (kret = delete_principal(kcontext,
+                                       playback_principal(passno))) {
+               linkage = "finally ";
+               oparg = playback_name(passno);
+               goto cya;
+           }
+           if (timing) {
+               elapsed = swatch_eltime();
+               accumulated[2].t_time += elapsed;
+               accumulated[2].t_number++;
+           }
+           if (verbose > 4)
+               fprintf(stderr, "XD(%s)\n", playback_name(passno));
+       }
+    cya:
+       if (verbose)
+           fprintf(stdout,
+                   "%s: highwater mark was %d principals\n",
+                   programname, highwater);
+       if (accumulated[0].t_number && timing)
+           fprintf(stdout,
+                   "%s: performed %8d additions in %9.4f seconds (%9.4f/add)\n",
+                   programname, accumulated[0].t_number,
+                   accumulated[0].t_time, 
+                   accumulated[0].t_time / (float) accumulated[0].t_number);
+       if (accumulated[1].t_number && timing)
+           fprintf(stdout,
+                   "%s: performed %8d lookups   in %9.4f seconds (%9.4f/search)\n",
+                   programname, accumulated[1].t_number,
+                   accumulated[1].t_time, 
+                   accumulated[1].t_time / (float) accumulated[1].t_number);
+       if (accumulated[2].t_number && timing)
+           fprintf(stdout,
+                   "%s: performed %8d deletions in %9.4f seconds (%9.4f/delete)\n",
+                   programname, accumulated[2].t_number,
+                   accumulated[2].t_time, 
+                   accumulated[2].t_time / (float) accumulated[2].t_number);
+       if (kret)
+           goto goodbye;
+    }
+    else {
+       /*
+        * Generate principal names.
+        */
+       for (passno=0; passno<passes; passno++) {
+           op = "generating principal name";
+           if (kret = gen_principal(kcontext,
+                                    realm,
+                                    rcases,
+                                    passno,
+                                    &principal,
+                                    &pname))
+               goto goodbye;
+           record_principal(passno, principal, pname);
+       }
+       /*
+        * Add principals.
+        */
+       if (timing) {
+           swatch_on();
+       }
+       for (passno=0; passno<passes; passno++) {
+           op = "adding principal";
+           if (kret = add_principal(kcontext,
+                                    playback_principal(passno),
+                                    &master_encblock,
+                                    &stat_kb,
+                                    rseed))
+               goto goodbye;
+           if (verbose > 4)
+               fprintf(stderr, "*A(%s)\n", playback_name(passno));
+       }
+       if (timing) {
+           elapsed = swatch_eltime();
+           fprintf(stdout,
+                   "%s:   added %d principals in %9.4f seconds (%9.4f/add)\n",
+                   programname, passes, elapsed, elapsed/((float) passes));
+       }
+
+       /*
+        * Lookup principals.
+        */
+       if (timing) {
+           swatch_on();
+       }
+       for (passno=0; passno<passes; passno++) {
+           op = "looking up principal";
+           if (kret = find_principal(kcontext,
+                                     playback_principal(passno),
+                                     check))
+               goto goodbye;
+           if (verbose > 4)
+               fprintf(stderr, "-S(%s)\n", playback_name(passno));
+       }
+       if (timing) {
+           elapsed = swatch_eltime();
+           fprintf(stdout,
+                   "%s:   found %d principals in %9.4f seconds (%9.4f/search)\n",
+                   programname, passes, elapsed, elapsed/((float) passes));
+       }
+
+       /*
+        * Delete principals.
+        */
+       if (timing) {
+           swatch_on();
+       }
+       for (passno=passes-1; passno>=0; passno--) {
+           op = "deleting principal";
+           if (kret = delete_principal(kcontext,
+                                       playback_principal(passno)))
+               goto goodbye;
+           if (verbose > 4)
+               fprintf(stderr, "XD(%s)\n", playback_name(passno));
+       }
+       if (timing) {
+           elapsed = swatch_eltime();
+           fprintf(stdout,
+                   "%s: deleted %d principals in %9.4f seconds (%9.4f/delete)\n",
+                   programname, passes, elapsed, elapsed/((float) passes));
+       }
+    }
+
+ goodbye:
+    if (kret)
+       fprintf(stderr, "%s: error while %s %s%s(%s)\n",
+               programname, op, linkage, oparg, error_message(kret));
+    free_principals(kcontext, passes);
+    if (db_open)
+       (void) krb5_db_fini(kcontext);
+    if (db_created) {
+       char *fnbuf;
+
+       if (!kret && !save_db) {
+           fnbuf = (char *) malloc(MAXPATHLEN);
+           if (fnbuf) {
+               sprintf(fnbuf, "%s.ok", db);
+               unlink(fnbuf);
+               sprintf(fnbuf, "%s.dir", db);
+               unlink(fnbuf);
+               sprintf(fnbuf, "%s.pag", db);
+               unlink(fnbuf);
+               sprintf(fnbuf, "%s.db", db);
+               unlink(fnbuf);
+               free(fnbuf);
+           }
+       }
+       else {
+           if (kret && verbose)
+               fprintf(stderr, "%s: database not deleted because of error\n",
+                       programname);
+       }
+    }
+    return((kret) ? 1 : 0);
+}
+
+/*
+ * usage:
+ *     t_kdb   [-t]            - Get timing information.
+ *             [-r]            - Generate random cases.
+ *             [-n <num>]      - Use <num> as the number of passes.
+ *             [-c]            - Check contents.
+ *             [-v]            - Verbose output.
+ *             [-d <dbname>]   - Database name.
+ *             [-s]            - Save database even on successful completion.
+ */
+int
+main(argc, argv)
+    int                argc;
+    char       *argv[];
+{
+    char       option;
+    extern char        *optarg;
+
+    int                do_time, do_random, num_passes, check_cont, verbose, error;
+    int                save_db;
+    char       *db_name;
+
+    programname = argv[0];
+    if (strrchr(programname, (int) '/'))
+       programname = strrchr(programname, (int) '/') + 1;
+    SRAND((RAND_TYPE)time((void *) NULL));
+
+    /* Default values. */
+    do_time = 0;
+    do_random = 0;
+    num_passes = T_KDB_N_PASSES;
+    check_cont = 0;
+    verbose = 0;
+    db_name = T_KDB_DEF_DB;
+    save_db = 0;
+    error = 0;
+
+    /* Parse argument list */
+    while ((option = getopt(argc, argv, "cd:n:rstv")) != EOF) {
+       switch (option) {
+       case 'c':
+           check_cont = 1;
+           break;
+       case 'd':
+           db_name = optarg;
+           break;
+       case 'n':
+           if (sscanf(optarg, "%d", &num_passes) != 1) {
+               fprintf(stderr, "%s: %s is not a valid number for %c option\n",
+                       programname, optarg, option);
+               error++;
+           }
+           break;
+       case 'r':
+           do_random = 1;
+           break;
+       case 's':
+           save_db = 1;
+           break;
+       case 't':
+           do_time = 1;
+           break;
+       case 'v':
+           verbose++;
+           break;
+       default:
+           error++;
+           break;
+       }
+    }
+    if (error)
+       fprintf(stderr, "%s: usage is %s [-crstv] [-d <dbname>] [-n <num>]\n",
+               programname, programname);
+    else
+       error = do_testing(db_name,
+                          num_passes,
+                          verbose,
+                          do_time,
+                          do_random,
+                          check_cont,
+                          save_db);
+    return(error);
+}