summaryrefslogtreecommitdiffstats
path: root/src/kadmin/export
diff options
context:
space:
mode:
authorMarc Horowitz <marc@mit.edu>1996-07-22 20:49:46 +0000
committerMarc Horowitz <marc@mit.edu>1996-07-22 20:49:46 +0000
commitedf8b4d8a6a665c2aa150993cd813ea6c5cf12e1 (patch)
tree6c2974a97b448c040fa4a31708ec5e02f187526c /src/kadmin/export
parent013bb1391582ed9e653ae706e398ddb8d08cfcc9 (diff)
downloadkrb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.gz
krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.xz
krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.zip
this commit includes all the changes on the OV_9510_INTEGRATION and
OV_MERGE branches. This includes, but is not limited to, the new openvision admin system, and major changes to gssapi to add functionality, and bring the implementation in line with rfc1964. before committing, the code was built and tested for netbsd and solaris. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8774 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kadmin/export')
-rw-r--r--src/kadmin/export/ChangeLog19
-rw-r--r--src/kadmin/export/Makefile.in20
-rw-r--r--src/kadmin/export/Makefile.ov24
-rw-r--r--src/kadmin/export/configure.in12
-rw-r--r--src/kadmin/export/export.c242
-rw-r--r--src/kadmin/export/export_err.et19
-rw-r--r--src/kadmin/export/local.h15
-rw-r--r--src/kadmin/export/ovsec_adm_export.c159
-rw-r--r--src/kadmin/export/unit-test/ChangeLog5
-rw-r--r--src/kadmin/export/unit-test/Makefile.ov19
-rw-r--r--src/kadmin/export/unit-test/add-to-db.sh55
-rw-r--r--src/kadmin/export/unit-test/config/unix.exp36
-rw-r--r--src/kadmin/export/unit-test/dotest.sh75
-rw-r--r--src/kadmin/export/unit-test/export.0/dotest.exp29
-rw-r--r--src/kadmin/export/unit-test/export.0/output.exp43
-rw-r--r--src/kadmin/export/unit-test/export.0/usage.exp25
-rw-r--r--src/kadmin/export/unit-test/helpers.exp126
17 files changed, 923 insertions, 0 deletions
diff --git a/src/kadmin/export/ChangeLog b/src/kadmin/export/ChangeLog
new file mode 100644
index 000000000..97a8078c4
--- /dev/null
+++ b/src/kadmin/export/ChangeLog
@@ -0,0 +1,19 @@
+Thu Jul 18 20:39:32 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Mon Jul 15 16:51:51 1996 Marc Horowitz <marc@mit.edu>
+
+ * export.c (print_princ): return should return a value.
+
+ * configure.in (USE_GSSAPI_LIBRARY): shared libraries require all
+ symbols to be resolved, so this needs to be here.
+
+Wed Jul 10 01:26:18 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in, configure.in: added autoconf support
+
+Tue Jul 9 16:45:52 1996 Marc Horowitz <marc@mit.edu>
+
+ * export.c: renamed <ovsec_admin/foo.h> to <kadm5/foo.h>
+
diff --git a/src/kadmin/export/Makefile.in b/src/kadmin/export/Makefile.in
new file mode 100644
index 000000000..5fa282d89
--- /dev/null
+++ b/src/kadmin/export/Makefile.in
@@ -0,0 +1,20 @@
+CFLAGS = $(CCOPTS) $(DEFS) -I. $(LOCALINCLUDE)
+
+PROG = kadm5_export
+OBJS = ovsec_adm_export.o export.o export_err.o
+
+all:: $(PROG)
+
+export_err.c export_err.h: $(srcdir)/export_err.et
+
+export.o: export_err.h
+ovsec_adm_export.o: export_err.h
+
+$(PROG): $(OBJS) $(DEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install::
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean::
+ $(RM) $(PROG) $(OBJS)
diff --git a/src/kadmin/export/Makefile.ov b/src/kadmin/export/Makefile.ov
new file mode 100644
index 000000000..83e8c7219
--- /dev/null
+++ b/src/kadmin/export/Makefile.ov
@@ -0,0 +1,24 @@
+TOP = ..
+include $(TOP)/config.mk/template
+# CFLAGS := $(CFLAGS) -Wall
+
+# The next line *shouldn't* work, because the : should be a ::.
+# However, it does work, and if I change it to :: gmake does really
+# weird things.
+ovsec_adm_export: export_err.h
+
+depend:: export_err.h
+
+PROG := kadm5_export
+OBJS := ovsec_adm_export.o export.o export_err.o
+SRCS := ovsec_adm_export.c export.c export_err.et
+ETABLES := export_err.et
+
+LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDYN) $(LIBDB)
+
+expand ErrorTables
+expand InstallAdmin
+expand Depend
+
+SUBDIRS = unit-test
+expand SubdirTarget
diff --git a/src/kadmin/export/configure.in b/src/kadmin/export/configure.in
new file mode 100644
index 000000000..992d591a1
--- /dev/null
+++ b/src/kadmin/export/configure.in
@@ -0,0 +1,12 @@
+AC_INIT(ovsec_adm_export.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_PROG_AWK
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/export/export.c b/src/kadmin/export/export.c
new file mode 100644
index 000000000..3d41c4d9d
--- /dev/null
+++ b/src/kadmin/export/export.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <sys/time.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <unistd.h>
+
+#include <kadm5/adb.h>
+#include "export_err.h"
+#include "local.h"
+
+extern int errno;
+
+void print_key_data(FILE *f, krb5_key_data *key_data)
+{
+ int c;
+
+ fprintf(f, "%d\t%d\t", key_data->key_data_type[0],
+ key_data->key_data_length[0]);
+ for(c = 0; c < key_data->key_data_length[0]; c++)
+ fprintf(f, "%02x ",
+ key_data->key_data_contents[0][c]);
+}
+
+/*
+ * Function: print_princ
+ *
+ * Purpose: output osa_adb_princ_ent data in a human
+ * readable format (which is a format suitable for
+ * ovsec_adm_import consumption)
+ *
+ * Arguments:
+ * data (input) pointer to a structure containing a FILE *
+ * and a record counter.
+ * entry (input) entry to get dumped.
+ * <return value> void
+ *
+ * Requires:
+ * nuttin
+ *
+ * Effects:
+ * writes data to the specified file pointerp.
+ *
+ * Modifies:
+ * nuttin
+ *
+ */
+krb5_error_code print_princ(krb5_pointer data, krb5_db_entry *kdb)
+{
+ char *princstr;
+ int x, y, foundcrc, ret;
+ struct retdata *d;
+ krb5_tl_data tl_data;
+ osa_princ_ent_rec adb;
+ XDR xdrs;
+
+ d = (struct retdata *) data;
+
+ /*
+ * XXX Currently, lookup_tl_data always returns zero; it sets
+ * tl_data->tl_data_length to zero if the type isn't found.
+ * This should be fixed...
+ */
+ /*
+ * XXX Should this function do nothing for a principal with no
+ * admin data, or print a record of "default" values? See
+ * comment in server_kdb.c to help decide.
+ */
+ tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+ if ((ret = krb5_dbe_lookup_tl_data(d->context, kdb, &tl_data))
+ || (tl_data.tl_data_length == 0))
+ return(0);
+
+ memset(&adb, 0, sizeof(adb));
+ xdrmem_create(&xdrs, tl_data.tl_data_contents,
+ tl_data.tl_data_length, XDR_DECODE);
+ if (! xdr_osa_princ_ent_rec(&xdrs, &adb)) {
+ xdr_destroy(&xdrs);
+ return(OSA_ADB_XDR_FAILURE);
+ }
+ xdr_destroy(&xdrs);
+
+ krb5_unparse_name(d->context, kdb->princ, &princstr);
+ fprintf(d->fp, "princ\t%s\t", princstr);
+ if(adb.policy == NULL)
+ fputc('\t', d->fp);
+ else
+ fprintf(d->fp, "%s\t", adb.policy);
+ fprintf(d->fp, "%x\t%d\t%d\t%d", adb.aux_attributes,
+ adb.old_key_len,adb.old_key_next, adb.admin_history_kvno);
+
+ for (x = 0; x < adb.old_key_len; x++) {
+ if (! d->ovsec_compat)
+ fprintf(d->fp, "\t%d", adb.old_keys[x].n_key_data);
+
+ foundcrc = 0;
+ for (y = 0; y < adb.old_keys[x].n_key_data; y++) {
+ krb5_key_data *key_data = &adb.old_keys[x].key_data[y];
+
+ if (d->ovsec_compat) {
+ if (key_data->key_data_type[0] != ENCTYPE_DES_CBC_CRC)
+ continue;
+ if (foundcrc) {
+ fprintf(stderr, error_message(EXPORT_DUP_DESCRC),
+ princstr);
+ continue;
+ }
+ foundcrc++;
+ }
+ fputc('\t', d->fp);
+ print_key_data(d->fp, key_data);
+ }
+ if (d->ovsec_compat && !foundcrc)
+ fprintf(stderr, error_message(EXPORT_NO_DESCRC), princstr);
+ }
+
+ d->count++;
+ fputc('\n', d->fp);
+ free(princstr);
+ return(0);
+}
+
+/*
+ * Function: print_policy
+ *
+ * Purpose: Print the contents of a policy entry in a human readable format.
+ * This format is also suitable for consumption for dbimport.
+ *
+ * Arguments:
+ * data (input) a pointer to a structure containing a FILE *
+ * and a record counter.
+ * entry (input) policy entry
+ * <return value> void
+ *
+ * Requires:
+ * nuttin
+ *
+ * Effects:
+ * writes data to file
+ *
+ * Modifies:
+ * nuttin
+ *
+ */
+
+void
+print_policy(void *data, osa_policy_ent_t entry)
+{
+ struct retdata *d;
+
+ d = (struct retdata *) data;
+ fprintf(d->fp, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name,
+ entry->pw_min_life, entry->pw_max_life, entry->pw_min_length,
+ entry->pw_min_classes, entry->pw_history_num,
+ entry->policy_refcnt);
+ d->count++;
+ return;
+}
+
+/*
+ * Function: export_principal
+ *
+ * Purpose: interates through the principal database with the
+ * osa_adb_iter_princ function which calls the print_princ
+ * routine with the FILE * of our filename. If the file
+ * name that gets passed in is NULL then we use stdout.
+ *
+ * Arguments:
+ * d (input) pointer to retdata.
+ * <return value> error code. 0 if sucsessful.
+ *
+ * Requires:
+ * nuttin
+ *
+ * Effects:
+ * calls osa_adb_iter_princ which calls print_princ
+ *
+ * Modifies:
+ * nuttin
+ *
+ */
+osa_adb_ret_t
+export_principal(struct retdata *d, kadm5_config_params *params)
+{
+ int ret;
+
+ if (ret = krb5_db_set_name(d->context, params->dbname))
+ return ret;
+
+ if (ret = krb5_db_init(d->context))
+ return ret;
+
+ if (ret = krb5_dbm_db_iterate(d->context, print_princ, d))
+ return ret;
+
+ if (ret = krb5_db_fini(d->context))
+ return ret;
+
+ return 0;
+}
+
+/*
+ * Function: export_policy
+ *
+ * Purpose: iterates through the policy database with the
+ * osa_adb_iter_policy function which calls the print_policy
+ * routine with the FILE * of our filename. If the file name
+ * that gets passed in is NULL then we use stdout.
+ *
+ * Arguments:
+ * d (input) a pointer to retdata
+ * <return value> error code 0 if sucsessfull.
+ *
+ * Requires:
+ * nuttin
+ *
+ * Effects:
+ * calls osa_adb_iter_policy which calls print_policy
+ *
+ * Modifies:
+ * nuttin
+ *
+ */
+osa_adb_ret_t
+export_policy(struct retdata *d, osa_adb_policy_t db)
+{
+ osa_adb_ret_t ret;
+
+ if((ret = osa_adb_iter_policy(db, print_policy, (void *) d))
+ != OSA_ADB_OK) {
+ return ret;
+ }
+ return OSA_ADB_OK;
+}
diff --git a/src/kadmin/export/export_err.et b/src/kadmin/export/export_err.et
new file mode 100644
index 000000000..6c99a47b0
--- /dev/null
+++ b/src/kadmin/export/export_err.et
@@ -0,0 +1,19 @@
+error_table exp
+error_code EXPORT_NO_ERR, "Database export complete, %d record%s processed.\n"
+error_code EXPORT_UNK_OPTION, "Unknown Option\nUsage: ovsec_adm_export [filename]"
+error_code EXPORT_OUTPUT_OPEN, "while opening output file"
+error_code EXPORT_OUTPUT_CHMOD, "while changing mode of file"
+error_code EXPORT_OUTPUT_STAT, "while trying to stat file"
+error_code EXPORT_DATABASE_OPEN, "while opening database"
+error_code EXPORT_PRINCIPAL, "while exporting principal database"
+error_code EXPORT_POLICY, "while exporting policy database"
+error_code EXPORT_LOCK, "while locking database"
+error_code EXPORT_UNLOCK, "while unlocking database"
+error_code EXPORT_CLOSE, "while closing database"
+error_code EXPORT_SINGLE_RECORD, ""
+error_code EXPORT_PLURAL_RECORDS, "s"
+error_code EXPORT_NO_DESCRC, "Warning! No DES-CBC-CRC key for principal %s, cannot generate ovsec_adm_export-compatible record; skipping."
+error_code EXPORT_DUP_DESCRC, "Warning! Multiple DES-CBC-CRC keys for principal %s; skipping duplicates."
+error_code EXPORT_GET_CONFIG, "while retrieving configuration parameters"
+end
+
diff --git a/src/kadmin/export/local.h b/src/kadmin/export/local.h
new file mode 100644
index 000000000..3ec895ab2
--- /dev/null
+++ b/src/kadmin/export/local.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+struct retdata {
+ krb5_context context;
+ FILE *fp;
+ int count;
+ int ovsec_compat;
+};
+
+osa_adb_ret_t export_principal(struct retdata *, kadm5_config_params *);
+osa_adb_ret_t export_policy(struct retdata *d, osa_adb_policy_t);
diff --git a/src/kadmin/export/ovsec_adm_export.c b/src/kadmin/export/ovsec_adm_export.c
new file mode 100644
index 000000000..ded21ba55
--- /dev/null
+++ b/src/kadmin/export/ovsec_adm_export.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <kadm5/adb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "export_err.h"
+#include "local.h"
+
+int
+main(int argc, char *argv[])
+{
+ char *filename;
+ struct retdata d;
+ struct stat statb;
+ int ret, fd;
+ time_t now;
+ char *whoami = argv[0];
+ osa_adb_policy_t policy_db;
+ kadm5_config_params params;
+
+ memset(&params, 0, sizeof(params));
+ memset(&d, 0, sizeof(d));
+
+ filename = NULL;
+ initialize_exp_error_table();
+ initialize_adb_error_table();
+ krb5_init_context(&d.context);
+ krb5_init_ets(d.context);
+
+ while(--argc) {
+ if(*++argv == NULL)
+ break;
+ if(!strcmp(*argv, "-princ")) {
+ params.dbname = *++argv;
+ params.mask |= KADM5_CONFIG_DBNAME;
+ continue;
+ }
+ if(!strcmp(*argv, "-policy")) {
+ params.admin_dbname = *++argv;
+ params.mask |= KADM5_CONFIG_ADBNAME;
+ continue;
+ }
+ if(!strcmp(*argv, "-ovsec")) {
+ d.ovsec_compat++;
+ continue;
+ }
+ if (*argv[0] == '-') {
+ com_err(whoami, EXPORT_UNK_OPTION, NULL);
+ exit(2);
+ }
+ if(filename == NULL)
+ filename = *argv;
+ else {
+ com_err(whoami, EXPORT_UNK_OPTION, NULL);
+ exit(2);
+ }
+ }
+
+ if (ret = kadm5_get_config_params(d.context, NULL, NULL, &params,
+ &params)) {
+ com_err(whoami, ret, error_message(EXPORT_GET_CONFIG));
+ exit(2);
+ }
+#define REQUIRED_MASK (KADM5_CONFIG_DBNAME | \
+ KADM5_CONFIG_ADBNAME)
+ if ((params.mask & REQUIRED_MASK) != REQUIRED_MASK) {
+ com_err(whoami, KADM5_BAD_SERVER_PARAMS,
+ error_message(EXPORT_GET_CONFIG));
+ exit(2);
+ }
+
+ if(filename != NULL) {
+ if((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0400)) == -1) {
+ com_err(whoami, errno, "%s (%s)",
+ error_message(EXPORT_OUTPUT_OPEN), filename);
+ exit(2);
+ }
+ if(fstat(fd, &statb) == -1) {
+ com_err(whoami, errno, "%s (%s)",
+ error_message(EXPORT_OUTPUT_STAT), filename);
+ exit(2);
+ }
+ if(S_ISREG(statb.st_mode)) {
+ int mask = umask(0);
+ (void) umask(mask);
+ if (fchmod(fd, (0400 & ~mask)) == -1) {
+ com_err(whoami, errno, "%s (%s)",
+ error_message(EXPORT_OUTPUT_CHMOD), filename);
+ exit(2);
+ }
+ }
+ if ((d.fp = fdopen(fd, "w")) == NULL) {
+ com_err(whoami, errno, "%s (%s)",
+ error_message(EXPORT_OUTPUT_OPEN), filename);
+ exit(2);
+ }
+ } else d.fp = stdout;
+
+ if((ret = osa_adb_open_policy(&policy_db, &params)) != OSA_ADB_OK) {
+ com_err(argv[0], ret, error_message(EXPORT_DATABASE_OPEN));
+ exit(2);
+ }
+ if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_SHARED) != OSA_ADB_OK)) {
+ com_err(argv[0], ret, error_message(EXPORT_LOCK));
+ exit(2);
+ }
+
+ d.count = 0;
+
+ now = time(NULL);
+ if (d.ovsec_compat)
+ fprintf(d.fp, "OpenV*Secure V1.0\t%s", ctime(&now));
+ else
+ fprintf(d.fp, "Kerberos KADM5 database V2.0\t%s",
+ ctime(&now));
+
+ if ((ret = export_policy(&d, policy_db)) != OSA_ADB_OK) {
+ com_err(whoami, ret, "%s (%s)", error_message(EXPORT_POLICY),
+ params.admin_dbname);
+ exit(2);
+ }
+ if ((ret = export_principal(&d, &params)) !=
+ OSA_ADB_OK) {
+ com_err(whoami, ret, "%s (%s)", error_message(EXPORT_PRINCIPAL),
+ params.dbname);
+ exit(2);
+ }
+ fprintf(d.fp, "End of Database\t%d\trecords\n", d.count);
+
+ if ((ret = osa_adb_release_lock(policy_db)) != OSA_ADB_OK) {
+ com_err(argv[0], ret, error_message(EXPORT_UNLOCK));
+ exit(2);
+ }
+ if ((ret = osa_adb_close_policy(policy_db)) != OSA_ADB_OK) {
+ com_err(argv[0], ret, error_message(EXPORT_CLOSE));
+ exit(2);
+ }
+
+ fprintf(stderr, error_message(EXPORT_NO_ERR), d.count,
+ (d.count == 1) ? error_message(EXPORT_SINGLE_RECORD) :
+ error_message(EXPORT_PLURAL_RECORDS));
+ exit(0);
+}
+
+
+
diff --git a/src/kadmin/export/unit-test/ChangeLog b/src/kadmin/export/unit-test/ChangeLog
new file mode 100644
index 000000000..5db33c7c5
--- /dev/null
+++ b/src/kadmin/export/unit-test/ChangeLog
@@ -0,0 +1,5 @@
+Mon Jul 15 16:55:03 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.ov (unit-test-body), dotest.sh: ovsec_adm_*port is now
+ kadm5_*port
+
diff --git a/src/kadmin/export/unit-test/Makefile.ov b/src/kadmin/export/unit-test/Makefile.ov
new file mode 100644
index 000000000..25b1bf6c7
--- /dev/null
+++ b/src/kadmin/export/unit-test/Makefile.ov
@@ -0,0 +1,19 @@
+#
+# $Id$
+#
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+unit-test:: unit-test-setup unit-test-body unit-test-cleanup
+
+unit-test-setup::
+ $(SAVE_FILES)
+ $(FIX_CONF_FILES)
+ $(INITDB)
+
+unit-test-body::
+ $(RUNTEST) EXPORT=../kadm5_export --tool export
+
+unit-test-cleanup::
+ $(RESTORE_FILES)
diff --git a/src/kadmin/export/unit-test/add-to-db.sh b/src/kadmin/export/unit-test/add-to-db.sh
new file mode 100644
index 000000000..c50541546
--- /dev/null
+++ b/src/kadmin/export/unit-test/add-to-db.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+REALM=SECURE-TEST.OV.COM; export REALM
+DUMMY=${TESTDIR=$TOP/testing}; export TESTDIR
+DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL
+DUMMY=${TCLUTIL=$TESTDIR/tcl/util.t}; export TCLUTIL
+
+$SRVTCL <<'EOF'
+global r
+
+source $env(TCLUTIL)
+set r $env(REALM)
+
+proc newpol { pname } {
+ puts stdout [ovsec_kadm_create_policy $server_handle [simple_policy "$pname"] {OVSEC_KADM_POLICY}]
+}
+
+proc newprinc { name } {
+ global r
+ puts stdout [ovsec_kadm_create_principal $server_handle [simple_principal "$name@$r"] {OVSEC_KADM_PRINCIPAL} $name]
+}
+
+proc chpass { princ pass } {
+ global server_handle
+ puts stdout [ovsec_kadm_chpass_principal $server_handle "$princ" "$pass"]
+}
+
+puts stdout [ovsec_kadm_init $env(SRVTCL) mrroot null $r $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle]
+
+puts stdout [ovsec_kadm_create_policy $server_handle "export_pwhist 0 0 0 0 10 0" {OVSEC_KADM_POLICY OVSEC_KADM_PW_HISTORY_NUM}]
+
+### Commented out since this isn't going to work for the december beta
+#newprinc "export_with space"
+#newprinc "export_with\"dquote"
+#newprinc "export_with\nnewline"
+
+puts stdout [ovsec_kadm_create_principal $server_handle [princ_w_pol export_hist1@$r export_pwhist] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} hist1]
+
+chpass export_hist1@$r hist1_a
+
+puts stdout [ovsec_kadm_create_principal $server_handle [princ_w_pol export_hist10@$r export_pwhist] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} hist10]
+
+chpass export_hist10@$r hist10_a
+chpass export_hist10@$r hist10_b
+chpass export_hist10@$r hist10_c
+chpass export_hist10@$r hist10_d
+chpass export_hist10@$r hist10_e
+chpass export_hist10@$r hist10_f
+chpass export_hist10@$r hist10_g
+chpass export_hist10@$r hist10_h
+chpass export_hist10@$r hist10_i
+
+puts stdout [ovsec_kadm_destroy $server_handle]
+
+EOF
diff --git a/src/kadmin/export/unit-test/config/unix.exp b/src/kadmin/export/unit-test/config/unix.exp
new file mode 100644
index 000000000..e8d852f89
--- /dev/null
+++ b/src/kadmin/export/unit-test/config/unix.exp
@@ -0,0 +1,36 @@
+#
+# export_version -- extract and print the version number of export
+#
+
+proc export_version {} {
+ global EXPORT
+ set tmp [exec ident $EXPORT]
+ if [regexp {Header: .*export.c,v ([0-9]+\.[0-9]+)} $tmp \
+ dummy version] then {
+ clone_output "$EXPORT version $version\n"
+ } else {
+ clone_output "$EXPORT version <unknown>\n"
+ }
+}
+#
+# export_load -- loads the program
+#
+proc export_load {} {
+ #
+}
+
+# export_exit -- clean up and exit
+proc export_exit {} {
+ #
+}
+
+#
+# export_start -- start export running
+#
+proc export_start { args } {
+ global EXPORT
+ global spawn_id
+
+ verbose "% $EXPORT $args" 1
+ eval spawn $EXPORT $args
+}
diff --git a/src/kadmin/export/unit-test/dotest.sh b/src/kadmin/export/unit-test/dotest.sh
new file mode 100644
index 000000000..53d4fe0ab
--- /dev/null
+++ b/src/kadmin/export/unit-test/dotest.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${BSDDB_DUMP=$TESTDIR/util/bsddb_dump}
+DUMMY=${KDB5_EDIT=$TOP/../admin/edit/kdb5_edit}
+
+DPRINC=/tmp/dbdump.princ
+DPOL=/tmp/dbdump.policy
+
+DPRINC1=$DPRINC.1
+DPRINC2=$DPRINC.2
+
+DPOL1=$DPOL.1
+DPOL2=$DPOL.2
+
+DEXPORT=/tmp/dbexport
+
+./add-to-db.sh
+
+rm -f $DEXPORT
+../kadm5_export > $DEXPORT
+
+if $KDB5_EDIT -R ddb | sort > $DPRINC1; then
+ :
+else
+ echo "error dumping princ.1"
+fi
+if $BSDDB_DUMP /krb5/kadb5 | sort > $DPOL1; then
+ :
+else
+ echo "error dumping policy.1"
+fi
+
+rm -f /krb5/kadb5*
+touch /krb5/ovsec_adm.lock
+
+../../import/kadm5_import < $DEXPORT
+
+if $KDB5_EDIT -R ddb | sort > $DPRINC2; then
+ :
+else
+ echo "error dumping princ.2"
+fi
+if $BSDDB_DUMP /krb5/kadb5 | sort > $DPOL2; then
+ :
+else
+ echo "error dumping policy.2"
+fi
+
+
+status=0
+
+if test -s $DPRINC1 && \
+ test -s $DPRINC2 && \
+ cmp -s $DPRINC1 $DPRINC2; then
+ echo "export/import principal db succeeded"
+else
+ echo "export/import principal db failed"
+ status=1
+fi
+
+if test -s $DPOL1 && \
+ test -s $DPOL2 && \
+ cmp -s $DPOL1 $DPOL2; then
+ echo "export/import policy db succeeded"
+else
+ echo "export/import policy db failed"
+ status=1
+fi
+
+if [ $status -eq 0 ]; then
+ rm -f $DPRINC* $DPOL* $DEXPORT
+fi
+
+exit $status
diff --git a/src/kadmin/export/unit-test/export.0/dotest.exp b/src/kadmin/export/unit-test/export.0/dotest.exp
new file mode 100644
index 000000000..93ac21250
--- /dev/null
+++ b/src/kadmin/export/unit-test/export.0/dotest.exp
@@ -0,0 +1,29 @@
+#
+# $Id$
+#
+
+verbose "starting test: dotest.sh"
+
+spawn ./dotest.sh
+
+set timeout 60
+
+expect {
+ -re "error dumping (princ|policy)\.(\[12\])"
+ { fail $expect_out(0,string); exp_continue }
+ -re "export/import (principal|policy) db (failed|succeeded)"
+ {
+ if {![string compare $expect_out(2,string) failed]} {
+ fail $expect_out(0,string)
+ } else {
+ pass $expect_out(0,string)
+ }
+ exp_continue
+ }
+ eof break
+ timeout { fail "timeout"; close }
+}
+
+set w [wait]
+
+verbose "% Exit $w"
diff --git a/src/kadmin/export/unit-test/export.0/output.exp b/src/kadmin/export/unit-test/export.0/output.exp
new file mode 100644
index 000000000..6e0d4144b
--- /dev/null
+++ b/src/kadmin/export/unit-test/export.0/output.exp
@@ -0,0 +1,43 @@
+#
+# $Id$
+#
+
+set timeout 30
+
+load_lib "helpers.exp"
+
+#
+# Here are the tests
+#
+
+exec rm -f /tmp/dbexport
+
+export_win "B.25: General success" /tmp/dbexport
+
+check_mode "B.26" /tmp/dbexport 0400
+
+if {[catch "exec chmod 666 /tmp/dbexport" output]} {
+ unresolved "B.27: can't chmod /tmp/dbexport: $output"
+} else {
+ export_win "prep for B.27" /tmp/dbexport
+ check_mode "B.27" /tmp/dbexport 0400
+ exec rm -f /tmp/dbexport
+}
+
+proc test28 {} {
+ if {[catch "file stat /dev/null stats" output]} {
+ unresolved "B.28: can't stat /dev/null: $output"
+ return
+ }
+ set stats(mode) [expr $stats(mode) & 07777]
+ if {$stats(mode) == [expr 0400]} {
+ if {[catch "exec chmod 666 /dev/null" output]} {
+ unresolved "B.28: can't chmod /dev/null: $output"
+ return
+ }
+ set stats(mode) [expr 0666]
+ }
+ export_win "prep for B.28" /dev/null
+ check_mode "B.28" /dev/null $stats(mode)
+}
+test28
diff --git a/src/kadmin/export/unit-test/export.0/usage.exp b/src/kadmin/export/unit-test/export.0/usage.exp
new file mode 100644
index 000000000..9a592c9b8
--- /dev/null
+++ b/src/kadmin/export/unit-test/export.0/usage.exp
@@ -0,0 +1,25 @@
+#
+# $Id$
+#
+
+set timeout 30
+
+load_lib "helpers.exp"
+
+#
+# Here are the tests
+#
+
+export_lose "A.9: output file not writable" /foo/bar/baz \
+ "No such file or directory while opening output file"
+
+export_lose "A.10: two arguments" {foo bar} \
+ "Usage:"
+
+# XXX this depends on this being the last test run
+
+system {rm /krb5/kadb5}
+
+export_lose "A.2: /krb5 doesn't exist" /tmp/dbexport \
+ "No such file or directory while opening database"
+
diff --git a/src/kadmin/export/unit-test/helpers.exp b/src/kadmin/export/unit-test/helpers.exp
new file mode 100644
index 000000000..c53630f4b
--- /dev/null
+++ b/src/kadmin/export/unit-test/helpers.exp
@@ -0,0 +1,126 @@
+#
+# $Id$
+#
+
+if {[info commands exp_version] != {}} {
+ set exp_version_4 [regexp {^4} [exp_version]]
+} else {
+ set exp_version_4 [regexp {^4} [expect_version]]
+}
+
+# Backward compatibility until we're using expect 5 everywhere
+if {$exp_version_4} {
+ global wait_error_index wait_errno_index wait_status_index
+ set wait_error_index 0
+ set wait_errno_index 1
+ set wait_status_index 1
+} else {
+ set wait_error_index 2
+ set wait_errno_index 3
+ set wait_status_index 3
+}
+
+proc myfail { comment } {
+ global mytest_name
+ global mytest_status
+ wait
+ fail "$mytest_name: $comment"
+ set mytest_status 1
+}
+
+proc mypass {} {
+}
+
+##
+## When you expect on an id, and eof is detected, the spawn_id is closed.
+## It may be waited for, but calling expect or close on this id is an ERROR!
+##
+
+proc mytest { name kpargs status args } {
+ global spawn_id
+ global timeout
+ global mytest_name
+ global mytest_status
+ global wait_error_index wait_errno_index wait_status_index
+
+ verbose "starting test: $name"
+
+ set mytest_name "$name"
+
+ eval export_start $kpargs
+
+ # at the end, eof is success
+
+ lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } }
+
+ # for each test argument....
+ # rep invariant: when this foreach ends, the id is close'd, but
+ # not wait'ed.
+
+ foreach test $args {
+ set mytest_status 0
+
+ # treat the arg as an expect parameter
+ # if failure, the process will be closed and waited.
+
+ uplevel 1 "expect {
+ $test
+ timeout { close; myfail \"timeout\"}
+ eof { myfail \"eof read before expected message string\" }
+ }"
+
+ if {$mytest_status == 1} { return }
+ }
+
+ # at this point, the id is closed and we can wait on it.
+
+ set ret [wait]
+ verbose "% Exit $ret" 1
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail "$name: wait returned error [lindex $ret $wait_errno_index]"
+ } else {
+ if { ((![string compare $status zero]) &&
+ ([lindex $ret $wait_status_index] == 0)) ||
+ ((![string compare $status nonzero]) &&
+ ([lindex $ret $wait_status_index] != 0)) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ }
+ }
+}
+
+proc export_win { name args } {
+ mytest "$name" "$args" zero {
+ -re "Database export complete, \[0-9\]+ records processed."
+ { mypass }
+ eof
+ { myfail "error: $expect_out(buffer)" }
+ }
+}
+
+proc export_lose { name args error } {
+ mytest "$name" "$args" nonzero {
+ -re "Database export complete, \[0-9\]+ records processed."
+ { close; myfail "unexpected success" }
+ -re "ovsec_adm_export: .*$error"
+ { mypass }
+ eof
+ { myfail "error: $expect_out(buffer)" }
+ }
+}
+
+proc check_mode { test file mode } {
+ if {[catch "file stat $file stats" output]} {
+ unresolved "$test: can't stat $file: $output"
+ } else {
+ set stats(mode) [format "%o" [expr $stats(mode) & 07777]]
+ set mode [format "%o" [expr $mode]]
+ if {$stats(mode) != $mode} {
+ fail "$test: wrong mode ($stats(mode) should be $mode)"
+ } else {
+ verbose "$test: file $file has mode $mode"
+ pass $test
+ }
+ }
+}