summaryrefslogtreecommitdiffstats
path: root/src/kadmin
diff options
context:
space:
mode:
Diffstat (limited to 'src/kadmin')
-rw-r--r--src/kadmin/ChangeLog10
-rw-r--r--src/kadmin/Makefile.ov11
-rw-r--r--src/kadmin/cli/ChangeLog28
-rw-r--r--src/kadmin/cli/Makefile.in19
-rw-r--r--src/kadmin/cli/Makefile.ov28
-rw-r--r--src/kadmin/cli/attic/Makefile11
-rw-r--r--src/kadmin/cli/attic/Makefile.in45
-rw-r--r--src/kadmin/cli/attic/configure.in15
-rw-r--r--src/kadmin/cli/attic/getdate.y1006
-rw-r--r--src/kadmin/cli/attic/kadmin.c958
-rw-r--r--src/kadmin/cli/attic/kadmin_ct.ct67
-rw-r--r--src/kadmin/cli/attic/memmove.c144
-rw-r--r--src/kadmin/cli/attic/setenv.c165
-rw-r--r--src/kadmin/cli/attic/ss_wrapper.c56
-rw-r--r--src/kadmin/cli/configure.in20
-rw-r--r--src/kadmin/cli/dump.c1485
-rw-r--r--src/kadmin/cli/getdate.y1009
-rw-r--r--src/kadmin/cli/kadmin.1473
-rw-r--r--src/kadmin/cli/kadmin.c1322
-rw-r--r--src/kadmin/cli/kadmin_ct.ct79
-rw-r--r--src/kadmin/cli/keytab.c420
-rw-r--r--src/kadmin/cli/memmove.c144
-rw-r--r--src/kadmin/cli/setenv.c165
-rw-r--r--src/kadmin/cli/ss_wrapper.c61
-rw-r--r--src/kadmin/cli/strftime.c469
-rw-r--r--src/kadmin/config.mk/ChangeLog10
-rw-r--r--src/kadmin/config.mk/aix3.2.def40
-rw-r--r--src/kadmin/config.mk/architecture68
-rw-r--r--src/kadmin/config.mk/config141
-rw-r--r--src/kadmin/config.mk/hpux9.01.def32
-rw-r--r--src/kadmin/config.mk/irix5.2.def22
-rw-r--r--src/kadmin/config.mk/linux.def23
-rw-r--r--src/kadmin/config.mk/netbsd1.def22
-rw-r--r--src/kadmin/config.mk/rules538
-rw-r--r--src/kadmin/config.mk/site.def52
-rw-r--r--src/kadmin/config.mk/solaris2.3.def39
-rw-r--r--src/kadmin/config.mk/sunos4.1.def22
-rw-r--r--src/kadmin/config.mk/template142
-rw-r--r--src/kadmin/configure.in3
-rw-r--r--src/kadmin/create/ChangeLog9
-rw-r--r--src/kadmin/create/Makefile.in15
-rw-r--r--src/kadmin/create/Makefile.ov12
-rw-r--r--src/kadmin/create/attic/Makefile.in20
-rw-r--r--src/kadmin/create/attic/configure.in12
-rw-r--r--src/kadmin/create/attic/make_extern16
-rw-r--r--src/kadmin/create/attic/ovsec_adm_create.c663
-rw-r--r--src/kadmin/create/configure.in11
-rw-r--r--src/kadmin/create/kadm5_create.c241
-rw-r--r--src/kadmin/create/kdb5_create.c536
-rw-r--r--src/kadmin/create/string_table.c91
-rw-r--r--src/kadmin/create/string_table.h40
-rw-r--r--src/kadmin/dbutil/ChangeLog440
-rw-r--r--src/kadmin/dbutil/Makefile.in18
-rw-r--r--src/kadmin/dbutil/Makefile.ov19
-rw-r--r--src/kadmin/dbutil/configure.in13
-rw-r--r--src/kadmin/dbutil/dump.c1957
-rw-r--r--src/kadmin/dbutil/dumpv4.c411
-rw-r--r--src/kadmin/dbutil/kadm5_create.c241
-rw-r--r--src/kadmin/dbutil/kdb5_create.c449
-rw-r--r--src/kadmin/dbutil/kdb5_destroy.c117
-rw-r--r--src/kadmin/dbutil/kdb5_edit.M179
-rw-r--r--src/kadmin/dbutil/kdb5_stash.c153
-rw-r--r--src/kadmin/dbutil/kdb5_util.M122
-rw-r--r--src/kadmin/dbutil/kdb5_util.c416
-rw-r--r--src/kadmin/dbutil/kdb5_util.h49
-rw-r--r--src/kadmin/dbutil/kdb5_util_ct.ct56
-rw-r--r--src/kadmin/dbutil/loadv4.c881
-rw-r--r--src/kadmin/dbutil/ss_wrapper.c85
-rw-r--r--src/kadmin/dbutil/string_table.c91
-rw-r--r--src/kadmin/dbutil/string_table.h40
-rw-r--r--src/kadmin/dbutil/tcl_wrapper.c235
-rw-r--r--src/kadmin/dbutil/util.c155
-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
-rw-r--r--src/kadmin/import/ChangeLog17
-rw-r--r--src/kadmin/import/Makefile.in18
-rw-r--r--src/kadmin/import/Makefile.ov24
-rw-r--r--src/kadmin/import/configure.in12
-rw-r--r--src/kadmin/import/import.c421
-rw-r--r--src/kadmin/import/import.h40
-rw-r--r--src/kadmin/import/import_err.et26
-rw-r--r--src/kadmin/import/misc.c95
-rw-r--r--src/kadmin/import/ovsec_adm_import.c164
-rw-r--r--src/kadmin/import/strtok.c131
-rw-r--r--src/kadmin/import/unit-test/Makefile.ov9
-rw-r--r--src/kadmin/import/unit-test/config/unix.exp36
-rw-r--r--src/kadmin/import/unit-test/helpers.exp93
-rw-r--r--src/kadmin/import/unit-test/import.0/usage.exp23
-rw-r--r--src/kadmin/import/unit-test/valid_export_file27
-rw-r--r--src/kadmin/kdbkeys/ChangeLog16
-rw-r--r--src/kadmin/kdbkeys/Makefile.in15
-rw-r--r--src/kadmin/kdbkeys/Makefile.ov21
-rw-r--r--src/kadmin/kdbkeys/configure.in11
-rw-r--r--src/kadmin/kdbkeys/do-test.pl56
-rw-r--r--src/kadmin/keytab/ChangeLog13
-rw-r--r--src/kadmin/keytab/Makefile.in19
-rw-r--r--src/kadmin/keytab/Makefile.ov30
-rw-r--r--src/kadmin/keytab/configure.in12
-rw-r--r--src/kadmin/keytab/keytab.c528
-rw-r--r--src/kadmin/keytab/unit-test/ChangeLog4
-rw-r--r--src/kadmin/keytab/unit-test/Makefile.ov21
-rw-r--r--src/kadmin/keytab/unit-test/add-princs.tcl12
-rw-r--r--src/kadmin/keytab/unit-test/config/unix.exp46
-rw-r--r--src/kadmin/keytab/unit-test/del-princs.tcl24
-rw-r--r--src/kadmin/keytab/unit-test/helpers.exp132
-rw-r--r--src/kadmin/keytab/unit-test/keytab.0/ChangeLog4
-rw-r--r--src/kadmin/keytab/unit-test/keytab.0/adding.exp119
-rw-r--r--src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp47
-rw-r--r--src/kadmin/keytab/unit-test/keytab.0/removing.exp125
-rw-r--r--src/kadmin/ktutil/ChangeLog4
-rw-r--r--src/kadmin/ktutil/configure.in1
-rw-r--r--src/kadmin/passwd/ChangeLog20
-rw-r--r--src/kadmin/passwd/Kpasswd46
-rw-r--r--src/kadmin/passwd/Makefile.in23
-rw-r--r--src/kadmin/passwd/Makefile.ov34
-rw-r--r--src/kadmin/passwd/configure.in13
-rw-r--r--src/kadmin/passwd/kpasswd.c280
-rw-r--r--src/kadmin/passwd/kpasswd_strings.et76
-rw-r--r--src/kadmin/passwd/tty_kpasswd.c80
-rw-r--r--src/kadmin/passwd/unit-test/Makefile.ov23
-rw-r--r--src/kadmin/passwd/unit-test/config/unix.exp36
-rw-r--r--src/kadmin/passwd/unit-test/helpers.exp217
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/changing.exp102
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp29
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/principal.exp55
-rw-r--r--src/kadmin/passwd/unit-test/kpasswd.0/usage.exp26
-rw-r--r--src/kadmin/passwd/xm_kpasswd.c450
-rw-r--r--src/kadmin/scripts/inst-hdrs.sh14
-rw-r--r--src/kadmin/server/Makefile.in15
-rw-r--r--src/kadmin/server/Makefile.ov39
-rw-r--r--src/kadmin/server/acls.l190
-rw-r--r--src/kadmin/server/configure.in17
-rw-r--r--src/kadmin/server/kadm_rpc_svc.c248
-rw-r--r--src/kadmin/server/misc.c138
-rw-r--r--src/kadmin/server/misc.h53
-rw-r--r--src/kadmin/server/ovsec_kadmd.c762
-rw-r--r--src/kadmin/server/server_glue_v1.c31
-rw-r--r--src/kadmin/server/server_stubs.c1045
-rw-r--r--src/kadmin/testing/Makefile.ov8
-rw-r--r--src/kadmin/testing/proto/ChangeLog9
-rw-r--r--src/kadmin/testing/proto/kdc.conf.proto20
-rw-r--r--src/kadmin/testing/proto/krb5.conf.proto17
-rw-r--r--src/kadmin/testing/proto/ovsec_adm.dict3
-rw-r--r--src/kadmin/testing/scripts/ChangeLog15
-rw-r--r--src/kadmin/testing/scripts/Makefile.ov19
-rw-r--r--src/kadmin/testing/scripts/compare_dump.pl.in242
-rw-r--r--src/kadmin/testing/scripts/compare_dump.plin242
-rw-r--r--src/kadmin/testing/scripts/find-make.sh18
-rw-r--r--src/kadmin/testing/scripts/fixup-conf-files.pl.in344
-rw-r--r--src/kadmin/testing/scripts/fixup-conf-files.plin344
-rw-r--r--src/kadmin/testing/scripts/init_db181
-rw-r--r--src/kadmin/testing/scripts/make-host-keytab.pl.in138
-rw-r--r--src/kadmin/testing/scripts/make-host-keytab.plin138
-rw-r--r--src/kadmin/testing/scripts/qualname18
-rw-r--r--src/kadmin/testing/scripts/save_files.sh67
-rw-r--r--src/kadmin/testing/scripts/simple_dump.pl.in88
-rw-r--r--src/kadmin/testing/scripts/simple_dump.plin88
-rw-r--r--src/kadmin/testing/scripts/start_servers70
-rw-r--r--src/kadmin/testing/scripts/start_servers_local196
-rw-r--r--src/kadmin/testing/scripts/stop_servers84
-rw-r--r--src/kadmin/testing/scripts/stop_servers_local49
-rw-r--r--src/kadmin/testing/scripts/verify_xrunner_report.pl.in38
-rw-r--r--src/kadmin/testing/scripts/verify_xrunner_report.plin38
-rw-r--r--src/kadmin/testing/tcl/util.t61
-rw-r--r--src/kadmin/testing/util/ChangeLog3
-rw-r--r--src/kadmin/testing/util/Makefile.ov34
-rw-r--r--src/kadmin/testing/util/bsddb_dump.c64
-rw-r--r--src/kadmin/testing/util/tcl_kadm5.c2312
-rw-r--r--src/kadmin/testing/util/tcl_krb5_hash.c163
-rw-r--r--src/kadmin/testing/util/tcl_ovsec_kadm.c2016
-rw-r--r--src/kadmin/testing/util/tcl_ovsec_kadm_syntax57
-rw-r--r--src/kadmin/testing/util/test.c32
-rw-r--r--src/kadmin/v4server/ChangeLog249
-rw-r--r--src/kadmin/v4server/Makefile.in23
-rw-r--r--src/kadmin/v4server/Makefile.ov42
-rw-r--r--src/kadmin/v4server/acl_files.c536
-rw-r--r--src/kadmin/v4server/acl_files.doc107
-rw-r--r--src/kadmin/v4server/admin_server.c684
-rw-r--r--src/kadmin/v4server/attic/ChangeLog25
-rw-r--r--src/kadmin/v4server/attic/Imakefile49
-rw-r--r--src/kadmin/v4server/attic/Makefile39
-rw-r--r--src/kadmin/v4server/attic/Makefile.in53
-rw-r--r--src/kadmin/v4server/attic/acl_files.c541
-rw-r--r--src/kadmin/v4server/attic/acl_files.doc107
-rw-r--r--src/kadmin/v4server/attic/aclocal.m43
-rw-r--r--src/kadmin/v4server/attic/admin_server.c668
-rw-r--r--src/kadmin/v4server/attic/configure.in22
-rw-r--r--src/kadmin/v4server/attic/kadm_err.et57
-rw-r--r--src/kadmin/v4server/attic/kadm_funcs.c876
-rw-r--r--src/kadmin/v4server/attic/kadm_ser_wrap.c288
-rw-r--r--src/kadmin/v4server/attic/kadm_server.c546
-rw-r--r--src/kadmin/v4server/attic/kadm_server.h64
-rw-r--r--src/kadmin/v4server/attic/kadm_stream.c276
-rw-r--r--src/kadmin/v4server/attic/kadm_supp.c114
-rw-r--r--src/kadmin/v4server/configure.in16
-rw-r--r--src/kadmin/v4server/kadm_err.et57
-rw-r--r--src/kadmin/v4server/kadm_funcs.c1066
-rw-r--r--src/kadmin/v4server/kadm_ser_wrap.c310
-rw-r--r--src/kadmin/v4server/kadm_server.c571
-rw-r--r--src/kadmin/v4server/kadm_server.h58
-rw-r--r--src/kadmin/v4server/kadm_stream.c277
-rw-r--r--src/kadmin/v4server/kadm_supp.c113
-rw-r--r--src/kadmin/v4server/unit-test/ChangeLog13
-rw-r--r--src/kadmin/v4server/unit-test/Makefile.ov19
-rw-r--r--src/kadmin/v4server/unit-test/config/ChangeLog7
-rw-r--r--src/kadmin/v4server/unit-test/config/unix.exp42
-rw-r--r--src/kadmin/v4server/unit-test/getpid.sh5
-rw-r--r--src/kadmin/v4server/unit-test/helpers.exp232
-rw-r--r--src/kadmin/v4server/unit-test/remove_changepw_perms.sh9
-rw-r--r--src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp11
-rw-r--r--src/kadmin/v4server/unit-test/v4server.1/access.exp88
-rw-r--r--src/kadmin/v4server/unit-test/v4server.1/change-password.exp59
-rw-r--r--src/kadmin/v4server/unit-test/v4server.1/usage.exp26
228 files changed, 40100 insertions, 2 deletions
diff --git a/src/kadmin/ChangeLog b/src/kadmin/ChangeLog
index 4dbf1ae4c..5408cd354 100644
--- a/src/kadmin/ChangeLog
+++ b/src/kadmin/ChangeLog
@@ -1,3 +1,13 @@
+Fri Jul 12 14:38:30 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in (CONFIG_DIRS): ktutil is still useful
+ functionality; add it back to the build.
+
+Wed Jul 10 16:27:11 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: kdbkeys is no longer necessary.
+ * configure.in (CONFIG_DIRS): added dbutil
+
Thu Aug 24 19:21:14 1995 Theodore Y. Ts'o <tytso@dcl>
* .Sanitize: Add ktutil directory
diff --git a/src/kadmin/Makefile.ov b/src/kadmin/Makefile.ov
new file mode 100644
index 000000000..1b5cb8e39
--- /dev/null
+++ b/src/kadmin/Makefile.ov
@@ -0,0 +1,11 @@
+#
+# $Id$
+#
+
+TOP = .
+include $(TOP)/config.mk/template
+
+SUBDIRS = ../lib/kadm5 server create passwd import export v4server \
+ keytab cli testing dbutil
+
+expand SubdirTarget
diff --git a/src/kadmin/cli/ChangeLog b/src/kadmin/cli/ChangeLog
new file mode 100644
index 000000000..a69639b86
--- /dev/null
+++ b/src/kadmin/cli/ChangeLog
@@ -0,0 +1,28 @@
+Fri Jul 19 16:10:39 1996 Marc Horowitz <marc@mit.edu>
+
+ * ss_wrapper.c (main): ss_execute_line was being called with three
+ args. There are only two, so no error was ever being returned.
+
+Thu Jul 18 19:14:51 1996 Marc Horowitz <marc@mit.edu>
+
+ * attic/configure.in: removed SS_RULES
+
+ * keytab.c (etype_string): ifdef out des3 reference
+
+ * configure.in: removed SS_RULES
+
+Mon Jul 15 16:56:43 1996 Barry Jaspan <bjaspan@mit.edu>
+
+ * kadmin.1, keytab.c (kadmin_keytab_add): change ktadd usage to
+ accept -glob
+
+Tue Jul 9 16:15:46 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in: complete rewrite
+ * configure.in: add the necessary USE_*_LIBRARY macros
+
+Mon Jul 8 16:45:20 1996 Barry Jaspan <bjaspan@mit.edu>
+
+ * kadmin.1: Update man page for kadm5 changes and functionality.
+
+
diff --git a/src/kadmin/cli/Makefile.in b/src/kadmin/cli/Makefile.in
new file mode 100644
index 000000000..c605dd81f
--- /dev/null
+++ b/src/kadmin/cli/Makefile.in
@@ -0,0 +1,19 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+PROG = kadmin
+OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab.o
+
+all:: $(PROG).local $(PROG)
+
+$(PROG).local: $(OBJS) $(SRVDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS)
+
+$(PROG): $(OBJS) $(CLNTDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS)
+
+install::
+ $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean::
+ $(RM) $(PROG).local $(PROG) $(OBJS)
diff --git a/src/kadmin/cli/Makefile.ov b/src/kadmin/cli/Makefile.ov
new file mode 100644
index 000000000..5163bebb8
--- /dev/null
+++ b/src/kadmin/cli/Makefile.ov
@@ -0,0 +1,28 @@
+TOP = ..
+include $(TOP)/config.mk/template
+
+PROG = kadmin
+SRCS = kadmin.c kadmin_ct.c ss_wrapper.c getdate.c keytab.c
+OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab.o
+LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5_ALL) \
+ $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) $(BSDLIB) $(NETLIB)
+DEPENDS = kadmin_ct.c getdate.c
+
+expand NormalProgram
+
+PROG = kadmin.local
+LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKDB5) \
+ $(LIBKRB5_ALL) $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) \
+ $(BSDLIB) $(NETLIB) $(REGEXLIB)
+
+expand NormalProgram
+
+depend:: $(SRCS)
+clean:: ; -rm -f getdate.c y.tab.h kadmin_ct.c
+
+install::
+ $(INSTCMD) kadmin.1 $(INSTALL_MANDIR)/cat1/kadmin.1
+
+# needed until we run makedepend
+kadmin_ct.c: kadmin_ct.ct
+kadmin_ct.o: kadmin_ct.c
diff --git a/src/kadmin/cli/attic/Makefile b/src/kadmin/cli/attic/Makefile
new file mode 100644
index 000000000..79e432fe4
--- /dev/null
+++ b/src/kadmin/cli/attic/Makefile
@@ -0,0 +1,11 @@
+TOP = ../..
+include $(TOP)/config.mk/template
+
+SRCS = kadmin.c kadmin_ct.ct ss_wrapper.c getdate.y
+OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o
+PROG = cli_secure_admin
+
+LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5_ALL) \
+ $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) $(BSDLIB) $(NETLIB)
+
+expand NormalProgram
diff --git a/src/kadmin/cli/attic/Makefile.in b/src/kadmin/cli/attic/Makefile.in
new file mode 100644
index 000000000..160016af9
--- /dev/null
+++ b/src/kadmin/cli/attic/Makefile.in
@@ -0,0 +1,45 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) $(OVSECINC)
+OVSECROOT=/home/tlyu/ovsecure
+OVSECSTAGE=/afs/dev.mit.edu/reference/ovsecure/sunos/stage
+OVSECINC=-I$(OVSECROOT)/include -I$(OVSECSTAGE)/include
+OVSECLIB=-L$(OVSECSTAGE)/lib -lclient -lcommon -lrpclib -ldyn
+LDFLAGS = -g
+LIBOBJS=@LIBOBJS@
+ISODELIB=$(OVSECROOT)/lib/libisode.a
+COMERRLIB=$(OVSECROOT)/lib/libcom_err.a
+SSLIB=$(OVSECSTAGE)/lib/libss.a
+DBMLIB=$(OVSECROOT)/lib/libdb.a
+KDBLIB=$(OVSECROOT)/lib/libkdb5.a
+
+all::
+
+KLIB = $(OVSECROOT)/lib/libgssapi_krb5.a $(OVSECROOT)/lib/libkrb5.a $(OVSECROOT)/lib/libcrypto.a $(ISODELIB) $(SSLIB) $(COMERRLIB) $(DBMLIB)
+DEPKLIB = $(TOPLIBD)/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COMERRLIB) $(DBMLIB)
+
+SRCS =
+
+OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o $(LIBOBJS)
+
+all:: kadmin
+kadmin.o:
+ $(CC) -c $(CCOPTS) $(OVSECINC) $(DEFS) kadmin.c
+getdate.c getdate.h: getdate.y
+ $(RM) getdate.c getdate.h y.tab.*
+ $(YACC) -d $(srcdir)/getdate.y
+ $(MV) y.tab.c getdate.c
+ $(MV) y.tab.h getdate.h
+
+kadmin: $(OBJS)
+ $(CC) -o kadmin $(CFLAGS) $(OBJS) $(OVSECLIB) $(KLIB) $(LIBS)
+
+# needed until we run makedepend
+kadmin_ct.c: kadmin_ct.ct
+
+kadmin_ct.o: kadmin_ct.c
+
+clean::
+ $(RM) kadmin $(OBJS) kadmin_ct.c getdate.c getdate.h y.tab.c y.tab.h
+
+# testing rule for building getdate
+getdate: getdate.c
+ $(CC) -o getdate $(CFLAGS) -DTEST getdate.c
diff --git a/src/kadmin/cli/attic/configure.in b/src/kadmin/cli/attic/configure.in
new file mode 100644
index 000000000..8cca51316
--- /dev/null
+++ b/src/kadmin/cli/attic/configure.in
@@ -0,0 +1,15 @@
+AC_INIT(getdate.y)
+WITH_CCOPTS
+CONFIG_RULES
+AC_SET_BUILDTOP
+AC_PROG_INSTALL
+AC_PROG_YACC
+AC_HAVE_HEADERS(unistd.h sys/timeb.h alloca.h)
+AC_HAVE_FUNCS(ftime timezone)
+AC_CHECK_LIB(ndbm,main)
+AC_CHECK_LIB(dbm,main)
+AC_REPLACE_FUNCS([setenv memmove])
+KRB_INCLUDE
+ISODE_INCLUDE
+WITH_KRB5ROOT
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/cli/attic/getdate.y b/src/kadmin/cli/attic/getdate.y
new file mode 100644
index 000000000..6b03e73bb
--- /dev/null
+++ b/src/kadmin/cli/attic/getdate.y
@@ -0,0 +1,1006 @@
+%{
+/*
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+** send any email to Rich.
+**
+** This grammar has nine shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
+/* SUPPRESS 288 on yyerrlab *//* Label unused */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+
+/* Since the code of getdate.y is not included in the Emacs executable
+ itself, there is no need to #define static in this file. Even if
+ the code were included in the Emacs executable, it probably
+ wouldn't do any harm to #undef it here; this will only cause
+ problems if we try to write to a static variable, which I don't
+ think this code needs to do. */
+#ifdef emacs
+#undef static
+#endif
+
+/* The following block of alloca-related preprocessor directives is here
+ solely to allow compilation by non GNU-C compilers of the C parser
+ produced from this file by old versions of bison. Newer versions of
+ bison include a block similar to this one in bison.simple. */
+
+#ifdef __GNUC__
+#undef alloca
+#define alloca __builtin_alloca
+#else
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX /* for Bison */
+ #pragma alloca
+#else
+void *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* The code at the top of get_date which figures out the offset of the
+ current time zone checks various CPP symbols to see if special
+ tricks are need, but defaults to using the gettimeofday system call.
+ Include <sys/time.h> if that will be used. */
+
+#if defined(vms)
+
+#include <types.h>
+#include <time.h>
+
+#else
+
+#include <sys/types.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef timezone
+#undef timezone /* needed for sgi */
+#endif
+
+#if defined(HAVE_SYS_TIMEB_H)
+#include <sys/timeb.h>
+#else
+/*
+** We use the obsolete `struct timeb' as part of our interface!
+** Since the system doesn't have it, we define it here;
+** our callers must do likewise.
+*/
+struct timeb {
+ time_t time; /* Seconds since the epoch */
+ unsigned short millitm; /* Field not used */
+ short timezone; /* Minutes west of GMT */
+ short dstflag; /* Field not used */
+};
+#endif /* defined(HAVE_SYS_TIMEB_H) */
+
+#endif /* defined(vms) */
+
+#if defined (STDC_HEADERS) || defined (USG)
+#include <string.h>
+#endif
+
+/* Some old versions of bison generate parsers that use bcopy.
+ That loses on systems that don't provide the function, so we have
+ to redefine it here. */
+#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
+#define bcopy(from, to, len) memcpy ((to), (from), (len))
+#endif
+
+extern struct tm *gmtime();
+extern struct tm *localtime();
+
+#define yyparse getdate_yyparse
+#define yylex getdate_yylex
+#define yyerror getdate_yyerror
+
+static int yylex ();
+static int yyerror ();
+
+#if !defined(lint) && !defined(SABER)
+static char RCS[] =
+ "$Header$";
+#endif /* !defined(lint) && !defined(SABER) */
+
+
+#define EPOCH 1970
+#define HOUR(x) ((time_t)(x) * 60)
+#define SECSPERDAY (24L * 60L * 60L)
+
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+** Daylight-savings mode: on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+ DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static char *yyInput;
+static DSTMODE yyDSTmode;
+static time_t yyDayOrdinal;
+static time_t yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static time_t yyTimezone;
+static time_t yyDay;
+static time_t yyHour;
+static time_t yyMinutes;
+static time_t yyMonth;
+static time_t yySeconds;
+static time_t yyYear;
+static MERIDIAN yyMeridian;
+static time_t yyRelMonth;
+static time_t yyRelSeconds;
+
+%}
+
+%union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+}
+
+%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
+%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> tMERIDIAN o_merid
+
+%%
+
+spec : /* NULL */
+ | spec item
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ | number
+ ;
+
+time : tUNUMBER tMERIDIAN {
+ yyHour = $1;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = 0;
+ yyMeridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
+ }
+ ;
+
+zone : tZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSToff;
+ }
+ | tDAYZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ |
+ tZONE tDST {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ ;
+
+day : tDAY {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tDAY ',' {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tUNUMBER tDAY {
+ yyDayOrdinal = $1;
+ yyDayNumber = $2;
+ }
+ ;
+
+date : tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ yyYear = $5;
+ }
+ | tUNUMBER tSNUMBER tSNUMBER {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = $1;
+ yyMonth = -$2;
+ yyDay = -$3;
+ }
+ | tUNUMBER tMONTH tSNUMBER {
+ /* e.g. 17-JUN-1992. */
+ yyDay = $1;
+ yyMonth = $2;
+ yyYear = -$3;
+ }
+ | tMONTH tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ yyYear = $4;
+ }
+ | tUNUMBER tMONTH {
+ yyMonth = $2;
+ yyDay = $1;
+ }
+ | tUNUMBER tMONTH tUNUMBER {
+ yyMonth = $2;
+ yyDay = $1;
+ yyYear = $3;
+ }
+ ;
+
+rel : relunit tAGO {
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMonth = -yyRelMonth;
+ }
+ | relunit
+ ;
+
+relunit : tUNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tSNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tMINUTE_UNIT {
+ yyRelSeconds += $1 * 60L;
+ }
+ | tSNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tUNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tSEC_UNIT {
+ yyRelSeconds++;
+ }
+ | tSNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tUNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tMONTH_UNIT {
+ yyRelMonth += $1;
+ }
+ ;
+
+number : tUNUMBER {
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = $1;
+ else {
+ if($1>10000) {
+ yyHaveDate++;
+ yyDay= ($1)%100;
+ yyMonth= ($1/100)%100;
+ yyYear = $1/10000;
+ }
+ else {
+ yyHaveTime++;
+ if ($1 < 100) {
+ yyHour = $1;
+ yyMinutes = 0;
+ }
+ else {
+ yyHour = $1 / 100;
+ yyMinutes = $1 % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ }
+ ;
+
+o_merid : /* NULL */ {
+ $$ = MER24;
+ }
+ | tMERIDIAN {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* Month and day table. */
+static TABLE const MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL }
+};
+
+/* Time units table. */
+static TABLE const UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
+ { "week", tMINUTE_UNIT, 7 * 24 * 60 },
+ { "day", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "hour", tMINUTE_UNIT, 60 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL }
+};
+
+/* Assorted relative-time words. */
+static TABLE const OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL }
+};
+
+/* The timezone table. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE const TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR(1) }, /* Central European */
+ { "met", tZONE, -HOUR(1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR(1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
+ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ { NULL }
+};
+
+/* Military timezone table. */
+static TABLE const MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL }
+};
+
+
+
+
+/* ARGSUSED */
+static int
+yyerror(s)
+ char *s;
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(Hours, Minutes, Seconds, Meridian)
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+}
+
+
+static time_t
+Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
+ time_t Month;
+ time_t Day;
+ time_t Year;
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+ DSTMODE DSTmode;
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ if (Year < EPOCH || Year > 1999
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ return -1;
+
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
+ Julian -= 60 * 60;
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(Start, Future)
+ time_t Start;
+ time_t Future;
+{
+ time_t StartDay;
+ time_t FutureDay;
+
+ StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+ FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(Start, DayOrdinal, DayNumber)
+ time_t Start;
+ time_t DayOrdinal;
+ time_t DayNumber;
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+
+static time_t
+RelativeMonth(Start, RelMonth)
+ time_t Start;
+ time_t RelMonth;
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(buff)
+ char *buff;
+{
+ register char *p;
+ register char *q;
+ register const TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen(buff) == 3)
+ abbrev = 1;
+ else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && isalpha(*buff)) {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+
+static int
+yylex()
+{
+ register char c;
+ register char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for ( ; ; ) {
+ while (isspace(*yyInput))
+ yyInput++;
+
+ if (isdigit(c = *yyInput) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit(*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit(c = *yyInput++); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha(c)) {
+ for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ }
+}
+
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds. */
+static time_t
+difftm(a, b)
+ struct tm *a, *b;
+{
+ int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+ int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+ return
+ (
+ (
+ (
+ /* difference in day of year */
+ a->tm_yday - b->tm_yday
+ /* + intervening leap days */
+ + ((ay >> 2) - (by >> 2))
+ - (ay/100 - by/100)
+ + ((ay/100 >> 2) - (by/100 >> 2))
+ /* + difference in years * 365 */
+ + (time_t)(ay-by) * 365
+ )*24 + (a->tm_hour - b->tm_hour)
+ )*60 + (a->tm_min - b->tm_min)
+ )*60 + (a->tm_sec - b->tm_sec);
+}
+
+time_t
+get_date(p, now)
+ char *p;
+ struct timeb *now;
+{
+ struct tm *tm, gmt;
+ struct timeb ftz;
+ time_t Start;
+ time_t tod;
+
+ yyInput = p;
+ if (now == NULL) {
+ now = &ftz;
+ (void)time(&ftz.time);
+
+ if (! (tm = gmtime (&ftz.time)))
+ return -1;
+ gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
+ ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60;
+ }
+
+ tm = localtime(&now->time);
+ yyYear = tm->tm_year;
+ yyMonth = tm->tm_mon + 1;
+ yyDay = tm->tm_mday;
+ yyTimezone = now->timezone;
+ yyDSTmode = DSTmaybe;
+ yyHour = 0;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMonth = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ if (yyHaveDate || yyHaveTime || yyHaveDay) {
+ Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+ yyMeridian, yyDSTmode);
+ if (Start < 0)
+ return -1;
+ }
+ else {
+ Start = now->time;
+ if (!yyHaveRel)
+ Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
+ }
+
+ Start += yyRelSeconds;
+ Start += RelativeMonth(Start, yyRelMonth);
+
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
+ Start += tod;
+ }
+
+ /* Have to do *something* with a legitimate -1 so it's distinguishable
+ * from the error return value. (Alternately could set errno on error.) */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+main(ac, av)
+ int ac;
+ char *av[];
+{
+ char buff[128];
+ time_t d;
+
+ (void)printf("Enter date, or blank line to exit.\n\t> ");
+ (void)fflush(stdout);
+ while (gets(buff) && buff[0]) {
+ d = get_date(buff, (struct timeb *)NULL);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("%s", ctime(&d));
+ (void)printf("\t> ");
+ (void)fflush(stdout);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/src/kadmin/cli/attic/kadmin.c b/src/kadmin/cli/attic/kadmin.c
new file mode 100644
index 000000000..91d2a71e4
--- /dev/null
+++ b/src/kadmin/cli/attic/kadmin.c
@@ -0,0 +1,958 @@
+/*
+ * Copyright 1994 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. 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.
+ *
+ * kadmin.c: base functions for a kadmin command line interface using
+ * the OVSecure library
+ */
+
+#include <krb5/krb5.h>
+#include <krb5/los-proto.h>
+#include <krb5/ext-proto.h>
+#include <krb5/kdb.h>
+#include <ovsec_admin/admin.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <math.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/timeb.h>
+
+/* special struct to convert flag names for principals
+ to actual krb5_flags for a principal */
+struct pflag {
+ char *flagname; /* name of flag as typed to CLI */
+ int flaglen; /* length of string (not counting -,+) */
+ krb5_flags theflag; /* actual principal flag to set/clear */
+ int set; /* 0 means clear, 1 means set (on '-') */
+};
+
+static struct pflag flags[] = {
+{"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1},
+{"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1},
+{"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1},
+{"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1},
+{"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1},
+{"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1},
+{"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1},
+{"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0},
+{"requres_hwauth", 14, KRB5_KDB_REQUIRES_HW_AUTH, 0},
+{"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0},
+{"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1},
+{"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 }
+};
+
+static char *prflags[] = {
+ "DISALLOW_POSTDATED", /* 0x00000001 */
+ "DISALLOW_FORWARDABLE", /* 0x00000002 */
+ "DISALLOW_TGT_BASED", /* 0x00000004 */
+ "DISALLOW_RENEWABLE", /* 0x00000008 */
+ "DISALLOW_PROXIABLE", /* 0x00000010 */
+ "DISALLOW_DUP_SKEY", /* 0x00000020 */
+ "DISALLOW_ALL_TIX", /* 0x00000040 */
+ "REQUIRES_PRE_AUTH", /* 0x00000080 */
+ "REQUIRES_HW_AUTH", /* 0x00000100 */
+ "REQUIRES_PWCHANGE", /* 0x00000200 */
+ "UNKNOWN_0x00000400", /* 0x00000400 */
+ "UNKNOWN_0x00000800", /* 0x00000800 */
+ "DISALLOW_SVR", /* 0x00001000 */
+ "PWCHANGE_SERVICE" /* 0x00002000 */
+};
+
+char *getenv();
+struct passwd *getpwuid();
+int exit_status = 0;
+char *def_realm = NULL;
+
+void *ovsec_hndl = NULL;
+
+void usage()
+{
+ fprintf(stderr,
+ "usage: kadmin [-r realm] [-p principal] [-k keytab] [-q query]\n");
+ exit(1);
+}
+
+/* this is a wrapper to go around krb5_parse_principal so we can set
+ the default realm up properly */
+krb5_error_code kadmin_parse_name(name, principal)
+ char *name;
+ krb5_principal *principal;
+{
+ char *cp, *fullname;
+ krb5_error_code retval;
+
+ /* assumes def_realm is initialized! */
+ fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1);
+ if (fullname == NULL)
+ return ENOMEM;
+ strcpy(fullname, name);
+ cp = strchr(fullname, '@');
+ while (cp) {
+ if (cp - fullname && *(cp - 1) != '\\')
+ break;
+ else
+ cp = strchr(cp, '@');
+ }
+ if (cp == NULL) {
+ strcat(fullname, "@");
+ strcat(fullname, def_realm);
+ }
+ retval = krb5_parse_name(fullname, principal);
+ free(fullname);
+ return retval;
+}
+
+char *kadmin_startup(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ char *princstr = NULL, *keytab = NULL, *query = NULL;
+ char *luser, *canon, *cp;
+ int optchar, freeprinc = 0;
+ struct passwd *pw;
+ ovsec_kadm_ret_t retval;
+ krb5_ccache cc;
+ krb5_principal princ;
+
+ while ((optchar = getopt(argc, argv, "r:p:k:q:")) != EOF) {
+ switch (optchar) {
+ case 'r':
+ def_realm = optarg;
+ break;
+ case 'p':
+ princstr = optarg;
+ break;
+ case 'k':
+ fprintf(stderr, "kadmin: -k not supported yet\n");
+ exit(1);
+ break;
+ case 'q':
+ query = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (def_realm == NULL && krb5_get_default_realm(&def_realm)) {
+ if (freeprinc)
+ free(princstr);
+ fprintf(stderr, "kadmin: unable to get default realm\n");
+ exit(1);
+ }
+ if (princstr == NULL) {
+ if (!krb5_cc_default(&cc) && !krb5_cc_get_principal(cc, &princ)) {
+ char *realm = NULL;
+ if (krb5_unparse_name(princ, &canon)) {
+ fprintf(stderr,
+ "kadmin: unable to canonicalize principal\n");
+ krb5_free_principal(princ);
+ exit(1);
+ }
+ /* strip out realm of principal if it's there */
+ realm = strchr(canon, '@');
+ while (realm) {
+ if (realm - canon && *(realm - 1) != '\\')
+ break;
+ else
+ realm = strchr(realm, '@');
+ }
+ if (realm)
+ *realm++ = '\0';
+ cp = strchr(canon, '/');
+ while (cp) {
+ if (cp - canon && *(cp - 1) != '\\')
+ break;
+ else
+ cp = strchr(cp, '/');
+ }
+ if (cp != NULL)
+ *cp = '\0';
+ princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ +
+ (realm ? 1 + strlen(realm) : 0) + 1);
+ if (princstr == NULL) {
+ fprintf(stderr, "kadmin: out of memory\n");
+ exit(1);
+ }
+ strcpy(princstr, canon);
+ strcat(princstr, "/admin");
+ if (realm) {
+ strcat(princstr, "@");
+ strcat(princstr, realm);
+ }
+ free(canon);
+ krb5_free_principal(princ);
+ freeprinc++;
+ } else if (luser = getenv("USER")) {
+ princstr = malloc(strlen(luser) + 7 /* "/admin@" */
+ + strlen(def_realm) + 1);
+ if (princstr == NULL) {
+ fprintf(stderr, "kadmin: out of memory\n");
+ exit(1);
+ }
+ strcpy(princstr, luser);
+ strcat(princstr, "/admin");
+ strcat(princstr, "@");
+ strcat(princstr, def_realm);
+ freeprinc++;
+ } else if (pw = getpwuid(getuid())) {
+ princstr = malloc(strlen(pw->pw_name) + 7 /* "/admin@" */
+ + strlen(def_realm) + 1);
+ if (princstr == NULL) {
+ fprintf(stderr, "kadmin: out of memory\n");
+ exit(1);
+ }
+ strcpy(princstr, pw->pw_name);
+ strcat(princstr, "/admin@");
+ strcat(princstr, def_realm);
+ freeprinc++;
+ } else {
+ fprintf(stderr, "kadmin: unable to figure out a principal name\n");
+ exit(1);
+ }
+ }
+ retval = ovsec_kadm_init_with_password(princstr, NULL,
+ OVSEC_KADM_ADMIN_SERVICE,
+ def_realm,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &ovsec_hndl);
+ if (freeprinc)
+ free(princstr);
+ if (retval) { /* assume kadm_init does init_ets() */
+ com_err("kadmin", retval, "while initializing kadmin interface");
+ exit(1);
+ }
+ return query;
+}
+
+int quit()
+{
+ ovsec_kadm_destroy(ovsec_hndl);
+ /* insert more random cleanup here */
+}
+
+void kadmin_delprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ ovsec_kadm_ret_t retval;
+ krb5_principal princ;
+ char *canon;
+ char reply[5];
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "delete_principal: wrong number of arguments\n");
+ return;
+ }
+ if (argc == 3 &&
+ (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) {
+ fprintf(stderr, "delete_principal: bad arguments\n");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &princ);
+ if (retval) {
+ com_err("delete_principal", retval, "while parsing principal name");
+ return;
+ }
+ retval = krb5_unparse_name(princ, &canon);
+ if (retval) {
+ com_err("delete_principal", retval,
+ "while canonicalizing principal");
+ krb5_free_principal(princ);
+ return;
+ }
+ if (argc == 2) {
+ printf("Are you sure you want to delete the principal \"%s\"? (yes/no): ", canon);
+ fgets(reply, sizeof (reply), stdin);
+ if (strcmp("yes\n", reply)) {
+ fprintf(stderr, "Principal \"%s\" not deleted\n", canon);
+ free(canon);
+ krb5_free_principal(princ);
+ return;
+ }
+ }
+ retval = ovsec_kadm_delete_principal(ovsec_hndl, princ);
+ krb5_free_principal(princ);
+ if (retval) {
+ com_err("delete_principal", retval,
+ "while deleteing principal \"%s\"", canon);
+ free(canon);
+ return;
+ }
+ printf("Principal \"%s\" deleted.\nMake sure that you have removed this principal from all ACLs before reusing.\n", canon);
+ free(canon);
+ return;
+}
+
+void kadmin_renprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_principal oldprinc, newprinc;
+ char *oldcanon, *newcanon;
+ char reply[5];
+ ovsec_kadm_ret_t retval;
+
+ if (argc < 3 || argc > 4) {
+ fprintf(stderr, "rename_principal: wrong number of arguments\n");
+ return;
+ }
+ if (argc == 4 &&
+ (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) {
+ fprintf(stderr, "rename_principal: bad arguments\n");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 2], &oldprinc);
+ if (retval) {
+ com_err("rename_principal", retval, "while parsing old principal");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &newprinc);
+ if (retval) {
+ krb5_free_principal(oldprinc);
+ com_err("rename_principal", retval, "while parsing new principal");
+ return;
+ }
+ retval = krb5_unparse_name(oldprinc, &oldcanon);
+ if (retval) {
+ com_err("rename_principal", retval,
+ "while canonicalizing old principal");
+ krb5_free_principal(newprinc);
+ krb5_free_principal(oldprinc);
+ return;
+ }
+ retval = krb5_unparse_name(newprinc, &newcanon);
+ if (retval) {
+ com_err("rename_principal", retval,
+ "while canonicalizing new principal");
+ free(oldcanon);
+ krb5_free_principal(newprinc);
+ krb5_free_principal(oldprinc);
+ return;
+ }
+ if (argc == 3) {
+ printf("Are you sure you want to rename the principal \"%s\" to \"%s\"? (yes/no): ",
+ oldcanon, newcanon);
+ fgets(reply, sizeof (reply), stdin);
+ if (strcmp("yes\n", reply)) {
+ fprintf(stderr,
+ "rename_principal: \"%s\" NOT renamed to \"%s\".\n",
+ oldcanon, newcanon);
+ free(newcanon);
+ free(oldcanon);
+ krb5_free_principal(newprinc);
+ krb5_free_principal(oldprinc);
+ return;
+ }
+ }
+ retval = ovsec_kadm_rename_principal(ovsec_hndl, oldprinc, newprinc);
+ krb5_free_principal(oldprinc);
+ krb5_free_principal(newprinc);
+ if (retval) {
+ com_err("rename_principal", retval,
+ "while renaming \"%s\" to \"%s\".", oldcanon,
+ newcanon);
+ free(newcanon);
+ free(oldcanon);
+ return;
+ }
+ printf("Principal \"%s\" renamed to \"%s\".\nMake sure that you have removed \"%s\" from all ACLs before reusing.\n",
+ oldcanon, newcanon, newcanon);
+ return;
+}
+
+void kadmin_cpw(argc, argv)
+ int argc;
+ char *argv[];
+{
+ ovsec_kadm_ret_t retval;
+ static char newpw[1024];
+ static char prompt1[1024], prompt2[1024];
+ char *canon;
+ krb5_principal princ;
+
+ if (argc < 2 || argc > 4) {
+ fprintf(stderr, "change_password: too many arguments\n");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &princ);
+ if (retval) {
+ com_err("change_password", retval, "while parsing principal name");
+ return;
+ }
+ retval = krb5_unparse_name(princ, &canon);
+ if (retval) {
+ com_err("change_password", retval, "while canonicalizing principal");
+ krb5_free_principal(princ);
+ return;
+ }
+ if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) {
+ retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, argv[2]);
+ krb5_free_principal(princ);
+ if (retval) {
+ com_err("change_password", retval,
+ "while changing password for \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ printf("Password for \"%s\" changed.\n", canon);
+ free(canon);
+ return;
+ } else if ((argc == 3) && (strlen(argv[1]) == 8) &&
+ !strcmp("-randkey", argv[1])) {
+ krb5_keyblock *newkey = NULL;
+ retval = ovsec_kadm_randkey_principal(ovsec_hndl, princ, &newkey);
+ krb5_free_principal(princ);
+ if (retval) {
+ com_err("change_password", retval,
+ "while randomizing key for \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ memset(newkey->contents, 0, newkey->length);
+ printf("Key for \"%s\" randomized.\n", canon);
+ free(canon);
+ return;
+ } else if (argc == 2) {
+ int i = sizeof (newpw) - 1;
+
+ sprintf(prompt1, "Enter password for principal \"%.900s\": ",
+ argv[1]);
+ sprintf(prompt2,
+ "Re-enter password for principal \"%.900s\": ",
+ argv[1]);
+ retval = krb5_read_password(prompt1, prompt2,
+ newpw, &i);
+ if (retval) {
+ com_err("change_password", retval,
+ "while reading password for \"%s\".", canon);
+ free(canon);
+ krb5_free_principal(princ);
+ return;
+ }
+ retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, newpw);
+ krb5_free_principal(princ);
+ memset(newpw, 0, sizeof (newpw));
+ if (retval) {
+ com_err("change_password", retval,
+ "while changing password for \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ printf("Password for \"%s\" changed.\n", canon);
+ free(canon);
+ return;
+ }
+ fprintf(stderr, "change_password: bad arguments\n");
+ free(canon);
+ krb5_free_principal(princ);
+ return;
+}
+
+int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, caller)
+ int argc;
+ char *argv[];
+ ovsec_kadm_principal_ent_t oprinc;
+ u_int32 *mask;
+ char **pass, *caller;
+{
+ int i, j;
+ struct timeb now;
+ krb5_error_code retval;
+
+ *mask = 0;
+ *pass = NULL;
+ ftime(&now);
+ for (i = 1; i < argc - 1; i++) {
+ if (strlen(argv[i]) == 7 &&
+ !strcmp("-expire", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->princ_expire_time = get_date(argv[i], now);
+ *mask |= OVSEC_KADM_PRINC_EXPIRE_TIME;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 9 &&
+ !strcmp("-pwexpire", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->pw_expiration = get_date(argv[i], now);
+ *mask |= OVSEC_KADM_PW_EXPIRATION;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 8 &&
+ !strcmp("-maxlife", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->max_life = get_date(argv[i], now) - now.time;
+ *mask |= OVSEC_KADM_MAX_LIFE;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 5 &&
+ !strcmp("-kvno", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->kvno = atoi(argv[i]);
+ *mask |= OVSEC_KADM_KVNO;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 8 &&
+ !strcmp("-policy", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->policy = argv[i];
+ *mask |= OVSEC_KADM_POLICY;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 12 &&
+ !strcmp("-clearpolicy", argv[i])) {
+ oprinc->policy = NULL;
+ *mask |= OVSEC_KADM_POLICY_CLR;
+ continue;
+ }
+ if (strlen(argv[i]) == 3 &&
+ !strcmp("-pw", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ *pass = argv[i];
+ continue;
+ }
+ }
+ for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) {
+ if (strlen(argv[i]) == flags[j].flaglen + 1 &&
+ !strcmp(flags[j].flagname,
+ &argv[i][1] /* strip off leading + or - */)) {
+ if (flags[j].set && argv[i][0] == '-' ||
+ !flags[j].set && argv[i][0] == '+') {
+ oprinc->attributes |= flags[j].theflag;
+ *mask |= OVSEC_KADM_ATTRIBUTES;
+ break;
+ } else if (flags[j].set && argv[i][0] == '+' ||
+ !flags[j].set && argv[i][0] == '-') {
+ oprinc->attributes &= ~flags[j].theflag;
+ *mask |= OVSEC_KADM_ATTRIBUTES;
+ break;
+ } else {
+ return -1;
+ }
+ }
+ }
+ return -1;
+ }
+ if (i != argc - 1) {
+ fprintf(stderr, "%s: parser lost count!\n", caller);
+ return -1;
+ }
+ retval = kadmin_parse_name(argv[i], &oprinc->principal);
+ if (retval) {
+ com_err(caller, retval, "while parsing principal");
+ return -1;
+ }
+ return 0;
+}
+
+void kadmin_addprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ ovsec_kadm_principal_ent_rec princ;
+ u_int32 mask;
+ char *pass, *canon;
+ krb5_error_code retval;
+ static char newpw[1024];
+ static char prompt1[1024], prompt2[1024];
+
+ princ.attributes = 0;
+ if (kadmin_parse_princ_args(argc, argv,
+ &princ, &mask, &pass, "add_principal")) {
+ fprintf(stderr, "add_principal: bad arguments\n");
+ return;
+ }
+ retval = krb5_unparse_name(princ.principal, &canon);
+ if (retval) {
+ com_err("add_principal",
+ retval, "while canonicalizing principal");
+ krb5_free_principal(princ.principal);
+ return;
+ }
+ if (pass == NULL) {
+ int i = sizeof (newpw) - 1;
+
+ sprintf(prompt1, "Enter password for principal \"%.900s\": ",
+ argv[1]);
+ sprintf(prompt2,
+ "Re-enter password for principal \"%.900s\": ",
+ argv[1]);
+ retval = krb5_read_password(prompt1, prompt2,
+ newpw, &i);
+ if (retval) {
+ com_err("add_principal", retval,
+ "while reading password for \"%s\".", canon);
+ free(canon);
+ krb5_free_principal(princ.principal);
+ return;
+ }
+ pass = newpw;
+ }
+ mask |= OVSEC_KADM_PRINCIPAL;
+ retval = ovsec_kadm_create_principal(ovsec_hndl, &princ, mask, pass);
+ krb5_free_principal(princ.principal);
+ if (retval) {
+ com_err("add_principal", retval, "while creating \"%s\".",
+ canon);
+ free(canon);
+ return;
+ }
+ printf("Principal \"%s\" created.\n", canon);
+ free(canon);
+}
+
+void kadmin_modprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ ovsec_kadm_principal_ent_rec princ;
+ u_int32 mask;
+ krb5_error_code retval;
+ char *pass, *canon;
+
+ princ.attributes = 0;
+ if (kadmin_parse_princ_args(argc, argv,
+ &princ, &mask, &pass, "modify_principal")) {
+ fprintf(stderr, "modify_principal: bad arguments\n");
+ return;
+ }
+ retval = krb5_unparse_name(princ.principal, &canon);
+ if (retval) {
+ com_err("modify_principal", retval,
+ "while canonicalizing principal");
+ krb5_free_principal(princ.principal);
+ return;
+ }
+ retval = ovsec_kadm_modify_principal(ovsec_hndl, &princ, mask);
+ if (retval) {
+ com_err("modify_principal", retval, "while modifying \"%s\".",
+ argv[argc - 1]);
+ return;
+ }
+}
+
+void kadmin_getprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ ovsec_kadm_principal_ent_t dprinc;
+ krb5_principal princ;
+ krb5_error_code retval;
+ char *canon, *modcanon;
+ int i;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "get_principal: wrong number of arguments\n");
+ return;
+ }
+ if (argc == 3 &&
+ (strlen(argv[1]) == 6 ? strcmp("-terse", argv[1]) : 1)) {
+ fprintf(stderr, "get_principal: bad arguments\n");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &princ);
+ if (retval) {
+ com_err("get_principal", retval, "while parsing principal");
+ return;
+ }
+ retval = krb5_unparse_name(princ, &canon);
+ if (retval) {
+ com_err("get_principal", retval, "while canonicalizing principal");
+ krb5_free_principal(princ);
+ return;
+ }
+ retval = ovsec_kadm_get_principal(ovsec_hndl, princ, &dprinc);
+ krb5_free_principal(princ);
+ if (retval) {
+ com_err("get_principal", retval, "while retrieving \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ retval = krb5_unparse_name(dprinc->mod_name, &modcanon);
+ if (retval) {
+ com_err("get_principal", retval, "while unparsing modname");
+ ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc);
+ free(canon);
+ return;
+ }
+ if (argc == 2) {
+ printf("Principal: %s\n", canon);
+ printf("Expiration date: %d\n", dprinc->princ_expire_time);
+ printf("Last password change: %d\n", dprinc->last_pwd_change);
+ printf("Password expiration date: %d\n", dprinc->pw_expiration);
+ printf("Maximum life: %d\n", dprinc->max_life);
+ printf("Last modified: by %s\n\ton %d\n",
+ modcanon, dprinc->mod_date);
+ printf("Attributes: ");
+ for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
+ if (dprinc->attributes & (krb5_flags) 1 << i)
+ printf(" %s", prflags[i]);
+ }
+ printf("\n");
+ printf("Key version: %d\n", dprinc->kvno);
+ printf("Master key version: %d\n", dprinc->mkvno);
+ printf("Policy: %s\n", dprinc->policy);
+ } else {
+ printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\n",
+ canon, dprinc->princ_expire_time, dprinc->last_pwd_change,
+ dprinc->pw_expiration, dprinc->max_life, modcanon,
+ dprinc->mod_date, dprinc->attributes, dprinc->kvno,
+ dprinc->mkvno, dprinc->policy);
+ }
+ free(modcanon);
+ ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc);
+ free(canon);
+}
+
+int kadmin_parse_policy_args(argc, argv, policy, mask, caller)
+ int argc;
+ char *argv[];
+ ovsec_kadm_policy_ent_t policy;
+ u_int32 *mask;
+ char *caller;
+{
+ int i;
+ struct timeb now;
+ krb5_error_code retval;
+
+ ftime(&now);
+ *mask = 0;
+ for (i = 1; i < argc - 1; i++) {
+ if (strlen(argv[i]) == 8 &&
+ !strcmp(argv[i], "-maxlife")) {
+ if (++i > argc -2)
+ return -1;
+ else {
+ policy->pw_max_life = get_date(argv[i], now) - now.time;
+ *mask |= OVSEC_KADM_PW_MAX_LIFE;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 8 &&
+ !strcmp(argv[i], "-minlife")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ policy->pw_min_life = get_date(argv[i], now) - now.time;
+ *mask |= OVSEC_KADM_PW_MIN_LIFE;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 10 &&
+ !strcmp(argv[i], "-minlength")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ policy->pw_min_length = atoi(argv[i]);
+ *mask |= OVSEC_KADM_PW_MIN_LENGTH;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 11 &&
+ !strcmp(argv[i], "-minclasses")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ policy->pw_min_classes = atoi(argv[i]);
+ *mask |= OVSEC_KADM_PW_MIN_CLASSES;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 8 &&
+ !strcmp(argv[i], "-history")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ policy->pw_history_num = atoi(argv[i]);
+ *mask |= OVSEC_KADM_PW_HISTORY_NUM;
+ continue;
+ }
+ } else
+ return -1;
+ }
+ if (i != argc -1) {
+ fprintf(stderr, "%s: parser lost count!\n", caller);
+ return -1;
+ } else
+ return 0;
+}
+
+void kadmin_addpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ u_int32 mask;
+ ovsec_kadm_policy_ent_rec policy;
+
+ if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) {
+ fprintf(stderr, "add_policy: bad arguments\n");
+ return;
+ } else {
+ policy.policy = argv[argc - 1];
+ mask |= OVSEC_KADM_POLICY;
+ retval = ovsec_kadm_create_policy(ovsec_hndl, &policy, mask);
+ if (retval) {
+ com_err("add_policy", retval, "while creating policy \"%s\".",
+ policy.policy);
+ return;
+ }
+ }
+ return;
+}
+
+void kadmin_modpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ u_int32 mask;
+ ovsec_kadm_policy_ent_rec policy;
+
+ if (kadmin_parse_policy_args(argc, argv, &policy, &mask,
+ "modify_policy")) {
+ fprintf(stderr, "modify_policy: bad arguments\n");
+ return;
+ } else {
+ policy.policy = argv[argc - 1];
+ retval = ovsec_kadm_modify_policy(ovsec_hndl, &policy, mask);
+ if (retval) {
+ com_err("modify_policy", retval, "while modifying policy \"%s\".",
+ policy.policy);
+ return;
+ }
+ }
+ return;
+}
+
+void kadmin_delpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ char reply[5];
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "delete_policy: wrong number of arguments\n");
+ return;
+ }
+ if (argc == 3 &&
+ (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) {
+ fprintf(stderr, "delete_policy: bad arguments\n");
+ return;
+ }
+ if (argc == 2) {
+ printf("Are you sure you want to delete the policy \"%s\"? (yes/no): ", argv[1]);
+ fgets(reply, sizeof (reply), stdin);
+ if (strcmp("yes\n", reply)) {
+ fprintf(stderr, "Policy \"%s\" not deleted.\n", argv[1]);
+ return;
+ }
+ }
+ retval = ovsec_kadm_delete_policy(ovsec_hndl, argv[argc - 1]);
+ if (retval) {
+ com_err("delete_policy:", retval, "while deleting policy \"%s\"",
+ argv[argc - 1]);
+ return;
+ }
+ return;
+}
+
+void kadmin_getpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ ovsec_kadm_policy_ent_t policy;
+
+ if (argc < 2 || argc > 3) {
+ fprintf(stderr, "get_policy: wrong number of arguments\n");
+ return;
+ }
+ if (argc == 3 &&
+ (strlen(argv[1]) == 6 ? strcmp("-terse", argv[1]) : 1)) {
+ fprintf(stderr, "get_policy: bad arguments\n");
+ return;
+ }
+ retval = ovsec_kadm_get_policy(ovsec_hndl, argv[argc - 1], &policy);
+ if (retval) {
+ com_err("get_policy", retval, "while retrieving policy \"%s\".",
+ argv[argc - 1]);
+ return;
+ }
+ if (argc == 2) {
+ printf("Policy: %s\n", policy->policy);
+ printf("Maximum password life: %d\n", policy->pw_max_life);
+ printf("Minimum password life: %d\n", policy->pw_min_life);
+ printf("Minimum password length: %d\n", policy->pw_min_length);
+ printf("Minimum number of password character classes: %d\n",
+ policy->pw_min_classes);
+ printf("Number of old keys kept: %d\n", policy->pw_history_num);
+ printf("Reference count: %d\n", policy->policy_refcnt);
+ } else {
+ printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ policy->policy, policy->pw_max_life, policy->pw_min_life,
+ policy->pw_min_length, policy->pw_min_classes,
+ policy->pw_history_num, policy->policy_refcnt);
+ }
+ ovsec_kadm_free_policy_ent(ovsec_hndl, policy);
+ return;
+}
+
+kadmin_getprivs(argc, argv)
+ int argc;
+ char *argv[];
+{
+ static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE"};
+ krb5_error_code retval;
+ int i;
+ u_int32 plist;
+
+ if (argc != 1) {
+ fprintf(stderr, "get_privs: bad arguments\n");
+ return;
+ }
+ retval = ovsec_kadm_get_privs(ovsec_hndl, &plist);
+ if (retval) {
+ com_err("get_privs", retval, "while retrieving privileges");
+ return;
+ }
+ printf("current privileges:");
+ for (i = 0; i < sizeof (privs) / sizeof (char *); i++) {
+ if (plist & 1 << i)
+ printf(" %s", privs[i]);
+ }
+ printf("\n");
+ return;
+}
diff --git a/src/kadmin/cli/attic/kadmin_ct.ct b/src/kadmin/cli/attic/kadmin_ct.ct
new file mode 100644
index 000000000..f5a67ed53
--- /dev/null
+++ b/src/kadmin/cli/attic/kadmin_ct.ct
@@ -0,0 +1,67 @@
+# Copyright 1994 by the Massachusetts Institute of Technology.
+# All Rights Reserved.
+#
+# Export of this software from the United States of America may
+# require a specific license from the United States Government.
+# It is the responsibility of any person or organization contemplating
+# export to obtain such a license before exporting.
+#
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission. 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.
+#
+#
+# Command table for kadmin CLI for OVSecure
+#
+
+command_table kadmin_cmds;
+
+request kadmin_addprinc, "Add principal",
+ add_prinicpal, addprinc, ank;
+
+request kadmin_delprinc, "Delete principal",
+ delete_principal, delprinc;
+
+request kadmin_modprinc, "Modify principal",
+ modify_principal, modprinc;
+
+request kadmin_renprinc, "Rename principal",
+ rename_principal, renprinc;
+
+request kadmin_cpw, "Change password",
+ change_password, cpw;
+
+request kadmin_getprinc, "Get principal",
+ get_principal, getprinc;
+
+request kadmin_addpol, "Add policy",
+ add_policy, addpol;
+
+request kadmin_modpol, "Modify policy",
+ modify_policy, modpol;
+
+request kadmin_delpol, "Delete policy",
+ delete_policy, delpol;
+
+request kadmin_getpol, "Get policy",
+ get_policy, getpol;
+
+request kadmin_getprivs, "Get privileges",
+ get_privs, getprivs;
+
+# list_requests is generic -- unrelated to Kerberos
+request ss_list_requests, "List available requests.",
+ list_requests, lr, "?";
+
+request ss_quit, "Exit program.",
+ quit, exit, q;
+
+end;
+
diff --git a/src/kadmin/cli/attic/memmove.c b/src/kadmin/cli/attic/memmove.c
new file mode 100644
index 000000000..abc91e923
--- /dev/null
+++ b/src/kadmin/cli/attic/memmove.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define MEMMOVE
+
+/* based on @(#)bcopy.c 5.11 (Berkeley) 6/21/91 */
+
+#include <krb5/osconf.h>
+#include <krb5/config.h>
+#ifdef USE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+/*
+ * sizeof(word) MUST BE A POWER OF TWO
+ * SO THAT wmask BELOW IS ALL ONES
+ */
+typedef int word; /* "word" used for optimal copy speed */
+
+#define wsize sizeof(word)
+#define wmask (wsize - 1)
+
+/*
+ * Copy a block of memory, handling overlap.
+ * This is the routine that actually implements
+ * (the portable versions of) bcopy, memcpy, and memmove.
+ */
+#ifdef MEMCOPY
+void *
+memcpy(dst0, src0, length)
+#else
+#ifdef MEMMOVE
+void *
+memmove(dst0, src0, length)
+#else
+void
+bcopy(src0, dst0, length)
+#endif
+#endif
+ void *dst0;
+ const void *src0;
+ register size_t length;
+{
+ register char *dst = dst0;
+ register const char *src = src0;
+ register size_t t;
+
+ if (length == 0 || dst == src) /* nothing to do */
+ goto done;
+
+ /*
+ * Macros: loop-t-times; and loop-t-times, t>0
+ */
+#define TLOOP(s) if (t) TLOOP1(s)
+#define TLOOP1(s) do { s; } while (--t)
+
+ if ((unsigned long)dst < (unsigned long)src) {
+ /*
+ * Copy forward.
+ */
+ t = (int)src; /* only need low bits */
+ if ((t | (int)dst) & wmask) {
+ /*
+ * Try to align operands. This cannot be done
+ * unless the low bits match.
+ */
+ if ((t ^ (int)dst) & wmask || length < wsize)
+ t = length;
+ else
+ t = wsize - (t & wmask);
+ length -= t;
+ TLOOP1(*dst++ = *src++);
+ }
+ /*
+ * Copy whole words, then mop up any trailing bytes.
+ */
+ t = length / wsize;
+ TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
+ t = length & wmask;
+ TLOOP(*dst++ = *src++);
+ } else {
+ /*
+ * Copy backwards. Otherwise essentially the same.
+ * Alignment works as before, except that it takes
+ * (t&wmask) bytes to align, not wsize-(t&wmask).
+ */
+ src += length;
+ dst += length;
+ t = (int)src;
+ if ((t | (int)dst) & wmask) {
+ if ((t ^ (int)dst) & wmask || length <= wsize)
+ t = length;
+ else
+ t &= wmask;
+ length -= t;
+ TLOOP1(*--dst = *--src);
+ }
+ t = length / wsize;
+ TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
+ t = length & wmask;
+ TLOOP(*--dst = *--src);
+ }
+done:
+#if defined(MEMCOPY) || defined(MEMMOVE)
+ return (dst0);
+#else
+ return;
+#endif
+}
diff --git a/src/kadmin/cli/attic/setenv.c b/src/kadmin/cli/attic/setenv.c
new file mode 100644
index 000000000..a2432c3d6
--- /dev/null
+++ b/src/kadmin/cli/attic/setenv.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* based on @(#)setenv.c 8.1 (Berkeley) 6/4/93 */
+/* based on @(#)getenv.c 8.1 (Berkeley) 6/4/93 */
+
+#ifndef __STDC__
+#define const
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __P
+#define __P(x) ()
+#endif
+char *__findenv __P((const char *, int *));
+
+/*
+ * setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If rewrite is set, replace any current value.
+ */
+setenv(name, value, rewrite)
+ register const char *name;
+ register const char *value;
+ int rewrite;
+{
+ extern char **environ;
+ static int alloced; /* if allocated space before */
+ register char *c;
+ int l_value, offset;
+
+ if (*value == '=') /* no `=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((c = __findenv(name, &offset))) { /* find if already exists */
+ if (!rewrite)
+ return (0);
+ if (strlen(c) >= l_value) { /* old larger; copy over */
+ while (*c++ = *value++);
+ return (0);
+ }
+ } else { /* create new slot */
+ register int cnt;
+ register char **p;
+
+ for (p = environ, cnt = 0; *p; ++p, ++cnt);
+ if (alloced) { /* just increase size */
+ environ = (char **)realloc((char *)environ,
+ (size_t)(sizeof(char *) * (cnt + 2)));
+ if (!environ)
+ return (-1);
+ }
+ else { /* get new space */
+ alloced = 1; /* copy old entries into it */
+ p = (char **)malloc((size_t)(sizeof(char *) * (cnt + 2)));
+ if (!p)
+ return (-1);
+ memcpy(p, environ, cnt * sizeof(char *));
+ environ = p;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */
+ if (!(environ[offset] = /* name + `=' + value */
+ malloc((size_t)((int)(c - name) + l_value + 2))))
+ return (-1);
+ for (c = environ[offset]; (*c = *name++) && *c != '='; ++c);
+ for (*c++ = '='; *c++ = *value++;);
+ return (0);
+}
+
+/*
+ * unsetenv(name) --
+ * Delete environmental variable "name".
+ */
+void
+unsetenv(name)
+ const char *name;
+{
+ extern char **environ;
+ register char **p;
+ int offset;
+
+ while (__findenv(name, &offset)) /* if set multiple times */
+ for (p = &environ[offset];; ++p)
+ if (!(*p = *(p + 1)))
+ break;
+}
+
+/*
+ * getenv --
+ * Returns ptr to value associated with name, if any, else NULL.
+ */
+char *
+getenv(name)
+ const char *name;
+{
+ int offset;
+
+ return (__findenv(name, &offset));
+}
+
+/*
+ * __findenv --
+ * Returns pointer to value associated with name, if any, else NULL.
+ * Sets offset to be the offset of the name/value combination in the
+ * environmental array, for use by setenv(3) and unsetenv(3).
+ * Explicitly removes '=' in argument name.
+ */
+static char *
+__findenv(name, offset)
+ register const char *name;
+ int *offset;
+{
+ extern char **environ;
+ register int len;
+ register const char *np;
+ register char **p, *c;
+
+ if (name == NULL || environ == NULL)
+ return (NULL);
+ for (np = name; *np && *np != '='; ++np)
+ continue;
+ len = np - name;
+ for (p = environ; (c = *p) != NULL; ++p)
+ if (strncmp(c, name, len) == 0 && c[len] == '=') {
+ *offset = p - environ;
+ return (c + len + 1);
+ }
+ return (NULL);
+}
diff --git a/src/kadmin/cli/attic/ss_wrapper.c b/src/kadmin/cli/attic/ss_wrapper.c
new file mode 100644
index 000000000..f7bbda516
--- /dev/null
+++ b/src/kadmin/cli/attic/ss_wrapper.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright 1994 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. 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.
+ *
+ *
+ * ss wrapper for kadmin
+ */
+
+#include <krb5/krb5.h>
+#include <ss/ss.h>
+#include <stdio.h>
+
+extern ss_request_table kadmin_cmds;
+extern int exit_status;
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *request;
+ krb5_error_code retval;
+ int sci_idx, code = 0;
+
+ request = kadmin_startup(argc, argv);
+ sci_idx = ss_create_invocation("kadmin", "5.0", (char *) NULL,
+ &kadmin_cmds, &retval);
+ if (retval) {
+ ss_perror(sci_idx, retval, "creating invocation");
+ exit(1);
+ }
+ if (request) {
+ (void) ss_execute_line(sci_idx, request, &code);
+ if (code != 0) {
+ ss_perror(sci_idx, code, request);
+ exit_status++;
+ }
+ } else
+ ss_listen(sci_idx, &retval);
+ return quit() ? 1 : exit_status;
+}
diff --git a/src/kadmin/cli/configure.in b/src/kadmin/cli/configure.in
new file mode 100644
index 000000000..713d7d212
--- /dev/null
+++ b/src/kadmin/cli/configure.in
@@ -0,0 +1,20 @@
+AC_INIT(getdate.y)
+WITH_CCOPTS
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_PROG_YACC
+AC_HAVE_HEADERS(unistd.h sys/timeb.h alloca.h)
+AC_HAVE_FUNCS(ftime timezone)
+AC_CHECK_LIB(ndbm,main)
+AC_CHECK_LIB(dbm,main)
+AC_REPLACE_FUNCS([setenv memmove strftime])
+KRB_INCLUDE
+USE_KADMCLNT_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+USE_SS_LIBRARY
+KRB5_LIBRARIES
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/cli/dump.c b/src/kadmin/cli/dump.c
new file mode 100644
index 000000000..2c5e4e753
--- /dev/null
+++ b/src/kadmin/cli/dump.c
@@ -0,0 +1,1485 @@
+/*
+ * admin/edit/dump.c
+ *
+ * Copyright 1990,1991 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.
+ *
+ * Dump a KDC database. This file was originally written to be part
+ * of kdb5_edit but has now been adapted for kadmin.
+ */
+
+#include <stdio.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+
+struct dump_args {
+ char *programname;
+ FILE *ofile;
+ krb5_context context;
+ int verbose;
+};
+
+/* External data */
+extern int exit_status;
+extern krb5_context context;
+extern void *handle;
+
+/* Strings */
+
+static const char k5beta5_dump_header[] = "kdb5_edit load_dump version 2.0\n";
+static const char k5_dump_header[] = "kdb5_edit load_dump version 3.0\n";
+static const char kadm5_dump_header[] = "kadm5 load_dump version 4.0\n";
+
+static const char null_mprinc_name[] = "kdb5_dump@MISSING";
+
+/* Message strings */
+static const char regex_err[] = "%s: regular expression error - %s\n";
+static const char regex_merr[] = "%s: regular expression match error - %s\n";
+static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n";
+static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n";
+static const char nokeys_err[] = "%s: cannot find any standard key for %s\n";
+static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n";
+static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n";
+static const char oflock_error[] = "%s: cannot lock %s (%s)\n";
+static const char dumprec_err[] = "%s: error performing %s dump (%s)\n";
+static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n";
+static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: ";
+static const char read_name_string[] = "name string";
+static const char read_key_type[] = "key type";
+static const char read_key_data[] = "key data";
+static const char read_pr_data1[] = "first set of principal attributes";
+static const char read_mod_name[] = "modifier name";
+static const char read_pr_data2[] = "second set of principal attributes";
+static const char read_salt_data[] = "salt data";
+static const char read_akey_type[] = "alternate key type";
+static const char read_akey_data[] = "alternate key data";
+static const char read_asalt_type[] = "alternate salt type";
+static const char read_asalt_data[] = "alternate salt data";
+static const char read_exp_data[] = "expansion data";
+static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n";
+static const char add_princ_fmt[] = "%s\n";
+static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n";
+static const char read_err_fmt[] = "%s(%d): cannot read %s\n";
+static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n";
+static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n";
+static const char err_line_fmt[] = "%s: error processing line %d of %s\n";
+static const char head_bad_fmt[] = "%s: dump header bad in %s\n";
+static const char read_bytecnt[] = "record byte count";
+static const char read_encdata[] = "encoded data";
+static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n";
+static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n";
+static const char read_nint_data[] = "principal static attributes";
+static const char read_tcontents[] = "tagged data contents";
+static const char read_ttypelen[] = "tagged data type and length";
+static const char read_kcontents[] = "key data contents";
+static const char read_ktypelen[] = "key data type and length";
+static const char read_econtents[] = "extra data contents";
+static const char k5beta5_fmt_name[] = "Kerberos version 5 old format";
+static const char k5beta6_fmt_name[] = "Kerberos version 5 beta 6 format";
+static const char lusage_err_fmt[] = "%s: usage is %s [%s] [%s] [%s] filename dbname\n";
+static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n";
+static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n";
+static const char stdin_name[] = "standard input";
+static const char restfail_fmt[] = "%s: %s restore failed\n";
+static const char close_err_fmt[] = "%s: cannot close database (%s)\n";
+static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n";
+static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n";
+static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n";
+static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n";
+static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n";
+static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n";
+
+static const char oldoption[] = "-old";
+static const char verboseoption[] = "-verbose";
+static const char updateoption[] = "-update";
+static const char dump_tmptrail[] = "~";
+
+/* Can't use krb5_dbe_find_enctype because we have a */
+/* kadm5_principal_ent_t and not a krb5_db_entry */
+static krb5_error_code
+find_enctype(dbentp, enctype, salttype, kentp)
+ kadm5_principal_ent_rec *dbentp;
+ krb5_enctype enctype;
+ krb5_int32 salttype;
+ krb5_key_data **kentp;
+{
+ int i;
+ int maxkvno;
+ krb5_key_data *datap;
+
+ maxkvno = -1;
+ datap = (krb5_key_data *) NULL;
+ for (i=0; i<dbentp->n_key_data; i++) {
+ if ((dbentp->key_data[i].key_data_type[0] == enctype) &&
+ ((dbentp->key_data[i].key_data_type[1] == salttype) ||
+ (salttype < 0))) {
+ maxkvno = dbentp->key_data[i].key_data_kvno;
+ datap = &dbentp->key_data[i];
+ }
+ }
+ if (maxkvno >= 0) {
+ *kentp = datap;
+ return(0);
+ }
+ return(ENOENT);
+}
+
+
+/*
+ * dump_k5beta5_header() - Make a dump header that is recognizable by Kerberos
+ * Version 5 Beta 5 and previous releases.
+ */
+static krb5_error_code
+dump_k5beta5_header(arglist)
+ struct dump_args *arglist;
+{
+ /* The old header consists of the leading string */
+ fprintf(arglist->ofile, k5beta5_dump_header);
+ return(0);
+}
+
+
+/*
+ * dump_k5beta5_iterator() - Dump an entry in a format that is usable
+ * by Kerberos Version 5 Beta 5 and previous
+ * releases.
+ */
+static krb5_error_code
+dump_k5beta5_iterator(ptr, name, entry)
+ krb5_pointer ptr;
+ char *name;
+ kadm5_principal_ent_rec *entry;
+{
+ krb5_error_code retval;
+ struct dump_args *arg;
+ char *mod_name;
+ krb5_tl_data *pwchg;
+ krb5_key_data *pkey, *akey, nullkey;
+ int i;
+
+ /* Initialize */
+ arg = (struct dump_args *) ptr;
+ mod_name = (char *) NULL;
+ memset(&nullkey, 0, sizeof(nullkey));
+
+ /*
+ * Deserialize the modifier record.
+ */
+ mod_name = (char *) NULL;
+ pkey = akey = (krb5_key_data *) NULL;
+
+ /*
+ * Flatten the modifier name.
+ */
+ if ((retval = krb5_unparse_name(arg->context,
+ entry->mod_name,
+ &mod_name)))
+ fprintf(stderr, mname_unp_err, arg->programname,
+ error_message(retval));
+
+ /*
+ * Find the 'primary' key and the 'alternate' key.
+ */
+ if ((retval = find_enctype(entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_NORMAL,
+ &pkey)) &&
+ (retval = find_enctype(entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ &akey))) {
+ fprintf(stderr, nokeys_err, arg->programname, name);
+ krb5_xfree(mod_name);
+ return(retval);
+ }
+
+ /* If we only have one type, then ship it out as the primary. */
+ if (!pkey && akey) {
+ pkey = akey;
+ akey = &nullkey;
+ }
+ else {
+ if (!akey)
+ akey = &nullkey;
+ }
+
+ /*
+ * First put out strings representing the length of the variable
+ * length data in this record, then the name and the primary key type.
+ */
+ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name),
+ strlen(mod_name),
+ (krb5_int32) pkey->key_data_length[0],
+ (krb5_int32) akey->key_data_length[0],
+ (krb5_int32) pkey->key_data_length[1],
+ (krb5_int32) akey->key_data_length[1],
+ name,
+ (krb5_int32) pkey->key_data_type[0]);
+ for (i=0; i<pkey->key_data_length[0]; i++) {
+ fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]);
+ }
+ /*
+ * Second, print out strings representing the standard integer
+ * data in this record.
+ */
+ fprintf(arg->ofile,
+ "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t",
+ (krb5_int32) pkey->key_data_kvno,
+ entry->max_life, entry->max_renewable_life,
+ 1 /* Fake mkvno */, entry->princ_expire_time, entry->pw_expiration,
+ entry->last_pwd_change, entry->last_success, entry->last_failed,
+ entry->fail_auth_count, mod_name, entry->mod_date,
+ entry->attributes, pkey->key_data_type[1]);
+
+ /* Pound out the salt data, if present. */
+ for (i=0; i<pkey->key_data_length[1]; i++) {
+ fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]);
+ }
+ /* Pound out the alternate key type and contents */
+ fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]);
+ for (i=0; i<akey->key_data_length[0]; i++) {
+ fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]);
+ }
+ /* Pound out the alternate salt type and contents */
+ fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]);
+ for (i=0; i<akey->key_data_length[1]; i++) {
+ fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]);
+ }
+ /* Pound out the expansion data. (is null) */
+ for (i=0; i < 8; i++) {
+ fprintf(arg->ofile, "\t%u", 0);
+ }
+ fprintf(arg->ofile, ";\n");
+ /* If we're blabbing, do it */
+ if (arg->verbose)
+ fprintf(stderr, "%s\n", name);
+ krb5_xfree(mod_name);
+
+ return(0);
+}
+
+
+/*
+ * dump_k5beta6_header() - Output the k5beta6 dump header.
+ */
+static krb5_error_code
+dump_k5beta6_header(arglist)
+ struct dump_args *arglist;
+{
+ /* The k5beta6 header consists of the leading string */
+ fprintf(arglist->ofile, k5_dump_header);
+ return(0);
+}
+
+
+/*
+ * dump_k5beta6_iterator() - Output a dump record in k5beta6 format.
+ */
+static krb5_error_code
+dump_k5beta6_iterator(ptr, name, entry)
+ krb5_pointer ptr;
+ char *name;
+ kadm5_principal_ent_rec *entry;
+{
+ krb5_error_code retval = 0;
+ struct dump_args *arg;
+ krb5_tl_data *tlp, *etl;
+ krb5_key_data *kdata;
+ int counter, i, j;
+
+ /* Initialize */
+ arg = (struct dump_args *) ptr;
+
+ /*
+ * We'd like to just blast out the contents as they would appear in
+ * the database so that we can just suck it back in, but it doesn't
+ * lend itself to easy editing.
+ */
+
+ /*
+ * The dump format is as follows:
+ * len strlen(name) n_tl_data n_key_data e_length
+ * name
+ * attributes max_life max_renewable_life expiration
+ * pw_expiration last_success last_failed fail_auth_count
+ * n_tl_data*[type length <contents>]
+ * n_key_data*[ver kvno ver*(type length <contents>)]
+ * <e_data>
+ * Fields which are not encapsulated by angle-brackets are to appear
+ * verbatim. Bracketed fields absence is indicated by a -1 in its
+ * place
+ */
+
+ /*
+ * Make sure that the tagged list is reasonably correct, and find
+ * E_DATA while we're at it.
+ */
+ counter = 0;
+ etl = NULL;
+ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) {
+ if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA)
+ etl = tlp;
+ counter++;
+ }
+
+ if (counter == entry->n_tl_data) {
+ /* Pound out header */
+ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t",
+ KRB5_KDB_V1_BASE_LENGTH + (etl ? etl->tl_data_length : 0),
+ strlen(name),
+ (int) entry->n_tl_data,
+ (int) entry->n_key_data,
+ etl ? etl->tl_data_length : 0,
+ name);
+ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",
+ entry->attributes,
+ entry->max_life,
+ entry->max_renewable_life,
+ entry->princ_expire_time,
+ entry->pw_expiration,
+ entry->last_success,
+ entry->last_failed,
+ entry->fail_auth_count);
+ /* Pound out tagged data. */
+ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) {
+ /* skip E_DATA since it is included later */
+ if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA)
+ continue;
+
+ fprintf(arg->ofile, "%d\t%d\t",
+ (int) tlp->tl_data_type,
+ (int) tlp->tl_data_length);
+ if (tlp->tl_data_length)
+ for (i=0; i<tlp->tl_data_length; i++)
+ fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]);
+ else
+ fprintf(arg->ofile, "%d", -1);
+ fprintf(arg->ofile, "\t");
+ }
+
+ /* Pound out key data */
+ for (counter=0; counter<entry->n_key_data; counter++) {
+ kdata = &entry->key_data[counter];
+ fprintf(arg->ofile, "%d\t%d\t",
+ (int) kdata->key_data_ver,
+ (int) kdata->key_data_kvno);
+ for (i=0; i<kdata->key_data_ver; i++) {
+ fprintf(arg->ofile, "%d\t%d\t",
+ kdata->key_data_type[i],
+ kdata->key_data_length[i]);
+ if (kdata->key_data_length[i])
+ for (j=0; j<kdata->key_data_length[i]; j++)
+ fprintf(arg->ofile, "%02x",
+ kdata->key_data_contents[i][j]);
+ else
+ fprintf(arg->ofile, "%d", -1);
+ fprintf(arg->ofile, "\t");
+ }
+ }
+
+ /* Pound out extra data */
+ if (etl && etl->tl_data_length)
+ for (i=0; i<etl->tl_data_length; i++)
+ fprintf(arg->ofile, "%02x", etl->tl_data_contents[i]);
+ else
+ fprintf(arg->ofile, "%d", -1);
+
+ /* Print trailer */
+ fprintf(arg->ofile, ";\n");
+
+ if (arg->verbose)
+ fprintf(stderr, "%s\n", name);
+ }
+ else {
+ fprintf(stderr, sdump_tl_inc_err,
+ arg->programname, name, counter, (int) entry->n_tl_data);
+ retval = EINVAL;
+ }
+ return(retval);
+}
+
+
+/*
+ * usage is:
+ * dump_db [-old] [-verbose] [filename|- [principals...]]
+ */
+void dump_db(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *f;
+ struct dump_args arglist;
+ int error;
+ char *programname;
+ char *ofile;
+ krb5_error_code kret;
+ krb5_error_code (*dump_iterator) PROTOTYPE((krb5_pointer,
+ char *,
+ kadm5_principal_ent_rec *));
+ krb5_error_code (*dump_header) PROTOTYPE((struct dump_args *));
+ const char * dump_name;
+ int aindex, num, i;
+ krb5_boolean locked;
+ char **princs;
+ kadm5_principal_ent_rec princ_ent;
+ krb5_principal princ;
+
+ /*
+ * Parse the arguments.
+ */
+ programname = argv[0];
+ if (strrchr(programname, (int) '/'))
+ programname = strrchr(argv[0], (int) '/') + 1;
+ ofile = (char *) NULL;
+ error = 0;
+ dump_iterator = dump_k5beta6_iterator;
+ dump_header = dump_k5beta6_header;
+ dump_name = k5beta6_fmt_name;
+ arglist.verbose = 0;
+
+ /*
+ * Parse the qualifiers.
+ */
+ for (aindex = 1; aindex < argc; aindex++) {
+ if (!strcmp(argv[aindex], oldoption)) {
+ dump_iterator = dump_k5beta5_iterator;
+ dump_header = dump_k5beta5_header;
+ dump_name = k5beta5_fmt_name;
+ }
+ else if (!strcmp(argv[aindex], verboseoption)) {
+ arglist.verbose++;
+ }
+ else
+ break;
+ }
+
+ if (aindex < argc) {
+ ofile = argv[aindex];
+ aindex++;
+ }
+
+ /* this works because of the way aindex and argc are used below */
+ if (aindex == argc) {
+ argv[aindex] = "*";
+ argc++;
+ }
+
+ locked = 0;
+ if (ofile) {
+ /*
+ * Make sure that we don't open and truncate on the fopen,
+ * since that may hose an on-going kprop process.
+ *
+ * We could also control this by opening for read and
+ * write, doing an flock with LOCK_EX, and then
+ * truncating the file once we have gotten the lock,
+ * but that would involve more OS dependencies than I
+ * want to get into.
+ */
+ unlink(ofile);
+ if (!(f = fopen(ofile, "w"))) {
+ fprintf(stderr, ofopen_error,
+ programname, ofile, error_message(errno));
+ exit_status++;
+ goto cleanup;
+ }
+ if ((kret = krb5_lock_file(context,
+ fileno(f),
+ KRB5_LOCKMODE_EXCLUSIVE))) {
+ fprintf(stderr, oflock_error,
+ programname, ofile, error_message(kret));
+ exit_status++;
+ goto cleanup;
+ }
+ else
+ locked = 1;
+ } else {
+ f = stdout;
+ }
+
+ arglist.programname = programname;
+ arglist.ofile = f;
+ arglist.context = context;
+
+ if (kret = (*dump_header)(&arglist)) {
+ fprintf(stderr, dumphdr_err,
+ programname, dump_name, error_message(kret));
+ exit_status++;
+ goto cleanup;
+ }
+
+ while (aindex < argc) {
+ if (kret = kadm5_get_principals(handle, argv[aindex],
+ &princs, &num)) {
+ fprintf(stderr, "%s: error retrieving principals "
+ "matching %s: (%s)\n", programname,
+ argv[aindex], error_message(kret));
+ exit_status++;
+ goto cleanup;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (kret = krb5_parse_name(context, princs[i],
+ &princ)) {
+ com_err(programname, kret,
+ "while parsing principal name");
+ exit_status++;
+ break;
+ }
+ if (kret = kadm5_get_principal(handle, princ,
+ &princ_ent,
+ KADM5_PRINCIPAL_NORMAL_MASK |
+ KADM5_KEY_DATA|KADM5_TL_DATA)){
+ com_err(programname, kret,
+ "while retrieving principal entry");
+ krb5_free_principal(context, princ);
+ exit_status++;
+ break;
+ }
+ if (kret = (*dump_iterator)(&arglist, princs[i], &princ_ent)) {
+ exit_status++;
+ krb5_free_principal(context, princ);
+ kadm5_free_principal_ent(handle, &princ_ent);
+ break;
+ }
+
+ krb5_free_principal(context, princ);
+ kadm5_free_principal_ent(handle, &princ_ent);
+ }
+
+ kadm5_free_name_list(handle, princs, num);
+ aindex++;
+ if (kret)
+ goto cleanup;
+ }
+
+cleanup:
+ if (ofile)
+ fclose(f);
+
+ if (locked)
+ (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK);
+}
+
+
+/*
+ * Read a string of bytes while counting the number of lines passed.
+ */
+static int
+read_string(f, buf, len, lp)
+ FILE *f;
+ char *buf;
+ int len;
+ int *lp;
+{
+ int c;
+ int i, retval;
+
+ retval = 0;
+ for (i=0; i<len; i++) {
+ c = (char) fgetc(f);
+ if (c < 0) {
+ retval = 1;
+ break;
+ }
+ if (c == '\n')
+ (*lp)++;
+ buf[i] = (char) c;
+ }
+ buf[len] = '\0';
+ return(retval);
+}
+
+/*
+ * Read a string of two character representations of bytes.
+ */
+static int
+read_octet_string(f, buf, len)
+ FILE *f;
+ krb5_octet *buf;
+ int len;
+{
+ int c;
+ int i, retval;
+
+ retval = 0;
+ for (i=0; i<len; i++) {
+ if (fscanf(f, "%02x", &c) != 1) {
+ retval = 1;
+ break;
+ }
+ buf[i] = (krb5_octet) c;
+ }
+ return(retval);
+}
+
+/*
+ * Find the end of an old format record.
+ */
+static void
+find_record_end(f, fn, lineno)
+ FILE *f;
+ char *fn;
+ int lineno;
+{
+ int ch;
+
+ if (((ch = fgetc(f)) != ';') || ((ch = fgetc(f)) != '\n')) {
+ fprintf(stderr, trash_end_fmt, fn, lineno);
+ while (ch != '\n') {
+ putc(ch, stderr);
+ ch = fgetc(f);
+ }
+ putc(ch, stderr);
+ }
+}
+
+#if 0
+/*
+ * process_k5beta5_record() - Handle a dump record in old format.
+ *
+ * Returns -1 for end of file, 0 for success and 1 for failure.
+ */
+static int
+process_k5beta5_record(fname, context, filep, verbose, linenop)
+ char *fname;
+ krb5_context context;
+ FILE *filep;
+ int verbose;
+ int *linenop;
+{
+ int nmatched;
+ int retval;
+ krb5_db_entry dbent;
+ int name_len, mod_name_len, key_len;
+ int alt_key_len, salt_len, alt_salt_len;
+ char *name;
+ char *mod_name;
+ int tmpint1, tmpint2, tmpint3;
+ int error;
+ const char *try2read;
+ int i;
+ krb5_key_data *pkey, *akey;
+ krb5_timestamp last_pwd_change, mod_date;
+ krb5_principal mod_princ;
+ krb5_error_code kret;
+
+ try2read = (char *) NULL;
+ (*linenop)++;
+ retval = 1;
+ memset((char *)&dbent, 0, sizeof(dbent));
+
+ /* Make sure we've got key_data entries */
+ if (krb5_dbe_create_key_data(context, &dbent) ||
+ krb5_dbe_create_key_data(context, &dbent)) {
+ krb5_db_free_principal(context, &dbent, 1);
+ return(1);
+ }
+ pkey = &dbent.key_data[0];
+ akey = &dbent.key_data[1];
+
+ /*
+ * Match the sizes. 6 tokens to match.
+ */
+ nmatched = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t",
+ &name_len, &mod_name_len, &key_len,
+ &alt_key_len, &salt_len, &alt_salt_len);
+ if (nmatched == 6) {
+ pkey->key_data_length[0] = key_len;
+ akey->key_data_length[0] = alt_key_len;
+ pkey->key_data_length[1] = salt_len;
+ akey->key_data_length[1] = alt_salt_len;
+ name = (char *) NULL;
+ mod_name = (char *) NULL;
+ /*
+ * Get the memory for the variable length fields.
+ */
+ if ((name = (char *) malloc((size_t) (name_len + 1))) &&
+ (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) &&
+ (!key_len ||
+ (pkey->key_data_contents[0] =
+ (krb5_octet *) malloc((size_t) (key_len + 1)))) &&
+ (!alt_key_len ||
+ (akey->key_data_contents[0] =
+ (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) &&
+ (!salt_len ||
+ (pkey->key_data_contents[1] =
+ (krb5_octet *) malloc((size_t) (salt_len + 1)))) &&
+ (!alt_salt_len ||
+ (akey->key_data_contents[1] =
+ (krb5_octet *) malloc((size_t) (alt_salt_len + 1))))
+ ) {
+ error = 0;
+
+ /* Read the principal name */
+ if (read_string(filep, name, name_len, linenop)) {
+ try2read = read_name_string;
+ error++;
+ }
+ /* Read the key type */
+ if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) {
+ try2read = read_key_type;
+ error++;
+ }
+ pkey->key_data_type[0] = tmpint1;
+ /* Read the old format key */
+ if (!error && read_octet_string(filep,
+ pkey->key_data_contents[0],
+ pkey->key_data_length[0])) {
+ try2read = read_key_data;
+ error++;
+ }
+ /* convert to a new format key */
+ /* the encrypted version is stored as the unencrypted key length
+ (4 bytes, MSB first) followed by the encrypted key. */
+ if ((pkey->key_data_length[0] > 4)
+ && (pkey->key_data_contents[0][0] == 0)
+ && (pkey->key_data_contents[0][1] == 0)) {
+ /* this really does look like an old key, so drop and swap */
+ /* the *new* length is 2 bytes, LSB first, sigh. */
+ size_t shortlen = pkey->key_data_length[0]-4+2;
+ char *shortcopy = (krb5_octet *) malloc(shortlen);
+ char *origdata = pkey->key_data_contents[0];
+ shortcopy[0] = origdata[3];
+ shortcopy[1] = origdata[2];
+ memcpy(shortcopy+2,origdata+4,shortlen-2);
+ free(origdata);
+ pkey->key_data_length[0] = shortlen;
+ pkey->key_data_contents[0] = shortcopy;
+ }
+
+ /* Read principal attributes */
+ if (!error && (fscanf(filep,
+ "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t",
+ &tmpint1, &dbent.max_life,
+ &dbent.max_renewable_life,
+ &tmpint2, &dbent.expiration,
+ &dbent.pw_expiration, &last_pwd_change,
+ &dbent.last_success, &dbent.last_failed,
+ &tmpint3) != 10)) {
+ try2read = read_pr_data1;
+ error++;
+ }
+ pkey->key_data_kvno = tmpint1;
+ dbent.fail_auth_count = tmpint3;
+ /* Read modifier name */
+ if (!error && read_string(filep,
+ mod_name,
+ mod_name_len,
+ linenop)) {
+ try2read = read_mod_name;
+ error++;
+ }
+ /* Read second set of attributes */
+ if (!error && (fscanf(filep, "\t%u\t%u\t%u\t",
+ &mod_date, &dbent.attributes,
+ &tmpint1) != 3)) {
+ try2read = read_pr_data2;
+ error++;
+ }
+ pkey->key_data_type[1] = tmpint1;
+ /* Read salt data */
+ if (!error && read_octet_string(filep,
+ pkey->key_data_contents[1],
+ pkey->key_data_length[1])) {
+ try2read = read_salt_data;
+ error++;
+ }
+ /* Read alternate key type */
+ if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) {
+ try2read = read_akey_type;
+ error++;
+ }
+ akey->key_data_type[0] = tmpint1;
+ /* Read alternate key */
+ if (!error && read_octet_string(filep,
+ akey->key_data_contents[0],
+ akey->key_data_length[0])) {
+ try2read = read_akey_data;
+ error++;
+ }
+
+ /* convert to a new format key */
+ /* the encrypted version is stored as the unencrypted key length
+ (4 bytes, MSB first) followed by the encrypted key. */
+ if ((akey->key_data_length[0] > 4)
+ && (akey->key_data_contents[0][0] == 0)
+ && (akey->key_data_contents[0][1] == 0)) {
+ /* this really does look like an old key, so drop and swap */
+ /* the *new* length is 2 bytes, LSB first, sigh. */
+ size_t shortlen = akey->key_data_length[0]-4+2;
+ char *shortcopy = (krb5_octet *) malloc(shortlen);
+ char *origdata = akey->key_data_contents[0];
+ shortcopy[0] = origdata[3];
+ shortcopy[1] = origdata[2];
+ memcpy(shortcopy+2,origdata+4,shortlen-2);
+ free(origdata);
+ akey->key_data_length[0] = shortlen;
+ akey->key_data_contents[0] = shortcopy;
+ }
+
+ /* Read alternate salt type */
+ if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) {
+ try2read = read_asalt_type;
+ error++;
+ }
+ akey->key_data_type[1] = tmpint1;
+ /* Read alternate salt data */
+ if (!error && read_octet_string(filep,
+ akey->key_data_contents[1],
+ akey->key_data_length[1])) {
+ try2read = read_asalt_data;
+ error++;
+ }
+ /* Read expansion data - discard it */
+ if (!error) {
+ for (i=0; i<8; i++) {
+ if (fscanf(filep, "\t%u", &tmpint1) != 1) {
+ try2read = read_exp_data;
+ error++;
+ break;
+ }
+ }
+ if (!error)
+ find_record_end(filep, fname, *linenop);
+ }
+
+ /*
+ * If no error, then we're done reading. Now parse the names
+ * and store the database dbent.
+ */
+ if (!error) {
+ if (!(kret = krb5_parse_name(context,
+ name,
+ &dbent.princ))) {
+ if (!(kret = krb5_parse_name(context,
+ mod_name,
+ &mod_princ))) {
+ if (!(kret =
+ krb5_dbe_update_mod_princ_data(context,
+ &dbent,
+ mod_date,
+ mod_princ)) &&
+ !(kret =
+ krb5_dbe_update_last_pwd_change(context,
+ &dbent,
+ last_pwd_change))) {
+ int one = 1;
+
+ dbent.len = KRB5_KDB_V1_BASE_LENGTH;
+ pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ?
+ 2 : 1;
+ akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ?
+ 2 : 1;
+ if ((pkey->key_data_type[0] ==
+ akey->key_data_type[0]) &&
+ (pkey->key_data_type[1] ==
+ akey->key_data_type[1]))
+ dbent.n_key_data--;
+ else if ((akey->key_data_type[0] == 0)
+ && (akey->key_data_length[0] == 0)
+ && (akey->key_data_type[1] == 0)
+ && (akey->key_data_length[1] == 0))
+ dbent.n_key_data--;
+ if ((kret = krb5_db_put_principal(context,
+ &dbent,
+ &one)) ||
+ (one != 1)) {
+ fprintf(stderr, store_err_fmt,
+ fname, *linenop, name,
+ error_message(kret));
+ error++;
+ }
+ else {
+ if (verbose)
+ fprintf(stderr, add_princ_fmt, name);
+ retval = 0;
+ }
+ dbent.n_key_data = 2;
+ }
+ krb5_free_principal(context, mod_princ);
+ }
+ else {
+ fprintf(stderr, parse_err_fmt,
+ fname, *linenop, mod_name,
+ error_message(kret));
+ error++;
+ }
+ }
+ else {
+ fprintf(stderr, parse_err_fmt,
+ fname, *linenop, name, error_message(kret));
+ error++;
+ }
+ }
+ else {
+ fprintf(stderr, read_err_fmt, fname, *linenop, try2read);
+ }
+ }
+ else {
+ fprintf(stderr, no_mem_fmt, fname, *linenop);
+ }
+
+ krb5_db_free_principal(context, &dbent, 1);
+ if (mod_name)
+ free(mod_name);
+ if (name)
+ free(name);
+ }
+ else {
+ if (nmatched != EOF)
+ fprintf(stderr, rhead_err_fmt, fname, *linenop);
+ else
+ retval = -1;
+ }
+ return(retval);
+}
+
+
+/*
+ * process_k5_record() - Handle a dump record in new format.
+ *
+ * Returns -1 for end of file, 0 for success and 1 for failure.
+ */
+static int
+process_k5_record(fname, context, filep, verbose, linenop)
+ char *fname;
+ krb5_context context;
+ FILE *filep;
+ int verbose;
+ int *linenop;
+{
+ int retval;
+ krb5_db_entry dbentry;
+ krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9;
+ int nread;
+ int error;
+ int i, j, one;
+ char *name;
+ krb5_key_data *kp, *kdatap;
+ krb5_tl_data **tlp, *tl;
+ krb5_octet *op;
+ krb5_error_code kret;
+ const char *try2read;
+
+ try2read = (char *) NULL;
+ memset((char *) &dbentry, 0, sizeof(dbentry));
+ (*linenop)++;
+ retval = 1;
+ name = (char *) NULL;
+ kp = (krb5_key_data *) NULL;
+ op = (krb5_octet *) NULL;
+ error = 0;
+ kret = 0;
+ nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5);
+ if (nread == 5) {
+ /* Get memory for flattened principal name */
+ if (!(name = (char *) malloc((size_t) t2 + 1)))
+ error++;
+
+ /* Get memory for and form tagged data linked list */
+ tlp = &dbentry.tl_data;
+ for (i=0; i<t3; i++) {
+ if ((*tlp = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) {
+ memset(*tlp, 0, sizeof(krb5_tl_data));
+ tlp = &((*tlp)->tl_data_next);
+ dbentry.n_tl_data++;
+ }
+ else {
+ error++;
+ break;
+ }
+ }
+
+ /* Get memory for key list */
+ if (t4 && !(kp = (krb5_key_data *) malloc((size_t)
+ (t4*sizeof(krb5_key_data)))))
+ error++;
+
+ /* Get memory for extra data */
+ if (t5 && !(op = (krb5_octet *) malloc((size_t) t5)))
+ error++;
+
+ if (!error) {
+ dbentry.len = t1;
+ dbentry.n_key_data = t4;
+ dbentry.e_length = t5;
+ if (kp) {
+ memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data)));
+ dbentry.key_data = kp;
+ kp = (krb5_key_data *) NULL;
+ }
+ if (op) {
+ memset(op, 0, (size_t) t5);
+ dbentry.e_data = op;
+ op = (krb5_octet *) NULL;
+ }
+
+ /* Read in and parse the principal name */
+ if (!read_string(filep, name, t2, linenop) &&
+ !(kret = krb5_parse_name(context, name, &dbentry.princ))) {
+
+ /* Get the fixed principal attributes */
+ nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",
+ &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9);
+ if (nread == 8) {
+ dbentry.attributes = (krb5_flags) t2;
+ dbentry.max_life = (krb5_deltat) t3;
+ dbentry.max_renewable_life = (krb5_deltat) t4;
+ dbentry.expiration = (krb5_timestamp) t5;
+ dbentry.pw_expiration = (krb5_timestamp) t6;
+ dbentry.last_success = (krb5_timestamp) t7;
+ dbentry.last_failed = (krb5_timestamp) t8;
+ dbentry.fail_auth_count = (krb5_kvno) t9;
+ } else {
+ try2read = read_nint_data;
+ error++;
+ }
+
+ /* Get the tagged data */
+ if (!error && dbentry.n_tl_data) {
+ for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) {
+ nread = fscanf(filep, "%d\t%d\t", &t1, &t2);
+ if (nread == 2) {
+ tl->tl_data_type = (krb5_int16) t1;
+ tl->tl_data_length = (krb5_int16) t2;
+ if (tl->tl_data_length) {
+ if (!(tl->tl_data_contents =
+ (krb5_octet *) malloc((size_t) t2+1)) ||
+ read_octet_string(filep,
+ tl->tl_data_contents,
+ t2)) {
+ try2read = read_tcontents;
+ error++;
+ break;
+ }
+ }
+ else {
+ /* Should be a null field */
+ nread = fscanf(filep, "%d", &t9);
+ if ((nread != 1) || (t9 != -1)) {
+ error++;
+ try2read = read_tcontents;
+ break;
+ }
+ }
+ }
+ else {
+ try2read = read_ttypelen;
+ error++;
+ break;
+ }
+ }
+ }
+
+ /* Get the key data */
+ if (!error && dbentry.n_key_data) {
+ for (i=0; !error && (i<dbentry.n_key_data); i++) {
+ kdatap = &dbentry.key_data[i];
+ nread = fscanf(filep, "%d\t%d\t", &t1, &t2);
+ if (nread == 2) {
+ kdatap->key_data_ver = (krb5_int16) t1;
+ kdatap->key_data_kvno = (krb5_int16) t2;
+
+ for (j=0; j<t1; j++) {
+ nread = fscanf(filep, "%d\t%d\t", &t3, &t4);
+ if (nread == 2) {
+ kdatap->key_data_type[j] = t3;
+ kdatap->key_data_length[j] = t4;
+ if (t4) {
+ if (!(kdatap->key_data_contents[j] =
+ (krb5_octet *)
+ malloc((size_t) t4+1)) ||
+ read_octet_string(filep,
+ kdatap->key_data_contents[j],
+ t4)) {
+ try2read = read_kcontents;
+ error++;
+ break;
+ }
+ }
+ else {
+ /* Should be a null field */
+ nread = fscanf(filep, "%d", &t9);
+ if ((nread != 1) || (t9 != -1)) {
+ error++;
+ try2read = read_kcontents;
+ break;
+ }
+ }
+ }
+ else {
+ try2read = read_ktypelen;
+ error++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Get the extra data */
+ if (!error && dbentry.e_length) {
+ if (read_octet_string(filep,
+ dbentry.e_data,
+ (int) dbentry.e_length)) {
+ try2read = read_econtents;
+ error++;
+ }
+ }
+ else {
+ nread = fscanf(filep, "%d", &t9);
+ if ((nread != 1) || (t9 != -1)) {
+ error++;
+ try2read = read_econtents;
+ }
+ }
+
+ /* Finally, find the end of the record. */
+ if (!error)
+ find_record_end(filep, fname, *linenop);
+
+ /*
+ * We have either read in all the data or choked.
+ */
+ if (!error) {
+ one = 1;
+ if ((kret = krb5_db_put_principal(context,
+ &dbentry,
+ &one))) {
+ fprintf(stderr, store_err_fmt,
+ fname, *linenop,
+ name, error_message(kret));
+ }
+ else {
+ if (verbose)
+ fprintf(stderr, add_princ_fmt, name);
+ retval = 0;
+ }
+ }
+ else {
+ fprintf(stderr, read_err_fmt, fname, *linenop, try2read);
+ }
+ }
+ else {
+ if (kret)
+ fprintf(stderr, parse_err_fmt,
+ fname, *linenop, name, error_message(kret));
+ else
+ fprintf(stderr, no_mem_fmt, fname, *linenop);
+ }
+ }
+ else {
+ fprintf(stderr, rhead_err_fmt, fname, *linenop);
+ }
+
+ if (op)
+ free(op);
+ if (kp)
+ free(kp);
+ if (name)
+ free(name);
+ krb5_db_free_principal(context, &dbentry, 1);
+ }
+ else {
+ if (nread == EOF)
+ retval = -1;
+ }
+ return(retval);
+}
+
+
+/*
+ * restore_k5beta5_compat() - Restore the database from a K5 Beta
+ * format dump file.
+ */
+static int
+restore_k5beta5_compat(programname, context, dumpfile, f, verbose)
+ const char *programname;
+ krb5_context context;
+ const char *dumpfile;
+ FILE *f;
+ int verbose;
+{
+ int error;
+ int lineno;
+ char buf[2*sizeof(k5beta5_dump_header)];
+
+ /*
+ * Get/check the header.
+ */
+ error = 0;
+ fgets(buf, sizeof(buf), f);
+ if (!strcmp(buf, k5beta5_dump_header)) {
+ lineno = 1;
+ /*
+ * Process the records.
+ */
+ while (!(error = process_k5beta5_record(dumpfile,
+ context,
+ f,
+ verbose,
+ &lineno)))
+ ;
+ if (error != -1)
+ fprintf(stderr, err_line_fmt, programname, lineno, dumpfile);
+ else
+ error = 0;
+
+ /*
+ * Close the input file.
+ */
+ if (f != stdin)
+ fclose(f);
+ }
+ else {
+ fprintf(stderr, head_bad_fmt, programname, dumpfile);
+ error++;
+ }
+ return(error);
+}
+
+
+/*
+ * restore_dump() - Restore the database from a standard dump file.
+ */
+static int
+restore_dump(programname, context, dumpfile, f, verbose)
+ const char *programname;
+ krb5_context context;
+ const char *dumpfile;
+ FILE *f;
+ int verbose;
+{
+ int error;
+ int lineno;
+ char buf[2*sizeof(k5_dump_header)];
+
+ /*
+ * Get/check the header.
+ */
+ error = 0;
+ fgets(buf, sizeof(buf), f);
+ if (!strcmp(buf, k5_dump_header)) {
+ lineno = 1;
+ /*
+ * Process the records.
+ */
+ while (!(error = process_k5_record(dumpfile,
+ context,
+ f,
+ verbose,
+ &lineno)))
+ ;
+ if (error != -1)
+ fprintf(stderr, err_line_fmt, programname, lineno, dumpfile);
+ else
+ error = 0;
+
+ /*
+ * Close the input file.
+ */
+ if (f != stdin)
+ fclose(f);
+ }
+ else {
+ fprintf(stderr, head_bad_fmt, programname, dumpfile);
+ error++;
+ }
+ return(error);
+}
+
+/*
+ * Usage is
+ * load_db [-old] [-verbose] [-update] filename dbname
+ */
+void
+load_db(argc, argv)
+ int argc;
+ char **argv;
+{
+ krb5_error_code kret;
+ krb5_context context;
+ FILE *f;
+ extern char *optarg;
+ extern int optind;
+ const char *programname;
+ const char *dumpfile;
+ char *dbname;
+ char *dbname_tmp;
+ int (*restore_function) PROTOTYPE((const char *,
+ krb5_context,
+ const char *,
+ FILE *,
+ int));
+ const char * restore_name;
+ int update, verbose;
+ int aindex;
+
+ /*
+ * Parse the arguments.
+ */
+ programname = argv[0];
+ if (strrchr(programname, (int) '/'))
+ programname = strrchr(argv[0], (int) '/') + 1;
+ dumpfile = (char *) NULL;
+ dbname = (char *) NULL;
+ restore_function = restore_dump;
+ restore_name = standard_fmt_name;
+ update = 0;
+ verbose = 0;
+ exit_status = 0;
+ dbname_tmp = (char *) NULL;
+ for (aindex = 1; aindex < argc; aindex++) {
+ if (!strcmp(argv[aindex], oldoption)) {
+ restore_function = restore_k5beta5_compat;
+ restore_name = k5beta5_fmt_name;
+ }
+ else if (!strcmp(argv[aindex], verboseoption)) {
+ verbose = 1;
+ }
+ else if (!strcmp(argv[aindex], updateoption)) {
+ update = 1;
+ }
+ else
+ break;
+ }
+ if ((argc - aindex) != 2) {
+ fprintf(stderr, lusage_err_fmt, argv[0], argv[0],
+ oldoption, verboseoption, updateoption);
+ exit_status++;
+ return;
+ }
+
+ dumpfile = argv[aindex];
+ dbname = argv[aindex+1];
+ if (!(dbname_tmp = (char *) malloc(strlen(dbname)+
+ strlen(dump_tmptrail)+1))) {
+ fprintf(stderr, no_name_mem_fmt, argv[0]);
+ exit_status++;
+ return;
+ }
+ strcpy(dbname_tmp, dbname);
+ strcat(dbname_tmp, dump_tmptrail);
+
+ /*
+ * Initialize the Kerberos context and error tables.
+ */
+ if ((kret = krb5_init_context(&context))) {
+ fprintf(stderr, ctx_err_fmt, programname);
+ free(dbname_tmp);
+ exit_status++;
+ return;
+ }
+ krb5_init_ets(context);
+
+ /*
+ * Open the dumpfile
+ */
+ if (dumpfile) {
+ if ((f = fopen(dumpfile, "r+"))) {
+ kret = krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_SHARED);
+ }
+ }
+ else {
+ f = stdin;
+ }
+ if (f && !kret) {
+ /*
+ * Create the new database if not an update restoration.
+ */
+ if (update || !(kret = krb5_db_create(context, dbname_tmp))) {
+ /*
+ * Point ourselves at it.
+ */
+ if (!(kret = krb5_db_set_name(context,
+ (update) ? dbname : dbname_tmp))) {
+ /*
+ * Initialize the database.
+ */
+ if (!(kret = krb5_db_init(context))) {
+ if ((*restore_function)(programname,
+ context,
+ (dumpfile) ? dumpfile : stdin_name,
+ f,
+ verbose)) {
+ fprintf(stderr, restfail_fmt,
+ programname, restore_name);
+ exit_status++;
+ }
+ if ((kret = krb5_db_fini(context))) {
+ fprintf(stderr, close_err_fmt,
+ programname, error_message(kret));
+ exit_status++;
+ }
+ }
+ else {
+ fprintf(stderr, dbinit_err_fmt,
+ programname, error_message(kret));
+ exit_status++;
+ }
+ }
+ else {
+ fprintf(stderr, dbname_err_fmt,
+ programname,
+ (update) ? dbname : dbname_tmp, error_message(kret));
+ exit_status++;
+ }
+ /*
+ * If there was an error and this is not an update, then
+ * destroy the database.
+ */
+ if (!update) {
+ if (exit_status) {
+ if ((kret = kdb5_db_destroy(context, dbname))) {
+ fprintf(stderr, dbdelerr_fmt,
+ programname, dbname_tmp, error_message(kret));
+ exit_status++;
+ }
+ }
+ else {
+ if ((kret = krb5_db_rename(context,
+ dbname_tmp,
+ dbname))) {
+ fprintf(stderr, dbrenerr_fmt,
+ programname, dbname_tmp, dbname,
+ error_message(kret));
+ exit_status++;
+ }
+ }
+ }
+ }
+ else {
+ fprintf(stderr, dbcreaterr_fmt,
+ programname, dbname, error_message(kret));
+ exit_status++;
+ }
+ if (dumpfile) {
+ (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK);
+ fclose(f);
+ }
+ }
+ else {
+ fprintf(stderr, dfile_err_fmt, dumpfile, error_message(errno));
+ exit_status++;
+ }
+ free(dbname_tmp);
+ krb5_free_context(context);
+}
+#endif
diff --git a/src/kadmin/cli/getdate.y b/src/kadmin/cli/getdate.y
new file mode 100644
index 000000000..975a819f5
--- /dev/null
+++ b/src/kadmin/cli/getdate.y
@@ -0,0 +1,1009 @@
+%{
+/*
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+** send any email to Rich.
+**
+** This grammar has nine shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+*/
+/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */
+/* SUPPRESS 288 on yyerrlab *//* Label unused */
+
+#ifdef HAVE_CONFIG_H
+#if defined (emacs) || defined (CONFIG_BROKETS)
+#include <config.h>
+#else
+#include "config.h"
+#endif
+#endif
+#include <string.h>
+
+/* Since the code of getdate.y is not included in the Emacs executable
+ itself, there is no need to #define static in this file. Even if
+ the code were included in the Emacs executable, it probably
+ wouldn't do any harm to #undef it here; this will only cause
+ problems if we try to write to a static variable, which I don't
+ think this code needs to do. */
+#ifdef emacs
+#undef static
+#endif
+
+/* The following block of alloca-related preprocessor directives is here
+ solely to allow compilation by non GNU-C compilers of the C parser
+ produced from this file by old versions of bison. Newer versions of
+ bison include a block similar to this one in bison.simple. */
+
+#ifdef __GNUC__
+#undef alloca
+#define alloca __builtin_alloca
+#else
+#ifdef HAVE_ALLOCA_H
+#include <alloca.h>
+#else
+#ifdef _AIX /* for Bison */
+ #pragma alloca
+#else
+void *alloca ();
+#endif
+#endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* The code at the top of get_date which figures out the offset of the
+ current time zone checks various CPP symbols to see if special
+ tricks are need, but defaults to using the gettimeofday system call.
+ Include <sys/time.h> if that will be used. */
+
+#if defined(vms)
+
+#include <types.h>
+#include <time.h>
+
+#else
+
+#include <sys/types.h>
+
+#ifdef TIME_WITH_SYS_TIME
+#include <sys/time.h>
+#include <time.h>
+#else
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#endif
+
+#ifdef timezone
+#undef timezone /* needed for sgi */
+#endif
+
+#if defined(HAVE_SYS_TIMEB_H)
+#include <sys/timeb.h>
+#else
+/*
+** We use the obsolete `struct timeb' as part of our interface!
+** Since the system doesn't have it, we define it here;
+** our callers must do likewise.
+*/
+struct timeb {
+ time_t time; /* Seconds since the epoch */
+ unsigned short millitm; /* Field not used */
+ short timezone; /* Minutes west of GMT */
+ short dstflag; /* Field not used */
+};
+#endif /* defined(HAVE_SYS_TIMEB_H) */
+
+#endif /* defined(vms) */
+
+#if defined (STDC_HEADERS) || defined (USG)
+#include <string.h>
+#endif
+
+/* Some old versions of bison generate parsers that use bcopy.
+ That loses on systems that don't provide the function, so we have
+ to redefine it here. */
+#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy)
+#define bcopy(from, to, len) memcpy ((to), (from), (len))
+#endif
+
+extern struct tm *gmtime();
+extern struct tm *localtime();
+
+#define yyparse getdate_yyparse
+#define yylex getdate_yylex
+#define yyerror getdate_yyerror
+
+static int yylex ();
+static int yyerror ();
+
+#if !defined(lint) && !defined(SABER)
+static char RCS[] =
+ "$Header$";
+#endif /* !defined(lint) && !defined(SABER) */
+
+
+#define EPOCH 1970
+#define HOUR(x) ((time_t)(x) * 60)
+#define SECSPERDAY (24L * 60L * 60L)
+
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+** Daylight-savings mode: on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+ DSTon, DSToff, DSTmaybe
+} DSTMODE;
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static char *yyInput;
+static DSTMODE yyDSTmode;
+static time_t yyDayOrdinal;
+static time_t yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static time_t yyTimezone;
+static time_t yyDay;
+static time_t yyHour;
+static time_t yyMinutes;
+static time_t yyMonth;
+static time_t yySeconds;
+static time_t yyYear;
+static MERIDIAN yyMeridian;
+static time_t yyRelMonth;
+static time_t yyRelSeconds;
+
+%}
+
+%union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+}
+
+%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
+%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> tMERIDIAN o_merid
+
+%%
+
+spec : /* NULL */
+ | spec item
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ | number
+ ;
+
+time : tUNUMBER tMERIDIAN {
+ yyHour = $1;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = 0;
+ yyMeridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
+ }
+ ;
+
+zone : tZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSToff;
+ }
+ | tDAYZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ |
+ tZONE tDST {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ ;
+
+day : tDAY {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tDAY ',' {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tUNUMBER tDAY {
+ yyDayOrdinal = $1;
+ yyDayNumber = $2;
+ }
+ ;
+
+date : tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ yyYear = $5;
+ }
+ | tUNUMBER tSNUMBER tSNUMBER {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = $1;
+ yyMonth = -$2;
+ yyDay = -$3;
+ }
+ | tUNUMBER tMONTH tSNUMBER {
+ /* e.g. 17-JUN-1992. */
+ yyDay = $1;
+ yyMonth = $2;
+ yyYear = -$3;
+ }
+ | tMONTH tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ yyYear = $4;
+ }
+ | tUNUMBER tMONTH {
+ yyMonth = $2;
+ yyDay = $1;
+ }
+ | tUNUMBER tMONTH tUNUMBER {
+ yyMonth = $2;
+ yyDay = $1;
+ yyYear = $3;
+ }
+ ;
+
+rel : relunit tAGO {
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMonth = -yyRelMonth;
+ }
+ | relunit
+ ;
+
+relunit : tUNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tSNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tMINUTE_UNIT {
+ yyRelSeconds += $1 * 60L;
+ }
+ | tSNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tUNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tSEC_UNIT {
+ yyRelSeconds++;
+ }
+ | tSNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tUNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tMONTH_UNIT {
+ yyRelMonth += $1;
+ }
+ ;
+
+number : tUNUMBER {
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = $1;
+ else {
+ if($1>10000) {
+ yyHaveDate++;
+ yyDay= ($1)%100;
+ yyMonth= ($1/100)%100;
+ yyYear = $1/10000;
+ }
+ else {
+ yyHaveTime++;
+ if ($1 < 100) {
+ yyHour = $1;
+ yyMinutes = 0;
+ }
+ else {
+ yyHour = $1 / 100;
+ yyMinutes = $1 % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ }
+ ;
+
+o_merid : /* NULL */ {
+ $$ = MER24;
+ }
+ | tMERIDIAN {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* Month and day table. */
+static TABLE const MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL }
+};
+
+/* Time units table. */
+static TABLE const UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
+ { "week", tMINUTE_UNIT, 7 * 24 * 60 },
+ { "day", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "hour", tMINUTE_UNIT, 60 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL }
+};
+
+/* Assorted relative-time words. */
+static TABLE const OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL }
+};
+
+/* The timezone table. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE const TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR(1) }, /* Central European */
+ { "met", tZONE, -HOUR(1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR(1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
+ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+ { "kst", tZONE, -HOUR(9) }, /* Korean Standard */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "kdt", tZONE, -HOUR(10) }, /* Korean Daylight */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ { NULL }
+};
+
+/* Military timezone table. */
+static TABLE const MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL }
+};
+
+
+
+
+/* ARGSUSED */
+static int
+yyerror(s)
+ char *s;
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(Hours, Minutes, Seconds, Meridian)
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+}
+
+
+static time_t
+Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode)
+ time_t Month;
+ time_t Day;
+ time_t Year;
+ time_t Hours;
+ time_t Minutes;
+ time_t Seconds;
+ MERIDIAN Meridian;
+ DSTMODE DSTmode;
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ if (Year < EPOCH || Year > 1999
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ return -1;
+
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
+ Julian -= 60 * 60;
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(Start, Future)
+ time_t Start;
+ time_t Future;
+{
+ time_t StartDay;
+ time_t FutureDay;
+
+ StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+ FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(Start, DayOrdinal, DayNumber)
+ time_t Start;
+ time_t DayOrdinal;
+ time_t DayNumber;
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+
+static time_t
+RelativeMonth(Start, RelMonth)
+ time_t Start;
+ time_t RelMonth;
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(buff)
+ char *buff;
+{
+ register char *p;
+ register char *q;
+ register const TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen(buff) == 3)
+ abbrev = 1;
+ else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && isalpha(*buff)) {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+
+static int
+yylex()
+{
+ register char c;
+ register char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for ( ; ; ) {
+ while (isspace(*yyInput))
+ yyInput++;
+
+ if (isdigit(c = *yyInput) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit(*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit(c = *yyInput++); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha(c)) {
+ for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ }
+}
+
+
+#define TM_YEAR_ORIGIN 1900
+
+/* Yield A - B, measured in seconds. */
+static time_t
+difftm(a, b)
+ struct tm *a, *b;
+{
+ int ay = a->tm_year + (TM_YEAR_ORIGIN - 1);
+ int by = b->tm_year + (TM_YEAR_ORIGIN - 1);
+ return
+ (
+ (
+ (
+ /* difference in day of year */
+ a->tm_yday - b->tm_yday
+ /* + intervening leap days */
+ + ((ay >> 2) - (by >> 2))
+ - (ay/100 - by/100)
+ + ((ay/100 >> 2) - (by/100 >> 2))
+ /* + difference in years * 365 */
+ + (time_t)(ay-by) * 365
+ )*24 + (a->tm_hour - b->tm_hour)
+ )*60 + (a->tm_min - b->tm_min)
+ )*60 + (a->tm_sec - b->tm_sec);
+}
+
+time_t
+get_date(p, now)
+ char *p;
+ struct timeb *now;
+{
+ struct tm *tm, gmt;
+ struct timeb ftz;
+ time_t Start;
+ time_t tod;
+
+ yyInput = p;
+ if (now == NULL) {
+ now = &ftz;
+ (void)time(&ftz.time);
+
+ if (! (tm = gmtime (&ftz.time)))
+ return -1;
+ gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
+ ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60;
+ }
+
+ tm = localtime(&now->time);
+ yyYear = tm->tm_year;
+ yyMonth = tm->tm_mon + 1;
+ yyDay = tm->tm_mday;
+ yyTimezone = now->timezone;
+ yyDSTmode = DSTmaybe;
+ yyHour = 0;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMonth = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ if (yyHaveDate || yyHaveTime || yyHaveDay) {
+ Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+ yyMeridian, yyDSTmode);
+ if (Start < 0)
+ return -1;
+ }
+ else {
+ Start = now->time;
+ if (!yyHaveRel)
+ Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
+ }
+
+ Start += yyRelSeconds;
+ Start += RelativeMonth(Start, yyRelMonth);
+
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
+ Start += tod;
+ }
+
+ /* Have to do *something* with a legitimate -1 so it's distinguishable
+ * from the error return value. (Alternately could set errno on error.) */
+ return Start == -1 ? 0 : Start;
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+main(ac, av)
+ int ac;
+ char *av[];
+{
+ char buff[128];
+ time_t d;
+
+ (void)printf("Enter date, or blank line to exit.\n\t> ");
+ (void)fflush(stdout);
+ while (gets(buff) && buff[0]) {
+ d = get_date(buff, (struct timeb *)NULL);
+ if (d == -1)
+ (void)printf("Bad format - couldn't convert.\n");
+ else
+ (void)printf("%s", ctime(&d));
+ (void)printf("\t> ");
+ (void)fflush(stdout);
+ }
+ exit(0);
+ /* NOTREACHED */
+}
+#endif /* defined(TEST) */
diff --git a/src/kadmin/cli/kadmin.1 b/src/kadmin/cli/kadmin.1
new file mode 100644
index 000000000..a8db58334
--- /dev/null
+++ b/src/kadmin/cli/kadmin.1
@@ -0,0 +1,473 @@
+KADMIN(8) USER_COMMANDS KADMIN(8)
+
+NAME
+ kadmin - a command line interface to the Kerberos KADM5
+ administration system
+
+SYNOPSIS
+ kadmin [-r realm] [-p principal] [-q query] [clnt|local args]
+ clnt args: [-p principal] [[-c ccache]|[-k [-t keytab]]]
+ [-w] [-s admin_server[:port]]
+ local args: [-d dbname] [-e \"enc:salt ...\"] [-m]
+
+DESCRIPTION
+ kadmin is a command-line interface to the Kerberos KADM5
+ administration system. It provides for the maintainance of
+ Kerberos principals, KADM5 policies, and service key tables
+ (keytabs). It exists as both a remote client, using Kerberos
+ authentication and an encrypted RPC to operate securely from
+ anywhere on the network, and as a local client intended to run
+ directly on the KDC without Kerberos authentication. The
+ local version provides all of the functionality of the now
+ obsolete kdb5_edit(8) except for database dump and load, which
+ is now provided by the kdb5_util(8) utility.
+
+COMMAND LINE ARGUMENTS
+ If -r is specified, then kadmin will use the specified realm
+ as the default database realm rather than the default realm
+ for the local machine.
+
+ The -q option allows the passing of a request directly to
+ kadmin, which will then exit. This can be useful for writing
+ scripts.
+
+ The remote version authenticates to the KADM5 server using the
+ service kadmin/admin, and therefore needs a client Kerberos
+ principal name as which to authenticate. The -p, -c, and -k
+ are designed to work together to specify which principal as
+ which to authenticate and where the service ticket or
+ password/key for that principal should be obtained. If given
+ the -p option, kadmin will use the specified principal to
+ authenticate. Otherwise, if given -c option then the primary
+ principal name of the ccache is used. Otherwise, if given the
+ -k option, the principal name host/<hostname> is used.
+ Otherwise, kadmin will append "/admin" to the primary
+ principal name of the default ccache, the value of the USER
+ environment variable, or the username as obtained with
+ getpwuid, in order of preference.
+
+ Once kadmin knows the principal name as which to authenticate,
+ it needs to acquire a Kerberos service ticket for the KADM5
+ server. If the -c ccache argument is specified, the ccache
+ should contain a service ticket for the kadmin/admin service;
+ it can be acquired with the kinit(1) program. Otherwise,
+ kadmin requests a new service ticket from the KDC and stores
+ it in its own temporary ccache. If the -k keytab argument is
+ specified, the keytab is used to decrypt the KDC response;
+ otherwise, a password is required. By default, the user is
+ prompted for the password on the TTY. However, if given the
+ -w option, kadmin will use the password provided on the
+ command line instead of prompting for one on the TTY.
+ WARNING! Placing the password for a Kerberos principal with
+ administration access into a shell script is EXTREMELY
+ DANGEROUS and should only be done if you are highly sure that
+ the script will not fall into the wrong hands.
+
+ If given the -d argument, kadmin will use the specified
+ database name instead of the default defined in kdc.conf.
+ Note that specifying a different KDC database name also
+ specifies a different name for the KADM5 policy database and
+ lock file.
+
+ If given the -e argument, kadmin will use the specified list
+ of encryption and salt type tuples instead of the values
+ specified in kdc.conf. This is useful, for example, if you
+ want to create a single principal with a particular key/salt
+ type without affecting any other principals.
+
+ If given the -m argument, kadmin will prompt for the Kerberos
+ master password on the command line instead of attempting to
+ use the stash file.
+
+DATE FORMAT
+ Various commands in kadmin can take a variety of
+ date formats, specifying durations or absolute times.
+ Examples of valid formats are:
+
+ 1 month ago
+ 2 hours ago
+ 400000 seconds ago
+ last year
+ last Monday
+ yesterday
+ a fortnight ago
+ 3/31/92 10:00:07 PST
+ January 23, 1987 10:05pm
+ 22:00 GMT
+
+ Dates which do not have the "ago" specifier default to being
+ absolute dates, unless they appear in a field where a duration
+ is expected. In that case the time specifier will be
+ interpreted as relative. Specifying "ago" on a duration may
+ result in unexpected behaviour.
+
+COMMAND DESCRIPTIONS
+
+add_principal [options] _newprinc_
+ creates the principal _newprinc_, prompting twice for a
+ password. This command requires the "add" privilege. This
+ command has the aliases "addprinc", "ank".
+
+ OPTIONS
+ -salt _salttype_
+ uses the specified salt instead of the default V5 salt
+ for generating the key. Valid values for _salttype_
+ are:
+ full_name (aliases "v5_salt", "normal")
+ name_only
+ realm_only
+ no_salt (alias "v4_salt")
+
+ -expire _expdate_
+ expiration date of the principal
+
+ -pwexpire _pwexpdate_
+ password expiration date
+
+ -maxlife _maxlife_
+ maximum ticket life of the principal
+
+ -kvno _kvno_
+ explicity set the key version number. This is not
+ recommended.
+
+ -policy _policy_
+ policy used by this principal. If no policy is
+ supplied, the principal will default to having no
+ policy, and a warning message will be printed.
+
+ {-|+}allow_tgs_req
+ "-allow_tgs_req" specifies that a TGS request for a
+ ticket for a service ticket for this principal is not
+ permitted. This option is useless for most things.
+ "+allow_tgs_req" clears this flag. The default is
+ "+allow_tgs_req". In effect, "-allow_tgs_req" sets
+ the KRB5_KDB_DISALLOW_TGT_BASED flag on the principal
+ in the database.
+
+ {-|+}allow_tix
+ "-allow_tix" forbids the issuance of any tickets for
+ this principal. "+allow_tix" clears this flag. The
+ default is "+allow_tix". In effect, "-allow_tix" sets
+ the KRB5_KDB_DISALLOW_ALL_TIX flag on the principal in
+ the database.
+
+ {-|+}needchange
+ "+needchange" sets a flag in attributes field to force
+ a password change; "-needchange" clears it. The
+ default is "-needchange". In effect, "+needchange"
+ sets the KRB5_KDB_REQUIRES_PWCHANGE flag on the
+ principal in the database.
+
+ {-|+}password_changing_service
+ "+password_changing_service" sets a flag in the
+ attributes field marking this as a password change
+ service principal (useless for most things).
+ "-password_changing_service" clears the flag. This
+ flag intentionally has a long name. The default is
+ "-password_changing_service". In effect,
+ "+password_changing_service" sets the
+ KRB5_KDB_PWCHANGE_SERVICE flag on the principal in the
+ database.
+
+ -randpass
+ sets the key of the principal to a random value
+
+ -pw _password_
+ sets the key of the principal to the specified string
+ and does not prompt for a password. This is not
+ recommended.
+
+ EXAMPLE
+ kadmin: addprinc tlyu/deity
+ WARNING: no policy specified for "tlyu/deity@ATHENA.MIT.EDU";
+ defaulting to no policy.
+ Enter password for principal tlyu/deity@ATHENA.MIT.EDU:
+ Re-enter password for principal tlyu/deity@ATHENA.MIT.EDU:
+ Principal "tlyu/deity@ATHENA.MIT.EDU" created.
+ kadmin:
+
+ ERRORS
+ KADM5_AUTH_ADD (requires "add" privilege)
+ KADM5_BAD_MASK (shouldn't happen)
+ KADM5_DUP (principal exists already)
+ KADM5_UNK_POLICY (policy does not exist)
+ KADM5_PASS_Q_* (password quality violations)
+
+delete_principal [-force] _principal_
+ deletes the specified principal from the database. This
+ command prompts for deletion, unless the "-force" option is
+ given. This command requires the "delete" privilege. Aliased
+ to "delprinc".
+
+ EXAMPLE
+ kadmin: delprinc mwm_user
+ Are you sure you want to delete the principal
+ "mwm_user@ATHENA.MIT.EDU"? (yes/no): yes
+ Principal "mwm_user@ATHENA.MIT.EDU" deleted.
+ Make sure that you have removed this principal from
+ all ACLs before reusing.
+ kadmin:
+
+ ERRORS
+ KADM5_AUTH_DELETE (reequires "delete" privilege)
+ KADM5_UNK_PRINC (principal does not exist)
+
+modify_principal [options] _principal_
+ modifies the specified principal, changing the fields as
+ specified. The options are as above for "add_principal",
+ except that password changing is forbidden by this command.
+ In addition, the option "-clearpolicy" will remove clear the
+ current policy of a principal. This command requires the
+ "modify" privilege. Aliased to "modprinc".
+
+ ERRORS
+ KADM5_AUTH_MODIFY (requires "modify" privilege)
+ KADM5_UNK_PRINC (principal does not exist)
+ KADM5_UNK_POLICY (policy does not exist)
+ KADM5_BAD_MASK (shouldn't happen)
+
+rename_principal [-force] _old_ _new_
+ rename the principal _old_ to _new_. Prompts for
+ confirmation, unless the "-force" option is given. Requires
+ both the "add" and "delete" privileges. Aliased to
+ "renprinc".
+
+ EXAMPLE
+ kadmin: renprinc tlyutest test0
+ Are you sure you want to rename the principal
+ "tlyutest@ATHENA.MIT.EDU" to
+ "test0@ATHENA.MIT.EDU"? (yes/no): yes
+ Principal "tlyutest@ATHENA.MIT.EDU" renamed to
+ "test0@ATHENA.MIT.EDU".
+ Make sure that you have removed "tlyutest@ATHENA.MIT.EDU" from
+ all ACLs before reusing.
+ kadmin:
+
+ ERRORS
+ KADM5_AUTH_ADD (requires "add" privilege)
+ KADM5_AUTH_DELETE (requires "delete" privilege)
+ KADM5_UNK_PRINC (source principal does not exist)
+ KADM5_DUP (target principal already exists)
+
+change_password [options] _principal_
+ changes the password of _principal_. Prompts for a new
+ password if neither -randpass or -pw is specified. Requires
+ the "modify" privilege, or that the principal that is running
+ the program to be the same as the one changed. Aliased to
+ "cpw".
+
+ OPTIONS
+ -salt _salttype_
+ uses the specified salt instead of the default V5 salt
+ for generating the key. Options are the same as for
+ add_principal.
+
+ -randpass
+ sets the key of the principal to a random value
+
+ -pw _password_
+ set the password to the specified string. Not
+ recommended.
+
+ EXAMPLE
+ kadmin: cpw systest
+ Enter password for principal systest@ATHENA.MIT.EDU:
+ Re-enter password for principal systest@ATHENA.MIT.EDU:
+ Password for systest@ATHENA.MIT.EDU changed.
+ kadmin:
+
+ ERRORS
+ KADM5_AUTH_MODIFY (requires the modify privilege)
+ KADM5_UNK_PRINC (principal does not exist)
+ KADM5_PASS_Q_* (password policy violation errors)
+ KADM5_PADD_REUSE (password is in principal's password istory)
+ KADM5_PASS_TOOSOON (current password minimum life not xpired)
+
+get_principal [-terse] _principal_
+ gets the attributes of _principal_. Requires the "get"
+ privilege, or that the principal that is running the the
+ program to be the same as the one being listed. With the
+ "-terse" option, outputs fields as a quoted tab-separated
+ strings. Alias "getprinc".
+
+ EXAMPLES
+ kadmin: getprinc tlyu/deity
+ Principal: tlyu/deity@ATHENA.MIT.EDU
+ Key version: 3
+ Maximum life: 1 day 00:00:00
+ Maximum renewable life: 7 days 00:00:00
+ Master key version: 1
+ Expires: Mon Jan 18 22:14:07 EDT 2038
+ Password expires: Mon Sep 19 14:40:00 EDT 1994
+ Password last changed: Mon Jan 31 02:06:40 EDT 1994
+ Last modified: by tlyu/admin@ATHENA.MIT.EDU
+ on Wed Jul 13 18:27:08 EDT 1994
+ Attributes: DISALLOW_FORWARDABLE, DISALLOW_PROXIABLE,
+ REQUIRES_HW_AUTH
+ Salt type: DEFAULT
+ kadmin: getprinc systest
+ systest@ATHENA.MIT.EDU 3 86400 604800 1
+ 785926535 753241234 785900000
+ tlyu/admin@ATHENA.MIT.EDU 786100034 0
+ 0
+ kadmin:
+
+ ERRORS
+ KADM5_AUTH_GET (requires the get privilege)
+ KADM5_UNK_PRINC (principal does not exist)
+
+get_principals [expression]
+ Retrieves all or some principal names. _expression_ is a
+ shell-style glob expression that can contain the wild-card
+ characters ?, *, and []'s. All principal names matching the
+ expression are printed. If no expression is provided, the
+ expression "*" is assumed. If the expression does not contain
+ an "@" character, an "@" character followed by the local realm
+ is appended to the expression. Requires the "list" priviledge.
+ Alias "getprincs".
+
+ EXAMPLES
+ kadmin: getprincs test*
+ test3@SECURE-TEST.OV.COM
+ test2@SECURE-TEST.OV.COM
+ test1@SECURE-TEST.OV.COM
+ testuser@SECURE-TEST.OV.COM
+ kadmin:
+
+add_policy [options] _policy_
+ adds the named policy to the policy database. Requires the
+ "add" privilege. Aliased to "addpol".
+
+ OPTIONS
+ -maxlife _time_
+ sets the maximum lifetime of a password
+
+ -minlife _time_
+ sets the minimum lifetime of a password
+
+ -minlength _length_
+ sets the minimum length of a password
+
+ -minclasses _number_
+ sets the minimum number of character classes allowed
+ in a password
+
+ -history _number_
+ sets the number of past keys kept for a principal
+
+ ERRORS
+ KADM5_AUTH_ADD (requires the add privilege)
+ KADM5_DUP (policy already exists)
+
+delete_policy _policy_
+ deletes the named policy. Prompts for confirmation before
+ deletion. The command will fail if the policy is in use by
+ any principals. Requires the "delete" privilege. Alias
+ "delpol".
+
+ EXAMPLE
+ kadmin: del_policy guests
+ Are you sure you want to delete the policy "guests"?
+ (yes/no): yes
+ Policy "guests" deleted.
+ kadmin:
+
+ ERRORS
+ KADM5_AUTH_DELETE (requires the delete privilege)
+ KADM5_UNK_POLICY (policy does not exist)
+ KADM5_POLICY_REF (reference count on policy is not zero)
+
+modify_policy [options] _policy_
+ modifies the named policy. Options are as above for
+ "add_policy". Requires the "modify" privilege". Alias
+ "modpol".
+
+ ERRORS
+ KADM5_AUTH_MODIFY (requires the modify privilege)
+ KADM5_UNK_POLICY (policy does not exist)
+
+get_policy [-terse] _policy_
+ displays the values of the named policy. Requires the "get"
+ privilege. With the "-terse" flag, outputs the fields as
+ quoted strings separated by tabs. Alias "getpol".
+
+ EXAMPLES
+ kadmin: get_policy admin
+ Policy: admin
+ Maximum password life: 180 days 00:00:00
+ Minimum password life: 00:00:00
+ Minimum password length: 6
+ Minimum number of password character classes: 2
+ Number of old keys kept: 5
+ Reference count: 17
+ kadmin: get_policy -terse admin
+ admin 15552000 0 6 2 5 17
+ kadmin:
+
+ ERRORS
+ KADM5_AUTH_GET (requires the get privilege)
+ KADM5_UNK_POLICY (policy does not exist)
+
+get_policies [expression]
+ Retrieves all or some policy names. _expression_ is a
+ shell-style glob expression that can contain the wild-card
+ characters ?, *, and []'s. All policy names matching the
+ expression are printed. If no expression is provided, the
+ expression "*" is assumed. Requires the "list" priviledge.
+ Alias "getpols".
+
+ EXAMPLES
+ kadmin: getpols
+ test-pol
+ dict-only
+ once-a-min
+ test-pol-nopw
+ kadmin: getpols t*
+ test-pol
+ test-pol-nopw
+ kadmin:
+
+ktadd [-k keytab] [-q] [principal | -glob princ-exp] [...]
+ Adds principal or all principals matching princ-exp to a
+ keytab. princ-exp follows the same rules described for the
+ get_principals command. An entry for each of the principal's
+ unique encryption types is added, ignoring multiple keys with
+ the same encryption type but different salt types. If the -k
+ argument is not specified, the default keytab /etc/v5srvtab is
+ used. If the -q option is specified, less verbose status
+ information is displayed.
+
+ The -glob option requires the "list" privilege.
+
+ EXAMPLES
+ kadmin% ktadd -k /krb5/kadmind.keytab kadmin/admin kadmin/changepw
+ kadmin: Entry for principal kadmin/admin@ATHENA.MIT.EDU with
+ kvno 3, encryption type DES-CBC-CRC added to keytab
+ WRFILE:/krb5/kadmind.keytab.
+ kadmin: Entry for principal kadmin/changepw@ATHENA.MIT.EDU
+ with kvno 3, encryption type DES-CBC-CRC added to keytab
+ WRFILE:/krb5/kadmind.keytab.
+ kadmin:
+
+ktremove [-k keytab] [-q] principal [kvno|"all"|"old"]
+ Removes entries for the specified principal from a keytab. If
+ the string "all" is specified, all entries for that principal
+ are removed; if the string "old" is specified, all entries for
+ that principal except those with the highest kvno are removed.
+ Otherwise, the value specified is parsed as an integer, and
+ all entries whose kvno match that integer are removed. If the
+ -k argument is not specifeid, the default keytab /etc/v5srvtab
+ is used. If the -q is specified, less verbose status
+ information is displayed.
+
+ EXAMPLES
+ kadmin: ktremove -k /krb5/kadmind.keytab kadmin/admin
+ kadmin: Entry for principal kadmin/admin with kvno 3 removed
+ from keytab WRFILE:/krb5/kadmind.keytab.
+ kadmin:
+
+SEE ALSO
+ kerberos(1), kdb5_util(8)
+
+
diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c
new file mode 100644
index 000000000..e19383dd9
--- /dev/null
+++ b/src/kadmin/cli/kadmin.c
@@ -0,0 +1,1322 @@
+/*
+ * Copyright 1994 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. 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.
+ *
+ * kadmin.c: base functions for a kadmin command line interface using
+ * the OVSecure library
+ */
+
+#include <krb5.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <math.h>
+#include <unistd.h>
+#include <pwd.h>
+/* #include <sys/timeb.h> */
+#include <time.h>
+
+/* special struct to convert flag names for principals
+ to actual krb5_flags for a principal */
+struct pflag {
+ char *flagname; /* name of flag as typed to CLI */
+ int flaglen; /* length of string (not counting -,+) */
+ krb5_flags theflag; /* actual principal flag to set/clear */
+ int set; /* 0 means clear, 1 means set (on '-') */
+};
+
+static struct pflag flags[] = {
+{"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1},
+{"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1},
+{"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1},
+{"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1},
+{"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1},
+{"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1},
+{"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1},
+{"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0},
+{"requires_hwauth", 15, KRB5_KDB_REQUIRES_HW_AUTH, 0},
+{"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0},
+{"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1},
+{"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 }
+};
+
+static char *prflags[] = {
+ "DISALLOW_POSTDATED", /* 0x00000001 */
+ "DISALLOW_FORWARDABLE", /* 0x00000002 */
+ "DISALLOW_TGT_BASED", /* 0x00000004 */
+ "DISALLOW_RENEWABLE", /* 0x00000008 */
+ "DISALLOW_PROXIABLE", /* 0x00000010 */
+ "DISALLOW_DUP_SKEY", /* 0x00000020 */
+ "DISALLOW_ALL_TIX", /* 0x00000040 */
+ "REQUIRES_PRE_AUTH", /* 0x00000080 */
+ "REQUIRES_HW_AUTH", /* 0x00000100 */
+ "REQUIRES_PWCHANGE", /* 0x00000200 */
+ "UNKNOWN_0x00000400", /* 0x00000400 */
+ "UNKNOWN_0x00000800", /* 0x00000800 */
+ "DISALLOW_SVR", /* 0x00001000 */
+ "PWCHANGE_SERVICE" /* 0x00002000 */
+};
+
+char *getenv();
+struct passwd *getpwuid();
+int exit_status = 0;
+char *def_realm = NULL;
+char *whoami = NULL;
+time_t get_date();
+
+void *handle = NULL;
+krb5_context context;
+char *ccache_name = NULL;
+
+void usage()
+{
+ fprintf(stderr,
+ "Usage: %s [-r realm] [-p principal] [-q query] [clnt|local args]\n"
+ "\tclnt args: [-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]]\n"
+ "\tlocal args: [-d dbname] [-e \"enc:salt ...\"] [-m]\n", whoami);
+ exit(1);
+}
+
+char *strdur(duration)
+ time_t duration;
+{
+ static char out[50];
+ int days, hours, minutes, seconds;
+
+ days = duration / (24 * 3600);
+ duration %= 24 * 3600;
+ hours = duration / 3600;
+ duration %= 3600;
+ minutes = duration / 60;
+ duration %= 60;
+ seconds = duration;
+ sprintf(out, "%d %s %02d:%02d:%02d", days, days == 1 ? "day" : "days",
+ hours, minutes, seconds);
+ return out;
+}
+
+char *strdate(when)
+ krb5_timestamp when;
+{
+ struct tm *tm;
+ static char out[30];
+
+ time_t lcltim = when;
+ tm = localtime(&lcltim);
+ strftime(out, 30, "%a %b %d %H:%M:%S %Z %Y", tm);
+ return out;
+}
+
+/* this is a wrapper to go around krb5_parse_principal so we can set
+ the default realm up properly */
+krb5_error_code kadmin_parse_name(name, principal)
+ char *name;
+ krb5_principal *principal;
+{
+ char *cp, *fullname;
+ krb5_error_code retval;
+
+ /* assumes def_realm is initialized! */
+ fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1);
+ if (fullname == NULL)
+ return ENOMEM;
+ strcpy(fullname, name);
+ cp = strchr(fullname, '@');
+ while (cp) {
+ if (cp - fullname && *(cp - 1) != '\\')
+ break;
+ else
+ cp = strchr(cp, '@');
+ }
+ if (cp == NULL) {
+ strcat(fullname, "@");
+ strcat(fullname, def_realm);
+ }
+ retval = krb5_parse_name(context, fullname, principal);
+ free(fullname);
+ return retval;
+}
+
+char *kadmin_startup(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern krb5_kt_ops krb5_ktf_writable_ops;
+ extern char *optarg;
+ char *princstr = NULL, *keytab_name = NULL, *query = NULL;
+ char *password = NULL;
+ char *luser, *canon, *cp;
+ int optchar, freeprinc = 0, use_keytab = 0;
+ struct passwd *pw;
+ kadm5_ret_t retval;
+ krb5_ccache cc;
+ krb5_principal princ;
+ kadm5_config_params params;
+
+ memset((char *) &params, 0, sizeof(params));
+
+ if (retval = krb5_init_context(&context)) {
+ com_err(whoami, retval, "while initializing krb5 library");
+ exit(1);
+ }
+ krb5_init_ets(context);
+
+ while ((optchar = getopt(argc, argv, "r:p:kq:w:d:s:m:c:t:e:")) != EOF) {
+ switch (optchar) {
+ case 'r':
+ def_realm = optarg;
+ break;
+ case 'p':
+ princstr = optarg;
+ break;
+ case 'c':
+ ccache_name = optarg;
+ break;
+ case 'k':
+ use_keytab++;
+ break;
+ case 't':
+ keytab_name = optarg;
+ break;
+ case 'w':
+ password = optarg;
+ break;
+ case 'q':
+ query = optarg;
+ break;
+ case 'd':
+ params.dbname = optarg;
+ params.mask |= KADM5_CONFIG_DBNAME;
+ break;
+ case 's':
+ params.admin_server = optarg;
+ params.mask |= KADM5_CONFIG_ADMIN_SERVER;
+ break;
+ case 'm':
+ params.mkey_from_kbd = 1;
+ params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
+ break;
+ case 'e':
+ retval = krb5_string_to_keysalts(optarg,
+ ", \t",
+ ":.-",
+ 0,
+ &params.keysalts,
+ &params.num_keysalts);
+ if (retval) {
+ com_err(whoami, retval, "while parsing keysalts %s", optarg);
+ exit(1);
+ }
+ params.mask |= KADM5_CONFIG_ENCTYPES;
+ break;
+ default:
+ usage();
+ }
+ }
+ if ((ccache_name && use_keytab) ||
+ (keytab_name && !use_keytab))
+ usage();
+
+ if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) {
+ if (freeprinc)
+ free(princstr);
+ fprintf(stderr, "%s: unable to get default realm\n", whoami);
+ exit(1);
+ }
+
+ params.mask |= KADM5_CONFIG_REALM;
+ params.realm = def_realm;
+
+ /*
+ * Set cc to an open credentials cache, either specified by the -c
+ * argument or the default.
+ */
+ if (ccache_name == NULL) {
+ if (retval = krb5_cc_default(context, &cc)) {
+ com_err(whoami, retval,
+ "while opening default credentials cache");
+ exit(1);
+ }
+ } else {
+ if (retval = krb5_cc_resolve(context, ccache_name, &cc)) {
+ com_err(whoami, retval,
+ "while opening credentials cache %s", ccache_name);
+ exit(1);
+ }
+ }
+
+ /*
+ * If no principal name is specified: If a ccache was specified
+ * and its primary principal name can be read, it is used, else if
+ * a keytab was specified, the principal name is host/hostname,
+ * otherwise append "/admin" to the primary name of the default
+ * ccache, $USER, or pw_name.
+ *
+ * Gee, 100+ lines to figure out the client principal name. This
+ * should be compressed...
+ */
+
+ if (princstr == NULL) {
+ if (ccache_name != NULL &&
+ !krb5_cc_get_principal(context, cc, &princ)) {
+ if (retval = krb5_unparse_name(context, princ, &princstr)) {
+ com_err(whoami, retval,
+ "while canonicalizing principal name");
+ krb5_free_principal(context, princ);
+ exit(1);
+ }
+ krb5_free_principal(context, princ);
+ freeprinc++;
+ } else if (use_keytab != NULL) {
+ if (retval = krb5_sname_to_principal(context, NULL,
+ "host",
+ KRB5_NT_SRV_HST,
+ &princ)) {
+ com_err(whoami, retval,
+ "creating host service principal");
+ exit(1);
+ }
+ if (retval = krb5_unparse_name(context, princ, &princstr)) {
+ com_err(whoami, retval,
+ "while canonicalizing principal name");
+ krb5_free_principal(context, princ);
+ exit(1);
+ }
+ krb5_free_principal(context, princ);
+ freeprinc++;
+ } else if (!krb5_cc_get_principal(context, cc, &princ)) {
+ char *realm = NULL;
+ if (krb5_unparse_name(context, princ, &canon)) {
+ fprintf(stderr,
+ "%s: unable to canonicalize principal\n", whoami);
+ krb5_free_principal(context, princ);
+ exit(1);
+ }
+ /* strip out realm of principal if it's there */
+ realm = strchr(canon, '@');
+ while (realm) {
+ if (realm - canon && *(realm - 1) != '\\')
+ break;
+ else
+ realm = strchr(realm, '@');
+ }
+ if (realm)
+ *realm++ = '\0';
+ cp = strchr(canon, '/');
+ while (cp) {
+ if (cp - canon && *(cp - 1) != '\\')
+ break;
+ else
+ cp = strchr(cp, '/');
+ }
+ if (cp != NULL)
+ *cp = '\0';
+ princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ +
+ (realm ? 1 + strlen(realm) : 0) + 1);
+ if (princstr == NULL) {
+ fprintf(stderr, "%s: out of memory\n", whoami);
+ exit(1);
+ }
+ strcpy(princstr, canon);
+ strcat(princstr, "/admin");
+ if (realm) {
+ strcat(princstr, "@");
+ strcat(princstr, realm);
+ }
+ free(canon);
+ krb5_free_principal(context, princ);
+ freeprinc++;
+ } else if (luser = getenv("USER")) {
+ princstr = (char *) malloc(strlen(luser) + 7 /* "/admin@" */
+ + strlen(def_realm) + 1);
+ if (princstr == NULL) {
+ fprintf(stderr, "%s: out of memory\n", whoami);
+ exit(1);
+ }
+ strcpy(princstr, luser);
+ strcat(princstr, "/admin");
+ strcat(princstr, "@");
+ strcat(princstr, def_realm);
+ freeprinc++;
+ } else if (pw = getpwuid(getuid())) {
+ princstr = (char *) malloc(strlen(pw->pw_name) + 7 /* "/admin@" */
+ + strlen(def_realm) + 1);
+ if (princstr == NULL) {
+ fprintf(stderr, "%s: out of memory\n", whoami);
+ exit(1);
+ }
+ strcpy(princstr, pw->pw_name);
+ strcat(princstr, "/admin@");
+ strcat(princstr, def_realm);
+ freeprinc++;
+ } else {
+ fprintf(stderr, "%s: unable to figure out a principal name\n",
+ whoami);
+ exit(1);
+ }
+ }
+
+ /*
+ * Initialize the kadm5 connection. If we were given a ccache,
+ * use it. Otherwise, use/prompt for the password.
+ */
+ if (ccache_name)
+ retval = kadm5_init_with_creds(princstr, cc,
+ KADM5_ADMIN_SERVICE,
+ &params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &handle);
+ else if (use_keytab)
+ retval = kadm5_init_with_skey(princstr, keytab_name,
+ KADM5_ADMIN_SERVICE,
+ &params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &handle);
+ else
+ retval = kadm5_init_with_password(princstr, password,
+ KADM5_ADMIN_SERVICE,
+ &params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &handle);
+ if (retval) {
+ com_err(whoami, retval, "while initializing %s interface", whoami);
+ if (retval == KADM5_BAD_CLIENT_PARAMS ||
+ retval == KADM5_BAD_SERVER_PARAMS)
+ usage();
+ exit(1);
+ }
+ if (freeprinc)
+ free(princstr);
+
+ if (retval = krb5_cc_close(context, cc)) {
+ com_err(whoami, retval, "while closing ccache %s",
+ ccache_name);
+ exit(1);
+ }
+
+ /* register the WRFILE keytab type and set it as the default */
+ if (retval = krb5_kt_register(context, &krb5_ktf_writable_ops)) {
+ com_err(whoami, retval,
+ "while registering writable key table functions");
+ exit(1);
+ }
+ {
+#define DEFAULT_KEYTAB "WRFILE:/etc/v5srvtab"
+ extern char *krb5_defkeyname;
+ krb5_defkeyname = DEFAULT_KEYTAB;
+ }
+
+ return query;
+}
+
+int quit()
+{
+ krb5_ccache cc;
+ int retval;
+
+ kadm5_destroy(handle);
+ if (ccache_name != NULL) {
+ fprintf(stderr,
+ "\n\a\a\aAdministration credentials NOT DESTROYED.\n");
+ }
+
+ /* insert more random cleanup here */
+}
+
+void kadmin_delprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ kadm5_ret_t retval;
+ krb5_principal princ;
+ char *canon;
+ char reply[5];
+
+ if (! (argc == 2 ||
+ (argc == 3 && !strcmp("-force", argv[1])))) {
+ fprintf(stderr, "usage: delete_principal [-force] principal\n");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &princ);
+ if (retval) {
+ com_err("delete_principal", retval, "while parsing principal name");
+ return;
+ }
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("delete_principal", retval,
+ "while canonicalizing principal");
+ krb5_free_principal(context, princ);
+ return;
+ }
+ if (argc == 2) {
+ printf("Are you sure you want to delete the principal \"%s\"? (yes/no): ", canon);
+ fgets(reply, sizeof (reply), stdin);
+ if (strcmp("yes\n", reply)) {
+ fprintf(stderr, "Principal \"%s\" not deleted\n", canon);
+ free(canon);
+ krb5_free_principal(context, princ);
+ return;
+ }
+ }
+ retval = kadm5_delete_principal(handle, princ);
+ krb5_free_principal(context, princ);
+ if (retval) {
+ com_err("delete_principal", retval,
+ "while deleteing principal \"%s\"", canon);
+ free(canon);
+ return;
+ }
+ printf("Principal \"%s\" deleted.\nMake sure that you have removed this principal from all ACLs before reusing.\n", canon);
+ free(canon);
+ return;
+}
+
+void kadmin_renprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_principal oldprinc, newprinc;
+ char *oldcanon, *newcanon;
+ char reply[5];
+ kadm5_ret_t retval;
+
+ if (! (argc == 3 ||
+ (argc == 4 && !strcmp("-force", argv[1])))) {
+ fprintf(stderr, "usage: rename_principal [-force] old new\n");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 2], &oldprinc);
+ if (retval) {
+ com_err("rename_principal", retval, "while parsing old principal");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &newprinc);
+ if (retval) {
+ krb5_free_principal(context, oldprinc);
+ com_err("rename_principal", retval, "while parsing new principal");
+ return;
+ }
+ retval = krb5_unparse_name(context, oldprinc, &oldcanon);
+ if (retval) {
+ com_err("rename_principal", retval,
+ "while canonicalizing old principal");
+ krb5_free_principal(context, newprinc);
+ krb5_free_principal(context, oldprinc);
+ return;
+ }
+ retval = krb5_unparse_name(context, newprinc, &newcanon);
+ if (retval) {
+ com_err("rename_principal", retval,
+ "while canonicalizing new principal");
+ free(oldcanon);
+ krb5_free_principal(context, newprinc);
+ krb5_free_principal(context, oldprinc);
+ return;
+ }
+ if (argc == 3) {
+ printf("Are you sure you want to rename the principal \"%s\" to \"%s\"? (yes/no): ",
+ oldcanon, newcanon);
+ fgets(reply, sizeof (reply), stdin);
+ if (strcmp("yes\n", reply)) {
+ fprintf(stderr,
+ "rename_principal: \"%s\" NOT renamed to \"%s\".\n",
+ oldcanon, newcanon);
+ free(newcanon);
+ free(oldcanon);
+ krb5_free_principal(context, newprinc);
+ krb5_free_principal(context, oldprinc);
+ return;
+ }
+ }
+ retval = kadm5_rename_principal(handle, oldprinc, newprinc);
+ krb5_free_principal(context, oldprinc);
+ krb5_free_principal(context, newprinc);
+ if (retval) {
+ com_err("rename_principal", retval,
+ "while renaming \"%s\" to \"%s\".", oldcanon,
+ newcanon);
+ free(newcanon);
+ free(oldcanon);
+ return;
+ }
+ printf("Principal \"%s\" renamed to \"%s\".\nMake sure that you have removed \"%s\" from all ACLs before reusing.\n",
+ oldcanon, newcanon, newcanon);
+ return;
+}
+
+void kadmin_cpw(argc, argv)
+ int argc;
+ char *argv[];
+{
+ kadm5_ret_t retval;
+ static char newpw[1024];
+ static char prompt1[1024], prompt2[1024];
+ char *canon;
+ krb5_principal princ;
+
+ if (argc < 2) {
+ goto usage;
+ }
+
+ retval = kadmin_parse_name(argv[argc - 1], &princ);
+ if (retval) {
+ com_err("change_password", retval, "while parsing principal name");
+ return;
+ }
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("change_password", retval, "while canonicalizing principal");
+ krb5_free_principal(context, princ);
+ return;
+ }
+ if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) {
+ retval = kadm5_chpass_principal(handle, princ, argv[2]);
+ krb5_free_principal(context, princ);
+ if (retval) {
+ com_err("change_password", retval,
+ "while changing password for \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ printf("Password for \"%s\" changed.\n", canon);
+ free(canon);
+ return;
+ } else if ((argc == 3) && (strlen(argv[1]) == 8) &&
+ !strcmp("-randkey", argv[1])) {
+ retval = kadm5_randkey_principal(handle, princ, NULL, NULL);
+ krb5_free_principal(context, princ);
+ if (retval) {
+ com_err("change_password", retval,
+ "while randomizing key for \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ printf("Key for \"%s\" randomized.\n", canon);
+ free(canon);
+ return;
+ } else if (argc == 2) {
+ int i = sizeof (newpw) - 1;
+
+ sprintf(prompt1, "Enter password for principal \"%.900s\": ",
+ argv[1]);
+ sprintf(prompt2,
+ "Re-enter password for principal \"%.900s\": ",
+ argv[1]);
+ retval = krb5_read_password(context, prompt1, prompt2,
+ newpw, &i);
+ if (retval) {
+ com_err("change_password", retval,
+ "while reading password for \"%s\".", canon);
+ free(canon);
+ krb5_free_principal(context, princ);
+ return;
+ }
+ retval = kadm5_chpass_principal(handle, princ, newpw);
+ krb5_free_principal(context, princ);
+ memset(newpw, 0, sizeof (newpw));
+ if (retval) {
+ com_err("change_password", retval,
+ "while changing password for \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ printf("Password for \"%s\" changed.\n", canon);
+ free(canon);
+ return;
+ } else {
+ free(canon);
+ krb5_free_principal(context, princ);
+ usage:
+ fprintf(stderr,
+ "usage: change_password [-randpass] [-pw passowrd] "
+ "principal\n");
+ return;
+ }
+}
+
+int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey, caller)
+ int argc;
+ char *argv[];
+ kadm5_principal_ent_t oprinc;
+ long *mask;
+ char **pass;
+ int *randkey;
+ char *caller;
+{
+ int i, j, attrib_set;
+ time_t date;
+ time_t now;
+ krb5_error_code retval;
+
+ *mask = 0;
+ *pass = NULL;
+ time(&now);
+ *randkey = 0;
+ for (i = 1; i < argc - 1; i++) {
+ attrib_set = 0;
+ if (strlen(argv[i]) == 7 &&
+ !strcmp("-expire", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ date = get_date(argv[i], NULL);
+ oprinc->princ_expire_time = date == (time_t)-1 ? 0 : date;
+ *mask |= KADM5_PRINC_EXPIRE_TIME;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 9 &&
+ !strcmp("-pwexpire", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ date = get_date(argv[i], NULL);
+ oprinc->pw_expiration = date == (time_t)-1 ? 0 : date;
+ *mask |= KADM5_PW_EXPIRATION;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 8 &&
+ !strcmp("-maxlife", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->max_life = get_date(argv[i], NULL) - now;
+ *mask |= KADM5_MAX_LIFE;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 5 &&
+ !strcmp("-kvno", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->kvno = atoi(argv[i]);
+ *mask |= KADM5_KVNO;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 7 &&
+ !strcmp("-policy", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ oprinc->policy = argv[i];
+ *mask |= KADM5_POLICY;
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 12 &&
+ !strcmp("-clearpolicy", argv[i])) {
+ oprinc->policy = NULL;
+ *mask |= KADM5_POLICY_CLR;
+ continue;
+ }
+ if (strlen(argv[i]) == 3 &&
+ !strcmp("-pw", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ *pass = argv[i];
+ continue;
+ }
+ }
+ if (strlen(argv[i]) == 8 &&
+ !strcmp("-randkey", argv[i])) {
+ ++*randkey;
+ continue;
+ }
+ for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) {
+ if (strlen(argv[i]) == flags[j].flaglen + 1 &&
+ !strcmp(flags[j].flagname,
+ &argv[i][1] /* strip off leading + or - */)) {
+ if (flags[j].set && argv[i][0] == '-' ||
+ !flags[j].set && argv[i][0] == '+') {
+ oprinc->attributes |= flags[j].theflag;
+ *mask |= KADM5_ATTRIBUTES;
+ attrib_set++;
+ break;
+ } else if (flags[j].set && argv[i][0] == '+' ||
+ !flags[j].set && argv[i][0] == '-') {
+ oprinc->attributes &= ~flags[j].theflag;
+ *mask |= KADM5_ATTRIBUTES;
+ attrib_set++;
+ break;
+ } else {
+ return -1;
+ }
+ }
+ }
+ if (!attrib_set)
+ return -1; /* nothing was parsed */
+ }
+ if (i != argc - 1) {
+ fprintf(stderr, "%s: parser lost count!\n", caller);
+ return -1;
+ }
+ retval = kadmin_parse_name(argv[i], &oprinc->principal);
+ if (retval) {
+ com_err(caller, retval, "while parsing principal");
+ return -1;
+ }
+ return 0;
+}
+
+void kadmin_addmodprinc_usage(func)
+ char *func;
+{
+ fprintf(stderr, "usage: %s [options] principal\n", func);
+ fprintf(stderr, "\toptions are:\n");
+ fprintf(stderr, "\t\t[-salt salttype] [-expire expdate] [-pwexpire pwexpdate]\n\t\t[-maxlife maxtixlife] [-kvno kvno] [-policy policy]\n\t\t[-randkey] [-pw password] [{+|-}attribute]\n");
+ fprintf(stderr, "\tattributes are:\n");
+ fprintf(stderr, "\t\tallow_tgs_req, allow_tix, needchange, password_changing_service\n");
+}
+
+void kadmin_addprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ kadm5_principal_ent_rec princ;
+ long mask;
+ int randkey = 0;
+ char *pass, *canon;
+ krb5_error_code retval;
+ static char newpw[1024];
+ static char prompt1[1024], prompt2[1024];
+
+ princ.attributes = 0;
+ if (kadmin_parse_princ_args(argc, argv,
+ &princ, &mask, &pass, &randkey,
+ "add_principal")) {
+ kadmin_addmodprinc_usage("add_principal");
+ return;
+ }
+ retval = krb5_unparse_name(context, princ.principal, &canon);
+ if (retval) {
+ com_err("add_principal",
+ retval, "while canonicalizing principal");
+ krb5_free_principal(context, princ.principal);
+ return;
+ }
+ if (randkey) { /* do special stuff if -randkey specified */
+ princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; /* set notix */
+ mask |= KADM5_ATTRIBUTES;
+ pass = "dummy";
+ } else if (pass == NULL) {
+ int i = sizeof (newpw) - 1;
+
+ sprintf(prompt1, "Enter password for principal \"%.900s\": ",
+ canon);
+ sprintf(prompt2,
+ "Re-enter password for principal \"%.900s\": ",
+ canon);
+ retval = krb5_read_password(context, prompt1, prompt2,
+ newpw, &i);
+ if (retval) {
+ com_err("add_principal", retval,
+ "while reading password for \"%s\".", canon);
+ free(canon);
+ krb5_free_principal(context, princ.principal);
+ return;
+ }
+ pass = newpw;
+ }
+ mask |= KADM5_PRINCIPAL;
+ retval = kadm5_create_principal(handle, &princ, mask, pass);
+ if (retval) {
+ com_err("add_principal", retval, "while creating \"%s\".",
+ canon);
+ krb5_free_principal(context, princ.principal);
+ free(canon);
+ return;
+ }
+ if (randkey) { /* more special stuff for -randkey */
+ retval = kadm5_randkey_principal(handle, princ.principal,
+ NULL, NULL);
+ if (retval) {
+ com_err("add_principal", retval,
+ "while randomizing key for \"%s\".", canon);
+ krb5_free_principal(context, princ.principal);
+ free(canon);
+ return;
+ }
+ princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; /* clear notix */
+ mask = KADM5_ATTRIBUTES;
+ retval = kadm5_modify_principal(handle, &princ, mask);
+ if (retval) {
+ com_err("add_principal", retval,
+ "while clearing DISALLOW_ALL_TIX for \"%s\".", canon);
+ krb5_free_principal(context, princ.principal);
+ free(canon);
+ return;
+ }
+ }
+ krb5_free_principal(context, princ.principal);
+ printf("Principal \"%s\" created.\n", canon);
+ free(canon);
+}
+
+void kadmin_modprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ kadm5_principal_ent_rec princ, oldprinc;
+ krb5_principal kprinc;
+ long mask;
+ krb5_error_code retval;
+ char *pass, *canon;
+ int randkey = 0;
+
+ if (argc < 2) {
+ kadmin_addmodprinc_usage("modify_principal");
+ return;
+ }
+
+ retval = kadmin_parse_name(argv[argc - 1], &kprinc);
+ if (retval) {
+ com_err("modify_principal", retval, "while parsing principal");
+ return;
+ }
+ retval = krb5_unparse_name(context, kprinc, &canon);
+ if (retval) {
+ com_err("modify_principal", retval,
+ "while canonicalizing principal");
+ krb5_free_principal(context, kprinc);
+ return;
+ }
+ retval = kadm5_get_principal(handle, kprinc, &oldprinc,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ krb5_free_principal(context, kprinc);
+ if (retval) {
+ com_err("modify_principal", retval, "while getting \"%s\".",
+ canon);
+ free(canon);
+ return;
+ }
+ princ.attributes = oldprinc.attributes;
+ kadm5_free_principal_ent(handle, &oldprinc);
+ retval = kadmin_parse_princ_args(argc, argv,
+ &princ, &mask,
+ &pass, &randkey,
+ "modify_principal");
+ if (retval) {
+ kadmin_addmodprinc_usage("modify_principal");
+ free(canon);
+ return;
+ }
+ if (randkey) {
+ fprintf(stderr, "modify_principal: -randkey not allowed\n");
+ krb5_free_principal(context, princ.principal);
+ free(canon);
+ return;
+ }
+ retval = kadm5_modify_principal(handle, &princ, mask);
+ krb5_free_principal(context, princ.principal);
+ if (retval) {
+ com_err("modify_principal", retval,
+ "while modifying \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ printf("Principal \"%s\" modified.\n", canon);
+ free(canon);
+}
+
+void kadmin_getprinc(argc, argv)
+ int argc;
+ char *argv[];
+{
+ kadm5_principal_ent_rec dprinc;
+ krb5_principal princ;
+ krb5_error_code retval;
+ char *canon, *modcanon;
+ int i;
+
+ if (! (argc == 2 ||
+ (argc == 3 && !strcmp("-terse", argv[1])))) {
+ fprintf(stderr, "usage: get_principal [-terse] principal\n");
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &princ);
+ if (retval) {
+ com_err("get_principal", retval, "while parsing principal");
+ return;
+ }
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("get_principal", retval, "while canonicalizing principal");
+ krb5_free_principal(context, princ);
+ return;
+ }
+ retval = kadm5_get_principal(handle, princ, &dprinc,
+ KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA);
+ krb5_free_principal(context, princ);
+ if (retval) {
+ com_err("get_principal", retval, "while retrieving \"%s\".", canon);
+ free(canon);
+ return;
+ }
+ retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon);
+ if (retval) {
+ com_err("get_principal", retval, "while unparsing modname");
+ kadm5_free_principal_ent(handle, &dprinc);
+ free(canon);
+ return;
+ }
+ if (argc == 2) {
+ printf("Principal: %s\n", canon);
+ printf("Expiration date: %s\n", strdate(dprinc.princ_expire_time));
+ printf("Last password change: %s\n",
+ strdate(dprinc.last_pwd_change));
+ printf("Password expiration date: %s\n",
+ dprinc.pw_expiration ?
+ strdate(dprinc.pw_expiration) : "[none]");
+ printf("Maximum ticket life: %s\n", strdur(dprinc.max_life));
+ printf("Last modified: by %s\n\ton %s\n",
+ modcanon, strdate(dprinc.mod_date));
+ printf("Last successful authentication: %s\n",
+ strdate(dprinc.last_success));
+ printf("Last failed authentication: %s\n",
+ strdate(dprinc.last_failed));
+ printf("Failed password attempts: %d\n",
+ dprinc.fail_auth_count);
+ printf("Number of keys: %d\n", dprinc.n_key_data);
+ for (i = 0; i < dprinc.n_key_data; i++) {
+ krb5_key_data *key_data = &dprinc.key_data[i];
+ char enctype[BUFSIZ], salttype[BUFSIZ];
+
+ if (krb5_enctype_to_string(key_data->key_data_type[0],
+ enctype, sizeof(enctype)))
+ sprintf(enctype, "<Encryption type 0x%x>",
+ key_data->key_data_type[0]);
+ printf("Key: vno %d, %s, ", key_data->key_data_kvno, enctype);
+ if (key_data->key_data_ver > 1) {
+ if (krb5_salttype_to_string(key_data->key_data_type[1],
+ salttype, sizeof(salttype)))
+ sprintf(salttype, "<Salt type 0x%x>",
+ key_data->key_data_type[1]);
+ printf("%s\n", salttype);
+ } else
+ printf("no salt\n");
+ }
+
+ printf("Attributes:");
+ for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) {
+ if (dprinc.attributes & (krb5_flags) 1 << i)
+ printf(" %s", prflags[i]);
+ }
+ printf("\n");
+ printf("Policy: %s\n", dprinc.policy ? dprinc.policy : "[none]");
+ } else {
+ printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\""
+ "\t%d\t%d\t%d\t%d\t%d",
+ canon, dprinc.princ_expire_time, dprinc.last_pwd_change,
+ dprinc.pw_expiration, dprinc.max_life, modcanon,
+ dprinc.mod_date, dprinc.attributes, dprinc.kvno,
+ dprinc.mkvno, dprinc.policy,
+ dprinc.max_renewable_life, dprinc.last_success,
+ dprinc.last_failed, dprinc.fail_auth_count,
+ dprinc.n_key_data);
+ for (i = 0; i < dprinc.n_key_data; i++)
+ printf("\t%d\t%d\t%d\t%d",
+ dprinc.key_data[i].key_data_ver,
+ dprinc.key_data[i].key_data_kvno,
+ dprinc.key_data[i].key_data_type[0],
+ dprinc.key_data[i].key_data_type[1]);
+ printf("\n");
+ }
+ free(modcanon);
+ kadm5_free_principal_ent(handle, &dprinc);
+ free(canon);
+}
+
+void kadmin_getprincs(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ char *exp, **names;
+ int i, count;
+
+ exp = NULL;
+ if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) {
+ fprintf(stderr, "usage: get_principals [expression]\n");
+ return;
+ }
+ retval = kadm5_get_principals(handle, exp, &names, &count);
+ if (retval) {
+ com_err("get_principals", retval, "while retrieving list.");
+ return;
+ }
+ for (i = 0; i < count; i++)
+ printf("%s\n", names[i]);
+ kadm5_free_name_list(handle, names, count);
+}
+
+int kadmin_parse_policy_args(argc, argv, policy, mask, caller)
+ int argc;
+ char *argv[];
+ kadm5_policy_ent_t policy;
+ long *mask;
+ char *caller;
+{
+ int i;
+ time_t now;
+ time_t date;
+ krb5_error_code retval;
+
+ time(&now);
+ *mask = 0;
+ for (i = 1; i < argc - 1; i++) {
+ if (strlen(argv[i]) == 8 &&
+ !strcmp(argv[i], "-maxlife")) {
+ if (++i > argc -2)
+ return -1;
+ else {
+ date = get_date(argv[i], NULL);
+ policy->pw_max_life =
+ (date == (time_t)-1 ? 0 : date) - now;
+ *mask |= KADM5_PW_MAX_LIFE;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 8 &&
+ !strcmp(argv[i], "-minlife")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ date = get_date(argv[i], NULL);
+ policy->pw_min_life =
+ (date == (time_t)-1 ? 0 : date) - now;
+ *mask |= KADM5_PW_MIN_LIFE;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 10 &&
+ !strcmp(argv[i], "-minlength")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ policy->pw_min_length = atoi(argv[i]);
+ *mask |= KADM5_PW_MIN_LENGTH;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 11 &&
+ !strcmp(argv[i], "-minclasses")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ policy->pw_min_classes = atoi(argv[i]);
+ *mask |= KADM5_PW_MIN_CLASSES;
+ continue;
+ }
+ } else if (strlen(argv[i]) == 8 &&
+ !strcmp(argv[i], "-history")) {
+ if (++i > argc - 2)
+ return -1;
+ else {
+ policy->pw_history_num = atoi(argv[i]);
+ *mask |= KADM5_PW_HISTORY_NUM;
+ continue;
+ }
+ } else
+ return -1;
+ }
+ if (i != argc -1) {
+ fprintf(stderr, "%s: parser lost count!\n", caller);
+ return -1;
+ } else
+ return 0;
+}
+
+void kadmin_addmodpol_usage(func)
+ char *func;
+{
+ fprintf(stderr, "usage; %s [options] policy\n", func);
+ fprintf(stderr, "\toptions are:\n");
+ fprintf(stderr, "\t\t[-maxlife time] [-minlife time] [-minlength length]\n\t\t[-minclasses number] [-history number]\n");
+}
+
+void kadmin_addpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ long mask;
+ kadm5_policy_ent_rec policy;
+
+ if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) {
+ kadmin_addmodpol_usage("add_policy");
+ return;
+ } else {
+ policy.policy = argv[argc - 1];
+ mask |= KADM5_POLICY;
+ retval = kadm5_create_policy(handle, &policy, mask);
+ if (retval) {
+ com_err("add_policy", retval, "while creating policy \"%s\".",
+ policy.policy);
+ return;
+ }
+ }
+ return;
+}
+
+void kadmin_modpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ long mask;
+ kadm5_policy_ent_rec policy;
+
+ if (kadmin_parse_policy_args(argc, argv, &policy, &mask,
+ "modify_policy")) {
+ kadmin_addmodpol_usage("modify_policy");
+ return;
+ } else {
+ policy.policy = argv[argc - 1];
+ retval = kadm5_modify_policy(handle, &policy, mask);
+ if (retval) {
+ com_err("modify_policy", retval, "while modifying policy \"%s\".",
+ policy.policy);
+ return;
+ }
+ }
+ return;
+}
+
+void kadmin_delpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ char reply[5];
+
+ if (! (argc == 2 ||
+ (argc == 3 && !strcmp("-force", argv[1])))) {
+ fprintf(stderr, "usage: delete_policy [-force] policy\n");
+ return;
+ }
+ if (argc == 2) {
+ printf("Are you sure you want to delete the policy \"%s\"? (yes/no): ", argv[1]);
+ fgets(reply, sizeof (reply), stdin);
+ if (strcmp("yes\n", reply)) {
+ fprintf(stderr, "Policy \"%s\" not deleted.\n", argv[1]);
+ return;
+ }
+ }
+ retval = kadm5_delete_policy(handle, argv[argc - 1]);
+ if (retval) {
+ com_err("delete_policy:", retval, "while deleting policy \"%s\"",
+ argv[argc - 1]);
+ return;
+ }
+ return;
+}
+
+void kadmin_getpol(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ kadm5_policy_ent_rec policy;
+
+ if (! (argc == 2 ||
+ (argc == 3 && !strcmp("-terse", argv[1])))) {
+ fprintf(stderr, "usage: get_policy [-terse] policy\n");
+ return;
+ }
+ retval = kadm5_get_policy(handle, argv[argc - 1], &policy);
+ if (retval) {
+ com_err("get_policy", retval, "while retrieving policy \"%s\".",
+ argv[argc - 1]);
+ return;
+ }
+ if (argc == 2) {
+ printf("Policy: %s\n", policy.policy);
+ printf("Maximum password life: %d\n", policy.pw_max_life);
+ printf("Minimum password life: %d\n", policy.pw_min_life);
+ printf("Minimum password length: %d\n", policy.pw_min_length);
+ printf("Minimum number of password character classes: %d\n",
+ policy.pw_min_classes);
+ printf("Number of old keys kept: %d\n", policy.pw_history_num);
+ printf("Reference count: %d\n", policy.policy_refcnt);
+ } else {
+ printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n",
+ policy.policy, policy.pw_max_life, policy.pw_min_life,
+ policy.pw_min_length, policy.pw_min_classes,
+ policy.pw_history_num, policy.policy_refcnt);
+ }
+ kadm5_free_policy_ent(handle, &policy);
+ return;
+}
+
+void kadmin_getpols(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ char *exp, **names;
+ int i, count;
+
+ exp = NULL;
+ if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) {
+ fprintf(stderr, "usage: get_policies [expression]\n");
+ return;
+ }
+ retval = kadm5_get_policies(handle, exp, &names, &count);
+ if (retval) {
+ com_err("get_policies", retval, "while retrieving list.");
+ return;
+ }
+ for (i = 0; i < count; i++)
+ printf("%s\n", names[i]);
+ kadm5_free_name_list(handle, names, count);
+}
+
+kadmin_getprivs(argc, argv)
+ int argc;
+ char *argv[];
+{
+ static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE"};
+ krb5_error_code retval;
+ int i;
+ long plist;
+
+ if (argc != 1) {
+ fprintf(stderr, "usage: get_privs\n");
+ return;
+ }
+ retval = kadm5_get_privs(handle, &plist);
+ if (retval) {
+ com_err("get_privs", retval, "while retrieving privileges");
+ return;
+ }
+ printf("current privileges:");
+ for (i = 0; i < sizeof (privs) / sizeof (char *); i++) {
+ if (plist & 1 << i)
+ printf(" %s", privs[i]);
+ }
+ printf("\n");
+ return;
+}
diff --git a/src/kadmin/cli/kadmin_ct.ct b/src/kadmin/cli/kadmin_ct.ct
new file mode 100644
index 000000000..d22229bf0
--- /dev/null
+++ b/src/kadmin/cli/kadmin_ct.ct
@@ -0,0 +1,79 @@
+# Copyright 1994 by the Massachusetts Institute of Technology.
+# All Rights Reserved.
+#
+# Export of this software from the United States of America may
+# require a specific license from the United States Government.
+# It is the responsibility of any person or organization contemplating
+# export to obtain such a license before exporting.
+#
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission. 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.
+#
+#
+# Command table for kadmin CLI for OVSecure
+#
+
+command_table kadmin_cmds;
+
+request kadmin_addprinc, "Add principal",
+ add_prinicpal, addprinc, ank;
+
+request kadmin_delprinc, "Delete principal",
+ delete_principal, delprinc;
+
+request kadmin_modprinc, "Modify principal",
+ modify_principal, modprinc;
+
+request kadmin_renprinc, "Rename principal",
+ rename_principal, renprinc;
+
+request kadmin_cpw, "Change password",
+ change_password, cpw;
+
+request kadmin_getprinc, "Get principal",
+ get_principal, getprinc;
+
+request kadmin_getprincs, "Get principals",
+ get_principals, getprincs;
+
+request kadmin_addpol, "Add policy",
+ add_policy, addpol;
+
+request kadmin_modpol, "Modify policy",
+ modify_policy, modpol;
+
+request kadmin_delpol, "Delete policy",
+ delete_policy, delpol;
+
+request kadmin_getpol, "Get policy",
+ get_policy, getpol;
+
+request kadmin_getpols, "Get policies",
+ get_policies, getpols;
+
+request kadmin_getprivs, "Get privileges",
+ get_privs, getprivs;
+
+request kadmin_keytab_add, "Add entry(s) to a keytab",
+ ktadd, xst;
+
+request kadmin_keytab_remove, "Remove entry(s) from a keytab",
+ ktremove, ktrem;
+
+# list_requests is generic -- unrelated to Kerberos
+request ss_list_requests, "List available requests.",
+ list_requests, lr, "?";
+
+request ss_quit, "Exit program.",
+ quit, exit, q;
+
+end;
+
diff --git a/src/kadmin/cli/keytab.c b/src/kadmin/cli/keytab.c
new file mode 100644
index 000000000..4b51140b5
--- /dev/null
+++ b/src/kadmin/cli/keytab.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <krb5.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+
+static int add_principal(void *handle, char *keytab_str, krb5_keytab keytab,
+ char *princ_str);
+static int remove_principal(char *keytab_str, krb5_keytab keytab, char
+ *princ_str, char *kvno_str);
+static char *etype_string(krb5_enctype enctype);
+
+extern char *krb5_defkeyname;
+extern char *whoami;
+extern krb5_context context;
+extern void *handle;
+static int quiet;
+
+void add_usage()
+{
+ fprintf(stderr, "Usage: ktadd [-k[eytab] keytab] [-q] [principal | -glob princ-exp] [...]\n");
+}
+
+void rem_usage()
+{
+ fprintf(stderr, "Usage: ktremove [-k[eytab] keytab] [-q] principal [kvno|\"all\"|\"old\"]\n");
+}
+
+int process_keytab(krb5_context context, char **keytab_str,
+ krb5_keytab *keytab)
+{
+ int code;
+
+ if (*keytab_str == NULL) {
+ if (! (*keytab_str = strdup(krb5_defkeyname))) {
+ com_err(whoami, ENOMEM, "while creating keytab name");
+ return 1;
+ }
+ code = krb5_kt_default(context, keytab);
+ if (code != 0) {
+ com_err(whoami, code, "while opening default keytab");
+ free(*keytab_str);
+ return 1;
+ }
+ } else {
+ if (strchr(*keytab_str, ':') != NULL) {
+ *keytab_str = strdup(*keytab_str);
+ if (*keytab_str == NULL) {
+ com_err(whoami, ENOMEM, "while creating keytab name");
+ return 1;
+ }
+ } else {
+ char *tmp = *keytab_str;
+
+ *keytab_str = (char *)
+ malloc(strlen("WRFILE:")+strlen(tmp)+1);
+ if (*keytab_str == NULL) {
+ com_err(whoami, ENOMEM, "while creating keytab name");
+ return 1;
+ }
+ sprintf(*keytab_str, "WRFILE:%s", tmp);
+ }
+
+ code = krb5_kt_resolve(context, *keytab_str, keytab);
+ if (code != 0) {
+ com_err(whoami, code, "while resolving keytab %s", *keytab_str);
+ free(keytab_str);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+void kadmin_keytab_add(int argc, char **argv)
+{
+ krb5_keytab keytab = 0;
+ char *princ_str, *keytab_str = NULL, **princs;
+ int code, num, i;
+
+ argc--; argv++;
+ while (argc) {
+ if (strncmp(*argv, "-k", 2) == 0) {
+ argc--; argv++;
+ if (!argc || keytab_str) {
+ add_usage();
+ return;
+ }
+ keytab_str = *argv;
+ } else if (strcmp(*argv, "-q") == 0) {
+ quiet++;
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (argc == 0) {
+ add_usage();
+ return;
+ }
+
+ if (process_keytab(context, &keytab_str, &keytab))
+ return;
+
+ while (*argv) {
+ if (strcmp(*argv, "-glob") == 0) {
+ if (*++argv == NULL) {
+ add_usage();
+ break;
+ }
+
+ if (code = kadm5_get_principals(handle, *argv, &princs, &num)) {
+ com_err(whoami, code, "while expanding expression \"%s\".",
+ *argv);
+ argv++;
+ continue;
+ }
+
+ for (i = 0; i < num; i++)
+ (void) add_principal(handle, keytab_str, keytab,
+ princs[i]);
+ kadm5_free_name_list(handle, princs, num);
+ } else
+ (void) add_principal(handle, keytab_str, keytab, *argv);
+ argv++;
+ }
+
+ code = krb5_kt_close(context, keytab);
+ if (code != 0)
+ com_err(whoami, code, "while closing keytab");
+
+ free(keytab_str);
+}
+
+void kadmin_keytab_remove(int argc, char **argv)
+{
+ krb5_keytab keytab = 0;
+ char *princ_str, *keytab_str = NULL;
+ int code;
+
+ argc--; argv++;
+ while (argc) {
+ if (strncmp(*argv, "-k", 2) == 0) {
+ argc--; argv++;
+ if (!argc || keytab_str) {
+ rem_usage();
+ return;
+ }
+ keytab_str = *argv;
+ } else if (strcmp(*argv, "-q") == 0) {
+ quiet++;
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (argc != 1 && argc != 2) {
+ rem_usage();
+ return;
+ }
+ if (process_keytab(context, &keytab_str, &keytab))
+ return;
+
+ (void) remove_principal(keytab_str, keytab, argv[0], argv[1]);
+
+ code = krb5_kt_close(context, keytab);
+ if (code != 0)
+ com_err(whoami, code, "while closing keytab");
+
+ free(keytab_str);
+}
+
+int add_principal(void *handle, char *keytab_str, krb5_keytab keytab,
+ char *princ_str)
+{
+ kadm5_principal_ent_rec princ_rec;
+ krb5_principal princ;
+ krb5_keytab_entry new_entry;
+ krb5_keyblock *keys;
+ int code, code2, mask, nkeys, i;
+
+ (void) memset((char *)&princ_rec, 0, sizeof(princ_rec));
+
+ princ = NULL;
+ keys = NULL;
+ nkeys = 0;
+
+ code = krb5_parse_name(context, princ_str, &princ);
+ if (code != 0) {
+ com_err(whoami, code, "while parsing -add principal name %s",
+ princ_str);
+ goto cleanup;
+ }
+
+ code = kadm5_randkey_principal(handle, princ, &keys, &nkeys);
+ if (code != 0) {
+ if (code == KADM5_UNK_PRINC) {
+ fprintf(stderr, "%s: Principal %s does not exist.\n",
+ whoami, princ_str);
+ } else
+ com_err(whoami, code, "while changing %s's key",
+ princ_str);
+ goto cleanup;
+ }
+
+ code = kadm5_get_principal(handle, princ, &princ_rec,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ if (code != 0) {
+ com_err(whoami, code, "while retrieving principal");
+ goto cleanup;
+ }
+
+ for (i = 0; i < nkeys; i++) {
+ memset((char *) &new_entry, 0, sizeof(new_entry));
+ new_entry.principal = princ;
+ new_entry.key = keys[i];
+ new_entry.vno = princ_rec.kvno;
+
+ code = krb5_kt_add_entry(context, keytab, &new_entry);
+ if (code != 0) {
+ com_err(whoami, code, "while adding key to keytab");
+ (void) kadm5_free_principal_ent(handle, &princ_rec);
+ goto cleanup;
+ }
+
+ if (!quiet)
+ printf("%s: Entry for principal %s with kvno %d, "
+ "encryption type %s added to keytab %s.\n",
+ whoami, princ_str, princ_rec.kvno,
+ etype_string(keys[i].enctype), keytab_str);
+ }
+
+ code = kadm5_free_principal_ent(handle, &princ_rec);
+ if (code != 0) {
+ com_err(whoami, code, "while freeing principal entry");
+ goto cleanup;
+ }
+
+cleanup:
+ if (nkeys) {
+ for (i = 0; i < nkeys; i++)
+ krb5_free_keyblock(context, &keys[i]);
+ free(keys);
+ }
+ if (princ)
+ krb5_free_principal(context, princ);
+
+ return code;
+}
+
+int remove_principal(char *keytab_str, krb5_keytab keytab, char
+ *princ_str, char *kvno_str)
+{
+ krb5_principal princ;
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
+ enum { UNDEF, SPEC, HIGH, ALL, OLD } mode;
+ int code, kvno, did_something;
+
+ code = krb5_parse_name(context, princ_str, &princ);
+ if (code != 0) {
+ com_err(whoami, code, "while parsing principal name %s",
+ princ_str);
+ return code;
+ }
+
+ mode = UNDEF;
+ if (kvno_str == NULL) {
+ mode = HIGH;
+ kvno = 0;
+ } else if (strcmp(kvno_str, "all") == 0) {
+ mode = ALL;
+ kvno = 0;
+ } else if (strcmp(kvno_str, "old") == 0) {
+ mode = OLD;
+ kvno = 0;
+ } else {
+ mode = SPEC;
+ kvno = atoi(kvno_str);
+ }
+
+ /* kvno is set to specified value for SPEC, 0 otherwise */
+ code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry);
+ if (code != 0) {
+ if (code == ENOENT) {
+ fprintf(stderr, "%s: Keytab %s does not exist.\n",
+ whoami, keytab_str);
+ } else if (code == KRB5_KT_NOTFOUND) {
+ if (mode != SPEC)
+ fprintf(stderr, "%s: No entry for principal "
+ "%s exists in keytab %s\n",
+ whoami, princ_str, keytab_str);
+ else
+ fprintf(stderr, "%s: No entry for principal "
+ "%s with kvno %d exists in keytab "
+ "%s.\n", whoami, princ_str, kvno,
+ keytab_str);
+ } else {
+ com_err(whoami, code, "while retrieving highest kvno "
+ "from keytab");
+ }
+ return code;
+ }
+
+ /* set kvno to spec'ed value for SPEC, highest kvno otherwise */
+ kvno = entry.vno;
+ krb5_kt_free_entry(context, &entry);
+
+ code = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (code != 0) {
+ com_err(whoami, code, "while starting keytab scan");
+ return code;
+ }
+
+ did_something = 0;
+ while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
+ if (krb5_principal_compare(context, princ, entry.principal) &&
+ ((mode == ALL) ||
+ (mode == SPEC && entry.vno == kvno) ||
+ (mode == OLD && entry.vno != kvno) ||
+ (mode == HIGH && entry.vno == kvno))) {
+
+ /*
+ * Ack! What a kludge... the scanning functions lock
+ * the keytab so entries cannot be removed while they
+ * are operating.
+ */
+ code = krb5_kt_end_seq_get(context, keytab, &cursor);
+ if (code != 0) {
+ com_err(whoami, code, "while temporarily ending "
+ "keytab scan");
+ return code;
+ }
+ code = krb5_kt_remove_entry(context, keytab, &entry);
+ if (code != 0) {
+ com_err(whoami, code, "while deleting entry from keytab");
+ return code;
+ }
+ code = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (code != 0) {
+ com_err(whoami, code, "while restarting keytab scan");
+ return code;
+ }
+
+ did_something++;
+ if (!quiet)
+ printf("%s: Entry for principal %s with kvno %d "
+ "removed from keytab %s.\n", whoami,
+ princ_str, entry.vno, keytab_str);
+ }
+ krb5_kt_free_entry(context, &entry);
+ }
+ if (code && code != KRB5_KT_END) {
+ com_err(whoami, code, "while scanning keytab");
+ return code;
+ }
+ if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) {
+ com_err(whoami, code, "while ending keytab scan");
+ return code;
+ }
+
+ /*
+ * If !did_someting then mode must be OLD or we would have
+ * already returned with an error. But check it anyway just to
+ * prevent unexpected error messages...
+ */
+ if (!did_something && mode == OLD) {
+ fprintf(stderr, "%s: There is only one entry for principal "
+ "%s in keytab %s\n", whoami, princ_str, keytab_str);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * etype_string(enctype): return a string representation of the
+ * encryption type. XXX copied from klist.c; this should be a
+ * library function, or perhaps just #defines
+ */
+static char *etype_string(enctype)
+ krb5_enctype enctype;
+{
+ static char buf[12];
+
+ switch (enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ return "DES-CBC-CRC";
+ break;
+ case ENCTYPE_DES_CBC_MD4:
+ return "DES-CBC-MD4";
+ break;
+ case ENCTYPE_DES_CBC_MD5:
+ return "DES-CBC-MD5";
+ break;
+#if 0
+ case ENCTYPE_DES3_CBC_MD5:
+ return "DES3-CBC-MD5";
+ break;
+#endif
+ default:
+ sprintf(buf, "etype %d", enctype);
+ return buf;
+ break;
+ }
+}
diff --git a/src/kadmin/cli/memmove.c b/src/kadmin/cli/memmove.c
new file mode 100644
index 000000000..abc91e923
--- /dev/null
+++ b/src/kadmin/cli/memmove.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define MEMMOVE
+
+/* based on @(#)bcopy.c 5.11 (Berkeley) 6/21/91 */
+
+#include <krb5/osconf.h>
+#include <krb5/config.h>
+#ifdef USE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+
+/*
+ * sizeof(word) MUST BE A POWER OF TWO
+ * SO THAT wmask BELOW IS ALL ONES
+ */
+typedef int word; /* "word" used for optimal copy speed */
+
+#define wsize sizeof(word)
+#define wmask (wsize - 1)
+
+/*
+ * Copy a block of memory, handling overlap.
+ * This is the routine that actually implements
+ * (the portable versions of) bcopy, memcpy, and memmove.
+ */
+#ifdef MEMCOPY
+void *
+memcpy(dst0, src0, length)
+#else
+#ifdef MEMMOVE
+void *
+memmove(dst0, src0, length)
+#else
+void
+bcopy(src0, dst0, length)
+#endif
+#endif
+ void *dst0;
+ const void *src0;
+ register size_t length;
+{
+ register char *dst = dst0;
+ register const char *src = src0;
+ register size_t t;
+
+ if (length == 0 || dst == src) /* nothing to do */
+ goto done;
+
+ /*
+ * Macros: loop-t-times; and loop-t-times, t>0
+ */
+#define TLOOP(s) if (t) TLOOP1(s)
+#define TLOOP1(s) do { s; } while (--t)
+
+ if ((unsigned long)dst < (unsigned long)src) {
+ /*
+ * Copy forward.
+ */
+ t = (int)src; /* only need low bits */
+ if ((t | (int)dst) & wmask) {
+ /*
+ * Try to align operands. This cannot be done
+ * unless the low bits match.
+ */
+ if ((t ^ (int)dst) & wmask || length < wsize)
+ t = length;
+ else
+ t = wsize - (t & wmask);
+ length -= t;
+ TLOOP1(*dst++ = *src++);
+ }
+ /*
+ * Copy whole words, then mop up any trailing bytes.
+ */
+ t = length / wsize;
+ TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize);
+ t = length & wmask;
+ TLOOP(*dst++ = *src++);
+ } else {
+ /*
+ * Copy backwards. Otherwise essentially the same.
+ * Alignment works as before, except that it takes
+ * (t&wmask) bytes to align, not wsize-(t&wmask).
+ */
+ src += length;
+ dst += length;
+ t = (int)src;
+ if ((t | (int)dst) & wmask) {
+ if ((t ^ (int)dst) & wmask || length <= wsize)
+ t = length;
+ else
+ t &= wmask;
+ length -= t;
+ TLOOP1(*--dst = *--src);
+ }
+ t = length / wsize;
+ TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src);
+ t = length & wmask;
+ TLOOP(*--dst = *--src);
+ }
+done:
+#if defined(MEMCOPY) || defined(MEMMOVE)
+ return (dst0);
+#else
+ return;
+#endif
+}
diff --git a/src/kadmin/cli/setenv.c b/src/kadmin/cli/setenv.c
new file mode 100644
index 000000000..a2432c3d6
--- /dev/null
+++ b/src/kadmin/cli/setenv.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1987, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* based on @(#)setenv.c 8.1 (Berkeley) 6/4/93 */
+/* based on @(#)getenv.c 8.1 (Berkeley) 6/4/93 */
+
+#ifndef __STDC__
+#define const
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __P
+#define __P(x) ()
+#endif
+char *__findenv __P((const char *, int *));
+
+/*
+ * setenv --
+ * Set the value of the environmental variable "name" to be
+ * "value". If rewrite is set, replace any current value.
+ */
+setenv(name, value, rewrite)
+ register const char *name;
+ register const char *value;
+ int rewrite;
+{
+ extern char **environ;
+ static int alloced; /* if allocated space before */
+ register char *c;
+ int l_value, offset;
+
+ if (*value == '=') /* no `=' in value */
+ ++value;
+ l_value = strlen(value);
+ if ((c = __findenv(name, &offset))) { /* find if already exists */
+ if (!rewrite)
+ return (0);
+ if (strlen(c) >= l_value) { /* old larger; copy over */
+ while (*c++ = *value++);
+ return (0);
+ }
+ } else { /* create new slot */
+ register int cnt;
+ register char **p;
+
+ for (p = environ, cnt = 0; *p; ++p, ++cnt);
+ if (alloced) { /* just increase size */
+ environ = (char **)realloc((char *)environ,
+ (size_t)(sizeof(char *) * (cnt + 2)));
+ if (!environ)
+ return (-1);
+ }
+ else { /* get new space */
+ alloced = 1; /* copy old entries into it */
+ p = (char **)malloc((size_t)(sizeof(char *) * (cnt + 2)));
+ if (!p)
+ return (-1);
+ memcpy(p, environ, cnt * sizeof(char *));
+ environ = p;
+ }
+ environ[cnt + 1] = NULL;
+ offset = cnt;
+ }
+ for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */
+ if (!(environ[offset] = /* name + `=' + value */
+ malloc((size_t)((int)(c - name) + l_value + 2))))
+ return (-1);
+ for (c = environ[offset]; (*c = *name++) && *c != '='; ++c);
+ for (*c++ = '='; *c++ = *value++;);
+ return (0);
+}
+
+/*
+ * unsetenv(name) --
+ * Delete environmental variable "name".
+ */
+void
+unsetenv(name)
+ const char *name;
+{
+ extern char **environ;
+ register char **p;
+ int offset;
+
+ while (__findenv(name, &offset)) /* if set multiple times */
+ for (p = &environ[offset];; ++p)
+ if (!(*p = *(p + 1)))
+ break;
+}
+
+/*
+ * getenv --
+ * Returns ptr to value associated with name, if any, else NULL.
+ */
+char *
+getenv(name)
+ const char *name;
+{
+ int offset;
+
+ return (__findenv(name, &offset));
+}
+
+/*
+ * __findenv --
+ * Returns pointer to value associated with name, if any, else NULL.
+ * Sets offset to be the offset of the name/value combination in the
+ * environmental array, for use by setenv(3) and unsetenv(3).
+ * Explicitly removes '=' in argument name.
+ */
+static char *
+__findenv(name, offset)
+ register const char *name;
+ int *offset;
+{
+ extern char **environ;
+ register int len;
+ register const char *np;
+ register char **p, *c;
+
+ if (name == NULL || environ == NULL)
+ return (NULL);
+ for (np = name; *np && *np != '='; ++np)
+ continue;
+ len = np - name;
+ for (p = environ; (c = *p) != NULL; ++p)
+ if (strncmp(c, name, len) == 0 && c[len] == '=') {
+ *offset = p - environ;
+ return (c + len + 1);
+ }
+ return (NULL);
+}
diff --git a/src/kadmin/cli/ss_wrapper.c b/src/kadmin/cli/ss_wrapper.c
new file mode 100644
index 000000000..89e94b36b
--- /dev/null
+++ b/src/kadmin/cli/ss_wrapper.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 1994 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. 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.
+ *
+ *
+ * ss wrapper for kadmin
+ */
+
+#include <krb5.h>
+#include <ss/ss.h>
+#include <stdio.h>
+#include <string.h>
+
+extern ss_request_table kadmin_cmds;
+extern int exit_status;
+extern char *kadmin_startup();
+extern char *whoami;
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *request;
+ krb5_error_code retval;
+ int sci_idx, code = 0;
+
+ whoami = ((whoami = strrchr(argv[0], '/')) ? whoami+1 : argv[0]);
+
+ request = kadmin_startup(argc, argv);
+ sci_idx = ss_create_invocation(whoami, "5.0", (char *) NULL,
+ &kadmin_cmds, &retval);
+ if (retval) {
+ ss_perror(sci_idx, retval, "creating invocation");
+ exit(1);
+ }
+ if (request) {
+ code = ss_execute_line(sci_idx, request);
+ if (code != 0) {
+ ss_perror(sci_idx, code, request);
+ exit_status++;
+ }
+ } else
+ ss_listen(sci_idx, &retval);
+ return quit() ? 1 : exit_status;
+}
diff --git a/src/kadmin/cli/strftime.c b/src/kadmin/cli/strftime.c
new file mode 100644
index 000000000..484852a72
--- /dev/null
+++ b/src/kadmin/cli/strftime.c
@@ -0,0 +1,469 @@
+/* strftime - custom formatting of date and/or time
+ Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* Note: this version of strftime lacks locale support,
+ but it is standalone.
+
+ Performs `%' substitutions similar to those in printf. Except
+ where noted, substituted fields have a fixed size; numeric fields are
+ padded if necessary. Padding is with zeros by default; for fields
+ that display a single number, padding can be changed or inhibited by
+ following the `%' with one of the modifiers described below. Unknown
+ field specifiers are copied as normal characters. All other
+ characters are copied to the output without change.
+
+ Supports a superset of the ANSI C field specifiers.
+
+ Literal character fields:
+ % %
+ n newline
+ t tab
+
+ Numeric modifiers (a nonstandard extension):
+ - do not pad the field
+ _ pad the field with spaces
+
+ Time fields:
+ %H hour (00..23)
+ %I hour (01..12)
+ %k hour ( 0..23)
+ %l hour ( 1..12)
+ %M minute (00..59)
+ %p locale's AM or PM
+ %r time, 12-hour (hh:mm:ss [AP]M)
+ %R time, 24-hour (hh:mm)
+ %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension)
+ %S second (00..61)
+ %T time, 24-hour (hh:mm:ss)
+ %X locale's time representation (%H:%M:%S)
+ %Z time zone (EDT), or nothing if no time zone is determinable
+
+ Date fields:
+ %a locale's abbreviated weekday name (Sun..Sat)
+ %A locale's full weekday name, variable length (Sunday..Saturday)
+ %b locale's abbreviated month name (Jan..Dec)
+ %B locale's full month name, variable length (January..December)
+ %c locale's date and time (Sat Nov 04 12:02:33 EST 1989)
+ %C century (00..99)
+ %d day of month (01..31)
+ %e day of month ( 1..31)
+ %D date (mm/dd/yy)
+ %h same as %b
+ %j day of year (001..366)
+ %m month (01..12)
+ %U week number of year with Sunday as first day of week (00..53)
+ %w day of week (0..6)
+ %W week number of year with Monday as first day of week (00..53)
+ %x locale's date representation (mm/dd/yy)
+ %y last two digits of year (00..99)
+ %Y year (1970...)
+
+ David MacKenzie <djm@gnu.ai.mit.edu> */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME))
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+
+#ifndef STDC_HEADERS
+time_t mktime ();
+#endif
+
+#if defined(HAVE_TZNAME)
+extern char *tzname[2];
+#endif
+
+/* Types of padding for numbers in date and time. */
+enum padding
+{
+ none, blank, zero
+};
+
+static char const* const days[] =
+{
+ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+};
+
+static char const * const months[] =
+{
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+
+/* Add character C to STRING and increment LENGTH,
+ unless LENGTH would exceed MAX. */
+
+#define add_char(c) \
+ do \
+ { \
+ if (length + 1 <= max) \
+ string[length++] = (c); \
+ } \
+ while (0)
+
+/* Add a 2 digit number to STRING, padding if specified.
+ Return the number of characters added, up to MAX. */
+
+static int
+add_num2 (string, num, max, pad)
+ char *string;
+ int num;
+ int max;
+ enum padding pad;
+{
+ int top = num / 10;
+ int length = 0;
+
+ if (top == 0 && pad == blank)
+ add_char (' ');
+ else if (top != 0 || pad == zero)
+ add_char (top + '0');
+ add_char (num % 10 + '0');
+ return length;
+}
+
+/* Add a 3 digit number to STRING, padding if specified.
+ Return the number of characters added, up to MAX. */
+
+static int
+add_num3 (string, num, max, pad)
+ char *string;
+ int num;
+ int max;
+ enum padding pad;
+{
+ int top = num / 100;
+ int mid = (num - top * 100) / 10;
+ int length = 0;
+
+ if (top == 0 && pad == blank)
+ add_char (' ');
+ else if (top != 0 || pad == zero)
+ add_char (top + '0');
+ if (mid == 0 && top == 0 && pad == blank)
+ add_char (' ');
+ else if (mid != 0 || top != 0 || pad == zero)
+ add_char (mid + '0');
+ add_char (num % 10 + '0');
+ return length;
+}
+
+/* Like strncpy except return the number of characters copied. */
+
+static int
+add_str (to, from, max)
+ char *to;
+ const char *from;
+ int max;
+{
+ int i;
+
+ for (i = 0; from[i] && i <= max; ++i)
+ to[i] = from[i];
+ return i;
+}
+
+static int
+add_num_time_t (string, max, num)
+ char *string;
+ int max;
+ time_t num;
+{
+ /* This buffer is large enough to hold the character representation
+ (including the trailing NUL) of any unsigned decimal quantity
+ whose binary representation fits in 128 bits. */
+ char buf[40];
+ int length;
+
+ if (sizeof (num) > 16)
+ abort ();
+ sprintf (buf, "%lu", (unsigned long) num);
+ length = add_str (string, buf, max);
+ return length;
+}
+
+/* Return the week in the year of the time in TM, with the weeks
+ starting on Sundays. */
+
+static int
+sun_week (tm)
+ struct tm *tm;
+{
+ int dl;
+
+ /* Set `dl' to the day in the year of the last day of the week previous
+ to the one containing the day specified in TM. If the day specified
+ in TM is in the first week of the year, `dl' will be negative or 0.
+ Otherwise, calculate the number of complete weeks before our week
+ (dl / 7) and add any partial week at the start of the year (dl % 7). */
+ dl = tm->tm_yday - tm->tm_wday;
+ return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
+}
+
+/* Return the week in the year of the time in TM, with the weeks
+ starting on Mondays. */
+
+static int
+mon_week (tm)
+ struct tm *tm;
+{
+ int dl, wday;
+
+ if (tm->tm_wday == 0)
+ wday = 6;
+ else
+ wday = tm->tm_wday - 1;
+ dl = tm->tm_yday - wday;
+ return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0);
+}
+
+#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)
+char *
+zone_name (tp)
+ struct tm *tp;
+{
+ char *timezone ();
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday (&tv, &tz);
+ return timezone (tz.tz_minuteswest, tp->tm_isdst);
+}
+#endif
+
+/* Format the time given in TM according to FORMAT, and put the
+ results in STRING.
+ Return the number of characters (not including terminating null)
+ that were put into STRING, or 0 if the length would have
+ exceeded MAX. */
+
+size_t
+strftime (string, max, format, tm)
+ char *string;
+ size_t max;
+ const char *format;
+ const struct tm *tm;
+{
+ enum padding pad; /* Type of padding to apply. */
+ size_t length = 0; /* Characters put in STRING so far. */
+
+ for (; *format && length < max; ++format)
+ {
+ if (*format != '%')
+ add_char (*format);
+ else
+ {
+ ++format;
+ /* Modifiers: */
+ if (*format == '-')
+ {
+ pad = none;
+ ++format;
+ }
+ else if (*format == '_')
+ {
+ pad = blank;
+ ++format;
+ }
+ else
+ pad = zero;
+
+ switch (*format)
+ {
+ /* Literal character fields: */
+ case 0:
+ case '%':
+ add_char ('%');
+ break;
+ case 'n':
+ add_char ('\n');
+ break;
+ case 't':
+ add_char ('\t');
+ break;
+ default:
+ add_char (*format);
+ break;
+
+ /* Time fields: */
+ case 'H':
+ case 'k':
+ length +=
+ add_num2 (&string[length], tm->tm_hour, max - length,
+ *format == 'H' ? pad : blank);
+ break;
+ case 'I':
+ case 'l':
+ {
+ int hour12;
+
+ if (tm->tm_hour == 0)
+ hour12 = 12;
+ else if (tm->tm_hour > 12)
+ hour12 = tm->tm_hour - 12;
+ else
+ hour12 = tm->tm_hour;
+ length +=
+ add_num2 (&string[length], hour12, max - length,
+ *format == 'I' ? pad : blank);
+ }
+ break;
+ case 'M':
+ length +=
+ add_num2 (&string[length], tm->tm_min, max - length, pad);
+ break;
+ case 'p':
+ if (tm->tm_hour < 12)
+ add_char ('A');
+ else
+ add_char ('P');
+ add_char ('M');
+ break;
+ case 'r':
+ length +=
+ strftime (&string[length], max - length, "%I:%M:%S %p", tm);
+ break;
+ case 'R':
+ length +=
+ strftime (&string[length], max - length, "%H:%M", tm);
+ break;
+
+ case 's':
+ {
+ struct tm writable_tm;
+ writable_tm = *tm;
+ length += add_num_time_t (&string[length], max - length,
+ mktime (&writable_tm));
+ }
+ break;
+
+ case 'S':
+ length +=
+ add_num2 (&string[length], tm->tm_sec, max - length, pad);
+ break;
+ case 'T':
+ length +=
+ strftime (&string[length], max - length, "%H:%M:%S", tm);
+ break;
+ case 'X':
+ length +=
+ strftime (&string[length], max - length, "%H:%M:%S", tm);
+ break;
+ case 'Z':
+#ifdef HAVE_TM_ZONE
+ length += add_str (&string[length], tm->tm_zone, max - length);
+#else
+#ifdef HAVE_TZNAME
+ if (tm->tm_isdst && tzname[1] && *tzname[1])
+ length += add_str (&string[length], tzname[1], max - length);
+ else
+ length += add_str (&string[length], tzname[0], max - length);
+#else
+ length += add_str (&string[length], zone_name (tm), max - length);
+#endif
+#endif
+ break;
+
+ /* Date fields: */
+ case 'a':
+ add_char (days[tm->tm_wday][0]);
+ add_char (days[tm->tm_wday][1]);
+ add_char (days[tm->tm_wday][2]);
+ break;
+ case 'A':
+ length +=
+ add_str (&string[length], days[tm->tm_wday], max - length);
+ break;
+ case 'b':
+ case 'h':
+ add_char (months[tm->tm_mon][0]);
+ add_char (months[tm->tm_mon][1]);
+ add_char (months[tm->tm_mon][2]);
+ break;
+ case 'B':
+ length +=
+ add_str (&string[length], months[tm->tm_mon], max - length);
+ break;
+ case 'c':
+ length +=
+ strftime (&string[length], max - length,
+ "%a %b %d %H:%M:%S %Z %Y", tm);
+ break;
+ case 'C':
+ length +=
+ add_num2 (&string[length], (tm->tm_year + 1900) / 100,
+ max - length, pad);
+ break;
+ case 'd':
+ length +=
+ add_num2 (&string[length], tm->tm_mday, max - length, pad);
+ break;
+ case 'e':
+ length +=
+ add_num2 (&string[length], tm->tm_mday, max - length, blank);
+ break;
+ case 'D':
+ length +=
+ strftime (&string[length], max - length, "%m/%d/%y", tm);
+ break;
+ case 'j':
+ length +=
+ add_num3 (&string[length], tm->tm_yday + 1, max - length, pad);
+ break;
+ case 'm':
+ length +=
+ add_num2 (&string[length], tm->tm_mon + 1, max - length, pad);
+ break;
+ case 'U':
+ length +=
+ add_num2 (&string[length], sun_week (tm), max - length, pad);
+ break;
+ case 'w':
+ add_char (tm->tm_wday + '0');
+ break;
+ case 'W':
+ length +=
+ add_num2 (&string[length], mon_week (tm), max - length, pad);
+ break;
+ case 'x':
+ length +=
+ strftime (&string[length], max - length, "%m/%d/%y", tm);
+ break;
+ case 'y':
+ length +=
+ add_num2 (&string[length], tm->tm_year % 100,
+ max - length, pad);
+ break;
+ case 'Y':
+ add_char ((tm->tm_year + 1900) / 1000 + '0');
+ length +=
+ add_num3 (&string[length],
+ (1900 + tm->tm_year) % 1000, max - length, zero);
+ break;
+ }
+ }
+ }
+ add_char (0);
+ return length - 1;
+}
diff --git a/src/kadmin/config.mk/ChangeLog b/src/kadmin/config.mk/ChangeLog
new file mode 100644
index 000000000..5dd46d71a
--- /dev/null
+++ b/src/kadmin/config.mk/ChangeLog
@@ -0,0 +1,10 @@
+Fri Jul 12 14:39:28 1996 Marc Horowitz <marc@mit.edu>
+
+ * architecture: add uname test for NetBSD
+ * netbsd1.def: added
+
+Mon Jul 8 16:39:36 1996 Barry Jaspan <bjaspan@mit.edu>
+
+ * template, site.def, rules: Add SITEMAKEFILES to specify -f
+ Makefile.ov, add .ct default rule.
+
diff --git a/src/kadmin/config.mk/aix3.2.def b/src/kadmin/config.mk/aix3.2.def
new file mode 100644
index 000000000..44fd07b1f
--- /dev/null
+++ b/src/kadmin/config.mk/aix3.2.def
@@ -0,0 +1,40 @@
+export PS_ALL = ps auxww
+# Make sure there's no extra whitespace at the end of this line!
+export PS_PID = ps auxww
+# Make sure there is a blank space at the end of this line!
+export PS_TTY = ps -t
+export RSH_CMD = /usr/bin/rsh
+export INSTCMD = /usr/local/bin/ginstall -c
+export LEX = /usr/local/bin/flex
+export AWK_CMD = /usr/local/bin/gawk
+
+# needed for fd_set to be defined
+# Not convinced! Experimenting with disabling _BSD. - jik 3/21/95
+ifndef KRB5B4
+CFLAGS += -D_BSD
+endif
+CFLAGS += -D_ALL_SOURCE
+
+PCC_STRUCT_RETURN = -fpcc-struct-return
+D_NEEDS_RPCENT = -D_SUN
+
+D_NEEDS_GETUSERSHELL = -DNEEDS_GETUSERSHELL
+D_NEEDS_TREEWALK = -DNEEDS_TREEWALK
+D_SETEUID = -DNO_SETEUID -DSETEUID_INCLUDE='<sys/id.h>' \
+ -DSETEUIDPRE='setuidx(ID_REAL|ID_EFFECTIVE,' -DSETEUIDPOST=')'
+D_NO_SETENV = -DNO_SETENV
+D_REGEXP_TYPE = -DPOSIX_REGEXPS
+
+TERMCAPLIB = -ltermcap
+
+# extra libraries needed for login
+LOGINLIB = -ls -lcfg -lodm
+
+UTMP_FILE = /etc/utmp
+WTMP_FILE = /usr/adm/wtmp
+
+XINCDIR = /usr/lpp/X11/include
+XLIBDIR = /usr/lpp/X11/lib
+
+OMIT_DOC = true
+DONT_STRIP_NXLIBS = true
diff --git a/src/kadmin/config.mk/architecture b/src/kadmin/config.mk/architecture
new file mode 100644
index 000000000..d976c27b2
--- /dev/null
+++ b/src/kadmin/config.mk/architecture
@@ -0,0 +1,68 @@
+# $Id$
+
+# sample `uname -a` output:
+# SunOS dun-dun-n 4.1.3 3 sun4c
+# SunOS samosa 5.3 Generic_101318-31 sun4m sparc
+# HP-UX strange- A.09.01 A 9000/710 2005970723 two-user license
+# AIX krusty 2 3 000131533500
+
+# each section here should set ARCH_OS to a symbol specifically describing
+# the OS, and also set "relevant" capability symbols
+
+ifndef ARCH_OS
+__UNAME_A := $(shell uname -a)
+ifeq "$(strip $(filter SunOS 4.1.3, $(__UNAME_A)))" "SunOS 4.1.3"
+ export ARCH_OS := sunos4.1
+endif
+ifeq "$(strip $(filter SunOS 4.1.3C, $(__UNAME_A)))" "SunOS 4.1.3C"
+ export ARCH_OS := sunos4.1
+endif
+ifeq "$(strip $(filter SunOS 4.1.3_U1, $(__UNAME_A)))" "SunOS 4.1.3_U1"
+ export ARCH_OS := sunos4.1
+endif
+ifeq "$(strip $(filter SunOS 4.1.4, $(__UNAME_A)))" "SunOS 4.1.4"
+ export ARCH_OS := sunos4.1
+endif
+ifeq "$(strip $(filter SunOS 5.3, $(__UNAME_A)))" "SunOS 5.3"
+ export ARCH_OS := solaris2.3
+endif
+# For now, assume that Solaris 2.4 is the same as Solaris 2.3.
+ifeq "$(strip $(filter SunOS 5.4, $(__UNAME_A)))" "SunOS 5.4"
+ export ARCH_OS := solaris2.3
+endif
+ifeq "$(strip $(filter HP-UX A.09.01, $(__UNAME_A)))" "HP-UX A.09.01"
+ export ARCH_OS := hpux9.01
+endif
+ifeq "$(strip $(filter AIX 2, $(__UNAME_A)))" "AIX 2"
+ export ARCH_OS := aix3.2
+endif
+ifeq "$(strip $(filter sweet-and-sour-sauce, $(__UNAME_A)))" "sweet-and-sour-sauce"
+ export ARCH_OS := aix3.2
+ d:=$(shell echo "*** WARNING! Used hostname for architecture." 1>&2)
+endif
+ifeq "$(strip $(filter Linux, $(__UNAME_A)))" "Linux"
+ export ARCH_OS := linux
+endif
+ifeq "$(strip $(filter IRIX, $(__UNAME_A)))" "IRIX"
+ export ARCH_OS := irix5.2
+endif
+ifeq "$(strip $(filter NetBSD, $(__UNAME_A)))" "NetBSD NetBSD"
+ export ARCH_OS := netbsd1
+endif
+
+ifndef ARCH_OS
+ d:=$(shell echo "*** WARNING! Unknown architecture: $(__UNAME_A)" 1>&2)
+ export ARCH_OS := dummy
+ export ARCH_OS_UNKNOWN := dummy
+endif
+endif
+
+ifndef ARCH_OS_UNKNOWN
+ define IncludeArchFile
+ include $(CONFDIR)/$(ARCH_OS).def
+ endef
+else
+ define IncludeArchFile
+ endef
+endif
+
diff --git a/src/kadmin/config.mk/config b/src/kadmin/config.mk/config
new file mode 100644
index 000000000..9bb800e84
--- /dev/null
+++ b/src/kadmin/config.mk/config
@@ -0,0 +1,141 @@
+# $Id$
+# $Source$
+
+#
+# local Programs
+#
+
+INSTHDRS = $(TOP)/scripts/inst-hdrs.sh
+COMPILE_ET = $(TOP)/../util/et/compile_et
+MK_CMDS = $(TOP)/../util/ss/mk_cmds
+SYM_RANDOMIZE = $(TOP)/intl/sym-randomize.pl
+
+#
+# Directories
+#
+
+STAGETOP= $(TOP)/..
+STAGE_BINDIR = $(STAGETOP)/bin
+STAGE_INCDIR = $(STAGETOP)/include
+STAGE_LIBDIR = $(STAGETOP)/lib
+
+INSTALLTOP= $(TOP)/..
+INSTALL_BINDIR = $(INSTALLTOP)/bin
+INSTALL_ETCDIR = $(INSTALLTOP)/etc
+INSTALL_INCDIR = $(INSTALLTOP)/include
+INSTALL_LIBDIR = $(INSTALLTOP)/lib
+INSTALL_CONFDIR = $(INSTALLTOP)/config
+INSTALL_PROTODIR = $(INSTALLTOP)/proto
+INSTALL_ADMINDIR = $(INSTALLTOP)/admin
+INSTALL_SERVERDIR = $(INSTALLTOP)/sbin
+INSTALL_INSTDIR = $(INSTALLTOP)/install
+INSTALL_DOCDIR = $(INSTALLTOP)/doc
+INSTALL_MANDIR = $(INSTALLTOP)/man
+INSTALL_SRCDIR = $(INSTALLTOP)/src
+
+#
+# libraries
+#
+
+# system
+LIBM = -lm
+
+# stage
+LIBADMCLNT := $(STAGE_LIBDIR)/libkadm5clnt.a
+LIBADMSRV := $(STAGE_LIBDIR)/libkadm5srv.a
+LIBRPCLIB := $(STAGE_LIBDIR)/libgssrpc.a
+LIBDYN := $(STAGE_LIBDIR)/libdyn.a
+LIBSS := $(STAGE_LIBDIR)/libss.a
+LIBOVSEC_UTIL := $(STAGE_LIBDIR)/libkadm5_util.a
+LIBFTPSEC := $(STAGE_LIBDIR)/libftpsec.a
+
+# install
+LIBGSSAPI_KRB5 := $(INSTALL_LIBDIR)/libgssapi_krb5.a
+LIBGSSAPI_KRB5_DX := $(INSTALL_LIBDIR)/libgssapi_krb5_dx.a
+LIBGSSAPI_KRB5_WX := $(INSTALL_LIBDIR)/libgssapi_krb5_wx.a
+LIBGSSAPI_KRB5_NX := $(INSTALL_LIBDIR)/libgssapi_krb5_nx.a
+LIBGSSAPI_TRUST := $(INSTALL_LIBDIR)/libgssapi_trust.a
+LIBDB := $(INSTALL_LIBDIR)/libdb.a
+LIBKRB5 := $(INSTALL_LIBDIR)/libkrb5.a
+LIBKDB5 := $(INSTALL_LIBDIR)/libkdb5.a
+LIBCRYPTO := $(INSTALL_LIBDIR)/libcrypto.a
+ifndef KRB5B4
+LIBISODE := $(INSTALL_LIBDIR)/libisode.a
+endif
+LIBCOM_ERR := $(INSTALL_LIBDIR)/libcom_err.a
+LIBKRB5_ALL := $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) $(LIBCOM_ERR)
+LIBKRB := $(INSTALL_LIBDIR)/libkrb4.a
+LIBKDB := $(INSTALL_LIBDIR)/libkdb.a
+LIBKADM := $(INSTALL_LIBDIR)/libkadm.a
+LIBKRB425 := $(INSTALL_LIBDIR)/libkrb425.a
+LIBDES425 := $(INSTALL_LIBDIR)/libdes425.a
+
+# X libraries. XXX this uses -L! but perhaps it doesn't matter, see
+# [secure-build/2649]
+XLIB = -L$(XLIBDIR) -lXext -lX11
+
+#
+# library name mangling for export
+#
+
+ADMIN_INTERFACE_SYMBOL_FILES := $(TOP)/intl/adm-export-symbols \
+ $(TOP)/intl/misc-export-symbols
+OTHER_INTERFACE_SYMBOL_FILES := $(TOP)/intl/gss-export-symbols
+
+ADMIN_CRYPTO_LIBS := $(LIBADMCLNT) $(LIBADMSRV) $(LIBRPCLIB) \
+ $(LIBGSSAPI_KRB5) $(LIBKRB5) $(LIBKDB5) $(LIBCRYPTO) \
+ $(LIBISODE) $(LIBKRB) $(LIBKDB) $(LIBDES425)
+OTHER_CRYPTO_LIBS := $(LIBGSSAPI_KRB5_DX) $(LIBGSSAPI_KRB5_WX) \
+ $(LIBGSSAPI_KRB5_NX)
+
+#
+# compiler/linker flags
+#
+
+CFLAGS := $(CFLAGS) -I$(INSTALL_INCDIR)
+
+#
+# Variables for testing
+#
+# These are all exported because lots of test scripts (/bin/sh, perl,
+# tcl) use them.
+#
+
+export TESTDIR = $(TOP)/testing
+export COMPARE_DUMP = $(TESTDIR)/scripts/compare_dump.pl
+export FIX_CONF_FILES = $(TESTDIR)/scripts/fixup-conf-files.pl
+export INITDB = $(TESTDIR)/scripts/init_db
+export MAKE_KEYTAB = $(TESTDIR)/scripts/make-host-keytab.pl
+export LOCAL_MAKE_KEYTAB= $(TESTDIR)/scripts/make-host-keytab.pl
+export RESTORE_FILES = $(TESTDIR)/scripts/restore_files.sh
+export SAVE_FILES = $(TESTDIR)/scripts/save_files.sh
+export SIMPLE_DUMP = $(TESTDIR)/scripts/simple_dump.pl
+export TCLUTIL = $(TESTDIR)/tcl/util.t
+export BSDDB_DUMP = $(TESTDIR)/util/bsddb_dump
+export CLNTTCL = $(TESTDIR)/util/ovsec_kadm_clnt_tcl
+export SRVTCL = $(TESTDIR)/util/ovsec_kadm_srv_tcl
+export QUALNAME = $(TOP)/inst-scripts/qualname
+
+export START_SERVERS = $(TESTDIR)/scripts/start_servers $(TEST_SERVER)
+export START_SERVERS_LOCAL = $(TESTDIR)/scripts/start_servers_local
+
+export STOP_SERVERS = $(TESTDIR)/scripts/stop_servers $(TEST_SERVER)
+export STOP_SERVERS_LOCAL = $(TESTDIR)/scripts/stop_servers_local
+
+export KRB5_CONFIG = /krb5/krb5.conf
+export KRB5_KDC_PROFILE = /krb5/kdc.conf
+export KRB5_KTNAME = /krb5/ovsec_adm.srvtab
+
+ifdef TEST_SERVER
+MAKE_KEYTAB += -server $(TEST_SERVER)
+ifdef TEST_PATH
+MAKE_KEYTAB += -top $(TEST_PATH)
+START_SERVERS += $(TEST_PATH)
+STOP_SERVERS += $(TEST_PATH)
+endif
+endif
+
+export VERBOSE_TEST DEJALFLAGS
+
+# We're running low on disk space, so strip installed binaries
+STRIP_INSTALLED = true
diff --git a/src/kadmin/config.mk/hpux9.01.def b/src/kadmin/config.mk/hpux9.01.def
new file mode 100644
index 000000000..17da13c52
--- /dev/null
+++ b/src/kadmin/config.mk/hpux9.01.def
@@ -0,0 +1,32 @@
+export ARCH_SKIP_RANLIB := 1
+export INSTCMD = ginstall -c
+export PS_ALL = ps -ef
+# Make sure there's no extra whitespace at the end of this line!
+export PS_PID = ps -fp
+# Make sure there is a blank space at the end of this line!
+export PS_TTY = ps -t
+export STATIC_LINK_GUI := 1
+export RSH_CMD = /usr/bin/remsh
+
+PCC_STRUCT_RETURN = -fpcc-struct-return
+# this must always be at the end.
+REGEXLIB = -lc -lPW
+TERMCAPLIB = -ltermcap
+
+XINCDIR = /usr/include/X11R5
+XLIBDIR = /usr/lib/X11R5
+NDBMLIB := /usr/lib/libndbm.a
+ARCH_CFLAGS = -mpa-risc-1-0
+
+CFLAGS := $(ARCH_CFLAGS) $(CFLAGS)
+
+UTMP_FILE = /etc/utmp
+WTMP_FILE = /etc/wtmp
+
+D_RANDOM = -Dsrandom=srand48 -Drandom=lrand48
+D_SETEUID = -DNO_SETEUID \
+ -DSETEUIDPRE='setresuid(-1,' -DSETEUIDPOST=',-1)'
+D_FATALINLIBS = -Dfatal=ftpd_fatal
+D_NO_SETENV = -DNO_SETENV
+D_REGEXP_TYPE = -DPOSIX_REGEXPS
+
diff --git a/src/kadmin/config.mk/irix5.2.def b/src/kadmin/config.mk/irix5.2.def
new file mode 100644
index 000000000..053d9328e
--- /dev/null
+++ b/src/kadmin/config.mk/irix5.2.def
@@ -0,0 +1,22 @@
+export PS_ALL = /usr/bin/ps -ef
+# Make sure there's no extra whitespace at the end of this line!
+export PS_PID = /usr/bin/ps -fp
+# Make sure there is a blank space at the end of this line!
+export PS_TTY = /usr/bin/ps -t
+
+RANLIB = /bin/true
+D_NO_SETENV = -DNO_SETENV
+D_HAVE_SYSLOG_H = -DHAVE_SYSLOG_H
+D_HAVE_STDARG_H = -DHAVE_STDARG_H
+D_HAVE_SYSLOG = -DHAVE_SYSLOG
+D_HAVE_VSPRINTF = -DHAVE_VSPRINTF
+D_HAVE_OPENLOG = -DHAVE_OPENLOG
+D_HAVE_CLOSELOG = -DHAVE_CLOSELOG
+D_HAVE_STRFTIME = -DHAVE_STRFTIME
+
+# libgen.a is for regcmp and regex, used in /usr/lib/libXm.a; this is
+# a different set of regexp routines than used by svr_iters.c
+XM_LIB = /usr/lib/libXm.a /usr/lib/libgen.a
+XT_LIB = /usr/lib/libXt.a
+X_LIB = /usr/lib/libX11.so
+
diff --git a/src/kadmin/config.mk/linux.def b/src/kadmin/config.mk/linux.def
new file mode 100644
index 000000000..69a0c6287
--- /dev/null
+++ b/src/kadmin/config.mk/linux.def
@@ -0,0 +1,23 @@
+export PS_ALL = ps auxww
+# Make sure there's no extra whitespace at the end of this line!
+export PS_PID = ps auxww
+export RSH_CMD = /usr/bin/rsh
+
+XINCDIR = /usr/include
+XLIBDIR = /usr/lib
+
+NDBMLIB = -ldbm
+
+PERL = /usr/bin/perl
+LEX_LIB = -lfl
+
+OMIT_GUI = true
+OMIT_XM_KPASSWD = true
+OMIT_DOC = true
+
+UTMP_FILE = /etc/utmp
+WTMP_FILE = /var/adm/wtmp
+
+LIBTCL = -ltcl
+
+TERMCAPLIB = -ltermcap
diff --git a/src/kadmin/config.mk/netbsd1.def b/src/kadmin/config.mk/netbsd1.def
new file mode 100644
index 000000000..2b6090355
--- /dev/null
+++ b/src/kadmin/config.mk/netbsd1.def
@@ -0,0 +1,22 @@
+export PS_ALL = ps auxww
+# Make sure there's no extra whitespace at the end of this line!
+export PS_PID = ps auxww
+# Make sure there's no extra whitespace at the end of this line!
+export PS_TTY = ps -t
+export RSH_CMD = /usr/bin/rsh
+
+PCC_STRUCT_RETURN =
+TERMCAPLIB = -ltermcap
+
+XINCDIR = /usr/X11/include
+XLIBDIR = /usr/X11/lib
+
+UTMP_FILE = /etc/utmp
+WTMP_FILE = /usr/adm/wtmp
+
+D_NO_SETENV = -DNO_SETENV
+
+SHLIBCFLAGS := -fpic
+SHLIBLDFLAGS := -assert pure-text
+SHLIBEXT := so
+SHLIBSEP := .
diff --git a/src/kadmin/config.mk/rules b/src/kadmin/config.mk/rules
new file mode 100644
index 000000000..35b647bda
--- /dev/null
+++ b/src/kadmin/config.mk/rules
@@ -0,0 +1,538 @@
+#
+# $Id$
+# $Source$
+#
+
+#
+# DefaultRules --- clean, depend, all, stage, includes, and
+# install.
+#
+
+define DefaultRules
+clean::
+ $(CLEAN) core *.o *~ *.bak #* y.output
+
+depend::
+
+all::
+
+includes::
+
+stage::
+
+install::
+
+unit-test::
+
+traverse::
+
+endef
+
+
+#
+# SubdirTarget
+#
+# SUBDIRS = subdirs to work in
+#
+define SubdirTarget
+__SUBDIR_TARGET := clean
+expand _DoSubdir
+
+__SUBDIR_TARGET := depend
+expand _DoSubdir
+
+__SUBDIR_TARGET := all
+expand _DoSubdir
+
+__SUBDIR_TARGET := includes
+expand _DoSubdir
+
+__SUBDIR_TARGET := stage
+expand _DoSubdir
+
+__SUBDIR_TARGET := install
+expand _DoSubdir
+
+__SUBDIR_TARGET := unit-test
+expand _DoSubdir
+
+__SUBDIR_TARGET := traverse
+expand _DoSubdir
+
+ALL_SUBDIRS_TARGET := everything
+expand AllTargetsTarget
+endef
+
+#
+# Makes the target $(ALL_SUBDIRS_TARGET) perform the targets includes
+# depend stage all install in $(SUBDIRS).
+#
+define AllTargetsTarget
+$(ALL_SUBDIRS_TARGET)::
+ @cwd=`pwd`; \
+ for d in $(SUBDIRS); do \
+ echo "--- Making includes depend stage all install in $(CUR_DIR)/$$$$d"; \
+ cd $$$$d && \
+ $(MAKE) includes depend CUR_DIR=$(CUR_DIR)/$$$$d; \
+ $(MAKE) stage all install CUR_DIR=$(CUR_DIR)/$$$$d; \
+ cd $$$$cwd; \
+ done
+
+endef
+
+define _DoSubdir
+# $(__SUBDIR_TARGET)::
+# @echo "--- SKIPPING $(__SUBDIR_TARGET) in $(SUBDIRS)"
+$(__SUBDIR_TARGET)::
+ @cwd=`pwd`; \
+ for d in $(SUBDIRS); do \
+ echo "--- Making $(__SUBDIR_TARGET) in $(CUR_DIR)/$$$$d"; \
+ cd $$$$d && \
+ $(MAKE) $(SITEMAKEFLAGS) $(__SUBDIR_TARGET) CUR_DIR=$(CUR_DIR)/$$$$d; \
+ cd $$$$cwd; \
+ done
+endef
+
+#
+# Program -- compile prog, adds all and clean targets
+#
+# PROG program name
+# OBJS object files
+# DEPS additional dependencies (e.g.: libraries)
+# LIBS libraries
+# LDFLAGS arguments for link stage
+# PUREFLAGS arguments for purify
+# PURELIBS extra libraries to link in when using purify
+#
+define Program
+all:: $(PROG)
+
+$(PROG): $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS))
+ifdef BUILD_IN_TMP
+ rm -f /tmp/$(PROG).$(__PID__)
+ $(CC) $(LDFLAGS) -o /tmp/$(PROG).$(__PID__) $(OBJS) $(LIBS) $(STD_LIBS)
+ mv /tmp/$(PROG).$(__PID__) $(PROG)
+else
+ $(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS) $(STD_LIBS)
+endif
+
+clean::
+ $(CLEAN) $(PROG)
+
+proof:: $(PROG).tc
+
+$(PROG).tc: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS))
+ifdef BUILD_IN_TMP
+ rm -f /tmp/$(PROG).tc.$(__PID__)
+ $(PROOF) $(CC) $(LDFLAGS) -o /tmp/$(PROG).tc.$(__PID__) $(OBJS) \
+ $(LIBS) $(STD_LIBS)
+ mv /tmp/$(PROG).tc.$(__PID__) $(PROG).tc
+else
+ $(PROOF) $(CC) $(LDFLAGS) -o $(PROG).tc $(OBJS) $(LIBS) $(STD_LIBS)
+endif
+
+clean::
+ $(CLEAN) $(PROG).tc $(PROG).tc.*
+
+pure:: $(PROG).pure
+
+$(PROG).pure: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS))
+ifdef BUILD_IN_TMP
+ rm -f /tmp/$(PROG).pure.$(__PID__)
+ $(PURIFY) $(PUREFLAGS) \
+ $(CC) $(LDFLAGS) -o /tmp/$(PROG).pure.$(__PID__) $(OBJS) \
+ $(LIBS) $(STD_LIBS) $(PURELIBS)
+ mv /tmp/$(PROG).pure.$(__PID__) $(PROG).pure
+else
+ $(PURIFY) $(PUREFLAGS) $(CC) $(LDFLAGS) -o $(PROG).pure $(OBJS) $(LIBS) $(STD_LIBS) $(PURELIBS)
+endif
+
+clean::
+ $(CLEAN) $(PROG).pure $(PROG).pure.pure_*
+
+quant:: $(PROG).quant
+
+$(PROG).quant: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS))
+ifdef BUILD_IN_TMP
+ rm -f /tmp/$(PROG).quant.$(__PID__)
+ $(QUANTIFY) $(QUANTFLAGS) \
+ $(CC) $(LDFLAGS) -o /tmp/$(PROG).quant.$(__PID__) $(OBJS) \
+ $(LIBS) $(STD_LIBS) $(QUANTLIBS)
+ mv /tmp/$(PROG).quant.$(__PID__) $(PROG).quant
+else
+ $(QUANTIFY) $(QUANTFLAGS) $(CC) $(LDFLAGS) -o $(PROG).quant $(OBJS) $(LIBS) $(STD_LIBS) $(QUANTLIBS)
+endif
+
+clean::
+ $(CLEAN) $(PROG).quant $(PROG).quant.quant.*.qv
+endef
+
+#
+# InstallProgram -- build program, install in INSTALL_BINDIR, adds
+# install target
+#
+define InstallProgram
+__INST_DIR := $(INSTALL_BINDIR)
+expand Program
+expand InstallExecutable
+endef
+
+#
+# InstallUtil -- build program, install in INSTALL_ETCDIR, adds
+# install target
+#
+define InstallUtil
+__INST_DIR := $(INSTALL_ETCDIR)
+expand Program
+expand InstallExecutable
+endef
+
+#
+# InstallAdmin -- build program, install in INSTALL_ADMINDIR, adds
+# install target
+#
+define InstallAdmin
+__INST_DIR := $(INSTALL_ADMINDIR)
+expand Program
+expand InstallExecutable
+endef
+
+#
+# InstallServer -- build program, install in INSTALL_SERVERDIR, adds
+# install target
+#
+define InstallServer
+__INST_DIR := $(INSTALL_SERVERDIR)
+expand Program
+expand InstallExecutable
+endef
+
+#
+# InstallExecuteable(bin, as, dir)
+#
+define InstallExecutable
+ifndef INST_NAME
+install:: $(PROG)
+ rm -f $(__INST_DIR)/$(PROG)
+ $(INSTCMD) $(INST_PROG_FLAGS) $(PROG) $(__INST_DIR)/$(PROG)
+else
+install:: $(PROG)
+ rm -f $(__INST_DIR)/$(INST_NAME)
+ $(INSTCMD) $(INST_PROG_FLAGS) $(PROG) $(__INST_DIR)/$(INST_NAME)
+endif
+endef
+
+#
+# Library -- create library from object files and ranlib it, adds all
+# and clean targets
+#
+# LIB name of library (e.g. libfoo.a)
+# OBJS object files in library
+#
+define Library
+all:: $(LIB)
+
+$(LIB): $(OBJS) $(OTHER_OBJS)
+ $(RM) -f $(LIB)
+ $(AR) $(ARFLAGS) $(LIB) $(OBJS)
+ifndef ARCH_SKIP_RANLIB
+ $(RANLIB) $(LIB)
+endif
+
+clean::
+ $(CLEAN) $(LIB)
+endef
+
+#
+# SharedLibrary -- create shared library from object files, adds all
+# and clean targets
+#
+# LIB basename of library (e.g. libfoo)
+# (this will be basenamified, anyway)
+# OBJS object files in library
+# VERSION version of shared library
+#
+define SharedLibrary
+ifdef DO_SHARED_LIBRARIES
+ifdef SHLIBLDFLAGS
+ifdef SHLIBSEP
+__LIB := $(basename $(LIB)).$(SHLIBEXT)$(SHLIBSEP)$(VERSION)
+else
+__LIB := $(basename $(LIB)).$(SHLIBEXT)
+endif
+__LIBNV := $(basename $(LIB)).$(SHLIBEXT)
+expand SharedLibrary_1
+endif
+endif
+endef
+
+define SharedLibrary_1
+ifdef DO_SHARED_LIBRARIES
+all:: $(__LIB)
+
+$(__LIB): $(OBJS) $(OTHER_OBJS)
+ $(RM) -f $(__LIB) $(__LIBNV)
+ $(LD) -o $(__LIB) $(SHLIBLDFLAGS) $(OBJS)
+ $(LNSOFT) $(__LIB) $(__LIBNV)
+clean::
+ $(CLEAN) $(__LIB) $(__LIBNV)
+endif
+endef
+
+#
+# StageLibrary -- build library, install in STAGE_LIBDIR, adds stage target
+#
+define StageLibrary
+expand Library
+
+stage:: $(STAGE_LIBDIR)/$(LIB)
+
+$(STAGE_LIBDIR)/$(LIB): $(LIB)
+ $(INSTCMD) $(LIB) $(STAGE_LIBDIR)/$(LIB)
+ifndef ARCH_SKIP_RANLIB
+ $(RANLIB) $(STAGE_LIBDIR)/$(LIB)
+endif
+endef
+
+#
+# StageSharedLibrary -- build library, install in STAGE_LIBDIR, adds
+# stage target
+#
+define StageSharedLibrary
+ifdef DO_SHARED_LIBRARIES
+expand SharedLibrary
+expand StageSharedLibrary_1
+endif
+endef
+
+define StageSharedLibrary_1
+ifdef DO_SHARED_LIBRARIES
+ifdef __LIB
+stage:: $(STAGE_LIBDIR)/$(__LIB)
+
+$(STAGE_LIBDIR)/$(__LIB): $(__LIB)
+ $(INSTCMD) $(__LIB) $(STAGE_LIBDIR)/$(__LIB)
+endif
+endif
+endef
+
+#
+# InstallLibrary -- build library, install in INSTALL_LIBDIR,
+# adds install target
+#
+define InstallLibrary
+expand Library
+
+install:: $(INSTALL_LIBDIR)/$(LIB)
+
+$(INSTALL_LIBDIR)/$(LIB): $(LIB)
+ $(INSTCMD) $(LIB) $(INSTALL_LIBDIR)/$(LIB)
+ifndef ARCH_SKIP_RANLIB
+ $(RANLIB) $(INSTALL_LIBDIR)/$(LIB)
+endif
+endef
+
+#
+# InstallSharedLibrary -- build library, install in INSTALL_LIBDIR,
+# adds install target
+#
+define InstallSharedLibrary
+ifdef DO_SHARED_LIBRARIES
+expand SharedLibrary
+expand InstallSharedLibrary_1
+endif
+endef
+
+define InstallSharedLibrary_1
+ifdef DO_SHARED_LIBRARIES
+ifdef __LIB
+install:: $(INSTALL_LIBDIR)/$(__LIB)
+
+$(INSTALL_LIBDIR)/$(__LIB): $(__LIB)
+ $(INSTCMD) $(__LIB) $(INSTALL_LIBDIR)/$(__LIB)
+endif
+endif
+endef
+
+#
+# StageFiles(files, dir)
+#
+define StageFiles
+stage:: $(FILES)
+ $(INSTCMD) $(FILES) $(DIR)
+endef
+
+#
+# StageIncludes -- copy include files to staging area, adds includes
+# and stage target
+#
+# HDRS header files to copy
+# HDRS_DIR subdir of STAGE_INCDIR to copy to
+#
+define StageIncludes
+includes:: $(HDRS)
+ $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(HDRS)
+
+stage:: $(HDRS)
+ $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(HDRS)
+endef
+
+#
+# InstallIncludes -- copy include files to staging area, adds includes
+# and install target
+#
+# HDRS header files to copy
+# HDRS_DIR subdir of INSTALL_INCDIR to copy to
+#
+define InstallIncludes
+includes:: $(HDRS)
+ $(INSTHDRS) $(INSTALL_INCDIR)/$(HDRS_DIR) $(HDRS)
+
+install:: $(HDRS)
+ $(INSTHDRS) $(INSTALL_INCDIR)/$(HDRS_DIR) $(HDRS)
+endef
+
+#
+# Depend -- run makedepend
+#
+# SRCS source files to generate dependencies for
+# DEPENDS dependencies for "make depend"
+# ETABLES error tables whose .c and .h files are implicitly
+# dependencies of make depend
+#
+define Depend
+depend:: $(DEPENDS) $(addsuffix .c,$(basename $(ETABLES))) $(addsuffix .h,$(basename $(ETABLES)))
+
+ifeq (,$(findstring -a,$(__MDFLAGS)))
+ @rm -f Makefile.depend; touch Makefile.depend
+endif
+ $(MAKEDEPEND) $(__MDFLAGS) $(MDFLAGS) -fMakefile.depend -- $(CFLAGS) -- $(SRCS)
+
+clean::
+ $(CLEAN) Makefile.depend
+
+ifeq (,$(findstring -a,$(__MDFLAGS)))
+__MDFLAGS += -a
+endif
+endef
+
+#
+# NormalProgram -- everything for a single program
+#
+# PROG = program name
+# SRCS = list of .c sources
+# HDRS = list of .h sources
+# OBJS = list of .o files to depend and link against
+# STAGELIBS = foo.a within the stage area tree to link against
+# INSTALLLIBS = foo.a within the install area tree to link against
+# LIBS = system libraries to link against
+#
+# STAGELIBS And INSTALLLIBS are added to the dependencies for PROG.
+#
+define NormalProgram
+expand SaveStuff
+LIBS := $(addprefix $(STAGE_LIBDIR)/,$(STAGELIBS)) \
+ $(addprefix $(INSTALL_LIBDIR)/,$(INSTALLLIBS)) \
+ $(LIBS)
+DEPS := $(addprefix $(STAGE_LIBDIR)/,$(STAGELIBS)) \
+ $(addprefix $(INSTALL_LIBDIR)/,$(INSTALLLIBS)) \
+ $(DEPS)
+
+clean::
+ $(CLEAN) $(OBJS)
+
+expand InstallProgram
+expand Depend
+expand RestoreStuff
+endef
+
+define NormalLibrary
+expand Library
+
+clean::
+ $(CLEAN) $(OBJS)
+
+expand Depend
+endef
+
+#
+# ErrorTables -- compile an error table with compile_et
+#
+# ETABLES list of .et files
+# CFLAGS for saber target
+#
+#
+define ErrorTables
+__ETABLE_HS := $(addsuffix .h,$(basename $(ETABLES)))
+__ETABLE_CS := $(addsuffix .c,$(basename $(ETABLES)))
+expand ErrorTables_1
+endef
+
+define ErrorTables_1
+
+saber::
+ #load $(CFLAGS) $(__ETABLE_CS)
+
+clean::
+ $(CLEAN) $(__ETABLE_HS) $(__ETABLE_CS)
+endef
+
+#
+# StageErrorTables -- copy generated include file into staging area.
+#
+# ETABLES list of .et file
+# HDRS_DIR subdir of STAGE_INCDIR to copy to
+#
+define StageErrorTables
+expand ErrorTables
+expand StageErrorTables_1
+endef
+
+define StageErrorTables_1
+includes:: $(__ETABLE_HS)
+ $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(__ETABLE_HS)
+
+stage:: $(__ETABLE_HS)
+ $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(__ETABLE_HS)
+endef
+
+define SaveStuff
+__LIBS := $(LIBS)
+__DEPS := $(DEPS)
+endef
+
+define RestoreStuff
+LIBS := $(__LIBS)
+DEPS := $(__DEPS)
+endef
+
+#
+# XDR -- generate .c and .h from .x
+#
+# XDRS list of .x files
+#
+define XDR
+__XDR_HS := $(addsuffix .h,$(basename $(XDRS)))
+__XDR_CS := $(addsuffix .c,$(basename $(XDRS)))
+expand XDR_1
+endef
+
+define XDR_1
+saber::
+ #load $(CFLAGS) $(__XDR_CS)
+
+clean::
+ $(CLEAN) $(__XDR_HS) $(__XDR_CS)
+endef
+
+
+
+#
+# Saber -- load files into saber
+#
+# SRCS list of .c files
+#
+define Saber
+saber::
+ #load $(CFLAGS) $(SRCS)
+endef
diff --git a/src/kadmin/config.mk/site.def b/src/kadmin/config.mk/site.def
new file mode 100644
index 000000000..be7fe9e25
--- /dev/null
+++ b/src/kadmin/config.mk/site.def
@@ -0,0 +1,52 @@
+# $Id$
+
+# XXXX this file will probably have lots of ARCH_OS defines in it.
+
+#
+# Misc settings
+#
+
+#
+# site-specific compiler/linker flags
+#
+
+CFLAGS := $(CFLAGS) -I$(TOP)/../../src/include \
+ -I$(TOP)/../../src/include/krb5 \
+ -I$(TOP)/../include \
+ -I$(TOP)/../include/krb5 \
+ $(PCC_STRUCT_RETURN) -g
+
+SITEMAKEFLAGS := -f Makefile.ov
+
+#
+# Packages whose locations we need to know
+#
+
+ifeq ($(shell ls -d /.afs/gza.com/product/secure 2>/dev/null),/.afs/gza.com/product/secure)
+AFS_ROOT=/.afs
+else
+AFS_ROOT=/afs
+endif
+
+PERL = /afs/athena/contrib/perl/p
+# /afs/sipb/project/tcl/lib/libtcl.a
+LIBTCL = /mit/gnu/lib/libtcl.a
+# /afs/sipb/project/tcl/include
+TCLINC = /mit/gnu/include
+
+#
+# Default locations
+#
+
+YACC = bison -y
+
+#
+# I'm not really sure where this should go, but it is often useful to
+# be able to set up a test environment from anywhere in the build
+# tree.
+#
+start-servers:
+ $(START_SERVERS)
+
+stop-servers:
+ $(STOP_SERVERS)
diff --git a/src/kadmin/config.mk/solaris2.3.def b/src/kadmin/config.mk/solaris2.3.def
new file mode 100644
index 000000000..d18e0e164
--- /dev/null
+++ b/src/kadmin/config.mk/solaris2.3.def
@@ -0,0 +1,39 @@
+export PS_ALL = /usr/bin/ps -ef
+# Make sure there's no extra whitespace at the end of this line!
+export PS_PID = /usr/bin/ps -fp
+# Make sure there is a blank space at the end of this line!
+export PS_TTY = /usr/bin/ps -t
+export RSH_CMD = /usr/ucb/rsh
+export INSTCMD = /usr/ucb/install
+export PATH := $(PATH):/usr/ucb:/usr/ccs/bin
+
+PCC_STRUCT_RETURN = -fpcc-struct-return
+D_NEEDS_RPCENT = -DNEEDS_RPCENT
+D_SYSV = -DSYSV
+D_POSIX = -DPOSIX
+D_NO_SETENV = -DNO_SETENV
+D_POSIX_SIGNALS = -DPOSIX_SIGNALS
+D_RANDOM = -Dsrandom=srand48 -Drandom=lrand48
+D_REGEXP_TYPE = -DSOLARIS_REGEXPS
+NETLIB = -lsocket -lnsl
+#BSDLIB = /usr/ucblib/libucb.a
+TERMCAPLIB = -lcurses -ltermcap
+REGEXLIB = -lgen
+LOGINLIB = -lcmd
+
+XINCDIR = /usr/openwin/include
+XLIBDIR = /usr/openwin/lib
+RANLIB = /bin/true
+
+OMIT_GUI = true
+
+# These are used by admin/v4server/Makefile. They are determined
+# automatically by the krb5 beta 4 auto-configure, but we're not using
+# that right now.
+WAIT_USES_INT = true
+OPEN_NEEDS_FCNTL = true
+
+UTMP_FILE = /var/adm/utmp
+WTMP_FILE = /var/adm/wtmp
+UTMPX_FILE = /var/adm/utmpx
+WTMPX_FILE = /var/adm/wtmpx
diff --git a/src/kadmin/config.mk/sunos4.1.def b/src/kadmin/config.mk/sunos4.1.def
new file mode 100644
index 000000000..942615511
--- /dev/null
+++ b/src/kadmin/config.mk/sunos4.1.def
@@ -0,0 +1,22 @@
+export PS_ALL = ps auxww
+# Make sure there's no extra whitespace at the end of this line!
+export PS_PID = ps auxww
+# Make sure there's no extra whitespace at the end of this line!
+export PS_TTY = ps -t
+export RSH_CMD = /usr/ucb/rsh
+
+PCC_STRUCT_RETURN = -fpcc-struct-return
+TERMCAPLIB = -ltermcap
+
+XINCDIR = /usr/openwin/include
+XLIBDIR = /usr/openwin/lib
+
+UTMP_FILE = /etc/utmp
+WTMP_FILE = /usr/adm/wtmp
+
+D_NO_SETENV = -DNO_SETENV
+
+SHLIBCFLAGS := -fpic
+SHLIBLDFLAGS := -assert pure-text
+SHLIBEXT := so
+SHLIBSEP := .
diff --git a/src/kadmin/config.mk/template b/src/kadmin/config.mk/template
new file mode 100644
index 000000000..caf3a2d9d
--- /dev/null
+++ b/src/kadmin/config.mk/template
@@ -0,0 +1,142 @@
+# $Id$
+# $Source$
+
+export TOP
+
+KRB5B4 = true
+CONFDIR = $(TOP)/config.mk
+
+ifndef CUR_DIR
+CUR_DIR = .
+endif
+
+#
+# get the os name
+#
+
+include $(CONFDIR)/architecture
+
+#
+# Programs
+#
+IMAKE = imake
+# The purpose of this variable setting is to prevent -w from being
+# passed down via environment variables into sub-makes that use SunOS
+# Make rather than GNU make.
+ifndef MAKE_PRINT_DIRECTORY
+MAKE := $(MAKE) --no-print-directory MAKEFLAGS=$(MAKEFLAGS) MFLAGS=$(MFLAGS)
+endif
+CC = gcc
+AR = ar
+RANLIB = ranlib
+LD = ld
+RM = rm
+CLEAN = rm -f
+MV = mv
+LN = ln
+LNSOFT = $(LN) -s
+MAKEDEPEND = makedepend
+RPCGEN = rpcgen
+PERL = /usr/local/bin/perl
+DUMPPERL = /usr/local/bin/perl.static
+UNDUMP = undump
+YACC = $(TOP)/scripts/newyacc.sh
+GENPROT = $(TOP)/scripts/genproto.sh
+INSTCMD = install -c
+export INSTCMD
+PURIFY = purify
+PROOF = proof
+QUANTIFY = quantify
+LEX_LIB = -ll
+PERL = /usr/local/bin/perl
+OBJDUMP = /usr/local/bin/gobjdump
+OBJCOPY = /usr/local/bin/gobjcopy
+
+# Dejagnu variables
+
+# We have to set the host with --host so that setup_xfail will work.
+# If we don't set it, then the host type used is "native", which
+# doesn't match "*-*-*".
+
+DEJAFLAGS := $(DEJALFLAGS) $(CLFLAGS) --debug --host \
+ unknown-$(shell uname -m)-$(shell uname -s)$(shell uname -r)
+ifdef VERBOSE_TEST
+DEJAFLAGS += --verbose
+endif
+
+RUNTEST := runtest $(DEJAFLAGS)
+
+#
+# Flags. Since this is the initial setting, don't preserve current
+# values; otherwise, recursive makes will get the sum of everything.
+#
+YFLAGS = -d
+ARFLAGS = cru
+CFLAGS := $(CLFLAGS)
+LDFLAGS := $(CLFLAGS)
+
+#
+# The default target is "all". Put this before any includes, in case
+# the includes define new targets. Or perhaps they should be allowed
+# to define a new default target...
+#
+all::
+
+#
+# Get a unique number for files built in /tmp
+#
+__PID__ := $(shell echo $$$$)
+
+include $(CONFDIR)/config
+
+include $(CONFDIR)/rules
+
+expand IncludeArchFile
+
+include $(CONFDIR)/site.def
+
+ifdef STRIP_INSTALLED
+INST_PROG_FLAGS = -s
+endif
+
+# avoid makefiles from failing on default rules
+expand DefaultRules
+
+# include dependencies
+ifeq ($(shell [ -r Makefile.depend ] && echo yes),yes)
+include Makefile.depend
+endif
+
+# disable RCS frobbing
+% :: RCS/%,v
+
+# fix lex rule
+.l.c:
+ $(RM) -f $@
+ $(LEX) $(LFLAGS) -t $< > $@
+
+# error table rule
+.SUFFIXES: .et
+.et.c:
+ $(COMPILE_ET) $<
+.et.h:
+ $(COMPILE_ET) $<
+
+# rpcgen rule
+.SUFFIXES: .x
+.x.c:
+ $(RPCGEN) -o $@ -c $<
+.x.h:
+ $(RPCGEN) -o $@ -h $<
+
+# command table rule
+.SUFFIXES: .ct
+.ct.c:
+ $(MK_CMDS) $<
+.ct.h:
+ $(MK_CMDS) $<
+
+CMD="echo 'You must specify CMD to use the 'execute' rule.'; exit 1"
+
+execute:
+ @eval $(CMD)
diff --git a/src/kadmin/configure.in b/src/kadmin/configure.in
index 12d4f04ef..959626b36 100644
--- a/src/kadmin/configure.in
+++ b/src/kadmin/configure.in
@@ -1,5 +1,6 @@
AC_INIT(configure.in)
CONFIG_RULES
-CONFIG_DIRS(kpasswd v5server v5client ktutil)
+dnl CONFIG_DIRS(kpasswd v5server v5client)
+CONFIG_DIRS(create export import keytab cli dbutil passwd ktutil server v4server)
DO_SUBDIRS
V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/create/ChangeLog b/src/kadmin/create/ChangeLog
new file mode 100644
index 000000000..0c724422d
--- /dev/null
+++ b/src/kadmin/create/ChangeLog
@@ -0,0 +1,9 @@
+Fri Jul 12 14:43:56 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in (USE_GSSAPI_LIBRARY): shared libraries require that
+ all symbols be resolved, even if they are not used by the
+ executeable. Thus, create needs to link against gssapi
+
+Wed Jul 10 01:24:29 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in, configure.in: added autoconf support
diff --git a/src/kadmin/create/Makefile.in b/src/kadmin/create/Makefile.in
new file mode 100644
index 000000000..547bd39c6
--- /dev/null
+++ b/src/kadmin/create/Makefile.in
@@ -0,0 +1,15 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+PROG = kdb5_create
+OBJS = kdb5_create.o kadm5_create.o string_table.o
+
+all:: $(PROG)
+
+$(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/create/Makefile.ov b/src/kadmin/create/Makefile.ov
new file mode 100644
index 000000000..cdec414b7
--- /dev/null
+++ b/src/kadmin/create/Makefile.ov
@@ -0,0 +1,12 @@
+TOP = ..
+include $(TOP)/config.mk/template
+
+PROG = kadmin_create
+SRCS = kdb5_create.c kadm5_create.c string_table.c
+OBJS = kdb5_create.o kadm5_create.o string_table.o
+
+LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) \
+ $(LIBDYN) $(NDBMLIB) $(LIBDB) $(BSDLIB) $(NETLIB)
+
+expand InstallAdmin
+expand Depend
diff --git a/src/kadmin/create/attic/Makefile.in b/src/kadmin/create/attic/Makefile.in
new file mode 100644
index 000000000..f7bd9ca38
--- /dev/null
+++ b/src/kadmin/create/attic/Makefile.in
@@ -0,0 +1,20 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+all::
+
+SRCS = $(srcdir)/ovsec_adm_create.c \
+ $(srcdir)/string_table.c
+
+OBJS = ovsec_adm_create.o \
+ string_table.o
+
+all:: ovsec_adm_create
+
+ovsec_adm_create: $(OBJS) $(DEPLIBS)
+ $(LD) $(LDFLAGS) $(LDARGS) -o ovsec_adm_create $(OBJS) $(LIBS)
+
+install::
+ $(INSTALL_PROGRAM) ./ovsec_adm_create ${DESTDIR}$(SERVER_BINDIR)/kadmind5
+
+clean::
+ $(RM) ovsec_adm_create
diff --git a/src/kadmin/create/attic/configure.in b/src/kadmin/create/attic/configure.in
new file mode 100644
index 000000000..67b8f7c52
--- /dev/null
+++ b/src/kadmin/create/attic/configure.in
@@ -0,0 +1,12 @@
+AC_INIT(ovsec_adm_create.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_KDB5_LIBRARY
+USE_DYN_LIBRARY
+USE_DB_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/create/attic/make_extern b/src/kadmin/create/attic/make_extern
new file mode 100644
index 000000000..5432edf66
--- /dev/null
+++ b/src/kadmin/create/attic/make_extern
@@ -0,0 +1,16 @@
+#!/bin/csh
+
+echo '/*'
+echo ' * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.'
+echo ' * '
+echo ' * $Header$'
+echo ' *'
+echo ' */'
+echo ' '
+echo '#ifndef _OVSEC_ADM_STRINGS_'
+echo ' '
+
+cat $1 | grep -v rcsid | grep ^char | awk '{printf "extern %s %s;\n",$1,$2}'
+
+echo ' '
+echo '#endif /* _OVSEC_ADM_STRINGS_ */'
diff --git a/src/kadmin/create/attic/ovsec_adm_create.c b/src/kadmin/create/attic/ovsec_adm_create.c
new file mode 100644
index 000000000..90be0c406
--- /dev/null
+++ b/src/kadmin/create/attic/ovsec_adm_create.c
@@ -0,0 +1,663 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ *
+ * $Log$
+ * Revision 1.23 1996/07/22 20:24:35 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.22.4.1 1996/07/18 03:01:22 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.22.2.1 1996/06/20 21:44:55 marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.22 1996/06/19 15:09:32 bjaspan
+ * changes to work in mit tree
+ *
+ * Revision 1.21 1995/11/07 23:27:28 grier
+ * Add stdlib.h
+ * Add string.h
+ *
+ * Revision 1.20 1995/08/13 16:41:11 jik
+ * Fix a nonsensical comment about the iterator() function. See PR
+ * secure-admin/470.
+ *
+ * Revision 1.19 1995/07/02 19:55:13 jik
+ * Key version numbers should start out at 1, not 0.
+ * Should get the master key version number from the master_db entry in
+ * server_kdb.c, rather than assuming that the master key version number
+ * is 0.
+ *
+ * Revision 1.18 1995/03/14 16:58:50 jik
+ * Use krb5_xfree instead of xfree if KRB5B4 is defined.
+ *
+ * Revision 1.17 1994/03/11 19:37:34 bjaspan
+ * [secure-admin/1593: ovsec_adm_create non-error messages go to stderr]
+ * [secure-releng/1608: audit secure-admin/1593: ovsec_adm_create non-error messages go to stderr]
+ *
+ * Sandbox:
+ *
+ * Normal messages should be printed to stdout rather than displayed
+ * using com_err, which will cause then to go to stderr.
+ *
+ * Revision 1.17 1994/03/09 22:21:33 jik
+ * Normal messages should be printed to stdout rather than displayed
+ * using com_err, which will cause then to go to stderr.
+ *
+ * Revision 1.16 1993/12/21 20:26:34 marc
+ * create new principals with policy NULL, not ""
+ *
+ * Revision 1.15 1993/12/14 22:51:35 marc
+ * missing * in call to krb5_random_key
+ *
+ * Revision 1.14 1993/11/27 20:42:32 bjaspan
+ * fix secure/621: coredumps with default realm
+ *
+ * Revision 1.13 1993/11/19 20:03:51 shanzer
+ * osa_adb_open_T takes a file name argument.
+ *
+ * Revision 1.12 1993/11/10 21:30:24 bjaspan
+ * move init code to main, accept -m
+ *
+ * Revision 1.11 1993/11/10 04:33:35 bjaspan
+ * rewrote adding principals to kdb, and set lifetimes
+ *
+ * Revision 1.10 1993/11/06 00:08:44 bjaspan
+ * use new OVSEC_KADM_* names, use correct realm
+ *
+ * Revision 1.9 1993/11/05 05:05:35 bjaspan
+ * added -r realm argument
+ *
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include "string_table.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ovsec_admin/adb.h>
+#include <ovsec_admin/admin.h>
+
+#include <krb5.h>
+#include <krb5/kdb.h>
+
+int add_admin_princ(void *handle, krb5_context context,
+ char *name, char *realm, int attrs, int lifetime);
+
+#define ERR 1
+#define OK 0
+
+#define ADMIN_LIFETIME 60*60*3 /* 3 hours */
+#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */
+
+char *whoami;
+
+extern krb5_encrypt_block master_encblock;
+extern krb5_keyblock master_keyblock;
+extern krb5_db_entry master_db;
+
+/*
+ * Function: main
+ *
+ * Purpose: create admin principals, create and populate admin dbs
+ *
+ * Arguments:
+ *
+ * input none
+ * <return value> exit status 1 for error 0 for success
+ *
+ * Requires:
+ *
+ *
+ * Effects:
+ *
+ *
+ * Modifies:
+ *
+ */
+
+void usage()
+{
+ fprintf(stderr, "%s\n", str_PROG_CREATE_USAGE);
+ exit(1);
+}
+
+void main(int argc, char **argv)
+{
+ char *realm = NULL;
+ int freerealm = 0;
+ int retval, from_keyboard = 0;
+ krb5_principal creator = NULL;
+ void *handle;
+ krb5_context context;
+
+ whoami = str_PROG_NAME_CREATE;
+
+ argc--; argv++;
+ while (argc) {
+ if (strcmp(*argv, "-r") == 0) {
+ argc--; argv++;
+ if (!argc)
+ usage();
+ realm = *argv;
+ } else if (strcmp(*argv, "-m") == 0) {
+ from_keyboard = 1;
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (argc != 0)
+ usage();
+
+ if (retval = krb5_init_context(&context))
+ exit(ERR);
+
+ if (realm == NULL) {
+ if ((retval = krb5_get_default_realm(context, &realm)) != 0)
+ exit(retval);
+ freerealm = 1;
+ }
+
+ if ((retval = ovsec_kadm_init(whoami, from_keyboard?"non-null":NULL,
+ NULL, realm,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &handle))) {
+ com_err(whoami, retval, str_INITING_KCONTEXT);
+
+ krb5_free_context(context);
+ exit(ERR);
+ }
+
+ retval = add_admin_princs(handle, context, realm);
+
+ ovsec_kadm_destroy(handle);
+ krb5_free_context(context);
+
+ if (retval)
+ exit(retval);
+
+ exit(0);
+}
+
+/*
+ * Function: build_name_with_realm
+ *
+ * Purpose: concatenate a name and a realm to form a krb5 name
+ *
+ * Arguments:
+ *
+ * name (input) the name
+ * realm (input) the realm
+ *
+ * Returns:
+ *
+ * pointer to name@realm, in allocated memory, or NULL if it
+ * cannot be allocated
+ *
+ * Requires: both strings are null-terminated
+ */
+char *build_name_with_realm(char *name, char *realm)
+{
+ char *n;
+
+ n = (char *) malloc(strlen(name) + strlen(realm) + 2);
+ sprintf(n, "%s@%s", name, realm);
+ return n;
+}
+
+/*
+ * Function: add_admin_princs
+ *
+ * Purpose: create admin principals
+ *
+ * Arguments:
+ *
+ * rseed (input) random seed
+ * realm (input) realm, or NULL for default realm
+ * <return value> (output) status, 0 for success, 1 for serious error
+ *
+ * Requires:
+ *
+ * Effects:
+ *
+ * add_admin_princs creates OVSEC_KADM_ADMIN_SERVICE,
+ * OVSEC_KADM_CHANGEPW_SERVICE, and OVSEC_KADM_HIST_PRINCIPAL. If any
+ * of these exist a message is printed. If any of these existing
+ * principal do not have the proper attributes, a warning message is
+ * printed.
+ */
+int add_admin_princs(void *handle, krb5_context context, char *realm)
+{
+ krb5_error_code ret = 0;
+
+ if ((ret = add_admin_princ(handle, context,
+ OVSEC_KADM_ADMIN_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED,
+ ADMIN_LIFETIME)))
+ goto clean_and_exit;
+
+ if ((ret = add_admin_princ(handle, context,
+ OVSEC_KADM_CHANGEPW_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED |
+ KRB5_KDB_PWCHANGE_SERVICE,
+ CHANGEPW_LIFETIME)))
+ goto clean_and_exit;
+
+#if 0
+ /* this is now done inside kdb_init_hist in the admin server */
+
+ if ((ret = add_admin_princ(handle, context,
+ OVSEC_KADM_HIST_PRINCIPAL, realm,
+ KRB5_KDB_DISALLOW_ALL_TIX,
+ 0)))
+ goto clean_and_exit;
+#endif
+
+clean_and_exit:
+
+ return ret;
+}
+
+/*
+ * Function: add_admin_princ
+ *
+ * Arguments:
+ *
+ * creator (r) principal to use as "mod_by"
+ * rseed (r) seed for random key generator
+ * name (r) principal name
+ * realm (r) realm name for principal
+ * attrs (r) principal's attributes
+ * lifetime (r) principal's max life, or 0
+ * not_unique (r) error message for multiple entries, never used
+ * exists (r) warning message for principal exists
+ * wrong_attrs (r) warning message for wrong attributes
+ *
+ * Returns:
+ *
+ * OK on success
+ * ERR on serious errors
+ *
+ * Effects:
+ *
+ * If the principal is not unique, not_unique is printed (but this
+ * never happens). If the principal exists, then exists is printed
+ * and if the principals attributes != attrs, wrong_attrs is printed.
+ * Otherwise, the principal is created with mod_by creator and
+ * attributes attrs and max life of lifetime (if not zero).
+ */
+
+int add_admin_princ(void *handle, krb5_context context,
+ char *name, char *realm, int attrs, int lifetime)
+{
+ char *fullname;
+ int nprincs;
+ krb5_error_code ret;
+ ovsec_kadm_principal_ent_rec ent;
+
+ memset(&ent, 0, sizeof(ent));
+
+ fullname = build_name_with_realm(name, realm);
+ if (ret = krb5_parse_name(context, fullname, &ent.principal)) {
+ com_err(whoami, ret, str_PARSE_NAME);
+ return(ERR);
+ }
+ ent.max_life = lifetime;
+ ent.attributes = attrs;
+
+ if (ret = ovsec_kadm_create_principal(handle, &ent,
+ (OVSEC_KADM_PRINCIPAL |
+ OVSEC_KADM_MAX_LIFE |
+ OVSEC_KADM_ATTRIBUTES),
+ "to-be-random")) {
+ if (ret == OVSEC_KADM_DUP)
+ ret = ovsec_kadm_modify_principal(handle, &ent,
+ (OVSEC_KADM_PRINCIPAL |
+ OVSEC_KADM_MAX_LIFE |
+ OVSEC_KADM_ATTRIBUTES));
+
+ if (ret) {
+ com_err(whoami, ret, str_PUT_PRINC, fullname);
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+ return ERR;
+ }
+ }
+
+ ret = ovsec_kadm_randkey_principal(handle, ent.principal, NULL);
+
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+
+ if (ret) {
+ com_err(whoami, ret, str_RANDOM_KEY, fullname);
+ return ERR;
+ }
+
+ return OK;
+}
+
+#if 0
+/*
+ * Function: main
+ *
+ * Purpose: Return "garbage" if the caller asks for it.
+ *
+ * Arguments:
+ *
+ * input (input) A null-terminated string,
+ * or NULL.
+ * delay (input/output) The number of seconds the
+ * function should delay before returning.
+ * <return value> (output) A string.
+ *
+ * Requires:
+ *
+ * "input" must either be NULL or point to an address in the
+ * program's address space. "delay" must point to an address in
+ * the program's address space.
+ *
+ * Effects:
+ *
+ * The function first sleeps for approximately the number of
+ * seconds specified in "delay".
+ *
+ * Then, if "input" is non-NULL and points to a null-terminated
+ * string which is equal to "garbage", the function sets "delay"
+ * to 42 and returns a string allocated with malloc(3) containing
+ * "more-garbage".
+ *
+ * If "input" is NULL or does not contain "garbage", the function
+ * returns NULL without modifying "delay".
+ *
+ * If "<return value>" is non-NULL, the caller should deallocate
+ * the string in it (with free(3)) when it is no longer needed.
+ *
+ * Modifies:
+ *
+ * May allocate a new block of memory in the malloc(3) arena.
+ * May change the value in the memory location pointed to by
+ * "delay".
+ */
+
+krb5_error_code add_random_princ(princ_str, princ, attrs, lifetime,
+ creator, rseed)
+ char *princ_str;
+ krb5_principal princ, creator;
+ krb5_flags attrs;
+ int lifetime;
+ krb5_pointer *rseed;
+{
+ krb5_db_entry entry;
+ krb5_error_code ret;
+ krb5_encrypted_keyblock ekey;
+ krb5_keyblock *rkey;
+ int nentries = 1;
+
+ memset((char *) &entry, 0, sizeof(entry));
+ entry.principal = princ;
+ entry.kvno = 1;
+ entry.max_life = KRB5_KDB_MAX_LIFE;
+ entry.max_renewable_life = 0;
+ entry.mkvno = master_db.mkvno;
+ entry.expiration = KRB5_KDB_EXPIRATION;
+ entry.mod_name = creator;
+ if (lifetime != 0)
+ entry.max_life = lifetime;
+
+ if (ret = krb5_timeofday(&entry.mod_date))
+ return(ret);
+
+ entry.attributes = attrs;
+
+ ret = krb5_random_key(&master_encblock, *rseed, &rkey);
+ if (ret != 0) {
+ com_err(whoami, ret, str_RANDOM_KEY, princ_str);
+ return (ERR);
+ }
+
+
+ ret = krb5_kdb_encrypt_key(&master_encblock, rkey, &ekey);
+ krb5_free_keyblock(rkey);
+ if (ret != 0) {
+ com_err(whoami, ret, str_ENCRYPT_KEY, princ_str);
+ return (ERR);
+ }
+
+ entry.key = ekey;
+ entry.salt_type = KRB5_KDB_SALTTYPE_NORMAL;
+ entry.salt_length = 0;
+ entry.salt = 0;
+
+ ret = krb5_db_put_principal(&entry, &nentries);
+ if (ret != 0)
+ com_err(whoami, ret, str_PUT_PRINC, princ_str);
+#ifdef KRB5B4
+ krb5_xfree(ekey.contents);
+#else
+ xfree(ekey.contents);
+#endif
+
+ if (ret) return(ERR);
+
+ printf(str_CREATED_PRINC, whoami, princ_str);
+
+ return(OK);
+}
+
+/*
+ * Function: create_admin_policy_db
+ *
+ * Purpose: Return "garbage" if the caller asks for it.
+ *
+ * Arguments:
+ *
+ * input (input) A null-terminated string,
+ * or NULL.
+ * delay (input/output) The number of seconds the
+ * function should delay before returning.
+ * <return value> (output) A string.
+ *
+ * Requires:
+ *
+ * "input" must either be NULL or point to an address in the
+ * program's address space. "delay" must point to an address in
+ * the program's address space.
+ *
+ * Effects:
+ *
+ * The function first sleeps for approximately the number of
+ * seconds specified in "delay".
+ *
+ * Then, if "input" is non-NULL and points to a null-terminated
+ * string which is equal to "garbage", the function sets "delay"
+ * to 42 and returns a string allocated with malloc(3) containing
+ * "more-garbage".
+ *
+ * If "input" is NULL or does not contain "garbage", the function
+ * returns NULL without modifying "delay".
+ *
+ * If "<return value>" is non-NULL, the caller should deallocate
+ * the string in it (with free(3)) when it is no longer needed.
+ *
+ * Modifies:
+ *
+ * May allocate a new block of memory in the malloc(3) arena.
+ * May change the value in the memory location pointed to by
+ * "delay".
+ */
+
+int create_admin_policy_db()
+{
+ /* We don't have a create/destroy routine, so opening the db and
+ closing it will have to do. */
+ osa_adb_policy_t policy_db = NULL;
+ osa_adb_ret_t ret;
+
+ ret = osa_adb_open_policy(&policy_db, POLICY_DB);
+ if (ret != OSA_ADB_OK) {
+ com_err (whoami, ret, str_CREATING_POLICY_DB);
+ return(-1);
+ }
+
+ /* Should create sample policies here */
+
+ ret = osa_adb_close_policy(policy_db);
+ if (ret != OSA_ADB_OK) {
+ com_err (whoami, ret, str_CLOSING_POLICY_DB);
+ return(-1);
+ }
+
+ printf(str_CREATED_POLICY_DB, whoami);
+
+ return(OK);
+}
+
+/*
+
+ * Function: iterator(ptr, entry)
+ *
+ * Purpose:
+ *
+ * Creates an entry in the Admin database corresponding to the
+ * specified entry in the Kerberos database.
+ *
+ * Arguments:
+ *
+ * ptr (input) Actually of type osa_adb_princ_t,
+ * represents the Admin database in which to
+ * create the principal.
+ * entry (input) The entry in the Kerberos database for
+ * which to create an entry in the Admin
+ * database.
+ *
+ * Requires:
+ *
+ * "ptr" represents a valid, open Admin principal database.
+ * "entry" represents a valid, decoded Kerberos database
+ * principal entry.
+ *
+ * Effects:
+ *
+ * Modifies the Admin principal database by creating a principal
+ * in the database with the same name as "entry" and no other
+ * information.
+ *
+ * Modifies:
+ *
+ * Does not modify any global memory. Modifies the Admin
+ * principal database whose handle is passed into it.
+ */
+
+krb5_error_code
+iterator(ptr, entry)
+krb5_pointer ptr;
+krb5_db_entry *entry;
+{
+ osa_adb_ret_t retval;
+ krb5_error_code retval2;
+ char *princ_str = NULL;
+ osa_princ_ent_rec osa_princ;
+
+ /* Zero the whole struct, and fill in the princ name */
+ memset(&osa_princ, 0, sizeof(osa_princ_ent_rec));
+
+ osa_princ.name = entry->principal;
+ osa_princ.policy = NULL;
+
+ retval = osa_adb_create_princ((osa_adb_princ_t) ptr, &osa_princ);
+ if (retval != OSA_ADB_OK) {
+ if (retval2 = krb5_unparse_name(entry->principal, &princ_str)) {
+ com_err(whoami, retval2, str_UNPARSE_PRINC);
+ }
+ com_err(whoami, retval, str_CREATING_PRINC_ENTRY,
+ (princ_str ? princ_str : str_A_PRINC));
+ if (princ_str) free(princ_str);
+ }
+ return (0);
+}
+
+/*
+ * Function: create_and_populate_admin_princ_db
+ *
+ * Purpose: Return "garbage" if the caller asks for it.
+ *
+ * Arguments:
+ *
+ * input (input) A null-terminated string,
+ * or NULL.
+ * delay (input/output) The number of seconds the
+ * function should delay before returning.
+ * <return value> (output) A string.
+ *
+ * Requires:
+ *
+ * "input" must either be NULL or point to an address in the
+ * program's address space. "delay" must point to an address in
+ * the program's address space.
+ *
+ * Effects:
+ *
+ * The function first sleeps for approximately the number of
+ * seconds specified in "delay".
+ *
+ * Then, if "input" is non-NULL and points to a null-terminated
+ * string which is equal to "garbage", the function sets "delay"
+ * to 42 and returns a string allocated with malloc(3) containing
+ * "more-garbage".
+ *
+ * If "input" is NULL or does not contain "garbage", the function
+ * returns NULL without modifying "delay".
+ *
+ * If "<return value>" is non-NULL, the caller should deallocate
+ * the string in it (with free(3)) when it is no longer needed.
+ *
+ * Modifies:
+ *
+ * May allocate a new block of memory in the malloc(3) arena.
+ * May change the value in the memory location pointed to by
+ * "delay".
+ */
+
+int create_and_populate_admin_princ_db()
+{
+ osa_adb_princ_t princ_db = NULL;
+ osa_adb_ret_t ret;
+
+ /* We don't have a create/destroy routine, so opening the db and
+ closing it will have to do. */
+
+ ret = osa_adb_open_princ(&princ_db, PRINCIPAL_DB);
+ if (ret != OSA_ADB_OK) {
+ com_err (whoami, ret, str_CREATING_PRINC_DB);
+ return(-1);
+ }
+
+ printf(str_CREATED_PRINC_DB, whoami);
+
+ (void) krb5_db_iterate(iterator, princ_db);
+
+ ret = osa_adb_close_princ(princ_db);
+ if (ret != OSA_ADB_OK) {
+ com_err (whoami, ret, str_CLOSING_PRINC_DB);
+ return(-1);
+ }
+
+
+ return(OK);
+}
+
+#endif
diff --git a/src/kadmin/create/configure.in b/src/kadmin/create/configure.in
new file mode 100644
index 000000000..403034263
--- /dev/null
+++ b/src/kadmin/create/configure.in
@@ -0,0 +1,11 @@
+AC_INIT(kdb5_create.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+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/create/kadm5_create.c b/src/kadmin/create/kadm5_create.c
new file mode 100644
index 000000000..33b30ec9c
--- /dev/null
+++ b/src/kadmin/create/kadm5_create.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include "string_table.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kadm5/adb.h>
+#include <kadm5/admin.h>
+
+#include <krb5.h>
+#include <krb5/kdb.h>
+
+int add_admin_princ(void *handle, krb5_context context,
+ char *name, char *realm, int attrs, int lifetime);
+
+#define ERR 1
+#define OK 0
+
+#define ADMIN_LIFETIME 60*60*3 /* 3 hours */
+#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */
+
+extern char *whoami;
+
+extern krb5_encrypt_block master_encblock;
+extern krb5_keyblock master_keyblock;
+extern krb5_db_entry master_db;
+
+/*
+ * Function: kadm5_create
+ *
+ * Purpose: create admin principals in KDC database
+ *
+ * Arguments: params (r) configuration parameters to use
+ *
+ * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE
+ * principals in the KDC database and sets their attributes
+ * appropriately.
+ */
+void kadm5_create(kadm5_config_params *params)
+{
+ int retval;
+ void *handle;
+ krb5_context context;
+ FILE *f;
+
+
+ if (retval = krb5_init_context(&context))
+ exit(ERR);
+
+ /*
+ * The lock file has to exist before calling kadm5_init, but
+ * params->admin_lockfile may not be set yet...
+ */
+ if (retval = kadm5_get_config_params(context, NULL, NULL,
+ params, params)) {
+ com_err(whoami, retval, str_INITING_KCONTEXT);
+ exit(1);
+ }
+
+ if (retval = osa_adb_create_policy_db(params)) {
+ com_err(whoami, retval, str_CREATING_POLICY_DB);
+ exit(1);
+ }
+
+ if ((retval = kadm5_init(whoami, NULL, NULL, params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &handle))) {
+ com_err(whoami, retval, str_INITING_KCONTEXT);
+
+ krb5_free_context(context);
+ exit(ERR);
+ }
+
+ retval = add_admin_princs(handle, context, params->realm);
+
+ kadm5_destroy(handle);
+ krb5_free_context(context);
+
+ if (retval)
+ exit(retval);
+
+ exit(0);
+}
+
+/*
+ * Function: build_name_with_realm
+ *
+ * Purpose: concatenate a name and a realm to form a krb5 name
+ *
+ * Arguments:
+ *
+ * name (input) the name
+ * realm (input) the realm
+ *
+ * Returns:
+ *
+ * pointer to name@realm, in allocated memory, or NULL if it
+ * cannot be allocated
+ *
+ * Requires: both strings are null-terminated
+ */
+char *build_name_with_realm(char *name, char *realm)
+{
+ char *n;
+
+ n = (char *) malloc(strlen(name) + strlen(realm) + 2);
+ sprintf(n, "%s@%s", name, realm);
+ return n;
+}
+
+/*
+ * Function: add_admin_princs
+ *
+ * Purpose: create admin principals
+ *
+ * Arguments:
+ *
+ * rseed (input) random seed
+ * realm (input) realm, or NULL for default realm
+ * <return value> (output) status, 0 for success, 1 for serious error
+ *
+ * Requires:
+ *
+ * Effects:
+ *
+ * add_admin_princs creates KADM5_ADMIN_SERVICE,
+ * KADM5_CHANGEPW_SERVICE. If any of these exist a message is
+ * printed. If any of these existing principal do not have the proper
+ * attributes, a warning message is printed.
+ */
+int add_admin_princs(void *handle, krb5_context context, char *realm)
+{
+ krb5_error_code ret = 0;
+
+ if ((ret = add_admin_princ(handle, context,
+ KADM5_ADMIN_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED,
+ ADMIN_LIFETIME)))
+ goto clean_and_exit;
+
+ if ((ret = add_admin_princ(handle, context,
+ KADM5_CHANGEPW_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED |
+ KRB5_KDB_PWCHANGE_SERVICE,
+ CHANGEPW_LIFETIME)))
+ goto clean_and_exit;
+
+clean_and_exit:
+
+ return ret;
+}
+
+/*
+ * Function: add_admin_princ
+ *
+ * Arguments:
+ *
+ * creator (r) principal to use as "mod_by"
+ * rseed (r) seed for random key generator
+ * name (r) principal name
+ * realm (r) realm name for principal
+ * attrs (r) principal's attributes
+ * lifetime (r) principal's max life, or 0
+ * not_unique (r) error message for multiple entries, never used
+ * exists (r) warning message for principal exists
+ * wrong_attrs (r) warning message for wrong attributes
+ *
+ * Returns:
+ *
+ * OK on success
+ * ERR on serious errors
+ *
+ * Effects:
+ *
+ * If the principal is not unique, not_unique is printed (but this
+ * never happens). If the principal exists, then exists is printed
+ * and if the principals attributes != attrs, wrong_attrs is printed.
+ * Otherwise, the principal is created with mod_by creator and
+ * attributes attrs and max life of lifetime (if not zero).
+ */
+
+int add_admin_princ(void *handle, krb5_context context,
+ char *name, char *realm, int attrs, int lifetime)
+{
+ char *fullname;
+ int nprincs;
+ krb5_error_code ret;
+ kadm5_principal_ent_rec ent;
+
+ memset(&ent, 0, sizeof(ent));
+
+ fullname = build_name_with_realm(name, realm);
+ if (ret = krb5_parse_name(context, fullname, &ent.principal)) {
+ com_err(whoami, ret, str_PARSE_NAME);
+ return(ERR);
+ }
+ ent.max_life = lifetime;
+ ent.attributes = attrs;
+
+ if (ret = kadm5_create_principal(handle, &ent,
+ (KADM5_PRINCIPAL |
+ KADM5_MAX_LIFE |
+ KADM5_ATTRIBUTES),
+ "to-be-random")) {
+ if (ret == KADM5_DUP)
+ ret = kadm5_modify_principal(handle, &ent,
+ (KADM5_PRINCIPAL |
+ KADM5_MAX_LIFE |
+ KADM5_ATTRIBUTES));
+
+ if (ret) {
+ com_err(whoami, ret, str_PUT_PRINC, fullname);
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+ return ERR;
+ }
+ }
+
+ ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL);
+
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+
+ if (ret) {
+ com_err(whoami, ret, str_RANDOM_KEY, fullname);
+ return ERR;
+ }
+
+ return OK;
+}
diff --git a/src/kadmin/create/kdb5_create.c b/src/kadmin/create/kdb5_create.c
new file mode 100644
index 000000000..8b167b6b9
--- /dev/null
+++ b/src/kadmin/create/kdb5_create.c
@@ -0,0 +1,536 @@
+/*
+ * admin/create/kdb5_create.c
+ *
+ * Copyright 1990,1991 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.
+ *
+ *
+ * Generate (from scratch) a Kerberos KDC database.
+ */
+
+#include <stdio.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+
+enum ap_op {
+ NULL_KEY, /* setup null keys */
+ MASTER_KEY, /* use master key as new key */
+ TGT_KEY /* special handling for tgt key */
+};
+
+krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL };
+
+struct realm_info {
+ krb5_deltat max_life;
+ krb5_deltat max_rlife;
+ krb5_timestamp expiration;
+ krb5_flags flags;
+ krb5_encrypt_block *eblock;
+ krb5_pointer rseed;
+ krb5_int32 nkslist;
+ krb5_key_salt_tuple *kslist;
+} rblock = { /* XXX */
+ KRB5_KDB_MAX_LIFE,
+ KRB5_KDB_MAX_RLIFE,
+ KRB5_KDB_EXPIRATION,
+ KRB5_KDB_DEF_FLAGS,
+ (krb5_encrypt_block *) NULL,
+ (krb5_pointer) NULL,
+ 1,
+ &def_kslist
+};
+
+struct iterate_args {
+ krb5_context ctx;
+ struct realm_info *rblock;
+ krb5_db_entry *dbentp;
+};
+
+static krb5_error_code add_principal
+ PROTOTYPE((krb5_context,
+ krb5_principal,
+ enum ap_op,
+ struct realm_info *));
+
+/*
+ * Steps in creating a database:
+ *
+ * 1) use the db calls to open/create a new database
+ *
+ * 2) get a realm name for the new db
+ *
+ * 3) get a master password for the new db; convert to an encryption key.
+ *
+ * 4) create various required entries in the database
+ *
+ * 5) close & exit
+ */
+
+static void
+usage(who, status)
+char *who;
+int status;
+{
+ fprintf(stderr, "usage: %s [-d dbpathname] [-r realmname] [-k enctype]\n\
+\t[-M mkeyname]\n",
+ who);
+ exit(status);
+}
+
+krb5_keyblock master_keyblock;
+krb5_principal master_princ;
+krb5_encrypt_block master_encblock;
+krb5_data master_salt;
+
+krb5_data tgt_princ_entries[] = {
+ {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
+ {0, 0, 0} };
+
+krb5_data db_creator_entries[] = {
+ {0, sizeof("db_creation")-1, "db_creation"} };
+
+/* XXX knows about contents of krb5_principal, and that tgt names
+ are of form TGT/REALM@REALM */
+krb5_principal_data tgt_princ = {
+ 0, /* magic number */
+ {0, 0, 0}, /* krb5_data realm */
+ tgt_princ_entries, /* krb5_data *data */
+ 2, /* int length */
+ KRB5_NT_SRV_INST /* int type */
+};
+
+krb5_principal_data db_create_princ = {
+ 0, /* magic number */
+ {0, 0, 0}, /* krb5_data realm */
+ db_creator_entries, /* krb5_data *data */
+ 1, /* int length */
+ KRB5_NT_SRV_INST /* int type */
+};
+
+char *mkey_password = 0;
+char *whoami;
+
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ extern char *optarg;
+ int optchar;
+
+ krb5_error_code retval;
+ char *dbname = (char *) NULL;
+ char *realm = 0;
+ char *mkey_name = 0;
+ char *mkey_fullname;
+ char *defrealm;
+ char *pw_str = 0;
+ char *keyfile = 0;
+ int pw_size = 0;
+ int enctypedone = 0;
+ int do_stash = 0;
+ krb5_data pwd;
+ krb5_context context;
+ krb5_realm_params *rparams;
+ kadm5_config_params kadm5_params;
+
+ memset(&kadm5_params, 0, sizeof(kadm5_params));
+
+ krb5_init_context(&context);
+ krb5_init_ets(context);
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+ whoami = argv[0];
+
+ kadm5_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
+ kadm5_params.mkey_from_kbd = 1;
+
+ while ((optchar = getopt(argc, argv, "d:r:k:M:e:P:sf:")) != EOF) {
+ switch(optchar) {
+ case 'd': /* set db name */
+ kadm5_params.dbname = dbname = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_DBNAME;
+ break;
+ case 'r':
+ kadm5_params.realm = realm = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_REALM;
+ break;
+ case 'k':
+ if (!krb5_string_to_enctype(optarg, &master_keyblock.enctype)){
+ enctypedone++;
+ kadm5_params.enctype = master_keyblock.enctype;
+ kadm5_params.mask |= KADM5_CONFIG_ENCTYPE;
+ }
+ else
+ com_err(argv[0], 0, "%s is an invalid enctype", optarg);
+ break;
+ case 's':
+ do_stash++;
+ kadm5_params.mkey_from_kbd = 0;
+ break;
+ case 'f':
+ kadm5_params.stash_file = keyfile = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_STASH_FILE;
+ break;
+ case 'M': /* master key name in DB */
+ kadm5_params.mkey_name = mkey_name = optarg;
+ kadm5_params.mask |= KADM5_CONFIG_MKEY_NAME;
+ break;
+ case 'P': /* Only used for testing!!! */
+ mkey_password = optarg;
+ break;
+ case '?':
+ default:
+ usage(argv[0], 1);
+ /*NOTREACHED*/
+ }
+ }
+
+ /*
+ * Attempt to read the KDC profile. If we do, then read appropriate values
+ * from it and augment values supplied on the command line.
+ */
+ if (!(retval = krb5_read_realm_params(context,
+ realm,
+ (char *) NULL,
+ (char *) NULL,
+ &rparams))) {
+ /* Get the value for the database */
+ if (rparams->realm_dbname && !dbname)
+ dbname = strdup(rparams->realm_dbname);
+
+ /* Get the value for the master key name */
+ if (rparams->realm_mkey_name && !mkey_name)
+ mkey_name = strdup(rparams->realm_mkey_name);
+
+ /* Get the value for the master key type */
+ if (rparams->realm_enctype_valid && !enctypedone) {
+ master_keyblock.enctype = rparams->realm_enctype;
+ enctypedone++;
+ }
+
+ /* Get the value for maximum ticket lifetime. */
+ if (rparams->realm_max_life_valid)
+ rblock.max_life = rparams->realm_max_life;
+
+ /* Get the value for maximum renewable ticket lifetime. */
+ if (rparams->realm_max_rlife_valid)
+ rblock.max_rlife = rparams->realm_max_rlife;
+
+ /* Get the value for the default principal expiration */
+ if (rparams->realm_expiration_valid)
+ rblock.expiration = rparams->realm_expiration;
+
+ /* Get the value for the default principal flags */
+ if (rparams->realm_flags_valid)
+ rblock.flags = rparams->realm_flags;
+
+ /* Get the value of the supported key/salt pairs */
+ if (rparams->realm_num_keysalts) {
+ rblock.nkslist = rparams->realm_num_keysalts;
+ rblock.kslist = rparams->realm_keysalts;
+ rparams->realm_num_keysalts = 0;
+ rparams->realm_keysalts = (krb5_key_salt_tuple *) NULL;
+ }
+
+ /* Get the value for the stash file */
+ if (rparams->realm_stash_file && !keyfile)
+ keyfile = strdup(rparams->realm_stash_file);
+
+ krb5_free_realm_params(context, rparams);
+ }
+
+ if (!dbname)
+ dbname = DEFAULT_KDB_FILE;
+
+ if (!enctypedone)
+ master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
+
+ if (!valid_enctype(master_keyblock.enctype)) {
+ char tmp[32];
+ if (krb5_enctype_to_string(master_keyblock.enctype, tmp, sizeof(tmp)))
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d", master_keyblock.enctype);
+ else
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp);
+ exit(1);
+ }
+
+ krb5_use_enctype(context, &master_encblock, master_keyblock.enctype);
+
+ retval = krb5_db_set_name(context, dbname);
+ if (!retval) retval = EEXIST;
+
+ if (retval == EEXIST || retval == EACCES || retval == EPERM) {
+ /* it exists ! */
+ com_err(argv[0], 0, "The database '%s' appears to already exist",
+ dbname);
+ exit(1);
+ }
+ if (!realm) {
+ if ((retval = krb5_get_default_realm(context, &defrealm))) {
+ com_err(argv[0], retval, "while retrieving default realm name");
+ exit(1);
+ }
+ realm = defrealm;
+ }
+
+ /* assemble & parse the master key name */
+
+ if ((retval = krb5_db_setup_mkey_name(context, mkey_name, realm,
+ &mkey_fullname, &master_princ))) {
+ com_err(argv[0], retval, "while setting up master key name");
+ exit(1);
+ }
+
+ krb5_princ_set_realm_data(context, &db_create_princ, realm);
+ krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm));
+ krb5_princ_set_realm_data(context, &tgt_princ, realm);
+ krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm));
+ krb5_princ_component(context, &tgt_princ,1)->data = realm;
+ krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm);
+
+ printf("Initializing database '%s' for realm '%s',\n\
+master key name '%s'\n",
+ dbname, realm, mkey_fullname);
+
+ if (!mkey_password) {
+ printf("You will be prompted for the database Master Password.\n");
+ printf("It is important that you NOT FORGET this password.\n");
+ fflush(stdout);
+
+ pw_size = 1024;
+ pw_str = malloc(pw_size);
+
+ retval = krb5_read_password(context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
+ pw_str, &pw_size);
+ if (retval) {
+ com_err(argv[0], retval, "while reading master key from keyboard");
+ exit(1);
+ }
+ mkey_password = pw_str;
+ }
+
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ retval = krb5_principal2salt(context, master_princ, &master_salt);
+ if (retval) {
+ com_err(argv[0], retval, "while calculated master key salt");
+ exit(1);
+ }
+ if (retval = krb5_string_to_key(context, &master_encblock,
+ &master_keyblock, &pwd, &master_salt)) {
+ com_err(argv[0], retval, "while transforming master key from password");
+ exit(1);
+ }
+
+ if ((retval = krb5_process_key(context, &master_encblock,
+ &master_keyblock))) {
+ com_err(argv[0], retval, "while processing master key");
+ exit(1);
+ }
+
+ rblock.eblock = &master_encblock;
+ if ((retval = krb5_init_random_key(context, &master_encblock,
+ &master_keyblock, &rblock.rseed))) {
+ com_err(argv[0], retval, "while initializing random key generator");
+ (void) krb5_finish_key(context, &master_encblock);
+ exit(1);
+ }
+ if ((retval = krb5_db_create(context, dbname))) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while creating database '%s'",
+ dbname);
+ exit(1);
+ }
+ if ((retval = krb5_db_set_name(context, dbname))) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while setting active database to '%s'",
+ dbname);
+ exit(1);
+ }
+ if ((retval = krb5_db_init(context))) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while initializing the database '%s'",
+ dbname);
+ exit(1);
+ }
+
+ if ((retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) ||
+ (retval = add_principal(context, &tgt_princ, TGT_KEY, &rblock))) {
+ (void) krb5_db_fini(context);
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while adding entries to the database");
+ exit(1);
+ }
+ if (do_stash &&
+ ((retval = krb5_db_store_mkey(context, keyfile, master_princ,
+ &master_keyblock)))) {
+ com_err(argv[0], errno, "while storing key");
+ printf("Warning: couldn't stash master key.\n");
+ }
+ /* clean up */
+ (void) krb5_db_fini(context);
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ free(master_keyblock.contents);
+ if (pw_str) {
+ memset(pw_str, 0, pw_size);
+ free(pw_str);
+ }
+ free(master_salt.data);
+
+ kadm5_create(&kadm5_params);
+
+ exit(0);
+
+}
+
+static krb5_error_code
+tgt_keysalt_iterate(ksent, ptr)
+ krb5_key_salt_tuple *ksent;
+ krb5_pointer ptr;
+{
+ krb5_context context;
+ krb5_error_code kret;
+ struct iterate_args *iargs;
+ krb5_keyblock random_keyblock, *key;
+ krb5_int32 ind;
+ krb5_encrypt_block random_encblock;
+ krb5_pointer rseed;
+ krb5_data pwd;
+
+ iargs = (struct iterate_args *) ptr;
+ kret = 0;
+
+ context = iargs->ctx;
+
+ /*
+ * Convert the master key password into a key for this particular
+ * encryption system.
+ */
+ krb5_use_enctype(context, &random_encblock, ksent->ks_enctype);
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ if (kret = krb5_string_to_key(context, &random_encblock, &random_keyblock,
+ &pwd, &master_salt))
+ return kret;
+ if ((kret = krb5_init_random_key(context, &random_encblock,
+ &random_keyblock, &rseed)))
+ return kret;
+
+ if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
+ ind = iargs->dbentp->n_key_data-1;
+ if (!(kret = krb5_random_key(context,
+ &random_encblock, rseed,
+ &key))) {
+ kret = krb5_dbekd_encrypt_key_data(context,
+ iargs->rblock->eblock,
+ key,
+ NULL,
+ 1,
+ &iargs->dbentp->key_data[ind]);
+ krb5_free_keyblock(context, key);
+ }
+ }
+ memset((char *)random_keyblock.contents, 0, random_keyblock.length);
+ free(random_keyblock.contents);
+ (void) krb5_finish_random_key(context, &random_encblock, &rseed);
+ return(kret);
+}
+
+static krb5_error_code
+add_principal(context, princ, op, pblock)
+ krb5_context context;
+ krb5_principal princ;
+ enum ap_op op;
+ struct realm_info *pblock;
+{
+ krb5_error_code retval;
+ krb5_db_entry entry;
+
+ krb5_timestamp now;
+ struct iterate_args iargs;
+
+ int nentries = 1;
+
+ memset((char *) &entry, 0, sizeof(entry));
+
+ entry.len = KRB5_KDB_V1_BASE_LENGTH;
+ entry.attributes = pblock->flags;
+ entry.max_life = pblock->max_life;
+ entry.max_renewable_life = pblock->max_rlife;
+ entry.expiration = pblock->expiration;
+
+ if ((retval = krb5_copy_principal(context, princ, &entry.princ)))
+ goto error_out;
+
+ if ((retval = krb5_timeofday(context, &now)))
+ goto error_out;
+
+ if ((retval = krb5_dbe_update_mod_princ_data(context, &entry,
+ now, &db_create_princ)))
+ goto error_out;
+
+ switch (op) {
+ case MASTER_KEY:
+ if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))
+ == NULL)
+ goto error_out;
+ memset((char *) entry.key_data, 0, sizeof(krb5_key_data));
+ entry.n_key_data = 1;
+
+ entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock,
+ &master_keyblock, NULL,
+ 1, entry.key_data)))
+ return retval;
+ break;
+ case TGT_KEY:
+ iargs.ctx = context;
+ iargs.rblock = pblock;
+ iargs.dbentp = &entry;
+ /*
+ * Iterate through the key/salt list, ignoring salt types.
+ */
+ if ((retval = krb5_keysalt_iterate(pblock->kslist,
+ pblock->nkslist,
+ 1,
+ tgt_keysalt_iterate,
+ (krb5_pointer) &iargs)))
+ return retval;
+ break;
+ case NULL_KEY:
+ return EOPNOTSUPP;
+ default:
+ break;
+ }
+
+ retval = krb5_db_put_principal(context, &entry, &nentries);
+
+error_out:;
+ krb5_dbe_free_contents(context, &entry);
+ return retval;
+}
diff --git a/src/kadmin/create/string_table.c b/src/kadmin/create/string_table.c
new file mode 100644
index 000000000..b9f86a363
--- /dev/null
+++ b/src/kadmin/create/string_table.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+/* String table of messages for kadm5_create */
+
+char *str_INITING_KCONTEXT = "while initializing the kerberos context";
+
+char *str_PARSE_NAME = "while parsing admin principal name.";
+
+char *str_HISTORY_PARSE_NAME = "while parsing admin history principal name.";
+
+char *str_ADMIN_PRINC_EXISTS = "Warning! Admin principal already exists.";
+
+char *str_CHANGEPW_PRINC_EXISTS = "Warning! Changepw principal already exists.";
+
+char *str_HISTORY_PRINC_EXISTS = "Warning! Admin history principal already exists.";
+
+char *str_ADMIN_PRINC_WRONG_ATTRS =
+ "Warning! Admin principal has incorrect attributes.\n"
+ "\tDISALLOW_TGT should be set, and max_life should be three hours.\n"
+ "\tThis program will leave them as-is, but beware!.";
+
+char *str_CHANGEPW_PRINC_WRONG_ATTRS =
+ "Warning! Changepw principal has incorrect attributes.\n"
+ "\tDISALLOW_TGT and PW_CHANGE_SERVICE should both be set, and "
+ "max_life should be five minutes.\n"
+ "\tThis program will leave them as-is, but beware!.";
+
+char *str_HISTORY_PRINC_WRONG_ATTRS =
+ "Warning! Admin history principal has incorrect attributes.\n"
+ "\tDISALLOW_ALL_TIX should be set.\n"
+ "\tThis program will leave it as-is, but beware!.";
+
+char *str_CREATED_PRINC_DB =
+ "%s: Admin principal database created (or it already existed).\n"; /* whoami */
+
+char *str_CREATED_POLICY_DB =
+ "%s: Admin policy database created (or it already existed).\n"; /* whoami */
+
+char *str_RANDOM_KEY =
+ "while calling random key for %s."; /* principal name */
+
+char *str_ENCRYPT_KEY =
+ "while calling encrypt key for %s."; /* principal name */
+
+char *str_PUT_PRINC =
+ "while calling storing %s in Kerberos database."; /* principal name */
+
+char *str_CREATING_POLICY_DB = "while creating/opening admin policy database.";
+
+char *str_CLOSING_POLICY_DB = "while closing admin policy database.";
+
+char *str_CREATING_PRINC_DB = "while creating/opening admin principal database.";
+
+char *str_CLOSING_PRINC_DB = "while closing admin principal database.";
+
+char *str_CREATING_PRINC_ENTRY =
+ "while creating admin principal database entry for %s."; /* princ_name */
+
+char *str_A_PRINC = "a principal";
+
+char *str_UNPARSE_PRINC = "while unparsing principal.";
+
+char *str_CREATED_PRINC = "%s: Created %s principal.\n"; /* whoami, princ_name */
+
+char *str_INIT_KDB = "while initializing kdb.";
+
+char *str_NO_KDB =
+"while initializing kdb.\nThe Kerberos KDC database needs to exist in /krb5.\n\
+If you haven't run kdb5_create you need to do so before running this command.";
+
+
+char *str_INIT_RANDOM_KEY = "while initializing random key generator.";
+
+char *str_TOO_MANY_ADMIN_PRINC =
+ "while fetching admin princ. Can only have one admin principal.";
+
+char *str_TOO_MANY_CHANGEPW_PRINC =
+ "while fetching changepw princ. Can only have one changepw principal.";
+
+char *str_TOO_MANY_HIST_PRINC =
+ "while fetching history princ. Can only have one history principal.";
+
+char *str_WHILE_DESTROYING_ADMIN_SESSION = "while closing session with admin server and destroying tickets.";
diff --git a/src/kadmin/create/string_table.h b/src/kadmin/create/string_table.h
new file mode 100644
index 000000000..e8cb45367
--- /dev/null
+++ b/src/kadmin/create/string_table.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ */
+
+#ifndef _OVSEC_ADM_STRINGS_
+
+extern char *str_INITING_KCONTEXT;
+extern char *str_PARSE_NAME;
+extern char *str_HISTORY_PARSE_NAME;
+extern char *str_ADMIN_PRINC_EXISTS;
+extern char *str_CHANGEPW_PRINC_EXISTS;
+extern char *str_HISTORY_PRINC_EXISTS;
+extern char *str_ADMIN_PRINC_WRONG_ATTRS;
+extern char *str_CHANGEPW_PRINC_WRONG_ATTRS;
+extern char *str_HISTORY_PRINC_WRONG_ATTRS;
+extern char *str_CREATED_PRINC_DB;
+extern char *str_CREATED_POLICY_DB;
+extern char *str_RANDOM_KEY;
+extern char *str_ENCRYPT_KEY;
+extern char *str_PUT_PRINC;
+extern char *str_CREATING_POLICY_DB;
+extern char *str_CLOSING_POLICY_DB;
+extern char *str_CREATING_PRINC_DB;
+extern char *str_CLOSING_PRINC_DB;
+extern char *str_CREATING_PRINC_ENTRY;
+extern char *str_A_PRINC;
+extern char *str_UNPARSE_PRINC;
+extern char *str_CREATED_PRINC;
+extern char *str_INIT_KDB;
+extern char *str_NO_KDB;
+extern char *str_INIT_RANDOM_KEY;
+extern char *str_TOO_MANY_ADMIN_PRINC;
+extern char *str_TOO_MANY_CHANGEPW_PRINC;
+extern char *str_TOO_MANY_HIST_PRINC;
+extern char *str_WHILE_DESTROYING_ADMIN_SESSION;
+
+#endif /* _OVSEC_ADM_STRINGS_ */
diff --git a/src/kadmin/dbutil/ChangeLog b/src/kadmin/dbutil/ChangeLog
new file mode 100644
index 000000000..7719cc749
--- /dev/null
+++ b/src/kadmin/dbutil/ChangeLog
@@ -0,0 +1,440 @@
+Thu Jul 18 19:22:04 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed SS_RULES
+
+Wed Jul 10 19:43:22 1996 Marc Horowitz <marc@mit.edu>
+
+ * dumpv4.c (configure.in, Makefile.in): make autoconf work after
+ barry's carnage
+
+Sun May 12 00:27:44 1996 Marc Horowitz <marc@mit.edu>
+
+ * loadv4.c (enter_in_v5_db, add_principal), kdb5_edit.c
+ (create_db_entry, modent), dumpv4.c (dump_v4_iterator), dump.c
+ (dump_k5beta_iterator, process_k5beta_record): convert to use new
+ krb5_dbe_* tl_data functions.
+
+ * cpw.c (enter_pwd_key): krb5_dbe_cpw() takes a kvno now.
+
+Tue May 7 23:16:57 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: USE_KADM_LIBRARY replaced by USE_KADMSRV_LIBRARY
+
+Thu Apr 11 19:32:36 1996 Richard Basch <basch@lehman.com>
+
+ * kdb5_edit.c (extract_v4_srvtab): Use the matching key_data's kvno;
+ don't assume that key_data[0]'s kvno is necessarily the matching
+ key_data's kvno.
+
+Wed Apr 10 19:17:58 1996 Richard Basch <basch@lehman.com>
+
+ * kdb5_edit.c (extract_v4_srvtab): Translate the principal name to
+ the common V4 name.
+
+Tue Mar 19 18:00:58 1996 Richard Basch <basch@lehman.com>
+
+ * kdb5_edit.c (extract_v4_srvtab): do not test to make sure we
+ fetched a key of enctype 1 (des-cbc-crc), since we may have gotten
+ another des key from the database, which is just as useful in a
+ v4 srvtab
+
+ * dumpv4.c (dump_v4_iterator): use krb5_524_conv_principal to do the
+ v5 to v4 principal translation, instead of having yet another
+ hard-coded table.
+
+Wed Mar 6 16:17:20 1996 Richard Basch <basch@lehman.com>
+
+ * dumpv4.c: The V4 master key & schedule was never initialized,
+ so the dump created by dump_v4db was garbage. Read the V4
+ master key from /.k or prompt for the V4 master key password.
+ If there is no V4-salt key in the database, but there is a DES
+ key, include it in the V4 dump, in case it is merely a random
+ service key for which there is no associated password.
+ Skip over K/M in the V5 database (use the entered V4 master key).
+ Both krbtgt and afs keys often have domain-qualifed instances.
+
+Tue Mar 5 12:18:22 1996 Richard Basch <basch@lehman.com>
+
+ * dump.c: POSIX locking requires that the file be opened read-write.
+
+Mon Feb 26 22:42:09 1996 Mark Eichin <eichin@cygnus.com>
+
+ * kdb5_edit.c: new command line option -f stashfile.
+ * kdb5_edit.M: document stashfile option.
+
+Mon Feb 26 22:13:45 1996 Mark Eichin <eichin@cygnus.com>
+
+ * dump.c (process_k5beta_record): since V4 salt type has no data
+ either, only set key_data_ver to 1 for data_type 0 with 0-length
+ salt. Also, don't include alternate key if akey has all-zero type
+ and length in both fields.
+
+Sat Feb 24 04:02:18 1996 Mark W. Eichin <eichin@cygnus.com>
+
+ * dump.c (process_k5beta_record): encrypted keys used to have 4
+ byte lengths in MSB order, need to convert to 2 byte LSB order
+ lengths before storing. Handle primary key and alternate key.
+
+Fri Feb 23 18:44:10 1996 Mark Eichin <eichin@cygnus.com>
+
+ * kdb5_edit.c (kdb5_edit_Init): set manual_mkey for testing with -P
+
+Wed Feb 14 09:52:18 1996 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kdb5_edit.c (enter_master_key, set_dbname_help): If master key
+ enctype is unknown, set to DEFAULT_KDC_ENCTYPE.
+
+Tue Feb 13 16:08:07 1996 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kdb5_edit.c (extract_v4_srvtab): krb5_dbekd_decrypt_key_data
+ takes krb5_key_data *, not **.
+
+Tue Jan 30 18:28:57 1996 Mark Eichin <eichin@cygnus.com>
+
+ * dump.c (load_db): dbrenerr_fmt prints "from" first, so pass it
+ to fprintf correctly.
+
+Sun Jan 28 14:31:47 1996 Mark Eichin <eichin@cygnus.com>
+
+ * dump.c (process_k5_record): t2..t9 is only 8 vars, not 9.
+
+Thu Jan 25 16:07:42 1996 Sam Hartman <hartmans@tertius.mit.edu>
+
+ * kdb5_edit.c (extract_srvtab): Extract *all* the keys in a
+ dbentry, not the first one.
+ (extract_v4_srvtab): Attempt to find the right v4 keys.
+
+Wed Jan 24 18:48:38 1996 Tom Yu <tlyu@dragons-lair.MIT.EDU>
+
+ * Makefile.in: Remove spurious @DEFS@
+
+
+Wed Dec 13 03:44:58 1995 Chris Provenzano (proven@mit.edu)
+
+ * dump.c, dumpv4.c, kdb5_edit.c, loadv4.c :
+ Remove mkvno from krb5_db_entry.
+
+Sun Dec 10 11:07:51 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kdb5_edit.M: Document that modent exists
+
+ * kdb5_edit.c (modent): Add usage as suggested by jhawk@mit.edu.
+
+Thu Nov 09 17:05:57 1995 Chris Provenzano (proven@mit.edu)
+
+ * kdb5_edit.c : Remove krb5_enctype from krb5_string_to_key() args.
+
+Fri Oct 27 13:37:04 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * dump.c (process_k5_record): Fix off by one in malloc.
+
+Mon Oct 9 16:35:19 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kdb5_edit.c (extract_v4_srvtab): Extract a one byte version
+ number for v4 srvtabs (from warlord).
+
+Thu Oct 5 10:35:35 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * cpw.c: Declare std_ks_tuple as extern.
+ * kdb5_edit.h: Remove std_ks_tuple declaration as not all sources
+ include adm.h for structures
+
+Tue Oct 3 23:10:57 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * cpw.c (enter_rnd_key, enter_pwd_key):
+ * kdb5_edit.c (kdb5_edit_Init): Use the kdc.conf file to determine
+ the default list of keysalt tuples to be used. This is
+ stored in std_ks_tuple, and is used by cpw.c for random
+ keys and when a list of keysalts is not specified.
+
+Mon Sep 18 03:59:47 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kdb5_edit.c (show_principal): Show key version and last password
+ change.
+
+ * cpw.c: Fix typo in below change in which list was terminated
+ after third entry. (extra } removed)
+
+Fri Sep 15 14:21:25 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * cpw.c: Add DES_CBC_MD5 and DES_CBC_CRC with the V4 salt as
+ default key/salt tuples to be added. (Once proven's DES_*
+ folding code is implemented, we can shorten this list.)
+ Eventually, this list should be read in from kdc.conf.
+
+Thu Sep 7 20:41:24 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * loadv4.c (load_v4db): Provide a dummy routine if krb4
+ compatibility is not compiled in.
+
+Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu)
+
+ * cpw.c, dump.c, dumpv4.c, kdb5_edit.c, loadv4.c :
+ s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g
+
+Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu)
+
+ * cpw.c, dump.c, dumpv4.c, kdb5_edit.c, loadv4.c : Remove krb5_enctype
+ references, and replace with krb5_keytype where appropriate.
+
+Fri Aug 25 17:37:33 EDT 1995 Paul Park (pjpark@mit.edu)
+ * dumpv4.c - Fix handle_keys(). It was trying to recreate work that
+ has already been done.
+ * Makefile.in, .Sanitize, loadv4.c, kdb5_ed_ct.ct - Add lddb4, the
+ command to load a v4 dump file. This is basically, kdb5_
+ convert reconstituted to fit within the framework of kdb5_edit.
+
+Thu Aug 24 19:28:39 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * .Sanitize: Update file list
+
+Mon Aug 21 16:45:39 EDT 1995 Paul Park (pjpark@mit.edu)
+ * dump.c - Completely rework this logic to support old (e.g. Beta 5
+ and previous) dump format and new dump format using the same
+ commands. This is differentiated by using the "-old" command
+ qualifier.
+
+ * kdb5_edit.M - Add description of -R and -s. Remove "ascii represen-
+ tation of a decimal number". Remove "Bugs".
+
+Fri Aug 18 17:06:06 EDT 1995 Paul Park (pjpark@mit.edu)
+
+ * ss_wrapper.c - Change sense of fgets() check so scripts work.
+
+
+Tue Aug 15 14:22:50 EDT 1995 Paul Park (pjpark@mit.edu)
+
+ * kdb5_edit.c, ss_wrapper.c, cpw.c, kdb5_edit.h - Add support for
+ -s scriptfile and fix up assorted gcc -Wall complaints.
+
+
+Mon Aug 7 17:32:31 EDT 1995 Paul Park (pjpark@mit.edu)
+ * cpw.c - Use krb5_string_to_keysalts() to generate a list of unique
+ key/salt pairs supplied in argv.
+
+
+Mon Aug 07 11:16:03 1995 Chris Provenzano (proven@mit.edu)
+
+ * cpw.c : Uses new kdb change password routines for ank, ark, cpw,
+ and crk. Also remove v4 variants of ank and cpw.
+ * krb5_edit.c : Deleted old variants of rotuines now in cpw.c
+ * kdb5_ed_ct.ct, kdb5_edit.M, tcl_wrapper.c:
+ Removed references to v4 variants of ank and cpw.
+ * kdb5_edit.h (enter_pwd_key()) : Removed proto, it's nolonger
+ necessary as it's a static routine in cpw.c
+
+Thu Aug 03 12:13:50 1995 Chris Provenzano (proven@mit.edu)
+
+ * cpw.c : New change password code for kdb5_edit.
+ * dumpv4.c : Get it to compile with new kdb format.
+
+Mon Jul 31 15:47:30 EDT 1995 Paul Park (pjpark@mit.edu)
+ * kdb5_edit.c - Use libkadm string conversion routines. These are
+ shared by all utilities.
+ * Makefile.in - Remove getdate.y.
+ * configure.in - Remove getdate.y dependency checks.
+ * getdate.y - Sayonara.
+
+
+Thu Jul 27 15:01:01 EDT 1995 Paul Park (pjpark@mit.edu)
+ * configure.in - Add --with-dbm and check for already checking for dbm.
+
+
+Thu Jul 27 02:59:05 1995 Chris Provenzano (proven@mit.edu)
+
+ * dump.c kdb5_edit.c kdb5_edit.h util.c : Use new kdb format.
+
+Mon Jul 17 15:00:08 EDT 1995 Paul Park (pjpark@mit.edu)
+ * configure.in - Add KADM library.
+ * dumpv4.c - Change calling sequence to krb5_db_fetch_mkey().
+ * kdb5_edit.c - Change calling sequence to krb5_db_fetch_mkey() which
+ uses the stash file. Add KDC profile reading/handling as a
+ supplement to command line supplied arguments.
+
+
+Wed Jul 12 12:01:04 EDT 1995 Paul Park (pjpark@mit.edu)
+ * configure.in - Temporarily add --with-kdb4 option. Default is without
+ kdb4. Without kdb4 enables a define. With kdb4 uses -lkdb4 and
+ -l[n]dbm libraries.
+ * dumpv4.c - Conditionalize references to kdb4 routines with
+ KDB4_DISABLE. Replace two required routines:
+ kdb_encrypt_key -> pcbc_encrypt
+ kdb_get_master_key -> des_read_password/printf/key_sched
+
+
+Fri Jul 7 15:38:00 EDT 1995 Paul Park (pjpark@mit.edu)
+ * Makefile.in - Remove all explicit library handling and LDFLAGS.
+ * configure.in - Add USE_<mumble> and KRB5_LIBRARIES.
+
+
+Thu Jun 15 15:34:59 EDT 1995 Paul Park (pjpark@mit.edu)
+ * Makefile.in - Change explicit library names to -l<lib> form, and
+ change target link line to use $(LD) and associated flags.
+ Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these wer
+ split out.
+ * configure.in - Add shared library usage check.
+
+Fri Jun 9 18:14:43 1995 <tytso@rsx-11.mit.edu>
+
+ * configure.in: Remove standardized set of autoconf macros, which
+ are now handled by CONFIG_RULES.
+
+ * dumpv4.c: Change name of controlling #ifdef to be
+ KRB5_KRB4_COMPAT instead of KRB4.
+
+Sun May 21 14:20:32 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * dumpv4.c: Include k5-int.h before krb.h so that PROTOTYPE is not
+ redefined.
+
+Sun May 7 13:46:30 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * configure.in: Add AC_HEADER_STDC to define STDC_HEADERS for
+ getdate.y.
+
+Mon May 1 13:36:41 1995 Theodore Y. Ts'o (tytso@dcl)
+
+ * kdb5_edit.c (kdb5_edit_Init): Check the return code from
+ kdb5_init_context().
+
+Fri Apr 28 18:04:26 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (LOCAL_LIBRARIES): put KRB4_LIB inside KLIB, and put
+ KDB4_LIB ahead of them both.
+
+Thu Apr 27 13:47:23 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (LOCAL_LIBRARIES): use KRB4_LIB and KDB4_LIB
+ directly.
+ * configure.in: just use WITH_KRB4.
+
+Wed Apr 19 13:59:47 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kdb5_edit.c (kdb5_edit_Init): If a default realm is specified
+ (with -r), use krb5_set_default_realm so that created keys
+ will have the correct realm.
+
+Thu Mar 23 23:28:26 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * kdb5_edit.c (show_principal, parse_princ_args): Add
+ "support_desmd5" flag.
+
+Tue Mar 14 16:29:05 1995 <tytso@rsx-11.mit.edu>
+
+ * ss_wrapper.c (main): Set the return code from ss_execute_line(),
+ so that appropriate error checking is done.
+
+Thu Mar 2 12:18:57 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * Makefile.in (ISODELIB): Remove reference to $(ISODELIB).
+
+Wed Mar 1 11:53:02 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket
+ and -lnsl with WITH_NETLIB check.
+
+Tue Feb 28 02:06:26 1995 John Gilmore (gnu at toad.com)
+
+ * dump.c, dumpv4.c, kdb5_edit.c, ss_wrapper.c, tcl_wrapper.c,
+ util.c: Avoid <krb5/...> includes.
+
+Thu Feb 23 19:52:35 1995 Mark Eichin (eichin@cygnus.com)
+
+ * kdb5_edit.c: add struct timeb and sys/timeb includes from
+ getdate.y.
+ (ftime): new function, in case we don't HAVE_FTIME.
+
+Tue Feb 14 17:55:47 1995 Tom Yu (tlyu@dragons-lair)
+
+ * kdb5_edit.c: add modent
+ * getdate.y: import get_date
+ * kdbt_ed_ct.ct: add modent
+ * configure.in:
+ * Makefile.in: support for getdate.y
+
+Wed Feb 8 20:08:36 1995 Tom Yu (tlyu@dragons-lair)
+
+ * kdb5_edit.c (show_principal): make sane and print all useful
+ fields
+
+Wed Jan 25 16:54:40 1995 Chris Provenzano (proven@mit.edu)
+
+ * Removed all narrow types and references to wide.h and narrow.h
+
+Fri Jan 13 15:23:47 1995 Chris Provenzano (proven@mit.edu)
+
+ * Added krb5_context to all krb5_routines
+
+Mon Dec 19 18:04:11 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * configure.in:
+ * Makefile.in:
+ * dumpv4.c (dump_v4db): Do the right thing if we are compiling
+ without V4 support. (The dump_v4db command is disabled.)
+
+Wed Dec 7 00:07:46 1994 <tytso@rsx-11.mit.edu>
+
+ * dumpv4.c (v4_print_time): gmtime expects a pointer to a time_t,
+ not a long. On most systems these are the same, on
+ others....
+
+Wed Nov 16 01:03:42 1994 Mark Eichin (eichin@cygnus.com)
+
+ * dumpv4.c: new file. New command dump_v4db which creates a v4
+ slave dump out of a v5 database, leaving out any keys which aren't
+ using v4 salt, and any keys that aren't for the current
+ realm. Reencrypts using v4 master key, synthesizes arbitrary
+ master key version number.
+ * configure.in: use WITH_KRB4 for dump support.
+ * kdb5_ed_ct.ct: add new dump_v4 command.
+ * Makefile.in: link in dumpv4.
+
+Fri Oct 14 23:31:49 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * dump.c (load_db): When scanning a database entry, read
+ fail_auth_count into a temporary integer variable, and
+ then copy that into entry.fail_auth_count, which is a
+ char.
+
+Fri Oct 7 00:01:40 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * kdb5_edit.c (kdb5_edit_Init): Don't let errors in
+ set_dbname_help initially cause the exit status to be set.
+ Commands like load_db don't need a valid database to be
+ opened.
+
+ * ss_wrapper.c (main): Clear code before ss_execute_line, since
+ ss_execute_line doesn't set code to 0 if there are no
+ problems.
+
+ * kdb5_edit.c (kdb5_edit_Init): Add a new option so that the
+ master key password can be entered on the command line ---
+ for testing only; not documented!!
+
+Mon Oct 3 19:10:47 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * Makefile.in: Use $(srcdir) to find manual page for make install.
+
+Thu Sep 29 15:52:22 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * dump.c (update_ok_file): Make sure mod time on the dump_ok file
+ is updated. (Some systems don't update the mod-time when
+ a file is opened for writing.)
+
+ * Makefile.in: Relink executable when libraries change.
+
+ * kdb5_edit.c (show_principal): Pass variable with correct type to
+ ctime().
+
+ * tcl_wrapper.c (doquit):
+ ss_wrapper.c (main):
+ kdb5_edit.c:
+ dump.c: Exit with a non-zero exit status if there was an error
+ in a executed command.
+
+Thu Sep 15 11:00:30 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * dump.c (load_db): Fix error string on failed fopen. ("for
+ writing" -> "for reading")
+
+
diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in
new file mode 100644
index 000000000..b5bba09d9
--- /dev/null
+++ b/src/kadmin/dbutil/Makefile.in
@@ -0,0 +1,18 @@
+CFLAGS = $(CCOPTS) $(DEFS) -DKDB4_DISABLE $(LOCALINCLUDE) @KRB4_INCLUDES@
+
+PROG = kdb5_util
+OBJS = kdb5_create.o kadm5_create.o string_table.o
+OBJS = kdb5_util.o kdb5_util_ct.o dump.o dumpv4.o loadv4.o ss_wrapper.o \
+ kdb5_create.o kadm5_create.o string_table.o kdb5_stash.o \
+ kdb5_destroy.o
+
+all:: $(PROG)
+
+$(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/dbutil/Makefile.ov b/src/kadmin/dbutil/Makefile.ov
new file mode 100644
index 000000000..f762282f2
--- /dev/null
+++ b/src/kadmin/dbutil/Makefile.ov
@@ -0,0 +1,19 @@
+TOP = ..
+include $(TOP)/config.mk/template
+
+CFLAGS += -I$(TOP)/../include/kerberosIV
+
+PROG = kdb5_util
+SRCS = kdb5_util.c kdb5_util_ct.c dump.c ss_wrapper.c dumpv4.c loadv4.c \
+ kdb5_create.c kadm5_create.c string_table.c kdb5_stash.c \
+ kdb5_destroy.c
+OBJS = kdb5_util.o kdb5_util_ct.o dump.o dumpv4.o loadv4.o ss_wrapper.o \
+ kdb5_create.o kadm5_create.o string_table.o kdb5_stash.o \
+ kdb5_destroy.o
+ETABLES = kdb5_util_ct.ct
+
+LIBS = $(LIBADMSRV) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBRPCLIB) $(LIBDYN) \
+ $(LIBSS) $(LIBDB)
+
+expand NormalProgram
+expand ErrorTables
diff --git a/src/kadmin/dbutil/configure.in b/src/kadmin/dbutil/configure.in
new file mode 100644
index 000000000..c9234524e
--- /dev/null
+++ b/src/kadmin/dbutil/configure.in
@@ -0,0 +1,13 @@
+AC_INIT(kdb5_create.c)
+CONFIG_RULES
+WITH_KRB4
+AC_PROG_INSTALL
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_KDB5_LIBRARY
+USE_DYN_LIBRARY
+USE_SS_LIBRARY
+USE_KRB4_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c
new file mode 100644
index 000000000..8bd54ca4d
--- /dev/null
+++ b/src/kadmin/dbutil/dump.c
@@ -0,0 +1,1957 @@
+/*
+ * admin/edit/dump.c
+ *
+ * Copyright 1990,1991 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.
+ *
+ *
+ * Dump a KDC database
+ */
+
+#include <stdio.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <kadm5/adb.h>
+#include <com_err.h>
+#include "kdb5_util.h"
+#if HAVE_REGEX_H
+#include <regex.h>
+#endif /* HAVE_REGEX_H */
+
+/*
+ * Use compile(3) if no regcomp present.
+ */
+#if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H)
+#define INIT char *sp = instring;
+#define GETC() (*sp++)
+#define PEEKC() (*sp)
+#define UNGETC(c) (--sp)
+#define RETURN(c) return(c)
+#define ERROR(c)
+#define RE_BUF_SIZE 1024
+#include <regexp.h>
+#endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */
+
+struct dump_args {
+ char *programname;
+ FILE *ofile;
+ krb5_context kcontext;
+ char **names;
+ int nnames;
+ int verbose;
+};
+
+static krb5_error_code dump_k5beta_iterator PROTOTYPE((krb5_pointer,
+ krb5_db_entry *));
+static krb5_error_code dump_k5beta6_iterator PROTOTYPE((krb5_pointer,
+ krb5_db_entry *));
+static krb5_error_code dump_k5beta7_princ PROTOTYPE((krb5_pointer,
+ krb5_db_entry *));
+static void dump_k5beta7_policy PROTOTYPE((void *, osa_policy_ent_t));
+
+typedef krb5_error_code (*dump_func)PROTOTYPE((krb5_pointer,
+ krb5_db_entry *));
+
+static int process_k5beta_record PROTOTYPE((char *, krb5_context,
+ FILE *, int, int *, void *));
+static int process_k5beta6_record PROTOTYPE((char *, krb5_context,
+ FILE *, int, int *, void *));
+static int process_k5beta7_record PROTOTYPE((char *, krb5_context,
+ FILE *, int, int *, void *));
+typedef krb5_error_code (*load_func)PROTOTYPE((char *, krb5_context,
+ FILE *, int, int *, void *));
+
+typedef struct _dump_version {
+ char *name;
+ char *header;
+ dump_func dump_princ;
+ osa_adb_iter_policy_func dump_policy;
+ load_func load_record;
+} dump_version;
+
+dump_version old_version = {
+ "Kerberos version 5 old format",
+ "kdb5_edit load_dump version 2.0\n",
+ dump_k5beta_iterator,
+ NULL,
+ process_k5beta_record,
+};
+dump_version beta6_version = {
+ "Kerberos version 5 beta 6 format",
+ "kdb5_edit load_dump version 3.0\n",
+ dump_k5beta6_iterator,
+ NULL,
+ process_k5beta6_record,
+};
+dump_version beta7_version = {
+ "Kerberos version 5",
+ "kdb5_util load_dump version 4\n",
+ dump_k5beta7_princ,
+ dump_k5beta7_policy,
+ process_k5beta7_record,
+};
+
+/* External data */
+extern char *current_dbname;
+extern krb5_boolean dbactive;
+extern int exit_status;
+extern krb5_context util_context;
+extern kadm5_config_params global_params;
+
+/* Strings */
+
+static const char k5beta_dump_header[] = "kdb5_edit load_dump version 2.0\n";
+static const char k5beta6_dump_header[] = "kdb5_edit load_dump version 3.0\n";
+static const char k5beta7_dump_header[] = "kdb5_edit load_dump version 4\n";
+
+static const char null_mprinc_name[] = "kdb5_dump@MISSING";
+
+/* Message strings */
+static const char regex_err[] = "%s: regular expression error - %s\n";
+static const char regex_merr[] = "%s: regular expression match error - %s\n";
+static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n";
+static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n";
+static const char nokeys_err[] = "%s: cannot find any standard key for %s\n";
+static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n";
+static const char stand_fmt_name[] = "Kerberos version 5";
+static const char old_fmt_name[] = "Kerberos version 5 old format";
+static const char b6_fmt_name[] = "Kerberos version 5 beta 6 format";
+static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n";
+static const char oflock_error[] = "%s: cannot lock %s (%s)\n";
+static const char dumprec_err[] = "%s: error performing %s dump (%s)\n";
+static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n";
+static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: ";
+static const char read_name_string[] = "name string";
+static const char read_key_type[] = "key type";
+static const char read_key_data[] = "key data";
+static const char read_pr_data1[] = "first set of principal attributes";
+static const char read_mod_name[] = "modifier name";
+static const char read_pr_data2[] = "second set of principal attributes";
+static const char read_salt_data[] = "salt data";
+static const char read_akey_type[] = "alternate key type";
+static const char read_akey_data[] = "alternate key data";
+static const char read_asalt_type[] = "alternate salt type";
+static const char read_asalt_data[] = "alternate salt data";
+static const char read_exp_data[] = "expansion data";
+static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n";
+static const char add_princ_fmt[] = "%s\n";
+static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n";
+static const char read_err_fmt[] = "%s(%d): cannot read %s\n";
+static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n";
+static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n";
+static const char err_line_fmt[] = "%s: error processing line %d of %s\n";
+static const char head_bad_fmt[] = "%s: dump header bad in %s\n";
+static const char read_bytecnt[] = "record byte count";
+static const char read_encdata[] = "encoded data";
+static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n";
+static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n";
+static const char read_nint_data[] = "principal static attributes";
+static const char read_tcontents[] = "tagged data contents";
+static const char read_ttypelen[] = "tagged data type and length";
+static const char read_kcontents[] = "key data contents";
+static const char read_ktypelen[] = "key data type and length";
+static const char read_econtents[] = "extra data contents";
+static const char k5beta_fmt_name[] = "Kerberos version 5 old format";
+static const char standard_fmt_name[] = "Kerberos version 5 format";
+static const char lusage_err_fmt[] = "%s: usage is %s [%s] [%s] [%s] filename dbname [admin_dbname]\n";
+static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n";
+static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n";
+static const char stdin_name[] = "standard input";
+static const char restfail_fmt[] = "%s: %s restore failed\n";
+static const char close_err_fmt[] = "%s: cannot close database (%s)\n";
+static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n";
+static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n";
+static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n";
+static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n";
+static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n";
+static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n";
+
+static const char oldoption[] = "-old";
+static const char b6option[] = "-b6";
+static const char verboseoption[] = "-verbose";
+static const char updateoption[] = "-update";
+static const char dump_tmptrail[] = "~";
+
+/*
+ * Update the "ok" file.
+ */
+void update_ok_file (file_name)
+ char *file_name;
+{
+ /* handle slave locking/failure stuff */
+ char *file_ok;
+ int fd;
+ static char ok[]=".dump_ok";
+
+ if ((file_ok = (char *)malloc(strlen(file_name) + strlen(ok) + 1))
+ == NULL) {
+ com_err(progname, ENOMEM,
+ "while allocating filename for update_ok_file");
+ exit_status++;
+ return;
+ }
+ strcpy(file_ok, file_name);
+ strcat(file_ok, ok);
+ if ((fd = open(file_ok, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) {
+ com_err(progname, errno, "while creating 'ok' file, '%s'",
+ file_ok);
+ exit_status++;
+ free(file_ok);
+ return;
+ }
+ if (write(fd, "", 1) != 1) {
+ com_err(progname, errno, "while writing to 'ok' file, '%s'",
+ file_ok);
+ exit_status++;
+ free(file_ok);
+ return;
+ }
+
+ free(file_ok);
+ close(fd);
+ return;
+}
+
+/*
+ * name_matches() - See if a principal name matches a regular expression
+ * or string.
+ */
+static int
+name_matches(name, arglist)
+ char *name;
+ struct dump_args *arglist;
+{
+#if HAVE_REGCOMP
+ regex_t match_exp;
+ regmatch_t match_match;
+ int match_error;
+ char match_errmsg[BUFSIZ];
+ size_t errmsg_size;
+#elif HAVE_REGEXP_H
+ char regexp_buffer[RE_BUF_SIZE];
+#elif HAVE_RE_COMP
+ extern char *re_comp();
+ char *re_result;
+#endif /* HAVE_RE_COMP */
+ int i, match;
+
+ /*
+ * Plow, brute force, through the list of names/regular expressions.
+ */
+ match = (arglist->nnames) ? 0 : 1;
+ for (i=0; i<arglist->nnames; i++) {
+#if HAVE_REGCOMP
+ /*
+ * Compile the regular expression.
+ */
+ if (match_error = regcomp(&match_exp,
+ arglist->names[i],
+ REG_EXTENDED)) {
+ errmsg_size = regerror(match_error,
+ &match_exp,
+ match_errmsg,
+ sizeof(match_errmsg));
+ fprintf(stderr, regex_err, arglist->programname, match_errmsg);
+ break;
+ }
+ /*
+ * See if we have a match.
+ */
+ if (match_error = regexec(&match_exp, name, 1, &match_match, 0)) {
+ if (match_error != REG_NOMATCH) {
+ errmsg_size = regerror(match_error,
+ &match_exp,
+ match_errmsg,
+ sizeof(match_errmsg));
+ fprintf(stderr, regex_merr,
+ arglist->programname, match_errmsg);
+ break;
+ }
+ }
+ else {
+ /*
+ * We have a match. See if it matches the whole
+ * name.
+ */
+ if ((match_match.rm_so == 0) &&
+ (match_match.rm_eo == strlen(name)))
+ match = 1;
+ }
+ regfree(&match_exp);
+#elif HAVE_REGEXP_H
+ /*
+ * Compile the regular expression.
+ */
+ compile(arglist->names[i],
+ regexp_buffer,
+ &regexp_buffer[RE_BUF_SIZE],
+ '\0');
+ if (step(name, regexp_buffer)) {
+ if ((loc1 == name) &&
+ (loc2 == &name[strlen(name)]))
+ match = 1;
+ }
+#elif HAVE_RE_COMP
+ /*
+ * Compile the regular expression.
+ */
+ if (re_result = re_comp(arglist->names[i])) {
+ fprintf(stderr, regex_err, arglist->programname, re_result);
+ break;
+ }
+ if (re_exec(name))
+ match = 1;
+#else /* HAVE_RE_COMP */
+ /*
+ * If no regular expression support, then just compare the strings.
+ */
+ if (!strcmp(arglist->names[i], name))
+ match = 1;
+#endif /* HAVE_REGCOMP */
+ if (match)
+ break;
+ }
+ return(match);
+}
+
+static krb5_error_code
+find_enctype(dbentp, enctype, salttype, kentp)
+ krb5_db_entry *dbentp;
+ krb5_enctype enctype;
+ krb5_int32 salttype;
+ krb5_key_data **kentp;
+{
+ int i;
+ int maxkvno;
+ krb5_key_data *datap;
+
+ maxkvno = -1;
+ datap = (krb5_key_data *) NULL;
+ for (i=0; i<dbentp->n_key_data; i++) {
+ if ((dbentp->key_data[i].key_data_type[0] == enctype) &&
+ ((dbentp->key_data[i].key_data_type[1] == salttype) ||
+ (salttype < 0))) {
+ maxkvno = dbentp->key_data[i].key_data_kvno;
+ datap = &dbentp->key_data[i];
+ }
+ }
+ if (maxkvno >= 0) {
+ *kentp = datap;
+ return(0);
+ }
+ return(ENOENT);
+}
+
+/*
+ * dump_k5beta_header() - Make a dump header that is recognizable by Kerberos
+ * Version 5 Beta 5 and previous releases.
+ */
+static krb5_error_code
+dump_k5beta_header(arglist)
+ struct dump_args *arglist;
+{
+ /* The old header consists of the leading string */
+ fprintf(arglist->ofile, k5beta_dump_header);
+ return(0);
+}
+
+/*
+ * dump_k5beta_iterator() - Dump an entry in a format that is usable
+ * by Kerberos Version 5 Beta 5 and previous
+ * releases.
+ */
+static krb5_error_code
+dump_k5beta_iterator(ptr, entry)
+ krb5_pointer ptr;
+ krb5_db_entry *entry;
+{
+ krb5_error_code retval;
+ struct dump_args *arg;
+ char *name, *mod_name;
+ krb5_principal mod_princ;
+ krb5_tl_data *pwchg;
+ krb5_key_data *pkey, *akey, nullkey;
+ krb5_timestamp mod_date, last_pwd_change;
+ int i;
+
+ /* Initialize */
+ arg = (struct dump_args *) ptr;
+ name = (char *) NULL;
+ mod_name = (char *) NULL;
+ memset(&nullkey, 0, sizeof(nullkey));
+
+ /*
+ * Flatten the principal name.
+ */
+ if ((retval = krb5_unparse_name(arg->kcontext,
+ entry->princ,
+ &name))) {
+ fprintf(stderr, pname_unp_err,
+ arg->programname, error_message(retval));
+ return(retval);
+ }
+ /*
+ * If we don't have any match strings, or if our name matches, then
+ * proceed with the dump, otherwise, just forget about it.
+ */
+ if (!arg->nnames || name_matches(name, arg)) {
+ /*
+ * Deserialize the modifier record.
+ */
+ mod_name = (char *) NULL;
+ mod_princ = NULL;
+ last_pwd_change = mod_date = 0;
+ pkey = akey = (krb5_key_data *) NULL;
+ if (!(retval = krb5_dbe_lookup_mod_princ_data(arg->kcontext,
+ entry,
+ &mod_date,
+ &mod_princ))) {
+ if (mod_princ) {
+ /*
+ * Flatten the modifier name.
+ */
+ if ((retval = krb5_unparse_name(arg->kcontext,
+ mod_princ,
+ &mod_name)))
+ fprintf(stderr, mname_unp_err, arg->programname,
+ error_message(retval));
+ krb5_free_principal(arg->kcontext, mod_princ);
+ }
+ }
+ if (!mod_name)
+ mod_name = strdup(null_mprinc_name);
+
+ /*
+ * Find the last password change record and set it straight.
+ */
+ if (retval =
+ krb5_dbe_lookup_last_pwd_change(arg->kcontext, entry,
+ &last_pwd_change)) {
+ fprintf(stderr, nokeys_err, arg->programname, name);
+ krb5_xfree(mod_name);
+ krb5_xfree(name);
+ return(retval);
+ }
+
+ /*
+ * Find the 'primary' key and the 'alternate' key.
+ */
+ if ((retval = find_enctype(entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_NORMAL,
+ &pkey)) &&
+ (retval = find_enctype(entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ &akey))) {
+ fprintf(stderr, nokeys_err, arg->programname, name);
+ krb5_xfree(mod_name);
+ krb5_xfree(name);
+ return(retval);
+ }
+
+ /* If we only have one type, then ship it out as the primary. */
+ if (!pkey && akey) {
+ pkey = akey;
+ akey = &nullkey;
+ }
+ else {
+ if (!akey)
+ akey = &nullkey;
+ }
+
+ /*
+ * First put out strings representing the length of the variable
+ * length data in this record, then the name and the primary key type.
+ */
+ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name),
+ strlen(mod_name),
+ (krb5_int32) pkey->key_data_length[0],
+ (krb5_int32) akey->key_data_length[0],
+ (krb5_int32) pkey->key_data_length[1],
+ (krb5_int32) akey->key_data_length[1],
+ name,
+ (krb5_int32) pkey->key_data_type[0]);
+ for (i=0; i<pkey->key_data_length[0]; i++) {
+ fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]);
+ }
+ /*
+ * Second, print out strings representing the standard integer
+ * data in this record.
+ */
+ fprintf(arg->ofile,
+ "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t",
+ (krb5_int32) pkey->key_data_kvno,
+ entry->max_life, entry->max_renewable_life,
+ 1 /* Fake mkvno */, entry->expiration, entry->pw_expiration,
+ last_pwd_change, entry->last_success, entry->last_failed,
+ entry->fail_auth_count, mod_name, mod_date,
+ entry->attributes, pkey->key_data_type[1]);
+
+ /* Pound out the salt data, if present. */
+ for (i=0; i<pkey->key_data_length[1]; i++) {
+ fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]);
+ }
+ /* Pound out the alternate key type and contents */
+ fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]);
+ for (i=0; i<akey->key_data_length[0]; i++) {
+ fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]);
+ }
+ /* Pound out the alternate salt type and contents */
+ fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]);
+ for (i=0; i<akey->key_data_length[1]; i++) {
+ fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]);
+ }
+ /* Pound out the expansion data. (is null) */
+ for (i=0; i < 8; i++) {
+ fprintf(arg->ofile, "\t%u", 0);
+ }
+ fprintf(arg->ofile, ";\n");
+ /* If we're blabbing, do it */
+ if (arg->verbose)
+ fprintf(stderr, "%s\n", name);
+ krb5_xfree(mod_name);
+ }
+ krb5_xfree(name);
+ return(0);
+}
+
+/*
+ * dump_k5beta6_iterator() - Output a dump record in krb5b6 format.
+ */
+static krb5_error_code
+dump_k5beta6_iterator(ptr, entry)
+ krb5_pointer ptr;
+ krb5_db_entry *entry;
+{
+ krb5_error_code retval;
+ struct dump_args *arg;
+ char *name;
+ krb5_tl_data *tlp;
+ krb5_key_data *kdata;
+ int counter, i, j;
+
+ /* Initialize */
+ arg = (struct dump_args *) ptr;
+ name = (char *) NULL;
+
+ /*
+ * Flatten the principal name.
+ */
+ if ((retval = krb5_unparse_name(arg->kcontext,
+ entry->princ,
+ &name))) {
+ fprintf(stderr, pname_unp_err,
+ arg->programname, error_message(retval));
+ return(retval);
+ }
+ /*
+ * If we don't have any match strings, or if our name matches, then
+ * proceed with the dump, otherwise, just forget about it.
+ */
+ if (!arg->nnames || name_matches(name, arg)) {
+ /*
+ * We'd like to just blast out the contents as they would appear in
+ * the database so that we can just suck it back in, but it doesn't
+ * lend itself to easy editing.
+ */
+
+ /*
+ * The dump format is as follows:
+ * len strlen(name) n_tl_data n_key_data e_length
+ * name
+ * attributes max_life max_renewable_life expiration
+ * pw_expiration last_success last_failed fail_auth_count
+ * n_tl_data*[type length <contents>]
+ * n_key_data*[ver kvno ver*(type length <contents>)]
+ * <e_data>
+ * Fields which are not encapsulated by angle-brackets are to appear
+ * verbatim. Bracketed fields absence is indicated by a -1 in its
+ * place
+ */
+
+ /*
+ * Make sure that the tagged list is reasonably correct.
+ */
+ counter = 0;
+ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next)
+ counter++;
+ if (counter == entry->n_tl_data) {
+ /* Pound out header */
+ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t",
+ (int) entry->len,
+ strlen(name),
+ (int) entry->n_tl_data,
+ (int) entry->n_key_data,
+ (int) entry->e_length,
+ name);
+ fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",
+ entry->attributes,
+ entry->max_life,
+ entry->max_renewable_life,
+ entry->expiration,
+ entry->pw_expiration,
+ entry->last_success,
+ entry->last_failed,
+ entry->fail_auth_count);
+ /* Pound out tagged data. */
+ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) {
+ fprintf(arg->ofile, "%d\t%d\t",
+ (int) tlp->tl_data_type,
+ (int) tlp->tl_data_length);
+ if (tlp->tl_data_length)
+ for (i=0; i<tlp->tl_data_length; i++)
+ fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]);
+ else
+ fprintf(arg->ofile, "%d", -1);
+ fprintf(arg->ofile, "\t");
+ }
+
+ /* Pound out key data */
+ for (counter=0; counter<entry->n_key_data; counter++) {
+ kdata = &entry->key_data[counter];
+ fprintf(arg->ofile, "%d\t%d\t",
+ (int) kdata->key_data_ver,
+ (int) kdata->key_data_kvno);
+ for (i=0; i<kdata->key_data_ver; i++) {
+ fprintf(arg->ofile, "%d\t%d\t",
+ kdata->key_data_type[i],
+ kdata->key_data_length[i]);
+ if (kdata->key_data_length[i])
+ for (j=0; j<kdata->key_data_length[i]; j++)
+ fprintf(arg->ofile, "%02x",
+ kdata->key_data_contents[i][j]);
+ else
+ fprintf(arg->ofile, "%d", -1);
+ fprintf(arg->ofile, "\t");
+ }
+ }
+
+ /* Pound out extra data */
+ if (entry->e_length)
+ for (i=0; i<entry->e_length; i++)
+ fprintf(arg->ofile, "%02x", entry->e_data[i]);
+ else
+ fprintf(arg->ofile, "%d", -1);
+
+ /* Print trailer */
+ fprintf(arg->ofile, ";\n");
+
+ if (arg->verbose)
+ fprintf(stderr, "%s\n", name);
+ }
+ else {
+ fprintf(stderr, sdump_tl_inc_err,
+ arg->programname, name, counter, (int) entry->n_tl_data);
+ retval = EINVAL;
+ }
+ }
+ krb5_xfree(name);
+ return(retval);
+}
+
+/*
+ * dump_k5beta7_iterator() - Output a dump record in krb5b7 format.
+ */
+static krb5_error_code
+dump_k5beta7_princ(ptr, entry)
+ krb5_pointer ptr;
+ krb5_db_entry *entry;
+{
+ krb5_error_code retval;
+ struct dump_args *arg;
+ char *name;
+ int tmp_nnames;
+
+ /* Initialize */
+ arg = (struct dump_args *) ptr;
+ name = (char *) NULL;
+
+ /*
+ * Flatten the principal name.
+ */
+ if ((retval = krb5_unparse_name(arg->kcontext,
+ entry->princ,
+ &name))) {
+ fprintf(stderr, pname_unp_err,
+ arg->programname, error_message(retval));
+ return(retval);
+ }
+ /*
+ * If we don't have any match strings, or if our name matches, then
+ * proceed with the dump, otherwise, just forget about it.
+ */
+ if (!arg->nnames || name_matches(name, arg)) {
+ fprintf(arg->ofile, "princ\t");
+
+ /* save the callee from matching the name again */
+ tmp_nnames = arg->nnames;
+ arg->nnames = 0;
+ retval = dump_k5beta6_iterator(ptr, entry);
+ arg->nnames = tmp_nnames;
+ }
+
+ free(name);
+ return retval;
+}
+
+void dump_k5beta7_policy(void *data, osa_policy_ent_t entry)
+{
+ struct dump_args *arg;
+
+ arg = (struct dump_args *) data;
+ fprintf(arg->ofile, "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);
+}
+
+/*
+ * usage is:
+ * dump_db [-old] [-b6] [-verbose] [filename [principals...]]
+ */
+void
+dump_db(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *f;
+ struct dump_args arglist;
+ int error;
+ char *programname;
+ char *ofile;
+ krb5_error_code kret;
+ dump_version *dump;
+ int aindex;
+ krb5_boolean locked;
+ extern osa_adb_policy_t policy_db;
+
+ /*
+ * Parse the arguments.
+ */
+ programname = argv[0];
+ if (strrchr(programname, (int) '/'))
+ programname = strrchr(argv[0], (int) '/') + 1;
+ ofile = (char *) NULL;
+ error = 0;
+ dump = &beta7_version;
+ arglist.verbose = 0;
+
+ /*
+ * Parse the qualifiers.
+ */
+ for (aindex = 1; aindex < argc; aindex++) {
+ if (!strcmp(argv[aindex], oldoption))
+ dump = &old_version;
+ else if (!strcmp(argv[aindex], b6option))
+ dump = &beta6_version;
+ else if (!strcmp(argv[aindex], verboseoption))
+ arglist.verbose++;
+ else
+ break;
+ }
+
+ arglist.names = (char **) NULL;
+ arglist.nnames = 0;
+ if (aindex < argc) {
+ ofile = argv[aindex];
+ aindex++;
+ if (aindex < argc) {
+ arglist.names = &argv[aindex];
+ arglist.nnames = argc - aindex;
+ }
+ }
+
+ /*
+ * Attempt to open the database. The policy database only has to
+ * be opened if we try a dump that uses it.
+ */
+ if (!dbactive || (dump->dump_policy != NULL && policy_db == NULL)) {
+ com_err(argv[0], 0, Err_no_database);
+ exit_status++;
+ return;
+ }
+
+ kret = 0;
+ locked = 0;
+ if (ofile && strcmp(ofile, "-")) {
+ /*
+ * Make sure that we don't open and truncate on the fopen,
+ * since that may hose an on-going kprop process.
+ *
+ * We could also control this by opening for read and
+ * write, doing an flock with LOCK_EX, and then
+ * truncating the file once we have gotten the lock,
+ * but that would involve more OS dependencies than I
+ * want to get into.
+ */
+ unlink(ofile);
+ if (!(f = fopen(ofile, "w"))) {
+ fprintf(stderr, ofopen_error,
+ programname, ofile, error_message(errno));
+ exit_status++;
+ }
+ if ((kret = krb5_lock_file(util_context,
+ fileno(f),
+ KRB5_LOCKMODE_EXCLUSIVE))) {
+ fprintf(stderr, oflock_error,
+ programname, ofile, error_message(kret));
+ exit_status++;
+ }
+ else
+ locked = 1;
+ } else {
+ f = stdout;
+ }
+ if (f && !(kret)) {
+ arglist.programname = programname;
+ arglist.ofile = f;
+ arglist.kcontext = util_context;
+ fprintf(arglist.ofile, "%s", dump->header);
+ if ((kret = krb5_db_iterate(util_context,
+ dump->dump_princ,
+ (krb5_pointer) &arglist))) {
+ fprintf(stderr, dumprec_err,
+ programname, dump->name, error_message(kret));
+ exit_status++;
+ }
+ if (dump->dump_policy &&
+ (kret = osa_adb_iter_policy(policy_db, dump->dump_policy,
+ &arglist))) {
+ fprintf(stderr, dumprec_err, programname, dump->name,
+ error_message(kret));
+ exit_status++;
+ }
+ if (ofile && !exit_status) {
+ fclose(f);
+ update_ok_file(ofile);
+ }
+ }
+ if (locked)
+ (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK);
+}
+
+/*
+ * Read a string of bytes while counting the number of lines passed.
+ */
+static int
+read_string(f, buf, len, lp)
+ FILE *f;
+ char *buf;
+ int len;
+ int *lp;
+{
+ int c;
+ int i, retval;
+
+ retval = 0;
+ for (i=0; i<len; i++) {
+ c = (char) fgetc(f);
+ if (c < 0) {
+ retval = 1;
+ break;
+ }
+ if (c == '\n')
+ (*lp)++;
+ buf[i] = (char) c;
+ }
+ buf[len] = '\0';
+ return(retval);
+}
+
+/*
+ * Read a string of two character representations of bytes.
+ */
+static int
+read_octet_string(f, buf, len)
+ FILE *f;
+ krb5_octet *buf;
+ int len;
+{
+ int c;
+ int i, retval;
+
+ retval = 0;
+ for (i=0; i<len; i++) {
+ if (fscanf(f, "%02x", &c) != 1) {
+ retval = 1;
+ break;
+ }
+ buf[i] = (krb5_octet) c;
+ }
+ return(retval);
+}
+
+/*
+ * Find the end of an old format record.
+ */
+static void
+find_record_end(f, fn, lineno)
+ FILE *f;
+ char *fn;
+ int lineno;
+{
+ int ch;
+
+ if (((ch = fgetc(f)) != ';') || ((ch = fgetc(f)) != '\n')) {
+ fprintf(stderr, trash_end_fmt, fn, lineno);
+ while (ch != '\n') {
+ putc(ch, stderr);
+ ch = fgetc(f);
+ }
+ putc(ch, stderr);
+ }
+}
+
+#if 0
+/*
+ * update_tl_data() - Generate the tl_data entries.
+ */
+static krb5_error_code
+update_tl_data(kcontext, dbentp, mod_name, mod_date, last_pwd_change)
+ krb5_context kcontext;
+ krb5_db_entry *dbentp;
+ krb5_principal mod_name;
+ krb5_timestamp mod_date;
+ krb5_timestamp last_pwd_change;
+{
+ krb5_error_code kret;
+
+ kret = 0 ;
+
+ /*
+ * Handle modification principal.
+ */
+ if (mod_name) {
+ krb5_tl_mod_princ mprinc;
+
+ memset(&mprinc, 0, sizeof(mprinc));
+ if (!(kret = krb5_copy_principal(kcontext,
+ mod_name,
+ &mprinc.mod_princ))) {
+ mprinc.mod_date = mod_date;
+ kret = krb5_dbe_encode_mod_princ_data(kcontext,
+ &mprinc,
+ dbentp);
+ }
+ if (mprinc.mod_princ)
+ krb5_free_principal(kcontext, mprinc.mod_princ);
+ }
+
+ /*
+ * Handle last password change.
+ */
+ if (!kret) {
+ krb5_tl_data *pwchg;
+ krb5_boolean linked;
+
+ /* Find a previously existing entry */
+ for (pwchg = dbentp->tl_data;
+ (pwchg) && (pwchg->tl_data_type != KRB5_TL_LAST_PWD_CHANGE);
+ pwchg = pwchg->tl_data_next);
+
+ /* Check to see if we found one. */
+ linked = 0;
+ if (!pwchg) {
+ /* No, allocate a new one */
+ if ((pwchg = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) {
+ memset(pwchg, 0, sizeof(krb5_tl_data));
+ if (!(pwchg->tl_data_contents =
+ (krb5_octet *) malloc(sizeof(krb5_timestamp)))) {
+ free(pwchg);
+ pwchg = (krb5_tl_data *) NULL;
+ }
+ else {
+ pwchg->tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
+ pwchg->tl_data_length =
+ (krb5_int16) sizeof(krb5_timestamp);
+ }
+ }
+ }
+ else
+ linked = 1;
+
+ /* Do we have an entry? */
+ if (pwchg && pwchg->tl_data_contents) {
+ /* Encode it */
+ krb5_kdb_encode_int32(last_pwd_change, pwchg->tl_data_contents);
+ /* Link it in if necessary */
+ if (!linked) {
+ pwchg->tl_data_next = dbentp->tl_data;
+ dbentp->tl_data = pwchg;
+ dbentp->n_tl_data++;
+ }
+ }
+ else
+ kret = ENOMEM;
+ }
+
+ return(kret);
+}
+#endif
+
+/*
+ * process_k5beta_record() - Handle a dump record in old format.
+ *
+ * Returns -1 for end of file, 0 for success and 1 for failure.
+ */
+static int
+process_k5beta_record(fname, kcontext, filep, verbose, linenop, pol_db)
+ char *fname;
+ krb5_context kcontext;
+ FILE *filep;
+ int verbose;
+ int *linenop;
+ void *pol_db;
+{
+ int nmatched;
+ int retval;
+ krb5_db_entry dbent;
+ int name_len, mod_name_len, key_len;
+ int alt_key_len, salt_len, alt_salt_len;
+ char *name;
+ char *mod_name;
+ int tmpint1, tmpint2, tmpint3;
+ int error;
+ const char *try2read;
+ int i;
+ krb5_key_data *pkey, *akey;
+ krb5_timestamp last_pwd_change, mod_date;
+ krb5_principal mod_princ;
+ krb5_error_code kret;
+
+ try2read = (char *) NULL;
+ (*linenop)++;
+ retval = 1;
+ memset((char *)&dbent, 0, sizeof(dbent));
+
+ /* Make sure we've got key_data entries */
+ if (krb5_dbe_create_key_data(kcontext, &dbent) ||
+ krb5_dbe_create_key_data(kcontext, &dbent)) {
+ krb5_db_free_principal(kcontext, &dbent, 1);
+ return(1);
+ }
+ pkey = &dbent.key_data[0];
+ akey = &dbent.key_data[1];
+
+ /*
+ * Match the sizes. 6 tokens to match.
+ */
+ nmatched = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t",
+ &name_len, &mod_name_len, &key_len,
+ &alt_key_len, &salt_len, &alt_salt_len);
+ if (nmatched == 6) {
+ pkey->key_data_length[0] = key_len;
+ akey->key_data_length[0] = alt_key_len;
+ pkey->key_data_length[1] = salt_len;
+ akey->key_data_length[1] = alt_salt_len;
+ name = (char *) NULL;
+ mod_name = (char *) NULL;
+ /*
+ * Get the memory for the variable length fields.
+ */
+ if ((name = (char *) malloc((size_t) (name_len + 1))) &&
+ (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) &&
+ (!key_len ||
+ (pkey->key_data_contents[0] =
+ (krb5_octet *) malloc((size_t) (key_len + 1)))) &&
+ (!alt_key_len ||
+ (akey->key_data_contents[0] =
+ (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) &&
+ (!salt_len ||
+ (pkey->key_data_contents[1] =
+ (krb5_octet *) malloc((size_t) (salt_len + 1)))) &&
+ (!alt_salt_len ||
+ (akey->key_data_contents[1] =
+ (krb5_octet *) malloc((size_t) (alt_salt_len + 1))))
+ ) {
+ error = 0;
+
+ /* Read the principal name */
+ if (read_string(filep, name, name_len, linenop)) {
+ try2read = read_name_string;
+ error++;
+ }
+ /* Read the key type */
+ if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) {
+ try2read = read_key_type;
+ error++;
+ }
+ pkey->key_data_type[0] = tmpint1;
+ /* Read the old format key */
+ if (!error && read_octet_string(filep,
+ pkey->key_data_contents[0],
+ pkey->key_data_length[0])) {
+ try2read = read_key_data;
+ error++;
+ }
+ /* convert to a new format key */
+ /* the encrypted version is stored as the unencrypted key length
+ (4 bytes, MSB first) followed by the encrypted key. */
+ if ((pkey->key_data_length[0] > 4)
+ && (pkey->key_data_contents[0][0] == 0)
+ && (pkey->key_data_contents[0][1] == 0)) {
+ /* this really does look like an old key, so drop and swap */
+ /* the *new* length is 2 bytes, LSB first, sigh. */
+ size_t shortlen = pkey->key_data_length[0]-4+2;
+ char *shortcopy = (krb5_octet *) malloc(shortlen);
+ char *origdata = pkey->key_data_contents[0];
+ shortcopy[0] = origdata[3];
+ shortcopy[1] = origdata[2];
+ memcpy(shortcopy+2,origdata+4,shortlen-2);
+ free(origdata);
+ pkey->key_data_length[0] = shortlen;
+ pkey->key_data_contents[0] = shortcopy;
+ }
+
+ /* Read principal attributes */
+ if (!error && (fscanf(filep,
+ "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t",
+ &tmpint1, &dbent.max_life,
+ &dbent.max_renewable_life,
+ &tmpint2, &dbent.expiration,
+ &dbent.pw_expiration, &last_pwd_change,
+ &dbent.last_success, &dbent.last_failed,
+ &tmpint3) != 10)) {
+ try2read = read_pr_data1;
+ error++;
+ }
+ pkey->key_data_kvno = tmpint1;
+ dbent.fail_auth_count = tmpint3;
+ /* Read modifier name */
+ if (!error && read_string(filep,
+ mod_name,
+ mod_name_len,
+ linenop)) {
+ try2read = read_mod_name;
+ error++;
+ }
+ /* Read second set of attributes */
+ if (!error && (fscanf(filep, "\t%u\t%u\t%u\t",
+ &mod_date, &dbent.attributes,
+ &tmpint1) != 3)) {
+ try2read = read_pr_data2;
+ error++;
+ }
+ pkey->key_data_type[1] = tmpint1;
+ /* Read salt data */
+ if (!error && read_octet_string(filep,
+ pkey->key_data_contents[1],
+ pkey->key_data_length[1])) {
+ try2read = read_salt_data;
+ error++;
+ }
+ /* Read alternate key type */
+ if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) {
+ try2read = read_akey_type;
+ error++;
+ }
+ akey->key_data_type[0] = tmpint1;
+ /* Read alternate key */
+ if (!error && read_octet_string(filep,
+ akey->key_data_contents[0],
+ akey->key_data_length[0])) {
+ try2read = read_akey_data;
+ error++;
+ }
+
+ /* convert to a new format key */
+ /* the encrypted version is stored as the unencrypted key length
+ (4 bytes, MSB first) followed by the encrypted key. */
+ if ((akey->key_data_length[0] > 4)
+ && (akey->key_data_contents[0][0] == 0)
+ && (akey->key_data_contents[0][1] == 0)) {
+ /* this really does look like an old key, so drop and swap */
+ /* the *new* length is 2 bytes, LSB first, sigh. */
+ size_t shortlen = akey->key_data_length[0]-4+2;
+ char *shortcopy = (krb5_octet *) malloc(shortlen);
+ char *origdata = akey->key_data_contents[0];
+ shortcopy[0] = origdata[3];
+ shortcopy[1] = origdata[2];
+ memcpy(shortcopy+2,origdata+4,shortlen-2);
+ free(origdata);
+ akey->key_data_length[0] = shortlen;
+ akey->key_data_contents[0] = shortcopy;
+ }
+
+ /* Read alternate salt type */
+ if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) {
+ try2read = read_asalt_type;
+ error++;
+ }
+ akey->key_data_type[1] = tmpint1;
+ /* Read alternate salt data */
+ if (!error && read_octet_string(filep,
+ akey->key_data_contents[1],
+ akey->key_data_length[1])) {
+ try2read = read_asalt_data;
+ error++;
+ }
+ /* Read expansion data - discard it */
+ if (!error) {
+ for (i=0; i<8; i++) {
+ if (fscanf(filep, "\t%u", &tmpint1) != 1) {
+ try2read = read_exp_data;
+ error++;
+ break;
+ }
+ }
+ if (!error)
+ find_record_end(filep, fname, *linenop);
+ }
+
+ /*
+ * If no error, then we're done reading. Now parse the names
+ * and store the database dbent.
+ */
+ if (!error) {
+ if (!(kret = krb5_parse_name(kcontext,
+ name,
+ &dbent.princ))) {
+ if (!(kret = krb5_parse_name(kcontext,
+ mod_name,
+ &mod_princ))) {
+ if (!(kret =
+ krb5_dbe_update_mod_princ_data(kcontext,
+ &dbent,
+ mod_date,
+ mod_princ)) &&
+ !(kret =
+ krb5_dbe_update_last_pwd_change(kcontext,
+ &dbent,
+ last_pwd_change))) {
+ int one = 1;
+
+ dbent.len = KRB5_KDB_V1_BASE_LENGTH;
+ pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ?
+ 2 : 1;
+ akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ?
+ 2 : 1;
+ if ((pkey->key_data_type[0] ==
+ akey->key_data_type[0]) &&
+ (pkey->key_data_type[1] ==
+ akey->key_data_type[1]))
+ dbent.n_key_data--;
+ else if ((akey->key_data_type[0] == 0)
+ && (akey->key_data_length[0] == 0)
+ && (akey->key_data_type[1] == 0)
+ && (akey->key_data_length[1] == 0))
+ dbent.n_key_data--;
+ if ((kret = krb5_db_put_principal(kcontext,
+ &dbent,
+ &one)) ||
+ (one != 1)) {
+ fprintf(stderr, store_err_fmt,
+ fname, *linenop, name,
+ error_message(kret));
+ error++;
+ }
+ else {
+ if (verbose)
+ fprintf(stderr, add_princ_fmt, name);
+ retval = 0;
+ }
+ dbent.n_key_data = 2;
+ }
+ krb5_free_principal(kcontext, mod_princ);
+ }
+ else {
+ fprintf(stderr, parse_err_fmt,
+ fname, *linenop, mod_name,
+ error_message(kret));
+ error++;
+ }
+ }
+ else {
+ fprintf(stderr, parse_err_fmt,
+ fname, *linenop, name, error_message(kret));
+ error++;
+ }
+ }
+ else {
+ fprintf(stderr, read_err_fmt, fname, *linenop, try2read);
+ }
+ }
+ else {
+ fprintf(stderr, no_mem_fmt, fname, *linenop);
+ }
+
+ krb5_db_free_principal(kcontext, &dbent, 1);
+ if (mod_name)
+ free(mod_name);
+ if (name)
+ free(name);
+ }
+ else {
+ if (nmatched != EOF)
+ fprintf(stderr, rhead_err_fmt, fname, *linenop);
+ else
+ retval = -1;
+ }
+ return(retval);
+}
+
+/*
+ * process_k5beta6_record() - Handle a dump record in krb5b6 format.
+ *
+ * Returns -1 for end of file, 0 for success and 1 for failure.
+ */
+static int
+process_k5beta6_record(fname, kcontext, filep, verbose, linenop, pol_db)
+ char *fname;
+ krb5_context kcontext;
+ FILE *filep;
+ int verbose;
+ int *linenop;
+ void *pol_db;
+{
+ int retval;
+ krb5_db_entry dbentry;
+ krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9;
+ int nread;
+ int error;
+ int i, j, one;
+ char *name;
+ krb5_key_data *kp, *kdatap;
+ krb5_tl_data **tlp, *tl;
+ krb5_octet *op;
+ krb5_error_code kret;
+ const char *try2read;
+
+ try2read = (char *) NULL;
+ memset((char *) &dbentry, 0, sizeof(dbentry));
+ (*linenop)++;
+ retval = 1;
+ name = (char *) NULL;
+ kp = (krb5_key_data *) NULL;
+ op = (krb5_octet *) NULL;
+ error = 0;
+ kret = 0;
+ nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5);
+ if (nread == 5) {
+ /* Get memory for flattened principal name */
+ if (!(name = (char *) malloc((size_t) t2 + 1)))
+ error++;
+
+ /* Get memory for and form tagged data linked list */
+ tlp = &dbentry.tl_data;
+ for (i=0; i<t3; i++) {
+ if ((*tlp = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) {
+ memset(*tlp, 0, sizeof(krb5_tl_data));
+ tlp = &((*tlp)->tl_data_next);
+ dbentry.n_tl_data++;
+ }
+ else {
+ error++;
+ break;
+ }
+ }
+
+ /* Get memory for key list */
+ if (t4 && !(kp = (krb5_key_data *) malloc((size_t)
+ (t4*sizeof(krb5_key_data)))))
+ error++;
+
+ /* Get memory for extra data */
+ if (t5 && !(op = (krb5_octet *) malloc((size_t) t5)))
+ error++;
+
+ if (!error) {
+ dbentry.len = t1;
+ dbentry.n_key_data = t4;
+ dbentry.e_length = t5;
+ if (kp) {
+ memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data)));
+ dbentry.key_data = kp;
+ kp = (krb5_key_data *) NULL;
+ }
+ if (op) {
+ memset(op, 0, (size_t) t5);
+ dbentry.e_data = op;
+ op = (krb5_octet *) NULL;
+ }
+
+ /* Read in and parse the principal name */
+ if (!read_string(filep, name, t2, linenop) &&
+ !(kret = krb5_parse_name(kcontext, name, &dbentry.princ))) {
+
+ /* Get the fixed principal attributes */
+ nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",
+ &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9);
+ if (nread == 8) {
+ dbentry.attributes = (krb5_flags) t2;
+ dbentry.max_life = (krb5_deltat) t3;
+ dbentry.max_renewable_life = (krb5_deltat) t4;
+ dbentry.expiration = (krb5_timestamp) t5;
+ dbentry.pw_expiration = (krb5_timestamp) t6;
+ dbentry.last_success = (krb5_timestamp) t7;
+ dbentry.last_failed = (krb5_timestamp) t8;
+ dbentry.fail_auth_count = (krb5_kvno) t9;
+ } else {
+ try2read = read_nint_data;
+ error++;
+ }
+
+ /* Get the tagged data */
+ if (!error && dbentry.n_tl_data) {
+ for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) {
+ nread = fscanf(filep, "%d\t%d\t", &t1, &t2);
+ if (nread == 2) {
+ tl->tl_data_type = (krb5_int16) t1;
+ tl->tl_data_length = (krb5_int16) t2;
+ if (tl->tl_data_length) {
+ if (!(tl->tl_data_contents =
+ (krb5_octet *) malloc((size_t) t2+1)) ||
+ read_octet_string(filep,
+ tl->tl_data_contents,
+ t2)) {
+ try2read = read_tcontents;
+ error++;
+ break;
+ }
+ }
+ else {
+ /* Should be a null field */
+ nread = fscanf(filep, "%d", &t9);
+ if ((nread != 1) || (t9 != -1)) {
+ error++;
+ try2read = read_tcontents;
+ break;
+ }
+ }
+ }
+ else {
+ try2read = read_ttypelen;
+ error++;
+ break;
+ }
+ }
+ }
+
+ /* Get the key data */
+ if (!error && dbentry.n_key_data) {
+ for (i=0; !error && (i<dbentry.n_key_data); i++) {
+ kdatap = &dbentry.key_data[i];
+ nread = fscanf(filep, "%d\t%d\t", &t1, &t2);
+ if (nread == 2) {
+ kdatap->key_data_ver = (krb5_int16) t1;
+ kdatap->key_data_kvno = (krb5_int16) t2;
+
+ for (j=0; j<t1; j++) {
+ nread = fscanf(filep, "%d\t%d\t", &t3, &t4);
+ if (nread == 2) {
+ kdatap->key_data_type[j] = t3;
+ kdatap->key_data_length[j] = t4;
+ if (t4) {
+ if (!(kdatap->key_data_contents[j] =
+ (krb5_octet *)
+ malloc((size_t) t4+1)) ||
+ read_octet_string(filep,
+ kdatap->key_data_contents[j],
+ t4)) {
+ try2read = read_kcontents;
+ error++;
+ break;
+ }
+ }
+ else {
+ /* Should be a null field */
+ nread = fscanf(filep, "%d", &t9);
+ if ((nread != 1) || (t9 != -1)) {
+ error++;
+ try2read = read_kcontents;
+ break;
+ }
+ }
+ }
+ else {
+ try2read = read_ktypelen;
+ error++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Get the extra data */
+ if (!error && dbentry.e_length) {
+ if (read_octet_string(filep,
+ dbentry.e_data,
+ (int) dbentry.e_length)) {
+ try2read = read_econtents;
+ error++;
+ }
+ }
+ else {
+ nread = fscanf(filep, "%d", &t9);
+ if ((nread != 1) || (t9 != -1)) {
+ error++;
+ try2read = read_econtents;
+ }
+ }
+
+ /* Finally, find the end of the record. */
+ if (!error)
+ find_record_end(filep, fname, *linenop);
+
+ /*
+ * We have either read in all the data or choked.
+ */
+ if (!error) {
+ one = 1;
+ if ((kret = krb5_db_put_principal(kcontext,
+ &dbentry,
+ &one))) {
+ fprintf(stderr, store_err_fmt,
+ fname, *linenop,
+ name, error_message(kret));
+ }
+ else {
+ if (verbose)
+ fprintf(stderr, add_princ_fmt, name);
+ retval = 0;
+ }
+ }
+ else {
+ fprintf(stderr, read_err_fmt, fname, *linenop, try2read);
+ }
+ }
+ else {
+ if (kret)
+ fprintf(stderr, parse_err_fmt,
+ fname, *linenop, name, error_message(kret));
+ else
+ fprintf(stderr, no_mem_fmt, fname, *linenop);
+ }
+ }
+ else {
+ fprintf(stderr, rhead_err_fmt, fname, *linenop);
+ }
+
+ if (op)
+ free(op);
+ if (kp)
+ free(kp);
+ if (name)
+ free(name);
+ krb5_db_free_principal(kcontext, &dbentry, 1);
+ }
+ else {
+ if (nread == EOF)
+ retval = -1;
+ }
+ return(retval);
+}
+
+int process_k5beta7_policy(fname, kcontext, filep, verbose, linenop, pol_db)
+ char *fname;
+ krb5_context kcontext;
+ FILE *filep;
+ int verbose;
+ int *linenop;
+ void *pol_db;
+{
+ osa_policy_ent_rec rec;
+ char namebuf[1024];
+ int nread, ret;
+
+ (*linenop)++;
+ rec.name = namebuf;
+
+ nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d", rec.name,
+ &rec.pw_min_life, &rec.pw_max_life,
+ &rec.pw_min_length, &rec.pw_min_classes,
+ &rec.pw_history_num, &rec.policy_refcnt);
+ if (nread == EOF)
+ return -1;
+ else if (nread != 7) {
+ fprintf(stderr, "cannot parse policy on line %d (%d read)\n",
+ *linenop, nread);
+ return 1;
+ }
+
+ if (ret = osa_adb_create_policy(pol_db, &rec)) {
+ fprintf(stderr, "cannot create policy on line %d: %s\n",
+ *linenop, error_message(ret));
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "created policy %s\n", rec.name);
+
+ return 0;
+}
+
+
+/*
+ * process_k5beta7_record() - Handle a dump record in krb5b6 format.
+ *
+ * Returns -1 for end of file, 0 for success and 1 for failure.
+ */
+static int
+process_k5beta7_record(fname, kcontext, filep, verbose, linenop, pol_db)
+ char *fname;
+ krb5_context kcontext;
+ FILE *filep;
+ int verbose;
+ int *linenop;
+ void *pol_db;
+{
+ int nread;
+ char rectype[100];
+
+ nread = fscanf(filep, "%100s\t", rectype);
+ if (nread == EOF)
+ return -1;
+ else if (nread != 1)
+ return 1;
+ if (strcmp(rectype, "princ") == 0)
+ process_k5beta6_record(fname, kcontext, filep, verbose,
+ linenop, pol_db);
+ else if (strcmp(rectype, "policy") == 0)
+ process_k5beta7_policy(fname, kcontext, filep, verbose,
+ linenop, pol_db);
+ else {
+ fprintf(stderr, "unknown record type \"%s\" on line %d\n",
+ rectype, *linenop);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * restore_dump() - Restore the database from any version dump file.
+ */
+static int
+restore_dump(programname, kcontext, dumpfile, f, verbose, dump, pol_db)
+ char *programname;
+ krb5_context kcontext;
+ char *dumpfile;
+ FILE *f;
+ int verbose;
+ dump_version *dump;
+ osa_adb_policy_t pol_db;
+{
+ int error;
+ int lineno;
+
+ error = 0;
+ lineno = 1;
+
+ /*
+ * Process the records.
+ */
+ while (!(error = (*dump->load_record)(dumpfile,
+ kcontext,
+ f,
+ verbose,
+ &lineno,
+ pol_db)))
+ ;
+ if (error != -1)
+ fprintf(stderr, err_line_fmt, programname, lineno, dumpfile);
+ else
+ error = 0;
+
+ return(error);
+}
+
+/*
+ * Usage is
+ * load_db [-old] [-verbose] [-update] filename dbname
+ */
+void
+load_db(argc, argv)
+ int argc;
+ char **argv;
+{
+ kadm5_config_params newparams;
+ osa_adb_policy_t pol_db;
+ krb5_error_code kret;
+ krb5_context kcontext;
+ FILE *f;
+ extern char *optarg;
+ extern int optind;
+ char *programname;
+ char *dumpfile;
+ char *dbname, *adbname;
+ char *dbname_tmp, *adbname_real;
+ char buf[BUFSIZ];
+ dump_version *load;
+ int update, verbose;
+ int aindex;
+
+ /*
+ * Parse the arguments.
+ */
+ programname = argv[0];
+ if (strrchr(programname, (int) '/'))
+ programname = strrchr(argv[0], (int) '/') + 1;
+ dumpfile = (char *) NULL;
+ dbname = (char *) NULL;
+ load = NULL;
+ update = 0;
+ verbose = 0;
+ exit_status = 0;
+ dbname_tmp = (char *) NULL;
+ for (aindex = 1; aindex < argc; aindex++) {
+ if (!strcmp(argv[aindex], oldoption))
+ load = &old_version;
+ else if (!strcmp(argv[aindex], b6option))
+ load = &beta6_version;
+ else if (!strcmp(argv[aindex], verboseoption))
+ verbose = 1;
+ else if (!strcmp(argv[aindex], updateoption))
+ update = 1;
+ else
+ break;
+ }
+ if ((argc - aindex) != 2 && (argc - aindex) != 3) {
+ fprintf(stderr, lusage_err_fmt, argv[0], argv[0],
+ oldoption, verboseoption, updateoption);
+ exit_status++;
+ return;
+ }
+
+ dumpfile = argv[aindex];
+ dbname = argv[aindex+1];
+ adbname = argv[aindex+2];
+ if (!(dbname_tmp = (char *) malloc(strlen(dbname)+
+ strlen(dump_tmptrail)+1))) {
+ fprintf(stderr, no_name_mem_fmt, argv[0]);
+ exit_status++;
+ return;
+ }
+ strcpy(dbname_tmp, dbname);
+ strcat(dbname_tmp, dump_tmptrail);
+
+ /*
+ * Initialize the Kerberos context and error tables.
+ */
+ if ((kret = krb5_init_context(&kcontext))) {
+ fprintf(stderr, ctx_err_fmt, programname);
+ free(dbname_tmp);
+ exit_status++;
+ return;
+ }
+ krb5_init_ets(kcontext);
+
+ /*
+ * Open the dumpfile
+ */
+ if (dumpfile) {
+ if ((f = fopen(dumpfile, "r+")) == NULL) {
+ fprintf(stderr, dfile_err_fmt, programname, dumpfile,
+ error_message(errno));
+ exit_status++;
+ return;
+ }
+ if (kret = krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_SHARED)) {
+ fprintf(stderr, "%s: Cannot lock %s: %s\n", programname,
+ dumpfile, error_message(errno));
+ exit_status++;
+ return;
+ }
+ } else
+ f = stdin;
+
+ /*
+ * Auto-detect dump version if we weren't told, verify if we
+ * were told.
+ */
+ fgets(buf, sizeof(buf), f);
+ if (load) {
+ if (strcmp(buf, load->header) != 0) {
+ fprintf(stderr, head_bad_fmt, programname, dumpfile);
+ exit_status++;
+ if (dumpfile) fclose(f);
+ return;
+ }
+ } else {
+ /* perhaps this should be in an array, but so what? */
+ if (strcmp(buf, old_version.header) == 0)
+ load = &old_version;
+ else if (strcmp(buf, beta6_version.header) == 0)
+ load = &beta6_version;
+ else if (strcmp(buf, beta7_version.header) == 0)
+ load = &beta7_version;
+ else {
+ fprintf(stderr, head_bad_fmt, programname, dumpfile);
+ exit_status++;
+ if (dumpfile) fclose(f);
+ return;
+ }
+
+ if (load != &beta7_version && adbname != NULL) {
+ fprintf(stderr, lusage_err_fmt, argv[0], argv[0],
+ oldoption, verboseoption, updateoption);
+ exit_status++;
+ return;
+ }
+ }
+
+ /*
+ * Cons up config params for new policy database. Use adbname is
+ * specified, otherwise let the policy dbname key off the dbname.
+ * However, after the name is retrieved, change the actual file
+ * name to a temp name that we'll rename later (but use the
+ * correct lock file).
+ */
+ newparams = global_params;
+ newparams.mask &= ~(KADM5_CONFIG_ADBNAME | KADM5_CONFIG_ADB_LOCKFILE);
+ newparams.dbname = dbname;
+ newparams.mask |= KADM5_CONFIG_DBNAME;
+ if (adbname) {
+ newparams.admin_dbname = adbname;
+ newparams.mask |= KADM5_CONFIG_ADBNAME;
+ }
+ if (kret = kadm5_get_config_params(kcontext, NULL, NULL, &newparams,
+ &newparams)) {
+ fprintf(stderr, "%s while retrieiving configuration "
+ "parameters.\n", error_message(kret));
+ if (dumpfile) fclose(f);
+ exit_status++;
+ return;
+ }
+ adbname_real = newparams.admin_dbname;
+ newparams.admin_dbname = (char *) malloc(strlen(adbname_real) +
+ strlen(dump_tmptrail) + 1);
+ strcpy(newparams.admin_dbname, adbname_real);
+ strcat(newparams.admin_dbname, dump_tmptrail);
+
+ /*
+ * Create the new database if not an update restoration. Always
+ * create the policy db, even if we are not loading a dump file
+ * with policy info, because they may be loading an old dump
+ * intending to use it with the new kadm5 system (ie: using load
+ * as create).
+ */
+ if (!update && (kret = krb5_db_create(kcontext, dbname_tmp))) {
+ fprintf(stderr, dbcreaterr_fmt,
+ programname, dbname, error_message(kret));
+ exit_status++;
+ kadm5_free_config_params(kcontext, &newparams);
+ if (dumpfile) fclose(f);
+ return;
+ }
+ if (!update && (kret = osa_adb_create_policy_db(&newparams))) {
+ fprintf(stderr, "%s: %s while creating policy database\n",
+ programname, error_message(kret));
+ kadm5_free_config_params(kcontext, &newparams);
+ if (dumpfile) fclose(f);
+ return;
+ }
+
+ /*
+ * Point ourselves at the new databases.
+ */
+ if (kret = krb5_db_set_name(kcontext,
+ (update) ? dbname : dbname_tmp)) {
+ fprintf(stderr, dbname_err_fmt,
+ programname,
+ (update) ? dbname : dbname_tmp, error_message(kret));
+ exit_status++;
+ goto error;
+ }
+ if (kret = osa_adb_open_policy(&pol_db, &newparams)) {
+ fprintf(stderr, "%s: %s while opening policy database\n",
+ programname, error_message(kret));
+ exit_status++;
+ goto error;
+ }
+
+ /*
+ * Initialize the database.
+ */
+ if (kret = krb5_db_init(kcontext)) {
+ fprintf(stderr, dbinit_err_fmt,
+ programname, error_message(kret));
+ exit_status++;
+ goto error;
+ }
+
+ if (restore_dump(programname, kcontext, (dumpfile) ? dumpfile : stdin_name,
+ f, verbose, load, pol_db)) {
+ fprintf(stderr, restfail_fmt,
+ programname, load->name);
+ exit_status++;
+ }
+
+ if ((kret = krb5_db_fini(kcontext)) ||
+ (kret = osa_adb_close_policy(pol_db))) {
+ fprintf(stderr, close_err_fmt,
+ programname, error_message(kret));
+ exit_status++;
+ }
+
+error:
+ /*
+ * If there was an error and this is not an update, then
+ * destroy the database.
+ */
+ if (!update) {
+ if (exit_status) {
+ if ((kret = kdb5_db_destroy(kcontext, dbname))) {
+ fprintf(stderr, dbdelerr_fmt,
+ programname, dbname_tmp, error_message(kret));
+ exit_status++;
+ }
+ }
+ else {
+ if ((kret = krb5_db_rename(kcontext,
+ dbname_tmp,
+ dbname))) {
+ fprintf(stderr, dbrenerr_fmt,
+ programname, dbname_tmp, dbname,
+ error_message(kret));
+ exit_status++;
+ }
+
+ if (kret = osa_adb_get_lock(pol_db, OSA_ADB_PERMANENT)) {
+ fprintf(stderr,
+ "%s: %s while getting permanent lock\n",
+ programname, error_message(kret));
+ exit_status++;
+ } else if (rename(newparams.admin_dbname,
+ adbname_real) < 0) {
+ fprintf(stderr, "%s: %s while renaming %s to %s\n",
+ programname, error_message(kret),
+ newparams.admin_dbname, adbname_real);
+ exit_status++;
+ } else if (kret = osa_adb_release_lock(pol_db)) {
+ fprintf(stderr, "%s: %s while unlocking %s\n",
+ programname, error_message(kret),
+ adbname_real);
+ exit_status++;
+ }
+ }
+ }
+
+ if (dumpfile) {
+ (void) krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_UNLOCK);
+ fclose(f);
+ }
+
+ free(newparams.admin_dbname);
+ newparams.admin_dbname = adbname_real;
+ kadm5_free_config_params(kcontext, &newparams);
+ free(dbname_tmp);
+ krb5_free_context(kcontext);
+}
diff --git a/src/kadmin/dbutil/dumpv4.c b/src/kadmin/dbutil/dumpv4.c
new file mode 100644
index 000000000..a51626db3
--- /dev/null
+++ b/src/kadmin/dbutil/dumpv4.c
@@ -0,0 +1,411 @@
+/*
+ * admin/edit/dumpv4.c
+ *
+ * Copyright 1990,1991, 1994 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. 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.
+ *
+ *
+ * Dump a KDC database into a V4 slave dump.
+ */
+
+#ifdef KRB5_KRB4_COMPAT
+
+#include "k5-int.h"
+#include "com_err.h"
+
+#include <des.h>
+#include <krb.h>
+#include <krb_db.h>
+/* MKEYFILE is now defined in kdc.h */
+#include <kdc.h>
+
+#include <stdio.h>
+#include "kdb5_util.h"
+
+struct dump_record {
+ char *comerr_name;
+ FILE *f;
+ krb5_encrypt_block *v5master;
+ C_Block v4_master_key;
+ Key_schedule v4_master_key_schedule;
+ long master_key_version;
+ char *realm;
+};
+
+extern krb5_encrypt_block master_encblock;
+extern krb5_keyblock master_keyblock;
+extern krb5_principal master_princ;
+extern krb5_boolean dbactive;
+extern int exit_status;
+extern krb5_context util_context;
+
+void update_ok_file();
+
+#define ANAME_SZ 40
+#define INST_SZ 40
+
+static char *v4_mkeyfile = "/.k";
+
+static int
+v4init(arg, manual)
+ struct dump_record *arg;
+ int manual;
+{
+ int fd;
+ int ok = 0;
+
+ if (!manual) {
+ fd = open(v4_mkeyfile, O_RDONLY, 0600);
+ if (fd >= 0) {
+ if (read(fd,arg->v4_master_key,sizeof(C_Block)) == sizeof(C_Block))
+ ok = 1;
+ close(fd);
+ }
+ }
+ if (!ok) {
+ des_read_password(arg->v4_master_key, "V4 Kerberos master key: ", 1);
+ printf("\n");
+ }
+ arg->master_key_version = 1;
+ key_sched(arg->v4_master_key, arg->v4_master_key_schedule);
+
+ return 0;
+}
+
+v4_print_time(file, timeval)
+ FILE *file;
+ unsigned long timeval;
+{
+ struct tm *tm;
+ struct tm *gmtime();
+ tm = gmtime((time_t *)&timeval);
+ fprintf(file, " %04d%02d%02d%02d%02d",
+ tm->tm_year < 1900 ? tm->tm_year + 1900: tm->tm_year,
+ tm->tm_mon + 1,
+ tm->tm_mday,
+ tm->tm_hour,
+ tm->tm_min);
+}
+
+
+
+krb5_error_code
+dump_v4_iterator(ptr, entry)
+ krb5_pointer ptr;
+ krb5_db_entry *entry;
+{
+ struct dump_record *arg = (struct dump_record *) ptr;
+ krb5_principal mod_princ;
+ krb5_timestamp mod_time;
+ krb5_error_code retval;
+ int i, max_kvno, ok_key;
+
+ struct v4princ {
+ char name[ANAME_SZ+1];
+ char instance[INST_SZ+1];
+ char realm[REALM_SZ+1];
+ int max_life;
+ int kdc_key_ver, key_version, attributes;
+ char mod_name[ANAME_SZ+1];
+ char mod_instance[INST_SZ+1];
+ char mod_realm[REALM_SZ+1];
+ } v4princ, *principal;
+ des_cblock v4key;
+
+ principal = &v4princ;
+
+ if (strcmp(krb5_princ_realm(util_context, entry->princ)->data, arg->realm))
+ /* skip this because it's a key for a different realm, probably
+ * a paired krbtgt key */
+ return 0;
+
+ retval = krb5_524_conv_principal(util_context, entry->princ,
+ principal->name, principal->instance,
+ principal->realm);
+ if (retval)
+ /* Skip invalid V4 principals */
+ return 0;
+
+ if (!strcmp(principal->name, "K") && !strcmp(principal->instance, "M"))
+ /* The V4 master key is handled specially */
+ return 0;
+
+ if (! principal->name[0])
+ return 0;
+ if (! principal->instance[0])
+ strcpy(principal->instance, "*");
+
+ /* Now move to mod princ */
+ if (retval = krb5_dbe_lookup_mod_princ_data(util_context,entry,
+ &mod_time, &mod_princ)){
+ com_err(arg->comerr_name, retval, "while unparsing db entry");
+ exit_status++;
+ return retval;
+ }
+ retval = krb5_524_conv_principal(util_context, mod_princ,
+ principal->mod_name, principal->mod_instance,
+ principal->mod_realm);
+ if (retval) {
+ /* Invalid V4 mod principal */
+ principal->mod_name[0] = '\0';
+ principal->mod_instance[0] = '\0';
+ }
+
+ if (! principal->mod_name[0])
+ strcpy(principal->mod_name, "*");
+ if (! principal->mod_instance[0])
+ strcpy(principal->mod_instance, "*");
+
+ /* OK deal with the key now. */
+ for (max_kvno = i = 0; i < entry->n_key_data; i++) {
+ if (max_kvno < entry->key_data[i].key_data_kvno) {
+ max_kvno = entry->key_data[i].key_data_kvno;
+ ok_key = i;
+ }
+ }
+
+ i = ok_key;
+ while (ok_key < entry->n_key_data) {
+ if (max_kvno == entry->key_data[ok_key].key_data_kvno) {
+ if (entry->key_data[ok_key].key_data_type[1]
+ == KRB5_KDB_SALTTYPE_V4) {
+ goto found_one;
+ }
+ }
+ ok_key++;
+ }
+
+ /* See if there are any DES keys that may be suitable */
+ ok_key = i;
+ while (ok_key < entry->n_key_data) {
+ if (max_kvno == entry->key_data[ok_key].key_data_kvno) {
+ krb5_enctype enctype = entry->key_data[ok_key].key_data_type[0];
+ if ((enctype == ENCTYPE_DES_CBC_CRC) ||
+ (enctype == ENCTYPE_DES_CBC_MD5) ||
+ (enctype == ENCTYPE_DES_CBC_RAW))
+ goto found_one;
+ }
+ ok_key++;
+ }
+ /* skip this because it's a new style key and we can't help it */
+ return 0;
+
+found_one:;
+ principal->key_version = max_kvno;
+ if ((principal->max_life = entry->max_life / (60 * 5)) > 255)
+ principal->max_life = 255;
+ principal->kdc_key_ver = arg->master_key_version;
+ principal->attributes = 0; /* ??? not preserved either */
+
+ fprintf(arg->f, "%s %s %d %d %d %d ",
+ principal->name,
+ principal->instance,
+ principal->max_life,
+ principal->kdc_key_ver,
+ principal->key_version,
+ principal->attributes);
+
+ handle_one_key(arg, arg->v5master, &entry->key_data[ok_key], v4key);
+
+ for (i = 0; i < 8; i++) {
+ fprintf(arg->f, "%02x", ((unsigned char*)v4key)[i]);
+ if (i == 3) fputc(' ', arg->f);
+ }
+
+ v4_print_time(arg->f, entry->expiration);
+ v4_print_time(arg->f, mod_time);
+
+ fprintf(arg->f, " %s %s\n", principal->mod_name, principal->mod_instance);
+ return 0;
+}
+
+/*ARGSUSED*/
+void dump_v4db(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *f;
+ struct dump_record arg;
+
+ if (argc > 2) {
+ com_err(argv[0], 0, "Usage: %s filename", argv[0]);
+ exit_status++;
+ return;
+ }
+ if (!dbactive) {
+ com_err(argv[0], 0, Err_no_database);
+ exit_status++;
+ return;
+ }
+ if (argc == 2) {
+ /*
+ * Make sure that we don't open and truncate on the fopen,
+ * since that may hose an on-going kprop process.
+ *
+ * We could also control this by opening for read and
+ * write, doing an flock with LOCK_EX, and then
+ * truncating the file once we have gotten the lock,
+ * but that would involve more OS dependancies than I
+ * want to get into.
+ */
+ unlink(argv[1]);
+ if (!(f = fopen(argv[1], "w"))) {
+ com_err(argv[0], errno,
+ "While opening file %s for writing", argv[1]);
+ exit_status++;
+ return;
+ }
+ } else {
+ f = stdout;
+ }
+
+ arg.comerr_name = argv[0];
+ arg.f = f;
+ v4init(&arg, 0);
+ handle_keys(&arg);
+
+ /* special handling for K.M since it isn't preserved */
+ {
+ des_cblock v4key;
+ int i;
+
+ /* assume:
+ max lifetime (255)
+ key version == 1 (actually, should be whatever the v5 one is)
+ master key version == key version
+ args == 0 (none are preserved)
+ expiration date is the default 2000
+ last mod time is near zero (arbitrarily.)
+ creator is db_creation *
+ */
+
+ fprintf(f,"K M 255 1 1 0 ");
+
+#ifndef KDB4_DISABLE
+ kdb_encrypt_key (arg.v4_master_key, v4key,
+ arg.v4_master_key, arg.v4_master_key_schedule,
+ ENCRYPT);
+#else /* KDB4_DISABLE */
+ pcbc_encrypt((C_Block *) arg.v4_master_key,
+ (C_Block *) v4key,
+ (long) sizeof(C_Block),
+ arg.v4_master_key_schedule,
+ (C_Block *) arg.v4_master_key,
+ ENCRYPT);
+#endif /* KDB4_DISABLE */
+
+ for (i=0; i<8; i++) {
+ fprintf(f, "%02x", ((unsigned char*)v4key)[i]);
+ if (i == 3) fputc(' ', f);
+ }
+ fprintf(f," 200001010459 197001020000 db_creation *\n");
+ }
+
+ (void) krb5_db_iterate(util_context, dump_v4_iterator,
+ (krb5_pointer) &arg);
+ if (argc == 2)
+ fclose(f);
+ if (argv[1])
+ update_ok_file(argv[1]);
+}
+
+int handle_keys(arg)
+ struct dump_record *arg;
+{
+ krb5_error_code retval;
+ char *defrealm;
+ char *mkey_name = 0;
+ char *mkey_fullname;
+ krb5_principal master_princ;
+
+ if (retval = krb5_get_default_realm(util_context, &defrealm)) {
+ com_err(arg->comerr_name, retval,
+ "while retrieving default realm name");
+ exit(1);
+ }
+ arg->realm = defrealm;
+
+ /* assemble & parse the master key name */
+
+ if (retval = krb5_db_setup_mkey_name(util_context, mkey_name, arg->realm,
+ &mkey_fullname, &master_princ)) {
+ com_err(arg->comerr_name, retval, "while setting up master key name");
+ exit(1);
+ }
+
+ krb5_use_enctype(util_context, &master_encblock, DEFAULT_KDC_ENCTYPE);
+ if (retval = krb5_db_fetch_mkey(util_context, master_princ,
+ &master_encblock, 0,
+ 0, (char *) NULL, 0, &master_keyblock)) {
+ com_err(arg->comerr_name, retval, "while reading master key");
+ exit(1);
+ }
+ if (retval = krb5_process_key(util_context, &master_encblock,
+ &master_keyblock)) {
+ com_err(arg->comerr_name, retval, "while processing master key");
+ exit(1);
+ }
+ arg->v5master = &master_encblock;
+ return(0);
+}
+
+handle_one_key(arg, v5master, v5key, v4key)
+ struct dump_record *arg;
+ krb5_encrypt_block *v5master;
+ krb5_key_data *v5key;
+ des_cblock v4key;
+{
+ krb5_error_code retval;
+
+ krb5_keyblock v4v5key;
+ krb5_keyblock v5plainkey;
+ /* v4key is the actual v4 key from the file. */
+
+ if (retval = krb5_dbekd_decrypt_key_data(util_context, v5master, v5key,
+ &v5plainkey, NULL))
+ return retval;
+
+ /* v4v5key.contents = (krb5_octet *)v4key; */
+ /* v4v5key.enctype = ENCTYPE_DES; */
+ /* v4v5key.length = sizeof(v4key); */
+
+ memcpy(v4key, v5plainkey.contents, sizeof(des_cblock));
+#ifndef KDB4_DISABLE
+ kdb_encrypt_key (v4key, v4key,
+ arg->v4_master_key, arg->v4_master_key_schedule,
+ ENCRYPT);
+#else /* KDB4_DISABLE */
+ pcbc_encrypt((C_Block *) v4key,
+ (C_Block *) v4key,
+ (long) sizeof(C_Block),
+ arg->v4_master_key_schedule,
+ (C_Block *) arg->v4_master_key,
+ ENCRYPT);
+#endif /* KDB4_DISABLE */
+ return 0;
+}
+
+#else /* KRB5_KRB4_COMPAT */
+void dump_v4db(argc, argv)
+ int argc;
+ char **argv;
+{
+ printf("This version of krb5_edit does not support the V4 dump command.\n");
+}
+#endif /* KRB5_KRB4_COMPAT */
diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c
new file mode 100644
index 000000000..d31ce3319
--- /dev/null
+++ b/src/kadmin/dbutil/kadm5_create.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include "string_table.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <kadm5/adb.h>
+#include <kadm5/admin.h>
+
+#include <krb5.h>
+#include <krb5/kdb.h>
+
+int add_admin_princ(void *handle, krb5_context context,
+ char *name, char *realm, int attrs, int lifetime);
+
+#define ERR 1
+#define OK 0
+
+#define ADMIN_LIFETIME 60*60*3 /* 3 hours */
+#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */
+
+extern char *whoami;
+
+extern krb5_encrypt_block master_encblock;
+extern krb5_keyblock master_keyblock;
+extern krb5_db_entry master_db;
+
+/*
+ * Function: kadm5_create
+ *
+ * Purpose: create admin principals in KDC database
+ *
+ * Arguments: params (r) configuration parameters to use
+ *
+ * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE
+ * principals in the KDC database and sets their attributes
+ * appropriately.
+ */
+int kadm5_create(kadm5_config_params *params)
+{
+ int retval;
+ void *handle;
+ krb5_context context;
+ FILE *f;
+
+
+ if (retval = krb5_init_context(&context))
+ exit(ERR);
+
+ /*
+ * The lock file has to exist before calling kadm5_init, but
+ * params->admin_lockfile may not be set yet...
+ */
+ if (retval = kadm5_get_config_params(context, NULL, NULL,
+ params, params)) {
+ com_err(whoami, retval, str_INITING_KCONTEXT);
+ return 1;
+ }
+
+ if (retval = osa_adb_create_policy_db(params)) {
+ com_err(whoami, retval, str_CREATING_POLICY_DB);
+ return 1;
+ }
+
+ if ((retval = kadm5_init(whoami, NULL, NULL, params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &handle))) {
+ com_err(whoami, retval, str_INITING_KCONTEXT);
+
+ krb5_free_context(context);
+ exit(ERR);
+ }
+
+ retval = add_admin_princs(handle, context, params->realm);
+
+ kadm5_destroy(handle);
+ krb5_free_context(context);
+
+ if (retval)
+ exit(retval);
+
+ return 0;
+}
+
+/*
+ * Function: build_name_with_realm
+ *
+ * Purpose: concatenate a name and a realm to form a krb5 name
+ *
+ * Arguments:
+ *
+ * name (input) the name
+ * realm (input) the realm
+ *
+ * Returns:
+ *
+ * pointer to name@realm, in allocated memory, or NULL if it
+ * cannot be allocated
+ *
+ * Requires: both strings are null-terminated
+ */
+char *build_name_with_realm(char *name, char *realm)
+{
+ char *n;
+
+ n = (char *) malloc(strlen(name) + strlen(realm) + 2);
+ sprintf(n, "%s@%s", name, realm);
+ return n;
+}
+
+/*
+ * Function: add_admin_princs
+ *
+ * Purpose: create admin principals
+ *
+ * Arguments:
+ *
+ * rseed (input) random seed
+ * realm (input) realm, or NULL for default realm
+ * <return value> (output) status, 0 for success, 1 for serious error
+ *
+ * Requires:
+ *
+ * Effects:
+ *
+ * add_admin_princs creates KADM5_ADMIN_SERVICE,
+ * KADM5_CHANGEPW_SERVICE. If any of these exist a message is
+ * printed. If any of these existing principal do not have the proper
+ * attributes, a warning message is printed.
+ */
+int add_admin_princs(void *handle, krb5_context context, char *realm)
+{
+ krb5_error_code ret = 0;
+
+ if ((ret = add_admin_princ(handle, context,
+ KADM5_ADMIN_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED,
+ ADMIN_LIFETIME)))
+ goto clean_and_exit;
+
+ if ((ret = add_admin_princ(handle, context,
+ KADM5_CHANGEPW_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED |
+ KRB5_KDB_PWCHANGE_SERVICE,
+ CHANGEPW_LIFETIME)))
+ goto clean_and_exit;
+
+clean_and_exit:
+
+ return ret;
+}
+
+/*
+ * Function: add_admin_princ
+ *
+ * Arguments:
+ *
+ * creator (r) principal to use as "mod_by"
+ * rseed (r) seed for random key generator
+ * name (r) principal name
+ * realm (r) realm name for principal
+ * attrs (r) principal's attributes
+ * lifetime (r) principal's max life, or 0
+ * not_unique (r) error message for multiple entries, never used
+ * exists (r) warning message for principal exists
+ * wrong_attrs (r) warning message for wrong attributes
+ *
+ * Returns:
+ *
+ * OK on success
+ * ERR on serious errors
+ *
+ * Effects:
+ *
+ * If the principal is not unique, not_unique is printed (but this
+ * never happens). If the principal exists, then exists is printed
+ * and if the principals attributes != attrs, wrong_attrs is printed.
+ * Otherwise, the principal is created with mod_by creator and
+ * attributes attrs and max life of lifetime (if not zero).
+ */
+
+int add_admin_princ(void *handle, krb5_context context,
+ char *name, char *realm, int attrs, int lifetime)
+{
+ char *fullname;
+ int nprincs;
+ krb5_error_code ret;
+ kadm5_principal_ent_rec ent;
+
+ memset(&ent, 0, sizeof(ent));
+
+ fullname = build_name_with_realm(name, realm);
+ if (ret = krb5_parse_name(context, fullname, &ent.principal)) {
+ com_err(whoami, ret, str_PARSE_NAME);
+ return(ERR);
+ }
+ ent.max_life = lifetime;
+ ent.attributes = attrs;
+
+ if (ret = kadm5_create_principal(handle, &ent,
+ (KADM5_PRINCIPAL |
+ KADM5_MAX_LIFE |
+ KADM5_ATTRIBUTES),
+ "to-be-random")) {
+ if (ret == KADM5_DUP)
+ ret = kadm5_modify_principal(handle, &ent,
+ (KADM5_PRINCIPAL |
+ KADM5_MAX_LIFE |
+ KADM5_ATTRIBUTES));
+
+ if (ret) {
+ com_err(whoami, ret, str_PUT_PRINC, fullname);
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+ return ERR;
+ }
+ }
+
+ ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL);
+
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+
+ if (ret) {
+ com_err(whoami, ret, str_RANDOM_KEY, fullname);
+ return ERR;
+ }
+
+ return OK;
+}
diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c
new file mode 100644
index 000000000..b7520d98d
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_create.c
@@ -0,0 +1,449 @@
+/*
+ * admin/create/kdb5_create.c
+ *
+ * Copyright 1990,1991 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.
+ *
+ *
+ * Generate (from scratch) a Kerberos KDC database.
+ */
+
+#include <stdio.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <kadm5/adb.h>
+
+enum ap_op {
+ NULL_KEY, /* setup null keys */
+ MASTER_KEY, /* use master key as new key */
+ TGT_KEY /* special handling for tgt key */
+};
+
+krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL };
+
+struct realm_info {
+ krb5_deltat max_life;
+ krb5_deltat max_rlife;
+ krb5_timestamp expiration;
+ krb5_flags flags;
+ krb5_encrypt_block *eblock;
+ krb5_pointer rseed;
+ krb5_int32 nkslist;
+ krb5_key_salt_tuple *kslist;
+} rblock = { /* XXX */
+ KRB5_KDB_MAX_LIFE,
+ KRB5_KDB_MAX_RLIFE,
+ KRB5_KDB_EXPIRATION,
+ KRB5_KDB_DEF_FLAGS,
+ (krb5_encrypt_block *) NULL,
+ (krb5_pointer) NULL,
+ 1,
+ &def_kslist
+};
+
+struct iterate_args {
+ krb5_context ctx;
+ struct realm_info *rblock;
+ krb5_db_entry *dbentp;
+};
+
+static krb5_error_code add_principal
+ PROTOTYPE((krb5_context,
+ krb5_principal,
+ enum ap_op,
+ struct realm_info *));
+
+/*
+ * Steps in creating a database:
+ *
+ * 1) use the db calls to open/create a new database
+ *
+ * 2) get a realm name for the new db
+ *
+ * 3) get a master password for the new db; convert to an encryption key.
+ *
+ * 4) create various required entries in the database
+ *
+ * 5) close & exit
+ */
+
+extern krb5_keyblock master_keyblock;
+extern krb5_principal master_princ;
+extern krb5_encrypt_block master_encblock;
+krb5_data master_salt;
+
+krb5_data tgt_princ_entries[] = {
+ {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
+ {0, 0, 0} };
+
+krb5_data db_creator_entries[] = {
+ {0, sizeof("db_creation")-1, "db_creation"} };
+
+/* XXX knows about contents of krb5_principal, and that tgt names
+ are of form TGT/REALM@REALM */
+krb5_principal_data tgt_princ = {
+ 0, /* magic number */
+ {0, 0, 0}, /* krb5_data realm */
+ tgt_princ_entries, /* krb5_data *data */
+ 2, /* int length */
+ KRB5_NT_SRV_INST /* int type */
+};
+
+krb5_principal_data db_create_princ = {
+ 0, /* magic number */
+ {0, 0, 0}, /* krb5_data realm */
+ db_creator_entries, /* krb5_data *data */
+ 1, /* int length */
+ KRB5_NT_SRV_INST /* int type */
+};
+
+static char *mkey_password = 0;
+char *whoami;
+
+extern int exit_status;
+extern osa_adb_policy_t policy_db;
+extern kadm5_config_params global_params;
+extern krb5_context util_context;
+
+static void usage()
+{
+ fprintf(stderr, "usage: %s [-s]\n", whoami);
+ exit_status++;
+}
+
+void kdb5_create(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ int optchar;
+
+ krb5_error_code retval;
+ char *mkey_fullname;
+ char *pw_str = 0;
+ int pw_size = 0;
+ int do_stash = 0;
+ krb5_data pwd;
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+ whoami = argv[0];
+
+ mkey_password = NULL;
+ optind = 1;
+ while ((optchar = getopt(argc, argv, "P:s")) != EOF) {
+ switch(optchar) {
+ case 's':
+ do_stash++;
+ break;
+ case 'P': /* Only used for testing!!! */
+ mkey_password = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ return;
+ }
+ }
+
+ rblock.max_life = global_params.max_life;
+ rblock.max_rlife = global_params.max_rlife;
+ rblock.expiration = global_params.expiration;
+ rblock.flags = global_params.flags;
+ rblock.nkslist = global_params.num_keysalts;
+ rblock.kslist = global_params.keysalts;
+
+ krb5_use_enctype(util_context, &master_encblock, master_keyblock.enctype);
+
+ retval = krb5_db_set_name(util_context, global_params.dbname);
+ if (!retval) retval = EEXIST;
+
+ if (retval == EEXIST || retval == EACCES || retval == EPERM) {
+ /* it exists ! */
+ com_err(argv[0], 0, "The database '%s' appears to already exist",
+ global_params.dbname);
+ exit_status++; return;
+ }
+
+ /* assemble & parse the master key name */
+
+ if ((retval = krb5_db_setup_mkey_name(util_context,
+ global_params.mkey_name,
+ global_params.realm,
+ &mkey_fullname, &master_princ))) {
+ com_err(argv[0], retval, "while setting up master key name");
+ exit_status++; return;
+ }
+
+ krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm);
+ krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm));
+ krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm);
+ krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm));
+ krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm;
+ krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm);
+
+ printf("Initializing database '%s' for realm '%s',\n\
+master key name '%s'\n",
+ global_params.dbname, global_params.realm, mkey_fullname);
+
+ if (!mkey_password) {
+ printf("You will be prompted for the database Master Password.\n");
+ printf("It is important that you NOT FORGET this password.\n");
+ fflush(stdout);
+
+ pw_size = 1024;
+ pw_str = malloc(pw_size);
+
+ retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
+ pw_str, &pw_size);
+ if (retval) {
+ com_err(argv[0], retval, "while reading master key from keyboard");
+ exit_status++; return;
+ }
+ mkey_password = pw_str;
+ }
+
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ retval = krb5_principal2salt(util_context, master_princ, &master_salt);
+ if (retval) {
+ com_err(argv[0], retval, "while calculated master key salt");
+ exit_status++; return;
+ }
+ if (retval = krb5_string_to_key(util_context, &master_encblock,
+ &master_keyblock, &pwd, &master_salt)) {
+ com_err(argv[0], retval, "while transforming master key from password");
+ exit_status++; return;
+ }
+
+ if ((retval = krb5_process_key(util_context, &master_encblock,
+ &master_keyblock))) {
+ com_err(argv[0], retval, "while processing master key");
+ exit_status++; return;
+ }
+
+ rblock.eblock = &master_encblock;
+ if ((retval = krb5_init_random_key(util_context, &master_encblock,
+ &master_keyblock, &rblock.rseed))) {
+ com_err(argv[0], retval, "while initializing random key generator");
+ (void) krb5_finish_key(util_context, &master_encblock);
+ exit_status++; return;
+ }
+ if ((retval = krb5_db_create(util_context, global_params.dbname))) {
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while creating database '%s'",
+ global_params.dbname);
+ exit_status++; return;
+ }
+ if (retval = krb5_db_fini(util_context)) {
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock,
+ &rblock.rseed);
+ com_err(argv[0], retval, "while closing current database");
+ exit_status++; return;
+ }
+ if ((retval = krb5_db_set_name(util_context, global_params.dbname))) {
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while setting active database to '%s'",
+ global_params.dbname);
+ exit_status++; return;
+ }
+ if ((retval = krb5_db_init(util_context))) {
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while initializing the database '%s'",
+ global_params.dbname);
+ exit_status++; return;
+ }
+
+ if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) ||
+ (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) {
+ (void) krb5_db_fini(util_context);
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed);
+ com_err(argv[0], retval, "while adding entries to the database");
+ exit_status++; return;
+ }
+ /*
+ * Always stash the master key so kadm5_create does not prompt for
+ * it; delete the file below if it was not requested. DO NOT EXIT
+ * BEFORE DELETING THE KEYFILE if do_stash is not set.
+ */
+ if (retval = krb5_db_store_mkey(util_context,
+ global_params.stash_file,
+ master_princ,
+ &master_keyblock)) {
+ com_err(argv[0], errno, "while storing key");
+ printf("Warning: couldn't stash master key.\n");
+ }
+ /* clean up */
+ (void) krb5_db_fini(util_context);
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed);
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ free(master_keyblock.contents);
+ if (pw_str) {
+ memset(pw_str, 0, pw_size);
+ free(pw_str);
+ }
+ free(master_salt.data);
+
+ if (kadm5_create(&global_params)) {
+ if (!do_stash) unlink(global_params.stash_file);
+ exit_status++;
+ return;
+ }
+ if (!do_stash) unlink(global_params.stash_file);
+
+ /* now open the database */
+ open_db_and_mkey();
+
+ return;
+
+}
+
+static krb5_error_code
+tgt_keysalt_iterate(ksent, ptr)
+ krb5_key_salt_tuple *ksent;
+ krb5_pointer ptr;
+{
+ krb5_context context;
+ krb5_error_code kret;
+ struct iterate_args *iargs;
+ krb5_keyblock random_keyblock, *key;
+ krb5_int32 ind;
+ krb5_encrypt_block random_encblock;
+ krb5_pointer rseed;
+ krb5_data pwd;
+
+ iargs = (struct iterate_args *) ptr;
+ kret = 0;
+
+ context = iargs->ctx;
+
+ /*
+ * Convert the master key password into a key for this particular
+ * encryption system.
+ */
+ krb5_use_enctype(context, &random_encblock, ksent->ks_enctype);
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ if (kret = krb5_string_to_key(context, &random_encblock, &random_keyblock,
+ &pwd, &master_salt))
+ return kret;
+ if ((kret = krb5_init_random_key(context, &random_encblock,
+ &random_keyblock, &rseed)))
+ return kret;
+
+ if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
+ ind = iargs->dbentp->n_key_data-1;
+ if (!(kret = krb5_random_key(context,
+ &random_encblock, rseed,
+ &key))) {
+ kret = krb5_dbekd_encrypt_key_data(context,
+ iargs->rblock->eblock,
+ key,
+ NULL,
+ 1,
+ &iargs->dbentp->key_data[ind]);
+ krb5_free_keyblock(context, key);
+ }
+ }
+ memset((char *)random_keyblock.contents, 0, random_keyblock.length);
+ free(random_keyblock.contents);
+ (void) krb5_finish_random_key(context, &random_encblock, &rseed);
+ return(kret);
+}
+
+static krb5_error_code
+add_principal(context, princ, op, pblock)
+ krb5_context context;
+ krb5_principal princ;
+ enum ap_op op;
+ struct realm_info *pblock;
+{
+ krb5_error_code retval;
+ krb5_db_entry entry;
+
+ krb5_timestamp now;
+ struct iterate_args iargs;
+
+ int nentries = 1;
+
+ memset((char *) &entry, 0, sizeof(entry));
+
+ entry.len = KRB5_KDB_V1_BASE_LENGTH;
+ entry.attributes = pblock->flags;
+ entry.max_life = pblock->max_life;
+ entry.max_renewable_life = pblock->max_rlife;
+ entry.expiration = pblock->expiration;
+
+ if ((retval = krb5_copy_principal(context, princ, &entry.princ)))
+ goto error_out;
+
+ if ((retval = krb5_timeofday(context, &now)))
+ goto error_out;
+
+ if ((retval = krb5_dbe_update_mod_princ_data(context, &entry,
+ now, &db_create_princ)))
+ goto error_out;
+
+ switch (op) {
+ case MASTER_KEY:
+ if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data)))
+ == NULL)
+ goto error_out;
+ memset((char *) entry.key_data, 0, sizeof(krb5_key_data));
+ entry.n_key_data = 1;
+
+ entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock,
+ &master_keyblock, NULL,
+ 1, entry.key_data)))
+ return retval;
+ break;
+ case TGT_KEY:
+ iargs.ctx = context;
+ iargs.rblock = pblock;
+ iargs.dbentp = &entry;
+ /*
+ * Iterate through the key/salt list, ignoring salt types.
+ */
+ if ((retval = krb5_keysalt_iterate(pblock->kslist,
+ pblock->nkslist,
+ 1,
+ tgt_keysalt_iterate,
+ (krb5_pointer) &iargs)))
+ return retval;
+ break;
+ case NULL_KEY:
+ return EOPNOTSUPP;
+ default:
+ break;
+ }
+
+ retval = krb5_db_put_principal(context, &entry, &nentries);
+
+error_out:;
+ krb5_dbe_free_contents(context, &entry);
+ return retval;
+}
diff --git a/src/kadmin/dbutil/kdb5_destroy.c b/src/kadmin/dbutil/kdb5_destroy.c
new file mode 100644
index 000000000..7c6873df7
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_destroy.c
@@ -0,0 +1,117 @@
+/*
+ * admin/destroy/kdb5_destroy.c
+ *
+ * Copyright 1990 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.
+ *
+ *
+ * kdb_dest(roy): destroy the named database.
+ *
+ * This version knows about DBM format databases.
+ */
+
+#include "k5-int.h"
+#include <stdio.h>
+#include "com_err.h"
+#include <kadm5/admin.h>
+#include <kadm5/adb.h>
+
+extern int errno;
+extern int exit_status;
+extern krb5_boolean dbactive;
+extern kadm5_config_params global_params;
+
+char *yes = "yes\n"; /* \n to compare against result of
+ fgets */
+
+static void
+usage(who, status)
+ char *who;
+ int status;
+{
+ fprintf(stderr, "usage: %s [-f]\n", who);
+}
+
+void
+kdb5_destroy(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ int optchar;
+ char *dbname;
+ char buf[5];
+ char dbfilename[MAXPATHLEN];
+ krb5_error_code retval, retval1, retval2;
+ krb5_context context;
+ int force = 0;
+
+ krb5_init_context(&context);
+ krb5_init_ets(context);
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+
+ dbname = global_params.dbname;
+
+ optind = 1;
+ while ((optchar = getopt(argc, argv, "f")) != EOF) {
+ switch(optchar) {
+ case 'f':
+ force++;
+ break;
+ case '?':
+ default:
+ usage(argv[0], 1);
+ return;
+ /*NOTREACHED*/
+ }
+ }
+ if (!force) {
+ printf("Deleting KDC database stored in '%s', are you sure?\n", dbname);
+ printf("(type 'yes' to confirm)? ");
+ if (fgets(buf, sizeof(buf), stdin) == NULL) {
+ exit_status++; return;
+ }
+ if (strcmp(buf, yes)) {
+ exit_status++; return;
+ }
+ printf("OK, deleting database '%s'...\n", dbname);
+ }
+
+ if (retval = krb5_db_set_name(context, dbname)) {
+ com_err(argv[0], retval, "'%s'",dbname);
+ exit_status++; return;
+ }
+ retval1 = kdb5_db_destroy(context, dbname);
+ retval2 = osa_adb_destroy_policy_db(&global_params);
+ if (retval1) {
+ com_err(argv[0], retval1, "deleting database '%s'",dbname);
+ exit_status++; return;
+ }
+ if (retval2) {
+ com_err(argv[0], retval2, "destroying policy database");
+ exit_status++; return;
+ }
+
+ dbactive = FALSE;
+ printf("** Database '%s' destroyed.\n", dbname);
+ return;
+}
diff --git a/src/kadmin/dbutil/kdb5_edit.M b/src/kadmin/dbutil/kdb5_edit.M
new file mode 100644
index 000000000..8405c01cd
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_edit.M
@@ -0,0 +1,179 @@
+.\" admin/edit/kdb5_edit.M
+.\"
+.\" Copyright 1990 by the Massachusetts Institute of Technology.
+.\"
+.\" 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.
+.\"
+.\"
+.TH KDB5_EDIT 8 "Kerberos Version 5.0" "MIT Project Athena"
+.SH NAME
+kdb5_edit \- edit a Kerberos V5 principal database
+.SH SYNOPSIS
+.B kdb5_edit
+[
+.B \-r
+.I realm
+] [
+.B \-d
+.I dbname
+] [
+.B \-k
+.I keytype
+] [
+.B \-M
+.I mkeyname
+] [
+.B \-e
+.I enctype
+] [
+.B \-m
+] [
+.B \-R
+.I command
+] [
+.B \-s
+.I script
+] [
+.B \-f
+.I stashfile
+]
+.br
+.SH DESCRIPTION
+.I kdb5_edit
+allows an administrator to add, delete, and edit entries in a Kerberos
+version 5 principal database.
+After themaster key is verified, commands are to
+.I kdb5_edit
+are issued using one of three mechanisms. If a single command is supplied
+using the
+.B \-R
+.I command
+argument, then that single command is processed and execution ceases. If a
+script file is provided using the
+.B \-s
+.I script
+argument, then commands are read from this file until either an error occurs
+or an end of file is detected. Finally, if neither a command or a script is
+specified, the invoker is placed into a shell-like command loop, from which
+[s]he may issue commands to modify the
+database.
+.PP
+The
+.B \-r
+.I realm
+option specifies the realm of the database;
+by default the realm returned by
+.IR krb5_default_local_realm (3)
+is used.
+.PP
+The
+.B \-d
+.I dbname
+option specifies the name under which the principal database is stored;
+by default the database is in DEFAULT_DBM_FILE (defined in <krb5/osconf.h>).
+.PP
+The
+.B \-k
+.I keytype
+option specifies the key type of the master key in the database; the default is
+the string representation of DEFAULT_KDC_KEYTYPE (defined in <krb5/osconf.h>).
+.PP
+The
+.B \-f
+.I stashfile
+option specifies the filename of the stashed V5 master key. The default is
+defined as DEFAULT_KEYFILE_STUB in <krb5/osconf.h> and is
+typically $(prefix)/lib/krb5kdc/.k5.REALMNAME. (In previous
+releases, this would have been /.k5.REALMNAME.)
+.PP
+The
+.B \-M
+.I mkeyname
+option specifies the principal name for the master key in the database;
+the default is KRB5_KDB_M_NAME (defined in <krb5/kdb.h>).
+.PP
+The
+.B \-e
+.I enctype
+option specifies the encryption type to be used when placing entries in
+the database; the default is the string representation of DEFAULT_KDC_ETYPE
+(defined in <krb5/osconf.h>).
+.PP
+The
+.B \-m
+option specifies that the master database password should be fetched
+from the keyboard rather than from a file on disk.
+.SH AVAILABLE COMMANDS
+
+The following is a list of commands and their aliases that the system
+administrator may use to manipulate the database:
+
+.IP add_new_key,ank
+Add new entry to Kerberos database (prompting for password)
+
+.IP change_pwd_key,cpw
+Change key of an entry in the Kerberos database (prompting for password)
+
+.IP add_rnd_key,ark
+Add new entry to Kerberos database, using a random key
+
+.IP change_rnd_key,crk
+Change key of an entry in the Kerberos database (select a new random key)
+
+.IP delete_entry,delent,del
+Delete an entry from the database
+
+.IP extract_srvtab,xst,ex_st
+Extract service key table
+
+.IP extract_v4_srvtab,xst4
+Extract service key table
+
+.IP modify_entry,modent
+Modify entry
+
+.IP list_db,ldb
+List database entries
+
+.IP dump_db,ddb
+Dump database entries to a file
+
+.IP load_db,lddb
+Load database entries from a file
+
+.IP set_dbname,sdbn
+Change database name
+
+.IP enter_master_key,emk
+Enter the master key for a database
+
+.IP change_working_directory,cwd,cd
+Change working directory
+
+.IP print_working_direcotry,pwd
+Print working directory
+
+.IP list_requests,lr,?
+List available requests.
+
+.IP quit,exit,q
+Exit program.
+
+.SH SEE ALSO
+krb5(3), krb5kdc(8), ss(3)
+.SH BUGS
+
diff --git a/src/kadmin/dbutil/kdb5_stash.c b/src/kadmin/dbutil/kdb5_stash.c
new file mode 100644
index 000000000..6952db7ec
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_stash.c
@@ -0,0 +1,153 @@
+/*
+ * admin/stash/kdb5_stash.c
+ *
+ * Copyright 1990 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.
+ *
+ *
+ * Store the master database key in a file.
+ */
+
+#include "k5-int.h"
+#include "com_err.h"
+#include <kadm5/admin.h>
+#include <stdio.h>
+
+extern int errno;
+
+extern krb5_keyblock master_keyblock;
+extern krb5_principal master_princ;
+extern krb5_encrypt_block master_encblock;
+extern kadm5_config_params global_params;
+
+extern int exit_status;
+
+static void
+usage(who, status)
+char *who;
+int status;
+{
+ fprintf(stderr, "usage: %s [-f keyfile]\n", who);
+ exit_status++; return;
+}
+
+
+void
+kdb5_stash(argc, argv)
+int argc;
+char *argv[];
+{
+ extern char *optarg;
+ int optchar;
+ krb5_error_code retval;
+ char *dbname = (char *) NULL;
+ char *realm = 0;
+ char *mkey_name = 0;
+ char *mkey_fullname;
+ char *keyfile = 0;
+ krb5_context context;
+ krb5_realm_params *rparams;
+
+ int enctypedone = 0;
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+
+ krb5_init_context(&context);
+ krb5_init_ets(context);
+
+ dbname = global_params.dbname;
+ realm = global_params.realm;
+ mkey_name = global_params.mkey_name;
+ keyfile = global_params.stash_file;
+
+ optind = 1;
+ while ((optchar = getopt(argc, argv, "f:")) != EOF) {
+ switch(optchar) {
+ case 'f':
+ keyfile = optarg;
+ break;
+ case '?':
+ default:
+ usage(argv[0], 1);
+ return;
+ }
+ }
+
+ if (!valid_enctype(master_keyblock.enctype)) {
+ char tmp[32];
+ if (krb5_enctype_to_string(master_keyblock.enctype, tmp, sizeof(tmp)))
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d", master_keyblock.enctype);
+ else
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp);
+ exit_status++; return;
+ }
+
+ krb5_use_enctype(context, &master_encblock, master_keyblock.enctype);
+
+ if (retval = krb5_db_set_name(context, dbname)) {
+ com_err(argv[0], retval, "while setting active database to '%s'",
+ dbname);
+ exit_status++; return;
+ }
+
+ /* assemble & parse the master key name */
+
+ if (retval = krb5_db_setup_mkey_name(context, mkey_name, realm,
+ &mkey_fullname, &master_princ)) {
+ com_err(argv[0], retval, "while setting up master key name");
+ exit_status++; return;
+ }
+
+ if (retval = krb5_db_init(context)) {
+ com_err(argv[0], retval, "while initializing the database '%s'",
+ dbname);
+ exit_status++; return;
+ }
+
+ /* TRUE here means read the keyboard, but only once */
+ if (retval = krb5_db_fetch_mkey(context, master_princ, &master_encblock,
+ TRUE, FALSE, (char *) NULL,
+ 0, &master_keyblock)) {
+ com_err(argv[0], retval, "while reading master key");
+ (void) krb5_db_fini(context);
+ exit_status++; return;
+ }
+ if (retval = krb5_db_verify_master_key(context, master_princ,
+ &master_keyblock,&master_encblock)) {
+ com_err(argv[0], retval, "while verifying master key");
+ (void) krb5_db_fini(context);
+ exit_status++; return;
+ }
+ if (retval = krb5_db_store_mkey(context, keyfile, master_princ,
+ &master_keyblock)) {
+ com_err(argv[0], errno, "while storing key");
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ (void) krb5_db_fini(context);
+ exit_status++; return;
+ }
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ if (retval = krb5_db_fini(context)) {
+ com_err(argv[0], retval, "closing database '%s'", dbname);
+ exit_status++; return;
+ }
+
+ return;
+}
diff --git a/src/kadmin/dbutil/kdb5_util.M b/src/kadmin/dbutil/kdb5_util.M
new file mode 100644
index 000000000..746e018c0
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_util.M
@@ -0,0 +1,122 @@
+KDB5_UTIL(8)
+
+NAME
+ kdb5_util - Kerberos database maintainance utility
+
+SYNOPSIS
+ kdb5_util [-d dbpathname ] [-r realmname] [-R request ]
+ [-s scriptfile] [-k enctype] [-M mkeyname]
+ [-f stashfile]
+
+DESCRIPTION
+ kdb5_util allows an administrator to perform low-level
+ maintainance procedures on the Kerberos and KADM5 database.
+ Databases can be created, destroyed, and dumped to and loaded
+ from ASCII files. Additionally, kdb5_util can create a
+ Kerberos master key stash file. kdb5_util subsumes the
+ functionality of and makes obsolete the previous database
+ maintainance programs kdb5_create, kdb5_edit, kdb5_destroy,
+ and kdb5_stash.
+
+ When the program is first run, it attempts to acquire the
+ master key and open the database. Execution continues whether
+ or not it is successful, however, because the database may not
+ exist yet or the stash file may be corrupt. Commands can be
+ issued using one of three mechanisms. If a single command is
+ supplied using the request argument, then that single command
+ is processed and execution ceases. If a script file is
+ provided using the -s script argument, then commands are read
+ from this file until either an error occurs or an end of file
+ is detected. Finally, if neither a command or a script is
+ specified, the invoker is placed into a shell-like command
+ loop, from which commands may be executed.
+
+ The -r realm option specifies the realm of the database; by
+ default the realm returned by krb5_default_local_realm(3) is
+ used.
+
+ The -d dbname option specifies the name under which the
+ principal database is stored; by default the database is
+ controlled by kdc.conf. The KADM5 policy database and lock
+ file are also derived from this value.
+
+ The -k keytype option specifies the key type of the master key
+ in the database; the default is controlled by kdc.conf.
+
+ The -f stashfile option specifies the filename of the stashed
+ V5 master key. The default is controlled by kdc.conf and is
+ typically <krb5-prefix>/lib/krb5kdc/.k5.REALMNAME. (In
+ previous releases, this would have been /.k5.REALMNAME.)
+
+ The -M mkeyname option specifies the principal name for the
+ master key in the database; the default is controlled by
+ kdc.conf.
+
+ The -m option specifies that the master database password
+ should be fetched from the keyboard rather than from a file on
+ disk.
+
+AVAILABLE COMMANDS
+ create_db [-s]
+
+ Alias: create. Creates a new database. If the -s option is
+ specified, the stash file is also created. This command fails
+ if the database already exists. If the command is successful,
+ the database is opened just as if it had already existed when
+ the program was first run.
+
+ destroy_db [-f]
+
+ Alias: destroy. Destroys the database, first overwriting the
+ disk sectors and then unlinking the files, after prompting the
+ user for confirmation. With the -f argument, does not prompt
+ the user.
+
+ stash_mkey [-f keyfile]
+
+ Alias: stash. Stores the master principal's keys in a stash
+ file. The -f argument can be used to override the keyfile
+ specified at startup.
+
+ dump_db [-old] [-b6] [-verbose] [filename [principals...]]
+
+ Alias: ddb. Dumps the current Kerberos and KADM5 database
+ into an ASCII file. By default, the database is dumped in
+ current format, "kdb5_util load_dump version 4". The -b6
+ argument causes the dump to be in the Kerberos 5 Beta 6 format
+ ("kdb5_edit load_dump version 3.0"). The -old argument causes
+ the dump to be in the Kerberos 5 Beta 5 and earlier dump
+ format ("kdb5_edit load_dump version 2.0"). The -verbose
+ option causes the name of each principal and policy to be
+ printed as it is dumped.
+
+ load_db [-old] [-b6] [-verbose] [-update] filename dbname
+ [admin_dbname]
+
+ Alias: lddb. Loads a database dump from the named file into
+ the named database. The -old and -b6 options require the dump
+ to be in the specified format (see dump_db); otherwise, the
+ format of the dump file is detected automatically and handled
+ as appropriate. If the -update argument is specified, records
+ from the dump file are merely added to or updated in the
+ existing database; otherwise, a new database is created
+ containing only what is in the dump file and the old one
+ destroyed on a successful completion. The dbname argument is
+ required (XXX probably shouldn't be) and overrides the value
+ specified on the command line or the default. The
+ admin_dbname is optional and is derived from dbname if not
+ specified.
+
+ dump_v4db [filename]
+
+ Alias: d4db. Dumps the current database into the Kerberos 4
+ database dump format.
+
+ load_v4db [-d v5dbpathname] [-t] [-n] [-r realmname] [-K]
+ [-k enctype] [-M mkeyname] -f inputfile
+
+ Alias: lddb4. Loads a Kerberos 4 database dump file. XXX Not
+ sure what all the arguments mean.
+
+SEE ALSO
+ kadm5_export(8), kadm5_import(8)
diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c
new file mode 100644
index 000000000..3f31fcb14
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_util.c
@@ -0,0 +1,416 @@
+/*
+ * admin/edit/kdb5_edit.c
+ *
+ * (C) Copyright 1990,1991, 1996 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.
+ *
+ *
+ * Edit a KDC database.
+ */
+
+#include <stdio.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <kadm5/adb.h>
+#include <time.h>
+#include "kdb5_util.h"
+
+char *Err_no_master_msg = "Master key not entered!\n";
+char *Err_no_database = "Database not currently opened!\n";
+
+/*
+ * XXX Ick, ick, ick. These global variables shouldn't be global....
+ */
+static char *mkey_password = 0;
+
+/*
+ * I can't figure out any way for this not to be global, given how ss
+ * works.
+ */
+
+int exit_status = 0;
+krb5_context util_context;
+osa_adb_policy_t policy_db;
+kadm5_config_params global_params;
+
+/*
+ * Script input, specified by -s.
+ */
+FILE *scriptfile = (FILE *) NULL;
+
+static void
+usage(who, status)
+ char *who;
+ int status;
+{
+ fprintf(stderr,
+ "usage: %s [-d dbpathname ] [-r realmname] [-R request ]\n",
+ who);
+ fprintf(stderr, "\t [-k enctype] [-M mkeyname] [-f stashfile]\n");
+ exit(status);
+}
+
+krb5_keyblock master_keyblock;
+krb5_principal master_princ;
+krb5_db_entry master_entry;
+krb5_encrypt_block master_encblock;
+krb5_pointer master_random;
+int valid_master_key = 0;
+
+char *progname;
+krb5_boolean manual_mkey = FALSE;
+krb5_boolean dbactive = FALSE;
+
+char *kdb5_util_Init(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ int optchar;
+ krb5_error_code retval;
+ char *request = NULL;
+
+ retval = krb5_init_context(&util_context);
+ if (retval) {
+ fprintf(stderr, "krb5_init_context failed with error #%ld\n",
+ (long) retval);
+ exit(1);
+ }
+ krb5_init_ets(util_context);
+ initialize_adb_error_table();
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+
+ progname = argv[0];
+
+ while ((optchar = getopt(argc, argv, "P:d:a:r:R:k:M:e:ms:f:")) != EOF) {
+ switch(optchar) {
+ case 'P': /* Only used for testing!!! */
+ mkey_password = optarg;
+ manual_mkey = TRUE;
+ break;
+ case 'd':
+ global_params.dbname = optarg;
+ global_params.mask |= KADM5_CONFIG_DBNAME;
+ break;
+ case 'a':
+ global_params.admin_dbname = optarg;
+ global_params.mask |= KADM5_CONFIG_ADBNAME;
+ break;
+ case 'r':
+ global_params.realm = optarg;
+ global_params.mask |= KADM5_CONFIG_REALM;
+ /* not sure this is really necessary */
+ if ((retval = krb5_set_default_realm(util_context,
+ global_params.realm))) {
+ com_err(progname, retval, "while setting default realm name");
+ exit(1);
+ }
+ break;
+ case 'R':
+ request = optarg;
+ break;
+ case 'k':
+ if (krb5_string_to_enctype(optarg, &global_params.enctype))
+ com_err(argv[0], 0, "%s is an invalid enctype", optarg);
+ global_params.mask |= KADM5_CONFIG_ENCTYPE;
+ break;
+ case 'M': /* master key name in DB */
+ global_params.mkey_name = optarg;
+ global_params.mask |= KADM5_CONFIG_MKEY_NAME;
+ break;
+ case 'm':
+ manual_mkey = TRUE;
+ global_params.mkey_from_kbd = 1;
+ global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
+ break;
+ case 's':
+ /* Open the script file */
+ if (!(scriptfile = fopen(optarg, "r"))) {
+ com_err(argv[0], errno, "while opening script file %s",
+ optarg);
+ exit(1);
+ }
+ break;
+ case 'f':
+ global_params.stash_file = optarg;
+ global_params.mask |= KADM5_CONFIG_STASH_FILE;
+ break;
+ case '?':
+ default:
+ usage(progname, 1);
+ /*NOTREACHED*/
+ }
+ }
+
+ if (retval = kadm5_get_config_params(util_context, NULL, NULL,
+ &global_params, &global_params)) {
+ com_err(argv[0], retval, "while retreiving configuration parameters");
+ exit(1);
+ }
+
+ /*
+ * Dump creates files which should not be world-readable. It is
+ * easiest to do a single umask call here; any shells run by the
+ * ss command interface will have umask = 77 but that is not a
+ * serious problem.
+ */
+ (void) umask(077);
+
+ master_keyblock.enctype = global_params.enctype;
+ if (master_keyblock.enctype != ENCTYPE_UNKNOWN) {
+ if (!valid_enctype(master_keyblock.enctype)) {
+ char tmp[32];
+ if (krb5_enctype_to_string(master_keyblock.enctype,
+ tmp, sizeof(tmp)))
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d", master_keyblock.enctype);
+ else
+ com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp);
+ exit(1);
+ }
+ krb5_use_enctype(util_context, &master_encblock,
+ master_keyblock.enctype);
+ }
+
+
+ open_db_and_mkey();
+
+ exit_status = 0; /* It's OK if we get errors in open_db_and_mkey */
+ return request;
+}
+
+#if 0
+/*
+ * This function is no longer used in kdb5_util (and it would no
+ * longer work, anyway).
+ */
+void set_dbname(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+
+ if (argc < 3) {
+ com_err(argv[0], 0, "Too few arguments");
+ com_err(argv[0], 0, "Usage: %s dbpathname realmname", argv[0]);
+ exit_status++;
+ return;
+ }
+ if (dbactive) {
+ if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) {
+ com_err(argv[0], retval, "while closing previous database");
+ exit_status++;
+ return;
+ }
+ if (valid_master_key) {
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock,
+ &master_random);
+ memset((char *)master_keyblock.contents, 0,
+ master_keyblock.length);
+ krb5_xfree(master_keyblock.contents);
+ master_keyblock.contents = NULL;
+ valid_master_key = 0;
+ }
+ krb5_free_principal(util_context, master_princ);
+ dbactive = FALSE;
+ }
+
+ (void) set_dbname_help(argv[0], argv[1]);
+ return;
+}
+#endif
+
+/*
+ * open_db_and_mkey: Opens the KDC and policy database, and sets the
+ * global master_* variables. Sets dbactive to TRUE if the databases
+ * are opened, and valid_master_key to 1 if the global master
+ * variables are set properly. Returns 0 on success, and 1 on
+ * failure, but it is not considered a failure if the master key
+ * cannot be fetched (the master key stash file may not exist when the
+ * program is run).
+ */
+int open_db_and_mkey()
+{
+ krb5_error_code retval;
+ int nentries, i;
+ krb5_boolean more;
+ krb5_data scratch, pwd;
+
+ dbactive = FALSE;
+ valid_master_key = 0;
+
+ if ((retval = krb5_db_set_name(util_context, global_params.dbname))) {
+ com_err(progname, retval, "while setting active database to '%s'",
+ global_params.dbname);
+ exit_status++;
+ return(1);
+ }
+ if ((retval = krb5_db_init(util_context))) {
+ com_err(progname, retval, "while initializing database");
+ exit_status++;
+ return(1);
+ }
+ if (retval = osa_adb_open_policy(&policy_db, &global_params)) {
+ com_err(progname, retval, "opening policy database");
+ exit_status++;
+ return (1);
+ }
+
+ /* assemble & parse the master key name */
+
+ if ((retval = krb5_db_setup_mkey_name(util_context,
+ global_params.mkey_name,
+ global_params.realm,
+ 0, &master_princ))) {
+ com_err(progname, retval, "while setting up master key name");
+ exit_status++;
+ return(1);
+ }
+ nentries = 1;
+ if ((retval = krb5_db_get_principal(util_context, master_princ,
+ &master_entry, &nentries, &more))) {
+ com_err(progname, retval, "while retrieving master entry");
+ exit_status++;
+ (void) krb5_db_fini(util_context);
+ return(1);
+ } else if (more) {
+ com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE,
+ "while retrieving master entry");
+ exit_status++;
+ (void) krb5_db_fini(util_context);
+ return(1);
+ } else if (!nentries) {
+ com_err(progname, KRB5_KDB_NOENTRY, "while retrieving master entry");
+ exit_status++;
+ (void) krb5_db_fini(util_context);
+ return(1);
+ }
+
+ krb5_db_free_principal(util_context, &master_entry, nentries);
+
+ /* the databases are now open, and the master principal exists */
+ dbactive = TRUE;
+
+ if (mkey_password) {
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ retval = krb5_principal2salt(util_context, master_princ, &scratch);
+ if (retval) {
+ com_err(progname, retval, "while calculated master key salt");
+ return(1);
+ }
+
+ /* If no encryption type is set, use the default */
+ if (master_keyblock.enctype == ENCTYPE_UNKNOWN) {
+ master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
+ if (!valid_enctype(master_keyblock.enctype)) {
+ char tmp[32];
+ if (krb5_enctype_to_string(master_keyblock.enctype,
+ tmp, sizeof(tmp)))
+ com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d", master_keyblock.enctype);
+ else
+ com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, tmp);
+ exit(1);
+ }
+ krb5_use_enctype(util_context, &master_encblock,
+ master_keyblock.enctype);
+ }
+
+ retval = krb5_string_to_key(util_context, &master_encblock,
+ &master_keyblock, &pwd, &scratch);
+ if (retval) {
+ com_err(progname, retval,
+ "while transforming master key from password");
+ return(1);
+ }
+ free(scratch.data);
+ mkey_password = 0;
+ } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
+ &master_encblock, manual_mkey,
+ FALSE, global_params.stash_file,
+ 0, &master_keyblock))) {
+ com_err(progname, retval, "while reading master key");
+ com_err(progname, 0, "Warning: proceeding without master key");
+ exit_status++;
+ return(0);
+ }
+ if ((retval = krb5_db_verify_master_key(util_context, master_princ,
+ &master_keyblock,&master_encblock))
+ ) {
+ com_err(progname, retval, "while verifying master key");
+ exit_status++;
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ krb5_xfree(master_keyblock.contents);
+ return(1);
+ }
+ if ((retval = krb5_process_key(util_context, &master_encblock,
+ &master_keyblock))) {
+ com_err(progname, retval, "while processing master key");
+ exit_status++;
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ krb5_xfree(master_keyblock.contents);
+ return(1);
+ }
+ if ((retval = krb5_init_random_key(util_context, &master_encblock,
+ &master_keyblock,
+ &master_random))) {
+ com_err(progname, retval, "while initializing random key generator");
+ exit_status++;
+ (void) krb5_finish_key(util_context, &master_encblock);
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ krb5_xfree(master_keyblock.contents);
+ return(1);
+ }
+
+ valid_master_key = 1;
+ dbactive = TRUE;
+ return 0;
+}
+
+#ifdef HAVE_GETCWD
+#undef getwd
+#endif
+
+int
+quit()
+{
+ krb5_error_code retval;
+ static krb5_boolean finished = 0;
+
+ if (finished)
+ return 0;
+ if (valid_master_key) {
+ (void) krb5_finish_key(util_context, &master_encblock);
+ (void) krb5_finish_random_key(util_context, &master_encblock,
+ &master_random);
+ }
+ retval = krb5_db_fini(util_context);
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ finished = TRUE;
+ if (retval && retval != KRB5_KDB_DBNOTINITED) {
+ com_err(progname, retval, "while closing database");
+ exit_status++;
+ return 1;
+ }
+ return 0;
+}
diff --git a/src/kadmin/dbutil/kdb5_util.h b/src/kadmin/dbutil/kdb5_util.h
new file mode 100644
index 000000000..b580e2f6a
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_util.h
@@ -0,0 +1,49 @@
+/*
+ * admin/edit/kdb5_edit.h
+ *
+ * Copyright 1992 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.
+ *
+ */
+
+#define REALM_SEP '@'
+#define REALM_SEP_STR "@"
+
+extern char *progname;
+extern char *Err_no_database;
+
+void add_key
+ PROTOTYPE((char const *, char const *,
+ krb5_const_principal, const krb5_keyblock *,
+ krb5_kvno, krb5_keysalt *));
+int set_dbname_help
+ PROTOTYPE((char *, char *));
+
+char *kdb5_util_Init PROTOTYPE((int, char **));
+
+int quit();
+
+int check_for_match
+ PROTOTYPE((char *, int, krb5_db_entry *, int, int));
+
+void parse_token
+ PROTOTYPE((char *, int *, int *, char *));
+
+int create_db_entry
+ PROTOTYPE((krb5_principal, krb5_db_entry *));
diff --git a/src/kadmin/dbutil/kdb5_util_ct.ct b/src/kadmin/dbutil/kdb5_util_ct.ct
new file mode 100644
index 000000000..bac1df125
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_util_ct.ct
@@ -0,0 +1,56 @@
+# admin/edit/kdb5_ed_ct.ct
+#
+# Copyright 1990 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.
+#
+#
+# Command table for Kerberos administration edit
+#
+
+command_table kdb5_edit_cmds;
+
+request kdb5_create, "Create a new Kerberos database",
+ create_db, create;
+
+request kdb5_destroy, "Destroy a Kerberos database",
+ destroy_db, destroy;
+
+request kdb5_stash, "Stash the Kerberos master key",
+ stash_mkey, stash;
+
+request dump_db, "Dump database entries to a file",
+ dump_db, ddb;
+
+request dump_v4db, "Dump database entries to a V4 slave dump file",
+ dump_v4db, d4db;
+
+request load_db, "Load database entries from a file",
+ load_db, lddb;
+
+request load_v4db, "Load database entries from a V4 slave dump file",
+ load_v4db, lddb4;
+
+# list_requests is generic -- unrelated to Kerberos
+request ss_list_requests, "List available requests.",
+ list_requests, lr, "?";
+
+request ss_quit, "Exit program.",
+ quit, exit, q;
+
+end;
diff --git a/src/kadmin/dbutil/loadv4.c b/src/kadmin/dbutil/loadv4.c
new file mode 100644
index 000000000..a1d37edc7
--- /dev/null
+++ b/src/kadmin/dbutil/loadv4.c
@@ -0,0 +1,881 @@
+/*
+ * admin/edit/loadv4.c
+ *
+ * Copyright 1990,1991 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.
+ *
+ *
+ * Generate (from scratch) a Kerberos V5 KDC database, filling it in with the
+ * entries from a V4 database.
+ */
+
+#ifdef KRB5_KRB4_COMPAT
+
+#include <des.h>
+#include <krb.h>
+#include <krb_db.h>
+/* MKEYFILE is now defined in kdc.h */
+#include <kdc.h>
+
+static C_Block master_key;
+static Key_schedule master_key_schedule;
+static long master_key_version;
+
+static char *v4_mkeyfile = "/.k";
+
+#include "k5-int.h"
+#include "com_err.h"
+#include "adm.h"
+#include "adm_proto.h"
+#include <stdio.h>
+
+#include <netinet/in.h> /* ntohl */
+
+#define PROGNAME argv[0]
+
+enum ap_op {
+ NULL_KEY, /* setup null keys */
+ MASTER_KEY, /* use master key as new key */
+ RANDOM_KEY /* choose a random key */
+};
+
+struct realm_info {
+ krb5_deltat max_life;
+ krb5_deltat max_rlife;
+ krb5_timestamp expiration;
+ krb5_flags flags;
+ krb5_encrypt_block *eblock;
+ krb5_pointer rseed;
+};
+
+static struct realm_info rblock = { /* XXX */
+ KRB5_KDB_MAX_LIFE,
+ KRB5_KDB_MAX_RLIFE,
+ KRB5_KDB_EXPIRATION,
+ KRB5_KDB_DEF_FLAGS,
+ 0
+};
+
+static int verbose = 0;
+
+static krb5_error_code add_principal
+ PROTOTYPE((krb5_context,
+ krb5_principal,
+ enum ap_op,
+ struct realm_info *));
+
+static int v4init PROTOTYPE((char *, char *, int, char *));
+static krb5_error_code enter_in_v5_db PROTOTYPE((krb5_context,
+ char *, Principal *));
+static krb5_error_code process_v4_dump PROTOTYPE((krb5_context, char *,
+ char *));
+static krb5_error_code fixup_database PROTOTYPE((krb5_context, char *));
+
+static int create_local_tgt = 0;
+
+static void
+usage(who, status)
+char *who;
+int status;
+{
+ fprintf(stderr, "usage: %s [-d v5dbpathname] [-t] [-n] [-r realmname] [-K] [-k enctype]\n\
+\t[-M mkeyname] -f inputfile\n",
+ who);
+ return;
+}
+
+static krb5_keyblock master_keyblock;
+static krb5_principal master_princ;
+static krb5_encrypt_block master_encblock;
+
+static krb5_data tgt_princ_entries[] = {
+ {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME},
+ {0, 0, 0} };
+
+static krb5_data db_creator_entries[] = {
+ {0, sizeof("db_creation")-1, "db_creation"} };
+
+/* XXX knows about contents of krb5_principal, and that tgt names
+ are of form TGT/REALM@REALM */
+static krb5_principal_data tgt_princ = {
+ 0, /* magic number */
+ {0, 0, 0}, /* krb5_data realm */
+ tgt_princ_entries, /* krb5_data *data */
+ 2, /* int length */
+ KRB5_NT_SRV_INST /* int type */
+};
+
+static krb5_principal_data db_create_princ = {
+ 0, /* magic number */
+ {0, 0, 0}, /* krb5_data realm */
+ db_creator_entries, /* krb5_data *data */
+ 1, /* int length */
+ KRB5_NT_SRV_INST /* int type */
+};
+
+
+void
+load_v4db(argc, argv)
+int argc;
+char *argv[];
+{
+ krb5_error_code retval;
+ /* The kdb library will default to this, but it is convenient to
+ make it explicit (error reporting and temporary filename generation
+ use it). */
+ char *dbname = DEFAULT_KDB_FILE;
+ char *v4dbname = 0;
+ char *v4dumpfile = 0;
+ char *realm = 0;
+ char *mkey_name = 0;
+ char *mkey_fullname;
+ char *defrealm;
+ int enctypedone = 0;
+ int v4manual = 0;
+ int read_mkey = 0;
+ int tempdb = 0;
+ char *tempdbname;
+ krb5_context context;
+ char *stash_file = (char *) NULL;
+ krb5_realm_params *rparams;
+ int persist, op_ind;
+
+ krb5_init_context(&context);
+
+ krb5_init_ets(context);
+
+ if (strrchr(argv[0], '/'))
+ argv[0] = strrchr(argv[0], '/')+1;
+
+ persist = 1;
+ op_ind = 1;
+ while (persist && (op_ind < argc)) {
+ if (!strcmp(argv[op_ind], "-d") && ((argc - op_ind) >= 2)) {
+ dbname = argv[op_ind+1];
+ op_ind++;
+ }
+ else if (!strcmp(argv[op_ind], "-T")) {
+ create_local_tgt = 1;
+ }
+ else if (!strcmp(argv[op_ind], "-t")) {
+ tempdb = 1;
+ }
+ else if (!strcmp(argv[op_ind], "-r") && ((argc - op_ind) >= 2)) {
+ realm = argv[op_ind+1];
+ op_ind++;
+ }
+ else if (!strcmp(argv[op_ind], "-K")) {
+ read_mkey = 1;
+ }
+ else if (!strcmp(argv[op_ind], "-v")) {
+ verbose = 1;
+ }
+ else if (!strcmp(argv[op_ind], "-k") && ((argc - op_ind) >= 2)) {
+ if (!krb5_string_to_enctype(argv[op_ind+1],
+ &master_keyblock.enctype))
+ enctypedone++;
+ else
+ com_err(argv[0], 0, "%s is an invalid enctype",
+ argv[op_ind+1]);
+ op_ind++;
+ }
+ else if (!strcmp(argv[op_ind], "-M") && ((argc - op_ind) >= 2)) {
+ mkey_name = argv[op_ind+1];
+ op_ind++;
+ }
+ else if (!strcmp(argv[op_ind], "-n")) {
+ v4manual++;
+ }
+ else if (!strcmp(argv[op_ind], "-f") && ((argc - op_ind) >= 2)) {
+ if (v4dbname) {
+ usage(PROGNAME, 1);
+ return;
+ }
+ v4dumpfile = argv[op_ind+1];
+ op_ind++;
+ }
+ else
+ persist = 0;
+ op_ind++;
+ }
+
+ /*
+ * Attempt to read the KDC profile. If we do, then read appropriate values
+ * from it and augment values supplied on the command line.
+ */
+ if (!(retval = krb5_read_realm_params(context,
+ realm,
+ (char *) NULL,
+ (char *) NULL,
+ &rparams))) {
+ /* Get the value for the database */
+ if (rparams->realm_dbname && !dbname)
+ dbname = strdup(rparams->realm_dbname);
+
+ /* Get the value for the master key name */
+ if (rparams->realm_mkey_name && !mkey_name)
+ mkey_name = strdup(rparams->realm_mkey_name);
+
+ /* Get the value for the master key type */
+ if (rparams->realm_enctype_valid && !enctypedone) {
+ master_keyblock.enctype = rparams->realm_enctype;
+ enctypedone++;
+ }
+
+ /* Get the value for the stashfile */
+ if (rparams->realm_stash_file)
+ stash_file = strdup(rparams->realm_stash_file);
+
+ /* Get the value for maximum ticket lifetime. */
+ if (rparams->realm_max_life_valid)
+ rblock.max_life = rparams->realm_max_life;
+
+ /* Get the value for maximum renewable ticket lifetime. */
+ if (rparams->realm_max_rlife_valid)
+ rblock.max_rlife = rparams->realm_max_rlife;
+
+ /* Get the value for the default principal expiration */
+ if (rparams->realm_expiration_valid)
+ rblock.expiration = rparams->realm_expiration;
+
+ /* Get the value for the default principal flags */
+ if (rparams->realm_flags_valid)
+ rblock.flags = rparams->realm_flags;
+
+ krb5_free_realm_params(context, rparams);
+ }
+
+ if (!v4dumpfile) {
+ usage(PROGNAME, 1);
+ return;
+ }
+
+ if (!enctypedone)
+ master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
+
+ if (!valid_enctype(master_keyblock.enctype)) {
+ com_err(PROGNAME, KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d", master_keyblock.enctype);
+ return;
+ }
+
+ krb5_use_enctype(context, &master_encblock, master_keyblock.enctype);
+
+ /* If the user has not requested locking, don't modify an existing database. */
+ if (! tempdb) {
+ retval = krb5_db_set_name(context, dbname);
+ if (retval != ENOENT) {
+ fprintf(stderr,
+ "%s: The v5 database appears to already exist.\n",
+ PROGNAME);
+ return;
+ }
+ tempdbname = dbname;
+ } else {
+ int dbnamelen = strlen(dbname);
+ tempdbname = malloc(dbnamelen + 2);
+ if (tempdbname == 0) {
+ com_err(PROGNAME, ENOMEM, "allocating temporary filename");
+ return;
+ }
+ strcpy(tempdbname, dbname);
+ tempdbname[dbnamelen] = '~';
+ tempdbname[dbnamelen+1] = 0;
+ (void) kdb5_db_destroy(context, tempdbname);
+ }
+
+
+ if (!realm) {
+ if (retval = krb5_get_default_realm(context, &defrealm)) {
+ com_err(PROGNAME, retval, "while retrieving default realm name");
+ return;
+ }
+ realm = defrealm;
+ }
+
+ /* assemble & parse the master key name */
+
+ if (retval = krb5_db_setup_mkey_name(context, mkey_name, realm,
+ &mkey_fullname, &master_princ)) {
+ com_err(PROGNAME, retval, "while setting up master key name");
+ return;
+ }
+
+ krb5_princ_set_realm_data(context, &db_create_princ, realm);
+ krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm));
+ krb5_princ_set_realm_data(context, &tgt_princ, realm);
+ krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm));
+ krb5_princ_component(context, &tgt_princ,1)->data = realm;
+ krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm);
+
+ printf("Initializing database '%s' for realm '%s',\n\
+master key name '%s'\n",
+ dbname, realm, mkey_fullname);
+
+ if (read_mkey) {
+ puts("You will be prompted for the version 5 database Master Password.");
+ puts("It is important that you NOT FORGET this password.");
+ fflush(stdout);
+ }
+
+ if (retval = krb5_db_fetch_mkey(context, master_princ, &master_encblock,
+ read_mkey, read_mkey, stash_file, 0,
+ &master_keyblock)) {
+ com_err(PROGNAME, retval, "while reading master key");
+ return;
+ }
+ if (retval = krb5_process_key(context, &master_encblock, &master_keyblock)) {
+ com_err(PROGNAME, retval, "while processing master key");
+ return;
+ }
+
+ rblock.eblock = &master_encblock;
+ if (retval = krb5_init_random_key(context, &master_encblock,
+ &master_keyblock, &rblock.rseed)) {
+ com_err(PROGNAME, retval, "while initializing random key generator");
+ (void) krb5_finish_key(context, &master_encblock);
+ return;
+ }
+ if (retval = krb5_db_create(context, tempdbname)) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ (void) krb5_dbm_db_destroy(context, tempdbname);
+ com_err(PROGNAME, retval, "while creating %sdatabase '%s'",
+ tempdb ? "temporary " : "", tempdbname);
+ return;
+ }
+ if (retval = krb5_db_set_name(context, tempdbname)) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ (void) krb5_dbm_db_destroy(context, tempdbname);
+ com_err(PROGNAME, retval, "while setting active database to '%s'",
+ tempdbname);
+ return;
+ }
+ if (v4init(PROGNAME, v4dbname, v4manual, v4dumpfile)) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ (void) krb5_dbm_db_destroy(context, tempdbname);
+ return;
+ }
+ if ((retval = krb5_db_init(context)) ||
+ (retval = krb5_dbm_db_open_database(context))) {
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ (void) krb5_dbm_db_destroy(context, tempdbname);
+ com_err(PROGNAME, retval, "while initializing the database '%s'",
+ tempdbname);
+ return;
+ }
+
+ if (retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) {
+ (void) krb5_db_fini(context);
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ (void) krb5_dbm_db_destroy(context, tempdbname);
+ com_err(PROGNAME, retval, "while adding K/M to the database");
+ return;
+ }
+
+ if (create_local_tgt &&
+ (retval = add_principal(context, &tgt_princ, RANDOM_KEY, &rblock))) {
+ (void) krb5_db_fini(context);
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ (void) krb5_dbm_db_destroy(context, tempdbname);
+ com_err(PROGNAME, retval, "while adding TGT service to the database");
+ return;
+ }
+
+ retval = process_v4_dump(context, v4dumpfile, realm);
+ putchar('\n');
+ if (retval)
+ com_err(PROGNAME, retval, "while translating entries to the database");
+ else {
+ retval = fixup_database(context, realm);
+ }
+
+ /* clean up; rename temporary database if there were no errors */
+ if (retval == 0) {
+ if (retval = krb5_db_fini (context))
+ com_err(PROGNAME, retval, "while shutting down database");
+ else if (tempdb && (retval = krb5_dbm_db_rename(context, tempdbname,
+ dbname)))
+ com_err(PROGNAME, retval, "while renaming temporary database");
+ } else {
+ (void) krb5_db_fini (context);
+ if (tempdb)
+ (void) krb5_dbm_db_destroy (context, tempdbname);
+ }
+ (void) krb5_finish_key(context, &master_encblock);
+ (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed);
+ memset((char *)master_keyblock.contents, 0, master_keyblock.length);
+ krb5_free_context(context);
+ return;
+}
+
+static int
+v4init(pname, name, manual, dumpfile)
+char *pname, *name;
+int manual;
+char *dumpfile;
+{
+ int fd;
+ int ok = 0;
+
+ if (!manual) {
+ fd = open(v4_mkeyfile, O_RDONLY, 0600);
+ if (fd >= 0) {
+ if (read(fd, master_key, sizeof(master_key)) == sizeof(master_key))
+ ok = 1;
+ close(fd);
+ }
+ }
+ if (!ok) {
+ des_read_password(master_key, "V4 Kerberos master key: ", 0);
+ printf("\n");
+ }
+ key_sched(master_key, master_key_schedule);
+ return 0;
+}
+
+static krb5_error_code
+enter_in_v5_db(context, realm, princ)
+krb5_context context;
+char *realm;
+Principal *princ;
+{
+ krb5_db_entry entry;
+ krb5_error_code retval;
+ krb5_keyblock v4v5key;
+ int nentries = 1;
+ des_cblock v4key;
+ char *name;
+ krb5_timestamp mod_time;
+ krb5_principal mod_princ;
+ krb5_keysalt keysalt;
+
+ /* don't convert local TGT if we created a TGT already.... */
+ if (create_local_tgt && !strcmp(princ->name, "krbtgt") &&
+ !strcmp(princ->instance, realm)) {
+ if (verbose)
+ printf("\nignoring local TGT: '%s.%s' ...",
+ princ->name, princ->instance);
+ return 0;
+ }
+ if (!strcmp(princ->name, KERB_M_NAME) &&
+ !strcmp(princ->instance, KERB_M_INST)) {
+ des_cblock key_from_db;
+ int val;
+
+ /* here's our chance to verify the master key */
+ /*
+ * use the master key to decrypt the key in the db, had better
+ * be the same!
+ */
+ memcpy(key_from_db, (char *)&princ->key_low, 4);
+ memcpy(((char *) key_from_db) + 4, (char *)&princ->key_high, 4);
+ pcbc_encrypt((C_Block *) &key_from_db,
+ (C_Block *) &key_from_db,
+ (long) sizeof(C_Block),
+ master_key_schedule,
+ (C_Block *) master_key,
+ DECRYPT);
+ val = memcmp((char *) master_key, (char *) key_from_db,
+ sizeof(master_key));
+ memset((char *)key_from_db, 0, sizeof(key_from_db));
+ if (val) {
+ return KRB5_KDB_BADMASTERKEY;
+ }
+ if (verbose)
+ printf("\nignoring '%s.%s' ...", princ->name, princ->instance);
+ return 0;
+ }
+ memset((char *) &entry, 0, sizeof(entry));
+ if (retval = krb5_425_conv_principal(context, princ->name, princ->instance,
+ realm, &entry.princ))
+ return retval;
+ if (verbose) {
+ if (retval = krb5_unparse_name(context, entry.princ, &name))
+ name = strdup("<not unparsable name!>");
+ if (verbose)
+ printf("\ntranslating %s...", name);
+ free(name);
+ }
+
+ if (retval = krb5_build_principal(context, &mod_princ,
+ strlen(realm),
+ realm, princ->mod_name,
+ princ->mod_instance[0] ? princ->mod_instance : 0,
+ 0)) {
+ krb5_free_principal(context, entry.princ);
+ return retval;
+ }
+ mod_time = princ->mod_date;
+
+ entry.max_life = princ->max_life * 60 * 5;
+ entry.max_renewable_life = rblock.max_rlife;
+ entry.len = KRB5_KDB_V1_BASE_LENGTH;
+ entry.expiration = princ->exp_date;
+ entry.attributes = rblock.flags; /* XXX is there a way to convert
+ the old attrs? */
+
+ memcpy((char *)v4key, (char *)&(princ->key_low), 4);
+ memcpy((char *) (((char *) v4key) + 4), (char *)&(princ->key_high), 4);
+ pcbc_encrypt((C_Block *) &v4key,
+ (C_Block *) &v4key,
+ (long) sizeof(C_Block),
+ master_key_schedule,
+ (C_Block *) master_key,
+ DECRYPT);
+
+ v4v5key.magic = KV5M_KEYBLOCK;
+ v4v5key.contents = (krb5_octet *)v4key;
+ v4v5key.enctype = ENCTYPE_DES_CBC_CRC;
+ v4v5key.length = sizeof(v4key);
+
+ retval = krb5_dbe_create_key_data(context, &entry);
+ if (retval) {
+ krb5_free_principal(context, entry.princ);
+ krb5_free_principal(context, mod_princ);
+ return retval;
+ }
+
+ keysalt.type = KRB5_KDB_SALTTYPE_V4;
+ keysalt.data.length = 0;
+ keysalt.data.data = (char *) NULL;
+ retval = krb5_dbekd_encrypt_key_data(context, rblock.eblock,
+ &v4v5key, &keysalt,
+ princ->key_version,
+ &entry.key_data[0]);
+ if (!retval)
+ retval = krb5_dbe_update_mod_princ_data(context, &entry,
+ mod_time, mod_princ);
+ if (retval) {
+ krb5_db_free_principal(context, &entry, 1);
+ krb5_free_principal(context, mod_princ);
+ return retval;
+ }
+ memset((char *)v4key, 0, sizeof(v4key));
+
+ retval = krb5_db_put_principal(context, &entry, &nentries);
+
+ if (!retval && !strcmp(princ->name, "krbtgt") &&
+ strcmp(princ->instance, realm) && princ->instance[0]) {
+ krb5_free_principal(context, entry.princ);
+ if (retval = krb5_build_principal(context, &entry.princ,
+ strlen(princ->instance),
+ princ->instance,
+ "krbtgt", realm, 0))
+ return retval;
+ retval = krb5_db_put_principal(context, &entry, &nentries);
+ }
+
+ krb5_db_free_principal(context, &entry, 1);
+ krb5_free_principal(context, mod_princ);
+
+ return retval;
+}
+
+static krb5_error_code
+add_principal(context, princ, op, pblock)
+krb5_context context;
+krb5_principal princ;
+enum ap_op op;
+struct realm_info *pblock;
+{
+ krb5_db_entry entry;
+ krb5_error_code retval;
+ krb5_keyblock *rkey;
+ int nentries = 1;
+ krb5_timestamp mod_time;
+ krb5_principal mod_princ;
+
+ memset((char *) &entry, 0, sizeof(entry));
+ if (retval = krb5_copy_principal(context, princ, &entry.princ))
+ return(retval);
+ entry.max_life = pblock->max_life;
+ entry.max_renewable_life = pblock->max_rlife;
+ entry.len = KRB5_KDB_V1_BASE_LENGTH;
+ entry.expiration = pblock->expiration;
+
+ if ((retval = krb5_timeofday(context, &mod_time))) {
+ krb5_db_free_principal(context, &entry, 1);
+ return retval;
+ }
+ entry.attributes = pblock->flags;
+
+ if (retval = krb5_dbe_create_key_data(context, &entry)) {
+ krb5_db_free_principal(context, &entry, 1);
+ return(retval);
+ }
+
+ switch (op) {
+ case MASTER_KEY:
+ entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock,
+ &master_keyblock,
+ (krb5_keysalt *) NULL, 1,
+ &entry.key_data[0])) {
+ krb5_db_free_principal(context, &entry, 1);
+ return retval;
+ }
+ break;
+ case RANDOM_KEY:
+ if (retval = krb5_random_key(context, pblock->eblock, pblock->rseed,
+ &rkey)) {
+ krb5_db_free_principal(context, &entry, 1);
+ return retval;
+ }
+ if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock,
+ rkey,
+ (krb5_keysalt *) NULL, 1,
+ &entry.key_data[0])) {
+ krb5_db_free_principal(context, &entry, 1);
+ return(retval);
+ }
+ krb5_free_keyblock(context, rkey);
+ break;
+ case NULL_KEY:
+ return EOPNOTSUPP;
+ default:
+ break;
+ }
+
+ retval = krb5_dbe_update_mod_princ_data(context, &entry,
+ mod_time, &db_create_princ);
+ if (!retval)
+ retval = krb5_db_put_principal(context, &entry, &nentries);
+ krb5_db_free_principal(context, &entry, 1);
+ return retval;
+}
+
+/*
+ * Convert a struct tm * to a UNIX time.
+ */
+
+
+#define daysinyear(y) (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366)))
+
+#define SECSPERDAY 24*60*60
+#define SECSPERHOUR 60*60
+#define SECSPERMIN 60
+
+static int cumdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334,
+ 365};
+
+static int leapyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static int nonleapyear[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static long
+maketime(tp, local)
+register struct tm *tp;
+int local;
+{
+ register long retval;
+ int foo;
+ int *marray;
+
+ if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
+ tp->tm_hour < 0 || tp->tm_hour > 23 ||
+ tp->tm_min < 0 || tp->tm_min > 59 ||
+ tp->tm_sec < 0 || tp->tm_sec > 59) /* out of range */
+ return 0;
+
+ retval = 0;
+ if (tp->tm_year < 1900)
+ foo = tp->tm_year + 1900;
+ else
+ foo = tp->tm_year;
+
+ if (foo < 1901 || foo > 2038) /* year is too small/large */
+ return 0;
+
+ if (daysinyear(foo) == 366) {
+ if (tp->tm_mon > 1)
+ retval+= SECSPERDAY; /* add leap day */
+ marray = leapyear;
+ } else
+ marray = nonleapyear;
+
+ if (tp->tm_mday < 0 || tp->tm_mday > marray[tp->tm_mon])
+ return 0; /* out of range */
+
+ while (--foo >= 1970)
+ retval += daysinyear(foo) * SECSPERDAY;
+
+ retval += cumdays[tp->tm_mon] * SECSPERDAY;
+ retval += (tp->tm_mday-1) * SECSPERDAY;
+ retval += tp->tm_hour * SECSPERHOUR + tp->tm_min * SECSPERMIN + tp->tm_sec;
+
+ if (local) {
+ /* need to use local time, so we retrieve timezone info */
+ struct timezone tz;
+ struct timeval tv;
+ if (gettimeofday(&tv, &tz) < 0) {
+ /* some error--give up? */
+ return(retval);
+ }
+ retval += tz.tz_minuteswest * SECSPERMIN;
+ }
+ return(retval);
+}
+
+static long
+time_explode(cp)
+register char *cp;
+{
+ char wbuf[5];
+ struct tm tp;
+ int local;
+
+ memset((char *)&tp, 0, sizeof(tp));
+
+ if (strlen(cp) > 10) { /* new format */
+ (void) strncpy(wbuf, cp, 4);
+ wbuf[4] = 0;
+ tp.tm_year = atoi(wbuf);
+ cp += 4; /* step over the year */
+ local = 0; /* GMT */
+ } else { /* old format: local time,
+ year is 2 digits, assuming 19xx */
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ wbuf[2] = 0;
+ tp.tm_year = 1900 + atoi(wbuf);
+ local = 1; /* local */
+ }
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ wbuf[2] = 0;
+ tp.tm_mon = atoi(wbuf)-1;
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ tp.tm_mday = atoi(wbuf);
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ tp.tm_hour = atoi(wbuf);
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ tp.tm_min = atoi(wbuf);
+
+
+ return(maketime(&tp, local));
+}
+
+static krb5_error_code
+process_v4_dump(context, dumpfile, realm)
+krb5_context context;
+char *dumpfile;
+char *realm;
+{
+ krb5_error_code retval;
+ FILE *input_file;
+ Principal aprinc;
+ char exp_date_str[50];
+ char mod_date_str[50];
+ int temp1, temp2, temp3;
+ long time_explode();
+
+ input_file = fopen(dumpfile, "r");
+ if (!input_file)
+ return errno;
+
+ for (;;) { /* explicit break on eof from fscanf */
+ int nread;
+
+ memset((char *)&aprinc, 0, sizeof(aprinc));
+ nread = fscanf(input_file,
+ "%s %s %d %d %d %hd %x %x %s %s %s %s\n",
+ aprinc.name,
+ aprinc.instance,
+ &temp1,
+ &temp2,
+ &temp3,
+ &aprinc.attributes,
+ &aprinc.key_low,
+ &aprinc.key_high,
+ exp_date_str,
+ mod_date_str,
+ aprinc.mod_name,
+ aprinc.mod_instance);
+ if (nread != 12) {
+ retval = nread == EOF ? 0 : KRB5_KDB_DB_CORRUPT;
+ break;
+ }
+ aprinc.key_low = ntohl (aprinc.key_low);
+ aprinc.key_high = ntohl (aprinc.key_high);
+ aprinc.max_life = (unsigned char) temp1;
+ aprinc.kdc_key_ver = (unsigned char) temp2;
+ aprinc.key_version = (unsigned char) temp3;
+ aprinc.exp_date = time_explode(exp_date_str);
+ aprinc.mod_date = time_explode(mod_date_str);
+ if (aprinc.instance[0] == '*')
+ aprinc.instance[0] = '\0';
+ if (aprinc.mod_name[0] == '*')
+ aprinc.mod_name[0] = '\0';
+ if (aprinc.mod_instance[0] == '*')
+ aprinc.mod_instance[0] = '\0';
+ if (retval = enter_in_v5_db(context, realm, &aprinc))
+ break;
+ }
+ (void) fclose(input_file);
+ return retval;
+}
+
+static krb5_error_code fixup_database(context, realm)
+ krb5_context context;
+ char * realm;
+{
+ krb5_db_entry entry;
+ krb5_error_code retval;
+ int nprincs;
+ krb5_boolean more;
+
+ nprincs = 1;
+ if (retval = krb5_db_get_principal(context, &tgt_princ, &entry,
+ &nprincs, &more))
+ return retval;
+
+ if (nprincs == 0)
+ return 0;
+
+ entry.attributes |= KRB5_KDB_SUPPORT_DESMD5;
+
+ retval = krb5_db_put_principal(context, &entry, &nprincs);
+
+ if (nprincs)
+ krb5_db_free_principal(context, &entry, nprincs);
+
+ return retval;
+}
+
+#else /* KRB5_KRB4_COMPAT */
+void
+load_v4db(argc, argv)
+ int argc;
+ char *argv[];
+{
+ printf("This version of krb5_edit does not support the V4 load command.\n");
+}
+#endif /* KRB5_KRB4_COMPAT */
diff --git a/src/kadmin/dbutil/ss_wrapper.c b/src/kadmin/dbutil/ss_wrapper.c
new file mode 100644
index 000000000..ada85efc9
--- /dev/null
+++ b/src/kadmin/dbutil/ss_wrapper.c
@@ -0,0 +1,85 @@
+/*
+ * admin/edit/ss_wrapper.c
+ *
+ * Copyright 1990,1991 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.
+ *
+ *
+ * ss wrapper for kdb5_edit
+ */
+
+#include <k5-int.h>
+#include "kdb5_util.h"
+#include <ss/ss.h>
+#include <stdio.h>
+
+extern ss_request_table kdb5_edit_cmds;
+extern int exit_status;
+extern FILE *scriptfile;
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *request;
+ krb5_error_code retval;
+ int sci_idx, code = 0;
+
+ request = kdb5_util_Init(argc, argv);
+ sci_idx = ss_create_invocation("kdb5_util", "5.0", (char *) NULL,
+ &kdb5_edit_cmds, &retval);
+ if (retval) {
+ ss_perror(sci_idx, retval, "creating invocation");
+ exit(1);
+ }
+
+ if (request) {
+ code = ss_execute_line(sci_idx, request, &code);
+ if (code != 0) {
+ ss_perror(sci_idx, code, request);
+ exit_status++;
+ }
+ } else if (scriptfile) {
+ char *command;
+ int nread;
+
+ /* Get a buffer */
+ if ((command = (char *) malloc(BUFSIZ))) {
+ /* Process commands from the script until end-of-file or error */
+ while (!feof(scriptfile) &&
+ (fgets(command, BUFSIZ, scriptfile))) {
+
+ /* Strip trailing newline */
+ if (command[strlen(command)-1] == '\n')
+ command[strlen(command)-1] = '\0';
+
+ /* Execute the command */
+ code = ss_execute_line(sci_idx, command, &code);
+ if (code != 0) {
+ ss_perror(sci_idx, code, command);
+ exit_status++;
+ break;
+ }
+ }
+ free(command);
+ }
+ } else
+ ss_listen(sci_idx, &retval);
+ return quit() ? 1 : exit_status;
+}
diff --git a/src/kadmin/dbutil/string_table.c b/src/kadmin/dbutil/string_table.c
new file mode 100644
index 000000000..b9f86a363
--- /dev/null
+++ b/src/kadmin/dbutil/string_table.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+/* String table of messages for kadm5_create */
+
+char *str_INITING_KCONTEXT = "while initializing the kerberos context";
+
+char *str_PARSE_NAME = "while parsing admin principal name.";
+
+char *str_HISTORY_PARSE_NAME = "while parsing admin history principal name.";
+
+char *str_ADMIN_PRINC_EXISTS = "Warning! Admin principal already exists.";
+
+char *str_CHANGEPW_PRINC_EXISTS = "Warning! Changepw principal already exists.";
+
+char *str_HISTORY_PRINC_EXISTS = "Warning! Admin history principal already exists.";
+
+char *str_ADMIN_PRINC_WRONG_ATTRS =
+ "Warning! Admin principal has incorrect attributes.\n"
+ "\tDISALLOW_TGT should be set, and max_life should be three hours.\n"
+ "\tThis program will leave them as-is, but beware!.";
+
+char *str_CHANGEPW_PRINC_WRONG_ATTRS =
+ "Warning! Changepw principal has incorrect attributes.\n"
+ "\tDISALLOW_TGT and PW_CHANGE_SERVICE should both be set, and "
+ "max_life should be five minutes.\n"
+ "\tThis program will leave them as-is, but beware!.";
+
+char *str_HISTORY_PRINC_WRONG_ATTRS =
+ "Warning! Admin history principal has incorrect attributes.\n"
+ "\tDISALLOW_ALL_TIX should be set.\n"
+ "\tThis program will leave it as-is, but beware!.";
+
+char *str_CREATED_PRINC_DB =
+ "%s: Admin principal database created (or it already existed).\n"; /* whoami */
+
+char *str_CREATED_POLICY_DB =
+ "%s: Admin policy database created (or it already existed).\n"; /* whoami */
+
+char *str_RANDOM_KEY =
+ "while calling random key for %s."; /* principal name */
+
+char *str_ENCRYPT_KEY =
+ "while calling encrypt key for %s."; /* principal name */
+
+char *str_PUT_PRINC =
+ "while calling storing %s in Kerberos database."; /* principal name */
+
+char *str_CREATING_POLICY_DB = "while creating/opening admin policy database.";
+
+char *str_CLOSING_POLICY_DB = "while closing admin policy database.";
+
+char *str_CREATING_PRINC_DB = "while creating/opening admin principal database.";
+
+char *str_CLOSING_PRINC_DB = "while closing admin principal database.";
+
+char *str_CREATING_PRINC_ENTRY =
+ "while creating admin principal database entry for %s."; /* princ_name */
+
+char *str_A_PRINC = "a principal";
+
+char *str_UNPARSE_PRINC = "while unparsing principal.";
+
+char *str_CREATED_PRINC = "%s: Created %s principal.\n"; /* whoami, princ_name */
+
+char *str_INIT_KDB = "while initializing kdb.";
+
+char *str_NO_KDB =
+"while initializing kdb.\nThe Kerberos KDC database needs to exist in /krb5.\n\
+If you haven't run kdb5_create you need to do so before running this command.";
+
+
+char *str_INIT_RANDOM_KEY = "while initializing random key generator.";
+
+char *str_TOO_MANY_ADMIN_PRINC =
+ "while fetching admin princ. Can only have one admin principal.";
+
+char *str_TOO_MANY_CHANGEPW_PRINC =
+ "while fetching changepw princ. Can only have one changepw principal.";
+
+char *str_TOO_MANY_HIST_PRINC =
+ "while fetching history princ. Can only have one history principal.";
+
+char *str_WHILE_DESTROYING_ADMIN_SESSION = "while closing session with admin server and destroying tickets.";
diff --git a/src/kadmin/dbutil/string_table.h b/src/kadmin/dbutil/string_table.h
new file mode 100644
index 000000000..e8cb45367
--- /dev/null
+++ b/src/kadmin/dbutil/string_table.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ */
+
+#ifndef _OVSEC_ADM_STRINGS_
+
+extern char *str_INITING_KCONTEXT;
+extern char *str_PARSE_NAME;
+extern char *str_HISTORY_PARSE_NAME;
+extern char *str_ADMIN_PRINC_EXISTS;
+extern char *str_CHANGEPW_PRINC_EXISTS;
+extern char *str_HISTORY_PRINC_EXISTS;
+extern char *str_ADMIN_PRINC_WRONG_ATTRS;
+extern char *str_CHANGEPW_PRINC_WRONG_ATTRS;
+extern char *str_HISTORY_PRINC_WRONG_ATTRS;
+extern char *str_CREATED_PRINC_DB;
+extern char *str_CREATED_POLICY_DB;
+extern char *str_RANDOM_KEY;
+extern char *str_ENCRYPT_KEY;
+extern char *str_PUT_PRINC;
+extern char *str_CREATING_POLICY_DB;
+extern char *str_CLOSING_POLICY_DB;
+extern char *str_CREATING_PRINC_DB;
+extern char *str_CLOSING_PRINC_DB;
+extern char *str_CREATING_PRINC_ENTRY;
+extern char *str_A_PRINC;
+extern char *str_UNPARSE_PRINC;
+extern char *str_CREATED_PRINC;
+extern char *str_INIT_KDB;
+extern char *str_NO_KDB;
+extern char *str_INIT_RANDOM_KEY;
+extern char *str_TOO_MANY_ADMIN_PRINC;
+extern char *str_TOO_MANY_CHANGEPW_PRINC;
+extern char *str_TOO_MANY_HIST_PRINC;
+extern char *str_WHILE_DESTROYING_ADMIN_SESSION;
+
+#endif /* _OVSEC_ADM_STRINGS_ */
diff --git a/src/kadmin/dbutil/tcl_wrapper.c b/src/kadmin/dbutil/tcl_wrapper.c
new file mode 100644
index 000000000..d527fa0d1
--- /dev/null
+++ b/src/kadmin/dbutil/tcl_wrapper.c
@@ -0,0 +1,235 @@
+/*
+ * admin/edit/tcl_wrapper.c
+ *
+ * Copyright 1990,1991 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.
+ *
+ *
+ * Tcl wrapper for kdb5_edit
+ */
+
+#include "k5-int.h"
+#include "kdb5_edit.h"
+#include <tcl.h>
+
+#define CMDDECL(x) int x(clientData, interp, argc, argv)\
+ ClientData clientData;\
+ Tcl_Interp * interp;\
+ int argc;\
+ char ** argv;
+#define CMDPROTO(x) int x PROTOTYPE((ClientData, Tcl_Interp,\
+ int, char **))
+#define MKCMD(name,cmd) Tcl_CreateCommand(interp, name, cmd,\
+ (ClientData)NULL,\
+ (Tcl_CmdDeleteProc *)NULL)
+
+extern int main();
+int *tclDummyMainPtr = (int *) main; /* force ld to suck in main()
+ from libtcl.a */
+extern Tcl_Interp *interp; /* XXX yes, this is gross,
+ but we do need it for some things */
+extern int exit_status;
+
+void show_principal PROTOTYPE((int, char **));
+void add_new_key PROTOTYPE((int, char **));
+void change_pwd_key PROTOTYPE((int, char **));
+void add_rnd_key PROTOTYPE((int, char **));
+void change_rnd_key PROTOTYPE((int, char **));
+void delete_entry PROTOTYPE((int, char **));
+void extract_srvtab PROTOTYPE((krb5_context, int, char **));
+void extract_v4_srvtab PROTOTYPE((int, char **));
+void list_db PROTOTYPE((int, char **));
+void dump_db PROTOTYPE((int, char **));
+void load_db PROTOTYPE((int, char **));
+void set_dbname PROTOTYPE((krb5_context, int, char **));
+void enter_master_key PROTOTYPE((krb5_context, int, char **));
+
+/*
+ * this is mostly stolen from tcl_ExitCmd()
+ * we need to do a few extra things, though...
+ */
+int doquit(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ int value;
+
+ if ((argc != 1) && (argc != 2)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ?returnCode?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 1) {
+ exit(quit() ? 1 : exit_status);
+ }
+ if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ (void)quit();
+ exit(value);
+ /*NOTREACHED*/
+ return TCL_OK; /* Better not ever reach this! */
+}
+
+int list_requests(clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ Tcl_SetResult(interp, "show_principal, show: Show the Kerberos database entry for a principal\nadd_new_key, ank: Add new entry to the Kerberos database (prompting for password\nchange_pwd_key, cpw: Change key of an entry in the Kerberos database (prompting for password)\nadd_rnd_key, ark: Add new entry to Kerberos database, using a random key\nchange_rnd_key, crk: Change key of an entry in the Kerberos database (select a random key)\ndelete_entry, delent: Delete an entry from the database\nextract_srvtab, xst, ex_st: Extract service key table\nextract_v4_srvtab, xst4: Extract service key table\nlist_db, ldb: List database entries\nset_dbname, sdbn: Change database name\nenter_master_key, emk: Enter the master key for a database\nchange_working_directory, cwd, cd: Change working directory\nprint_working_directory, pwd: Print working directory\nlist_requests, lr: List available requests\nquit, exit: Exit program", TCL_STATIC);
+ return TCL_OK;
+}
+
+int wrapper(func, interp, argc, argv)
+ void (*func)();
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+ (*func)(argc, argv);
+ return TCL_OK;
+}
+
+int Tcl_AppInit(interp)
+ Tcl_Interp *interp;
+{
+ int argc;
+ char **argv, **mostly_argv;
+ char *interp_argv, *interp_argv0, *request;
+ Tcl_CmdInfo cmdInfo;
+
+ if (Tcl_Init(interp) == TCL_ERROR)
+ return TCL_ERROR;
+ /*
+ * the following is, admittedly, sorta gross, but the only way
+ * to grab the original argc, argv once the interpreter is running
+ */
+ interp_argv = Tcl_GetVar(interp, "argv", 0);
+ if (interp_argv == NULL)
+ return TCL_ERROR;
+ else if (Tcl_SplitList(interp, interp_argv,
+ &argc, &mostly_argv) != TCL_OK)
+ return TCL_ERROR;
+ interp_argv0 = Tcl_GetVar(interp, "argv0", 0);
+ if (interp_argv0 == NULL)
+ return TCL_ERROR;
+ if ((argv = (char **)malloc((argc + 1) * sizeof (char *))) == NULL)
+ return TCL_ERROR;
+ argv[0] = interp_argv0;
+ memcpy(argv + 1, mostly_argv, argc++ * sizeof (char *));
+ /*
+ * set up a prompt
+ */
+ if (Tcl_SetVar(interp, "tcl_prompt1",
+ "puts -nonewline \"kdb5_edit: \"", 0) == NULL)
+ return TCL_ERROR;
+ /*
+ * we don't want arbitrary programs to get exec'd by accident
+ */
+ if (Tcl_SetVar(interp, "auto_noexec", "{}", 0) == NULL)
+ return TCL_ERROR;
+ request = kdb5_edit_Init(argc, argv);
+ Tcl_CallWhenDeleted(interp, doquit,
+ (ClientData)0);
+ Tcl_CreateCommand(interp, "quit", doquit,
+ (ClientData)0,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "exit", doquit,
+ (ClientData)0,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "list_requests", list_requests,
+ (ClientData)0,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "lr", list_requests,
+ (ClientData)0,
+ (Tcl_CmdDeleteProc *)0);
+ if (Tcl_GetCommandInfo(interp, "cd", &cmdInfo)) {
+ Tcl_CreateCommand(interp, "cwd", cmdInfo.proc,
+ (ClientData)0,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "change_working_directory", cmdInfo.proc,
+ (ClientData)0,
+ (Tcl_CmdDeleteProc *)0);
+ }
+ if (Tcl_GetCommandInfo(interp, "pwd", &cmdInfo)) {
+ Tcl_CreateCommand(interp, "print_working_directory", cmdInfo.proc,
+ (ClientData)0,
+ (Tcl_CmdDeleteProc *)0);
+ }
+ Tcl_CreateCommand(interp, "show_principal", wrapper, show_principal,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "show", wrapper, show_principal,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "add_new_key", wrapper, add_new_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "ank", wrapper, add_new_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "change_pwd_key", wrapper, change_pwd_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "cpw", wrapper, change_pwd_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "add_rnd_key", wrapper, add_rnd_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "ark", wrapper, add_rnd_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "change_rnd_key", wrapper, change_rnd_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "crk", wrapper, change_rnd_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "delete_entry", wrapper, delete_entry,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "delent", wrapper, delete_entry,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "extract_srvtab", wrapper, extract_srvtab,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "xst", wrapper, extract_srvtab,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "ex_st", wrapper, extract_srvtab,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "extract_v4_srvtab", wrapper, extract_v4_srvtab,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "xv4st", wrapper, extract_v4_srvtab,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "list_db", wrapper, list_db,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "ldb", wrapper, list_db,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "dump_db", wrapper, dump_db,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "ddb", wrapper, dump_db,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "load_db", wrapper, load_db,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "lddb", wrapper, load_db,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "set_dbname", wrapper, set_dbname,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "sdbn", wrapper, set_dbname,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "enter_master_key", wrapper, enter_master_key,
+ (Tcl_CmdDeleteProc *)0);
+ Tcl_CreateCommand(interp, "emk", wrapper, enter_master_key,
+ (Tcl_CmdDeleteProc *)0);
+ if (request && (Tcl_Eval(interp, request) == TCL_ERROR))
+ return TCL_ERROR;
+ return TCL_OK;
+}
diff --git a/src/kadmin/dbutil/util.c b/src/kadmin/dbutil/util.c
new file mode 100644
index 000000000..78de2cd6f
--- /dev/null
+++ b/src/kadmin/dbutil/util.c
@@ -0,0 +1,155 @@
+/*
+ * admin/edit/util.c
+ *
+ * Copyright 1992 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.
+ *
+ * Utilities for kdb5_edit.
+ *
+ * Some routines derived from code contributed by the Sandia National
+ * Laboratories. Sandia National Laboratories also makes no
+ * representations about the suitability of the modifications, or
+ * additions to this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ */
+
+#include "k5-int.h"
+#include "./kdb5_edit.h"
+
+#if defined(sysvimp) || ( defined(mips) && defined(SYSTYPE_BSD43)) || (defined(vax) && !defined(ultrix))
+char *
+strstr(s1, s2)
+char *s1;
+char *s2;
+{
+ int s2len;
+ int i;
+ char *temp_ptr;
+
+ temp_ptr = s1;
+ for ( i = 0; i < strlen(s1); i++) {
+ if (memcmp(temp_ptr, s2, strlen(s2)) == 0) return(temp_ptr);
+ temp_ptr += 1;
+ }
+ return ((char *) 0);
+}
+#endif /* sysvimp */
+
+void
+parse_token(token_in, must_be_first_char, num_tokens, tokens_out)
+char *token_in;
+int *must_be_first_char;
+int *num_tokens;
+char *tokens_out;
+{
+ int i, j;
+ int token_count = 0;
+
+ i = 0;
+ j = 0;
+
+ /* Eliminate Up Front Asterisks */
+ *must_be_first_char = 1;
+ for (i = 0; token_in[i] == '*'; i++) {
+ *must_be_first_char = 0;
+ }
+
+ if (i == strlen(token_in)) {
+ *num_tokens = 0;
+ return;
+ }
+
+ /* Fill first token_out */
+ token_count++;
+ while ((token_in[i] != '*') && (token_in[i] != '\0')) {
+ tokens_out[j] = token_in[i];
+ j++;
+ i++;
+ }
+
+ if (i == strlen(token_in)) {
+ tokens_out[j] = '\0';
+ *num_tokens = token_count;
+ return;
+ }
+
+ /* Then All Subsequent Tokens */
+ while (i < strlen(token_in)) {
+ if (token_in[i] == '*') {
+ token_count++;
+ tokens_out[j] = '\t';
+ } else {
+ tokens_out[j] = token_in[i];
+ }
+ i++;
+ j++;
+ }
+ tokens_out[j] = '\0';
+
+ if (tokens_out[j - 1] == '\t') {
+ token_count--;
+ tokens_out[j - 1] = '\0';
+ }
+
+ *num_tokens = token_count;
+ return;
+}
+
+int
+check_for_match(search_field, must_be_first_character, chk_entry,
+ num_tokens, type)
+int must_be_first_character;
+char *search_field;
+krb5_db_entry *chk_entry;
+int num_tokens;
+int type;
+{
+ char token1[256];
+ char *found1;
+ char token2[256];
+ char *found2;
+ char token3[256];
+ char *found3;
+ char *local_entry;
+
+ local_entry = chk_entry->princ->data[type].data;
+
+ token1[0] = token2[0] = token3[0] = '\0';
+
+ (void) sscanf(search_field, "%s\t%s\t%s", token1, token2, token3);
+
+ found1 = strstr(local_entry, token1);
+
+ if (must_be_first_character && (found1 != local_entry)) return(0);
+
+ if (found1 && (num_tokens == 1)) return(1);
+
+ if (found1 && (num_tokens > 1)) {
+ found2 = strstr(local_entry, token2);
+ if (found2 && (found2 > found1) && (num_tokens == 2)) return(1);
+ }
+
+ if ((found2 > found1) && (num_tokens == 3)) {
+ found3 = strstr(local_entry, token3);
+ if (found3 && (found3 > found2) && (found2 > found1)) return(1);
+ }
+ return(0);
+}
+
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
+ }
+ }
+}
diff --git a/src/kadmin/import/ChangeLog b/src/kadmin/import/ChangeLog
new file mode 100644
index 000000000..ca7c0bc2d
--- /dev/null
+++ b/src/kadmin/import/ChangeLog
@@ -0,0 +1,17 @@
+Thu Jul 18 20:40:32 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Mon Jul 15 16:58:08 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in (USE_GSSAPI_LIBRARY): shared libraries require all
+ symbols to be resolved, so this needs to be here.
+
+Wed Jul 10 01:26:47 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in, configure.in: added autoconf support
+
+Tue Jul 9 17:12:08 1996 Marc Horowitz <marc@mit.edu>
+
+ * ovsec_adm_import.c, import.c: renamed <ovsec_admin/foo.h> to
+ <kadm5/foo.h>
diff --git a/src/kadmin/import/Makefile.in b/src/kadmin/import/Makefile.in
new file mode 100644
index 000000000..2f15e706f
--- /dev/null
+++ b/src/kadmin/import/Makefile.in
@@ -0,0 +1,18 @@
+CFLAGS = $(CCOPTS) $(DEFS) -I. $(LOCALINCLUDE)
+
+PROG = kadm5_import
+OBJS = ovsec_adm_import.o import.o import_err.o strtok.o misc.o
+
+all:: $(PROG)
+
+import_err.c import_err.h: $(srcdir)/import_err.et
+$(OBJS): import_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/import/Makefile.ov b/src/kadmin/import/Makefile.ov
new file mode 100644
index 000000000..f2750313d
--- /dev/null
+++ b/src/kadmin/import/Makefile.ov
@@ -0,0 +1,24 @@
+TOP = ..
+include $(TOP)/config.mk/template
+CFLAGS := $(CFLAGS)
+
+# 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_import: import_err.h
+
+depend:: import_err.h
+
+PROG := kadm5_import
+OBJS := ovsec_adm_import.o import_err.o import.o strtok.o misc.o
+SRCS := ovsec_adm_import.c import.c import_err.et strtok.c misc.c
+ETABLES := import_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/import/configure.in b/src/kadmin/import/configure.in
new file mode 100644
index 000000000..ad66ebd7c
--- /dev/null
+++ b/src/kadmin/import/configure.in
@@ -0,0 +1,12 @@
+AC_INIT(ovsec_adm_import.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/import/import.c b/src/kadmin/import/import.c
new file mode 100644
index 000000000..b1a8c0a76
--- /dev/null
+++ b/src/kadmin/import/import.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <memory.h>
+
+#include <kadm5/adb.h>
+#include "import_err.h"
+#include "import.h"
+
+#define LINESIZE 32768 /* XXX */
+#define PLURAL(count) (((count) == 1) ? error_message(IMPORT_SINGLE_RECORD) : error_message(IMPORT_PLURAL_RECORDS))
+
+int parse_pw_hist_ent(current, hist, ovsec_compat)
+ char *current;
+ osa_pw_hist_ent *hist;
+ int ovsec_compat;
+{
+ int tmp, i, j, ret;
+ char *cp;
+
+ ret = 0;
+ if (!ovsec_compat) {
+ if ((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ return IMPORT_FAILED;
+ }
+ hist->n_key_data = atoi(cp);
+ } else
+ hist->n_key_data = 1;
+
+ hist->key_data = (krb5_key_data *) malloc(hist->n_key_data *
+ sizeof(krb5_key_data));
+ if (hist->key_data == NULL)
+ return ENOMEM;
+ memset(hist->key_data, 0, sizeof(krb5_key_data)*hist->n_key_data);
+
+ for (i = 0; i < hist->n_key_data; i++) {
+ krb5_key_data *key_data = &hist->key_data[i];
+
+ key_data->key_data_ver = 1;
+
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ key_data->key_data_type[0] = atoi(cp);
+
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ key_data->key_data_length[0] = atoi(cp);
+
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ if(!(key_data->key_data_contents[0] =
+ (krb5_octet *) malloc(key_data->key_data_length[0]+1))) {
+ ret = ENOMEM;
+ goto done;
+ }
+ for(j = 0; j < key_data->key_data_length[0]; j++) {
+ if(sscanf(cp, "%02x", &tmp) != 1) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ key_data->key_data_contents[0][j] = tmp;
+ cp = strchr(cp, ' ') + 1;
+ }
+ }
+
+done:
+ return ret;
+}
+
+
+
+/*
+ * Function: parse_principal
+ *
+ * Purpose: parse principal line in db dump file
+ *
+ * Arguments:
+ * <return value> 0 on sucsess, error code on failure
+ *
+ * Requires:
+ * principal database to be opened.
+ * nstrtok(3) to have a valid buffer in memory.
+ *
+ * Effects:
+ * [effects]
+ *
+ * Modifies:
+ * [modifies]
+ *
+ */
+int parse_principal(context, ovsec_compat)
+ krb5_context context;
+ int ovsec_compat;
+{
+ XDR xdrs;
+ osa_princ_ent_t rec;
+ osa_adb_ret_t ret;
+ krb5_tl_data tl_data;
+ krb5_principal princ;
+ krb5_db_entry kdb;
+ char *current;
+ char *cp;
+ int tmp, x, i, one, more;
+
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL)
+ return IMPORT_BAD_FILE;
+ if((rec = (osa_princ_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL)
+ return ENOMEM;
+ memset(rec, 0, sizeof(osa_princ_ent_rec));
+ if((ret = krb5_parse_name(context, cp, &princ)))
+ goto done;
+ krb5_unparse_name(context, princ, &current);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ } else {
+ if(strcmp(cp, "")) {
+ if((rec->policy = (char *) malloc(strlen(cp)+1)) == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ strcpy(rec->policy, cp);
+ } else rec->policy = NULL;
+ }
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->aux_attributes = strtol(cp, (char **)NULL, 16);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->old_key_len = atoi(cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->old_key_next = atoi(cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", current);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->admin_history_kvno = atoi(cp);
+ if (! rec->old_key_len) {
+ rec->old_keys = NULL;
+ } else {
+ if(!(rec->old_keys = (osa_pw_hist_ent *)
+ malloc(sizeof(osa_pw_hist_ent) * rec->old_key_len))) {
+ ret = ENOMEM;
+ goto done;
+ }
+ memset(rec->old_keys,0,
+ sizeof(osa_pw_hist_ent) * rec->old_key_len);
+ for(x = 0; x < rec->old_key_len; x++)
+ parse_pw_hist_ent(current, &rec->old_keys[x], ovsec_compat);
+ }
+
+ xdralloc_create(&xdrs, XDR_ENCODE);
+ if (! xdr_osa_princ_ent_rec(&xdrs, rec)) {
+ xdr_destroy(&xdrs);
+ ret = OSA_ADB_XDR_FAILURE;
+ goto done;
+ }
+
+ tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+ tl_data.tl_data_length = xdr_getpos(&xdrs);
+ tl_data.tl_data_contents = xdralloc_getdata(&xdrs);
+
+ one = 1;
+ ret = krb5_db_get_principal(context, princ, &kdb, &one,
+ &more);
+ if (ret)
+ goto done;
+
+ if (ret = krb5_dbe_update_tl_data(context, &kdb,
+ &tl_data))
+ goto done;
+
+ if (ret = krb5_db_put_principal(context, &kdb, &one))
+ goto done;
+
+ xdr_destroy(&xdrs);
+
+done:
+ free(current);
+ krb5_free_principal(context, princ);
+ osa_free_princ_ent(rec);
+ return ret;
+}
+
+/*
+ * Function: parse-policy
+ *
+ * Purpose: parse the ascii text of a dump file and turn it into
+ * a policy_ent_rec.
+ *
+ * Arguments:
+ * <return value> 0 on sucsess, error code on failure;
+ *
+ * Requires:
+ * nstrtok to have a buffer in memory
+ *
+ * Effects:
+ * write data out to db.
+ *
+ * Modifies:
+ * policy db.
+ *
+ */
+int
+parse_policy(pol_db)
+ osa_adb_policy_t pol_db;
+{
+ osa_policy_ent_t rec;
+ char *cp;
+ osa_adb_ret_t ret;
+
+ if((rec = (osa_policy_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL)
+ return ENOMEM;
+ memset(rec, 0, sizeof(osa_princ_ent_rec));
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ ret = IMPORT_BAD_FILE;
+ goto done;
+ }
+ if((rec->name = (char *) malloc(strlen(cp) + 1)) == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ strcpy(rec->name, cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->pw_min_life = atoi(cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->pw_max_life = atoi(cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->pw_min_length = atoi(cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->pw_min_classes = atoi(cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->pw_history_num = atoi(cp);
+ if((cp = nstrtok((char *) NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name);
+ ret = IMPORT_FAILED;
+ goto done;
+ }
+ rec->policy_refcnt = atoi(cp);
+ ret = osa_adb_create_policy(pol_db, rec);
+done:
+ osa_free_policy_ent(rec);
+ return ret;
+}
+
+/*
+ * Function: import-file
+ *
+ * Purpose: import a flat ascii file and convert it to a db file.
+ *
+ * Arguments:
+ * fp (input) file pointer to read db.
+ * <return value> 0 or error code on error.
+ *
+ * Requires:
+ * fp be valid
+ *
+ * Effects:
+ * calls appropriate routine to write out db files.
+ *
+ * Modifies:
+ * database file.
+ *
+ */
+
+int import_file(krb5_context context, FILE *fp, int merge_princs,
+ osa_adb_policy_t pol_db)
+{
+
+ int count = 0;
+ int errcnt = 0;
+ int ret = 0;
+ int found_footer = 0;
+ int file_count;
+ int ovsec_compat;
+ char line[LINESIZE];
+ char version[BUFSIZ];
+ char date[BUFSIZ];
+ char *cp;
+
+ if(fgets(line, LINESIZE, fp) == NULL)
+ return IMPORT_BAD_FILE;
+ if ((sscanf(line, "%[^\t]\t %[^\t]", version, date)) != 2)
+ return IMPORT_BAD_FILE;
+ if(!strcmp(version, VERSION_OVSEC_10))
+ ovsec_compat++;
+ else if (strcmp(version, VERSION_KADM5_20))
+ return IMPORT_BAD_VERSION;
+
+ while(fgets(line, LINESIZE, fp) != (char *) NULL) {
+ if(found_footer) {
+ com_err(NULL, IMPORT_EXTRA_DATA, NULL);
+ break;
+ }
+ cp = nstrtok(line, "\t");
+ if(!strcasecmp(cp, "princ")) {
+ if(merge_princs &&
+ (ret = parse_principal(context, ovsec_compat)) != OSA_ADB_OK) {
+ if(ret == IMPORT_FAILED) {
+ if(!confirm())
+ break;
+ else {
+ errcnt++;
+ continue;
+ }
+ } else break;
+ } else {
+ count++;
+ continue;
+ }
+ }
+ if(!strcasecmp(cp, "policy")) {
+ if((ret = parse_policy(pol_db)) != OSA_ADB_OK) {
+ if(ret == IMPORT_FAILED) {
+ if(!confirm())
+ break;
+ else {
+ errcnt++;
+ continue;
+ }
+ } else break;
+ } else {
+ count++;
+ continue;
+ }
+ }
+ if(!strcasecmp(cp, "end of database")) {
+ found_footer = 1;
+ } else {
+ com_err(NULL, IMPORT_BAD_TOKEN, "%s", cp);
+ if(!confirm()) {
+ ret = IMPORT_BAD_FILE;
+ break;
+ } else {
+ errcnt++;
+ continue;
+ }
+ }
+ }
+ if(ret == OSA_ADB_OK && found_footer) {
+ if((cp = nstrtok(NULL, "\t")) == NULL) {
+ com_err(NULL, IMPORT_BAD_FOOTER, NULL);
+ if(!confirm())
+ ret = IMPORT_BAD_FOOTER;
+ else
+ ret = OSA_ADB_OK;
+ } else
+ file_count = atoi(cp);
+ if(file_count != (count + errcnt)) {
+ fprintf(stderr, error_message(IMPORT_COUNT_MESSAGE), file_count,
+ PLURAL(file_count), count, PLURAL(count));
+ if(!confirm())
+ ret = IMPORT_MISMATCH_COUNT;
+ else
+ ret = OSA_ADB_OK;
+ } else fprintf(stderr, error_message(IMPORT_NO_ERR), count,
+ PLURAL(count));
+ } else if(ret == OSA_ADB_OK && !found_footer) {
+ com_err(NULL, IMPORT_BAD_FOOTER, NULL);
+ if(!confirm())
+ ret = IMPORT_BAD_FOOTER;
+ else
+ ret = OSA_ADB_OK;
+ }
+
+ return ret;
+}
+
diff --git a/src/kadmin/import/import.h b/src/kadmin/import/import.h
new file mode 100644
index 000000000..e0c3fedf9
--- /dev/null
+++ b/src/kadmin/import/import.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ *
+ * $Log$
+ * Revision 1.3 1996/07/22 20:26:27 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.2.4.1 1996/07/18 03:02:23 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.2.2.1 1996/06/20 21:48:24 marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.2 1996/06/05 20:52:28 bjaspan
+ * initial hack at porting to mit kerberos
+ *
+ * Revision 1.1 1993/11/17 06:13:23 shanzer
+ * Initial revision
+ *
+ */
+
+#include <stdio.h>
+
+/*
+ * XXX These should be defined somewhere so import and export get the
+ * same value.
+ */
+#define VERSION_OVSEC_10 "OpenV*Secure V1.0"
+#define VERSION_KADM5_20 "Kerberos KADM5 database V2.0"
+
+int import_file(krb5_context context, FILE *fp, int merge_princs,
+ osa_adb_policy_t pol_db);
+int confirm(void);
+char *nstrtok(char *str, char *delim);
diff --git a/src/kadmin/import/import_err.et b/src/kadmin/import/import_err.et
new file mode 100644
index 000000000..e091fe33c
--- /dev/null
+++ b/src/kadmin/import/import_err.et
@@ -0,0 +1,26 @@
+error_table imp
+error_code IMPORT_NO_ERR, "Successfully imported %d record%s.\n"
+error_code IMPORT_BAD_FILE, "Input not recognized as database dump"
+error_code IMPORT_BAD_TOKEN, "Bad token in dump file."
+error_code IMPORT_BAD_VERSION, "Bad version in dump file"
+error_code IMPORT_BAD_RECORD, "Defective record encountered: "
+error_code IMPORT_BAD_FOOTER, "Truncated input file detected."
+error_code IMPORT_FAILED, "Import of dump failed"
+error_code IMPORT_COUNT_MESSAGE, "Mismatched record count: %d record%s indicated, %d record%s scanned.\n"
+error_code IMPORT_MISMATCH_COUNT, "Number of records imported does not match count"
+error_code IMPORT_UNK_OPTION, "Unknown command line option.\nUsage: ovsec_adm_import [filename]"
+error_code IMPORT_WARN_DB, "Warning -- continuing to import will overwrite existing databases!"
+error_code IMPORT_RENAME_FAILED, "Database rename Failed!!"
+error_code IMPORT_EXTRA_DATA, "Extra data after footer is ignored."
+error_code IMPORT_CONFIRM, "Proceed <y|n>?"
+error_code IMPORT_OPEN_DUMP, "while opening input file"
+error_code IMPORT_IMPORT, "while importing databases"
+error_code IMPORT_TTY, "cannot open /dev/tty!!"
+error_code IMPORT_RENAME_OPEN, "while opening databases"
+error_code IMPORT_RENAME_LOCK, "while acquiring permanent lock"
+error_code IMPORT_RENAME_UNLOCK, "while releasing permanent lock"
+error_code IMPORT_RENAME_CLOSE, "while closing databases"
+error_code IMPORT_SINGLE_RECORD, ""
+error_code IMPORT_PLURAL_RECORDS, "s"
+error_code IMPORT_GET_PARAMS, "while retrieving configuration parameters"
+end
diff --git a/src/kadmin/import/misc.c b/src/kadmin/import/misc.c
new file mode 100644
index 000000000..bc58c7e7f
--- /dev/null
+++ b/src/kadmin/import/misc.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ *
+ * $Log$
+ * Revision 1.4 1996/07/22 20:26:31 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.3.4.1 1996/07/18 03:02:26 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.3.2.1 1996/06/20 21:48:39 marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.3 1994/04/11 23:52:10 jik
+ * Sandbox:
+ *
+ * Include <com_err.h> to get the declaration of error_message.
+ *
+ * Revision 1.3 1994/03/29 21:18:54 jik
+ * Include <com_err.h> to get the declaration of error_message.
+ *
+ * Revision 1.2 1993/12/21 18:59:25 shanzer
+ * make sure we prompt for input from /dev/tty
+ *
+ * Revision 1.1 1993/11/14 23:51:04 shanzer
+ * Initial revision
+ *
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <com_err.h> /* for error_message() */
+#include "import_err.h"
+
+#ifndef TRUE
+#define TRUE (1);
+#endif
+#ifndef FALSE
+#define FALSE (0);
+#endif
+
+/*
+ * Function: confirm
+ *
+ * Purpose: ask a yes or no question you must answer
+ * with a 'y|n|Y|n'
+ *
+ * Arguments:
+ * (input) none
+ * <return value> 1 if answered yes. 0 if no.
+ *
+ * Requires:
+ * IMPORT_CONFIRM be be defined. and com_err be init.
+ *
+ * Effects:
+ * none
+ *
+ * Modifies:
+ * nuttin
+ *
+ */
+
+int
+confirm(void)
+{
+ char buf[BUFSIZ]; /* can we say overkill ... */
+ FILE *fp;
+
+ if ((fp = fopen("/dev/tty", "r")) == NULL) {
+ fprintf(stderr, error_message(IMPORT_TTY));
+ return FALSE;
+ }
+ while(1) {
+ fprintf(stderr, error_message(IMPORT_CONFIRM));
+ fgets(buf, BUFSIZ, fp);
+ if(buf[0] == 'y' || buf[0] == 'Y') {
+ fclose(fp);
+ return TRUE;
+ }
+ if(buf[0] == 'n' || buf[0] == 'N') {
+ fclose(fp);
+ return FALSE;
+ }
+ }
+}
+
diff --git a/src/kadmin/import/ovsec_adm_import.c b/src/kadmin/import/ovsec_adm_import.c
new file mode 100644
index 000000000..4ca920434
--- /dev/null
+++ b/src/kadmin/import/ovsec_adm_import.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <kadm5/adb.h>
+#include "import_err.h"
+#include "import.h"
+
+#define TMP_POLICY_FMT "/krb5/#ovsec_import_policy.%d"
+
+int
+main(int argc, char *argv[])
+{
+ char *filename,
+ *whoami;
+ int ret, merge_princs;
+ FILE *fp;
+ osa_adb_policy_t policy_db;
+ char pol_dbfile[BUFSIZ];
+ kadm5_config_params params;
+ krb5_context context;
+
+ filename = NULL;
+ initialize_imp_error_table();
+ initialize_adb_error_table();
+ krb5_init_context(&context);
+ krb5_init_ets(context);
+
+ whoami = argv[0];
+ merge_princs = 0;
+ while(--argc) {
+ if(*++argv == NULL)
+ break;
+ if (!strcmp(*argv, "-merge_princs")) {
+ merge_princs++;
+ continue;
+ }
+ if (*argv[0] == '-') {
+ com_err(whoami, IMPORT_UNK_OPTION, NULL);
+ exit(2);
+ }
+ if(filename == NULL)
+ filename = *argv;
+ else {
+ com_err(whoami, IMPORT_UNK_OPTION, NULL);
+ exit(2);
+ }
+ }
+ if(filename != NULL) {
+ if ((fp = fopen(filename, "r")) == NULL) {
+ com_err(whoami, errno, "%s (%s)", error_message(IMPORT_OPEN_DUMP),
+ filename);
+ exit(2);
+ }
+ } else fp = stdin;
+
+ sprintf(pol_dbfile, TMP_POLICY_FMT, getpid());
+ if(access(pol_dbfile, F_OK) == 0) {
+ if(unlink(pol_dbfile) != 0)
+ return errno;
+ }
+
+ params.mask = 0;
+ if (ret = kadm5_get_config_params(context, NULL, NULL, &params,
+ &params)) {
+ com_err(whoami, ret, error_message(IMPORT_GET_PARAMS));
+ 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(IMPORT_GET_PARAMS));
+ exit(2);
+ }
+ /*
+ * This trick lets me use the temporary policy db name but the
+ * standard policy db lockfile, thus ensuring that no one changes
+ * the policy while this program is working.
+ */
+ params.admin_dbname = pol_dbfile;
+
+ if((ret = osa_adb_open_policy(&policy_db, &params)) != OSA_ADB_OK) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN));
+ exit(2);
+ }
+ if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_PERMANENT) != OSA_ADB_OK)) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_LOCK));
+ exit(2);
+ }
+ if (merge_princs) {
+ if ((ret = krb5_db_set_name(context, params.dbname)) ||
+ (ret = krb5_db_init(context))) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN));
+ exit(2);
+ }
+ }
+
+ if((ret = import_file(context, fp, merge_princs, policy_db)) !=
+ OSA_ADB_OK) {
+ unlink(pol_dbfile);
+ com_err(whoami, ret, error_message(IMPORT_IMPORT));
+ exit(2);
+ }
+
+ if (merge_princs && (ret = krb5_db_fini(context))) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_CLOSE));
+ exit(2);
+ }
+
+ kadm5_free_config_params(context, &params);
+ params.mask = 0;
+ if (ret = kadm5_get_config_params(context, NULL, NULL, &params,
+ &params)) {
+ com_err(whoami, ret, error_message(IMPORT_GET_PARAMS));
+ exit(2);
+ }
+
+ if (access(params.admin_dbname, F_OK) == 0) {
+ puts(error_message(IMPORT_WARN_DB));
+ if(!confirm()) {
+ com_err(whoami, IMPORT_FAILED, NULL);
+ exit(2);
+ }
+ }
+
+ if((ret = osa_adb_open_policy(&policy_db, &params)) != OSA_ADB_OK) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN));
+ exit(2);
+ }
+ if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_PERMANENT) != OSA_ADB_OK)) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_LOCK));
+ exit(2);
+ }
+ if (rename(pol_dbfile, params.admin_dbname) != 0) {
+ com_err(whoami, IMPORT_RENAME_FAILED, NULL);
+
+ /* WARNING! Permanent lock is not replaced. This will */
+ /* require manual administrative action! */
+ exit(2);
+ }
+ if ((ret = osa_adb_release_lock(policy_db)) != OSA_ADB_OK) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_UNLOCK));
+
+ /* WARNING! Permanent lock is not replaced. This will */
+ /* require manual administrative action! */
+ exit(2);
+ }
+ if ((ret = osa_adb_close_policy(policy_db)) != OSA_ADB_OK) {
+ com_err(whoami, ret, error_message(IMPORT_RENAME_CLOSE));
+ exit(2);
+ }
+ exit(0);
+}
diff --git a/src/kadmin/import/strtok.c b/src/kadmin/import/strtok.c
new file mode 100644
index 000000000..7b7155786
--- /dev/null
+++ b/src/kadmin/import/strtok.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ *
+ * $Log$
+ * Revision 1.2 1996/07/22 20:26:35 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.1.4.1 1996/07/18 03:02:29 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.1.2.1 1996/06/20 21:49:01 marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.1 1993/11/14 23:51:23 shanzer
+ * Initial revision
+ *
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement: ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)strtok.c 5.7 (Berkeley) 6/1/90";
+#endif /* LIBC_SCCS and not lint */
+
+#include <stddef.h>
+#include <string.h>
+
+/*
+ * Function: nstrtok
+ *
+ * Purpose: the same as strtok ... just different. does not deal with
+ * multiple tokens in row.
+ *
+ * Arguments:
+ * s (input) string to scan
+ * delim (input) list of delimiters
+ * <return value> string or null on error.
+ *
+ * Requires:
+ * nuttin
+ *
+ * Effects:
+ * sets last to string
+ *
+ * Modifies:
+ * last
+ *
+ */
+
+char *
+nstrtok(s, delim)
+ register char *s, *delim;
+{
+ register char *spanp;
+ register int c, sc;
+ char *tok;
+ static char *last;
+
+
+ if (s == NULL && (s = last) == NULL)
+ return (NULL);
+
+ /*
+ * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+ */
+#ifdef OLD
+cont:
+ c = *s++;
+ for (spanp = delim; (sc = *spanp++) != 0;) {
+ if (c == sc)
+ goto cont;
+ }
+
+ if (c == 0) { /* no non-delimiter characters */
+ last = NULL;
+ return (NULL);
+ }
+ tok = s - 1;
+#else
+ tok = s;
+#endif
+
+ /*
+ * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+ * Note that delim must have one NUL; we stop if we see that, too.
+ */
+ for (;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ last = s;
+ return (tok);
+ }
+ } while (sc != 0);
+ }
+ /* NOTREACHED */
+}
+
diff --git a/src/kadmin/import/unit-test/Makefile.ov b/src/kadmin/import/unit-test/Makefile.ov
new file mode 100644
index 000000000..c7ac33c0b
--- /dev/null
+++ b/src/kadmin/import/unit-test/Makefile.ov
@@ -0,0 +1,9 @@
+#
+# $Id$
+#
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+unit-test::
+ $(RUNTEST) IMPORT=../ovsec_adm_import --tool import
diff --git a/src/kadmin/import/unit-test/config/unix.exp b/src/kadmin/import/unit-test/config/unix.exp
new file mode 100644
index 000000000..af4ff443e
--- /dev/null
+++ b/src/kadmin/import/unit-test/config/unix.exp
@@ -0,0 +1,36 @@
+#
+# import_version -- extract and print the version number of import
+#
+
+proc import_version {} {
+ global IMPORT
+ set tmp [exec ident $IMPORT]
+ if [regexp {Header: .*import.c,v ([0-9]+\.[0-9]+)} $tmp \
+ dummy version] then {
+ clone_output "$IMPORT version $version\n"
+ } else {
+ clone_output "$IMPORT version <unknown>\n"
+ }
+}
+#
+# import_load -- loads the program
+#
+proc import_load {} {
+ #
+}
+
+# import_exit -- clean up and exit
+proc import_exit {} {
+ #
+}
+
+#
+# import_start -- start import running
+#
+proc import_start { args } {
+ global IMPORT
+ global spawn_id
+
+ verbose "% $IMPORT $args" 1
+ eval spawn $IMPORT $args
+}
diff --git a/src/kadmin/import/unit-test/helpers.exp b/src/kadmin/import/unit-test/helpers.exp
new file mode 100644
index 000000000..5905614e7
--- /dev/null
+++ b/src/kadmin/import/unit-test/helpers.exp
@@ -0,0 +1,93 @@
+#
+# $Id$
+#
+
+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
+
+ verbose "starting test: $name"
+
+ set mytest_name "$name"
+
+ eval import_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 0] == -1} {
+ fail "$name: wait returned error [lindex $ret 1]"
+ } else {
+ if { ((![string compare $status zero]) &&
+ ([lindex $ret 1] == 0)) ||
+ ((![string compare $status nonzero]) &&
+ ([lindex $ret 1] != 0)) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret 1], should be $status"
+ }
+ }
+}
+
+proc import_win { name args } {
+ mytest "$name" "$args" zero {
+ -re "Successfully imported \[0-9\]+ records."
+ { mypass }
+ eof
+ { myfail "error: $expect_out(buffer)" }
+ }
+}
+
+proc import_lose { name args error } {
+ mytest "$name" "$args" nonzero {
+ -re "Successfully imported \[0-9\]+ records."
+ { myfail "unexpected success" }
+ -re "ovsec_adm_import: .*$error"
+ { mypass }
+ eof
+ { myfail "error: $expect_out(buffer)" }
+ }
+}
+
diff --git a/src/kadmin/import/unit-test/import.0/usage.exp b/src/kadmin/import/unit-test/import.0/usage.exp
new file mode 100644
index 000000000..8e82f5a21
--- /dev/null
+++ b/src/kadmin/import/unit-test/import.0/usage.exp
@@ -0,0 +1,23 @@
+#
+# $Id$
+#
+
+set timeout 5
+
+load_lib "helpers.exp"
+
+#
+# Here are the tests
+#
+
+import_lose "C.6: input file not readable" /foo/bar/baz \
+ "No such file or directory while opening input file"
+
+import_lose "C.7: two arguments" {foo bar} \
+ "Usage:"
+
+system {rm -rf /krb5}
+
+import_lose "C.2: /krb5 doesn't exist" ./valid_export_file \
+ "Secure administration database lock file missing while importing"
+
diff --git a/src/kadmin/import/unit-test/valid_export_file b/src/kadmin/import/unit-test/valid_export_file
new file mode 100644
index 000000000..dad19c874
--- /dev/null
+++ b/src/kadmin/import/unit-test/valid_export_file
@@ -0,0 +1,27 @@
+OpenV*Secure V1.0 Tue Dec 21 14:18:18 1993
+policy test-pol 0 10000 8 2 3 1
+policy dict-only 0 0 1 1 1 1
+policy once-a-min 30 0 1 1 1 1
+policy test-pol-nopw 0 0 1 1 1 2
+princ admin/delete@SECURE-TEST.OV.COM 0 0 0 0
+princ test3@SECURE-TEST.OV.COM 0 0 0 0
+princ admin/modify@SECURE-TEST.OV.COM 0 0 0 0
+princ ovsec_adm/changepw@SECURE-TEST.OV.COM 0 0 0 0
+princ test2@SECURE-TEST.OV.COM 0 0 0 0
+princ admin/pol@SECURE-TEST.OV.COM test-pol-nopw 800 0 0 0
+princ admin/rename@SECURE-TEST.OV.COM 0 0 0 0
+princ test1@SECURE-TEST.OV.COM 0 0 0 0
+princ krbtgt/SECURE-TEST.OV.COM@SECURE-TEST.OV.COM 0 0 0 0
+princ pol3@SECURE-TEST.OV.COM dict-only 800 0 0 0
+princ admin/get-pol@SECURE-TEST.OV.COM test-pol-nopw 800 0 0 0
+princ admin/none@SECURE-TEST.OV.COM 0 0 0 0
+princ testuser@SECURE-TEST.OV.COM 0 0 0 0
+princ pol2@SECURE-TEST.OV.COM once-a-min 800 0 0 0
+princ K/M@SECURE-TEST.OV.COM 0 0 0 0
+princ pol1@SECURE-TEST.OV.COM test-pol 800 0 0 0
+princ ovsec_adm/history@SECURE-TEST.OV.COM 0 0 0 0
+princ admin@SECURE-TEST.OV.COM 0 0 0 0
+princ admin/add@SECURE-TEST.OV.COM 0 0 0 0
+princ admin/get@SECURE-TEST.OV.COM 0 0 0 0
+princ ovsec_adm/admin@SECURE-TEST.OV.COM 0 0 0 0
+End of Database 25 records
diff --git a/src/kadmin/kdbkeys/ChangeLog b/src/kadmin/kdbkeys/ChangeLog
new file mode 100644
index 000000000..206aea9be
--- /dev/null
+++ b/src/kadmin/kdbkeys/ChangeLog
@@ -0,0 +1,16 @@
+Thu Jul 18 19:44:10 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Wed Jul 10 01:00:49 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in, configure.in: added autoconf support
+
+ * kdbkeys.c: rename <ovsec_adm/foo.h> to <kadm5/foo.h>, rename
+ <krb5/krb5.h to <krb5.h>
+
+Tue Jul 9 13:25:00 1996 Barry Jaspan <bjaspan@mit.edu>
+
+ * do-test.pl: rewrite to use kdb5_util instead of kdbkeys
+
+
diff --git a/src/kadmin/kdbkeys/Makefile.in b/src/kadmin/kdbkeys/Makefile.in
new file mode 100644
index 000000000..5672bd0bf
--- /dev/null
+++ b/src/kadmin/kdbkeys/Makefile.in
@@ -0,0 +1,15 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+PROG = kdbkeys
+OBJS = kdbkeys.o
+
+all:: $(PROG)
+
+$(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/kdbkeys/Makefile.ov b/src/kadmin/kdbkeys/Makefile.ov
new file mode 100644
index 000000000..72c11695a
--- /dev/null
+++ b/src/kadmin/kdbkeys/Makefile.ov
@@ -0,0 +1,21 @@
+#
+# $Id$
+#
+
+TOP = ..
+include $(TOP)/config.mk/template
+
+# Need $(STOP_SERVERS_LOCAL) because any running servers need to be
+# killed so that they won't keep the database open so that we can't
+# blow it away.
+
+unit-test:: unit-test-setup unit-test-body unit-test-cleanup
+
+unit-test-setup::
+ $(START_SERVERS_LOCAL)
+ $(STOP_SERVERS_LOCAL)
+
+unit-test-body::
+ $(PERL) ./do-test.pl 10
+
+unit-test-cleanup::
diff --git a/src/kadmin/kdbkeys/configure.in b/src/kadmin/kdbkeys/configure.in
new file mode 100644
index 000000000..6c027fc27
--- /dev/null
+++ b/src/kadmin/kdbkeys/configure.in
@@ -0,0 +1,11 @@
+AC_INIT(kdbkeys.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_PROG_AWK
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/kdbkeys/do-test.pl b/src/kadmin/kdbkeys/do-test.pl
new file mode 100644
index 000000000..7acb425f7
--- /dev/null
+++ b/src/kadmin/kdbkeys/do-test.pl
@@ -0,0 +1,56 @@
+#!/afs/athena/contrib/perl/p
+
+#
+# $Id$
+#
+
+$debug = $ARGV[1] || $ENV{'VERBOSE_TEST'};
+
+die "Need a number.\n" if !$ARGV[0];
+
+die "Neither \$TOP nor \$TESTDIR is set.\n"
+ if (! ($ENV{'TOP'} || $ENV{'TESTDIR'}));
+
+$TESTDIR = ($ENV{'TESTDIR'} || "$ENV{'TOP'}/testing");
+$INITDB = ($ENV{'INITDB'} || "$TESTDIR/scripts/init_db");
+
+for ($i=0; $i<$ARGV[0]; $i++) {
+ print "Trial $i\n" if $debug;
+
+ system("$INITDB > /dev/null 2>&1") &&
+ die "Error in init_db\n";
+
+ open(KEYS,"../dbutil/kdb5_util -R dump_db|") ||
+ die "Couldn't run kdb5_util: $!\n";
+ chop($header = <KEYS>);
+ if ($header ne "kdb5_util load_dump version 4") {
+ die "Cannot operate on dump version \"$header\"; version 4 required.";
+ }
+ while(<KEYS>) {
+ next if ((!/^princ.*kadmin\//) && (!/^princ.*krbtgt/));
+
+ print if $debug > 1;
+
+ split;
+
+ $princ = $_[6];
+ $nkeys = $_[4];
+ $ntls = $_[3];
+ print "$princ: nkeys $nkeys, ntls $ntls\n" if $debug;
+ for ($j = 15 + $ntls*3; $nkeys > 0; $nkeys--) {
+ $ver = $_[$j++];
+ $kvno = $_[$j++];
+ $keytype = $_[$j++];
+ $keylen = $_[$j++];
+ $keydata = $_[$j++];
+ $j += 3 if ($ver > 1);
+
+ print "$princ, ver $ver, kvno $kvno, type $keytype, len $keylen, "
+ . "data $keydata\n" if $debug;
+
+ die "Duplicated key $princ = $keydata\n" if
+ $keys{$keydata}++;
+ }
+ }
+ close(KEYS);
+}
diff --git a/src/kadmin/keytab/ChangeLog b/src/kadmin/keytab/ChangeLog
new file mode 100644
index 000000000..5aeb2a44e
--- /dev/null
+++ b/src/kadmin/keytab/ChangeLog
@@ -0,0 +1,13 @@
+Thu Jul 18 20:41:14 1996 Marc Horowitz <marc@mit.edu>
+
+ * keytab.c (etype_string): ifdef'd out reference to des3
+
+Mon Jul 15 16:59:35 1996 Marc Horowitz <marc@mit.edu>
+
+ * keytab.c (main): the default keytab name logic was inappropriate
+ for beta6, since it ignores the env vars, etc. This version still
+ has the problem that the default keytab probably isn't writeable.
+
+Wed Jul 10 01:27:44 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in, configure.in: added autoconf support
diff --git a/src/kadmin/keytab/Makefile.in b/src/kadmin/keytab/Makefile.in
new file mode 100644
index 000000000..74426a51f
--- /dev/null
+++ b/src/kadmin/keytab/Makefile.in
@@ -0,0 +1,19 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+PROG = kadm5_keytab
+OBJS = keytab.o
+
+all:: $(PROG).local $(PROG)
+
+$(PROG).local: $(OBJS) $(SRVDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS)
+
+$(PROG): $(OBJS) $(CLNTDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS)
+
+install::
+ $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean::
+ $(RM) $(PROG).local $(PROG) $(OBJS)
diff --git a/src/kadmin/keytab/Makefile.ov b/src/kadmin/keytab/Makefile.ov
new file mode 100644
index 000000000..15cd6d5d9
--- /dev/null
+++ b/src/kadmin/keytab/Makefile.ov
@@ -0,0 +1,30 @@
+#
+# Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+#
+# $Id$
+# $Source$
+#
+
+TOP = ..
+include $(TOP)/config.mk/template
+
+SRCS = keytab.c
+OBJS = keytab.o
+PROG = ovsec_edit_keytab
+
+LIBS = $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBGSSAPI_KRB5) $(LIBRPCLIB) \
+ $(LIBDYN) $(LIBDB) $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) \
+ $(BSDLIB) $(NETLIB)
+
+expand InstallAdmin
+
+PROG = ovsec_edit_keytab.local
+LIBS = $(LIBADMSRV) $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5) $(NDBMLIB) \
+ $(LIBRPCLIB) $(LIBDYN) $(LIBDB) $(LIBKRB5) $(LIBCRYPTO) \
+ $(LIBCOM_ERR) $(BSDLIB) $(NETLIB)
+
+expand InstallAdmin
+
+SUBDIRS = unit-test
+expand SubdirTarget
+
diff --git a/src/kadmin/keytab/configure.in b/src/kadmin/keytab/configure.in
new file mode 100644
index 000000000..0e043f7e5
--- /dev/null
+++ b/src/kadmin/keytab/configure.in
@@ -0,0 +1,12 @@
+AC_INIT(keytab.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+USE_KADMCLNT_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/keytab/keytab.c b/src/kadmin/keytab/keytab.c
new file mode 100644
index 000000000..2561252ef
--- /dev/null
+++ b/src/kadmin/keytab/keytab.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <krb5.h>
+#include <k5-int.h>
+#include <kadm5/admin.h>
+
+int add_principal(char *keytab_str, krb5_keytab keytab, char *me_str,
+ char *princ_str, int create);
+int remove_principal(char *keytab_str, krb5_keytab keytab, char
+ *princ_str, char *kvno_str);
+static char *etype_string(krb5_enctype enctype);
+
+krb5_context context;
+char *whoami;
+int quiet;
+
+void usage()
+{
+ fprintf(stderr, "Usage: ovsec_edit_keytab [-k[eytab] keytab] [-q] cmd\n");
+ fprintf(stderr, " cmds are:\t-a[dd] [-c[reate] [-p principal] principal\n");
+ fprintf(stderr, "\t\t-c[hange] [-p principal] principal\n");
+ fprintf(stderr, "\t\t-r[emove] principal [kvno|\"all\"|\"old\"]\n");
+ exit(1);
+}
+
+main(int argc0, char **argv0)
+{
+ extern krb5_kt_ops krb5_ktf_writable_ops;
+ krb5_keytab keytab = 0;
+ char *me_str, *princ_str, *keytab_str, *kvno_str;
+ char keytab_buf[1024];
+ int argc, code, did_something, create;
+ char **argv;
+
+ whoami = strrchr(argv0[0], '/') ? strrchr(argv0[0], '/') + 1 : argv0[0];
+
+ if (code = krb5_init_context(&context)) {
+ com_err(whoami, code, "while initializing krb5 context");
+ exit(1);
+ }
+
+ krb5_init_ets(context);
+
+ /* register the WRFILE keytab type and set it as the default */
+ if (code = krb5_kt_register(context, &krb5_ktf_writable_ops)) {
+ com_err(whoami, code,
+ "while registering writable key table functions");
+ exit(1);
+ }
+
+ /* process non-action arguments first */
+ argc = argc0-1;
+ argv = argv0+1;
+ while (argc) {
+ if (strncmp(*argv, "-k", 2) == 0) {
+ argc--; argv++;
+ if (!argc) usage();
+
+ if (keytab == NULL) {
+ if (strchr(*argv, ':') != NULL) {
+ keytab_str = strdup(*argv);
+ if (keytab_str == NULL) {
+ com_err(whoami, ENOMEM,
+ "while creating keytab name");
+ exit(1);
+ }
+ } else {
+ keytab_str = (char *)
+ malloc(strlen("WRFILE:")+strlen(*argv)+1);
+ if (keytab_str == NULL) {
+ com_err(whoami, ENOMEM,
+ "while creating keytab name");
+ exit(1);
+ }
+ sprintf(keytab_str, "WRFILE:%s", *argv);
+ }
+
+ code = krb5_kt_resolve(context, keytab_str, &keytab);
+ if (code != 0) {
+ com_err(whoami, code, "while resolving keytab %s",
+ keytab_str);
+ exit(1);
+ }
+ } else {
+ usage();
+ }
+ } else if (strcmp(*argv, "-q") == 0) {
+ quiet++;
+ }
+ /* otherwise ignore the argument, for now */
+ argc--; argv++;
+ }
+
+ if (keytab == NULL) {
+ code = krb5_kt_default(context, &keytab);
+ if (code != 0) {
+ com_err(whoami, code, "while opening default keytab");
+ exit(1);
+ }
+ code = krb5_kt_get_name(context, keytab,
+ keytab_buf, sizeof(keytab_buf));
+ keytab_str = keytab_buf;
+ }
+
+ argc = argc0-1;
+ argv = argv0+1;
+
+ did_something = 0;
+
+ /* now process the action arguments */
+ while (argc) {
+ if (strncmp(*argv, "-k", 2) == 0) {
+ /* if there is no keytab argument the previous loop */
+ /* would have called usage(), so just skip it */
+ argc--; argv++;
+ } else if (strcmp(*argv, "-q") == 0) {
+ /* skip it */
+ } else if (strncmp(*argv, "-a", 2) == 0 ||
+ strncmp(*argv, "-c", 2) == 0) {
+ did_something++;
+
+ argc--; argv++;
+ if (!argc) usage();
+
+ me_str = NULL;
+ create = 0;
+ while (argc) {
+ if (strcmp(*argv, "-p") == 0) {
+ argc--; argv++;
+ if (argc < 1) usage();
+
+ me_str = *argv;
+ } else if (strncmp(*argv, "-c", 2) == 0) {
+ create++;
+ } else
+ break;
+ argc--; argv++;
+ }
+ if (argc != 1) usage();
+
+ code = add_principal(keytab_str, keytab, me_str ? me_str :
+ *argv, *argv, create);
+ break;
+ } else if (strncmp(*argv, "-r", 2) == 0) {
+ did_something++;
+
+ argc--; argv++;
+ if (!argc) usage();
+ princ_str = *argv;
+ if (argc > 0) {
+ argc--;
+ argv++;
+ kvno_str = *argv;
+ } else
+ kvno_str = NULL;
+
+ code = remove_principal(keytab_str, keytab, princ_str,
+ kvno_str);
+ break;
+ } else {
+ fprintf(stderr, "%s: Unknown command line option %s.\n",
+ whoami, *argv);
+ usage();
+ }
+
+ argc--; argv++;
+ }
+
+ /* argv ends up pointing at the last recognized argument */
+ if (!did_something || argc > 1)
+ usage();
+
+ /* use argc as temp */
+ argc = krb5_kt_close(context, keytab);
+ if (argc != 0) {
+ com_err(whoami, argc, "while closing keytab");
+ code = argc;
+ }
+
+ free(keytab_str);
+
+ return (code != 0);
+}
+
+int add_principal(char *keytab_str, krb5_keytab keytab, char *me_str,
+ char *princ_str, int create)
+{
+ kadm5_principal_ent_rec princ_rec;
+ krb5_principal me, princ;
+ krb5_keytab_entry new_entry;
+ krb5_keyblock *keys;
+ void *handle;
+ int code, code2, mask, nkeys, i;
+
+ (void) memset((char *)&princ_rec, 0, sizeof(princ_rec));
+
+ me = princ = NULL;
+ handle = NULL;
+ keys = NULL;
+ nkeys = 0;
+
+ code = krb5_parse_name(context, me_str, &me);
+ if (code != 0) {
+ com_err(whoami, code, "while parsing -p principal name %s",
+ me_str);
+ goto cleanup;
+ }
+
+ code = krb5_parse_name(context, princ_str, &princ);
+ if (code != 0) {
+ com_err(whoami, code, "while parsing -add principal name %s",
+ princ_str);
+ goto cleanup;
+ }
+
+ /* first try using the keytab */
+ code = kadm5_init_with_skey(me_str, keytab_str,
+ KADM5_ADMIN_SERVICE,
+ NULL, /* default configuration */
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2, &handle);
+ if (code != 0) {
+ /* KRB5_KT_NOTFOUND and ENOENT are not "errors" because this */
+ /* program does not require the keytab entry to exist */
+ if (code != KRB5_KT_NOTFOUND && code != ENOENT) {
+ if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN)
+ fprintf(stderr, "%s: Principal %s does not exist.\n",
+ whoami, me_str);
+ else
+ com_err(whoami, code, "while authenticating as principal "
+ "%s from keytab", me_str);
+ }
+
+ code2 = kadm5_init_with_password(me_str, NULL,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &handle);
+ if (code2 != 0) {
+ if (code2 != code) /* don't dup error messages */ {
+ com_err(whoami, code2, "while authenticating as "
+ "principal %s from password", me_str);
+ }
+ goto cleanup;
+ }
+ }
+
+ if (create) {
+ /* always try to create and just ignore dup errors because it */
+ /* reduces duplicate code... and how often will this happen? */
+
+ /* be sure to create the principal with the secure sequence */
+ /* of events as specified in the functional spec */
+
+ princ_rec.principal = princ;
+ princ_rec.attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+ mask = KADM5_PRINCIPAL | KADM5_ATTRIBUTES;
+ code = kadm5_create_principal(handle, &princ_rec,
+ mask, "dummy");
+ if (code == KADM5_DUP) {
+ printf("%s: Principal %s already exists.\n",
+ whoami, princ_str);
+ } else if (code != 0) {
+ if (code == KADM5_AUTH_ADD) {
+ fprintf(stderr, "%s: Operation requires "
+ "``add'' and ``modify'' privileges while creating "
+ "principal.\n", whoami);
+ } else {
+ com_err(whoami, code, "while creating "
+ "principal %s.", princ_str);
+ }
+ goto cleanup;
+ } else if (!quiet)
+ printf("%s: Created principal %s.\n", whoami, princ_str);
+ }
+
+ code = kadm5_randkey_principal(handle, princ, &keys, &nkeys);
+ if (code != 0) {
+ if (code == KADM5_UNK_PRINC) {
+ fprintf(stderr, "%s: Principal %s does not exist.\n",
+ whoami, princ_str);
+ } else
+ com_err(whoami, code, "while changing %s's key",
+ princ_str);
+ goto cleanup;
+ }
+
+ code = kadm5_get_principal(handle, princ, &princ_rec,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ if (code != 0) {
+ com_err(whoami, code, "while retrieving principal");
+ goto cleanup;
+ }
+
+ if (create) {
+ /* complete the secure principal-creation sequence */
+ princ_rec.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX;
+ mask = KADM5_ATTRIBUTES;
+ code = kadm5_modify_principal(handle, &princ_rec, mask);
+ if (code != 0) {
+ if (code == KADM5_AUTH_ADD) {
+ fprintf(stderr, "%s: Operation requires "
+ "``add'' and ``modify'' privileges while creating "
+ "principal.\n", whoami);
+ } else
+ com_err(whoami, code, "while modifying newly created "
+ "principal");
+ (void) kadm5_free_principal_ent(handle, &princ_rec);
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; i < nkeys; i++) {
+ memset((char *) &new_entry, 0, sizeof(new_entry));
+ new_entry.principal = princ;
+ new_entry.key = keys[i];
+ new_entry.vno = princ_rec.kvno;
+
+ code = krb5_kt_add_entry(context, keytab, &new_entry);
+ if (code != 0) {
+ com_err(whoami, code, "while adding key to keytab");
+ (void) kadm5_free_principal_ent(handle, &princ_rec);
+ goto cleanup;
+ }
+
+ if (!quiet)
+ printf("%s: Entry for principal %s with kvno %d, "
+ "encryption type %s added to keytab %s.\n",
+ whoami, princ_str, princ_rec.kvno,
+ etype_string(keys[i].enctype), keytab_str);
+ }
+
+ code = kadm5_free_principal_ent(handle, &princ_rec);
+ if (code != 0) {
+ com_err(whoami, code, "while freeing principal entry");
+ goto cleanup;
+ }
+
+cleanup:
+ if (handle) {
+ code2 = kadm5_destroy(handle);
+ if (code2 != 0) {
+ com_err(whoami, code2, "while closing admin server connection");
+ }
+ }
+ if (nkeys) {
+ for (i = 0; i < nkeys; i++)
+ krb5_free_keyblock(context, &keys[i]);
+ free(keys);
+ }
+ if (me)
+ krb5_free_principal(context, me);
+ if (princ)
+ krb5_free_principal(context, princ);
+
+ return code;
+}
+
+int remove_principal(char *keytab_str, krb5_keytab keytab, char
+ *princ_str, char *kvno_str)
+{
+ krb5_principal princ;
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
+ enum { UNDEF, SPEC, HIGH, ALL, OLD } mode;
+ int code, kvno, did_something;
+
+ code = krb5_parse_name(context, princ_str, &princ);
+ if (code != 0) {
+ com_err(whoami, code, "while parsing principal name %s",
+ princ_str);
+ return code;
+ }
+
+ mode = UNDEF;
+ if (kvno_str == NULL) {
+ mode = HIGH;
+ kvno = 0;
+ } else if (strcmp(kvno_str, "all") == 0) {
+ mode = ALL;
+ kvno = 0;
+ } else if (strcmp(kvno_str, "old") == 0) {
+ mode = OLD;
+ kvno = 0;
+ } else {
+ mode = SPEC;
+ kvno = atoi(kvno_str);
+ }
+
+ /* kvno is set to specified value for SPEC, 0 otherwise */
+ code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry);
+ if (code != 0) {
+ if (code == ENOENT) {
+ fprintf(stderr, "%s: Keytab %s does not exist.\n",
+ whoami, keytab_str);
+ } else if (code == KRB5_KT_NOTFOUND) {
+ if (mode != SPEC)
+ fprintf(stderr, "%s: No entry for principal "
+ "%s exists in keytab %s\n",
+ whoami, princ_str, keytab_str);
+ else
+ fprintf(stderr, "%s: No entry for principal "
+ "%s with kvno %d exists in keytab "
+ "%s.\n", whoami, princ_str, kvno,
+ keytab_str);
+ } else {
+ com_err(whoami, code, "while retrieving highest kvno "
+ "from keytab");
+ }
+ return code;
+ }
+
+ /* set kvno to spec'ed value for SPEC, highest kvno otherwise */
+ kvno = entry.vno;
+ krb5_kt_free_entry(context, &entry);
+
+ code = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (code != 0) {
+ com_err(whoami, code, "while starting keytab scan");
+ return code;
+ }
+
+ did_something = 0;
+ while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) {
+ if (krb5_principal_compare(context, princ, entry.principal) &&
+ ((mode == ALL) ||
+ (mode == SPEC && entry.vno == kvno) ||
+ (mode == OLD && entry.vno != kvno) ||
+ (mode == HIGH && entry.vno == kvno))) {
+
+ /*
+ * Ack! What a kludge... the scanning functions lock
+ * the keytab so entries cannot be removed while they
+ * are operating.
+ */
+ code = krb5_kt_end_seq_get(context, keytab, &cursor);
+ if (code != 0) {
+ com_err(whoami, code, "while temporarily ending "
+ "keytab scan");
+ return code;
+ }
+ code = krb5_kt_remove_entry(context, keytab, &entry);
+ if (code != 0) {
+ com_err(whoami, code, "while deleting entry from keytab");
+ return code;
+ }
+ code = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (code != 0) {
+ com_err(whoami, code, "while restarting keytab scan");
+ return code;
+ }
+
+ did_something++;
+ if (!quiet)
+ printf("%s: Entry for principal %s with kvno %d "
+ "removed from keytab %s.\n", whoami,
+ princ_str, entry.vno, keytab_str);
+ }
+ krb5_kt_free_entry(context, &entry);
+ }
+ if (code && code != KRB5_KT_END) {
+ com_err(whoami, code, "while scanning keytab");
+ return code;
+ }
+ if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) {
+ com_err(whoami, code, "while ending keytab scan");
+ return code;
+ }
+
+ /*
+ * If !did_someting then mode must be OLD or we would have
+ * already returned with an error. But check it anyway just to
+ * prevent unexpected error messages...
+ */
+ if (!did_something && mode == OLD) {
+ fprintf(stderr, "%s: There is only one entry for principal "
+ "%s in keytab %s\n", whoami, princ_str, keytab_str);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * etype_string(enctype): return a string representation of the
+ * encryption type. XXX copied from klist.c; this should be a
+ * library function, or perhaps just #defines
+ */
+static char *etype_string(enctype)
+ krb5_enctype enctype;
+{
+ static char buf[12];
+
+ switch (enctype) {
+ case ENCTYPE_DES_CBC_CRC:
+ return "DES-CBC-CRC";
+ break;
+ case ENCTYPE_DES_CBC_MD4:
+ return "DES-CBC-MD4";
+ break;
+ case ENCTYPE_DES_CBC_MD5:
+ return "DES-CBC-MD5";
+ break;
+#if 0
+ case ENCTYPE_DES3_CBC_MD5:
+ return "DES3-CBC-MD5";
+ break;
+#endif
+ default:
+ sprintf(buf, "etype %d", enctype);
+ return buf;
+ break;
+ }
+}
diff --git a/src/kadmin/keytab/unit-test/ChangeLog b/src/kadmin/keytab/unit-test/ChangeLog
new file mode 100644
index 000000000..69df6be92
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/ChangeLog
@@ -0,0 +1,4 @@
+Mon Jul 15 17:03:28 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.ov (unit-test-body): ovsec_adm_keytab is now
+ kadm5_keytab
diff --git a/src/kadmin/keytab/unit-test/Makefile.ov b/src/kadmin/keytab/unit-test/Makefile.ov
new file mode 100644
index 000000000..1b3366d8b
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/Makefile.ov
@@ -0,0 +1,21 @@
+#
+# $Id$
+#
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+unit-test:: unit-test-setup unit-test-body unit-test-cleanup
+
+unit-test-body::
+ $(CLNTTCL) ./del-princs.tcl
+ $(RUNTEST) KEYTAB=../kadm5_keytab \
+ KLIST=../../../clients/klist/klist \
+ QUALNAME=../../testing/scripts/qualname --tool keytab
+
+unit-test-setup::
+ $(START_SERVERS)
+ $(CLNTTCL) ./add-princs.tcl
+
+unit-test-cleanup::
+ $(STOP_SERVERS)
diff --git a/src/kadmin/keytab/unit-test/add-princs.tcl b/src/kadmin/keytab/unit-test/add-princs.tcl
new file mode 100644
index 000000000..247a4382b
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/add-princs.tcl
@@ -0,0 +1,12 @@
+source $env(TCLUTIL)
+
+ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \
+ $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle
+
+ovsec_kadm_create_principal $server_handle [simple_principal kttest1] \
+ {OVSEC_KADM_PRINCIPAL} kttest1
+
+ovsec_kadm_create_principal $server_handle [simple_principal kttest2] \
+ {OVSEC_KADM_PRINCIPAL} kttest2
+
+ovsec_kadm_destroy $server_handle
diff --git a/src/kadmin/keytab/unit-test/config/unix.exp b/src/kadmin/keytab/unit-test/config/unix.exp
new file mode 100644
index 000000000..bc07d4f6e
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/config/unix.exp
@@ -0,0 +1,46 @@
+set klist $KLIST
+set hostname hostname
+set qualname $QUALNAME
+
+set rm /bin/rm
+
+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 keytab_version {} {
+ global KEYTAB
+ puts "$KEYTAB version unknown"
+}
+
+proc keytab_load {} {
+ #
+}
+
+proc keytab_exit {} {
+ #
+}
+
+proc keytab_start { args } {
+ global KEYTAB
+ global spawn_id
+
+ verbose "% $KEYTAB $args" 1
+ eval spawn $KEYTAB $args
+}
+
+
diff --git a/src/kadmin/keytab/unit-test/del-princs.tcl b/src/kadmin/keytab/unit-test/del-princs.tcl
new file mode 100644
index 000000000..9b9cab5c4
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/del-princs.tcl
@@ -0,0 +1,24 @@
+source $env(TCLUTIL)
+
+proc check_err {error} {
+ if {! [string match {*OVSEC_KADM_UNK_PRINC*} $error]} {
+ error $error
+ }
+}
+
+proc delprinc {princ} {
+ global server_handle
+
+ catch {ovsec_kadm_delete_principal $server_handle $princ}
+ if {[info exists errorInfo]} {
+ check_err $errorInfo
+ }
+}
+
+ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \
+ $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle
+
+delprinc dne1
+delprinc dne2
+
+ovsec_kadm_destroy $server_handle
diff --git a/src/kadmin/keytab/unit-test/helpers.exp b/src/kadmin/keytab/unit-test/helpers.exp
new file mode 100644
index 000000000..a9f7ca402
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/helpers.exp
@@ -0,0 +1,132 @@
+#
+# $Id$
+#
+
+#
+# Create a keytab "name" with an entry for each element in the array
+# "entries". If "name" already exists it is destroyed. Connections
+# to the admin server are made as the principal "admin" with the
+# password "password".
+#
+proc setup_keytab { testname ktname admin password entries } {
+ global klist rm
+ global wait_error_index wait_status_index
+ global verbose
+
+ verbose "setting up test: $testname" 1
+
+ if {[regexp {(.+):(.+)} $ktname dummy type filename] == 0} {
+ set filename $ktname
+ }
+
+ if {[file exists $filename] && [catch "exec $rm $filename"] != 0} {
+ error "$testname: cannot delete keytab file $filename";
+ }
+
+ if {$type == "WRFILE"} {
+ set type "FILE"
+ }
+
+ foreach entry $entries {
+ keytab_run "$testname setup" \
+ "-k $ktname -a -p $admin $entry" 0 {
+ "Enter password:" {
+ send "$password\n"
+ }
+ }
+ # if "Enter password:" needs to be optional:
+ # { timeout { } }
+ }
+
+ if {$verbose > 1} {
+ if {[file exists $filename]} {
+ puts "% exec $klist -k $type:$filename\n"
+ if {[catch "exec $klist -k $type:$filename"] != 0} {
+ error "$testname: $klist failed"
+ }
+ }
+ }
+}
+
+#
+# Run $KEYTAB with args ktargs. Each element of args is treated as an
+# expect block for the process, in turn. If all elements match and
+# then eof occurs with exit status status, the test passes; otherwise
+# it fails.
+#
+proc keytab_run { testname ktargs status args } {
+ global spawn_id timeout
+ global wait_error_index wait_status_index
+ global progname
+
+ verbose "running $progname for test: $testname" 2
+
+ eval keytab_start $ktargs
+
+ # wait for eof after exps
+ lappend args { eof { verbose $expect_out(buffer) 2 } }
+
+ foreach exp $args {
+ uplevel 1 "expect {
+ $exp
+ timeout { close; fail \"$testname: timeout\"; return }
+ eof { fail \"$testname: eof before expected message\"; return }
+ }"
+ }
+
+ set ret [wait]
+ verbose "% Exit $ret" 2
+
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail "$testname: wait returned error [lindex $ret $wait_errno_index]"
+ } else {
+ if { [lindex $ret $wait_status_index] == $status ||
+ (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } {
+ pass "$testname"
+ } else {
+ fail "$testname: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ }
+ }
+}
+
+
+proc klist_check { testname ktname args } {
+ global klist
+
+ if {[regexp {(.+):(.+)} $ktname dummy type filename] == 0} {
+ set filename $ktname
+ }
+
+ set lines [list "^Keytab name: (WR)?FILE:$filename" \
+ "^KVNO Principal" "^---- -------"]
+
+ foreach entry $args {
+ if {[lindex $entry 1] == 0} {
+ set line "^ *\[0-9\]+ [lindex $entry 0]"
+ } else {
+ set line "^ *[lindex $entry 1] [lindex $entry 0]"
+ }
+ lappend lines $line
+ }
+
+ set kl [open "|$klist -k FILE:$filename" r]
+
+ while {[gets $kl line] >= 0} {
+ if {([llength $lines] == 0) ||
+ ([regexp [lindex $lines 0] $line] == 0)} {
+ fail "$testname: klist check: \
+ [lindex $lines 0] does not match $line"
+ }
+ set lines [lrange $lines 1 end]
+ }
+ if {[catch "close $kl" msg] != 0} {
+ fail "$testname: klist: $msg"
+ return
+ }
+
+ if {[llength $lines] == 0} {
+ pass "$testname: klist check"
+ } else {
+ fail "$testname: klist check: too few entries in keytab"
+ }
+}
diff --git a/src/kadmin/keytab/unit-test/keytab.0/ChangeLog b/src/kadmin/keytab/unit-test/keytab.0/ChangeLog
new file mode 100644
index 000000000..1b807089a
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/keytab.0/ChangeLog
@@ -0,0 +1,4 @@
+Mon Jul 15 17:09:01 1996 Marc Horowitz <marc@mit.edu>
+
+ * keytab-spec.exp: use /krb5/v5srvtab, since /krb5 is the test
+ dir.
diff --git a/src/kadmin/keytab/unit-test/keytab.0/adding.exp b/src/kadmin/keytab/unit-test/keytab.0/adding.exp
new file mode 100644
index 000000000..159ac638b
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/keytab.0/adding.exp
@@ -0,0 +1,119 @@
+#
+# $Id$
+#
+
+set timeout 20
+
+load_lib "helpers.exp"
+
+if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} {
+ error "cannot set progname from $KEYTAB"
+}
+
+set ktscratch_file /tmp/keytab_test
+set ktscratch WRFILE:$ktscratch_file
+set ktarg "-k $ktscratch"
+set add_admin "$ktarg -a -p admin"
+set pwprompt { "Enter password:" { send "admin\n" } }
+
+setup_keytab "A1,A6" $ktscratch admin admin {}
+keytab_run "A1,A6" "$add_admin kttest1" 0 "$pwprompt" {
+ -re \
+ "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." {}
+}
+klist_check "A1,A6" $ktscratch {kttest1 0}
+
+setup_keytab "A2" $ktscratch admin admin {}
+keytab_run "A2" "-q $add_admin kttest1" 0 "$pwprompt" {
+ -re
+ "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." {
+ close; fail "A2: -q"; return }
+ eof { break }
+}
+klist_check "A2" $ktscratch {kttest1 0}
+
+setup_keytab "A3" $ktscratch admin admin {kttest1 kttest1}
+set kvno_key1 [exec $klist -k -K FILE:$ktscratch_file | \
+ awk "/kttest1/ && NR==4 {print \$1 \" \" \$3}"]
+set kvno_key2 [exec $klist -k -K FILE:$ktscratch_file | \
+ awk "/kttest1/ && NR==5 {print \$1 \" \" \$3}"]
+if {[lindex $kvno_key1 1] == [lindex $kvno_key2 1]} {
+ fail "A3: key compare"
+} else {
+ klist_check "A3" $ktscratch "kttest1 [lindex $kvno_key1 0]" \
+ "kttest1 [expr [lindex $kvno_key1 0]+1]"
+}
+
+setup_keytab "A7" $ktscratch admin admin {}
+keytab_run "A7" "$add_admin does-not-exist" 1 "$pwprompt" {
+ -re
+ "$progname: Principal does-not-exist does not exist." {}
+}
+
+setup_keytab "A4,A10,A11,A13,A15" $ktscratch admin admin { kttest1 kttest2}
+keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest1" 0
+keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest2" 0
+keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest1" 0
+keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest2" 0
+klist_check "A4,A10,A11,A13,A15" $ktscratch { kttest1 0 } \
+ { kttest2 0 } { kttest1 0 } { kttest2 0 } { kttest1 0 } { kttest2 0 }
+
+setup_keytab "A12" $ktscratch admin admin {}
+keytab_run "A12" "$ktarg -a -p admin/get-add kttest1" 1 "$pwprompt" {
+ "Operation requires ``change-password'' privilege while changing" {}
+}
+
+setup_keytab "A14" $ktscratch admin admin {}
+# assume the exit status won't be -1, so if the password prompt
+# doesn't appear the test will fail
+keytab_run "A14" "$ktarg -a kttest1" -1 {
+ "Enter password:" { send "\n"; expect eof; pass "A14: no -p"; return }
+}
+
+setup_keytab "A16" $ktscratch admin admin {}
+keytab_run "A16" "$ktarg -a -p does-not-exist kttest1" 1 {
+ "$progname: Principal does-not-exist does not exist." {}
+}
+
+setup_keytab "A17" $ktscratch admin admin { kttest1 kttest2}
+keytab_run "A17" "$ktarg -a -p kttest2 kttest1" 1 {
+ "Enter password:" { close; fail "A17: no password prompt"; return }
+ default { break }
+}
+
+setup_keytab "A18" $ktscratch admin admin { }
+keytab_run "A18" "$ktarg -a -c -p admin dne1" 0 "$pwprompt" {
+ "$progname: Created principal dne1" {}
+} {
+ -re
+ "$progname: Entry.*dne1.*kvno \[0-9\]+.*keytab $ktscratch." {}
+}
+klist_check "A18" $ktscratch {dne1 0}
+
+setup_keytab "A19" $ktscratch admin admin {}
+keytab_run "A9" "-q $ktarg -a -c -p admin dne2" 0 "$pwprompt" {
+ "$progname: Created principal dne2" { close; fail "A19: -q"; return }
+ eof { break }
+}
+klist_check "A19" $ktscratch {dne2 0}
+
+setup_keytab "A21" $ktscratch admin admin {}
+keytab_run "A21" "$ktarg -a -c -p admin kttest1" 0 "$pwprompt" {
+ "$progname: Principal kttest1 already exists." {}
+} {
+ -re \
+ "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." {}
+}
+klist_check "A21" $ktscratch {kttest1 0}
+
+setup_keytab "A22" $ktscratch admin admin {}
+keytab_run "A22" "$ktarg -a -c -p admin/modify kttest1" 1 "$pwprompt" {
+ "Operation requires ``add'' and ``modify'' privileges while creating" {}
+}
+
+setup_keytab "A23" $ktscratch admin admin {}
+keytab_run "A23" "$ktarg -a -c -p admin/get kttest1" 1 "$pwprompt" {
+ "Operation requires ``add'' and ``modify'' privileges while creating" {}
+}
+
+exec rm -f $ktscratch_file
diff --git a/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp b/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp
new file mode 100644
index 000000000..8da80eb69
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp
@@ -0,0 +1,47 @@
+#
+# $Id$
+#
+
+set timeout 10
+
+load_lib "helpers.exp"
+
+if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} {
+ error "cannot set progname from $KEYTAB"
+}
+
+set hname [exec $hostname]
+set qname [exec $qualname $hname]
+
+set testfile1 /tmp/keytab-test1
+set testfile2 /tmp/keytab-test2
+
+if {[info exists env(KRB5_KTNAME)]} {
+ set ktname_orig $env(KRB5_KTNAME)
+ unset env(KRB5_KTNAME)
+}
+
+setup_keytab "K1" WRFILE:/krb5/v5srvtab admin admin "host/$qname"
+klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0"
+keytab_run "K1" "-a host/$qname" 0
+klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0" "host/$qname 0"
+keytab_run "K1" "-r host/$qname old" 0
+klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0"
+
+if {[info exists ktname_orig]} {
+ set env(KRB5_KTNAME) $ktname_orig
+}
+
+setup_keytab "K2" WRFILE:$testfile1 admin admin {}
+keytab_run "K2" "-k WRFILE:$testfile1 -a -p admin kttest1" 0 {
+ "Enter password:" { send "admin\n" }
+}
+klist_check "K2" FILE:$testfile1 "kttest1 0"
+
+setup_keytab "K2" WRFILE:$testfile2 admin admin {}
+keytab_run "K3" "-k $testfile2 -a -p admin kttest1" 0 {
+ "Enter password:" { send "admin\n" }
+}
+klist_check "K3" FILE:$testfile2 "kttest1 0"
+
+exec rm -f $testfile1 $testfile2
diff --git a/src/kadmin/keytab/unit-test/keytab.0/removing.exp b/src/kadmin/keytab/unit-test/keytab.0/removing.exp
new file mode 100644
index 000000000..a7a50f045
--- /dev/null
+++ b/src/kadmin/keytab/unit-test/keytab.0/removing.exp
@@ -0,0 +1,125 @@
+#
+# $Id$
+#
+
+set timeout 10
+
+load_lib "helpers.exp"
+
+if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} {
+ error "cannot set progname from $KEYTAB"
+}
+
+set ktscratch_file /tmp/keytab_test
+set ktscratch WRFILE:$ktscratch_file
+set ktarg "-k $ktscratch"
+
+# Get the kvnos we will need later
+setup_keytab "setup" $ktscratch admin admin { kttest1 kttest2 }
+set kvno1 [exec $klist -k -K FILE:$ktscratch_file | \
+ awk "/kttest1/ {print \$1}"]
+set kvno2 [exec $klist -k -K FILE:$ktscratch_file | \
+ awk "/kttest2/ {print \$1}"]
+
+setup_keytab "R1" $ktscratch admin admin { kttest1 }
+set kvno1 [expr $kvno1+1]
+keytab_run "R1" "$ktarg -r kttest1" 0 {
+ -re
+ "$progname: Entry for principal kttest1 with kvno \[0-9\]+\
+ removed from keytab $ktscratch" {}
+}
+klist_check "R1" $ktscratch
+
+setup_keytab "R2" $ktscratch admin admin { kttest1 }
+set kvno1 [expr $kvno1+1]
+keytab_run "R2" "$ktarg -q -r kttest1" 0 {
+ -re
+ "$progname: Entry for principal kttest1 with kvno \[0-9\]+\
+ removed from keytab $ktscratch" { close; fail "R2: -q"; return }
+ eof { break }
+}
+klist_check "R2" $ktscratch
+
+setup_keytab "R3" $ktscratch admin admin { kttest1 }
+set kvno1 [expr $kvno1+1]
+klist_check "R3" $ktscratch "kttest1 $kvno1"
+keytab_run "R3" "$ktarg -r kttest1 $kvno1" 0
+klist_check "R3" $ktscratch
+
+setup_keytab "R4" $ktscratch admin admin { kttest1 kttest1 kttest1 }
+set kvno1 [expr $kvno1+3]
+klist_check "R4" $ktscratch "kttest1 [expr $kvno1-2]" \
+ "kttest1 [expr $kvno1-1]" "kttest1 $kvno1"
+keytab_run "R4" "$ktarg -r kttest1" 0
+klist_check "R4" $ktscratch "kttest1 [expr $kvno1-2]" \
+ "kttest1 [expr $kvno1-1]"
+
+setup_keytab "R5" $ktscratch admin admin { kttest1 kttest1 kttest1 }
+set kvno1 [expr $kvno1+3]
+keytab_run "R5" "$ktarg -r kttest1 old" 0
+klist_check "R5" $ktscratch "kttest1 $kvno1"
+
+setup_keytab "R6" $ktscratch admin admin { kttest1 kttest1 kttest1 }
+set kvno1 [expr $kvno1+3]
+keytab_run "R6" "$ktarg -r kttest1 old" 0
+klist_check "R6" $ktscratch "kttest1 $kvno1"
+
+setup_keytab "R7" $ktscratch admin admin { kttest1 kttest1 kttest1 }
+set kvno1 [expr $kvno1+3]
+keytab_run "R7" "$ktarg -r kttest1 all" 0 {
+ "$progname: Entry for principal kttest1" {}
+} {
+ "$progname: Entry for principal kttest1" {}
+} {
+ "$progname: Entry for principal kttest1" {}
+}
+klist_check "R7" $ktscratch
+
+setup_keytab "R8" $ktscratch admin admin { kttest1 }
+set kvno1 [expr $kvno1+1]
+keytab_run "R8" "$ktarg -r kttest2" 1 {
+ "$progname: No entry for principal kttest2 exists in keytab" {}
+}
+klist_check "R8" $ktscratch "kttest1 $kvno1"
+
+setup_keytab "R9" $ktscratch admin admin { kttest1 }
+set kvno1 [expr $kvno1+1]
+keytab_run "R9" "$ktarg -r kttest2 1" 1 {
+ "$progname: No entry for principal kttest2 with kvno 1 exists in keytab" {}
+}
+klist_check "R9" $ktscratch "kttest1 $kvno1"
+
+setup_keytab "R10" $ktscratch admin admin { kttest1 }
+set kvno1 [expr $kvno1+1]
+keytab_run "R10" "$ktarg -r kttest2 all" 1 {
+ "$progname: No entry for principal kttest2 exists in keytab" {}
+}
+klist_check "R10" $ktscratch "kttest1 $kvno1"
+
+setup_keytab "R11" $ktscratch admin admin { kttest1 }
+set kvno1 [expr $kvno1+1]
+keytab_run "R11" "$ktarg -r kttest1 old" 1 {
+ "$progname: There is only one entry for principal kttest1 in keytab" {}
+}
+klist_check "R11" $ktscratch "kttest1 $kvno1"
+
+setup_keytab "R13" $ktscratch admin admin { kttest1 kttest2 kttest1 }
+set kvno1 [expr $kvno1+2]
+set kvno2 [expr $kvno2+1]
+keytab_run "R13" "$ktarg -r kttest2 $kvno2" 0
+klist_check "R13" $ktscratch "kttest1 [expr $kvno1-1]" "kttest1 $kvno1"
+
+setup_keytab "R14" $ktscratch admin admin { kttest1 kttest2 kttest1 kttest2 }
+set kvno1 [expr $kvno1+2]
+set kvno2 [expr $kvno2+2]
+keytab_run "R14" "$ktarg -r kttest1 all" 0
+klist_check "R14" $ktscratch "kttest2 [expr $kvno2-1]" "kttest2 $kvno2"
+
+setup_keytab "R15" $ktscratch admin admin { kttest1 kttest2 kttest1 kttest2 }
+set kvno1 [expr $kvno1+2]
+set kvno2 [expr $kvno2+2]
+keytab_run "R15" "$ktarg -r kttest1 old" 0
+klist_check "R15" $ktscratch "kttest2 [expr $kvno2-1]" \
+ "kttest1 $kvno1" "kttest2 $kvno2"
+
+exec rm -f $ktscratch_file
diff --git a/src/kadmin/ktutil/ChangeLog b/src/kadmin/ktutil/ChangeLog
index 7c8582fd1..6ca64712d 100644
--- a/src/kadmin/ktutil/ChangeLog
+++ b/src/kadmin/ktutil/ChangeLog
@@ -2,6 +2,10 @@ Thu Jun 13 21:42:11 1996 Tom Yu <tlyu@voltage-multiplier.mit.edu>
* configure.in: remove ref to SS_RULES
+Fri Jul 12 14:37:47 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in (USE_KADM_LIBRARY): removed. it wasn't needed.
+
Tue Mar 19 19:41:31 1996 Richard Basch <basch@lehman.com>
* ktutil_funcs.c (ktutil_write_srvtab): use any type of des key
diff --git a/src/kadmin/ktutil/configure.in b/src/kadmin/ktutil/configure.in
index 6be5dc2aa..7e9f3e1a0 100644
--- a/src/kadmin/ktutil/configure.in
+++ b/src/kadmin/ktutil/configure.in
@@ -1,7 +1,6 @@
AC_INIT(ktutil.c)
CONFIG_RULES
AC_PROG_INSTALL
-USE_KADM_LIBRARY
USE_KRB4_LIBRARY
USE_SS_LIBRARY
KRB5_LIBRARIES
diff --git a/src/kadmin/passwd/ChangeLog b/src/kadmin/passwd/ChangeLog
new file mode 100644
index 000000000..d0f4cbafc
--- /dev/null
+++ b/src/kadmin/passwd/ChangeLog
@@ -0,0 +1,20 @@
+Mon Jul 22 04:07:02 1996 Marc Horowitz <marc@mit.edu>
+
+ * tty_kpasswd.c: main returns int, not void
+
+Thu Jul 18 19:46:24 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Wed Jul 10 01:28:12 1996 Marc Horowitz <marc@mit.edu>
+
+ * Makefile.in, configure.in: added autoconf support
+
+Tue Jul 9 15:03:13 1996 Marc Horowitz <marc@mit.edu>
+
+ * kpasswd.c, tty_kpasswd.c, xm_kpasswd.c: renamed
+ <ovsec_admin/foo.h> to <kadm5/foo.h>
+
+ * configure.in (CONFIG_DIRS): build the subdirs for the new admin
+ system, not the old one.
+
diff --git a/src/kadmin/passwd/Kpasswd b/src/kadmin/passwd/Kpasswd
new file mode 100644
index 000000000..a7ec03161
--- /dev/null
+++ b/src/kadmin/passwd/Kpasswd
@@ -0,0 +1,46 @@
+*xm_ovpasswd.title: PW-CHG-GUI
+*form.shadowThickness: 2
+
+*foreground: black
+*background: grey80
+*topShadowColor: grey95
+*bottomShadowColor: grey20
+*fontList: -*-helvetica-medium-r-*-*-14-*
+*main_lbl.fontList: -*-helvetica-bold-r-*-*-14-*
+*XmForm.Spacing: 5
+
+*main_lbl.labelString: Changing password.
+*old_lbl.labelString: Old password:
+*new_lbl.labelString: New password:
+*again_lbl.labelString: New password (again):
+*sep.leftOffset: 0
+*sep.rightOffset: 0
+*Quit.labelString: Quit
+*Help.labelString: Help
+
+*main_lbl.alignment: ALIGNMENT_CENTER
+*lbl_form*alignment: ALIGNMENT_END
+*scroll_win.shadowThickness: 0
+
+*scroll_text.value: \
+Enter your old password below, and press return. You will not be able to see what you\n\
+are typing. After correctly entering your old password, you will be prompted twice for\n\
+your new password. Other messages and directions will appear in this space as necessary.
+*scroll_text.rows: 5
+*scroll_text.columns: 66
+*scroll_text.scrollHorizontal: FALSE
+*scroll_text.cursorPositionVisible: FALSE
+
+*help_dlg_popup.title: PW-CHG-GUI Help
+*help_dlg.messageString: \
+Welcome to the Kerberos password changing GUI.\n\
+\n\
+In the main window, enter your old password when prompted. After verifying\n\
+your old password, the policy governing your password will be displayed, and\n\
+you will be prompted for a new password. You will then be asked to enter it\n\
+a second time, to make sure you have not made any typos. Assuming that\n\
+your new password complies with your password policy, you should receive\n\
+an acknowledgement that your password has been changed.\n\
+\n\
+If an error occurs, the process will start over from the beginning. You may\n\
+exit the application at any time by pressing the "Quit" button.
diff --git a/src/kadmin/passwd/Makefile.in b/src/kadmin/passwd/Makefile.in
new file mode 100644
index 000000000..46e962070
--- /dev/null
+++ b/src/kadmin/passwd/Makefile.in
@@ -0,0 +1,23 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) -I. -DUSE_KADM5_API_VERSION=1
+
+PROG = kpasswd
+OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o
+
+all:: $(PROG).local $(PROG)
+
+kpasswd_strings.c kpasswd_strings.h: $(srcdir)/kpasswd_strings.et
+
+$(OBJS): kpasswd_strings.h
+
+$(PROG).local: $(OBJS) $(SRVDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS)
+
+$(PROG): $(OBJS) $(CLNTDEPLIBS)
+ $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS)
+
+install::
+ $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean::
+ $(RM) kpasswd_strings.c kpasswd_strings.h $(PROG).local $(PROG) $(OBJS)
diff --git a/src/kadmin/passwd/Makefile.ov b/src/kadmin/passwd/Makefile.ov
new file mode 100644
index 000000000..8841a4762
--- /dev/null
+++ b/src/kadmin/passwd/Makefile.ov
@@ -0,0 +1,34 @@
+TOP = ..
+include $(TOP)/config.mk/template
+
+CFLAGS := $(CFLAGS) -DUSE_KADM5_API_VERSION=1
+
+# This used as a string table, not an error table
+ETABLES = kpasswd_strings.et
+expand ErrorTables
+
+kpasswd.o: kpasswd_strings.h
+depend:: kpasswd_strings.h
+
+PROG = kpasswd
+SRCS = tty_kpasswd.c kpasswd.c kpasswd_strings.c
+OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o
+LIBS = $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBGSSAPI_KRB5) $(LIBRPCLIB) \
+ $(LIBDYN) $(LIBDB) $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \
+ $(LIBISODE) $(NDBMLIB) $(BSDLIB) $(NETLIB)
+
+expand NormalProgram
+
+ifndef OMIT_XM_KPASSWD
+PROG = xm_kpasswd
+CFLAGS := -I$(XM_INC) -I$(XT_INC) -I$(X_INC) $(CFLAGS)
+SRCS := xm_kpasswd.c kpasswd.c kpasswd_strings.c
+OBJS := xm_kpasswd.o kpasswd.o kpasswd_strings.o
+LIBS := $(LIBS) $(XM_LIB) $(XT_LIB) $(X_LIB) $(REGEXLIB)
+
+expand NormalProgram
+endif
+
+SUBDIRS = unit-test
+
+expand SubdirTarget
diff --git a/src/kadmin/passwd/configure.in b/src/kadmin/passwd/configure.in
new file mode 100644
index 000000000..fe1389a83
--- /dev/null
+++ b/src/kadmin/passwd/configure.in
@@ -0,0 +1,13 @@
+AC_INIT(kpasswd.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_PROG_AWK
+USE_KADMCLNT_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/passwd/kpasswd.c b/src/kadmin/passwd/kpasswd.c
new file mode 100644
index 000000000..f87ad1cb0
--- /dev/null
+++ b/src/kadmin/passwd/kpasswd.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ *
+ */
+
+static char rcsid[] = "$Id$";
+
+#include <kadm5/admin.h>
+#include <krb5.h>
+
+#include "kpasswd_strings.h"
+#define string_text error_message
+#define initialize_kpasswd_strings initialize_kpws_error_table
+
+#include <stdio.h>
+#include <pwd.h>
+#include <string.h>
+
+extern char *whoami;
+
+extern void display_intro_message();
+extern long read_old_password();
+extern long read_new_password();
+
+#define MISC_EXIT_STATUS 6
+
+/*
+ * Function: kpasswd
+ *
+ * Purpose: Initialize and call lower level routines to change a password
+ *
+ * Arguments:
+ *
+ * context (r) krb5_context to use
+ * argc/argv (r) principal name to use, optional
+ * read_old_password (f) function to read old password
+ * read_new_password (f) function to read new and change password
+ * display_intro_message (f) function to display intro message
+ * whoami (extern) argv[0]
+ *
+ * Returns:
+ * exit status of 0 for success
+ * 1 principal unknown
+ * 2 old password wrong
+ * 3 cannot initialize admin server session
+ * 4 new passwd mismatch or error trying to change pw
+ * 5 password not typed
+ * 6 misc error
+ * 7 incorrect usage
+ *
+ * Requires:
+ * Passwords cannot be more than 255 characters long.
+ *
+ * Effects:
+ *
+ * If argc is 2, the password for the principal specified in argv[1]
+ * is changed; otherwise, the principal of the default credential
+ * cache or username is used. display_intro_message is called with
+ * the arguments KPW_STR_CHANGING_PW_FOR and the principal name.
+ * read_old_password is then called to prompt for the old password.
+ * The admin system is then initialized, the principal's policy
+ * retrieved and explained, if appropriate, and finally
+ * read_new_password is called to read the new password and change the
+ * principal's password (presumably ovsec_kadm_chpass_principal).
+ * admin system is de-initialized before the function returns.
+ *
+ * Modifies:
+ *
+ * Changes the principal's password.
+ *
+ */
+int
+kpasswd(context, argc, argv)
+ krb5_context context;
+ int argc;
+ char *argv[];
+{
+ int code;
+ krb5_ccache ccache = NULL;
+ krb5_principal princ = 0;
+ char *princ_str;
+ struct passwd *pw = 0;
+ int pwsize;
+ char password[255]; /* I don't really like 255 but that's what kinit uses */
+ char msg_ret[1024], admin_realm[1024];
+ ovsec_kadm_principal_ent_t principal_entry = NULL;
+ ovsec_kadm_policy_ent_t policy_entry = NULL;
+ void *server_handle;
+
+ if (argc > 2) {
+ com_err(whoami, KPW_STR_USAGE, 0);
+ return(7);
+ /*NOTREACHED*/
+ }
+
+ krb5_init_ets(context);
+
+ /************************************
+ * Get principal name to change *
+ ************************************/
+
+ /* Look on the command line first, followed by the default credential
+ cache, followed by defaulting to the Unix user name */
+
+ if (argc == 2)
+ princ_str = strdup(argv[1]);
+ else {
+ code = krb5_cc_default(context, &ccache);
+ /* If we succeed, find who is in the credential cache */
+ if (code == 0) {
+ /* Get default principal from cache if one exists */
+ code = krb5_cc_get_principal(context, ccache, &princ);
+ /* if we got a principal, unparse it, otherwise get out of the if
+ with an error code */
+ (void) krb5_cc_close(context, ccache);
+ if (code == 0) {
+ code = krb5_unparse_name(context, princ, &princ_str);
+ if (code != 0) {
+ com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME));
+ return(MISC_EXIT_STATUS);
+ }
+ }
+ }
+
+ /* this is a crock.. we want to compare against */
+ /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */
+ /* both the file and stdio types return FCC_NOFILE. If there is */
+ /* ever another ccache type (or if the error codes are ever */
+ /* fixed), this code will have to be updated. */
+ if (code && code != KRB5_FCC_NOFILE) {
+ com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC));
+ return(MISC_EXIT_STATUS);
+ }
+
+ /* if either krb5_cc failed check the passwd file */
+ if (code != 0) {
+ pw = getpwuid((int) getuid());
+ if (pw == NULL) {
+ com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE));
+ return(MISC_EXIT_STATUS);
+ }
+ princ_str = strdup(pw->pw_name);
+ }
+ }
+
+ display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str);
+
+ /* Need to get a krb5_principal, unless we started from with one from
+ the credential cache */
+
+ if (! princ) {
+ code = krb5_parse_name (context, princ_str, &princ);
+ if (code != 0) {
+ com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str);
+ free(princ_str);
+ return(MISC_EXIT_STATUS);
+ }
+ }
+
+ pwsize = sizeof(password);
+ code = read_old_password(context, password, &pwsize);
+
+ if (code != 0) {
+ memset(password, 0, sizeof(password));
+ com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD));
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ return(MISC_EXIT_STATUS);
+ }
+ if (pwsize == 0) {
+ memset(password, 0, sizeof(password));
+ com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ));
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ return(5);
+ }
+
+ admin_realm[0] = '\0';
+ strncat(admin_realm, krb5_princ_realm(context, princ)->data,
+ krb5_princ_realm(context, princ)->length);
+
+ code = ovsec_kadm_init(princ_str, password, OVSEC_KADM_CHANGEPW_SERVICE,
+ admin_realm /* we probably should take a -r */
+ /* someday */,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &server_handle);
+ if (code != 0) {
+ if (code == OVSEC_KADM_BAD_PASSWORD)
+ com_err(whoami, 0, string_text(KPW_STR_OLD_PASSWORD_INCORRECT));
+ else
+ com_err(whoami, 0, string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER), admin_realm,
+ error_message(code));
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ return((code == OVSEC_KADM_BAD_PASSWORD)?2:3);
+ }
+
+ /* Explain policy restrictions on new password if any. */
+ /* Note: copy of this exists in login (kverify.c/get_verified_in_tkt). */
+
+ code = ovsec_kadm_get_principal(server_handle, princ, &principal_entry);
+ if (code != 0) {
+ com_err(whoami, 0,
+ string_text((code == OVSEC_KADM_UNK_PRINC)
+ ? KPW_STR_PRIN_UNKNOWN : KPW_STR_CANT_GET_POLICY_INFO),
+ princ_str);
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ (void) ovsec_kadm_destroy(server_handle);
+ return((code == OVSEC_KADM_UNK_PRINC) ? 1 : MISC_EXIT_STATUS);
+ }
+ if ((principal_entry->aux_attributes & OVSEC_KADM_POLICY) != 0) {
+ code = ovsec_kadm_get_policy(server_handle,
+ principal_entry->policy, &policy_entry);
+ if (code != 0) {
+ /* doesn't matter which error comes back, there's no nice recovery
+ or need to differentiate to the user */
+ com_err(whoami, 0,
+ string_text(KPW_STR_CANT_GET_POLICY_INFO), princ_str);
+ (void) ovsec_kadm_free_principal_ent(server_handle, principal_entry);
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ com_err(whoami, 0, string_text(KPW_STR_POLICY_EXPLANATION),
+ princ_str, principal_entry->policy,
+ policy_entry->pw_min_length, policy_entry->pw_min_classes);
+ if (code = ovsec_kadm_free_principal_ent(server_handle, principal_entry)) {
+ (void) ovsec_kadm_free_policy_ent(server_handle, policy_entry);
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ if (code = ovsec_kadm_free_policy_ent(server_handle, policy_entry)) {
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_POLICY));
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ }
+ else {
+ /* kpasswd *COULD* output something here to encourage the choice
+ of good passwords, in the absence of an enforced policy. */
+ if (code = ovsec_kadm_free_principal_ent(server_handle,
+ principal_entry)) {
+ krb5_free_principal(context, princ);
+ free(princ_str);
+ com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL));
+ (void) ovsec_kadm_destroy(server_handle);
+ return(MISC_EXIT_STATUS);
+ }
+ }
+
+ pwsize = sizeof(password);
+ code = read_new_password(server_handle, password, &pwsize, msg_ret, princ);
+ memset(password, 0, sizeof(password));
+
+ if (code)
+ com_err(whoami, 0, msg_ret);
+
+ krb5_free_principal(context, princ);
+ free(princ_str);
+
+ (void) ovsec_kadm_destroy(server_handle);
+
+ if (code == KRB5_LIBOS_CANTREADPWD)
+ return(5);
+ else if (code)
+ return(4);
+ else
+ return(0);
+}
diff --git a/src/kadmin/passwd/kpasswd_strings.et b/src/kadmin/passwd/kpasswd_strings.et
new file mode 100644
index 000000000..b78aa9d6f
--- /dev/null
+++ b/src/kadmin/passwd/kpasswd_strings.et
@@ -0,0 +1,76 @@
+#
+# Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+#
+# String table of messages for kpasswd
+
+
+error_table kpws
+
+# /* M1 */
+error_code KPW_STR_USAGE, "Usage: kpasswd [principal_name]."
+
+error_code KPW_STR_PRIN_UNKNOWN,
+ "Kerberos principal name %s is not recognized."
+# /* <name> */
+
+# /* M2 */
+error_code KPW_STR_WHILE_LOOKING_AT_CC,
+ "while reading principal name from credential cache."
+
+# /* M4 */
+error_code KPW_STR_OLD_PASSWORD_INCORRECT,
+ "Old Kerberos password is incorrect. Please try again."
+
+# /* M5 */
+error_code KPW_STR_CANT_OPEN_ADMIN_SERVER,
+"Cannot establish a session with the Kerberos administrative server for\n\
+realm %s. %s."
+# /* <realm-name>, <Specific error message from admin server library>. */
+
+# /* M6 */
+error_code KPW_STR_NEW_PASSWORD_MISMATCH,
+ "New passwords do not match - password not changed.\n"
+
+# /* M7 */
+error_code KPW_STR_PASSWORD_CHANGED, "Kerberos password changed.\n"
+
+# /* M13 */
+error_code KPW_STR_PASSWORD_NOT_CHANGED, "Password not changed."
+
+error_code KPW_STR_PARSE_NAME, "when parsing name %s."
+error_code KPW_STR_UNPARSE_NAME, "when unparsing name."
+error_code KPW_STR_NOT_IN_PASSWD_FILE, "Unable to identify user from password file."
+
+# /* M3 */
+error_code KPW_STR_CHANGING_PW_FOR, "Changing password for %s."
+# /* principal@realm */
+
+error_code KPW_STR_OLD_PASSWORD_PROMPT, "Old password:"
+error_code KPW_STR_WHILE_READING_PASSWORD, "while reading new password."
+
+# /* M4 */
+error_code KPW_STR_NO_PASSWORD_READ,
+"You must type a password. Passwords must be at least one character long."
+
+# /* M14 */
+error_code KPW_STR_WHILE_TRYING_TO_CHANGE, "while trying to change password."
+
+error_code KPW_STR_WHILE_DESTROYING_ADMIN_SESSION,
+"while closing session with admin server and destroying tickets."
+
+error_code KPW_STR_WHILE_FREEING_PRINCIPAL,
+"while freeing admin principal entry"
+
+error_code KPW_STR_WHILE_FREEING_POLICY,
+"while freeing admin policy entry"
+
+error_code KPW_STR_CANT_GET_POLICY_INFO,
+"Could not get password policy information for principal %s."
+# /* principal@realm */
+
+error_code KPW_STR_POLICY_EXPLANATION,
+"%s's password is controlled by the policy %s, which\nrequires a minimum of %u characters from at least %u classes (the five classes\nare lowercase, uppercase, numbers, punctuation, and all other characters)."
+# /* principal_name policy_name min_length min_classes */
+
+end
+
diff --git a/src/kadmin/passwd/tty_kpasswd.c b/src/kadmin/passwd/tty_kpasswd.c
new file mode 100644
index 000000000..6d865a020
--- /dev/null
+++ b/src/kadmin/passwd/tty_kpasswd.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ *
+ */
+
+static char rcsid[] = "$Id$";
+
+#include <kadm5/admin.h>
+#include <krb5.h>
+
+#include "kpasswd_strings.h"
+#define string_text error_message
+#define initialize_kpasswd_strings initialize_kpws_error_table
+
+#include <stdio.h>
+#include <pwd.h>
+#include <string.h>
+
+char *whoami;
+
+void display_intro_message(fmt_string, arg_string)
+ char *fmt_string;
+ char *arg_string;
+{
+ com_err(whoami, 0, fmt_string, arg_string);
+}
+
+long read_old_password(context, password, pwsize)
+ krb5_context context;
+ char *password;
+ int *pwsize;
+{
+ long code = krb5_read_password(context,
+ (char *)string_text(KPW_STR_OLD_PASSWORD_PROMPT),
+ 0, password, pwsize);
+ return code;
+}
+
+long read_new_password(server_handle, password, pwsize, msg_ret, princ)
+ void *server_handle;
+ char *password;
+ int *pwsize;
+ char *msg_ret;
+ krb5_principal princ;
+{
+ return (ovsec_kadm_chpass_principal_util(server_handle, princ, NULL,
+ NULL /* don't need new pw back */,
+ msg_ret));
+}
+
+
+/*
+ * main() for tty version of kpasswd.c
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_context context;
+ int retval;
+
+ initialize_kpasswd_strings();
+
+ whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0];
+
+ if (retval = krb5_init_context(&context)) {
+ com_err(whoami, retval, "initializing krb5 context");
+ exit(retval);
+ }
+ retval = kpasswd(context, argc, argv);
+
+ if (!retval)
+ printf(string_text(KPW_STR_PASSWORD_CHANGED));
+
+ exit(retval);
+}
diff --git a/src/kadmin/passwd/unit-test/Makefile.ov b/src/kadmin/passwd/unit-test/Makefile.ov
new file mode 100644
index 000000000..db042720b
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/Makefile.ov
@@ -0,0 +1,23 @@
+#
+# $Id$
+#
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+USER = root
+
+unit-test:: unit-test-setup unit-test-body unit-test-cleanup
+
+unit-test-body::
+ $(RUNTEST) KPASSWD=../kpasswd \
+ KINIT=$(TOP)/../clients/kinit/kinit \
+ KDESTROY=$(TOP)/../clients/kdestroy/kdestroy \
+ USER=$(USER) --tool kpasswd
+
+unit-test-setup::
+ $(START_SERVERS)
+ echo "source $(TCLUTIL); catch {ovsec_kadm_init admin admin \$$OVSEC_KADM_ADMIN_SERVICE null \$$OVSEC_KADM_STRUCT_VERSION \$$OVSEC_KADM_API_VERSION_1 server_handle; ovsec_kadm_create_principal \$$server_handle [simple_principal $(USER)] {OVSEC_KADM_PRINCIPAL} $(USER); ovsec_kadm_destroy \$$server_handle;}; if {[info exists errorInfo]} { puts stderr \$$errorInfo; exit 1; }" | $(CLNTTCL)
+
+unit-test-cleanup::
+ $(STOP_SERVERS)
diff --git a/src/kadmin/passwd/unit-test/config/unix.exp b/src/kadmin/passwd/unit-test/config/unix.exp
new file mode 100644
index 000000000..c77aa016a
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/config/unix.exp
@@ -0,0 +1,36 @@
+#
+# kpasswd_version -- extract and print the version number of kpasswd
+#
+
+proc kpasswd_version {} {
+ global KPASSWD
+ catch "exec ident $KPASSWD" tmp
+ if [regexp {Id: kpasswd.c,v ([0-9]+\.[0-9]+)} $tmp \
+ dummy version] then {
+ clone_output "$KPASSWD version $version\n"
+ } else {
+ clone_output "$KPASSWD version <unknown>\n"
+ }
+}
+#
+# kpasswd_load -- loads the program
+#
+proc kpasswd_load {} {
+ #
+}
+
+# kpasswd_exit -- clean up and exit
+proc kpasswd_exit {} {
+ #
+}
+
+#
+# kpasswd_start -- start kpasswd running
+#
+proc kpasswd_start { args } {
+ global KPASSWD
+ global spawn_id
+
+ verbose "% $KPASSWD $args" 1
+ eval spawn $KPASSWD $args
+}
diff --git a/src/kadmin/passwd/unit-test/helpers.exp b/src/kadmin/passwd/unit-test/helpers.exp
new file mode 100644
index 000000000..9dcfbcf01
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/helpers.exp
@@ -0,0 +1,217 @@
+#
+# $Id$
+#
+
+global s
+set s "\[\r\n\t\ \]"
+
+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 kpasswd_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 { [lindex $ret $wait_status_index] == $status ||
+ (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ }
+ }
+}
+
+proc kinit { princ pass } {
+ global env;
+ global KINIT
+ spawn -noecho $KINIT $princ;
+
+ expect {
+ -re {Password for .*: $}
+ {send "$pass\n"}
+ timeout {puts "Timeout waiting for prompt" ; close }
+ }
+
+ # this necessary so close(1) in the child will not sleep waiting for
+ # the parent, which is us, to read pending data.
+
+ expect {
+ eof {}
+ }
+ wait
+}
+
+proc kdestroy {} {
+ global KDESTROY
+ global errorCode errorInfo
+ global env
+
+ if {[info exists errorCode]} {
+ set saveErrorCode $errorCode
+ }
+ if {[info exists errorInfo]} {
+ set saveErrorInfo $errorInfo
+ }
+ catch "system $KDESTROY 2>/dev/null"
+ if {[info exists saveErrorCode]} {
+ set errorCode $saveErrorCode
+ } elseif {[info exists errorCode]} {
+ unset errorCode
+ }
+ if {[info exists saveErrorInfo]} {
+ set errorInfo $saveErrorInfo
+ } elseif {[info exists errorInfo]} {
+ unset errorInfo
+ }
+}
+
+global initerr_str
+global initerr_regexp
+set initerr_str "Cannot establish a session with the Kerberos administrative server for realm \[^\r\n\]*\\. "
+set initerr_regexp "Cannot establish a session with the Kerberos administrative server for$s+realm \[^\r\n\]*\\.$s+"
+
+proc test_win { args name princ pass1 { pass2 "\001\001" } } {
+ global s
+ global initerr_regexp
+
+ if { $pass2 == "\001\001" } { set pass2 "$pass1" }
+
+ mytest "$name" $args 0 {
+ -re "Changing password for $princ.*\\.$s+Old password:"
+ { send "$pass1\n" }
+ } {
+ -re "Old Kerberos password is incorrect. Please try again."
+ { close; myfail "Old password incorrect" }
+ -re "${initerr_regexp}(.+\[^\r\n\t\ \])\r\n"
+ { close; myfail "init error: $expect_out(1,string)" }
+ -re "^$s+New password:"
+ { send "$pass2\n" }
+ -re "^$s+.*$s+.*$s+.*$s+New password:"
+ { send "$pass2\n" }
+ } {
+ -re "^$s+New password \\(again\\):"
+ { send "$pass2\n" }
+ } {
+ -re "^$s+Kerberos password changed."
+ { mypass }
+ -re "^$s+Password changed."
+ { close; myfail "Wrong message on success." }
+ }
+}
+
+proc test_initerr { args name princ pass status err } {
+ global s
+ global initerr_regexp
+
+ regsub -all "$s+" $err "$s+" err2
+
+ mytest "$name" $args $status {
+ -re "Changing password for $princ.*\\.$s+Old password:"
+ { send "$pass\n" }
+ } {
+ -re "$err2"
+ { mypass }
+ -re "Old Kerberos password is incorrect. Please try again."
+ { close; myfail "Old password incorrect" }
+ -re "${initerr_regexp}(.+)\r\n"
+ { close; myfail "init error: $expect_out(1,string)" }
+ }
+}
+
+proc test_3pass { args name princ pass1 pass2 pass3 status err } {
+ global s
+ global initerr_regexp
+
+ regsub -all "$s+" $err "$s+" err2
+
+ mytest "$name" $args $status {
+ -re "Changing password for $princ.*\\.$s+Old password:"
+ { send "$pass1\n" }
+ } {
+ -re "Old Kerberos password is incorrect. Please try again."
+ { close; myfail "Old password incorrect" }
+ -re "${initerr_regexp}(.+)\r\n"
+ { close; myfail "init error: $expect_out(1,string)" }
+ -re "^$s+New password:"
+ { send "$pass2\n" }
+ -re "^$s+.*$s+.*$s+.*$s+New password:"
+ { send "$pass2\n" }
+ } {
+ -re "^$s+New password \\(again\\):"
+ { send "$pass3\n" }
+ } {
+ -re "$s+$err2"
+ { mypass }
+ }
+}
+
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp
new file mode 100644
index 000000000..4f0354c63
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp
@@ -0,0 +1,102 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+if [info exist env(DEBUG)] { debug 1 }
+
+#
+# Here are the tests
+#
+
+test_3pass {test2} {D.5: different new passwords} test2 test2 test2 foobar \
+ 4 {New passwords do not match - password not changed.}
+
+test_3pass {test2} {D.7.5: empty/empty} test2 test2 {} {} \
+ 5 {You must type a password. Passwords must be at least one character long.}
+
+test_3pass {test2} {D.6: empty/non-empty} test2 test2 {} test2 \
+ 4 {New passwords do not match - password not changed.}
+
+test_3pass {test2} {D.7: non-empty/empty} test2 test2 test2 {} \
+ 4 {New passwords do not match - password not changed.}
+
+
+test_win {test1} {D.8: change password} test1 test1 newpass
+
+test_win {test1} {D.9: test changed password} test1 newpass test1
+
+mytest "D.22: No policy description was shown" test1 4 {
+ -re "Changing password for test1.*\\.$s+Old password:"
+ { send "test1\n" }
+} {
+ -re "$s+.*$s+.*$s+.*char.*classes.*"
+ { myfail "policy description displayed" }
+ timeout { mypass }
+} {
+ -re "^$s+New password:"
+ { send "newpass\n" }
+} {
+ -re "^$s+New password \\(again\\):"
+ { send "ssapwen\n" }
+} {
+ -re "$s+New passwords do not match - password not changed."
+ { mypass }
+}
+
+test_3pass {pol1} {D.10: new password too short} pol1 pol111111 que que \
+ 4 {New password is too short. Please choose a password which is at least [0-9]+ characters long.}
+
+test_3pass {pol1} {D.13: too few char classes in new password} pol1 \
+ pol111111 123456789 123456789 \
+ 4 {New password does not have enough character classes. The character classes are: - lower-case letters, - upper-case letters, - digits, - punctuation, and - all other characters \(e.g., control characters\). Please choose a password with at least [0-9]+ character classes.}
+
+test_3pass {pol1} {D.14: new password in dictionary} pol1 \
+ pol111111 Discordianism Discordianism \
+ 4 {New password was found in a dictionary of possible passwords and therefore may be easily guessed. Please choose another password. See the ovpasswd man page for help in choosing a good password.}
+
+test_win {pol1} {successful change} pol1 pol111111 polAAAAAA
+# fail "successful change: XXXX password history is majorly broken"
+
+test_3pass {pol1} {D.11: new password same as old} pol1 \
+ polAAAAAA polAAAAAA polAAAAAA \
+ 4 {New password was used previously. Please choose a different password.}
+
+test_3pass {pol1} {D.12: new password in history} pol1 \
+ polAAAAAA pol111111 pol111111 \
+ 4 {New password was used previously. Please choose a different password.}
+
+mytest "D.18: Policy description was shown" pol1 4 {
+ -re "Changing password for pol1.*\\.$s+Old password:"
+ { send "polAAAAAA\n" }
+} {
+ -re "$s+.*$s+.*$s+.*8 char.*2 classes.*$s+New password:"
+ { send "newpass1234\n" }
+} {
+ -re "^$s+New password \\(again\\):"
+ { send "newpass4321\n" }
+} {
+ -re "$s+New passwords do not match - password not changed."
+ { mypass }
+}
+
+# restore pol1's password to its initial value; see discussion in
+# secure-kpasswd/2204 about secure-releng/2191 if you are confused
+test_win {pol1} {successful change} pol1 polAAAAAA polBBBBBB
+test_win {pol1} {successful change} pol1 polBBBBBB polCCCCCC
+test_win {pol1} {successful change} pol1 polCCCCCC pol111111
+
+test_win {pol2} {successful change} pol2 pol222222 polbbbbbb
+
+test_3pass {pol2} {D.15: too soon to change password} pol2 \
+ polbbbbbb pol222222 pol222222 \
+ 4 {Password cannot be changed because it was changed too recently. Please wait until .*199[0-9] before you change it. If you need to change your password before then, contact your system security administrator.}
+
+verbose "(sleeping 30 seconds)"
+catch "exec sleep 30"
+
+test_win {pol2} {password min life passed} pol2 polbbbbbb pol222222
+
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp
new file mode 100644
index 000000000..2cda17a6a
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp
@@ -0,0 +1,29 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+if [info exist env(DEBUG)] { debug 1 }
+
+#
+# Here are the tests
+#
+
+test_initerr {test2} {C.4: empty old password (XXXX)} test2 {} \
+ 5 {You must type a password. Passwords must be at least one character long.}
+
+test_initerr {test2} {C.5: incorrect old password} test2 foobar \
+ 2 "Old Kerberos password is incorrect. Please try again."
+
+# set timeout 60
+#
+#test_initerr {test2@SECURE-TEST-DEAD.OV.COM} {C.8: server up, daemon down} \
+# test2 test2 \
+# 3 ""
+#
+#test_initerr {test2@SECURE-TEST-DOWN.OV.COM} {C.8.5: server down} \
+# test2 test2 \
+# 3 "${initerr_str}Cannot contact any KDC for requested realm"
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp
new file mode 100644
index 000000000..e2bc20569
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp
@@ -0,0 +1,55 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+if [info exist env(DEBUG)] { debug 1 }
+
+#
+# Here are the tests
+#
+
+if {[info exists env(KRB5CCNAME)]} {
+ unset env(KRB5CCNAME)
+}
+kdestroy
+
+#### no principal specified
+
+set whoami $USER
+test_win {} {B.7: default nonexisting ccache(1)} $whoami $whoami newpass
+test_win {} {B.7: default nonexisting ccache(2)} $whoami newpass $whoami
+
+kinit test2 test2
+test_win {} {B.4: default existing cache containing existing principal} \
+ test2 test2 newpass
+kdestroy
+
+set env(KRB5CCNAME) FILE:/tmp/ovsec_adm_test_ccache
+kinit test2 newpass
+test_win {} {B.3: specified existing cache containing existing principal} \
+ test2 newpass test2
+kdestroy
+unset env(KRB5CCNAME)
+
+#### principal on command line
+
+#
+test_win {test2} {B.14: existing principal, no realm} test2 test2 newpass
+
+#
+test_initerr {bogus} {B.15, C.6: non-existent principal, no realm} bogus bogus \
+ 3 "${initerr_str}Client not found in Kerberos database"
+
+#
+test_win {test2@SECURE-TEST.OV.COM} {B.16: existing principal, with realm} \
+ test2 newpass test2
+
+#
+test_initerr {bogus@SECURE-TEST.OV.COM} \
+ {B.17: non-existent principal, with realm} \
+ bogus bogus \
+ 3 "${initerr_str}Client not found in Kerberos database"
diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp
new file mode 100644
index 000000000..e132bab2f
--- /dev/null
+++ b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp
@@ -0,0 +1,26 @@
+#
+# $Id$
+#
+
+set timeout 15
+
+load_lib "helpers.exp"
+
+#
+# Here are the tests
+#
+
+mytest {A.1: two args} {foo bar} 7 {
+ -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass }
+}
+
+mytest {A.2: three args} {foo bar baz} 7 {
+ -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass }
+}
+
+set env(KRB5CCNAME) bogus_type:bogus_ccname
+mytest {B.5: malformed ccache name} {} 6 {
+ -re {[a-z./]+passwd: Unknown credential cache type while reading principal name from credential cache} { mypass }
+}
+unset env(KRB5CCNAME)
+
diff --git a/src/kadmin/passwd/xm_kpasswd.c b/src/kadmin/passwd/xm_kpasswd.c
new file mode 100644
index 000000000..0db1111c6
--- /dev/null
+++ b/src/kadmin/passwd/xm_kpasswd.c
@@ -0,0 +1,450 @@
+/*
+ * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Header$
+ *
+ *
+ */
+
+static char rcsid_2[] = "$Id$";
+
+#include <kadm5/admin.h>
+#include <krb5.h>
+
+#include "kpasswd_strings.h"
+#define string_text error_message
+#define initialize_kpasswd_strings initialize_kpws_error_table
+
+#include <stdio.h>
+#include <pwd.h>
+#include <string.h>
+
+char *whoami;
+
+#include <Xm/Xm.h>
+#include <Xm/MessageB.h>
+#include <Xm/ScrolledW.h>
+#include <Xm/Form.h>
+#include <Xm/Text.h>
+#include <Xm/PushB.h>
+#include <Xm/Label.h>
+#include <Xm/Separator.h>
+#include <X11/cursorfont.h>
+#include <X11/Shell.h>
+
+Widget toplevel, scroll_text, prompt_text;
+Widget quit_btn, help_btn, old_lbl, new_lbl, again_lbl, main_lbl;
+XtAppContext app_con;
+int looping;
+int retval=0;
+
+
+/***************************************************************************
+ *
+ * A few utility functions for setting/unsetting the busy cursor
+ * (i.e. the watch cursor).
+ */
+static void
+SetCursor(w,c)
+ Widget w;
+ Cursor c;
+{
+ while (XtIsSubclass(w, shellWidgetClass) != True)
+ w = XtParent(w);
+
+ XDefineCursor(XtDisplay(w), XtWindow(w), c);
+ XFlush(XtDisplay(w));
+}
+
+
+static void
+SetStandardCursor()
+{
+ static Cursor ArrowCursor = (Cursor)NULL;
+
+ if (ArrowCursor == (Cursor)NULL)
+ ArrowCursor = XCreateFontCursor(XtDisplay(toplevel), XC_top_left_arrow);
+ SetCursor(toplevel, ArrowCursor);
+}
+
+
+static void
+SetWatchCursor()
+{
+ static Cursor WatchCursor = (Cursor)NULL;
+
+ if (WatchCursor == (Cursor)NULL)
+ WatchCursor = XCreateFontCursor(XtDisplay(toplevel), XC_watch);
+ SetCursor(toplevel, WatchCursor);
+}
+
+
+/***************************************************************************
+ *
+ * Set up a com_err hook, for displaying to a motif scrolling widget.
+ */
+
+#if __STDC__
+# include <stdarg.h>
+#else /* varargs: not STDC or no <stdarg> */
+ /* Non-ANSI, always take <varargs.h> path. */
+# undef VARARGS
+# define VARARGS 1
+# include <varargs.h>
+#endif /* varargs */
+
+static void
+#ifdef __STDC__
+motif_com_err (const char *whoami, long code, const char *fmt, va_list args)
+#else
+motif_com_err (whoami, code, fmt, args)
+ const char *whoami;
+ long code;
+ const char *fmt;
+ va_list args;
+#endif
+{
+ XEvent event;
+ char buf[2048];
+
+ buf[0] = '\0';
+
+ if (whoami)
+ {
+ strcpy(buf, whoami);
+ strcat(buf, ": ");
+ }
+ if (code)
+ {
+ strcat(buf, error_message(code));
+ strcat(buf, " ");
+ }
+ if (fmt)
+ {
+ vsprintf(buf + strlen(buf), fmt, args);
+ }
+
+ XtVaSetValues(scroll_text, XmNvalue, buf, NULL);
+
+ for (; XtAppPending(app_con); )
+ {
+ XtAppNextEvent(app_con, &event);
+ XtDispatchEvent(&event);
+ }
+}
+
+
+/***************************************************************************
+ *
+ * Function to display help widget.
+ */
+static void
+help()
+{
+ static Widget help_dlg = NULL;
+
+ if (!help_dlg)
+ {
+ help_dlg = XmCreateInformationDialog(toplevel, "help_dlg", NULL,
+ 0);
+ XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_CANCEL_BUTTON));
+ XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_HELP_BUTTON));
+ }
+ XtManageChild(help_dlg);
+}
+
+
+/***************************************************************************
+ *
+ * Unset the global "looping" when we want to get out of reading a
+ * password.
+ */
+static void
+unset_looping()
+{
+ looping = 0;
+}
+
+
+/***************************************************************************
+ *
+ * Function to exit the gui. Callback on the "Exit" button.
+ */
+static void
+quit()
+{
+ exit(retval);
+}
+
+
+/***************************************************************************
+ *
+ * Set up motif widgets, callbacks, etc.
+ */
+static void
+create_widgets(argc, argv)
+ int *argc;
+ char *argv[];
+{
+ Widget form, lbl_form,
+ sep,
+ scroll_win;
+ Pixel bg;
+
+ toplevel = XtAppInitialize(&app_con, "Kpasswd", NULL, 0, argc, argv,
+ NULL, NULL, 0);
+ form = XtCreateManagedWidget("form", xmFormWidgetClass, toplevel, NULL, 0);
+ quit_btn = XtVaCreateManagedWidget("Quit", xmPushButtonWidgetClass,
+ form,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ XtAddCallback(quit_btn, XmNactivateCallback, quit, 0);
+ help_btn = XtVaCreateManagedWidget("Help", xmPushButtonWidgetClass,
+ form,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ /* XmNshowAsDefault, TRUE, */
+ NULL);
+ XtAddCallback(help_btn, XmNactivateCallback, help, 0);
+ sep = XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass,
+ form,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, quit_btn,
+ NULL);
+ lbl_form = XtVaCreateManagedWidget("lbl_form", xmFormWidgetClass,
+ form,
+ XmNspacing, 0,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, sep,
+ NULL);
+ old_lbl = XtVaCreateManagedWidget("old_lbl", xmLabelWidgetClass,
+ lbl_form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ new_lbl = XtVaCreateManagedWidget("new_lbl", xmLabelWidgetClass,
+ lbl_form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ again_lbl = XtVaCreateManagedWidget("again_lbl", xmLabelWidgetClass,
+ lbl_form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_FORM,
+ NULL);
+ prompt_text = XtVaCreateManagedWidget("prompt_text", xmTextWidgetClass,
+ form,
+ XmNeditMode, XmSINGLE_LINE_EDIT,
+ XmNleftAttachment, XmATTACH_WIDGET,
+ XmNleftWidget, lbl_form,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, sep,
+ NULL);
+ XtAddCallback(prompt_text, XmNactivateCallback, unset_looping, 0);
+ XtVaGetValues(prompt_text, XmNbackground, &bg, NULL);
+ XtVaSetValues(prompt_text, XmNforeground, bg, NULL);
+
+ main_lbl = XtVaCreateWidget("main_lbl", xmLabelWidgetClass,
+ form,
+ XmNtopAttachment, XmATTACH_FORM,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ NULL);
+ scroll_win = XtVaCreateManagedWidget("scroll_win",
+ xmScrolledWindowWidgetClass,
+ form,
+ XmNscrollingPolicy, XmAPPLICATION_DEFINED,
+ XmNscrollBarDisplayPolicy, XmSTATIC,
+ XmNtopAttachment, XmATTACH_WIDGET,
+ XmNtopWidget, main_lbl,
+ XmNleftAttachment, XmATTACH_FORM,
+ XmNrightAttachment, XmATTACH_FORM,
+ XmNbottomAttachment, XmATTACH_WIDGET,
+ XmNbottomWidget, prompt_text,
+ NULL);
+ scroll_text = XtVaCreateManagedWidget("scroll_text", xmTextWidgetClass,
+ scroll_win,
+ XmNeditMode, XmMULTI_LINE_EDIT,
+ XmNeditable, FALSE,
+ NULL);
+ XtRealizeWidget(toplevel);
+}
+
+
+/***************************************************************************
+ *
+ *
+ */
+static long
+read_password(password, pwsize)
+ char *password;
+ int *pwsize;
+{
+ XEvent event;
+ char *text_val;
+
+ /* OK, this next part is gross... but this is due to the fact that */
+ /* this is not your traditional X program, which would be event */
+ /* driven. Instead, this program is more 'CLI' in nature, so we */
+ /* handle the dialogs synchronously... */
+
+ XtVaSetValues(prompt_text, XmNmaxLength, *pwsize, XmNvalue, "", NULL);
+ for (looping=1; looping; )
+ {
+ XtAppNextEvent(app_con, &event);
+ XtDispatchEvent(&event);
+ }
+ XtVaGetValues(prompt_text, XmNvalue, &text_val, NULL);
+ *pwsize = strlen(text_val);
+ strcpy(password, text_val);
+ memset(text_val, 0, *pwsize);
+ XtVaSetValues(prompt_text, XmNvalue, text_val, NULL);
+ return(0);
+}
+
+
+/***************************************************************************
+ *
+ *
+ */
+void
+display_intro_message(fmt_string, arg_string)
+ char *fmt_string;
+ char *arg_string;
+{
+ XmString xmstr;
+ char buf[1024];
+
+ sprintf(buf, fmt_string, arg_string);
+
+ xmstr = XmStringCreateLtoR(buf, XmSTRING_DEFAULT_CHARSET);
+ XtVaSetValues(main_lbl, XmNlabelString, xmstr, NULL);
+ XmStringFree(xmstr);
+ XtManageChild(main_lbl);
+}
+
+
+long
+read_old_password(context, password, pwsize)
+ krb5_context context;
+ char *password;
+ int *pwsize;
+{
+ long code;
+
+ XtManageChild(old_lbl);
+ code = read_password(password, pwsize);
+ SetWatchCursor();
+ return code;
+}
+
+long
+read_new_password(server_handle, password, pwsize, msg_ret, princ)
+ void *server_handle;
+ char *password;
+ int *pwsize;
+ char *msg_ret;
+ krb5_principal princ;
+{
+ char *password2 = (char *) malloc(*pwsize * sizeof(char));
+ int pwsize2 = *pwsize;
+
+ SetStandardCursor();
+
+ if (password2 == NULL)
+ {
+ strcpy(msg_ret, error_message(ENOMEM));
+ SetWatchCursor();
+ return(ENOMEM);
+ }
+
+ XtManageChild(new_lbl); XtUnmanageChild(old_lbl);
+ read_password(password, pwsize);
+ XtManageChild(again_lbl); XtUnmanageChild(new_lbl);
+ read_password(password2, &pwsize2);
+
+ if (strcmp(password, password2))
+ {
+ memset(password, 0, *pwsize);
+
+ memset(password2, 0, pwsize2);
+ free(password2);
+
+ strcpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH));
+ SetWatchCursor();
+ return(KRB5_LIBOS_BADPWDMATCH);
+ }
+
+ memset(password2, 0, pwsize2);
+ free(password2);
+
+ SetWatchCursor();
+ return (ovsec_kadm_chpass_principal_util(server_handle, princ, password,
+ NULL /* don't need new pw back */,
+ msg_ret));
+}
+
+
+/***************************************************************************
+ *
+ *
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_context context;
+ int code;
+
+ initialize_kpasswd_strings();
+
+ whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0];
+
+ (void) set_com_err_hook(motif_com_err);
+
+ create_widgets(&argc, argv);
+ XmProcessTraversal(prompt_text, XmTRAVERSE_CURRENT);
+
+ if (retval = krb5_init_context(&context)) {
+ com_err(whoami, retval, "initializing krb5 context");
+ exit(retval);
+ }
+
+ while (1)
+ {
+ retval = kpasswd(context, argc, argv);
+ SetStandardCursor();
+
+ if (!retval)
+ com_err(0, 0, string_text(KPW_STR_PASSWORD_CHANGED));
+
+ if (retval == 0) /* 0 is success, so presumably the user */
+ /* is done. */
+ XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT);
+
+ if ((retval == 1) || /* the rest are "fatal", so we should */
+ (retval == 3) || /* "force" the user to quit... */
+ (retval == 6) ||
+ (retval == 7))
+ {
+ XtSetSensitive(prompt_text, FALSE);
+ XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT);
+ XtAppMainLoop(app_con);
+ }
+ }
+
+ /* NOTREACHED */
+ exit(retval);
+}
diff --git a/src/kadmin/scripts/inst-hdrs.sh b/src/kadmin/scripts/inst-hdrs.sh
new file mode 100644
index 000000000..242be89e9
--- /dev/null
+++ b/src/kadmin/scripts/inst-hdrs.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+dir=$1; shift
+while [ $# -gt 0 ]; do
+ file=$1
+ cmp -s $file $dir/$file
+ if [ $? != 0 ]; then
+ echo "+ rm $dir/$file"
+ rm -f $dir/$file
+ echo "+ cp $file $dir/$file"
+ cp $file $dir/$file
+ fi
+ shift
+done
diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in
new file mode 100644
index 000000000..41bea4f45
--- /dev/null
+++ b/src/kadmin/server/Makefile.in
@@ -0,0 +1,15 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+PROG = kadmind
+OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o misc.o server_glue_v1.o
+
+all:: $(PROG)
+
+$(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/server/Makefile.ov b/src/kadmin/server/Makefile.ov
new file mode 100644
index 000000000..4f8e489fb
--- /dev/null
+++ b/src/kadmin/server/Makefile.ov
@@ -0,0 +1,39 @@
+TOP = ..
+include $(TOP)/config.mk/template
+CFLAGS := $(CFLAGS)
+
+ifdef KRB5B4
+CFLAGS += -DKRB5B4
+endif
+
+PROG := kadmind
+SRCS := kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c misc.c server_glue_v1.c
+OBJS := kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o misc.o server_glue_v1.o
+LIBS := $(LIBADMSRV) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBDYN) \
+ $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDB) \
+ $(NDBMLIB) $(NETLIB) $(BSDLIB) $(REGEXLIB)
+
+expand InstallServer
+expand Depend
+
+clean::
+ $(CLEAN) acls.c
+
+expand Saber
+
+SABER_LIBS := $(LIBDB) $(LIBGSSAPI_KRB5) $(LIBDYN) $(LIBKDB5) \
+ $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) $(LIBCOM_ERR)
+SABER_FLAGS := -G
+
+saber::
+ #cd ../../rpc
+ #make saber
+ #cd ../admin/lib/adb
+ #make saber
+ #cd ../common
+ #make saber
+ #cd ../server
+ #make saber
+ #cd ../../server
+ #load /usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.4.5/libgcc.a
+ #load $(SABER_FLAGS) $(LDFLAGS) $(GSSLIB) $(SABER_LIBS)
diff --git a/src/kadmin/server/acls.l b/src/kadmin/server/acls.l
new file mode 100644
index 000000000..aee4801e9
--- /dev/null
+++ b/src/kadmin/server/acls.l
@@ -0,0 +1,190 @@
+%{
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ *
+ * $Log$
+ * Revision 1.3 1996/07/22 20:28:49 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.2.4.1 1996/07/18 03:03:31 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.2.2.1 1996/06/20 21:56:31 marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.2 1993/11/05 07:47:46 bjaspan
+ * add and use cmp_gss_names, fix regexp bug
+ *
+ * Revision 1.1 1993/11/05 07:08:48 bjaspan
+ * Initial revision
+ *
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+enum tokens {
+ NEWLINE = 257,
+ COMMA,
+ SEMI,
+
+ GET = 300,
+ ADD,
+ MODIFY,
+ DELETE,
+
+ ID = 350,
+};
+
+typedef union {
+ char *s;
+} toktype;
+
+toktype tokval;
+int acl_lineno = 0;
+
+%}
+
+%%
+
+\n acl_lineno++;
+[ \t]* ;
+[ ]*#.* ;
+"," return (COMMA);
+";" return (SEMI);
+"get" return (GET);
+"add" return (ADD);
+"modify" return (MODIFY);
+"delete" return (DELETE);
+^[^ \t\n]+ { tokval.s = yytext; return (ID); }
+
+%%
+
+#include <string.h>
+#include <syslog.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+#include <ovsec_admin/admin.h>
+
+typedef struct _entry {
+ gss_name_t gss_name;
+ char *name;
+ u_int privs;
+ struct _entry *next;
+} acl_entry;
+
+static acl_entry *acl_head = NULL;
+
+static void error(char *msg);
+
+int parse_aclfile(FILE *acl_file)
+{
+ OM_uint32 gssstat, minor_stat;
+ gss_buffer_desc in_buf;
+ acl_entry *entry;
+ enum tokens tok;
+
+ yyin = acl_file;
+
+ acl_lineno = 1;
+ while ((tok = yylex()) != 0) {
+ if (tok != ID) {
+ error("expected identifier");
+ goto error;
+ }
+
+ entry = (acl_entry *) malloc(sizeof(acl_entry));
+ if (entry == NULL) {
+ error("out of memory");
+ goto error;
+ }
+ entry->name = strdup(tokval.s);
+ entry->privs = 0;
+ while (1) {
+ switch (tok = yylex()) {
+ case GET:
+ entry->privs |= OVSEC_KADM_PRIV_GET;
+ break;
+ case ADD:
+ entry->privs |= OVSEC_KADM_PRIV_ADD;
+ break;
+ case MODIFY:
+ entry->privs |= OVSEC_KADM_PRIV_MODIFY;
+ break;
+ case DELETE:
+ entry->privs |= OVSEC_KADM_PRIV_DELETE;
+ break;
+ default:
+ error("expected privilege");
+ goto error;
+ }
+ tok = yylex();
+ if (tok == COMMA)
+ continue;
+ else if (tok == SEMI)
+ break;
+ else {
+ error("expected comma or semicolon");
+ goto error;
+ }
+ }
+
+ in_buf.value = entry->name;
+ in_buf.length = strlen(entry->name) + 1;
+ gssstat = gss_import_name(&minor_stat, &in_buf,
+ gss_nt_krb5_name, &entry->gss_name);
+ if (gssstat != GSS_S_COMPLETE) {
+ error("invalid name");
+ goto error;
+ }
+
+ if (acl_head == NULL) {
+ entry->next = NULL;
+ acl_head = entry;
+ } else {
+ entry->next = acl_head;
+ acl_head = entry;
+ }
+ }
+ return 0;
+
+error:
+ return 1;
+}
+
+int acl_check(gss_name_t caller, int priv)
+{
+ acl_entry *entry;
+
+ entry = acl_head;
+ while (entry) {
+ if (cmp_gss_names(entry->gss_name, caller) && entry->privs & priv)
+ return 1;
+ entry = entry->next;
+ }
+ return 0;
+}
+
+int cmp_gss_names(gss_name_t name1, gss_name_t name2)
+{
+ OM_uint32 minor_stat;
+ int eq;
+ (void) gss_compare_name(&minor_stat, name1, name2, &eq);
+ return eq;
+}
+
+static void error(char *msg)
+{
+ syslog(LOG_ERR, "Error while parsing acl file, line %d: %s\n",
+ acl_lineno, msg);
+}
+
+yywrap() { return(1); }
diff --git a/src/kadmin/server/configure.in b/src/kadmin/server/configure.in
new file mode 100644
index 000000000..98492f909
--- /dev/null
+++ b/src/kadmin/server/configure.in
@@ -0,0 +1,17 @@
+AC_INIT(ovsec_kadmd.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+dnl AC_CHECK_FUNCS(waitpid vsprintf)
+dnl AC_CHECK_HEADERS(sys/select.h)
+dnl CHECK_SIGNALS
+dnl CHECK_SETJMP
+dnl CHECK_WAIT_TYPE
+dnl ET_RULES
+USE_KADMSRV_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_KDB5_LIBRARY
+USE_DYN_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c
new file mode 100644
index 000000000..9128821d5
--- /dev/null
+++ b/src/kadmin/server/kadm_rpc_svc.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ *
+ * $Log$
+ * Revision 1.12 1996/07/22 20:28:53 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.11.4.1 1996/07/18 03:03:35 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.11.2.2 1996/07/09 20:07:57 marc
+ * * kadm_rpc_svc.c: renamed <ovsec_admin/foo.h> to <kadm5/foo.h>
+ *
+ * Revision 1.11.2.1 1996/06/20 21:56:44 marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.11 1996/06/17 19:49:28 bjaspan
+ * use krb5_klog_syslog
+ *
+ * Revision 1.10 1996/05/29 21:07:53 bjaspan
+ * be a bit more loud when warning, and don't exit when args can't be freed
+ *
+ * Revision 1.9 1996/05/20 21:34:56 bjaspan
+ * log an error when sendreply fails
+ *
+ * Revision 1.8 1996/05/12 07:06:23 marc
+ * - fixup includes to match beta6
+ *
+ * Revision 1.7 1995/08/01 19:25:59 bjaspan
+ * [secure/1318] allow retrieval of some/all principal/policy names
+ *
+ * Revision 1.6 1994/09/20 16:25:33 bjaspan
+ * [secure-admin/2436: API versioning fixes to various admin files]
+ * [secure-releng/2502: audit secure-admin/2436: random API versioning fixes]
+ *
+ * Sandbox:
+ *
+ * More API versioning stuff -- need to add api_version field to RPC
+ * return structures in addition to calling structures.
+ *
+ * Revision 1.6 1994/09/12 20:19:16 jik
+ * More API versioning stuff -- need to add api_version field to RPC
+ * return structures in addition to calling structures.
+ *
+ * Revision 1.5 1994/08/16 18:55:46 jik
+ * Versioning changes.
+ *
+ * Revision 1.4 1994/04/25 17:05:05 bjaspan
+ * [secure-admin/1832] accept old gssapi number, log error when number
+ * is wrong
+ *
+ * Revision 1.3 1993/11/15 02:30:54 shanzer
+ * added funky procedure header comments.
+ *
+ * Revision 1.2 1993/11/10 23:11:21 bjaspan
+ * added getprivs
+ *
+ * Revision 1.1 1993/11/05 07:09:00 bjaspan
+ * Initial revision
+ *
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <memory.h>
+#include <kadm5/kadm_rpc.h>
+#include <krb5.h>
+#include <kadm5/admin.h>
+
+/*
+ * Function: kadm_1
+ *
+ * Purpose: RPC proccessing procedure.
+ * originally generated from rpcgen
+ *
+ * Arguments:
+ * rqstp (input) rpc request structure
+ * transp (input) rpc transport structure
+ * (input/output)
+ * <return value>
+ *
+ * Requires:
+ * Effects:
+ * Modifies:
+ */
+
+void kadm_1(rqstp, transp)
+ struct svc_req *rqstp;
+ register SVCXPRT *transp;
+{
+ union {
+ cprinc_arg create_principal_1_arg;
+ dprinc_arg delete_principal_1_arg;
+ mprinc_arg modify_principal_1_arg;
+ rprinc_arg rename_principal_1_arg;
+ gprinc_arg get_principal_1_arg;
+ chpass_arg chpass_principal_1_arg;
+ chrand_arg chrand_principal_1_arg;
+ cpol_arg create_policy_1_arg;
+ dpol_arg delete_policy_1_arg;
+ mpol_arg modify_policy_1_arg;
+ gpol_arg get_policy_1_arg;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ if (rqstp->rq_cred.oa_flavor != AUTH_GSSAPI &&
+ rqstp->rq_cred.oa_flavor != AUTH_GSSAPI_COMPAT) {
+ krb5_klog_syslog(LOG_ERR, "Authentication attempt failed: %s, invalid "
+ "RPC authentication flavor %d",
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
+ rqstp->rq_cred.oa_flavor);
+ svcerr_weakauth(transp);
+ return;
+ }
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ (void) svc_sendreply(transp, xdr_void, (char *)NULL);
+ return;
+
+ case CREATE_PRINCIPAL:
+ xdr_argument = xdr_cprinc_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) create_principal_1;
+ break;
+
+ case DELETE_PRINCIPAL:
+ xdr_argument = xdr_dprinc_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) delete_principal_1;
+ break;
+
+ case MODIFY_PRINCIPAL:
+ xdr_argument = xdr_mprinc_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) modify_principal_1;
+ break;
+
+ case RENAME_PRINCIPAL:
+ xdr_argument = xdr_rprinc_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) rename_principal_1;
+ break;
+
+ case GET_PRINCIPAL:
+ xdr_argument = xdr_gprinc_arg;
+ xdr_result = xdr_gprinc_ret;
+ local = (char *(*)()) get_principal_1;
+ break;
+
+ case GET_PRINCS:
+ xdr_argument = xdr_gprincs_arg;
+ xdr_result = xdr_gprincs_ret;
+ local = (char *(*)()) get_princs_1;
+ break;
+
+ case CHPASS_PRINCIPAL:
+ xdr_argument = xdr_chpass_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) chpass_principal_1;
+ break;
+
+ case CHRAND_PRINCIPAL:
+ xdr_argument = xdr_chrand_arg;
+ xdr_result = xdr_chrand_ret;
+ local = (char *(*)()) chrand_principal_1;
+ break;
+
+ case CREATE_POLICY:
+ xdr_argument = xdr_cpol_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) create_policy_1;
+ break;
+
+ case DELETE_POLICY:
+ xdr_argument = xdr_dpol_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) delete_policy_1;
+ break;
+
+ case MODIFY_POLICY:
+ xdr_argument = xdr_mpol_arg;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) modify_policy_1;
+ break;
+
+ case GET_POLICY:
+ xdr_argument = xdr_gpol_arg;
+ xdr_result = xdr_gpol_ret;
+ local = (char *(*)()) get_policy_1;
+ break;
+
+ case GET_POLS:
+ xdr_argument = xdr_gpols_arg;
+ xdr_result = xdr_gpols_ret;
+ local = (char *(*)()) get_pols_1;
+ break;
+
+ case GET_PRIVS:
+ xdr_argument = xdr_u_int32;
+ xdr_result = xdr_getprivs_ret;
+ local = (char *(*)()) get_privs_1;
+ break;
+
+ case INIT:
+ xdr_argument = xdr_u_int32;
+ xdr_result = xdr_generic_ret;
+ local = (char *(*)()) init_1;
+ break;
+
+ default:
+ krb5_klog_syslog(LOG_ERR, "Invalid OVSEC_KADM procedure number: %s, %d",
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
+ rqstp->rq_proc);
+ svcerr_noproc(transp);
+ return;
+ }
+ memset((char *)&argument, 0, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ krb5_klog_syslog(LOG_ERR, "WARNING! Unable to send function results, "
+ "continuing.");
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ krb5_klog_syslog(LOG_ERR, "WARNING! Unable to free arguments, "
+ "continuing.");
+ }
+ return;
+}
diff --git a/src/kadmin/server/misc.c b/src/kadmin/server/misc.c
new file mode 100644
index 000000000..9dc3d9d28
--- /dev/null
+++ b/src/kadmin/server/misc.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <kadm5/adb.h>
+#include <kadm5/server_internal.h>
+#include <krb5/kdb.h>
+#include "misc.h"
+
+/*
+ * Function: chpass_principal_wrapper
+ *
+ * Purpose: wrapper to kadm5_chpass_principal that checks to see if
+ * pw_min_life has been reached. if not it returns an error.
+ * otherwise it calls kadm5_chpass_principal
+ *
+ * Arguments:
+ * principal (input) krb5_principals whose password we are
+ * changing
+ * passoword (input) passowrd we are going to change to.
+ * <return value> 0 on sucsess error code on failure.
+ *
+ * Requires:
+ * kadm5_init to have been run.
+ *
+ * Effects:
+ * calls kadm5_chpass_principal which changes the kdb and the
+ * the admin db.
+ *
+ */
+kadm5_ret_t
+chpass_principal_wrapper(void *server_handle,
+ krb5_principal principal, char *password)
+{
+ krb5_int32 now;
+ kadm5_ret_t ret;
+ kadm5_policy_ent_rec pol;
+ kadm5_principal_ent_rec princ;
+ kadm5_server_handle_t handle = server_handle;
+
+ if (ret = krb5_timeofday(handle->context, &now))
+ return ret;
+
+ if((ret = kadm5_get_principal(handle->lhandle, principal,
+ &princ,
+ KADM5_PRINCIPAL_NORMAL_MASK)) !=
+ KADM5_OK)
+ return ret;
+ if(princ.aux_attributes & KADM5_POLICY) {
+ if((ret=kadm5_get_policy(handle->lhandle,
+ princ.policy, &pol)) != KADM5_OK) {
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return ret;
+ }
+ if((now - princ.last_pwd_change) < pol.pw_min_life &&
+ !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ (void) kadm5_free_policy_ent(handle->lhandle, &pol);
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return KADM5_PASS_TOOSOON;
+ }
+ if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) {
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return ret;
+ }
+ }
+ if (ret = kadm5_free_principal_ent(handle->lhandle, &princ))
+ return ret;
+
+ return kadm5_chpass_principal(server_handle, principal, password);
+}
+
+
+/*
+ * Function: randkey_principal_wrapper
+ *
+ * Purpose: wrapper to kadm5_randkey_principal which checks the
+ passwords min. life.
+ *
+ * Arguments:
+ * principal (input) krb5_principal whose password we are
+ * changing
+ * key (output) new random key
+ * <return value> 0, error code on error.
+ *
+ * Requires:
+ * kadm5_init needs to be run
+ *
+ * Effects:
+ * calls kadm5_randkey_principal
+ *
+ */
+kadm5_ret_t
+randkey_principal_wrapper(void *server_handle,
+ krb5_principal principal,
+ krb5_keyblock **keys, int *n_keys)
+{
+
+ krb5_int32 now;
+ kadm5_ret_t ret;
+ kadm5_policy_ent_rec pol;
+ kadm5_principal_ent_rec princ;
+ kadm5_server_handle_t handle = server_handle;
+
+ if (ret = krb5_timeofday(handle->context, &now))
+ return ret;
+
+ if((ret = kadm5_get_principal(handle->lhandle,
+ principal, &princ,
+ KADM5_PRINCIPAL_NORMAL_MASK)) !=
+ OSA_ADB_OK)
+ return ret;
+ if(princ.aux_attributes & KADM5_POLICY) {
+ if((ret=kadm5_get_policy(handle->lhandle,
+ princ.policy, &pol)) != KADM5_OK) {
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return ret;
+ }
+ if((now - princ.last_pwd_change) < pol.pw_min_life &&
+ !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ (void) kadm5_free_policy_ent(handle->lhandle, &pol);
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return KADM5_PASS_TOOSOON;
+ }
+ if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) {
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return ret;
+ }
+ }
+ if (ret = kadm5_free_principal_ent(handle->lhandle, &princ))
+ return ret;
+ return kadm5_randkey_principal(server_handle, principal, keys, n_keys);
+}
diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h
new file mode 100644
index 000000000..c92f5fe32
--- /dev/null
+++ b/src/kadmin/server/misc.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1994 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ *
+ * $Log$
+ * Revision 1.6 1996/07/22 20:28:56 marc
+ * this commit includes all the changes on the OV_9510_INTEGRATION and
+ * OV_MERGE branches. This includes, but is not limited to, the new openvision
+ * admin system, and major changes to gssapi to add functionality, and bring
+ * the implementation in line with rfc1964. before committing, the
+ * code was built and tested for netbsd and solaris.
+ *
+ * Revision 1.5.4.1 1996/07/18 03:03:40 marc
+ * merged in changes from OV_9510_BP to OV_9510_FINAL1
+ *
+ * Revision 1.5.2.1 1996/06/20 21:57:20 marc
+ * File added to the repository on a branch
+ *
+ * Revision 1.5 1996/05/30 21:13:24 bjaspan
+ * kadm5_get_principal_v1 takes a kadm5_principal_ent_t_v1
+ * add kadm5_get_policy_v1
+ *
+ * Revision 1.4 1996/05/20 21:39:05 bjaspan
+ * rename to kadm5
+ * add kadm5_get_principal_v1
+ *
+ * Revision 1.3 1994/09/13 18:24:41 jik
+ * Back out randkey changes.
+ *
+ * Revision 1.2 1994/09/12 20:26:12 jik
+ * randkey_principal_wrapper now takes a new_kvno option.
+ *
+ * Revision 1.1 1994/08/11 17:00:44 jik
+ * Initial revision
+ *
+ */
+
+kadm5_ret_t chpass_principal_wrapper(void *server_handle,
+ krb5_principal principal,
+ char *password);
+
+kadm5_ret_t randkey_principal_wrapper(void *server_handle,
+ krb5_principal principal,
+ krb5_keyblock **key,
+ int *n_keys);
+
+kadm5_ret_t kadm5_get_principal_v1(void *server_handle,
+ krb5_principal principal,
+ kadm5_principal_ent_t_v1 *ent);
+
+kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name,
+ kadm5_policy_ent_t *ent);
diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c
new file mode 100644
index 000000000..34f233893
--- /dev/null
+++ b/src/kadmin/server/ovsec_kadmd.c
@@ -0,0 +1,762 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <stdio.h>
+#include <signal.h>
+#include <syslog.h>
+#include <sys/types.h>
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h> /* inet_ntoa */
+#include <netdb.h>
+#include <rpc/rpc.h>
+#include <gssapi/gssapi_krb5.h>
+#include <rpc/auth_gssapi.h>
+#include <kadm5/admin.h>
+#include <kadm5/kadm_rpc.h>
+#include <string.h>
+
+#ifdef PURIFY
+#include "purify.h"
+
+int signal_pure_report = 0;
+int signal_pure_clear = 0;
+void request_pure_report(int);
+void request_pure_clear(int);
+#endif /* PURIFY */
+
+int signal_request_exit = 0;
+int signal_request_reset = 0;
+void request_exit(int);
+void request_reset_db(int);
+void reset_db(void);
+void sig_pipe(int);
+void kadm_svc_run(void);
+
+#define TIMEOUT 15
+
+gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
+void *global_server_handle;
+
+/*
+ * This is a kludge, but the server needs these constants to be
+ * compatible with old clients. They are defined in <kadm5/admin.h>,
+ * but only if USE_KADM5_API_VERSION == 1.
+ */
+#define OVSEC_KADM_ADMIN_SERVICE "ovsec_adm/admin"
+#define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw"
+
+/*
+ * This enables us to set the keytab that gss_acquire_cred uses, but
+ * it also restricts us to linking against the Kv5 GSS-API library.
+ * Since this is *k*admind, that shouldn't be a problem.
+ */
+extern char *krb5_defkeyname;
+
+char *build_princ_name(char *name, char *realm);
+void log_badauth(OM_uint32 major, OM_uint32 minor,
+ struct sockaddr_in *addr, char *data);
+void log_badverf(gss_name_t client_name, gss_name_t server_name,
+ struct svc_req *rqst, struct rpc_msg *msg,
+ char *data);
+void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char
+ *error, char *data);
+void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor);
+void log_badauth_display_status_1(char *m, OM_uint32 code, int type,
+ int rec);
+
+
+/*
+ * Function: usage
+ *
+ * Purpose: print out the server usage message
+ *
+ * Arguments:
+ * Requires:
+ * Effects:
+ * Modifies:
+ */
+
+void usage()
+{
+ fprintf(stderr, "Usage: kadmind [-r realm] [-m] [-nofork] "
+ "[-port port-number]\n");
+ exit(1);
+}
+
+/* XXX yuck. the signal handlers need this */
+static krb5_context context;
+
+int main(int argc, char *argv[])
+{
+ void kadm_1(struct svc_req *, SVCXPRT *);
+ register SVCXPRT *transp;
+ extern char *optarg;
+ extern int optind, opterr;
+ int ret, rlen, nofork, oldnames = 0;
+ OM_uint32 OMret;
+ char *whoami;
+ FILE *acl_file;
+ gss_buffer_desc in_buf;
+ struct servent *srv;
+ struct sockaddr_in addr;
+ int s;
+ short port = 0;
+ auth_gssapi_name names[4];
+ kadm5_config_params params;
+
+ names[0].name = names[1].name = names[2].name = names[3].name = NULL;
+ names[0].type = names[1].type = names[2].type = names[3].type =
+ gss_nt_krb5_name;
+
+#ifdef PURIFY
+ purify_start_batch();
+#endif /* PURIFY */
+ whoami = argv[0];
+
+ nofork = 0;
+
+ memset((char *) &params, 0, sizeof(params));
+
+ argc--; argv++;
+ while (argc) {
+ if (strcmp(*argv, "-r") == 0) {
+ argc--; argv++;
+ if (!argc)
+ usage();
+ params.realm = *argv;
+ params.mask |= KADM5_CONFIG_REALM;
+ argc--; argv++;
+ continue;
+ } else if (strcmp(*argv, "-m") == 0) {
+ params.mkey_from_kbd = 1;
+ params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
+ } else if (strcmp(*argv, "-nofork") == 0) {
+ nofork = 1;
+ } else if(strcmp(*argv, "-port") == 0) {
+ argc--; argv++;
+ if(!argc)
+ usage();
+ params.kadmind_port = atoi(*argv);
+ params.mask |= KADM5_CONFIG_KADMIND_PORT;
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (argc != 0)
+ usage();
+
+ if (ret = krb5_init_context(&context)) {
+ fprintf(stderr, "%s: %s while initializing context, aborting\n",
+ whoami, error_message(ret));
+ exit(1);
+ }
+
+ krb5_klog_init(context, "admin_server", whoami, 1);
+
+ if((ret = kadm5_init("kadmind", NULL,
+ NULL, &params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_2,
+ &global_server_handle)) !=
+ KADM5_OK) {
+ krb5_klog_syslog(LOG_ERR, "%s while initializing, aborting",
+ error_message(ret));
+ fprintf(stderr, "%s: %s while initializing, aborting\n",
+ whoami, error_message(ret));
+ krb5_klog_close();
+ exit(1);
+ }
+
+ if (ret = kadm5_get_config_params(context, NULL, NULL, &params,
+ &params)) {
+ krb5_klog_syslog(LOG_ERR, "%s: %s while initializing, aborting\n",
+ whoami, error_message(ret));
+ fprintf(stderr, "%s: %s while initializing, aborting\n",
+ whoami, error_message(ret));
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE | \
+ KADM5_CONFIG_ADMIN_KEYTAB)
+
+ if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
+ krb5_klog_syslog(LOG_ERR, "%s: Missing required configuration values "
+ "(%x) while initializing, aborting\n", whoami,
+ (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
+ fprintf(stderr, "%s: Missing required configuration values "
+ "(%x) while initializing, aborting\n", whoami,
+ (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS);
+ krb5_klog_close();
+ kadm5_destroy(global_server_handle);
+ exit(1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons(params.kadmind_port);
+
+ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s",
+ error_message(errno));
+ fprintf(stderr, "Cannot create TCP socket: %s",
+ error_message(errno));
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+ #ifdef SO_REUSEADDR
+ /* the old admin server turned on SO_REUSEADDR for non-default
+ port numbers. this was necessary, on solaris, for the tests
+ to work. jhawk argues that the debug and production modes
+ should be the same. I think I agree, so I'm always going to set
+ SO_REUSEADDR. The other option is to have the unit tests wait
+ until the port is useable, or use a different port each time.
+ --marc */
+
+ {
+ int allowed;
+
+ allowed = 1;
+ if (setsockopt(s,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *) &allowed,
+ sizeof(allowed)) < 0) {
+ krb5_klog_syslog(LOG_ERR, "Cannot set SO_REUSEADDR: %s",
+ error_message(errno));
+ fprintf(stderr, "Cannot set SO_REUSEADDR: %s",
+ error_message(errno));
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+ }
+ #endif
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ int oerrno = errno;
+ fprintf(stderr, "%s: Cannot bind socket.\n", whoami);
+ fprintf(stderr, "bind: %s\n", error_message(oerrno));
+ errno = oerrno;
+ krb5_klog_syslog(LOG_ERR, "Cannot bind socket: %m");
+ if(oerrno == EADDRINUSE) {
+ char *w = strrchr(whoami, '/');
+ if (w) {
+ w++;
+ }
+ else {
+ w = whoami;
+ }
+ fprintf(stderr,
+"This probably means that another %s process is already\n"
+"running, or that another program is using the server port (number %d)\n"
+"after being assigned it by the RPC portmap deamon. If another\n"
+"%s is already running, you should kill it before\n"
+"restarting the server. If, on the other hand, another program is\n"
+"using the server port, you should kill it before running\n"
+"%s, and ensure that the conflict does not occur in the\n"
+"future by making sure that %s is started on reboot\n"
+ "before portmap.\n", w, ntohs(addr.sin_port), w, w, w);
+ krb5_klog_syslog(LOG_ERR, "Check for already-running %s or for "
+ "another process using port %d", w,
+ htons(addr.sin_port));
+ }
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+ transp = svctcp_create(s, 0, 0);
+ if(transp == NULL) {
+ fprintf(stderr, "%s: Cannot create RPC service.\n", whoami);
+ krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %m");
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+ if(!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) {
+ fprintf(stderr, "%s: Cannot register RPC service.\n", whoami);
+ krb5_klog_syslog(LOG_ERR, "Cannot register RPC service, failing.");
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+ names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm);
+ names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm);
+ names[2].name = build_princ_name(OVSEC_KADM_ADMIN_SERVICE, params.realm);
+ names[3].name = build_princ_name(OVSEC_KADM_CHANGEPW_SERVICE,
+ params.realm);
+ if (names[0].name == NULL || names[1].name == NULL ||
+ names[2].name == NULL || names[3].name == NULL) {
+ krb5_klog_syslog(LOG_ERR, "Cannot initialize GSS-API authentication, "
+ "failing.");
+ fprintf(stderr, "%s: Cannot initialize GSS-API authentication.\n",
+ whoami);
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+ krb5_defkeyname = params.admin_keytab;
+
+ /*
+ * Try to acquire creds for the old OV services as well as the
+ * new names, but if that fails just fall back on the new names.
+ */
+ if (_svcauth_gssapi_set_names(names, 4) == TRUE)
+ oldnames++;
+ if (!oldnames && _svcauth_gssapi_set_names(names, 2) == FALSE) {
+ krb5_klog_syslog(LOG_ERR, "Cannot initialize GSS-API authentication, "
+ "failing.");
+ fprintf(stderr, "%s: Cannot initialize GSS-API authentication.\n",
+ whoami);
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+ /* if set_names succeeded, this will too */
+ in_buf.value = names[1].name;
+ in_buf.length = strlen(names[1].name) + 1;
+ (void) gss_import_name(&OMret, &in_buf, gss_nt_krb5_name,
+ &gss_changepw_name);
+ if (oldnames) {
+ in_buf.value = names[3].name;
+ in_buf.length = strlen(names[3].name) + 1;
+ (void) gss_import_name(&OMret, &in_buf, gss_nt_krb5_name,
+ &gss_oldchangepw_name);
+ }
+
+ _svcauth_gssapi_set_log_badauth_func(log_badauth, NULL);
+ _svcauth_gssapi_set_log_badverf_func(log_badverf, NULL);
+ _svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL);
+
+ if (ret = acl_init(context, 0, params.acl_file)) {
+ krb5_klog_syslog(LOG_ERR, "Cannot initialize acl file: %s",
+ error_message(ret));
+ fprintf(stderr, "%s: Cannot initialize acl file: %s\n",
+ whoami, error_message(ret));
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+ if (!nofork && (ret = daemon(0, 0))) {
+ ret = errno;
+ krb5_klog_syslog(LOG_ERR, "Cannot detach from tty: %s", error_message(ret));
+ fprintf(stderr, "%s: Cannot detach from tty: %s\n",
+ whoami, error_message(ret));
+ kadm5_destroy(global_server_handle);
+ krb5_klog_close();
+ exit(1);
+ }
+
+ signal(SIGINT, request_exit);
+ signal(SIGTERM, request_exit);
+ signal(SIGQUIT, request_exit);
+ signal(SIGHUP, request_reset_db);
+ signal(SIGPIPE, sig_pipe);
+#ifdef PURIFY
+ signal(SIGUSR1, request_pure_report);
+ signal(SIGUSR2, request_pure_clear);
+#endif /* PURIFY */
+ krb5_klog_syslog(LOG_INFO, "starting");
+
+ kadm_svc_run();
+ krb5_klog_syslog(LOG_INFO, "finished, exiting");
+ kadm5_destroy(global_server_handle);
+ close(s);
+ krb5_klog_close();
+ exit(2);
+}
+
+/*
+ * Function: kadm_svc_run
+ *
+ * Purpose: modified version of sunrpc svc_run.
+ * which closes the database every TIMEOUT seconds.
+ *
+ * Arguments:
+ * Requires:
+ * Effects:
+ * Modifies:
+ */
+
+void kadm_svc_run(void)
+{
+ fd_set rfd;
+ int sz = _rpc_dtablesize();
+ struct timeval timeout;
+
+ while(signal_request_exit == 0) {
+ if (signal_request_reset)
+ reset_db();
+#ifdef PURIFY
+ if (signal_pure_report) /* check to see if a report */
+ /* should be dumped... */
+ {
+ purify_new_reports();
+ signal_pure_report = 0;
+ }
+ if (signal_pure_clear) /* ...before checking whether */
+ /* the info should be cleared. */
+ {
+ purify_clear_new_reports();
+ signal_pure_clear = 0;
+ }
+#endif /* PURIFY */
+ timeout.tv_sec = TIMEOUT;
+ timeout.tv_usec = 0;
+ rfd = svc_fdset;
+ switch(select(sz, (fd_set *) &rfd, NULL, NULL, &timeout)) {
+ case -1:
+ if(errno == EINTR)
+ continue;
+ perror("select");
+ return;
+ case 0:
+ reset_db();
+ break;
+ default:
+ svc_getreqset(&rfd);
+ }
+ }
+}
+
+#ifdef PURIFY
+/*
+ * Function: request_pure_report
+ *
+ * Purpose: sets flag saying the server got a signal and that it should
+ * dump a purify report when convenient.
+ *
+ * Arguments:
+ * Requires:
+ * Effects:
+ * Modifies:
+ * sets signal_pure_report to one
+ */
+
+void request_pure_report(int signum)
+{
+ krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report");
+ signal_pure_report = 1;
+ return;
+}
+
+/*
+ * Function: request_pure_clear
+ *
+ * Purpose: sets flag saying the server got a signal and that it should
+ * dump a purify report when convenient, then clear the
+ * purify tables.
+ *
+ * Arguments:
+ * Requires:
+ * Effects:
+ * Modifies:
+ * sets signal_pure_report to one
+ * sets signal_pure_clear to one
+ */
+
+void request_pure_clear(int signum)
+{
+ krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report and clear the old Purify info");
+ signal_pure_report = 1;
+ signal_pure_clear = 1;
+ return;
+}
+#endif /* PURIFY */
+
+/*
+ * Function: request_reset_db
+ *
+ * Purpose: sets flag saying the server got a signal and that it should
+ * reset the database files when convenient.
+ *
+ * Arguments:
+ * Requires:
+ * Effects:
+ * Modifies:
+ * sets signal_request_reset to one
+ */
+
+void request_reset_db(int signum)
+{
+ krb5_klog_syslog(LOG_DEBUG, "Got signal to request resetting the databases");
+ signal_request_reset = 1;
+ return;
+}
+
+/*
+ * Function: reset-db
+ *
+ * Purpose: flushes the currently opened database files to disk.
+ *
+ * Arguments:
+ * Requires:
+ * Effects:
+ *
+ * Currently, just sets signal_request_reset to 0. The kdb and adb
+ * libraries used to be sufficiently broken that it was prudent to
+ * close and reopen the databases periodically. They are no longer
+ * that broken, so this function is not necessary.
+ */
+void reset_db(void)
+{
+#ifdef notdef
+ kadm5_ret_t ret;
+
+ if (ret = kadm5_flush(global_server_handle)) {
+ krb5_klog_syslog(LOG_ERR, "FATAL ERROR! %s while flushing databases. "
+ "Databases may be corrupt! Aborting.",
+ error_message(ret));
+ krb5_klog_close();
+ exit(3);
+ }
+#endif
+
+ signal_request_reset = 0;
+ return;
+}
+
+/*
+ * Function: request-exit
+ *
+ * Purpose: sets flags saying the server got a signal and that it
+ * should exit when convient.
+ *
+ * Arguments:
+ * Requires:
+ * Effects:
+ * modifies signal_request_exit which ideally makes the server exit
+ * at some point.
+ *
+ * Modifies:
+ * signal_request_exit
+ */
+
+void request_exit(int signum)
+{
+ krb5_klog_syslog(LOG_DEBUG, "Got signal to request exit");
+ signal_request_exit = 1;
+ return;
+}
+
+/*
+ * Function: sig_pipe
+ *
+ * Purpose: SIGPIPE handler
+ *
+ * Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns,
+ * thus causing the read() or write() to fail and, presumable, the RPC
+ * to recover. Otherwise, the process aborts.
+ */
+void sig_pipe(int unused)
+{
+ krb5_klog_syslog(LOG_NOTICE, "Warning: Received a SIGPIPE; probably a "
+ "client aborted. Continuing.");
+ return;
+}
+
+/*
+ * Function: build_princ_name
+ *
+ * Purpose: takes a name and a realm and builds a string that can be
+ * consumed by krb5_parse_name.
+ *
+ * Arguments:
+ * name (input) name to be part of principal
+ * realm (input) realm part of principal
+ * <return value> char * pointing to "name@realm"
+ *
+ * Requires:
+ * name be non-null.
+ *
+ * Effects:
+ * Modifies:
+ */
+
+char *build_princ_name(char *name, char *realm)
+{
+ char *fullname;
+
+ fullname = (char *) malloc(strlen(name) + 1 +
+ (realm ? strlen(realm) + 1 : 0));
+ if (fullname == NULL)
+ return NULL;
+ if (realm)
+ sprintf(fullname, "%s@%s", name, realm);
+ else
+ strcpy(fullname, name);
+ return fullname;
+}
+
+/*
+ * Function: log_badverf
+ *
+ * Purpose: Call from GSS-API Sun RPC for garbled/forged/replayed/etc
+ * messages.
+ *
+ * Argiments:
+ * client_name (r) GSS-API client name
+ * server_name (r) GSS-API server name
+ * rqst (r) RPC service request
+ * msg (r) RPC message
+ * data (r) arbitrary data (NULL), not used
+ *
+ * Effects:
+ *
+ * Logs the invalid request via krb5_klog_syslog(); see functional spec for
+ * format.
+ */
+void log_badverf(gss_name_t client_name, gss_name_t server_name,
+ struct svc_req *rqst, struct rpc_msg *msg, char
+ *data)
+{
+ static const char *const proc_names[] = {
+ "kadm5_create_principal",
+ "kadm5_delete_principal",
+ "kadm5_modify_principal",
+ "kadm5_rename_principal",
+ "kadm5_get_principal",
+ "kadm5_chpass_principal",
+ "kadm5_randkey_principal",
+ "kadm5_create_policy",
+ "kadm5_delete_policy",
+ "kadm5_modify_policy",
+ "kadm5_get_policy",
+ "kadm5_get_privs",
+ };
+ OM_uint32 minor;
+ gss_buffer_desc client, server;
+ gss_OID gss_type;
+ char *a;
+
+ (void) gss_display_name(&minor, client_name, &client, &gss_type);
+ (void) gss_display_name(&minor, server_name, &server, &gss_type);
+ a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr);
+
+ krb5_klog_syslog(LOG_NOTICE, "WARNING! Forged/garbled request: %s, "
+ "claimed client = %s, server = %s, addr = %s",
+ proc_names[msg->rm_call.cb_proc], client.value,
+ server.value, a);
+
+ (void) gss_release_buffer(&minor, &client);
+ (void) gss_release_buffer(&minor, &server);
+}
+
+/*
+ * Function: log_miscerr
+ *
+ * Purpose: Callback from GSS-API Sun RPC for miscellaneous errors
+ *
+ * Arguments:
+ * rqst (r) RPC service request
+ * msg (r) RPC message
+ * error (r) error message from RPC
+ * data (r) arbitrary data (NULL), not used
+ *
+ * Effects:
+ *
+ * Logs the error via krb5_klog_syslog(); see functional spec for
+ * format.
+ */
+void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg,
+ char *error, char *data)
+{
+ char *a;
+
+ a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr);
+ krb5_klog_syslog(LOG_NOTICE, "Miscellaneous RPC error: %s, %s", a, error);
+}
+
+
+
+/*
+ * Function: log_badauth
+ *
+ * Purpose: Callback from GSS-API Sun RPC for authentication
+ * failures/errors.
+ *
+ * Arguments:
+ * major (r) GSS-API major status
+ * minor (r) GSS-API minor status
+ * addr (r) originating address
+ * data (r) arbitrary data (NULL), not used
+ *
+ * Effects:
+ *
+ * Logs the GSS-API error via krb5_klog_syslog(); see functional spec for
+ * format.
+ */
+void log_badauth(OM_uint32 major, OM_uint32 minor,
+ struct sockaddr_in *addr, char *data)
+{
+ char *a;
+
+ /* Authentication attempt failed: <IP address>, <GSS-API error */
+ /* strings> */
+
+ a = inet_ntoa(addr->sin_addr);
+
+ krb5_klog_syslog(LOG_NOTICE, "Authentication attempt failed: %s, GSS-API "
+ "error strings are:", a);
+ log_badauth_display_status(" ", major, minor);
+ krb5_klog_syslog(LOG_NOTICE, " GSS-API error strings complete.\n");
+}
+
+void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor)
+{
+ log_badauth_display_status_1(msg, major, GSS_C_GSS_CODE, 0);
+ log_badauth_display_status_1(msg, minor, GSS_C_MECH_CODE, 0);
+}
+
+void log_badauth_display_status_1(char *m, OM_uint32 code, int type,
+ int rec)
+{
+ OM_uint32 gssstat, minor_stat;
+ gss_buffer_desc msg;
+ int msg_ctx;
+
+ msg_ctx = 0;
+ while (1) {
+ gssstat = gss_display_status(&minor_stat, code,
+ type, GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ if (gssstat != GSS_S_COMPLETE) {
+ if (!rec) {
+ log_badauth_display_status_1(m,gssstat,GSS_C_GSS_CODE,1);
+ log_badauth_display_status_1(m, minor_stat,
+ GSS_C_MECH_CODE, 1);
+ } else
+ krb5_klog_syslog(LOG_ERR, "GSS-API authentication error %s: "
+ "recursive failure!\n", msg);
+ return;
+ }
+
+ krb5_klog_syslog(LOG_NOTICE, "%s %s\n", m, (char *)msg.value);
+ (void) gss_release_buffer(&minor_stat, &msg);
+
+ if (!msg_ctx)
+ break;
+ }
+}
diff --git a/src/kadmin/server/server_glue_v1.c b/src/kadmin/server/server_glue_v1.c
new file mode 100644
index 000000000..c0bec26e6
--- /dev/null
+++ b/src/kadmin/server/server_glue_v1.c
@@ -0,0 +1,31 @@
+#define USE_KADM5_API_VERSION 1
+#include <kadm5/admin.h>
+
+/*
+ * In server_stubs.c, kadmind has to be able to call kadm5 functions
+ * with the arguments appropriate for any api version. Because of the
+ * prototypes in admin.h, however, the compiler will only allow one
+ * set of arguments to be passed. This file exports the old api
+ * definitions with a different name, so they can be called from
+ * server_stubs.c, and just passes on the call to the real api
+ * function; it uses the old api version, however, so it can actually
+ * call the real api functions whereas server_stubs.c cannot.
+ *
+ * This is most useful for functions like kadm5_get_principal that
+ * take a different number of arguments based on API version. For
+ * kadm5_get_policy, the same thing could be accomplished with
+ * typecasts instead.
+ */
+
+kadm5_ret_t kadm5_get_principal_v1(void *server_handle,
+ krb5_principal principal,
+ kadm5_principal_ent_t_v1 *ent)
+{
+ return kadm5_get_principal(server_handle, principal, ent);
+}
+
+kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name,
+ kadm5_policy_ent_t *ent)
+{
+ return kadm5_get_policy(server_handle, name, ent);
+}
diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c
new file mode 100644
index 000000000..8107160af
--- /dev/null
+++ b/src/kadmin/server/server_stubs.c
@@ -0,0 +1,1045 @@
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ * $Header$
+ */
+
+#if !defined(lint) && !defined(__CODECENTER__)
+static char *rcsid = "$Header$";
+#endif
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h> /* for gss_nt_krb5_name */
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include <kadm5/kadm_rpc.h>
+#include <kadm5/server_internal.h>
+#include <kadm5/server_acl.h>
+#include <syslog.h>
+#include "misc.h"
+
+#define LOG_UNAUTH "Unauthorized request: %s, %s, client=%s, service=%s, addr=%s"
+#define LOG_DONE "Request: %s, %s, %s, client=%s, service=%s, addr=%s"
+
+extern gss_name_t gss_changepw_name;
+extern gss_name_t gss_oldchangepw_name;
+extern void * global_server_handle;
+
+#define CHANGEPW_SERVICE(rqstp) \
+ (cmp_gss_names(acceptor_name(rqstp->rq_svccred), gss_changepw_name) |\
+ (gss_oldchangepw_name && \
+ cmp_gss_names(acceptor_name(rqstp->rq_svccred), \
+ gss_oldchangepw_name)))
+
+int cmp_gss_names(gss_name_t n1, gss_name_t n2)
+{
+ OM_uint32 emaj, emin;
+ int equal;
+
+ if (GSS_ERROR(emaj = gss_compare_name(&emin, n1, n2, &equal)))
+ return(0);
+
+ return(equal);
+}
+
+/*
+ * Function check_handle
+ *
+ * Purpose: Check a server handle and return a com_err code if it is
+ * invalid or 0 if it is valid.
+ *
+ * Arguments:
+ *
+ * handle The server handle.
+ */
+
+static int check_handle(void *handle)
+{
+ CHECK_HANDLE(handle);
+ return 0;
+}
+
+/*
+ * Function: new_server_handle
+ *
+ * Purpose: Constructs a server handle suitable for passing into the
+ * server library API functions, by folding the client's API version
+ * and calling principal into the server handle returned by
+ * kadm5_init.
+ *
+ * Arguments:
+ * api_version (input) The API version specified by the client
+ * rqstp (input) The RPC request
+ * handle (output) The returned handle
+ * <return value> (output) An error code, or 0 if no error occurred
+ *
+ * Effects:
+ * Returns a pointer to allocated storage containing the server
+ * handle. If an error occurs, then no allocated storage is
+ * returned, and the return value of the function will be a
+ * non-zero com_err code.
+ *
+ * The allocated storage for the handle should be freed with
+ * free_server_handle (see below) when it is no longer needed.
+ */
+
+static kadm5_ret_t new_server_handle(krb5_ui_4 api_version,
+ struct svc_req *rqstp,
+ kadm5_server_handle_t
+ *out_handle)
+{
+ kadm5_server_handle_t handle;
+
+ if (! (handle = (kadm5_server_handle_t)
+ malloc(sizeof(*handle))))
+ return ENOMEM;
+
+ *handle = *(kadm5_server_handle_t)global_server_handle;
+ handle->api_version = api_version;
+
+ if (! gss_to_krb5_name(handle, rqstp->rq_clntcred,
+ &handle->current_caller)) {
+ free(handle);
+ return KADM5_FAILURE;
+ }
+
+ *out_handle = handle;
+ return 0;
+}
+
+/*
+ * Function: free_server_handle
+ *
+ * Purpose: Free handle memory allocated by new_server_handle
+ *
+ * Arguments:
+ * handle (input/output) The handle to free
+ */
+static void free_server_handle(kadm5_server_handle_t handle)
+{
+ krb5_free_principal(handle->context, handle->current_caller);
+ free(handle);
+}
+
+/*
+ * Function: setup_gss_names
+ *
+ * Purpose: Create printable representations of the client and server
+ * names.
+ *
+ * Arguments:
+ * rqstp (r) the RPC request
+ * client_name (w) the gss_buffer_t for the client name
+ * server_name (w) the gss_buffer_t for the server name
+ *
+ * Effects:
+ *
+ * Unparses the client and server names into client_name and
+ * server_name, both of which must be freed by the caller. Returns 0
+ * on success and -1 on failure.
+ */
+int setup_gss_names(struct svc_req *rqstp,
+ gss_buffer_desc *client_name,
+ gss_buffer_desc *server_name)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t server_gss_name;
+
+ if (gss_name_to_string(rqstp->rq_clntcred, client_name) != 0)
+ return -1;
+ maj_stat = gss_inquire_context(&min_stat, rqstp->rq_svccred, NULL,
+ &server_gss_name, NULL, NULL, NULL,
+ NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ gss_release_buffer(&min_stat, client_name);
+ return -1;
+ }
+ if (gss_name_to_string(server_gss_name, server_name) != 0) {
+ gss_release_buffer(&min_stat, client_name);
+ return -1;
+ }
+ return 0;
+}
+
+gss_name_t acceptor_name(gss_ctx_id_t context)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t name;
+
+ maj_stat = gss_inquire_context(&min_stat, context, NULL, &name,
+ NULL, NULL, NULL, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ return NULL;
+ return name;
+}
+
+int cmp_gss_krb5_name(kadm5_server_handle_t handle,
+ gss_name_t gss_name, krb5_principal princ)
+{
+ krb5_principal princ2;
+ int stat;
+
+ if (! gss_to_krb5_name(handle, gss_name, &princ2))
+ return 0;
+ stat = krb5_principal_compare(handle->context, princ, princ2);
+ krb5_free_principal(handle->context, princ2);
+ return stat;
+}
+
+int gss_to_krb5_name(kadm5_server_handle_t handle,
+ gss_name_t gss_name, krb5_principal *princ)
+{
+ OM_uint32 stat, minor_stat;
+ gss_buffer_desc gss_str;
+ gss_OID gss_type;
+ int success;
+
+ stat = gss_display_name(&minor_stat, gss_name, &gss_str, &gss_type);
+ if ((stat != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name))
+ return 0;
+ success = (krb5_parse_name(handle->context, gss_str.value, princ) == 0);
+ gss_release_buffer(&minor_stat, &gss_str);
+ return success;
+}
+int
+gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str)
+{
+ OM_uint32 stat, minor_stat;
+ gss_OID gss_type;
+ int ret;
+
+ stat = gss_display_name(&minor_stat, gss_name, str, &gss_type);
+ if ((stat != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name))
+ return 1;
+ return 0;
+}
+
+generic_ret *
+create_principal_1(cprinc_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name, service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg);
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_ADD,
+ arg->rec.principal)) {
+ ret.code = KADM5_AUTH_ADD;
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_principal",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ ret.code = kadm5_create_principal((void *)handle,
+ &arg->rec, arg->mask,
+ arg->passwd);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_create_principal",
+ prime_arg,((ret.code == 0) ? "success" :
+ error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ free(prime_arg);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *
+delete_principal_1(dprinc_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ krb5_unparse_name(handle->context, arg->princ, &prime_arg);
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_DELETE,
+ arg->princ)) {
+ ret.code = KADM5_AUTH_DELETE;
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_principal",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ ret.code = kadm5_delete_principal((void *)handle, arg->princ);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_delete_principal", prime_arg,
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free(prime_arg);
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *
+modify_principal_1(mprinc_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg);
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_MODIFY,
+ arg->rec.principal)) {
+ ret.code = KADM5_AUTH_MODIFY;
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_principal",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ ret.code = kadm5_modify_principal((void *)handle, &arg->rec,
+ arg->mask);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_modify_principal",
+ prime_arg, ((ret.code == 0) ? "success" :
+ error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ free(prime_arg);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *
+rename_principal_1(rprinc_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg1,
+ *prime_arg2;
+ char prime_arg[BUFSIZ];
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ krb5_unparse_name(handle->context, arg->src, &prime_arg1);
+ krb5_unparse_name(handle->context, arg->dest, &prime_arg2);
+ sprintf(prime_arg, "%s to %s", prime_arg1, prime_arg2);
+
+ ret.code = KADM5_OK;
+ if (! CHANGEPW_SERVICE(rqstp)) {
+ if (!acl_check(handle->context, rqstp->rq_clntcred,
+ ACL_DELETE, arg->src))
+ ret.code = KADM5_AUTH_DELETE;
+ if (!acl_check(handle->context, rqstp->rq_clntcred,
+ ACL_ADD, arg->dest)) {
+ if (ret.code == KADM5_AUTH_DELETE)
+ ret.code = KADM5_AUTH_INSUFFICIENT;
+ else
+ ret.code = KADM5_AUTH_ADD;
+ }
+ } else
+ ret.code = KADM5_AUTH_INSUFFICIENT;
+ if (ret.code != KADM5_OK) {
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_rename_principal",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ ret.code = kadm5_rename_principal((void *)handle, arg->src,
+ arg->dest);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_rename_principal",
+ prime_arg, ((ret.code == 0) ? "success" :
+ error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ free(prime_arg1);
+ free(prime_arg2);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+gprinc_ret *
+get_principal_1(gprinc_arg *arg, struct svc_req *rqstp)
+{
+ static gprinc_ret ret;
+ kadm5_principal_ent_t_v1 e;
+ char *prime_arg, *funcname;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_gprinc_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ funcname = handle->api_version == KADM5_API_VERSION_1 ?
+ "kadm5_get_principal (V1)" : "kadm5_get_principal";
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ krb5_unparse_name(handle->context, arg->princ, &prime_arg);
+
+ if (! cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ) &&
+ (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_INQUIRE,
+ arg->princ))) {
+ ret.code = KADM5_AUTH_GET;
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname,
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ if (handle->api_version == KADM5_API_VERSION_1) {
+ ret.code = kadm5_get_principal_v1((void *)handle,
+ arg->princ, &e);
+ if(ret.code == KADM5_OK) {
+ memcpy(&ret.rec, e, sizeof(kadm5_principal_ent_rec_v1));
+ free(e);
+ }
+ } else {
+ ret.code = kadm5_get_principal((void *)handle,
+ arg->princ, &ret.rec,
+ arg->mask);
+ }
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname,
+ prime_arg,
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ free(prime_arg);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+gprincs_ret *
+get_princs_1(gprincs_arg *arg, struct svc_req *rqstp)
+{
+ static gprincs_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_gprincs_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ prime_arg = arg->exp;
+ if (prime_arg == NULL)
+ prime_arg = "*";
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_LIST,
+ NULL)) {
+ ret.code = KADM5_AUTH_LIST;
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_principals",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ ret.code = kadm5_get_principals((void *)handle,
+ arg->exp, &ret.princs,
+ &ret.count);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_principals",
+ prime_arg,
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *
+chpass_principal_1(chpass_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ krb5_unparse_name(handle->context, arg->princ, &prime_arg);
+
+ if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) {
+ ret.code = chpass_principal_wrapper((void *)handle, arg->princ,
+ arg->pass);
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ acl_check(handle->context, rqstp->rq_clntcred,
+ ACL_CHANGEPW, arg->princ)) {
+ ret.code = kadm5_chpass_principal((void *)handle, arg->princ,
+ arg->pass);
+ } else {
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_chpass_principal",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ ret.code = KADM5_AUTH_CHANGEPW;
+ }
+
+ if(ret.code != KADM5_AUTH_CHANGEPW) {
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_chpass_principal",
+ prime_arg, ((ret.code == 0) ? "success" :
+ error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+
+ free_server_handle(handle);
+ free(prime_arg);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+chrand_ret *
+chrand_principal_1(chrand_arg *arg, struct svc_req *rqstp)
+{
+ static chrand_ret ret;
+ krb5_keyblock *k;
+ int nkeys;
+ char *prime_arg, *funcname;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_chrand_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ funcname = handle->api_version == KADM5_API_VERSION_1 ?
+ "kadm5_randkey_principal (V1)" : "kadm5_randkey_principal";
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ krb5_unparse_name(handle->context, arg->princ, &prime_arg);
+
+ if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) {
+ ret.code = randkey_principal_wrapper((void *)handle,
+ arg->princ, &k, &nkeys);
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ acl_check(handle->context, rqstp->rq_clntcred,
+ ACL_CHANGEPW, arg->princ)) {
+ ret.code = kadm5_randkey_principal((void *)handle, arg->princ,
+ &k, &nkeys);
+ } else {
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname,
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ ret.code = KADM5_AUTH_CHANGEPW;
+ }
+
+ if(ret.code == KADM5_OK) {
+ if (handle->api_version == KADM5_API_VERSION_1) {
+ krb5_copy_keyblock_contents(handle->context, k, &ret.key);
+ krb5_free_keyblock(handle->context, k);
+ } else {
+ ret.keys = k;
+ ret.n_keys = nkeys;
+ }
+ }
+
+ if(ret.code != KADM5_AUTH_CHANGEPW) {
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname,
+ prime_arg, ((ret.code == 0) ? "success" :
+ error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ free(prime_arg);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *
+create_policy_1(cpol_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ prime_arg = arg->rec.policy;
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_ADD, NULL)) {
+ ret.code = KADM5_AUTH_ADD;
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_policy",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+
+ } else {
+ ret.code = kadm5_create_policy((void *)handle, &arg->rec,
+ arg->mask);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_create_policy",
+ ((prime_arg == NULL) ? "(null)" : prime_arg),
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *
+delete_policy_1(dpol_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ prime_arg = arg->name;
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_DELETE, NULL)) {
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_policy",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ ret.code = KADM5_AUTH_DELETE;
+ } else {
+ ret.code = kadm5_delete_policy((void *)handle, arg->name);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_delete_policy",
+ ((prime_arg == NULL) ? "(null)" : prime_arg),
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *
+modify_policy_1(mpol_arg *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ prime_arg = arg->rec.policy;
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_MODIFY, NULL)) {
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_policy",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ ret.code = KADM5_AUTH_MODIFY;
+ } else {
+ ret.code = kadm5_modify_policy((void *)handle, &arg->rec,
+ arg->mask);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_modify_policy",
+ ((prime_arg == NULL) ? "(null)" : prime_arg),
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+gpol_ret *
+get_policy_1(gpol_arg *arg, struct svc_req *rqstp)
+{
+ static gpol_ret ret;
+ kadm5_ret_t ret2;
+ char *prime_arg, *funcname;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_policy_ent_t e;
+ kadm5_principal_ent_rec caller_ent;
+ krb5_principal caller;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_gpol_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ funcname = handle->api_version == KADM5_API_VERSION_1 ?
+ "kadm5_get_policy (V1)" : "kadm5_get_policy";
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ prime_arg = arg->name;
+
+ ret.code = KADM5_AUTH_GET;
+ if (!CHANGEPW_SERVICE(rqstp) && acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_INQUIRE, NULL))
+ ret.code = KADM5_OK;
+ else {
+ ret.code = kadm5_get_principal(handle->lhandle,
+ handle->current_caller,
+ &caller_ent,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ if (ret.code == KADM5_OK) {
+ if (caller_ent.aux_attributes & KADM5_POLICY &&
+ strcmp(caller_ent.policy, arg->name) == 0) {
+ ret.code = KADM5_OK;
+ } else ret.code = KADM5_AUTH_GET;
+ ret2 = kadm5_free_principal_ent(handle->lhandle,
+ &caller_ent);
+ ret.code = ret.code ? ret.code : ret2;
+ }
+ }
+
+ if (ret.code == KADM5_OK) {
+ if (handle->api_version == KADM5_API_VERSION_1) {
+ ret.code = kadm5_get_policy_v1((void *)handle, arg->name, &e);
+ if(ret.code == KADM5_OK) {
+ memcpy(&ret.rec, e, sizeof(kadm5_policy_ent_rec));
+ free(e);
+ }
+ } else {
+ ret.code = kadm5_get_policy((void *)handle, arg->name,
+ &ret.rec);
+ }
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname,
+ ((prime_arg == NULL) ? "(null)" : prime_arg),
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname,
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+
+}
+
+gpols_ret *
+get_pols_1(gpols_arg *arg, struct svc_req *rqstp)
+{
+ static gpols_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name,
+ service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_gpols_ret, &ret);
+
+ if (ret.code = new_server_handle(arg->api_version, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+ prime_arg = arg->exp;
+ if (prime_arg == NULL)
+ prime_arg = "*";
+
+ if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context,
+ rqstp->rq_clntcred,
+ ACL_LIST, NULL)) {
+ ret.code = KADM5_AUTH_LIST;
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_policies",
+ prime_arg, client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ } else {
+ ret.code = kadm5_get_policies((void *)handle,
+ arg->exp, &ret.pols,
+ &ret.count);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_policies",
+ prime_arg,
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ }
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+getprivs_ret * get_privs_1(krb5_ui_4 *arg, struct svc_req *rqstp)
+{
+ static getprivs_ret ret;
+ char *prime_arg;
+ gss_buffer_desc client_name, service_name;
+ OM_uint32 minor_stat;
+ kadm5_server_handle_t handle;
+
+ xdr_free(xdr_getprivs_ret, &ret);
+
+ if (ret.code = new_server_handle(*arg, rqstp, &handle))
+ return &ret;
+
+ if (ret.code = check_handle((void *)handle)) {
+ free_server_handle(handle);
+ return &ret;
+ }
+
+ ret.api_version = handle->api_version;
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+
+ ret.code = kadm5_get_privs((void *)handle, &ret.privs);
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_privs",
+ client_name.value,
+ ((ret.code == 0) ? "success" : error_message(ret.code)),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ free_server_handle(handle);
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+ return &ret;
+}
+
+generic_ret *init_1(krb5_ui_4 *arg, struct svc_req *rqstp)
+{
+ static generic_ret ret;
+ gss_buffer_desc client_name,
+ service_name;
+ kadm5_server_handle_t handle;
+ OM_uint32 minor_stat;
+
+ xdr_free(xdr_generic_ret, &ret);
+
+ if (ret.code = new_server_handle(*arg, rqstp, &handle))
+ return &ret;
+ if (! (ret.code = check_handle((void *)handle))) {
+ ret.api_version = handle->api_version;
+ }
+
+ free_server_handle(handle);
+
+ if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
+ ret.code = KADM5_FAILURE;
+ return &ret;
+ }
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_DONE,
+ (ret.api_version == KADM5_API_VERSION_1 ?
+ "kadm5_init (V1)" : "kadm5_init"),
+ client_name.value,
+ (ret.code == 0) ? "success" : error_message(ret.code),
+ client_name.value, service_name.value,
+ inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+ gss_release_buffer(&minor_stat, &client_name);
+ gss_release_buffer(&minor_stat, &service_name);
+
+ return(&ret);
+}
diff --git a/src/kadmin/testing/Makefile.ov b/src/kadmin/testing/Makefile.ov
new file mode 100644
index 000000000..206bd8551
--- /dev/null
+++ b/src/kadmin/testing/Makefile.ov
@@ -0,0 +1,8 @@
+# $Id$
+
+TOP = ..
+include $(TOP)/config.mk/template
+
+SUBDIRS=util scripts
+
+expand SubdirTarget
diff --git a/src/kadmin/testing/proto/ChangeLog b/src/kadmin/testing/proto/ChangeLog
new file mode 100644
index 000000000..71959a3b0
--- /dev/null
+++ b/src/kadmin/testing/proto/ChangeLog
@@ -0,0 +1,9 @@
+Mon Jul 15 17:11:35 1996 Marc Horowitz <marc@mit.edu>
+
+ * krb5.conf.proto: specify a default_keytab_name in /krb5
+
+Fri Jul 12 14:46:17 1996 Marc Horowitz <marc@mit.edu>
+
+ * kdc.conf.proto: put the stash file in /krb5, so that the root
+ dir does not need to be writeable. also, the admin system
+ requires a reference in the conf file to admin_keytab
diff --git a/src/kadmin/testing/proto/kdc.conf.proto b/src/kadmin/testing/proto/kdc.conf.proto
new file mode 100644
index 000000000..798d6c51b
--- /dev/null
+++ b/src/kadmin/testing/proto/kdc.conf.proto
@@ -0,0 +1,20 @@
+[kdcdefaults]
+ kdc_ports = 1750
+
+[realms]
+ __REALM__ = {
+ profile = /krb5/krb5.conf
+ database_name = /krb5/kdb5
+ admin_database_name = /krb5/kadb5
+ admin_database_lockfile = /krb5/ovsec_adm.lock
+ admin_keytab = /krb5/ovsec_adm.srvtab
+ key_stash_file = /krb5/.k5.__REALM__
+ acl_file = /krb5/ovsec_adm.acl
+ dict_file = /krb5/ovsec_adm.dict
+ kadmind_port = 1751
+ max_life = 10h 0m 0s
+ max_renewable_life = 7d 0h 0m 0s
+ master_key_type = des-cbc-crc
+ supported_enctypes = des-cbc-crc:normal des-cbc-crc:v4
+ }
+
diff --git a/src/kadmin/testing/proto/krb5.conf.proto b/src/kadmin/testing/proto/krb5.conf.proto
new file mode 100644
index 000000000..6e0c4687d
--- /dev/null
+++ b/src/kadmin/testing/proto/krb5.conf.proto
@@ -0,0 +1,17 @@
+[libdefaults]
+ default_realm = __REALM__
+ default_keytab_name = FILE:/krb5/v5srvtab
+
+[realms]
+ __REALM__ = {
+ kdc = localhost:1750
+ admin_server = localhost:1751
+ }
+
+[domain_realm]
+ localhost = __REALM__
+
+[logging]
+ admin_server = SYSLOG=ERR:LOCAL6
+ kdc = SYSLOG=ERR:LOCAL6
+ default = SYSLOG=ERR:LOCAL6
diff --git a/src/kadmin/testing/proto/ovsec_adm.dict b/src/kadmin/testing/proto/ovsec_adm.dict
new file mode 100644
index 000000000..b54e3a85e
--- /dev/null
+++ b/src/kadmin/testing/proto/ovsec_adm.dict
@@ -0,0 +1,3 @@
+Abyssinia
+Discordianism
+foo
diff --git a/src/kadmin/testing/scripts/ChangeLog b/src/kadmin/testing/scripts/ChangeLog
new file mode 100644
index 000000000..5d7069186
--- /dev/null
+++ b/src/kadmin/testing/scripts/ChangeLog
@@ -0,0 +1,15 @@
+Fri Jul 12 14:48:20 1996 Marc Horowitz <marc@mit.edu>
+
+ * stop_servers_local (true, false): use the path to find these,
+ instead of looking in /bin explicitly.
+
+ * start_servers_local (/usr/tmp): /usr/tmp doesn't exist on some
+ systems. Check for that and /var/tmp, and use the one which
+ exists. (true, false): use the path to find these, instead of
+ looking in /bin explicitly.
+
+ * make-host-keytab.pl.in: perl5 requires that @ in strings be
+ backwhacked. (EDIT_KEYTAB): ovsec_adm_keytab is now kadm5_keytab.
+
+ * init_db: kadmin_create should be kdb5_create
+
diff --git a/src/kadmin/testing/scripts/Makefile.ov b/src/kadmin/testing/scripts/Makefile.ov
new file mode 100644
index 000000000..335b636e7
--- /dev/null
+++ b/src/kadmin/testing/scripts/Makefile.ov
@@ -0,0 +1,19 @@
+# $Id$
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+GEN_SCRIPTS = compare_dump.pl fixup-conf-files.pl make-host-keytab.pl \
+ simple_dump.pl verify_xrunner_report.pl
+
+all:: $(GEN_SCRIPTS)
+
+%.pl: %.pl.in
+ -rm -f $@.tmp
+ echo "#!$(PERL)" > $@.tmp
+ sed 1d $@.in >> $@.tmp
+ chmod +x $@.tmp
+ mv $@.tmp $@
+
+clean::
+ -rm -f $(GEN_SCRIPTS) *.tmp
diff --git a/src/kadmin/testing/scripts/compare_dump.pl.in b/src/kadmin/testing/scripts/compare_dump.pl.in
new file mode 100644
index 000000000..df93df4a0
--- /dev/null
+++ b/src/kadmin/testing/scripts/compare_dump.pl.in
@@ -0,0 +1,242 @@
+#!/usr/local/bin/perl
+
+#
+# $Id$
+#
+
+# $debug = 1;
+
+sub usage { die "usage: $0 before after changes\n";}
+
+sub unique {
+ local(@list) = @_;
+ local(%ary);
+
+ print "unique? ",join(" ",@list),"\n" if $debug;
+
+ foreach (@list) {
+ return(0) if $ary{$_}++;
+ }
+
+ 1;
+}
+
+$before = shift(@ARGV) || &usage;
+$debug++ if $before =~ /^-d/;
+$before = shift(@ARGV) || &usage if $debug;
+$after = shift(@ARGV) || &usage;
+$changes = shift(@ARGV) || &usage;
+@ARGV && &usage;
+
+%policy =
+ (
+ "FIRST",2,
+ "pw_min_life",2,
+ "pw_max_life",3,
+ "pw_min_length",4,
+ "pw_min_classes",5,
+ "pw_history_num",6,
+ "policy_refcnt",7,
+ "LAST",7,
+ );
+
+%princ =
+ (
+ "FIRST",2,
+ "kvno",2,
+ "mod_name",3,
+ "max_life",4,
+ "princ_expire_time",5,
+ "expiration",5,
+ "pw_expiration",6,
+ "attributes",7,
+ "policy",8,
+ "aux_attributes",9,
+ "LAST",9,
+ );
+
+%keytab =
+ (
+ "LAST",-1,
+ );
+
+sub re { # @_ = ($cnt, $line)
+ local($cnt, $line) = @_;
+ local(@fields) = split(' ',$line);
+
+ @list = ('\S+') x $cnt;
+ for $f (@fields[3..$#fields]) {
+ ($f =~ /=/) || die "Bad field: $f in $_";
+ if (!defined($this{$`})) { die "Bad parameter $` in $_"; }
+
+ if (($list[$this{$`}] = $') eq '\S+') {
+ $list[$this{$`}] = '[^\s]+';
+ }
+ }
+
+ join('\s+',@list)."\$";
+}
+
+open(CHANGES, $changes) || die "Couldn't open $changes: $!\n";
+
+while(<CHANGES>) {
+ next if s/^\s*\#\#\!\s*\#//;
+ next if !s/^\s*\#\#\!\s*//;
+
+ split;
+
+ if ($_[1] =~ /princ/) {
+ %this = %princ;
+ $this = "princ";
+ } elsif ($_[1] =~ /policy/) {
+ %this = %policy;
+ $this = "policy";
+ } elsif ($_[1] =~ /keytab/) {
+ %this = %keytab;
+ $this = $_[1];
+ } else {
+ die "Bad line: $_";
+ }
+
+ $cnt = $this{"LAST"}+1;
+
+ if ($_[0] =~ /add/) {
+ $diff{"+$this\t$_[2]"} = &re($cnt,$_);
+ } elsif ($_[0] =~ /delete/) {
+ $diff{"-$this\t$_[2]"} = &re($cnt,$_);
+ } elsif ($_[0] =~ /changefrom/) {
+ $diff{"-$this\t$_[2]"} = &re($cnt,$_);
+ } elsif ($_[0] =~ /changeto/) {
+ $ndiff{"-$this\t$_[2]"} = &re($cnt,$_);
+ } else {
+ die "Bad line: $_";
+ }
+}
+
+close(CHANGES);
+
+if ($debug) {
+ for (keys %diff) {
+ print " %diff: \"$_\" /$diff{$_}/\n";
+ }
+
+ for (keys %ndiff) {
+ print "%ndiff: \"$_\" /$ndiff{$_}/\n";
+ }
+
+ print "\n";
+}
+
+open(DIFF,"gdiff -u0 $before $after|") || die "Couldn't diff: $!\n";
+
+$warnings = 0;
+
+while(<DIFF>) {
+ next if /^\+{3}/;
+ next if /^\-{3}/;
+ next if /^@@/;
+
+ print "LINE: $_" if $debug;
+
+ split;
+
+ $key = "$_[0]\t$_[1]";
+ $re = $diff{$key};
+
+ delete $diff{$key};
+
+ print "%diff: \"$key\" /$re/\n" if $debug;
+
+ if (!$re) {
+ warn "Unexpected: \"$key\"\n";
+ $warnings++;
+ next;
+ }
+
+ if (!/$re/) {
+ warn "Failed: $key\n";
+ $warnings++;
+ next;
+ }
+
+ if ($new = $ndiff{$key}) {
+ delete $ndiff{$key};
+
+ @new = split(/\\s\+/, $new);
+ for ($i=1;$i<@new;$i++) {
+ print "NEW: $new[$i]\n" if $debug;
+
+ if ($new[$i] ne '\S+') {
+ $_[$i] = $new[$i];
+ }
+ }
+ $_[0] =~ s/^\-//;
+ $key =~ s/^\-/\+/;
+
+ $diff{$key} = join("\t",@_);
+ }
+}
+
+close(DIFF);
+
+open(BEFORE, $before) || die "Couldn't open $before: $!\n";
+
+while(<BEFORE>) {
+ next if !/^keytab/;
+
+ split;
+
+ if (!$seen{$key = $_[0]." ".$_[1]}++) {
+ $key =~ s/-\d+$//;
+ $ktkeys{$key} .= " ".$_[2];
+ $kttimes{$key} .= " ".$_[3];
+ }
+}
+
+close(BEFORE);
+
+open(AFTER, $after) || die "Couldn't open $after: $!\n";
+
+while(<AFTER>) {
+ next if !/^keytab/;
+
+ split;
+
+ if (!$seen{$key = $_[0]." ".$_[1]}++) {
+ $key =~ s/-\d+$//;
+ $ktkeys{$key} .= " ".$_[2];
+ $kttimes{$key} .= " ".$_[3];
+ }
+}
+
+close(AFTER);
+
+for (keys %diff) {
+ warn "Unseen: \"$_\" /$diff{$_}/\n";
+ $warnings++;
+}
+
+for (keys %ndiff) {
+ warn "Unseen changes: \"$_\" /$ndiff{$_}/\n";
+ $warnings++;
+}
+
+for (keys %ktkeys) {
+ if (!&unique(split(' ',$ktkeys{$_}))) {
+ warn "Some keys not unique for $_\n";
+ $warnings++;
+ }
+}
+
+for (keys %kttimes) {
+ if (!&unique(split(' ',$kttimes{$_}))) {
+ warn "Some timestamps not unique for $_\n";
+ $warnings++;
+ }
+}
+
+if ($warnings) {
+ warn "$warnings warnings.\n";
+}
+
+exit($warnings);
diff --git a/src/kadmin/testing/scripts/compare_dump.plin b/src/kadmin/testing/scripts/compare_dump.plin
new file mode 100644
index 000000000..df93df4a0
--- /dev/null
+++ b/src/kadmin/testing/scripts/compare_dump.plin
@@ -0,0 +1,242 @@
+#!/usr/local/bin/perl
+
+#
+# $Id$
+#
+
+# $debug = 1;
+
+sub usage { die "usage: $0 before after changes\n";}
+
+sub unique {
+ local(@list) = @_;
+ local(%ary);
+
+ print "unique? ",join(" ",@list),"\n" if $debug;
+
+ foreach (@list) {
+ return(0) if $ary{$_}++;
+ }
+
+ 1;
+}
+
+$before = shift(@ARGV) || &usage;
+$debug++ if $before =~ /^-d/;
+$before = shift(@ARGV) || &usage if $debug;
+$after = shift(@ARGV) || &usage;
+$changes = shift(@ARGV) || &usage;
+@ARGV && &usage;
+
+%policy =
+ (
+ "FIRST",2,
+ "pw_min_life",2,
+ "pw_max_life",3,
+ "pw_min_length",4,
+ "pw_min_classes",5,
+ "pw_history_num",6,
+ "policy_refcnt",7,
+ "LAST",7,
+ );
+
+%princ =
+ (
+ "FIRST",2,
+ "kvno",2,
+ "mod_name",3,
+ "max_life",4,
+ "princ_expire_time",5,
+ "expiration",5,
+ "pw_expiration",6,
+ "attributes",7,
+ "policy",8,
+ "aux_attributes",9,
+ "LAST",9,
+ );
+
+%keytab =
+ (
+ "LAST",-1,
+ );
+
+sub re { # @_ = ($cnt, $line)
+ local($cnt, $line) = @_;
+ local(@fields) = split(' ',$line);
+
+ @list = ('\S+') x $cnt;
+ for $f (@fields[3..$#fields]) {
+ ($f =~ /=/) || die "Bad field: $f in $_";
+ if (!defined($this{$`})) { die "Bad parameter $` in $_"; }
+
+ if (($list[$this{$`}] = $') eq '\S+') {
+ $list[$this{$`}] = '[^\s]+';
+ }
+ }
+
+ join('\s+',@list)."\$";
+}
+
+open(CHANGES, $changes) || die "Couldn't open $changes: $!\n";
+
+while(<CHANGES>) {
+ next if s/^\s*\#\#\!\s*\#//;
+ next if !s/^\s*\#\#\!\s*//;
+
+ split;
+
+ if ($_[1] =~ /princ/) {
+ %this = %princ;
+ $this = "princ";
+ } elsif ($_[1] =~ /policy/) {
+ %this = %policy;
+ $this = "policy";
+ } elsif ($_[1] =~ /keytab/) {
+ %this = %keytab;
+ $this = $_[1];
+ } else {
+ die "Bad line: $_";
+ }
+
+ $cnt = $this{"LAST"}+1;
+
+ if ($_[0] =~ /add/) {
+ $diff{"+$this\t$_[2]"} = &re($cnt,$_);
+ } elsif ($_[0] =~ /delete/) {
+ $diff{"-$this\t$_[2]"} = &re($cnt,$_);
+ } elsif ($_[0] =~ /changefrom/) {
+ $diff{"-$this\t$_[2]"} = &re($cnt,$_);
+ } elsif ($_[0] =~ /changeto/) {
+ $ndiff{"-$this\t$_[2]"} = &re($cnt,$_);
+ } else {
+ die "Bad line: $_";
+ }
+}
+
+close(CHANGES);
+
+if ($debug) {
+ for (keys %diff) {
+ print " %diff: \"$_\" /$diff{$_}/\n";
+ }
+
+ for (keys %ndiff) {
+ print "%ndiff: \"$_\" /$ndiff{$_}/\n";
+ }
+
+ print "\n";
+}
+
+open(DIFF,"gdiff -u0 $before $after|") || die "Couldn't diff: $!\n";
+
+$warnings = 0;
+
+while(<DIFF>) {
+ next if /^\+{3}/;
+ next if /^\-{3}/;
+ next if /^@@/;
+
+ print "LINE: $_" if $debug;
+
+ split;
+
+ $key = "$_[0]\t$_[1]";
+ $re = $diff{$key};
+
+ delete $diff{$key};
+
+ print "%diff: \"$key\" /$re/\n" if $debug;
+
+ if (!$re) {
+ warn "Unexpected: \"$key\"\n";
+ $warnings++;
+ next;
+ }
+
+ if (!/$re/) {
+ warn "Failed: $key\n";
+ $warnings++;
+ next;
+ }
+
+ if ($new = $ndiff{$key}) {
+ delete $ndiff{$key};
+
+ @new = split(/\\s\+/, $new);
+ for ($i=1;$i<@new;$i++) {
+ print "NEW: $new[$i]\n" if $debug;
+
+ if ($new[$i] ne '\S+') {
+ $_[$i] = $new[$i];
+ }
+ }
+ $_[0] =~ s/^\-//;
+ $key =~ s/^\-/\+/;
+
+ $diff{$key} = join("\t",@_);
+ }
+}
+
+close(DIFF);
+
+open(BEFORE, $before) || die "Couldn't open $before: $!\n";
+
+while(<BEFORE>) {
+ next if !/^keytab/;
+
+ split;
+
+ if (!$seen{$key = $_[0]." ".$_[1]}++) {
+ $key =~ s/-\d+$//;
+ $ktkeys{$key} .= " ".$_[2];
+ $kttimes{$key} .= " ".$_[3];
+ }
+}
+
+close(BEFORE);
+
+open(AFTER, $after) || die "Couldn't open $after: $!\n";
+
+while(<AFTER>) {
+ next if !/^keytab/;
+
+ split;
+
+ if (!$seen{$key = $_[0]." ".$_[1]}++) {
+ $key =~ s/-\d+$//;
+ $ktkeys{$key} .= " ".$_[2];
+ $kttimes{$key} .= " ".$_[3];
+ }
+}
+
+close(AFTER);
+
+for (keys %diff) {
+ warn "Unseen: \"$_\" /$diff{$_}/\n";
+ $warnings++;
+}
+
+for (keys %ndiff) {
+ warn "Unseen changes: \"$_\" /$ndiff{$_}/\n";
+ $warnings++;
+}
+
+for (keys %ktkeys) {
+ if (!&unique(split(' ',$ktkeys{$_}))) {
+ warn "Some keys not unique for $_\n";
+ $warnings++;
+ }
+}
+
+for (keys %kttimes) {
+ if (!&unique(split(' ',$kttimes{$_}))) {
+ warn "Some timestamps not unique for $_\n";
+ $warnings++;
+ }
+}
+
+if ($warnings) {
+ warn "$warnings warnings.\n";
+}
+
+exit($warnings);
diff --git a/src/kadmin/testing/scripts/find-make.sh b/src/kadmin/testing/scripts/find-make.sh
new file mode 100644
index 000000000..904730dfa
--- /dev/null
+++ b/src/kadmin/testing/scripts/find-make.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+POSSIBILITIES='
+/usr/local/bin/gmake
+/usr/local/bin/make
+'
+
+for file in $POSSIBILITIES; do
+ if [ -f $file ]; then
+ echo $file
+ exit 0
+ fi
+done
+
+echo gmake
+echo '$0 could not find make!' 1>&2
+exit 1
+
diff --git a/src/kadmin/testing/scripts/fixup-conf-files.pl.in b/src/kadmin/testing/scripts/fixup-conf-files.pl.in
new file mode 100644
index 000000000..d7834d1c7
--- /dev/null
+++ b/src/kadmin/testing/scripts/fixup-conf-files.pl.in
@@ -0,0 +1,344 @@
+#!/usr/local/bin/perl
+#
+# Usage: fixup-conf-files.pl [-server hostname]
+
+$verbose = $ENV{'VERBOSE_TEST'};
+$archos = $ENV{'ARCH_OS'};
+
+$REALM = "SECURE-TEST.OV.COM";
+
+sub replace {
+ local($old, $new, $backup) = @_;
+ local($dev, $ino, $mode);
+
+ $new = $old.".new" if !$new;
+ $backup = $old.".bak" if !$backup;
+
+ chmod($mode,$new) if (($dev, $ino, $mode) = stat($old));
+
+ unlink($backup);
+ link($old, $backup) || die "couldn't make backup link: $backup: $!\n"
+ if -e $old;
+ rename($new, $old) || die "couldn't rename $old to $new: $!\n";
+}
+
+if (@ARGV == 2 && $ARGV[0] eq "-server") {
+ $servername = $ARGV[1];
+} elsif (@ARGV != 0) {
+ print STDERR "Usage: $0 fixup-conf-files.pl [-server hostname]\n";
+}
+
+sub canonicalize_name {
+ local($hostname) = @_;
+ local($d, $addr, $addrtype);
+
+ ($host,$d,$addrtype,$d,$addr) = gethostbyname($hostname);
+ die "couldn't get hostname $hostname\n" if !$host;
+ ($host) = gethostbyaddr($addr,$addrtype);
+ die "couldn't reverse-resolve $hostname\n" if !$host;
+ return $host;
+}
+
+## Get server's canonical hostname.
+if ($servername) {
+ $serverhost = $servername;
+} else {
+ chop ($serverhost = `hostname`);
+}
+$serverhost = &canonicalize_name($serverhost);
+
+## Get local canonical hostname
+chop($localhost=`hostname`);
+$localhost = &canonicalize_name($localhost);
+
+## parse krb.conf
+
+if (open(KCONF, "/etc/athena/krb.conf")) {
+ chop($hrealm = <KCONF>);
+
+ $confok = 0;
+
+ while(<KCONF>) {
+ $confs .= $_ if !/^$REALM\s+/o;
+ $confok = 1 if /^$REALM\s+$serverhost\s+admin\s+server$/oi;
+ }
+
+ close(KCONF);
+}
+
+## rewrite krb.conf if necessary.
+
+if (($hrealm ne $REALM) || !$confok) {
+ print "Rewriting /etc/athena/krb.conf...\n" if $verbose;
+
+ open(KCONF, ">/etc/athena/krb.conf.new") ||
+ die "couldn't open /etc/athena/krb.conf.new: $!\n";
+
+ print KCONF "$REALM\n";
+ print KCONF "$REALM $serverhost admin server\n";
+ print KCONF $confs;
+
+ close(KCONF);
+
+ &replace("/etc/athena/krb.conf");
+}
+
+## parse krb.realms
+
+if (open(KREALMS, "/etc/athena/krb.realms")) {
+ $serverrealmok = 0;
+ $localrealmok = 0;
+
+ while(<KREALMS>) {
+ $realms .= $_
+ if !/^$serverhost\s+$REALM$/oi && !/^$localhost\s+$REALM$/oi;
+ $serverrealmok = 1 if /^$serverhost\s+$REALM$/oi;
+ $localrealmok = 1 if /^$localhost\s+$REALM$/oi;
+ }
+
+ close(KREALMS);
+}
+
+## rewrite krb.realms if necessary.
+
+if (!$serverrealmok || !$localrealmok) {
+ print "Rewriting /etc/athean/krb.realms...\n" if $verbose;
+
+ open(KREALMS, ">/etc/athena/krb.realms.new") ||
+ die "couldn't open /etc/athena/krb.realms.new: $!\n";
+
+ print KREALMS "$serverhost $REALM\n";
+ print KREALMS "$localhost $REALM\n" if ($localhost ne $serverhost);
+ print KREALMS $realms;
+
+ close(KREALMS);
+
+ &replace("/etc/athena/krb.realms");
+}
+
+# ## read /etc/passwd
+#
+# open(PASSWD, "/etc/passwd") || die "couldn't open /etc/passwd: $!\n";
+#
+# $passok = 0;
+#
+# if ($archos ne "solaris2.3") {
+# %mypass =
+# (
+# "root", crypt("testroot","St"),
+# "testenc", crypt("notath","HJ"),
+# "testuser", "KERBEROS5",
+# "pol1", "KERBEROS5",
+# "pol2", "KERBEROS5",
+# "pol3", "KERBEROS5",
+# );
+# } else {
+# %mypass =
+# (
+# "root", "x",
+# "testenc", "x",
+# "testuser", "x",
+# "pol1", "x",
+# "pol2", "x",
+# "pol3", "x",
+# );
+# %myshadow =
+# (
+# "root", crypt("testroot","St"),
+# "testenc", crypt("notath","HJ"),
+# "testuser", "KERBEROS5",
+# "pol1", "KERBEROS5",
+# "pol2", "KERBEROS5",
+# "pol3", "KERBEROS5",
+# );
+# }
+#
+# $chpw = 0;
+#
+# while(<PASSWD>) {
+# if (/^([^:]+):([^:]+):/ && $mypass{$1}) {
+# $users{$1}++;
+# if ($2 ne $mypass{$1}) {
+# s/^([^:]+):([^:]+):/$1:$mypass{$1}:/;
+# $chpw++;
+# }
+# }
+# $pass .= $_;
+# }
+#
+# $passok = 1;
+#
+# for (keys %mypass) {
+# if (!$users{$_}) {
+# $pass .= "$_:$mypass{$_}:32765:101::/tmp:/bin/csh\n";
+# $passok = 0;
+# }
+# }
+# close(PASSWD);
+#
+# ## rewrite passwd if necessary.
+#
+# if ($chpw || !$passok) {
+# print "Rewriting /etc/passwd...\n" if $verbose;
+#
+# open(PASSWD, ">/etc/passwd.new") ||
+# die "couldn't open /etc/passwd.new: $!\n";
+#
+# print PASSWD $pass;
+#
+# close(PASSWD);
+#
+# &replace("/etc/passwd");
+# }
+#
+# if ($archos eq "solaris2.3") {
+#
+# ## read /etc/shadow
+#
+# open(SHADOW, "/etc/shadow") || die "couldn't open /etc/shadow: $!\n";
+#
+# $shadowok = 0;
+# $chpw = 0;
+# %users = ();
+#
+# while(<SHADOW>) {
+# if (/^([^:]+):([^:]+):/ && $myshadow{$1}) {
+# $users{$1}++;
+# if ($2 ne $myshadow{$1}) {
+# s/^([^:]+):([^:]+):/$1:$myshadow{$1}:/;
+# $chpw++;
+# }
+# }
+# $shadow .= $_;
+# }
+#
+# $shadowok = 1;
+#
+# for (keys %myshadow) {
+# if (!$users{$_}) {
+# $shadow .= "$_:$myshadow{$_}:6445::::::\n";
+# $shadowok = 0;
+# }
+# }
+# close(SHADOW);
+#
+# ## rewrite shadow if necessary.
+#
+# if ($chpw || !$shadowok) {
+# print "Rewriting /etc/shadow...\n" if $verbose;
+#
+# open(SHADOW, ">/etc/shadow.new") ||
+# die "couldn't open /etc/shadow.new: $!\n";
+#
+# print SHADOW $shadow;
+#
+# close(SHADOW);
+#
+# &replace("/etc/shadow");
+# }
+# }
+#
+# if ($archos eq "aix3.2") {
+#
+# ## read /etc/security/passwd
+#
+# open(SHADOW, "/etc/security/passwd") || die "couldn't open /etc/security/passwd: $!\n";
+#
+# $shadowok = 0;
+# %users = ();
+#
+# while(<SHADOW>) {
+# if (/^([^:]+):\s*$/ && $mypass{$1}) {
+# $user = $1;
+# $users{$user}++;
+# # arrange for the user to have a password entry and none other
+# while (<SHADOW>) {
+# last if (!/=/);
+# }
+# $shadow .= "$user:\n\tpassword = KERBEROS5\n\n";
+# } else {
+# $shadow .= $_;
+# }
+# }
+#
+# $shadowok = 1;
+#
+# for (keys %mypass) {
+# if (!$users{$_}) {
+# $shadow .= "$_:\n\tpassword = KERBEROS5\n\n";
+# $shadowok = 0;
+# }
+# }
+# close(SHADOW);
+#
+# ## rewrite shadow if necessary.
+#
+# if (!$shadowok) {
+# print "Rewriting /etc/security/passwd...\n" if $verbose;
+#
+# open(SHADOW, ">/etc/security/passwd.new") ||
+# die "couldn't open /etc/security/passwd.new: $!\n";
+#
+# print SHADOW $shadow;
+#
+# close(SHADOW);
+#
+# &replace("/etc/security/passwd");
+# }
+# }
+#
+# open(SERVICES, "/etc/services") || die "couldn't open /etc/services: $!\n";
+# open(NEW_SERVICES, ">/etc/services.new") ||
+# die "couldn't open /etc/services.new: $!\n";
+#
+# print "Rewriting /etc/services...\n" if $verbose;
+#
+# @needed_services = ('klogin', 'kshell', 'kerberos', 'kerberos-sec',
+# 'kerberos5', 'kerberos4', 'kerberos_master',
+# 'passwd_server', 'eklogin', 'krb5_prop',
+# 'kerberos_adm', 'kerberos-adm');
+# for (@needed_services) {
+# $needed_services{$_}++;
+# }
+#
+# while (<SERVICES>) {
+# m/^\s*([^\#\s][^\s]+)/;
+# if ($needed_services{$1}) {
+# print "+ Commenting out old entry: $1\n" if $verbose;
+# print NEW_SERVICES "# $_";
+# } else {
+# print NEW_SERVICES $_;
+# }
+# }
+#
+# close(SERVICES);
+#
+# print NEW_SERVICES <<EOF || die "writing to /etc/services.new: $!\n";
+#
+# klogin 543/tcp # Kerberos authenticated rlogin
+# kshell 544/tcp cmd # and remote shell
+# kerberos 88/udp kdc # Kerberos authentication--udp
+# kerberos 88/tcp kdc # Kerberos authentication--tcp
+# kerberos-sec 750/udp # Kerberos authentication--udp
+# kerberos-sec 750/tcp # Kerberos authentication--tcp
+# kerberos5 88/udp kdc # Kerberos authentication--udp
+# kerberos5 88/tcp kdc # Kerberos authentication--tcp
+# kerberos4 750/udp # Kerberos authentication--udp
+# kerberos4 750/tcp # Kerberos authentication--tcp
+# kerberos_master 751/udp # Kerberos authentication
+# kerberos_master 751/tcp # Kerberos authentication
+# passwd_server 752/udp # Kerberos passwd server
+# eklogin 2105/tcp # Kerberos encrypted rlogin
+# krb5_prop 754/tcp # Kerberos slave propagation
+# kerberos_adm 752/tcp # Kerberos 5 admin/changepw
+# kerberos-adm 752/tcp # Kerberos 5 admin/changepw
+# EOF
+#
+# close(NEW_SERVICES) || die "error closing /etc/services.new: $!\n";
+#
+# rename("/etc/services", "/etc/services.old") ||
+# die "couldn't rename /etc/services to /etc/services.old: $!\n";
+# rename("/etc/services.new", "/etc/services") ||
+# die "couldn't rename /etc/services.new to /etc/services: $!\n";
+# unlink("/etc/services.old") || die "couldn't unlink /etc/services: $!\n";
+#
diff --git a/src/kadmin/testing/scripts/fixup-conf-files.plin b/src/kadmin/testing/scripts/fixup-conf-files.plin
new file mode 100644
index 000000000..d7834d1c7
--- /dev/null
+++ b/src/kadmin/testing/scripts/fixup-conf-files.plin
@@ -0,0 +1,344 @@
+#!/usr/local/bin/perl
+#
+# Usage: fixup-conf-files.pl [-server hostname]
+
+$verbose = $ENV{'VERBOSE_TEST'};
+$archos = $ENV{'ARCH_OS'};
+
+$REALM = "SECURE-TEST.OV.COM";
+
+sub replace {
+ local($old, $new, $backup) = @_;
+ local($dev, $ino, $mode);
+
+ $new = $old.".new" if !$new;
+ $backup = $old.".bak" if !$backup;
+
+ chmod($mode,$new) if (($dev, $ino, $mode) = stat($old));
+
+ unlink($backup);
+ link($old, $backup) || die "couldn't make backup link: $backup: $!\n"
+ if -e $old;
+ rename($new, $old) || die "couldn't rename $old to $new: $!\n";
+}
+
+if (@ARGV == 2 && $ARGV[0] eq "-server") {
+ $servername = $ARGV[1];
+} elsif (@ARGV != 0) {
+ print STDERR "Usage: $0 fixup-conf-files.pl [-server hostname]\n";
+}
+
+sub canonicalize_name {
+ local($hostname) = @_;
+ local($d, $addr, $addrtype);
+
+ ($host,$d,$addrtype,$d,$addr) = gethostbyname($hostname);
+ die "couldn't get hostname $hostname\n" if !$host;
+ ($host) = gethostbyaddr($addr,$addrtype);
+ die "couldn't reverse-resolve $hostname\n" if !$host;
+ return $host;
+}
+
+## Get server's canonical hostname.
+if ($servername) {
+ $serverhost = $servername;
+} else {
+ chop ($serverhost = `hostname`);
+}
+$serverhost = &canonicalize_name($serverhost);
+
+## Get local canonical hostname
+chop($localhost=`hostname`);
+$localhost = &canonicalize_name($localhost);
+
+## parse krb.conf
+
+if (open(KCONF, "/etc/athena/krb.conf")) {
+ chop($hrealm = <KCONF>);
+
+ $confok = 0;
+
+ while(<KCONF>) {
+ $confs .= $_ if !/^$REALM\s+/o;
+ $confok = 1 if /^$REALM\s+$serverhost\s+admin\s+server$/oi;
+ }
+
+ close(KCONF);
+}
+
+## rewrite krb.conf if necessary.
+
+if (($hrealm ne $REALM) || !$confok) {
+ print "Rewriting /etc/athena/krb.conf...\n" if $verbose;
+
+ open(KCONF, ">/etc/athena/krb.conf.new") ||
+ die "couldn't open /etc/athena/krb.conf.new: $!\n";
+
+ print KCONF "$REALM\n";
+ print KCONF "$REALM $serverhost admin server\n";
+ print KCONF $confs;
+
+ close(KCONF);
+
+ &replace("/etc/athena/krb.conf");
+}
+
+## parse krb.realms
+
+if (open(KREALMS, "/etc/athena/krb.realms")) {
+ $serverrealmok = 0;
+ $localrealmok = 0;
+
+ while(<KREALMS>) {
+ $realms .= $_
+ if !/^$serverhost\s+$REALM$/oi && !/^$localhost\s+$REALM$/oi;
+ $serverrealmok = 1 if /^$serverhost\s+$REALM$/oi;
+ $localrealmok = 1 if /^$localhost\s+$REALM$/oi;
+ }
+
+ close(KREALMS);
+}
+
+## rewrite krb.realms if necessary.
+
+if (!$serverrealmok || !$localrealmok) {
+ print "Rewriting /etc/athean/krb.realms...\n" if $verbose;
+
+ open(KREALMS, ">/etc/athena/krb.realms.new") ||
+ die "couldn't open /etc/athena/krb.realms.new: $!\n";
+
+ print KREALMS "$serverhost $REALM\n";
+ print KREALMS "$localhost $REALM\n" if ($localhost ne $serverhost);
+ print KREALMS $realms;
+
+ close(KREALMS);
+
+ &replace("/etc/athena/krb.realms");
+}
+
+# ## read /etc/passwd
+#
+# open(PASSWD, "/etc/passwd") || die "couldn't open /etc/passwd: $!\n";
+#
+# $passok = 0;
+#
+# if ($archos ne "solaris2.3") {
+# %mypass =
+# (
+# "root", crypt("testroot","St"),
+# "testenc", crypt("notath","HJ"),
+# "testuser", "KERBEROS5",
+# "pol1", "KERBEROS5",
+# "pol2", "KERBEROS5",
+# "pol3", "KERBEROS5",
+# );
+# } else {
+# %mypass =
+# (
+# "root", "x",
+# "testenc", "x",
+# "testuser", "x",
+# "pol1", "x",
+# "pol2", "x",
+# "pol3", "x",
+# );
+# %myshadow =
+# (
+# "root", crypt("testroot","St"),
+# "testenc", crypt("notath","HJ"),
+# "testuser", "KERBEROS5",
+# "pol1", "KERBEROS5",
+# "pol2", "KERBEROS5",
+# "pol3", "KERBEROS5",
+# );
+# }
+#
+# $chpw = 0;
+#
+# while(<PASSWD>) {
+# if (/^([^:]+):([^:]+):/ && $mypass{$1}) {
+# $users{$1}++;
+# if ($2 ne $mypass{$1}) {
+# s/^([^:]+):([^:]+):/$1:$mypass{$1}:/;
+# $chpw++;
+# }
+# }
+# $pass .= $_;
+# }
+#
+# $passok = 1;
+#
+# for (keys %mypass) {
+# if (!$users{$_}) {
+# $pass .= "$_:$mypass{$_}:32765:101::/tmp:/bin/csh\n";
+# $passok = 0;
+# }
+# }
+# close(PASSWD);
+#
+# ## rewrite passwd if necessary.
+#
+# if ($chpw || !$passok) {
+# print "Rewriting /etc/passwd...\n" if $verbose;
+#
+# open(PASSWD, ">/etc/passwd.new") ||
+# die "couldn't open /etc/passwd.new: $!\n";
+#
+# print PASSWD $pass;
+#
+# close(PASSWD);
+#
+# &replace("/etc/passwd");
+# }
+#
+# if ($archos eq "solaris2.3") {
+#
+# ## read /etc/shadow
+#
+# open(SHADOW, "/etc/shadow") || die "couldn't open /etc/shadow: $!\n";
+#
+# $shadowok = 0;
+# $chpw = 0;
+# %users = ();
+#
+# while(<SHADOW>) {
+# if (/^([^:]+):([^:]+):/ && $myshadow{$1}) {
+# $users{$1}++;
+# if ($2 ne $myshadow{$1}) {
+# s/^([^:]+):([^:]+):/$1:$myshadow{$1}:/;
+# $chpw++;
+# }
+# }
+# $shadow .= $_;
+# }
+#
+# $shadowok = 1;
+#
+# for (keys %myshadow) {
+# if (!$users{$_}) {
+# $shadow .= "$_:$myshadow{$_}:6445::::::\n";
+# $shadowok = 0;
+# }
+# }
+# close(SHADOW);
+#
+# ## rewrite shadow if necessary.
+#
+# if ($chpw || !$shadowok) {
+# print "Rewriting /etc/shadow...\n" if $verbose;
+#
+# open(SHADOW, ">/etc/shadow.new") ||
+# die "couldn't open /etc/shadow.new: $!\n";
+#
+# print SHADOW $shadow;
+#
+# close(SHADOW);
+#
+# &replace("/etc/shadow");
+# }
+# }
+#
+# if ($archos eq "aix3.2") {
+#
+# ## read /etc/security/passwd
+#
+# open(SHADOW, "/etc/security/passwd") || die "couldn't open /etc/security/passwd: $!\n";
+#
+# $shadowok = 0;
+# %users = ();
+#
+# while(<SHADOW>) {
+# if (/^([^:]+):\s*$/ && $mypass{$1}) {
+# $user = $1;
+# $users{$user}++;
+# # arrange for the user to have a password entry and none other
+# while (<SHADOW>) {
+# last if (!/=/);
+# }
+# $shadow .= "$user:\n\tpassword = KERBEROS5\n\n";
+# } else {
+# $shadow .= $_;
+# }
+# }
+#
+# $shadowok = 1;
+#
+# for (keys %mypass) {
+# if (!$users{$_}) {
+# $shadow .= "$_:\n\tpassword = KERBEROS5\n\n";
+# $shadowok = 0;
+# }
+# }
+# close(SHADOW);
+#
+# ## rewrite shadow if necessary.
+#
+# if (!$shadowok) {
+# print "Rewriting /etc/security/passwd...\n" if $verbose;
+#
+# open(SHADOW, ">/etc/security/passwd.new") ||
+# die "couldn't open /etc/security/passwd.new: $!\n";
+#
+# print SHADOW $shadow;
+#
+# close(SHADOW);
+#
+# &replace("/etc/security/passwd");
+# }
+# }
+#
+# open(SERVICES, "/etc/services") || die "couldn't open /etc/services: $!\n";
+# open(NEW_SERVICES, ">/etc/services.new") ||
+# die "couldn't open /etc/services.new: $!\n";
+#
+# print "Rewriting /etc/services...\n" if $verbose;
+#
+# @needed_services = ('klogin', 'kshell', 'kerberos', 'kerberos-sec',
+# 'kerberos5', 'kerberos4', 'kerberos_master',
+# 'passwd_server', 'eklogin', 'krb5_prop',
+# 'kerberos_adm', 'kerberos-adm');
+# for (@needed_services) {
+# $needed_services{$_}++;
+# }
+#
+# while (<SERVICES>) {
+# m/^\s*([^\#\s][^\s]+)/;
+# if ($needed_services{$1}) {
+# print "+ Commenting out old entry: $1\n" if $verbose;
+# print NEW_SERVICES "# $_";
+# } else {
+# print NEW_SERVICES $_;
+# }
+# }
+#
+# close(SERVICES);
+#
+# print NEW_SERVICES <<EOF || die "writing to /etc/services.new: $!\n";
+#
+# klogin 543/tcp # Kerberos authenticated rlogin
+# kshell 544/tcp cmd # and remote shell
+# kerberos 88/udp kdc # Kerberos authentication--udp
+# kerberos 88/tcp kdc # Kerberos authentication--tcp
+# kerberos-sec 750/udp # Kerberos authentication--udp
+# kerberos-sec 750/tcp # Kerberos authentication--tcp
+# kerberos5 88/udp kdc # Kerberos authentication--udp
+# kerberos5 88/tcp kdc # Kerberos authentication--tcp
+# kerberos4 750/udp # Kerberos authentication--udp
+# kerberos4 750/tcp # Kerberos authentication--tcp
+# kerberos_master 751/udp # Kerberos authentication
+# kerberos_master 751/tcp # Kerberos authentication
+# passwd_server 752/udp # Kerberos passwd server
+# eklogin 2105/tcp # Kerberos encrypted rlogin
+# krb5_prop 754/tcp # Kerberos slave propagation
+# kerberos_adm 752/tcp # Kerberos 5 admin/changepw
+# kerberos-adm 752/tcp # Kerberos 5 admin/changepw
+# EOF
+#
+# close(NEW_SERVICES) || die "error closing /etc/services.new: $!\n";
+#
+# rename("/etc/services", "/etc/services.old") ||
+# die "couldn't rename /etc/services to /etc/services.old: $!\n";
+# rename("/etc/services.new", "/etc/services") ||
+# die "couldn't rename /etc/services.new to /etc/services: $!\n";
+# unlink("/etc/services.old") || die "couldn't unlink /etc/services: $!\n";
+#
diff --git a/src/kadmin/testing/scripts/init_db b/src/kadmin/testing/scripts/init_db
new file mode 100644
index 000000000..c53ff96c1
--- /dev/null
+++ b/src/kadmin/testing/scripts/init_db
@@ -0,0 +1,181 @@
+#!/bin/sh
+
+# If it's set, set it to true
+VERBOSE=${VERBOSE_TEST:+true}
+# Otherwise, set it to false
+DUMMY=${VERBOSE:=false}
+
+if $VERBOSE; then
+ REDIRECT=
+else
+ REDIRECT='>/dev/null'
+fi
+
+# Requires that /krb5, /etc/krb.conf, and .k5.$REALM be world-writeable.
+
+if [ "$TOP" = "" ]; then
+ echo "init_db: Environment variable \$TOP must point to top of build tree" 1>&2
+ exit 1
+fi
+
+IROOT=$TOP/..
+ADMIN=$TOP/create:$IROOT/admin/stash:$IROOT/admin/destroy
+BIN=$IROOT/bin
+ETC=$IROOT/etc
+SBIN=$TOP/keytab:$TOP/server
+DUMMY=${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
+DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl}
+
+PATH=$ADMIN:$BIN:$ETC:$SBIN:$PATH; export PATH
+
+rm -rf /krb5/*
+if [ -d /krb5 ]; then
+ true
+else
+ mkdir /krb5
+fi
+
+# touch /krb5/syslog
+# for pid in `$PS_ALL | awk '/syslogd/ && !/awk/ {print $2}'` ; do
+# case "$pid" in
+# xxx) ;;
+# *)
+# if $VERBOSE; then $PS_PID$pid | grep -v COMMAND; fi
+# kill -1 $pid
+# ;;
+# esac
+# done
+
+sed -e "s/__REALM__/$REALM/" < $TESTDIR/proto/krb5.conf.proto > /krb5/krb5.conf
+sed -e "s/__REALM__/$REALM/" < $TESTDIR/proto/kdc.conf.proto > /krb5/kdc.conf
+
+kdb5_create -P mrroot -s -r $REALM $REDIRECT
+
+cp $TESTDIR/proto/ovsec_adm.dict /krb5/ovsec_adm.dict
+
+eval $SRVTCL <<'EOF' $REDIRECT
+source $env(TCLUTIL)
+set r $env(REALM)
+
+set cmds {
+ {ovsec_kadm_init $env(SRVTCL) mrroot null $r $OVSEC_KADM_STRUCT_VERSION \
+ $OVSEC_KADM_API_VERSION_1 server_handle}
+
+ {ovsec_kadm_create_policy $server_handle "test-pol 0 10000 8 2 3 0" \
+ {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LENGTH OVSEC_KADM_PW_MIN_CLASSES OVSEC_KADM_PW_MAX_LIFE OVSEC_KADM_PW_HISTORY_NUM}}
+ {ovsec_kadm_create_policy $server_handle "once-a-min 30 0 0 0 0 0" \
+ {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LIFE}}
+ {ovsec_kadm_create_policy $server_handle "dict-only 0 0 0 0 0 0" \
+ {OVSEC_KADM_POLICY}}
+ {ovsec_kadm_create_policy $server_handle [simple_policy test-pol-nopw] \
+ {OVSEC_KADM_POLICY}}
+
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal testuser@$r] {OVSEC_KADM_PRINCIPAL} notathena}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal test1@$r] {OVSEC_KADM_PRINCIPAL} test1}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal test2@$r] {OVSEC_KADM_PRINCIPAL} test2}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal test3@$r] {OVSEC_KADM_PRINCIPAL} test3}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/get@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/modify@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/delete@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/add@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/none@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/rename@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/mod-add@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/mod-delete@$r] {OVSEC_KADM_PRINCIPAL} \
+ admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/get-add@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/get-delete@$r] {OVSEC_KADM_PRINCIPAL} \
+ admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/get-mod@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/no-add@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal admin/no-delete@$r] {OVSEC_KADM_PRINCIPAL} admin}
+ {ovsec_kadm_create_principal $server_handle \
+ [princ_w_pol pol1@$r test-pol] {OVSEC_KADM_PRINCIPAL \
+ OVSEC_KADM_POLICY} pol111111}
+ {ovsec_kadm_create_principal $server_handle \
+ [princ_w_pol pol2@$r once-a-min] {OVSEC_KADM_PRINCIPAL \
+ OVSEC_KADM_POLICY} pol222222}
+ {ovsec_kadm_create_principal $server_handle \
+ [princ_w_pol pol3@$r dict-only] {OVSEC_KADM_PRINCIPAL \
+ OVSEC_KADM_POLICY} pol333333}
+ {ovsec_kadm_create_principal $server_handle \
+ [princ_w_pol admin/get-pol@$r test-pol-nopw] \
+ {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} StupidAdmin}
+ {ovsec_kadm_create_principal $server_handle \
+ [princ_w_pol admin/pol@$r test-pol-nopw] {OVSEC_KADM_PRINCIPAL \
+ OVSEC_KADM_POLICY} StupidAdmin}
+
+ {ovsec_kadm_create_principal $server_handle \
+ [simple_principal changepw/kerberos] \
+ {OVSEC_KADM_PRINCIPAL} {XXX THIS IS WRONG}}
+
+ {ovsec_kadm_destroy $server_handle}
+}
+
+foreach cmd $cmds {
+ if {[catch $cmd output]} {
+ puts stderr "Error! Command: $cmd\nError: $output"
+ exit 1
+ } else {
+ puts stdout $output
+ }
+}
+EOF
+
+if [ $? -ne 0 ]; then
+ echo "Error in $SRVTCL!" 1>&2
+ exit 1
+fi
+
+cat > /krb5/ovsec_adm.acl <<EOF
+admin@$REALM admcil
+admin/get@$REALM il
+admin/modify@$REALM mc
+admin/delete@$REALM d
+admin/add@$REALM a
+admin/get-pol@$REALM il
+admin/rename@$REALM adil
+admin/mod-add@$REALM amc
+admin/mod-delete@$REALM mcd
+admin/get-add@$REALM ail
+admin/get-delete@$REALM ild
+admin/get-mod@$REALM ilmc
+admin/no-add@$REALM mcdil
+admin/no-delete@$REALM amcil
+changepw/kerberos@$REALM cil
+
+EOF
+
+eval $LOCAL_MAKE_KEYTAB -princ kadmin/admin -princ kadmin/changepw -princ ovsec_adm/admin -princ ovsec_adm/changepw /krb5/ovsec_adm.srvtab $REDIRECT
+
+# Create /krb5/setup.csh to make it easy to run other programs against
+# the test db
+cat > /krb5/setup.csh <<EOF
+setenv KRB5_CONFIG $KRB5_CONFIG
+setenv KRB5_KDC_PROFILE $KRB5_KDC_PROFILE
+setenv KRB5_KTNAME $KRB5_KTNAME
+EOF
+
diff --git a/src/kadmin/testing/scripts/make-host-keytab.pl.in b/src/kadmin/testing/scripts/make-host-keytab.pl.in
new file mode 100644
index 000000000..14d7b10b5
--- /dev/null
+++ b/src/kadmin/testing/scripts/make-host-keytab.pl.in
@@ -0,0 +1,138 @@
+#!/usr/local/bin/perl
+
+$server = undef;
+@princs = ();
+$top = undef;
+
+($whoami = $0) =~ s,.*/,,;
+$usage = "Usage: $whoami [ -server server ] [ -princ principal ]
+ [ -top dirname ] [ -verbose ] filename
+ Server defaults to the local host.
+ Default principals are host/hostname\@SECURE-TEST.OV.COM and
+ test/hostname\@SECURE-TEST.OV.COM.
+ If any principals are specified, the default principals are
+ not added to the srvtab.
+ The string \"xCANONHOSTx\" in a principal specification will be
+ replaced by the canonical host name of the local host.";
+
+@ORIG_ARGV = @ARGV;
+
+while (($_ = $ARGV[0]) && /^-/) {
+ shift;
+ if (/^-server$/) {
+ ($server = shift) || die "Missing argument to $_ option.\n$usage\n";
+ }
+ elsif (/^-princ$/) {
+ ($princ = shift) || die "Missing argument to $_ option.\n$usage\n";
+ push(@princs, $princ);
+ }
+ elsif (/^-top$/) {
+ ($top = shift) || die "Missing argument to $_ option.\n$usage\n";
+ }
+ elsif (/^-verbose$/) {
+ $verbose++;
+ }
+ elsif (/^--$/) {
+ last;
+ }
+ else {
+ die "Unknown option $_.\n$usage\n";
+ }
+}
+
+@princs = ("host/xCANONHOSTx\@SECURE-TEST.OV.COM",
+ "test/xCANONHOSTx\@SECURE-TEST.OV.COM")
+ if (! @princs);
+
+$ktfile = shift(@ARGV) || die "need a keytab file\n";
+
+$verbose++ if ($ENV{'VERBOSE_TEST'});
+
+print "In $0 @ORIG_ARGV...\n" if ($verbose);
+
+chop ($canonhost = `hostname`);
+
+($canonhost,$aliases,$addrtype,$length,@addrs) = gethostbyname($canonhost);
+die "couldn't get canonical hostname\n" if !($canonhost && @addrs);
+($canonhost) = gethostbyaddr($addrs[0],$addrtype);
+die "couldn't get canonical hostname\n" if (!$canonhost);
+
+for (@princs) {
+ s/xCANONHOSTx/$canonhost/g;
+}
+
+die "Neither \$TOP nor \$TESTDIR is set, and -top not specified.\n"
+ if (! ($top || $ENV{'TOP'} || $ENV{'TESTDIR'}));
+
+$top = $ENV{'TOP'} if (! $top);
+$TESTDIR = ($ENV{'TESTDIR'} || "$top/testing");
+$MAKE_KEYTAB = ($ENV{'MAKE_KEYTAB'} || "$TESTDIR/scripts/$whoami");
+$SRVTCL = ($ENV{'SRVTCL'} || "$TESTDIR/util/ovsec_kadm_srv_tcl");
+$TCLUTIL = ($ENV{'TCLUTIL'} || "$TESTDIR/tcl/util.t");
+# This'll be wrong sometimes
+$RSH_CMD = ($ENV{'RSH_CMD'} || '/usr/ucb/rsh');
+$EDIT_KEYTAB = ($ENV{'EDIT_KEYTAB'} || "$top/keytab/kadm5_keytab.local");
+
+if ($server) {
+# XXX Using /usr/ucb/rsh for now.
+
+# Strip command line options because we're adding our own.
+
+ $MAKE_KEYTAB =~ s/ .*//;
+
+ if ($ENV{'TOP'} && ($top ne $ENV{'TOP'})) {
+# Replace the old TOP with the new one where necessary
+ for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') {
+ eval "\$$_ =~ s/^\$ENV{'TOP'}/\$top/;";
+ }
+
+# Make the paths as short as possible so our command line isn't too long.
+# for ('SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') {
+# eval "\$$_ =~ s/^\$TESTDIR/\\\\\\\$TESTDIR/;";
+# }
+# for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') {
+# eval "\$$_ =~ s/^\$top/\\\\\\\$TOP/;";
+# }
+ }
+
+ $cmd = "cd $top; \\`testing/scripts/find-make.sh\\` execute TOP=$top ";
+ $cmd .= "VERBOSE_TEST=$verbose " if ($verbose);
+ $cmd .= "TESTDIR=$TESTDIR ";
+ $cmd .= "SRVTCL=$SRVTCL ";
+ $cmd .= "TCLUTIL=$TCLUTIL ";
+
+ $cmd .= "CMD='$MAKE_KEYTAB ";
+ for (@princs) {
+ $cmd .= "-princ $_ ";
+ }
+ $cmd .= " /tmp/make-keytab.$canonhost.$$'";#';
+
+ $cmd = "$RSH_CMD $server -l root -n \"$cmd\"";
+
+ $cmd2 = "$RSH_CMD $server -l root -n \"cat /tmp/make-keytab.$canonhost.$$\" > $ktfile";
+
+ $cmd3 = "$RSH_CMD $server -l root -n \"rm /tmp/make-keytab.$canonhost.$$\"";
+
+ for ($cmd, $cmd2, $cmd3) {
+ print "$_\n" if ($verbose);
+
+ system($_) && die "Couldn't run $_: $!.\n";
+ }
+}
+else {
+ $redirect = "> /dev/null" if (! $verbose);
+
+ $cmd = "$EDIT_KEYTAB -k $ktfile";
+ $cmd .= " -q" if (! $verbose);
+ $cmd .= " -a -c";
+ for (@princs) {
+ if (system "$cmd $_") {
+ sleep(1);
+ die "Error in system($cmd $_)\n";
+ }
+ }
+}
+
+if (! -f $ktfile) {
+ die "$ktfile not created.\n";
+}
diff --git a/src/kadmin/testing/scripts/make-host-keytab.plin b/src/kadmin/testing/scripts/make-host-keytab.plin
new file mode 100644
index 000000000..14d7b10b5
--- /dev/null
+++ b/src/kadmin/testing/scripts/make-host-keytab.plin
@@ -0,0 +1,138 @@
+#!/usr/local/bin/perl
+
+$server = undef;
+@princs = ();
+$top = undef;
+
+($whoami = $0) =~ s,.*/,,;
+$usage = "Usage: $whoami [ -server server ] [ -princ principal ]
+ [ -top dirname ] [ -verbose ] filename
+ Server defaults to the local host.
+ Default principals are host/hostname\@SECURE-TEST.OV.COM and
+ test/hostname\@SECURE-TEST.OV.COM.
+ If any principals are specified, the default principals are
+ not added to the srvtab.
+ The string \"xCANONHOSTx\" in a principal specification will be
+ replaced by the canonical host name of the local host.";
+
+@ORIG_ARGV = @ARGV;
+
+while (($_ = $ARGV[0]) && /^-/) {
+ shift;
+ if (/^-server$/) {
+ ($server = shift) || die "Missing argument to $_ option.\n$usage\n";
+ }
+ elsif (/^-princ$/) {
+ ($princ = shift) || die "Missing argument to $_ option.\n$usage\n";
+ push(@princs, $princ);
+ }
+ elsif (/^-top$/) {
+ ($top = shift) || die "Missing argument to $_ option.\n$usage\n";
+ }
+ elsif (/^-verbose$/) {
+ $verbose++;
+ }
+ elsif (/^--$/) {
+ last;
+ }
+ else {
+ die "Unknown option $_.\n$usage\n";
+ }
+}
+
+@princs = ("host/xCANONHOSTx\@SECURE-TEST.OV.COM",
+ "test/xCANONHOSTx\@SECURE-TEST.OV.COM")
+ if (! @princs);
+
+$ktfile = shift(@ARGV) || die "need a keytab file\n";
+
+$verbose++ if ($ENV{'VERBOSE_TEST'});
+
+print "In $0 @ORIG_ARGV...\n" if ($verbose);
+
+chop ($canonhost = `hostname`);
+
+($canonhost,$aliases,$addrtype,$length,@addrs) = gethostbyname($canonhost);
+die "couldn't get canonical hostname\n" if !($canonhost && @addrs);
+($canonhost) = gethostbyaddr($addrs[0],$addrtype);
+die "couldn't get canonical hostname\n" if (!$canonhost);
+
+for (@princs) {
+ s/xCANONHOSTx/$canonhost/g;
+}
+
+die "Neither \$TOP nor \$TESTDIR is set, and -top not specified.\n"
+ if (! ($top || $ENV{'TOP'} || $ENV{'TESTDIR'}));
+
+$top = $ENV{'TOP'} if (! $top);
+$TESTDIR = ($ENV{'TESTDIR'} || "$top/testing");
+$MAKE_KEYTAB = ($ENV{'MAKE_KEYTAB'} || "$TESTDIR/scripts/$whoami");
+$SRVTCL = ($ENV{'SRVTCL'} || "$TESTDIR/util/ovsec_kadm_srv_tcl");
+$TCLUTIL = ($ENV{'TCLUTIL'} || "$TESTDIR/tcl/util.t");
+# This'll be wrong sometimes
+$RSH_CMD = ($ENV{'RSH_CMD'} || '/usr/ucb/rsh');
+$EDIT_KEYTAB = ($ENV{'EDIT_KEYTAB'} || "$top/keytab/kadm5_keytab.local");
+
+if ($server) {
+# XXX Using /usr/ucb/rsh for now.
+
+# Strip command line options because we're adding our own.
+
+ $MAKE_KEYTAB =~ s/ .*//;
+
+ if ($ENV{'TOP'} && ($top ne $ENV{'TOP'})) {
+# Replace the old TOP with the new one where necessary
+ for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') {
+ eval "\$$_ =~ s/^\$ENV{'TOP'}/\$top/;";
+ }
+
+# Make the paths as short as possible so our command line isn't too long.
+# for ('SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') {
+# eval "\$$_ =~ s/^\$TESTDIR/\\\\\\\$TESTDIR/;";
+# }
+# for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') {
+# eval "\$$_ =~ s/^\$top/\\\\\\\$TOP/;";
+# }
+ }
+
+ $cmd = "cd $top; \\`testing/scripts/find-make.sh\\` execute TOP=$top ";
+ $cmd .= "VERBOSE_TEST=$verbose " if ($verbose);
+ $cmd .= "TESTDIR=$TESTDIR ";
+ $cmd .= "SRVTCL=$SRVTCL ";
+ $cmd .= "TCLUTIL=$TCLUTIL ";
+
+ $cmd .= "CMD='$MAKE_KEYTAB ";
+ for (@princs) {
+ $cmd .= "-princ $_ ";
+ }
+ $cmd .= " /tmp/make-keytab.$canonhost.$$'";#';
+
+ $cmd = "$RSH_CMD $server -l root -n \"$cmd\"";
+
+ $cmd2 = "$RSH_CMD $server -l root -n \"cat /tmp/make-keytab.$canonhost.$$\" > $ktfile";
+
+ $cmd3 = "$RSH_CMD $server -l root -n \"rm /tmp/make-keytab.$canonhost.$$\"";
+
+ for ($cmd, $cmd2, $cmd3) {
+ print "$_\n" if ($verbose);
+
+ system($_) && die "Couldn't run $_: $!.\n";
+ }
+}
+else {
+ $redirect = "> /dev/null" if (! $verbose);
+
+ $cmd = "$EDIT_KEYTAB -k $ktfile";
+ $cmd .= " -q" if (! $verbose);
+ $cmd .= " -a -c";
+ for (@princs) {
+ if (system "$cmd $_") {
+ sleep(1);
+ die "Error in system($cmd $_)\n";
+ }
+ }
+}
+
+if (! -f $ktfile) {
+ die "$ktfile not created.\n";
+}
diff --git a/src/kadmin/testing/scripts/qualname b/src/kadmin/testing/scripts/qualname
new file mode 100644
index 000000000..3d047c550
--- /dev/null
+++ b/src/kadmin/testing/scripts/qualname
@@ -0,0 +1,18 @@
+#!/afs/athena/contrib/perl/p
+
+if ($#ARGV == -1) {
+ chop($hostname = `hostname`);
+} else {
+ $hostname = $ARGV[0];
+}
+
+if (! (($type,$addr) = (gethostbyname($hostname))[2,4])) {
+ print STDERR "No such host: $hostname\n";
+ exit(1);
+}
+if (! ($qualname = (gethostbyaddr($addr,$type))[0])) {
+ print STDERR "No address information for host $hostname\n";
+ exit(1);
+}
+print "$qualname\n";
+
diff --git a/src/kadmin/testing/scripts/save_files.sh b/src/kadmin/testing/scripts/save_files.sh
new file mode 100644
index 000000000..b9fc37319
--- /dev/null
+++ b/src/kadmin/testing/scripts/save_files.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+while [ $# -gt 0 ] ; do
+ case $1 in
+ -start_servers)
+ start_servers=$1
+ ;;
+ esac
+ shift
+done
+
+# If it's set, set it to true
+VERBOSE=${VERBOSE_TEST:+true}
+# Otherwise, set it to false
+DUMMY=${VERBOSE:=false}
+
+# files="/etc/inetd.conf /etc/syslog.conf /etc/krb.conf \
+# /etc/krb.realms /etc/passwd /etc/services /etc/v5srvtab \
+# /etc/rc.local /etc/shadow /etc/security/passwd /.k5login \
+# /.secure/etc/passwd /etc/athena/inetd.conf"
+
+files="/etc/krb.conf /etc/krb.realms /etc/athena/krb.conf \
+ /etc/athena/krb.realms /etc/v5srvtab"
+
+name=`basename $0`
+
+make_dne_name()
+{
+ dne_name="/tmp/"`echo $1 | sed -e 's,/,#,g'`".did-not-exist"
+}
+
+for f in $files ; do
+ if [ "$name" = "save_files.sh" ]; then
+ if [ -f $f.pre-secure ]; then
+ if $VERBOSE; then
+ echo "Warning! $f.pre-secure exists, not saving."
+ fi
+ elif [ ! -f $f ]; then
+ make_dne_name $f
+ cp /dev/null $dne_name
+ else
+ cp $f $f.pre-secure
+ fi
+ else
+ make_dne_name $f
+ if [ -f $dne_name ]; then
+ rm -f $f $dne_name
+ elif [ ! -f $f.pre-secure ]; then
+ if [ "x$start_servers" = "x" ]; then
+ echo "Warning! $f.pre-secure does not exist!" 1>&2
+ fi
+ else
+ if cp $f.pre-secure $f; then
+ rm $f.pre-secure
+ else
+ echo "Warning! cp failed!" 1>&2
+ fi
+ fi
+ fi
+done
+
+# DUMMY=${INETD:=/etc/inetd}
+# if $VERBOSE; then
+# echo "Killing and restarting $INETD"
+# fi
+# kill `$PS_ALL | awk '/inetd/ && !/awk/ {print $2}'`
+# $INETD
diff --git a/src/kadmin/testing/scripts/simple_dump.pl.in b/src/kadmin/testing/scripts/simple_dump.pl.in
new file mode 100644
index 000000000..ea94ab2d1
--- /dev/null
+++ b/src/kadmin/testing/scripts/simple_dump.pl.in
@@ -0,0 +1,88 @@
+#!/usr/local/bin/perl
+
+#
+# $Id$
+#
+
+## ovsec_adm_export format
+## [0]"policy" [1]name [2]pw_min_life [3]pw_max_life [4]pw_min_length [5]pw_min_classes [6]pw_history_num [7]policy_refcnt
+## [0]"princ" [1]name [2]policy [3]aux_attributes [4]old_key_len [5]admin_history_kvno [6..]old_keys
+$oaevers = "1.0";
+
+open(SORT, "|sort") || die "Couldn't open pipe to sort for output: $!\n";
+
+open(OAE, "$ENV{'TOP'}/install/admin/ovsec_adm_export|") ||
+ die "Couldn't get oae: $!\n";
+
+$header = <OAE>;
+
+die "Not ovsec_adm_export output\n"
+ if ($header !~ /^OpenV\*Secure V(\d+\.\d+)/);
+
+$stdinvers = $1;
+
+die "Expected oae version $oaevers, got $stdinvers instead.\n"
+ if $stdinvers ne $oaevers;
+
+while(<OAE>) {
+ if (/^End of Database/) {
+ last;
+ } elsif (/^policy/) {
+ print SORT;
+ } elsif (/^princ/) {
+ split(/\t/);
+
+ $_[2] = "\"\"" if !$_[2];
+
+ $_[3] = hex("0x".$_[3]);
+
+ $princ{$_[1]} = sprintf("%s\t0x%04x",@_[2,3]);
+ }
+}
+
+## kdb_edit ddb format
+## [0]strlen(principal) [1]strlen(mod_name) [2]key.length [3]alt_key.length [4]salt_length [5]alt_salt_length [6]principal [7]key.key_type [8]key.contents [9]kvno [10]max_life [11]max_renewable_life [12]mkvno [13]expiration [14]pw_expiration [15]last_pwd_change [16]last_success [17]last_failed [18]fail_auth_count [19]mod_name [20]mod_date [21]attributes [22]salt_type [23]salt [24]alt_key.contents [25]alt_salt [26..33]expansion*8;
+$ddbvers = "2.0";
+
+open(DDB, "$ENV{'TOP'}/install/admin/kdb5_edit -r SECURE-TEST.OV.COM -R ddb|") ||
+ die "Couldn't get ddb: $!\n";
+
+$header = <DDB>;
+
+die "Not a kdb5_edit ddb\n"
+ if ($header !~ /^kdb5_edit load_dump version (\d+\.\d+)/);
+
+$stdinvers = $1;
+
+die "Expected ddb version $ddbvers, got $stdinvers instead.\n"
+ if $stdinvers ne $ddbvers;
+
+## [6]principal [9]kvno [19]mod_name [10]max_life [13]expiration [14]pw_expiration [21]attributes // [2]policy [3]aux_attributes
+
+while(<DDB>) {
+ split;
+
+ print SORT join("\t","princ",(@_)[6,9,19,10,13,14],
+ sprintf("0x%04x",$_[21]),
+ $princ{$_[6]}),"\n";
+}
+
+close(DDB);
+
+for $keytab (@ARGV) {
+ open(KLIST, "$ENV{'TOP'}/install/bin/klist -k -t -K FILE:$keytab|") ||
+ die "Couldn't list $keytab: $!\n";
+
+ $dummy = <KLIST>;
+ $dummy = <KLIST>;
+ $dummy = <KLIST>;
+
+ while(<KLIST>) {
+ s/^\s+//;
+ split;
+ printf(SORT "keytab:FILE:%s\t%s-%s\t%s\t%s,%s\n",$keytab,
+ @_[3,0,4,1,2]);
+ }
+}
+
+close(SORT);
diff --git a/src/kadmin/testing/scripts/simple_dump.plin b/src/kadmin/testing/scripts/simple_dump.plin
new file mode 100644
index 000000000..ea94ab2d1
--- /dev/null
+++ b/src/kadmin/testing/scripts/simple_dump.plin
@@ -0,0 +1,88 @@
+#!/usr/local/bin/perl
+
+#
+# $Id$
+#
+
+## ovsec_adm_export format
+## [0]"policy" [1]name [2]pw_min_life [3]pw_max_life [4]pw_min_length [5]pw_min_classes [6]pw_history_num [7]policy_refcnt
+## [0]"princ" [1]name [2]policy [3]aux_attributes [4]old_key_len [5]admin_history_kvno [6..]old_keys
+$oaevers = "1.0";
+
+open(SORT, "|sort") || die "Couldn't open pipe to sort for output: $!\n";
+
+open(OAE, "$ENV{'TOP'}/install/admin/ovsec_adm_export|") ||
+ die "Couldn't get oae: $!\n";
+
+$header = <OAE>;
+
+die "Not ovsec_adm_export output\n"
+ if ($header !~ /^OpenV\*Secure V(\d+\.\d+)/);
+
+$stdinvers = $1;
+
+die "Expected oae version $oaevers, got $stdinvers instead.\n"
+ if $stdinvers ne $oaevers;
+
+while(<OAE>) {
+ if (/^End of Database/) {
+ last;
+ } elsif (/^policy/) {
+ print SORT;
+ } elsif (/^princ/) {
+ split(/\t/);
+
+ $_[2] = "\"\"" if !$_[2];
+
+ $_[3] = hex("0x".$_[3]);
+
+ $princ{$_[1]} = sprintf("%s\t0x%04x",@_[2,3]);
+ }
+}
+
+## kdb_edit ddb format
+## [0]strlen(principal) [1]strlen(mod_name) [2]key.length [3]alt_key.length [4]salt_length [5]alt_salt_length [6]principal [7]key.key_type [8]key.contents [9]kvno [10]max_life [11]max_renewable_life [12]mkvno [13]expiration [14]pw_expiration [15]last_pwd_change [16]last_success [17]last_failed [18]fail_auth_count [19]mod_name [20]mod_date [21]attributes [22]salt_type [23]salt [24]alt_key.contents [25]alt_salt [26..33]expansion*8;
+$ddbvers = "2.0";
+
+open(DDB, "$ENV{'TOP'}/install/admin/kdb5_edit -r SECURE-TEST.OV.COM -R ddb|") ||
+ die "Couldn't get ddb: $!\n";
+
+$header = <DDB>;
+
+die "Not a kdb5_edit ddb\n"
+ if ($header !~ /^kdb5_edit load_dump version (\d+\.\d+)/);
+
+$stdinvers = $1;
+
+die "Expected ddb version $ddbvers, got $stdinvers instead.\n"
+ if $stdinvers ne $ddbvers;
+
+## [6]principal [9]kvno [19]mod_name [10]max_life [13]expiration [14]pw_expiration [21]attributes // [2]policy [3]aux_attributes
+
+while(<DDB>) {
+ split;
+
+ print SORT join("\t","princ",(@_)[6,9,19,10,13,14],
+ sprintf("0x%04x",$_[21]),
+ $princ{$_[6]}),"\n";
+}
+
+close(DDB);
+
+for $keytab (@ARGV) {
+ open(KLIST, "$ENV{'TOP'}/install/bin/klist -k -t -K FILE:$keytab|") ||
+ die "Couldn't list $keytab: $!\n";
+
+ $dummy = <KLIST>;
+ $dummy = <KLIST>;
+ $dummy = <KLIST>;
+
+ while(<KLIST>) {
+ s/^\s+//;
+ split;
+ printf(SORT "keytab:FILE:%s\t%s-%s\t%s\t%s,%s\n",$keytab,
+ @_[3,0,4,1,2]);
+ }
+}
+
+close(SORT);
diff --git a/src/kadmin/testing/scripts/start_servers b/src/kadmin/testing/scripts/start_servers
new file mode 100644
index 000000000..2e395faf8
--- /dev/null
+++ b/src/kadmin/testing/scripts/start_servers
@@ -0,0 +1,70 @@
+#!/bin/sh
+#
+# Usage: start_servers [hostname [path]]
+#
+# This script turns a host into a OpenV*Secure primary server for the
+# realm SECURE-TEST.OV.COM. If no arguments are specified,
+# the local host is affected. Otherwise, the host hostname is
+# affected; the path argument is the top of the Secure install tree on
+# that host, and if it is not specified the current canonical value of
+# TOP is used.
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${SAVE_FILES=$TESTDIR/scripts/save_files.sh}
+DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl}
+DUMMY=${START_SERVERS_LOCAL=$TESTDIR/scripts/start_servers_local}
+# This'll be wrong sometimes
+DUMMY=${RSH_CMD=/usr/ucb/rsh}
+
+# If it's set, set it to true
+VERBOSE=${VERBOSE_TEST:+true}
+# Otherwise, set it to false
+DUMMY=${VERBOSE:=false}
+
+local=1
+
+if [ $# -gt 0 ]; then
+ if [ $# != 1 -a $# != 2 ]; then
+ echo "Usage: $0 [hostname [path]]" 1>&2
+ exit 1
+ fi
+
+ local=0
+ hostname=$1
+ if [ $# = 1 ]; then
+ rempath=`sh -c "cd $TOP && pwd"`
+ else
+ rempath=$2
+ fi
+fi
+
+if [ $local = 0 ]; then
+ $SAVE_FILES || exit 1
+ $FIX_CONF_FILES -server $hostname || exit 1
+
+# Using /usr/ucb/rsh and getting rid of "-k $REALM" until we get
+# around to fixing the fact that Kerberos rsh doesn't strip out "-k
+# REALM" when falling back.
+
+ START_SERVERS_LOCAL=`echo $START_SERVERS_LOCAL|sed "s%$TOP%$rempath%"`
+ CMD="$RSH_CMD $hostname -l root -n \
+ \"cd $rempath; \\\`testing/scripts/find-make.sh\\\` execute VERBOSE_TEST=$VERBOSE_TEST \
+ TOP=$rempath \
+ CMD='$START_SERVERS_LOCAL $rempath'\""
+
+ if $VERBOSE; then
+ echo "+++"
+ echo "+++ Begin execution of start_servers_local on $hostname"
+ echo "+++"
+ echo $CMD
+ fi
+ eval $CMD
+ if $VERBOSE; then
+ echo "+++"
+ echo "+++ End execution of start_servers_local on $hostname"
+ echo "+++"
+ fi
+else
+ $START_SERVERS_LOCAL
+fi
+
diff --git a/src/kadmin/testing/scripts/start_servers_local b/src/kadmin/testing/scripts/start_servers_local
new file mode 100644
index 000000000..a9c8e7957
--- /dev/null
+++ b/src/kadmin/testing/scripts/start_servers_local
@@ -0,0 +1,196 @@
+#!/bin/sh
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${SAVE_FILES=$TESTDIR/scripts/save_files.sh}
+DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl}
+DUMMY=${INITDB=$TESTDIR/scripts/init_db}
+DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL
+DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl}
+DUMMY=${STOP_SERVERS_LOCAL=$TESTDIR/scripts/stop_servers_local}
+
+if [ -d /usr/tmp ]; then
+ usrtmp=/usr/tmp
+else
+ usrtmp=/var/tmp
+fi
+
+$STOP_SERVERS_LOCAL -start_servers
+
+# If it's set, set it to true
+VERBOSE=${VERBOSE_TEST:+true}
+# Otherwise, set it to false
+DUMMY=${VERBOSE:=false}
+
+if $VERBOSE; then
+ REDIRECT=
+else
+ REDIRECT='>/dev/null'
+fi
+
+v4files=false
+while :; do
+ case $1 in
+ -keysalt)
+ shift
+ if [ $# -gt 0 ]; then
+ keysalts="$keysalts $1"
+ else
+ break
+ fi
+ ;;
+ -kdcport)
+ shift
+ if [ $# -gt 0 ]; then
+ kdcport=$1
+ else
+ break
+ fi
+ ;;
+ -v4files)
+ if [ "`whoami`" != "root" ]; then
+ echo "You must be root to use -v4files!" 1>&2
+ exit 1
+ fi
+ v4files=true
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+done
+
+if [ $# -gt 1 ]; then
+ echo "Usage: $0 [-kdcport port] [-keysalts tuple] ... [top]" 1>&2
+ exit 1
+elif [ $# = 1 ]; then
+ TOP=$1
+ export TOP
+fi
+
+# fixup the system config files
+if $v4files; then
+ $SAVE_FILES || exit 1
+ $FIX_CONF_FILES || exit 1
+fi
+
+# create a fresh db
+
+$INITDB "$keysalts" || exit 1
+
+# Post-process the config files based on our arguments
+if [ "$keysalts" != "" ]; then
+ sedcmd="s/\([ ]*supported_enctypes =\).*/\1 $keysalts/"
+ sed -e "$sedcmd" < /krb5/kdc.conf > /krb5/kdc.conf.new
+ mv /krb5/kdc.conf.new /krb5/kdc.conf
+fi
+if [ "$kdcport" != "" ] ; then
+ sedcmd="s/\(kdc_ports = .*\)[ ]*/\1, $kdcport/"
+ sed -e "$sedcmd" < /krb5/kdc.conf > /krb5/kdc.conf.new
+ mv /krb5/kdc.conf.new /krb5/kdc.conf
+fi
+
+# allow admin to krlogin as root (for cleanup)
+DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM
+hostname=`hostname`
+QUALNAME=`$TOP/testing/scripts/qualname $hostname`; export QUALNAME
+
+eval $SRVTCL <<'EOF' $REDIRECT
+source $env(TOP)/testing/tcl/util.t
+set r $env(REALM)
+set q $env(QUALNAME)
+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_principal $server_handle \
+ [simple_principal host/$q@$r] {OVSEC_KADM_PRINCIPAL} notathena]
+puts stdout [ovsec_kadm_destroy $server_handle]
+EOF
+
+# rm -f /etc/v5srvtab
+# eval $LOCAL_MAKE_KEYTAB -princ host/xCANONHOSTx /etc/v5srvtab $REDIRECT
+
+# run the servers (from the build tree)
+
+adm_start_file=/tmp/adm_server_start.$$
+kdc_start_file=/tmp/kdc_server_start.$$
+
+rm -f $kdc_start_file
+
+(trap "" 2; cd $TOP/../kdc; ./krb5kdc; touch $kdc_start_file) \
+ < /dev/null > $usrtmp/kdc-log 2>&1 &
+
+s=10
+max_s=60
+sofar_s=0
+timewait_s=300
+
+while true; do
+ rm -f $adm_start_file
+
+ (sleep 5; cd $TOP/server; ./kadmind $ovadm_args; \
+ touch $adm_start_file) < /dev/null > $usrtmp/kadm-log 2>&1 &
+
+ # wait until they start
+
+ while [ $sofar_s -le $max_s ]; do
+ if $VERBOSE; then
+ echo "Sleeping for $s seconds to allow servers" \
+ "to start..."
+ fi
+
+ sofar_s=`expr $sofar_s + $s`
+
+ sleep $s
+
+ if [ -f $adm_start_file -a -f $kdc_start_file ]; then
+ break
+ fi
+
+ done
+
+ if [ $sofar_s -le $max_s ]; then
+ if $VERBOSE; then
+ LOG_USER='log_user 1'
+ else
+ LOG_USER='log_user 0'
+ fi
+ if expect <<EOF
+ $LOG_USER
+ spawn telnet localhost 1751
+ expect {
+ "Connection refused" {
+ close
+ wait
+ exit 1
+ }
+ "Connected" {
+ send "close\n"
+ close
+ wait
+ exit 0
+ }
+ default {
+ catch {close}
+ wait
+ exit 1
+ }
+ }
+EOF
+ then
+ rm -f $kdc_start_file $adm_start_file
+ break
+ else
+ if $VERBOSE; then
+ echo "Could not connect to Admin server;" \
+ "attempting restart ($sofar_s" \
+ "seconds so far)."
+ fi
+ max_s=$timewait_s
+ continue
+ fi
+ else
+ echo "Admin server or KDC failed to start after $sofar_s" \
+ "seconds." 1>&2
+ exit 1
+ fi
+done
diff --git a/src/kadmin/testing/scripts/stop_servers b/src/kadmin/testing/scripts/stop_servers
new file mode 100644
index 000000000..fc5372dd4
--- /dev/null
+++ b/src/kadmin/testing/scripts/stop_servers
@@ -0,0 +1,84 @@
+#!/bin/sh
+#
+# Usage: stop_servers [hostname [path]]
+#
+# This script turns a host into a OpenV*Secure primary server for the
+# realm SECURE-TEST.OV.COM. If no arguments are specified,
+# the local host is affected. Otherwise, the host hostname is
+# affected; the path argument is the top of the Secure install tree on
+# that host, and if it is not specified the current canonical value of
+# TOP is used.
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl}
+DUMMY=${STOP_SERVERS_LOCAL=$TESTDIR/scripts/stop_servers_local}
+# This'll be wrong sometimes
+DUMMY=${RSH_CMD=/usr/ucb/rsh}
+DUMMY=${RESTORE_FILES=$TESTDIR/scripts/restore_files.sh}
+
+# If it's set, set it to true
+VERBOSE=${VERBOSE_TEST:+true}
+# Otherwise, set it to false
+DUMMY=${VERBOSE:=false}
+
+local=1
+
+if [ $# -gt 0 ]; then
+ if [ $# != 1 -a $# != 2 ]; then
+ echo "Usage: $0 [hostname [path]]" 1>&2
+ exit 1
+ fi
+
+ local=0
+ hostname=$1
+ if [ $# = 1 ]; then
+ rempath=`sh -c "cd $TOP && pwd"`
+ else
+ rempath=$2
+ fi
+fi
+
+if [ $local = 0 ]; then
+ if $VERBOSE; then
+ echo "+++ Stopping servers on remote host $hostname..."
+ fi
+
+# $FIX_CONF_FILES -server $hostname
+#
+# KRB5CCNAME=FILE:/tmp/krb5cc_stop_servers; export KRB5CCNAME
+#
+# expect <<EOF
+#spawn kinit admin
+#expect {
+# -re "Password for admin@SECURE-TEST.OV.COM" {
+# send "admin\n"
+# }
+#}
+#expect { eof { } }
+#EOF
+
+# Using /usr/ucb/rsh and getting rid of "-k REALM" until we get around
+# to fixing the fact that Kerberos rsh doesn't strip out "-k REALM"
+# when falling back.
+
+ STOP_SERVERS_LOCAL=`echo $STOP_SERVERS_LOCAL | sed "s%$TOP%$rempath%"`
+ CMD="$RSH_CMD $hostname -l root -n\
+ \"cd $rempath; \\\`testing/scripts/find-make.sh\\\` execute VERBOSE_TEST=$VERBOSE_TEST \
+ TOP=$rempath \
+ CMD='$STOP_SERVERS_LOCAL $rempath'\""
+ if $VERBOSE; then
+ echo "+++"
+ echo "+++ Begin execution of stop_servers_local on $hostname"
+ echo "+++"
+ echo $CMD
+ fi
+ eval $CMD
+ if $VERBOSE; then
+ echo "+++"
+ echo "+++ End execution of stop_servers_local on $hostname"
+ echo "+++"
+ fi
+ $RESTORE_FILES
+else
+ $STOP_SERVERS_LOCAL
+fi
diff --git a/src/kadmin/testing/scripts/stop_servers_local b/src/kadmin/testing/scripts/stop_servers_local
new file mode 100644
index 000000000..c0a97ef27
--- /dev/null
+++ b/src/kadmin/testing/scripts/stop_servers_local
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${RESTORE_FILES=$TESTDIR/scripts/restore_files.sh}
+
+# If it's set, set it to true
+VERBOSE=${VERBOSE_TEST:+true}
+# Otherwise, set it to false
+DUMMY=${VERBOSE:=false}
+
+v4files=false
+while [ $# -gt 0 ] ; do
+ case $1 in
+ -start_servers)
+ start_servers=$1
+ ;;
+ -v4files)
+ v4files=true
+ ;;
+ *)
+ TOP=$1
+ export TOP
+ ;;
+ esac
+ shift
+done
+
+# kill any running servers.
+
+if $VERBOSE; then echo "Killing servers:"; fi
+
+for pid in xxx \
+ `$PS_ALL | grep krb5kdc | grep -v grep | awk '{print $2}'` \
+ `$PS_ALL | grep kadmind | grep -v grep | awk '{print $2}'` \
+ ; do
+ case "$pid" in
+ xxx)
+ ;;
+ *)
+ if $VERBOSE; then $PS_PID$pid | grep -v COMMAND; fi
+ kill $pid
+ ;;
+ esac
+done
+
+# restore saved system config files
+if $v4files; then
+ $RESTORE_FILES $start_servers
+fi
diff --git a/src/kadmin/testing/scripts/verify_xrunner_report.pl.in b/src/kadmin/testing/scripts/verify_xrunner_report.pl.in
new file mode 100644
index 000000000..9d83c3ea2
--- /dev/null
+++ b/src/kadmin/testing/scripts/verify_xrunner_report.pl.in
@@ -0,0 +1,38 @@
+#!/usr/local/bin/perl
+
+sub usage { die "usage: $0 reportfile\n"; }
+
+$report = shift(@ARGV) || die &usage;
+
+open(REPORT, $report) || die "Couldn't open $report: $!\n";
+
+while(<REPORT>) {
+ if (/Process termination:/ && !/\bOK\b/) {
+ warn "Process termination not OK\n";
+ $warnings++;
+ } elsif (/Number of detected mismatches:\s*(\d+)/ && ($1 ne "0")) {
+ warn "Number of detected mismatches = $1\n";
+ $warnings++;
+ } elsif (/Detailed Results Description/) {
+ break;
+ }
+}
+
+while(<REPORT>) {
+ next if !/^\d+\s+/;
+
+ split;
+
+ if (($_[2] ne "run") &&
+ ($_[2] ne "OK") &&
+ ($_[2] ne "end-of-test")) {
+ warn "Unexpected result code $_[2] from test $_[4]\n";
+ $warnings++;
+ }
+}
+
+if ($warnings) {
+ warn "$warnings warnings.\n";
+}
+
+exit($warnings);
diff --git a/src/kadmin/testing/scripts/verify_xrunner_report.plin b/src/kadmin/testing/scripts/verify_xrunner_report.plin
new file mode 100644
index 000000000..9d83c3ea2
--- /dev/null
+++ b/src/kadmin/testing/scripts/verify_xrunner_report.plin
@@ -0,0 +1,38 @@
+#!/usr/local/bin/perl
+
+sub usage { die "usage: $0 reportfile\n"; }
+
+$report = shift(@ARGV) || die &usage;
+
+open(REPORT, $report) || die "Couldn't open $report: $!\n";
+
+while(<REPORT>) {
+ if (/Process termination:/ && !/\bOK\b/) {
+ warn "Process termination not OK\n";
+ $warnings++;
+ } elsif (/Number of detected mismatches:\s*(\d+)/ && ($1 ne "0")) {
+ warn "Number of detected mismatches = $1\n";
+ $warnings++;
+ } elsif (/Detailed Results Description/) {
+ break;
+ }
+}
+
+while(<REPORT>) {
+ next if !/^\d+\s+/;
+
+ split;
+
+ if (($_[2] ne "run") &&
+ ($_[2] ne "OK") &&
+ ($_[2] ne "end-of-test")) {
+ warn "Unexpected result code $_[2] from test $_[4]\n";
+ $warnings++;
+ }
+}
+
+if ($warnings) {
+ warn "$warnings warnings.\n";
+}
+
+exit($warnings);
diff --git a/src/kadmin/testing/tcl/util.t b/src/kadmin/testing/tcl/util.t
new file mode 100644
index 000000000..f4688aeee
--- /dev/null
+++ b/src/kadmin/testing/tcl/util.t
@@ -0,0 +1,61 @@
+proc simple_principal {name} {
+ return "{$name} 0 0 0 0 {$name} 0 0 0 0 null 0"
+}
+
+proc princ_w_pol {name policy} {
+ return "{$name} 0 0 0 0 {$name} 0 0 0 0 {$policy} 0"
+}
+
+proc simple_policy {name} {
+ return "{$name} 0 0 0 0 0 0"
+}
+
+proc config_params {masks values} {
+ if {[llength $masks] != [llength $values]} {
+ error "config_params: length of mask and values differ"
+ }
+
+ set params [list $masks 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 {}]
+ for {set i 0} {$i < [llength $masks]} {incr i} {
+ set mask [lindex $masks $i]
+ set value [lindex $values $i]
+ switch -glob -- $mask {
+ "KADM5_CONFIG_REALM" {set params [lreplace $params 1 1 $value]}
+ "KADM5_CONFIG_PROFILE" {set params [lreplace $params 2 2 $value]}
+ "KADM5_CONFIG_KADMIND_PORT" {
+ set params [lreplace $params 3 3 $value]}
+ "KADM5_CONFIG_ADMIN_SERVER" {
+ set params [lreplace $params 4 4 $value]}
+ "KADM5_CONFIG_DBNAME" {set params [lreplace $params 5 5 $value]}
+ "KADM5_CONFIG_ADBNAME" {set params [lreplace $params 6 6 $value]}
+ "KADM5_CONFIG_ADB_LOCKFILE" {
+ set params [lreplace $params 7 7 $value]}
+ "KADM5_CONFIG_ADMIN_KEYTAB" {
+ set params [lreplace $params 8 8 $value]}
+ "KADM5_CONFIG_ACL_FILE" {set params [lreplace $params 9 9 $value]}
+ "KADM5_CONFIG_DICT_FILE" {
+ set params [lreplace $params 10 10 $value]}
+ "KADM5_CONFIG_MKEY_FROM_KBD" {
+ set params [lreplace $params 11 11 $value]}
+ "KADM5_CONFIG_STASH_FILE" {
+ set params [lreplace $params 12 12 $value]}
+ "KADM5_CONFIG_MKEY_NAME" {
+ set params [lreplace $params 13 13 $value]}
+ "KADM5_CONFIG_ENCTYPE" {set params [lreplace $params 14 14 $value]}
+ "KADM5_CONFIG_MAX_LIFE" {
+ set params [lreplace $params 15 15 $value]}
+ "KADM5_CONFIG_MAX_RLIFE" {
+ set params [lreplace $params 16 16 $value]}
+ "KADM5_CONFIG_EXPIRATION" {
+ set params [lreplace $params 17 17 $value]}
+ "KADM5_CONFIG_FLAGS" {set params [lreplace $params 18 18 $value]}
+ "KADM5_CONFIG_ENCTYPES" {
+ set params [lreplace $params 19 20 [llength $value] $value]}
+ "*" {error "config_params: unknown mask $mask"}
+ }
+ }
+ return $params
+}
+
+
+
diff --git a/src/kadmin/testing/util/ChangeLog b/src/kadmin/testing/util/ChangeLog
new file mode 100644
index 000000000..36df4616d
--- /dev/null
+++ b/src/kadmin/testing/util/ChangeLog
@@ -0,0 +1,3 @@
+Fri Jul 12 15:04:52 1996 Marc Horowitz <marc@mit.edu>
+
+ * tcl_ovsec_kadm.c: renamed <ovsec_admin/foo.h> to <kadm5/foo.h>
diff --git a/src/kadmin/testing/util/Makefile.ov b/src/kadmin/testing/util/Makefile.ov
new file mode 100644
index 000000000..6d0159092
--- /dev/null
+++ b/src/kadmin/testing/util/Makefile.ov
@@ -0,0 +1,34 @@
+# $Id$
+
+TOP = ../..
+include $(TOP)/config.mk/template
+
+CFLAGS += -I$(TCLINC)
+
+SRCS = tcl_ovsec_kadm.c tcl_kadm5.c test.c
+OBJS = tcl_ovsec_kadm.o tcl_kadm5.o test.o
+
+PROG = ovsec_kadm_srv_tcl
+LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBDYN) $(LIBGSSAPI_KRB5) \
+ $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \
+ $(LIBISODE) $(LIBTCL) $(LIBM) $(LIBDB) $(LIBCOM_ERR) \
+ $(NDBMLIB) $(NETLIB)
+
+expand Program
+expand Depend
+
+PROG = ovsec_kadm_clnt_tcl
+LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBDYN) $(LIBGSSAPI_KRB5) \
+ $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \
+ $(LIBISODE) $(LIBTCL) $(LIBM) $(LIBDB) $(LIBCOM_ERR) \
+ $(NDBMLIB) $(BSDLIB) $(NETLIB)
+
+expand Program
+
+PROG = bsddb_dump
+SRCS = bsddb_dump.c
+OBJS = bsddb_dump.o
+LIBS = $(LIBDB)
+
+expand Program
+expand Depend
diff --git a/src/kadmin/testing/util/bsddb_dump.c b/src/kadmin/testing/util/bsddb_dump.c
new file mode 100644
index 000000000..ba69b8461
--- /dev/null
+++ b/src/kadmin/testing/util/bsddb_dump.c
@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ */
+
+#include <sys/file.h>
+#include <fcntl.h>
+#include <db.h>
+#include <stdio.h>
+
+main(int argc, char *argv[])
+{
+ char *file;
+ DB *db;
+ DBT dbkey, dbdata;
+ int code, i;
+
+ HASHINFO info;
+
+ info.hash = NULL;
+ info.bsize = 256;
+ info.ffactor = 8;
+ info.nelem = 25000;
+ info.lorder = 0;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: argv[0] dbfile\n");
+ exit(2);
+ }
+
+ file = argv[1];
+
+ if((db = dbopen(file, O_RDWR, 0666, DB_HASH, &info)) == NULL) {
+ perror("Opening db file");
+ exit(1);
+ }
+
+ if ((code = (*db->seq)(db, &dbkey, &dbdata, R_FIRST)) == -1) {
+ perror("starting db iteration");
+ exit(1);
+ }
+
+ while (code == 0) {
+ for (i=0; i<dbkey.size; i++)
+ printf("%02x", (int) ((unsigned char *) dbkey.data)[i]);
+ printf("\t");
+ for (i=0; i<dbdata.size; i++)
+ printf("%02x", (int) ((unsigned char *) dbdata.data)[i]);
+ printf("\n");
+
+ code = (*db->seq)(db, &dbkey, &dbdata, R_NEXT);
+ }
+
+ if (code == -1) {
+ perror("during db iteration");
+ exit(1);
+ }
+
+ if ((*db->close)(db) == -1) {
+ perror("closing db");
+ exit(1);
+ }
+
+ exit(0);
+}
diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c
new file mode 100644
index 000000000..b102cdfc2
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_kadm5.c
@@ -0,0 +1,2312 @@
+#include <stdio.h>
+#include <string.h>
+#include <tcl.h>
+#define USE_KADM5_API_VERSION 2
+#include <kadm5/admin.h>
+#include <com_err.h>
+#include <malloc.h>
+#include <k5-int.h>
+#include <errno.h>
+#include <stdlib.h>
+
+struct flagval {
+ char *name;
+ krb5_flags val;
+};
+
+/* XXX This should probably be in the hash table like server_handle */
+static krb5_context context;
+
+static struct flagval krb5_flags_array[] = {
+ {"KRB5_KDB_DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED},
+ {"KRB5_KDB_DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE},
+ {"KRB5_KDB_DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED},
+ {"KRB5_KDB_DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE},
+ {"KRB5_KDB_DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE},
+ {"KRB5_KDB_DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY},
+ {"KRB5_KDB_DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX},
+ {"KRB5_KDB_REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH},
+ {"KRB5_KDB_REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH},
+ {"KRB5_KDB_REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE},
+ {"KRB5_KDB_DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR},
+ {"KRB5_KDB_PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE}
+};
+
+static struct flagval aux_attributes[] = {
+ {"KADM5_POLICY", KADM5_POLICY}
+};
+
+static struct flagval principal_mask_flags[] = {
+ {"KADM5_PRINCIPAL", KADM5_PRINCIPAL},
+ {"KADM5_PRINC_EXPIRE_TIME", KADM5_PRINC_EXPIRE_TIME},
+ {"KADM5_PW_EXPIRATION", KADM5_PW_EXPIRATION},
+ {"KADM5_LAST_PWD_CHANGE", KADM5_LAST_PWD_CHANGE},
+ {"KADM5_ATTRIBUTES", KADM5_ATTRIBUTES},
+ {"KADM5_MAX_LIFE", KADM5_MAX_LIFE},
+ {"KADM5_MOD_TIME", KADM5_MOD_TIME},
+ {"KADM5_MOD_NAME", KADM5_MOD_NAME},
+ {"KADM5_KVNO", KADM5_KVNO},
+ {"KADM5_MKVNO", KADM5_MKVNO},
+ {"KADM5_AUX_ATTRIBUTES", KADM5_AUX_ATTRIBUTES},
+ {"KADM5_POLICY", KADM5_POLICY},
+ {"KADM5_POLICY_CLR", KADM5_POLICY_CLR},
+ {"KADM5_MAX_RLIFE", KADM5_MAX_RLIFE},
+ {"KADM5_LAST_SUCCESS", KADM5_LAST_SUCCESS},
+ {"KADM5_LAST_FAILED", KADM5_LAST_FAILED},
+ {"KADM5_FAIL_AUTH_COUNT", KADM5_FAIL_AUTH_COUNT},
+ {"KADM5_KEY_DATA", KADM5_KEY_DATA},
+ {"KADM5_TL_DATA", KADM5_TL_DATA},
+ {"KADM5_PRINCIPAL_NORMAL_MASK", KADM5_PRINCIPAL_NORMAL_MASK}
+};
+
+static struct flagval policy_mask_flags[] = {
+ {"KADM5_POLICY", KADM5_POLICY},
+ {"KADM5_PW_MAX_LIFE", KADM5_PW_MAX_LIFE},
+ {"KADM5_PW_MIN_LIFE", KADM5_PW_MIN_LIFE},
+ {"KADM5_PW_MIN_LENGTH", KADM5_PW_MIN_LENGTH},
+ {"KADM5_PW_MIN_CLASSES", KADM5_PW_MIN_CLASSES},
+ {"KADM5_PW_HISTORY_NUM", KADM5_PW_HISTORY_NUM},
+ {"KADM5_REF_COUNT", KADM5_REF_COUNT}
+};
+
+static struct flagval config_mask_flags[] = {
+ {"KADM5_CONFIG_REALM", KADM5_CONFIG_REALM},
+ {"KADM5_CONFIG_DBNAME", KADM5_CONFIG_DBNAME},
+ {"KADM5_CONFIG_MKEY_NAME", KADM5_CONFIG_MKEY_NAME},
+ {"KADM5_CONFIG_MAX_LIFE", KADM5_CONFIG_MAX_LIFE},
+ {"KADM5_CONFIG_MAX_RLIFE", KADM5_CONFIG_MAX_RLIFE},
+ {"KADM5_CONFIG_EXPIRATION", KADM5_CONFIG_EXPIRATION},
+ {"KADM5_CONFIG_FLAGS", KADM5_CONFIG_FLAGS},
+ {"KADM5_CONFIG_ADMIN_KEYTAB", KADM5_CONFIG_ADMIN_KEYTAB},
+ {"KADM5_CONFIG_STASH_FILE", KADM5_CONFIG_STASH_FILE},
+ {"KADM5_CONFIG_ENCTYPE", KADM5_CONFIG_ENCTYPE},
+ {"KADM5_CONFIG_ADBNAME", KADM5_CONFIG_ADBNAME},
+ {"KADM5_CONFIG_ADB_LOCKFILE", KADM5_CONFIG_ADB_LOCKFILE},
+ {"KADM5_CONFIG_PROFILE", KADM5_CONFIG_PROFILE},
+ {"KADM5_CONFIG_ACL_FILE", KADM5_CONFIG_ACL_FILE},
+ {"KADM5_CONFIG_KADMIND_PORT", KADM5_CONFIG_KADMIND_PORT},
+ {"KADM5_CONFIG_ENCTYPES", KADM5_CONFIG_ENCTYPES},
+ {"KADM5_CONFIG_ADMIN_SERVER", KADM5_CONFIG_ADMIN_SERVER},
+ {"KADM5_CONFIG_DICT_FILE", KADM5_CONFIG_DICT_FILE},
+ {"KADM5_CONFIG_MKEY_FROM_KBD", KADM5_CONFIG_MKEY_FROM_KBD},
+};
+
+static struct flagval priv_flags[] = {
+ {"KADM5_PRIV_GET", KADM5_PRIV_GET},
+ {"KADM5_PRIV_ADD", KADM5_PRIV_ADD},
+ {"KADM5_PRIV_MODIFY", KADM5_PRIV_MODIFY},
+ {"KADM5_PRIV_DELETE", KADM5_PRIV_DELETE}
+};
+
+
+static char *arg_error = "wrong # args";
+
+static Tcl_HashTable *struct_table = 0;
+
+static int put_server_handle(Tcl_Interp *interp, void *handle, char **name)
+{
+ int i = 1, newPtr = 0;
+ static char buf[20];
+ Tcl_HashEntry *entry;
+
+ if (! struct_table) {
+ if (! (struct_table =
+ malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ do {
+ /*
+ * Handles from ovsec_kadm_init() and kadm5_init() should not
+ * be mixed during unit tests, but the API would happily
+ * accept them. Making the hash entry names different in
+ * tcl_kadm.c and tcl_ovsec_kadm.c ensures that GET_HANDLE
+ * will fail if presented a handle from the other API.
+ */
+ sprintf(buf, "kadm5_handle%d", i);
+ entry = Tcl_CreateHashEntry(struct_table, buf, &newPtr);
+ i++;
+ } while (! newPtr);
+
+ Tcl_SetHashValue(entry, handle);
+
+ *name = buf;
+
+ return TCL_OK;
+}
+
+static int get_server_handle(Tcl_Interp *interp, char *name, void **handle)
+{
+ Tcl_HashEntry *entry;
+
+ if(!strcasecmp(name, "null"))
+ *handle = 0;
+ else {
+ if (! (struct_table &&
+ (entry = Tcl_FindHashEntry(struct_table, name)))) {
+ if (strncmp(name, "ovsec_kadm_handle", 17) == 0)
+ Tcl_AppendResult(interp, "ovsec_kadm handle "
+ "specified for kadm5 api: ", name, 0);
+ else
+ Tcl_AppendResult(interp, "unknown server handle ", name, 0);
+ return TCL_ERROR;
+ }
+ *handle = (void *) Tcl_GetHashValue(entry);
+ }
+ return TCL_OK;
+}
+
+static int remove_server_handle(Tcl_Interp *interp, char *name)
+{
+ Tcl_HashEntry *entry;
+
+ if (! (struct_table &&
+ (entry = Tcl_FindHashEntry(struct_table, name)))) {
+ Tcl_AppendResult(interp, "unknown server handle ", name, 0);
+ return TCL_ERROR;
+ }
+
+ Tcl_SetHashValue(entry, NULL);
+ return TCL_OK;
+}
+
+#define GET_HANDLE(num_args, ignored) \
+ void *server_handle; \
+ char *whoami = argv[0]; \
+ argv++, argc--; \
+ if (argc != num_args + 1) { \
+ Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \
+ return TCL_ERROR; \
+ } \
+ { \
+ int tcl_ret; \
+ if ((tcl_ret = get_server_handle(interp, argv[0], &server_handle)) \
+ != TCL_OK) { \
+ return tcl_ret; \
+ } \
+ } \
+ argv++, argc--;
+
+static Tcl_HashTable *create_flag_table(struct flagval *flags, int size)
+{
+ Tcl_HashTable *table;
+ Tcl_HashEntry *entry;
+ int i;
+
+ if (! (table = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_InitHashTable(table, TCL_STRING_KEYS);
+
+ for (i = 0; i < size; i++) {
+ int newPtr;
+
+ if (! (entry = Tcl_CreateHashEntry(table, flags[i].name, &newPtr))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_SetHashValue(entry, &flags[i].val);
+ }
+
+ return table;
+}
+
+
+static Tcl_DString *unparse_str(char *in_str)
+{
+ Tcl_DString *str;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ if (! in_str) {
+ Tcl_DStringAppend(str, "null", -1);
+ }
+ else {
+ Tcl_DStringAppend(str, in_str, -1);
+ }
+
+ return str;
+}
+
+
+
+static int parse_str(Tcl_Interp *interp, char *in_str, char **out_str)
+{
+ if (! in_str) {
+ *out_str = 0;
+ }
+ else if (! strcasecmp(in_str, "null")) {
+ *out_str = 0;
+ }
+ else {
+ *out_str = in_str;
+ }
+ return TCL_OK;
+}
+
+
+static void set_ok(Tcl_Interp *interp, char *string)
+{
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ Tcl_AppendElement(interp, "KADM5_OK");
+ Tcl_AppendElement(interp, string);
+}
+
+
+
+static Tcl_DString *unparse_err(kadm5_ret_t code)
+{
+ char *code_string, *error_string;
+ Tcl_DString *dstring;
+
+ switch (code) {
+ case KADM5_FAILURE: code_string = "KADM5_FAILURE"; break;
+ case KADM5_AUTH_GET: code_string = "KADM5_AUTH_GET"; break;
+ case KADM5_AUTH_ADD: code_string = "KADM5_AUTH_ADD"; break;
+ case KADM5_AUTH_MODIFY:
+ code_string = "KADM5_AUTH_MODIFY"; break;
+ case KADM5_AUTH_DELETE:
+ code_string = "KADM5_AUTH_DELETE"; break;
+ case KADM5_AUTH_INSUFFICIENT:
+ code_string = "KADM5_AUTH_INSUFFICIENT"; break;
+ case KADM5_BAD_DB: code_string = "KADM5_BAD_DB"; break;
+ case KADM5_DUP: code_string = "KADM5_DUP"; break;
+ case KADM5_RPC_ERROR: code_string = "KADM5_RPC_ERROR"; break;
+ case KADM5_NO_SRV: code_string = "KADM5_NO_SRV"; break;
+ case KADM5_BAD_HIST_KEY:
+ code_string = "KADM5_BAD_HIST_KEY"; break;
+ case KADM5_NOT_INIT: code_string = "KADM5_NOT_INIT"; break;
+ case KADM5_INIT: code_string = "KADM5_INIT"; break;
+ case KADM5_BAD_PASSWORD:
+ code_string = "KADM5_BAD_PASSWORD"; break;
+ case KADM5_UNK_PRINC: code_string = "KADM5_UNK_PRINC"; break;
+ case KADM5_UNK_POLICY: code_string = "KADM5_UNK_POLICY"; break;
+ case KADM5_BAD_MASK: code_string = "KADM5_BAD_MASK"; break;
+ case KADM5_BAD_CLASS: code_string = "KADM5_BAD_CLASS"; break;
+ case KADM5_BAD_LENGTH: code_string = "KADM5_BAD_LENGTH"; break;
+ case KADM5_BAD_POLICY: code_string = "KADM5_BAD_POLICY"; break;
+ case KADM5_BAD_HISTORY: code_string = "KADM5_BAD_HISTORY"; break;
+ case KADM5_BAD_PRINCIPAL:
+ code_string = "KADM5_BAD_PRINCIPAL"; break;
+ case KADM5_BAD_AUX_ATTR:
+ code_string = "KADM5_BAD_AUX_ATTR"; break;
+ case KADM5_PASS_Q_TOOSHORT:
+ code_string = "KADM5_PASS_Q_TOOSHORT"; break;
+ case KADM5_PASS_Q_CLASS:
+ code_string = "KADM5_PASS_Q_CLASS"; break;
+ case KADM5_PASS_Q_DICT:
+ code_string = "KADM5_PASS_Q_DICT"; break;
+ case KADM5_PASS_REUSE: code_string = "KADM5_PASS_REUSE"; break;
+ case KADM5_PASS_TOOSOON:
+ code_string = "KADM5_PASS_TOOSOON"; break;
+ case KADM5_POLICY_REF:
+ code_string = "KADM5_POLICY_REF"; break;
+ case KADM5_PROTECT_PRINCIPAL:
+ code_string = "KADM5_PROTECT_PRINCIPAL"; break;
+ case KADM5_BAD_SERVER_HANDLE:
+ code_string = "KADM5_BAD_SERVER_HANDLE"; break;
+ case KADM5_BAD_STRUCT_VERSION:
+ code_string = "KADM5_BAD_STRUCT_VERSION"; break;
+ case KADM5_OLD_STRUCT_VERSION:
+ code_string = "KADM5_OLD_STRUCT_VERSION"; break;
+ case KADM5_NEW_STRUCT_VERSION:
+ code_string = "KADM5_NEW_STRUCT_VERSION"; break;
+ case KADM5_BAD_API_VERSION:
+ code_string = "KADM5_BAD_API_VERSION"; break;
+ case KADM5_OLD_LIB_API_VERSION:
+ code_string = "KADM5_OLD_LIB_API_VERSION"; break;
+ case KADM5_OLD_SERVER_API_VERSION:
+ code_string = "KADM5_OLD_SERVER_API_VERSION"; break;
+ case KADM5_NEW_LIB_API_VERSION:
+ code_string = "KADM5_NEW_LIB_API_VERSION"; break;
+ case KADM5_NEW_SERVER_API_VERSION:
+ code_string = "KADM5_NEW_SERVER_API_VERSION"; break;
+ case KADM5_SECURE_PRINC_MISSING:
+ code_string = "KADM5_SECURE_PRINC_MISSING"; break;
+ case KADM5_NO_RENAME_SALT:
+ code_string = "KADM5_NO_RENAME_SALT"; break;
+ case KADM5_BAD_CLIENT_PARAMS:
+ code_string = "KADM5_BAD_CLIENT_PARAMS"; break;
+ case KADM5_BAD_SERVER_PARAMS:
+ code_string = "KADM5_BAD_SERVER_PARAMS"; break;
+ case KADM5_AUTH_LIST:
+ code_string = "KADM5_AUTH_LIST"; break;
+ case KADM5_AUTH_CHANGEPW:
+ code_string = "KADM5_AUTH_CHANGEPW"; break;
+ case KADM5_GSS_ERROR: code_string = "KADM5_GSS_ERROR"; break;
+ case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break;
+ case OSA_ADB_NOENT: code_string = "ENOENT"; break;
+ case OSA_ADB_DBINIT: code_string = "OSA_ADB_DBINIT"; break;
+ case OSA_ADB_BAD_POLICY: code_string = "Bad policy name"; break;
+ case OSA_ADB_BAD_PRINC: code_string = "Bad principal name"; break;
+ case OSA_ADB_BAD_DB: code_string = "Invalid database."; break;
+ case OSA_ADB_XDR_FAILURE: code_string = "OSA_ADB_XDR_FAILURE"; break;
+ case OSA_ADB_BADLOCKMODE: code_string = "OSA_ADB_BADLOCKMODE"; break;
+ case OSA_ADB_CANTLOCK_DB: code_string = "OSA_ADB_CANTLOCK_DB"; break;
+ case OSA_ADB_NOTLOCKED: code_string = "OSA_ADB_NOTLOCKED"; break;
+ case OSA_ADB_NOLOCKFILE: code_string = "OSA_ADB_NOLOCKFILE"; break;
+ case OSA_ADB_NOEXCL_PERM: code_string = "OSA_ADB_NOEXCL_PERM"; break;
+ case KRB5_KDB_INUSE: code_string = "KRB5_KDB_INUSE"; break;
+ case KRB5_KDB_UK_SERROR: code_string = "KRB5_KDB_UK_SERROR"; break;
+ case KRB5_KDB_UK_RERROR: code_string = "KRB5_KDB_UK_RERROR"; break;
+ case KRB5_KDB_UNAUTH: code_string = "KRB5_KDB_UNAUTH"; break;
+ case KRB5_KDB_NOENTRY: code_string = "KRB5_KDB_NOENTRY"; break;
+ case KRB5_KDB_ILL_WILDCARD: code_string = "KRB5_KDB_ILL_WILDCARD"; break;
+ case KRB5_KDB_DB_INUSE: code_string = "KRB5_KDB_DB_INUSE"; break;
+ case KRB5_KDB_DB_CHANGED: code_string = "KRB5_KDB_DB_CHANGED"; break;
+ case KRB5_KDB_TRUNCATED_RECORD:
+ code_string = "KRB5_KDB_TRUNCATED_RECORD"; break;
+ case KRB5_KDB_RECURSIVELOCK:
+ code_string = "KRB5_KDB_RECURSIVELOCK"; break;
+ case KRB5_KDB_NOTLOCKED: code_string = "KRB5_KDB_NOTLOCKED"; break;
+ case KRB5_KDB_BADLOCKMODE: code_string = "KRB5_KDB_BADLOCKMODE"; break;
+ case KRB5_KDB_DBNOTINITED: code_string = "KRB5_KDB_DBNOTINITED"; break;
+ case KRB5_KDB_DBINITED: code_string = "KRB5_KDB_DBINITED"; break;
+ case KRB5_KDB_ILLDIRECTION: code_string = "KRB5_KDB_ILLDIRECTION"; break;
+ case KRB5_KDB_NOMASTERKEY: code_string = "KRB5_KDB_NOMASTERKEY"; break;
+ case KRB5_KDB_BADMASTERKEY: code_string = "KRB5_KDB_BADMASTERKEY"; break;
+ case KRB5_KDB_INVALIDKEYSIZE:
+ code_string = "KRB5_KDB_INVALIDKEYSIZE"; break;
+ case KRB5_KDB_CANTREAD_STORED:
+ code_string = "KRB5_KDB_CANTREAD_STORED"; break;
+ case KRB5_KDB_BADSTORED_MKEY:
+ code_string = "KRB5_KDB_BADSTORED_MKEY"; break;
+ case KRB5_KDB_CANTLOCK_DB: code_string = "KRB5_KDB_CANTLOCK_DB"; break;
+ case KRB5_KDB_DB_CORRUPT: code_string = "KRB5_KDB_DB_CORRUPT"; break;
+ case KRB5_PARSE_ILLCHAR: code_string = "KRB5_PARSE_ILLCHAR"; break;
+ case KRB5_PARSE_MALFORMED: code_string = "KRB5_PARSE_MALFORMED"; break;
+ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN"; break;
+ case KRB5_REALM_UNKNOWN: code_string = "KRB5_REALM_UNKNOWN"; break;
+ case KRB5_KDC_UNREACH: code_string = "KRB5_KDC_UNREACH"; break;
+ case KRB5_KDCREP_MODIFIED: code_string = "KRB5_KDCREP_MODIFIED"; break;
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY: code_string = "KRB5KRB_AP_ERR_BAD_INTEGRITY"; break;
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"; break;
+ case KRB5_CONFIG_BADFORMAT: code_string = "KRB5_CONFIG_BADFORMAT"; break;
+ case EINVAL: code_string = "EINVAL"; break;
+ case ENOENT: code_string = "ENOENT"; break;
+ default: fprintf(stderr, "**** CODE %d ***\n", code); code_string = "UNKNOWN"; break;
+ }
+
+ error_string = (char *) error_message(code);
+
+ if (! (dstring = (Tcl_DString *) malloc(sizeof(Tcl_DString)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX Do we really want to exit? Ok if this is */
+ /* just a test program, but what about if it gets */
+ /* used for other things later? */
+ }
+
+ Tcl_DStringInit(dstring);
+
+ if (! (Tcl_DStringAppendElement(dstring, "ERROR") &&
+ Tcl_DStringAppendElement(dstring, code_string) &&
+ Tcl_DStringAppendElement(dstring, error_string))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ return dstring;
+}
+
+
+
+static void stash_error(Tcl_Interp *interp, krb5_error_code code)
+{
+ Tcl_DString *dstring = unparse_err(code);
+ Tcl_DStringResult(interp, dstring);
+ Tcl_DStringFree(dstring);
+ free(dstring);
+}
+
+static Tcl_DString *unparse_key_data(krb5_key_data *key_data, int n_key_data)
+{
+ Tcl_DString *str;
+ char buf[2048];
+ int i, j;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+ for (i = 0; i < n_key_data; i++) {
+ krb5_key_data *key = &key_data[i];
+
+ Tcl_DStringStartSublist(str);
+ sprintf(buf, "%d", key->key_data_type[0]);
+ Tcl_DStringAppendElement(str, buf);
+ sprintf(buf, "%d", key->key_data_ver > 1 ?
+ key->key_data_type[1] : -1);
+ Tcl_DStringAppendElement(str, buf);
+ if (key->key_data_contents[0]) {
+ sprintf(buf, "0x");
+ for (j = 0; j < key->key_data_length[0]; j++) {
+ sprintf(buf + 2*(j+1), "%02x",
+ key->key_data_contents[0][j]);
+ }
+ } else *buf = '\0';
+ Tcl_DStringAppendElement(str, buf);
+ Tcl_DStringEndSublist(str);
+ }
+
+ return str;
+}
+
+static Tcl_DString *unparse_tl_data(krb5_tl_data *tl_data, int n_tl_data)
+{
+ Tcl_DString *str;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+ if (n_tl_data > 0 && tl_data->tl_data_contents)
+ Tcl_DStringAppendElement(str, "[cannot unparse tl data yet]");
+ else
+ Tcl_DStringAppendElement(str, "");
+
+ return str;
+}
+
+static Tcl_DString *unparse_flags(struct flagval *array, int size,
+ krb5_int32 flags)
+{
+ int i;
+ Tcl_DString *str;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ for (i = 0; i < size; i++) {
+ if (flags & array[i].val) {
+ Tcl_DStringAppendElement(str, array[i].name);
+ }
+ }
+
+ return str;
+}
+
+
+static int parse_flags(Tcl_Interp *interp, Tcl_HashTable *table,
+ struct flagval *array, int size, char *str,
+ krb5_flags *flags)
+{
+ int tcl_ret, tmp, argc, i, retcode = TCL_OK;
+ char **argv;
+ Tcl_HashEntry *entry;
+
+ if ((tcl_ret = Tcl_GetInt(interp, str, &tmp)) == TCL_OK) {
+ *flags = tmp;
+ return TCL_OK;
+ }
+ Tcl_ResetResult(interp);
+
+ if ((tcl_ret = Tcl_SplitList(interp, str, &argc, &argv)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (! table) {
+ table = create_flag_table(array, size);
+ }
+
+ *flags = 0;
+
+ for (i = 0; i < argc; i++) {
+ if (! (entry = Tcl_FindHashEntry(table, argv[i]))) {
+ Tcl_AppendResult(interp, "unknown krb5 flag ", argv[i], 0);
+ retcode = TCL_ERROR;
+ break;
+ }
+ *flags |= *(krb5_flags *) Tcl_GetHashValue(entry);
+ }
+
+ free(argv);
+ return(retcode);
+}
+
+static Tcl_DString *unparse_privs(krb5_flags flags)
+{
+ return unparse_flags(priv_flags, sizeof(priv_flags) /
+ sizeof(struct flagval), flags);
+}
+
+
+static Tcl_DString *unparse_krb5_flags(krb5_flags flags)
+{
+ return unparse_flags(krb5_flags_array, sizeof(krb5_flags_array) /
+ sizeof(struct flagval), flags);
+}
+
+static int parse_krb5_flags(Tcl_Interp *interp, char *str, krb5_flags *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, krb5_flags_array,
+ sizeof(krb5_flags_array) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+static Tcl_DString *unparse_aux_attributes(krb5_int32 flags)
+{
+ return unparse_flags(aux_attributes, sizeof(aux_attributes) /
+ sizeof(struct flagval), flags);
+}
+
+
+static int parse_aux_attributes(Tcl_Interp *interp, char *str, long *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, aux_attributes,
+ sizeof(aux_attributes) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+static int parse_principal_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, principal_mask_flags,
+ sizeof(principal_mask_flags) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+static int parse_policy_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, policy_mask_flags,
+ sizeof(policy_mask_flags) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+
+static Tcl_DString *unparse_principal_ent(kadm5_principal_ent_t princ)
+{
+ Tcl_DString *str, *tmp_dstring;
+ char *tmp;
+ char buf[20];
+ krb5_error_code krb5_ret;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ tmp = 0; /* It looks to me from looking at the library source */
+ /* code for krb5_parse_name that the pointer passed into */
+ /* it should be initialized to 0 if I want it do be */
+ /* allocated automatically. */
+ if (krb5_ret = krb5_unparse_name(context, princ->principal, &tmp)) {
+ /* XXX Do we want to return an error? Not sure. */
+ Tcl_DStringAppendElement(str, "[unparseable principal]");
+ }
+ else {
+ Tcl_DStringAppendElement(str, tmp);
+ free(tmp);
+ }
+
+ sprintf(buf, "%d", princ->princ_expire_time);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->last_pwd_change);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->pw_expiration);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->max_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ tmp = 0;
+ if (krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp)) {
+ /* XXX */
+ Tcl_DStringAppendElement(str, "[unparseable principal]");
+ }
+ else {
+ Tcl_DStringAppendElement(str, tmp);
+ free(tmp);
+ }
+
+ sprintf(buf, "%d", princ->mod_date);
+ Tcl_DStringAppendElement(str, buf);
+
+ tmp_dstring = unparse_krb5_flags(princ->attributes);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ sprintf(buf, "%d", princ->kvno);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->mkvno);
+ Tcl_DStringAppendElement(str, buf);
+
+ /* XXX This may be dangerous, because the contents of the policy */
+ /* field are undefined if the POLICY bit isn't set. However, I */
+ /* think it's a bug for the field not to be null in that case */
+ /* anyway, so we should assume that it will be null so that we'll */
+ /* catch it if it isn't. */
+
+ tmp_dstring = unparse_str(princ->policy);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ tmp_dstring = unparse_aux_attributes(princ->aux_attributes);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ sprintf(buf, "%d", princ->max_renewable_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->last_success);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->last_failed);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->fail_auth_count);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->n_key_data);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->n_tl_data);
+ Tcl_DStringAppendElement(str, buf);
+
+ tmp_dstring = unparse_key_data(princ->key_data, princ->n_key_data);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ tmp_dstring = unparse_tl_data(princ->tl_data, princ->n_tl_data);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ return str;
+}
+
+static int parse_keysalts(Tcl_Interp *interp, char *list,
+ krb5_key_salt_tuple **keysalts,
+ int num_keysalts)
+{
+ char **argv, **argv1 = NULL;
+ int i, tmp, argc, argc1, retcode;
+
+ *keysalts = NULL;
+ if (list == NULL)
+ return TCL_OK;
+
+ if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return retcode;
+ }
+ if (argc != num_keysalts) {
+ sprintf(interp->result, "%d keysalts specified, "
+ "but num_keysalts is %d", argc, num_keysalts);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ *keysalts = (krb5_key_salt_tuple *)
+ malloc(sizeof(krb5_key_salt_tuple)*num_keysalts);
+ for (i = 0; i < num_keysalts; i++) {
+ if ((retcode = Tcl_SplitList(interp, argv[i], &argc1, &argv1)) !=
+ TCL_OK) {
+ goto finished;
+ }
+ if (argc1 != 2) {
+ sprintf(interp->result, "wrong # fields in keysalt "
+ "(%d should be 2)", argc1);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing ks_enctype");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ (*keysalts)[i].ks_enctype = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing ks_salttype");
+ goto finished;
+ }
+ (*keysalts)[i].ks_salttype = tmp;
+
+ free(argv1);
+ }
+
+finished:
+ if (argv1)
+ free(argv1);
+ if (*keysalts)
+ free(*keysalts);
+ free(argv);
+ return retcode;
+}
+
+static int parse_config_params(Tcl_Interp *interp, char *list,
+ kadm5_config_params *params)
+{
+ static Tcl_HashTable *table = 0;
+ char **argv = NULL;
+ int tmp, argc, retcode;
+
+ memset(params, 0, sizeof(kadm5_config_params));
+ if (list == NULL)
+ return TCL_OK;
+
+ if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return retcode;
+ }
+
+ if (argc != 21) {
+ sprintf(interp->result,
+ "wrong # args in config params structure (%d should be 21)",
+ argc);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((retcode = parse_flags(interp, table, config_mask_flags,
+ sizeof(config_mask_flags) /
+ sizeof(struct flagval),
+ argv[0], &tmp)) != TCL_OK) {
+ goto finished;
+ }
+ params->mask = tmp;
+
+ if ((retcode = parse_str(interp, argv[1], &params->realm)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing realm name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[2], &params->profile)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing profile name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = Tcl_GetInt(interp, argv[3], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing kadmind_port");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->kadmind_port = tmp;
+ if ((retcode = parse_str(interp, argv[4], &params->admin_server))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing profile name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[5], &params->dbname)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing profile name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[6], &params->admin_dbname)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing admin_dbname name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[7], &params->admin_lockfile)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing admin_lockfile name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[8], &params->admin_keytab)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing admin_keytab name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[9], &params->acl_file)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing acl_file name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[10], &params->dict_file)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing dict_file name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = Tcl_GetInt(interp, argv[11], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing mkey_from_kbd");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->mkey_from_kbd = tmp;
+ if ((retcode = parse_str(interp, argv[12], &params->stash_file)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing stash_file name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[13], &params->mkey_name)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing mkey_name name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = Tcl_GetInt(interp, argv[14], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing enctype");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->enctype = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv[15], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing max_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->max_life = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv[16], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing max_rlife");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->max_rlife = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv[17], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing expiration");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->expiration = tmp;
+ if ((retcode = parse_krb5_flags(interp, argv[18], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing flags");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->flags = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv[19], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing num_keysalts");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->num_keysalts = tmp;
+ if ((retcode = parse_keysalts(interp, argv[20], &params->keysalts,
+ params->num_keysalts)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing keysalts");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+finished:
+ return retcode;
+}
+
+static int parse_principal_ent(Tcl_Interp *interp, char *list,
+ kadm5_principal_ent_t *out_princ)
+{
+ kadm5_principal_ent_t princ;
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ int argc;
+ char **argv;
+ int tmp;
+ int retcode = TCL_OK;
+
+ if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ if (argc != 12) {
+ sprintf(interp->result, "wrong # args in principal structure (%d should be 12)",
+ argc);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if (! (princ = malloc(sizeof *princ))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ if ((krb5_ret = krb5_parse_name(context, argv[0], &princ->principal)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ /*
+ * All of the numerical values parsed here are parsed into an
+ * "int" and then assigned into the structure in case the actual
+ * width of the field in the Kerberos structure is different from
+ * the width of an integer.
+ */
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing princ_expire_time");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->princ_expire_time = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing last_pwd_change");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->last_pwd_change = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_expiration");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->pw_expiration = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing max_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->max_life = tmp;
+
+ if ((krb5_ret = krb5_parse_name(context, argv[5], &princ->mod_name)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing mod_name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing mod_date");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->mod_date = tmp;
+
+ if ((tcl_ret = parse_krb5_flags(interp, argv[7], &princ->attributes))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing attributes");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing kvno");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->kvno = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing mkvno");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->mkvno = tmp;
+
+ if ((tcl_ret = parse_str(interp, argv[10], &princ->policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if(princ->policy != NULL) {
+ if(!(princ->policy = strdup(princ->policy))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1);
+ }
+ }
+
+ if ((tcl_ret = parse_aux_attributes(interp, argv[11],
+ &princ->aux_attributes)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing aux_attributes");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+finished:
+ free(argv);
+ *out_princ = princ;
+ return retcode;
+}
+
+
+static void free_principal_ent(kadm5_principal_ent_t *princ)
+{
+ krb5_free_principal(context, (*princ)->principal);
+ krb5_free_principal(context, (*princ)->mod_name);
+ free(*princ);
+ *princ = 0;
+}
+
+static Tcl_DString *unparse_policy_ent(kadm5_policy_ent_t policy)
+{
+ Tcl_DString *str, *tmp_dstring;
+ char buf[20];
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ tmp_dstring = unparse_str(policy->policy);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ sprintf(buf, "%d", policy->pw_min_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_max_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_min_length);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_min_classes);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_history_num);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->policy_refcnt);
+ Tcl_DStringAppendElement(str, buf);
+
+ return str;
+}
+
+
+
+static int parse_policy_ent(Tcl_Interp *interp, char *list,
+ kadm5_policy_ent_t *out_policy)
+{
+ kadm5_policy_ent_t policy;
+ int tcl_ret;
+ int argc;
+ char **argv;
+ int tmp;
+ int retcode = TCL_OK;
+
+ if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ if (argc != 7) {
+ sprintf(interp->result, "wrong # args in policy structure (%d should be 7)",
+ argc);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if (! (policy = malloc(sizeof *policy))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy->policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if(policy->policy != NULL) {
+ if (! (policy->policy = strdup(policy->policy))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ }
+
+ /*
+ * All of the numerical values parsed here are parsed into an
+ * "int" and then assigned into the structure in case the actual
+ * width of the field in the Kerberos structure is different from
+ * the width of an integer.
+ */
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_min_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_min_life = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_max_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_max_life = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_min_length");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_min_length = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_min_classes");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_min_classes = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[5], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_history_num");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_history_num = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy_refcnt");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->policy_refcnt = tmp;
+
+finished:
+ free(argv);
+ *out_policy = policy;
+ return retcode;
+}
+
+
+static void free_policy_ent(kadm5_policy_ent_t *policy)
+{
+ free(*policy);
+ *policy = 0;
+}
+
+static Tcl_DString *unparse_keytype(krb5_enctype enctype)
+{
+ Tcl_DString *str;
+ char buf[50];
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ switch (enctype) {
+ /* XXX is this right? */
+ case ENCTYPE_NULL: Tcl_DStringAppend(str, "ENCTYPE_NULL", -1); break;
+ case ENCTYPE_DES_CBC_CRC:
+ Tcl_DStringAppend(str, "ENCTYPE_DES_CBC_CRC", -1); break;
+ default:
+ sprintf(buf, "UNKNOWN KEYTYPE (0x%x)", enctype);
+ Tcl_DStringAppend(str, buf, -1);
+ break;
+ }
+
+ return str;
+}
+
+
+static Tcl_DString *unparse_keyblocks(krb5_keyblock *keyblocks, int num_keys)
+{
+ Tcl_DString *str;
+ Tcl_DString *keytype;
+ int i, j;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ for (j = 0; j < num_keys; j++) {
+ krb5_keyblock *keyblock = &keyblocks[j];
+
+ Tcl_DStringStartSublist(str);
+
+ keytype = unparse_keytype(keyblock->enctype);
+ Tcl_DStringAppendElement(str, keytype->string);
+ Tcl_DStringFree(keytype);
+ free(keytype);
+ if (keyblock->length == 0) {
+ Tcl_DStringAppendElement(str, "0x00");
+ }
+ else {
+ Tcl_DStringAppendElement(str, "0x");
+ for (i = 0; i < keyblock->length; i++) {
+ char buf[3];
+ sprintf(buf, "%02x", (int) keyblock->contents[i]);
+ Tcl_DStringAppend(str, buf, -1);
+ }
+ }
+
+ Tcl_DStringEndSublist(str);
+ }
+
+
+ return str;
+}
+
+enum init_type { INIT_NONE, INIT_PASS, INIT_CREDS };
+
+int _tcl_kadm5_init_any(enum init_type init_type, ClientData clientData,
+ Tcl_Interp *interp, int argc, char *argv[])
+{
+ kadm5_ret_t ret;
+ char *client_name, *pass, *service_name, *realm;
+ int tcl_ret;
+ krb5_ui_4 struct_version, api_version;
+ char *handle_var;
+ void *server_handle;
+ char *handle_name, *params_str;
+ char *whoami = argv[0];
+ kadm5_config_params params;
+
+ argv++, argc--;
+
+ krb5_init_context(&context);
+
+ if (argc != 7) {
+ Tcl_AppendResult(interp, whoami, ": ", arg_error, 0);
+ return TCL_ERROR;
+ }
+
+ if (((tcl_ret = parse_str(interp, argv[0], &client_name)) != TCL_OK) ||
+ ((tcl_ret = parse_str(interp, argv[1], &pass)) != TCL_OK) ||
+ ((tcl_ret = parse_str(interp, argv[2], &service_name)) != TCL_OK) ||
+ ((tcl_ret = parse_str(interp, argv[3], &params_str)) != TCL_OK) ||
+ ((tcl_ret = parse_config_params(interp, params_str, &params))
+ != TCL_OK) ||
+ ((tcl_ret = Tcl_GetInt(interp, argv[4], (int *) &struct_version)) !=
+ TCL_OK) ||
+ ((tcl_ret = Tcl_GetInt(interp, argv[5], (int *) &api_version)) !=
+ TCL_OK)) {
+ return tcl_ret;
+ }
+
+ handle_var = argv[6];
+
+ if (! (handle_var && *handle_var)) {
+ Tcl_SetResult(interp, "must specify server handle variable name",
+ TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ if (init_type == INIT_CREDS) {
+ krb5_ccache cc;
+
+ if (pass == NULL) {
+ if (ret = krb5_cc_default(context, &cc)) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ } else {
+ if (ret = krb5_cc_resolve(context, pass, &cc)) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ }
+
+ ret = kadm5_init_with_creds(client_name, cc, service_name,
+ &params, struct_version,
+ api_version, &server_handle);
+
+ (void) krb5_cc_close(context, cc);
+ } else
+ ret = kadm5_init(client_name, pass, service_name, &params,
+ struct_version, api_version, &server_handle);
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = put_server_handle(interp, server_handle, &handle_name))
+ != TCL_OK) {
+ return tcl_ret;
+ }
+
+ if (! Tcl_SetVar(interp, handle_var, handle_name, TCL_LEAVE_ERR_MSG)) {
+ return TCL_ERROR;
+ }
+
+ set_ok(interp, "KADM5 API initialized.");
+ return TCL_OK;
+}
+
+int tcl_kadm5_init(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ return _tcl_kadm5_init_any(INIT_PASS, clientData, interp, argc, argv);
+}
+
+int tcl_kadm5_init_with_creds(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ return _tcl_kadm5_init_any(INIT_CREDS, clientData, interp, argc, argv);
+}
+
+int tcl_kadm5_destroy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ kadm5_ret_t ret;
+ int tcl_ret;
+
+ GET_HANDLE(0, 0);
+
+ ret = kadm5_destroy(server_handle);
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = remove_server_handle(interp, argv[-1])) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ set_ok(interp, "KADM5 API deinitialized.");
+ return TCL_OK;
+}
+
+int tcl_kadm5_create_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ int tcl_ret;
+ kadm5_ret_t ret;
+ int retcode = TCL_OK;
+ char *princ_string;
+ kadm5_principal_ent_t princ = 0;
+ krb5_int32 mask;
+ char *pw;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+
+ GET_HANDLE(3, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing principal");
+ return tcl_ret;
+ }
+
+ if (princ_string &&
+ ((tcl_ret = parse_principal_ent(interp, princ_string, &princ))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[2], &pw)) != TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+#ifdef OVERRIDE
+ if ((tcl_ret = Tcl_GetBoolean(interp, argv[3], &override_qual)) !=
+ TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+#endif
+
+#ifdef OVERRIDE
+ ret = kadm5_create_principal(server_handle, princ, mask, pw,
+ override_qual);
+#else
+ ret = kadm5_create_principal(server_handle, princ, mask, pw);
+#endif
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ else {
+ set_ok(interp, "Principal created.");
+ }
+
+finished:
+ if (princ) {
+ free_principal_ent(&princ);
+ }
+ return retcode;
+}
+
+
+
+int tcl_kadm5_delete_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ krb5_error_code krb5_ret;
+ kadm5_ret_t ret;
+ int tcl_ret;
+ char *name;
+
+ GET_HANDLE(1, 0);
+
+ if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK)
+ return tcl_ret;
+ if(name != NULL) {
+ if (krb5_ret = krb5_parse_name(context, name, &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal");
+ return TCL_ERROR;
+ }
+ } else princ = NULL;
+ ret = kadm5_delete_principal(server_handle, princ);
+
+ if(princ != NULL)
+ krb5_free_principal(context, princ);
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Principal deleted.");
+ return TCL_OK;
+ }
+}
+
+
+
+int tcl_kadm5_modify_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *princ_string;
+ kadm5_principal_ent_t princ = 0;
+ int tcl_ret;
+ krb5_int32 mask;
+ int retcode = TCL_OK;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(2, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing principal");
+ return tcl_ret;
+ }
+
+ if (princ_string &&
+ ((tcl_ret = parse_principal_ent(interp, princ_string, &princ))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = kadm5_modify_principal(server_handle, princ, mask);
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Principal modified.");
+ }
+
+finished:
+ if (princ) {
+ free_principal_ent(&princ);
+ }
+ return retcode;
+}
+
+
+int tcl_kadm5_rename_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal source, target;
+ krb5_error_code krb5_ret;
+ kadm5_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(2, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &source)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing source");
+ return TCL_ERROR;
+ }
+
+ if (krb5_ret = krb5_parse_name(context, argv[1], &target)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing target");
+ krb5_free_principal(context, source);
+ return TCL_ERROR;
+ }
+
+ ret = kadm5_rename_principal(server_handle, source, target);
+
+ if (ret == KADM5_OK) {
+ set_ok(interp, "Principal renamed.");
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+ krb5_free_principal(context, source);
+ krb5_free_principal(context, target);
+ return retcode;
+}
+
+
+
+int tcl_kadm5_chpass_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ char *pw;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ int retcode = TCL_OK;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(2, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &pw)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing password");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+#ifdef OVERRIDE
+ if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing override_qual");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = kadm5_chpass_principal(server_handle,
+ princ, pw, override_qual);
+#else
+ ret = kadm5_chpass_principal(server_handle, princ, pw);
+#endif
+
+ if (ret == KADM5_OK) {
+ set_ok(interp, "Password changed.");
+ goto finished;
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ krb5_free_principal(context, princ);
+ return retcode;
+}
+
+
+
+int tcl_kadm5_chpass_principal_util(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ char *new_pw;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+ char *pw_ret, *pw_ret_var;
+ char msg_ret[1024], *msg_ret_var;
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ kadm5_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(4, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &new_pw)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing new password");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+#ifdef OVERRIDE
+ if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing override_qual");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+#endif
+ if ((tcl_ret = parse_str(interp, argv[3], &pw_ret_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_ret variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[4], &msg_ret_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing msg_ret variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = kadm5_chpass_principal_util(server_handle, princ, new_pw,
+#ifdef OVERRIDE
+ override_qual,
+#endif
+ pw_ret_var ? &pw_ret : 0,
+ msg_ret_var ? msg_ret : 0);
+
+ if (ret == KADM5_OK) {
+ if (pw_ret_var &&
+ (! Tcl_SetVar(interp, pw_ret_var, pw_ret,
+ TCL_LEAVE_ERR_MSG))) {
+ Tcl_AppendElement(interp, "while setting pw_ret variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if (msg_ret_var &&
+ (! Tcl_SetVar(interp, msg_ret_var, msg_ret,
+ TCL_LEAVE_ERR_MSG))) {
+ Tcl_AppendElement(interp,
+ "while setting msg_ret variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Password changed.");
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ krb5_free_principal(context, princ);
+ return retcode;
+}
+
+
+
+int tcl_kadm5_randkey_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ krb5_keyblock *keyblocks;
+ int num_keys;
+ char *keyblock_var, *num_var, buf[50];
+ Tcl_DString *keyblock_dstring = 0;
+ krb5_error_code krb5_ret;
+ kadm5_ret_t ret;
+ int tcl_ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(3, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &keyblock_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing keyblock variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((tcl_ret = parse_str(interp, argv[2], &num_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing keyblock variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = kadm5_randkey_principal(server_handle,
+ princ, keyblock_var ? &keyblocks : 0,
+ num_var ? &num_keys : 0);
+
+ if (ret == KADM5_OK) {
+ if (keyblock_var) {
+ keyblock_dstring = unparse_keyblocks(keyblocks, num_keys);
+ if (! Tcl_SetVar(interp, keyblock_var,
+ keyblock_dstring->string,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting keyblock variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ }
+ if (num_var) {
+ sprintf(buf, "%d", num_keys);
+ if (! Tcl_SetVar(interp, num_var, buf,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting num_keys variable");
+ }
+ }
+ set_ok(interp, "Key randomized.");
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ krb5_free_principal(context, princ);
+ if (keyblock_dstring) {
+ Tcl_DStringFree(keyblock_dstring);
+ free(keyblock_dstring);
+ }
+ return retcode;
+}
+
+
+
+int tcl_kadm5_get_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ kadm5_principal_ent_rec ent;
+ Tcl_DString *ent_dstring = 0;
+ char *ent_var;
+ char *name;
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ kadm5_ret_t ret = -1;
+ krb5_int32 mask;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(3, 1);
+
+ if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK)
+ return tcl_ret;
+ if(name != NULL) {
+ if (krb5_ret = krb5_parse_name(context, name, &princ)) {
+ stash_error(interp, ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+ } else princ = NULL;
+
+ if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((tcl_ret = parse_principal_mask(interp, argv[2], &mask)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing principal mask");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = kadm5_get_principal(server_handle, princ, ent_var ? &ent : 0,
+ mask);
+
+ if (ret == KADM5_OK) {
+ if (ent_var) {
+ ent_dstring = unparse_principal_ent(&ent);
+ if (! Tcl_SetVar(interp, ent_var, ent_dstring->string,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting entry variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Principal retrieved.");
+ }
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ if (ent_dstring) {
+ Tcl_DStringFree(ent_dstring);
+ free(ent_dstring);
+ }
+ if(princ != NULL)
+ krb5_free_principal(context, princ);
+ if (ret == KADM5_OK && ent_var &&
+ (ret = kadm5_free_principal_ent(server_handle, &ent)) &&
+ (retcode == TCL_OK)) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ return retcode;
+}
+
+int tcl_kadm5_create_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ int tcl_ret;
+ kadm5_ret_t ret;
+ int retcode = TCL_OK;
+ char *policy_string;
+ kadm5_policy_ent_t policy = 0;
+ krb5_int32 mask;
+
+ GET_HANDLE(2, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy");
+ return tcl_ret;
+ }
+
+ if (policy_string &&
+ ((tcl_ret = parse_policy_ent(interp, policy_string, &policy))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+
+ ret = kadm5_create_policy(server_handle, policy, mask);
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ else {
+ set_ok(interp, "Policy created.");
+ }
+
+finished:
+ if (policy) {
+ free_policy_ent(&policy);
+ }
+ return retcode;
+}
+
+
+
+int tcl_kadm5_delete_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ krb5_error_code krb5_ret;
+ kadm5_ret_t ret;
+ char *policy;
+ int tcl_ret;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy name");
+ return TCL_ERROR;
+ }
+
+ ret = kadm5_delete_policy(server_handle, policy);
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Policy deleted.");
+ return TCL_OK;
+ }
+}
+
+
+
+int tcl_kadm5_modify_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *policy_string;
+ kadm5_policy_ent_t policy = 0;
+ int tcl_ret;
+ krb5_int32 mask;
+ int retcode = TCL_OK;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(2, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy");
+ return tcl_ret;
+ }
+
+ if (policy_string &&
+ ((tcl_ret = parse_policy_ent(interp, policy_string, &policy))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = kadm5_modify_policy(server_handle, policy, mask);
+
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Policy modified.");
+ }
+
+finished:
+ if (policy) {
+ free_policy_ent(&policy);
+ }
+ return retcode;
+}
+
+
+int tcl_kadm5_get_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ kadm5_policy_ent_rec ent;
+ Tcl_DString *ent_dstring = 0;
+ char *policy;
+ char *ent_var;
+ int tcl_ret;
+ kadm5_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(2, 1);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry variable name");
+ return TCL_ERROR;
+ }
+
+ ret = kadm5_get_policy(server_handle, policy, ent_var ? &ent : 0);
+
+ if (ret == KADM5_OK) {
+ if (ent_var) {
+ ent_dstring = unparse_policy_ent(&ent);
+ if (! Tcl_SetVar(interp, ent_var, ent_dstring->string,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting entry variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Policy retrieved.");
+ }
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ if (ent_dstring) {
+ Tcl_DStringFree(ent_dstring);
+ free(ent_dstring);
+ }
+ if (ent_var && ret == KADM5_OK &&
+ (ret = kadm5_free_policy_ent(server_handle, &ent)) &&
+ (retcode == TCL_OK)) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ return retcode;
+}
+
+
+
+int tcl_kadm5_free_principal_ent(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *ent_name;
+ kadm5_principal_ent_t ent;
+ int tcl_ret;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry name");
+ return TCL_ERROR;
+ }
+
+ if ((! ent_name) &&
+ (ret = kadm5_free_principal_ent(server_handle, 0))) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ Tcl_HashEntry *entry;
+
+ if (strncmp(ent_name, "principal", sizeof("principal")-1)) {
+ Tcl_AppendResult(interp, "invalid principal handle \"",
+ ent_name, "\"", 0);
+ return TCL_ERROR;
+ }
+ if (! struct_table) {
+ if (! (struct_table = malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) {
+ Tcl_AppendResult(interp, "principal handle \"", ent_name,
+ "\" not found", 0);
+ return TCL_ERROR;
+ }
+
+ ent = Tcl_GetHashValue(entry);
+
+ if (ret = kadm5_free_principal_ent(server_handle, ent)) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ Tcl_DeleteHashEntry(entry);
+ }
+ set_ok(interp, "Principal freed.");
+ return TCL_OK;
+}
+
+
+int tcl_kadm5_free_policy_ent(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *ent_name;
+ kadm5_policy_ent_t ent;
+ int tcl_ret;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry name");
+ return TCL_ERROR;
+ }
+
+ if ((! ent_name) &&
+ (ret = kadm5_free_policy_ent(server_handle, 0))) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ Tcl_HashEntry *entry;
+
+ if (strncmp(ent_name, "policy", sizeof("policy")-1)) {
+ Tcl_AppendResult(interp, "invalid principal handle \"",
+ ent_name, "\"", 0);
+ return TCL_ERROR;
+ }
+ if (! struct_table) {
+ if (! (struct_table = malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) {
+ Tcl_AppendResult(interp, "policy handle \"", ent_name,
+ "\" not found", 0);
+ return TCL_ERROR;
+ }
+
+ ent = Tcl_GetHashValue(entry);
+
+ if (ret = kadm5_free_policy_ent(server_handle, ent)) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ Tcl_DeleteHashEntry(entry);
+ }
+ set_ok(interp, "Policy freed.");
+ return TCL_OK;
+}
+
+
+int tcl_kadm5_get_privs(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ int tcl_ret;
+ char *set_ret;
+ kadm5_ret_t ret;
+ char *priv_var;
+ long privs;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &priv_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing privs variable name");
+ return TCL_ERROR;
+ }
+
+ ret = kadm5_get_privs(server_handle, priv_var ? &privs : 0);
+
+ if (ret == KADM5_OK) {
+ if (priv_var) {
+ Tcl_DString *str = unparse_privs(privs);
+ set_ret = Tcl_SetVar(interp, priv_var, str->string,
+ TCL_LEAVE_ERR_MSG);
+ Tcl_DStringFree(str);
+ free(str);
+ if (! set_ret) {
+ Tcl_AppendElement(interp, "while setting priv variable");
+ return TCL_ERROR;
+ }
+ }
+ set_ok(interp, "Privileges retrieved.");
+ return TCL_OK;
+ }
+ else {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+}
+
+
+void Tcl_kadm5_init(Tcl_Interp *interp)
+{
+ char buf[20];
+
+ Tcl_SetVar(interp, "KADM5_ADMIN_SERVICE",
+ KADM5_ADMIN_SERVICE, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "KADM5_CHANGEPW_SERVICE",
+ KADM5_CHANGEPW_SERVICE, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION);
+ Tcl_SetVar(interp, "KADM5_STRUCT_VERSION", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", KADM5_API_VERSION_1);
+ Tcl_SetVar(interp, "KADM5_API_VERSION_1", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", KADM5_API_VERSION_2);
+ Tcl_SetVar(interp, "KADM5_API_VERSION_2", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", KADM5_API_VERSION_MASK);
+ Tcl_SetVar(interp, "KADM5_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION_MASK);
+ Tcl_SetVar(interp, "KADM5_STRUCT_VERSION_MASK", buf,
+ TCL_GLOBAL_ONLY);
+
+ Tcl_CreateCommand(interp, "kadm5_init", tcl_kadm5_init, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_init_with_creds",
+ tcl_kadm5_init_with_creds, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_destroy", tcl_kadm5_destroy, 0,
+ 0);
+ Tcl_CreateCommand(interp, "kadm5_create_principal",
+ tcl_kadm5_create_principal, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_delete_principal",
+ tcl_kadm5_delete_principal, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_modify_principal",
+ tcl_kadm5_modify_principal, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_rename_principal",
+ tcl_kadm5_rename_principal, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_chpass_principal",
+ tcl_kadm5_chpass_principal, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_chpass_principal_util",
+ tcl_kadm5_chpass_principal_util, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_randkey_principal",
+ tcl_kadm5_randkey_principal, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_get_principal",
+ tcl_kadm5_get_principal, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_create_policy",
+ tcl_kadm5_create_policy, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_delete_policy",
+ tcl_kadm5_delete_policy, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_modify_policy",
+ tcl_kadm5_modify_policy, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_get_policy",
+ tcl_kadm5_get_policy, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_free_principal_ent",
+ tcl_kadm5_free_principal_ent, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_free_policy_ent",
+ tcl_kadm5_free_policy_ent, 0, 0);
+ Tcl_CreateCommand(interp, "kadm5_get_privs",
+ tcl_kadm5_get_privs, 0, 0);
+}
diff --git a/src/kadmin/testing/util/tcl_krb5_hash.c b/src/kadmin/testing/util/tcl_krb5_hash.c
new file mode 100644
index 000000000..95a3451d9
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_krb5_hash.c
@@ -0,0 +1,163 @@
+/*
+ * All of the TCL krb5 functions which return (or place into output
+ * variables) structures or pointers to structures that can't be
+ * represented as tcl native types, do so by returning a handle for
+ * the appropriate structure. The handle is a string of the form
+ * "type$id", where "type" is the type of datum represented by the
+ * handle and "id" is a unique identifier for it. This handle can
+ * then be used later by the caller to refer to the object, and
+ * internally to retrieve the actually datum from the appropriate hash
+ * table.
+ *
+ * The functions in this file do four things:
+ *
+ * 1) Given a pointer to a datum and a string representing the type of
+ * datum to which the pointer refers, create a new handle for the
+ * datum, store the datum in the hash table using the new handle as
+ * its key, and return the new handle.
+ *
+ * 2) Given a handle, locate and return the appropriate hash table
+ * datum.
+ *
+ * 3) Given a handle, look through a table of types and unparse
+ * functions to figure out what function to call to get a string
+ * representation of the datum, call it with the appropriate pointer
+ * (obtained from the hash table) as an argument, and return the
+ * resulting string as the unparsed form of the datum.
+ *
+ * 4) Given a handle, remove that handle and its associated datum from
+ * the hash table (but don't free it -- it's assumed to have already
+ * been freed by the caller).
+ */
+
+#include <tcl.h>
+#include <assert.h>
+
+#define SEP_STR "$"
+
+static char *memory_error = "out of memory";
+
+/*
+ * Right now, we're only using one hash table. However, at some point
+ * in the future, we might decide to use a separate hash table for
+ * every type. Therefore, I'm putting this function in as an
+ * abstraction so it's the only thing we'll have to change if we
+ * decide to do that.
+ *
+ * Also, this function allows us to put in just one place the code for
+ * checking to make sure that the hash table exists and initializing
+ * it if it doesn't.
+ */
+
+static TclHashTable *get_hash_table(Tcl_Interp *interp,
+ char *type)
+{
+ static Tcl_HashTable *hash_table = 0;
+
+ if (! hash_table) {
+ if (! (hash_table = malloc(sizeof(*hash_table)))) {
+ Tcl_SetResult(interp, memory_error, TCL_STATIC);
+ return 0;
+ }
+ Tcl_InitHashTable(hash_table, TCL_STRING_KEYS);
+ }
+ return hash_table;
+}
+
+#define MAX_ID 999999999
+#define ID_BUF_SIZE 10
+
+static Tcl_HashEntry *get_new_handle(Tcl_Interp *interp,
+ char *type)
+{
+ static unsigned long int id_counter = 0;
+ Tcl_DString *handle;
+ char int_buf[ID_BUF_SIZE];
+
+ if (! (handle = malloc(sizeof(*handle)))) {
+ Tcl_SetResult(interp, memory_error, TCL_STATIC);
+ return 0;
+ }
+ Tcl_DStringInit(handle);
+
+ assert(id_counter <= MAX_ID);
+
+ sprintf(int_buf, "%d", id_counter++);
+
+ Tcl_DStringAppend(handle, type, -1);
+ Tcl_DStringAppend(handle, SEP_STR, -1);
+ Tcl_DStringAppend(handle, int_buf, -1);
+
+ return handle;
+}
+
+
+Tcl_DString *tcl_krb5_create_object(Tcl_Interp *interp,
+ char *type,
+ ClientData datum)
+{
+ Tcl_HashTable *table;
+ Tcl_DString *handle;
+ Tcl_HashEntry *entry;
+ int entry_created = 0;
+
+ if (! (table = get_hash_table(interp, type))) {
+ return 0;
+ }
+
+ if (! (handle = get_new_handle(interp, type))) {
+ return 0;
+ }
+
+ if (! (entry = Tcl_CreateHashEntry(table, handle, &entry_created))) {
+ Tcl_SetResult(interp, "error creating hash entry", TCL_STATIC);
+ Tcl_DStringFree(handle);
+ return TCL_ERROR;
+ }
+
+ assert(entry_created);
+
+ Tcl_SetHashValue(entry, datum);
+
+ return handle;
+}
+
+ClientData tcl_krb5_get_object(Tcl_Interp *interp,
+ char *handle)
+{
+ char *myhandle, *id_ptr;
+ Tcl_HashTable *table;
+ Tcl_HashEntry *entry;
+
+ if (! (myhandle = strdup(handle))) {
+ Tcl_SetResult(interp, memory_error, TCL_STATIC);
+ return 0;
+ }
+
+ if (! (id_ptr = index(myhandle, *SEP_STR))) {
+ free(myhandle);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "malformatted handle \"", handle,
+ "\"", 0);
+ return 0;
+ }
+
+ *id_ptr = '\0';
+
+ if (! (table = get_hash_table(interp, myhandle))) {
+ free(myhandle);
+ return 0;
+ }
+
+ free(myhandle);
+
+ if (! (entry = Tcl_FindHashEntry(table, handle))) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "no object corresponding to handle \"",
+ handle, "\"", 0);
+ return 0;
+ }
+
+ return(Tcl_GetHashValue(entry));
+}
+
diff --git a/src/kadmin/testing/util/tcl_ovsec_kadm.c b/src/kadmin/testing/util/tcl_ovsec_kadm.c
new file mode 100644
index 000000000..0c6aaac9c
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_ovsec_kadm.c
@@ -0,0 +1,2016 @@
+#include <stdio.h>
+#include <string.h>
+#include <tcl.h>
+#define USE_KADM5_API_VERSION 1
+#include <kadm5/admin.h>
+#include <com_err.h>
+#include <malloc.h>
+#include <k5-int.h>
+#include <errno.h>
+#include <stdlib.h>
+
+struct flagval {
+ char *name;
+ krb5_flags val;
+};
+
+/* XXX This should probably be in the hash table like server_handle */
+static krb5_context context;
+
+struct flagval krb5_flags_array[] = {
+ {"KRB5_KDB_DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED},
+ {"KRB5_KDB_DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE},
+ {"KRB5_KDB_DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED},
+ {"KRB5_KDB_DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE},
+ {"KRB5_KDB_DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE},
+ {"KRB5_KDB_DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY},
+ {"KRB5_KDB_DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX},
+ {"KRB5_KDB_REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH},
+ {"KRB5_KDB_REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH},
+ {"KRB5_KDB_REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE},
+ {"KRB5_KDB_DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR},
+ {"KRB5_KDB_PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE}
+};
+
+struct flagval aux_attributes[] = {
+ {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY}
+};
+
+struct flagval principal_mask_flags[] = {
+ {"OVSEC_KADM_PRINCIPAL", OVSEC_KADM_PRINCIPAL},
+ {"OVSEC_KADM_PRINC_EXPIRE_TIME", OVSEC_KADM_PRINC_EXPIRE_TIME},
+ {"OVSEC_KADM_PW_EXPIRATION", OVSEC_KADM_PW_EXPIRATION},
+ {"OVSEC_KADM_LAST_PWD_CHANGE", OVSEC_KADM_LAST_PWD_CHANGE},
+ {"OVSEC_KADM_ATTRIBUTES", OVSEC_KADM_ATTRIBUTES},
+ {"OVSEC_KADM_MAX_LIFE", OVSEC_KADM_MAX_LIFE},
+ {"OVSEC_KADM_MOD_TIME", OVSEC_KADM_MOD_TIME},
+ {"OVSEC_KADM_MOD_NAME", OVSEC_KADM_MOD_NAME},
+ {"OVSEC_KADM_KVNO", OVSEC_KADM_KVNO},
+ {"OVSEC_KADM_MKVNO", OVSEC_KADM_MKVNO},
+ {"OVSEC_KADM_AUX_ATTRIBUTES", OVSEC_KADM_AUX_ATTRIBUTES},
+ {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY},
+ {"OVSEC_KADM_POLICY_CLR", OVSEC_KADM_POLICY_CLR}
+};
+
+struct flagval policy_mask_flags[] = {
+ {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY},
+ {"OVSEC_KADM_PW_MAX_LIFE", OVSEC_KADM_PW_MAX_LIFE},
+ {"OVSEC_KADM_PW_MIN_LIFE", OVSEC_KADM_PW_MIN_LIFE},
+ {"OVSEC_KADM_PW_MIN_LENGTH", OVSEC_KADM_PW_MIN_LENGTH},
+ {"OVSEC_KADM_PW_MIN_CLASSES", OVSEC_KADM_PW_MIN_CLASSES},
+ {"OVSEC_KADM_PW_HISTORY_NUM", OVSEC_KADM_PW_HISTORY_NUM},
+ {"OVSEC_KADM_REF_COUNT", OVSEC_KADM_REF_COUNT}
+};
+
+struct flagval priv_flags[] = {
+ {"OVSEC_KADM_PRIV_GET", OVSEC_KADM_PRIV_GET},
+ {"OVSEC_KADM_PRIV_ADD", OVSEC_KADM_PRIV_ADD},
+ {"OVSEC_KADM_PRIV_MODIFY", OVSEC_KADM_PRIV_MODIFY},
+ {"OVSEC_KADM_PRIV_DELETE", OVSEC_KADM_PRIV_DELETE}
+};
+
+
+static char *arg_error = "wrong # args";
+
+static Tcl_HashTable *struct_table = 0;
+
+static int put_server_handle(Tcl_Interp *interp, void *handle, char **name)
+{
+ int i = 1, newPtr = 0;
+ static char buf[20];
+ Tcl_HashEntry *entry;
+
+ if (! struct_table) {
+ if (! (struct_table =
+ malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ do {
+ /*
+ * Handles from ovsec_kadm_init() and kadm5_init() should not
+ * be mixed during unit tests, but the API would happily
+ * accept them. Making the hash entry names different in
+ * tcl_kadm.c and tcl_ovsec_kadm.c ensures that GET_HANDLE
+ * will fail if presented a handle from the other API.
+ */
+ sprintf(buf, "ovsec_kadm_handle%d", i);
+ entry = Tcl_CreateHashEntry(struct_table, buf, &newPtr);
+ i++;
+ } while (! newPtr);
+
+ Tcl_SetHashValue(entry, handle);
+
+ *name = buf;
+
+ return TCL_OK;
+}
+
+static int get_server_handle(Tcl_Interp *interp, char *name, void **handle)
+{
+ Tcl_HashEntry *entry;
+
+ if(!strcasecmp(name, "null"))
+ *handle = 0;
+ else {
+ if (! (struct_table &&
+ (entry = Tcl_FindHashEntry(struct_table, name)))) {
+ if (strncmp(name, "kadm5_handle", 12) == 0)
+ Tcl_AppendResult(interp, "kadm5 handle specified "
+ "for ovsec_kadm api: ", name, 0);
+ else
+ Tcl_AppendResult(interp, "unknown server handle ", name, 0);
+ return TCL_ERROR;
+ }
+ *handle = (void *) Tcl_GetHashValue(entry);
+ }
+ return TCL_OK;
+}
+
+static int remove_server_handle(Tcl_Interp *interp, char *name)
+{
+ Tcl_HashEntry *entry;
+
+ if (! (struct_table &&
+ (entry = Tcl_FindHashEntry(struct_table, name)))) {
+ Tcl_AppendResult(interp, "unknown server handle ", name, 0);
+ return TCL_ERROR;
+ }
+
+ Tcl_DeleteHashEntry(entry);
+ return TCL_OK;
+}
+
+#define GET_HANDLE(num_args, do_dostruct) \
+ void *server_handle; \
+ int dostruct = 0; \
+ char *whoami = argv[0]; \
+ argv++, argc--; \
+ if ((argc > 0) && (! strcmp(argv[0], "-struct"))) { \
+ if (! do_dostruct) { \
+ Tcl_AppendResult(interp, "-struct isn't a valid option for ", \
+ whoami, 0); \
+ return TCL_ERROR; \
+ } \
+ dostruct++; \
+ argv++, argc--; \
+ } \
+ if (argc != num_args + 1) { \
+ Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \
+ return TCL_ERROR; \
+ } \
+ { \
+ int tcl_ret; \
+ if ((tcl_ret = get_server_handle(interp, argv[0], &server_handle)) \
+ != TCL_OK) { \
+ return tcl_ret; \
+ } \
+ } \
+ argv++, argc--;
+
+static Tcl_HashTable *create_flag_table(struct flagval *flags, int size)
+{
+ Tcl_HashTable *table;
+ Tcl_HashEntry *entry;
+ int i;
+
+ if (! (table = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_InitHashTable(table, TCL_STRING_KEYS);
+
+ for (i = 0; i < size; i++) {
+ int newPtr;
+
+ if (! (entry = Tcl_CreateHashEntry(table, flags[i].name, &newPtr))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_SetHashValue(entry, &flags[i].val);
+ }
+
+ return table;
+}
+
+
+static Tcl_DString *unparse_str(char *in_str)
+{
+ Tcl_DString *str;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ if (! in_str) {
+ Tcl_DStringAppend(str, "null", -1);
+ }
+ else {
+ Tcl_DStringAppend(str, in_str, -1);
+ }
+
+ return str;
+}
+
+
+
+static int parse_str(Tcl_Interp *interp, char *in_str, char **out_str)
+{
+ if (! in_str) {
+ *out_str = 0;
+ }
+ else if (! strcasecmp(in_str, "null")) {
+ *out_str = 0;
+ }
+ else {
+ *out_str = in_str;
+ }
+ return TCL_OK;
+}
+
+
+static void set_ok(Tcl_Interp *interp, char *string)
+{
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ Tcl_AppendElement(interp, "OVSEC_KADM_OK");
+ Tcl_AppendElement(interp, string);
+}
+
+
+
+static Tcl_DString *unparse_err(ovsec_kadm_ret_t code)
+{
+ char *code_string, *error_string;
+ Tcl_DString *dstring;
+
+ switch (code) {
+ case OVSEC_KADM_FAILURE: code_string = "OVSEC_KADM_FAILURE"; break;
+ case OVSEC_KADM_AUTH_GET: code_string = "OVSEC_KADM_AUTH_GET"; break;
+ case OVSEC_KADM_AUTH_ADD: code_string = "OVSEC_KADM_AUTH_ADD"; break;
+ case OVSEC_KADM_AUTH_MODIFY:
+ code_string = "OVSEC_KADM_AUTH_MODIFY"; break;
+ case OVSEC_KADM_AUTH_DELETE:
+ code_string = "OVSEC_KADM_AUTH_DELETE"; break;
+ case OVSEC_KADM_AUTH_INSUFFICIENT:
+ code_string = "OVSEC_KADM_AUTH_INSUFFICIENT"; break;
+ case OVSEC_KADM_BAD_DB: code_string = "OVSEC_KADM_BAD_DB"; break;
+ case OVSEC_KADM_DUP: code_string = "OVSEC_KADM_DUP"; break;
+ case OVSEC_KADM_RPC_ERROR: code_string = "OVSEC_KADM_RPC_ERROR"; break;
+ case OVSEC_KADM_NO_SRV: code_string = "OVSEC_KADM_NO_SRV"; break;
+ case OVSEC_KADM_BAD_HIST_KEY:
+ code_string = "OVSEC_KADM_BAD_HIST_KEY"; break;
+ case OVSEC_KADM_NOT_INIT: code_string = "OVSEC_KADM_NOT_INIT"; break;
+ case OVSEC_KADM_INIT: code_string = "OVSEC_KADM_INIT"; break;
+ case OVSEC_KADM_BAD_PASSWORD:
+ code_string = "OVSEC_KADM_BAD_PASSWORD"; break;
+ case OVSEC_KADM_UNK_PRINC: code_string = "OVSEC_KADM_UNK_PRINC"; break;
+ case OVSEC_KADM_UNK_POLICY: code_string = "OVSEC_KADM_UNK_POLICY"; break;
+ case OVSEC_KADM_BAD_MASK: code_string = "OVSEC_KADM_BAD_MASK"; break;
+ case OVSEC_KADM_BAD_CLASS: code_string = "OVSEC_KADM_BAD_CLASS"; break;
+ case OVSEC_KADM_BAD_LENGTH: code_string = "OVSEC_KADM_BAD_LENGTH"; break;
+ case OVSEC_KADM_BAD_POLICY: code_string = "OVSEC_KADM_BAD_POLICY"; break;
+ case OVSEC_KADM_BAD_HISTORY: code_string = "OVSEC_KADM_BAD_HISTORY"; break;
+ case OVSEC_KADM_BAD_PRINCIPAL:
+ code_string = "OVSEC_KADM_BAD_PRINCIPAL"; break;
+ case OVSEC_KADM_BAD_AUX_ATTR:
+ code_string = "OVSEC_KADM_BAD_AUX_ATTR"; break;
+ case OVSEC_KADM_PASS_Q_TOOSHORT:
+ code_string = "OVSEC_KADM_PASS_Q_TOOSHORT"; break;
+ case OVSEC_KADM_PASS_Q_CLASS:
+ code_string = "OVSEC_KADM_PASS_Q_CLASS"; break;
+ case OVSEC_KADM_PASS_Q_DICT:
+ code_string = "OVSEC_KADM_PASS_Q_DICT"; break;
+ case OVSEC_KADM_PASS_REUSE: code_string = "OVSEC_KADM_PASS_REUSE"; break;
+ case OVSEC_KADM_PASS_TOOSOON:
+ code_string = "OVSEC_KADM_PASS_TOOSOON"; break;
+ case OVSEC_KADM_POLICY_REF:
+ code_string = "OVSEC_KADM_POLICY_REF"; break;
+ case OVSEC_KADM_PROTECT_PRINCIPAL:
+ code_string = "OVSEC_KADM_PROTECT_PRINCIPAL"; break;
+ case OVSEC_KADM_BAD_SERVER_HANDLE:
+ code_string = "OVSEC_KADM_BAD_SERVER_HANDLE"; break;
+ case OVSEC_KADM_BAD_STRUCT_VERSION:
+ code_string = "OVSEC_KADM_BAD_STRUCT_VERSION"; break;
+ case OVSEC_KADM_OLD_STRUCT_VERSION:
+ code_string = "OVSEC_KADM_OLD_STRUCT_VERSION"; break;
+ case OVSEC_KADM_NEW_STRUCT_VERSION:
+ code_string = "OVSEC_KADM_NEW_STRUCT_VERSION"; break;
+ case OVSEC_KADM_BAD_API_VERSION:
+ code_string = "OVSEC_KADM_BAD_API_VERSION"; break;
+ case OVSEC_KADM_OLD_LIB_API_VERSION:
+ code_string = "OVSEC_KADM_OLD_LIB_API_VERSION"; break;
+ case OVSEC_KADM_OLD_SERVER_API_VERSION:
+ code_string = "OVSEC_KADM_OLD_SERVER_API_VERSION"; break;
+ case OVSEC_KADM_NEW_LIB_API_VERSION:
+ code_string = "OVSEC_KADM_NEW_LIB_API_VERSION"; break;
+ case OVSEC_KADM_NEW_SERVER_API_VERSION:
+ code_string = "OVSEC_KADM_NEW_SERVER_API_VERSION"; break;
+ case OVSEC_KADM_SECURE_PRINC_MISSING:
+ code_string = "OVSEC_KADM_SECURE_PRINC_MISSING"; break;
+ case KADM5_NO_RENAME_SALT:
+ code_string = "KADM5_NO_RENAME_SALT"; break;
+ case KADM5_BAD_CLIENT_PARAMS:
+ code_string = "KADM5_BAD_CLIENT_PARAMS"; break;
+ case KADM5_BAD_SERVER_PARAMS:
+ code_string = "KADM5_BAD_SERVER_PARAMS"; break;
+ case KADM5_AUTH_LIST:
+ code_string = "KADM5_AUTH_LIST"; break;
+ case KADM5_AUTH_CHANGEPW:
+ code_string = "KADM5_AUTH_CHANGEPW"; break;
+ case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break;
+ case OSA_ADB_NOENT: code_string = "ENOENT"; break;
+ case OSA_ADB_DBINIT: code_string = "OSA_ADB_DBINIT"; break;
+ case OSA_ADB_BAD_POLICY: code_string = "Bad policy name"; break;
+ case OSA_ADB_BAD_PRINC: code_string = "Bad principal name"; break;
+ case OSA_ADB_BAD_DB: code_string = "Invalid database."; break;
+ case OSA_ADB_XDR_FAILURE: code_string = "OSA_ADB_XDR_FAILURE"; break;
+ case KRB5_KDB_INUSE: code_string = "KRB5_KDB_INUSE"; break;
+ case KRB5_KDB_UK_SERROR: code_string = "KRB5_KDB_UK_SERROR"; break;
+ case KRB5_KDB_UK_RERROR: code_string = "KRB5_KDB_UK_RERROR"; break;
+ case KRB5_KDB_UNAUTH: code_string = "KRB5_KDB_UNAUTH"; break;
+ case KRB5_KDB_NOENTRY: code_string = "KRB5_KDB_NOENTRY"; break;
+ case KRB5_KDB_ILL_WILDCARD: code_string = "KRB5_KDB_ILL_WILDCARD"; break;
+ case KRB5_KDB_DB_INUSE: code_string = "KRB5_KDB_DB_INUSE"; break;
+ case KRB5_KDB_DB_CHANGED: code_string = "KRB5_KDB_DB_CHANGED"; break;
+ case KRB5_KDB_TRUNCATED_RECORD:
+ code_string = "KRB5_KDB_TRUNCATED_RECORD"; break;
+ case KRB5_KDB_RECURSIVELOCK:
+ code_string = "KRB5_KDB_RECURSIVELOCK"; break;
+ case KRB5_KDB_NOTLOCKED: code_string = "KRB5_KDB_NOTLOCKED"; break;
+ case KRB5_KDB_BADLOCKMODE: code_string = "KRB5_KDB_BADLOCKMODE"; break;
+ case KRB5_KDB_DBNOTINITED: code_string = "KRB5_KDB_DBNOTINITED"; break;
+ case KRB5_KDB_DBINITED: code_string = "KRB5_KDB_DBINITED"; break;
+ case KRB5_KDB_ILLDIRECTION: code_string = "KRB5_KDB_ILLDIRECTION"; break;
+ case KRB5_KDB_NOMASTERKEY: code_string = "KRB5_KDB_NOMASTERKEY"; break;
+ case KRB5_KDB_BADMASTERKEY: code_string = "KRB5_KDB_BADMASTERKEY"; break;
+ case KRB5_KDB_INVALIDKEYSIZE:
+ code_string = "KRB5_KDB_INVALIDKEYSIZE"; break;
+ case KRB5_KDB_CANTREAD_STORED:
+ code_string = "KRB5_KDB_CANTREAD_STORED"; break;
+ case KRB5_KDB_BADSTORED_MKEY:
+ code_string = "KRB5_KDB_BADSTORED_MKEY"; break;
+ case KRB5_KDB_CANTLOCK_DB: code_string = "KRB5_KDB_CANTLOCK_DB"; break;
+ case KRB5_KDB_DB_CORRUPT: code_string = "KRB5_KDB_DB_CORRUPT"; break;
+ case KRB5_PARSE_ILLCHAR: code_string = "KRB5_PARSE_ILLCHAR"; break;
+ case KRB5_PARSE_MALFORMED: code_string = "KRB5_PARSE_MALFORMED"; break;
+ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN"; break;
+ case KRB5_REALM_UNKNOWN: code_string = "KRB5_REALM_UNKNOWN"; break;
+ case KRB5_KDC_UNREACH: code_string = "KRB5_KDC_UNREACH"; break;
+ case KRB5_KDCREP_MODIFIED: code_string = "KRB5_KDCREP_MODIFIED"; break;
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY: code_string = "KRB5KRB_AP_ERR_BAD_INTEGRITY"; break;
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"; break;
+ case EINVAL: code_string = "EINVAL"; break;
+ case ENOENT: code_string = "ENOENT"; break;
+ default: fprintf(stderr, "**** CODE %d ***\n", code); code_string = "UNKNOWN"; break;
+ }
+
+ error_string = (char *) error_message(code);
+
+ if (! (dstring = (Tcl_DString *) malloc(sizeof(Tcl_DString)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX Do we really want to exit? Ok if this is */
+ /* just a test program, but what about if it gets */
+ /* used for other things later? */
+ }
+
+ Tcl_DStringInit(dstring);
+
+ if (! (Tcl_DStringAppendElement(dstring, "ERROR") &&
+ Tcl_DStringAppendElement(dstring, code_string) &&
+ Tcl_DStringAppendElement(dstring, error_string))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ return dstring;
+}
+
+
+
+static void stash_error(Tcl_Interp *interp, krb5_error_code code)
+{
+ Tcl_DString *dstring = unparse_err(code);
+ Tcl_DStringResult(interp, dstring);
+ Tcl_DStringFree(dstring);
+ free(dstring);
+}
+
+
+
+static Tcl_DString *unparse_flags(struct flagval *array, int size,
+ krb5_int32 flags)
+{
+ int i;
+ Tcl_DString *str;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ for (i = 0; i < size; i++) {
+ if (flags & array[i].val) {
+ Tcl_DStringAppendElement(str, array[i].name);
+ }
+ }
+
+ return str;
+}
+
+
+static int parse_flags(Tcl_Interp *interp, Tcl_HashTable *table,
+ struct flagval *array, int size, char *str,
+ krb5_flags *flags)
+{
+ int tcl_ret, tmp, argc, i, retcode = TCL_OK;
+ char **argv;
+ Tcl_HashEntry *entry;
+
+ if ((tcl_ret = Tcl_GetInt(interp, str, &tmp)) == TCL_OK) {
+ *flags = tmp;
+ return TCL_OK;
+ }
+ Tcl_ResetResult(interp);
+
+ if ((tcl_ret = Tcl_SplitList(interp, str, &argc, &argv)) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (! table) {
+ table = create_flag_table(array, size);
+ }
+
+ *flags = 0;
+
+ for (i = 0; i < argc; i++) {
+ if (! (entry = Tcl_FindHashEntry(table, argv[i]))) {
+ Tcl_AppendResult(interp, "unknown krb5 flag ", argv[i], 0);
+ retcode = TCL_ERROR;
+ break;
+ }
+ *flags |= *(krb5_flags *) Tcl_GetHashValue(entry);
+ }
+
+ free(argv);
+ return(retcode);
+}
+
+static Tcl_DString *unparse_privs(krb5_flags flags)
+{
+ return unparse_flags(priv_flags, sizeof(priv_flags) /
+ sizeof(struct flagval), flags);
+}
+
+
+static Tcl_DString *unparse_krb5_flags(krb5_flags flags)
+{
+ return unparse_flags(krb5_flags_array, sizeof(krb5_flags_array) /
+ sizeof(struct flagval), flags);
+}
+
+static int parse_krb5_flags(Tcl_Interp *interp, char *str, krb5_flags *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, krb5_flags_array,
+ sizeof(krb5_flags_array) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+static Tcl_DString *unparse_aux_attributes(krb5_int32 flags)
+{
+ return unparse_flags(aux_attributes, sizeof(aux_attributes) /
+ sizeof(struct flagval), flags);
+}
+
+
+static int parse_aux_attributes(Tcl_Interp *interp, char *str, long *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, aux_attributes,
+ sizeof(aux_attributes) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+static int parse_principal_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, principal_mask_flags,
+ sizeof(principal_mask_flags) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+
+static int parse_policy_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags)
+{
+ krb5_flags tmp;
+ static Tcl_HashTable *table = 0;
+ int tcl_ret;
+
+ if ((tcl_ret = parse_flags(interp, table, policy_mask_flags,
+ sizeof(policy_mask_flags) /
+ sizeof(struct flagval),
+ str, &tmp)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ *flags = tmp;
+ return TCL_OK;
+}
+
+
+static Tcl_DString *unparse_principal_ent(ovsec_kadm_principal_ent_t princ)
+{
+ Tcl_DString *str, *tmp_dstring;
+ char *tmp;
+ char buf[20];
+ krb5_error_code krb5_ret;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ tmp = 0; /* It looks to me from looking at the library source */
+ /* code for krb5_parse_name that the pointer passed into */
+ /* it should be initialized to 0 if I want it do be */
+ /* allocated automatically. */
+ if (krb5_ret = krb5_unparse_name(context, princ->principal, &tmp)) {
+ /* XXX Do we want to return an error? Not sure. */
+ Tcl_DStringAppendElement(str, "[unparseable principal]");
+ }
+ else {
+ Tcl_DStringAppendElement(str, tmp);
+ free(tmp);
+ }
+
+ sprintf(buf, "%d", princ->princ_expire_time);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->last_pwd_change);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->pw_expiration);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->max_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ tmp = 0;
+ if (krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp)) {
+ /* XXX */
+ Tcl_DStringAppendElement(str, "[unparseable principal]");
+ }
+ else {
+ Tcl_DStringAppendElement(str, tmp);
+ free(tmp);
+ }
+
+ sprintf(buf, "%d", princ->mod_date);
+ Tcl_DStringAppendElement(str, buf);
+
+ tmp_dstring = unparse_krb5_flags(princ->attributes);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ sprintf(buf, "%d", princ->kvno);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", princ->mkvno);
+ Tcl_DStringAppendElement(str, buf);
+
+ /* XXX This may be dangerous, because the contents of the policy */
+ /* field are undefined if the POLICY bit isn't set. However, I */
+ /* think it's a bug for the field not to be null in that case */
+ /* anyway, so we should assume that it will be null so that we'll */
+ /* catch it if it isn't. */
+
+ tmp_dstring = unparse_str(princ->policy);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ tmp_dstring = unparse_aux_attributes(princ->aux_attributes);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ return str;
+}
+
+
+
+static int parse_principal_ent(Tcl_Interp *interp, char *list,
+ ovsec_kadm_principal_ent_t *out_princ)
+{
+ ovsec_kadm_principal_ent_t princ;
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ int argc;
+ char **argv;
+ int tmp;
+ int retcode = TCL_OK;
+
+ if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ if (argc != 12) {
+ sprintf(interp->result, "wrong # args in principal structure (%d should be 12)",
+ argc);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if (! (princ = malloc(sizeof *princ))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ if ((krb5_ret = krb5_parse_name(context, argv[0], &princ->principal)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ /*
+ * All of the numerical values parsed here are parsed into an
+ * "int" and then assigned into the structure in case the actual
+ * width of the field in the Kerberos structure is different from
+ * the width of an integer.
+ */
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing princ_expire_time");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->princ_expire_time = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing last_pwd_change");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->last_pwd_change = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_expiration");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->pw_expiration = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing max_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->max_life = tmp;
+
+ if ((krb5_ret = krb5_parse_name(context, argv[5], &princ->mod_name)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing mod_name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing mod_date");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->mod_date = tmp;
+
+ if ((tcl_ret = parse_krb5_flags(interp, argv[7], &princ->attributes))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing attributes");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing kvno");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->kvno = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing mkvno");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->mkvno = tmp;
+
+ if ((tcl_ret = parse_str(interp, argv[10], &princ->policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if(princ->policy != NULL) {
+ if(!(princ->policy = strdup(princ->policy))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1);
+ }
+ }
+
+ if ((tcl_ret = parse_aux_attributes(interp, argv[11],
+ &princ->aux_attributes)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing aux_attributes");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+finished:
+ free(argv);
+ *out_princ = princ;
+ return retcode;
+}
+
+
+static void free_principal_ent(ovsec_kadm_principal_ent_t *princ)
+{
+ krb5_free_principal(context, (*princ)->principal);
+ krb5_free_principal(context, (*princ)->mod_name);
+ free(*princ);
+ *princ = 0;
+}
+
+static Tcl_DString *unparse_policy_ent(ovsec_kadm_policy_ent_t policy)
+{
+ Tcl_DString *str, *tmp_dstring;
+ char buf[20];
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ tmp_dstring = unparse_str(policy->policy);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+
+ sprintf(buf, "%d", policy->pw_min_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_max_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_min_length);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_min_classes);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_history_num);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->policy_refcnt);
+ Tcl_DStringAppendElement(str, buf);
+
+ return str;
+}
+
+
+
+static int parse_policy_ent(Tcl_Interp *interp, char *list,
+ ovsec_kadm_policy_ent_t *out_policy)
+{
+ ovsec_kadm_policy_ent_t policy;
+ int tcl_ret;
+ int argc;
+ char **argv;
+ int tmp;
+ int retcode = TCL_OK;
+
+ if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ if (argc != 7) {
+ sprintf(interp->result, "wrong # args in policy structure (%d should be 7)",
+ argc);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if (! (policy = malloc(sizeof *policy))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy->policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if(policy->policy != NULL) {
+ if (! (policy->policy = strdup(policy->policy))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ }
+
+ /*
+ * All of the numerical values parsed here are parsed into an
+ * "int" and then assigned into the structure in case the actual
+ * width of the field in the Kerberos structure is different from
+ * the width of an integer.
+ */
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_min_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_min_life = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_max_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_max_life = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_min_length");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_min_length = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_min_classes");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_min_classes = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[5], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_history_num");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_history_num = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy_refcnt");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->policy_refcnt = tmp;
+
+finished:
+ free(argv);
+ *out_policy = policy;
+ return retcode;
+}
+
+
+static void free_policy_ent(ovsec_kadm_policy_ent_t *policy)
+{
+ free(*policy);
+ *policy = 0;
+}
+
+static Tcl_DString *unparse_keytype(krb5_enctype enctype)
+{
+ Tcl_DString *str;
+ char buf[50];
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ switch (enctype) {
+ /* XXX is this right? */
+ case ENCTYPE_NULL: Tcl_DStringAppend(str, "ENCTYPE_NULL", -1); break;
+ case ENCTYPE_DES_CBC_CRC:
+ Tcl_DStringAppend(str, "ENCTYPE_DES_CBC_CRC", -1); break;
+ default:
+ sprintf(buf, "UNKNOWN KEYTYPE (0x%x)", enctype);
+ Tcl_DStringAppend(str, buf, -1);
+ break;
+ }
+
+ return str;
+}
+
+
+static Tcl_DString *unparse_keyblock(krb5_keyblock *keyblock)
+{
+ Tcl_DString *str;
+ Tcl_DString *keytype;
+ int i;
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+
+ keytype = unparse_keytype(keyblock->enctype);
+ Tcl_DStringAppendElement(str, keytype->string);
+ Tcl_DStringFree(keytype);
+ free(keytype);
+ if (keyblock->length == 0) {
+ Tcl_DStringAppendElement(str, "0x00");
+ }
+ else {
+ Tcl_DStringAppendElement(str, "0x");
+ for (i = 0; i < keyblock->length; i++) {
+ char buf[3];
+ sprintf(buf, "%02x", (int) keyblock->contents[i]);
+ Tcl_DStringAppend(str, buf, -1);
+ }
+ }
+
+ return str;
+}
+
+
+
+int tcl_ovsec_kadm_init(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ ovsec_kadm_ret_t ret;
+ char *client_name, *pass, *service_name, *realm;
+ int tcl_ret;
+ krb5_ui_4 struct_version, api_version;
+ char *handle_var;
+ void *server_handle;
+ char *handle_name;
+ char *whoami = argv[0];
+
+ argv++, argc--;
+
+ krb5_init_context(&context);
+
+ if (argc != 7) {
+ Tcl_AppendResult(interp, whoami, ": ", arg_error, 0);
+ return TCL_ERROR;
+ }
+
+ if (((tcl_ret = parse_str(interp, argv[0], &client_name)) != TCL_OK) ||
+ ((tcl_ret = parse_str(interp, argv[1], &pass)) != TCL_OK) ||
+ ((tcl_ret = parse_str(interp, argv[2], &service_name)) != TCL_OK) ||
+ ((tcl_ret = parse_str(interp, argv[3], &realm)) != TCL_OK) ||
+ ((tcl_ret = Tcl_GetInt(interp, argv[4], (int *) &struct_version)) !=
+ TCL_OK) ||
+ ((tcl_ret = Tcl_GetInt(interp, argv[5], (int *) &api_version)) !=
+ TCL_OK)) {
+ return tcl_ret;
+ }
+
+ handle_var = argv[6];
+
+ if (! (handle_var && *handle_var)) {
+ Tcl_SetResult(interp, "must specify server handle variable name",
+ TCL_STATIC);
+ return TCL_ERROR;
+ }
+
+ ret = ovsec_kadm_init(client_name, pass, service_name, realm,
+ struct_version, api_version, &server_handle);
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = put_server_handle(interp, server_handle, &handle_name))
+ != TCL_OK) {
+ return tcl_ret;
+ }
+
+ if (! Tcl_SetVar(interp, handle_var, handle_name, TCL_LEAVE_ERR_MSG)) {
+ return TCL_ERROR;
+ }
+
+ set_ok(interp, "OV Admin system initialized.");
+ return TCL_OK;
+}
+
+
+
+int tcl_ovsec_kadm_destroy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ ovsec_kadm_ret_t ret;
+ int tcl_ret;
+
+ GET_HANDLE(0, 0);
+
+ ret = ovsec_kadm_destroy(server_handle);
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = remove_server_handle(interp, argv[-1])) != TCL_OK) {
+ return tcl_ret;
+ }
+
+ set_ok(interp, "OV Admin system deinitialized.");
+ return TCL_OK;
+}
+
+int tcl_ovsec_kadm_create_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ int tcl_ret;
+ ovsec_kadm_ret_t ret;
+ int retcode = TCL_OK;
+ char *princ_string;
+ ovsec_kadm_principal_ent_t princ = 0;
+ krb5_int32 mask;
+ char *pw;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+
+ GET_HANDLE(3, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing principal");
+ return tcl_ret;
+ }
+
+ if (princ_string &&
+ ((tcl_ret = parse_principal_ent(interp, princ_string, &princ))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[2], &pw)) != TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+#ifdef OVERRIDE
+ if ((tcl_ret = Tcl_GetBoolean(interp, argv[3], &override_qual)) !=
+ TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+#endif
+
+#ifdef OVERRIDE
+ ret = ovsec_kadm_create_principal(server_handle, princ, mask, pw,
+ override_qual);
+#else
+ ret = ovsec_kadm_create_principal(server_handle, princ, mask, pw);
+#endif
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ else {
+ set_ok(interp, "Principal created.");
+ }
+
+finished:
+ if (princ) {
+ free_principal_ent(&princ);
+ }
+ return retcode;
+}
+
+
+
+int tcl_ovsec_kadm_delete_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ krb5_error_code krb5_ret;
+ ovsec_kadm_ret_t ret;
+ int tcl_ret;
+ char *name;
+
+ GET_HANDLE(1, 0);
+
+ if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK)
+ return tcl_ret;
+ if(name != NULL) {
+ if (krb5_ret = krb5_parse_name(context, name, &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal");
+ return TCL_ERROR;
+ }
+ } else princ = NULL;
+ ret = ovsec_kadm_delete_principal(server_handle, princ);
+
+ if(princ != NULL)
+ krb5_free_principal(context, princ);
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Principal deleted.");
+ return TCL_OK;
+ }
+}
+
+
+
+int tcl_ovsec_kadm_modify_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *princ_string;
+ ovsec_kadm_principal_ent_t princ = 0;
+ int tcl_ret;
+ krb5_int32 mask;
+ int retcode = TCL_OK;
+ ovsec_kadm_ret_t ret;
+
+ GET_HANDLE(2, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing principal");
+ return tcl_ret;
+ }
+
+ if (princ_string &&
+ ((tcl_ret = parse_principal_ent(interp, princ_string, &princ))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = ovsec_kadm_modify_principal(server_handle, princ, mask);
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Principal modified.");
+ }
+
+finished:
+ if (princ) {
+ free_principal_ent(&princ);
+ }
+ return retcode;
+}
+
+
+int tcl_ovsec_kadm_rename_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal source, target;
+ krb5_error_code krb5_ret;
+ ovsec_kadm_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(2, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &source)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing source");
+ return TCL_ERROR;
+ }
+
+ if (krb5_ret = krb5_parse_name(context, argv[1], &target)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing target");
+ krb5_free_principal(context, source);
+ return TCL_ERROR;
+ }
+
+ ret = ovsec_kadm_rename_principal(server_handle, source, target);
+
+ if (ret == OVSEC_KADM_OK) {
+ set_ok(interp, "Principal renamed.");
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+ krb5_free_principal(context, source);
+ krb5_free_principal(context, target);
+ return retcode;
+}
+
+
+
+int tcl_ovsec_kadm_chpass_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ char *pw;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ int retcode = TCL_OK;
+ ovsec_kadm_ret_t ret;
+
+ GET_HANDLE(2, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &pw)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing password");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+#ifdef OVERRIDE
+ if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing override_qual");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = ovsec_kadm_chpass_principal(server_handle,
+ princ, pw, override_qual);
+#else
+ ret = ovsec_kadm_chpass_principal(server_handle, princ, pw);
+#endif
+
+ if (ret == OVSEC_KADM_OK) {
+ set_ok(interp, "Password changed.");
+ goto finished;
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ krb5_free_principal(context, princ);
+ return retcode;
+}
+
+
+
+int tcl_ovsec_kadm_chpass_principal_util(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ char *new_pw;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+ char *pw_ret, *pw_ret_var;
+ char msg_ret[1024], *msg_ret_var;
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ ovsec_kadm_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(4, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &new_pw)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing new password");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+#ifdef OVERRIDE
+ if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing override_qual");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+#endif
+ if ((tcl_ret = parse_str(interp, argv[3], &pw_ret_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_ret variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[4], &msg_ret_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing msg_ret variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = ovsec_kadm_chpass_principal_util(server_handle, princ, new_pw,
+#ifdef OVERRIDE
+ override_qual,
+#endif
+ pw_ret_var ? &pw_ret : 0,
+ msg_ret_var ? msg_ret : 0);
+
+ if (ret == OVSEC_KADM_OK) {
+ if (pw_ret_var &&
+ (! Tcl_SetVar(interp, pw_ret_var, pw_ret,
+ TCL_LEAVE_ERR_MSG))) {
+ Tcl_AppendElement(interp, "while setting pw_ret variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if (msg_ret_var &&
+ (! Tcl_SetVar(interp, msg_ret_var, msg_ret,
+ TCL_LEAVE_ERR_MSG))) {
+ Tcl_AppendElement(interp,
+ "while setting msg_ret variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Password changed.");
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ krb5_free_principal(context, princ);
+ return retcode;
+}
+
+
+
+int tcl_ovsec_kadm_randkey_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ krb5_keyblock *keyblock;
+ char *keyblock_var;
+ Tcl_DString *keyblock_dstring = 0;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+ krb5_error_code krb5_ret;
+ ovsec_kadm_ret_t ret;
+ int tcl_ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(2, 0);
+
+ if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &keyblock_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing keyblock variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+#ifdef OVERRIDE
+ if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing override_qual");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = ovsec_kadm_randkey_principal(server_handle,
+ princ, keyblock_var ? &keyblock : 0,
+ override_qual);
+#else
+ ret = ovsec_kadm_randkey_principal(server_handle,
+ princ, keyblock_var ? &keyblock : 0);
+#endif
+
+ if (ret == OVSEC_KADM_OK) {
+ if (keyblock_var) {
+ keyblock_dstring = unparse_keyblock(keyblock);
+ if (! Tcl_SetVar(interp, keyblock_var,
+ keyblock_dstring->string,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting keyblock variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ }
+ set_ok(interp, "Key randomized.");
+
+ }
+ else {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ krb5_free_principal(context, princ);
+ if (keyblock_dstring) {
+ Tcl_DStringFree(keyblock_dstring);
+ free(keyblock_dstring);
+ }
+ return retcode;
+}
+
+
+
+int tcl_ovsec_kadm_get_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ ovsec_kadm_principal_ent_t ent;
+ Tcl_DString *ent_dstring = 0;
+ char *ent_var;
+ char *name;
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ ovsec_kadm_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(2, 1);
+
+ if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK)
+ return tcl_ret;
+ if(name != NULL) {
+ if (krb5_ret = krb5_parse_name(context, name, &princ)) {
+ stash_error(interp, ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+ } else princ = NULL;
+
+ if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = ovsec_kadm_get_principal(server_handle, princ, ent_var ? &ent : 0);
+
+ if (ret == OVSEC_KADM_OK) {
+ if (ent_var) {
+ if (dostruct) {
+ char buf[20];
+ int i = 1, newPtr = 0;
+ Tcl_HashEntry *entry;
+
+ if (! struct_table) {
+ if (! (struct_table =
+ malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ do {
+ sprintf(buf, "principal%d", i);
+ entry = Tcl_CreateHashEntry(struct_table, buf,
+ &newPtr);
+ i++;
+ } while (! newPtr);
+
+ Tcl_SetHashValue(entry, ent);
+ if (! Tcl_SetVar(interp, ent_var, buf,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting entry variable");
+ Tcl_DeleteHashEntry(entry);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Principal structure retrieved.");
+ }
+ else {
+ ent_dstring = unparse_principal_ent(ent);
+ if (! Tcl_SetVar(interp, ent_var, ent_dstring->string,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting entry variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Principal retrieved.");
+ }
+ }
+ }
+ else {
+ ent = 0;
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ if (ent_dstring) {
+ Tcl_DStringFree(ent_dstring);
+ free(ent_dstring);
+ }
+ if(princ != NULL)
+ krb5_free_principal(context, princ);
+ if (ent && ((! dostruct) || (retcode != TCL_OK))) {
+ if ((ret = ovsec_kadm_free_principal_ent(server_handle, ent)) &&
+ (retcode == TCL_OK)) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ }
+ return retcode;
+}
+
+int tcl_ovsec_kadm_create_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ int tcl_ret;
+ ovsec_kadm_ret_t ret;
+ int retcode = TCL_OK;
+ char *policy_string;
+ ovsec_kadm_policy_ent_t policy = 0;
+ krb5_int32 mask;
+
+ GET_HANDLE(2, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy");
+ return tcl_ret;
+ }
+
+ if (policy_string &&
+ ((tcl_ret = parse_policy_ent(interp, policy_string, &policy))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = tcl_ret;
+ goto finished;
+ }
+
+ ret = ovsec_kadm_create_policy(server_handle, policy, mask);
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ else {
+ set_ok(interp, "Policy created.");
+ }
+
+finished:
+ if (policy) {
+ free_policy_ent(&policy);
+ }
+ return retcode;
+}
+
+
+
+int tcl_ovsec_kadm_delete_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ krb5_principal princ;
+ krb5_error_code krb5_ret;
+ ovsec_kadm_ret_t ret;
+ char *policy;
+ int tcl_ret;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy name");
+ return TCL_ERROR;
+ }
+
+ ret = ovsec_kadm_delete_policy(server_handle, policy);
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Policy deleted.");
+ return TCL_OK;
+ }
+}
+
+
+
+int tcl_ovsec_kadm_modify_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *policy_string;
+ ovsec_kadm_policy_ent_t policy = 0;
+ int tcl_ret;
+ krb5_int32 mask;
+ int retcode = TCL_OK;
+ ovsec_kadm_ret_t ret;
+
+ GET_HANDLE(2, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy");
+ return tcl_ret;
+ }
+
+ if (policy_string &&
+ ((tcl_ret = parse_policy_ent(interp, policy_string, &policy))
+ != TCL_OK)) {
+ return tcl_ret;
+ }
+
+ if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) {
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ ret = ovsec_kadm_modify_policy(server_handle, policy, mask);
+
+ if (ret != OVSEC_KADM_OK) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ else {
+ set_ok(interp, "Policy modified.");
+ }
+
+finished:
+ if (policy) {
+ free_policy_ent(&policy);
+ }
+ return retcode;
+}
+
+
+int tcl_ovsec_kadm_get_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ ovsec_kadm_policy_ent_t ent;
+ Tcl_DString *ent_dstring = 0;
+ char *policy;
+ char *ent_var;
+ int tcl_ret;
+ ovsec_kadm_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(2, 1);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy name");
+ return TCL_ERROR;
+ }
+
+ if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry variable name");
+ return TCL_ERROR;
+ }
+
+ ret = ovsec_kadm_get_policy(server_handle, policy, ent_var ? &ent : 0);
+
+ if (ret == OVSEC_KADM_OK) {
+ if (ent_var) {
+ if (dostruct) {
+ char buf[20];
+ int i = 1, newPtr = 0;
+ Tcl_HashEntry *entry;
+
+ if (! struct_table) {
+ if (! (struct_table =
+ malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ do {
+ sprintf(buf, "policy%d", i);
+ entry = Tcl_CreateHashEntry(struct_table, buf,
+ &newPtr);
+ i++;
+ } while (! newPtr);
+
+ Tcl_SetHashValue(entry, ent);
+ if (! Tcl_SetVar(interp, ent_var, buf,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting entry variable");
+ Tcl_DeleteHashEntry(entry);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Policy structure retrieved.");
+ }
+ else {
+ ent_dstring = unparse_policy_ent(ent);
+ if (! Tcl_SetVar(interp, ent_var, ent_dstring->string,
+ TCL_LEAVE_ERR_MSG)) {
+ Tcl_AppendElement(interp,
+ "while setting entry variable");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ set_ok(interp, "Policy retrieved.");
+ }
+ }
+ }
+ else {
+ ent = 0;
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+
+finished:
+ if (ent_dstring) {
+ Tcl_DStringFree(ent_dstring);
+ free(ent_dstring);
+ }
+ if (ent && ((! dostruct) || (retcode != TCL_OK))) {
+ if ((ret = ovsec_kadm_free_policy_ent(server_handle, ent)) &&
+ (retcode == TCL_OK)) {
+ stash_error(interp, ret);
+ retcode = TCL_ERROR;
+ }
+ }
+ return retcode;
+}
+
+
+
+int tcl_ovsec_kadm_free_principal_ent(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *ent_name;
+ ovsec_kadm_principal_ent_t ent;
+ int tcl_ret;
+ ovsec_kadm_ret_t ret;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry name");
+ return TCL_ERROR;
+ }
+
+ if ((! ent_name) &&
+ (ret = ovsec_kadm_free_principal_ent(server_handle, 0))) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ Tcl_HashEntry *entry;
+
+ if (strncmp(ent_name, "principal", sizeof("principal")-1)) {
+ Tcl_AppendResult(interp, "invalid principal handle \"",
+ ent_name, "\"", 0);
+ return TCL_ERROR;
+ }
+ if (! struct_table) {
+ if (! (struct_table = malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) {
+ Tcl_AppendResult(interp, "principal handle \"", ent_name,
+ "\" not found", 0);
+ return TCL_ERROR;
+ }
+
+ ent = Tcl_GetHashValue(entry);
+
+ if (ret = ovsec_kadm_free_principal_ent(server_handle, ent)) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ Tcl_DeleteHashEntry(entry);
+ }
+ set_ok(interp, "Principal freed.");
+ return TCL_OK;
+}
+
+
+int tcl_ovsec_kadm_free_policy_ent(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ char *ent_name;
+ ovsec_kadm_policy_ent_t ent;
+ int tcl_ret;
+ ovsec_kadm_ret_t ret;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing entry name");
+ return TCL_ERROR;
+ }
+
+ if ((! ent_name) &&
+ (ret = ovsec_kadm_free_policy_ent(server_handle, 0))) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ else {
+ Tcl_HashEntry *entry;
+
+ if (strncmp(ent_name, "policy", sizeof("policy")-1)) {
+ Tcl_AppendResult(interp, "invalid principal handle \"",
+ ent_name, "\"", 0);
+ return TCL_ERROR;
+ }
+ if (! struct_table) {
+ if (! (struct_table = malloc(sizeof(*struct_table)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ Tcl_InitHashTable(struct_table, TCL_STRING_KEYS);
+ }
+
+ if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) {
+ Tcl_AppendResult(interp, "policy handle \"", ent_name,
+ "\" not found", 0);
+ return TCL_ERROR;
+ }
+
+ ent = Tcl_GetHashValue(entry);
+
+ if (ret = ovsec_kadm_free_policy_ent(server_handle, ent)) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ Tcl_DeleteHashEntry(entry);
+ }
+ set_ok(interp, "Policy freed.");
+ return TCL_OK;
+}
+
+
+int tcl_ovsec_kadm_get_privs(ClientData clientData, Tcl_Interp *interp,
+ int argc, char *argv[])
+{
+ int tcl_ret;
+ char *set_ret;
+ ovsec_kadm_ret_t ret;
+ char *priv_var;
+ long privs;
+
+ GET_HANDLE(1, 0);
+
+ if ((tcl_ret = parse_str(interp, argv[0], &priv_var)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing privs variable name");
+ return TCL_ERROR;
+ }
+
+ ret = ovsec_kadm_get_privs(server_handle, priv_var ? &privs : 0);
+
+ if (ret == OVSEC_KADM_OK) {
+ if (priv_var) {
+ Tcl_DString *str = unparse_privs(privs);
+ set_ret = Tcl_SetVar(interp, priv_var, str->string,
+ TCL_LEAVE_ERR_MSG);
+ Tcl_DStringFree(str);
+ free(str);
+ if (! set_ret) {
+ Tcl_AppendElement(interp, "while setting priv variable");
+ return TCL_ERROR;
+ }
+ }
+ set_ok(interp, "Privileges retrieved.");
+ return TCL_OK;
+ }
+ else {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+}
+
+
+void Tcl_ovsec_kadm_init(Tcl_Interp *interp)
+{
+ char buf[20];
+
+ Tcl_SetVar(interp, "OVSEC_KADM_ADMIN_SERVICE",
+ OVSEC_KADM_ADMIN_SERVICE, TCL_GLOBAL_ONLY);
+ Tcl_SetVar(interp, "OVSEC_KADM_CHANGEPW_SERVICE",
+ OVSEC_KADM_CHANGEPW_SERVICE, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", OVSEC_KADM_STRUCT_VERSION);
+ Tcl_SetVar(interp, "OVSEC_KADM_STRUCT_VERSION", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", OVSEC_KADM_API_VERSION_1);
+ Tcl_SetVar(interp, "OVSEC_KADM_API_VERSION_1", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", OVSEC_KADM_API_VERSION_MASK);
+ Tcl_SetVar(interp, "OVSEC_KADM_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", OVSEC_KADM_STRUCT_VERSION_MASK);
+ Tcl_SetVar(interp, "OVSEC_KADM_STRUCT_VERSION_MASK", buf,
+ TCL_GLOBAL_ONLY);
+
+ Tcl_CreateCommand(interp, "ovsec_kadm_init", tcl_ovsec_kadm_init, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_destroy", tcl_ovsec_kadm_destroy, 0,
+ 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_create_principal",
+ tcl_ovsec_kadm_create_principal, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_delete_principal",
+ tcl_ovsec_kadm_delete_principal, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_modify_principal",
+ tcl_ovsec_kadm_modify_principal, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_rename_principal",
+ tcl_ovsec_kadm_rename_principal, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_chpass_principal",
+ tcl_ovsec_kadm_chpass_principal, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_chpass_principal_util",
+ tcl_ovsec_kadm_chpass_principal_util, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_randkey_principal",
+ tcl_ovsec_kadm_randkey_principal, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_get_principal",
+ tcl_ovsec_kadm_get_principal, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_create_policy",
+ tcl_ovsec_kadm_create_policy, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_delete_policy",
+ tcl_ovsec_kadm_delete_policy, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_modify_policy",
+ tcl_ovsec_kadm_modify_policy, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_get_policy",
+ tcl_ovsec_kadm_get_policy, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_free_principal_ent",
+ tcl_ovsec_kadm_free_principal_ent, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_free_policy_ent",
+ tcl_ovsec_kadm_free_policy_ent, 0, 0);
+ Tcl_CreateCommand(interp, "ovsec_kadm_get_privs",
+ tcl_ovsec_kadm_get_privs, 0, 0);
+}
diff --git a/src/kadmin/testing/util/tcl_ovsec_kadm_syntax b/src/kadmin/testing/util/tcl_ovsec_kadm_syntax
new file mode 100644
index 000000000..3fc77fbcb
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_ovsec_kadm_syntax
@@ -0,0 +1,57 @@
+Here's a brief summary of the syntax of the tcl versions of the
+ovsec_kadm commands:
+
+string Can be a string or "null" which will turn into a null pointer
+principal_ent A 12-field list in the order of the principal_ent
+ structure: {string number number number number string
+ number mask number number string mask}
+ It can also be "null", like a string, to indicate that
+ a null structure pointer should be used.
+mask Either a number, representing the actual value of the
+ mask, or a sequence of symbols in a list. Example:
+ {PRINCIPAL ATTRIBUTES} is a valid principal mask.
+boolean "1", "0", "true", "false", etc.
+varname The name of a Tcl variable, or "null" to not assign.
+policy_ent Similar to principal_ent, but with seven fields,
+ instead of 12. The first is a string, and the rest
+ are numbers.
+
+init
+ client_name:string pass:string service_name:string
+ realm:string struct_version:int api_version:int
+ server_handle_ret:varname
+destroy
+ server_handle:string
+create_principal
+ server_handle:string principal:principal_ent
+ mask:principal_mask password:string
+delete_principal
+ server_handle:string name:string
+modify_principal
+ server_handle:string principal_principal_ent
+ mask:principal_mask
+rename_principal
+ server_handle:string source:string target:string
+chpass_principal
+ server_handle:string name:string password:string
+chpass_principal_util
+ server_handle:string name:string password:string
+ pw_ret:varname msg_ret:varname
+randkey_principal
+ server_handle:string name:string keyblock_var:varname
+get_principal [-struct]
+ server_handle:string name:string princ_var:varname
+create_policy
+ server_handle:string policy:policy_ent mask:policy_mask
+delete_policy
+ server_handle:string name:string
+modify_policy
+ server_handle:string policy:policy_ent mask:policy_mask
+get_policy [-struct]
+ server_handle:string name:string policy_var:varname
+free_principal_ent
+ server_handle:string handle:string
+free_policy_ent
+ server_handle:string handle:string
+get_privs
+ server_handle:string privs:priv_var
diff --git a/src/kadmin/testing/util/test.c b/src/kadmin/testing/util/test.c
new file mode 100644
index 000000000..75a0fc25f
--- /dev/null
+++ b/src/kadmin/testing/util/test.c
@@ -0,0 +1,32 @@
+#include <tcl.h>
+
+#define IS_TCL_7_5 ((TCL_MAJOR_VERSION * 100 + TCL_MINOR_VERSION) >= 705)
+
+#if IS_TCL_7_5
+int
+main(argc, argv)
+ int argc; /* Number of command-line arguments. */
+ char **argv; /* Values of command-line arguments. */
+{
+ Tcl_Main(argc, argv, Tcl_AppInit);
+ return 0; /* Needed only to prevent compiler warning. */
+}
+#else
+/*
+ * The following variable is a special hack that allows applications
+ * to be linked using the procedure "main" from the Tcl library. The
+ * variable generates a reference to "main", which causes main to
+ * be brought in from the library (and all of Tcl with it).
+ */
+
+extern int main();
+int *tclDummyMainPtr = (int *) main;
+#endif
+
+int Tcl_AppInit(Tcl_Interp *interp)
+{
+ Tcl_ovsec_kadm_init(interp);
+ Tcl_kadm5_init(interp);
+
+ return(TCL_OK);
+}
diff --git a/src/kadmin/v4server/ChangeLog b/src/kadmin/v4server/ChangeLog
new file mode 100644
index 000000000..604014849
--- /dev/null
+++ b/src/kadmin/v4server/ChangeLog
@@ -0,0 +1,249 @@
+Thu Jul 18 19:46:49 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Tue Jul 9 17:18:56 1996 Marc Horowitz <marc@mit.edu>
+
+ * kadm_stream.c: rename HAS_STDLIB_H to HAVE_STDLIB_H to conform
+ to the autoconf convention
+ * configure.in: the old configure.in seemed to be written for some
+ other directory. Now it's right.
+ * admin_server.c, kadm_ser_wrap.c, kadm_server.c: renamed
+ <ovsec_admin/foo.h> to <kadm5/foo.h>
+ * Makefile.in: complete rewrite.
+
+Thu Mar 21 20:33:43 1996 Richard Basch <basch@lehman.com>
+
+ * kadm_funcs.c: new principals were being created with two keys,
+ one of which the key_data_ver=0 and had no valid data.
+
+Tue Mar 19 19:42:37 1996 Richard Basch <basch@lehman.com>
+
+ * kadm_funcs.c:
+ changed all references of des-cbc-md5 to des-cbc-crc
+ fixed uninitialized variable
+ set kvno modulo 256 in database
+
+Wed Feb 21 23:34:31 1996 Richard Basch <basch@lehman.com>
+
+ * kadm_funcs.c: Initialize the length element of the krb5_db_entry
+ structure in kadm_princ2entry (add_entry was failing).
+
+Wed Dec 13 03:51:53 1995 Chris Provenzano (proven@mit.edu)
+
+ * kadm_funcs.c : Remove mkvno for krb5_db_entry
+
+Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu)
+
+ * admin_server.c, kadm_funcs.c kadm_ser_wrap.c :
+ s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g
+
+Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu)
+
+ * admin_server.c, kadm_funcs.c, kadm_ser_wrap.c : Remove krb5_enctype
+ references, and replace with krb5_keytype where appropriate.
+
+Tue Aug 15 14:31:37 EDT 1995 Paul Park (pjpark@mit.edu)
+ * admin_server,kadm_funcs,kadm_ser_wrap.c - Replace kadm_find_keytype()
+ with krb5_dbe_find_keytype().
+
+
+Thu Aug 10 14:48:26 EDT 1995 Paul Park (pjpark@mit.edu)
+ * kadm_funcs.c - Add kadm_find_keytype() to find a particular key/salt
+ pair. Use this to find keys instead of assuming that the
+ right one's in the first slot.
+ Fix transposed arguments to strncpy().
+ Handle mod_princ_data stuff.
+ Supply saltblock to encrypt_key_data().
+ * admin_server, kadm_ser_wrap.c - Use kadm_find_keytype() to find keys.
+
+
+Mon Aug 7 13:30:46 EDT 1995 Paul Park (pjpark@mit.edu)
+ * admin_server,kadm_funcs,kadm_ser_wrap.c - Brute force substitutions
+ to get this to compile.
+
+
+Mon Jul 17 15:12:30 EDT 1995 Paul Park (pjpark@mit.edu)
+ * kadm_ser_wrap.c - Add NULL stash file argument to krb5_db_fetch_mkey.
+
+
+Fri Jul 7 16:05:11 EDT 1995 Paul Park (pjpark@mit.edu)
+ * Makefile.in - Remove all explicit library handling and LDFLAGS.
+ * configure.in - Add USE_<mumble> and KRB5_LIBRARIES.
+
+
+Tue Jun 27 16:05:27 EDT 1995 Paul Park (pjpark@mit.edu)
+ * acl_files.c - Change check for return value from fputs(3) from NULL
+ to EOF. That's what's returned on error.
+ * admin_server.c - Cast 4th argument of setsockopt(2) to be const char *
+ * kadm_funcs.c - Cast argument to ctime(3)
+ * kadm_server.c - Cast first argument to strcpy(3) and strcat(3).
+
+Tue Jun 20 14:44:54 1995 Tom Yu (tlyu@dragons-lair)
+
+ * configure.in: add tests for TIME_WITH_SYS_TIME and sys/time.h
+
+Thu Jun 15 17:52:29 EDT 1995 Paul Park (pjpark@mit.edu)
+ * Makefile.in - Change explicit library names to -l<lib> form, and
+ change target link line to use $(LD) and associated flags.
+ Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these were
+ split out.
+ * configure.in - Add shared library usage check.
+
+Fri Jun 9 19:07:25 1995 <tytso@rsx-11.mit.edu>
+
+ * configure.in: Remove standardized set of autoconf macros, which
+ are now handled by CONFIG_RULES.
+
+Fri Jun 9 06:49:36 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kadm_stream.c (vts_long, stv_long): Change u_long to krb5_ui_4
+
+ * kadm_server.c (kadm_ser_ckpw): Change u_long to krb5_ui_4
+
+ * kadm_ser_wrap.c (errpkt, kadm_ser_in): Change u_long to krb5_ui_4
+
+ * kadm_funcs.c (kadm_add_entry): Change u_long to krb5_ui_4
+
+ * admin_server.c (process_client): Change u_long to krb5_ui_4
+
+Sat May 20 22:33:58 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * kadm_stream.c: Based on presence of stdlib.h, include or declare
+ malloc.
+
+ * configure.in: Check for stdlib.h
+
+Sun May 7 13:49:54 1995 Ezra Peisach <epeisach@kangaroo.mit.edu>
+
+ * admin_server.c: Avoid warning of redeclaring POSIX_SIGNALS if
+ already defined.
+
+Sat Apr 29 00:34:01 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * admin_server.c (kadm_listen): Use Posix sigaction() instead of
+ signal() to set signal handlers. This allows us not to
+ worry about System V signal semantics. Make the code use
+ POSIX_SIGNALS by default.
+
+Fri Apr 28 18:08:05 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (KLIB): put KRB4_LIB inside KLIB.
+
+Thu Apr 27 13:53:41 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (v4kadmind): use KRB4_LIB directly.
+
+Thu Apr 20 23:21:42 1995 Theodore Y. Ts'o (tytso@dcl)
+
+ * kadm_funcs.c: Don't #include <ndbm.h>, since that's
+ automatically included by k5-config.h
+
+Thu Apr 20 15:26:48 1995 Ezra Peisach (epeisach@kangaroo.mit.edu)
+
+ * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): krb_int32 should be
+ krb5_int32.
+
+ * acl_files.c: Declare acl_abort as static at top of file.
+
+Sun Apr 16 19:10:17 1995 Mark Eichin <eichin@cygnus.com>
+
+ * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): use krb_int32, not
+ long, for network 4 byte quantities. Should get rid of the
+ use of memcpy at some point.
+
+Sat Mar 25 16:59:55 1995 Mark Eichin <eichin@cygnus.com>
+
+ * kadm_funcs.c (kadm_entry2princ): pass kadm_context in to
+ krb5_524_conv_principal.
+
+Tue Mar 14 16:45:18 1995 <tytso@rsx-11.mit.edu>
+
+ * Makefile.in: Don't link in the V4 DES library; use the des425
+ library to avoid linking the DES code in twice.
+
+Thu Mar 2 12:25:13 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * Makefile.in (ISODELIB): Remove reference to $(ISODELIB).
+
+Wed Mar 1 16:30:08 1995 Theodore Y. Ts'o <tytso@dcl>
+
+ * kadm_server.c: Remove declataions of malloc(); should be done by
+ header files.
+
+ * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket
+ and -lnsl with WITH_NETLIB check.
+
+Tue Feb 28 02:24:56 1995 John Gilmore (gnu at toad.com)
+
+ * admin_server.c, kadm_server.c, kadm-server.h: Avoid
+ <krb5/...> includes.
+
+Tue Feb 7 16:42:54 1995 Mark Eichin <eichin@cygnus.com>
+
+ * kadm_funcs.c (kadm_del_entry): fixed call to db_delete_principal.
+
+Wed Jan 25 18:42:42 1995 Mark Eichin (eichin@tweedledumber.cygnus.com)
+
+ * kadm_server.h (DEL_ACL_FILE): new define, acl file for V4 delete
+ function.
+ * kadm_server.c (kadm_ser_add): new function, wrapper for V4 delete.
+ * kadm_funcs.c (check_access): declare int; add DEL.
+ (kadm_del_entry): new function, V4 delete from CNS.
+ (failadd): fix spelling error in log entry.
+
+Mon Dec 12 13:21:48 1994 Mark Eichin (eichin@cygnus.com)
+
+ * kadm_funcs.c (kadm_entry2princ, kadm_princ2entry,
+ kadm_chg_srvtab): V4 and V5 max_life are in *different units* so
+ use the 60*5 conversion factor.
+
+Fri Nov 18 15:51:11 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change): Add
+ magic numbers of keyblock structre.
+
+Fri Nov 18 01:11:58 1994 Mark Eichin <eichin@cygnus.com>
+
+ * configure.in: use CHECK_SIGNALS instead of expansion (from
+ epeisach).
+
+Wed Oct 19 18:53:45 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * kadm_ser_wrap.c (kadm_ser_init): Use krb5_use_cstype() to
+ initialize the master_encblock structure.
+
+Thu Sep 29 22:41:20 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * Makefile.in: relink executable if libraries change
+
+Thu Sep 15 10:53:37 1994 Theodore Y. Ts'o (tytso@dcl)
+
+ * admin_server.c (close_syslog, byebye): Move these two functions
+ before main(), so that they get declared properly. Otherwise
+ suncc will refuse to compile the file.
+
+ * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change,
+ kadm_chg_srvtab): use krb5_timeofday instead of time(0).
+
+Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair)
+
+ * admin_server.c: pick up <sys/time.h> (needed to get FD_SET,
+ etc.)
+
+Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair)
+
+ * Makefile.in: no longer trying to install v4kadmind as krb5kdc
+ :-)
+ * configure.in: another try at making dbm libs dtrt
+
+Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair)
+
+ * admin_server.c: fixed calls that should have invoked
+ krb5_init_ets
+
+Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair)
+
+ * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to
+ krb5_build_principal
+
diff --git a/src/kadmin/v4server/Makefile.in b/src/kadmin/v4server/Makefile.in
new file mode 100644
index 000000000..01500167b
--- /dev/null
+++ b/src/kadmin/v4server/Makefile.in
@@ -0,0 +1,23 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) \
+ -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 -DNEED_SOCKETS
+
+LOCALINCLUDE = -I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I.
+
+PROG = kadmind4
+OBJS = kadm_server.o admin_server.o kadm_ser_wrap.o \
+ kadm_stream.o kadm_supp.o acl_files.o kadm_err.o
+
+all:: $(PROG)
+
+kadm_err.c kadm_err.h: $(srcdir)/kadm_err.et
+
+$(OBJS): kadm_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/v4server/Makefile.ov b/src/kadmin/v4server/Makefile.ov
new file mode 100644
index 000000000..a365e8ea7
--- /dev/null
+++ b/src/kadmin/v4server/Makefile.ov
@@ -0,0 +1,42 @@
+TOP = ..
+include $(TOP)/config.mk/template
+
+ifdef KRB5B4
+CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS)
+endif
+
+ETABLES = kadm_err.et
+expand ErrorTables
+
+depend:: kadm_err.h
+
+PROG := ovsec_v4adm_server
+
+SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \
+ kadm_stream.c kadm_supp.c acl_files.c kadm_err.c
+
+OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \
+ kadm_stream.o kadm_supp.o acl_files.o kadm_err.o
+
+LIBS := $(LIBADMCLNT) $(LIBRPCLIB) \
+ $(LIBKADM) $(LIBKRB) $(LIBDES425) \
+ $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5_ALL) \
+ $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB)
+
+ifdef WAIT_USES_INT
+WAIT_FLAGS = -DWAIT_USES_INT
+endif
+ifdef OPEN_NEEDS_FCNTL
+FCNTL_FLAGS = -DNEED_SYS_FCNTL_H
+endif
+
+CFLAGS := -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 \
+ $(WAIT_FLAGS) $(FCNLT_FLAGS) -I. \
+ -I$(TOP)/../include/kerberosIV -I$(TOP)/../../src/include/kerberosIV \
+ $(CFLAGS)
+
+expand InstallServer
+expand Depend
+
+SUBDIRS = unit-test
+expand SubdirTarget
diff --git a/src/kadmin/v4server/acl_files.c b/src/kadmin/v4server/acl_files.c
new file mode 100644
index 000000000..ae3b0c6bf
--- /dev/null
+++ b/src/kadmin/v4server/acl_files.c
@@ -0,0 +1,536 @@
+/*
+ * kadmin/v4server/acl_files.c
+ *
+ * Copyright 1987,1989 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ */
+
+
+/*** Routines for manipulating access control list files ***/
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include "krb.h"
+
+#ifndef KRB_REALM
+#define KRB_REALM "ATHENA.MIT.EDU"
+#endif
+
+/* "aname.inst@realm" */
+#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3)
+#define INST_SEP '.'
+#define REALM_SEP '@'
+
+#define LINESIZE 2048 /* Maximum line length in an acl file */
+
+#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */
+#define WAIT_TIME 300 /* Maximum time allowed write acl file */
+
+#define CACHED_ACLS 8 /* How many acls to cache */
+ /* Each acl costs 1 open file descriptor */
+#define ACL_LEN 16 /* Twice a reasonable acl length */
+
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#define COR(a,b) ((a!=NULL)?(a):(b))
+
+extern int errno;
+
+extern char *malloc(), *calloc();
+extern time_t time();
+
+static int acl_abort();
+
+/* Canonicalize a principal name */
+/* If instance is missing, it becomes "" */
+/* If realm is missing, it becomes the local realm */
+/* Canonicalized form is put in canon, which must be big enough to hold
+ MAX_PRINCIPAL_SIZE characters */
+void acl_canonicalize_principal(principal, canon)
+char *principal;
+char *canon;
+{
+ char *dot, *atsign, *end;
+ int len;
+
+ dot = strchr(principal, INST_SEP);
+ atsign = strchr(principal, REALM_SEP);
+
+ /* Maybe we're done already */
+ if(dot != NULL && atsign != NULL) {
+ if(dot < atsign) {
+ /* It's for real */
+ /* Copy into canon */
+ strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
+ canon[MAX_PRINCIPAL_SIZE-1] = '\0';
+ return;
+ } else {
+ /* Nope, it's part of the realm */
+ dot = NULL;
+ }
+ }
+
+ /* No such luck */
+ end = principal + strlen(principal);
+
+ /* Get the principal name */
+ len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
+ strncpy(canon, principal, len);
+ canon += len;
+
+ /* Add INST_SEP */
+ *canon++ = INST_SEP;
+
+ /* Get the instance, if it exists */
+ if(dot != NULL) {
+ ++dot;
+ len = MIN(INST_SZ, COR(atsign, end) - dot);
+ strncpy(canon, dot, len);
+ canon += len;
+ }
+
+ /* Add REALM_SEP */
+ *canon++ = REALM_SEP;
+
+ /* Get the realm, if it exists */
+ /* Otherwise, default to local realm */
+ if(atsign != NULL) {
+ ++atsign;
+ len = MIN(REALM_SZ, end - atsign);
+ strncpy(canon, atsign, len);
+ canon += len;
+ *canon++ = '\0';
+ } else if(krb_get_lrealm(canon, 1) != KSUCCESS) {
+ strcpy(canon, KRB_REALM);
+ }
+}
+
+/* Get a lock to modify acl_file */
+/* Return new FILE pointer */
+/* or NULL if file cannot be modified */
+/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */
+static FILE *acl_lock_file(acl_file)
+char *acl_file;
+{
+ struct stat s;
+ char new[LINESIZE];
+ int nfd;
+ FILE *nf;
+ int mode;
+
+ if(stat(acl_file, &s) < 0) return(NULL);
+ mode = s.st_mode;
+ sprintf(new, NEW_FILE, acl_file);
+ for(;;) {
+ /* Open the new file */
+ if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
+ if(errno == EEXIST) {
+ /* Maybe somebody got here already, maybe it's just old */
+ if(stat(new, &s) < 0) return(NULL);
+ if(time(0) - s.st_ctime > WAIT_TIME) {
+ /* File is stale, kill it */
+ unlink(new);
+ continue;
+ } else {
+ /* Wait and try again */
+ sleep(1);
+ continue;
+ }
+ } else {
+ /* Some other error, we lose */
+ return(NULL);
+ }
+ }
+
+ /* If we got to here, the lock file is ours and ok */
+ /* Reopen it under stdio */
+ if((nf = fdopen(nfd, "w")) == NULL) {
+ /* Oops, clean up */
+ unlink(new);
+ }
+ return(nf);
+ }
+}
+
+/* Commit changes to acl_file written onto FILE *f */
+/* Returns zero if successful */
+/* Returns > 0 if lock was broken */
+/* Returns < 0 if some other error occurs */
+/* Closes f */
+static int acl_commit(acl_file, f)
+char *acl_file;
+FILE *f;
+{
+ char new[LINESIZE];
+ int ret;
+ struct stat s;
+
+ sprintf(new, NEW_FILE, acl_file);
+ if(fflush(f) < 0
+ || fstat(fileno(f), &s) < 0
+ || s.st_nlink == 0) {
+ acl_abort(acl_file, f);
+ return(-1);
+ }
+
+ ret = rename(new, acl_file);
+ fclose(f);
+ return(ret);
+}
+
+/* Abort changes to acl_file written onto FILE *f */
+/* Returns 0 if successful, < 0 otherwise */
+/* Closes f */
+static int acl_abort(acl_file, f)
+char *acl_file;
+FILE *f;
+{
+ char new[LINESIZE];
+ int ret;
+ struct stat s;
+
+ /* make sure we aren't nuking someone else's file */
+ if(fstat(fileno(f), &s) < 0
+ || s.st_nlink == 0) {
+ fclose(f);
+ return(-1);
+ } else {
+ sprintf(new, NEW_FILE, acl_file);
+ ret = unlink(new);
+ fclose(f);
+ return(ret);
+ }
+}
+
+/* Initialize an acl_file */
+/* Creates the file with permissions perm if it does not exist */
+/* Erases it if it does */
+/* Returns return value of acl_commit */
+int acl_initialize(acl_file, perm)
+char *acl_file;
+int perm;
+{
+ FILE *new;
+ int fd;
+
+ /* Check if the file exists already */
+ if((new = acl_lock_file(acl_file)) != NULL) {
+ return(acl_commit(acl_file, new));
+ } else {
+ /* File must be readable and writable by owner */
+ if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) {
+ return(-1);
+ } else {
+ close(fd);
+ return(0);
+ }
+ }
+}
+
+/* Eliminate all whitespace character in buf */
+/* Modifies its argument */
+static nuke_whitespace(buf)
+char *buf;
+{
+ register char *pin, *pout;
+
+ for(pin = pout = buf; *pin != '\0'; pin++)
+ if(!isspace(*pin)) *pout++ = *pin;
+ *pout = '\0'; /* Terminate the string */
+}
+
+/* Hash table stuff */
+
+struct hashtbl {
+ int size; /* Max number of entries */
+ int entries; /* Actual number of entries */
+ char **tbl; /* Pointer to start of table */
+};
+
+/* Make an empty hash table of size s */
+static struct hashtbl *make_hash(size)
+int size;
+{
+ struct hashtbl *h;
+
+ if(size < 1) size = 1;
+ h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
+ h->size = size;
+ h->entries = 0;
+ h->tbl = (char **) calloc(size, sizeof(char *));
+ return(h);
+}
+
+/* Destroy a hash table */
+static destroy_hash(h)
+struct hashtbl *h;
+{
+ int i;
+
+ for(i = 0; i < h->size; i++) {
+ if(h->tbl[i] != NULL) free(h->tbl[i]);
+ }
+ free(h->tbl);
+ free(h);
+}
+
+/* Compute hash value for a string */
+static unsigned hashval(s)
+register char *s;
+{
+ register unsigned hv;
+
+ for(hv = 0; *s != '\0'; s++) {
+ hv ^= ((hv << 3) ^ *s);
+ }
+ return(hv);
+}
+
+/* Add an element to a hash table */
+static add_hash(h, el)
+struct hashtbl *h;
+char *el;
+{
+ unsigned hv;
+ char *s;
+ char **old;
+ int i;
+
+ /* Make space if it isn't there already */
+ if(h->entries + 1 > (h->size >> 1)) {
+ old = h->tbl;
+ h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
+ for(i = 0; i < h->size; i++) {
+ if(old[i] != NULL) {
+ hv = hashval(old[i]) % (h->size << 1);
+ while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
+ h->tbl[hv] = old[i];
+ }
+ }
+ h->size = h->size << 1;
+ free(old);
+ }
+
+ hv = hashval(el) % h->size;
+ while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
+ s = malloc(strlen(el)+1);
+ strcpy(s, el);
+ h->tbl[hv] = s;
+ h->entries++;
+}
+
+/* Returns nonzero if el is in h */
+static check_hash(h, el)
+struct hashtbl *h;
+char *el;
+{
+ unsigned hv;
+
+ for(hv = hashval(el) % h->size;
+ h->tbl[hv] != NULL;
+ hv = (hv + 1) % h->size) {
+ if(!strcmp(h->tbl[hv], el)) return(1);
+ }
+ return(0);
+}
+
+struct acl {
+ char filename[LINESIZE]; /* Name of acl file */
+ int fd; /* File descriptor for acl file */
+ struct stat status; /* File status at last read */
+ struct hashtbl *acl; /* Acl entries */
+};
+
+static struct acl acl_cache[CACHED_ACLS];
+
+static int acl_cache_count = 0;
+static int acl_cache_next = 0;
+
+/* Returns < 0 if unsuccessful in loading acl */
+/* Returns index into acl_cache otherwise */
+/* Note that if acl is already loaded, this is just a lookup */
+static int acl_load(name)
+char *name;
+{
+ int i;
+ FILE *f;
+ struct stat s;
+ char buf[MAX_PRINCIPAL_SIZE];
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ /* See if it's there already */
+ for(i = 0; i < acl_cache_count; i++) {
+ if(!strcmp(acl_cache[i].filename, name)
+ && acl_cache[i].fd >= 0) goto got_it;
+ }
+
+ /* It isn't, load it in */
+ /* maybe there's still room */
+ if(acl_cache_count < CACHED_ACLS) {
+ i = acl_cache_count++;
+ } else {
+ /* No room, clean one out */
+ i = acl_cache_next;
+ acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
+ close(acl_cache[i].fd);
+ if(acl_cache[i].acl) {
+ destroy_hash(acl_cache[i].acl);
+ acl_cache[i].acl = (struct hashtbl *) 0;
+ }
+ }
+
+ /* Set up the acl */
+ strcpy(acl_cache[i].filename, name);
+ if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
+ /* Force reload */
+ acl_cache[i].acl = (struct hashtbl *) 0;
+
+ got_it:
+ /*
+ * See if the stat matches
+ *
+ * Use stat(), not fstat(), as the file may have been re-created by
+ * acl_add or acl_delete. If this happens, the old inode will have
+ * no changes in the mod-time and the following test will fail.
+ */
+ if(stat(acl_cache[i].filename, &s) < 0) return(-1);
+ if(acl_cache[i].acl == (struct hashtbl *) 0
+ || s.st_nlink != acl_cache[i].status.st_nlink
+ || s.st_mtime != acl_cache[i].status.st_mtime
+ || s.st_ctime != acl_cache[i].status.st_ctime) {
+ /* Gotta reload */
+ if(acl_cache[i].fd >= 0) close(acl_cache[i].fd);
+ if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
+ if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1);
+ if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
+ acl_cache[i].acl = make_hash(ACL_LEN);
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ nuke_whitespace(buf);
+ acl_canonicalize_principal(buf, canon);
+ add_hash(acl_cache[i].acl, canon);
+ }
+ fclose(f);
+ acl_cache[i].status = s;
+ }
+ return(i);
+}
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Principal is not canonicalized, and no wildcarding is done */
+acl_exact_match(acl, principal)
+char *acl;
+char *principal;
+{
+ int idx;
+
+ return((idx = acl_load(acl)) >= 0
+ && check_hash(acl_cache[idx].acl, principal));
+}
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Recognizes wildcards in acl of the form
+ name.*@realm, *.*@realm, and *.*@* */
+acl_check(acl, principal)
+char *acl;
+char *principal;
+{
+ char buf[MAX_PRINCIPAL_SIZE];
+ char canon[MAX_PRINCIPAL_SIZE];
+ char *realm, *tmp;
+
+ acl_canonicalize_principal(principal, canon);
+
+ /* Is it there? */
+ if(acl_exact_match(acl, canon)) return(1);
+
+ /* Try the wildcards */
+ realm = strchr(canon, REALM_SEP);
+ tmp = strchr(canon, INST_SEP);
+ *tmp = '\0'; /* Chuck the instance */
+
+ sprintf(buf, "%s.*%s", canon, realm);
+ if(acl_exact_match(acl, buf)) return(1);
+
+ sprintf(buf, "*.*%s", realm);
+ if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1);
+
+ return(0);
+}
+
+/* Adds principal to acl */
+/* Wildcards are interpreted literally */
+acl_add(acl, principal)
+char *acl;
+char *principal;
+{
+ int idx;
+ int i;
+ FILE *new;
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ acl_canonicalize_principal(principal, canon);
+
+ if((new = acl_lock_file(acl)) == NULL) return(-1);
+ if((acl_exact_match(acl, canon))
+ || (idx = acl_load(acl)) < 0) {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ /* It isn't there yet, copy the file and put it in */
+ for(i = 0; i < acl_cache[idx].acl->size; i++) {
+ if(acl_cache[idx].acl->tbl[i] != NULL) {
+ if(fputs(acl_cache[idx].acl->tbl[i], new) == EOF
+ || putc('\n', new) != '\n') {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ }
+ }
+ fputs(canon, new);
+ putc('\n', new);
+ return(acl_commit(acl, new));
+}
+
+/* Removes principal from acl */
+/* Wildcards are interpreted literally */
+acl_delete(acl, principal)
+char *acl;
+char *principal;
+{
+ int idx;
+ int i;
+ FILE *new;
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ acl_canonicalize_principal(principal, canon);
+
+ if((new = acl_lock_file(acl)) == NULL) return(-1);
+ if((!acl_exact_match(acl, canon))
+ || (idx = acl_load(acl)) < 0) {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ /* It isn't there yet, copy the file and put it in */
+ for(i = 0; i < acl_cache[idx].acl->size; i++) {
+ if(acl_cache[idx].acl->tbl[i] != NULL
+ && strcmp(acl_cache[idx].acl->tbl[i], canon)) {
+ fputs(acl_cache[idx].acl->tbl[i], new);
+ putc('\n', new);
+ }
+ }
+ return(acl_commit(acl, new));
+}
+
diff --git a/src/kadmin/v4server/acl_files.doc b/src/kadmin/v4server/acl_files.doc
new file mode 100644
index 000000000..78c448a6d
--- /dev/null
+++ b/src/kadmin/v4server/acl_files.doc
@@ -0,0 +1,107 @@
+PROTOTYPE ACL LIBRARY
+
+Introduction
+
+An access control list (ACL) is a list of principals, where each
+principal is is represented by a text string which cannot contain
+whitespace. The library allows application programs to refer to named
+access control lists to test membership and to atomically add and
+delete principals using a natural and intuitive interface. At
+present, the names of access control lists are required to be Unix
+filenames, and refer to human-readable Unix files; in the future, when
+a networked ACL server is implemented, the names may refer to a
+different namespace specific to the ACL service.
+
+
+Usage
+
+cc <files> -lacl -lkrb.
+
+
+
+Principal Names
+
+Principal names have the form
+
+<name>[.<instance>][@<realm>]
+
+e.g.
+
+asp
+asp.root
+asp@ATHENA.MIT.EDU
+asp.@ATHENA.MIT.EDU
+asp.root@ATHENA.MIT.EDU
+
+It is possible for principals to be underspecified. If instance is
+missing, it is assumed to be "". If realm is missing, it is assumed
+to be local_realm. The canonical form contains all of name, instance,
+and realm; the acl_add and acl_delete routines will always
+leave the file in that form. Note that the canonical form of
+asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU.
+
+
+Routines
+
+acl_canonicalize_principal(principal, buf)
+char *principal;
+char *buf; /*RETVAL*/
+
+Store the canonical form of principal in buf. Buf must contain enough
+space to store a principal, given the limits on the sizes of name,
+instance, and realm specified in /usr/include/krb.h.
+
+acl_check(acl, principal)
+char *acl;
+char *principal;
+
+Returns nonzero if principal appears in acl. Returns 0 if principal
+does not appear in acl, or if an error occurs. Canonicalizes
+principal before checking, and allows the ACL to contain wildcards.
+
+acl_exact_match(acl, principal)
+char *acl;
+char *principal;
+
+Like acl_check, but does no canonicalization or wildcarding.
+
+acl_add(acl, principal)
+char *acl;
+char *principal;
+
+Atomically adds principal to acl. Returns 0 if successful, nonzero
+otherwise. It is considered a failure if principal is already in acl.
+This routine will canonicalize principal, but will treat wildcards
+literally.
+
+acl_delete(acl, principal)
+char *acl;
+char *principal;
+
+Atomically deletes principal from acl. Returns 0 if successful,
+nonzero otherwise. It is consider a failure if principal is not
+already in acl. This routine will canonicalize principal, but will
+treat wildcards literally.
+
+acl_initialize(acl, mode)
+char *acl;
+int mode;
+
+Initialize acl. If acl file does not exist, creates it with mode
+mode. If acl exists, removes all members. Returns 0 if successful,
+nonzero otherwise. WARNING: Mode argument is likely to change with
+the eventual introduction of an ACL service.
+
+
+Known problems
+
+In the presence of concurrency, there is a very small chance that
+acl_add or acl_delete could report success even though it would have
+had no effect. This is a necessary side effect of using lock files
+for concurrency control rather than flock(2), which is not supported
+by NFS.
+
+The current implementation caches ACLs in memory in a hash-table
+format for increased efficiency in checking membership; one effect of
+the caching scheme is that one file descriptor will be kept open for
+each ACL cached, up to a maximum of 8.
diff --git a/src/kadmin/v4server/admin_server.c b/src/kadmin/v4server/admin_server.c
new file mode 100644
index 000000000..7a207d7c5
--- /dev/null
+++ b/src/kadmin/v4server/admin_server.c
@@ -0,0 +1,684 @@
+/*
+ * kadmin/v4server/admin_server.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Top-level loop of the kerberos Administration server
+ */
+
+#include <mit-copyright.h>
+/*
+ admin_server.c
+ this holds the main loop and initialization and cleanup code for the server
+*/
+
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+/* define it for now */
+#ifndef POSIX_SIGNALS
+#define POSIX_SIGNALS
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifndef POSIX_SIGNALS
+#ifndef sigmask
+#define sigmask(m) (1 <<((m)-1))
+#endif
+#endif /* POSIX_SIGNALS */
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#ifdef OVSEC_KADM
+#include <kadm5/admin.h>
+void *ovsec_handle;
+kadm5_config_params params;
+#endif
+
+#include "k5-int.h"
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.h>
+#include "com_err.h"
+#include "kadm_server.h"
+
+#ifdef POSIX_SIGTYPE
+#define SIGNAL_RETURN return
+#else
+#define SIGNAL_RETURN return(0)
+#endif
+
+/* Almost all procs and such need this, so it is global */
+admin_params prm; /* The command line parameters struct */
+
+char prog[32]; /* WHY IS THIS NEEDED??????? */
+char *progname = prog;
+char *acldir = DEFAULT_ACL_DIR;
+char krbrlm[REALM_SZ];
+extern Kadm_Server server_parm;
+krb5_context kadm_context;
+int debug;
+
+/* close the system log file */
+void close_syslog()
+{
+ syslog(LOG_INFO, "Shutting down V4 admin server");
+}
+
+void byebye() /* say goodnight gracie */
+{
+ printf("Admin Server (kadm server) has completed operation.\n");
+}
+
+/*
+** Main does the logical thing, it sets up the database and RPC interface,
+** as well as handling the creation and maintenance of the syslog file...
+*/
+main(argc, argv) /* admin_server main routine */
+int argc;
+char *argv[];
+{
+ int errval;
+ int c;
+ char *lrealm;
+ extern char *optarg;
+ extern int fascist_cpw;
+
+#ifdef OVSEC_KADM
+ memset(&params, 0, sizeof(params));
+#endif
+
+ krb5_init_context(&kadm_context);
+ krb5_init_ets(kadm_context);
+ initialize_kadm_error_table();
+ prog[sizeof(prog)-1]='\0'; /* Terminate... */
+ (void) strncpy(prog, argv[0], sizeof(prog)-1);
+
+ /* initialize the admin_params structure */
+ prm.sysfile = KADM_SYSLOG; /* default file name */
+ prm.inter = 1;
+
+ memset(krbrlm, 0, sizeof(krbrlm));
+
+ fascist_cpw = 1; /* by default, enable fascist mode */
+ while ((c = getopt(argc, argv, "Df:hnd:a:r:FN")) != EOF)
+ switch(c) {
+ case 'D':
+ debug++;
+ break;
+ case 'f': /* Syslog file name change */
+ prm.sysfile = optarg;
+ break;
+ case 'n':
+ prm.inter = 0;
+ break;
+ case 'a': /* new acl directory */
+ acldir = optarg;
+ break;
+ case 'd':
+#ifdef OVSEC_KADM
+ params.dbname = optarg;
+ params.mask |= KADM5_CONFIG_DBNAME;
+#else
+ if (errval = krb5_db_set_name(kadm_context, optarg)) {
+ com_err(argv[0], errval, "while setting dbname");
+ exit(1);
+ }
+#endif
+ break;
+ case 'F':
+ fascist_cpw++;
+ break;
+ case 'N':
+ fascist_cpw = 0;
+ break;
+ case 'r':
+ (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1);
+ break;
+ case 'h': /* get help on using admin_server */
+ default:
+ printf("Usage: admin_server [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n");
+ exit(-1); /* failure */
+ }
+
+ if (krbrlm[0] == 0) {
+ if (errval = krb5_get_default_realm(kadm_context, &lrealm)) {
+ com_err(argv[0], errval, "while attempting to get local realm");
+ exit(1);
+ }
+ (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1);
+ }
+
+#ifdef OVSEC_KADM
+ params.realm = krbrlm;
+ params.mask |= KADM5_CONFIG_REALM;
+
+ if (errval = kadm5_get_config_params(kadm_context, NULL, NULL,
+ &params, &params)) {
+ com_err(argv[0], errval, "while retrieving kadm5 params");
+ exit(1);
+ }
+ if (errval = krb5_db_set_name(kadm_context, params.dbname)) {
+ com_err(argv[0], errval, "while setting dbname");
+ exit(1);
+ }
+#endif /* OVSEC_KADM */
+
+ printf("KADM Server %s initializing\n",KADM_VERSTR);
+ printf("Please do not use 'kill -9' to kill this job, use a\n");
+ printf("regular kill instead\n\n");
+
+#ifdef OVSEC_KADM
+ printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n",
+ debug ? "" : "parent ", getpid());
+#else
+ printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST");
+#endif
+
+ openlog(argv[0], LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */
+ syslog(LOG_INFO, "V4 admin server starting");
+
+ errval = krb5_db_init(kadm_context); /* Open the Kerberos database */
+ if (errval) {
+ fprintf(stderr, "error: krb5_db_init() failed");
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+ if (errval = krb5_db_set_lockmode(kadm_context, TRUE)) {
+ com_err(argv[0], errval, "while setting db to nonblocking");
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+ /* set up the server_parm struct */
+ if ((errval = kadm_ser_init(prm.inter, krbrlm
+#ifdef OVSEC_KADM
+ , &params
+#endif
+ ))==KADM_SUCCESS) {
+ krb5_db_fini(kadm_context); /* Close the Kerberos database--
+ will re-open later */
+ errval = kadm_listen(); /* listen for calls to server from
+ clients */
+ }
+ if (errval != KADM_SUCCESS) {
+ fprintf(stderr,"error: %s\n",error_message(errval));
+ krb5_db_fini(kadm_context); /* Close if error */
+ }
+ close_syslog(); /* Close syslog file, print
+ closing note */
+ byebye(); /* Say bye bye on the terminal
+ in use */
+ return 0;
+} /* procedure main */
+
+
+static void clear_secrets()
+{
+ krb5_finish_key(kadm_context, &server_parm.master_encblock);
+ memset((char *)&server_parm.master_encblock, 0,
+ sizeof (server_parm.master_encblock));
+ memset((char *)server_parm.master_keyblock.contents, 0,
+ server_parm.master_keyblock.length);
+ server_parm.mkvno = 0L;
+ return;
+}
+
+static exit_now = 0;
+
+krb5_sigtype
+doexit(sig)
+ int sig;
+{
+ exit_now = 1;
+ SIGNAL_RETURN;
+}
+
+unsigned pidarraysize = 0;
+int *pidarray = (int *)0;
+int unknown_child = 0;
+
+/*
+kadm_listen
+listen on the admin servers port for a request
+*/
+kadm_listen()
+{
+ extern int errno;
+ int found;
+ int admin_fd;
+ int peer_fd;
+ fd_set mask, readfds;
+ struct sockaddr_in peer;
+ int addrlen;
+ void process_client(), kill_children();
+ int pid;
+ krb5_sigtype do_child();
+#ifdef POSIX_SIGNALS
+ struct sigaction new_act;
+
+ new_act.sa_handler = doexit;
+ sigemptyset(&new_act.sa_mask);
+ sigaction(SIGINT, &new_act, 0);
+ sigaction(SIGTERM, &new_act, 0);
+ sigaction(SIGHUP, &new_act, 0);
+ sigaction(SIGQUIT, &new_act, 0);
+ sigaction(SIGALRM, &new_act, 0);
+ new_act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &new_act, 0);
+ new_act.sa_handler = do_child;
+ sigaction(SIGCHLD, &new_act, 0);
+#else
+ signal(SIGINT, doexit);
+ signal(SIGTERM, doexit);
+ signal(SIGHUP, doexit);
+ signal(SIGQUIT, doexit);
+ signal(SIGPIPE, SIG_IGN); /* get errors on write() */
+ signal(SIGALRM, doexit);
+ signal(SIGCHLD, do_child);
+#endif
+
+ if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return KADM_NO_SOCK;
+ if (debug) {
+ int one = 1;
+ if (setsockopt(admin_fd, SOL_SOCKET, SO_REUSEADDR, &one,
+ sizeof(int)) < 0) {
+ syslog(LOG_ERR, "setsockopt: %m");
+ return KADM_NO_BIND;
+ }
+ }
+ if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr,
+ sizeof(struct sockaddr_in)) < 0)
+ return KADM_NO_BIND;
+ (void) listen(admin_fd, 1);
+ FD_ZERO(&mask);
+ FD_SET(admin_fd, &mask);
+
+ for (;;) { /* loop nearly forever */
+ if (exit_now) {
+ clear_secrets();
+ kill_children();
+ return(0);
+ }
+ readfds = mask;
+ if ((found = select(admin_fd+1,&readfds,(fd_set *)0,
+ (fd_set *)0, (struct timeval *)0)) == 0)
+ continue; /* no things read */
+ if (found < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "select: %s", error_message(errno));
+ continue;
+ }
+ if (FD_ISSET(admin_fd, &readfds)) {
+ /* accept the conn */
+ addrlen = sizeof(peer);
+ if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer,
+ &addrlen)) < 0) {
+ syslog(LOG_ERR, "accept: %s", error_message(errno));
+ continue;
+ }
+
+ if (debug) {
+ process_client(peer_fd, &peer);
+ } else if (pid = fork()) {
+ /* parent */
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %s", error_message(errno));
+ (void) close(peer_fd);
+ continue;
+ }
+ /* fork succeeded: keep tabs on child */
+ (void) close(peer_fd);
+ if (unknown_child != pid) {
+ if (pidarray) {
+ pidarray = (int *)realloc((char *)pidarray,
+ (++pidarraysize * sizeof(int)));
+ pidarray[pidarraysize-1] = pid;
+ } else {
+ pidarray = (int *)malloc((pidarraysize = 1) * sizeof(int));
+ pidarray[0] = pid;
+ }
+ } /* End if unknown_child != pid.*/
+ } else {
+ /* child */
+ (void) close(admin_fd);
+ process_client(peer_fd, &peer);
+ }
+ } else {
+ syslog(LOG_ERR, "something else woke me up!");
+ return(0);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+void process_client(fd, who)
+ int fd;
+ struct sockaddr_in *who;
+{
+ u_char *dat;
+ int dat_len;
+ u_short dlen;
+ int retval;
+ int on = 1;
+ int nentries = 1;
+ krb5_db_entry sprinc_entries;
+ krb5_boolean more;
+ krb5_keyblock cpw_skey;
+ krb5_key_data *kdatap;
+ int status;
+
+#ifdef OVSEC_KADM
+#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab"
+ char *service_name;
+
+ service_name = (char *) malloc(strlen(server_parm.sname) +
+ strlen(server_parm.sinst) +
+ strlen(server_parm.krbrlm) + 3);
+ if (service_name == NULL) {
+ syslog(LOG_ERR, "error: out of memory allocating service name");
+ cleanexit(1);
+ }
+ sprintf(service_name, "%s/%s@%s", server_parm.sname,
+ server_parm.sinst, server_parm.krbrlm);
+
+ retval = ovsec_kadm_init_with_skey(service_name,
+ OVSEC_KADM_SRVTAB,
+ OVSEC_KADM_ADMIN_SERVICE, krbrlm,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &ovsec_handle);
+ if (retval) {
+ syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s",
+ error_message(retval));
+ cleanexit(1);
+ }
+ free(service_name);
+
+ if (retval = krb5_db_set_name(kadm_context, params.dbname)) {
+ syslog(LOG_ERR, "%s while setting dbname", error_message(retval));
+ cleanexit(1);
+ }
+#endif
+
+#ifndef NOENCRYPTION
+ /* Must do it here, since this is after the fork() call */
+ des_init_random_number_generator(server_parm.master_keyblock.contents);
+#endif /* NOENCRYPTION */
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
+ (const char *) &on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt keepalive: %d", errno);
+
+ server_parm.recv_addr = *who;
+
+ if (retval = krb5_db_init(kadm_context)) { /* Open as client */
+ syslog(LOG_ERR, "can't open krb db: %s", error_message(retval));
+ cleanexit(1);
+ }
+ /* need to set service key to changepw.KRB_MASTER */
+
+ status = krb5_db_get_principal(kadm_context, server_parm.sprinc,
+ &sprinc_entries,
+ &nentries, &more);
+ /* ugh... clean this up later */
+ if (status == KRB5_KDB_DB_INUSE) {
+ /* db locked */
+ krb5_ui_4 retcode = KADM_DB_INUSE;
+ char *pdat;
+
+ dat_len = KADM_VERSIZE + sizeof(krb5_ui_4);
+ dat = (u_char *) malloc((unsigned)dat_len);
+ pdat = (char *) dat;
+ retcode = htonl((krb5_ui_4) KADM_DB_INUSE);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4));
+ goto out;
+ } else if (!nentries) {
+ syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst);
+ cleanexit(2);
+ } else if (status) {
+ syslog(LOG_ERR, error_message(status));
+ cleanexit(2);
+ }
+
+ status = krb5_dbe_find_enctype(kadm_context,
+ &sprinc_entries,
+ ENCTYPE_DES_CBC_MD5,
+ -1,
+ -1,
+ &kdatap);
+ if (status) {
+ syslog(LOG_ERR, "find enctype failed: %s", error_message(status));
+ cleanexit(1);
+ }
+
+ status = krb5_dbekd_decrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ kdatap,
+ &cpw_skey,
+ (krb5_keysalt *) NULL);
+ if (status) {
+ syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status));
+ cleanexit(1);
+ }
+ /* if error, will show up when rd_req fails */
+ (void) krb_set_key((char *)cpw_skey.contents, 0);
+ while (1) {
+ if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) !=
+ sizeof(u_short)) {
+ if (retval < 0)
+ syslog(LOG_ERR, "dlen read: %s", error_message(errno));
+ else if (retval)
+ syslog(LOG_ERR, "short dlen read: %d", retval);
+ (void) close(fd);
+#ifdef OVSEC_KADM
+ (void) ovsec_kadm_destroy(ovsec_handle);
+#endif
+ cleanexit(retval ? 3 : 0);
+ }
+ if (exit_now) {
+ cleanexit(0);
+ }
+ dat_len = (int) ntohs(dlen);
+ dat = (u_char *) malloc((unsigned)dat_len);
+ if (!dat) {
+ syslog(LOG_ERR, "malloc: No memory");
+ (void) close(fd);
+ cleanexit(4);
+ }
+ if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) {
+ if (retval < 0)
+ syslog(LOG_ERR, "data read: %s", error_message(errno));
+ else
+ syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval);
+ (void) close(fd);
+ cleanexit(5);
+ }
+ if (exit_now) {
+ cleanexit(0);
+ }
+ if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS)
+ syslog(LOG_ERR, "processing request: %s", error_message(retval));
+
+ /* kadm_ser_in did the processing and returned stuff in
+ dat & dat_len , return the appropriate data */
+
+ out:
+ dlen = (u_short) dat_len;
+
+ if (dat_len != (int)dlen) {
+ clear_secrets();
+ abort(); /* XXX */
+ }
+ dlen = htons(dlen);
+
+ if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) {
+ syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno));
+ (void) close(fd);
+ cleanexit(6);
+ }
+
+ if (krb_net_write(fd, (char *)dat, dat_len) < 0) {
+ syslog(LOG_ERR, "writing to client: %s", error_message(errno));
+ (void) close(fd);
+ cleanexit(7);
+ }
+ free((char *)dat);
+ }
+ /*NOTREACHED*/
+}
+
+krb5_sigtype
+do_child(sig)
+ int sig;
+{
+ /* SIGCHLD brings us here */
+ int pid;
+ register int i, j;
+
+#ifdef WAIT_USES_INT
+ int status;
+#else
+ union wait status;
+#endif
+
+ pid = wait(&status);
+
+ for (i = 0; i < pidarraysize; i++)
+ if (pidarray[i] == pid) {
+ /* found it */
+ for (j = i; j < pidarraysize-1; j++)
+ /* copy others down */
+ pidarray[j] = pidarray[j+1];
+ pidarraysize--;
+#ifdef WAIT_USES_INT
+ if (WIFEXITED(status) || WIFSIGNALED(status))
+ if (WTERMSIG(status) || WEXITSTATUS(status))
+ syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid,
+ WTERMSIG(status), WEXITSTATUS(status));
+
+#else
+ if (status.w_retcode || status.w_coredump || status.w_termsig)
+ syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d",
+ pid, status.w_termsig, status.w_coredump, status.w_retcode);
+
+#endif
+ SIGNAL_RETURN;
+ }
+ unknown_child = pid;
+#ifdef WAIT_USES_INT
+ syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid,
+ WTERMSIG(status), WEXITSTATUS(status));
+
+#else
+ syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d",
+ pid, status.w_termsig, status.w_coredump, status.w_retcode);
+
+#endif
+ SIGNAL_RETURN;
+}
+
+cleanexit(val)
+{
+ krb5_db_fini(kadm_context);
+ clear_secrets();
+ exit(val);
+}
+
+void
+kill_children()
+{
+ register int i;
+#ifdef POSIX_SIGNALS
+ sigset_t oldmask, igmask;
+#else
+ int osigmask;
+#endif
+
+#ifdef POSIX_SIGNALS
+ sigemptyset(&igmask);
+ sigaddset(&igmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &igmask, &oldmask);
+#else
+ osigmask = sigblock(sigmask(SIGCHLD));
+#endif
+
+ for (i = 0; i < pidarraysize; i++) {
+ kill(pidarray[i], SIGINT);
+ syslog(LOG_ERR, "killing child %d", pidarray[i]);
+ }
+#ifdef POSIX_SIGNALS
+ sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0);
+#else
+ sigsetmask(osigmask);
+#endif
+ return;
+}
+
+#ifdef OVSEC_KADM
+krb5_ui_4 convert_ovsec_to_kadm(val)
+ krb5_ui_4 val;
+{
+ switch (val) {
+ case KADM5_AUTH_GET:
+ case KADM5_AUTH_ADD:
+ case KADM5_AUTH_MODIFY:
+ case KADM5_AUTH_DELETE:
+ case KADM5_AUTH_INSUFFICIENT:
+ case KADM5_AUTH_LIST:
+ case KADM5_AUTH_CHANGEPW:
+ return KADM_UNAUTH;
+ case KADM5_BAD_DB:
+ return KADM_UK_RERROR;
+ case KADM5_DUP:
+ case KADM5_POLICY_REF:
+ return KADM_INUSE;
+ case KADM5_RPC_ERROR:
+ return KADM_NO_CONN;
+ case KADM5_NO_SRV:
+ return KADM_NO_HOST;
+ case KADM5_UNK_PRINC:
+ case KADM5_UNK_POLICY:
+ return KADM_NOENTRY;
+ case KADM5_PASS_Q_TOOSHORT:
+ case KADM5_PASS_Q_CLASS:
+ case KADM5_PASS_Q_DICT:
+ case KADM5_PASS_REUSE:
+ case KADM5_PASS_TOOSOON:
+ case CHPASS_UTIL_PASSWORD_TOO_SOON:
+ return KADM_INSECURE_PW;
+ case KADM5_BAD_PASSWORD:
+ return KADM_NO_CRED;
+ case KADM5_PROTECT_PRINCIPAL:
+ return KADM_NO_OPCODE;
+ case KADM5_NOT_INIT:
+ case KADM5_BAD_HIST_KEY:
+ case KADM5_BAD_MASK:
+ case KADM5_BAD_CLASS:
+ case KADM5_BAD_LENGTH:
+ case KADM5_BAD_POLICY:
+ case KADM5_BAD_PRINCIPAL:
+ case KADM5_BAD_AUX_ATTR:
+ case KADM5_BAD_HISTORY:
+ case KADM5_BAD_MIN_PASS_LIFE:
+ return -1;
+ }
+ return val;
+}
+#endif
diff --git a/src/kadmin/v4server/attic/ChangeLog b/src/kadmin/v4server/attic/ChangeLog
new file mode 100644
index 000000000..6eefc24c7
--- /dev/null
+++ b/src/kadmin/v4server/attic/ChangeLog
@@ -0,0 +1,25 @@
+Thu Jul 18 19:47:58 1996 Marc Horowitz <marc@mit.edu>
+
+ * configure.in: removed ET_RULES, replaced with AC_PROG_AWK
+
+Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair)
+
+ * admin_server.c: pick up <sys/time.h> (needed to get FD_SET,
+ etc.)
+
+Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair)
+
+ * Makefile.in: no longer trying to install v4kadmind as krb5kdc
+ :-)
+ * configure.in: another try at making dbm libs dtrt
+
+Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair)
+
+ * admin_server.c: fixed calls that should have invoked
+ krb5_init_ets
+
+Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair)
+
+ * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to
+ krb5_build_principal
+
diff --git a/src/kadmin/v4server/attic/Imakefile b/src/kadmin/v4server/attic/Imakefile
new file mode 100644
index 000000000..e1449ef32
--- /dev/null
+++ b/src/kadmin/v4server/attic/Imakefile
@@ -0,0 +1,49 @@
+# $Source$
+# $Author$
+# $Header$
+#
+# Copyright 1989 by the Massachusetts Institute of Technology.
+#
+# For copying and distribution information,
+# please see the file <mit-copyright.h>.
+#
+# Imakefile for Kerberos admin server library.
+
+DEFINES = $(KRB4DEF)
+INCLUDES = $(KRB4INCLUDES) -I.
+SRCS = \
+ kadm_server.c \
+ kadm_funcs.c \
+ admin_server.c \
+ kadm_ser_wrap.c \
+ kadm_stream.c \
+ kadm_supp.c \
+ kadm_err.c \
+ acl_files.c
+OBJS = \
+ kadm_server.o \
+ kadm_funcs.o \
+ admin_server.o \
+ kadm_ser_wrap.o \
+ kadm_stream.o \
+ kadm_supp.o \
+ kadm_err.o \
+ acl_files.o
+
+ErrorTableObjectRule()
+
+all:: v4kadmind
+
+depend:: kadm_err.c
+
+kadm_err.c: kadm_err.et
+
+NormalProgramTarget(v4kadmind,$(OBJS),$(KDBDEPLIB) $(DEPKLIB), \
+ $(KDBLIB) $(KRB4LIB) $(KLIB) ,)
+
+Krb5InstallServerProgram(v4kadmind)
+
+clean::
+ $(RM) kadm_err.c kadm_err.h
+
+DependTarget()
diff --git a/src/kadmin/v4server/attic/Makefile b/src/kadmin/v4server/attic/Makefile
new file mode 100644
index 000000000..d0acac021
--- /dev/null
+++ b/src/kadmin/v4server/attic/Makefile
@@ -0,0 +1,39 @@
+TOP = ../..
+include $(TOP)/config.mk/template
+
+ifdef KRB5B4
+CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS)
+endif
+
+PROG := ovsec_v4adm_server
+
+SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \
+ kadm_stream.c kadm_supp.c acl_files.c
+
+OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \
+ kadm_stream.o kadm_supp.o acl_files.o
+
+LIBS := $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5) \
+ $(LIBKADM) $(LIBKRB) $(LIBDES425) $(LIBKDB5) \
+ $(LIBCRYPTO) $(LIBISODE) \
+ $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB)
+
+ifdef WAIT_USES_INT
+WAIT_FLAGS = -DWAIT_USES_INT
+endif
+ifdef OPEN_NEEDS_FCNTL
+FCNTL_FLAGS = -DNEED_SYS_FCNTL_H
+endif
+
+# XXX the -D's should probably be moved somewhere; in krb5.4.2 they
+# are in osconf.h
+CFLAGS := -DOVSEC_KADM \
+ -DKADM_SYSLOG="\"/krb5/admin_server.syslog\"" \
+ -DDEFAULT_ACL_DIR="\"/krb5\"" $(WAIT_FLAGS) $(FCNTL_FLAGS) \
+ $(CFLAGS)
+
+expand InstallServer
+expand Depend
+
+SUBDIRS = unit-test
+expand SubdirTarget
diff --git a/src/kadmin/v4server/attic/Makefile.in b/src/kadmin/v4server/attic/Makefile.in
new file mode 100644
index 000000000..f5206aa66
--- /dev/null
+++ b/src/kadmin/v4server/attic/Makefile.in
@@ -0,0 +1,53 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+LDFLAGS = -g
+
+ISODELIB=@ISODELIB@
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+DBMLIB=
+KDBLIB=$(TOPLIBD)/libkdb5.a
+
+KRB4LIB = $(KRB4)/lib/libkrb.a $(TOPLIBD)/libdes425.a
+
+KLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(ISODELIB) $(COMERRLIB) $(DBMLIB)
+
+LOCALINCLUDE=-I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I.
+
+SRCS = \
+ $(srcdir)/kadm_server.c \
+ $(srcdir)/kadm_funcs.c \
+ $(srcdir)/admin_server.c \
+ $(srcdir)/kadm_ser_wrap.c \
+ $(srcdir)/kadm_stream.c \
+ $(srcdir)/kadm_supp.c \
+ $(srcdir)/kadm_err.c \
+ $(srcdir)/acl_files.c
+OBJS = \
+ kadm_server.o \
+ kadm_funcs.o \
+ admin_server.o \
+ kadm_ser_wrap.o \
+ kadm_stream.o \
+ kadm_supp.o \
+ kadm_err.o \
+ acl_files.o
+
+all:: kadm_err.h v4kadmind
+
+depend:: kadm_err.c
+
+kadm_err.c: kadm_err.et
+
+kadm_err.h: kadm_err.et
+
+v4kadmind: $(OBJS) $(KDBDEPLIB) $(DEPKLIB)
+ $(CC) $(CFLAGS) -o v4kadmind $(OBJS) $(KDBLIB) $(KLIB) $(KRB4LIB) $(LIBS) $(KRB4)/lib/libdes.a
+
+install::
+ $(INSTALL_PROGRAM) v4kadmind ${DESTDIR}$(SERVER_BINDIR)/v4kadmind
+
+clean::
+ $(RM) kadm_err.h kadm_err.c
+
+clean::
+ $(RM) v4kadmind
+
diff --git a/src/kadmin/v4server/attic/acl_files.c b/src/kadmin/v4server/attic/acl_files.c
new file mode 100644
index 000000000..81275ae26
--- /dev/null
+++ b/src/kadmin/v4server/attic/acl_files.c
@@ -0,0 +1,541 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1987,1989 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ */
+
+#ifndef lint
+static char rcsid_acl_files_c[] = "$Id$";
+#endif lint
+
+
+/*** Routines for manipulating access control list files ***/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <ctype.h>
+#ifdef NEED_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+#include "krb.h"
+#include <krb5/krb5.h>
+
+#ifndef KRB_REALM
+#define KRB_REALM "ATHENA.MIT.EDU"
+#endif
+
+/* "aname.inst@realm" */
+#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3)
+#define INST_SEP '.'
+#define REALM_SEP '@'
+
+#define LINESIZE 2048 /* Maximum line length in an acl file */
+
+#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */
+#define WAIT_TIME 300 /* Maximum time allowed write acl file */
+
+#define CACHED_ACLS 8 /* How many acls to cache */
+ /* Each acl costs 1 open file descriptor */
+#define ACL_LEN 16 /* Twice a reasonable acl length */
+
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+#define COR(a,b) ((a!=NULL)?(a):(b))
+
+extern int errno;
+
+extern char *malloc(), *calloc();
+extern time_t time();
+
+static int acl_abort PROTOTYPE((char *acl_file, FILE *f));
+
+/* Canonicalize a principal name */
+/* If instance is missing, it becomes "" */
+/* If realm is missing, it becomes the local realm */
+/* Canonicalized form is put in canon, which must be big enough to hold
+ MAX_PRINCIPAL_SIZE characters */
+acl_canonicalize_principal(principal, canon)
+char *principal;
+char *canon;
+{
+ char *dot, *atsign, *end;
+ int len;
+
+ dot = strchr(principal, INST_SEP);
+ atsign = strchr(principal, REALM_SEP);
+
+ /* Maybe we're done already */
+ if(dot != NULL && atsign != NULL) {
+ if(dot < atsign) {
+ /* It's for real */
+ /* Copy into canon */
+ strncpy(canon, principal, MAX_PRINCIPAL_SIZE);
+ canon[MAX_PRINCIPAL_SIZE-1] = '\0';
+ return;
+ } else {
+ /* Nope, it's part of the realm */
+ dot = NULL;
+ }
+ }
+
+ /* No such luck */
+ end = principal + strlen(principal);
+
+ /* Get the principal name */
+ len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal);
+ strncpy(canon, principal, len);
+ canon += len;
+
+ /* Add INST_SEP */
+ *canon++ = INST_SEP;
+
+ /* Get the instance, if it exists */
+ if(dot != NULL) {
+ ++dot;
+ len = MIN(INST_SZ, COR(atsign, end) - dot);
+ strncpy(canon, dot, len);
+ canon += len;
+ }
+
+ /* Add REALM_SEP */
+ *canon++ = REALM_SEP;
+
+ /* Get the realm, if it exists */
+ /* Otherwise, default to local realm */
+ if(atsign != NULL) {
+ ++atsign;
+ len = MIN(REALM_SZ, end - atsign);
+ strncpy(canon, atsign, len);
+ canon += len;
+ *canon++ = '\0';
+ } else if(krb_get_lrealm(canon, 1) != KSUCCESS) {
+ strcpy(canon, KRB_REALM);
+ }
+}
+
+/* Get a lock to modify acl_file */
+/* Return new FILE pointer */
+/* or NULL if file cannot be modified */
+/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */
+static FILE *acl_lock_file(acl_file)
+char *acl_file;
+{
+ struct stat s;
+ char new[LINESIZE];
+ int nfd;
+ FILE *nf;
+ int mode;
+
+ if(stat(acl_file, &s) < 0) return(NULL);
+ mode = s.st_mode;
+ sprintf(new, NEW_FILE, acl_file);
+ for(;;) {
+ /* Open the new file */
+ if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) {
+ if(errno == EEXIST) {
+ /* Maybe somebody got here already, maybe it's just old */
+ if(stat(new, &s) < 0) return(NULL);
+ if(time(0) - s.st_ctime > WAIT_TIME) {
+ /* File is stale, kill it */
+ unlink(new);
+ continue;
+ } else {
+ /* Wait and try again */
+ sleep(1);
+ continue;
+ }
+ } else {
+ /* Some other error, we lose */
+ return(NULL);
+ }
+ }
+
+ /* If we got to here, the lock file is ours and ok */
+ /* Reopen it under stdio */
+ if((nf = fdopen(nfd, "w")) == NULL) {
+ /* Oops, clean up */
+ unlink(new);
+ }
+ return(nf);
+ }
+}
+
+/* Commit changes to acl_file written onto FILE *f */
+/* Returns zero if successful */
+/* Returns > 0 if lock was broken */
+/* Returns < 0 if some other error occurs */
+/* Closes f */
+static int acl_commit(acl_file, f)
+char *acl_file;
+FILE *f;
+{
+ char new[LINESIZE];
+ int ret;
+ struct stat s;
+
+ sprintf(new, NEW_FILE, acl_file);
+ if(fflush(f) < 0
+ || fstat(fileno(f), &s) < 0
+ || s.st_nlink == 0) {
+ acl_abort(acl_file, f);
+ return(-1);
+ }
+
+ ret = rename(new, acl_file);
+ fclose(f);
+ return(ret);
+}
+
+/* Abort changes to acl_file written onto FILE *f */
+/* Returns 0 if successful, < 0 otherwise */
+/* Closes f */
+static int acl_abort(acl_file, f)
+char *acl_file;
+FILE *f;
+{
+ char new[LINESIZE];
+ int ret;
+ struct stat s;
+
+ /* make sure we aren't nuking someone else's file */
+ if(fstat(fileno(f), &s) < 0
+ || s.st_nlink == 0) {
+ fclose(f);
+ return(-1);
+ } else {
+ sprintf(new, NEW_FILE, acl_file);
+ ret = unlink(new);
+ fclose(f);
+ return(ret);
+ }
+}
+
+/* Initialize an acl_file */
+/* Creates the file with permissions perm if it does not exist */
+/* Erases it if it does */
+/* Returns return value of acl_commit */
+int acl_initialize(acl_file, perm)
+char *acl_file;
+int perm;
+{
+ FILE *new;
+ int fd;
+
+ /* Check if the file exists already */
+ if((new = acl_lock_file(acl_file)) != NULL) {
+ return(acl_commit(acl_file, new));
+ } else {
+ /* File must be readable and writable by owner */
+ if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) {
+ return(-1);
+ } else {
+ close(fd);
+ return(0);
+ }
+ }
+}
+
+/* Eliminate all whitespace character in buf */
+/* Modifies its argument */
+static nuke_whitespace(buf)
+char *buf;
+{
+ register char *pin, *pout;
+
+ for(pin = pout = buf; *pin != '\0'; pin++)
+ if(!isspace(*pin)) *pout++ = *pin;
+ *pout = '\0'; /* Terminate the string */
+}
+
+/* Hash table stuff */
+
+struct hashtbl {
+ int size; /* Max number of entries */
+ int entries; /* Actual number of entries */
+ char **tbl; /* Pointer to start of table */
+};
+
+/* Make an empty hash table of size s */
+static struct hashtbl *make_hash(size)
+int size;
+{
+ struct hashtbl *h;
+
+ if(size < 1) size = 1;
+ h = (struct hashtbl *) malloc(sizeof(struct hashtbl));
+ h->size = size;
+ h->entries = 0;
+ h->tbl = (char **) calloc(size, sizeof(char *));
+ return(h);
+}
+
+/* Destroy a hash table */
+static destroy_hash(h)
+struct hashtbl *h;
+{
+ int i;
+
+ for(i = 0; i < h->size; i++) {
+ if(h->tbl[i] != NULL) free(h->tbl[i]);
+ }
+ free(h->tbl);
+ free(h);
+}
+
+/* Compute hash value for a string */
+static unsigned hashval(s)
+register char *s;
+{
+ register unsigned hv;
+
+ for(hv = 0; *s != '\0'; s++) {
+ hv ^= ((hv << 3) ^ *s);
+ }
+ return(hv);
+}
+
+/* Add an element to a hash table */
+static add_hash(h, el)
+struct hashtbl *h;
+char *el;
+{
+ unsigned hv;
+ char *s;
+ char **old;
+ int i;
+
+ /* Make space if it isn't there already */
+ if(h->entries + 1 > (h->size >> 1)) {
+ old = h->tbl;
+ h->tbl = (char **) calloc(h->size << 1, sizeof(char *));
+ for(i = 0; i < h->size; i++) {
+ if(old[i] != NULL) {
+ hv = hashval(old[i]) % (h->size << 1);
+ while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1);
+ h->tbl[hv] = old[i];
+ }
+ }
+ h->size = h->size << 1;
+ free(old);
+ }
+
+ hv = hashval(el) % h->size;
+ while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size;
+ s = malloc(strlen(el)+1);
+ strcpy(s, el);
+ h->tbl[hv] = s;
+ h->entries++;
+}
+
+/* Returns nonzero if el is in h */
+static check_hash(h, el)
+struct hashtbl *h;
+char *el;
+{
+ unsigned hv;
+
+ for(hv = hashval(el) % h->size;
+ h->tbl[hv] != NULL;
+ hv = (hv + 1) % h->size) {
+ if(!strcmp(h->tbl[hv], el)) return(1);
+ }
+ return(0);
+}
+
+struct acl {
+ char filename[LINESIZE]; /* Name of acl file */
+ int fd; /* File descriptor for acl file */
+ struct stat status; /* File status at last read */
+ struct hashtbl *acl; /* Acl entries */
+};
+
+static struct acl acl_cache[CACHED_ACLS];
+
+static int acl_cache_count = 0;
+static int acl_cache_next = 0;
+
+/* Returns < 0 if unsuccessful in loading acl */
+/* Returns index into acl_cache otherwise */
+/* Note that if acl is already loaded, this is just a lookup */
+static int acl_load(name)
+char *name;
+{
+ int i;
+ FILE *f;
+ struct stat s;
+ char buf[MAX_PRINCIPAL_SIZE];
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ /* See if it's there already */
+ for(i = 0; i < acl_cache_count; i++) {
+ if(!strcmp(acl_cache[i].filename, name)
+ && acl_cache[i].fd >= 0) goto got_it;
+ }
+
+ /* It isn't, load it in */
+ /* maybe there's still room */
+ if(acl_cache_count < CACHED_ACLS) {
+ i = acl_cache_count++;
+ } else {
+ /* No room, clean one out */
+ i = acl_cache_next;
+ acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS;
+ close(acl_cache[i].fd);
+ if(acl_cache[i].acl) {
+ destroy_hash(acl_cache[i].acl);
+ acl_cache[i].acl = (struct hashtbl *) 0;
+ }
+ }
+
+ /* Set up the acl */
+ strcpy(acl_cache[i].filename, name);
+ if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
+ /* Force reload */
+ acl_cache[i].acl = (struct hashtbl *) 0;
+
+ got_it:
+ /*
+ * See if the stat matches
+ *
+ * Use stat(), not fstat(), as the file may have been re-created by
+ * acl_add or acl_delete. If this happens, the old inode will have
+ * no changes in the mod-time and the following test will fail.
+ */
+ if(stat(acl_cache[i].filename, &s) < 0) return(-1);
+ if(acl_cache[i].acl == (struct hashtbl *) 0
+ || s.st_nlink != acl_cache[i].status.st_nlink
+ || s.st_mtime != acl_cache[i].status.st_mtime
+ || s.st_ctime != acl_cache[i].status.st_ctime) {
+ /* Gotta reload */
+ if(acl_cache[i].fd >= 0) close(acl_cache[i].fd);
+ if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1);
+ if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1);
+ if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl);
+ acl_cache[i].acl = make_hash(ACL_LEN);
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ nuke_whitespace(buf);
+ acl_canonicalize_principal(buf, canon);
+ add_hash(acl_cache[i].acl, canon);
+ }
+ fclose(f);
+ acl_cache[i].status = s;
+ }
+ return(i);
+}
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Principal is not canonicalized, and no wildcarding is done */
+acl_exact_match(acl, principal)
+char *acl;
+char *principal;
+{
+ int idx;
+
+ return((idx = acl_load(acl)) >= 0
+ && check_hash(acl_cache[idx].acl, principal));
+}
+
+/* Returns nonzero if it can be determined that acl contains principal */
+/* Recognizes wildcards in acl of the form
+ name.*@realm, *.*@realm, and *.*@* */
+acl_check(acl, principal)
+char *acl;
+char *principal;
+{
+ char buf[MAX_PRINCIPAL_SIZE];
+ char canon[MAX_PRINCIPAL_SIZE];
+ char *realm, *tmp;
+
+ acl_canonicalize_principal(principal, canon);
+
+ /* Is it there? */
+ if(acl_exact_match(acl, canon)) return(1);
+
+ /* Try the wildcards */
+ realm = strchr(canon, REALM_SEP);
+ tmp = strchr(canon, INST_SEP);
+ *tmp = '\0'; /* Chuck the instance */
+
+ sprintf(buf, "%s.*%s", canon, realm);
+ if(acl_exact_match(acl, buf)) return(1);
+
+ sprintf(buf, "*.*%s", realm);
+ if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1);
+
+ return(0);
+}
+
+/* Adds principal to acl */
+/* Wildcards are interpreted literally */
+acl_add(acl, principal)
+char *acl;
+char *principal;
+{
+ int idx;
+ int i;
+ FILE *new;
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ acl_canonicalize_principal(principal, canon);
+
+ if((new = acl_lock_file(acl)) == NULL) return(-1);
+ if((acl_exact_match(acl, canon))
+ || (idx = acl_load(acl)) < 0) {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ /* It isn't there yet, copy the file and put it in */
+ for(i = 0; i < acl_cache[idx].acl->size; i++) {
+ if(acl_cache[idx].acl->tbl[i] != NULL) {
+ if((fputs(acl_cache[idx].acl->tbl[i], new) == EOF)
+ || (putc('\n', new) != '\n')) {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ }
+ }
+ fputs(canon, new);
+ putc('\n', new);
+ return(acl_commit(acl, new));
+}
+
+/* Removes principal from acl */
+/* Wildcards are interpreted literally */
+acl_delete(acl, principal)
+char *acl;
+char *principal;
+{
+ int idx;
+ int i;
+ FILE *new;
+ char canon[MAX_PRINCIPAL_SIZE];
+
+ acl_canonicalize_principal(principal, canon);
+
+ if((new = acl_lock_file(acl)) == NULL) return(-1);
+ if((!acl_exact_match(acl, canon))
+ || (idx = acl_load(acl)) < 0) {
+ acl_abort(acl, new);
+ return(-1);
+ }
+ /* It isn't there yet, copy the file and put it in */
+ for(i = 0; i < acl_cache[idx].acl->size; i++) {
+ if(acl_cache[idx].acl->tbl[i] != NULL
+ && strcmp(acl_cache[idx].acl->tbl[i], canon)) {
+ fputs(acl_cache[idx].acl->tbl[i], new);
+ putc('\n', new);
+ }
+ }
+ return(acl_commit(acl, new));
+}
+
diff --git a/src/kadmin/v4server/attic/acl_files.doc b/src/kadmin/v4server/attic/acl_files.doc
new file mode 100644
index 000000000..78c448a6d
--- /dev/null
+++ b/src/kadmin/v4server/attic/acl_files.doc
@@ -0,0 +1,107 @@
+PROTOTYPE ACL LIBRARY
+
+Introduction
+
+An access control list (ACL) is a list of principals, where each
+principal is is represented by a text string which cannot contain
+whitespace. The library allows application programs to refer to named
+access control lists to test membership and to atomically add and
+delete principals using a natural and intuitive interface. At
+present, the names of access control lists are required to be Unix
+filenames, and refer to human-readable Unix files; in the future, when
+a networked ACL server is implemented, the names may refer to a
+different namespace specific to the ACL service.
+
+
+Usage
+
+cc <files> -lacl -lkrb.
+
+
+
+Principal Names
+
+Principal names have the form
+
+<name>[.<instance>][@<realm>]
+
+e.g.
+
+asp
+asp.root
+asp@ATHENA.MIT.EDU
+asp.@ATHENA.MIT.EDU
+asp.root@ATHENA.MIT.EDU
+
+It is possible for principals to be underspecified. If instance is
+missing, it is assumed to be "". If realm is missing, it is assumed
+to be local_realm. The canonical form contains all of name, instance,
+and realm; the acl_add and acl_delete routines will always
+leave the file in that form. Note that the canonical form of
+asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU.
+
+
+Routines
+
+acl_canonicalize_principal(principal, buf)
+char *principal;
+char *buf; /*RETVAL*/
+
+Store the canonical form of principal in buf. Buf must contain enough
+space to store a principal, given the limits on the sizes of name,
+instance, and realm specified in /usr/include/krb.h.
+
+acl_check(acl, principal)
+char *acl;
+char *principal;
+
+Returns nonzero if principal appears in acl. Returns 0 if principal
+does not appear in acl, or if an error occurs. Canonicalizes
+principal before checking, and allows the ACL to contain wildcards.
+
+acl_exact_match(acl, principal)
+char *acl;
+char *principal;
+
+Like acl_check, but does no canonicalization or wildcarding.
+
+acl_add(acl, principal)
+char *acl;
+char *principal;
+
+Atomically adds principal to acl. Returns 0 if successful, nonzero
+otherwise. It is considered a failure if principal is already in acl.
+This routine will canonicalize principal, but will treat wildcards
+literally.
+
+acl_delete(acl, principal)
+char *acl;
+char *principal;
+
+Atomically deletes principal from acl. Returns 0 if successful,
+nonzero otherwise. It is consider a failure if principal is not
+already in acl. This routine will canonicalize principal, but will
+treat wildcards literally.
+
+acl_initialize(acl, mode)
+char *acl;
+int mode;
+
+Initialize acl. If acl file does not exist, creates it with mode
+mode. If acl exists, removes all members. Returns 0 if successful,
+nonzero otherwise. WARNING: Mode argument is likely to change with
+the eventual introduction of an ACL service.
+
+
+Known problems
+
+In the presence of concurrency, there is a very small chance that
+acl_add or acl_delete could report success even though it would have
+had no effect. This is a necessary side effect of using lock files
+for concurrency control rather than flock(2), which is not supported
+by NFS.
+
+The current implementation caches ACLs in memory in a hash-table
+format for increased efficiency in checking membership; one effect of
+the caching scheme is that one file descriptor will be kept open for
+each ACL cached, up to a maximum of 8.
diff --git a/src/kadmin/v4server/attic/aclocal.m4 b/src/kadmin/v4server/attic/aclocal.m4
new file mode 100644
index 000000000..70bf66a7d
--- /dev/null
+++ b/src/kadmin/v4server/attic/aclocal.m4
@@ -0,0 +1,3 @@
+sinclude([./../../aclocal.m4])dnl
+undefine([AC_BUILDTOP])dnl
+define(AC_BUILDTOP,[./../..])dnl
diff --git a/src/kadmin/v4server/attic/admin_server.c b/src/kadmin/v4server/attic/admin_server.c
new file mode 100644
index 000000000..04155bca1
--- /dev/null
+++ b/src/kadmin/v4server/attic/admin_server.c
@@ -0,0 +1,668 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Top-level loop of the kerberos Administration server
+ */
+
+#include <mit-copyright.h>
+
+/*
+ admin_server.c
+ this holds the main loop and initialization and cleanup code for the server
+*/
+
+#ifdef _AIX
+#include <sys/select.h>
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <string.h>
+
+#ifndef POSIX_SIGNALS
+#ifndef sigmask
+#define sigmask(m) (1 <<((m)-1))
+#endif
+#endif /* POSIX_SIGNALS */
+#ifdef _AIX
+#include <sys/resource.h>
+#endif /* _AIX */
+#include <sys/wait.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include <krb5/krb5.h>
+#include <krb5/kdb.h>
+#include <krb5/kdb_dbm.h>
+#include <krb5/los-proto.h>
+#include <krb5/config.h>
+
+#ifdef OVSEC_KADM
+#include <ovsec_admin/admin.h>
+void *ovsec_handle;
+#endif
+
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.h>
+#include "kadm_server.h"
+
+/* Almost all procs and such need this, so it is global */
+admin_params prm; /* The command line parameters struct */
+
+char prog[32]; /* WHY IS THIS NEEDED??????? */
+char *progname = prog;
+char *acldir = DEFAULT_ACL_DIR;
+char krbrlm[REALM_SZ];
+extern Kadm_Server server_parm;
+int des_debug; /* used by the des425 libraries */
+int debug = 0;
+
+/*
+** Main does the logical thing, it sets up the database and RPC interface,
+** as well as handling the creation and maintenance of the syslog file...
+*/
+main(argc, argv) /* admin_server main routine */
+int argc;
+char *argv[];
+{
+ int errval;
+ int c;
+ char *db_name, *lrealm;
+ extern char *optarg;
+ extern int fascist_cpw;
+
+ krb5_init_ets();
+ initialize_kadm_error_table();
+ prog[sizeof(prog)-1]='\0'; /* Terminate... */
+ (void) strncpy(prog, argv[0], sizeof(prog)-1);
+
+ /* initialize the admin_params structure */
+ prm.sysfile = KADM_SYSLOG; /* default file name */
+ prm.inter = 1;
+
+ memset(krbrlm, 0, sizeof(krbrlm));
+
+ fascist_cpw = 1; /* by default, enable fascist mode */
+ while ((c = getopt(argc, argv, "f:hnd:Da:r:FN")) != EOF)
+ switch(c) {
+ case 'd':
+ if (errval = krb5_db_set_name(optarg)) {
+ com_err(argv[0], errval, "while setting dbname");
+ exit(1);
+ }
+ break;
+ case 'D':
+ debug++;
+ break;
+#ifndef OVSEC_KADM
+ case 'f': /* Syslog file name change */
+ prm.sysfile = optarg;
+ break;
+ case 'F':
+ fascist_cpw++;
+ break;
+ case 'N':
+ fascist_cpw = 0;
+ break;
+#endif
+ case 'n':
+ prm.inter = 0;
+ break;
+ case 'a': /* new acl directory */
+ acldir = optarg;
+ break;
+ case 'r':
+ (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1);
+ break;
+ case 'h': /* get help on using admin_server */
+ default:
+#ifdef OVSEC_KADM
+ fprintf(stderr, "Usage: ovsec_v4adm_server [-D] [-h] [-n] [-r realm] [-d dbname] [-a acldir]\n");
+
+#else
+ printf("Usage: admin_server [-D] [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n");
+#endif
+ exit(-1); /* failure */
+ }
+
+ if (krbrlm[0] == 0) {
+ if (errval = krb5_get_default_realm(&lrealm)) {
+ com_err(argv[0], errval, "while attempting to get local realm");
+ exit(1);
+ }
+ (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1);
+ }
+ printf("KADM Server %s initializing\n",KADM_VERSTR);
+ printf("Please do not use 'kill -9' to kill this job, use a\n");
+ printf("regular kill instead\n\n");
+
+#ifdef OVSEC_KADM
+ printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n",
+ debug ? "" : "parent ", getpid());
+#else
+ printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST");
+#endif
+
+ open_syslog(argv[0], "V4 admin server (parent) starting");
+
+ errval = krb5_db_init(); /* Open the Kerberos database */
+ if (errval) {
+ fprintf(stderr, "error: krb5_db_init() failed");
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+ if (errval = krb5_db_set_lockmode(TRUE)) {
+ com_err(argv[0], errval, "while setting db to nonblocking");
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+
+ /* set up the server_parm struct */
+ if ((errval = kadm_ser_init(prm.inter, krbrlm)) != KADM_SUCCESS) {
+ fprintf(stderr, "error initializing: %s\n", error_message(errval));
+ krb5_db_fini();
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+
+ /* detach from the terminal */
+ if (!debug) {
+ if (
+#ifdef KRB5B4
+ daemon(0, 0)
+#else
+ errval = krb5_detach_process()
+#endif
+ ) {
+#ifdef KRB5B4
+ errval = errno;
+#endif
+ fprintf(stderr, "error detaching from terminal: %s\n",
+ error_message(errval));
+ syslog(LOG_ERR, "error detaching from terminal: %s",
+ error_message(errval));
+ krb5_db_fini();
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+ open_syslog(argv[0], "V4 admin server (child) starting");
+ }
+
+ krb5_db_fini();
+
+ if (errval = kadm_listen()) {
+ fprintf(stderr, "error while listening for requests: %s\n",
+ error_message(errval));
+ syslog(LOG_ERR, "error while listening for requests: %s",
+ error_message(errval));
+ krb5_db_fini();
+ close_syslog();
+ byebye();
+ exit(1);
+ }
+
+ close_syslog();
+ byebye();
+ exit(0);
+} /* procedure main */
+
+
+/* open the system log file */
+open_syslog(whoami, message)
+ char *whoami, *message;
+{
+ static int opened = 0;
+
+ if (opened) {
+ closelog();
+ }
+ openlog(whoami, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */
+ syslog(LOG_INFO, message);
+ opened++;
+}
+
+/* close the system log file */
+close_syslog()
+{
+ syslog(LOG_INFO, "Shutting down V4 admin server");
+}
+
+byebye() /* say goodnight gracie */
+{
+ printf("Admin Server (kadm server) has completed operation.\n");
+}
+
+static clear_secrets()
+{
+ krb5_finish_key(&server_parm.master_encblock);
+ memset((char *)&server_parm.master_encblock, 0,
+ sizeof (server_parm.master_encblock));
+ memset((char *)server_parm.master_keyblock.contents, 0,
+ server_parm.master_keyblock.length);
+ server_parm.mkvno = 0L;
+ return;
+}
+
+static exit_now = 0;
+
+krb5_sigtype doexit()
+{
+ exit_now = 1;
+}
+
+unsigned pidarraysize = 0;
+int *pidarray = (int *)0;
+int unknown_child = 0;
+
+/*
+kadm_listen
+listen on the admin servers port for a request
+*/
+kadm_listen()
+{
+ extern int errno;
+ int found;
+ int admin_fd;
+ int peer_fd;
+ fd_set mask, readfds;
+ struct sockaddr_in peer;
+ int addrlen;
+ void process_client(), kill_children();
+ int pid;
+ krb5_sigtype do_child();
+
+ (void) signal(SIGINT, doexit);
+ (void) signal(SIGTERM, doexit);
+ (void) signal(SIGHUP, doexit);
+ (void) signal(SIGQUIT, doexit);
+ (void) signal(SIGPIPE, SIG_IGN); /* get errors on write() */
+ (void) signal(SIGALRM, doexit);
+ (void) signal(SIGCHLD, do_child);
+
+ if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ return KADM_NO_SOCK;
+ if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr,
+ sizeof(struct sockaddr_in)) < 0)
+ return KADM_NO_BIND;
+ (void) listen(admin_fd, 1);
+ FD_ZERO(&mask);
+ FD_SET(admin_fd, &mask);
+
+ for (;;) { /* loop nearly forever */
+ if (exit_now) {
+ clear_secrets();
+ kill_children();
+ return(0);
+ }
+ readfds = mask;
+ if ((found = select(admin_fd+1,&readfds,(fd_set *)0,
+ (fd_set *)0, (struct timeval *)0)) == 0)
+ continue; /* no things read */
+ if (found < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "select: %s", error_message(errno));
+ continue;
+ }
+ if (FD_ISSET(admin_fd, &readfds)) {
+ /* accept the conn */
+ addrlen = sizeof(peer);
+ if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer,
+ &addrlen)) < 0) {
+ syslog(LOG_ERR, "accept: %s", error_message(errno));
+ continue;
+ }
+
+ if (debug) {
+ process_client(peer_fd, &peer);
+ } else if (pid = fork()) {
+ /* parent */
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %s", error_message(errno));
+ (void) close(peer_fd);
+ continue;
+ }
+ /* fork succeeded: keep tabs on child */
+ (void) close(peer_fd);
+ if (unknown_child != pid) {
+ if (pidarray) {
+ pidarray = (int *)realloc((char *)pidarray,
+ (++pidarraysize * sizeof(int)));
+ pidarray[pidarraysize-1] = pid;
+ } else {
+ pidarray = (int *)malloc((pidarraysize = 1) *
+ sizeof(int));
+ pidarray[0] = pid;
+ }
+ } /* End if unknown_child != pid.*/
+ } else {
+ /* child */
+ (void) close(admin_fd);
+ process_client(peer_fd, &peer);
+ }
+ } else {
+ syslog(LOG_ERR, "something else woke me up!");
+ return(0);
+ }
+ }
+ /*NOTREACHED*/
+}
+
+void process_client(fd, who)
+ int fd;
+ struct sockaddr_in *who;
+{
+ u_char *dat;
+ int dat_len;
+ u_short dlen;
+ int retval;
+ int on = 1;
+ Principal service;
+ des_cblock skey;
+ int nentries = 1;
+ krb5_db_entry sprinc_entries;
+ krb5_boolean more;
+ krb5_keyblock cpw_skey;
+ int status;
+
+#ifdef OVSEC_KADM
+#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab"
+ char *service_name;
+
+ service_name = (char *) malloc(strlen(server_parm.sname) +
+ strlen(server_parm.sinst) +
+ strlen(server_parm.krbrlm) + 3);
+ if (service_name == NULL) {
+ syslog(LOG_ERR, "error: out of memory allocating service name");
+ }
+ sprintf(service_name, "%s/%s@%s", server_parm.sname,
+ server_parm.sinst, server_parm.krbrlm);
+
+ retval = ovsec_kadm_init_with_skey(service_name,
+ OVSEC_KADM_SRVTAB,
+ OVSEC_KADM_ADMIN_SERVICE, krbrlm,
+ OVSEC_KADM_STRUCT_VERSION,
+ OVSEC_KADM_API_VERSION_1,
+ &ovsec_handle);
+ if (retval) {
+ syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s",
+ error_message(retval));
+ cleanexit(1);
+ }
+ free(service_name);
+
+#endif
+
+#if !defined(NOENCRYPTION)
+ /* Must do it here, since this is after the fork() call. */
+ des_init_random_number_generator(server_parm.master_keyblock.contents);
+#endif
+
+ if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt keepalive: %d", errno);
+
+ server_parm.recv_addr = *who;
+
+ if (krb5_db_init()) { /* Open as client */
+ syslog(LOG_ERR, "can't open krb db");
+ cleanexit(1);
+ }
+
+ /* need to set service key to changepw.KRB_MASTER */
+
+ status = krb5_db_get_principal(server_parm.sprinc,
+ &sprinc_entries,
+ &nentries, &more);
+ /* ugh... clean this up later */
+ if (status == KRB5_KDB_DB_INUSE) {
+ /* db locked */
+ krb5_ui_4 retcode = KADM_DB_INUSE;
+ char *pdat;
+
+ dat_len = KADM_VERSIZE + sizeof(u_int);
+ dat = (u_char *) malloc((unsigned)dat_len);
+ pdat = (char *) dat;
+ /* This must be 32 bit integer due to the htonl */
+ retcode = htonl((krb5_ui_4) KADM_DB_INUSE);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4));
+ goto out;
+ } else if (!nentries) {
+ syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst);
+ cleanexit(2);
+ } else if (status) {
+ syslog(LOG_ERR, error_message(status));
+ cleanexit(2);
+ }
+
+ status = krb5_kdb_decrypt_key(&server_parm.master_encblock,
+ &sprinc_entries.key,
+ &cpw_skey);
+ if (status) {
+ syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status));
+ cleanexit(1);
+ }
+ /* if error, will show up when rd_req fails */
+ (void) krb_set_key((char *)cpw_skey.contents, 0);
+#ifdef KRB5_FREE_KEYBLOCK_CONTENTS_EXISTS
+ krb5_free_keyblock_contents(&cpw_skey);
+#else
+ memset((char*)cpw_skey.contents, 0, cpw_skey.length);
+ free(cpw_skey.contents);
+#endif
+
+ krb5_dbm_db_free_principal(&sprinc_entries, nentries);
+
+ while (1) {
+ if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) !=
+ sizeof(u_short)) {
+ if (retval < 0)
+ syslog(LOG_ERR, "dlen read: %s", error_message(errno));
+ else if (retval)
+ syslog(LOG_ERR, "short dlen read: %d", retval);
+ (void) close(fd);
+#ifdef OVSEC_KADM
+ (void) ovsec_kadm_destroy(ovsec_handle);
+#endif
+ if (debug)
+ return;
+ else
+ cleanexit(retval ? 3 : 0);
+ }
+ if (exit_now) {
+ cleanexit(0);
+ }
+ dat_len = (int) ntohs(dlen);
+ dat = (u_char *) malloc((unsigned)dat_len);
+ if (!dat) {
+ syslog(LOG_ERR, "malloc: No memory");
+ (void) close(fd);
+ cleanexit(4);
+ }
+ if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) {
+ if (retval < 0)
+ syslog(LOG_ERR, "data read: %s", error_message(errno));
+ else
+ syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval);
+ (void) close(fd);
+ cleanexit(5);
+ }
+ if (exit_now) {
+ cleanexit(0);
+ }
+ if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS)
+ syslog(LOG_ERR, "processing request: %s", error_message(retval));
+
+ /* kadm_ser_in did the processing and returned stuff in
+ dat & dat_len , return the appropriate data */
+
+ out:
+ dlen = (u_short) dat_len;
+
+ if (dat_len != (int)dlen) {
+ clear_secrets();
+ abort(); /* XXX */
+ }
+ dlen = htons(dlen);
+
+ if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) {
+ syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno));
+ (void) close(fd);
+ cleanexit(6);
+ }
+
+ if (krb_net_write(fd, (char *)dat, dat_len) < 0) {
+ syslog(LOG_ERR, "writing to client: %s", error_message(errno));
+ (void) close(fd);
+ cleanexit(7);
+ }
+ free((char *)dat);
+ }
+ /*NOTREACHED*/
+}
+
+krb5_sigtype do_child()
+{
+ /* SIGCHLD brings us here */
+ int pid;
+ register int i, j;
+
+#ifdef WAIT_USES_INT
+ int status;
+#else
+ union wait status;
+#endif
+
+ pid = wait(&status);
+
+ for (i = 0; i < pidarraysize; i++)
+ if (pidarray[i] == pid) {
+ /* found it */
+ for (j = i; j < pidarraysize-1; j++)
+ /* copy others down */
+ pidarray[j] = pidarray[j+1];
+ pidarraysize--;
+#ifdef WAIT_USES_INT
+ if (WIFEXITED(status) || WIFSIGNALED(status))
+ syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid,
+ WTERMSIG(status), WEXITSTATUS(status));
+
+#else
+ if (status.w_retcode || status.w_coredump || status.w_termsig)
+ syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d",
+ pid, status.w_termsig, status.w_coredump, status.w_retcode);
+
+#endif
+ goto done; /* use goto to avoid figuring out whether to
+ return a value */
+ }
+ unknown_child = pid;
+#ifdef WAIT_USES_INT
+ syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid,
+ WTERMSIG(status), WEXITSTATUS(status));
+#else
+ syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d",
+ pid, status.w_termsig, status.w_coredump, status.w_retcode);
+#endif
+
+ done:
+}
+
+cleanexit(val)
+{
+ krb5_db_fini();
+ clear_secrets();
+ exit(val);
+}
+
+void kill_children()
+{
+ register int i;
+#ifdef POSIX_SIGNALS
+ sigset_t oldmask, igmask;
+#else
+ int osigmask;
+#endif
+
+#ifdef POSIX_SIGNALS
+ sigemptyset(&igmask);
+ sigaddset(&igmask, SIGCHLD);
+ sigprocmask(SIG_BLOCK, &igmask, &oldmask);
+#else
+ osigmask = sigblock(sigmask(SIGCHLD));
+#endif
+
+ for (i = 0; i < pidarraysize; i++) {
+ kill(pidarray[i], SIGINT);
+ syslog(LOG_ERR, "killing child %d", pidarray[i]);
+ }
+#ifdef POSIX_SIGNALS
+ sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0);
+#else
+ sigsetmask(osigmask);
+#endif
+ return;
+}
+
+#ifdef OVSEC_KADM
+krb5_ui_4 convert_ovsec_to_kadm(val)
+ krb5_ui_4 val;
+{
+ switch (val) {
+ case OVSEC_KADM_AUTH_GET:
+ case OVSEC_KADM_AUTH_ADD:
+ case OVSEC_KADM_AUTH_MODIFY:
+ case OVSEC_KADM_AUTH_DELETE:
+ case OVSEC_KADM_AUTH_INSUFFICIENT:
+ return KADM_UNAUTH;
+ case OVSEC_KADM_BAD_DB:
+ return KADM_UK_RERROR;
+ case OVSEC_KADM_DUP:
+ case OVSEC_KADM_POLICY_REF:
+ return KADM_INUSE;
+ case OVSEC_KADM_RPC_ERROR:
+ return KADM_NO_CONN;
+ case OVSEC_KADM_NO_SRV:
+ return KADM_NO_HOST;
+ case OVSEC_KADM_UNK_PRINC:
+ case OVSEC_KADM_UNK_POLICY:
+ return KADM_NOENTRY;
+ case OVSEC_KADM_PASS_Q_TOOSHORT:
+ case OVSEC_KADM_PASS_Q_CLASS:
+ case OVSEC_KADM_PASS_Q_DICT:
+ case OVSEC_KADM_PASS_REUSE:
+ case OVSEC_KADM_PASS_TOOSOON:
+ case CHPASS_UTIL_PASSWORD_TOO_SOON:
+ return KADM_INSECURE_PW;
+ case OVSEC_KADM_BAD_PASSWORD:
+ return KADM_NO_CRED;
+ case OVSEC_KADM_PROTECT_PRINCIPAL:
+ return KADM_NO_OPCODE;
+ case OVSEC_KADM_NOT_INIT:
+ case OVSEC_KADM_BAD_HIST_KEY:
+ case OVSEC_KADM_BAD_MASK:
+ case OVSEC_KADM_BAD_CLASS:
+ case OVSEC_KADM_BAD_LENGTH:
+ case OVSEC_KADM_BAD_POLICY:
+ case OVSEC_KADM_BAD_PRINCIPAL:
+ case OVSEC_KADM_BAD_AUX_ATTR:
+ case OVSEC_KADM_BAD_HISTORY:
+ case OVSEC_KADM_BAD_MIN_PASS_LIFE:
+ return -1;
+ }
+ return val;
+}
+#endif
diff --git a/src/kadmin/v4server/attic/configure.in b/src/kadmin/v4server/attic/configure.in
new file mode 100644
index 000000000..f09ba3a28
--- /dev/null
+++ b/src/kadmin/v4server/attic/configure.in
@@ -0,0 +1,22 @@
+AC_INIT(admin_server.c)
+WITH_CCOPTS
+CONFIG_RULES
+AC_SET_BUILDTOP
+AC_PROG_INSTALL
+AC_HAVE_LIBRARY(socket)
+AC_HAVE_LIBRARY(nsl)
+AC_HAVE_LIBRARY(-lndbm)
+AC_HAVE_LIBRARY(-ldbm)
+CHECK_WAIT_TYPE
+CHECK_FCNTL
+AC_FUNC_CHECK(sigprocmask,
+AC_COMPILE_CHECK([sigset_t],
+[#include <signal.h>],
+[sigset_t x],
+AC_DEFINE(POSIX_SIGNALS)))
+AC_PROG_AWK
+KRB_INCLUDE
+ISODE_INCLUDE
+WITH_KRB4
+WITH_KRB5ROOT
+AC_OUTPUT(Makefile,[EXTRA_RULES])
diff --git a/src/kadmin/v4server/attic/kadm_err.et b/src/kadmin/v4server/attic/kadm_err.et
new file mode 100644
index 000000000..a19273083
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_err.et
@@ -0,0 +1,57 @@
+# kadmin.v4/server/kadm_err.et
+#
+# Copyright 1988 by the Massachusetts Institute of Technology.
+#
+# For copying and distribution information, please see the file
+# <mit-copyright.h>.
+#
+# Kerberos administration server error table
+#
+ et kadm
+
+# KADM_SUCCESS, as all success codes should be, is zero
+
+ec KADM_RCSID, "$Header$"
+# /* Building and unbuilding the packet errors */
+ec KADM_NO_REALM, "Cannot fetch local realm"
+ec KADM_NO_CRED, "Unable to fetch credentials"
+ec KADM_BAD_KEY, "Bad key supplied"
+ec KADM_NO_ENCRYPT, "Can't encrypt data"
+ec KADM_NO_AUTH, "Cannot encode/decode authentication info"
+ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm"
+ec KADM_NO_ROOM, "Packet is too large"
+ec KADM_BAD_VER, "Version number is incorrect"
+ec KADM_BAD_CHK, "Checksum does not match"
+ec KADM_NO_READ, "Unsealing private data failed"
+ec KADM_NO_OPCODE, "Unsupported operation"
+ec KADM_NO_HOST, "Could not find administrating host"
+ec KADM_UNK_HOST, "Administrating host name is unknown"
+ec KADM_NO_SERV, "Could not find service name in services database"
+ec KADM_NO_SOCK, "Could not create socket"
+ec KADM_NO_CONN, "Could not connect to server"
+ec KADM_NO_HERE, "Could not fetch local socket address"
+ec KADM_NO_MAST, "Could not fetch master key"
+ec KADM_NO_VERI, "Could not verify master key"
+
+# /* From the server side routines */
+ec KADM_INUSE, "Entry already exists in database"
+ec KADM_UK_SERROR, "Database store error"
+ec KADM_UK_RERROR, "Database read error"
+ec KADM_UNAUTH, "Insufficient access to perform requested operation"
+# KADM_DATA isn't really an error, but...
+ec KADM_DATA, "Data is available for return to client"
+ec KADM_NOENTRY, "No such entry in the database"
+
+ec KADM_NOMEM, "Memory exhausted"
+ec KADM_NO_HOSTNAME, "Could not fetch system hostname"
+ec KADM_NO_BIND, "Could not bind port"
+ec KADM_LENGTH_ERROR, "Length mismatch problem"
+ec KADM_ILL_WILDCARD, "Illegal use of wildcard"
+
+ec KADM_DB_INUSE, "Database locked or in use"
+
+ec KADM_INSECURE_PW, "Insecure password rejected"
+ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match"
+
+ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request"
+end
diff --git a/src/kadmin/v4server/attic/kadm_funcs.c b/src/kadmin/v4server/attic/kadm_funcs.c
new file mode 100644
index 000000000..7ce9c7b4f
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_funcs.c
@@ -0,0 +1,876 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side database manipulation routines
+ */
+
+#ifndef lint
+static char rcsid_kadm_funcs_c[] =
+"$Id$";
+#endif lint
+
+#include <mit-copyright.h>
+/*
+kadm_funcs.c
+the actual database manipulation code
+*/
+
+#include <stdio.h>
+#include <sys/param.h>
+#include <ndbm.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.h>
+#include <syslog.h>
+#ifdef NEED_SYS_FCNTL_H
+#include <sys/fcntl.h>
+#endif
+
+#include "kadm_server.h"
+
+extern Kadm_Server server_parm;
+
+krb5_error_code
+kadm_entry2princ(entry, princ)
+ krb5_db_entry entry;
+ Principal *princ;
+{
+ char realm[REALM_SZ]; /* dummy values only */
+ krb5_error_code retval;
+ time_t lcltim;
+
+ /* NOTE: does not convert the key */
+ memset(princ, 0, sizeof (*princ));
+ retval = krb5_524_conv_principal(entry.principal,
+ princ->name, princ->instance, realm);
+ if (retval)
+ return retval;
+ princ->exp_date = entry.expiration;
+ lcltim = entry.expiration;
+ strncpy(princ->exp_date_txt, ctime(&lcltim),
+ DATE_SZ);
+ lcltim = princ->mod_date = entry.mod_date;
+ strncpy(princ->mod_date_txt, ctime(&lcltim),
+ DATE_SZ);
+ princ->attributes = entry.attributes;
+ princ->max_life = entry.max_life;
+ princ->kdc_key_ver = entry.mkvno;
+ princ->key_version = entry.kvno;
+ retval = krb5_524_conv_principal(entry.mod_name,
+ princ->mod_name, princ->mod_instance,
+ realm);
+ if (retval)
+ return retval;
+ return 0;
+}
+
+krb5_error_code
+kadm_princ2entry(princ, entry)
+ Principal princ;
+ krb5_db_entry *entry;
+{
+ krb5_error_code retval;
+
+ /* NOTE: does not convert the key */
+ memset(entry, 0, sizeof (*entry));
+ /* yeah yeah stupid v4 database doesn't store realm names */
+ retval = krb5_425_conv_principal(princ.name, princ.instance,
+ server_parm.krbrlm, &entry->principal);
+ if (retval)
+ return retval;
+ entry->kvno = princ.key_version;
+ entry->max_life = princ.max_life;
+ entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */
+ entry->mkvno = server_parm.mkvno; /* XXX */
+ entry->expiration = princ.exp_date;
+ retval = krb5_425_conv_principal(princ.mod_name, princ.mod_instance,
+ server_parm.krbrlm, &entry->mod_name);
+ if (retval)
+ return retval;
+ entry->mod_date = princ.mod_date;
+ entry->attributes = princ.attributes;
+ entry->salt_type = KRB5_KDB_SALTTYPE_V4;
+}
+
+check_access(pname, pinst, prealm, acltype)
+char *pname;
+char *pinst;
+char *prealm;
+enum acl_types acltype;
+{
+ char checkname[MAX_K_NAME_SZ];
+ char filename[MAXPATHLEN];
+ extern char *acldir;
+
+ (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm);
+
+ switch (acltype) {
+ case ADDACL:
+ (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE);
+ break;
+ case GETACL:
+ (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE);
+ break;
+ case MODACL:
+ (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE);
+ break;
+ case STABACL:
+ (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE);
+ break;
+ }
+ return(acl_check(filename, checkname));
+}
+
+int
+wildcard(str)
+char *str;
+{
+ if (!strcmp(str, WILDCARD_STR))
+ return(1);
+ return(0);
+}
+
+#define failadd(code) { (void) syslog(LOG_ERR, "FAILED addding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_add_entry (rname, rinstance, rrealm, valsin, valsout)
+char *rname; /* requestors name */
+char *rinstance; /* requestors instance */
+char *rrealm; /* requestors realm */
+Kadm_vals *valsin;
+Kadm_vals *valsout;
+{
+ Principal data_i, data_o; /* temporary principal */
+ u_char flags[4];
+ krb5_principal default_princ;
+ krb5_error_code retval;
+ krb5_db_entry newentry, tmpentry;
+ krb5_boolean more;
+ krb5_keyblock newpw;
+ krb5_encrypted_keyblock encpw;
+ int numfound;
+
+ if (!check_access(rname, rinstance, rrealm, ADDACL)) {
+ syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'",
+ rname, rinstance, rrealm, valsin->name, valsin->instance);
+ return KADM_UNAUTH;
+ }
+
+ /* Need to check here for "legal" name and instance */
+ if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+ failadd(KADM_ILL_WILDCARD);
+ }
+
+ syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'",
+ valsin->name, valsin->instance, rname, rinstance, rrealm);
+
+ kadm_vals_to_prin(valsin->fields, &data_i, valsin);
+ (void) strncpy(data_i.name, valsin->name, ANAME_SZ);
+ (void) strncpy(data_i.instance, valsin->instance, INST_SZ);
+
+ if (!IS_FIELD(KADM_EXPDATE,valsin->fields))
+ data_i.exp_date = server_parm.expiration;
+ if (!IS_FIELD(KADM_ATTR,valsin->fields))
+ data_i.attributes = server_parm.flags;
+ if (!IS_FIELD(KADM_MAXLIFE,valsin->fields))
+ data_i.max_life = server_parm.max_life;
+
+ if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL)
+ failadd(KADM_NOMEM);
+ data_i.key_low = ntohl(data_i.key_low);
+ data_i.key_high = ntohl(data_i.key_high);
+ memcpy(newpw.contents, &data_i.key_low, 4);
+ memcpy((char *)(((krb4_int32 *) newpw.contents) + 1), &data_i.key_high, 4);
+ newpw.length = 8;
+ newpw.keytype = KEYTYPE_DES;
+ /* encrypt new key in master key */
+ retval = krb5_kdb_encrypt_key(&server_parm.master_encblock,
+ &newpw, &encpw);
+ memset((char *)newpw.contents, 0, newpw.length);
+ free(newpw.contents);
+ if (retval) {
+ failadd(retval);
+ }
+ data_o = data_i;
+
+ retval = kadm_princ2entry(data_i, &newentry);
+ if (retval) {
+ memset((char *)encpw.contents, 0, encpw.length);
+ free(encpw.contents);
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+
+ newentry.key = encpw;
+ numfound = 1;
+ retval = krb5_db_get_principal(newentry.principal,
+ &tmpentry, &numfound, &more);
+
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+ krb5_db_free_principal(&tmpentry, numfound);
+ if (numfound) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(KADM_INUSE);
+ } else {
+ newentry.kvno = ++data_i.key_version;
+ if (retval = krb5_timeofday(&newentry.mod_date)) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+ if (newentry.mod_name)
+ krb5_free_principal(newentry.mod_name);
+ newentry.mod_name = NULL; /* in case the following breaks */
+ retval = krb5_425_conv_principal(rname, rinstance, rrealm,
+ &newentry.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+
+ numfound = 1;
+ retval = krb5_db_put_principal(&newentry, &numfound);
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(retval);
+ }
+ if (!numfound) {
+ krb5_db_free_principal(&newentry, 1);
+ failadd(KADM_UK_SERROR);
+ } else {
+ numfound = 1;
+ retval = krb5_db_get_principal(newentry.principal,
+ &tmpentry,
+ &numfound, &more);
+ krb5_db_free_principal(&newentry, 1);
+ if (retval) {
+ failadd(retval);
+ } else if (numfound != 1 || more) {
+ krb5_db_free_principal(&tmpentry, numfound);
+ failadd(KADM_UK_RERROR);
+ }
+ kadm_entry2princ(tmpentry, &data_o);
+ krb5_db_free_principal(&tmpentry, numfound);
+ memset((char *)flags, 0, sizeof(flags));
+ SET_FIELD(KADM_NAME,flags);
+ SET_FIELD(KADM_INST,flags);
+ SET_FIELD(KADM_EXPDATE,flags);
+ SET_FIELD(KADM_ATTR,flags);
+ SET_FIELD(KADM_MAXLIFE,flags);
+ kadm_prin_to_vals(flags, valsout, &data_o);
+ syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance);
+ return KADM_DATA; /* Set all the appropriate fields */
+ }
+ }
+}
+#undef failadd
+
+#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout)
+char *rname; /* requestors name */
+char *rinstance; /* requestors instance */
+char *rrealm; /* requestors realm */
+Kadm_vals *valsin; /* what they wannt to get */
+u_char *flags; /* which fields we want */
+Kadm_vals *valsout; /* what data is there */
+{
+ int numfound; /* check how many were returned */
+ krb5_boolean more; /* To point to more name.instances */
+ Principal data_o; /* Data object to hold Principal */
+ krb5_principal inprinc;
+ krb5_db_entry entry;
+ krb5_error_code retval;
+
+ if (!check_access(rname, rinstance, rrealm, GETACL)) {
+ syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry",
+ rname, rinstance, rrealm, valsin->name, valsin->instance);
+ return KADM_UNAUTH;
+ }
+
+ if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+ failget(KADM_ILL_WILDCARD);
+ }
+
+ syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'",
+ valsin->name, valsin->instance, rname, rinstance, rrealm);
+
+ retval = krb5_425_conv_principal(valsin->name, valsin->instance,
+ server_parm.krbrlm, &inprinc);
+ if (retval)
+ failget(retval);
+ /* Look up the record in the database */
+ numfound = 1;
+ retval = krb5_db_get_principal(inprinc, &entry, &numfound, &more);
+ krb5_free_principal(inprinc);
+ if (retval) {
+ failget(retval);
+ } else if (!numfound || more) {
+ failget(KADM_NOENTRY);
+ }
+ retval = kadm_entry2princ(entry, &data_o);
+ krb5_db_free_principal(&entry, 1);
+ if (retval) {
+ failget(retval);
+ }
+ kadm_prin_to_vals(flags, valsout, &data_o);
+ syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance);
+ return KADM_DATA; /* Set all the appropriate fields */
+}
+#undef failget
+
+#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; }
+
+kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout)
+char *rname; /* requestors name */
+char *rinstance; /* requestors instance */
+char *rrealm; /* requestors realm */
+Kadm_vals *valsin1, *valsin2; /* holds the parameters being
+ passed in */
+Kadm_vals *valsout; /* the actual record which is returned */
+{
+ int numfound;
+ krb5_boolean more;
+ Principal data_o, temp_key;
+ u_char fields[4];
+ krb5_keyblock newpw;
+ krb5_encrypted_keyblock encpw;
+ krb5_error_code retval;
+ krb5_principal theprinc, rprinc;
+ krb5_db_entry newentry, odata;
+
+ if (wildcard(valsin1->name) || wildcard(valsin1->instance)) {
+ failmod(KADM_ILL_WILDCARD);
+ }
+
+ if (!check_access(rname, rinstance, rrealm, MODACL)) {
+ syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry",
+ rname, rinstance, rrealm, valsin1->name, valsin1->instance);
+ return KADM_UNAUTH;
+ }
+
+ syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ",
+ valsin1->name, valsin1->instance, rname, rinstance, rrealm);
+ krb5_425_conv_principal(valsin1->name, valsin1->instance,
+ server_parm.krbrlm, &theprinc);
+ if (retval)
+ failmod(retval);
+ numfound = 1;
+ retval = krb5_db_get_principal(theprinc, &newentry, &numfound, &more);
+ if (retval) {
+ krb5_free_principal(theprinc);
+ failmod(retval);
+ } else if (numfound == 1) {
+ kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2);
+ krb5_free_principal(newentry.principal);
+ newentry.principal = theprinc;
+ if (IS_FIELD(KADM_EXPDATE,valsin2->fields))
+ newentry.expiration = temp_key.exp_date;
+ if (IS_FIELD(KADM_ATTR,valsin2->fields))
+ newentry.attributes = temp_key.attributes;
+ if (IS_FIELD(KADM_MAXLIFE,valsin2->fields))
+ newentry.max_life = temp_key.max_life;
+ if (IS_FIELD(KADM_DESKEY,valsin2->fields)) {
+ newentry.kvno++;
+ newentry.mkvno = server_parm.mkvno;
+ if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) {
+ krb5_db_free_principal(&newentry, 1);
+ memset((char *)&temp_key, 0, sizeof (temp_key));
+ failmod(KADM_NOMEM);
+ }
+ newpw.length = 8;
+ newpw.keytype = KEYTYPE_DES;
+ temp_key.key_low = ntohl(temp_key.key_low);
+ temp_key.key_high = ntohl(temp_key.key_high);
+ memcpy(newpw.contents, &temp_key.key_low, 4);
+ memcpy(newpw.contents + 4, &temp_key.key_high, 4);
+ /* encrypt new key in master key */
+ retval = krb5_kdb_encrypt_key(&server_parm.master_encblock,
+ &newpw, &encpw);
+ memset(newpw.contents, 0, newpw.length);
+ free(newpw.contents);
+ memset((char *)&temp_key, 0, sizeof(temp_key));
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failmod(retval);
+ }
+ if (newentry.key.contents) {
+ memset((char *)newentry.key.contents, 0, newentry.key.length);
+ free(newentry.key.contents);
+ }
+ newentry.key = encpw;
+ }
+ if (retval = krb5_timeofday(&newentry.mod_date)) {
+ krb5_db_free_principal(&newentry, 1);
+ failmod(retval);
+ }
+ retval = krb5_425_conv_principal(rname, rinstance, rrealm,
+ &newentry.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failmod(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_put_principal(&newentry, &numfound);
+ memset((char *)&data_o, 0, sizeof(data_o));
+ if (retval) {
+ krb5_db_free_principal(&newentry, 1);
+ failmod(retval);
+ } else {
+ numfound = 1;
+ retval = krb5_db_get_principal(newentry.principal, &odata,
+ &numfound, &more);
+ krb5_db_free_principal(&newentry, 1);
+ if (retval) {
+ failmod(retval);
+ } else if (numfound != 1 || more) {
+ krb5_db_free_principal(&odata, numfound);
+ failmod(KADM_UK_RERROR);
+ }
+ retval = kadm_entry2princ(odata, &data_o);
+ krb5_db_free_principal(&odata, 1);
+ if (retval)
+ failmod(retval);
+ memset((char *) fields, 0, sizeof(fields));
+ SET_FIELD(KADM_NAME,fields);
+ SET_FIELD(KADM_INST,fields);
+ SET_FIELD(KADM_EXPDATE,fields);
+ SET_FIELD(KADM_ATTR,fields);
+ SET_FIELD(KADM_MAXLIFE,fields);
+ kadm_prin_to_vals(fields, valsout, &data_o);
+ syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance);
+ return KADM_DATA; /* Set all the appropriate fields */
+ }
+ } else {
+ failmod(KADM_NOENTRY);
+ }
+}
+#undef failmod
+
+#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; }
+
+kadm_change (rname, rinstance, rrealm, newpw)
+char *rname;
+char *rinstance;
+char *rrealm;
+des_cblock newpw;
+{
+ int numfound;
+ krb5_boolean more;
+ krb5_principal rprinc;
+ krb5_error_code retval;
+ krb5_keyblock localpw;
+ krb5_encrypted_keyblock encpw;
+ krb5_db_entry odata;
+
+ if (strcmp(server_parm.krbrlm, rrealm)) {
+ syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n",
+ rname, rinstance, rrealm);
+ return(KADM_WRONG_REALM);
+ }
+
+ if (wildcard(rname) || wildcard(rinstance)) {
+ failchange(KADM_ILL_WILDCARD);
+ }
+ syslog(LOG_INFO, "'%s.%s@%s' wants to change its password",
+ rname, rinstance, rrealm);
+ retval = krb5_425_conv_principal(rname, rinstance,
+ server_parm.krbrlm, &rprinc);
+ if (retval)
+ failchange(retval);
+ if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL)
+ failchange(KADM_NOMEM);
+ memcpy(localpw.contents, newpw, 8);
+ localpw.keytype = KEYTYPE_DES;
+ localpw.length = 8;
+ /* encrypt new key in master key */
+ retval = krb5_kdb_encrypt_key(&server_parm.master_encblock,
+ &localpw, &encpw);
+ memset((char *)localpw.contents, 0, 8);
+ free(localpw.contents);
+ if (retval) {
+ krb5_free_principal(rprinc);
+ failchange(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_get_principal(rprinc, &odata, &numfound, &more);
+ krb5_free_principal(rprinc);
+ if (retval) {
+ failchange(retval);
+ } else if (numfound == 1) {
+ odata.key = encpw;
+ odata.kvno++;
+ odata.mkvno = server_parm.mkvno;
+ if (retval = krb5_timeofday(&odata.mod_date)) {
+ krb5_db_free_principal(&odata, 1);
+ failchange(retval);
+ }
+ krb5_425_conv_principal(rname, rinstance,
+ server_parm.krbrlm, &odata.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&odata, 1);
+ failchange(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_put_principal(&odata, &numfound);
+ krb5_db_free_principal(&odata, 1);
+ if (retval) {
+ failchange(retval);
+ } else if (more) {
+ failchange(KADM_UK_SERROR);
+ } else {
+ syslog(LOG_INFO,
+ "'%s.%s@%s' password changed.", rname, rinstance, rrealm);
+ return KADM_SUCCESS;
+ }
+ }
+ else {
+ failchange(KADM_NOENTRY);
+ }
+}
+#undef failchange
+
+check_pw(newpw, checkstr)
+ des_cblock newpw;
+ char *checkstr;
+{
+#ifdef NOENCRYPTION
+ return 0;
+#else /* !NOENCRYPTION */
+ des_cblock checkdes;
+
+ (void) des_string_to_key(checkstr, checkdes);
+ return(!memcmp(checkdes, newpw, sizeof(des_cblock)));
+#endif /* NOENCRYPTION */
+}
+
+char *reverse(str)
+ char *str;
+{
+ static char newstr[80];
+ char *p, *q;
+ int i;
+
+ i = strlen(str);
+ if (i >= sizeof(newstr))
+ i = sizeof(newstr)-1;
+ p = str+i-1;
+ q = newstr;
+ q[i]='\0';
+ for(; i > 0; i--)
+ *q++ = *p--;
+
+ return(newstr);
+}
+
+int lower(str)
+ char *str;
+{
+ register char *cp;
+ int effect=0;
+
+ for (cp = str; *cp; cp++) {
+ if (isupper(*cp)) {
+ *cp = tolower(*cp);
+ effect++;
+ }
+ }
+ return(effect);
+}
+
+des_check_gecos(gecos, newpw)
+ char *gecos;
+ des_cblock newpw;
+{
+ char *cp, *ncp, *tcp;
+
+ for (cp = gecos; *cp; ) {
+ /* Skip past punctuation */
+ for (; *cp; cp++)
+ if (isalnum(*cp))
+ break;
+ /* Skip to the end of the word */
+ for (ncp = cp; *ncp; ncp++)
+ if (!isalnum(*ncp) && *ncp != '\'')
+ break;
+ /* Delimit end of word */
+ if (*ncp)
+ *ncp++ = '\0';
+ /* Check word to see if it's the password */
+ if (*cp) {
+ if (check_pw(newpw, cp))
+ return(KADM_INSECURE_PW);
+ tcp = reverse(cp);
+ if (check_pw(newpw, tcp))
+ return(KADM_INSECURE_PW);
+ if (lower(cp)) {
+ if (check_pw(newpw, cp))
+ return(KADM_INSECURE_PW);
+ tcp = reverse(cp);
+ if (check_pw(newpw, tcp))
+ return(KADM_INSECURE_PW);
+ }
+ cp = ncp;
+ } else
+ break;
+ }
+ return(0);
+}
+
+str_check_gecos(gecos, pwstr)
+ char *gecos;
+ char *pwstr;
+{
+ char *cp, *ncp, *tcp;
+
+ for (cp = gecos; *cp; ) {
+ /* Skip past punctuation */
+ for (; *cp; cp++)
+ if (isalnum(*cp))
+ break;
+ /* Skip to the end of the word */
+ for (ncp = cp; *ncp; ncp++)
+ if (!isalnum(*ncp) && *ncp != '\'')
+ break;
+ /* Delimit end of word */
+ if (*ncp)
+ *ncp++ = '\0';
+ /* Check word to see if it's the password */
+ if (*cp) {
+ if (!strcasecmp(pwstr, cp))
+ return(KADM_INSECURE_PW);
+ tcp = reverse(cp);
+ if (!strcasecmp(pwstr, tcp))
+ return(KADM_INSECURE_PW);
+ cp = ncp;
+ } else
+ break;
+ }
+ return(0);
+}
+
+
+kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring)
+char *rname;
+char *rinstance;
+char *rrealm;
+des_cblock newpw;
+char *pwstring;
+{
+ static DBM *pwfile = NULL;
+ int retval;
+ datum passwd, entry;
+ struct passwd *ent;
+#ifdef HESIOD
+ extern struct passwd *hes_getpwnam();
+#endif
+
+ if (pwstring && !check_pw(newpw, pwstring))
+ /*
+ * Someone's trying to toy with us....
+ */
+ return(KADM_PW_MISMATCH);
+ if (pwstring && (strlen(pwstring) < 5))
+ return(KADM_INSECURE_PW);
+ if (!pwfile) {
+ pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644);
+ }
+ if (pwfile) {
+ passwd.dptr = (char *) newpw;
+ passwd.dsize = 8;
+ entry = dbm_fetch(pwfile, passwd);
+ if (entry.dptr)
+ return(KADM_INSECURE_PW);
+ }
+ if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname)))
+ return(KADM_INSECURE_PW);
+#ifdef HESIOD
+ ent = hes_getpwnam(rname);
+#else
+ ent = getpwnam(rname);
+#endif
+ if (ent && ent->pw_gecos) {
+ if (pwstring)
+ retval = str_check_gecos(ent->pw_gecos, pwstring);
+ else
+ retval = des_check_gecos(ent->pw_gecos, newpw);
+ if (retval)
+ return(retval);
+ }
+ return(0);
+}
+
+/*
+ * This routine checks to see if a principal should be considered an
+ * allowable service name which can be changed by kadm_change_srvtab.
+ *
+ * We do this check by using the ACL library. This makes the
+ * (relatively) reasonable assumption that both the name and the
+ * instance will not contain '.' or '@'.
+ */
+kadm_check_srvtab(name, instance)
+ char *name;
+ char *instance;
+{
+ FILE *f;
+ char filename[MAXPATHLEN];
+ char buf[ANAME_SZ], *cp;
+ extern char *acldir;
+
+ (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE);
+ if (!acl_check(filename, name))
+ return(KADM_NOT_SERV_PRINC);
+
+ (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE);
+ if (acl_check(filename, instance))
+ return(KADM_NOT_SERV_PRINC);
+ return 0;
+}
+
+/*
+ * Routine to allow some people to change the key of a srvtab
+ * principal to a random key, which the admin server will return to
+ * the client.
+ */
+#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; }
+
+kadm_chg_srvtab(rname, rinstance, rrealm, values)
+ char *rname; /* requestors name */
+ char *rinstance; /* requestors instance */
+ char *rrealm; /* requestors realm */
+ Kadm_vals *values;
+{
+ int numfound, ret, isnew = 0;
+ des_cblock new_key;
+ Principal principal;
+ krb5_principal inprinc;
+ krb5_error_code retval;
+ krb5_db_entry odata;
+ krb5_boolean more;
+ krb5_keyblock newpw;
+ krb5_encrypted_keyblock encpw;
+
+ if (!check_access(rname, rinstance, rrealm, STABACL))
+ failsrvtab(KADM_UNAUTH);
+ if (wildcard(rname) || wildcard(rinstance))
+ failsrvtab(KADM_ILL_WILDCARD);
+ if (ret = kadm_check_srvtab(values->name, values->instance))
+ failsrvtab(ret);
+
+ retval = krb5_425_conv_principal(values->name, values->instance,
+ server_parm.krbrlm, &inprinc);
+ if (retval)
+ failsrvtab(retval);
+ /*
+ * OK, get the entry
+ */
+ numfound = 1;
+ retval = krb5_db_get_principal(inprinc, &odata, &numfound, &more);
+ if (retval) {
+ krb5_free_principal(inprinc);
+ failsrvtab(retval);
+ } else if (numfound) {
+ odata.kvno++;
+ } else {
+ /*
+ * This is a new srvtab entry that we're creating
+ */
+ isnew = 1;
+ memset((char *)&odata, 0, sizeof (odata));
+ odata.principal = inprinc;
+ odata.kvno = 1;
+ odata.max_life = server_parm.max_life;
+ odata.max_renewable_life = server_parm.max_rlife;
+ odata.mkvno = server_parm.mkvno;
+ odata.expiration = server_parm.expiration;
+ odata.attributes = 0;
+ }
+
+#ifdef NOENCRYPTION
+ memset(new_key, 0, sizeof(new_key));
+ new_key[0] = 127;
+#else
+ des_new_random_key(new_key);
+#endif
+ /*
+ * Store the new key in the return structure; also fill in the
+ * rest of the fields.
+ */
+ if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) {
+ krb5_db_free_principal(&odata, 1);
+ failsrvtab(KADM_NOMEM);
+ }
+ newpw.keytype = KEYTYPE_DES;
+ newpw.length = 8;
+ memcpy((char *)newpw.contents, new_key, 8);
+ memset((char *)new_key, 0, sizeof (new_key));
+ memcpy((char *)&values->key_low, newpw.contents, 4);
+ memcpy((char *)&values->key_high, newpw.contents + 4, 4);
+ values->key_low = htonl(values->key_low);
+ values->key_high = htonl(values->key_high);
+ values->max_life = odata.kvno;
+ values->exp_date = odata.expiration;
+ values->attributes = odata.attributes;
+ memset(values->fields, 0, sizeof(values->fields));
+ SET_FIELD(KADM_NAME, values->fields);
+ SET_FIELD(KADM_INST, values->fields);
+ SET_FIELD(KADM_EXPDATE, values->fields);
+ SET_FIELD(KADM_ATTR, values->fields);
+ SET_FIELD(KADM_MAXLIFE, values->fields);
+ SET_FIELD(KADM_DESKEY, values->fields);
+
+ /*
+ * Encrypt the new key with the master key, and then update
+ * the database record
+ */
+ retval = krb5_kdb_encrypt_key(&server_parm.master_encblock,
+ &newpw, &encpw);
+ memset((char *)newpw.contents, 0, 8);
+ free(newpw.contents);
+ if (odata.key.contents) {
+ memset((char *)odata.key.contents, 0, odata.key.length);
+ free(odata.key.contents);
+ }
+ odata.key = encpw;
+ if (retval) {
+ krb5_db_free_principal(&odata, 1);
+ failsrvtab(retval);
+ }
+ if (retval = krb5_timeofday(&odata.mod_date)) {
+ krb5_db_free_principal(&odata, 1);
+ failsrvtab(retval);
+ }
+ retval = krb5_425_conv_principal(rname, rinstance,
+ server_parm.krbrlm, &odata.mod_name);
+ if (retval) {
+ krb5_db_free_principal(&odata, 1);
+ failsrvtab(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_put_principal(&odata, &numfound);
+ krb5_db_free_principal(&odata, 1);
+ if (retval) {
+ failsrvtab(retval);
+ }
+ else if (!numfound) {
+ failsrvtab(KADM_UK_SERROR);
+ } else {
+ syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.",
+ values->name, values->instance,
+ numfound ? "changed" : "created",
+ rname, rinstance, rrealm);
+ return KADM_DATA;
+ }
+}
+
+#undef failsrvtab
diff --git a/src/kadmin/v4server/attic/kadm_ser_wrap.c b/src/kadmin/v4server/attic/kadm_ser_wrap.c
new file mode 100644
index 000000000..bca8b8e05
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_ser_wrap.c
@@ -0,0 +1,288 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side support functions
+ */
+
+#ifndef lint
+static char rcsid_module_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+/*
+kadm_ser_wrap.c
+unwraps wrapped packets and calls the appropriate server subroutine
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <krb.h>
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_err.h>
+#include <syslog.h>
+#include "kadm_server.h"
+
+#ifdef OVSEC_KADM
+#include <ovsec_admin/admin.h>
+extern void *ovsec_handle;
+#endif
+
+Kadm_Server server_parm;
+
+/*
+kadm_ser_init
+set up the server_parm structure
+*/
+kadm_ser_init(inter, realm)
+ int inter; /* interactive or from file */
+ char realm[];
+{
+ struct servent *sep;
+ struct hostent *hp;
+ char hostname[MAXHOSTNAMELEN];
+ char *mkey_name;
+ krb5_error_code retval;
+ int numfound = 1;
+ krb5_boolean more;
+ krb5_db_entry master_entry;
+
+ if (gethostname(hostname, sizeof(hostname)))
+ return KADM_NO_HOSTNAME;
+
+ (void) strcpy(server_parm.sname, PWSERV_NAME);
+ (void) strcpy(server_parm.sinst, KRB_MASTER);
+ (void) strcpy(server_parm.krbrlm, realm);
+ if (krb5_build_principal(&server_parm.sprinc,
+ strlen(realm),
+ realm,
+ PWSERV_NAME,
+ KRB_MASTER, 0))
+ return KADM_NO_MAST;
+
+ /* setting up the addrs */
+ server_parm.admin_fd = -1;
+ if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL)
+ return KADM_NO_SERV;
+ memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr));
+ server_parm.admin_addr.sin_family = AF_INET;
+ if ((hp = gethostbyname(hostname)) == NULL)
+ return KADM_NO_HOSTNAME;
+ memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr,
+ hp->h_length);
+ server_parm.admin_addr.sin_port = sep->s_port;
+
+ /* setting up the database */
+ mkey_name = KRB5_KDB_M_NAME;
+ server_parm.master_keyblock.keytype = KEYTYPE_DES;
+#ifdef PROVIDE_DES_CBC_CRC
+#ifdef KRB5B4
+ server_parm.master_encblock.crypto_entry = krb5_des_cst_entry.system;
+#else
+ server_parm.master_encblock.crypto_entry = &mit_des_cryptosystem_entry;
+#endif /* KRB5B4 */
+#else
+ error(You gotta figure out what cryptosystem to use in the KDC);
+#endif
+ retval = krb5_db_setup_mkey_name(mkey_name, realm, (char **) 0,
+ &server_parm.master_princ);
+ if (retval)
+ return KADM_NO_MAST;
+ krb5_db_fetch_mkey(server_parm.master_princ,
+ &server_parm.master_encblock,
+ (inter == 1), FALSE, NULL,
+ &server_parm.master_keyblock);
+ if (retval)
+ return KADM_NO_MAST;
+ retval = krb5_db_verify_master_key(server_parm.master_princ,
+ &server_parm.master_keyblock,
+ &server_parm.master_encblock);
+ if (retval)
+ return KADM_NO_VERI;
+ retval = krb5_process_key(&server_parm.master_encblock,
+ &server_parm.master_keyblock);
+ if (retval)
+ return KADM_NO_VERI;
+
+ retval = krb5_db_get_principal(server_parm.master_princ,
+ &master_entry, &numfound, &more);
+ if (retval || more || !numfound)
+ return KADM_NO_VERI;
+ server_parm.max_life = master_entry.max_life;
+ server_parm.max_rlife = master_entry.max_renewable_life;
+ server_parm.expiration = master_entry.expiration;
+ server_parm.mkvno = master_entry.kvno;
+ /* don't set flags, as master has some extra restrictions
+ (??? quoted from kdb_edit.c) */
+ krb5_db_free_principal(&master_entry, numfound);
+ return KADM_SUCCESS;
+}
+
+
+static void errpkt(dat, dat_len, code)
+u_char **dat;
+int *dat_len;
+int code;
+{
+ krb4_uint32 retcode;
+ char *pdat;
+
+ free((char *)*dat); /* free up req */
+ *dat_len = KADM_VERSIZE + sizeof(krb4_uint32);
+ *dat = (u_char *) malloc((unsigned)*dat_len);
+ if (!(*dat)) {
+ syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len);
+ abort();
+ }
+ pdat = (char *) *dat;
+ retcode = htonl((krb4_uint32) code);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb4_uint32));
+ return;
+}
+
+/*
+kadm_ser_in
+unwrap the data stored in dat, process, and return it.
+*/
+kadm_ser_in(dat,dat_len)
+u_char **dat;
+int *dat_len;
+{
+ u_char *in_st; /* pointer into the sent packet */
+ int in_len,retc; /* where in packet we are, for
+ returns */
+ krb4_uint32 r_len; /* length of the actual packet */
+ KTEXT_ST authent; /* the authenticator */
+ AUTH_DAT ad; /* who is this, klink */
+ krb4_uint32 ncksum; /* checksum of encrypted data */
+ des_key_schedule sess_sched; /* our schedule */
+ MSG_DAT msg_st;
+ u_char *retdat, *tmpdat;
+ int retval, retlen;
+
+ if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) {
+ errpkt(dat, dat_len, KADM_BAD_VER);
+ return KADM_BAD_VER;
+ }
+ in_len = KADM_VERSIZE;
+ /* get the length */
+ if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0)
+ return KADM_LENGTH_ERROR;
+ in_len += retc;
+ authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb4_uint32);
+ memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length);
+ authent.mbz = 0;
+ /* service key should be set before here */
+ if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst,
+ server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0))
+ {
+ errpkt(dat, dat_len,retc + krb_err_base);
+ return retc + krb_err_base;
+ }
+
+#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));}
+
+ in_st = *dat + *dat_len - r_len;
+#ifdef NOENCRYPTION
+ ncksum = 0;
+#else
+ ncksum = quad_cksum((des_cblock *)in_st, (des_cblock *)0, (krb4_int32) r_len, 0,
+ (des_cblock *)ad.session);
+#endif
+ if (ncksum!=ad.checksum) { /* yow, are we correct yet */
+ clr_cli_secrets();
+ errpkt(dat, dat_len,KADM_BAD_CHK);
+ return KADM_BAD_CHK;
+ }
+#ifdef NOENCRYPTION
+ memset(sess_sched, 0, sizeof(sess_sched));
+#else
+ des_key_sched(ad.session, sess_sched);
+#endif
+ if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session,
+ &server_parm.recv_addr,
+ &server_parm.admin_addr, &msg_st)) {
+ clr_cli_secrets();
+ errpkt(dat, dat_len,retc + krb_err_base);
+ return retc + krb_err_base;
+ }
+ switch (msg_st.app_data[0]) {
+ case CHANGE_PW:
+ retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+#ifndef OVSEC_KADM
+ case ADD_ENT:
+ retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case GET_ENT:
+ retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case MOD_ENT:
+ retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case CHECK_PW:
+ retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case CHG_STAB:
+ retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+#endif /* OVSEC_KADM */
+ default:
+ clr_cli_secrets();
+ errpkt(dat, dat_len, KADM_NO_OPCODE);
+ return KADM_NO_OPCODE;
+ }
+ /* Now seal the response back into a priv msg */
+ free((char *)*dat);
+ tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE +
+ sizeof(krb4_uint32)));
+ if (!tmpdat) {
+ clr_cli_secrets();
+ syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!",
+ retlen + KADM_VERSIZE + sizeof(krb4_uint32));
+ errpkt(dat, dat_len, KADM_NOMEM);
+ return KADM_NOMEM;
+ }
+ (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE);
+ retval = htonl((krb4_uint32)retval);
+ memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb4_uint32));
+ if (retlen) {
+ memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb4_uint32), (char *)retdat,
+ retlen);
+ free((char *)retdat);
+ }
+ /* slop for mk_priv stuff */
+ *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE +
+ sizeof(krb4_uint32) + 200));
+ if ((*dat_len = krb_mk_priv(tmpdat, *dat,
+ (krb4_uint32) (retlen + KADM_VERSIZE +
+ sizeof(krb4_uint32)),
+ sess_sched,
+ ad.session, &server_parm.admin_addr,
+ &server_parm.recv_addr)) < 0) {
+ clr_cli_secrets();
+ errpkt(dat, dat_len, KADM_NO_ENCRYPT);
+ free(tmpdat);
+ return KADM_NO_ENCRYPT;
+ }
+ clr_cli_secrets();
+ free(tmpdat);
+ return KADM_SUCCESS;
+}
diff --git a/src/kadmin/v4server/attic/kadm_server.c b/src/kadmin/v4server/attic/kadm_server.c
new file mode 100644
index 000000000..143af5d4e
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_server.c
@@ -0,0 +1,546 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side subroutines
+ */
+
+#ifndef lint
+static char rcsid_kadm_server_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+
+#include <krb5/osconf.h>
+#include <krb5/wordsize.h>
+
+#include <stdio.h>
+#ifdef USE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#include <string.h>
+
+#ifdef OVSEC_KADM
+#include <com_err.h>
+#include <ovsec_admin/admin.h>
+#include <ovsec_admin/chpass_util_strings.h>
+#include <krb5/kdb.h>
+extern void *ovsec_handle;
+#endif
+
+#include <kadm.h>
+#include <kadm_err.h>
+
+int fascist_cpw = 0; /* Be fascist about insecure passwords? */
+
+#ifdef OVSEC_KADM
+char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n";
+
+#else /* !OVSEC_KADM */
+
+char bad_pw_err[] =
+ "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n";
+
+char bad_pw_warn[] =
+ "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n";
+
+char check_pw_msg[] =
+ "You have entered an insecure password. You should choose another.\n\n";
+
+char pw_blurb[] =
+"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n";
+
+#endif /* OVSEC_KADM */
+
+/* from V4 month_sname.c -- was not part of API */
+/*
+ * Given an integer 1-12, month_sname() returns a string
+ * containing the first three letters of the corresponding
+ * month. Returns 0 if the argument is out of range.
+ */
+
+static char *month_sname(n)
+ int n;
+{
+ static char *name[] = {
+ "Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec"
+ };
+ return((n < 1 || n > 12) ? 0 : name [n-1]);
+}
+
+/* from V4 log.c -- was not part of API */
+
+/*
+ * krb_log() is used to add entries to the logfile (see krb_set_logfile()
+ * below). Note that it is probably not portable since it makes
+ * assumptions about what the compiler will do when it is called
+ * with less than the correct number of arguments which is the
+ * way it is usually called.
+ *
+ * The log entry consists of a timestamp and the given arguments
+ * printed according to the given "format".
+ *
+ * The log file is opened and closed for each log entry.
+ *
+ * The return value is undefined.
+ */
+
+/* static char *log_name = KRBLOG; */
+/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */
+static char *log_name = KADM_SYSLOG;
+
+static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0)
+ char *format;
+ int a1,a2,a3,a4,a5,a6,a7,a8,a9,a0;
+{
+ FILE *logfile, *fopen();
+ time_t now;
+ struct tm *tm;
+
+ if ((logfile = fopen(log_name,"a")) == NULL)
+ return;
+
+ (void) time(&now);
+ tm = localtime(&now);
+
+ fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday,
+ month_sname(tm->tm_mon + 1),tm->tm_year,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0);
+ fprintf(logfile,"\n");
+ (void) fclose(logfile);
+ return;
+}
+
+
+/*
+kadm_ser_cpw - the server side of the change_password routine
+ recieves : KTEXT, {key}
+ returns : CKSUM, RETCODE
+ acl : caller can change only own password
+
+Replaces the password (i.e. des key) of the caller with that specified in key.
+Returns no actual data from the master server, since this is called by a user
+*/
+kadm_ser_cpw(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ krb5_ui_4 keylow, keyhigh;
+ char pword[MAX_KPW_LEN];
+ int no_pword = 0;
+ des_cblock newkey;
+ int status, stvlen = 0;
+ int retval;
+ extern char *malloc();
+ extern int kadm_approve_pw();
+#ifdef OVSEC_KADM
+ ovsec_kadm_principal_ent_t princ_ent;
+ ovsec_kadm_policy_ent_t pol_ent;
+ krb5_principal user_princ;
+ char msg_ret[1024], *time_string, *ptr;
+ const char *msg_ptr;
+ krb5_int32 now;
+ time_t until;
+#endif
+
+ /* take key off the stream, and change the database */
+
+ if ((status = stv_long(dat, &keyhigh, 0, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((status = stv_long(dat, &keylow, stvlen, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) {
+ no_pword++;
+ pword[0]='\0';
+ }
+ stvlen += status;
+
+ keylow = ntohl(keylow);
+ keyhigh = ntohl(keyhigh);
+ memcpy((((krb5_int32 *)newkey) + 1), &keyhigh, 4);
+ memcpy(newkey, &keylow, 4);
+
+#ifdef OVSEC_KADM
+ /* we don't use the client-provided key itself */
+ keylow = keyhigh = 0;
+ memset(newkey, 0, sizeof(newkey));
+
+ if (no_pword) {
+ krb_log("Old-style change password request from '%s.%s@%s'!",
+ ad->pname, ad->pinst, ad->prealm);
+ *outlen = strlen(pw_required)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, pw_required);
+ } else {
+ *outlen = 0;
+ }
+ return KADM_INSECURE_PW;
+ }
+
+ if (krb5_build_principal(&user_princ,
+ strlen(ad->prealm),
+ ad->prealm,
+ ad->pname,
+ *ad->pinst ? ad->pinst : 0, 0))
+ /* this should never happen */
+ return KADM_NOENTRY;
+
+ *outlen = 0;
+
+ if (retval = krb5_timeofday(&now)) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ retval = ovsec_kadm_get_principal(ovsec_handle, user_princ,
+ &princ_ent);
+ if (retval != 0) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /*
+ * This daemon necessarily has the modify privilege, so
+ * ovsec_kadm_chpass_principal will allow it to violate the
+ * policy's minimum lifetime. Since that's A Bad Thing, we need
+ * to enforce it ourselves. Unfortunately, this means we are
+ * duplicating code from both ovsec_adm_server and
+ * ovsec_kadm_chpass_util().
+ */
+ if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) {
+ retval = ovsec_kadm_get_policy(ovsec_handle,
+ princ_ent->policy,
+ &pol_ent);
+ if (retval != 0) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /* make "now" a boolean, true == too soon */
+ now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life);
+
+ (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent);
+
+ if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ retval = CHPASS_UTIL_PASSWORD_TOO_SOON;
+
+ until = princ_ent->last_pwd_change + pol_ent->pw_min_life;
+ time_string = ctime(&until);
+
+ if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
+ *ptr = '\0';
+
+ sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON),
+ time_string);
+ msg_ptr = msg_ret;
+
+ goto send_response;
+ }
+ }
+
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+
+ retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ,
+ pword, NULL, msg_ret);
+ msg_ptr = msg_ret;
+ (void) krb5_free_principal(user_princ);
+
+send_response:
+
+ retval = convert_ovsec_to_kadm(retval);
+
+ if (retval) {
+ /* don't send message on success because kpasswd.v4 will */
+ /* print "password changed" too */
+ *outlen = strlen(msg_ptr)+2;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, msg_ptr);
+ strcat(*datout, "\n");
+ } else
+ *outlen = 0;
+ }
+ if (retval == KADM_INSECURE_PW) {
+ krb_log("'%s.%s@%s' tried to use an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+
+#else /* !OVSEC_KADM */
+
+ if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm,
+ newkey, no_pword ? 0 : pword)) {
+ if (retval == KADM_PW_MISMATCH) {
+ /*
+ * Very strange!!! This means that the cleartext
+ * password which was sent and the DES cblock
+ * didn't match!
+ */
+ (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?",
+ ad->pname, ad->pinst, ad->prealm);
+ return(retval);
+ }
+ if (fascist_cpw) {
+ *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, bad_pw_err);
+ strcat(*datout, pw_blurb);
+ } else
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+#ifdef notdef
+ /* For debugging only, probably a bad idea */
+ if (!no_pword)
+ (void) krb_log("The password was %s\n", pword);
+#endif
+ return(retval);
+ } else {
+ *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, bad_pw_warn);
+ strcat(*datout, pw_blurb);
+ } else
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' used an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+ } else {
+ *datout = 0;
+ *outlen = 0;
+ }
+
+ retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey);
+ keylow = keyhigh = 0;
+ memset(newkey, 0, sizeof(newkey));
+#endif /* OVSEC_KADM */
+
+ return retval;
+}
+
+/**********************************************************************/
+#ifndef OVSEC_KADM
+
+/*
+kadm_ser_add - the server side of the add_entry routine
+ recieves : KTEXT, {values}
+ returns : CKSUM, RETCODE, {values}
+ acl : su, sms (as alloc)
+
+Adds and entry containing values to the database
+returns the values of the entry, so if you leave certain fields blank you will
+ be able to determine the default values they are set to
+*/
+kadm_ser_add(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals values, retvals;
+ int status;
+
+ if ((status = stream_to_vals(dat, &values, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm,
+ &values, &retvals)) == KADM_DATA) {
+ *outlen = vals_to_stream(&retvals,datout);
+ retvals.key_low = retvals.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+/*
+kadm_ser_mod - the server side of the mod_entry routine
+ recieves : KTEXT, {values, values}
+ returns : CKSUM, RETCODE, {values}
+ acl : su, sms (as register or dealloc)
+
+Modifies all entries corresponding to the first values so they match the
+ second values.
+returns the values for the changed entries
+*/
+kadm_ser_mod(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals vals1, vals2, retvals;
+ int wh;
+ int status;
+
+ if ((wh = stream_to_vals(dat, &vals1, len)) < 0)
+ return KADM_LENGTH_ERROR;
+ if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0)
+ return KADM_LENGTH_ERROR;
+ if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1,
+ &vals2, &retvals)) == KADM_DATA) {
+ *outlen = vals_to_stream(&retvals,datout);
+ retvals.key_low = retvals.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+/*
+kadm_ser_get
+ recieves : KTEXT, {values, flags}
+ returns : CKSUM, RETCODE, {count, values, values, values}
+ acl : su
+
+gets the fields requested by flags from all entries matching values
+returns this data for each matching recipient, after a count of how many such
+ matches there were
+*/
+kadm_ser_get(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals values, retvals;
+ u_char fl[FLDSZ];
+ int loop,wh;
+ int status;
+
+ if ((wh = stream_to_vals(dat, &values, len)) < 0)
+ return KADM_LENGTH_ERROR;
+ if (wh + FLDSZ > len)
+ return KADM_LENGTH_ERROR;
+ for (loop=FLDSZ-1; loop>=0; loop--)
+ fl[loop] = dat[wh++];
+ if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm,
+ &values, fl, &retvals)) == KADM_DATA) {
+ *outlen = vals_to_stream(&retvals,datout);
+ retvals.key_low = retvals.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+/*
+kadm_ser_ckpw - the server side of the check_password routine
+ recieves : KTEXT, {key}
+ returns : CKSUM, RETCODE
+ acl : none
+
+Checks to see if the des key passed from the caller is a "secure" password.
+*/
+kadm_ser_ckpw(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ krb5_ui_4 keylow, keyhigh;
+ char pword[MAX_KPW_LEN];
+ int no_pword = 0;
+ des_cblock newkey;
+ int stvlen = 0,status;
+ int retval;
+ extern char *malloc();
+ extern int kadm_approve_pw();
+
+ /* take key off the stream, and check it */
+
+ if ((status = stv_long(dat, &keyhigh, 0, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((status = stv_long(dat, &keylow, stvlen, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) {
+ no_pword++;
+ pword[0]='\0';
+ }
+ stvlen += status;
+
+ keylow = ntohl(keylow);
+ keyhigh = ntohl(keyhigh);
+ memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4);
+ memcpy((char *)newkey, (char *)&keylow, 4);
+ keylow = keyhigh = 0;
+ retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey,
+ no_pword ? 0 : pword);
+ memset(newkey, 0, sizeof(newkey));
+ if (retval) {
+ *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, check_pw_msg);
+ strcat(*datout, pw_blurb);
+ } else
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' sent an insecure password to be checked",
+ ad->pname, ad->pinst, ad->prealm);
+ return(retval);
+ } else {
+ *datout = 0;
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' sent a secure password to be checked",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+ return(0);
+}
+
+/*
+kadm_ser_stab - the server side of the change_srvtab routine
+ recieves : KTEXT, {values}
+ returns : CKSUM, RETCODE, {values}
+ acl : su, sms (as register or dealloc)
+
+Creates or modifies the specified service principal to have a random
+key, which is sent back to the client. The key version is returned in
+the max_life field of the values structure. It's a hack, but it's a
+backwards compatible hack....
+*/
+kadm_ser_stab(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals values;
+ int status;
+
+ if ((status = stream_to_vals(dat, &values, len)) < 0)
+ return KADM_LENGTH_ERROR;
+ status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values);
+ if (status == KADM_DATA) {
+ *outlen = vals_to_stream(&values,datout);
+ values.key_low = values.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+#endif /* !OVSEC_KADM */
diff --git a/src/kadmin/v4server/attic/kadm_server.h b/src/kadmin/v4server/attic/kadm_server.h
new file mode 100644
index 000000000..4e6fd8c46
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_server.h
@@ -0,0 +1,64 @@
+/*
+ * $Source$
+ * $Author$
+ * $Header$
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Definitions for Kerberos administration server & client
+ */
+
+#ifndef KADM_SERVER_DEFS
+#define KADM_SERVER_DEFS
+
+#include <mit-copyright.h>
+/*
+ * kadm_server.h
+ * Header file for the fourth attempt at an admin server
+ * Doug Church, December 28, 1989, MIT Project Athena
+ * ps. Yes that means this code belongs to athena etc...
+ * as part of our ongoing attempt to copyright all greek names
+ */
+
+#include <sys/types.h>
+#include <krb.h>
+#include <des.h>
+
+#include <krb5/krb5.h>
+#include <krb5/kdb.h>
+#include <krb5/osconf.h>
+#include <krb5/config.h>
+#ifdef PROVIDE_DES_CBC_CRC
+#include <krb5/mit-des.h>
+#endif
+
+typedef struct {
+ struct sockaddr_in admin_addr;
+ struct sockaddr_in recv_addr;
+ int recv_addr_len;
+ int admin_fd; /* our link to clients */
+ char sname[ANAME_SZ];
+ char sinst[INST_SZ];
+ char krbrlm[REALM_SZ];
+ krb5_principal sprinc;
+ krb5_encrypt_block master_encblock;
+ krb5_principal master_princ;
+ krb5_keyblock master_keyblock;
+ krb5_deltat max_life;
+ krb5_deltat max_rlife;
+ krb5_timestamp expiration;
+ krb5_flags flags;
+ krb5_kvno mkvno;
+} Kadm_Server;
+
+#define ADD_ACL_FILE "/v4acl.add"
+#define GET_ACL_FILE "/v4acl.get"
+#define MOD_ACL_FILE "/v4acl.mod"
+#define STAB_ACL_FILE "/v4acl.srvtab"
+#define STAB_SERVICES_FILE "/v4stab_services"
+#define STAB_HOSTS_FILE "/v4stab_bad_hosts"
+
+#endif /* KADM_SERVER_DEFS */
diff --git a/src/kadmin/v4server/attic/kadm_stream.c b/src/kadmin/v4server/attic/kadm_stream.c
new file mode 100644
index 000000000..83aaa295c
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_stream.c
@@ -0,0 +1,276 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Stream conversion functions for Kerberos administration server
+ */
+
+#ifndef lint
+static char rcsid_kadm_stream_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+/*
+ kadm_stream.c
+ this holds the stream support routines for the kerberos administration server
+
+ vals_to_stream: converts a vals struct to a stream for transmission
+ internals build_field_header, vts_[string, char, krb4_int32, short]
+ stream_to_vals: converts a stream to a vals struct
+ internals check_field_header, stv_[string, char, krb4_int32, short]
+ error: prints out a kadm error message, returns
+ fatal: prints out a kadm fatal error message, exits
+*/
+
+#include "kadm.h"
+#include <string.h>
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/*
+vals_to_stream
+ recieves : kadm_vals *, u_char *
+ returns : a realloced and filled in u_char *
+
+this function creates a byte-stream representation of the kadm_vals structure
+*/
+vals_to_stream(dt_in, dt_out)
+Kadm_vals *dt_in;
+u_char **dt_out;
+{
+ int vsloop, stsize; /* loop counter, stream size */
+
+ stsize = build_field_header(dt_in->fields, dt_out);
+ for (vsloop=31; vsloop>=0; vsloop--)
+ if (IS_FIELD(vsloop,dt_in->fields)) {
+ switch (vsloop) {
+ case KADM_NAME:
+ stsize+=vts_string(dt_in->name, dt_out, stsize);
+ break;
+ case KADM_INST:
+ stsize+=vts_string(dt_in->instance, dt_out, stsize);
+ break;
+ case KADM_EXPDATE:
+ stsize+=vts_long(dt_in->exp_date, dt_out, stsize);
+ break;
+ case KADM_ATTR:
+ stsize+=vts_short(dt_in->attributes, dt_out, stsize);
+ break;
+ case KADM_MAXLIFE:
+ stsize+=vts_char(dt_in->max_life, dt_out, stsize);
+ break;
+ case KADM_DESKEY:
+ stsize+=vts_long(dt_in->key_high, dt_out, stsize);
+ stsize+=vts_long(dt_in->key_low, dt_out, stsize);
+ break;
+ default:
+ break;
+ }
+}
+ return(stsize);
+}
+
+build_field_header(cont, st)
+u_char *cont; /* container for fields data */
+u_char **st; /* stream */
+{
+ *st = (u_char *) malloc (4);
+ memcpy((void *) *st, (void *) cont, 4);
+ return 4; /* return pointer to current stream location */
+}
+
+vts_string(dat, st, loc)
+char *dat; /* a string to put on the stream */
+u_char **st; /* base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1));
+ memcpy((char *)(*st + loc), dat, strlen(dat)+1);
+ return strlen(dat)+1;
+}
+
+vts_short(dat, st, loc)
+u_short dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ u_short temp; /* to hold the net order short */
+
+ temp = htons(dat); /* convert to network order */
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short)));
+ memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short));
+ return sizeof(u_short);
+}
+
+vts_long(dat, st, loc)
+krb4_uint32 dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ krb4_uint32 temp; /* to hold the net order short */
+
+ temp = htonl(dat); /* convert to network order */
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb4_uint32)));
+ memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb4_uint32));
+ return sizeof(krb4_uint32);
+}
+
+
+vts_char(dat, st, loc)
+u_char dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char)));
+ (*st)[loc] = (u_char) dat;
+ return 1;
+}
+
+/*
+stream_to_vals
+ recieves : u_char *, kadm_vals *
+ returns : a kadm_vals filled in according to u_char *
+
+this decodes a byte stream represntation of a vals struct into kadm_vals
+*/
+stream_to_vals(dt_in, dt_out, maxlen)
+u_char *dt_in;
+Kadm_vals *dt_out;
+int maxlen; /* max length to use */
+{
+ register int vsloop, stsize; /* loop counter, stream size */
+ register int status;
+ krb4_int32 lcllong;
+
+ memset((char *) dt_out, 0, sizeof(*dt_out));
+
+ stsize = check_field_header(dt_in, dt_out->fields, maxlen);
+ if (stsize < 0)
+ return(-1);
+ for (vsloop=31; vsloop>=0; vsloop--)
+ if (IS_FIELD(vsloop,dt_out->fields))
+ switch (vsloop) {
+ case KADM_NAME:
+ if ((status = stv_string(dt_in, dt_out->name, stsize,
+ sizeof(dt_out->name), maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_INST:
+ if ((status = stv_string(dt_in, dt_out->instance, stsize,
+ sizeof(dt_out->instance), maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_EXPDATE:
+ if ((status = stv_long(dt_in, &lcllong, stsize,
+ maxlen)) < 0)
+ return(-1);
+ dt_out->exp_date = lcllong;
+ stsize += status;
+ break;
+ case KADM_ATTR:
+ if ((status = stv_short(dt_in, &dt_out->attributes, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_MAXLIFE:
+ if ((status = stv_char(dt_in, &dt_out->max_life, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_DESKEY:
+ if ((status = stv_long(dt_in, &dt_out->key_high, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ if ((status = stv_long(dt_in, &dt_out->key_low, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ default:
+ break;
+ }
+ return stsize;
+}
+
+check_field_header(st, cont, maxlen)
+u_char *st; /* stream */
+u_char *cont; /* container for fields data */
+int maxlen;
+{
+ if (4 > maxlen)
+ return(-1);
+ memcpy((char *) cont, (char *) st, 4);
+ return 4; /* return pointer to current stream location */
+}
+
+stv_string(st, dat, loc, stlen, maxlen)
+register u_char *st; /* base pointer to the stream */
+char *dat; /* a string to read from the stream */
+register int loc; /* offset into the stream for current data */
+int stlen; /* max length of string to copy in */
+int maxlen; /* max length of input stream */
+{
+ int maxcount; /* max count of chars to copy */
+
+ maxcount = min(maxlen - loc, stlen);
+
+ (void) strncpy(dat, (char *)st + loc, maxcount);
+
+ if (dat[maxcount-1]) /* not null-term --> not enuf room */
+ return(-1);
+ return strlen(dat)+1;
+}
+
+stv_short(st, dat, loc, maxlen)
+u_char *st; /* a base pointer to the stream */
+u_short *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen;
+{
+ u_short temp; /* to hold the net order short */
+
+ if (loc + sizeof(u_short) > maxlen)
+ return(-1);
+ memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(u_short));
+ *dat = ntohs(temp); /* convert to network order */
+ return sizeof(u_short);
+}
+
+stv_long(st, dat, loc, maxlen)
+u_char *st; /* a base pointer to the stream */
+krb4_uint32 *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen; /* maximum length of st */
+{
+ krb4_uint32 temp; /* to hold the net order short */
+
+ if (loc + sizeof(krb4_uint32) > maxlen)
+ return(-1);
+ memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(krb4_uint32));
+ *dat = ntohl(temp); /* convert to network order */
+ return sizeof(krb4_uint32);
+}
+
+stv_char(st, dat, loc, maxlen)
+u_char *st; /* a base pointer to the stream */
+u_char *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen;
+{
+ if (loc + 1 > maxlen)
+ return(-1);
+ *dat = *(st + loc);
+ return 1;
+}
+
diff --git a/src/kadmin/v4server/attic/kadm_supp.c b/src/kadmin/v4server/attic/kadm_supp.c
new file mode 100644
index 000000000..cf4ba40f4
--- /dev/null
+++ b/src/kadmin/v4server/attic/kadm_supp.c
@@ -0,0 +1,114 @@
+/*
+ * $Source$
+ * $Author$
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Support functions for Kerberos administration server & clients
+ */
+
+#ifndef lint
+static char rcsid_kadm_supp_c[] =
+"$Header$";
+#endif lint
+
+#include <mit-copyright.h>
+/*
+ kadm_supp.c
+ this holds the support routines for the kerberos administration server
+
+ error: prints out a kadm error message, returns
+ fatal: prints out a kadm fatal error message, exits
+ prin_vals: prints out data associated with a Principal in the vals
+ structure
+*/
+
+#include "kadm.h"
+#include "krb_db.h"
+
+/*
+prin_vals:
+ recieves : a vals structure
+*/
+prin_vals(vals)
+Kadm_vals *vals;
+{
+ printf("Info in Database for %s.%s:\n", vals->name, vals->instance);
+ printf(" Max Life: %d Exp Date: %s\n",vals->max_life,
+ asctime(localtime(&vals->exp_date)));
+ printf(" Attribs: %.2x key: %u %u\n",vals->attributes,
+ vals->key_low, vals->key_high);
+}
+
+#ifdef notdef
+nierror(s)
+int s;
+{
+ extern char *error_message();
+ printf("Kerberos admin server loses..... %s\n",error_message(s));
+ return(s);
+}
+#endif
+
+/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal,
+ it copies the fields in Principal specified by fields into Kadm_vals,
+ i.e from old to new */
+
+kadm_prin_to_vals(fields, new, old)
+u_char fields[FLDSZ];
+Kadm_vals *new;
+Principal *old;
+{
+ memset((char *)new, 0, sizeof(*new));
+ if (IS_FIELD(KADM_NAME,fields)) {
+ (void) strncpy(new->name, old->name, ANAME_SZ);
+ SET_FIELD(KADM_NAME, new->fields);
+ }
+ if (IS_FIELD(KADM_INST,fields)) {
+ (void) strncpy(new->instance, old->instance, INST_SZ);
+ SET_FIELD(KADM_INST, new->fields);
+ }
+ if (IS_FIELD(KADM_EXPDATE,fields)) {
+ new->exp_date = old->exp_date;
+ SET_FIELD(KADM_EXPDATE, new->fields);
+ }
+ if (IS_FIELD(KADM_ATTR,fields)) {
+ new->attributes = old->attributes;
+ SET_FIELD(KADM_MAXLIFE, new->fields);
+ }
+ if (IS_FIELD(KADM_MAXLIFE,fields)) {
+ new->max_life = old->max_life;
+ SET_FIELD(KADM_MAXLIFE, new->fields);
+ }
+ if (IS_FIELD(KADM_DESKEY,fields)) {
+ new->key_low = old->key_low;
+ new->key_high = old->key_high;
+ SET_FIELD(KADM_DESKEY, new->fields);
+ }
+}
+
+kadm_vals_to_prin(fields, new, old)
+u_char fields[FLDSZ];
+Principal *new;
+Kadm_vals *old;
+{
+
+ memset((char *)new, 0, sizeof(*new));
+ if (IS_FIELD(KADM_NAME,fields))
+ (void) strncpy(new->name, old->name, ANAME_SZ);
+ if (IS_FIELD(KADM_INST,fields))
+ (void) strncpy(new->instance, old->instance, INST_SZ);
+ if (IS_FIELD(KADM_EXPDATE,fields))
+ new->exp_date = old->exp_date;
+ if (IS_FIELD(KADM_ATTR,fields))
+ new->attributes = old->attributes;
+ if (IS_FIELD(KADM_MAXLIFE,fields))
+ new->max_life = old->max_life;
+ if (IS_FIELD(KADM_DESKEY,fields)) {
+ new->key_low = old->key_low;
+ new->key_high = old->key_high;
+ }
+}
diff --git a/src/kadmin/v4server/configure.in b/src/kadmin/v4server/configure.in
new file mode 100644
index 000000000..9d45c1e16
--- /dev/null
+++ b/src/kadmin/v4server/configure.in
@@ -0,0 +1,16 @@
+AC_INIT(admin_server.c)
+CONFIG_RULES
+AC_PROG_INSTALL
+AC_CHECK_HEADERS(sys/time.h unistd.h stdlib.h)
+CHECK_SIGNALS
+CHECK_WAIT_TYPE
+AC_PROG_AWK
+USE_KADMCLNT_LIBRARY
+USE_GSSRPC_LIBRARY
+USE_GSSAPI_LIBRARY
+USE_DYN_LIBRARY
+USE_KDB5_LIBRARY
+USE_KRB4_LIBRARY
+KRB5_LIBRARIES
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/v4server/kadm_err.et b/src/kadmin/v4server/kadm_err.et
new file mode 100644
index 000000000..a19273083
--- /dev/null
+++ b/src/kadmin/v4server/kadm_err.et
@@ -0,0 +1,57 @@
+# kadmin.v4/server/kadm_err.et
+#
+# Copyright 1988 by the Massachusetts Institute of Technology.
+#
+# For copying and distribution information, please see the file
+# <mit-copyright.h>.
+#
+# Kerberos administration server error table
+#
+ et kadm
+
+# KADM_SUCCESS, as all success codes should be, is zero
+
+ec KADM_RCSID, "$Header$"
+# /* Building and unbuilding the packet errors */
+ec KADM_NO_REALM, "Cannot fetch local realm"
+ec KADM_NO_CRED, "Unable to fetch credentials"
+ec KADM_BAD_KEY, "Bad key supplied"
+ec KADM_NO_ENCRYPT, "Can't encrypt data"
+ec KADM_NO_AUTH, "Cannot encode/decode authentication info"
+ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm"
+ec KADM_NO_ROOM, "Packet is too large"
+ec KADM_BAD_VER, "Version number is incorrect"
+ec KADM_BAD_CHK, "Checksum does not match"
+ec KADM_NO_READ, "Unsealing private data failed"
+ec KADM_NO_OPCODE, "Unsupported operation"
+ec KADM_NO_HOST, "Could not find administrating host"
+ec KADM_UNK_HOST, "Administrating host name is unknown"
+ec KADM_NO_SERV, "Could not find service name in services database"
+ec KADM_NO_SOCK, "Could not create socket"
+ec KADM_NO_CONN, "Could not connect to server"
+ec KADM_NO_HERE, "Could not fetch local socket address"
+ec KADM_NO_MAST, "Could not fetch master key"
+ec KADM_NO_VERI, "Could not verify master key"
+
+# /* From the server side routines */
+ec KADM_INUSE, "Entry already exists in database"
+ec KADM_UK_SERROR, "Database store error"
+ec KADM_UK_RERROR, "Database read error"
+ec KADM_UNAUTH, "Insufficient access to perform requested operation"
+# KADM_DATA isn't really an error, but...
+ec KADM_DATA, "Data is available for return to client"
+ec KADM_NOENTRY, "No such entry in the database"
+
+ec KADM_NOMEM, "Memory exhausted"
+ec KADM_NO_HOSTNAME, "Could not fetch system hostname"
+ec KADM_NO_BIND, "Could not bind port"
+ec KADM_LENGTH_ERROR, "Length mismatch problem"
+ec KADM_ILL_WILDCARD, "Illegal use of wildcard"
+
+ec KADM_DB_INUSE, "Database locked or in use"
+
+ec KADM_INSECURE_PW, "Insecure password rejected"
+ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match"
+
+ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request"
+end
diff --git a/src/kadmin/v4server/kadm_funcs.c b/src/kadmin/v4server/kadm_funcs.c
new file mode 100644
index 000000000..5025e3acb
--- /dev/null
+++ b/src/kadmin/v4server/kadm_funcs.c
@@ -0,0 +1,1066 @@
+/*
+ * kadmin/v4server/kadm_funcs.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side database manipulation routines
+ */
+
+
+#include <mit-copyright.h>
+/*
+kadm_funcs.c
+the actual database manipulation code
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+/* #include <ndbm.h> Gotten by kadmin_server.h */
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_db.h>
+#include <syslog.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#else
+#include <time.h>
+#endif
+
+#include "kadm_server.h"
+
+extern Kadm_Server server_parm;
+
+krb5_error_code
+kadm_entry2princ(entry, princ)
+ krb5_db_entry entry;
+ Principal *princ;
+{
+ char realm[REALM_SZ]; /* dummy values only */
+ krb5_tl_mod_princ *mprinc;
+ krb5_key_data *pkey;
+ krb5_error_code retval;
+
+ /* NOTE: does not convert the key */
+ memset(princ, 0, sizeof (*princ));
+ retval = krb5_524_conv_principal(kadm_context, entry.princ,
+ princ->name, princ->instance, realm);
+ if (retval)
+ return retval;
+ princ->exp_date = entry.expiration;
+ strncpy(princ->exp_date_txt, ctime((const time_t *) &entry.expiration),
+ DATE_SZ);
+ princ->attributes = entry.attributes;
+ princ->max_life = entry.max_life / (60 * 5);
+ princ->kdc_key_ver = 1; /* entry.mkvno; */
+ princ->key_version = entry.key_data[0].key_data_kvno;
+
+ retval = krb5_dbe_decode_mod_princ_data(kadm_context, &entry, &mprinc);
+ if (retval)
+ return retval;
+ princ->mod_date = mprinc->mod_date;
+ strncpy(princ->mod_date_txt,
+ ctime((const time_t *) &mprinc->mod_date),
+ DATE_SZ);
+ krb5_free_principal(kadm_context, mprinc->mod_princ);
+ krb5_xfree(mprinc);
+
+ /* Find the V4 key */
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey);
+ if (retval)
+ return retval;
+ princ->key_version = pkey->key_data_kvno;
+
+ return 0;
+}
+
+krb5_error_code
+kadm_princ2entry(princ, entry)
+ Principal princ;
+ krb5_db_entry *entry;
+{
+ krb5_error_code retval;
+ krb5_tl_mod_princ mprinc;
+ krb5_key_data *kdatap;
+
+ /* NOTE: does not convert the key */
+ memset(entry, 0, sizeof (*entry));
+ /* yeah yeah stupid v4 database doesn't store realm names */
+ retval = krb5_425_conv_principal(kadm_context, princ.name, princ.instance,
+ server_parm.krbrlm, &entry->princ);
+ if (retval)
+ return retval;
+
+ entry->len = KRB5_KDB_V1_BASE_LENGTH;
+ entry->max_life = princ.max_life * (60 * 5);
+ entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */
+ entry->expiration = princ.exp_date;
+ entry->attributes = princ.attributes;
+
+ retval = krb5_425_conv_principal(kadm_context, princ.mod_name,
+ princ.mod_instance,
+ server_parm.krbrlm, &mprinc.mod_princ);
+ if (retval)
+ return(retval);
+ mprinc.mod_date = princ.mod_date;
+
+ retval = krb5_dbe_encode_mod_princ_data(kadm_context, &mprinc, entry);
+ if (retval)
+ return(retval);
+
+ if (mprinc.mod_princ)
+ krb5_free_principal(kadm_context, mprinc.mod_princ);
+
+ if (retval = krb5_dbe_find_enctype(kadm_context,
+ entry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &kdatap)) {
+ if (!(retval = krb5_dbe_create_key_data(kadm_context, entry)))
+ kdatap = &entry->key_data[entry->n_key_data-1];
+ }
+ if (kdatap) {
+ kdatap->key_data_ver = 2;
+ kdatap->key_data_type[0] = (krb5_int16) ENCTYPE_DES_CBC_CRC;
+ kdatap->key_data_type[1] = (krb5_int16) KRB5_KDB_SALTTYPE_V4;
+ kdatap->key_data_kvno = (krb5_int16) princ.key_version;
+ }
+ return(retval);
+}
+
+int
+check_access(pname, pinst, prealm, acltype)
+char *pname;
+char *pinst;
+char *prealm;
+enum acl_types acltype;
+{
+ char checkname[MAX_K_NAME_SZ];
+ char filename[MAXPATHLEN];
+ extern char *acldir;
+
+ (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm);
+
+ switch (acltype) {
+ case ADDACL:
+ (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE);
+ break;
+ case GETACL:
+ (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE);
+ break;
+ case MODACL:
+ (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE);
+ break;
+ case DELACL:
+ (void) sprintf(filename, "%s%s", acldir, DEL_ACL_FILE);
+ break;
+ case STABACL:
+ (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE);
+ break;
+ }
+ return(acl_check(filename, checkname));
+}
+
+int
+wildcard(str)
+char *str;
+{
+ if (!strcmp(str, WILDCARD_STR))
+ return(1);
+ return(0);
+}
+
+#define failadd(code) { (void) syslog(LOG_ERR, "FAILED adding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_add_entry (rname, rinstance, rrealm, valsin, valsout)
+char *rname; /* requestors name */
+char *rinstance; /* requestors instance */
+char *rrealm; /* requestors realm */
+Kadm_vals *valsin;
+Kadm_vals *valsout;
+{
+ Principal data_i, data_o; /* temporary principal */
+ u_char flags[4];
+ krb5_principal default_princ;
+ krb5_error_code retval;
+ krb5_db_entry newentry, tmpentry;
+ krb5_boolean more;
+ krb5_keyblock newpw;
+ krb5_tl_mod_princ mprinc;
+ krb5_key_data *pkey;
+ krb5_keysalt sblock;
+ int numfound;
+
+ if (!check_access(rname, rinstance, rrealm, ADDACL)) {
+ syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'",
+ rname, rinstance, rrealm, valsin->name, valsin->instance);
+ return KADM_UNAUTH;
+ }
+
+ /* Need to check here for "legal" name and instance */
+ if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+ failadd(KADM_ILL_WILDCARD);
+ }
+
+ syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'",
+ valsin->name, valsin->instance, rname, rinstance, rrealm);
+
+ kadm_vals_to_prin(valsin->fields, &data_i, valsin);
+ (void) strncpy(data_i.name, valsin->name, ANAME_SZ);
+ (void) strncpy(data_i.instance, valsin->instance, INST_SZ);
+
+ if (!IS_FIELD(KADM_EXPDATE,valsin->fields))
+ data_i.exp_date = server_parm.expiration;
+ if (!IS_FIELD(KADM_ATTR,valsin->fields))
+ data_i.attributes = server_parm.flags;
+ if (!IS_FIELD(KADM_MAXLIFE,valsin->fields))
+ data_i.max_life = server_parm.max_life;
+
+ retval = kadm_princ2entry(data_i, &newentry);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+
+ newpw.magic = KV5M_KEYBLOCK;
+ if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL)
+ failadd(KADM_NOMEM);
+
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &newentry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey);
+ if (retval)
+ failadd(retval);
+
+ data_i.key_low = ntohl(data_i.key_low);
+ data_i.key_high = ntohl(data_i.key_high);
+ memcpy(newpw.contents, &data_i.key_low, 4);
+ memcpy((char *)(((krb5_int32 *) newpw.contents) + 1), &data_i.key_high, 4);
+ newpw.length = 8;
+ newpw.enctype = ENCTYPE_DES_CBC_CRC;
+ sblock.type = KRB5_KDB_SALTTYPE_V4;
+ sblock.data.length = 0;
+ sblock.data.data = (char *) NULL;
+ /* encrypt new key in master key */
+ retval = krb5_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &newpw,
+ &sblock,
+ (int) ++data_i.key_version,
+ pkey);
+ memset((char *)newpw.contents, 0, newpw.length);
+ free(newpw.contents);
+ if (retval) {
+ failadd(retval);
+ }
+ data_o = data_i;
+
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, newentry.princ,
+ &tmpentry, &numfound, &more);
+
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+ krb5_db_free_principal(kadm_context, &tmpentry, numfound);
+ if (numfound) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(KADM_INUSE);
+ } else {
+ if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+ mprinc.mod_princ = NULL; /* in case the following breaks */
+ retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm,
+ &mprinc.mod_princ);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+
+ retval = krb5_dbe_encode_mod_princ_data(kadm_context,
+ &mprinc,
+ &newentry);
+ krb5_free_principal(kadm_context, mprinc.mod_princ);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+
+ numfound = 1;
+ retval = krb5_db_put_principal(kadm_context, &newentry, &numfound);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(retval);
+ }
+ if (!numfound) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failadd(KADM_UK_SERROR);
+ } else {
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, newentry.princ,
+ &tmpentry,
+ &numfound, &more);
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ if (retval) {
+ failadd(retval);
+ } else if (numfound != 1 || more) {
+ krb5_db_free_principal(kadm_context, &tmpentry, numfound);
+ failadd(KADM_UK_RERROR);
+ }
+ kadm_entry2princ(tmpentry, &data_o);
+ krb5_db_free_principal(kadm_context, &tmpentry, numfound);
+ memset((char *)flags, 0, sizeof(flags));
+ SET_FIELD(KADM_NAME,flags);
+ SET_FIELD(KADM_INST,flags);
+ SET_FIELD(KADM_EXPDATE,flags);
+ SET_FIELD(KADM_ATTR,flags);
+ SET_FIELD(KADM_MAXLIFE,flags);
+ kadm_prin_to_vals(flags, valsout, &data_o);
+ syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance);
+ return KADM_DATA; /* Set all the appropriate fields */
+ }
+ }
+}
+#undef failadd
+
+#define faildel(code) { (void) syslog(LOG_ERR, "FAILED deleting '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_del_entry (rname, rinstance, rrealm, valsin, valsout)
+char *rname; /* requestors name */
+char *rinstance; /* requestors instance */
+char *rrealm; /* requestors realm */
+Kadm_vals *valsin;
+Kadm_vals *valsout;
+{
+ int numfound; /* check how many we get written */
+ krb5_boolean more; /* pointer to more grabbed records */
+ Principal data_i, data_o; /* temporary principal */
+ u_char flags[4];
+ krb5_db_entry entry, odata;
+ krb5_error_code retval;
+ krb5_principal inprinc;
+
+ if (!check_access(rname, rinstance, rrealm, DELACL)) {
+ (void) syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to delete an entry for '%s.%s'",
+ rname, rinstance, rrealm, valsin->name, valsin->instance);
+ return KADM_UNAUTH;
+ }
+
+ /* Need to check here for "legal" name and instance */
+ if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+ faildel(KADM_ILL_WILDCARD);
+ }
+
+ syslog(LOG_INFO, "request to delete an entry for '%s.%s' from '%s.%s@%s'",
+ valsin->name, valsin->instance, rname, rinstance, rrealm);
+
+ retval = krb5_425_conv_principal(kadm_context, valsin->name,
+ valsin->instance,
+ server_parm.krbrlm, &inprinc);
+ if (retval)
+ faildel(retval);
+
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound,
+ &more);
+
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &entry, numfound);
+ faildel(retval);
+ } else if (!numfound || more) {
+ faildel(KADM_NOENTRY);
+ }
+
+ retval = krb5_db_delete_principal(kadm_context, inprinc, &numfound);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &entry, numfound);
+ faildel(retval);
+ }
+ if (!numfound) {
+ krb5_db_free_principal(kadm_context, &entry, numfound);
+ faildel(KADM_UK_SERROR);
+ } else {
+ if (retval) {
+ faildel(retval);
+ } else if (numfound != 1 || more) {
+ krb5_db_free_principal(kadm_context, &entry, numfound);
+ faildel(KADM_UK_RERROR);
+ }
+ kadm_entry2princ(entry, &data_o);
+ krb5_db_free_principal(kadm_context, &entry, numfound);
+ memset((char *)flags, 0, sizeof(flags));
+ SET_FIELD(KADM_NAME,flags);
+ SET_FIELD(KADM_INST,flags);
+ SET_FIELD(KADM_EXPDATE,flags);
+ SET_FIELD(KADM_ATTR,flags);
+ SET_FIELD(KADM_MAXLIFE,flags);
+ kadm_prin_to_vals(flags, valsout, &data_o);
+ syslog(LOG_INFO, "'%s.%s' deleted.", valsin->name, valsin->instance);
+ return KADM_DATA; /* Set all the appropriate fields */
+ }
+}
+#undef faildel
+
+#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; }
+
+kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout)
+char *rname; /* requestors name */
+char *rinstance; /* requestors instance */
+char *rrealm; /* requestors realm */
+Kadm_vals *valsin; /* what they wannt to get */
+u_char *flags; /* which fields we want */
+Kadm_vals *valsout; /* what data is there */
+{
+ int numfound; /* check how many were returned */
+ krb5_boolean more; /* To point to more name.instances */
+ Principal data_o; /* Data object to hold Principal */
+ krb5_principal inprinc;
+ krb5_db_entry entry;
+ krb5_error_code retval;
+
+ if (!check_access(rname, rinstance, rrealm, GETACL)) {
+ syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry",
+ rname, rinstance, rrealm, valsin->name, valsin->instance);
+ return KADM_UNAUTH;
+ }
+
+ if (wildcard(valsin->name) || wildcard(valsin->instance)) {
+ failget(KADM_ILL_WILDCARD);
+ }
+
+ syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'",
+ valsin->name, valsin->instance, rname, rinstance, rrealm);
+
+ retval = krb5_425_conv_principal(kadm_context, valsin->name,
+ valsin->instance,
+ server_parm.krbrlm, &inprinc);
+ if (retval)
+ failget(retval);
+ /* Look up the record in the database */
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound,
+ &more);
+ krb5_free_principal(kadm_context, inprinc);
+ if (retval) {
+ failget(retval);
+ } else if (!numfound || more) {
+ failget(KADM_NOENTRY);
+ }
+ retval = kadm_entry2princ(entry, &data_o);
+ krb5_db_free_principal(kadm_context, &entry, 1);
+ if (retval) {
+ failget(retval);
+ }
+ kadm_prin_to_vals(flags, valsout, &data_o);
+ syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance);
+ return KADM_DATA; /* Set all the appropriate fields */
+}
+#undef failget
+
+#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; }
+
+kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout)
+char *rname; /* requestors name */
+char *rinstance; /* requestors instance */
+char *rrealm; /* requestors realm */
+Kadm_vals *valsin1, *valsin2; /* holds the parameters being
+ passed in */
+Kadm_vals *valsout; /* the actual record which is returned */
+{
+ int numfound;
+ krb5_boolean more;
+ Principal data_o, temp_key;
+ u_char fields[4];
+ krb5_keyblock newpw;
+ krb5_error_code retval;
+ krb5_principal theprinc;
+ krb5_db_entry newentry, odata;
+ krb5_tl_mod_princ mprinc;
+ krb5_key_data *pkey;
+ krb5_keysalt sblock;
+
+ if (wildcard(valsin1->name) || wildcard(valsin1->instance)) {
+ failmod(KADM_ILL_WILDCARD);
+ }
+
+ if (!check_access(rname, rinstance, rrealm, MODACL)) {
+ syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry",
+ rname, rinstance, rrealm, valsin1->name, valsin1->instance);
+ return KADM_UNAUTH;
+ }
+
+ syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ",
+ valsin1->name, valsin1->instance, rname, rinstance, rrealm);
+ retval = krb5_425_conv_principal(kadm_context,
+ valsin1->name, valsin1->instance,
+ server_parm.krbrlm, &theprinc);
+ if (retval)
+ failmod(retval);
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, theprinc, &newentry,
+ &numfound, &more);
+ if (retval) {
+ krb5_free_principal(kadm_context, theprinc);
+ failmod(retval);
+ } else if (numfound == 1) {
+ kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2);
+ krb5_free_principal(kadm_context, newentry.princ);
+ newentry.princ = theprinc;
+ if (IS_FIELD(KADM_EXPDATE,valsin2->fields))
+ newentry.expiration = temp_key.exp_date;
+ if (IS_FIELD(KADM_ATTR,valsin2->fields))
+ newentry.attributes = temp_key.attributes;
+ if (IS_FIELD(KADM_MAXLIFE,valsin2->fields))
+ newentry.max_life = temp_key.max_life;
+ if (IS_FIELD(KADM_DESKEY,valsin2->fields)) {
+ if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ memset((char *)&temp_key, 0, sizeof (temp_key));
+ failmod(KADM_NOMEM);
+ }
+ newpw.magic = KV5M_KEYBLOCK;
+ newpw.length = 8;
+ newpw.enctype = ENCTYPE_DES_CBC_CRC;
+ temp_key.key_low = ntohl(temp_key.key_low);
+ temp_key.key_high = ntohl(temp_key.key_high);
+ memcpy(newpw.contents, &temp_key.key_low, 4);
+ memcpy(newpw.contents + 4, &temp_key.key_high, 4);
+ if (retval = krb5_dbe_find_enctype(kadm_context,
+ &newentry,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey)) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ memset((char *)&temp_key, 0, sizeof (temp_key));
+ failmod(retval);
+ }
+ if (pkey->key_data_contents[0]) {
+ krb5_xfree(pkey->key_data_contents[0]);
+ pkey->key_data_contents[0] = (krb5_octet *) NULL;
+ }
+ /* encrypt new key in master key */
+ sblock.type = KRB5_KDB_SALTTYPE_V4;
+ sblock.data.length = 0;
+ sblock.data.data = (char *) NULL;
+ retval = krb5_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &newpw,
+ &sblock,
+ (int) pkey->key_data_kvno+1,
+ pkey);
+ memset(newpw.contents, 0, newpw.length);
+ free(newpw.contents);
+ memset((char *)&temp_key, 0, sizeof(temp_key));
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failmod(retval);
+ }
+ }
+ if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failmod(retval);
+ }
+ retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm,
+ &mprinc.mod_princ);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failmod(retval);
+ }
+
+ retval = krb5_dbe_encode_mod_princ_data(kadm_context,
+ &mprinc,
+ &newentry);
+ krb5_free_principal(kadm_context, mprinc.mod_princ);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failmod(retval);
+ }
+
+ numfound = 1;
+ retval = krb5_db_put_principal(kadm_context, &newentry, &numfound);
+ memset((char *)&data_o, 0, sizeof(data_o));
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ failmod(retval);
+ } else {
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, newentry.princ, &odata,
+ &numfound, &more);
+ krb5_db_free_principal(kadm_context, &newentry, 1);
+ if (retval) {
+ failmod(retval);
+ } else if (numfound != 1 || more) {
+ krb5_db_free_principal(kadm_context, &odata, numfound);
+ failmod(KADM_UK_RERROR);
+ }
+ retval = kadm_entry2princ(odata, &data_o);
+ krb5_db_free_principal(kadm_context, &odata, 1);
+ if (retval)
+ failmod(retval);
+ memset((char *) fields, 0, sizeof(fields));
+ SET_FIELD(KADM_NAME,fields);
+ SET_FIELD(KADM_INST,fields);
+ SET_FIELD(KADM_EXPDATE,fields);
+ SET_FIELD(KADM_ATTR,fields);
+ SET_FIELD(KADM_MAXLIFE,fields);
+ kadm_prin_to_vals(fields, valsout, &data_o);
+ syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance);
+ return KADM_DATA; /* Set all the appropriate fields */
+ }
+ } else {
+ failmod(KADM_NOENTRY);
+ }
+}
+#undef failmod
+
+#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; }
+
+kadm_change (rname, rinstance, rrealm, newpw)
+char *rname;
+char *rinstance;
+char *rrealm;
+des_cblock newpw;
+{
+ int numfound;
+ krb5_boolean more;
+ krb5_principal rprinc;
+ krb5_error_code retval;
+ krb5_keyblock localpw;
+ krb5_db_entry odata;
+ krb5_key_data *pkey;
+ krb5_keysalt sblock;
+
+ if (strcmp(server_parm.krbrlm, rrealm)) {
+ syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n",
+ rname, rinstance, rrealm);
+ return(KADM_WRONG_REALM);
+ }
+
+ if (wildcard(rname) || wildcard(rinstance)) {
+ failchange(KADM_ILL_WILDCARD);
+ }
+ syslog(LOG_INFO, "'%s.%s@%s' wants to change its password",
+ rname, rinstance, rrealm);
+ retval = krb5_425_conv_principal(kadm_context, rname, rinstance,
+ server_parm.krbrlm, &rprinc);
+ if (retval)
+ failchange(retval);
+ if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL)
+ failchange(KADM_NOMEM);
+ memcpy(localpw.contents, newpw, 8);
+ localpw.magic = KV5M_KEYBLOCK;
+ localpw.enctype = ENCTYPE_DES_CBC_CRC;
+ localpw.length = 8;
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, rprinc, &odata,
+ &numfound, &more);
+ krb5_free_principal(kadm_context, rprinc);
+ if (retval) {
+ memset(localpw.contents, 0, localpw.length);
+ free(localpw.contents);
+ failchange(retval);
+ } else if (numfound == 1) {
+ if (retval = krb5_dbe_find_enctype(kadm_context,
+ &odata,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey)) {
+ failchange(retval);
+ }
+ pkey->key_data_kvno++;
+ pkey->key_data_kvno %= 256;
+ numfound = 1;
+ sblock.type = KRB5_KDB_SALTTYPE_V4;
+ sblock.data.length = 0;
+ sblock.data.data = (char *) NULL;
+ retval = krb5_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &localpw,
+ &sblock,
+ (int) pkey->key_data_kvno,
+ pkey);
+ memset(localpw.contents, 0, localpw.length);
+ free(localpw.contents);
+ if (retval) {
+ failchange(retval);
+ }
+ retval = krb5_db_put_principal(kadm_context, &odata, &numfound);
+ krb5_db_free_principal(kadm_context, &odata, 1);
+ if (retval) {
+ failchange(retval);
+ } else if (more) {
+ failchange(KADM_UK_SERROR);
+ } else {
+ syslog(LOG_INFO,
+ "'%s.%s@%s' password changed.", rname, rinstance, rrealm);
+ return KADM_SUCCESS;
+ }
+ }
+ else {
+ failchange(KADM_NOENTRY);
+ }
+}
+#undef failchange
+
+check_pw(newpw, checkstr)
+ des_cblock newpw;
+ char *checkstr;
+{
+#ifdef NOENCRYPTION
+ return 0;
+#else /* !NOENCRYPTION */
+ des_cblock checkdes;
+
+ (void) des_string_to_key(checkstr, checkdes);
+ return(!memcmp(checkdes, newpw, sizeof(des_cblock)));
+#endif /* NOENCRYPTION */
+}
+
+char *reverse(str)
+ char *str;
+{
+ static char newstr[80];
+ char *p, *q;
+ int i;
+
+ i = strlen(str);
+ if (i >= sizeof(newstr))
+ i = sizeof(newstr)-1;
+ p = str+i-1;
+ q = newstr;
+ q[i]='\0';
+ for(; i > 0; i--)
+ *q++ = *p--;
+
+ return(newstr);
+}
+
+int lower(str)
+ char *str;
+{
+ register char *cp;
+ int effect=0;
+
+ for (cp = str; *cp; cp++) {
+ if (isupper(*cp)) {
+ *cp = tolower(*cp);
+ effect++;
+ }
+ }
+ return(effect);
+}
+
+des_check_gecos(gecos, newpw)
+ char *gecos;
+ des_cblock newpw;
+{
+ char *cp, *ncp, *tcp;
+
+ for (cp = gecos; *cp; ) {
+ /* Skip past punctuation */
+ for (; *cp; cp++)
+ if (isalnum(*cp))
+ break;
+ /* Skip to the end of the word */
+ for (ncp = cp; *ncp; ncp++)
+ if (!isalnum(*ncp) && *ncp != '\'')
+ break;
+ /* Delimit end of word */
+ if (*ncp)
+ *ncp++ = '\0';
+ /* Check word to see if it's the password */
+ if (*cp) {
+ if (check_pw(newpw, cp))
+ return(KADM_INSECURE_PW);
+ tcp = reverse(cp);
+ if (check_pw(newpw, tcp))
+ return(KADM_INSECURE_PW);
+ if (lower(cp)) {
+ if (check_pw(newpw, cp))
+ return(KADM_INSECURE_PW);
+ tcp = reverse(cp);
+ if (check_pw(newpw, tcp))
+ return(KADM_INSECURE_PW);
+ }
+ cp = ncp;
+ } else
+ break;
+ }
+ return(0);
+}
+
+str_check_gecos(gecos, pwstr)
+ char *gecos;
+ char *pwstr;
+{
+ char *cp, *ncp, *tcp;
+
+ for (cp = gecos; *cp; ) {
+ /* Skip past punctuation */
+ for (; *cp; cp++)
+ if (isalnum(*cp))
+ break;
+ /* Skip to the end of the word */
+ for (ncp = cp; *ncp; ncp++)
+ if (!isalnum(*ncp) && *ncp != '\'')
+ break;
+ /* Delimit end of word */
+ if (*ncp)
+ *ncp++ = '\0';
+ /* Check word to see if it's the password */
+ if (*cp) {
+ if (!strcasecmp(pwstr, cp))
+ return(KADM_INSECURE_PW);
+ tcp = reverse(cp);
+ if (!strcasecmp(pwstr, tcp))
+ return(KADM_INSECURE_PW);
+ cp = ncp;
+ } else
+ break;
+ }
+ return(0);
+}
+
+
+kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring)
+char *rname;
+char *rinstance;
+char *rrealm;
+des_cblock newpw;
+char *pwstring;
+{
+ static DBM *pwfile = NULL;
+ int retval;
+ datum passwd, entry;
+ struct passwd *ent;
+#ifdef HESIOD
+ extern struct passwd *hes_getpwnam();
+#endif
+
+ if (pwstring && !check_pw(newpw, pwstring))
+ /*
+ * Someone's trying to toy with us....
+ */
+ return(KADM_PW_MISMATCH);
+ if (pwstring && (strlen(pwstring) < 5))
+ return(KADM_INSECURE_PW);
+ if (!pwfile) {
+ pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644);
+ }
+ if (pwfile) {
+ passwd.dptr = (char *) newpw;
+ passwd.dsize = 8;
+ entry = dbm_fetch(pwfile, passwd);
+ if (entry.dptr)
+ return(KADM_INSECURE_PW);
+ }
+ if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname)))
+ return(KADM_INSECURE_PW);
+#ifdef HESIOD
+ ent = hes_getpwnam(rname);
+#else
+ ent = getpwnam(rname);
+#endif
+ if (ent && ent->pw_gecos) {
+ if (pwstring)
+ retval = str_check_gecos(ent->pw_gecos, pwstring);
+ else
+ retval = des_check_gecos(ent->pw_gecos, newpw);
+ if (retval)
+ return(retval);
+ }
+ return(0);
+}
+
+/*
+ * This routine checks to see if a principal should be considered an
+ * allowable service name which can be changed by kadm_change_srvtab.
+ *
+ * We do this check by using the ACL library. This makes the
+ * (relatively) reasonable assumption that both the name and the
+ * instance will not contain '.' or '@'.
+ */
+kadm_check_srvtab(name, instance)
+ char *name;
+ char *instance;
+{
+ char filename[MAXPATHLEN];
+ extern char *acldir;
+
+ (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE);
+ if (!acl_check(filename, name))
+ return(KADM_NOT_SERV_PRINC);
+
+ (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE);
+ if (acl_check(filename, instance))
+ return(KADM_NOT_SERV_PRINC);
+ return 0;
+}
+
+/*
+ * Routine to allow some people to change the key of a srvtab
+ * principal to a random key, which the admin server will return to
+ * the client.
+ */
+#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; }
+
+kadm_chg_srvtab(rname, rinstance, rrealm, values)
+ char *rname; /* requestors name */
+ char *rinstance; /* requestors instance */
+ char *rrealm; /* requestors realm */
+ Kadm_vals *values;
+{
+ int numfound, ret, isnew = 0;
+ des_cblock new_key;
+ krb5_principal inprinc;
+ krb5_error_code retval;
+ krb5_db_entry odata;
+ krb5_boolean more;
+ krb5_keyblock newpw;
+ krb5_key_data *pkey;
+
+ if (!check_access(rname, rinstance, rrealm, STABACL))
+ failsrvtab(KADM_UNAUTH);
+ if (wildcard(rname) || wildcard(rinstance))
+ failsrvtab(KADM_ILL_WILDCARD);
+ if (ret = kadm_check_srvtab(values->name, values->instance))
+ failsrvtab(ret);
+
+ retval = krb5_425_conv_principal(kadm_context, values->name,
+ values->instance,
+ server_parm.krbrlm, &inprinc);
+ if (retval)
+ failsrvtab(retval);
+ /*
+ * OK, get the entry
+ */
+ numfound = 1;
+ retval = krb5_db_get_principal(kadm_context, inprinc, &odata,
+ &numfound, &more);
+ if (retval) {
+ krb5_free_principal(kadm_context, inprinc);
+ failsrvtab(retval);
+ } else if (numfound) {
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &odata,
+ ENCTYPE_DES_CBC_CRC,
+ KRB5_KDB_SALTTYPE_V4,
+ -1,
+ &pkey);
+ if (retval) {
+ krb5_free_principal(kadm_context, inprinc);
+ failsrvtab(retval);
+ }
+ }
+ else {
+ /*
+ * This is a new srvtab entry that we're creating
+ */
+ isnew = 1;
+ memset((char *)&odata, 0, sizeof (odata));
+ odata.princ = inprinc;
+ odata.max_life = server_parm.max_life;
+ odata.max_renewable_life = server_parm.max_rlife;
+ odata.expiration = server_parm.expiration;
+ odata.attributes = 0;
+ if (!krb5_dbe_create_key_data(kadm_context, &odata)) {
+ pkey = &odata.key_data[0];
+ memset(pkey, 0, sizeof(*pkey));
+ pkey->key_data_ver = 2;
+ pkey->key_data_type[0] = ENCTYPE_DES_CBC_CRC;
+ pkey->key_data_type[1] = KRB5_KDB_SALTTYPE_V4;
+ }
+ }
+ pkey->key_data_kvno++;
+
+#ifdef NOENCRYPTION
+ memset(new_key, 0, sizeof(new_key));
+ new_key[0] = 127;
+#else
+ des_new_random_key(new_key);
+#endif
+ /*
+ * Store the new key in the return structure; also fill in the
+ * rest of the fields.
+ */
+ if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) {
+ krb5_db_free_principal(kadm_context, &odata, 1);
+ failsrvtab(KADM_NOMEM);
+ }
+ newpw.enctype = ENCTYPE_DES_CBC_CRC;
+ newpw.length = 8;
+ memcpy((char *)newpw.contents, new_key, 8);
+ memset((char *)new_key, 0, sizeof (new_key));
+ memcpy((char *)&values->key_low, newpw.contents, 4);
+ memcpy((char *)&values->key_high, newpw.contents + 4, 4);
+ values->key_low = htonl(values->key_low);
+ values->key_high = htonl(values->key_high);
+ values->max_life = odata.max_life / (60 * 5);
+ values->exp_date = odata.expiration;
+ values->attributes = odata.attributes;
+ memset(values->fields, 0, sizeof(values->fields));
+ SET_FIELD(KADM_NAME, values->fields);
+ SET_FIELD(KADM_INST, values->fields);
+ SET_FIELD(KADM_EXPDATE, values->fields);
+ SET_FIELD(KADM_ATTR, values->fields);
+ SET_FIELD(KADM_MAXLIFE, values->fields);
+ SET_FIELD(KADM_DESKEY, values->fields);
+
+ /*
+ * Encrypt the new key with the master key, and then update
+ * the database record
+ */
+ retval = krb5_dbekd_encrypt_key_data(kadm_context,
+ &server_parm.master_encblock,
+ &newpw,
+ (krb5_keysalt *) NULL,
+ (int) pkey->key_data_kvno,
+ pkey);
+ memset((char *)newpw.contents, 0, 8);
+ free(newpw.contents);
+ if (retval) {
+ krb5_db_free_principal(kadm_context, &odata, 1);
+ failsrvtab(retval);
+ }
+ numfound = 1;
+ retval = krb5_db_put_principal(kadm_context, &odata, &numfound);
+ krb5_db_free_principal(kadm_context, &odata, 1);
+ if (retval) {
+ failsrvtab(retval);
+ }
+ else if (!numfound) {
+ failsrvtab(KADM_UK_SERROR);
+ } else {
+ syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.",
+ values->name, values->instance,
+ numfound ? "changed" : "created",
+ rname, rinstance, rrealm);
+ return KADM_DATA;
+ }
+}
+
+#undef failsrvtab
diff --git a/src/kadmin/v4server/kadm_ser_wrap.c b/src/kadmin/v4server/kadm_ser_wrap.c
new file mode 100644
index 000000000..5e7f48508
--- /dev/null
+++ b/src/kadmin/v4server/kadm_ser_wrap.c
@@ -0,0 +1,310 @@
+/*
+ * kadmin/v4server/kadm_ser_wrap.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side support functions
+ */
+
+
+#include <mit-copyright.h>
+/*
+kadm_ser_wrap.c
+unwraps wrapped packets and calls the appropriate server subroutine
+*/
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include "kadm_server.h"
+#include <kadm.h>
+#include <kadm_err.h>
+#include <krb_err.h>
+#include <syslog.h>
+
+#ifdef OVSEC_KADM
+#include <kadm5/admin.h>
+extern void *ovsec_handle;
+#endif
+
+Kadm_Server server_parm;
+
+/*
+kadm_ser_init
+set up the server_parm structure
+*/
+#ifdef OVSEC_KADM
+kadm_ser_init(inter, realm, params)
+ int inter; /* interactive or from file */
+ char realm[];
+ kadm5_config_params *params;
+#else
+kadm_ser_init(inter, realm)
+ int inter; /* interactive or from file */
+ char realm[];
+#endif
+{
+ struct servent *sep;
+ struct hostent *hp;
+ char hostname[MAXHOSTNAMELEN];
+ char *mkey_name;
+ krb5_error_code retval;
+ int numfound = 1;
+ krb5_boolean more;
+ krb5_db_entry master_entry;
+ krb5_key_data *kdatap;
+
+ if (gethostname(hostname, sizeof(hostname)))
+ return KADM_NO_HOSTNAME;
+
+ (void) strcpy(server_parm.sname, PWSERV_NAME);
+ (void) strcpy(server_parm.sinst, KRB_MASTER);
+ (void) strcpy(server_parm.krbrlm, realm);
+ if (krb5_build_principal(kadm_context,
+ &server_parm.sprinc,
+ strlen(realm),
+ realm,
+ PWSERV_NAME,
+ KRB_MASTER, 0))
+ return KADM_NO_MAST;
+ server_parm.admin_fd = -1;
+ /* setting up the addrs */
+ if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL)
+ return KADM_NO_SERV;
+ memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr));
+ server_parm.admin_addr.sin_family = AF_INET;
+ if ((hp = gethostbyname(hostname)) == NULL)
+ return KADM_NO_HOSTNAME;
+ memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr,
+ hp->h_length);
+ server_parm.admin_addr.sin_port = sep->s_port;
+ /* setting up the database */
+ mkey_name = KRB5_KDB_M_NAME;
+
+#ifdef OVSEC_KADM
+ server_parm.master_keyblock.enctype = params->enctype;
+ krb5_use_enctype(kadm_context, &server_parm.master_encblock,
+ server_parm.master_keyblock.enctype);
+#else
+ if (inter == 1) {
+ server_parm.master_keyblock.enctype = ENCTYPE_DES_CBC_MD5;
+ krb5_use_enctype(kadm_context, &server_parm.master_encblock,
+ server_parm.master_keyblock.enctype);
+ } else
+ server_parm.master_keyblock.enctype = ENCTYPE_UNKNOWN;
+#endif
+
+ retval = krb5_db_setup_mkey_name(kadm_context, mkey_name, realm,
+ (char **) 0,
+ &server_parm.master_princ);
+ if (retval)
+ return KADM_NO_MAST;
+ krb5_db_fetch_mkey(kadm_context, server_parm.master_princ,
+ &server_parm.master_encblock,
+ (inter == 1), FALSE,
+#ifdef OVSEC_KADM
+ params->stash_file,
+#else
+ (char *) NULL,
+#endif
+ NULL,
+ &server_parm.master_keyblock);
+ if (retval)
+ return KADM_NO_MAST;
+ retval = krb5_db_verify_master_key(kadm_context, server_parm.master_princ,
+ &server_parm.master_keyblock,
+ &server_parm.master_encblock);
+ if (retval)
+ return KADM_NO_VERI;
+ retval = krb5_process_key(kadm_context, &server_parm.master_encblock,
+ &server_parm.master_keyblock);
+ if (retval)
+ return KADM_NO_VERI;
+ retval = krb5_db_get_principal(kadm_context, server_parm.master_princ,
+ &master_entry, &numfound, &more);
+ if (retval || more || !numfound)
+ return KADM_NO_VERI;
+
+ retval = krb5_dbe_find_enctype(kadm_context,
+ &master_entry,
+ -1, -1, -1,
+ &kdatap);
+ if (retval)
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+ server_parm.max_life = master_entry.max_life;
+ server_parm.max_rlife = master_entry.max_renewable_life;
+ server_parm.expiration = master_entry.expiration;
+ server_parm.mkvno = kdatap->key_data_kvno;
+ /* don't set flags, as master has some extra restrictions
+ (??? quoted from kdb_edit.c) */
+ krb5_db_free_principal(kadm_context, &master_entry, numfound);
+ return KADM_SUCCESS;
+}
+
+
+static void errpkt(dat, dat_len, code)
+u_char **dat;
+int *dat_len;
+int code;
+{
+ krb5_ui_4 retcode;
+ char *pdat;
+
+ free((char *)*dat); /* free up req */
+ *dat_len = KADM_VERSIZE + sizeof(krb5_ui_4);
+ *dat = (u_char *) malloc((unsigned)*dat_len);
+ if (!(*dat)) {
+ syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len);
+ abort();
+ }
+ pdat = (char *) *dat;
+ retcode = htonl((krb5_ui_4) code);
+ (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE);
+ memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4));
+ return;
+}
+
+/*
+kadm_ser_in
+unwrap the data stored in dat, process, and return it.
+*/
+kadm_ser_in(dat,dat_len)
+u_char **dat;
+int *dat_len;
+{
+ u_char *in_st; /* pointer into the sent packet */
+ int in_len,retc; /* where in packet we are, for
+ returns */
+ krb5_ui_4 r_len; /* length of the actual packet */
+ KTEXT_ST authent; /* the authenticator */
+ AUTH_DAT ad; /* who is this, klink */
+ krb5_ui_4 ncksum; /* checksum of encrypted data */
+ des_key_schedule sess_sched; /* our schedule */
+ MSG_DAT msg_st;
+ u_char *retdat, *tmpdat;
+ int retval, retlen;
+
+ if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) {
+ errpkt(dat, dat_len, KADM_BAD_VER);
+ return KADM_BAD_VER;
+ }
+ in_len = KADM_VERSIZE;
+ /* get the length */
+ if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0)
+ return KADM_LENGTH_ERROR;
+ in_len += retc;
+ authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb5_ui_4);
+ memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length);
+ authent.mbz = 0;
+ /* service key should be set before here */
+ if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst,
+ server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0))
+ {
+ errpkt(dat, dat_len,retc + krb_err_base);
+ return retc + krb_err_base;
+ }
+
+#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));}
+
+ in_st = *dat + *dat_len - r_len;
+#ifdef NOENCRYPTION
+ ncksum = 0;
+#else
+ ncksum = quad_cksum(in_st, (krb5_ui_4 *)0, (long) r_len, 0, ad.session);
+#endif
+ if (ncksum!=ad.checksum) { /* yow, are we correct yet */
+ clr_cli_secrets();
+ errpkt(dat, dat_len,KADM_BAD_CHK);
+ return KADM_BAD_CHK;
+ }
+#ifdef NOENCRYPTION
+ memset(sess_sched, 0, sizeof(sess_sched));
+#else
+ des_key_sched(ad.session, sess_sched);
+#endif
+ if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session,
+ &server_parm.recv_addr,
+ &server_parm.admin_addr, &msg_st)) {
+ clr_cli_secrets();
+ errpkt(dat, dat_len,retc + krb_err_base);
+ return retc + krb_err_base;
+ }
+ switch (msg_st.app_data[0]) {
+ case CHANGE_PW:
+ retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+#ifndef OVSEC_KADM
+ case ADD_ENT:
+ retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case DEL_ENT:
+ retval = kadm_ser_del(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case GET_ENT:
+ retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case MOD_ENT:
+ retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case CHECK_PW:
+ retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+ case CHG_STAB:
+ retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad,
+ &retdat, &retlen);
+ break;
+#endif /* OVSEC_KADM */
+ default:
+ clr_cli_secrets();
+ errpkt(dat, dat_len, KADM_NO_OPCODE);
+ return KADM_NO_OPCODE;
+ }
+ /* Now seal the response back into a priv msg */
+ free((char *)*dat);
+ tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE +
+ sizeof(krb5_ui_4)));
+ if (!tmpdat) {
+ clr_cli_secrets();
+ syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!",
+ retlen + KADM_VERSIZE + sizeof(krb5_ui_4));
+ errpkt(dat, dat_len, KADM_NOMEM);
+ return KADM_NOMEM;
+ }
+ (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE);
+ retval = htonl((krb5_ui_4)retval);
+ memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb5_ui_4));
+ if (retlen) {
+ memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb5_ui_4), (char *)retdat,
+ retlen);
+ free((char *)retdat);
+ }
+ /* slop for mk_priv stuff */
+ *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE +
+ sizeof(krb5_ui_4) + 200));
+ if ((*dat_len = krb_mk_priv(tmpdat, *dat,
+ (u_long) (retlen + KADM_VERSIZE +
+ sizeof(krb5_ui_4)),
+ sess_sched,
+ ad.session, &server_parm.admin_addr,
+ &server_parm.recv_addr)) < 0) {
+ clr_cli_secrets();
+ errpkt(dat, dat_len, KADM_NO_ENCRYPT);
+ return KADM_NO_ENCRYPT;
+ }
+ clr_cli_secrets();
+ return KADM_SUCCESS;
+}
diff --git a/src/kadmin/v4server/kadm_server.c b/src/kadmin/v4server/kadm_server.c
new file mode 100644
index 000000000..81e43f128
--- /dev/null
+++ b/src/kadmin/v4server/kadm_server.c
@@ -0,0 +1,571 @@
+/*
+ * kadmin/v4server/kadm_server.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Kerberos administration server-side subroutines
+ */
+
+
+#include <mit-copyright.h>
+
+#include "k5-int.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#ifdef TIME_WITH_SYS_TIME
+#include <time.h>
+#endif
+#else
+#include <time.h>
+#endif
+
+#ifdef OVSEC_KADM
+#include <com_err.h>
+#include <kadm5/admin.h>
+#include <kadm5/chpass_util_strings.h>
+#include <krb5/kdb.h>
+extern void *ovsec_handle;
+#endif
+
+#include <kadm.h>
+#include <kadm_err.h>
+
+extern krb5_context kadm_context;
+int fascist_cpw = 0; /* Be fascist about insecure passwords? */
+
+#ifdef OVSEC_KADM
+char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n";
+
+#else /* !OVSEC_KADM */
+
+char bad_pw_err[] =
+ "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n";
+
+char bad_pw_warn[] =
+ "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n";
+
+char check_pw_msg[] =
+ "You have entered an insecure password. You should choose another.\n\n";
+
+char pw_blurb[] =
+"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n";
+
+#endif /* OVSEC_KADM */
+
+/* from V4 month_sname.c -- was not part of API */
+/*
+ * Given an integer 1-12, month_sname() returns a string
+ * containing the first three letters of the corresponding
+ * month. Returns 0 if the argument is out of range.
+ */
+
+static char *month_sname(n)
+ int n;
+{
+ static char *name[] = {
+ "Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec"
+ };
+ return((n < 1 || n > 12) ? 0 : name [n-1]);
+}
+
+/* from V4 log.c -- was not part of API */
+
+/*
+ * krb_log() is used to add entries to the logfile (see krb_set_logfile()
+ * below). Note that it is probably not portable since it makes
+ * assumptions about what the compiler will do when it is called
+ * with less than the correct number of arguments which is the
+ * way it is usually called.
+ *
+ * The log entry consists of a timestamp and the given arguments
+ * printed according to the given "format".
+ *
+ * The log file is opened and closed for each log entry.
+ *
+ * The return value is undefined.
+ */
+
+/* static char *log_name = KRBLOG; */
+/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */
+static char *log_name = KADM_SYSLOG;
+
+static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0)
+ char *format;
+ char *a1,*a2,*a3,*a4,*a5,*a6,*a7,*a8,*a9,*a0;
+{
+ FILE *logfile;
+ time_t now;
+ struct tm *tm;
+
+ if ((logfile = fopen(log_name,"a")) == NULL)
+ return;
+
+ (void) time(&now);
+ tm = localtime(&now);
+
+ fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday,
+ month_sname(tm->tm_mon + 1),tm->tm_year,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0);
+ fprintf(logfile,"\n");
+ (void) fclose(logfile);
+ return;
+}
+
+
+/*
+kadm_ser_cpw - the server side of the change_password routine
+ recieves : KTEXT, {key}
+ returns : CKSUM, RETCODE
+ acl : caller can change only own password
+
+Replaces the password (i.e. des key) of the caller with that specified in key.
+Returns no actual data from the master server, since this is called by a user
+*/
+kadm_ser_cpw(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ krb5_int32 keylow, keyhigh;
+ char pword[MAX_KPW_LEN];
+ int no_pword = 0;
+ des_cblock newkey;
+ int status, stvlen = 0;
+ int retval;
+ extern int kadm_approve_pw();
+#ifdef OVSEC_KADM
+ ovsec_kadm_principal_ent_t princ_ent;
+ ovsec_kadm_policy_ent_t pol_ent;
+ krb5_principal user_princ;
+ char msg_ret[1024], *time_string, *ptr;
+ const char *msg_ptr;
+ krb5_int32 now;
+ time_t until;
+#endif
+
+ /* take key off the stream, and change the database */
+
+ if ((status = stv_long(dat, &keyhigh, 0, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((status = stv_long(dat, &keylow, stvlen, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) {
+ no_pword++;
+ pword[0]='\0';
+ }
+ stvlen += status;
+
+ keylow = ntohl(keylow);
+ keyhigh = ntohl(keyhigh);
+ memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4);
+ memcpy((char *)newkey, (char *)&keylow, 4);
+
+#ifdef OVSEC_KADM
+ /* we don't use the client-provided key itself */
+ keylow = keyhigh = 0;
+ memset(newkey, 0, sizeof(newkey));
+
+ if (no_pword) {
+ krb_log("Old-style change password request from '%s.%s@%s'!",
+ ad->pname, ad->pinst, ad->prealm);
+ *outlen = strlen(pw_required)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, pw_required);
+ } else {
+ *outlen = 0;
+ }
+ return KADM_INSECURE_PW;
+ }
+
+ if (krb5_build_principal(kadm_context, &user_princ,
+ strlen(ad->prealm),
+ ad->prealm,
+ ad->pname,
+ *ad->pinst ? ad->pinst : 0, 0))
+ /* this should never happen */
+ return KADM_NOENTRY;
+
+ *outlen = 0;
+
+ if (retval = krb5_timeofday(kadm_context, &now)) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ retval = ovsec_kadm_get_principal(ovsec_handle, user_princ,
+ &princ_ent);
+ if (retval != 0) {
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /*
+ * This daemon necessarily has the modify privilege, so
+ * ovsec_kadm_chpass_principal will allow it to violate the
+ * policy's minimum lifetime. Since that's A Bad Thing, we need
+ * to enforce it ourselves. Unfortunately, this means we are
+ * duplicating code from both ovsec_adm_server and
+ * ovsec_kadm_chpass_util().
+ */
+ if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) {
+ retval = ovsec_kadm_get_policy(ovsec_handle,
+ princ_ent->policy,
+ &pol_ent);
+ if (retval != 0) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ msg_ptr = error_message(retval);
+ goto send_response;
+ }
+
+ /* make "now" a boolean, true == too soon */
+ now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life);
+
+ (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent);
+
+ if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+ retval = CHPASS_UTIL_PASSWORD_TOO_SOON;
+
+ until = princ_ent->last_pwd_change + pol_ent->pw_min_life;
+ time_string = ctime(&until);
+
+ if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
+ *ptr = '\0';
+
+ sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON),
+ time_string);
+ msg_ptr = msg_ret;
+
+ goto send_response;
+ }
+ }
+
+ (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent);
+
+ retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ,
+ pword, NULL, msg_ret);
+ msg_ptr = msg_ret;
+ (void) krb5_free_principal(kadm_context, user_princ);
+
+send_response:
+
+ retval = convert_ovsec_to_kadm(retval);
+
+ if (retval) {
+ /* don't send message on success because kpasswd.v4 will */
+ /* print "password changed" too */
+ *outlen = strlen(msg_ptr)+2;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy(*datout, msg_ptr);
+ strcat(*datout, "\n");
+ } else
+ *outlen = 0;
+ }
+ if (retval == KADM_INSECURE_PW) {
+ krb_log("'%s.%s@%s' tried to use an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+#else /* OVSEC_KADM */
+ if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm,
+ newkey, no_pword ? 0 : pword)) {
+ if (retval == KADM_PW_MISMATCH) {
+ /*
+ * Very strange!!! This means that the cleartext
+ * password which was sent and the DES cblock
+ * didn't match!
+ */
+ (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?",
+ ad->pname, ad->pinst, ad->prealm);
+ return(retval);
+ }
+ if (fascist_cpw) {
+ *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy((char *) *datout, bad_pw_err);
+ strcat((char *) *datout, pw_blurb);
+ } else
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+#ifdef notdef
+ /* For debugging only, probably a bad idea */
+ if (!no_pword)
+ (void) krb_log("The password was %s\n", pword);
+#endif
+ return(retval);
+ } else {
+ *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy((char *) *datout, bad_pw_warn);
+ strcat((char *) *datout, pw_blurb);
+ } else
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' used an insecure password in changepw",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+ } else {
+ *datout = 0;
+ *outlen = 0;
+ }
+
+ retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey);
+ keylow = keyhigh = 0;
+ memset(newkey, 0, sizeof(newkey));
+#endif /* OVSEC_KADM */
+
+ return retval;
+}
+
+#ifndef OVSEC_KADM
+/*
+kadm_ser_add - the server side of the add_entry routine
+ recieves : KTEXT, {values}
+ returns : CKSUM, RETCODE, {values}
+ acl : su, sms (as alloc)
+
+Adds and entry containing values to the database
+returns the values of the entry, so if you leave certain fields blank you will
+ be able to determine the default values they are set to
+*/
+int
+kadm_ser_add(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals values, retvals;
+ int status;
+
+ if ((status = stream_to_vals(dat, &values, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm,
+ &values, &retvals)) == KADM_DATA) {
+ *outlen = vals_to_stream(&retvals,datout);
+ retvals.key_low = retvals.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+/*
+kadm_ser_del - the server side of the del_entry routine
+ recieves : KTEXT, {values}
+ returns : CKSUM, RETCODE, {values}
+ acl : su, sms (as alloc)
+
+Deletes an entry containing values to the database
+returns the values of the entry, so if you leave certain fields blank you will
+ be able to determine the default values they are set to
+*/
+kadm_ser_del(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals values, retvals;
+ int status;
+
+ if ((status = stream_to_vals(dat, &values, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ if ((status = kadm_del_entry(ad->pname, ad->pinst, ad->prealm,
+ &values, &retvals)) == KADM_DATA) {
+ *outlen = vals_to_stream(&retvals,datout);
+ retvals.key_low = retvals.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+/*
+kadm_ser_mod - the server side of the mod_entry routine
+ recieves : KTEXT, {values, values}
+ returns : CKSUM, RETCODE, {values}
+ acl : su, sms (as register or dealloc)
+
+Modifies all entries corresponding to the first values so they match the
+ second values.
+returns the values for the changed entries
+*/
+kadm_ser_mod(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals vals1, vals2, retvals;
+ int wh;
+ int status;
+
+ if ((wh = stream_to_vals(dat, &vals1, len)) < 0)
+ return KADM_LENGTH_ERROR;
+ if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0)
+ return KADM_LENGTH_ERROR;
+ if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1,
+ &vals2, &retvals)) == KADM_DATA) {
+ *outlen = vals_to_stream(&retvals,datout);
+ retvals.key_low = retvals.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+/*
+kadm_ser_get
+ recieves : KTEXT, {values, flags}
+ returns : CKSUM, RETCODE, {count, values, values, values}
+ acl : su
+
+gets the fields requested by flags from all entries matching values
+returns this data for each matching recipient, after a count of how many such
+ matches there were
+*/
+kadm_ser_get(dat,len,ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals values, retvals;
+ u_char fl[FLDSZ];
+ int loop,wh;
+ int status;
+
+ if ((wh = stream_to_vals(dat, &values, len)) < 0)
+ return KADM_LENGTH_ERROR;
+ if (wh + FLDSZ > len)
+ return KADM_LENGTH_ERROR;
+ for (loop=FLDSZ-1; loop>=0; loop--)
+ fl[loop] = dat[wh++];
+ if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm,
+ &values, fl, &retvals)) == KADM_DATA) {
+ *outlen = vals_to_stream(&retvals,datout);
+ retvals.key_low = retvals.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+
+/*
+kadm_ser_ckpw - the server side of the check_password routine
+ recieves : KTEXT, {key}
+ returns : CKSUM, RETCODE
+ acl : none
+
+Checks to see if the des key passed from the caller is a "secure" password.
+*/
+kadm_ser_ckpw(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ krb5_ui_4 keylow, keyhigh;
+ char pword[MAX_KPW_LEN];
+ int no_pword = 0;
+ des_cblock newkey;
+ int stvlen = 0,status;
+ int retval;
+ extern int kadm_approve_pw();
+
+ /* take key off the stream, and check it */
+
+ if ((status = stv_long(dat, &keyhigh, 0, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((status = stv_long(dat, &keylow, stvlen, len)) < 0)
+ return(KADM_LENGTH_ERROR);
+ stvlen += status;
+ if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) {
+ no_pword++;
+ pword[0]='\0';
+ }
+ stvlen += status;
+
+ keylow = ntohl(keylow);
+ keyhigh = ntohl(keyhigh);
+ memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4);
+ memcpy((char *)newkey, (char *)&keylow, 4);
+ keylow = keyhigh = 0;
+ retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey,
+ no_pword ? 0 : pword);
+ memset(newkey, 0, sizeof(newkey));
+ if (retval) {
+ *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1;
+ if (*datout = (u_char *) malloc(*outlen)) {
+ strcpy((char *) *datout, check_pw_msg);
+ strcat((char *) *datout, pw_blurb);
+ } else
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' sent an insecure password to be checked",
+ ad->pname, ad->pinst, ad->prealm);
+ return(retval);
+ } else {
+ *datout = 0;
+ *outlen = 0;
+ (void) krb_log("'%s.%s@%s' sent a secure password to be checked",
+ ad->pname, ad->pinst, ad->prealm);
+ }
+ return(0);
+}
+
+/*
+kadm_ser_stab - the server side of the change_srvtab routine
+ recieves : KTEXT, {values}
+ returns : CKSUM, RETCODE, {values}
+ acl : su, sms (as register or dealloc)
+
+Creates or modifies the specified service principal to have a random
+key, which is sent back to the client. The key version is returned in
+the max_life field of the values structure. It's a hack, but it's a
+backwards compatible hack....
+*/
+kadm_ser_stab(dat, len, ad, datout, outlen)
+u_char *dat;
+int len;
+AUTH_DAT *ad;
+u_char **datout;
+int *outlen;
+{
+ Kadm_vals values;
+ int status;
+
+ if ((status = stream_to_vals(dat, &values, len)) < 0)
+ return KADM_LENGTH_ERROR;
+ status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values);
+ if (status == KADM_DATA) {
+ *outlen = vals_to_stream(&values,datout);
+ values.key_low = values.key_high = 0;
+ return KADM_SUCCESS;
+ } else {
+ *outlen = 0;
+ return status;
+ }
+}
+#endif /* !OVSEC_KADM */
diff --git a/src/kadmin/v4server/kadm_server.h b/src/kadmin/v4server/kadm_server.h
new file mode 100644
index 000000000..d852bcaab
--- /dev/null
+++ b/src/kadmin/v4server/kadm_server.h
@@ -0,0 +1,58 @@
+/*
+ * kadmin/v4server/kadm_server.h
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Definitions for Kerberos administration server & client
+ */
+
+#ifndef KADM_SERVER_DEFS
+#define KADM_SERVER_DEFS
+
+#include <mit-copyright.h>
+/*
+ * kadm_server.h
+ * Header file for the fourth attempt at an admin server
+ * Doug Church, December 28, 1989, MIT Project Athena
+ * ps. Yes that means this code belongs to athena etc...
+ * as part of our ongoing attempt to copyright all greek names
+ */
+
+#include <sys/types.h>
+#include <krb.h>
+#include <des.h>
+#include "k5-int.h"
+
+typedef struct {
+ struct sockaddr_in admin_addr;
+ struct sockaddr_in recv_addr;
+ int recv_addr_len;
+ int admin_fd; /* our link to clients */
+ char sname[ANAME_SZ];
+ char sinst[INST_SZ];
+ char krbrlm[REALM_SZ];
+ krb5_principal sprinc;
+ krb5_encrypt_block master_encblock;
+ krb5_principal master_princ;
+ krb5_keyblock master_keyblock;
+ krb5_deltat max_life;
+ krb5_deltat max_rlife;
+ krb5_timestamp expiration;
+ krb5_flags flags;
+ krb5_kvno mkvno;
+} Kadm_Server;
+
+#define ADD_ACL_FILE "/v4acl.add"
+#define GET_ACL_FILE "/v4acl.get"
+#define MOD_ACL_FILE "/v4acl.mod"
+#define DEL_ACL_FILE "/v4acl.del"
+#define STAB_ACL_FILE "/v4acl.srvtab"
+#define STAB_SERVICES_FILE "/v4stab_services"
+#define STAB_HOSTS_FILE "/v4stab_bad_hosts"
+
+krb5_context kadm_context;
+
+#endif /* KADM_SERVER_DEFS */
diff --git a/src/kadmin/v4server/kadm_stream.c b/src/kadmin/v4server/kadm_stream.c
new file mode 100644
index 000000000..86da6c64f
--- /dev/null
+++ b/src/kadmin/v4server/kadm_stream.c
@@ -0,0 +1,277 @@
+/*
+ * kadmin/v4server/kadm_stream.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Stream conversion functions for Kerberos administration server
+ */
+
+
+#include <mit-copyright.h>
+#include <string.h>
+#include "k5-int.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#else
+extern char *malloc(), *calloc(), *realloc();
+#endif
+
+/*
+ kadm_stream.c
+ this holds the stream support routines for the kerberos administration server
+
+ vals_to_stream: converts a vals struct to a stream for transmission
+ internals build_field_header, vts_[string, char, long, short]
+ stream_to_vals: converts a stream to a vals struct
+ internals check_field_header, stv_[string, char, long, short]
+ error: prints out a kadm error message, returns
+ fatal: prints out a kadm fatal error message, exits
+*/
+
+#include "kadm.h"
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+
+/*
+vals_to_stream
+ recieves : kadm_vals *, u_char *
+ returns : a realloced and filled in u_char *
+
+this function creates a byte-stream representation of the kadm_vals structure
+*/
+vals_to_stream(dt_in, dt_out)
+Kadm_vals *dt_in;
+u_char **dt_out;
+{
+ int vsloop, stsize; /* loop counter, stream size */
+
+ stsize = build_field_header(dt_in->fields, dt_out);
+ for (vsloop=31; vsloop>=0; vsloop--)
+ if (IS_FIELD(vsloop,dt_in->fields)) {
+ switch (vsloop) {
+ case KADM_NAME:
+ stsize+=vts_string(dt_in->name, dt_out, stsize);
+ break;
+ case KADM_INST:
+ stsize+=vts_string(dt_in->instance, dt_out, stsize);
+ break;
+ case KADM_EXPDATE:
+ stsize+=vts_long(dt_in->exp_date, dt_out, stsize);
+ break;
+ case KADM_ATTR:
+ stsize+=vts_short(dt_in->attributes, dt_out, stsize);
+ break;
+ case KADM_MAXLIFE:
+ stsize+=vts_char(dt_in->max_life, dt_out, stsize);
+ break;
+ case KADM_DESKEY:
+ stsize+=vts_long(dt_in->key_high, dt_out, stsize);
+ stsize+=vts_long(dt_in->key_low, dt_out, stsize);
+ break;
+ default:
+ break;
+ }
+}
+ return(stsize);
+}
+
+build_field_header(cont, st)
+u_char *cont; /* container for fields data */
+u_char **st; /* stream */
+{
+ *st = (u_char *) malloc (4);
+ memcpy((char *) *st, (char *) cont, 4);
+ return 4; /* return pointer to current stream location */
+}
+
+vts_string(dat, st, loc)
+char *dat; /* a string to put on the stream */
+u_char **st; /* base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1));
+ memcpy((char *)(*st + loc), dat, strlen(dat)+1);
+ return strlen(dat)+1;
+}
+
+vts_short(dat, st, loc)
+u_short dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ u_short temp; /* to hold the net order short */
+
+ temp = htons(dat); /* convert to network order */
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short)));
+ memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short));
+ return sizeof(u_short);
+}
+
+vts_long(dat, st, loc)
+krb5_ui_4 dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ krb5_ui_4 temp; /* to hold the net order short */
+
+ temp = htonl(dat); /* convert to network order */
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb5_ui_4)));
+ memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb5_ui_4));
+ return sizeof(krb5_ui_4);
+}
+
+
+vts_char(dat, st, loc)
+u_char dat; /* the attributes field */
+u_char **st; /* a base pointer to the stream */
+int loc; /* offset into the stream for current data */
+{
+ *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char)));
+ (*st)[loc] = (u_char) dat;
+ return 1;
+}
+
+/*
+stream_to_vals
+ recieves : u_char *, kadm_vals *
+ returns : a kadm_vals filled in according to u_char *
+
+this decodes a byte stream represntation of a vals struct into kadm_vals
+*/
+stream_to_vals(dt_in, dt_out, maxlen)
+u_char *dt_in;
+Kadm_vals *dt_out;
+int maxlen; /* max length to use */
+{
+ register int vsloop, stsize; /* loop counter, stream size */
+ register int status;
+
+ memset((char *) dt_out, 0, sizeof(*dt_out));
+
+ stsize = check_field_header(dt_in, dt_out->fields, maxlen);
+ if (stsize < 0)
+ return(-1);
+ for (vsloop=31; vsloop>=0; vsloop--)
+ if (IS_FIELD(vsloop,dt_out->fields))
+ switch (vsloop) {
+ case KADM_NAME:
+ if ((status = stv_string(dt_in, dt_out->name, stsize,
+ sizeof(dt_out->name), maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_INST:
+ if ((status = stv_string(dt_in, dt_out->instance, stsize,
+ sizeof(dt_out->instance), maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_EXPDATE:
+ if ((status = stv_long(dt_in, &dt_out->exp_date, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_ATTR:
+ if ((status = stv_short(dt_in, &dt_out->attributes, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_MAXLIFE:
+ if ((status = stv_char(dt_in, &dt_out->max_life, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ case KADM_DESKEY:
+ if ((status = stv_long(dt_in, &dt_out->key_high, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ if ((status = stv_long(dt_in, &dt_out->key_low, stsize,
+ maxlen)) < 0)
+ return(-1);
+ stsize += status;
+ break;
+ default:
+ break;
+ }
+ return stsize;
+}
+
+check_field_header(st, cont, maxlen)
+u_char *st; /* stream */
+u_char *cont; /* container for fields data */
+int maxlen;
+{
+ if (4 > maxlen)
+ return(-1);
+ memcpy((char *) cont, (char *) st, 4);
+ return 4; /* return pointer to current stream location */
+}
+
+stv_string(st, dat, loc, stlen, maxlen)
+register u_char *st; /* base pointer to the stream */
+char *dat; /* a string to read from the stream */
+register int loc; /* offset into the stream for current data */
+int stlen; /* max length of string to copy in */
+int maxlen; /* max length of input stream */
+{
+ int maxcount; /* max count of chars to copy */
+
+ maxcount = min(maxlen - loc, stlen);
+
+ (void) strncpy(dat, (char *)st + loc, maxcount);
+
+ if (dat[maxcount-1]) /* not null-term --> not enuf room */
+ return(-1);
+ return strlen(dat)+1;
+}
+
+stv_short(st, dat, loc, maxlen)
+u_char *st; /* a base pointer to the stream */
+u_short *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen;
+{
+ u_short temp; /* to hold the net order short */
+
+ if (loc + sizeof(u_short) > maxlen)
+ return(-1);
+ memcpy((char *) &temp, (char *) st+ loc, sizeof(u_short));
+ *dat = ntohs(temp); /* convert to network order */
+ return sizeof(u_short);
+}
+
+stv_long(st, dat, loc, maxlen)
+u_char *st; /* a base pointer to the stream */
+krb5_ui_4 *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen; /* maximum length of st */
+{
+ krb5_ui_4 temp; /* to hold the net order short */
+
+ if (loc + sizeof(krb5_ui_4) > maxlen)
+ return(-1);
+ memcpy((char *) &temp, (char *) st + loc, sizeof(krb5_ui_4));
+ *dat = ntohl(temp); /* convert to network order */
+ return sizeof(krb5_ui_4);
+}
+
+stv_char(st, dat, loc, maxlen)
+u_char *st; /* a base pointer to the stream */
+u_char *dat; /* the attributes field */
+int loc; /* offset into the stream for current data */
+int maxlen;
+{
+ if (loc + 1 > maxlen)
+ return(-1);
+ *dat = *(st + loc);
+ return 1;
+}
+
diff --git a/src/kadmin/v4server/kadm_supp.c b/src/kadmin/v4server/kadm_supp.c
new file mode 100644
index 000000000..9d2f8deb2
--- /dev/null
+++ b/src/kadmin/v4server/kadm_supp.c
@@ -0,0 +1,113 @@
+/*
+ * kadmin/v4server/kadm_supp.c
+ *
+ * Copyright 1988 by the Massachusetts Institute of Technology.
+ *
+ * For copying and distribution information, please see the file
+ * <mit-copyright.h>.
+ *
+ * Support functions for Kerberos administration server & clients
+ */
+
+
+#include <mit-copyright.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+/*
+ kadm_supp.c
+ this holds the support routines for the kerberos administration server
+
+ error: prints out a kadm error message, returns
+ fatal: prints out a kadm fatal error message, exits
+ prin_vals: prints out data associated with a Principal in the vals
+ structure
+*/
+
+#include "kadm.h"
+#include "krb_db.h"
+
+/*
+prin_vals:
+ recieves : a vals structure
+*/
+void prin_vals(vals)
+Kadm_vals *vals;
+{
+ printf("Info in Database for %s.%s:\n", vals->name, vals->instance);
+ printf(" Max Life: %d Exp Date: %s\n",vals->max_life,
+ asctime(localtime((long *)&vals->exp_date)));
+ printf(" Attribs: %.2x key: %u %u\n",vals->attributes,
+ vals->key_low, vals->key_high);
+}
+
+#ifdef notdef
+nierror(s)
+int s;
+{
+ extern char *error_message();
+ printf("Kerberos admin server loses..... %s\n",error_message(s));
+ return(s);
+}
+#endif
+
+/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal,
+ it copies the fields in Principal specified by fields into Kadm_vals,
+ i.e from old to new */
+
+kadm_prin_to_vals(fields, new, old)
+u_char fields[FLDSZ];
+Kadm_vals *new;
+Principal *old;
+{
+ memset((char *)new, 0, sizeof(*new));
+ if (IS_FIELD(KADM_NAME,fields)) {
+ (void) strncpy(new->name, old->name, ANAME_SZ);
+ SET_FIELD(KADM_NAME, new->fields);
+ }
+ if (IS_FIELD(KADM_INST,fields)) {
+ (void) strncpy(new->instance, old->instance, INST_SZ);
+ SET_FIELD(KADM_INST, new->fields);
+ }
+ if (IS_FIELD(KADM_EXPDATE,fields)) {
+ new->exp_date = old->exp_date;
+ SET_FIELD(KADM_EXPDATE, new->fields);
+ }
+ if (IS_FIELD(KADM_ATTR,fields)) {
+ new->attributes = old->attributes;
+ SET_FIELD(KADM_MAXLIFE, new->fields);
+ }
+ if (IS_FIELD(KADM_MAXLIFE,fields)) {
+ new->max_life = old->max_life;
+ SET_FIELD(KADM_MAXLIFE, new->fields);
+ }
+ if (IS_FIELD(KADM_DESKEY,fields)) {
+ new->key_low = old->key_low;
+ new->key_high = old->key_high;
+ SET_FIELD(KADM_DESKEY, new->fields);
+ }
+}
+
+kadm_vals_to_prin(fields, new, old)
+u_char fields[FLDSZ];
+Principal *new;
+Kadm_vals *old;
+{
+
+ memset((char *)new, 0, sizeof(*new));
+ if (IS_FIELD(KADM_NAME,fields))
+ (void) strncpy(new->name, old->name, ANAME_SZ);
+ if (IS_FIELD(KADM_INST,fields))
+ (void) strncpy(new->instance, old->instance, INST_SZ);
+ if (IS_FIELD(KADM_EXPDATE,fields))
+ new->exp_date = old->exp_date;
+ if (IS_FIELD(KADM_ATTR,fields))
+ new->attributes = old->attributes;
+ if (IS_FIELD(KADM_MAXLIFE,fields))
+ new->max_life = old->max_life;
+ if (IS_FIELD(KADM_DESKEY,fields)) {
+ new->key_low = old->key_low;
+ new->key_high = old->key_high;
+ }
+}
diff --git a/src/kadmin/v4server/unit-test/ChangeLog b/src/kadmin/v4server/unit-test/ChangeLog
new file mode 100644
index 000000000..93120b8a4
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/ChangeLog
@@ -0,0 +1,13 @@
+Mon Jul 15 17:15:51 1996 Marc Horowitz <marc@mit.edu>
+
+ * helpers.exp (exp_prog): the check for non-newline-terminated
+ stdout was causing failures where there weren't any. Barry
+ doesn't remember why this was here to begin with.
+ * Makefile.ov (unit-test-body), helpers.exp: some versions of
+ runtest do not like digits in command-line variable names.
+ * Makefile.ov (unit-test-body), helpers.exp: ovsec_v4adm_server
+ renamed to kadmind4
+ * getpid.sh: grep out any programs with expect or kadmind4 in
+ their names.
+
+
diff --git a/src/kadmin/v4server/unit-test/Makefile.ov b/src/kadmin/v4server/unit-test/Makefile.ov
new file mode 100644
index 000000000..3af65607e
--- /dev/null
+++ b/src/kadmin/v4server/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::
+ $(START_SERVERS_LOCAL) -v4files -kdcport 750 -keysalt des-cbc-crc:v4
+ $(LOCAL_MAKE_KEYTAB) -princ changepw/kerberos /krb5/ovsec_adm.srvtab
+
+unit-test-body::
+ $(RUNTEST) VFOURSERVER=../kadmind4 --tool v4server \
+ KDBFIVE_EDIT=../../../admin/edit/kdb5_edit
+
+unit-test-cleanup::
+ $(STOP_SERVERS_LOCAL) -v4files
diff --git a/src/kadmin/v4server/unit-test/config/ChangeLog b/src/kadmin/v4server/unit-test/config/ChangeLog
new file mode 100644
index 000000000..aa01abc17
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/config/ChangeLog
@@ -0,0 +1,7 @@
+Mon Jul 15 17:18:56 1996 Marc Horowitz <marc@mit.edu>
+
+ * unix.exp: some versions of runtest do not like digits in
+ command-line variable names. ovsec_edit_keytab renamed to
+ kadm5_keytab
+
+
diff --git a/src/kadmin/v4server/unit-test/config/unix.exp b/src/kadmin/v4server/unit-test/config/unix.exp
new file mode 100644
index 000000000..874092311
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/config/unix.exp
@@ -0,0 +1,42 @@
+global env
+
+set kill /bin/kill
+
+if {[file exists /bin/sleep]} {
+ set sleep /bin/sleep
+} else {
+ set sleep /usr/bin/sleep
+}
+
+set kpasswd_v4 /usr/athena/bin/kpasswd
+set ovpasswd $env(TOP)/kpasswd/kpasswd
+set kadmin_local $env(TOP)/cli/kadmin.local
+set kdb5_edit $KDBFIVE_EDIT
+set remove_changepw_perms ./remove_changepw_perms.sh
+set getpid ./getpid.sh
+set ovsec_adm_server $env(TOP)/server/kadmind
+set ovsec_edit_keytab $env(TOP)/keytab/kadm5_keytab
+set hostname [exec hostname]
+
+# change-password.exp sends ^C to kpasswd to kill it; on HP-UX the
+# default intr character is DEL, and setting it on all platforms
+# won't hurt
+set stty_init "intr \\^c"
+
+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
+}
diff --git a/src/kadmin/v4server/unit-test/getpid.sh b/src/kadmin/v4server/unit-test/getpid.sh
new file mode 100644
index 000000000..5c1b1a690
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/getpid.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# tcl sucks big fat hairy rocks
+
+$PS_ALL | awk "/$1/"' && !/awk/ && !/getpid/ && !/expect/ && !/kadmind4/ { print $2 }'
diff --git a/src/kadmin/v4server/unit-test/helpers.exp b/src/kadmin/v4server/unit-test/helpers.exp
new file mode 100644
index 000000000..7f23b65c8
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/helpers.exp
@@ -0,0 +1,232 @@
+proc server_pids { } {
+ global env
+
+ return [eval [concat exec $env(PS_ALL) | \
+ awk {{/kadmind4/ && !/awk/ && !/expect/ {printf("%d ", $2)}}}]]
+}
+
+proc server_exit { name status } {
+ global wait_error_index wait_errno_index wait_status_index
+ global server_id
+ global kill
+
+ verbose "$name: stopping V4 kpasswd server." 1
+
+ # We can't know whether the process exists or not, so we have
+ # to ignore errors. XXX will close ever time out?
+ catch {close $server_id}
+ set pids [server_pids]
+ if {$pids != {}} {
+ verbose "server_exit killing process(es) $pids"
+ catch {exec $kill $pids}
+ } else {
+ verbose "server_exit: couldn't find running server(s) to kill"
+ }
+
+ # wait hangs on AIX if the process was killed; since status == -1
+ # in that case, solve the problem by not waiting; the zombies will
+ # be cleaned up when the test finishes
+ if {$status == -1} {
+ return 1
+ }
+
+ set ret [wait -i $server_id]
+ verbose "% Exit $ret" 2
+
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail "$name: wait returned error [lindex $ret $wait_errno_index]"
+ return 0
+ } else {
+ if { [lindex $ret $wait_status_index] == $status ||
+ (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ return 0
+ }
+ }
+
+ return 1
+}
+
+proc myfail { msg } {
+ global mytest_status
+ fail "$msg"
+ set mytest_status 1
+}
+
+proc server_start { name cmdline should_listen args } {
+ global spawn_id server_id
+ global VFOURSERVER
+ global mytest_status
+ global sleep hostname
+
+ set max_tries 60
+
+ verbose "$name: starting V4 kpasswd server." 1
+
+ for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} {
+ if {$num_tries} {
+ exec $sleep 5
+ verbose "Couldn't connect to V4 kpasswd server; retrying ($num_tries so far)."
+ }
+
+ spawn $VFOURSERVER $cmdline
+ set server_id $spawn_id
+
+ foreach test $args {
+ set mytest_status 0
+ uplevel 1 "expect {
+ -i $server_id
+ $test
+ timeout { myfail \"$name: timeout\" }
+ eof { myfail \"$name: eof while expecting string\" }
+ }"
+
+ if {$mytest_status == 1} {
+ return 0
+ }
+ }
+
+ set pids [server_pids]
+
+ if {$should_listen} {
+ exec $sleep 1
+ set save_spawn_id $spawn_id
+ spawn telnet $hostname kerberos_master
+ expect {
+ {Connection refused} {
+ close -i $save_spawn_id
+ wait -i $save_spawn_id
+ close
+ wait
+ continue
+ }
+ {Connected} {
+ send "close\n"
+ close
+ wait
+ set spawn_id $save_spawn_id
+ break
+ }
+ default {
+ close -i $save_spawn_id
+ wait -i $save_spawn_id
+ catch {close}
+ wait
+ continue
+ }
+ }
+ } else {
+ break
+ }
+ }
+
+ if {$pids == {}} {
+ # Try twice to find the server processes. Not sure why,
+ # but there seems to be some sort of race condition in the OS.
+
+ verbose "server_start: couldn't find server process(es) -- trying again"
+ exec $sleep 1
+ set pids [server_pids]
+ }
+
+ if {$num_tries > $max_tries} {
+ myfail "$name: couldn't connect to V4 kpasswd server"
+ return 0
+ } else {
+ if {$pids != {}} {
+ verbose "server_start: server process ID(s) is/are $pids"
+ }
+ pass "$name"
+ return 1
+ }
+}
+
+proc exp_prog { name prog cmdline status args } {
+ global spawn_id spawn_pid
+ global mytest_status
+ global wait_error_index wait_errno_index wait_status_index
+
+ verbose "$name: spawning $prog $cmdline" 1
+
+ set spawn_pid [eval "spawn $prog $cmdline"]
+
+ # at the end, eof is success
+
+# lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } }
+ lappend args { eof {} }
+
+ foreach test $args {
+ set mytest_status 0
+ uplevel 1 "expect {
+ $test
+ timeout { close; myfail \"$name: timeout\" }
+ eof { myfail \"$name: eof while expecting string\" }
+ }"
+
+ if {$mytest_status == 1} { return 0 }
+ }
+
+ # at this point, the id is closed and we can wait on it.
+
+ set ret [wait]
+ verbose "% Exit $ret" 2
+
+ if {$status == -1} { return 1 }
+
+ if {[lindex $ret $wait_error_index] == -1} {
+ fail "$name: wait returned error [lindex $ret $wait_errno_index]"
+ } else {
+ if { [lindex $ret $wait_status_index] == $status ||
+ (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } {
+ pass "$name"
+ } else {
+ fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status"
+ }
+ }
+
+ return 1
+}
+
+proc fix_salt { name fullname oldpw newpw } {
+ global kdb5_edit
+
+ exp_prog "$name: kdb5_edit" $kdb5_edit "" 0 {
+ "kdb5_edit:" { send "cpw $fullname\n" }
+ } {
+ "Enter password:" { send "$newpw\n" }
+ } {
+ "Re-enter password for verification:" { send "$newpw\n" }
+ } {
+ "kdb5_edit:" { send "quit\n" }
+ }
+}
+
+proc unexpire { name fullname } {
+ global kadmin_local
+
+ # While we're at it, make sure they aren't expired.
+ exp_prog "$name: kadmin.local" $kadmin_local "" 0 {
+ "kadmin.local:" {
+ send "modprinc -expire \"May 6, 1999\" $fullname\n"
+ }
+ } {
+ -re "Principal .* modified." { send "quit\n" }
+ }
+}
+
+proc kpasswd_v4 { name fullname status oldpw newpw args } {
+ global kpasswd_v4 s
+
+ eval [concat {
+ exp_prog $name $kpasswd_v4 "-u $fullname" $status {
+ -re "Old password for $fullname:" { send "$oldpw\n" }
+ } {
+ -re "New Password for $fullname:" { send "$newpw\n" }
+ } {
+ -re "Verifying, please re-enter New Password for $fullname:"
+ { send "$newpw\n" }
+ }
+ } $args]
+}
diff --git a/src/kadmin/v4server/unit-test/remove_changepw_perms.sh b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh
new file mode 100644
index 000000000..27d026ff3
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+# tcl sucks big fat hairy rocks
+
+ed /krb5/ovsec_adm.acl <<EOF >/dev/null 2>&1
+g/changepw\/kerberos/s/^/#/
+w
+q
+EOF
diff --git a/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp
new file mode 100644
index 000000000..3c8e181b2
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp
@@ -0,0 +1,11 @@
+load_lib "helpers.exp"
+
+set timeout 10
+
+exp_prog "setup" $ovsec_edit_keytab \
+ "-k /krb5/ovsec_adm.srvtab -a -c -p admin changepw/kerberos" \
+ 0 {
+ "Enter password:" { send "admin\n" }
+} {
+ -re "Entry for principal changepw/kerberos .* added to keytab" {}
+}
diff --git a/src/kadmin/v4server/unit-test/v4server.1/access.exp b/src/kadmin/v4server/unit-test/v4server.1/access.exp
new file mode 100644
index 000000000..4d30fc9c7
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.1/access.exp
@@ -0,0 +1,88 @@
+load_lib "helpers.exp"
+
+set timeout 30
+
+# Setup: make sure the principals we will use have V4 salt
+fix_salt "A.setup" testuser notathena notathena
+unexpire "A.setup" testuser
+unexpire "A.setup" changepw/kerberos
+
+proc kill_admin_server {} {
+ global env kill getpid
+
+ set pid [exec $getpid kadmind]
+ if {$pid != ""} {
+ exec $kill $pid
+ }
+}
+
+proc start_admin_server {} {
+ global ovsec_adm_server sleep
+
+ set max_tries 60
+
+ for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} {
+ if {$num_tries} {
+ exec $sleep 5
+ verbose "$ovsec_adm_server couldn't bind; retrying ($num_tries so far)"
+ }
+ if {[catch "exec $ovsec_adm_server" msg]} {
+ if {[regexp {Address already in use} $msg]} {
+ continue
+ }
+ fail "starting $ovsec_adm_server: $msg"
+ }
+ return
+ }
+ fail "starting $ovsec_adm_server: $msg"
+}
+
+proc remove_changepw_perms {} {
+ global remove_changepw_perms
+
+ exec $remove_changepw_perms
+}
+
+proc set_changepw_perms { perms } {
+ remove_changepw_perms
+
+ exec echo "changepw/kerberos@SECURE-TEST.OV.COM $perms" \
+ >> /krb5/ovsec_adm.acl
+}
+
+# start off with a dead admin server
+kill_admin_server
+
+set_changepw_perms "i"
+start_admin_server
+server_start A.1 "-n" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+kpasswd_v4 A.1 testuser 2 notathena foobar {
+ "Operation requires ``change-password'' privilege" {}
+} {
+ "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {}
+} {
+ "Password NOT changed." {}
+}
+server_exit A.1 -1
+kill_admin_server
+
+set_changepw_perms "c"
+start_admin_server
+server_start A.2 "-n" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+kpasswd_v4 A.2 testuser 2 notathena foobar {
+ "Operation requires ``get'' privilege" {}
+} {
+ "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {}
+} {
+ "Password NOT changed." {}
+}
+server_exit A.2 -1
+kill_admin_server
+
+set_changepw_perms "ci"
+
+start_admin_server
diff --git a/src/kadmin/v4server/unit-test/v4server.1/change-password.exp b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp
new file mode 100644
index 000000000..62b9ec30a
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp
@@ -0,0 +1,59 @@
+load_lib "helpers.exp"
+
+set timeout 30
+
+spawn stty -a
+expect { eof {} }
+wait
+
+# Setup: make sure the principals we will use have V4 salt
+fix_salt "CPW.setup" testuser notathena notathena
+fix_salt "CPW.setup" pol1 pol111111 pol111111
+fix_salt "CPW.setup" pol2 pol222222 pol222222
+unexpire "CPW.setup" testuser
+unexpire "CPW.setup" pol1
+unexpire "CPW.setup" pol2
+unexpire "CPW.setup" changepw/kerberos
+
+server_start "CPW.all" "-n" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+
+kpasswd_v4 CPW.1 testuser 0 notathena foobar { "Password changed." {} }
+kpasswd_v4 CPW.1 testuser 0 foobar notathena { "Password changed." {} }
+
+kpasswd_v4 CPW.3 pol1 -1 pol111111 foo {
+ "New password is too short." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+kpasswd_v4 CPW.4 pol1 -1 pol111111 foooooooo {
+ "New password does not have enough character classes." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+kpasswd_v4 CPW.5 pol1 -1 pol111111 Abyssinia {
+ "New password was found in a dictionary" {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+kpasswd_v4 CPW.6.setup pol1 0 pol111111 polAAAAAA { "Password changed." {} }
+kpasswd_v4 CPW.6 pol1 -1 polAAAAAA pol111111 {
+ "New password was used previously." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+# this relies on the fact that kdb5_edit resets last_pwd_change, which
+# it appears to
+kpasswd_v4 CPW.7.setup pol2 0 pol222222 polBBBBBB { "Password changed." {} }
+kpasswd_v4 CPW.7 pol2 -1 polBBBBBB pol222222 {
+ "Password cannot be changed because it was changed too recently." {}
+} {
+ "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break }
+}
+
+server_exit "CPW.all" -1
diff --git a/src/kadmin/v4server/unit-test/v4server.1/usage.exp b/src/kadmin/v4server/unit-test/v4server.1/usage.exp
new file mode 100644
index 000000000..4d292067a
--- /dev/null
+++ b/src/kadmin/v4server/unit-test/v4server.1/usage.exp
@@ -0,0 +1,26 @@
+load_lib "helpers.exp"
+
+set timeout 10
+
+server_start "U.1: -h" "-h" 0 {
+ -re {Usage: .*} {}
+} {
+ eof {}
+}
+server_exit "U.1: -h" 255
+
+server_start "U.4: -n" "-n" 1 {
+ "Enter KDC database master key:" {
+ myfail "unexpected password prompt"
+ }
+ "KADM Server starting in the OVSEC_KADM mode" {}
+}
+
+server_exit "U.4: -n" -1
+
+server_start "U.5: no -n" "" 1 {
+ "KADM Server starting in the OVSEC_KADM mode" {}
+} {
+ "Enter KDC database master key:" { send "mrroot\n" }
+}
+server_exit "U.5: no -n" -1