aboutsummaryrefslogtreecommitdiff
path: root/src/kadmin
diff options
context:
space:
mode:
Diffstat (limited to 'src/kadmin')
-rw-r--r--src/kadmin/Makefile.in6
-rw-r--r--src/kadmin/cli/Makefile.in39
-rw-r--r--src/kadmin/cli/deps63
-rw-r--r--src/kadmin/cli/getdate.y1050
-rwxr-xr-xsrc/kadmin/cli/k5srvutil.sh123
-rw-r--r--src/kadmin/cli/kadmin.c1988
-rw-r--r--src/kadmin/cli/kadmin.h88
-rw-r--r--src/kadmin/cli/kadmin_ct.ct100
-rw-r--r--src/kadmin/cli/keytab.c505
-rw-r--r--src/kadmin/cli/keytab_local.c10
-rw-r--r--src/kadmin/cli/ss_wrapper.c77
-rw-r--r--src/kadmin/cli/strftime.c465
-rw-r--r--src/kadmin/dbutil/Makefile.in43
-rw-r--r--src/kadmin/dbutil/deps206
-rw-r--r--src/kadmin/dbutil/dump.c1717
-rw-r--r--src/kadmin/dbutil/import_err.et22
-rw-r--r--src/kadmin/dbutil/kadm5_create.c270
-rw-r--r--src/kadmin/dbutil/kdb5_create.c502
-rw-r--r--src/kadmin/dbutil/kdb5_destroy.c94
-rw-r--r--src/kadmin/dbutil/kdb5_mkey.c1333
-rw-r--r--src/kadmin/dbutil/kdb5_stash.c139
-rw-r--r--src/kadmin/dbutil/kdb5_util.c651
-rw-r--r--src/kadmin/dbutil/kdb5_util.h99
-rw-r--r--src/kadmin/dbutil/nstrtok.h3
-rw-r--r--src/kadmin/dbutil/ovload.c205
-rw-r--r--src/kadmin/dbutil/strtok.c105
-rw-r--r--src/kadmin/dbutil/t_tdumputil.c115
-rwxr-xr-xsrc/kadmin/dbutil/t_tdumputil.py32
-rw-r--r--src/kadmin/dbutil/tabdump.c665
-rw-r--r--src/kadmin/dbutil/tdumputil.c266
-rw-r--r--src/kadmin/dbutil/tdumputil.h51
-rw-r--r--src/kadmin/deps1
-rw-r--r--src/kadmin/kdbkeys/Makefile.in16
-rwxr-xr-xsrc/kadmin/kdbkeys/do-test.pl56
-rw-r--r--src/kadmin/ktutil/Makefile.in30
-rw-r--r--src/kadmin/ktutil/deps26
-rw-r--r--src/kadmin/ktutil/ktutil.c274
-rw-r--r--src/kadmin/ktutil/ktutil.h69
-rw-r--r--src/kadmin/ktutil/ktutil_ct.ct59
-rw-r--r--src/kadmin/ktutil/ktutil_funcs.c353
-rwxr-xr-xsrc/kadmin/scripts/inst-hdrs.sh14
-rw-r--r--src/kadmin/server/Makefile.in22
-rw-r--r--src/kadmin/server/deps135
-rw-r--r--src/kadmin/server/ipropd_svc.c646
-rw-r--r--src/kadmin/server/kadm_rpc_svc.c370
-rw-r--r--src/kadmin/server/misc.c272
-rw-r--r--src/kadmin/server/misc.h71
-rw-r--r--src/kadmin/server/ovsec_kadmd.c562
-rw-r--r--src/kadmin/server/schpw.c487
-rw-r--r--src/kadmin/server/server_stubs.c1707
-rw-r--r--src/kadmin/testing/Makefile.in8
-rw-r--r--src/kadmin/testing/deps1
-rw-r--r--src/kadmin/testing/proto/kdc.conf.proto16
-rw-r--r--src/kadmin/testing/proto/krb5.conf.proto31
-rw-r--r--src/kadmin/testing/proto/ovsec_adm.dict3
-rw-r--r--src/kadmin/testing/scripts/Makefile.in31
-rwxr-xr-xsrc/kadmin/testing/scripts/compare_dump.plin242
-rw-r--r--src/kadmin/testing/scripts/deps1
-rwxr-xr-xsrc/kadmin/testing/scripts/env-setup.shin111
-rwxr-xr-xsrc/kadmin/testing/scripts/find-make.sh18
-rwxr-xr-xsrc/kadmin/testing/scripts/init_db232
-rwxr-xr-xsrc/kadmin/testing/scripts/make-host-keytab.plin144
-rwxr-xr-xsrc/kadmin/testing/scripts/qualname.plin19
-rwxr-xr-xsrc/kadmin/testing/scripts/simple_dump.plin88
-rwxr-xr-xsrc/kadmin/testing/scripts/start_servers71
-rwxr-xr-xsrc/kadmin/testing/scripts/start_servers_local161
-rwxr-xr-xsrc/kadmin/testing/scripts/stop_servers60
-rwxr-xr-xsrc/kadmin/testing/scripts/stop_servers_local44
-rwxr-xr-xsrc/kadmin/testing/scripts/verify_xrunner_report.plin38
-rw-r--r--src/kadmin/testing/tcl/util.t58
-rw-r--r--src/kadmin/testing/util/Makefile.in42
-rw-r--r--src/kadmin/testing/util/bsddb_dump.c65
-rw-r--r--src/kadmin/testing/util/deps16
-rw-r--r--src/kadmin/testing/util/tcl_kadm5.c2568
-rw-r--r--src/kadmin/testing/util/tcl_kadm5.h3
-rw-r--r--src/kadmin/testing/util/tcl_kadm5_syntax57
-rw-r--r--src/kadmin/testing/util/tcl_krb5_hash.c167
-rw-r--r--src/kadmin/testing/util/test.c38
78 files changed, 20535 insertions, 0 deletions
diff --git a/src/kadmin/Makefile.in b/src/kadmin/Makefile.in
new file mode 100644
index 000000000000..f4061f4f7a0c
--- /dev/null
+++ b/src/kadmin/Makefile.in
@@ -0,0 +1,6 @@
+mydir=kadmin
+BUILDTOP=$(REL)..
+SUBDIRS = cli dbutil ktutil server testing
+
+all:
+
diff --git a/src/kadmin/cli/Makefile.in b/src/kadmin/cli/Makefile.in
new file mode 100644
index 000000000000..adfea6e2b5ef
--- /dev/null
+++ b/src/kadmin/cli/Makefile.in
@@ -0,0 +1,39 @@
+mydir=kadmin$(S)cli
+BUILDTOP=$(REL)..$(S)..
+KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS)
+
+PROG = kadmin
+COMMON_OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o
+KADMIN_OBJS = $(COMMON_OBJS) keytab.o
+LOCAL_OBJS = $(COMMON_OBJS) keytab_local.o
+
+SRCS = kadmin.c kadmin_ct.c ss_wrapper.c getdate.c keytab.c keytab_local.c
+
+LOCALINCLUDES=-I$(srcdir)
+
+all: $(PROG).local $(PROG)
+
+$(PROG).local: $(LOCAL_OBJS) $(SS_DEPLIB) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $(PROG).local $(LOCAL_OBJS) $(SS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS)
+
+$(PROG): $(KADMIN_OBJS) $(SS_DEPLIB) $(KADMCLNT_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $(PROG) $(KADMIN_OBJS) $(SS_LIB) $(KADMCLNT_LIBS) $(KRB5_BASE_LIBS)
+
+kadmin_ct.o: kadmin_ct.c
+
+install:
+ $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG).local
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(CLIENT_BINDIR)/$(PROG)
+ $(INSTALL_SCRIPT) $(srcdir)/k5srvutil.sh ${DESTDIR}$(CLIENT_BINDIR)/k5srvutil
+
+generate-files-mac: kadmin_ct.c getdate.c
+
+clean:
+ $(RM) $(PROG).local $(PROG) $(COMMON_OBJS) $(KADMIN_OBJS) $(LOCAL_OBJS)
+clean-unix::
+ $(RM) datetest getdate.c kadmin_ct.c
+
+# for testing getdate.y
+# CC_LINK is not meant for compilation and this use may break in the future.
+datetest: getdate.c
+ $(CC_LINK) $(ALL_CFLAGS) -DTEST -o datetest getdate.c
diff --git a/src/kadmin/cli/deps b/src/kadmin/cli/deps
new file mode 100644
index 000000000000..a9c997b395ab
--- /dev/null
+++ b/src/kadmin/cli/deps
@@ -0,0 +1,63 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kadmin.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/krb5.h kadmin.c kadmin.h
+$(OUTPRE)kadmin_ct.$(OBJEXT): $(COM_ERR_DEPS) $(SS_DEPS) \
+ kadmin_ct.c
+$(OUTPRE)ss_wrapper.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(SS_DEPS) \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ kadmin.h ss_wrapper.c
+$(OUTPRE)getdate.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
+ getdate.c
+$(OUTPRE)keytab.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kadmin.h keytab.c
+$(OUTPRE)keytab_local.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kadmin.h keytab.c keytab_local.c
diff --git a/src/kadmin/cli/getdate.y b/src/kadmin/cli/getdate.y
new file mode 100644
index 000000000000..4f0c56f7eb20
--- /dev/null
+++ b/src/kadmin/cli/getdate.y
@@ -0,0 +1,1050 @@
+%{
+/*
+** 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 */
+
+#include "autoconf.h"
+#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>
+
+#if defined(HAVE_STDLIB_H)
+#include <stdlib.h>
+#endif
+
+/* 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
+
+/*
+** We use the obsolete `struct my_timeb' as part of our interface!
+** Since the system doesn't have it, we define it here;
+** our callers must do likewise.
+*/
+struct my_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(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. */
+#ifndef 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 getdate_yylex (void);
+static int getdate_yyerror (char *);
+
+
+#define EPOCH 1970
+#define EPOCH_END 2038 /* assumes 32 bits */
+#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 tNEVER
+
+%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
+ | tNEVER {
+ yyYear = 1970;
+ yyMonth = 1;
+ yyDay = 1;
+ yyHour = yyMinutes = yySeconds = 0;
+ yyDSTmode = DSToff;
+ yyTimezone = 0; /* gmt */
+ yyHaveDate++;
+ }
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ ;
+
+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;
+ }
+ ;
+
+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 },
+ { "never", tNEVER, 0 },
+ { 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 }
+};
+
+/* ARGSUSED */
+static int
+yyerror(char *s)
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(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 */
+}
+
+/*
+ * From hh:mm:ss [am|pm] mm/dd/yy [tz], compute and return the number
+ * of seconds since 00:00:00 1/1/70 GMT.
+ */
+static time_t
+Convert(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;
+ struct tm *tm;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 1900)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ if (Year < EPOCH
+ || Year > EPOCH_END
+ || 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) && ((Year % 100 != 0) ||
+ (Year % 400 == 0)));
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ if (DSTmode == DSTon)
+ Julian -= 60 * 60;
+ else if (DSTmode == DSTmaybe) {
+ tm = localtime(&Julian);
+ if (tm == NULL)
+ return -1;
+ else if (tm->tm_isdst)
+ Julian -= 60 * 60;
+ }
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(time_t Start, time_t Future, int *error)
+{
+ time_t StartDay;
+ time_t FutureDay;
+ struct tm *tm;
+
+ tm = localtime(&Start);
+ if (tm == NULL) {
+ *error = 1;
+ return -1;
+ }
+ StartDay = (tm->tm_hour + 1) % 24;
+ tm = localtime(&Future);
+ if (tm == NULL) {
+ *error = 1;
+ return -1;
+ }
+ FutureDay = (tm->tm_hour + 1) % 24;
+ *error = 0;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber, int *error)
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ if (tm == NULL) {
+ *error = 1;
+ return -1;
+ }
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now, error);
+}
+
+
+static time_t
+RelativeMonth(time_t Start, time_t RelMonth)
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+ time_t ret;
+ int error;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ if (tm == NULL)
+ return -1;
+ Month = 12 * tm->tm_year + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ ret = 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);
+ if (ret == -1)
+ return ret;
+ ret = DSTcorrect(Start, ret, &error);
+ if (error)
+ return -1;
+ return ret;
+}
+
+
+static int
+LookupWord(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((int) *p))
+ *p = tolower((int) *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;
+ }
+
+ /* 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((int) *yyInput))
+ yyInput++;
+
+ c = *yyInput;
+ if (isdigit((int) c) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit((int) (*++yyInput)))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit((int) (c = *yyInput++)); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha((int) c)) {
+ for (p = buff; isalpha((int) (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(struct tm *a, struct tm *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);
+}
+
+/* For get_date extern declaration compatibility check... yuck. */
+#include <krb5.h>
+int yyparse(void);
+
+time_t get_date_rel(char *, time_t);
+time_t get_date(char *);
+
+time_t
+get_date_rel(char *p, time_t nowtime)
+{
+ struct my_timeb *now = NULL;
+ struct tm *tm, gmt;
+ struct my_timeb ftz;
+ time_t Start;
+ time_t tod;
+ time_t delta;
+ int error;
+
+ yyInput = p;
+ if (now == NULL) {
+ now = &ftz;
+
+ ftz.time = nowtime;
+
+ if (! (tm = gmtime (&ftz.time)))
+ return -1;
+ gmt = *tm; /* Make a copy, in case localtime modifies *tm. */
+ tm = localtime(&ftz.time);
+ if (tm == NULL)
+ return -1;
+ ftz.timezone = difftm (&gmt, tm) / 60;
+ }
+
+ tm = localtime(&now->time);
+ if (tm == NULL)
+ return -1;
+ 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;
+
+ /*
+ * When yyparse returns, zero or more of yyHave{Time,Zone,Date,Day,Rel}
+ * will have been incremented. The value is number of items of
+ * that type that were found; for all but Rel, more than one is
+ * illegal.
+ *
+ * For each yyHave indicator, the following values are set:
+ *
+ * yyHaveTime:
+ * yyHour, yyMinutes, yySeconds: hh:mm:ss specified, initialized
+ * to zeros above
+ * yyMeridian: MERam, MERpm, or MER24
+ * yyTimeZone: time zone specified in minutes
+ * yyDSTmode: DSToff if yyTimeZone is set, otherwise unchanged
+ * (initialized above to DSTmaybe)
+ *
+ * yyHaveZone:
+ * yyTimezone: as above
+ * yyDSTmode: DSToff if a non-DST zone is specified, otherwise DSTon
+ * XXX don't understand interaction with yyHaveTime zone info
+ *
+ * yyHaveDay:
+ * yyDayNumber: 0-6 for Sunday-Saturday
+ * yyDayOrdinal: val specified with day ("second monday",
+ * Ordinal=2), otherwise 1
+ *
+ * yyHaveDate:
+ * yyMonth, yyDay, yyYear: mm/dd/yy specified, initialized to
+ * today above
+ *
+ * yyHaveRel:
+ * yyRelSeconds: seconds specified with MINUTE_UNITs ("3 hours") or
+ * SEC_UNITs ("30 seconds")
+ * yyRelMonth: months specified with MONTH_UNITs ("3 months", "1
+ * year")
+ *
+ * The code following yyparse turns these values into a single
+ * date stamp.
+ */
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ /*
+ * If an absolute time specified, set Start to the equivalent Unix
+ * timestamp. Otherwise, set Start to now, and if we do not have
+ * a relatime time (ie: only yyHaveZone), decrement Start to the
+ * beginning of today.
+ *
+ * By having yyHaveDay in the "absolute" list, "next Monday" means
+ * midnight next Monday. Otherwise, "next Monday" would mean the
+ * time right now, next Monday. It's not clear to me why the
+ * current behavior is preferred.
+ */
+ 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;
+ }
+
+ /*
+ * Add in the relative time specified. RelativeMonth adds in the
+ * months, accounting for the fact that the actual length of "3
+ * months" depends on where you start counting.
+ *
+ * XXX By having this separate from the previous block, we are
+ * allowing dates like "10:00am 3 months", which means 3 months
+ * from 10:00am today, or even "1/1/99 two days" which means two
+ * days after 1/1/99.
+ *
+ * XXX Shouldn't this only be done if yyHaveRel, just for
+ * thoroughness?
+ */
+ Start += yyRelSeconds;
+ delta = RelativeMonth(Start, yyRelMonth);
+ if (delta == (time_t) -1)
+ return -1;
+ Start += delta;
+
+ /*
+ * Now, if you specified a day of week and counter, add it in. By
+ * disallowing Date but allowing Time, you can say "5pm next
+ * monday".
+ *
+ * XXX The yyHaveDay && !yyHaveDate restriction should be enforced
+ * above and be able to cause failure.
+ */
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber, &error);
+ if (error != 0)
+ return -1;
+ 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;
+}
+
+
+time_t
+get_date(char *p)
+{
+ return get_date_rel(p, time(NULL));
+}
+
+
+#if defined(TEST)
+
+/* ARGSUSED */
+main(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);
+ 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/k5srvutil.sh b/src/kadmin/cli/k5srvutil.sh
new file mode 100755
index 000000000000..050fa8776ff4
--- /dev/null
+++ b/src/kadmin/cli/k5srvutil.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+
+# list_princs keytab
+# returns a list of principals in the keytab
+# sorted and uniquified
+list_princs() {
+ klist -k $keytab | awk '(NR > 3) {print $2}' | sort | uniq
+}
+
+set_command() {
+ if [ x$command != x ] ; then
+ cmd_error Only one command can be specified
+ usage
+ exit 1
+ fi
+ command=$1
+}
+
+#interactive_prompt prompt princ
+# If in interactive mode return true if the principal should be acted on
+# otherwise return true all the time
+interactive_prompt() {
+ if [ $interactive = 0 ] ; then
+ return 0
+ fi
+ printf "%s for %s? [yn]" "$1" "$2"
+ read ans
+ case $ans in
+ n*|N*)
+ return 1
+ ;;
+ esac
+ return 0
+ }
+
+cmd_error() {
+ echo $@ 2>&1
+ }
+
+usage() {
+ echo "Usage: $0 [-i] [-f file] [-e keysalts] list|change|delete|delold"
+}
+
+
+
+change_key() {
+ princs=`list_princs `
+ for princ in $princs; do
+ if interactive_prompt "Change key " $princ; then
+ kadmin -k -t $keytab -p $princ -q \
+ "ktadd -k $keytab $keysalts $princ"
+ fi
+ done
+ }
+
+delete_old_keys() {
+ princs=`list_princs `
+ for princ in $princs; do
+ if interactive_prompt "Delete old keys " $princ; then
+ kadmin -k -t $keytab -p $princ -q "ktrem -k $keytab $princ old"
+ fi
+ done
+ }
+
+delete_keys() {
+ interactive=1
+ princs=`list_princs `
+ for princ in $princs; do
+ if interactive_prompt "Delete all keys " $princ; then
+ kadmin -p $princ -k -t $keytab -q "ktrem -k $keytab $princ all"
+ fi
+ done
+ }
+
+
+keytab=/etc/krb5.keytab
+interactive=0
+keysalts=""
+
+while [ $# -gt 0 ] ; do
+ opt=$1
+ shift
+ case $opt in
+ "-f")
+ keytab=$1
+ shift
+ ;;
+ "-i")
+ interactive=1
+ ;;
+ "-e")
+ keysalts="$keysalts -e \"$1\""
+ shift
+ ;;
+ change|delold|delete|list)
+ set_command $opt
+ ;;
+ *)
+ cmd_error Illegal option: $opt
+ usage
+ exit 1
+ ;;
+ esac
+done
+
+
+case $command in
+ change)
+ change_key
+ ;;
+ delold)
+ delete_old_keys
+ ;;
+ delete)
+ delete_keys
+ ;;
+ list)
+ klist -k $keytab
+ ;;
+ *)
+ usage
+ ;;
+ esac
diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c
new file mode 100644
index 000000000000..c53c677a82d0
--- /dev/null
+++ b/src/kadmin/cli/kadmin.c
@@ -0,0 +1,1988 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* Base functions for a kadmin command line interface using the OVSecure
+ * library */
+
+/* for "_" macro */
+#include "k5-platform.h"
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <math.h>
+#include <unistd.h>
+#include <pwd.h>
+/* #include <sys/timeb.h> */
+#include <time.h>
+#include "kadmin.h"
+
+static krb5_boolean script_mode = FALSE;
+int exit_status = 0;
+char *def_realm = NULL;
+char *whoami = NULL;
+
+void *handle = NULL;
+krb5_context context;
+char *ccache_name = NULL;
+
+int locked = 0;
+
+static void
+info(const char *fmt, ...)
+#if !defined(__cplusplus) && (__GNUC__ > 2)
+ __attribute__((__format__(__printf__, 1, 2)))
+#endif
+ ;
+
+static void
+error(const char *fmt, ...)
+#if !defined(__cplusplus) && (__GNUC__ > 2)
+ __attribute__((__format__(__printf__, 1, 2)))
+#endif
+ ;
+
+/* Like printf, but suppressed if script_mode is set. */
+static void
+info(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (script_mode)
+ return;
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/* Like fprintf to stderr; also set exit_status if script_mode is set. */
+static void
+error(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (script_mode)
+ exit_status = 1;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+
+static void
+usage()
+{
+ error(_("Usage: %s [-r realm] [-p principal] [-q query] "
+ "[clnt|local args]\n"
+ " [command args...]\n"
+ "\tclnt args: [-s admin_server[:port]] "
+ "[[-c ccache]|[-k [-t keytab]]]|[-n]\n"
+ "\tlocal args: [-x db_args]* [-d dbname] "
+ "[-e \"enc:salt ...\"] [-m]"
+ "where,\n\t[-x db_args]* - any number of database specific "
+ "arguments.\n"
+ "\t\t\tLook at each database documentation for supported "
+ "arguments\n"), whoami);
+ exit(1);
+}
+
+static char *
+strdur(time_t duration)
+{
+ static char out[50];
+ int neg, days, hours, minutes, seconds;
+
+ if (duration < 0) {
+ duration *= -1;
+ neg = 1;
+ } else
+ neg = 0;
+ days = duration / (24 * 3600);
+ duration %= 24 * 3600;
+ hours = duration / 3600;
+ duration %= 3600;
+ minutes = duration / 60;
+ duration %= 60;
+ seconds = duration;
+ snprintf(out, sizeof(out), "%s%d %s %02d:%02d:%02d", neg ? "-" : "",
+ days, days == 1 ? "day" : "days",
+ hours, minutes, seconds);
+ return out;
+}
+
+static char *
+strdate(krb5_timestamp when)
+{
+ struct tm *tm;
+ static char out[40];
+
+ time_t lcltim = when;
+ tm = localtime(&lcltim);
+ strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm);
+ return out;
+}
+
+/* Parse a date string using getdate.y. On failure, output an error message
+ * and return (time_t)-1. */
+static time_t
+parse_date(char *str, time_t now)
+{
+ time_t date;
+
+ date = get_date_rel(str, now);
+ if (date == (time_t)-1)
+ error(_("Invalid date specification \"%s\".\n"), str);
+ return date;
+}
+
+/*
+ * Parse a time interval. Use krb5_string_to_deltat() if it works; otherwise
+ * use getdate.y and subtract now, with sanity checks. On failure, output an
+ * error message and return (time_t)-1.
+ */
+static time_t
+parse_interval(char *str, time_t now)
+{
+ time_t date;
+ krb5_deltat delta;
+
+ if (krb5_string_to_deltat(str, &delta) == 0)
+ return delta;
+
+ date = parse_date(str, now);
+ if (date == (time_t)-1)
+ return date;
+
+ /* Interpret an absolute time of 0 (e.g. "never") as an interval of 0. */
+ if (date == 0)
+ return 0;
+
+ /* Don't return a negative interval if the date is in the past. */
+ if (date < now) {
+ error(_("Interval specification \"%s\" is in the past.\n"), str);
+ return (time_t)-1;
+ }
+
+ return date - now;
+}
+
+/* this is a wrapper to go around krb5_parse_principal so we can set
+ the default realm up properly */
+static krb5_error_code
+kadmin_parse_name(char *name, krb5_principal *principal)
+{
+ char *cp, *fullname;
+ krb5_error_code retval;
+ int result;
+
+ /* assumes def_realm is initialized! */
+ cp = strchr(name, '@');
+ while (cp) {
+ if (cp - name && *(cp - 1) != '\\')
+ break;
+ else
+ cp = strchr(cp + 1, '@');
+ }
+ if (cp == NULL)
+ result = asprintf(&fullname, "%s@%s", name, def_realm);
+ else
+ result = asprintf(&fullname, "%s", name);
+ if (result < 0)
+ return ENOMEM;
+ retval = krb5_parse_name(context, fullname, principal);
+ free(fullname);
+ return retval;
+}
+
+static void
+extended_com_err_fn(const char *myprog, errcode_t code,
+ const char *fmt, va_list args)
+{
+ const char *emsg;
+
+ if (code) {
+ emsg = krb5_get_error_message(context, code);
+ error("%s: %s ", myprog, emsg);
+ krb5_free_error_message(context, emsg);
+ } else {
+ error("%s: ", myprog);
+ }
+ vfprintf(stderr, fmt, args);
+ error("\n");
+}
+
+/* Create a principal using the oldest appropriate kadm5 API. */
+static krb5_error_code
+create_princ(kadm5_principal_ent_rec *princ, long mask, int n_ks,
+ krb5_key_salt_tuple *ks, char *pass)
+{
+ if (ks)
+ return kadm5_create_principal_3(handle, princ, mask, n_ks, ks, pass);
+ else
+ return kadm5_create_principal(handle, princ, mask, pass);
+}
+
+/* Randomize a principal's password using the appropriate kadm5 API. */
+krb5_error_code
+randkey_princ(void *lhandle, krb5_principal princ, krb5_boolean keepold,
+ int n_ks, krb5_key_salt_tuple *ks, krb5_keyblock **key,
+ int *n_keys)
+{
+ krb5_error_code ret;
+
+ /* Try the newer API first, because the Solaris kadmind only creates DES
+ * keys when the old API is used. */
+ ret = kadm5_randkey_principal_3(lhandle, princ, keepold, n_ks, ks, key,
+ n_keys);
+
+ /* Fall back to the old version if we get an error and aren't using any new
+ * parameters. */
+ if (ret == KADM5_RPC_ERROR && !keepold && ks == NULL)
+ ret = kadm5_randkey_principal(lhandle, princ, key, n_keys);
+
+ return ret;
+}
+
+static krb5_boolean
+policy_exists(const char *name)
+{
+ kadm5_policy_ent_rec pol;
+
+ if (kadm5_get_policy(handle, (char *)name, &pol) != 0)
+ return FALSE;
+ kadm5_free_policy_ent(handle, &pol);
+ return TRUE;
+}
+
+void
+kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out)
+{
+ 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, use_anonymous = 0;
+ struct passwd *pw;
+ kadm5_ret_t retval;
+ krb5_ccache cc;
+ krb5_principal princ;
+ kadm5_config_params params;
+ char **db_args = NULL;
+ int db_args_size = 0;
+ char *db_name = NULL;
+ char *svcname, *realm;
+
+ memset(&params, 0, sizeof(params));
+
+ set_com_err_hook(extended_com_err_fn);
+
+ retval = kadm5_init_krb5_context(&context);
+ if (retval) {
+ com_err(whoami, retval, _("while initializing krb5 library"));
+ exit(1);
+ }
+
+ while ((optchar = getopt(argc, argv,
+ "+x:r:p:knq:w:d:s:mc:t:e:ON")) != EOF) {
+ switch (optchar) {
+ case 'x':
+ db_args_size++;
+ db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1));
+ if (db_args == NULL) {
+ error(_("%s: Cannot initialize. Not enough memory\n"), whoami);
+ exit(1);
+ }
+ db_args[db_args_size - 1] = optarg;
+ db_args[db_args_size] = NULL;
+ break;
+
+ case 'r':
+ def_realm = optarg;
+ break;
+ case 'p':
+ princstr = optarg;
+ break;
+ case 'c':
+ ccache_name = optarg;
+ break;
+ case 'k':
+ use_keytab++;
+ break;
+ case 'n':
+ use_anonymous++;
+ break;
+ case 't':
+ keytab_name = optarg;
+ break;
+ case 'w':
+ password = optarg;
+ break;
+ case 'q':
+ query = optarg;
+ break;
+ case 'd':
+ /* db_name has to be passed as part of the db_args. */
+ free(db_name);
+ asprintf(&db_name, "dbname=%s", optarg);
+
+ db_args_size++;
+ db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1));
+ if (db_args == NULL) {
+ error(_("%s: Cannot initialize. Not enough memory\n"), whoami);
+ exit(1);
+ }
+ db_args[db_args_size - 1] = db_name;
+ db_args[db_args_size] = NULL;
+ 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, NULL, NULL, 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;
+ case 'O':
+ params.mask |= KADM5_CONFIG_OLD_AUTH_GSSAPI;
+ break;
+ case 'N':
+ params.mask |= KADM5_CONFIG_AUTH_NOFALLBACK;
+ break;
+ default:
+ usage();
+ }
+ }
+ if ((ccache_name && use_keytab) ||
+ (keytab_name && !use_keytab) ||
+ (ccache_name && use_anonymous) ||
+ (use_anonymous && use_keytab))
+ usage();
+
+ if (query != NULL && argv[optind] != NULL) {
+ error(_("%s: -q is exclusive with command-line query"), whoami);
+ usage();
+ }
+
+ if (argv[optind] != NULL)
+ script_mode = TRUE;
+
+ if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) {
+ error(_("%s: unable to get default realm\n"), whoami);
+ exit(1);
+ }
+
+ params.mask |= KADM5_CONFIG_REALM;
+ params.realm = def_realm;
+
+ if (params.mask & KADM5_CONFIG_OLD_AUTH_GSSAPI)
+ svcname = KADM5_ADMIN_SERVICE;
+ else
+ svcname = NULL;
+
+ /*
+ * Set cc to an open credentials cache, either specified by the -c
+ * argument or the default.
+ */
+ if (ccache_name == NULL) {
+ retval = krb5_cc_default(context, &cc);
+ if (retval) {
+ com_err(whoami, retval,
+ _("while opening default credentials cache"));
+ exit(1);
+ }
+ } else {
+ retval = krb5_cc_resolve(context, ccache_name, &cc);
+ if (retval) {
+ com_err(whoami, retval, _("while opening credentials cache %s"),
+ ccache_name);
+ exit(1);
+ }
+ }
+
+ /*
+ * If no principal name is specified: If authenticating anonymously, use
+ * the anonymouse principal for the local realm, else 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 (use_anonymous) {
+ if (asprintf(&princstr, "%s/%s@%s", KRB5_WELLKNOWN_NAMESTR,
+ KRB5_ANONYMOUS_PRINCSTR, def_realm) < 0) {
+ error(_("%s: out of memory\n"), whoami);
+ exit(1);
+ }
+ freeprinc++;
+ } else if (ccache_name != NULL &&
+ !krb5_cc_get_principal(context, cc, &princ)) {
+ retval = krb5_unparse_name(context, princ, &princstr);
+ if (retval) {
+ com_err(whoami, retval,
+ _("while canonicalizing principal name"));
+ exit(1);
+ }
+ krb5_free_principal(context, princ);
+ freeprinc++;
+ } else if (use_keytab != 0) {
+ retval = krb5_sname_to_principal(context, NULL, "host",
+ KRB5_NT_SRV_HST, &princ);
+ if (retval) {
+ com_err(whoami, retval, _("creating host service principal"));
+ exit(1);
+ }
+ retval = krb5_unparse_name(context, princ, &princstr);
+ if (retval) {
+ com_err(whoami, retval,
+ _("while canonicalizing principal name"));
+ exit(1);
+ }
+ krb5_free_principal(context, princ);
+ freeprinc++;
+ } else if (!krb5_cc_get_principal(context, cc, &princ)) {
+ if (krb5_unparse_name(context, princ, &canon)) {
+ error(_("%s: unable to canonicalize principal\n"), whoami);
+ exit(1);
+ }
+ /* Strip out realm of principal if it's there. */
+ realm = strchr(canon, '@');
+ while (realm) {
+ if (realm > canon && *(realm - 1) != '\\')
+ break;
+ realm = strchr(realm + 1, '@');
+ }
+ if (realm)
+ *realm++ = '\0';
+ cp = strchr(canon, '/');
+ while (cp) {
+ if (cp > canon && *(cp - 1) != '\\')
+ break;
+ cp = strchr(cp + 1, '/');
+ }
+ if (cp != NULL)
+ *cp = '\0';
+ if (asprintf(&princstr, "%s/admin%s%s", canon,
+ (realm) ? "@" : "",
+ (realm) ? realm : "") < 0) {
+ error(_("%s: out of memory\n"), whoami);
+ exit(1);
+ }
+ free(canon);
+ krb5_free_principal(context, princ);
+ freeprinc++;
+ } else if ((luser = getenv("USER"))) {
+ if (asprintf(&princstr, "%s/admin@%s", luser, def_realm) < 0) {
+ error(_("%s: out of memory\n"), whoami);
+ exit(1);
+ }
+ freeprinc++;
+ } else if ((pw = getpwuid(getuid()))) {
+ if (asprintf(&princstr, "%s/admin@%s", pw->pw_name,
+ def_realm) < 0) {
+ error(_("%s: out of memory\n"), whoami);
+ exit(1);
+ }
+ freeprinc++;
+ } else {
+ error(_("%s: unable to figure out a principal name\n"), whoami);
+ exit(1);
+ }
+ }
+
+ retval = krb5_klog_init(context, "admin_server", whoami, 0);
+ if (retval) {
+ com_err(whoami, retval, _("while setting up logging"));
+ exit(1);
+ }
+
+ /*
+ * Initialize the kadm5 connection. If we were given a ccache,
+ * use it. Otherwise, use/prompt for the password.
+ */
+ if (ccache_name) {
+ info(_("Authenticating as principal %s with existing "
+ "credentials.\n"), princstr);
+ retval = kadm5_init_with_creds(context, princstr, cc, svcname, &params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_4, db_args, &handle);
+ } else if (use_anonymous) {
+ info(_("Authenticating as principal %s with password; "
+ "anonymous requested.\n"), princstr);
+ retval = kadm5_init_anonymous(context, princstr, svcname, &params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_4, db_args, &handle);
+ } else if (use_keytab) {
+ if (keytab_name != NULL) {
+ info(_("Authenticating as principal %s with keytab %s.\n"),
+ princstr, keytab_name);
+ } else {
+ info(_("Authenticating as principal %s with default keytab.\n"),
+ princstr);
+ }
+ retval = kadm5_init_with_skey(context, princstr, keytab_name, svcname,
+ &params, KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_4, db_args, &handle);
+ } else {
+ info(_("Authenticating as principal %s with password.\n"),
+ princstr);
+ retval = kadm5_init_with_password(context, princstr, password, svcname,
+ &params, KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_4, db_args,
+ &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);
+
+ free(params.keysalts);
+ free(db_name);
+ free(db_args);
+
+ retval = krb5_cc_close(context, cc);
+ if (retval) {
+ com_err(whoami, retval, _("while closing ccache %s"), ccache_name);
+ exit(1);
+ }
+
+ retval = kadm5_init_iprop(handle, 0);
+ if (retval) {
+ com_err(whoami, retval, _("while mapping update log"));
+ exit(1);
+ }
+
+ *request_out = query;
+ *args_out = argv + optind;
+}
+
+int
+quit()
+{
+ kadm5_ret_t retval;
+
+ if (locked) {
+ retval = kadm5_unlock(handle);
+ if (retval) {
+ com_err("quit", retval, _("while unlocking locked database"));
+ return 1;
+ }
+ locked = 0;
+ }
+
+ kadm5_destroy(handle);
+ if (ccache_name != NULL && !script_mode) {
+ fprintf(stderr, "\n\a\a\a%s",
+ _("Administration credentials NOT DESTROYED.\n"));
+ }
+
+ /* insert more random cleanup here */
+ krb5_klog_close(context);
+ krb5_free_context(context);
+ return 0;
+}
+
+void
+kadmin_lock(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+
+ if (locked)
+ return;
+ retval = kadm5_lock(handle);
+ if (retval) {
+ com_err("lock", retval, "");
+ return;
+ }
+ locked = 1;
+}
+
+void
+kadmin_unlock(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+
+ if (!locked)
+ return;
+ retval = kadm5_unlock(handle);
+ if (retval) {
+ com_err("unlock", retval, "");
+ return;
+ }
+ locked = 0;
+}
+
+void
+kadmin_delprinc(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+ krb5_principal princ = NULL;
+ char *canon = NULL;
+ char reply[5];
+
+ if (! (argc == 2 ||
+ (argc == 3 && !strcmp("-force", argv[1])))) {
+ error(_("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"));
+ goto cleanup;
+ }
+ if (argc == 2 && !script_mode) {
+ 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);
+ goto cleanup;
+ }
+ }
+ retval = kadm5_delete_principal(handle, princ);
+ if (retval) {
+ com_err("delete_principal", retval,
+ _("while deleting principal \"%s\""), canon);
+ goto cleanup;
+ }
+ info(_("Principal \"%s\" deleted.\n"), canon);
+ info(_("Make sure that you have removed this principal from all ACLs "
+ "before reusing.\n"));
+
+cleanup:
+ krb5_free_principal(context, princ);
+ free(canon);
+}
+
+void
+kadmin_renameprinc(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+ krb5_principal oprinc = NULL, nprinc = NULL;
+ char *ocanon = NULL, *ncanon = NULL;
+ char reply[5];
+
+ if (!(argc == 3 || (argc == 4 && !strcmp("-force", argv[1])))) {
+ error(_("usage: rename_principal [-force] old_principal "
+ "new_principal\n"));
+ return;
+ }
+ retval = kadmin_parse_name(argv[argc - 2], &oprinc);
+ if (retval) {
+ com_err("rename_principal", retval,
+ _("while parsing old principal name"));
+ goto cleanup;
+ }
+ retval = kadmin_parse_name(argv[argc - 1], &nprinc);
+ if (retval) {
+ com_err("rename_principal", retval,
+ _("while parsing new principal name"));
+ goto cleanup;
+ }
+ retval = krb5_unparse_name(context, oprinc, &ocanon);
+ if (retval) {
+ com_err("rename_principal", retval,
+ _("while canonicalizing old principal"));
+ goto cleanup;
+ }
+ retval = krb5_unparse_name(context, nprinc, &ncanon);
+ if (retval) {
+ com_err("rename_principal", retval,
+ _("while canonicalizing new principal"));
+ goto cleanup;
+ }
+ if (argc == 3 && !script_mode) {
+ printf(_("Are you sure you want to rename the principal \"%s\" "
+ "to \"%s\"? (yes/no): "), ocanon, ncanon);
+ fgets(reply, sizeof(reply), stdin);
+ if (strcmp("yes\n", reply)) {
+ fprintf(stderr, _("Principal \"%s\" not renamed\n"), ocanon);
+ goto cleanup;
+ }
+ }
+ retval = kadm5_rename_principal(handle, oprinc, nprinc);
+ if (retval) {
+ com_err("rename_principal", retval,
+ _("while renaming principal \"%s\" to \"%s\""),
+ ocanon, ncanon);
+ goto cleanup;
+ }
+ info(_("Principal \"%s\" renamed to \"%s\".\n"), ocanon, ncanon);
+ info(_("Make sure that you have removed the old principal from all ACLs "
+ "before reusing.\n"));
+
+cleanup:
+ krb5_free_principal(context, nprinc);
+ krb5_free_principal(context, oprinc);
+ free(ncanon);
+ free(ocanon);
+}
+
+static void
+cpw_usage(const char *str)
+{
+ if (str)
+ error("%s\n", str);
+ error(_("usage: change_password [-randkey] [-keepold] "
+ "[-e keysaltlist] [-pw password] principal\n"));
+}
+
+void
+kadmin_cpw(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+ static char newpw[1024];
+ static char prompt1[1024], prompt2[1024];
+ char *canon = NULL, *pwarg = NULL;
+ int n_ks_tuple = 0, randkey = 0;
+ krb5_boolean keepold = FALSE;
+ krb5_key_salt_tuple *ks_tuple = NULL;
+ krb5_principal princ = NULL;
+ char **db_args = NULL;
+ int db_args_size = 0;
+
+ if (argc < 2) {
+ cpw_usage(NULL);
+ return;
+ }
+ for (argv++, argc--; argc > 1; argc--, argv++) {
+ if (!strcmp("-x", *argv)) {
+ argc--;
+ if (argc < 1) {
+ cpw_usage(_("change_password: missing db argument"));
+ goto cleanup;
+ }
+ db_args_size++;
+ db_args = realloc(db_args, sizeof(char*) * (db_args_size + 1));
+ if (db_args == NULL) {
+ error(_("change_password: Not enough memory\n"));
+ exit(1);
+ }
+ db_args[db_args_size - 1] = *++argv;
+ db_args[db_args_size] = NULL;
+ } else if (!strcmp("-pw", *argv)) {
+ argc--;
+ if (argc < 1) {
+ cpw_usage(_("change_password: missing password arg"));
+ goto cleanup;
+ }
+ pwarg = *++argv;
+ } else if (!strcmp("-randkey", *argv)) {
+ randkey++;
+ } else if (!strcmp("-keepold", *argv)) {
+ keepold = TRUE;
+ } else if (!strcmp("-e", *argv)) {
+ argc--;
+ if (argc < 1) {
+ cpw_usage(_("change_password: missing keysaltlist arg"));
+ goto cleanup;
+ }
+ retval = krb5_string_to_keysalts(*++argv, NULL, NULL, 0,
+ &ks_tuple, &n_ks_tuple);
+ if (retval) {
+ com_err("change_password", retval,
+ _("while parsing keysalts %s"), *argv);
+ goto cleanup;
+ }
+ } else {
+ cpw_usage(NULL);
+ goto cleanup;
+ }
+ }
+ if (*argv == NULL) {
+ com_err("change_password", 0, _("missing principal name"));
+ cpw_usage(NULL);
+ goto cleanup;
+ }
+ retval = kadmin_parse_name(*argv, &princ);
+ if (retval) {
+ com_err("change_password", retval, _("while parsing principal name"));
+ goto cleanup;
+ }
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("change_password", retval,
+ _("while canonicalizing principal"));
+ goto cleanup;
+ }
+ if (pwarg != NULL) {
+ if (keepold || ks_tuple != NULL) {
+ retval = kadm5_chpass_principal_3(handle, princ, keepold,
+ n_ks_tuple, ks_tuple, pwarg);
+ } else {
+ retval = kadm5_chpass_principal(handle, princ, pwarg);
+ }
+ if (retval) {
+ com_err("change_password", retval,
+ _("while changing password for \"%s\"."), canon);
+ goto cleanup;
+ }
+ info(_("Password for \"%s\" changed.\n"), canon);
+ } else if (randkey) {
+ retval = randkey_princ(handle, princ, keepold, n_ks_tuple, ks_tuple,
+ NULL, NULL);
+ if (retval) {
+ com_err("change_password", retval,
+ _("while randomizing key for \"%s\"."), canon);
+ goto cleanup;
+ }
+ info(_("Key for \"%s\" randomized.\n"), canon);
+ } else {
+ unsigned int i = sizeof (newpw) - 1;
+
+ snprintf(prompt1, sizeof(prompt1),
+ _("Enter password for principal \"%s\""), canon);
+ snprintf(prompt2, sizeof(prompt2),
+ _("Re-enter password for principal \"%s\""), canon);
+ retval = krb5_read_password(context, prompt1, prompt2,
+ newpw, &i);
+ if (retval) {
+ com_err("change_password", retval,
+ _("while reading password for \"%s\"."), canon);
+ goto cleanup;
+ }
+ if (keepold || ks_tuple != NULL) {
+ retval = kadm5_chpass_principal_3(handle, princ, keepold,
+ n_ks_tuple, ks_tuple,
+ newpw);
+ } else {
+ retval = kadm5_chpass_principal(handle, princ, newpw);
+ }
+ memset(newpw, 0, sizeof (newpw));
+ if (retval) {
+ com_err("change_password", retval,
+ _("while changing password for \"%s\"."), canon);
+ goto cleanup;
+ }
+ info(_("Password for \"%s\" changed.\n"), canon);
+ }
+cleanup:
+ free(canon);
+ free(db_args);
+ krb5_free_principal(context, princ);
+ free(ks_tuple);
+}
+
+static void
+kadmin_free_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap)
+{
+ krb5_tl_data *tl_data = *tl_datap, *next;
+ int n_tl_data = *n_tl_datap;
+ int i;
+
+ *n_tl_datap = 0;
+ *tl_datap = NULL;
+
+ for (i = 0; tl_data && (i < n_tl_data); i++) {
+ next = tl_data->tl_data_next;
+ free(tl_data->tl_data_contents);
+ free(tl_data);
+ tl_data = next;
+ }
+}
+
+/* Construct a tl_data element and add it to the tail of *tl_datap. */
+static void
+add_tl_data(krb5_int16 *n_tl_datap, krb5_tl_data **tl_datap,
+ krb5_int16 tl_type, krb5_ui_2 len, krb5_octet *contents)
+{
+ krb5_tl_data *tl_data;
+ krb5_octet *copy;
+
+ copy = malloc(len);
+ tl_data = calloc(1, sizeof(*tl_data));
+ if (copy == NULL || tl_data == NULL) {
+ error(_("Not enough memory\n"));
+ exit(1);
+ }
+ memcpy(copy, contents, len);
+
+ tl_data->tl_data_type = tl_type;
+ tl_data->tl_data_length = len;
+ tl_data->tl_data_contents = copy;
+ tl_data->tl_data_next = NULL;
+
+ for (; *tl_datap != NULL; tl_datap = &(*tl_datap)->tl_data_next);
+ *tl_datap = tl_data;
+ (*n_tl_datap)++;
+}
+
+static void
+unlock_princ(kadm5_principal_ent_t princ, long *mask, const char *caller)
+{
+ krb5_error_code retval;
+ krb5_timestamp now;
+ krb5_octet timebuf[4];
+
+ /* Zero out the failed auth count. */
+ princ->fail_auth_count = 0;
+ *mask |= KADM5_FAIL_AUTH_COUNT;
+
+ /* Record the timestamp of this unlock operation so that slave KDCs will
+ * see it, since fail_auth_count is unreplicated. */
+ retval = krb5_timeofday(context, &now);
+ if (retval) {
+ com_err(caller, retval, _("while getting time"));
+ exit(1);
+ }
+ store_32_le((krb5_int32)now, timebuf);
+ add_tl_data(&princ->n_tl_data, &princ->tl_data,
+ KRB5_TL_LAST_ADMIN_UNLOCK, 4, timebuf);
+ *mask |= KADM5_TL_DATA;
+}
+
+/*
+ * Parse addprinc or modprinc arguments. Some output fields may be
+ * filled in on error.
+ */
+static int
+kadmin_parse_princ_args(int argc, char *argv[], kadm5_principal_ent_t oprinc,
+ long *mask, char **pass, krb5_boolean *randkey,
+ krb5_boolean *nokey, krb5_key_salt_tuple **ks_tuple,
+ int *n_ks_tuple, char *caller)
+{
+ int i;
+ time_t now, date, interval;
+ krb5_error_code retval;
+
+ *mask = 0;
+ *pass = NULL;
+ *n_ks_tuple = 0;
+ *ks_tuple = NULL;
+ time(&now);
+ *randkey = FALSE;
+ *nokey = FALSE;
+ for (i = 1; i < argc - 1; i++) {
+ if (!strcmp("-x",argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+
+ add_tl_data(&oprinc->n_tl_data, &oprinc->tl_data,
+ KRB5_TL_DB_ARGS, strlen(argv[i]) + 1,
+ (krb5_octet *)argv[i]);
+ *mask |= KADM5_TL_DATA;
+ continue;
+ }
+ if (!strcmp("-expire", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ date = parse_date(argv[i], now);
+ if (date == (time_t)-1)
+ return -1;
+ oprinc->princ_expire_time = date;
+ *mask |= KADM5_PRINC_EXPIRE_TIME;
+ continue;
+ }
+ if (!strcmp("-pwexpire", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ date = parse_date(argv[i], now);
+ if (date == (time_t)-1)
+ return -1;
+ oprinc->pw_expiration = date;
+ *mask |= KADM5_PW_EXPIRATION;
+ continue;
+ }
+ if (!strcmp("-maxlife", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ interval = parse_interval(argv[i], now);
+ if (interval == (time_t)-1)
+ return -1;
+ oprinc->max_life = interval;
+ *mask |= KADM5_MAX_LIFE;
+ continue;
+ }
+ if (!strcmp("-maxrenewlife", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ interval = parse_interval(argv[i], now);
+ if (interval == (time_t)-1)
+ return -1;
+ oprinc->max_renewable_life = interval;
+ *mask |= KADM5_MAX_RLIFE;
+ continue;
+ }
+ if (!strcmp("-kvno", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ oprinc->kvno = atoi(argv[i]);
+ *mask |= KADM5_KVNO;
+ continue;
+ }
+ if (!strcmp("-policy", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ oprinc->policy = argv[i];
+ *mask |= KADM5_POLICY;
+ continue;
+ }
+ if (!strcmp("-clearpolicy", argv[i])) {
+ oprinc->policy = NULL;
+ *mask |= KADM5_POLICY_CLR;
+ continue;
+ }
+ if (!strcmp("-pw", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ *pass = argv[i];
+ continue;
+ }
+ if (!strcmp("-randkey", argv[i])) {
+ *randkey = TRUE;
+ continue;
+ }
+ if (!strcmp("-nokey", argv[i])) {
+ *nokey = TRUE;
+ continue;
+ }
+ if (!strcmp("-unlock", argv[i])) {
+ unlock_princ(oprinc, mask, caller);
+ continue;
+ }
+ if (!strcmp("-e", argv[i])) {
+ if (++i > argc - 2)
+ return -1;
+ retval = krb5_string_to_keysalts(argv[i], NULL, NULL, 0,
+ ks_tuple, n_ks_tuple);
+ if (retval) {
+ com_err(caller, retval, _("while parsing keysalts %s"),
+ argv[i]);
+ return -1;
+ }
+ continue;
+ }
+ retval = krb5_flagspec_to_mask(argv[i], &oprinc->attributes,
+ &oprinc->attributes);
+ if (retval)
+ return -1;
+ else
+ *mask |= KADM5_ATTRIBUTES;
+ }
+ if (i != argc - 1)
+ return -1;
+ retval = kadmin_parse_name(argv[i], &oprinc->principal);
+ if (retval) {
+ com_err(caller, retval, _("while parsing principal"));
+ return -1;
+ }
+ return 0;
+}
+
+static void
+kadmin_addprinc_usage()
+{
+ error(_("usage: add_principal [options] principal\n"));
+ error(_("\toptions are:\n"));
+ error(_("\t\t[-randkey|-nokey] [-x db_princ_args]* [-expire expdate] "
+ "[-pwexpire pwexpdate] [-maxlife maxtixlife]\n"
+ "\t\t[-kvno kvno] [-policy policy] [-clearpolicy]\n"
+ "\t\t[-pw password] [-maxrenewlife maxrenewlife]\n"
+ "\t\t[-e keysaltlist]\n\t\t[{+|-}attribute]\n"));
+ error(_("\tattributes are:\n"));
+ error(_("\t\tallow_postdated allow_forwardable allow_tgs_req "
+ "allow_renewable\n"
+ "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n"
+ "\t\trequires_hwauth needchange allow_svr "
+ "password_changing_service\n"
+ "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"
+ "\t\tlockdown_keys\n"
+ "\nwhere,\n\t[-x db_princ_args]* - any number of database "
+ "specific arguments.\n"
+ "\t\t\tLook at each database documentation for supported "
+ "arguments\n"));
+}
+
+static void
+kadmin_modprinc_usage()
+{
+ error(_("usage: modify_principal [options] principal\n"));
+ error(_("\toptions are:\n"));
+ error(_("\t\t[-x db_princ_args]* [-expire expdate] "
+ "[-pwexpire pwexpdate] [-maxlife maxtixlife]\n"
+ "\t\t[-kvno kvno] [-policy policy] [-clearpolicy]\n"
+ "\t\t[-maxrenewlife maxrenewlife] [-unlock] [{+|-}attribute]\n"));
+ error(_("\tattributes are:\n"));
+ error(_("\t\tallow_postdated allow_forwardable allow_tgs_req "
+ "allow_renewable\n"
+ "\t\tallow_proxiable allow_dup_skey allow_tix requires_preauth\n"
+ "\t\trequires_hwauth needchange allow_svr "
+ "password_changing_service\n"
+ "\t\tok_as_delegate ok_to_auth_as_delegate no_auth_data_required\n"
+ "\t\tlockdown_keys\n"
+ "\nwhere,\n\t[-x db_princ_args]* - any number of database "
+ "specific arguments.\n"
+ "\t\t\tLook at each database documentation for supported "
+ "arguments\n"));
+}
+
+/* Create a dummy password for old-style (pre-1.8) randkey creation. */
+static void
+prepare_dummy_password(char *buf, size_t sz)
+{
+ size_t i;
+
+ /* Must try to pass any password policy in place, and be valid UTF-8. */
+ strlcpy(buf, "6F a[", sz);
+ for (i = strlen(buf); i < sz - 1; i++)
+ buf[i] = 'a' + (i % 26);
+ buf[sz - 1] = '\0';
+}
+
+void
+kadmin_addprinc(int argc, char *argv[])
+{
+ kadm5_principal_ent_rec princ;
+ long mask;
+ krb5_boolean randkey = FALSE, nokey = FALSE, old_style_randkey = FALSE;
+ int n_ks_tuple;
+ krb5_key_salt_tuple *ks_tuple = NULL;
+ char *pass, *canon = NULL;
+ krb5_error_code retval;
+ char newpw[1024], dummybuf[256];
+ static char prompt1[1024], prompt2[1024];
+
+ /* Zero all fields in request structure */
+ memset(&princ, 0, sizeof(princ));
+
+ princ.attributes = 0;
+ if (kadmin_parse_princ_args(argc, argv, &princ, &mask, &pass, &randkey,
+ &nokey, &ks_tuple, &n_ks_tuple,
+ "add_principal")) {
+ kadmin_addprinc_usage();
+ goto cleanup;
+ }
+
+ retval = krb5_unparse_name(context, princ.principal, &canon);
+ if (retval) {
+ com_err("add_principal", retval, _("while canonicalizing principal"));
+ goto cleanup;
+ }
+
+ if (mask & KADM5_POLICY) {
+ /* Warn if the specified policy does not exist. */
+ if (!script_mode && !policy_exists(princ.policy)) {
+ fprintf(stderr, _("WARNING: policy \"%s\" does not exist\n"),
+ princ.policy);
+ }
+ } else if (!(mask & KADM5_POLICY_CLR)) {
+ /* If the policy "default" exists, assign it. */
+ if (policy_exists("default")) {
+ if (!script_mode) {
+ fprintf(stderr, _("NOTICE: no policy specified for %s; "
+ "assigning \"default\"\n"), canon);
+ }
+ princ.policy = "default";
+ mask |= KADM5_POLICY;
+ } else if (!script_mode) {
+ fprintf(stderr, _("WARNING: no policy specified for %s; "
+ "defaulting to no policy\n"), canon);
+ }
+ }
+ /* Don't send KADM5_POLICY_CLR to the server. */
+ mask &= ~KADM5_POLICY_CLR;
+
+ if (nokey) {
+ pass = NULL;
+ mask |= KADM5_KEY_DATA;
+ } else if (randkey) {
+ pass = NULL;
+ } else if (pass == NULL) {
+ unsigned int sz = sizeof(newpw) - 1;
+
+ snprintf(prompt1, sizeof(prompt1),
+ _("Enter password for principal \"%s\""), canon);
+ snprintf(prompt2, sizeof(prompt2),
+ _("Re-enter password for principal \"%s\""), canon);
+ retval = krb5_read_password(context, prompt1, prompt2, newpw, &sz);
+ if (retval) {
+ com_err("add_principal", retval,
+ _("while reading password for \"%s\"."), canon);
+ goto cleanup;
+ }
+ pass = newpw;
+ }
+ mask |= KADM5_PRINCIPAL;
+ retval = create_princ(&princ, mask, n_ks_tuple, ks_tuple, pass);
+ if (retval == EINVAL && randkey) {
+ /*
+ * The server doesn't support randkey creation. Create the principal
+ * with a dummy password and disallow tickets.
+ */
+ prepare_dummy_password(dummybuf, sizeof(dummybuf));
+ princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ mask |= KADM5_ATTRIBUTES;
+ pass = dummybuf;
+ retval = create_princ(&princ, mask, n_ks_tuple, ks_tuple, pass);
+ old_style_randkey = 1;
+ }
+ if (retval == KADM5_BAD_MASK && nokey) {
+ error(_("Admin server does not support -nokey while creating "
+ "\"%s\"\n"), canon);
+ goto cleanup;
+ }
+ if (retval) {
+ com_err("add_principal", retval, "while creating \"%s\".", canon);
+ goto cleanup;
+ }
+ if (old_style_randkey) {
+ /* Randomize the password and re-enable tickets. */
+ retval = randkey_princ(handle, princ.principal, FALSE, n_ks_tuple,
+ ks_tuple, NULL, NULL);
+ if (retval) {
+ com_err("add_principal", retval,
+ _("while randomizing key for \"%s\"."), canon);
+ goto cleanup;
+ }
+ 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);
+ goto cleanup;
+ }
+ }
+ info("Principal \"%s\" created.\n", canon);
+
+cleanup:
+ krb5_free_principal(context, princ.principal);
+ free(ks_tuple);
+ free(canon);
+ kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data);
+}
+
+void
+kadmin_modprinc(int argc, char *argv[])
+{
+ kadm5_principal_ent_rec princ, oldprinc;
+ krb5_principal kprinc = NULL;
+ long mask;
+ krb5_error_code retval;
+ char *pass, *canon = NULL;
+ krb5_boolean randkey = FALSE, nokey = FALSE;
+ int n_ks_tuple = 0;
+ krb5_key_salt_tuple *ks_tuple = NULL;
+
+ if (argc < 2) {
+ kadmin_modprinc_usage();
+ return;
+ }
+
+ memset(&oldprinc, 0, sizeof(oldprinc));
+ memset(&princ, 0, sizeof(princ));
+
+ 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"));
+ goto cleanup;
+ }
+ retval = kadm5_get_principal(handle, kprinc, &oldprinc,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ if (retval) {
+ com_err("modify_principal", retval, _("while getting \"%s\"."), canon);
+ goto cleanup;
+ }
+ princ.attributes = oldprinc.attributes;
+ kadm5_free_principal_ent(handle, &oldprinc);
+ retval = kadmin_parse_princ_args(argc, argv,
+ &princ, &mask,
+ &pass, &randkey, &nokey,
+ &ks_tuple, &n_ks_tuple,
+ "modify_principal");
+ if (retval || ks_tuple != NULL || randkey || nokey || pass) {
+ kadmin_modprinc_usage();
+ goto cleanup;
+ }
+ if (mask & KADM5_POLICY) {
+ /* Warn if the specified policy does not exist. */
+ if (!script_mode && !policy_exists(princ.policy)) {
+ fprintf(stderr, _("WARNING: policy \"%s\" does not exist\n"),
+ princ.policy);
+ }
+ }
+ if (mask) {
+ /* Skip this if all we're doing is setting certhash. */
+ retval = kadm5_modify_principal(handle, &princ, mask);
+ }
+ if (retval) {
+ com_err("modify_principal", retval, _("while modifying \"%s\"."),
+ canon);
+ goto cleanup;
+ }
+ info(_("Principal \"%s\" modified.\n"), canon);
+cleanup:
+ krb5_free_principal(context, kprinc);
+ krb5_free_principal(context, princ.principal);
+ kadmin_free_tl_data(&princ.n_tl_data, &princ.tl_data);
+ free(canon);
+ free(ks_tuple);
+}
+
+void
+kadmin_getprinc(int argc, char *argv[])
+{
+ kadm5_principal_ent_rec dprinc;
+ krb5_principal princ = NULL;
+ krb5_error_code retval;
+ const char *polname, *noexist;
+ char *canon = NULL, *princstr = NULL, *modprincstr = NULL;
+ char **sp = NULL, **attrstrs = NULL;
+ int i;
+
+ if (!(argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) {
+ error(_("usage: get_principal [-terse] principal\n"));
+ return;
+ }
+
+ memset(&dprinc, 0, sizeof(dprinc));
+
+ 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"));
+ goto cleanup;
+ }
+ retval = kadm5_get_principal(handle, princ, &dprinc,
+ KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA);
+ if (retval) {
+ com_err("get_principal", retval, _("while retrieving \"%s\"."), canon);
+ goto cleanup;
+ }
+ retval = krb5_unparse_name(context, dprinc.principal, &princstr);
+ if (retval) {
+ com_err("get_principal", retval, _("while unparsing principal"));
+ goto cleanup;
+ }
+ retval = krb5_unparse_name(context, dprinc.mod_name, &modprincstr);
+ if (retval) {
+ com_err("get_principal", retval, _("while unparsing principal"));
+ goto cleanup;
+ }
+ if (argc == 2) {
+ printf(_("Principal: %s\n"), princstr);
+ printf(_("Expiration date: %s\n"), dprinc.princ_expire_time ?
+ strdate(dprinc.princ_expire_time) : _("[never]"));
+ printf(_("Last password change: %s\n"), dprinc.last_pwd_change ?
+ strdate(dprinc.last_pwd_change) : _("[never]"));
+ printf(_("Password expiration date: %s\n"),
+ dprinc.pw_expiration ?
+ strdate(dprinc.pw_expiration) : _("[never]"));
+ printf(_("Maximum ticket life: %s\n"), strdur(dprinc.max_life));
+ printf(_("Maximum renewable life: %s\n"),
+ strdur(dprinc.max_renewable_life));
+ printf(_("Last modified: %s (%s)\n"), strdate(dprinc.mod_date),
+ modprincstr);
+ printf(_("Last successful authentication: %s\n"),
+ dprinc.last_success ? strdate(dprinc.last_success) :
+ _("[never]"));
+ printf("Last failed authentication: %s\n",
+ dprinc.last_failed ? strdate(dprinc.last_failed) :
+ "[never]");
+ 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_name(key_data->key_data_type[0], FALSE,
+ enctype, sizeof(enctype)))
+ snprintf(enctype, sizeof(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 &&
+ key_data->key_data_type[1] != KRB5_KDB_SALTTYPE_NORMAL) {
+ if (krb5_salttype_to_string(key_data->key_data_type[1],
+ salttype, sizeof(salttype)))
+ snprintf(salttype, sizeof(salttype), _("<Salt type 0x%x>"),
+ key_data->key_data_type[1]);
+ printf(":%s", salttype);
+ }
+ printf("\n");
+ }
+ printf(_("MKey: vno %d\n"), dprinc.mkvno);
+
+ printf(_("Attributes:"));
+ retval = krb5_flags_to_strings(dprinc.attributes, &attrstrs);
+ if (retval) {
+ com_err("get_principal", retval, _("while printing flags"));
+ return;
+ }
+ for (sp = attrstrs; sp != NULL && *sp != NULL; sp++) {
+ printf(" %s", *sp);
+ free(*sp);
+ }
+ free(attrstrs);
+ printf("\n");
+ polname = (dprinc.policy != NULL) ? dprinc.policy : _("[none]");
+ noexist = (dprinc.policy != NULL && !policy_exists(dprinc.policy)) ?
+ _(" [does not exist]") : "";
+ printf(_("Policy: %s%s\n"), polname, noexist);
+ } 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",
+ princstr, dprinc.princ_expire_time, dprinc.last_pwd_change,
+ dprinc.pw_expiration, dprinc.max_life, modprincstr,
+ dprinc.mod_date, dprinc.attributes, dprinc.kvno,
+ dprinc.mkvno, dprinc.policy ? dprinc.policy : "[none]",
+ 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");
+ }
+cleanup:
+ krb5_free_principal(context, princ);
+ kadm5_free_principal_ent(handle, &dprinc);
+ free(canon);
+ free(princstr);
+ free(modprincstr);
+}
+
+void
+kadmin_getprincs(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ char *expr, **names;
+ int i, count;
+
+ expr = NULL;
+ if (!(argc == 1 || (argc == 2 && (expr = argv[1])))) {
+ error(_("usage: get_principals [expression]\n"));
+ return;
+ }
+ retval = kadm5_get_principals(handle, expr, &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);
+}
+
+static int
+kadmin_parse_policy_args(int argc, char *argv[], kadm5_policy_ent_t policy,
+ long *mask, char *caller)
+{
+ krb5_error_code retval;
+ int i;
+ time_t now, interval;
+
+ time(&now);
+ *mask = 0;
+ for (i = 1; i < argc - 1; i++) {
+ if (!strcmp(argv[i], "-maxlife")) {
+ if (++i > argc -2)
+ return -1;
+ interval = parse_interval(argv[i], now);
+ if (interval == (time_t)-1)
+ return -1;
+ policy->pw_max_life = interval;
+ *mask |= KADM5_PW_MAX_LIFE;
+ continue;
+ } else if (!strcmp(argv[i], "-minlife")) {
+ if (++i > argc - 2)
+ return -1;
+ interval = parse_interval(argv[i], now);
+ if (interval == (time_t)-1)
+ return -1;
+ policy->pw_min_life = interval;
+ *mask |= KADM5_PW_MIN_LIFE;
+ continue;
+ } else if (!strcmp(argv[i], "-minlength")) {
+ if (++i > argc - 2)
+ return -1;
+ policy->pw_min_length = atoi(argv[i]);
+ *mask |= KADM5_PW_MIN_LENGTH;
+ continue;
+ } else if (!strcmp(argv[i], "-minclasses")) {
+ if (++i > argc - 2)
+ return -1;
+ policy->pw_min_classes = atoi(argv[i]);
+ *mask |= KADM5_PW_MIN_CLASSES;
+ continue;
+ } else if (!strcmp(argv[i], "-history")) {
+ if (++i > argc - 2)
+ return -1;
+ policy->pw_history_num = atoi(argv[i]);
+ *mask |= KADM5_PW_HISTORY_NUM;
+ continue;
+ } else if (strlen(argv[i]) == 11 &&
+ !strcmp(argv[i], "-maxfailure")) {
+ if (++i > argc - 2)
+ return -1;
+ policy->pw_max_fail = atoi(argv[i]);
+ *mask |= KADM5_PW_MAX_FAILURE;
+ continue;
+ } else if (strlen(argv[i]) == 21 &&
+ !strcmp(argv[i], "-failurecountinterval")) {
+ if (++i > argc - 2)
+ return -1;
+ interval = parse_interval(argv[i], now);
+ if (interval == (time_t)-1)
+ return -1;
+ policy->pw_failcnt_interval = interval;
+ *mask |= KADM5_PW_FAILURE_COUNT_INTERVAL;
+ continue;
+ } else if (strlen(argv[i]) == 16 &&
+ !strcmp(argv[i], "-lockoutduration")) {
+ if (++i > argc - 2)
+ return -1;
+ interval = parse_interval(argv[i], now);
+ if (interval == (time_t)-1)
+ return -1;
+ policy->pw_lockout_duration = interval;
+ *mask |= KADM5_PW_LOCKOUT_DURATION;
+ continue;
+ } else if (!strcmp(argv[i], "-allowedkeysalts")) {
+ krb5_key_salt_tuple *ks_tuple = NULL;
+ int n_ks_tuple = 0;
+
+ if (++i > argc - 2)
+ return -1;
+ if (strcmp(argv[i], "-")) {
+ retval = krb5_string_to_keysalts(argv[i], ",", NULL, 0,
+ &ks_tuple, &n_ks_tuple);
+ if (retval) {
+ com_err(caller, retval, _("while parsing keysalts %s"),
+ argv[i]);
+ return -1;
+ }
+ free(ks_tuple);
+ policy->allowed_keysalts = argv[i];
+ }
+ *mask |= KADM5_POLICY_ALLOWED_KEYSALTS;
+ continue;
+ } else
+ return -1;
+ }
+ if (i != argc -1) {
+ error(_("%s: parser lost count!\n"), caller);
+ return -1;
+ } else
+ return 0;
+}
+
+static void
+kadmin_addmodpol_usage(char *func)
+{
+ error(_("usage; %s [options] policy\n"), func);
+ error(_("\toptions are:\n"));
+ error(_("\t\t[-maxlife time] [-minlife time] [-minlength length]\n"
+ "\t\t[-minclasses number] [-history number]\n"
+ "\t\t[-maxfailure number] [-failurecountinterval time]\n"
+ "\t\t[-allowedkeysalts keysalts]\n"));
+ error(_("\t\t[-lockoutduration time]\n"));
+}
+
+void
+kadmin_addpol(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ long mask;
+ kadm5_policy_ent_rec policy;
+
+ memset(&policy, 0, sizeof(policy));
+ if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) {
+ kadmin_addmodpol_usage("add_policy");
+ return;
+ }
+ 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);
+ }
+}
+
+void
+kadmin_modpol(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ long mask;
+ kadm5_policy_ent_rec policy;
+
+ memset(&policy, 0, sizeof(policy));
+ if (kadmin_parse_policy_args(argc, argv, &policy, &mask,
+ "modify_policy")) {
+ kadmin_addmodpol_usage("modify_policy");
+ return;
+ }
+ 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);
+ }
+}
+
+void
+kadmin_delpol(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ char reply[5];
+
+ if (!(argc == 2 || (argc == 3 && !strcmp("-force", argv[1])))) {
+ error(_("usage: delete_policy [-force] policy\n"));
+ return;
+ }
+ if (argc == 2 && !script_mode) {
+ 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]);
+ }
+}
+
+void
+kadmin_getpol(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ kadm5_policy_ent_rec policy;
+
+ if (!(argc == 2 || (argc == 3 && !strcmp("-terse", argv[1])))) {
+ error(_("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: %s\n"), strdur(policy.pw_max_life));
+ printf(_("Minimum password life: %s\n"), strdur(policy.pw_min_life));
+ printf(_("Minimum password length: %ld\n"), policy.pw_min_length);
+ printf(_("Minimum number of password character classes: %ld\n"),
+ policy.pw_min_classes);
+ printf(_("Number of old keys kept: %ld\n"), policy.pw_history_num);
+ printf(_("Maximum password failures before lockout: %lu\n"),
+ (unsigned long)policy.pw_max_fail);
+ printf(_("Password failure count reset interval: %s\n"),
+ strdur(policy.pw_failcnt_interval));
+ printf(_("Password lockout duration: %s\n"),
+ strdur(policy.pw_lockout_duration));
+ if (policy.allowed_keysalts != NULL)
+ printf(_("Allowed key/salt types: %s\n"), policy.allowed_keysalts);
+ } else {
+ /* Output 0 where we used to output policy_refcnt. */
+ printf("\"%s\"\t%ld\t%ld\t%ld\t%ld\t%ld\t0\t%lu\t%ld\t%ld\t%s\n",
+ policy.policy, policy.pw_max_life, policy.pw_min_life,
+ policy.pw_min_length, policy.pw_min_classes,
+ policy.pw_history_num, (unsigned long)policy.pw_max_fail,
+ (long)policy.pw_failcnt_interval,
+ (long)policy.pw_lockout_duration,
+ (policy.allowed_keysalts == NULL) ? "-" :
+ policy.allowed_keysalts);
+ }
+ kadm5_free_policy_ent(handle, &policy);
+}
+
+void
+kadmin_getpols(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ char *expr, **names;
+ int i, count;
+
+ expr = NULL;
+ if (!(argc == 1 || (argc == 2 && (expr = argv[1])))) {
+ error(_("usage: get_policies [expression]\n"));
+ return;
+ }
+ retval = kadm5_get_policies(handle, expr, &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);
+}
+
+void
+kadmin_getprivs(int argc, char *argv[])
+{
+ static char *privs[] = {"INQUIRE", "ADD", "MODIFY", "DELETE"};
+ krb5_error_code retval;
+ size_t i;
+ long plist;
+
+ if (argc != 1) {
+ error(_("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");
+}
+
+void
+kadmin_purgekeys(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+ int keepkvno = -1;
+ char *pname = NULL, *canon = NULL;
+ krb5_principal princ;
+
+ if (argc == 4 && strcmp(argv[1], "-keepkvno") == 0) {
+ keepkvno = atoi(argv[2]);
+ pname = argv[3];
+ } else if (argc == 3 && strcmp(argv[1], "-all") == 0) {
+ keepkvno = KRB5_INT32_MAX;
+ pname = argv[2];
+ } else if (argc == 2) {
+ pname = argv[1];
+ }
+ if (pname == NULL) {
+ error(_("usage: purgekeys [-all|-keepkvno oldest_kvno_to_keep] "
+ "principal\n"));
+ return;
+ }
+
+ retval = kadmin_parse_name(pname, &princ);
+ if (retval) {
+ com_err("purgekeys", retval, _("while parsing principal"));
+ return;
+ }
+
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("purgekeys", retval, _("while canonicalizing principal"));
+ goto cleanup;
+ }
+
+ retval = kadm5_purgekeys(handle, princ, keepkvno);
+ if (retval) {
+ com_err("purgekeys", retval,
+ _("while purging keys for principal \"%s\""), canon);
+ goto cleanup;
+ }
+
+ if (keepkvno == KRB5_INT32_MAX)
+ info(_("All keys for principal \"%s\" removed.\n"), canon);
+ else
+ info(_("Old keys for principal \"%s\" purged.\n"), canon);
+cleanup:
+ krb5_free_principal(context, princ);
+ free(canon);
+ return;
+}
+
+void
+kadmin_getstrings(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+ char *pname, *canon = NULL;
+ krb5_principal princ = NULL;
+ krb5_string_attr *strings = NULL;
+ int count, i;
+
+ if (argc != 2) {
+ error(_("usage: get_strings principal\n"));
+ return;
+ }
+ pname = argv[1];
+
+ retval = kadmin_parse_name(pname, &princ);
+ if (retval) {
+ com_err("get_strings", retval, _("while parsing principal"));
+ return;
+ }
+
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("get_strings", retval, _("while canonicalizing principal"));
+ goto cleanup;
+ }
+
+ retval = kadm5_get_strings(handle, princ, &strings, &count);
+ if (retval) {
+ com_err("get_strings", retval,
+ _("while getting attributes for principal \"%s\""), canon);
+ goto cleanup;
+ }
+
+ if (count == 0)
+ printf(_("(No string attributes.)\n"));
+ for (i = 0; i < count; i++)
+ printf("%s: %s\n", strings[i].key, strings[i].value);
+ kadm5_free_strings(handle, strings, count);
+
+cleanup:
+ krb5_free_principal(context, princ);
+ free(canon);
+ return;
+}
+
+void
+kadmin_setstring(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+ char *pname, *canon = NULL, *key, *value;
+ krb5_principal princ = NULL;
+
+ if (argc != 4) {
+ error(_("usage: set_string principal key value\n"));
+ return;
+ }
+ pname = argv[1];
+ key = argv[2];
+ value = argv[3];
+
+ retval = kadmin_parse_name(pname, &princ);
+ if (retval) {
+ com_err("set_string", retval, _("while parsing principal"));
+ return;
+ }
+
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("set_string", retval, _("while canonicalizing principal"));
+ goto cleanup;
+ }
+
+ retval = kadm5_set_string(handle, princ, key, value);
+ if (retval) {
+ com_err("set_string", retval,
+ _("while setting attribute on principal \"%s\""), canon);
+ goto cleanup;
+ }
+
+ info(_("Attribute set for principal \"%s\".\n"), canon);
+cleanup:
+ krb5_free_principal(context, princ);
+ free(canon);
+ return;
+}
+
+void
+kadmin_delstring(int argc, char *argv[])
+{
+ kadm5_ret_t retval;
+ char *pname, *canon = NULL, *key;
+ krb5_principal princ = NULL;
+
+ if (argc != 3) {
+ error(_("usage: del_string principal key\n"));
+ return;
+ }
+ pname = argv[1];
+ key = argv[2];
+
+ retval = kadmin_parse_name(pname, &princ);
+ if (retval) {
+ com_err("delstring", retval, _("while parsing principal"));
+ return;
+ }
+
+ retval = krb5_unparse_name(context, princ, &canon);
+ if (retval) {
+ com_err("del_string", retval, _("while canonicalizing principal"));
+ goto cleanup;
+ }
+
+ retval = kadm5_set_string(handle, princ, key, NULL);
+ if (retval) {
+ com_err("del_string", retval,
+ _("while deleting attribute from principal \"%s\""), canon);
+ goto cleanup;
+ }
+
+ info(_("Attribute removed from principal \"%s\".\n"), canon);
+cleanup:
+ krb5_free_principal(context, princ);
+ free(canon);
+ return;
+}
diff --git a/src/kadmin/cli/kadmin.h b/src/kadmin/cli/kadmin.h
new file mode 100644
index 000000000000..54a4818f72d0
--- /dev/null
+++ b/src/kadmin/cli/kadmin.h
@@ -0,0 +1,88 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/cli/kadmin.h */
+/*
+ * Copyright 2001 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ *
+ * Prototypes for kadmin functions called from SS library.
+ */
+
+#ifndef __KADMIN_H__
+#define __KADMIN_H__
+
+/* It would be nice if ss produced a header file we could reference */
+extern void kadmin_startup(int argc, char *argv[], char **request_out,
+ char ***args_out);
+extern int quit (void);
+extern void kadmin_lock(int argc, char *argv[]);
+extern void kadmin_unlock(int argc, char *argv[]);
+extern void kadmin_delprinc(int argc, char *argv[]);
+extern void kadmin_renameprinc(int argc, char *argv[]);
+extern void kadmin_cpw(int argc, char *argv[]);
+extern void kadmin_addprinc(int argc, char *argv[]);
+extern void kadmin_modprinc(int argc, char *argv[]);
+extern void kadmin_getprinc(int argc, char *argv[]);
+extern void kadmin_getprincs(int argc, char *argv[]);
+extern void kadmin_addpol(int argc, char *argv[]);
+extern void kadmin_modpol(int argc, char *argv[]);
+extern void kadmin_delpol(int argc, char *argv[]);
+extern void kadmin_getpol(int argc, char *argv[]);
+extern void kadmin_getpols(int argc, char *argv[]);
+extern void kadmin_getprivs(int argc, char *argv[]);
+extern void kadmin_keytab_add(int argc, char *argv[]);
+extern void kadmin_keytab_remove(int argc, char *argv[]);
+extern void kadmin_purgekeys(int argc, char *argv[]);
+extern void kadmin_getstrings(int argc, char *argv[]);
+extern void kadmin_setstring(int argc, char *argv[]);
+extern void kadmin_delstring(int argc, char *argv[]);
+
+#include <kdb.h>
+
+krb5_error_code
+randkey_princ(void *lhandle, krb5_principal princ, krb5_boolean keepold,
+ int n_ks, krb5_key_salt_tuple *ks, krb5_keyblock **key,
+ int *n_keys);
+
+#include "autoconf.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
+
+extern time_t get_date_rel(char *, time_t);
+
+/* Yucky global variables */
+extern krb5_context context;
+extern char *whoami;
+extern void *handle;
+
+#endif /* __KADMIN_H__ */
diff --git a/src/kadmin/cli/kadmin_ct.ct b/src/kadmin/cli/kadmin_ct.ct
new file mode 100644
index 000000000000..705e41840e3f
--- /dev/null
+++ b/src/kadmin/cli/kadmin_ct.ct
@@ -0,0 +1,100 @@
+# Copyright 1994 by the Massachusetts Institute of Technology.
+# All Rights Reserved.
+#
+# Export of this software from the United States of America may
+# require a specific license from the United States Government.
+# It is the responsibility of any person or organization contemplating
+# export to obtain such a license before exporting.
+#
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission. Furthermore if you modify this software you must label
+# your software as modified software and not distribute it in such a
+# fashion that it might be confused with the original M.I.T. software.
+# M.I.T. makes no representations about the suitability of
+# this software for any purpose. It is provided "as is" without express
+# or implied warranty.
+#
+#
+# Command table for kadmin CLI for OVSecure
+#
+
+command_table kadmin_cmds;
+
+request kadmin_addprinc, "Add principal",
+ add_principal, addprinc, ank;
+
+request kadmin_delprinc, "Delete principal",
+ delete_principal, delprinc;
+
+request kadmin_modprinc, "Modify principal",
+ modify_principal, modprinc;
+
+request kadmin_renameprinc, "Rename principal",
+ rename_principal, renprinc;
+
+request kadmin_cpw, "Change password",
+ change_password, cpw;
+
+request kadmin_getprinc, "Get principal",
+ get_principal, getprinc;
+
+request kadmin_getprincs, "List principals",
+ list_principals, listprincs, 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, "List policies",
+ list_policies, listpols, 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;
+
+request kadmin_lock, "Lock database exclusively (use with extreme caution!)",
+ lock;
+
+request kadmin_unlock, "Release exclusive database lock",
+ unlock;
+
+request kadmin_purgekeys, "Purge previously retained old keys from a principal",
+ purgekeys;
+
+request kadmin_getstrings, "Show string attributes on a principal",
+ get_strings, getstrs;
+
+request kadmin_setstring, "Set a string attribute on a principal",
+ set_string, setstr;
+
+request kadmin_delstring, "Delete a string attribute on a principal",
+ del_string, delstr;
+
+# 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 000000000000..b0c8378b40b6
--- /dev/null
+++ b/src/kadmin/cli/keytab.c
@@ -0,0 +1,505 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Id$
+ * $Source$
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "k5-int.h"
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+#include "kadmin.h"
+
+static void add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
+ krb5_boolean keepold,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ char *princ_str);
+static void remove_principal(char *keytab_str, krb5_keytab keytab,
+ char *princ_str, char *kvno_str);
+static char *etype_string(krb5_enctype enctype);
+
+static int quiet;
+
+static int norandkey;
+
+static void
+add_usage()
+{
+ fprintf(stderr, _("Usage: ktadd [-k[eytab] keytab] [-q] [-e keysaltlist] "
+ "[-norandkey] [principal | -glob princ-exp] [...]\n"));
+}
+
+static void
+rem_usage()
+{
+ fprintf(stderr, _("Usage: ktremove [-k[eytab] keytab] [-q] principal "
+ "[kvno|\"all\"|\"old\"]\n"));
+}
+
+static int
+process_keytab(krb5_context my_context, char **keytab_str,
+ krb5_keytab *keytab)
+{
+ int code;
+ char *name = *keytab_str;
+
+ if (name == NULL) {
+ name = malloc(BUFSIZ);
+ if (!name) {
+ com_err(whoami, ENOMEM, _("while creating keytab name"));
+ return 1;
+ }
+ code = krb5_kt_default(my_context, keytab);
+ if (code != 0) {
+ com_err(whoami, code, _("while opening default keytab"));
+ free(name);
+ return 1;
+ }
+ code = krb5_kt_get_name(my_context, *keytab, name, BUFSIZ);
+ if (code != 0) {
+ com_err(whoami, code, _("while getting keytab name"));
+ free(name);
+ return 1;
+ }
+ } else {
+ if (strchr(name, ':') != NULL)
+ name = strdup(name);
+ else if (asprintf(&name, "WRFILE:%s", name) < 0)
+ name = NULL;
+ if (name == NULL) {
+ com_err(whoami, ENOMEM, _("while creating keytab name"));
+ return 1;
+ }
+
+ code = krb5_kt_resolve(my_context, name, keytab);
+ if (code != 0) {
+ com_err(whoami, code, _("while resolving keytab %s"), name);
+ free(name);
+ return 1;
+ }
+ }
+
+ *keytab_str = name;
+ return 0;
+}
+
+void
+kadmin_keytab_add(int argc, char **argv)
+{
+ krb5_keytab keytab = 0;
+ char *keytab_str = NULL, **princs;
+ int code, num, i;
+ krb5_error_code retval;
+ int n_ks_tuple = 0;
+ krb5_boolean keepold = FALSE;
+ krb5_key_salt_tuple *ks_tuple = NULL;
+
+ argc--; argv++;
+ quiet = 0;
+ norandkey = 0;
+ 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 if (strcmp(*argv, "-norandkey") == 0) {
+ norandkey++;
+ } else if (strcmp(*argv, "-e") == 0) {
+ argc--;
+ if (argc < 1) {
+ add_usage();
+ return;
+ }
+ retval = krb5_string_to_keysalts(*++argv, NULL, NULL, 0,
+ &ks_tuple, &n_ks_tuple);
+ if (retval) {
+ com_err("ktadd", retval, _("while parsing keysalts %s"),
+ *argv);
+
+ return;
+ }
+ } else
+ break;
+ argc--; argv++;
+ }
+
+ if (argc == 0) {
+ add_usage();
+ return;
+ }
+
+ if (norandkey && ks_tuple) {
+ fprintf(stderr,
+ _("cannot specify keysaltlist when not changing key\n"));
+ return;
+ }
+
+ if (process_keytab(context, &keytab_str, &keytab))
+ return;
+
+ while (*argv) {
+ if (strcmp(*argv, "-glob") == 0) {
+ if (*++argv == NULL) {
+ add_usage();
+ break;
+ }
+
+ code = kadm5_get_principals(handle, *argv, &princs, &num);
+ if (code) {
+ com_err(whoami, code, _("while expanding expression \"%s\"."),
+ *argv);
+ argv++;
+ continue;
+ }
+
+ for (i = 0; i < num; i++)
+ add_principal(handle, keytab_str, keytab, keepold,
+ n_ks_tuple, ks_tuple, princs[i]);
+ kadm5_free_name_list(handle, princs, num);
+ } else {
+ add_principal(handle, keytab_str, keytab, keepold,
+ n_ks_tuple, ks_tuple, *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 *keytab_str = NULL;
+ int code;
+
+ argc--; argv++;
+ quiet = 0;
+ 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;
+
+ 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);
+}
+
+/* Generate new random keys for princ, and convert them into a kadm5_key_data
+ * array (with no salt information). */
+static krb5_error_code
+fetch_new_keys(void *lhandle, krb5_principal princ, krb5_boolean keepold,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ kadm5_key_data **key_data_out, int *nkeys_out)
+{
+ krb5_error_code code;
+ kadm5_key_data *key_data;
+ kadm5_principal_ent_rec princ_rec;
+ krb5_keyblock *keys = NULL;
+ int i, nkeys = 0;
+
+ *key_data_out = NULL;
+ *nkeys_out = 0;
+ memset(&princ_rec, 0, sizeof(princ_rec));
+
+ /* Generate new random keys. */
+ code = randkey_princ(lhandle, princ, keepold, n_ks_tuple, ks_tuple,
+ &keys, &nkeys);
+ if (code)
+ goto cleanup;
+
+ /* Get the principal entry to find the kvno of the new keys. (This is not
+ * atomic, but randkey doesn't report the new kvno.) */
+ code = kadm5_get_principal(lhandle, princ, &princ_rec,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ if (code)
+ goto cleanup;
+
+ key_data = k5calloc(nkeys, sizeof(*key_data), &code);
+ if (key_data == NULL)
+ goto cleanup;
+
+ /* Transfer the keyblocks and free the container array. */
+ for (i = 0; i < nkeys; i++) {
+ key_data[i].key = keys[i];
+ key_data[i].kvno = princ_rec.kvno;
+ }
+ *key_data_out = key_data;
+ *nkeys_out = nkeys;
+ free(keys);
+ keys = NULL;
+ nkeys = 0;
+
+cleanup:
+ for (i = 0; i < nkeys; i++)
+ krb5_free_keyblock_contents(context, &keys[i]);
+ free(keys);
+ kadm5_free_principal_ent(lhandle, &princ_rec);
+ return code;
+}
+
+static void
+add_principal(void *lhandle, char *keytab_str, krb5_keytab keytab,
+ krb5_boolean keepold, int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple, char *princ_str)
+{
+ krb5_principal princ = NULL;
+ krb5_keytab_entry new_entry;
+ kadm5_key_data *key_data;
+ int code, nkeys, i;
+
+ princ = NULL;
+ key_data = 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;
+ }
+
+ if (norandkey) {
+ code = kadm5_get_principal_keys(handle, princ, 0, &key_data, &nkeys);
+ } else {
+ code = fetch_new_keys(handle, princ, keepold, n_ks_tuple, ks_tuple,
+ &key_data, &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;
+ }
+
+ for (i = 0; i < nkeys; i++) {
+ memset(&new_entry, 0, sizeof(new_entry));
+ new_entry.principal = princ;
+ new_entry.key = key_data[i].key;
+ new_entry.vno = key_data[i].kvno;
+
+ code = krb5_kt_add_entry(context, keytab, &new_entry);
+ if (code != 0) {
+ com_err(whoami, code, _("while adding key to keytab"));
+ goto cleanup;
+ }
+
+ if (!quiet) {
+ printf(_("Entry for principal %s with kvno %d, "
+ "encryption type %s added to keytab %s.\n"),
+ princ_str, key_data[i].kvno,
+ etype_string(key_data[i].key.enctype), keytab_str);
+ }
+ }
+
+cleanup:
+ kadm5_free_kadm5_key_data(context, nkeys, key_data);
+ krb5_free_principal(context, princ);
+}
+
+static void
+remove_principal(char *keytab_str, krb5_keytab keytab,
+ char *princ_str, char *kvno_str)
+{
+ krb5_principal princ = NULL;
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
+ enum { UNDEF, SPEC, HIGH, ALL, OLD } mode;
+ int code, did_something;
+ krb5_kvno kvno;
+
+ code = krb5_parse_name(context, princ_str, &princ);
+ if (code != 0) {
+ com_err(whoami, code, _("while parsing principal name %s"), princ_str);
+ goto cleanup;
+ }
+
+ 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"));
+ }
+ goto cleanup;
+ }
+
+ /* set kvno to spec'ed value for SPEC, highest kvno otherwise */
+ if (mode != SPEC)
+ 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"));
+ goto cleanup;
+ }
+
+ 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"));
+ goto cleanup;
+ }
+ code = krb5_kt_remove_entry(context, keytab, &entry);
+ if (code != 0) {
+ com_err(whoami, code, _("while deleting entry from keytab"));
+ goto cleanup;
+ }
+ code = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (code != 0) {
+ com_err(whoami, code, _("while restarting keytab scan"));
+ goto cleanup;
+ }
+
+ did_something++;
+ if (!quiet) {
+ printf(_("Entry for principal %s with kvno %d removed from "
+ "keytab %s.\n"), 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"));
+ goto cleanup;
+ }
+ code = krb5_kt_end_seq_get(context, keytab, &cursor);
+ if (code) {
+ com_err(whoami, code, _("while ending keytab scan"));
+ goto cleanup;
+ }
+
+ /*
+ * 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);
+ }
+
+cleanup:
+ krb5_free_principal(context, princ);
+}
+
+/*
+ * 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(krb5_enctype enctype)
+{
+ static char buf[100];
+ krb5_error_code ret;
+
+ ret = krb5_enctype_to_name(enctype, FALSE, buf, sizeof(buf));
+ if (ret)
+ snprintf(buf, sizeof(buf), "etype %d", enctype);
+
+ return buf;
+}
diff --git a/src/kadmin/cli/keytab_local.c b/src/kadmin/cli/keytab_local.c
new file mode 100644
index 000000000000..bb9cd88dfc58
--- /dev/null
+++ b/src/kadmin/cli/keytab_local.c
@@ -0,0 +1,10 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * A wrapper around keytab.c used by kadmin.local to expose the -norandkey
+ * flag. This avoids building two object files from the same source file,
+ * which is otherwise tricky with compilers that don't support -c and -o
+ * at the same time.
+ */
+
+#define KADMIN_LOCAL
+#include "keytab.c"
diff --git a/src/kadmin/cli/ss_wrapper.c b/src/kadmin/cli/ss_wrapper.c
new file mode 100644
index 000000000000..7ae9f1a225d1
--- /dev/null
+++ b/src/kadmin/cli/ss_wrapper.c
@@ -0,0 +1,77 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1994 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <k5-platform.h>
+#include <krb5.h>
+#include <locale.h>
+#include <ss/ss.h>
+#include "kadmin.h"
+
+#ifdef NEED_SS_EXECUTE_COMMAND_PROTO
+int ss_execute_command(int, char **);
+#endif
+
+extern ss_request_table kadmin_cmds;
+extern int exit_status;
+extern char *whoami;
+
+int
+main(int argc, char *argv[])
+{
+ char *request, **args;
+ krb5_error_code retval;
+ int sci_idx, code = 0;
+
+ setlocale(LC_ALL, "");
+ whoami = ((whoami = strrchr(argv[0], '/')) ? whoami+1 : argv[0]);
+
+ kadmin_startup(argc, argv, &request, &args);
+ sci_idx = ss_create_invocation(whoami, "5.0", NULL, &kadmin_cmds, &retval);
+ if (retval) {
+ ss_perror(sci_idx, retval, _("creating invocation"));
+ exit(1);
+ }
+
+ if (*args != NULL) {
+ /* Execute post-option arguments as a single script-mode command. */
+ code = ss_execute_command(sci_idx, args);
+ if (code) {
+ ss_perror(sci_idx, code, *args);
+ exit_status = 1;
+ }
+ } else if (request != NULL) {
+ /* Execute the -q option as a single interactive command. */
+ code = ss_execute_line(sci_idx, request);
+ if (code != 0) {
+ ss_perror(sci_idx, code, request);
+ exit_status = 1;
+ }
+ } else {
+ /* Prompt for commands. */
+ (void)ss_listen(sci_idx);
+ }
+
+ return quit() ? 1 : exit_status;
+}
diff --git a/src/kadmin/cli/strftime.c b/src/kadmin/cli/strftime.c
new file mode 100644
index 000000000000..382a209b7756
--- /dev/null
+++ b/src/kadmin/cli/strftime.c
@@ -0,0 +1,465 @@
+/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
+/* $NetBSD: strftime.c,v 1.8 1999/02/07 17:33:30 augustss Exp $ */
+
+/*
+ * Copyright (c) 1989 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.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+#if 0
+static char *sccsid = "@(#)strftime.c 5.11 (Berkeley) 2/24/91";
+#else
+__RCSID("$NetBSD: strftime.c,v 1.8 1999/02/07 17:33:30 augustss Exp $");
+#endif
+#endif /* LIBC_SCCS and not lint */
+
+#include <string.h>
+#include <time.h>
+
+/* begin krb5 hack - replace stuff that would come from netbsd libc */
+#undef _CurrentTimeLocale
+#define _CurrentTimeLocale (&dummy_locale_info)
+
+struct dummy_locale_info_t {
+ char d_t_fmt[15];
+ char t_fmt_ampm[12];
+ char t_fmt[9];
+ char d_fmt[9];
+ char day[7][10];
+ char abday[7][4];
+ char mon[12][10];
+ char abmon[12][4];
+ char am_pm[2][3];
+};
+static const struct dummy_locale_info_t dummy_locale_info = {
+ "%a %b %d %X %Y", /* %c */
+ "%I:%M:%S %p", /* %r */
+ "%H:%M:%S", /* %X */
+ "%m/%d/%y", /* %x */
+ { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
+ "Saturday" },
+ { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
+ { "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December" },
+ { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" },
+ { "AM", "PM" },
+};
+#undef TM_YEAR_BASE
+#define TM_YEAR_BASE 1900
+
+#undef DAYSPERLYEAR
+#define DAYSPERLYEAR 366
+#undef DAYSPERNYEAR
+#define DAYSPERNYEAR 365
+#undef DAYSPERWEEK
+#define DAYSPERWEEK 7
+#undef isleap
+#define isleap(N) ((N % 4) == 0 && (N % 100 != 0 || N % 400 == 0))
+#undef tzname
+#define tzname my_tzname
+static const char *const tzname[2] = { 0, 0 };
+#undef tzset
+#define tzset()
+#undef __P
+#define __P(X) X /* we already require ansi c in this tree */
+/* end krb5 hack */
+
+static int _add __P((const char *, char **, const char *));
+static int _conv __P((int, int, int, char **, const char *));
+static int _secs __P((const struct tm *, char **, const char *));
+static size_t _fmt __P((const char *, const struct tm *, char **,
+ const char *));
+
+size_t
+strftime(s, maxsize, format, t)
+ char *s;
+ size_t maxsize;
+ const char *format;
+ const struct tm *t;
+{
+ char *pt;
+
+ tzset();
+ if (maxsize < 1)
+ return (0);
+
+ pt = s;
+ if (_fmt(format, t, &pt, s + maxsize)) {
+ *pt = '\0';
+ return (pt - s);
+ } else
+ return (0);
+}
+
+#define SUN_WEEK(t) (((t)->tm_yday + 7 - \
+ ((t)->tm_wday)) / 7)
+#define MON_WEEK(t) (((t)->tm_yday + 7 - \
+ ((t)->tm_wday ? (t)->tm_wday - 1 : 6)) / 7)
+
+static size_t
+_fmt(format, t, pt, ptlim)
+ const char *format;
+ const struct tm *t;
+ char **pt;
+ const char * const ptlim;
+{
+ for (; *format; ++format) {
+ if (*format == '%') {
+ ++format;
+ if (*format == 'E') {
+ /* Alternate Era */
+ ++format;
+ } else if (*format == 'O') {
+ /* Alternate numeric symbols */
+ ++format;
+ }
+ switch (*format) {
+ case '\0':
+ --format;
+ break;
+ case 'A':
+ if (t->tm_wday < 0 || t->tm_wday > 6)
+ return (0);
+ if (!_add(_CurrentTimeLocale->day[t->tm_wday],
+ pt, ptlim))
+ return (0);
+ continue;
+
+ case 'a':
+ if (t->tm_wday < 0 || t->tm_wday > 6)
+ return (0);
+ if (!_add(_CurrentTimeLocale->abday[t->tm_wday],
+ pt, ptlim))
+ return (0);
+ continue;
+ case 'B':
+ if (t->tm_mon < 0 || t->tm_mon > 11)
+ return (0);
+ if (!_add(_CurrentTimeLocale->mon[t->tm_mon],
+ pt, ptlim))
+ return (0);
+ continue;
+ case 'b':
+ case 'h':
+ if (t->tm_mon < 0 || t->tm_mon > 11)
+ return (0);
+ if (!_add(_CurrentTimeLocale->abmon[t->tm_mon],
+ pt, ptlim))
+ return (0);
+ continue;
+ case 'C':
+ if (!_conv((t->tm_year + TM_YEAR_BASE) / 100,
+ 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'c':
+ if (!_fmt(_CurrentTimeLocale->d_t_fmt, t, pt,
+ ptlim))
+ return (0);
+ continue;
+ case 'D':
+ if (!_fmt("%m/%d/%y", t, pt, ptlim))
+ return (0);
+ continue;
+ case 'd':
+ if (!_conv(t->tm_mday, 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'e':
+ if (!_conv(t->tm_mday, 2, ' ', pt, ptlim))
+ return (0);
+ continue;
+ case 'H':
+ if (!_conv(t->tm_hour, 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'I':
+ if (!_conv(t->tm_hour % 12 ?
+ t->tm_hour % 12 : 12, 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'j':
+ if (!_conv(t->tm_yday + 1, 3, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'k':
+ if (!_conv(t->tm_hour, 2, ' ', pt, ptlim))
+ return (0);
+ continue;
+ case 'l':
+ if (!_conv(t->tm_hour % 12 ?
+ t->tm_hour % 12: 12, 2, ' ', pt, ptlim))
+ return (0);
+ continue;
+ case 'M':
+ if (!_conv(t->tm_min, 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'm':
+ if (!_conv(t->tm_mon + 1, 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'n':
+ if (!_add("\n", pt, ptlim))
+ return (0);
+ continue;
+ case 'p':
+ if (!_add(_CurrentTimeLocale->am_pm[t->tm_hour
+ >= 12], pt, ptlim))
+ return (0);
+ continue;
+ case 'R':
+ if (!_fmt("%H:%M", t, pt, ptlim))
+ return (0);
+ continue;
+ case 'r':
+ if (!_fmt(_CurrentTimeLocale->t_fmt_ampm, t, pt,
+ ptlim))
+ return (0);
+ continue;
+ case 'S':
+ if (!_conv(t->tm_sec, 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 's':
+ if (!_secs(t, pt, ptlim))
+ return (0);
+ continue;
+ case 'T':
+ if (!_fmt("%H:%M:%S", t, pt, ptlim))
+ return (0);
+ continue;
+ case 't':
+ if (!_add("\t", pt, ptlim))
+ return (0);
+ continue;
+ case 'U':
+ if (!_conv(SUN_WEEK(t), 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'u':
+ if (!_conv(t->tm_wday ? t->tm_wday : 7, 1, '0',
+ pt, ptlim))
+ return (0);
+ continue;
+ case 'V': /* ISO 8601 week number */
+ case 'G': /* ISO 8601 year (four digits) */
+ case 'g': /* ISO 8601 year (two digits) */
+/*
+** From Arnold Robbins' strftime version 3.0: "the week number of the
+** year (the first Monday as the first day of week 1) as a decimal number
+** (01-53)."
+** (ado, 1993-05-24)
+**
+** From "http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html" by Markus Kuhn:
+** "Week 01 of a year is per definition the first week which has the
+** Thursday in this year, which is equivalent to the week which contains
+** the fourth day of January. In other words, the first week of a new year
+** is the week which has the majority of its days in the new year. Week 01
+** might also contain days from the previous year and the week before week
+** 01 of a year is the last week (52 or 53) of the previous year even if
+** it contains days from the new year. A week starts with Monday (day 1)
+** and ends with Sunday (day 7). For example, the first week of the year
+** 1997 lasts from 1996-12-30 to 1997-01-05..."
+** (ado, 1996-01-02)
+*/
+ {
+ int year;
+ int yday;
+ int wday;
+ int w;
+
+ year = t->tm_year + TM_YEAR_BASE;
+ yday = t->tm_yday;
+ wday = t->tm_wday;
+ for ( ; ; ) {
+ int len;
+ int bot;
+ int top;
+
+ len = isleap(year) ?
+ DAYSPERLYEAR :
+ DAYSPERNYEAR;
+ /*
+ ** What yday (-3 ... 3) does
+ ** the ISO year begin on?
+ */
+ bot = ((yday + 11 - wday) %
+ DAYSPERWEEK) - 3;
+ /*
+ ** What yday does the NEXT
+ ** ISO year begin on?
+ */
+ top = bot -
+ (len % DAYSPERWEEK);
+ if (top < -3)
+ top += DAYSPERWEEK;
+ top += len;
+ if (yday >= top) {
+ ++year;
+ w = 1;
+ break;
+ }
+ if (yday >= bot) {
+ w = 1 + ((yday - bot) /
+ DAYSPERWEEK);
+ break;
+ }
+ --year;
+ yday += isleap(year) ?
+ DAYSPERLYEAR :
+ DAYSPERNYEAR;
+ }
+#ifdef XPG4_1994_04_09
+ if ((w == 52
+ && t->tm_mon == TM_JANUARY)
+ || (w == 1
+ && t->tm_mon == TM_DECEMBER))
+ w = 53;
+#endif /* defined XPG4_1994_04_09 */
+ if (*format == 'V') {
+ if (!_conv(w, 2, '0',
+ pt, ptlim))
+ return (0);
+ } else if (*format == 'g') {
+ if (!_conv(year % 100, 2, '0',
+ pt, ptlim))
+ return (0);
+ } else if (!_conv(year, 4, '0',
+ pt, ptlim))
+ return (0);
+ }
+ continue;
+ case 'W':
+ if (!_conv(MON_WEEK(t), 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'w':
+ if (!_conv(t->tm_wday, 1, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'x':
+ if (!_fmt(_CurrentTimeLocale->d_fmt, t, pt,
+ ptlim))
+ return (0);
+ continue;
+ case 'X':
+ if (!_fmt(_CurrentTimeLocale->t_fmt, t, pt,
+ ptlim))
+ return (0);
+ continue;
+ case 'y':
+ if (!_conv((t->tm_year + TM_YEAR_BASE) % 100,
+ 2, '0', pt, ptlim))
+ return (0);
+ continue;
+ case 'Y':
+ if (!_conv((t->tm_year + TM_YEAR_BASE), 4, '0',
+ pt, ptlim))
+ return (0);
+ continue;
+ case 'Z':
+ if (tzname[t->tm_isdst ? 1 : 0] &&
+ !_add(tzname[t->tm_isdst ? 1 : 0], pt,
+ ptlim))
+ return (0);
+ continue;
+ case '%':
+ /*
+ * X311J/88-090 (4.12.3.5): if conversion char is
+ * undefined, behavior is undefined. Print out the
+ * character itself as printf(3) does.
+ */
+ default:
+ break;
+ }
+ }
+ if (*pt == ptlim)
+ return (0);
+ *(*pt)++ = *format;
+ }
+ return (ptlim - *pt);
+}
+
+static int
+_secs(t, pt, ptlim)
+ const struct tm *t;
+ char **pt;
+ const char * const ptlim;
+{
+ char buf[15];
+ time_t s;
+ char *p;
+ struct tm tmp;
+
+ buf[sizeof (buf) - 1] = '\0';
+ /* Make a copy, mktime(3) modifies the tm struct. */
+ tmp = *t;
+ s = mktime(&tmp);
+ for (p = buf + sizeof(buf) - 2; s > 0 && p > buf; s /= 10)
+ *p-- = (char)(s % 10 + '0');
+ return (_add(++p, pt, ptlim));
+}
+
+static int
+_conv(n, digits, pad, pt, ptlim)
+ int n, digits;
+ int pad;
+ char **pt;
+ const char * const ptlim;
+{
+ char buf[10];
+ char *p;
+
+ buf[sizeof (buf) - 1] = '\0';
+ for (p = buf + sizeof(buf) - 2; n > 0 && p > buf; n /= 10, --digits)
+ *p-- = n % 10 + '0';
+ while (p > buf && digits-- > 0)
+ *p-- = pad;
+ return (_add(++p, pt, ptlim));
+}
+
+static int
+_add(str, pt, ptlim)
+ const char *str;
+ char **pt;
+ const char * const ptlim;
+{
+
+ for (;; ++(*pt)) {
+ if (*pt == ptlim)
+ return (0);
+ if ((**pt = *str++) == '\0')
+ return (1);
+ }
+}
diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in
new file mode 100644
index 000000000000..1fa25d2a1319
--- /dev/null
+++ b/src/kadmin/dbutil/Makefile.in
@@ -0,0 +1,43 @@
+mydir=kadmin$(S)dbutil
+BUILDTOP=$(REL)..$(S)..
+LOCALINCLUDES = -I.
+KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS)
+
+PROG = kdb5_util
+
+SRCS = kdb5_util.c kdb5_create.c kadm5_create.c kdb5_destroy.c \
+ kdb5_stash.c import_err.c strtok.c dump.c ovload.c kdb5_mkey.c \
+ tabdump.c tdumputil.c
+EXTRADEPSRCS = t_tdumputil.c
+
+OBJS = kdb5_util.o kdb5_create.o kadm5_create.o kdb5_destroy.o \
+ kdb5_stash.o import_err.o strtok.o dump.o ovload.o kdb5_mkey.o \
+ tabdump.o tdumputil.o
+
+GETDATE = ../cli/getdate.o
+
+all: $(PROG)
+
+$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(GETDATE)
+ $(CC_LINK) -o $(PROG) $(OBJS) $(GETDATE) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS)
+
+import_err.c import_err.h: $(srcdir)/import_err.et
+
+$(OBJS): import_err.h
+
+install:
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean:
+ $(RM) $(PROG) $(OBJS) import_err.c import_err.h
+ $(RM) t_tdumputil.o t_tdumputil
+
+T_TDUMPUTIL_OBJS = t_tdumputil.o tdumputil.o
+
+t_tdumputil: $(T_TDUMPUTIL_OBJS) $(SUPPORT_DEPLIB)
+ $(CC_LINK) -o $@ $(T_TDUMPUTIL_OBJS) $(SUPPORT_LIB)
+
+depend: import_err.h
+
+check-pytests: t_tdumputil
+ $(RUNPYTEST) $(srcdir)/t_tdumputil.py $(PYTESTFLAGS)
diff --git a/src/kadmin/dbutil/deps b/src/kadmin/dbutil/deps
new file mode 100644
index 000000000000..4dcc336283f4
--- /dev/null
+++ b/src/kadmin/dbutil/deps
@@ -0,0 +1,206 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kdb5_util.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kdb5_util.c kdb5_util.h
+$(OUTPRE)kdb5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kdb5_create.c \
+ kdb5_util.h
+$(OUTPRE)kadm5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/fake-addrinfo.h \
+ $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \
+ $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \
+ $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \
+ $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \
+ $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \
+ $(top_srcdir)/include/iprop.h $(top_srcdir)/include/iprop_hdr.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/kdb_log.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ kadm5_create.c kdb5_util.h
+$(OUTPRE)kdb5_destroy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kdb5_destroy.c \
+ kdb5_util.h
+$(OUTPRE)kdb5_stash.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kdb5_stash.c kdb5_util.h
+$(OUTPRE)import_err.$(OBJEXT): $(COM_ERR_DEPS) import_err.c
+$(OUTPRE)strtok.$(OBJEXT): nstrtok.h strtok.c
+$(OUTPRE)dump.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h dump.c kdb5_util.h
+$(OUTPRE)ovload.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h import_err.h kdb5_util.h \
+ nstrtok.h ovload.c
+$(OUTPRE)kdb5_mkey.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kdb5_mkey.c kdb5_util.h
+$(OUTPRE)tabdump.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kdb5_util.h tabdump.c \
+ tdumputil.h
+$(OUTPRE)tdumputil.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h tdumputil.c tdumputil.h
+$(OUTPRE)t_tdumputil.$(OBJEXT): t_tdumputil.c tdumputil.h
diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c
new file mode 100644
index 000000000000..f7889bd234f5
--- /dev/null
+++ b/src/kadmin/dbutil/dump.c
@@ -0,0 +1,1717 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/dbutil/dump.c - Dump a KDC database */
+/*
+ * Copyright 1990,1991,2001,2006,2008,2009,2013 by the Massachusetts Institute
+ * of Technology. All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <kadm5/server_internal.h>
+#include <kdb.h>
+#include <com_err.h>
+#include "kdb5_util.h"
+#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
+#include <regex.h>
+#endif /* HAVE_REGEX_H */
+
+/* Needed for master key conversion. */
+static krb5_boolean mkey_convert;
+krb5_keyblock new_master_keyblock;
+krb5_kvno new_mkvno;
+
+#define K5Q1(x) #x
+#define K5Q(x) K5Q1(x)
+#define K5CONST_WIDTH_SCANF_STR(x) "%" K5Q(x) "s"
+
+/* 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 */
+
+typedef krb5_error_code (*dump_func)(krb5_context context,
+ krb5_db_entry *entry, const char *name,
+ FILE *fp, krb5_boolean verbose,
+ krb5_boolean omit_nra);
+typedef int (*load_func)(krb5_context context, const char *dumpfile, FILE *fp,
+ krb5_boolean verbose, int *linenop);
+
+typedef struct _dump_version {
+ char *name;
+ char *header;
+ int updateonly;
+ int iprop;
+ int ipropx;
+ dump_func dump_princ;
+ osa_adb_iter_policy_func dump_policy;
+ load_func load_record;
+} dump_version;
+
+struct dump_args {
+ FILE *ofile;
+ krb5_context context;
+ char **names;
+ int nnames;
+ krb5_boolean verbose;
+ krb5_boolean omit_nra; /* omit non-replicated attributes */
+ dump_version *dump;
+};
+
+/* External data */
+extern krb5_db_entry *master_entry;
+
+/*
+ * Re-encrypt the key_data with the new master key...
+ */
+krb5_error_code
+master_key_convert(krb5_context context, krb5_db_entry *db_entry)
+{
+ krb5_error_code retval;
+ krb5_keyblock v5plainkey, *key_ptr, *tmp_mkey;
+ krb5_keysalt keysalt;
+ krb5_key_data new_key_data, *key_data;
+ krb5_boolean is_mkey;
+ krb5_kvno kvno;
+ int i, j;
+
+ is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ);
+
+ if (is_mkey) {
+ return add_new_mkey(context, db_entry, &new_master_keyblock,
+ new_mkvno);
+ }
+
+ for (i = 0; i < db_entry->n_key_data; i++) {
+ key_data = &db_entry->key_data[i];
+ retval = krb5_dbe_find_mkey(context, db_entry, &tmp_mkey);
+ if (retval)
+ return retval;
+ retval = krb5_dbe_decrypt_key_data(context, tmp_mkey, key_data,
+ &v5plainkey, &keysalt);
+ if (retval)
+ return retval;
+
+ memset(&new_key_data, 0, sizeof(new_key_data));
+
+ key_ptr = &v5plainkey;
+ kvno = key_data->key_data_kvno;
+
+ retval = krb5_dbe_encrypt_key_data(context, &new_master_keyblock,
+ key_ptr, &keysalt, kvno,
+ &new_key_data);
+ if (retval)
+ return retval;
+ krb5_free_keyblock_contents(context, &v5plainkey);
+ for (j = 0; j < key_data->key_data_ver; j++) {
+ if (key_data->key_data_length[j])
+ free(key_data->key_data_contents[j]);
+ }
+ *key_data = new_key_data;
+ }
+ assert(new_mkvno > 0);
+ return krb5_dbe_update_mkvno(context, db_entry, new_mkvno);
+}
+
+/* Create temp file for new dump to be named ofile. */
+static FILE *
+create_ofile(char *ofile, char **tmpname)
+{
+ int fd = -1;
+ FILE *f;
+
+ *tmpname = NULL;
+ if (asprintf(tmpname, "%s-XXXXXX", ofile) < 0)
+ goto error;
+
+ fd = mkstemp(*tmpname);
+ if (fd == -1)
+ goto error;
+
+ f = fdopen(fd, "w+");
+ if (f != NULL)
+ return f;
+
+error:
+ com_err(progname, errno, _("while allocating temporary filename dump"));
+ if (fd >= 0)
+ unlink(*tmpname);
+ exit(1);
+}
+
+/* Rename new dump file into place. */
+static void
+finish_ofile(char *ofile, char **tmpname)
+{
+ if (rename(*tmpname, ofile) == -1) {
+ com_err(progname, errno, _("while renaming dump file into place"));
+ exit(1);
+ }
+ free(*tmpname);
+ *tmpname = NULL;
+}
+
+/* Create the .dump_ok file. */
+static int
+prep_ok_file(krb5_context context, char *file_name, int *fd)
+{
+ static char ok[] = ".dump_ok";
+ krb5_error_code retval;
+ char *file_ok;
+
+ if (asprintf(&file_ok, "%s%s", file_name, ok) < 0) {
+ com_err(progname, ENOMEM, _("while allocating dump_ok filename"));
+ exit_status++;
+ return 0;
+ }
+
+ *fd = open(file_ok, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ if (*fd == -1) {
+ com_err(progname, errno, _("while creating 'ok' file, '%s'"), file_ok);
+ exit_status++;
+ free(file_ok);
+ return 0;
+ }
+ retval = krb5_lock_file(context, *fd, KRB5_LOCKMODE_EXCLUSIVE);
+ if (retval) {
+ com_err(progname, retval, _("while locking 'ok' file, '%s'"), file_ok);
+ free(file_ok);
+ return 0;
+ }
+ free(file_ok);
+ return 1;
+}
+
+/*
+ * Update the "ok" file.
+ */
+static void
+update_ok_file(krb5_context context, int fd)
+{
+ write(fd, "", 1);
+ krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK);
+ close(fd);
+}
+
+/* Return true if a principal name matches a regular expression or string. */
+static int
+name_matches(char *name, struct dump_args *args)
+{
+#if HAVE_REGCOMP
+ regex_t reg;
+ regmatch_t rmatch;
+ int st;
+ char errmsg[BUFSIZ];
+#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;
+
+ /* Check each regular expression in args. */
+ match = args->nnames ? 0 : 1;
+ for (i = 0; i < args->nnames && !match; i++) {
+#if HAVE_REGCOMP
+ /* Compile the regular expression. */
+ st = regcomp(&reg, args->names[i], REG_EXTENDED);
+ if (st) {
+ regerror(st, &reg, errmsg, sizeof(errmsg));
+ fprintf(stderr, _("%s: regular expression error: %s\n"), progname,
+ errmsg);
+ break;
+ }
+ /* See if we have a match. */
+ st = regexec(&reg, name, 1, &rmatch, 0);
+ if (st == 0) {
+ /* See if it matches the whole name. */
+ if (rmatch.rm_so == 0 && (size_t)rmatch.rm_eo == strlen(name))
+ match = 1;
+ } else if (st != REG_NOMATCH) {
+ regerror(st, &reg, errmsg, sizeof(errmsg));
+ fprintf(stderr, _("%s: regular expression match error: %s\n"),
+ progname, errmsg);
+ break;
+ }
+ regfree(&reg);
+#elif HAVE_REGEXP_H
+ /* Compile the regular expression. */
+ compile(args->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. */
+ re_result = re_comp(args->names[i]);
+ if (re_result) {
+ fprintf(stderr, _("%s: regular expression error: %s\n"), progname,
+ 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(args->names[i], name))
+ match = 1;
+#endif /* HAVE_REGCOMP */
+ }
+ return match;
+}
+
+/* Output "-1" if len is 0; otherwise output len bytes of data in hex. */
+static void
+dump_octets_or_minus1(FILE *fp, unsigned char *data, size_t len)
+{
+ if (len > 0) {
+ for (; len > 0; len--)
+ fprintf(fp, "%02x", *data++);
+ } else {
+ fprintf(fp, "-1");
+ }
+}
+
+/*
+ * Dump TL data; common to principals and policies.
+ *
+ * If filter_kadm then the KRB5_TL_KADM_DATA (where a principal's policy
+ * name is stored) is filtered out. This is for dump formats that don't
+ * support policies.
+ */
+static void
+dump_tl_data(FILE *ofile, krb5_tl_data *tlp, krb5_boolean filter_kadm)
+{
+ for (; tlp != NULL; tlp = tlp->tl_data_next) {
+ if (tlp->tl_data_type == KRB5_TL_KADM_DATA && filter_kadm)
+ continue;
+ fprintf(ofile, "\t%d\t%d\t", (int)tlp->tl_data_type,
+ (int)tlp->tl_data_length);
+ dump_octets_or_minus1(ofile, tlp->tl_data_contents,
+ tlp->tl_data_length);
+ }
+}
+
+/* Dump a principal entry in krb5 beta 7 format. Omit kadmin tl-data if kadm
+ * is false. */
+static krb5_error_code
+k5beta7_common(krb5_context context, krb5_db_entry *entry,
+ const char *name, FILE *fp, krb5_boolean verbose,
+ krb5_boolean omit_nra, krb5_boolean kadm)
+{
+ krb5_tl_data *tlp;
+ krb5_key_data *kdata;
+ int counter, skip, i;
+
+ /*
+ * 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. A bracketed field's absence is indicated by a -1 in its
+ * place.
+ */
+
+ /* Make sure that the tagged list is reasonably correct. */
+ counter = skip = 0;
+ for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) {
+ /* Don't dump tl data types we know aren't understood by earlier
+ * versions. */
+ if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm)
+ skip++;
+ else
+ counter++;
+ }
+
+ if (counter + skip != entry->n_tl_data) {
+ fprintf(stderr, _("%s: tagged data list inconsistency for %s "
+ "(counted %d, stored %d)\n"), progname, name,
+ counter + skip, (int)entry->n_tl_data);
+ return EINVAL;
+ }
+
+ /* Write out header. */
+ fprintf(fp, "princ\t%d\t%lu\t%d\t%d\t%d\t%s\t", (int)entry->len,
+ (unsigned long)strlen(name), counter, (int)entry->n_key_data,
+ (int)entry->e_length, name);
+ fprintf(fp, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", entry->attributes,
+ entry->max_life, entry->max_renewable_life, entry->expiration,
+ entry->pw_expiration,
+ omit_nra ? 0 : entry->last_success,
+ omit_nra ? 0 : entry->last_failed,
+ omit_nra ? 0 : entry->fail_auth_count);
+
+ /* Write out tagged data. */
+ dump_tl_data(fp, entry->tl_data, !kadm);
+ fprintf(fp, "\t");
+
+ /* Write out key data. */
+ for (counter = 0; counter < entry->n_key_data; counter++) {
+ kdata = &entry->key_data[counter];
+ fprintf(fp, "%d\t%d\t", (int)kdata->key_data_ver,
+ (int)kdata->key_data_kvno);
+ for (i = 0; i < kdata->key_data_ver; i++) {
+ fprintf(fp, "%d\t%d\t", kdata->key_data_type[i],
+ kdata->key_data_length[i]);
+ dump_octets_or_minus1(fp, kdata->key_data_contents[i],
+ kdata->key_data_length[i]);
+ fprintf(fp, "\t");
+ }
+ }
+
+ /* Write out extra data. */
+ dump_octets_or_minus1(fp, entry->e_data, entry->e_length);
+
+ /* Write trailer. */
+ fprintf(fp, ";\n");
+
+ if (verbose)
+ fprintf(stderr, "%s\n", name);
+
+ return 0;
+}
+
+/* Output a dump record in krb5b7 format. */
+static krb5_error_code
+dump_k5beta7_princ(krb5_context context, krb5_db_entry *entry,
+ const char *name, FILE *fp, krb5_boolean verbose,
+ krb5_boolean omit_nra)
+{
+ return k5beta7_common(context, entry, name, fp, verbose, omit_nra, FALSE);
+}
+
+static krb5_error_code
+dump_k5beta7_princ_withpolicy(krb5_context context, krb5_db_entry *entry,
+ const char *name, FILE *fp, krb5_boolean verbose,
+ krb5_boolean omit_nra)
+{
+ return k5beta7_common(context, entry, name, fp, verbose, omit_nra, TRUE);
+}
+
+static void
+dump_k5beta7_policy(void *data, osa_policy_ent_t entry)
+{
+ struct dump_args *arg = 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, 0);
+}
+
+static void
+dump_r1_8_policy(void *data, osa_policy_ent_t entry)
+{
+ struct dump_args *arg = data;
+
+ fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\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,
+ 0, entry->pw_max_fail, entry->pw_failcnt_interval,
+ entry->pw_lockout_duration);
+}
+
+static void
+dump_r1_11_policy(void *data, osa_policy_ent_t entry)
+{
+ struct dump_args *arg = data;
+
+ fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t"
+ "%d\t%d\t%d\t%s\t%d", entry->name, entry->pw_min_life,
+ entry->pw_max_life, entry->pw_min_length, entry->pw_min_classes,
+ entry->pw_history_num, 0, entry->pw_max_fail,
+ entry->pw_failcnt_interval, entry->pw_lockout_duration,
+ entry->attributes, entry->max_life, entry->max_renewable_life,
+ entry->allowed_keysalts ? entry->allowed_keysalts : "-",
+ entry->n_tl_data);
+
+ dump_tl_data(arg->ofile, entry->tl_data, FALSE);
+ fprintf(arg->ofile, "\n");
+}
+
+static void
+print_key_data(FILE *f, krb5_key_data *kd)
+{
+ int c;
+
+ fprintf(f, "%d\t%d\t", kd->key_data_type[0], kd->key_data_length[0]);
+ for (c = 0; c < kd->key_data_length[0]; c++)
+ fprintf(f, "%02x ", kd->key_data_contents[0][c]);
+}
+
+/* Output osa_adb_princ_ent data in a printable serialized format, suitable for
+ * ovsec_adm_import consumption. */
+static krb5_error_code
+dump_ov_princ(krb5_context context, krb5_db_entry *entry, const char *name,
+ FILE *fp, krb5_boolean verbose, krb5_boolean omit_nra)
+{
+ char *princstr;
+ unsigned int x;
+ int y, foundcrc;
+ krb5_tl_data tl_data;
+ osa_princ_ent_rec adb;
+ XDR xdrs;
+ krb5_key_data *key_data;
+
+ tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+ if (krb5_dbe_lookup_tl_data(context, entry, &tl_data) ||
+ tl_data.tl_data_length == 0)
+ return 0;
+
+ memset(&adb, 0, sizeof(adb));
+ xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,
+ tl_data.tl_data_length, XDR_DECODE);
+ if (!xdr_osa_princ_ent_rec(&xdrs, &adb)) {
+ xdr_destroy(&xdrs);
+ return KADM5_XDR_FAILURE;
+ }
+ xdr_destroy(&xdrs);
+
+ krb5_unparse_name(context, entry->princ, &princstr);
+ fprintf(fp, "princ\t%s\t", princstr);
+ if (adb.policy == NULL)
+ fputc('\t', fp);
+ else
+ fprintf(fp, "%s\t", adb.policy);
+ fprintf(fp, "%lx\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++) {
+ foundcrc = 0;
+ for (y = 0; y < adb.old_keys[x].n_key_data; y++) {
+ key_data = &adb.old_keys[x].key_data[y];
+ if (key_data->key_data_type[0] != ENCTYPE_DES_CBC_CRC)
+ continue;
+ if (foundcrc) {
+ fprintf(stderr, _("Warning! Multiple DES-CBC-CRC keys for "
+ "principal %s; skipping duplicates.\n"),
+ princstr);
+ continue;
+ }
+ foundcrc++;
+
+ fputc('\t', fp);
+ print_key_data(fp, key_data);
+ }
+ if (!foundcrc) {
+ fprintf(stderr, _("Warning! No DES-CBC-CRC key for principal %s, "
+ "cannot generate OV-compatible record; "
+ "skipping\n"), princstr);
+ }
+ }
+
+ fputc('\n', fp);
+ free(princstr);
+ xdr_free(xdr_osa_princ_ent_rec, &adb);
+ return 0;
+}
+
+static krb5_error_code
+dump_iterator(void *ptr, krb5_db_entry *entry)
+{
+ krb5_error_code ret;
+ struct dump_args *args = ptr;
+ char *name;
+
+ ret = krb5_unparse_name(args->context, entry->princ, &name);
+ if (ret) {
+ com_err(progname, ret, _("while unparsing principal name"));
+ return ret;
+ }
+
+ /* Re-encode the keys in the new master key, if necessary. */
+ if (mkey_convert) {
+ ret = master_key_convert(args->context, entry);
+ if (ret) {
+ com_err(progname, ret, _("while converting %s to new master key"),
+ name);
+ goto cleanup;
+ }
+ }
+
+ /* Don't dump this entry if we have match strings and it doesn't match. */
+ if (args->nnames > 0 && !name_matches(name, args))
+ goto cleanup;
+
+ ret = args->dump->dump_princ(args->context, entry, name, args->ofile,
+ args->verbose, args->omit_nra);
+
+cleanup:
+ free(name);
+ return ret;
+}
+
+static inline void
+load_err(const char *fname, int lineno, const char *msg)
+{
+ fprintf(stderr, _("%s(%d): %s\n"), fname, lineno, msg);
+}
+
+/* Read a string of bytes. Increment *lp for each newline. Return 0 on
+ * success, 1 on failure. */
+static int
+read_string(FILE *f, char *buf, int len, int *lp)
+{
+ int c, i;
+
+ for (i = 0; i < len; i++) {
+ c = fgetc(f);
+ if (c < 0)
+ return 1;
+ if (c == '\n')
+ (*lp)++;
+ buf[i] = c;
+ }
+ buf[len] = '\0';
+ return 0;
+}
+
+/* Read a string of two-character representations of bytes. */
+static int
+read_octet_string(FILE *f, unsigned char *buf, int len)
+{
+ int c, i;
+
+ for (i = 0; i < len; i++) {
+ if (fscanf(f, "%02x", &c) != 1)
+ return 1;
+ buf[i] = c;
+ }
+ return 0;
+}
+
+/* Read the end of a dumpfile record. */
+static void
+read_record_end(FILE *f, const char *fn, int lineno)
+{
+ int ch;
+
+ if ((ch = fgetc(f)) != ';' || (ch = fgetc(f)) != '\n') {
+ fprintf(stderr, _("%s(%d): ignoring trash at end of line: "), fn,
+ lineno);
+ while (ch != '\n') {
+ putc(ch, stderr);
+ ch = fgetc(f);
+ }
+ putc(ch, stderr);
+ }
+}
+
+/* Allocate and form a TL data list of a desired size. */
+static int
+alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp)
+{
+ krb5_tl_data **tlp = tldp;
+ int i;
+
+ for (i = 0; i < n_tl_data; i++) {
+ *tlp = calloc(1, sizeof(krb5_tl_data));
+ if (*tlp == NULL)
+ return ENOMEM; /* caller cleans up */
+ tlp = &((*tlp)->tl_data_next);
+ }
+
+ return 0;
+}
+
+/* If len is zero, read the string "-1" from fp. Otherwise allocate space and
+ * read len octets. Return 0 on success, 1 on failure. */
+static int
+read_octets_or_minus1(FILE *fp, size_t len, unsigned char **out)
+{
+ int ival;
+ unsigned char *buf;
+
+ *out = NULL;
+ if (len == 0)
+ return fscanf(fp, "%d", &ival) != 1 || ival != -1;
+ buf = malloc(len);
+ if (buf == NULL)
+ return 1;
+ if (read_octet_string(fp, buf, len)) {
+ free(buf);
+ return 1;
+ }
+ *out = buf;
+ return 0;
+}
+
+/* Read TL data for a principal or policy. Print an error and return -1 on
+ * failure. */
+static int
+process_tl_data(const char *fname, FILE *filep, int lineno,
+ krb5_tl_data *tl_data)
+{
+ krb5_tl_data *tl;
+ int nread, i1;
+ unsigned int u1;
+
+ for (tl = tl_data; tl; tl = tl->tl_data_next) {
+ nread = fscanf(filep, "%d\t%u\t", &i1, &u1);
+ if (nread != 2) {
+ load_err(fname, lineno,
+ _("cannot read tagged data type and length"));
+ return EINVAL;
+ }
+ tl->tl_data_type = i1;
+ tl->tl_data_length = u1;
+ if (read_octets_or_minus1(filep, tl->tl_data_length,
+ &tl->tl_data_contents)) {
+ load_err(fname, lineno, _("cannot read tagged data contents"));
+ return EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/* Read a beta 7 entry and add it to the database. Return -1 for end of file,
+ * 0 for success and 1 for failure. */
+static int
+process_k5beta7_princ(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ int retval, nread, i, j;
+ krb5_db_entry *dbentry;
+ int t1, t2, t3, t4, t5, t6, t7;
+ unsigned int u1, u2, u3, u4, u5;
+ char *name = NULL;
+ krb5_key_data *kp = NULL, *kd;
+ krb5_tl_data *tl;
+ krb5_error_code ret;
+
+ dbentry = calloc(1, sizeof(*dbentry));
+ if (dbentry == NULL)
+ return 1;
+ (*linenop)++;
+ nread = fscanf(filep, "%u\t%u\t%u\t%u\t%u\t", &u1, &u2, &u3, &u4, &u5);
+ if (nread == EOF) {
+ retval = -1;
+ goto cleanup;
+ }
+ if (nread != 5) {
+ load_err(fname, *linenop, _("cannot match size tokens"));
+ goto fail;
+ }
+
+ /* Get memory for flattened principal name */
+ name = malloc(u2 + 1);
+ if (name == NULL)
+ goto fail;
+
+ /* Get memory for and form tagged data linked list */
+ if (alloc_tl_data(u3, &dbentry->tl_data))
+ goto fail;
+ dbentry->n_tl_data = u3;
+
+ /* Get memory for key list */
+ if (u4 && (kp = calloc(u4, sizeof(krb5_key_data))) == NULL)
+ goto fail;
+
+ dbentry->len = u1;
+ dbentry->n_key_data = u4;
+ dbentry->e_length = u5;
+
+ if (kp != NULL) {
+ dbentry->key_data = kp;
+ kp = NULL;
+ }
+
+ /* Read in and parse the principal name */
+ if (read_string(filep, name, u2, linenop)) {
+ load_err(fname, *linenop, _("cannot read name string"));
+ goto fail;
+ }
+ ret = krb5_parse_name(context, name, &dbentry->princ);
+ if (ret) {
+ com_err(progname, ret, _("while parsing name %s"), name);
+ goto fail;
+ }
+
+ /* Get the fixed principal attributes */
+ nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t",
+ &t1, &t2, &t3, &t4, &t5, &t6, &t7, &u1);
+ if (nread != 8) {
+ load_err(fname, *linenop, _("cannot read principal attributes"));
+ goto fail;
+ }
+ dbentry->attributes = t1;
+ dbentry->max_life = t2;
+ dbentry->max_renewable_life = t3;
+ dbentry->expiration = t4;
+ dbentry->pw_expiration = t5;
+ dbentry->last_success = t6;
+ dbentry->last_failed = t7;
+ dbentry->fail_auth_count = u1;
+ dbentry->mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
+ KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
+ KADM5_PRINC_EXPIRE_TIME | KADM5_LAST_SUCCESS |
+ KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT;
+
+ /* Read tagged data. */
+ if (dbentry->n_tl_data) {
+ if (process_tl_data(fname, filep, *linenop, dbentry->tl_data))
+ goto fail;
+ for (tl = dbentry->tl_data; tl; tl = tl->tl_data_next) {
+ /* test to set mask fields */
+ if (tl->tl_data_type == KRB5_TL_KADM_DATA) {
+ XDR xdrs;
+ osa_princ_ent_rec osa_princ_ent;
+
+ /*
+ * Assuming aux_attributes will always be
+ * there
+ */
+ dbentry->mask |= KADM5_AUX_ATTRIBUTES;
+
+ /* test for an actual policy reference */
+ memset(&osa_princ_ent, 0, sizeof(osa_princ_ent));
+ xdrmem_create(&xdrs, (char *)tl->tl_data_contents,
+ tl->tl_data_length, XDR_DECODE);
+ if (xdr_osa_princ_ent_rec(&xdrs, &osa_princ_ent)) {
+ if ((osa_princ_ent.aux_attributes & KADM5_POLICY) &&
+ osa_princ_ent.policy != NULL)
+ dbentry->mask |= KADM5_POLICY;
+ kdb_free_entry(NULL, NULL, &osa_princ_ent);
+ }
+ xdr_destroy(&xdrs);
+ }
+ }
+ dbentry->mask |= KADM5_TL_DATA;
+ }
+
+ /* Get the key data. */
+ for (i = 0; i < dbentry->n_key_data; i++) {
+ kd = &dbentry->key_data[i];
+ nread = fscanf(filep, "%d\t%d\t", &t1, &t2);
+ if (nread != 2) {
+ load_err(fname, *linenop, _("cannot read key size and version"));
+ goto fail;
+ }
+
+ kd->key_data_ver = t1;
+ kd->key_data_kvno = t2;
+
+ for (j = 0; j < t1; j++) {
+ nread = fscanf(filep, "%d\t%d\t", &t3, &t4);
+ if (nread != 2) {
+ load_err(fname, *linenop,
+ _("cannot read key type and length"));
+ goto fail;
+ }
+ kd->key_data_type[j] = t3;
+ kd->key_data_length[j] = t4;
+ if (read_octets_or_minus1(filep, t4, &kd->key_data_contents[j])) {
+ load_err(fname, *linenop, _("cannot read key data"));
+ goto fail;
+ }
+ }
+ }
+ if (dbentry->n_key_data)
+ dbentry->mask |= KADM5_KEY_DATA;
+
+ /* Get the extra data */
+ if (read_octets_or_minus1(filep, dbentry->e_length, &dbentry->e_data)) {
+ load_err(fname, *linenop, _("cannot read extra data"));
+ goto fail;
+ }
+
+ /* Finally, find the end of the record. */
+ read_record_end(filep, fname, *linenop);
+
+ ret = krb5_db_put_principal(context, dbentry);
+ if (ret) {
+ com_err(progname, ret, _("while storing %s"), name);
+ goto fail;
+ }
+
+ if (verbose)
+ fprintf(stderr, "%s\n", name);
+ retval = 0;
+
+cleanup:
+ free(kp);
+ free(name);
+ krb5_db_free_principal(context, dbentry);
+ return retval;
+
+fail:
+ retval = 1;
+ goto cleanup;
+}
+
+static int
+process_k5beta7_policy(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ osa_policy_ent_rec rec;
+ char namebuf[1024];
+ unsigned int refcnt;
+ int nread, ret;
+
+ memset(&rec, 0, sizeof(rec));
+
+ (*linenop)++;
+ rec.name = namebuf;
+
+ nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u", rec.name,
+ &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length,
+ &rec.pw_min_classes, &rec.pw_history_num, &refcnt);
+ if (nread == EOF)
+ return -1;
+ if (nread != 7) {
+ fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
+ return 1;
+ }
+
+ ret = krb5_db_create_policy(context, &rec);
+ if (ret)
+ ret = krb5_db_put_policy(context, &rec);
+ if (ret) {
+ com_err(progname, ret, _("while creating policy"));
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, _("created policy %s\n"), rec.name);
+
+ return 0;
+}
+
+static int
+process_r1_8_policy(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ osa_policy_ent_rec rec;
+ char namebuf[1024];
+ unsigned int refcnt;
+ int nread, ret;
+
+ memset(&rec, 0, sizeof(rec));
+
+ (*linenop)++;
+ rec.name = namebuf;
+
+ nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
+ rec.name, &rec.pw_min_life, &rec.pw_max_life,
+ &rec.pw_min_length, &rec.pw_min_classes,
+ &rec.pw_history_num, &refcnt, &rec.pw_max_fail,
+ &rec.pw_failcnt_interval, &rec.pw_lockout_duration);
+ if (nread == EOF)
+ return -1;
+ if (nread != 10) {
+ fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
+ return 1;
+ }
+
+ ret = krb5_db_create_policy(context, &rec);
+ if (ret)
+ ret = krb5_db_put_policy(context, &rec);
+ if (ret) {
+ com_err(progname, ret, _("while creating policy"));
+ return 1;
+ }
+ if (verbose)
+ fprintf(stderr, "created policy %s\n", rec.name);
+
+ return 0;
+}
+
+static int
+process_r1_11_policy(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ osa_policy_ent_rec rec;
+ krb5_tl_data *tl, *tl_next;
+ char namebuf[1024];
+ char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1];
+ unsigned int refcnt;
+ int nread, c, ret = 0;
+
+ memset(&rec, 0, sizeof(rec));
+
+ (*linenop)++;
+ rec.name = namebuf;
+
+ /*
+ * Due to a historical error, iprop dumps use the same version before and
+ * after the 1.11 policy extensions. So we need to accept both 1.8-format
+ * and 1.11-format policy entries. Begin by reading the 1.8 fields.
+ */
+ nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u",
+ rec.name, &rec.pw_min_life, &rec.pw_max_life,
+ &rec.pw_min_length, &rec.pw_min_classes,
+ &rec.pw_history_num, &refcnt, &rec.pw_max_fail,
+ &rec.pw_failcnt_interval, &rec.pw_lockout_duration);
+ if (nread == EOF)
+ return -1;
+ if (nread != 10) {
+ fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
+ return 1;
+ }
+
+ /* The next character should be a newline (1.8) or a tab (1.11). */
+ c = getc(filep);
+ if (c == EOF)
+ return -1;
+ if (c != '\n') {
+ /* Read the additional 1.11-format fields. */
+ rec.allowed_keysalts = keysaltbuf;
+ nread = fscanf(filep, "%u\t%u\t%u\t"
+ K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN)
+ "\t%hd", &rec.attributes, &rec.max_life,
+ &rec.max_renewable_life, rec.allowed_keysalts,
+ &rec.n_tl_data);
+ if (nread == EOF)
+ return -1;
+ if (nread != 5) {
+ fprintf(stderr, _("cannot parse policy (%d read)\n"), nread);
+ return 1;
+ }
+
+ if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-"))
+ rec.allowed_keysalts = NULL;
+
+ /* Get TL data */
+ ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data);
+ if (ret)
+ goto cleanup;
+
+ ret = process_tl_data(fname, filep, *linenop, rec.tl_data);
+ if (ret)
+ goto cleanup;
+ }
+
+ ret = krb5_db_create_policy(context, &rec);
+ if (ret)
+ ret = krb5_db_put_policy(context, &rec);
+ if (ret) {
+ com_err(progname, ret, _("while creating policy"));
+ goto cleanup;
+ }
+ if (verbose)
+ fprintf(stderr, "created policy %s\n", rec.name);
+
+cleanup:
+ for (tl = rec.tl_data; tl; tl = tl_next) {
+ tl_next = tl->tl_data_next;
+ free(tl->tl_data_contents);
+ free(tl);
+ }
+ return ret ? 1 : 0;
+}
+
+/* Read a record which is tagged with "princ" or "policy", calling princfn
+ * or policyfn as appropriate. */
+static int
+process_tagged(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop, load_func princfn,
+ load_func policyfn)
+{
+ int nread;
+ char rectype[100];
+
+ nread = fscanf(filep, "%99s\t", rectype);
+ if (nread == EOF)
+ return -1;
+ if (nread != 1)
+ return 1;
+ if (strcmp(rectype, "princ") == 0)
+ return (*princfn)(context, fname, filep, verbose, linenop);
+ if (strcmp(rectype, "policy") == 0)
+ return (*policyfn)(context, fname, filep, verbose, linenop);
+ if (strcmp(rectype, "End") == 0) /* Only expected for OV format */
+ return -1;
+
+ fprintf(stderr, _("unknown record type \"%s\"\n"), rectype);
+ return 1;
+}
+
+static int
+process_k5beta7_record(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ return process_tagged(context, fname, filep, verbose, linenop,
+ process_k5beta7_princ, process_k5beta7_policy);
+}
+
+static int
+process_ov_record(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ return process_tagged(context, fname, filep, verbose, linenop,
+ process_ov_principal, process_k5beta7_policy);
+}
+
+static int
+process_r1_8_record(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ return process_tagged(context, fname, filep, verbose, linenop,
+ process_k5beta7_princ, process_r1_8_policy);
+}
+
+static int
+process_r1_11_record(krb5_context context, const char *fname, FILE *filep,
+ krb5_boolean verbose, int *linenop)
+{
+ return process_tagged(context, fname, filep, verbose, linenop,
+ process_k5beta7_princ, process_r1_11_policy);
+}
+
+dump_version beta7_version = {
+ "Kerberos version 5",
+ "kdb5_util load_dump version 4\n",
+ 0,
+ 0,
+ 0,
+ dump_k5beta7_princ,
+ dump_k5beta7_policy,
+ process_k5beta7_record,
+};
+dump_version ov_version = {
+ "OpenV*Secure V1.0",
+ "OpenV*Secure V1.0\t",
+ 1,
+ 0,
+ 0,
+ dump_ov_princ,
+ dump_k5beta7_policy,
+ process_ov_record
+};
+dump_version r1_3_version = {
+ "Kerberos version 5 release 1.3",
+ "kdb5_util load_dump version 5\n",
+ 0,
+ 0,
+ 0,
+ dump_k5beta7_princ_withpolicy,
+ dump_k5beta7_policy,
+ process_k5beta7_record,
+};
+dump_version r1_8_version = {
+ "Kerberos version 5 release 1.8",
+ "kdb5_util load_dump version 6\n",
+ 0,
+ 0,
+ 0,
+ dump_k5beta7_princ_withpolicy,
+ dump_r1_8_policy,
+ process_r1_8_record,
+};
+dump_version r1_11_version = {
+ "Kerberos version 5 release 1.11",
+ "kdb5_util load_dump version 7\n",
+ 0,
+ 0,
+ 0,
+ dump_k5beta7_princ_withpolicy,
+ dump_r1_11_policy,
+ process_r1_11_record,
+};
+dump_version iprop_version = {
+ "Kerberos iprop version",
+ "iprop",
+ 0,
+ 1,
+ 0,
+ dump_k5beta7_princ_withpolicy,
+ dump_k5beta7_policy,
+ process_k5beta7_record,
+};
+dump_version ipropx_1_version = {
+ "Kerberos iprop extensible version",
+ "ipropx",
+ 0,
+ 1,
+ 1,
+ dump_k5beta7_princ_withpolicy,
+ dump_r1_11_policy,
+ process_r1_11_record,
+};
+
+/* Read the dump header. Return 1 on success, 0 if the file is not a
+ * recognized iprop dump format. */
+static int
+parse_iprop_header(char *buf, dump_version **dv, kdb_last_t *last)
+{
+ char head[128];
+ int nread;
+ uint32_t u[4];
+ uint32_t *up = &u[0];
+
+ nread = sscanf(buf, "%127s %u %u %u %u", head, &u[0], &u[1], &u[2], &u[3]);
+ if (nread < 1)
+ return 0;
+
+ if (!strcmp(head, ipropx_1_version.header)) {
+ if (nread != 5)
+ return 0;
+ if (u[0] == IPROPX_VERSION_0) {
+ *dv = &iprop_version;
+ } else if (u[0] == IPROPX_VERSION_1) {
+ *dv = &ipropx_1_version;
+ } else {
+ fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), progname,
+ u[0]);
+ return 0;
+ }
+ up = &u[1];
+ } else if (!strcmp(head, iprop_version.header)) {
+ if (nread != 4)
+ return 0;
+ *dv = &iprop_version;
+ } else {
+ fprintf(stderr, "Invalid iprop header\n");
+ return 0;
+ }
+
+ last->last_sno = *up++;
+ last->last_time.seconds = *up++;
+ last->last_time.useconds = *up++;
+ return 1;
+}
+
+/* Return true if the serial number and timestamp in an existing dump file is
+ * in the ulog. */
+static krb5_boolean
+current_dump_sno_in_ulog(krb5_context context, const char *ifile)
+{
+ update_status_t status;
+ dump_version *junk;
+ kdb_last_t last;
+ char buf[BUFSIZ];
+ FILE *f;
+
+ f = fopen(ifile, "r");
+ if (f == NULL)
+ return 0; /* aliasing other errors to ENOENT here is OK */
+
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ return errno ? -1 : 0;
+ fclose(f);
+
+ if (!parse_iprop_header(buf, &junk, &last))
+ return 0;
+
+ status = ulog_get_sno_status(context, &last);
+ return status == UPDATE_OK || status == UPDATE_NIL;
+}
+
+/*
+ * usage is:
+ * dump_db [-b7] [-ov] [-r13] [-r18] [-verbose] [-mkey_convert]
+ * [-new_mkey_file mkey_file] [-rev] [-recurse]
+ * [filename [principals...]]
+ */
+void
+dump_db(int argc, char **argv)
+{
+ FILE *f;
+ struct dump_args args;
+ char *ofile = NULL, *tmpofile = NULL, *new_mkey_file = NULL;
+ krb5_error_code ret, retval;
+ dump_version *dump;
+ int aindex, ok_fd = -1;
+ bool_t dump_sno = FALSE;
+ kdb_log_context *log_ctx;
+ unsigned int ipropx_version = IPROPX_VERSION_0;
+ krb5_kvno kt_kvno;
+ krb5_boolean conditional = FALSE;
+ kdb_last_t last;
+ krb5_flags iterflags = 0;
+
+ /* Parse the arguments. */
+ dump = &r1_11_version;
+ args.verbose = FALSE;
+ args.omit_nra = FALSE;
+ mkey_convert = FALSE;
+ log_ctx = util_context->kdblog_context;
+
+ /*
+ * Parse the qualifiers.
+ */
+ for (aindex = 1; aindex < argc; aindex++) {
+ if (!strcmp(argv[aindex], "-b7")) {
+ dump = &beta7_version;
+ } else if (!strcmp(argv[aindex], "-ov")) {
+ dump = &ov_version;
+ } else if (!strcmp(argv[aindex], "-r13")) {
+ dump = &r1_3_version;
+ } else if (!strcmp(argv[aindex], "-r18")) {
+ dump = &r1_8_version;
+ } else if (!strncmp(argv[aindex], "-i", 2)) {
+ if (log_ctx && log_ctx->iproprole) {
+ /* ipropx_version is the maximum version acceptable. */
+ ipropx_version = atoi(argv[aindex] + 2);
+ dump = ipropx_version ? &ipropx_1_version : &iprop_version;
+ /*
+ * dump_sno is used to indicate if the serial number should be
+ * populated in the output file to be used later by iprop for
+ * updating the slave's update log when loading.
+ */
+ dump_sno = TRUE;
+ /* FLAG_OMIT_NRA is set to indicate that non-replicated
+ * attributes should be omitted. */
+ args.omit_nra = TRUE;
+ } else {
+ fprintf(stderr, _("Iprop not enabled\n"));
+ goto error;
+ }
+ } else if (!strcmp(argv[aindex], "-c")) {
+ conditional = 1;
+ } else if (!strcmp(argv[aindex], "-verbose")) {
+ args.verbose = TRUE;
+ } else if (!strcmp(argv[aindex], "-mkey_convert")) {
+ mkey_convert = 1;
+ } else if (!strcmp(argv[aindex], "-new_mkey_file")) {
+ new_mkey_file = argv[++aindex];
+ mkey_convert = 1;
+ } else if (!strcmp(argv[aindex], "-rev")) {
+ iterflags |= KRB5_DB_ITER_REV;
+ } else if (!strcmp(argv[aindex], "-recurse")) {
+ iterflags |= KRB5_DB_ITER_RECURSE;
+ } else {
+ break;
+ }
+ }
+
+ args.names = NULL;
+ args.nnames = 0;
+ if (aindex < argc) {
+ ofile = argv[aindex];
+ aindex++;
+ if (aindex < argc) {
+ args.names = &argv[aindex];
+ args.nnames = argc - aindex;
+ }
+ }
+
+ /* If a conditional ipropx dump we check if the existing dump is
+ * good enough. */
+ if (ofile != NULL && conditional) {
+ if (!dump->iprop) {
+ com_err(progname, 0,
+ _("Conditional dump is an undocumented option for "
+ "use only for iprop dumps"));
+ goto error;
+ }
+ if (current_dump_sno_in_ulog(util_context, ofile))
+ return;
+ }
+
+ /*
+ * Make sure the database is open. The policy database only has
+ * to be opened if we try a dump that uses it.
+ */
+ if (!dbactive) {
+ com_err(progname, 0, _("Database not currently opened!"));
+ goto error;
+ }
+
+ /*
+ * If we're doing a master key conversion, set up for it.
+ */
+ if (mkey_convert) {
+ if (!valid_master_key) {
+ /* TRUE here means read the keyboard, but only once */
+ retval = krb5_db_fetch_mkey(util_context, master_princ,
+ master_keyblock.enctype, TRUE, FALSE,
+ NULL, NULL, NULL, &master_keyblock);
+ if (retval) {
+ com_err(progname, retval, _("while reading master key"));
+ exit(1);
+ }
+ retval = krb5_db_fetch_mkey_list(util_context, master_princ,
+ &master_keyblock);
+ if (retval) {
+ com_err(progname, retval, _("while verifying master key"));
+ exit(1);
+ }
+ }
+ new_master_keyblock.enctype = global_params.enctype;
+ if (new_master_keyblock.enctype == ENCTYPE_UNKNOWN)
+ new_master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
+
+ if (new_mkey_file) {
+ if (global_params.mask & KADM5_CONFIG_KVNO)
+ kt_kvno = global_params.kvno;
+ else
+ kt_kvno = IGNORE_VNO;
+
+ retval = krb5_db_fetch_mkey(util_context, master_princ,
+ new_master_keyblock.enctype, FALSE,
+ FALSE, new_mkey_file, &kt_kvno, NULL,
+ &new_master_keyblock);
+ if (retval) {
+ com_err(progname, retval, _("while reading new master key"));
+ exit(1);
+ }
+ } else {
+ printf(_("Please enter new master key....\n"));
+ retval = krb5_db_fetch_mkey(util_context, master_princ,
+ new_master_keyblock.enctype, TRUE,
+ TRUE, NULL, NULL, NULL,
+ &new_master_keyblock);
+ if (retval) {
+ com_err(progname, retval, _("while reading new master key"));
+ exit(1);
+ }
+ }
+ /* Get new master key vno that will be used to protect princs. */
+ new_mkvno = get_next_kvno(util_context, master_entry);
+ }
+
+ ret = 0;
+
+ if (ofile != NULL && strcmp(ofile, "-")) {
+ /* Discourage accidental dumping to filenames beginning with '-'. */
+ if (ofile[0] == '-')
+ usage();
+ if (!prep_ok_file(util_context, ofile, &ok_fd))
+ return; /* prep_ok_file() bumps exit_status */
+ f = create_ofile(ofile, &tmpofile);
+ if (f == NULL) {
+ com_err(progname, errno, _("while opening %s for writing"), ofile);
+ goto error;
+ }
+ } else {
+ f = stdout;
+ }
+
+ args.ofile = f;
+ args.context = util_context;
+ args.dump = dump;
+ fprintf(args.ofile, "%s", dump->header);
+
+ if (dump_sno) {
+ ret = ulog_get_last(util_context, &last);
+ if (ret) {
+ com_err(progname, ret, _("while reading update log header"));
+ goto error;
+ }
+ if (ipropx_version)
+ fprintf(f, " %u", IPROPX_VERSION);
+ fprintf(f, " %u", last.last_sno);
+ fprintf(f, " %u", last.last_time.seconds);
+ fprintf(f, " %u", last.last_time.useconds);
+ }
+
+ if (dump->header[strlen(dump->header)-1] != '\n')
+ fputc('\n', args.ofile);
+
+ ret = krb5_db_iterate(util_context, NULL, dump_iterator, &args, iterflags);
+ if (ret) {
+ com_err(progname, ret, _("performing %s dump"), dump->name);
+ goto error;
+ }
+
+ if (dump->dump_policy != NULL) {
+ ret = krb5_db_iter_policy(util_context, "*", dump->dump_policy, &args);
+ if (ret) {
+ com_err(progname, ret, _("performing %s dump"), dump->name);
+ goto error;
+ }
+ }
+
+ if (f != stdout) {
+ fclose(f);
+ finish_ofile(ofile, &tmpofile);
+ update_ok_file(util_context, ok_fd);
+ }
+ return;
+
+error:
+ if (tmpofile != NULL)
+ unlink(tmpofile);
+ free(tmpofile);
+ exit_status++;
+}
+
+/* Restore the database from any version dump file. */
+static int
+restore_dump(krb5_context context, char *dumpfile, FILE *f,
+ krb5_boolean verbose, dump_version *dump)
+{
+ int err = 0;
+ int lineno = 1;
+
+ /* Process the records. */
+ while (!(err = dump->load_record(context, dumpfile, f, verbose, &lineno)));
+ if (err != -1) {
+ fprintf(stderr, _("%s: error processing line %d of %s\n"), progname,
+ lineno, dumpfile);
+ return err;
+ }
+ return 0;
+}
+
+/*
+ * Usage: load_db [-ov] [-b7] [-r13] [-verbose] [-update] [-hash]
+ * filename
+ */
+void
+load_db(int argc, char **argv)
+{
+ krb5_error_code ret;
+ FILE *f = NULL;
+ char *dumpfile = NULL, *dbname, buf[BUFSIZ];
+ dump_version *load = NULL;
+ int aindex;
+ kdb_log_context *log_ctx;
+ kdb_last_t last;
+ krb5_boolean db_locked = FALSE, temp_db_created = FALSE;
+ krb5_boolean verbose = FALSE, update = FALSE, iprop_load = FALSE;
+
+ /* Parse the arguments. */
+ dbname = global_params.dbname;
+ exit_status = 0;
+ log_ctx = util_context->kdblog_context;
+
+ for (aindex = 1; aindex < argc; aindex++) {
+ if (!strcmp(argv[aindex], "-b7")){
+ load = &beta7_version;
+ } else if (!strcmp(argv[aindex], "-ov")) {
+ load = &ov_version;
+ } else if (!strcmp(argv[aindex], "-r13")) {
+ load = &r1_3_version;
+ } else if (!strcmp(argv[aindex], "-r18")){
+ load = &r1_8_version;
+ } else if (!strcmp(argv[aindex], "-i")) {
+ if (log_ctx && log_ctx->iproprole) {
+ load = &iprop_version;
+ iprop_load = TRUE;
+ } else {
+ fprintf(stderr, _("Iprop not enabled\n"));
+ goto error;
+ }
+ } else if (!strcmp(argv[aindex], "-verbose")) {
+ verbose = TRUE;
+ } else if (!strcmp(argv[aindex], "-update")){
+ update = TRUE;
+ } else if (!strcmp(argv[aindex], "-hash")) {
+ if (!add_db_arg("hash=true")) {
+ com_err(progname, ENOMEM, _("while parsing options"));
+ goto error;
+ }
+ } else {
+ break;
+ }
+ }
+ if (argc - aindex != 1)
+ usage();
+ dumpfile = argv[aindex];
+
+ /* Open the dumpfile. */
+ if (dumpfile != NULL) {
+ f = fopen(dumpfile, "r");
+ if (f == NULL) {
+ com_err(progname, errno, _("while opening %s"), dumpfile);
+ goto error;
+ }
+ } else {
+ f = stdin;
+ dumpfile = _("standard input");
+ }
+
+ /* Auto-detect dump version if we weren't told, or verify if we were. */
+ if (fgets(buf, sizeof(buf), f) == NULL) {
+ fprintf(stderr, _("%s: can't read dump header in %s\n"), progname,
+ dumpfile);
+ goto error;
+ }
+ if (load) {
+ /* Only check what we know; some headers only contain a prefix.
+ * NB: this should work for ipropx even though load is iprop */
+ if (strncmp(buf, load->header, strlen(load->header)) != 0) {
+ fprintf(stderr, _("%s: dump header bad in %s\n"), progname,
+ dumpfile);
+ goto error;
+ }
+ } else {
+ if (strcmp(buf, beta7_version.header) == 0) {
+ load = &beta7_version;
+ } else if (strcmp(buf, r1_3_version.header) == 0) {
+ load = &r1_3_version;
+ } else if (strcmp(buf, r1_8_version.header) == 0) {
+ load = &r1_8_version;
+ } else if (strcmp(buf, r1_11_version.header) == 0) {
+ load = &r1_11_version;
+ } else if (strncmp(buf, ov_version.header,
+ strlen(ov_version.header)) == 0) {
+ load = &ov_version;
+ } else {
+ fprintf(stderr, _("%s: dump header bad in %s\n"), progname,
+ dumpfile);
+ goto error;
+ }
+ }
+
+ if (global_params.iprop_enabled &&
+ ulog_map(util_context, global_params.iprop_logfile,
+ global_params.iprop_ulogsize)) {
+ fprintf(stderr, _("Could not open iprop ulog\n"));
+ goto error;
+ }
+
+ if (load->updateonly && !update) {
+ fprintf(stderr, _("%s: dump version %s can only be loaded with the "
+ "-update flag\n"), progname, load->name);
+ goto error;
+ }
+
+ /* If we are not in update mode, we create an alternate database and then
+ * promote it to be the live db. */
+ if (!update) {
+ if (!add_db_arg("temporary")) {
+ com_err(progname, ENOMEM, _("computing parameters for database"));
+ goto error;
+ }
+
+ if (iprop_load && !add_db_arg("merge_nra")) {
+ com_err(progname, ENOMEM, _("computing parameters for database"));
+ goto error;
+ }
+
+ ret = krb5_db_create(util_context, db5util_db_args);
+ if (ret) {
+ com_err(progname, ret, _("while creating database"));
+ goto error;
+ }
+ temp_db_created = TRUE;
+ } else {
+ /* Initialize the database. */
+ ret = krb5_db_open(util_context, db5util_db_args,
+ KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
+ if (ret) {
+ com_err(progname, ret, _("while opening database"));
+ goto error;
+ }
+
+ /* Make sure the db is left unusable if the update fails, if the db
+ * supports locking. */
+ ret = krb5_db_lock(util_context, KRB5_DB_LOCKMODE_PERMANENT);
+ if (ret == 0) {
+ db_locked = TRUE;
+ } else if (ret != KRB5_PLUGIN_OP_NOTSUPP) {
+ com_err(progname, ret, _("while permanently locking database"));
+ goto error;
+ }
+ }
+
+ if (log_ctx != NULL && log_ctx->iproprole && !update) {
+ /* Don't record updates we are making to the temporary DB. We will
+ * reinitialize or update the ulog header after promoting it. */
+ log_ctx->iproprole = IPROP_SLAVE;
+ if (iprop_load) {
+ /* Parse the iprop header information. */
+ if (!parse_iprop_header(buf, &load, &last))
+ goto error;
+ }
+ }
+
+ if (restore_dump(util_context, dumpfile ? dumpfile : _("standard input"),
+ f, verbose, load)) {
+ fprintf(stderr, _("%s: %s restore failed\n"), progname, load->name);
+ goto error;
+ }
+
+ if (db_locked && (ret = krb5_db_unlock(util_context))) {
+ com_err(progname, ret, _("while unlocking database"));
+ goto error;
+ }
+
+ if (!update) {
+ /* Initialize the ulog header before promoting so we can't leave behind
+ * the pre-load ulog state if we are killed just after promoting. */
+ if (log_ctx != NULL && log_ctx->iproprole) {
+ ret = ulog_init_header(util_context);
+ if (ret) {
+ com_err(progname, ret, _("while reinitializing update log"));
+ goto error;
+ }
+ }
+
+ ret = krb5_db_promote(util_context, db5util_db_args);
+ /* Ignore a not supported error since there is nothing to do about it
+ * anyway. */
+ if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {
+ com_err(progname, ret,
+ _("while making newly loaded database live"));
+ goto error;
+ }
+
+ if (log_ctx != NULL && log_ctx->iproprole) {
+ /* Reinitialize the ulog header since we replaced the DB, and
+ * record the iprop state if we received it. */
+ ret = ulog_init_header(util_context);
+ if (ret) {
+ com_err(progname, ret, _("while reinitializing update log"));
+ goto error;
+ }
+ if (iprop_load) {
+ ret = ulog_set_last(util_context, &last);
+ if (ret) {
+ com_err(progname, ret,
+ _("while writing update log header"));
+ goto error;
+ }
+ }
+ }
+ }
+
+cleanup:
+ /* If we created a temporary DB but didn't succeed, destroy it. */
+ if (exit_status && temp_db_created) {
+ ret = krb5_db_destroy(util_context, db5util_db_args);
+ /* Ignore a not supported error since there is nothing to do about
+ * it anyway. */
+ if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) {
+ com_err(progname, ret, _("while deleting bad database %s"),
+ dbname);
+ }
+ }
+
+ if (f != NULL && f != stdin)
+ fclose(f);
+
+ return;
+
+error:
+ exit_status++;
+ goto cleanup;
+}
diff --git a/src/kadmin/dbutil/import_err.et b/src/kadmin/dbutil/import_err.et
new file mode 100644
index 000000000000..48bb8fb9a924
--- /dev/null
+++ b/src/kadmin/dbutil/import_err.et
@@ -0,0 +1,22 @@
+error_table imp
+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_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_GET_PARAMS, "while retrieving configuration parameters"
+end
diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c
new file mode 100644
index 000000000000..1745a4d62039
--- /dev/null
+++ b/src/kadmin/dbutil/kadm5_create.c
@@ -0,0 +1,270 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ * $Source$
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <k5-int.h>
+#include <ctype.h>
+#include <kdb.h>
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+
+#include "fake-addrinfo.h"
+
+
+#include <krb5.h>
+#include <kdb.h>
+#include "kdb5_util.h"
+
+static int add_admin_princ(void *handle, krb5_context context,
+ char *name, char *realm, int attrs, int lifetime);
+static int add_admin_princs(void *handle, krb5_context context, char *realm);
+
+#define ERR 1
+#define OK 0
+
+#define ADMIN_LIFETIME 60*60*3 /* 3 hours */
+#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */
+
+/*
+ * 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;
+ krb5_context context;
+
+ kadm5_config_params lparams;
+
+ if ((retval = kadm5_init_krb5_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, 1,
+ params, &lparams))) {
+ com_err(progname, retval, _("while looking up the Kerberos "
+ "configuration"));
+ return 1;
+ }
+
+ retval = kadm5_create_magic_princs(&lparams, context);
+
+ kadm5_free_config_params(context, &lparams);
+ krb5_free_context(context);
+
+ return retval;
+}
+
+int kadm5_create_magic_princs(kadm5_config_params *params,
+ krb5_context context)
+{
+ int retval;
+ void *handle;
+
+ retval = krb5_klog_init(context, "admin_server", progname, 0);
+ if (retval)
+ return retval;
+ if ((retval = kadm5_init(context, progname, NULL, NULL, params,
+ KADM5_STRUCT_VERSION,
+ KADM5_API_VERSION_4,
+ db5util_db_args,
+ &handle))) {
+ com_err(progname, retval, _("while initializing the Kerberos admin "
+ "interface"));
+ return retval;
+ }
+
+ retval = add_admin_princs(handle, context, params->realm);
+
+ kadm5_destroy(handle);
+
+ krb5_klog_close(context);
+
+ return retval;
+}
+
+/*
+ * 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.
+ */
+static int add_admin_princs(void *handle, krb5_context context, char *realm)
+{
+ krb5_error_code ret = 0;
+ char *service_name = 0, *kiprop_name = 0, *canonhost = 0;
+ char localname[MAXHOSTNAMELEN];
+
+ if (gethostname(localname, MAXHOSTNAMELEN)) {
+ ret = errno;
+ perror("gethostname");
+ goto clean_and_exit;
+ }
+ ret = krb5_expand_hostname(context, localname, &canonhost);
+ if (ret) {
+ com_err(progname, ret, _("while canonicalizing local hostname"));
+ goto clean_and_exit;
+ }
+ if (asprintf(&service_name, "kadmin/%s", canonhost) < 0) {
+ ret = ENOMEM;
+ fprintf(stderr, _("Out of memory\n"));
+ goto clean_and_exit;
+ }
+ if (asprintf(&kiprop_name, "kiprop/%s", canonhost) < 0) {
+ ret = ENOMEM;
+ fprintf(stderr, _("Out of memory\n"));
+ goto clean_and_exit;
+ }
+
+ if ((ret = add_admin_princ(handle, context,
+ service_name, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED |
+ KRB5_KDB_LOCKDOWN_KEYS,
+ ADMIN_LIFETIME)))
+ goto clean_and_exit;
+
+ if ((ret = add_admin_princ(handle, context,
+ KADM5_ADMIN_SERVICE, realm,
+ KRB5_KDB_DISALLOW_TGT_BASED |
+ KRB5_KDB_LOCKDOWN_KEYS,
+ 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 |
+ KRB5_KDB_LOCKDOWN_KEYS,
+ CHANGEPW_LIFETIME)))
+ goto clean_and_exit;
+
+ ret = add_admin_princ(handle, context, kiprop_name, realm, 0, 0);
+
+clean_and_exit:
+ krb5_free_string(context, canonhost);
+ free(service_name);
+ free(kiprop_name);
+
+ 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;
+ krb5_error_code ret;
+ kadm5_principal_ent_rec ent;
+ long flags;
+
+ memset(&ent, 0, sizeof(ent));
+
+ if (asprintf(&fullname, "%s@%s", name, realm) < 0) {
+ com_err(progname, ENOMEM, _("while appending realm to principal"));
+ return ERR;
+ }
+ ret = krb5_parse_name(context, fullname, &ent.principal);
+ if (ret) {
+ com_err(progname, ret, _("while parsing admin principal name"));
+ return(ERR);
+ }
+ ent.max_life = lifetime;
+ ent.attributes = attrs;
+
+ flags = KADM5_PRINCIPAL | KADM5_ATTRIBUTES;
+ if (lifetime)
+ flags |= KADM5_MAX_LIFE;
+ ret = kadm5_create_principal(handle, &ent, flags, NULL);
+ if (ret && ret != KADM5_DUP) {
+ com_err(progname, ret, _("while creating principal %s"), fullname);
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+ return ERR;
+ }
+
+ krb5_free_principal(context, ent.principal);
+ free(fullname);
+
+ return OK;
+}
diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c
new file mode 100644
index 000000000000..92bb6f6eee75
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_create.c
@@ -0,0 +1,502 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/dbutil/kdb5_create.c - Create a KDC database */
+/*
+ * Copyright 1990,1991,2001, 2002, 2008 by the Massachusetts Institute of
+ * Technology. All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <k5-int.h>
+#include <kdb.h>
+#include <kadm5/server_internal.h>
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+#include "kdb5_util.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_keyblock *key;
+ 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_keyblock *) NULL,
+ 1,
+ &def_kslist
+};
+
+struct iterate_args {
+ krb5_context ctx;
+ struct realm_info *rblock;
+ krb5_db_entry *dbentp;
+};
+
+static krb5_error_code add_principal
+(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 char *mkey_fullname;
+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 */
+};
+
+extern char *mkey_password;
+
+extern char *progname;
+extern int exit_status;
+extern kadm5_config_params global_params;
+extern krb5_context util_context;
+
+void kdb5_create(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int optchar;
+
+ krb5_error_code retval;
+ char *pw_str = 0;
+ unsigned int pw_size = 0;
+ int do_stash = 0;
+ krb5_data pwd, seed;
+ kdb_log_context *log_ctx;
+ krb5_kvno mkey_kvno;
+ int strong_random = 1;
+
+ while ((optchar = getopt(argc, argv, "sW")) != -1) {
+ switch(optchar) {
+ case 's':
+ do_stash++;
+ break;
+ case 'h':
+ if (!add_db_arg("hash=true")) {
+ com_err(progname, ENOMEM,
+ _("while parsing command arguments\n"));
+ exit(1);
+ }
+ break;
+ case 'W':
+ strong_random = 0;
+ 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;
+
+ log_ctx = util_context->kdblog_context;
+
+ printf(_("Loading random data\n"));
+ retval = krb5_c_random_os_entropy (util_context, strong_random, NULL);
+ if (retval) {
+ com_err(progname, retval, _("Loading random data"));
+ 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(progname, 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);
+ if (pw_str == NULL) {
+ com_err(progname, ENOMEM, _("while creating new master key"));
+ exit_status++; return;
+ }
+
+ retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
+ pw_str, &pw_size);
+ if (retval) {
+ com_err(progname, 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(progname, retval, _("while calculating master key salt"));
+ exit_status++; return;
+ }
+
+ retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
+ &pwd, &master_salt, &master_keyblock);
+ if (retval) {
+ com_err(progname, retval,
+ _("while transforming master key from password"));
+ exit_status++; return;
+ }
+
+ rblock.key = &master_keyblock;
+
+ seed = make_data(master_keyblock.contents, master_keyblock.length);
+
+ if ((retval = krb5_c_random_seed(util_context, &seed))) {
+ com_err(progname, retval,
+ _("while initializing random key generator"));
+ exit_status++; return;
+ }
+ if ((retval = krb5_db_create(util_context,
+ db5util_db_args))) {
+ com_err(progname, retval, _("while creating database '%s'"),
+ global_params.dbname);
+ exit_status++; return;
+ }
+/* if ((retval = krb5_db_fini(util_context))) { */
+/* com_err(progname, retval, "while closing current database"); */
+/* exit_status++; return; */
+/* } */
+/* if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW))) { */
+/* com_err(progname, retval, "while initializing the database '%s'", */
+/* global_params.dbname); */
+/* exit_status++; return; */
+/* } */
+
+ if (log_ctx && log_ctx->iproprole) {
+ retval = ulog_map(util_context, global_params.iprop_logfile,
+ global_params.iprop_ulogsize);
+ if (retval) {
+ com_err(argv[0], retval, _("while creating update log"));
+ exit_status++;
+ return;
+ }
+
+ /*
+ * We're reinitializing the update log in case one already
+ * existed, but this should never happen.
+ */
+ retval = ulog_init_header(util_context);
+ if (retval) {
+ com_err(argv[0], retval, _("while initializing update log"));
+ exit_status++;
+ return;
+ }
+
+ /*
+ * Since we're creating a new db we shouldn't worry about
+ * adding the initial principals since any slave might as well
+ * do full resyncs from this newly created db.
+ */
+ log_ctx->iproprole = IPROP_NULL;
+ }
+
+ if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) ||
+ (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) {
+ com_err(progname, 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.
+ */
+
+ /*
+ * Determine the kvno to use, it must be that used to create the master key
+ * princ.
+ */
+ if (global_params.mask & KADM5_CONFIG_KVNO)
+ mkey_kvno = global_params.kvno; /* user specified */
+ else
+ mkey_kvno = 1; /* Default */
+
+ retval = krb5_db_store_master_key(util_context,
+ global_params.stash_file,
+ master_princ,
+ mkey_kvno,
+ &master_keyblock,
+ mkey_password);
+ if (retval) {
+ com_err(progname, retval, _("while storing key"));
+ printf(_("Warning: couldn't stash master key.\n"));
+ }
+ /* clean up */
+ zapfree(pw_str, pw_size);
+ 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);
+
+ 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 key;
+ krb5_int32 ind;
+ 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.
+ */
+ pwd.data = mkey_password;
+ pwd.length = strlen(mkey_password);
+ kret = krb5_c_random_seed(context, &pwd);
+ if (kret)
+ return kret;
+
+ if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) {
+ ind = iargs->dbentp->n_key_data-1;
+ if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype,
+ &key))) {
+ kret = krb5_dbe_encrypt_key_data(context, iargs->rblock->key,
+ &key, NULL, 1,
+ &iargs->dbentp->key_data[ind]);
+ krb5_free_keyblock_contents(context, &key);
+ }
+ }
+
+ 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_kvno mkey_kvno;
+ krb5_timestamp now;
+ struct iterate_args iargs;
+ krb5_actkvno_node actkvno;
+
+ entry = calloc(1, sizeof(*entry));
+ if (entry == NULL)
+ return ENOMEM;
+
+ 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(entry->key_data, 0, sizeof(krb5_key_data));
+ entry->n_key_data = 1;
+
+ if (global_params.mask & KADM5_CONFIG_KVNO)
+ mkey_kvno = global_params.kvno; /* user specified */
+ else
+ mkey_kvno = 1; /* Default */
+ entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if ((retval = krb5_dbe_encrypt_key_data(context, pblock->key,
+ &master_keyblock, NULL,
+ mkey_kvno, entry->key_data)))
+ return retval;
+ /*
+ * There should always be at least one "active" mkey so creating the
+ * KRB5_TL_ACTKVNO entry now so the initial mkey is active.
+ */
+ actkvno.next = NULL;
+ actkvno.act_kvno = mkey_kvno;
+ /* earliest possible time in case system clock is set back */
+ actkvno.act_time = 0;
+ if ((retval = krb5_dbe_update_actkvno(context, entry, &actkvno)))
+ return retval;
+
+ /* so getprinc shows the right kvno */
+ if ((retval = krb5_dbe_update_mkvno(context, entry, mkey_kvno)))
+ 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;
+ }
+
+ entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
+ KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA |
+ KADM5_PRINC_EXPIRE_TIME);
+ entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS;
+
+ retval = krb5_db_put_principal(context, entry);
+
+error_out:
+ krb5_db_free_principal(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 000000000000..fffce7429674
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_destroy.c
@@ -0,0 +1,94 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/dbutil/kdb5_destroy.c - Destroy a KDC database */
+/*
+ * Copyright 1990, 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include <stdio.h>
+#include "com_err.h"
+#include <kadm5/admin.h>
+#include <kdb.h>
+#include "kdb5_util.h"
+
+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 */
+
+void
+kdb5_destroy(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int optind;
+ int optchar;
+ char *dbname;
+ char buf[5];
+ krb5_error_code retval1;
+ int force = 0;
+
+ dbname = global_params.dbname;
+
+ optind = 1;
+ while ((optchar = getopt(argc, argv, "f")) != -1) {
+ switch(optchar) {
+ case 'f':
+ force++;
+ break;
+ case '?':
+ default:
+ usage();
+ 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);
+ }
+
+ retval1 = krb5_db_destroy(util_context, db5util_db_args);
+ if (retval1) {
+ com_err(progname, retval1, _("deleting database '%s'"), dbname);
+ exit_status++; return;
+ }
+
+ if (global_params.iprop_enabled) {
+ (void) unlink(global_params.iprop_logfile);
+ }
+
+ dbactive = FALSE;
+ printf(_("** Database '%s' destroyed.\n"), dbname);
+ return;
+}
diff --git a/src/kadmin/dbutil/kdb5_mkey.c b/src/kadmin/dbutil/kdb5_mkey.c
new file mode 100644
index 000000000000..7df8cbc83f21
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_mkey.c
@@ -0,0 +1,1333 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <k5-int.h>
+#include <kdb.h>
+#include <kadm5/server_internal.h>
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+#include "kdb5_util.h"
+#include <time.h>
+
+#if defined(HAVE_COMPILE) && defined(HAVE_STEP)
+#define SOLARIS_REGEXPS
+#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC)
+#define POSIX_REGEXPS
+#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC)
+#define BSD_REGEXPS
+#else
+#error I cannot find any regexp functions
+#endif
+#ifdef SOLARIS_REGEXPS
+#include <regexpr.h>
+#endif
+#ifdef POSIX_REGEXPS
+#include <regex.h>
+#endif
+
+extern krb5_keyblock master_keyblock; /* current mkey */
+extern krb5_kvno master_kvno;
+extern krb5_principal master_princ;
+extern krb5_data master_salt;
+extern char *mkey_fullname;
+extern char *mkey_password;
+extern char *progname;
+extern int exit_status;
+extern kadm5_config_params global_params;
+extern krb5_context util_context;
+extern time_t get_date(char *);
+
+static char *strdate(krb5_timestamp when)
+{
+ struct tm *tm;
+ static char out[40];
+
+ time_t lcltim = when;
+ tm = localtime(&lcltim);
+ strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm);
+ return out;
+}
+
+krb5_kvno
+get_next_kvno(krb5_context context, krb5_db_entry *entry)
+{
+ krb5_kvno new_kvno;
+
+ new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data,
+ entry->key_data);
+ new_kvno++;
+ /* deal with wrapping */
+ if (new_kvno == 0)
+ new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */
+
+ return (new_kvno);
+}
+
+krb5_error_code
+add_new_mkey(krb5_context context, krb5_db_entry *master_entry,
+ krb5_keyblock *new_mkey, krb5_kvno use_mkvno)
+{
+ krb5_error_code retval = 0;
+ int old_key_data_count, i;
+ krb5_kvno new_mkey_kvno;
+ krb5_key_data tmp_key_data;
+ krb5_mkey_aux_node *mkey_aux_data_head = NULL, **mkey_aux_data;
+ krb5_keylist_node *keylist_node;
+ krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(context);
+
+ /* do this before modifying master_entry key_data */
+ new_mkey_kvno = get_next_kvno(context, master_entry);
+ /* verify the requested mkvno if not 0 is the one that would be used here. */
+ if (use_mkvno != 0 && new_mkey_kvno != use_mkvno)
+ return (KRB5_KDB_KVNONOMATCH);
+
+ old_key_data_count = master_entry->n_key_data;
+
+ /* alloc enough space to hold new and existing key_data */
+ /*
+ * The encrypted key is malloc'ed by krb5_dbe_encrypt_key_data and
+ * krb5_key_data key_data_contents is a pointer to this key. Using some
+ * logic from master_key_convert().
+ */
+ for (i = 0; i < master_entry->n_key_data; i++)
+ krb5_free_key_data_contents(context, &master_entry->key_data[i]);
+ free(master_entry->key_data);
+ master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) *
+ (old_key_data_count + 1));
+ if (master_entry->key_data == NULL)
+ return (ENOMEM);
+
+ memset(master_entry->key_data, 0,
+ sizeof(krb5_key_data) * (old_key_data_count + 1));
+ master_entry->n_key_data = old_key_data_count + 1;
+
+ /* Note, mkey does not have salt */
+ /* add new mkey encrypted with itself to mkey princ entry */
+ if ((retval = krb5_dbe_encrypt_key_data(context, new_mkey, new_mkey, NULL,
+ (int) new_mkey_kvno,
+ &master_entry->key_data[0]))) {
+ return (retval);
+ }
+ /* the mvkno should be that of the newest mkey */
+ if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) {
+ krb5_free_key_data_contents(context, &master_entry->key_data[0]);
+ return (retval);
+ }
+ /*
+ * Need to decrypt old keys with the current mkey which is in the global
+ * master_keyblock and encrypt those keys with the latest mkey. And while
+ * the old keys are being decrypted, use those to create the
+ * KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of
+ * the older mkeys.
+ *
+ * The new mkey is followed by existing keys.
+ *
+ * First, set up for creating a krb5_mkey_aux_node list which will be used
+ * to update the mkey aux data for the mkey princ entry.
+ */
+ mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+ if (mkey_aux_data_head == NULL) {
+ retval = ENOMEM;
+ goto clean_n_exit;
+ }
+ memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node));
+ mkey_aux_data = &mkey_aux_data_head;
+
+ for (keylist_node = master_keylist, i = 1; keylist_node != NULL;
+ keylist_node = keylist_node->next, i++) {
+
+ /*
+ * Create a list of krb5_mkey_aux_node nodes. One node contains the new
+ * mkey encrypted by an old mkey and the old mkey's kvno (one node per
+ * old mkey).
+ */
+ if (*mkey_aux_data == NULL) {
+ /* *mkey_aux_data points to next field of previous node */
+ *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node));
+ if (*mkey_aux_data == NULL) {
+ retval = ENOMEM;
+ goto clean_n_exit;
+ }
+ memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node));
+ }
+
+ memset(&tmp_key_data, 0, sizeof(tmp_key_data));
+ /* encrypt the new mkey with the older mkey */
+ retval = krb5_dbe_encrypt_key_data(context, &keylist_node->keyblock,
+ new_mkey, NULL, (int) new_mkey_kvno,
+ &tmp_key_data);
+ if (retval)
+ goto clean_n_exit;
+
+ (*mkey_aux_data)->latest_mkey = tmp_key_data;
+ (*mkey_aux_data)->mkey_kvno = keylist_node->kvno;
+ mkey_aux_data = &((*mkey_aux_data)->next);
+
+ /*
+ * Store old key in master_entry keydata past the new mkey
+ */
+ retval = krb5_dbe_encrypt_key_data(context, new_mkey,
+ &keylist_node->keyblock,
+ NULL, (int) keylist_node->kvno,
+ &master_entry->key_data[i]);
+ if (retval)
+ goto clean_n_exit;
+ }
+ assert(i == old_key_data_count + 1);
+
+ if ((retval = krb5_dbe_update_mkey_aux(context, master_entry,
+ mkey_aux_data_head))) {
+ goto clean_n_exit;
+ }
+ master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
+
+clean_n_exit:
+ krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head);
+ return (retval);
+}
+
+void
+kdb5_add_mkey(int argc, char *argv[])
+{
+ int optchar;
+ krb5_error_code retval;
+ char *pw_str = 0;
+ unsigned int pw_size = 0;
+ int do_stash = 0;
+ krb5_data pwd;
+ krb5_kvno new_mkey_kvno;
+ krb5_keyblock new_mkeyblock;
+ krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN;
+ char *new_mkey_password;
+ krb5_db_entry *master_entry = NULL;
+ krb5_timestamp now;
+
+ /*
+ * The command table entry for this command causes open_db_and_mkey() to be
+ * called first to open the KDB and get the current mkey.
+ */
+
+ memset(&new_mkeyblock, 0, sizeof(new_mkeyblock));
+ master_salt.data = NULL;
+
+ while ((optchar = getopt(argc, argv, "e:s")) != -1) {
+ switch(optchar) {
+ case 'e':
+ if (krb5_string_to_enctype(optarg, &new_master_enctype)) {
+ com_err(progname, EINVAL, _("%s is an invalid enctype"),
+ optarg);
+ exit_status++;
+ return;
+ }
+ break;
+ case 's':
+ do_stash++;
+ break;
+ case '?':
+ default:
+ usage();
+ return;
+ }
+ }
+
+ if (new_master_enctype == ENCTYPE_UNKNOWN)
+ new_master_enctype = global_params.enctype;
+
+ retval = krb5_db_get_principal(util_context, master_princ, 0,
+ &master_entry);
+ if (retval != 0) {
+ com_err(progname, retval, _("while getting master key principal %s"),
+ mkey_fullname);
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ printf(_("Creating new master key for master key principal '%s'\n"),
+ mkey_fullname);
+
+ printf(_("You will be prompted for a new 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);
+ if (pw_str == NULL) {
+ com_err(progname, ENOMEM, _("while creating new master key"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2,
+ pw_str, &pw_size);
+ if (retval) {
+ com_err(progname, retval,
+ _("while reading new master key from keyboard"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ new_mkey_password = pw_str;
+
+ pwd.data = new_mkey_password;
+ pwd.length = strlen(new_mkey_password);
+ retval = krb5_principal2salt(util_context, master_princ, &master_salt);
+ if (retval) {
+ com_err(progname, retval, _("while calculating master key salt"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ retval = krb5_c_string_to_key(util_context, new_master_enctype,
+ &pwd, &master_salt, &new_mkeyblock);
+ if (retval) {
+ com_err(progname, retval,
+ _("while transforming master key from password"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ new_mkey_kvno = get_next_kvno(util_context, master_entry);
+ retval = add_new_mkey(util_context, master_entry, &new_mkeyblock,
+ new_mkey_kvno);
+ if (retval) {
+ com_err(progname, retval,
+ _("adding new master key to master principal"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_timeofday(util_context, &now))) {
+ com_err(progname, retval, _("while getting current time"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
+ now, master_princ))) {
+ com_err(progname, retval, _("while updating the master key principal "
+ "modification time"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_db_put_principal(util_context, master_entry))) {
+ com_err(progname, retval, _("while adding master key entry to the "
+ "database"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if (do_stash) {
+ retval = krb5_db_store_master_key(util_context,
+ global_params.stash_file,
+ master_princ,
+ new_mkey_kvno,
+ &new_mkeyblock,
+ mkey_password);
+ if (retval) {
+ com_err(progname, retval, _("while storing key"));
+ printf(_("Warning: couldn't stash master key.\n"));
+ }
+ }
+
+cleanup_return:
+ /* clean up */
+ krb5_db_free_principal(util_context, master_entry);
+ zap((char *)new_mkeyblock.contents, new_mkeyblock.length);
+ free(new_mkeyblock.contents);
+ if (pw_str) {
+ zap(pw_str, pw_size);
+ free(pw_str);
+ }
+ free(master_salt.data);
+ return;
+}
+
+void
+kdb5_use_mkey(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ krb5_kvno use_kvno;
+ krb5_timestamp now, start_time;
+ krb5_actkvno_node *actkvno_list = NULL, *new_actkvno = NULL,
+ *prev_actkvno, *cur_actkvno;
+ krb5_db_entry *master_entry = NULL;
+ krb5_keylist_node *keylist_node;
+ krb5_boolean inserted = FALSE;
+ krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
+
+ if (argc < 2 || argc > 3) {
+ /* usage calls exit */
+ usage();
+ }
+
+ use_kvno = atoi(argv[1]);
+ if (use_kvno == 0) {
+ com_err(progname, EINVAL, _("0 is an invalid KVNO value"));
+ exit_status++;
+ return;
+ } else {
+ /* verify use_kvno is valid */
+ for (keylist_node = master_keylist; keylist_node != NULL;
+ keylist_node = keylist_node->next) {
+ if (use_kvno == keylist_node->kvno)
+ break;
+ }
+ if (!keylist_node) {
+ com_err(progname, EINVAL, _("%d is an invalid KVNO value"),
+ use_kvno);
+ exit_status++;
+ return;
+ }
+ }
+
+ if ((retval = krb5_timeofday(util_context, &now))) {
+ com_err(progname, retval, _("while getting current time"));
+ exit_status++;
+ return;
+ }
+
+ if (argc == 3) {
+ time_t t = get_date(argv[2]);
+ if (t == -1) {
+ com_err(progname, 0, _("could not parse date-time string '%s'"),
+ argv[2]);
+ exit_status++;
+ return;
+ } else
+ start_time = (krb5_timestamp) t;
+ } else {
+ start_time = now;
+ }
+
+ /*
+ * Need to:
+ *
+ * 1. get mkey princ
+ * 2. get krb5_actkvno_node list
+ * 3. add use_kvno to actkvno list (sorted in right spot)
+ * 4. update mkey princ's tl data
+ * 5. put mkey princ.
+ */
+
+ retval = krb5_db_get_principal(util_context, master_princ, 0,
+ &master_entry);
+ if (retval != 0) {
+ com_err(progname, retval, _("while getting master key principal %s"),
+ mkey_fullname);
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
+ if (retval != 0) {
+ com_err(progname, retval,
+ _("while looking up active version of master key"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ /*
+ * If an entry already exists with the same kvno either delete it or if it's
+ * the only entry, just set its active time.
+ */
+ for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
+ cur_actkvno != NULL;
+ prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
+
+ if (cur_actkvno->act_kvno == use_kvno) {
+ /* delete it */
+ if (prev_actkvno) {
+ prev_actkvno->next = cur_actkvno->next;
+ cur_actkvno->next = NULL;
+ krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
+ } else {
+ if (cur_actkvno->next) {
+ /* delete it from front of list */
+ actkvno_list = cur_actkvno->next;
+ cur_actkvno->next = NULL;
+ krb5_dbe_free_actkvno_list(util_context, cur_actkvno);
+ } else {
+ /* There's only one entry, go ahead and change the time */
+ cur_actkvno->act_time = start_time;
+ inserted = TRUE;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!inserted) {
+ /* alloc enough space to hold new and existing key_data */
+ new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node));
+ if (new_actkvno == NULL) {
+ com_err(progname, ENOMEM, _("while adding new master key"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ memset(new_actkvno, 0, sizeof(krb5_actkvno_node));
+ new_actkvno->act_kvno = use_kvno;
+ new_actkvno->act_time = start_time;
+
+ /* insert new act kvno node */
+
+ if (actkvno_list == NULL) {
+ /* new actkvno is the list */
+ actkvno_list = new_actkvno;
+ } else {
+ for (prev_actkvno = NULL, cur_actkvno = actkvno_list;
+ cur_actkvno != NULL;
+ prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) {
+
+ if (new_actkvno->act_time < cur_actkvno->act_time) {
+ if (prev_actkvno) {
+ prev_actkvno->next = new_actkvno;
+ new_actkvno->next = cur_actkvno;
+ } else {
+ new_actkvno->next = actkvno_list;
+ actkvno_list = new_actkvno;
+ }
+ break;
+ } else if (cur_actkvno->next == NULL) {
+ /* end of line, just add new node to end of list */
+ cur_actkvno->next = new_actkvno;
+ break;
+ }
+ }
+ }
+ }
+
+ if (actkvno_list->act_time > now) {
+ com_err(progname, EINVAL,
+ _("there must be one master key currently active"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
+ actkvno_list))) {
+ com_err(progname, retval,
+ _("while updating actkvno data for master principal entry"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
+ now, master_princ))) {
+ com_err(progname, retval, _("while updating the master key principal "
+ "modification time"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_db_put_principal(util_context, master_entry))) {
+ com_err(progname, retval,
+ _("while adding master key entry to the database"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+cleanup_return:
+ /* clean up */
+ krb5_db_free_principal(util_context, master_entry);
+ krb5_dbe_free_actkvno_list(util_context, actkvno_list);
+ return;
+}
+
+void
+kdb5_list_mkeys(int argc, char *argv[])
+{
+ krb5_error_code retval;
+ char *output_str = NULL, enctype[BUFSIZ];
+ krb5_kvno act_kvno;
+ krb5_timestamp act_time;
+ krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno;
+ krb5_db_entry *master_entry = NULL;
+ krb5_keylist_node *cur_kb_node;
+ krb5_keyblock *act_mkey;
+ krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
+
+ if (master_keylist == NULL) {
+ com_err(progname, 0, _("master keylist not initialized"));
+ exit_status++;
+ return;
+ }
+
+ retval = krb5_db_get_principal(util_context, master_princ, 0,
+ &master_entry);
+ if (retval != 0) {
+ com_err(progname, retval, _("while getting master key principal %s"),
+ mkey_fullname);
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
+ if (retval != 0) {
+ com_err(progname, retval, _("while looking up active kvno list"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &act_kvno,
+ &act_mkey);
+ if (retval != 0) {
+ com_err(progname, retval, _("while looking up active master key"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ printf("Master keys for Principal: %s\n", mkey_fullname);
+
+ for (cur_kb_node = master_keylist; cur_kb_node != NULL;
+ cur_kb_node = cur_kb_node->next) {
+
+ if ((retval = krb5_enctype_to_name(cur_kb_node->keyblock.enctype,
+ FALSE, enctype, sizeof(enctype)))) {
+ com_err(progname, retval, _("while getting enctype description"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ act_time = -1; /* assume actkvno entry not found */
+ for (cur_actkvno = actkvno_list; cur_actkvno != NULL;
+ cur_actkvno = cur_actkvno->next) {
+ if (cur_actkvno->act_kvno == cur_kb_node->kvno) {
+ act_time = cur_actkvno->act_time;
+ break;
+ }
+ }
+
+ if (cur_kb_node->kvno == act_kvno) {
+ /* * indicates kvno is currently active */
+ retval = asprintf(&output_str,
+ _("KVNO: %d, Enctype: %s, Active on: %s *\n"),
+ cur_kb_node->kvno, enctype, strdate(act_time));
+ } else {
+ if (act_time != -1) {
+ retval = asprintf(&output_str,
+ _("KVNO: %d, Enctype: %s, Active on: %s\n"),
+ cur_kb_node->kvno, enctype, strdate(act_time));
+ } else {
+ retval = asprintf(&output_str,
+ _("KVNO: %d, Enctype: %s, No activate time "
+ "set\n"), cur_kb_node->kvno, enctype);
+ }
+ }
+ if (retval == -1) {
+ com_err(progname, ENOMEM, _("asprintf could not allocate enough "
+ "memory to hold output"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ printf("%s", output_str);
+ free(output_str);
+ output_str = NULL;
+ }
+
+cleanup_return:
+ /* clean up */
+ krb5_db_free_principal(util_context, master_entry);
+ free(output_str);
+ krb5_dbe_free_actkvno_list(util_context, actkvno_list);
+ return;
+}
+
+struct update_enc_mkvno {
+ unsigned int re_match_count;
+ unsigned int already_current;
+ unsigned int updated;
+ unsigned int dry_run : 1;
+ unsigned int verbose : 1;
+#ifdef SOLARIS_REGEXPS
+ char *expbuf;
+#endif
+#ifdef POSIX_REGEXPS
+ regex_t preg;
+#endif
+#if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS)
+ unsigned char placeholder;
+#endif
+};
+
+/* XXX Duplicated in libkadm5srv! */
+/*
+ * Function: glob_to_regexp
+ *
+ * Arguments:
+ *
+ * glob (r) the shell-style glob (?*[]) to convert
+ * realm (r) the default realm to append, or NULL
+ * regexp (w) the ed-style regexp created from glob
+ *
+ * Effects:
+ *
+ * regexp is filled in with allocated memory contained a regular
+ * expression to be used with re_comp/compile that matches what the
+ * shell-style glob would match. If glob does not contain an "@"
+ * character and realm is not NULL, "@*" is appended to the regexp.
+ *
+ * Conversion algorithm:
+ *
+ * quoted characters are copied quoted
+ * ? is converted to .
+ * * is converted to .*
+ * active characters are quoted: ^, $, .
+ * [ and ] are active but supported and have the same meaning, so
+ * they are copied
+ * other characters are copied
+ * regexp is anchored with ^ and $
+ */
+static int glob_to_regexp(char *glob, char *realm, char **regexp)
+{
+ int append_realm;
+ char *p;
+
+ /* validate the glob */
+ if (glob[strlen(glob)-1] == '\\')
+ return EINVAL;
+
+ /* A character of glob can turn into two in regexp, plus ^ and $ */
+ /* and trailing null. If glob has no @, also allocate space for */
+ /* the realm. */
+ append_realm = (realm != NULL) && (strchr(glob, '@') == NULL);
+ p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0));
+ if (p == NULL)
+ return ENOMEM;
+ *regexp = p;
+
+ *p++ = '^';
+ while (*glob) {
+ switch (*glob) {
+ case '?':
+ *p++ = '.';
+ break;
+ case '*':
+ *p++ = '.';
+ *p++ = '*';
+ break;
+ case '.':
+ case '^':
+ case '$':
+ *p++ = '\\';
+ *p++ = *glob;
+ break;
+ case '\\':
+ *p++ = '\\';
+ *p++ = *++glob;
+ break;
+ default:
+ *p++ = *glob;
+ break;
+ }
+ glob++;
+ }
+
+ if (append_realm) {
+ *p++ = '@';
+ *p++ = '.';
+ *p++ = '*';
+ }
+
+ *p++ = '$';
+ *p++ = '\0';
+ return 0;
+}
+
+static int
+update_princ_encryption_1(void *cb, krb5_db_entry *ent)
+{
+ struct update_enc_mkvno *p = cb;
+ char *pname = 0;
+ krb5_error_code retval;
+ int match;
+ krb5_timestamp now;
+ int result;
+ krb5_kvno old_mkvno;
+
+ retval = krb5_unparse_name(util_context, ent->princ, &pname);
+ if (retval) {
+ com_err(progname, retval,
+ _("getting string representation of principal name"));
+ goto fail;
+ }
+
+ if (krb5_principal_compare(util_context, ent->princ, master_princ)) {
+ goto skip;
+ }
+
+#ifdef SOLARIS_REGEXPS
+ match = (step(pname, p->expbuf) != 0);
+#endif
+#ifdef POSIX_REGEXPS
+ match = (regexec(&p->preg, pname, 0, NULL, 0) == 0);
+#endif
+#ifdef BSD_REGEXPS
+ match = (re_exec(pname) != 0);
+#endif
+ if (!match) {
+ goto skip;
+ }
+ p->re_match_count++;
+ retval = krb5_dbe_get_mkvno(util_context, ent, &old_mkvno);
+ if (retval) {
+ com_err(progname, retval,
+ _("determining master key used for principal '%s'"), pname);
+ goto fail;
+ }
+ /* Line up "skip" and "update" messages for viewing. */
+ if (old_mkvno == new_mkvno) {
+ if (p->dry_run && p->verbose)
+ printf(_("would skip: %s\n"), pname);
+ else if (p->verbose)
+ printf(_("skipping: %s\n"), pname);
+ p->already_current++;
+ goto skip;
+ }
+ if (p->dry_run) {
+ if (p->verbose)
+ printf(_("would update: %s\n"), pname);
+ p->updated++;
+ goto skip;
+ } else if (p->verbose)
+ printf(_("updating: %s\n"), pname);
+ retval = master_key_convert (util_context, ent);
+ if (retval) {
+ com_err(progname, retval,
+ _("error re-encrypting key for principal '%s'"), pname);
+ goto fail;
+ }
+ if ((retval = krb5_timeofday(util_context, &now))) {
+ com_err(progname, retval, _("while getting current time"));
+ goto fail;
+ }
+
+ if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent,
+ now, master_princ))) {
+ com_err(progname, retval,
+ _("while updating principal '%s' modification time"), pname);
+ goto fail;
+ }
+
+ ent->mask |= KADM5_KEY_DATA;
+
+ if ((retval = krb5_db_put_principal(util_context, ent))) {
+ com_err(progname, retval, _("while updating principal '%s' key data "
+ "in the database"), pname);
+ goto fail;
+ }
+ p->updated++;
+skip:
+ result = 0;
+ goto egress;
+fail:
+ exit_status++;
+ result = 1;
+egress:
+ if (pname)
+ krb5_free_unparsed_name(util_context, pname);
+ return result;
+}
+
+extern int are_you_sure (const char *, ...)
+#if !defined(__cplusplus) && (__GNUC__ > 2)
+ __attribute__((__format__(__printf__, 1, 2)))
+#endif
+ ;
+
+int
+are_you_sure (const char *format, ...)
+{
+ va_list va;
+ char ansbuf[100];
+
+ va_start(va, format);
+ vprintf(format, va);
+ va_end(va);
+ printf(_("\n(type 'yes' to confirm)? "));
+ fflush(stdout);
+ if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL)
+ return 0;
+ if (strcmp(ansbuf, "yes\n"))
+ return 0;
+ return 1;
+}
+
+void
+kdb5_update_princ_encryption(int argc, char *argv[])
+{
+ struct update_enc_mkvno data = { 0 };
+ char *name_pattern = NULL;
+ int force = 0;
+ int optchar;
+ krb5_error_code retval;
+ krb5_actkvno_node *actkvno_list = 0;
+ krb5_db_entry *master_entry = NULL;
+#ifdef BSD_REGEXPS
+ char *msg;
+#endif
+ char *regexp = NULL;
+ krb5_keyblock *act_mkey;
+ krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context);
+ krb5_flags iterflags = 0;
+
+ while ((optchar = getopt(argc, argv, "fnv")) != -1) {
+ switch (optchar) {
+ case 'f':
+ force = 1;
+ break;
+ case 'n':
+ data.dry_run = 1;
+ break;
+ case 'v':
+ data.verbose = 1;
+ break;
+ case '?':
+ case ':':
+ default:
+ usage();
+ }
+ }
+ if (argv[optind] != NULL) {
+ name_pattern = argv[optind];
+ if (argv[optind+1] != NULL)
+ usage();
+ }
+
+ if (master_keylist == NULL) {
+ com_err(progname, 0, _("master keylist not initialized"));
+ exit_status++;
+ goto cleanup;
+ }
+
+ /* The glob_to_regexp code only cares if the "realm" parameter is
+ NULL or not; the string data is irrelevant. */
+ if (name_pattern == NULL)
+ name_pattern = "*";
+ if (glob_to_regexp(name_pattern, "hi", &regexp) != 0) {
+ com_err(progname, ENOMEM,
+ _("converting glob pattern '%s' to regular expression"),
+ name_pattern);
+ exit_status++;
+ goto cleanup;
+ }
+
+ if (
+#ifdef SOLARIS_REGEXPS
+ ((data.expbuf = compile(regexp, NULL, NULL)) == NULL)
+#endif
+#ifdef POSIX_REGEXPS
+ ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0)
+#endif
+#ifdef BSD_REGEXPS
+ ((msg = (char *) re_comp(regexp)) != NULL)
+#endif
+ ) {
+ /* XXX syslog msg or regerr(regerrno) */
+ com_err(progname, 0, _("error compiling converted regexp '%s'"),
+ regexp);
+ exit_status++;
+ goto cleanup;
+ }
+
+ retval = krb5_db_get_principal(util_context, master_princ, 0,
+ &master_entry);
+ if (retval != 0) {
+ com_err(progname, retval, _("while getting master key principal %s"),
+ mkey_fullname);
+ exit_status++;
+ goto cleanup;
+ }
+
+ retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
+ if (retval != 0) {
+ com_err(progname, retval, _("while looking up active kvno list"));
+ exit_status++;
+ goto cleanup;
+ }
+
+ retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &new_mkvno,
+ &act_mkey);
+ if (retval) {
+ com_err(progname, retval, _("while looking up active master key"));
+ exit_status++;
+ goto cleanup;
+ }
+ new_master_keyblock = *act_mkey;
+
+ if (!force &&
+ !data.dry_run &&
+ !are_you_sure(_("Re-encrypt all keys not using master key vno %u?"),
+ new_mkvno)) {
+ printf(_("OK, doing nothing.\n"));
+ exit_status++;
+ goto cleanup;
+ }
+ if (data.verbose) {
+ if (data.dry_run) {
+ printf(_("Principals whose keys WOULD BE re-encrypted to master "
+ "key vno %u:\n"), new_mkvno);
+ } else {
+ printf(_("Principals whose keys are being re-encrypted to master "
+ "key vno %u if necessary:\n"), new_mkvno);
+ }
+ }
+
+ if (!data.dry_run) {
+ /* Grab a write lock so we don't have to upgrade to a write lock and
+ * reopen the DB while iterating. */
+ iterflags = KRB5_DB_ITER_WRITE;
+ }
+
+ retval = krb5_db_iterate(util_context, name_pattern,
+ update_princ_encryption_1, &data, iterflags);
+ /* If exit_status is set, then update_princ_encryption_1 already
+ printed a message. */
+ if (retval != 0 && exit_status == 0) {
+ com_err(progname, retval, _("trying to process principal database"));
+ exit_status++;
+ }
+ if (data.dry_run) {
+ printf(_("%u principals processed: %u would be updated, %u already "
+ "current\n"),
+ data.re_match_count, data.updated, data.already_current);
+ } else {
+ printf(_("%u principals processed: %u updated, %u already current\n"),
+ data.re_match_count, data.updated, data.already_current);
+ }
+
+cleanup:
+ krb5_db_free_principal(util_context, master_entry);
+ free(regexp);
+#ifdef POSIX_REGEXPS
+ regfree(&data.preg);
+#endif
+ memset(&new_master_keyblock, 0, sizeof(new_master_keyblock));
+ krb5_dbe_free_actkvno_list(util_context, actkvno_list);
+}
+
+struct kvnos_in_use {
+ krb5_kvno kvno;
+ unsigned int use_count;
+};
+
+struct purge_args {
+ krb5_context kcontext;
+ struct kvnos_in_use *kvnos;
+ unsigned int num_kvnos;
+};
+
+static krb5_error_code
+find_mkvnos_in_use(krb5_pointer ptr,
+ krb5_db_entry *entry)
+{
+ krb5_error_code retval;
+ struct purge_args * args;
+ unsigned int i;
+ krb5_kvno mkvno;
+
+ args = (struct purge_args *) ptr;
+
+ retval = krb5_dbe_get_mkvno(args->kcontext, entry, &mkvno);
+ if (retval)
+ return (retval);
+
+ for (i = 0; i < args->num_kvnos; i++) {
+ if (args->kvnos[i].kvno == mkvno) {
+ /* XXX do I need to worry about use_count wrapping? */
+ args->kvnos[i].use_count++;
+ break;
+ }
+ }
+ return 0;
+}
+
+void
+kdb5_purge_mkeys(int argc, char *argv[])
+{
+ int optchar;
+ krb5_error_code retval;
+ krb5_timestamp now;
+ krb5_db_entry *master_entry = NULL;
+ krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE;
+ struct purge_args args;
+ char buf[5];
+ unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged;
+ unsigned int old_key_data_count;
+ krb5_actkvno_node *actkvno_list = NULL, *actkvno_entry, *prev_actkvno_entry;
+ krb5_mkey_aux_node *mkey_aux_list = NULL, *mkey_aux_entry, *prev_mkey_aux_entry;
+ krb5_key_data *old_key_data;
+
+ /*
+ * Verify that the master key list has been initialized before doing
+ * anything else.
+ */
+ if (krb5_db_mkey_list_alias(util_context) == NULL) {
+ com_err(progname, KRB5_KDB_DBNOTINITED,
+ _("master keylist not initialized"));
+ exit_status++;
+ return;
+ }
+
+ memset(&args, 0, sizeof(args));
+
+ optind = 1;
+ while ((optchar = getopt(argc, argv, "fnv")) != -1) {
+ switch(optchar) {
+ case 'f':
+ force = TRUE;
+ break;
+ case 'n':
+ dry_run = TRUE; /* mkey princ will not be modified */
+ force = TRUE; /* implied */
+ break;
+ case 'v':
+ verbose = TRUE;
+ break;
+ case '?':
+ default:
+ usage();
+ return;
+ }
+ }
+
+ retval = krb5_db_get_principal(util_context, master_princ, 0,
+ &master_entry);
+ if (retval != 0) {
+ com_err(progname, retval, _("while getting master key principal %s"),
+ mkey_fullname);
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if (!force) {
+ printf(_("Will purge all unused master keys stored in the '%s' "
+ "principal, are you sure?\n"), mkey_fullname);
+ printf(_("(type 'yes' to confirm)? "));
+ if (fgets(buf, sizeof(buf), stdin) == NULL) {
+ exit_status++;
+ goto cleanup_return;
+ }
+ if (strcmp(buf, "yes\n")) {
+ exit_status++;
+ goto cleanup_return;
+ }
+ printf(_("OK, purging unused master keys from '%s'...\n"),
+ mkey_fullname);
+ }
+
+ /* save the old keydata */
+ old_key_data_count = master_entry->n_key_data;
+ if (old_key_data_count == 1) {
+ if (verbose)
+ printf(_("There is only one master key which can not be "
+ "purged.\n"));
+ goto cleanup_return;
+ }
+ old_key_data = master_entry->key_data;
+
+ args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count);
+ if (args.kvnos == NULL) {
+ retval = ENOMEM;
+ com_err(progname, ENOMEM, _("while allocating args.kvnos"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count);
+ args.num_kvnos = old_key_data_count;
+ args.kcontext = util_context;
+
+ /* populate the kvnos array with all the current mkvnos */
+ for (i = 0; i < old_key_data_count; i++)
+ args.kvnos[i].kvno = master_entry->key_data[i].key_data_kvno;
+
+ if ((retval = krb5_db_iterate(util_context,
+ NULL,
+ find_mkvnos_in_use,
+ (krb5_pointer) &args, 0))) {
+ com_err(progname, retval, _("while finding master keys in use"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ /*
+ * args.kvnos has been marked with the mkvno's that are currently protecting
+ * princ entries
+ */
+ if (dry_run) {
+ printf(_("Would purge the following master key(s) from %s:\n"),
+ mkey_fullname);
+ } else {
+ printf(_("Purging the following master key(s) from %s:\n"),
+ mkey_fullname);
+ }
+
+ /* find # of keys still in use or print out verbose info */
+ for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) {
+ if (args.kvnos[i].use_count > 0) {
+ num_kvnos_inuse++;
+ } else {
+ /* this key would be deleted */
+ if (args.kvnos[i].kvno == master_kvno) {
+ com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT,
+ _("master key stash file needs updating, command "
+ "aborting"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ num_kvnos_purged++;
+ printf(_("KVNO: %d\n"), args.kvnos[i].kvno);
+ }
+ }
+ /* didn't find any keys to purge */
+ if (num_kvnos_inuse == args.num_kvnos) {
+ printf(_("All keys in use, nothing purged.\n"));
+ goto cleanup_return;
+ }
+ if (dry_run) {
+ /* bail before doing anything else */
+ printf(_("%d key(s) would be purged.\n"), num_kvnos_purged);
+ goto cleanup_return;
+ }
+
+ retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list);
+ if (retval != 0) {
+ com_err(progname, retval, _("while looking up active kvno list"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ retval = krb5_dbe_lookup_mkey_aux(util_context, master_entry, &mkey_aux_list);
+ if (retval != 0) {
+ com_err(progname, retval, _("while looking up mkey aux data list"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse);
+ if (master_entry->key_data == NULL) {
+ retval = ENOMEM;
+ com_err(progname, ENOMEM, _("while allocating key_data"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ memset(master_entry->key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse);
+ master_entry->n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */
+
+ /*
+ * Assuming that the latest mkey will not be purged because it will always
+ * be "in use" so this code will not bother with encrypting keys again.
+ */
+ for (i = k = 0; i < old_key_data_count; i++) {
+ for (j = 0; j < args.num_kvnos; j++) {
+ if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) {
+ if (args.kvnos[j].use_count != 0) {
+ master_entry->key_data[k++] = old_key_data[i];
+ memset(&old_key_data[i], 0, sizeof(old_key_data[i]));
+ break;
+ } else {
+ /* remove unused mkey */
+ /* adjust the actkno data */
+ for (prev_actkvno_entry = actkvno_entry = actkvno_list;
+ actkvno_entry != NULL;
+ actkvno_entry = actkvno_entry->next) {
+
+ if (actkvno_entry->act_kvno == args.kvnos[j].kvno) {
+ if (actkvno_entry == actkvno_list) {
+ /* remove from head */
+ actkvno_list = actkvno_entry->next;
+ prev_actkvno_entry = actkvno_list;
+ } else if (actkvno_entry->next == NULL) {
+ /* remove from tail */
+ prev_actkvno_entry->next = NULL;
+ } else {
+ /* remove in between */
+ prev_actkvno_entry->next = actkvno_entry->next;
+ }
+ actkvno_entry->next = NULL;
+ krb5_dbe_free_actkvno_list(util_context, actkvno_entry);
+ break; /* deleted entry, no need to loop further */
+ } else {
+ prev_actkvno_entry = actkvno_entry;
+ }
+ }
+ /* adjust the mkey aux data */
+ for (prev_mkey_aux_entry = mkey_aux_entry = mkey_aux_list;
+ mkey_aux_entry != NULL;
+ mkey_aux_entry = mkey_aux_entry->next) {
+
+ if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) {
+ if (mkey_aux_entry == mkey_aux_list) {
+ mkey_aux_list = mkey_aux_entry->next;
+ prev_mkey_aux_entry = mkey_aux_list;
+ } else if (mkey_aux_entry->next == NULL) {
+ prev_mkey_aux_entry->next = NULL;
+ } else {
+ prev_mkey_aux_entry->next = mkey_aux_entry->next;
+ }
+ mkey_aux_entry->next = NULL;
+ krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_entry);
+ break; /* deleted entry, no need to loop further */
+ } else {
+ prev_mkey_aux_entry = mkey_aux_entry;
+ }
+ }
+ }
+ }
+ }
+ }
+ assert(k == num_kvnos_inuse);
+
+ /* Free any key data entries we did not consume in the loop above. */
+ for (i = 0; i < old_key_data_count; i++)
+ krb5_dbe_free_key_data_contents(util_context, &old_key_data[i]);
+ free(old_key_data);
+
+ if ((retval = krb5_dbe_update_actkvno(util_context, master_entry,
+ actkvno_list))) {
+ com_err(progname, retval,
+ _("while updating actkvno data for master principal entry"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_dbe_update_mkey_aux(util_context, master_entry,
+ mkey_aux_list))) {
+ com_err(progname, retval,
+ _("while updating mkey_aux data for master principal entry"));
+ exit_status++;
+ return;
+ }
+
+ if ((retval = krb5_timeofday(util_context, &now))) {
+ com_err(progname, retval, _("while getting current time"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry,
+ now, master_princ))) {
+ com_err(progname, retval, _("while updating the master key principal "
+ "modification time"));
+ exit_status++;
+ goto cleanup_return;
+ }
+
+ master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA;
+
+ if ((retval = krb5_db_put_principal(util_context, master_entry))) {
+ com_err(progname, retval,
+ _("while adding master key entry to the database"));
+ exit_status++;
+ goto cleanup_return;
+ }
+ printf(_("%d key(s) purged.\n"), num_kvnos_purged);
+
+cleanup_return:
+ krb5_db_free_principal(util_context, master_entry);
+ free(args.kvnos);
+ krb5_dbe_free_actkvno_list(util_context, actkvno_list);
+ krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_list);
+ return;
+}
diff --git a/src/kadmin/dbutil/kdb5_stash.c b/src/kadmin/dbutil/kdb5_stash.c
new file mode 100644
index 000000000000..e05944f29005
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_stash.c
@@ -0,0 +1,139 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/dbutil/kdb5_stash.c - Store the master database key in a file */
+/*
+ * 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. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "k5-int.h"
+#include "com_err.h"
+#include <kadm5/admin.h>
+#include <stdio.h>
+#include "kdb5_util.h"
+
+extern krb5_keyblock master_keyblock;
+extern krb5_principal master_princ;
+extern kadm5_config_params global_params;
+
+extern int exit_status;
+extern int close_policy_db;
+
+void
+kdb5_stash(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ int optchar;
+ krb5_error_code retval;
+ char *keyfile = 0;
+ krb5_kvno mkey_kvno;
+
+ keyfile = global_params.stash_file;
+
+ optind = 1;
+ while ((optchar = getopt(argc, argv, "f:")) != -1) {
+ switch(optchar) {
+ case 'f':
+ keyfile = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ return;
+ }
+ }
+
+ if (!krb5_c_valid_enctype(master_keyblock.enctype)) {
+ char tmp[32];
+ if (krb5_enctype_to_name(master_keyblock.enctype, FALSE,
+ 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, "%s", tmp);
+ exit_status++; return;
+ }
+
+ if (global_params.mask & KADM5_CONFIG_KVNO)
+ mkey_kvno = global_params.kvno; /* user specified */
+ else
+ mkey_kvno = IGNORE_VNO; /* use whatever krb5_db_fetch_mkey finds */
+
+ if (!valid_master_key) {
+ /* TRUE here means read the keyboard, but only once */
+ retval = krb5_db_fetch_mkey(util_context, master_princ,
+ master_keyblock.enctype,
+ TRUE, FALSE, (char *) NULL,
+ &mkey_kvno,
+ NULL, &master_keyblock);
+ if (retval) {
+ com_err(progname, retval, _("while reading master key"));
+ exit_status++; return;
+ }
+
+ retval = krb5_db_fetch_mkey_list(util_context, master_princ,
+ &master_keyblock);
+ if (retval) {
+ com_err(progname, retval, _("while getting master key list"));
+ exit_status++; return;
+ }
+ } else {
+ printf(_("Using existing stashed keys to update stash file.\n"));
+ }
+
+ retval = krb5_db_store_master_key_list(util_context, keyfile, master_princ,
+ NULL);
+ if (retval) {
+ com_err(progname, retval, _("while storing key"));
+ exit_status++; return;
+ }
+
+ exit_status = 0;
+ return;
+}
diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c
new file mode 100644
index 000000000000..000b5595c9fa
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_util.c
@@ -0,0 +1,651 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/dbutil/kdb5_util.c - Administer a KDC database */
+/*
+ * (C) Copyright 1990,1991, 1996, 2008, 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <k5-int.h>
+#include <kadm5/admin.h>
+#include <locale.h>
+#include <adm_proto.h>
+#include <time.h>
+#include "kdb5_util.h"
+
+/*
+ * XXX Ick, ick, ick. These global variables shouldn't be global....
+ */
+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;
+kadm5_config_params global_params;
+
+void usage()
+{
+ fprintf(stderr,
+ _("Usage: kdb5_util [-x db_args]* [-r realm] [-d dbname] "
+ "[-k mkeytype] [-M mkeyname]\n"
+ "\t [-kv mkeyVNO] [-sf stashfilename] [-m] cmd "
+ "[cmd_options]\n"
+ "\tcreate [-s]\n"
+ "\tdestroy [-f]\n"
+ "\tstash [-f keyfile]\n"
+ "\tdump [-old|-ov|-b6|-b7|-r13|-r18] [-verbose]\n"
+ "\t [-mkey_convert] [-new_mkey_file mkey_file]\n"
+ "\t [-rev] [-recurse] [filename [princs...]]\n"
+ "\tload [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-update] "
+ "filename\n"
+ "\tark [-e etype_list] principal\n"
+ "\tadd_mkey [-e etype] [-s]\n"
+ "\tuse_mkey kvno [time]\n"
+ "\tlist_mkeys\n"));
+ /* avoid a string length compiler warning */
+ fprintf(stderr,
+ _("\tupdate_princ_encryption [-f] [-n] [-v] [princ-pattern]\n"
+ "\tpurge_mkeys [-f] [-n] [-v]\n"
+ "\ttabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype\n"
+ "\nwhere,\n\t[-x db_args]* - any number of database specific "
+ "arguments.\n"
+ "\t\t\tLook at each database documentation for supported "
+ "arguments\n"));
+ exit(1);
+}
+
+krb5_keyblock master_keyblock;
+krb5_kvno master_kvno; /* fetched */
+extern krb5_principal master_princ;
+char *mkey_fullname;
+krb5_db_entry *master_entry = NULL;
+int valid_master_key = 0;
+
+char *progname;
+krb5_boolean manual_mkey = FALSE;
+krb5_boolean dbactive = FALSE;
+
+static int open_db_and_mkey(void);
+
+static void add_random_key(int, char **);
+
+typedef void (*cmd_func)(int, char **);
+
+struct _cmd_table {
+ char *name;
+ cmd_func func;
+ int opendb;
+} cmd_table[] = {
+ {"create", kdb5_create, 0},
+ {"destroy", kdb5_destroy, 1}, /* 1 opens the kdb */
+ {"stash", kdb5_stash, 1},
+ {"dump", dump_db, 1},
+ {"load", load_db, 0},
+ {"ark", add_random_key, 1},
+ {"add_mkey", kdb5_add_mkey, 1},
+ {"use_mkey", kdb5_use_mkey, 1},
+ {"list_mkeys", kdb5_list_mkeys, 1},
+ {"update_princ_encryption", kdb5_update_princ_encryption, 1},
+ {"purge_mkeys", kdb5_purge_mkeys, 1},
+ {"tabdump", tabdump, 1},
+ {NULL, NULL, 0},
+};
+
+static struct _cmd_table *cmd_lookup(name)
+ char *name;
+{
+ struct _cmd_table *cmd = cmd_table;
+ while (cmd->name) {
+ if (strcmp(cmd->name, name) == 0)
+ return cmd;
+ else
+ cmd++;
+ }
+
+ return NULL;
+}
+
+#define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL))
+
+char **db5util_db_args = NULL;
+int db5util_db_args_size = 0;
+
+static void extended_com_err_fn (const char *myprog, errcode_t code,
+ const char *fmt, va_list args)
+{
+ const char *emsg;
+ if (code) {
+ emsg = krb5_get_error_message (util_context, code);
+ fprintf (stderr, "%s: %s ", myprog, emsg);
+ krb5_free_error_message (util_context, emsg);
+ } else {
+ fprintf (stderr, "%s: ", myprog);
+ }
+ vfprintf (stderr, fmt, args);
+ fprintf (stderr, "\n");
+}
+
+int add_db_arg(char *arg)
+{
+ char **temp;
+ db5util_db_args_size++;
+ temp = realloc(db5util_db_args,
+ sizeof(char *) * (db5util_db_args_size + 1));
+ if (temp == NULL)
+ return 0;
+ db5util_db_args = temp;
+ db5util_db_args[db5util_db_args_size-1] = arg;
+ db5util_db_args[db5util_db_args_size] = NULL;
+ return 1;
+}
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct _cmd_table *cmd = NULL;
+ char *koptarg, **cmd_argv;
+ char *db_name_tmp = NULL;
+ int cmd_argc;
+ krb5_error_code retval;
+
+ setlocale(LC_ALL, "");
+ set_com_err_hook(extended_com_err_fn);
+
+ /*
+ * Ensure that "progname" is set before calling com_err.
+ */
+ progname = (strrchr(argv[0], '/') ?
+ strrchr(argv[0], '/') + 1 : argv[0]);
+
+ retval = kadm5_init_krb5_context(&util_context);
+ if (retval) {
+ com_err (progname, retval, _("while initializing Kerberos code"));
+ exit(1);
+ }
+
+ cmd_argv = (char **) malloc(sizeof(char *)*argc);
+ if (cmd_argv == NULL) {
+ com_err(progname, ENOMEM, _("while creating sub-command arguments"));
+ exit(1);
+ }
+ memset(cmd_argv, 0, sizeof(char *)*argc);
+ cmd_argc = 1;
+
+ argv++; argc--;
+ while (*argv) {
+ if (strcmp(*argv, "-P") == 0 && ARG_VAL) {
+ mkey_password = koptarg;
+ manual_mkey = TRUE;
+ } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) {
+ global_params.dbname = koptarg;
+ global_params.mask |= KADM5_CONFIG_DBNAME;
+
+ if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0)
+ {
+ com_err(progname, ENOMEM,
+ _("while parsing command arguments"));
+ exit(1);
+ }
+
+ if (!add_db_arg(db_name_tmp)) {
+ com_err(progname, ENOMEM,
+ _("while parsing command arguments\n"));
+ exit(1);
+ }
+
+ } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) {
+ if (!add_db_arg(koptarg)) {
+ com_err(progname, ENOMEM,
+ _("while parsing command arguments\n"));
+ exit(1);
+ }
+
+ } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) {
+ global_params.realm = koptarg;
+ 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);
+ }
+ } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) {
+ if (krb5_string_to_enctype(koptarg, &global_params.enctype)) {
+ com_err(progname, EINVAL, _(": %s is an invalid enctype"),
+ koptarg);
+ exit(1);
+ } else
+ global_params.mask |= KADM5_CONFIG_ENCTYPE;
+ } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) {
+ global_params.kvno = (krb5_kvno) atoi(koptarg);
+ if (global_params.kvno == IGNORE_VNO) {
+ com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"),
+ koptarg);
+ exit(1);
+ } else
+ global_params.mask |= KADM5_CONFIG_KVNO;
+ } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) {
+ global_params.mkey_name = koptarg;
+ global_params.mask |= KADM5_CONFIG_MKEY_NAME;
+ } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) {
+ global_params.stash_file = koptarg;
+ global_params.mask |= KADM5_CONFIG_STASH_FILE;
+ } else if (strcmp(*argv, "-m") == 0) {
+ manual_mkey = TRUE;
+ global_params.mkey_from_kbd = 1;
+ global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD;
+ } else if (cmd_lookup(*argv) != NULL) {
+ if (cmd_argv[0] == NULL)
+ cmd_argv[0] = *argv;
+ else
+ usage();
+ } else {
+ cmd_argv[cmd_argc++] = *argv;
+ }
+ argv++; argc--;
+ }
+
+ if (cmd_argv[0] == NULL)
+ usage();
+
+ if( !util_context->default_realm )
+ {
+ char *temp = NULL;
+ retval = krb5_get_default_realm(util_context, &temp);
+ if( retval )
+ {
+ com_err(progname, retval, _("while getting default realm"));
+ exit(1);
+ }
+ krb5_free_default_realm(util_context, temp);
+ }
+
+ retval = kadm5_get_config_params(util_context, 1,
+ &global_params, &global_params);
+ if (retval) {
+ com_err(progname, 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.
+ */
+ (void) umask(077);
+
+ master_keyblock.enctype = global_params.enctype;
+ if ((master_keyblock.enctype != ENCTYPE_UNKNOWN) &&
+ (!krb5_c_valid_enctype(master_keyblock.enctype))) {
+ com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d", master_keyblock.enctype);
+ }
+
+ cmd = cmd_lookup(cmd_argv[0]);
+ if (cmd->opendb && open_db_and_mkey())
+ return exit_status;
+
+ if (global_params.iprop_enabled == TRUE)
+ ulog_set_role(util_context, IPROP_MASTER);
+ else
+ ulog_set_role(util_context, IPROP_NULL);
+
+ (*cmd->func)(cmd_argc, cmd_argv);
+
+ if( db_name_tmp )
+ free( db_name_tmp );
+
+ if( db5util_db_args )
+ free(db5util_db_args);
+
+ quit();
+ kadm5_free_config_params(util_context, &global_params);
+ krb5_free_context(util_context);
+ free(cmd_argv);
+ return exit_status;
+}
+
+#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(progname, 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(progname, retval, _("while closing previous database"));
+ exit_status++;
+ return;
+ }
+ if (valid_master_key) {
+ krb5_free_keyblock_contents(util_context, &master_keyblock);
+ master_keyblock.contents = NULL;
+ valid_master_key = 0;
+ }
+ krb5_free_principal(util_context, master_princ);
+ free(mkey_fullname);
+ dbactive = FALSE;
+ }
+
+ (void) set_dbname_help(progname, 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).
+ */
+static int open_db_and_mkey()
+{
+ krb5_error_code retval;
+ krb5_data scratch, pwd, seed;
+
+ dbactive = FALSE;
+ valid_master_key = 0;
+
+ if ((retval = krb5_db_open(util_context, db5util_db_args,
+ KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) {
+ com_err(progname, retval, _("while initializing 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,
+ &mkey_fullname, &master_princ))) {
+ com_err(progname, retval, _("while setting up master key name"));
+ exit_status++;
+ return(1);
+ }
+ if ((retval = krb5_db_get_principal(util_context, master_princ, 0,
+ &master_entry))) {
+ com_err(progname, retval, _("while retrieving master entry"));
+ exit_status++;
+ (void) krb5_db_fini(util_context);
+ return(1);
+ }
+
+ if (global_params.mask & KADM5_CONFIG_KVNO)
+ master_kvno = global_params.kvno; /* user specified */
+ else
+ master_kvno = IGNORE_VNO;
+
+ /* 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"));
+ exit_status++;
+ return(1);
+ }
+
+ /* If no encryption type is set, use the default */
+ if (master_keyblock.enctype == ENCTYPE_UNKNOWN)
+ master_keyblock.enctype = DEFAULT_KDC_ENCTYPE;
+ if (!krb5_c_valid_enctype(master_keyblock.enctype))
+ com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP,
+ "while setting up enctype %d",
+ master_keyblock.enctype);
+
+ retval = krb5_c_string_to_key(util_context, master_keyblock.enctype,
+ &pwd, &scratch, &master_keyblock);
+ if (retval) {
+ com_err(progname, retval,
+ _("while transforming master key from password"));
+ exit_status++;
+ return(1);
+ }
+ free(scratch.data);
+ mkey_password = 0;
+
+ } else {
+ if ((retval = krb5_db_fetch_mkey(util_context, master_princ,
+ master_keyblock.enctype,
+ manual_mkey, FALSE,
+ global_params.stash_file,
+ &master_kvno,
+ 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_fetch_mkey_list(util_context, master_princ,
+ &master_keyblock))) {
+ com_err(progname, retval, "while getting master key list");
+ com_err(progname, 0, "Warning: proceeding without master key list");
+ exit_status++;
+ return(0);
+ }
+
+ seed.length = master_keyblock.length;
+ seed.data = (char *) master_keyblock.contents;
+
+ if ((retval = krb5_c_random_seed(util_context, &seed))) {
+ com_err(progname, retval, _("while seeding random number generator"));
+ exit_status++;
+ memset(master_keyblock.contents, 0, master_keyblock.length);
+ krb5_free_keyblock_contents(util_context, &master_keyblock);
+ return(1);
+ }
+
+ if (global_params.iprop_enabled) {
+ if (ulog_map(util_context, global_params.iprop_logfile,
+ global_params.iprop_ulogsize)) {
+ fprintf(stderr, _("%s: Could not map log\n"), progname);
+ exit_status++;
+ 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;
+ ulog_fini(util_context);
+ retval = krb5_db_fini(util_context);
+ zapfree(master_keyblock.contents, master_keyblock.length);
+ krb5_free_principal(util_context, master_princ);
+ finished = TRUE;
+ if (retval && retval != KRB5_KDB_DBNOTINITED) {
+ com_err(progname, retval, _("while closing database"));
+ exit_status++;
+ return 1;
+ }
+ return 0;
+}
+
+static void
+add_random_key(argc, argv)
+ int argc;
+ char **argv;
+{
+ krb5_error_code ret;
+ krb5_principal princ;
+ krb5_db_entry *dbent;
+ krb5_timestamp now;
+
+ krb5_key_salt_tuple *keysalts = NULL;
+ krb5_int32 num_keysalts = 0;
+
+ int free_keysalts;
+ char *me = progname;
+ char *ks_str = NULL;
+ char *pr_str;
+ krb5_keyblock *tmp_mkey;
+
+ if (argc < 2)
+ usage();
+ for (argv++, argc--; *argv; argv++, argc--) {
+ if (!strcmp(*argv, "-e")) {
+ argv++; argc--;
+ ks_str = *argv;
+ continue;
+ } else
+ break;
+ }
+ if (argc < 1)
+ usage();
+ pr_str = *argv;
+ ret = krb5_parse_name(util_context, pr_str, &princ);
+ if (ret) {
+ com_err(me, ret, _("while parsing principal name %s"), pr_str);
+ exit_status++;
+ return;
+ }
+ ret = krb5_db_get_principal(util_context, princ, 0, &dbent);
+ if (ret) {
+ com_err(me, ret, _("while fetching principal %s"), pr_str);
+ exit_status++;
+ return;
+ }
+ ret = krb5_string_to_keysalts(ks_str,
+ NULL, NULL, 0,
+ &keysalts,
+ &num_keysalts);
+ if (ret) {
+ com_err(me, ret, _("while parsing keysalts %s"), ks_str);
+ exit_status++;
+ return;
+ }
+ if (!num_keysalts || keysalts == NULL) {
+ num_keysalts = global_params.num_keysalts;
+ keysalts = global_params.keysalts;
+ free_keysalts = 0;
+ } else
+ free_keysalts = 1;
+
+ /* Find the mkey used to protect the existing keys */
+ ret = krb5_dbe_find_mkey(util_context, dbent, &tmp_mkey);
+ if (ret) {
+ com_err(me, ret, _("while finding mkey"));
+ krb5_db_free_principal(util_context, dbent);
+ exit_status++;
+ return;
+ }
+
+ ret = krb5_dbe_ark(util_context, tmp_mkey, keysalts, num_keysalts, dbent);
+ if (free_keysalts)
+ free(keysalts);
+ if (ret) {
+ com_err(me, ret, "while randomizing principal %s", pr_str);
+ krb5_db_free_principal(util_context, dbent);
+ exit_status++;
+ return;
+ }
+ dbent->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE;
+ ret = krb5_timeofday(util_context, &now);
+ if (ret) {
+ com_err(me, ret, _("while getting time"));
+ krb5_db_free_principal(util_context, dbent);
+ exit_status++;
+ return;
+ }
+ ret = krb5_dbe_update_last_pwd_change(util_context, dbent, now);
+ if (ret) {
+ com_err(me, ret, _("while setting changetime"));
+ krb5_db_free_principal(util_context, dbent);
+ exit_status++;
+ return;
+ }
+ ret = krb5_db_put_principal(util_context, dbent);
+ krb5_db_free_principal(util_context, dbent);
+ if (ret) {
+ com_err(me, ret, _("while saving principal %s"), pr_str);
+ exit_status++;
+ return;
+ }
+ printf(_("%s changed\n"), pr_str);
+}
diff --git a/src/kadmin/dbutil/kdb5_util.h b/src/kadmin/dbutil/kdb5_util.h
new file mode 100644
index 000000000000..69e186d4b21e
--- /dev/null
+++ b/src/kadmin/dbutil/kdb5_util.h
@@ -0,0 +1,99 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/dbutil/kdb5_util.h */
+/*
+ * Copyright 1992, 2008, 2009 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include <kdb_log.h>
+
+#define REALM_SEP '@'
+#define REALM_SEP_STR "@"
+
+extern char *progname;
+#ifndef V4_DECLARES_STATIC
+extern krb5_keyblock master_keyblock;
+extern krb5_principal master_princ;
+#endif
+extern krb5_boolean dbactive;
+extern int exit_status;
+extern krb5_context util_context;
+extern kadm5_config_params global_params;
+extern int valid_master_key;
+extern krb5_db_entry master_db;
+extern char **db5util_db_args;
+extern int db5util_db_args_size;
+extern krb5_kvno new_mkvno;
+extern krb5_keyblock new_master_keyblock;
+extern int add_db_arg(char *arg);
+
+extern void usage(void);
+
+extern void add_key
+(char const *, char const *,
+ krb5_const_principal, const krb5_keyblock *,
+ krb5_kvno, krb5_keysalt *);
+extern int set_dbname_help
+(char *, char *);
+
+extern char *kdb5_util_Init (int, char **);
+
+extern int quit (void);
+
+extern int check_for_match
+(char *, int, krb5_db_entry *, int, int);
+
+extern void parse_token
+(char *, int *, int *, char *);
+
+extern int create_db_entry (krb5_principal, krb5_db_entry *);
+
+extern int kadm5_create_magic_princs (kadm5_config_params *params,
+ krb5_context context);
+
+extern int process_ov_principal (krb5_context kcontext, const char *fname,
+ FILE *filep, krb5_boolean verbose,
+ int *linenop);
+
+extern void load_db (int argc, char **argv);
+extern void dump_db (int argc, char **argv);
+extern void kdb5_create (int argc, char **argv);
+extern void kdb5_destroy (int argc, char **argv);
+extern void kdb5_stash (int argc, char **argv);
+extern void kdb5_add_mkey (int argc, char **argv);
+extern void kdb5_use_mkey (int argc, char **argv);
+extern void kdb5_list_mkeys (int argc, char **argv);
+extern void kdb5_update_princ_encryption (int argc, char **argv);
+extern void tabdump (int argc, char **argv);
+
+extern krb5_error_code master_key_convert(krb5_context context,
+ krb5_db_entry *db_entry);
+extern void kdb5_purge_mkeys (int argc, char **argv);
+
+extern int kadm5_create (kadm5_config_params *params);
+
+extern krb5_error_code add_new_mkey(krb5_context, krb5_db_entry *,
+ krb5_keyblock *, krb5_kvno);
+
+extern krb5_kvno get_next_kvno(krb5_context, krb5_db_entry *);
+
+void usage (void);
diff --git a/src/kadmin/dbutil/nstrtok.h b/src/kadmin/dbutil/nstrtok.h
new file mode 100644
index 000000000000..3ee8f634c05b
--- /dev/null
+++ b/src/kadmin/dbutil/nstrtok.h
@@ -0,0 +1,3 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* Prototype for nstrtok */
+char *nstrtok(char *, const char *delim);
diff --git a/src/kadmin/dbutil/ovload.c b/src/kadmin/dbutil/ovload.c
new file mode 100644
index 000000000000..15a5ab300521
--- /dev/null
+++ b/src/kadmin/dbutil/ovload.c
@@ -0,0 +1,205 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include <k5-int.h>
+#include <unistd.h>
+
+#include <kadm5/admin.h>
+#include <kadm5/server_internal.h>
+#include <kdb.h>
+#include "import_err.h"
+#include "kdb5_util.h"
+#include "nstrtok.h"
+
+#define LINESIZE 32768 /* XXX */
+
+static int parse_pw_hist_ent(current, hist)
+ char *current;
+ osa_pw_hist_ent *hist;
+{
+ int tmp, i, j, ret;
+ char *cp;
+
+ ret = 0;
+ 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 success, error code on failure
+ *
+ * Requires:
+ * principal database to be opened.
+ * nstrtok(3) to have a valid buffer in memory.
+ *
+ * Effects:
+ * [effects]
+ *
+ * Modifies:
+ * [modifies]
+ *
+ */
+int process_ov_principal(kcontext, fname, filep, verbose, linenop)
+ krb5_context kcontext;
+ const char *fname;
+ FILE *filep;
+ krb5_boolean verbose;
+ int *linenop;
+{
+ XDR xdrs;
+ osa_princ_ent_t rec;
+ krb5_error_code ret;
+ krb5_tl_data tl_data;
+ krb5_principal princ;
+ krb5_db_entry *kdb = NULL;
+ char *current = 0;
+ char *cp;
+ unsigned int x;
+ char line[LINESIZE];
+
+ if (fgets(line, LINESIZE, filep) == (char *) NULL) {
+ return IMPORT_BAD_FILE;
+ }
+ if((cp = nstrtok(line, "\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(kcontext, cp, &princ)))
+ goto done;
+ krb5_unparse_name(kcontext, 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 = strdup(cp)) == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ } 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]);
+ }
+
+ xdralloc_create(&xdrs, XDR_ENCODE);
+ if (! xdr_osa_princ_ent_rec(&xdrs, rec)) {
+ xdr_destroy(&xdrs);
+ ret = KADM5_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 = (krb5_octet *) xdralloc_getdata(&xdrs);
+
+ ret = krb5_db_get_principal(kcontext, princ, 0, &kdb);
+ if (ret)
+ goto done;
+
+ ret = krb5_dbe_update_tl_data(kcontext, kdb, &tl_data);
+ if (ret)
+ goto done;
+
+ ret = krb5_db_put_principal(kcontext, kdb);
+ if (ret)
+ goto done;
+
+ xdr_destroy(&xdrs);
+
+ (*linenop)++;
+
+done:
+ free(current);
+ krb5_free_principal(kcontext, princ);
+ osa_free_princ_ent(rec);
+ krb5_db_free_principal(kcontext, kdb);
+ return ret;
+}
diff --git a/src/kadmin/dbutil/strtok.c b/src/kadmin/dbutil/strtok.c
new file mode 100644
index 000000000000..0640c747e44e
--- /dev/null
+++ b/src/kadmin/dbutil/strtok.c
@@ -0,0 +1,105 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ */
+
+/*
+ * 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.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include "nstrtok.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;
+ register const char *delim;
+{
+ register const 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/dbutil/t_tdumputil.c b/src/kadmin/dbutil/t_tdumputil.c
new file mode 100644
index 000000000000..a9ed0e547151
--- /dev/null
+++ b/src/kadmin/dbutil/t_tdumputil.c
@@ -0,0 +1,115 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/t_tdumputil.c - test tdumputil.c functions */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "tdumputil.h"
+
+static char *argv0;
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s: [-T rectype] [-c] nfields {fieldnames} {fields}\n",
+ argv0);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, csv = 0, i, nf;
+ char **a, *rectype = NULL;
+ struct rechandle *h;
+
+ argv0 = argv[0];
+ while ((ch = getopt(argc, argv, "T:c")) != -1) {
+ switch (ch) {
+ case 'T':
+ rectype = optarg;
+ break;
+ case 'c':
+ csv = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (csv)
+ h = rechandle_csv(stdout, rectype);
+ else
+ h = rechandle_tabsep(stdout, rectype);
+ if (h == NULL)
+ exit(1);
+
+ if (*argv == NULL)
+ usage();
+ nf = atoi(*argv);
+ argc--;
+ argv++;
+ a = calloc(nf + 1, sizeof(*a));
+ if (a == NULL)
+ exit(1);
+
+ for (i = 0; argv[i] != NULL && i < nf; i++)
+ a[i] = argv[i];
+ if (i != nf)
+ usage();
+ argv += nf;
+ a[nf] = NULL;
+
+ if (rectype == NULL && writeheader(h, a) < 0)
+ exit(1);
+ free(a);
+
+ while (*argv != NULL) {
+ if (startrec(h) < 0)
+ exit(1);
+ for (i = 0; argv[i] != NULL && i < nf; i++) {
+ if (writefield(h, "%s", argv[i]) < 0)
+ exit(1);
+ }
+ if (i != nf)
+ usage();
+ argv += nf;
+ if (endrec(h) < 0)
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/src/kadmin/dbutil/t_tdumputil.py b/src/kadmin/dbutil/t_tdumputil.py
new file mode 100755
index 000000000000..5d7ac38d2fba
--- /dev/null
+++ b/src/kadmin/dbutil/t_tdumputil.py
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+from k5test import *
+from subprocess import *
+
+realm = K5Realm(create_kdb=False)
+
+def compare(s, expected, msg):
+ if s == expected:
+ return
+ print 'expected:', repr(expected)
+ print 'got:', repr(s)
+ fail(msg)
+
+out = realm.run(['./t_tdumputil', '2', 'field1', 'field2',
+ 'value1', 'value2'])
+expected = 'field1\tfield2\nvalue1\tvalue2\n'
+compare(out, expected, 'tab-separated values')
+
+out = realm.run(['./t_tdumputil', '-c', '2', 'field1', 'field2',
+ 'space value', 'comma,value',
+ 'quote"value', 'quotes""value'])
+expected = 'field1,field2\nspace value,"comma,value"\n' \
+ '"quote""value","quotes""""value"\n'
+compare(out, expected, 'comma-separated values')
+
+out = realm.run(['./t_tdumputil', '-T', 'rectype', '2', 'field1', 'field2',
+ 'value1', 'value2'])
+expected = 'rectype\tvalue1\tvalue2\n'
+compare(out, expected, 'rectype prefixed')
+
+success('tabdump utilities')
diff --git a/src/kadmin/dbutil/tabdump.c b/src/kadmin/dbutil/tabdump.c
new file mode 100644
index 000000000000..69a3482ec935
--- /dev/null
+++ b/src/kadmin/dbutil/tabdump.c
@@ -0,0 +1,665 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/tabdump.c - reporting-friendly tabular KDB dumps */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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.
+ */
+
+#include <k5-int.h>
+#include "k5-platform.h" /* for asprintf */
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <kadm5/admin.h>
+#include <kadm5/server_internal.h>
+
+#include "adm_proto.h"
+#include "kdb5_util.h"
+#include "tdumputil.h"
+
+struct tdopts {
+ int csv; /* 1 for CSV, 0 for tab-separated */
+ int emptyhex_empty; /* print empty hex strings as "" not "-1" */
+ int numeric; /* numeric instead of symbolic output */
+ int omitheader; /* omit field headers */
+ int writerectype; /* write record type prefix */
+ char *fname; /* output file name */
+};
+
+struct rec_args;
+
+typedef int (tdump_princ_fn)(struct rec_args *, const char *, krb5_db_entry *);
+typedef int (tdump_policy_fn)(struct rec_args *, const char *,
+ kadm5_policy_ent_t);
+
+/* Descriptor for a tabdump record type */
+struct tdtype {
+ const char *rectype;
+ char * const *fieldnames;
+ tdump_princ_fn *princ_fn;
+ tdump_policy_fn *policy_fn;
+};
+
+static tdump_princ_fn keydata;
+static tdump_princ_fn keyinfo;
+static tdump_princ_fn princ_flags;
+static tdump_princ_fn princ_lockout;
+static tdump_princ_fn princ_meta;
+static tdump_princ_fn princ_stringattrs;
+static tdump_princ_fn princ_tktpolicy;
+
+static char * const keydata_fields[] = {
+ "name", "keyindex", "kvno", "enctype", "key", "salttype", "salt", NULL
+};
+static char * const keyinfo_fields[] = {
+ "name", "keyindex", "kvno", "enctype", "salttype", "salt", NULL
+};
+static char * const princ_flags_fields[] = {
+ "name", "flag", "value", NULL
+};
+static char * const princ_lockout_fields[] = {
+ "name", "last_success", "last_failed", "fail_count", NULL
+};
+static char * const princ_meta_fields[] = {
+ "name", "modby", "modtime", "lastpwd", "policy", "mkvno", "hist_kvno", NULL
+};
+static char * const princ_stringattrs_fields[] = {
+ "name", "key", "value", NULL
+};
+static char * const princ_tktpolicy_fields[] = {
+ "name", "expiration", "pw_expiration", "max_life", "max_renew_life", NULL
+};
+
+/* Lookup table for tabdump record types */
+static struct tdtype tdtypes[] = {
+ {"keydata", keydata_fields, keydata, NULL},
+ {"keyinfo", keyinfo_fields, keyinfo, NULL},
+ {"princ_flags", princ_flags_fields, princ_flags, NULL},
+ {"princ_lockout", princ_lockout_fields, princ_lockout, NULL},
+ {"princ_meta", princ_meta_fields, princ_meta, NULL},
+ {"princ_stringattrs", princ_stringattrs_fields, princ_stringattrs, NULL},
+ {"princ_tktpolicy", princ_tktpolicy_fields, princ_tktpolicy, NULL},
+};
+#define NTDTYPES (sizeof(tdtypes)/sizeof(tdtypes[0]))
+
+/* State to pass to KDB iterator */
+struct rec_args {
+ FILE *f;
+ struct tdtype *tdtype;
+ struct rechandle *rh;
+ struct tdopts *opts;
+};
+
+/* Decode the KADM_DATA from a DB entry.*/
+static int
+get_adb(krb5_db_entry *dbe, osa_princ_ent_rec *adb)
+{
+ XDR xdrs;
+ int success;
+ krb5_tl_data tl_data;
+ krb5_error_code ret;
+
+ memset(adb, 0, sizeof(*adb));
+ tl_data.tl_data_type = KRB5_TL_KADM_DATA;
+ ret = krb5_dbe_lookup_tl_data(util_context, dbe, &tl_data);
+ if (ret != 0 || tl_data.tl_data_length == 0)
+ return 0;
+ xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents,
+ tl_data.tl_data_length, XDR_DECODE);
+ success = xdr_osa_princ_ent_rec(&xdrs, adb);
+ xdr_destroy(&xdrs);
+ return success;
+}
+
+/* Write a date field as an ISO 8601 UTC date/time representation. */
+static int
+write_date_iso(struct rec_args *args, krb5_timestamp when)
+{
+ char buf[64];
+ time_t t;
+ struct tm *tm = NULL;
+ struct rechandle *h = args->rh;
+
+ t = when;
+ tm = gmtime(&t);
+ if (tm == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm) == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (writefield(h, "%s", buf) < 0)
+ return -1;
+ return 0;
+}
+
+/* Write a date field, optionally as a decimal POSIX timestamp. */
+static int
+write_date(struct rec_args *args, krb5_timestamp when)
+{
+ struct tdopts *opts = args->opts;
+ struct rechandle *h = args->rh;
+
+ if (opts->numeric)
+ return writefield(h, "%d", when);
+
+ return write_date_iso(args, when);
+}
+
+/* Write an enctype field, optionally as decimal. */
+static krb5_error_code
+write_enctype(struct rec_args *args, krb5_int16 etype)
+{
+ char buf[256];
+ krb5_error_code ret;
+ struct rechandle *h = args->rh;
+ struct tdopts *opts = args->opts;
+
+ if (!opts->numeric) {
+ ret = krb5_enctype_to_name(etype, 0, buf, sizeof(buf));
+ if (ret == 0) {
+ if (writefield(h, "%s", buf) < 0)
+ return errno;
+ return ret;
+ }
+ }
+ /* decimal if requested, or if conversion failed */
+ if (writefield(h, "%d", etype) < 0)
+ return errno;
+ return 0;
+}
+
+/* Write a salttype field, optionally as decimal. */
+static krb5_error_code
+write_salttype(struct rec_args *args, krb5_int16 salttype)
+{
+ char buf[256];
+ krb5_error_code ret;
+ struct rechandle *h = args->rh;
+ struct tdopts *opts = args->opts;
+
+ if (!opts->numeric) {
+ ret = krb5_salttype_to_string(salttype, buf, sizeof(buf));
+ if (ret == 0) {
+ if (writefield(h, "%s", buf) < 0)
+ return errno;
+ return ret;
+ }
+ }
+ /* decimal if requested, or if conversion failed */
+ if (writefield(h, "%d", salttype) < 0)
+ return errno;
+ return 0;
+}
+
+/*
+ * Write a field of bytes from krb5_data as a hexadecimal string. Write empty
+ * strings as "-1" unless requested.
+ */
+static int
+write_data(struct rec_args *args, krb5_data *data)
+{
+ int ret;
+ char *p;
+ size_t i;
+ struct k5buf buf;
+ struct rechandle *h = args->rh;
+ struct tdopts *opts = args->opts;
+
+ if (data->length == 0 && !opts->emptyhex_empty) {
+ if (writefield(h, "-1") < 0)
+ return -1;
+ return 0;
+ }
+ k5_buf_init_dynamic(&buf);
+ p = data->data;
+ for (i = 0; i < data->length; i++)
+ k5_buf_add_fmt(&buf, "%02x", (unsigned char)p[i]);
+
+ if (buf.data == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ret = writefield(h, "%s", (char *)buf.data);
+ k5_buf_free(&buf);
+ return ret;
+}
+
+/* Write a single record of a keydata/keyinfo key set. */
+static krb5_error_code
+keyinfo_rec(struct rec_args *args, const char *name, int i, krb5_key_data *kd,
+ int dumpkeys)
+{
+ int ret;
+ krb5_data data;
+ struct rechandle *h = args->rh;
+
+ if (startrec(h) < 0)
+ return errno;
+ if (writefield(h, "%s", name) < 0)
+ return errno;
+ if (writefield(h, "%d", i) < 0)
+ return errno;
+ if (writefield(h, "%d", kd->key_data_kvno) < 0)
+ return errno;
+ if (write_enctype(args, kd->key_data_type[0]) < 0)
+ return errno;
+ if (dumpkeys) {
+ data.length = kd->key_data_length[0];
+ data.data = (void *)kd->key_data_contents[0];
+ if (write_data(args, &data) < 0)
+ return errno;
+ }
+ ret = write_salttype(args, kd->key_data_type[1]);
+ if (ret)
+ return ret;
+ data.length = kd->key_data_length[1];
+ data.data = (void *)kd->key_data_contents[1];
+ if (write_data(args, &data) < 0)
+ return errno;
+ if (endrec(h) < 0)
+ return errno;
+ return 0;
+}
+
+/* Write out a principal's key set, optionally including actual key data. */
+static krb5_error_code
+keyinfo_common(struct rec_args *args, const char *name, krb5_db_entry *entry,
+ int dumpkeys)
+{
+ krb5_error_code ret;
+ krb5_key_data kd;
+ int i;
+
+ for (i = 0; i < entry->n_key_data; i++) {
+ kd = entry->key_data[i];
+ /* missing salt data -> normal salt */
+ if (kd.key_data_ver == 1) {
+ kd.key_data_ver = 2;
+ kd.key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL;
+ kd.key_data_length[1] = 0;
+ kd.key_data_contents[1] = NULL;
+ }
+ ret = keyinfo_rec(args, name, i, &kd, dumpkeys);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* Write a principal's key data. */
+static krb5_error_code
+keydata(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ return keyinfo_common(args, name, dbe, 1);
+}
+
+/* Write a principal's key info (suppressing actual key data). */
+static krb5_error_code
+keyinfo(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ return keyinfo_common(args, name, dbe, 0);
+}
+
+/* Write a record corresponding to a single principal flag setting. */
+static krb5_error_code
+princflag_rec(struct rechandle *h, const char *name, const char *flagname,
+ int set)
+{
+ if (startrec(h) < 0)
+ return errno;
+ if (writefield(h, "%s", name) < 0)
+ return errno;
+ if (writefield(h, "%s", flagname) < 0)
+ return errno;
+ if (writefield(h, "%d", set) < 0)
+ return errno;
+ if (endrec(h) < 0)
+ return errno;
+ return 0;
+}
+
+/* Write a principal's flag settings. */
+static krb5_error_code
+princ_flags(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ int i;
+ char *s = NULL;
+ krb5_flags flags = dbe->attributes;
+ krb5_error_code ret;
+ struct tdopts *opts = args->opts;
+ struct rechandle *h = args->rh;
+
+ for (i = 0; i < 32; i++) {
+ if (opts->numeric) {
+ if (asprintf(&s, "0x%08lx", 1UL << i) == -1)
+ return ENOMEM;
+ } else {
+ ret = krb5_flagnum_to_string(i, &s);
+ if (ret)
+ return ret;
+ /* Don't print unknown flags if they're not set and numeric output
+ * isn't requested. */
+ if (!(flags & (1UL << i)) && strncmp(s, "0x", 2) == 0) {
+ free(s);
+ continue;
+ }
+ }
+ ret = princflag_rec(h, name, s, ((flags & (1UL << i)) != 0));
+ free(s);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+/* Write a principal's lockout data. */
+static krb5_error_code
+princ_lockout(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ struct rechandle *h = args->rh;
+
+ if (startrec(h) < 0)
+ return errno;
+ if (writefield(h, "%s", name) < 0)
+ return errno;
+ if (write_date(args, dbe->last_success) < 0)
+ return errno;
+ if (write_date(args, dbe->last_failed) < 0)
+ return errno;
+ if (writefield(h, "%d", dbe->fail_auth_count) < 0)
+ return errno;
+ if (endrec(h) < 0)
+ return errno;
+ return 0;
+}
+
+/* Write a principal's metadata. */
+static krb5_error_code
+princ_meta(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ int got_adb = 0;
+ char *modby;
+ krb5_kvno mkvno;
+ const char *policy;
+ krb5_principal mod_princ = NULL;
+ krb5_timestamp mod_time, last_pwd;
+ krb5_error_code ret;
+ osa_princ_ent_rec adb;
+ struct rechandle *h = args->rh;
+
+ memset(&adb, 0, sizeof(adb));
+ if (startrec(h) < 0)
+ return errno;
+ if (writefield(h, "%s", name) < 0)
+ return errno;
+
+ ret = krb5_dbe_lookup_last_pwd_change(util_context, dbe, &last_pwd);
+ if (ret)
+ return ret;
+ ret = krb5_dbe_get_mkvno(util_context, dbe, &mkvno);
+ if (ret)
+ return ret;
+
+ ret = krb5_dbe_lookup_mod_princ_data(util_context, dbe, &mod_time,
+ &mod_princ);
+ if (ret)
+ return ret;
+ ret = krb5_unparse_name(util_context, mod_princ, &modby);
+ krb5_free_principal(util_context, mod_princ);
+ if (ret)
+ return ret;
+ ret = writefield(h, "%s", modby);
+ krb5_free_unparsed_name(util_context, modby);
+ if (ret < 0)
+ return errno;
+
+ if (write_date(args, mod_time) < 0)
+ return errno;
+ if (write_date(args, last_pwd) < 0)
+ return errno;
+
+ got_adb = get_adb(dbe, &adb);
+ if (got_adb && adb.policy != NULL)
+ policy = adb.policy;
+ else
+ policy = "";
+ ret = writefield(h, "%s", policy);
+ if (ret < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ if (writefield(h, "%d", mkvno) < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ if (writefield(h, "%d", adb.admin_history_kvno) < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ if (endrec(h) < 0)
+ ret = errno;
+ else
+ ret = 0;
+
+cleanup:
+ kdb_free_entry(NULL, NULL, &adb);
+ return ret;
+}
+
+/* Write a principal's string attributes. */
+static krb5_error_code
+princ_stringattrs(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ int i, nattrs;
+ krb5_error_code ret;
+ krb5_string_attr *attrs;
+ struct rechandle *h = args->rh;
+
+ ret = krb5_dbe_get_strings(util_context, dbe, &attrs, &nattrs);
+ if (ret)
+ return ret;
+ for (i = 0; i < nattrs; i++) {
+ if (startrec(h) < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ if (writefield(h, "%s", name) < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ if (writefield(h, "%s", attrs[i].key) < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ if (writefield(h, "%s", attrs[i].value) < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ if (endrec(h) < 0) {
+ ret = errno;
+ goto cleanup;
+ }
+ }
+cleanup:
+ krb5_dbe_free_strings(util_context, attrs, nattrs);
+ return ret;
+}
+
+/* Write a principal's ticket policy. */
+static krb5_error_code
+princ_tktpolicy(struct rec_args *args, const char *name, krb5_db_entry *dbe)
+{
+ struct rechandle *h = args->rh;
+
+ if (startrec(h) < 0)
+ return errno;
+ if (writefield(h, "%s", name) < 0)
+ return errno;
+ if (write_date(args, dbe->expiration) < 0)
+ return errno;
+ if (write_date(args, dbe->pw_expiration) < 0)
+ return errno;
+ if (writefield(h, "%d", dbe->max_life) < 0)
+ return errno;
+ if (writefield(h, "%d", dbe->max_renewable_life) < 0)
+ return errno;
+ if (endrec(h) < 0)
+ return errno;
+ return 0;
+}
+
+/* Iterator function for krb5_db_iterate() */
+static krb5_error_code
+tditer(void *ptr, krb5_db_entry *entry)
+{
+ krb5_error_code ret;
+ struct rec_args *args = ptr;
+ char *name;
+
+ ret = krb5_unparse_name(util_context, entry->princ, &name);
+ if (ret) {
+ com_err(progname, ret, _("while unparsing principal name"));
+ return ret;
+ }
+ ret = args->tdtype->princ_fn(args, name, entry);
+ krb5_free_unparsed_name(util_context, name);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+/* Set up state structure for the iterator. */
+static krb5_error_code
+setup_args(struct rec_args *args, struct tdtype *tdtype,
+ struct tdopts *opts)
+{
+ FILE *f = NULL;
+ const char *rectype = NULL;
+ struct rechandle *rh;
+
+ args->tdtype = tdtype;
+ args->opts = opts;
+ if (opts->fname != NULL && strcmp(opts->fname, "-") != 0) {
+ f = fopen(opts->fname, "w");
+ if (f == NULL) {
+ com_err(progname, errno, _("opening %s for writing"),
+ opts->fname);
+ return errno;
+ }
+ args->f = f;
+ } else {
+ f = stdout;
+ args->f = NULL;
+ }
+ if (opts->writerectype)
+ rectype = tdtype->rectype;
+ if (opts->csv)
+ rh = rechandle_csv(f, rectype);
+ else
+ rh = rechandle_tabsep(f, rectype);
+ if (rh == NULL)
+ return ENOMEM;
+ args->rh = rh;
+ if (!opts->omitheader && writeheader(rh, tdtype->fieldnames) < 0)
+ return errno;
+ return 0;
+}
+
+/* Clean up the state structure. */
+static void
+cleanup_args(struct rec_args *args)
+{
+ rechandle_free(args->rh);
+ if (args->f != NULL)
+ fclose(args->f);
+}
+
+/*
+ * Usaage is:
+ * tabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype
+ */
+void
+tabdump(int argc, char **argv)
+{
+ int ch;
+ size_t i;
+ const char *rectype;
+ struct rec_args args;
+ struct tdopts opts;
+ krb5_error_code ret;
+
+ memset(&opts, 0, sizeof(opts));
+ memset(&args, 0, sizeof(args));
+ optind = 1;
+ while ((ch = getopt(argc, argv, "Hceno:")) != -1) {
+ switch (ch) {
+ case 'H':
+ opts.omitheader = 1;
+ break;
+ case 'c':
+ opts.csv = 1;
+ break;
+ case 'e':
+ opts.emptyhex_empty = 1;
+ break;
+ case 'n':
+ opts.numeric = 1;
+ break;
+ case 'o':
+ opts.fname = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ if (argc - optind < 1)
+ usage();
+ rectype = argv[optind];
+ for (i = 0; i < NTDTYPES; i++) {
+ if (strcmp(rectype, tdtypes[i].rectype) == 0) {
+ setup_args(&args, &tdtypes[i], &opts);
+ break;
+ }
+ }
+ if (i >= NTDTYPES)
+ usage();
+ ret = krb5_db_iterate(util_context, NULL, tditer, &args, 0);
+ cleanup_args(&args);
+ if (ret) {
+ com_err(progname, ret, _("performing tabular dump"));
+ exit_status++;
+ }
+}
diff --git a/src/kadmin/dbutil/tdumputil.c b/src/kadmin/dbutil/tdumputil.c
new file mode 100644
index 000000000000..2c14d5670e08
--- /dev/null
+++ b/src/kadmin/dbutil/tdumputil.c
@@ -0,0 +1,266 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/tdumputil.c - utilities for tab-separated, etc. files */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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.
+ */
+
+#include "k5-int.h"
+#include "k5-platform.h" /* for vasprintf */
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "tdumputil.h"
+
+/*
+ * Structure describing flavor of a tabular output format.
+ *
+ * fieldsep is the field separator
+ *
+ * recordsep is the record/line separator
+ *
+ * quotechar begins and ends a quoted field. If an instance of quotechar
+ * occurs within a quoted field value, it is doubled.
+ *
+ * Values are only quoted if they contain fieldsep, recordsep, or quotechar.
+ */
+struct flavor {
+ int fieldsep; /* field separator */
+ int recordsep; /* record separator */
+ int quotechar; /* quote character */
+};
+
+struct rechandle {
+ FILE *fh;
+ const char *rectype;
+ int do_sep;
+ struct flavor flavor;
+};
+
+static const struct flavor tabsep = {
+ '\t', /* fieldsep */
+ '\n', /* recordsep */
+ '\0' /* quotechar */
+};
+
+static const struct flavor csv = {
+ ',', /* fieldsep */
+ '\n', /* recordsep */
+ '"' /* quotechar */
+};
+
+/*
+ * Double any quote characters present in a quoted field.
+ */
+static char *
+qquote(struct flavor *fl, const char *s)
+{
+ const char *sp;
+ struct k5buf buf;
+
+ k5_buf_init_dynamic(&buf);
+ for (sp = s; *sp != '\0'; sp++) {
+ k5_buf_add_len(&buf, sp, 1);
+ if (*sp == fl->quotechar)
+ k5_buf_add_len(&buf, sp, 1);
+ }
+ return buf.data;
+}
+
+/*
+ * Write an optionally quoted field.
+ */
+static int
+writequoted(struct rechandle *h, const char *fmt, va_list ap)
+{
+ int doquote = 0, ret;
+ char *s = NULL, *qs = NULL;
+ struct flavor fl = h->flavor;
+
+ assert(fl.quotechar != '\0');
+ ret = vasprintf(&s, fmt, ap);
+ if (ret < 0)
+ return ret;
+ if (strchr(s, fl.fieldsep) != NULL)
+ doquote = 1;
+ if (strchr(s, fl.recordsep) != NULL)
+ doquote = 1;
+ if (strchr(s, fl.quotechar) != NULL)
+ doquote = 1;
+
+ if (doquote) {
+ qs = qquote(&fl, s);
+ if (qs == NULL) {
+ ret = -1;
+ goto cleanup;
+ }
+ ret = fprintf(h->fh, "%c%s%c", fl.quotechar, qs, fl.quotechar);
+ } else {
+ ret = fprintf(h->fh, "%s", s);
+ }
+cleanup:
+ free(s);
+ free(qs);
+ return ret;
+}
+
+/*
+ * Return a rechandle with the requested file handle and rectype.
+ *
+ * rectype must be a valid pointer for the entire lifetime of the rechandle (or
+ * null)
+ */
+static struct rechandle *
+rechandle_common(FILE *fh, const char *rectype)
+{
+ struct rechandle *h = calloc(1, sizeof(*h));
+
+ if (h == NULL)
+ return NULL;
+ h->fh = fh;
+ h->rectype = rectype;
+ h->do_sep = 0;
+ return h;
+}
+
+/*
+ * Return a rechandle for tab-separated output.
+ */
+struct rechandle *
+rechandle_tabsep(FILE *fh, const char *rectype)
+{
+ struct rechandle *h = rechandle_common(fh, rectype);
+
+ if (h == NULL)
+ return NULL;
+ h->flavor = tabsep;
+ return h;
+}
+
+/*
+ * Return a rechandle for CSV output.
+ */
+struct rechandle *
+rechandle_csv(FILE *fh, const char *rectype)
+{
+ struct rechandle *h = rechandle_common(fh, rectype);
+
+ if (h == NULL)
+ return NULL;
+ h->flavor = csv;
+ return h;
+}
+
+/*
+ * Free a rechandle.
+ */
+void
+rechandle_free(struct rechandle *h)
+{
+ free(h);
+}
+
+/*
+ * Start a record. This includes writing a record type prefix (rectype) if
+ * specified.
+ */
+int
+startrec(struct rechandle *h)
+{
+ if (h->rectype == NULL) {
+ h->do_sep = 0;
+ return 0;
+ }
+ h->do_sep = 1;
+ return fputs(h->rectype, h->fh);
+}
+
+/*
+ * Write a single field of a record. This includes writing a separator
+ * character, if appropriate.
+ */
+int
+writefield(struct rechandle *h, const char *fmt, ...)
+{
+ int ret = 0;
+ va_list ap;
+ struct flavor fl = h->flavor;
+
+ if (h->do_sep) {
+ ret = fputc(fl.fieldsep, h->fh);
+ if (ret < 0)
+ return ret;
+ }
+ h->do_sep = 1;
+ va_start(ap, fmt);
+ if (fl.quotechar == '\0')
+ ret = vfprintf(h->fh, fmt, ap);
+ else
+ ret = writequoted(h, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+/*
+ * Finish a record (line).
+ */
+int
+endrec(struct rechandle *h)
+{
+ int ret = 0;
+ struct flavor fl = h->flavor;
+
+ ret = fputc(fl.recordsep, h->fh);
+ h->do_sep = 0;
+ return ret;
+}
+
+/*
+ * Write a header line if h->rectype is null. (If rectype is set, it will be
+ * prefixed to output lines, most likely in a mixed record type output file, so
+ * it doesn't make sense to output a header line in that case.)
+ */
+int
+writeheader(struct rechandle *h, char * const *a)
+{
+ int ret = 0;
+ char * const *p;
+
+ if (h->rectype != NULL)
+ return 0;
+ for (p = a; *p != NULL; p++) {
+ ret = writefield(h, "%s", *p);
+ if (ret < 0)
+ return ret;
+ }
+ ret = endrec(h);
+ return ret;
+}
diff --git a/src/kadmin/dbutil/tdumputil.h b/src/kadmin/dbutil/tdumputil.h
new file mode 100644
index 000000000000..b7437310ce6a
--- /dev/null
+++ b/src/kadmin/dbutil/tdumputil.h
@@ -0,0 +1,51 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kdc/tdumputil.h - utilities for tab-separated, etc. files */
+/*
+ * Copyright (C) 2015 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
+ * COPYRIGHT HOLDER 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.
+ */
+
+#ifndef TDUMPUTIL_H
+#define TDUMPUTIL_H
+
+struct rechandle;
+
+struct rechandle *rechandle_csv(FILE *, const char *);
+struct rechandle *rechandle_tabsep(FILE *, const char *);
+void rechandle_free(struct rechandle *);
+
+int startrec(struct rechandle *);
+int writefield(struct rechandle *, const char *, ...)
+#if !defined(__cplusplus) && (__GNUC__ > 2)
+ __attribute__((__format__(__printf__, 2, 3)))
+#endif
+ ;
+int endrec(struct rechandle *);
+int writeheader(struct rechandle *, char * const *);
+
+#endif /* TDUMPUTIL_H */
diff --git a/src/kadmin/deps b/src/kadmin/deps
new file mode 100644
index 000000000000..2feac3c9d388
--- /dev/null
+++ b/src/kadmin/deps
@@ -0,0 +1 @@
+# No dependencies here.
diff --git a/src/kadmin/kdbkeys/Makefile.in b/src/kadmin/kdbkeys/Makefile.in
new file mode 100644
index 000000000000..996febd8ea59
--- /dev/null
+++ b/src/kadmin/kdbkeys/Makefile.in
@@ -0,0 +1,16 @@
+mydir=.
+BUILDTOP=$(REL)..$(S)..
+
+PROG = kdbkeys
+OBJS = kdbkeys.o
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS)
+
+install:
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG)
+
+clean:
+ $(RM) $(PROG) $(OBJS)
diff --git a/src/kadmin/kdbkeys/do-test.pl b/src/kadmin/kdbkeys/do-test.pl
new file mode 100755
index 000000000000..7acb425f790b
--- /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/ktutil/Makefile.in b/src/kadmin/ktutil/Makefile.in
new file mode 100644
index 000000000000..f8f9b4111c32
--- /dev/null
+++ b/src/kadmin/ktutil/Makefile.in
@@ -0,0 +1,30 @@
+mydir=kadmin$(S)ktutil
+BUILDTOP=$(REL)..$(S)..
+
+OBJS= ktutil.o \
+ ktutil_ct.o \
+ ktutil_funcs.o
+
+SRCS= $(srcdir)/ktutil.c \
+ ktutil_ct.c \
+ $(srcdir)/ktutil_funcs.c
+
+all: ktutil
+
+ktutil: ktutil.o $(OBJS) $(SS_DEPLIB) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o ktutil $(OBJS) $(SS_LIB) $(KRB5_BASE_LIBS)
+
+install:
+ $(INSTALL_PROGRAM) ktutil ${DESTDIR}$(CLIENT_BINDIR)/ktutil
+
+generate-files-mac: ktutil_ct.c
+
+# needed until we run makedepend
+ktutil_ct.c: ktutil_ct.ct
+
+ktutil_ct.o: ktutil_ct.c
+
+clean:
+ $(RM) ktutil_ct.c ktutil
+
+depend: ktutil_ct.c
diff --git a/src/kadmin/ktutil/deps b/src/kadmin/ktutil/deps
new file mode 100644
index 000000000000..4df399924b56
--- /dev/null
+++ b/src/kadmin/ktutil/deps
@@ -0,0 +1,26 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)ktutil.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SS_DEPS) \
+ $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h ktutil.c ktutil.h
+$(OUTPRE)ktutil_ct.$(OBJEXT): $(COM_ERR_DEPS) $(SS_DEPS) \
+ ktutil_ct.c
+$(OUTPRE)ktutil_funcs.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h ktutil.h ktutil_funcs.c
diff --git a/src/kadmin/ktutil/ktutil.c b/src/kadmin/ktutil/ktutil.c
new file mode 100644
index 000000000000..ef16d37a5693
--- /dev/null
+++ b/src/kadmin/ktutil/ktutil.c
@@ -0,0 +1,274 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/ktutil/ktutil.c - SS user interface for ktutil */
+/*
+ * Copyright 1995, 1996, 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include "ktutil.h"
+#include <com_err.h>
+#include <locale.h>
+#include "adm_proto.h"
+#include <ss/ss.h>
+#include <stdio.h>
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+extern ss_request_table ktutil_cmds;
+krb5_context kcontext;
+krb5_kt_list ktlist = NULL;
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ int sci_idx;
+
+ setlocale(LC_ALL, "");
+ retval = krb5_init_context(&kcontext);
+ if (retval) {
+ com_err(argv[0], retval, _("while initializing krb5"));
+ exit(1);
+ }
+ sci_idx = ss_create_invocation("ktutil", "5.0", (char *)NULL,
+ &ktutil_cmds, &retval);
+ if (retval) {
+ ss_perror(sci_idx, retval, _("creating invocation"));
+ exit(1);
+ }
+ retval = ss_listen(sci_idx);
+ ktutil_free_kt_list(kcontext, ktlist);
+ exit(0);
+}
+
+void ktutil_clear_list(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+
+ if (argc != 1) {
+ fprintf(stderr, _("%s: invalid arguments\n"), argv[0]);
+ return;
+ }
+ retval = ktutil_free_kt_list(kcontext, ktlist);
+ if (retval)
+ com_err(argv[0], retval, _("while freeing ktlist"));
+ ktlist = NULL;
+}
+
+void ktutil_read_v5(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+
+ if (argc != 2) {
+ fprintf(stderr, _("%s: must specify keytab to read\n"), argv[0]);
+ return;
+ }
+ retval = ktutil_read_keytab(kcontext, argv[1], &ktlist);
+ if (retval)
+ com_err(argv[0], retval, _("while reading keytab \"%s\""), argv[1]);
+}
+
+void ktutil_read_v4(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+
+ if (argc != 2) {
+ fprintf(stderr, _("%s: must specify the srvtab to read\n"), argv[0]);
+ return;
+ }
+ retval = ktutil_read_srvtab(kcontext, argv[1], &ktlist);
+ if (retval)
+ com_err(argv[0], retval, _("while reading srvtab \"%s\""), argv[1]);
+}
+
+void ktutil_write_v5(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+
+ if (argc != 2) {
+ fprintf(stderr, _("%s: must specify keytab to write\n"), argv[0]);
+ return;
+ }
+ retval = ktutil_write_keytab(kcontext, ktlist, argv[1]);
+ if (retval)
+ com_err(argv[0], retval, _("while writing keytab \"%s\""), argv[1]);
+}
+
+void ktutil_write_v4(argc, argv)
+ int argc;
+ char *argv[];
+{
+ fprintf(stderr, _("%s: writing srvtabs is no longer supported\n"),
+ argv[0]);
+}
+
+void ktutil_add_entry(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ char *princ = NULL;
+ char *enctype = NULL;
+ krb5_kvno kvno = 0;
+ int use_pass = 0, use_key = 0, i;
+
+ for (i = 1; i < argc; i++) {
+ if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-p", 2)) {
+ princ = argv[++i];
+ continue;
+ }
+ if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-k", 2)) {
+ kvno = (krb5_kvno) atoi(argv[++i]);
+ continue;
+ }
+ if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-e", 2)) {
+ enctype = argv[++i];
+ continue;
+ }
+ if ((strlen(argv[i]) == 9) && !strncmp(argv[i], "-password", 9)) {
+ use_pass++;
+ continue;
+ }
+ if ((strlen(argv[i]) == 4) && !strncmp(argv[i], "-key", 4)) {
+ use_key++;
+ continue;
+ }
+ }
+
+ if (argc != 8 || !(princ && kvno && enctype) || (use_pass+use_key != 1)) {
+ fprintf(stderr, _("usage: %s (-key | -password) -p principal "
+ "-k kvno -e enctype\n"), argv[0]);
+ return;
+ }
+
+ retval = ktutil_add(kcontext, &ktlist, princ, kvno, enctype, use_pass);
+ if (retval)
+ com_err(argv[0], retval, _("while adding new entry"));
+}
+
+void ktutil_delete_entry(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+
+ if (argc != 2) {
+ fprintf(stderr, _("%s: must specify entry to delete\n"), argv[0]);
+ return;
+ }
+ retval = ktutil_delete(kcontext, &ktlist, atoi(argv[1]));
+ if (retval)
+ com_err(argv[0], retval, _("while deleting entry %d"), atoi(argv[1]));
+}
+
+void ktutil_list(argc, argv)
+ int argc;
+ char *argv[];
+{
+ krb5_error_code retval;
+ krb5_kt_list lp;
+ int show_time = 0, show_keys = 0, show_enctype = 0;
+ int i;
+ unsigned int j;
+ char *pname;
+
+ for (i = 1; i < argc; i++) {
+ if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-t", 2)) {
+ show_time++;
+ continue;
+ }
+ if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-k", 2)) {
+ show_keys++;
+ continue;
+ }
+ if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-e", 2)) {
+ show_enctype++;
+ continue;
+ }
+
+ fprintf(stderr, _("%s: usage: %s [-t] [-k] [-e]\n"), argv[0], argv[0]);
+ return;
+ }
+ /* XXX Translating would disturb table alignment; skip for now. */
+ if (show_time) {
+ printf("slot KVNO Timestamp Principal\n");
+ printf("---- ---- ----------------- ---------------------------------------------------\n");
+ } else {
+ printf("slot KVNO Principal\n");
+ printf("---- ---- ---------------------------------------------------------------------\n");
+ }
+ for (i = 1, lp = ktlist; lp; i++, lp = lp->next) {
+ retval = krb5_unparse_name(kcontext, lp->entry->principal, &pname);
+ if (retval) {
+ com_err(argv[0], retval, "while unparsing principal name");
+ return;
+ }
+ printf("%4d %4d ", i, lp->entry->vno);
+ if (show_time) {
+ char fmtbuf[18];
+ char fill;
+ time_t tstamp;
+
+ tstamp = lp->entry->timestamp;
+ (void) localtime(&tstamp);
+ lp->entry->timestamp = tstamp;
+ fill = ' ';
+ if (!krb5_timestamp_to_sfstring((krb5_timestamp)lp->entry->
+ timestamp,
+ fmtbuf,
+ sizeof(fmtbuf),
+ &fill))
+ printf("%s ", fmtbuf);
+ }
+ printf("%40s", pname);
+ if (show_enctype) {
+ static char buf[256];
+ if ((retval = krb5_enctype_to_name(lp->entry->key.enctype, FALSE,
+ buf, sizeof(buf)))) {
+ com_err(argv[0], retval,
+ _("While converting enctype to string"));
+ return;
+ }
+ printf(" (%s) ", buf);
+ }
+
+ if (show_keys) {
+ printf(" (0x");
+ for (j = 0; j < lp->entry->key.length; j++)
+ printf("%02x", lp->entry->key.contents[j]);
+ printf(")");
+ }
+ printf("\n");
+ free(pname);
+ }
+}
diff --git a/src/kadmin/ktutil/ktutil.h b/src/kadmin/ktutil/ktutil.h
new file mode 100644
index 000000000000..c4839ff12aa8
--- /dev/null
+++ b/src/kadmin/ktutil/ktutil.h
@@ -0,0 +1,69 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/ktutil/ktutil.h */
+/*
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+typedef struct _krb5_kt_list {
+ struct _krb5_kt_list *next;
+ krb5_keytab_entry *entry;
+} *krb5_kt_list;
+
+krb5_error_code ktutil_free_kt_list (krb5_context, krb5_kt_list);
+
+krb5_error_code ktutil_delete (krb5_context, krb5_kt_list *, int);
+
+krb5_error_code ktutil_add (krb5_context,
+ krb5_kt_list *,
+ char *,
+ krb5_kvno,
+ char *,
+ int);
+
+krb5_error_code ktutil_read_keytab (krb5_context,
+ char *,
+ krb5_kt_list *);
+
+krb5_error_code ktutil_write_keytab (krb5_context,
+ krb5_kt_list,
+ char *);
+
+krb5_error_code ktutil_read_srvtab (krb5_context,
+ char *,
+ krb5_kt_list *);
+
+void ktutil_add_entry (int, char *[]);
+
+void ktutil_clear_list (int, char *[]);
+
+void ktutil_read_v5 (int, char *[]);
+
+void ktutil_read_v4 (int, char *[]);
+
+void ktutil_write_v5 (int, char *[]);
+
+void ktutil_write_v4 (int, char *[]);
+
+void ktutil_delete_entry (int, char *[]);
+
+void ktutil_list (int, char *[]);
diff --git a/src/kadmin/ktutil/ktutil_ct.ct b/src/kadmin/ktutil/ktutil_ct.ct
new file mode 100644
index 000000000000..0c7ccb6894a7
--- /dev/null
+++ b/src/kadmin/ktutil/ktutil_ct.ct
@@ -0,0 +1,59 @@
+# Copyright 1995 by the Massachusetts Institute of Technology.
+# All Rights Reserved.
+#
+# Export of this software from the United States of America may
+# require a specific license from the United States Government.
+# It is the responsibility of any person or organization contemplating
+# export to obtain such a license before exporting.
+#
+# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+# distribute this software and its documentation for any purpose and
+# without fee is hereby granted, provided that the above copyright
+# notice appear in all copies and that both that copyright notice and
+# this permission notice appear in supporting documentation, and that
+# the name of M.I.T. not be used in advertising or publicity pertaining
+# to distribution of the software without specific, written prior
+# permission. Furthermore if you modify this software you must label
+# your software as modified software and not distribute it in such a
+# fashion that it might be confused with the original M.I.T. software.
+# M.I.T. makes no representations about the suitability of
+# this software for any purpose. It is provided "as is" without express
+# or implied warranty.
+#
+#
+# Command table for ktutil
+#
+
+command_table ktutil_cmds;
+
+request ktutil_clear_list, "Clear the current keylist.",
+ clear_list, clear;
+
+request ktutil_read_v5, "Read a krb5 keytab into the current keylist.",
+ read_kt, rkt;
+
+request ktutil_read_v4, "Read a krb4 srvtab into the current keylist.",
+ read_st, rst;
+
+request ktutil_write_v5, "Write the current keylist to a krb5 keytab.",
+ write_kt, wkt;
+
+request ktutil_write_v4, "Write the current keylist to a krb4 srvtab.",
+ write_st, wst;
+
+request ktutil_add_entry, "Add an entry to the current keylist.",
+ add_entry, addent;
+
+request ktutil_delete_entry, "Delete an entry from the current keylist.",
+ delete_entry, delent;
+
+request ktutil_list, "List the current keylist.",
+ list, l;
+
+request ss_list_requests, "List available requests.",
+ list_requests, lr, "?";
+
+request ss_quit, "Exit program.",
+ quit, exit, q;
+
+end;
diff --git a/src/kadmin/ktutil/ktutil_funcs.c b/src/kadmin/ktutil/ktutil_funcs.c
new file mode 100644
index 000000000000..20a348c80582
--- /dev/null
+++ b/src/kadmin/ktutil/ktutil_funcs.c
@@ -0,0 +1,353 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/ktutil/ktutil_funcs.c */
+/*
+ *(C) Copyright 1995, 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. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * Utility functions for ktutil.
+ */
+
+#include "k5-int.h"
+#include "ktutil.h"
+#include <string.h>
+#include <ctype.h>
+
+/*
+ * Free a kt_list
+ */
+krb5_error_code ktutil_free_kt_list(context, list)
+ krb5_context context;
+ krb5_kt_list list;
+{
+ krb5_kt_list lp, prev;
+ krb5_error_code retval = 0;
+
+ for (lp = list; lp;) {
+ retval = krb5_kt_free_entry(context, lp->entry);
+ free(lp->entry);
+ if (retval)
+ break;
+ prev = lp;
+ lp = lp->next;
+ free(prev);
+ }
+ return retval;
+}
+
+/*
+ * Delete a numbered entry in a kt_list. Takes a pointer to a kt_list
+ * in case head gets deleted.
+ */
+krb5_error_code ktutil_delete(context, list, idx)
+ krb5_context context;
+ krb5_kt_list *list;
+ int idx;
+{
+ krb5_kt_list lp, prev;
+ int i;
+
+ for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) {
+ if (i == idx) {
+ if (i == 1)
+ *list = lp->next;
+ else
+ prev->next = lp->next;
+ lp->next = NULL;
+ return ktutil_free_kt_list(context, lp);
+ }
+ }
+ return EINVAL;
+}
+
+/*
+ * Create a new keytab entry and add it to the keytab list.
+ * Based on the value of use_pass, either prompt the user for a
+ * password or key. If the keytab list is NULL, allocate a new
+ * one first.
+ */
+krb5_error_code ktutil_add(context, list, princ_str, kvno,
+ enctype_str, use_pass)
+ krb5_context context;
+ krb5_kt_list *list;
+ char *princ_str;
+ krb5_kvno kvno;
+ char *enctype_str;
+ int use_pass;
+{
+ krb5_keytab_entry *entry;
+ krb5_kt_list lp = NULL, prev = NULL;
+ krb5_principal princ;
+ krb5_enctype enctype;
+ krb5_timestamp now;
+ krb5_error_code retval;
+ krb5_data password, salt;
+ krb5_keyblock key;
+ char buf[BUFSIZ];
+ char promptstr[1024];
+
+ char *cp;
+ int i, tmp;
+ unsigned int pwsize = BUFSIZ;
+
+ retval = krb5_parse_name(context, princ_str, &princ);
+ if (retval)
+ return retval;
+ /* now unparse in order to get the default realm appended
+ to princ_str, if no realm was specified */
+ retval = krb5_unparse_name(context, princ, &princ_str);
+ if (retval)
+ return retval;
+ retval = krb5_string_to_enctype(enctype_str, &enctype);
+ if (retval)
+ return KRB5_BAD_ENCTYPE;
+ retval = krb5_timeofday(context, &now);
+ if (retval)
+ return retval;
+
+ if (*list) {
+ /* point lp at the tail of the list */
+ for (lp = *list; lp->next; lp = lp->next);
+ }
+ entry = (krb5_keytab_entry *) malloc(sizeof(krb5_keytab_entry));
+ if (!entry) {
+ return ENOMEM;
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ if (!lp) { /* if list is empty, start one */
+ lp = (krb5_kt_list) malloc(sizeof(*lp));
+ if (!lp) {
+ return ENOMEM;
+ }
+ } else {
+ lp->next = (krb5_kt_list) malloc(sizeof(*lp));
+ if (!lp->next) {
+ return ENOMEM;
+ }
+ prev = lp;
+ lp = lp->next;
+ }
+ lp->next = NULL;
+ lp->entry = entry;
+
+ if (use_pass) {
+ password.length = pwsize;
+ password.data = (char *) malloc(pwsize);
+ if (!password.data) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ snprintf(promptstr, sizeof(promptstr), _("Password for %.1000s"),
+ princ_str);
+ retval = krb5_read_password(context, promptstr, NULL, password.data,
+ &password.length);
+ if (retval)
+ goto cleanup;
+ retval = krb5_principal2salt(context, princ, &salt);
+ if (retval)
+ goto cleanup;
+ retval = krb5_c_string_to_key(context, enctype, &password,
+ &salt, &key);
+ if (retval)
+ goto cleanup;
+ memset(password.data, 0, password.length);
+ password.length = 0;
+ lp->entry->key = key;
+ } else {
+ printf(_("Key for %s (hex): "), princ_str);
+ fgets(buf, BUFSIZ, stdin);
+ /*
+ * We need to get rid of the trailing '\n' from fgets.
+ * If we have an even number of hex digits (as we should),
+ * write a '\0' over the '\n'. If for some reason we have
+ * an odd number of hex digits, force an even number of hex
+ * digits by writing a '0' into the last position (the string
+ * will still be null-terminated).
+ */
+ buf[strlen(buf) - 1] = strlen(buf) % 2 ? '\0' : '0';
+ if (strlen(buf) == 0) {
+ fprintf(stderr, _("addent: Error reading key.\n"));
+ retval = 0;
+ goto cleanup;
+ }
+
+ lp->entry->key.enctype = enctype;
+ lp->entry->key.contents = (krb5_octet *) malloc((strlen(buf) + 1) / 2);
+ if (!lp->entry->key.contents) {
+ retval = ENOMEM;
+ goto cleanup;
+ }
+
+ i = 0;
+ for (cp = buf; *cp; cp += 2) {
+ if (!isxdigit((int) cp[0]) || !isxdigit((int) cp[1])) {
+ fprintf(stderr, _("addent: Illegal character in key.\n"));
+ retval = 0;
+ goto cleanup;
+ }
+ sscanf(cp, "%02x", &tmp);
+ lp->entry->key.contents[i++] = (krb5_octet) tmp;
+ }
+ lp->entry->key.length = i;
+ }
+ lp->entry->principal = princ;
+ lp->entry->vno = kvno;
+ lp->entry->timestamp = now;
+
+ if (!*list)
+ *list = lp;
+
+ return 0;
+
+cleanup:
+ if (prev)
+ prev->next = NULL;
+ ktutil_free_kt_list(context, lp);
+ return retval;
+}
+
+/*
+ * Read in a keytab and append it to list. If list starts as NULL,
+ * allocate a new one if necessary.
+ */
+krb5_error_code ktutil_read_keytab(context, name, list)
+ krb5_context context;
+ char *name;
+ krb5_kt_list *list;
+{
+ krb5_kt_list lp = NULL, tail = NULL, back = NULL;
+ krb5_keytab kt;
+ krb5_keytab_entry *entry;
+ krb5_kt_cursor cursor;
+ krb5_error_code retval = 0;
+
+ if (*list) {
+ /* point lp at the tail of the list */
+ for (lp = *list; lp->next; lp = lp->next);
+ back = lp;
+ }
+ retval = krb5_kt_resolve(context, name, &kt);
+ if (retval)
+ return retval;
+ retval = krb5_kt_start_seq_get(context, kt, &cursor);
+ if (retval)
+ goto close_kt;
+ for (;;) {
+ entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry));
+ if (!entry) {
+ retval = ENOMEM;
+ break;
+ }
+ memset(entry, 0, sizeof (*entry));
+ retval = krb5_kt_next_entry(context, kt, entry, &cursor);
+ if (retval)
+ break;
+
+ if (!lp) { /* if list is empty, start one */
+ lp = (krb5_kt_list)malloc(sizeof (*lp));
+ if (!lp) {
+ retval = ENOMEM;
+ break;
+ }
+ } else {
+ lp->next = (krb5_kt_list)malloc(sizeof (*lp));
+ if (!lp->next) {
+ retval = ENOMEM;
+ break;
+ }
+ lp = lp->next;
+ }
+ if (!tail)
+ tail = lp;
+ lp->next = NULL;
+ lp->entry = entry;
+ }
+ if (entry)
+ free(entry);
+ if (retval) {
+ if (retval == KRB5_KT_END)
+ retval = 0;
+ else {
+ ktutil_free_kt_list(context, tail);
+ tail = NULL;
+ if (back)
+ back->next = NULL;
+ }
+ }
+ if (!*list)
+ *list = tail;
+ krb5_kt_end_seq_get(context, kt, &cursor);
+close_kt:
+ krb5_kt_close(context, kt);
+ return retval;
+}
+
+/*
+ * Takes a kt_list and writes it to the named keytab.
+ */
+krb5_error_code ktutil_write_keytab(context, list, name)
+ krb5_context context;
+ krb5_kt_list list;
+ char *name;
+{
+ krb5_kt_list lp;
+ krb5_keytab kt;
+ char ktname[MAXPATHLEN+sizeof("WRFILE:")+1];
+ krb5_error_code retval = 0;
+ int result;
+
+ result = snprintf(ktname, sizeof(ktname), "WRFILE:%s", name);
+ if (SNPRINTF_OVERFLOW(result, sizeof(ktname)))
+ return ENAMETOOLONG;
+ retval = krb5_kt_resolve(context, ktname, &kt);
+ if (retval)
+ return retval;
+ for (lp = list; lp; lp = lp->next) {
+ retval = krb5_kt_add_entry(context, kt, lp->entry);
+ if (retval)
+ break;
+ }
+ krb5_kt_close(context, kt);
+ return retval;
+}
+
+/*
+ * Read in a named krb4 srvtab and append to list. Allocate new list
+ * if needed.
+ */
+krb5_error_code ktutil_read_srvtab(context, name, list)
+ krb5_context context;
+ char *name;
+ krb5_kt_list *list;
+{
+ char *ktname;
+ krb5_error_code result;
+
+ if (asprintf(&ktname, "SRVTAB:%s", name) < 0)
+ return ENOMEM;
+ result = ktutil_read_keytab(context, ktname, list);
+ free(ktname);
+ return result;
+}
diff --git a/src/kadmin/scripts/inst-hdrs.sh b/src/kadmin/scripts/inst-hdrs.sh
new file mode 100755
index 000000000000..242be89e9193
--- /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 000000000000..3a013a4d4e15
--- /dev/null
+++ b/src/kadmin/server/Makefile.in
@@ -0,0 +1,22 @@
+mydir=kadmin$(S)server
+BUILDTOP=$(REL)..$(S)..
+KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS)
+
+LOCALINCLUDES = -I$(top_srcdir)/lib/gssapi/generic \
+ -I$(top_srcdir)/lib/gssapi/krb5 -I$(BUILDTOP)/lib/gssapi/generic \
+ -I$(BUILDTOP)/lib/gssapi/krb5 -I$(top_srcdir)/lib/kadm5/srv
+
+PROG = kadmind
+OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o misc.o ipropd_svc.o
+SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c ipropd_svc.c
+
+all: $(PROG)
+
+$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) $(VERTO_DEPLIB)
+ $(CC_LINK) -o $(PROG) $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS) $(VERTO_LIBS)
+
+install:
+ $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(SERVER_BINDIR)/$(PROG)
+
+clean:
+ $(RM) $(PROG) $(OBJS)
diff --git a/src/kadmin/server/deps b/src/kadmin/server/deps
new file mode 100644
index 000000000000..44311af19af7
--- /dev/null
+++ b/src/kadmin/server/deps
@@ -0,0 +1,135 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)kadm_rpc_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
+ $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_internal.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
+ $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
+ $(top_srcdir)/include/net-server.h $(top_srcdir)/include/port-sockets.h \
+ $(top_srcdir)/include/socket-utils.h kadm_rpc_svc.c \
+ misc.h
+$(OUTPRE)server_stubs.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
+ $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_acl.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/adm_proto.h \
+ $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \
+ $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \
+ $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \
+ $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \
+ $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \
+ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ misc.h server_stubs.c
+$(OUTPRE)ovsec_kadmd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \
+ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_acl.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(BUILDTOP)/lib/gssapi/generic/gssapi_err_generic.h \
+ $(BUILDTOP)/lib/gssapi/krb5/gssapi_err_krb5.h $(COM_ERR_DEPS) \
+ $(VERTO_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_gssapi.h \
+ $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \
+ $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \
+ $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \
+ $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \
+ $(top_srcdir)/include/iprop.h $(top_srcdir)/include/iprop_hdr.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/kdb_kt.h $(top_srcdir)/include/kdb_log.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ $(top_srcdir)/lib/gssapi/generic/gssapiP_generic.h \
+ $(top_srcdir)/lib/gssapi/generic/gssapi_ext.h $(top_srcdir)/lib/gssapi/generic/gssapi_generic.h \
+ $(top_srcdir)/lib/gssapi/krb5/gssapiP_krb5.h $(top_srcdir)/lib/gssapi/krb5/gssapi_krb5.h \
+ misc.h ovsec_kadmd.c
+$(OUTPRE)schpw.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+ $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/adm_proto.h \
+ $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \
+ $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \
+ $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \
+ $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \
+ $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ misc.h schpw.c
+$(OUTPRE)misc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+ $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+ $(BUILDTOP)/include/kadm5/server_acl.h $(BUILDTOP)/include/kadm5/server_internal.h \
+ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
+ $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \
+ $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \
+ $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \
+ $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \
+ $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \
+ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
+ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
+ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
+ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \
+ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
+ misc.c misc.h
+$(OUTPRE)ipropd_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
+ $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/kadm5/admin.h \
+ $(BUILDTOP)/include/kadm5/admin_internal.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/kadm5/kadm_rpc.h \
+ $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/include/osconf.h $(COM_ERR_DEPS) $(VERTO_DEPS) \
+ $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \
+ $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-platform.h \
+ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/kdb_log.h $(top_srcdir)/include/krb5.h \
+ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \
+ $(top_srcdir)/lib/gssapi/krb5/gssapi_krb5.h $(top_srcdir)/lib/kadm5/srv/server_acl.h \
+ ipropd_svc.c misc.h
diff --git a/src/kadmin/server/ipropd_svc.c b/src/kadmin/server/ipropd_svc.c
new file mode 100644
index 000000000000..bce668f4221a
--- /dev/null
+++ b/src/kadmin/server/ipropd_svc.c
@@ -0,0 +1,646 @@
+/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident "@(#)ipropd_svc.c 1.2 04/02/20 SMI" */
+
+
+#include "k5-platform.h"
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/resource.h> /* rlimit */
+#include <syslog.h>
+
+#include <kadm5/admin.h>
+#include <kadm5/kadm_rpc.h>
+#include <kadm5/server_internal.h>
+#include <server_acl.h>
+#include <adm_proto.h>
+#include <string.h>
+#include <gssapi_krb5.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <kdb_log.h>
+#include "misc.h"
+#include "osconf.h"
+
+extern gss_name_t rqst2name(struct svc_req *rqstp);
+
+extern void *global_server_handle;
+extern int nofork;
+extern short l_port;
+extern char *kdb5_util;
+extern char *kprop;
+extern char *dump_file;
+extern char *kprop_port;
+
+static char *reply_ok_str = "UPDATE_OK";
+static char *reply_err_str = "UPDATE_ERROR";
+static char *reply_fr_str = "UPDATE_FULL_RESYNC_NEEDED";
+static char *reply_busy_str = "UPDATE_BUSY";
+static char *reply_nil_str = "UPDATE_NIL";
+static char *reply_perm_str = "UPDATE_PERM_DENIED";
+static char *reply_unknown_str = "<UNKNOWN_CODE>";
+
+#define LOG_UNAUTH _("Unauthorized request: %s, client=%s, service=%s, addr=%s")
+#define LOG_DONE _("Request: %s, %s, %s, client=%s, service=%s, addr=%s")
+
+#ifdef DPRINT
+#undef DPRINT
+#endif
+#ifdef DEBUG
+#define DPRINT(...) \
+ do { \
+ if (nofork) { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ } \
+ } while (0)
+#else
+#define DPRINT(...)
+#endif
+
+static void
+debprret(char *w, update_status_t ret, kdb_sno_t sno)
+{
+ switch (ret) {
+ case UPDATE_OK:
+ printf("%s: end (OK, sno=%u)\n",
+ w, sno);
+ break;
+ case UPDATE_ERROR:
+ printf("%s: end (ERROR)\n", w);
+ break;
+ case UPDATE_FULL_RESYNC_NEEDED:
+ printf("%s: end (FR NEEDED)\n", w);
+ break;
+ case UPDATE_BUSY:
+ printf("%s: end (BUSY)\n", w);
+ break;
+ case UPDATE_NIL:
+ printf("%s: end (NIL)\n", w);
+ break;
+ case UPDATE_PERM_DENIED:
+ printf("%s: end (PERM)\n", w);
+ break;
+ default:
+ printf("%s: end (UNKNOWN return code (%d))\n", w, ret);
+ }
+}
+
+static char *
+replystr(update_status_t ret)
+{
+ switch (ret) {
+ case UPDATE_OK:
+ return (reply_ok_str);
+ case UPDATE_ERROR:
+ return (reply_err_str);
+ case UPDATE_FULL_RESYNC_NEEDED:
+ return (reply_fr_str);
+ case UPDATE_BUSY:
+ return (reply_busy_str);
+ case UPDATE_NIL:
+ return (reply_nil_str);
+ case UPDATE_PERM_DENIED:
+ return (reply_perm_str);
+ default:
+ return (reply_unknown_str);
+ }
+}
+
+/* Returns null on allocation failure.
+ Regardless of success or failure, frees the input buffer. */
+static char *
+buf_to_string(gss_buffer_desc *b)
+{
+ OM_uint32 min_stat;
+ char *s = malloc(b->length+1);
+
+ if (s) {
+ memcpy(s, b->value, b->length);
+ s[b->length] = 0;
+ }
+ (void) gss_release_buffer(&min_stat, b);
+ return s;
+}
+
+kdb_incr_result_t *
+iprop_get_updates_1_svc(kdb_last_t *arg, struct svc_req *rqstp)
+{
+ static kdb_incr_result_t ret;
+ char *whoami = "iprop_get_updates_1";
+ int kret;
+ kadm5_server_handle_t handle = global_server_handle;
+ char *client_name = 0, *service_name = 0;
+ char obuf[256] = {0};
+
+ /* default return code */
+ ret.ret = UPDATE_ERROR;
+
+ DPRINT("%s: start, last_sno=%lu\n", whoami,
+ (unsigned long)arg->last_sno);
+
+ if (!handle) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: server handle is NULL"),
+ whoami);
+ goto out;
+ }
+
+ {
+ gss_buffer_desc client_desc, service_desc;
+
+ if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: setup_gss_names failed"),
+ whoami);
+ goto out;
+ }
+ client_name = buf_to_string(&client_desc);
+ service_name = buf_to_string(&service_desc);
+ if (client_name == NULL || service_name == NULL) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: out of memory recording principal names"),
+ whoami);
+ goto out;
+ }
+ }
+
+ DPRINT("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n", whoami, client_name,
+ service_name);
+
+ if (!kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_IPROP,
+ NULL,
+ NULL)) {
+ ret.ret = UPDATE_PERM_DENIED;
+
+ DPRINT("%s: PERMISSION DENIED: clprinc=`%s'\n\tsvcprinc=`%s'\n",
+ whoami, client_name, service_name);
+
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
+ client_name, service_name,
+ client_addr(rqstp->rq_xprt));
+ goto out;
+ }
+
+ kret = ulog_get_entries(handle->context, arg, &ret);
+
+ if (ret.ret == UPDATE_OK) {
+ (void) snprintf(obuf, sizeof (obuf),
+ _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=%lu"),
+ replystr(ret.ret),
+ (unsigned long)arg->last_sno,
+ (unsigned long)ret.lastentry.last_sno);
+ } else {
+ (void) snprintf(obuf, sizeof (obuf),
+ _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=N/A"),
+ replystr(ret.ret),
+ (unsigned long)arg->last_sno);
+ }
+
+ DPRINT("%s: request %s %s\n\tclprinc=`%s'\n\tsvcprinc=`%s'\n",
+ whoami, obuf,
+ ((kret == 0) ? "success" : error_message(kret)),
+ client_name, service_name);
+
+ krb5_klog_syslog(LOG_NOTICE,
+ _("Request: %s, %s, %s, client=%s, service=%s, addr=%s"),
+ whoami,
+ obuf,
+ ((kret == 0) ? "success" : error_message(kret)),
+ client_name, service_name,
+ client_addr(rqstp->rq_xprt));
+
+out:
+ if (nofork)
+ debprret(whoami, ret.ret, ret.lastentry.last_sno);
+ free(client_name);
+ free(service_name);
+ return (&ret);
+}
+
+
+/*
+ * Given a client princ (foo/fqdn@R), copy (in arg cl) the fqdn substring.
+ * Return arg cl str ptr on success, else NULL.
+ */
+static char *
+getclhoststr(const char *clprinc, char *cl, size_t len)
+{
+ const char *s, *e;
+
+ if ((s = strchr(clprinc, '/')) == NULL || (e = strchr(++s, '@')) == NULL ||
+ (size_t)(e - s) >= len)
+ return NULL;
+ memcpy(cl, s, e - s);
+ cl[e - s] = '\0';
+ return (cl);
+}
+
+static kdb_fullresync_result_t *
+ipropx_resync(uint32_t vers, struct svc_req *rqstp)
+{
+ static kdb_fullresync_result_t ret;
+ char *ubuf = 0;
+ char clhost[NI_MAXHOST] = {0};
+ int pret, fret;
+ FILE *p;
+ kadm5_server_handle_t handle = global_server_handle;
+ OM_uint32 min_stat;
+ gss_name_t name = NULL;
+ char *client_name = NULL, *service_name = NULL;
+ char *whoami = "iprop_full_resync_1";
+
+ /*
+ * vers contains the highest version number the client is
+ * willing to accept. A client can always accept a lower
+ * version: the version number is indicated in the dump
+ * header.
+ */
+
+ /* default return code */
+ ret.ret = UPDATE_ERROR;
+
+ if (!handle) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: server handle is NULL"),
+ whoami);
+ goto out;
+ }
+
+ DPRINT("%s: start\n", whoami);
+
+ {
+ gss_buffer_desc client_desc, service_desc;
+
+ if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
+ DPRINT("%s: setup_gss_names failed\n", whoami);
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: setup_gss_names failed"),
+ whoami);
+ goto out;
+ }
+ client_name = buf_to_string(&client_desc);
+ service_name = buf_to_string(&service_desc);
+ if (client_name == NULL || service_name == NULL) {
+ DPRINT("%s: out of memory\n", whoami);
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: out of memory recording principal names"),
+ whoami);
+ goto out;
+ }
+ }
+
+ DPRINT("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n",
+ whoami, client_name, service_name);
+
+ if (!kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_IPROP,
+ NULL,
+ NULL)) {
+ ret.ret = UPDATE_PERM_DENIED;
+
+ DPRINT("%s: Permission denied\n", whoami);
+ krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
+ client_name, service_name,
+ client_addr(rqstp->rq_xprt));
+ goto out;
+ }
+
+ if (!getclhoststr(client_name, clhost, sizeof (clhost))) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: getclhoststr failed"),
+ whoami);
+ goto out;
+ }
+
+ /*
+ * Note the -i; modified version of kdb5_util dump format
+ * to include sno (serial number). This argument is now
+ * versioned (-i0 for legacy dump format, -i1 for ipropx
+ * version 1 format, etc).
+ *
+ * The -c option ("conditional") causes the dump to dump only if no
+ * dump already exists or that dump is not in ipropx format, or the
+ * sno and timestamp in the header of that dump are outside the
+ * ulog. This allows us to share a single global dump with all
+ * slaves, since it's OK to share an older dump, as long as its sno
+ * and timestamp are in the ulog (then the slaves can get the
+ * subsequent updates very iprop).
+ */
+ if (asprintf(&ubuf, "%s -r %s dump -i%d -c %s", kdb5_util,
+ handle->params.realm, vers, dump_file) < 0) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: cannot construct kdb5 util dump string too long; out of memory"),
+ whoami);
+ goto out;
+ }
+
+ /*
+ * Fork to dump the db and xfer it to the slave.
+ * (the fork allows parent to return quickly and the child
+ * acts like a callback to the slave).
+ */
+ fret = fork();
+ DPRINT("%s: fork=%d (%d)\n", whoami, fret, getpid());
+
+ switch (fret) {
+ case -1: /* error */
+ if (nofork) {
+ perror(whoami);
+ }
+ DPRINT("%s: fork failed\n", whoami);
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: fork failed: %s"),
+ whoami,
+ error_message(errno));
+ goto out;
+
+ case 0: /* child */
+ DPRINT("%s: run `%s' ...\n", whoami, ubuf);
+ (void) signal(SIGCHLD, SIG_DFL);
+ /* run kdb5_util(1M) dump for IProp */
+ p = popen(ubuf, "w");
+ if (p == NULL) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: popen failed: %s"),
+ whoami, error_message(errno));
+ _exit(1);
+ }
+ pret = pclose(p);
+ DPRINT("%s: pclose=%d\n", whoami, pret);
+ if (pret != 0) {
+ /* XXX popen/pclose may not set errno
+ properly, and the error could be from the
+ subprocess anyways. */
+ if (nofork) {
+ perror(whoami);
+ }
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: pclose(popen) failed: %s"),
+ whoami,
+ error_message(errno));
+ _exit(1);
+ }
+
+ if (kprop_port != NULL) {
+ DPRINT("%s: exec `kprop -r %s -f %s -P %s %s' ...\n",
+ whoami, handle->params.realm, dump_file, kprop_port,
+ clhost);
+ pret = execl(kprop, "kprop", "-r", handle->params.realm, "-f",
+ dump_file, "-P", kprop_port, clhost, NULL);
+ } else {
+ DPRINT("%s: exec `kprop -r %s -f %s %s' ...\n",
+ whoami, handle->params.realm, dump_file, clhost);
+ pret = execl(kprop, "kprop", "-r", handle->params.realm, "-f",
+ dump_file, clhost, NULL);
+ }
+ perror(whoami);
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: exec failed: %s"),
+ whoami,
+ error_message(errno));
+ _exit(1);
+
+ default: /* parent */
+ ret.ret = UPDATE_OK;
+ /* not used by slave (sno is retrieved from kdb5_util dump) */
+ ret.lastentry.last_sno = 0;
+ ret.lastentry.last_time.seconds = 0;
+ ret.lastentry.last_time.useconds = 0;
+
+ DPRINT("%s: spawned resync process %d, client=%s, "
+ "service=%s, addr=%s\n", whoami, fret, client_name,
+ service_name, client_addr(rqstp->rq_xprt));
+ krb5_klog_syslog(LOG_NOTICE,
+ _("Request: %s, spawned resync process %d, client=%s, service=%s, addr=%s"),
+ whoami, fret,
+ client_name, service_name,
+ client_addr(rqstp->rq_xprt));
+
+ goto out;
+ }
+
+out:
+ if (nofork)
+ debprret(whoami, ret.ret, 0);
+ free(client_name);
+ free(service_name);
+ if (name)
+ gss_release_name(&min_stat, &name);
+ free(ubuf);
+ return (&ret);
+}
+
+kdb_fullresync_result_t *
+iprop_full_resync_1_svc(/* LINTED */ void *argp, struct svc_req *rqstp)
+{
+ return ipropx_resync(IPROPX_VERSION_0, rqstp);
+}
+
+kdb_fullresync_result_t *
+iprop_full_resync_ext_1_svc(uint32_t *argp, struct svc_req *rqstp)
+{
+ return ipropx_resync(*argp, rqstp);
+}
+
+static int
+check_iprop_rpcsec_auth(struct svc_req *rqstp)
+{
+ /* XXX Since the client can authenticate against any principal in
+ the database, we need to do a sanity check. Only checking for
+ "kiprop" now, but that means theoretically the client could be
+ authenticating to kiprop on some other machine. */
+ /* Code taken from kadm_rpc_svc.c, tweaked. */
+
+ gss_ctx_id_t ctx;
+ krb5_context kctx;
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t name;
+ krb5_principal princ;
+ int ret, success;
+ krb5_data *c1, *realm;
+ gss_buffer_desc gss_str;
+ kadm5_server_handle_t handle;
+ size_t slen;
+ char *sdots;
+
+ success = 0;
+ handle = (kadm5_server_handle_t)global_server_handle;
+
+ if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS)
+ return 0;
+
+ ctx = rqstp->rq_svccred;
+
+ maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name,
+ NULL, NULL, NULL, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ krb5_klog_syslog(LOG_ERR,
+ _("check_rpcsec_auth: failed inquire_context, "
+ "stat=%u"), maj_stat);
+ log_badauth(maj_stat, min_stat, rqstp->rq_xprt, NULL);
+ goto fail_name;
+ }
+
+ kctx = handle->context;
+ ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
+ if (ret == 0)
+ goto fail_name;
+
+ slen = gss_str.length;
+ trunc_name(&slen, &sdots);
+ /*
+ * Since we accept with GSS_C_NO_NAME, the client can authenticate
+ * against the entire kdb. Therefore, ensure that the service
+ * name is something reasonable.
+ */
+ if (krb5_princ_size(kctx, princ) != 2)
+ goto fail_princ;
+
+ c1 = krb5_princ_component(kctx, princ, 0);
+ realm = krb5_princ_realm(kctx, princ);
+ if (strncmp(handle->params.realm, realm->data, realm->length) == 0
+ && strncmp("kiprop", c1->data, c1->length) == 0) {
+ success = 1;
+ }
+
+fail_princ:
+ if (!success) {
+ krb5_klog_syslog(LOG_ERR, _("bad service principal %.*s%s"),
+ (int) slen, (char *) gss_str.value, sdots);
+ }
+ gss_release_buffer(&min_stat, &gss_str);
+ krb5_free_principal(kctx, princ);
+fail_name:
+ gss_release_name(&min_stat, &name);
+ return success;
+}
+
+void
+krb5_iprop_prog_1(struct svc_req *rqstp,
+ register SVCXPRT *transp)
+{
+ union {
+ kdb_last_t iprop_get_updates_1_arg;
+ } argument;
+ char *result;
+ bool_t (*_xdr_argument)(), (*_xdr_result)();
+ char *(*local)(/* union XXX *, struct svc_req * */);
+ char *whoami = "krb5_iprop_prog_1";
+
+ if (!check_iprop_rpcsec_auth(rqstp)) {
+ krb5_klog_syslog(LOG_ERR, _("authentication attempt failed: %s, RPC "
+ "authentication flavor %d"),
+ client_addr(rqstp->rq_xprt),
+ 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 IPROP_GET_UPDATES:
+ _xdr_argument = xdr_kdb_last_t;
+ _xdr_result = xdr_kdb_incr_result_t;
+ local = (char *(*)()) iprop_get_updates_1_svc;
+ break;
+
+ case IPROP_FULL_RESYNC:
+ _xdr_argument = xdr_void;
+ _xdr_result = xdr_kdb_fullresync_result_t;
+ local = (char *(*)()) iprop_full_resync_1_svc;
+ break;
+
+ case IPROP_FULL_RESYNC_EXT:
+ _xdr_argument = xdr_u_int32;
+ _xdr_result = xdr_kdb_fullresync_result_t;
+ local = (char *(*)()) iprop_full_resync_ext_1_svc;
+ break;
+
+ default:
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC unknown request: %d (%s)"),
+ rqstp->rq_proc, whoami);
+ svcerr_noproc(transp);
+ return;
+ }
+ (void) memset(&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC svc_getargs failed (%s)"),
+ whoami);
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+
+ if (_xdr_result && result != NULL &&
+ !svc_sendreply(transp, _xdr_result, result)) {
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC svc_sendreply failed (%s)"),
+ whoami);
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) {
+ krb5_klog_syslog(LOG_ERR,
+ _("RPC svc_freeargs failed (%s)"),
+ whoami);
+
+ exit(1);
+ }
+
+ if (rqstp->rq_proc == IPROP_GET_UPDATES) {
+ /* LINTED */
+ kdb_incr_result_t *r = (kdb_incr_result_t *)result;
+
+ if (r->ret == UPDATE_OK) {
+ ulog_free_entries(r->updates.kdb_ulog_t_val,
+ r->updates.kdb_ulog_t_len);
+ r->updates.kdb_ulog_t_val = NULL;
+ r->updates.kdb_ulog_t_len = 0;
+ }
+ }
+
+}
+
+#if 0
+/*
+ * Get the host base service name for the kiprop principal. Returns
+ * KADM5_OK on success. Caller must free the storage allocated for
+ * host_service_name.
+ */
+kadm5_ret_t
+kiprop_get_adm_host_srv_name(krb5_context context,
+ const char *realm,
+ char **host_service_name)
+{
+ kadm5_ret_t ret;
+ char *name;
+ char *host;
+
+ if (ret = kadm5_get_master(context, realm, &host))
+ return (ret);
+
+ if (asprintf(&name, "%s@%s", KIPROP_SVC_NAME, host) < 0) {
+ free(host);
+ return (ENOMEM);
+ }
+ free(host);
+ *host_service_name = name;
+
+ return (KADM5_OK);
+}
+#endif
diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c
new file mode 100644
index 000000000000..e43ca0d578d8
--- /dev/null
+++ b/src/kadmin/server/kadm_rpc_svc.c
@@ -0,0 +1,370 @@
+/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.
+ *
+ */
+
+#include <k5-int.h>
+#include <gssrpc/rpc.h>
+#include <gssapi/gssapi_krb5.h> /* for gss_nt_krb5_name */
+#include <syslog.h>
+#include <kadm5/kadm_rpc.h>
+#include <krb5.h>
+#include <kadm5/admin.h>
+#include <adm_proto.h>
+#include "misc.h"
+#include "kadm5/server_internal.h"
+
+extern void *global_server_handle;
+
+static int check_rpcsec_auth(struct svc_req *);
+
+/*
+ * 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_2_arg;
+ dprinc_arg delete_principal_2_arg;
+ mprinc_arg modify_principal_2_arg;
+ rprinc_arg rename_principal_2_arg;
+ gprinc_arg get_principal_2_arg;
+ chpass_arg chpass_principal_2_arg;
+ chrand_arg chrand_principal_2_arg;
+ cpol_arg create_policy_2_arg;
+ dpol_arg delete_policy_2_arg;
+ mpol_arg modify_policy_2_arg;
+ gpol_arg get_policy_2_arg;
+ setkey_arg setkey_principal_2_arg;
+ setv4key_arg setv4key_principal_2_arg;
+ cprinc3_arg create_principal3_2_arg;
+ chpass3_arg chpass_principal3_2_arg;
+ chrand3_arg chrand_principal3_2_arg;
+ setkey3_arg setkey_principal3_2_arg;
+ setkey4_arg setkey_principal4_2_arg;
+ getpkeys_arg get_principal_keys_2_arg;
+ } argument;
+ union {
+ generic_ret gen_ret;
+ gprinc_ret get_principal_2_ret;
+ chrand_ret chrand_principal_2_ret;
+ gpol_ret get_policy_2_ret;
+ getprivs_ret get_privs_2_ret;
+ gprincs_ret get_princs_2_ret;
+ gpols_ret get_pols_2_ret;
+ chrand_ret chrand_principal3_2_ret;
+ gstrings_ret get_string_2_ret;
+ getpkeys_ret get_principal_keys_ret;
+ } result;
+ bool_t retval;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ bool_t (*local)();
+
+ if (rqstp->rq_cred.oa_flavor != AUTH_GSSAPI &&
+ !check_rpcsec_auth(rqstp)) {
+ krb5_klog_syslog(LOG_ERR, "Authentication attempt failed: %s, "
+ "RPC authentication flavor %d",
+ client_addr(rqstp->rq_xprt),
+ 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 = (bool_t (*)()) create_principal_2_svc;
+ break;
+
+ case DELETE_PRINCIPAL:
+ xdr_argument = xdr_dprinc_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) delete_principal_2_svc;
+ break;
+
+ case MODIFY_PRINCIPAL:
+ xdr_argument = xdr_mprinc_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) modify_principal_2_svc;
+ break;
+
+ case RENAME_PRINCIPAL:
+ xdr_argument = xdr_rprinc_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) rename_principal_2_svc;
+ break;
+
+ case GET_PRINCIPAL:
+ xdr_argument = xdr_gprinc_arg;
+ xdr_result = xdr_gprinc_ret;
+ local = (bool_t (*)()) get_principal_2_svc;
+ break;
+
+ case GET_PRINCS:
+ xdr_argument = xdr_gprincs_arg;
+ xdr_result = xdr_gprincs_ret;
+ local = (bool_t (*)()) get_princs_2_svc;
+ break;
+
+ case CHPASS_PRINCIPAL:
+ xdr_argument = xdr_chpass_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) chpass_principal_2_svc;
+ break;
+
+ case SETV4KEY_PRINCIPAL:
+ xdr_argument = xdr_setv4key_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) setv4key_principal_2_svc;
+ break;
+
+ case SETKEY_PRINCIPAL:
+ xdr_argument = xdr_setkey_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) setkey_principal_2_svc;
+ break;
+
+ case CHRAND_PRINCIPAL:
+ xdr_argument = xdr_chrand_arg;
+ xdr_result = xdr_chrand_ret;
+ local = (bool_t (*)()) chrand_principal_2_svc;
+ break;
+
+ case CREATE_POLICY:
+ xdr_argument = xdr_cpol_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) create_policy_2_svc;
+ break;
+
+ case DELETE_POLICY:
+ xdr_argument = xdr_dpol_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) delete_policy_2_svc;
+ break;
+
+ case MODIFY_POLICY:
+ xdr_argument = xdr_mpol_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) modify_policy_2_svc;
+ break;
+
+ case GET_POLICY:
+ xdr_argument = xdr_gpol_arg;
+ xdr_result = xdr_gpol_ret;
+ local = (bool_t (*)()) get_policy_2_svc;
+ break;
+
+ case GET_POLS:
+ xdr_argument = xdr_gpols_arg;
+ xdr_result = xdr_gpols_ret;
+ local = (bool_t (*)()) get_pols_2_svc;
+ break;
+
+ case GET_PRIVS:
+ xdr_argument = xdr_u_int32;
+ xdr_result = xdr_getprivs_ret;
+ local = (bool_t (*)()) get_privs_2_svc;
+ break;
+
+ case INIT:
+ xdr_argument = xdr_u_int32;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) init_2_svc;
+ break;
+
+ case CREATE_PRINCIPAL3:
+ xdr_argument = xdr_cprinc3_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) create_principal3_2_svc;
+ break;
+
+ case CHPASS_PRINCIPAL3:
+ xdr_argument = xdr_chpass3_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) chpass_principal3_2_svc;
+ break;
+
+ case CHRAND_PRINCIPAL3:
+ xdr_argument = xdr_chrand3_arg;
+ xdr_result = xdr_chrand_ret;
+ local = (bool_t (*)()) chrand_principal3_2_svc;
+ break;
+
+ case SETKEY_PRINCIPAL3:
+ xdr_argument = xdr_setkey3_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) setkey_principal3_2_svc;
+ break;
+
+ case PURGEKEYS:
+ xdr_argument = xdr_purgekeys_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) purgekeys_2_svc;
+ break;
+
+ case GET_STRINGS:
+ xdr_argument = xdr_gstrings_arg;
+ xdr_result = xdr_gstrings_ret;
+ local = (bool_t (*)()) get_strings_2_svc;
+ break;
+
+ case SET_STRING:
+ xdr_argument = xdr_sstring_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) set_string_2_svc;
+ break;
+
+ case SETKEY_PRINCIPAL4:
+ xdr_argument = xdr_setkey4_arg;
+ xdr_result = xdr_generic_ret;
+ local = (bool_t (*)()) setkey_principal4_2_svc;
+ break;
+
+ case EXTRACT_KEYS:
+ xdr_argument = xdr_getpkeys_arg;
+ xdr_result = xdr_getpkeys_ret;
+ local = (bool_t (*)()) get_principal_keys_2_svc;
+ break;
+
+ default:
+ krb5_klog_syslog(LOG_ERR, "Invalid KADM5 procedure number: %s, %d",
+ client_addr(rqstp->rq_xprt), rqstp->rq_proc);
+ svcerr_noproc(transp);
+ return;
+ }
+ memset(&argument, 0, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ memset(&result, 0, sizeof(result));
+ retval = (*local)(&argument, &result, rqstp);
+ if (retval && !svc_sendreply(transp, xdr_result, (void *)&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.");
+ }
+ if (!svc_freeargs(transp, xdr_result, &result)) {
+ krb5_klog_syslog(LOG_ERR, "WARNING! Unable to free results, "
+ "continuing.");
+ }
+ return;
+}
+
+static int
+check_rpcsec_auth(struct svc_req *rqstp)
+{
+ gss_ctx_id_t ctx;
+ krb5_context kctx;
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t name;
+ krb5_principal princ;
+ int ret, success;
+ krb5_data *c1, *c2, *realm;
+ gss_buffer_desc gss_str;
+ kadm5_server_handle_t handle;
+ size_t slen;
+ char *sdots;
+
+ success = 0;
+ handle = (kadm5_server_handle_t)global_server_handle;
+
+ if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS)
+ return 0;
+
+ ctx = rqstp->rq_svccred;
+
+ maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name,
+ NULL, NULL, NULL, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ krb5_klog_syslog(LOG_ERR, _("check_rpcsec_auth: failed "
+ "inquire_context, stat=%u"), maj_stat);
+ log_badauth(maj_stat, min_stat, rqstp->rq_xprt, NULL);
+ goto fail_name;
+ }
+
+ kctx = handle->context;
+ ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
+ if (ret == 0)
+ goto fail_name;
+
+ slen = gss_str.length;
+ trunc_name(&slen, &sdots);
+ /*
+ * Since we accept with GSS_C_NO_NAME, the client can authenticate
+ * against the entire kdb. Therefore, ensure that the service
+ * name is something reasonable.
+ */
+ if (krb5_princ_size(kctx, princ) != 2)
+ goto fail_princ;
+
+ c1 = krb5_princ_component(kctx, princ, 0);
+ c2 = krb5_princ_component(kctx, princ, 1);
+ realm = krb5_princ_realm(kctx, princ);
+ success = data_eq_string(*realm, handle->params.realm) &&
+ data_eq_string(*c1, "kadmin") && !data_eq_string(*c2, "history");
+
+fail_princ:
+ if (!success) {
+ krb5_klog_syslog(LOG_ERR, _("bad service principal %.*s%s"),
+ (int) slen, (char *) gss_str.value, sdots);
+ }
+ gss_release_buffer(&min_stat, &gss_str);
+ krb5_free_principal(kctx, princ);
+fail_name:
+ gss_release_name(&min_stat, &name);
+ return success;
+}
+
+int
+gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
+ krb5_principal *princ, gss_buffer_t gss_str)
+{
+ OM_uint32 status, minor_stat;
+ gss_OID gss_type;
+ char *str;
+ int success;
+
+ status = gss_display_name(&minor_stat, gss_name, gss_str, &gss_type);
+ if ((status != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name)) {
+ krb5_klog_syslog(LOG_ERR, _("gss_to_krb5_name: failed display_name "
+ "status %d"), status);
+ log_badauth(status, minor_stat, rqstp->rq_xprt, NULL);
+ return 0;
+ }
+ str = malloc(gss_str->length +1);
+ if (str == NULL)
+ return 0;
+ *str = '\0';
+
+ strncat(str, gss_str->value, gss_str->length);
+ success = (krb5_parse_name(ctx, str, princ) == 0);
+ free(str);
+ return success;
+}
diff --git a/src/kadmin/server/misc.c b/src/kadmin/server/misc.c
new file mode 100644
index 000000000000..27a6376af6b1
--- /dev/null
+++ b/src/kadmin/server/misc.c
@@ -0,0 +1,272 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ */
+
+#include <k5-int.h>
+#include <kdb.h>
+#include <kadm5/server_internal.h>
+#include <kadm5/server_acl.h>
+#include "misc.h"
+#include "net-server.h"
+
+/*
+ * Function: chpass_principal_wrapper_3
+ *
+ * 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
+ * keepold (input) whether to preserve old keys
+ * n_ks_tuple (input) the number of key-salt tuples in ks_tuple
+ * ks_tuple (input) array of tuples indicating the caller's
+ * requested enctypes/salttypes
+ * password (input) password we are going to change to.
+ * <return value> 0 on success 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_3(void *server_handle,
+ krb5_principal principal,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ char *password)
+{
+ kadm5_ret_t ret;
+
+ ret = check_min_life(server_handle, principal, NULL, 0);
+ if (ret)
+ return ret;
+
+ return kadm5_chpass_principal_3(server_handle, principal,
+ keepold, n_ks_tuple, ks_tuple,
+ password);
+}
+
+
+/*
+ * Function: randkey_principal_wrapper_3
+ *
+ * Purpose: wrapper to kadm5_randkey_principal which checks the
+ * password's min. life.
+ *
+ * Arguments:
+ * principal (input) krb5_principal whose password we are
+ * changing
+ * keepold (input) whether to preserve old keys
+ * n_ks_tuple (input) the number of key-salt tuples in ks_tuple
+ * ks_tuple (input) array of tuples indicating the caller's
+ * requested enctypes/salttypes
+ * 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_3(void *server_handle,
+ krb5_principal principal,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **keys, int *n_keys)
+{
+ kadm5_ret_t ret;
+
+ ret = check_min_life(server_handle, principal, NULL, 0);
+ if (ret)
+ return ret;
+ return kadm5_randkey_principal_3(server_handle, principal,
+ keepold, n_ks_tuple, ks_tuple,
+ keys, n_keys);
+}
+
+kadm5_ret_t
+schpw_util_wrapper(void *server_handle,
+ krb5_principal client,
+ krb5_principal target,
+ krb5_boolean initial_flag,
+ char *new_pw, char **ret_pw,
+ char *msg_ret, unsigned int msg_len)
+{
+ kadm5_ret_t ret;
+ kadm5_server_handle_t handle = server_handle;
+ krb5_boolean access_granted;
+ krb5_boolean self;
+
+ /*
+ * If no target is explicitly provided, then the target principal
+ * is the client principal.
+ */
+ if (target == NULL)
+ target = client;
+
+ /*
+ * A principal can always change its own password, as long as it
+ * has an initial ticket and meets the minimum password lifetime
+ * requirement.
+ */
+ self = krb5_principal_compare(handle->context, client, target);
+ if (self) {
+ ret = check_min_life(server_handle, target, msg_ret, msg_len);
+ if (ret != 0)
+ return ret;
+
+ access_granted = initial_flag;
+ } else
+ access_granted = FALSE;
+
+ if (!access_granted &&
+ kadm5int_acl_check_krb(handle->context, client,
+ ACL_CHANGEPW, target, NULL)) {
+ /*
+ * Otherwise, principals with appropriate privileges can change
+ * any password
+ */
+ access_granted = TRUE;
+ }
+
+ if (access_granted) {
+ ret = kadm5_chpass_principal_util(server_handle,
+ target,
+ new_pw, ret_pw,
+ msg_ret, msg_len);
+ } else {
+ ret = KADM5_AUTH_CHANGEPW;
+ strlcpy(msg_ret, "Unauthorized request", msg_len);
+ }
+
+ return ret;
+}
+
+kadm5_ret_t
+check_min_life(void *server_handle, krb5_principal principal,
+ char *msg_ret, unsigned int msg_len)
+{
+ 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 (msg_ret != NULL)
+ *msg_ret = '\0';
+
+ ret = krb5_timeofday(handle->context, &now);
+ if (ret)
+ return ret;
+
+ ret = kadm5_get_principal(handle->lhandle, principal,
+ &princ, KADM5_PRINCIPAL_NORMAL_MASK);
+ if(ret)
+ return ret;
+ if(princ.aux_attributes & KADM5_POLICY) {
+ /* Look up the policy. If it doesn't exist, treat this principal as if
+ * it had no policy. */
+ if((ret=kadm5_get_policy(handle->lhandle,
+ princ.policy, &pol)) != KADM5_OK) {
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return (ret == KADM5_UNK_POLICY) ? 0 : ret;
+ }
+ if((now - princ.last_pwd_change) < pol.pw_min_life &&
+ !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) {
+ if (msg_ret != NULL) {
+ time_t until;
+ char *time_string, *ptr;
+ const char *errstr;
+
+ until = princ.last_pwd_change + pol.pw_min_life;
+
+ time_string = ctime(&until);
+ errstr = error_message(CHPASS_UTIL_PASSWORD_TOO_SOON);
+
+ if (strlen(errstr) + strlen(time_string) < msg_len) {
+ if (*(ptr = &time_string[strlen(time_string)-1]) == '\n')
+ *ptr = '\0';
+ snprintf(msg_ret, msg_len, errstr, time_string);
+ }
+ }
+
+ (void) kadm5_free_policy_ent(handle->lhandle, &pol);
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return KADM5_PASS_TOOSOON;
+ }
+
+ ret = kadm5_free_policy_ent(handle->lhandle, &pol);
+ if (ret) {
+ (void) kadm5_free_principal_ent(handle->lhandle, &princ);
+ return ret;
+ }
+ }
+
+ return kadm5_free_principal_ent(handle->lhandle, &princ);
+}
+
+#define MAXPRINCLEN 125
+
+void
+trunc_name(size_t *len, char **dots)
+{
+ *dots = *len > MAXPRINCLEN ? "..." : "";
+ *len = *len > MAXPRINCLEN ? MAXPRINCLEN : *len;
+}
+
+krb5_error_code
+make_toolong_error (void *handle, krb5_data **out)
+{
+ krb5_error errpkt;
+ krb5_error_code retval;
+ krb5_data *scratch;
+ kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle;
+
+ retval = krb5_us_timeofday(server_handle->context, &errpkt.stime, &errpkt.susec);
+ if (retval)
+ return retval;
+ errpkt.error = KRB_ERR_FIELD_TOOLONG;
+ retval = krb5_build_principal(server_handle->context, &errpkt.server,
+ strlen(server_handle->params.realm),
+ server_handle->params.realm,
+ "kadmin", "changepw", NULL);
+ if (retval)
+ return retval;
+ errpkt.client = NULL;
+ errpkt.cusec = 0;
+ errpkt.ctime = 0;
+ errpkt.text.length = 0;
+ errpkt.text.data = 0;
+ errpkt.e_data.length = 0;
+ errpkt.e_data.data = 0;
+ scratch = malloc(sizeof(*scratch));
+ if (scratch == NULL)
+ return ENOMEM;
+ retval = krb5_mk_error(server_handle->context, &errpkt, scratch);
+ if (retval) {
+ free(scratch);
+ return retval;
+ }
+
+ *out = scratch;
+ return 0;
+}
+
+krb5_context get_context(void *handle)
+{
+ kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle;
+ return server_handle->context;
+}
diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h
new file mode 100644
index 000000000000..ea0fc7d2217a
--- /dev/null
+++ b/src/kadmin/server/misc.h
@@ -0,0 +1,71 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1994 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ */
+
+#ifndef _MISC_H
+#define _MISC_H 1
+
+#include "net-server.h" /* for krb5_fulladdr */
+
+int
+setup_gss_names(struct svc_req *, gss_buffer_desc *,
+ gss_buffer_desc *);
+
+
+kadm5_ret_t
+chpass_principal_wrapper_3(void *server_handle,
+ krb5_principal principal,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ char *password);
+
+kadm5_ret_t
+randkey_principal_wrapper_3(void *server_handle,
+ krb5_principal principal,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **keys, int *n_keys);
+
+kadm5_ret_t
+schpw_util_wrapper(void *server_handle, krb5_principal client,
+ krb5_principal target, krb5_boolean initial_flag,
+ char *new_pw, char **ret_pw,
+ char *msg_ret, unsigned int msg_len);
+
+kadm5_ret_t check_min_life(void *server_handle, krb5_principal principal,
+ char *msg_ret, unsigned int msg_len);
+
+void kadm_1(struct svc_req *, SVCXPRT *);
+void krb5_iprop_prog_1(struct svc_req *, SVCXPRT *);
+
+void trunc_name(size_t *len, char **dots);
+
+int
+gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
+ krb5_principal *princ, gss_buffer_t gss_str);
+
+
+void reset_db(void);
+
+void log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data);
+
+const char *client_addr(SVCXPRT *xprt);
+
+/* network.c */
+#include "net-server.h"
+
+
+void
+krb5_iprop_prog_1(struct svc_req *rqstp, SVCXPRT *transp);
+
+kadm5_ret_t
+kiprop_get_adm_host_srv_name(krb5_context,
+ const char *,
+ char **);
+
+
+#endif /* _MISC_H */
diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c
new file mode 100644
index 000000000000..a3edd3b00169
--- /dev/null
+++ b/src/kadmin/server/ovsec_kadmd.c
@@ -0,0 +1,562 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ */
+
+/*
+ * Copyright (C) 1998 by the FundsXpress, INC.
+ *
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may require
+ * a specific license from the United States Government. It is the
+ * responsibility of any person or organization contemplating export to
+ * obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of FundsXpress. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. FundsXpress makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <k5-platform.h>
+#include <errno.h>
+#include <locale.h>
+#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 <netdb.h>
+#include <gssrpc/rpc.h>
+#include <gssapi/gssapi.h>
+#include "gssapiP_krb5.h" /* for kg_get_context */
+#include <gssrpc/auth_gssapi.h>
+#include <kadm5/admin.h>
+#include <kadm5/kadm_rpc.h>
+#include <kadm5/server_acl.h>
+#include <adm_proto.h>
+#include "kdb_kt.h" /* for krb5_ktkdb_set_context */
+#include <string.h>
+#include "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */
+#include <kdb_log.h>
+
+#include "misc.h"
+
+#if defined(NEED_DAEMON_PROTO)
+int daemon(int, int);
+#endif
+
+#define TIMEOUT 15
+
+gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL;
+void *global_server_handle;
+int nofork = 0;
+char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL;
+char *kprop = KPROPD_DEFAULT_KPROP;
+char *dump_file = KPROP_DEFAULT_FILE;
+char *kprop_port = NULL;
+
+static krb5_context context;
+static char *progname;
+
+#ifdef USE_PASSWORD_SERVER
+void kadm5_set_use_password_server(void);
+#endif
+
+static void
+usage()
+{
+ fprintf(stderr, _("Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] "
+ "[-port port-number]\n"
+ "\t\t[-proponly] [-p path-to-kdb5_util] [-F dump-file]\n"
+ "\t\t[-K path-to-kprop] [-k kprop-port] [-P pid_file]\n"
+ "\nwhere,\n\t[-x db_args]* - any number of database "
+ "specific arguments.\n"
+ "\t\t\tLook at each database documentation for "
+ "supported arguments\n"));
+ exit(1);
+}
+
+/*
+ * Output a message to stderr and the admin server log, and exit with status 1.
+ * msg should not be punctuated. If code is given, msg should indicate what
+ * operation was taking place in the present progressive. Otherwise msg should
+ * be capitalized and should indicate what went wrong.
+ */
+static void
+fail_to_start(krb5_error_code code, const char *msg)
+{
+ const char *errmsg;
+
+ fprintf(stderr, "%s: ", progname);
+ if (code) {
+ errmsg = krb5_get_error_message(context, code);
+ fprintf(stderr, _("%s: %s while %s, aborting\n"), progname, errmsg,
+ msg);
+ krb5_klog_syslog(LOG_ERR, _("%s while %s, aborting\n"), errmsg, msg);
+ } else {
+ fprintf(stderr, _("%s: %s, aborting\n"), progname, msg);
+ krb5_klog_syslog(LOG_ERR, _("%s, aborting"), msg);
+ }
+ exit(1);
+}
+
+static int
+write_pid_file(const char *pid_file)
+{
+ FILE *file;
+ unsigned long pid;
+ int st1, st2;
+
+ file = fopen(pid_file, "w");
+ if (file == NULL)
+ return errno;
+ pid = (unsigned long)getpid();
+ st1 = (fprintf(file, "%ld\n", pid) < 0) ? errno : 0;
+ st2 = (fclose(file) == EOF) ? errno : 0;
+ return st1 ? st1 : st2;
+}
+
+/* Set up the main loop. If proponly is set, don't set up ports for kpasswd or
+ * kadmin. May set *ctx_out even on error. */
+static krb5_error_code
+setup_loop(int proponly, verto_ctx **ctx_out)
+{
+ krb5_error_code ret;
+ verto_ctx *ctx;
+ kadm5_server_handle_t handle = global_server_handle;
+
+ *ctx_out = ctx = loop_init(VERTO_EV_TYPE_SIGNAL);
+ if (ctx == NULL)
+ return ENOMEM;
+ ret = loop_setup_signals(ctx, global_server_handle, NULL);
+ if (ret)
+ return ret;
+ if (!proponly) {
+ ret = loop_add_udp_address(handle->params.kpasswd_port,
+ handle->params.kpasswd_listen);
+ if (ret)
+ return ret;
+ ret = loop_add_tcp_address(handle->params.kpasswd_port,
+ handle->params.kpasswd_listen);
+ if (ret)
+ return ret;
+ ret = loop_add_rpc_service(handle->params.kadmind_port,
+ handle->params.kadmind_listen,
+ KADM, KADMVERS, kadm_1);
+ if (ret)
+ return ret;
+ }
+#ifndef DISABLE_IPROP
+ if (handle->params.iprop_enabled) {
+ ret = loop_add_rpc_service(handle->params.iprop_port,
+ handle->params.iprop_listen,
+ KRB5_IPROP_PROG, KRB5_IPROP_VERS,
+ krb5_iprop_prog_1);
+ if (ret)
+ return ret;
+ }
+#endif
+ return loop_setup_network(ctx, global_server_handle, progname,
+ DEFAULT_TCP_LISTEN_BACKLOG);
+}
+
+/* Point GSSAPI at the KDB keytab so we don't need an actual file keytab. */
+static krb5_error_code
+setup_kdb_keytab()
+{
+ krb5_error_code ret;
+
+ ret = krb5_ktkdb_set_context(context);
+ if (ret)
+ return ret;
+ ret = krb5_db_register_keytab(context);
+ if (ret)
+ return ret;
+ return krb5_gss_register_acceptor_identity("KDB:");
+}
+
+
+/* Return "name@realm". */
+static char *
+build_princ_name(char *name, char *realm)
+{
+ char *fullname;
+
+ if (asprintf(&fullname, "%s@%s", name, realm) < 0)
+ return NULL;
+ return fullname;
+}
+
+/* Callback from GSSRPC for garbled/forged/replayed/etc messages. */
+static 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 struct {
+ rpcproc_t proc;
+ const char *proc_name;
+ } proc_names[] = {
+ {1, "CREATE_PRINCIPAL"},
+ {2, "DELETE_PRINCIPAL"},
+ {3, "MODIFY_PRINCIPAL"},
+ {4, "RENAME_PRINCIPAL"},
+ {5, "GET_PRINCIPAL"},
+ {6, "CHPASS_PRINCIPAL"},
+ {7, "CHRAND_PRINCIPAL"},
+ {8, "CREATE_POLICY"},
+ {9, "DELETE_POLICY"},
+ {10, "MODIFY_POLICY"},
+ {11, "GET_POLICY"},
+ {12, "GET_PRIVS"},
+ {13, "INIT"},
+ {14, "GET_PRINCS"},
+ {15, "GET_POLS"},
+ {16, "SETKEY_PRINCIPAL"},
+ {17, "SETV4KEY_PRINCIPAL"},
+ {18, "CREATE_PRINCIPAL3"},
+ {19, "CHPASS_PRINCIPAL3"},
+ {20, "CHRAND_PRINCIPAL3"},
+ {21, "SETKEY_PRINCIPAL3"},
+ {22, "PURGEKEYS"},
+ {23, "GET_STRINGS"},
+ {24, "SET_STRING"}
+ };
+ OM_uint32 minor;
+ gss_buffer_desc client, server;
+ gss_OID gss_type;
+ const char *a;
+ rpcproc_t proc;
+ unsigned int i;
+ const char *procname;
+ size_t clen, slen;
+ char *cdots, *sdots;
+
+ client.length = 0;
+ client.value = NULL;
+ server.length = 0;
+ server.value = NULL;
+
+ (void)gss_display_name(&minor, client_name, &client, &gss_type);
+ (void)gss_display_name(&minor, server_name, &server, &gss_type);
+ if (client.value == NULL) {
+ client.value = "(null)";
+ clen = sizeof("(null)") - 1;
+ } else {
+ clen = client.length;
+ }
+ trunc_name(&clen, &cdots);
+ if (server.value == NULL) {
+ server.value = "(null)";
+ slen = sizeof("(null)") - 1;
+ } else {
+ slen = server.length;
+ }
+ trunc_name(&slen, &sdots);
+ a = client_addr(rqst->rq_xprt);
+
+ proc = msg->rm_call.cb_proc;
+ procname = NULL;
+ for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) {
+ if (proc_names[i].proc == proc) {
+ procname = proc_names[i].proc_name;
+ break;
+ }
+ }
+ if (procname != NULL) {
+ krb5_klog_syslog(LOG_NOTICE,
+ _("WARNING! Forged/garbled request: %s, claimed "
+ "client = %.*s%s, server = %.*s%s, addr = %s"),
+ procname, (int)clen, (char *)client.value, cdots,
+ (int)slen, (char *)server.value, sdots, a);
+ } else {
+ krb5_klog_syslog(LOG_NOTICE,
+ _("WARNING! Forged/garbled request: %d, claimed "
+ "client = %.*s%s, server = %.*s%s, addr = %s"),
+ proc, (int)clen, (char *)client.value, cdots,
+ (int)slen, (char *)server.value, sdots, a);
+ }
+
+ (void)gss_release_buffer(&minor, &client);
+ (void)gss_release_buffer(&minor, &server);
+}
+
+/* Callback from GSSRPC for miscellaneous errors */
+static void
+log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char *error, char *data)
+{
+ krb5_klog_syslog(LOG_NOTICE, _("Miscellaneous RPC error: %s, %s"),
+ client_addr(rqst->rq_xprt), error);
+}
+
+static void
+log_badauth_display_status_1(char *m, OM_uint32 code, int type)
+{
+ OM_uint32 gssstat, minor_stat;
+ gss_buffer_desc msg;
+ OM_uint32 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) {
+ krb5_klog_syslog(LOG_ERR, _("%s Cannot decode status %d"), m,
+ (int)code);
+ return;
+ }
+
+ krb5_klog_syslog(LOG_NOTICE, "%s %.*s", m, (int)msg.length,
+ (char *)msg.value);
+ (void)gss_release_buffer(&minor_stat, &msg);
+
+ if (!msg_ctx)
+ break;
+ }
+}
+
+/* Callback from GSSRPC for authentication failures */
+void
+log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data)
+{
+ krb5_klog_syslog(LOG_NOTICE, _("Authentication attempt failed: %s, "
+ "GSS-API error strings are:"),
+ client_addr(xprt));
+ log_badauth_display_status_1(" ", major, GSS_C_GSS_CODE);
+ log_badauth_display_status_1(" ", minor, GSS_C_MECH_CODE);
+ krb5_klog_syslog(LOG_NOTICE, _(" GSS-API error strings complete."));
+}
+
+int
+main(int argc, char *argv[])
+{
+ OM_uint32 minor_status;
+ gss_buffer_desc in_buf;
+ gss_OID nt_krb5_name_oid = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME;
+ auth_gssapi_name names[4];
+ kadm5_config_params params;
+ verto_ctx *vctx;
+ const char *pid_file = NULL;
+ char **db_args = NULL, **tmpargs;
+ int ret, i, db_args_size = 0, strong_random = 1, proponly = 0;
+
+ setlocale(LC_ALL, "");
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ 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 =
+ nt_krb5_name_oid;
+
+ progname = (strrchr(argv[0], '/') != NULL) ? strrchr(argv[0], '/') + 1 :
+ argv[0];
+
+ memset(&params, 0, sizeof(params));
+
+ argc--, argv++;
+ while (argc) {
+ if (strcmp(*argv, "-x") == 0) {
+ argc--, argv++;
+ if (!argc)
+ usage();
+ db_args_size++;
+ tmpargs = realloc(db_args, sizeof(char *) * (db_args_size + 1));
+ if (tmpargs == NULL) {
+ fprintf(stderr, _("%s: cannot initialize. Not enough "
+ "memory\n"), progname);
+ exit(1);
+ }
+ db_args = tmpargs;
+ db_args[db_args_size - 1] = *argv;
+ db_args[db_args_size] = NULL;
+ } else 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;
+#ifdef USE_PASSWORD_SERVER
+ } else if (strcmp(*argv, "-passwordserver") == 0) {
+ kadm5_set_use_password_server();
+#endif
+#ifndef DISABLE_IPROP
+ } else if (strcmp(*argv, "-proponly") == 0) {
+ proponly = 1;
+#endif
+ } else if (strcmp(*argv, "-port") == 0) {
+ argc--, argv++;
+ if (!argc)
+ usage();
+ params.kadmind_port = atoi(*argv);
+ params.mask |= KADM5_CONFIG_KADMIND_PORT;
+ } else if (strcmp(*argv, "-P") == 0) {
+ argc--, argv++;
+ if (!argc)
+ usage();
+ pid_file = *argv;
+ } else if (strcmp(*argv, "-W") == 0) {
+ strong_random = 0;
+ } else if (strcmp(*argv, "-p") == 0) {
+ argc--, argv++;
+ if (!argc)
+ usage();
+ kdb5_util = *argv;
+ } else if (strcmp(*argv, "-F") == 0) {
+ argc--, argv++;
+ if (!argc)
+ usage();
+ dump_file = *argv;
+ } else if (strcmp(*argv, "-K") == 0) {
+ argc--, argv++;
+ if (!argc)
+ usage();
+ kprop = *argv;
+ } else if (strcmp(*argv, "-k") == 0) {
+ argc--, argv++;
+ if (!argc)
+ usage();
+ kprop_port = *argv;
+ } else {
+ break;
+ }
+ argc--, argv++;
+ }
+
+ if (argc != 0)
+ usage();
+
+ ret = kadm5_init_krb5_context(&context);
+ if (ret) {
+ fprintf(stderr, _("%s: %s while initializing context, aborting\n"),
+ progname, error_message(ret));
+ exit(1);
+ }
+
+ krb5_klog_init(context, "admin_server", progname, 1);
+
+ ret = kadm5_init(context, "kadmind", NULL, NULL, &params,
+ KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args,
+ &global_server_handle);
+ if (ret)
+ fail_to_start(ret, _("initializing"));
+
+ ret = kadm5_get_config_params(context, 1, &params, &params);
+ if (ret)
+ fail_to_start(ret, _("getting config parameters"));
+ if (!(params.mask & KADM5_CONFIG_REALM))
+ fail_to_start(0, _("Missing required realm configuration"));
+ if (!(params.mask & KADM5_CONFIG_ACL_FILE))
+ fail_to_start(0, _("Missing required ACL file configuration"));
+
+ ret = setup_loop(proponly, &vctx);
+ if (ret)
+ fail_to_start(ret, _("initializing network"));
+
+ names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm);
+ names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm);
+ if (names[0].name == NULL || names[1].name == NULL)
+ fail_to_start(0, _("Cannot build GSSAPI auth names"));
+
+ ret = setup_kdb_keytab();
+ if (ret)
+ fail_to_start(0, _("Cannot set up KDB keytab"));
+
+ if (svcauth_gssapi_set_names(names, 2) == FALSE)
+ fail_to_start(0, _("Cannot set GSSAPI authentication names"));
+
+ /* 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(&minor_status, &in_buf, nt_krb5_name_oid,
+ &gss_changepw_name);
+
+ svcauth_gssapi_set_log_badauth2_func(log_badauth, NULL);
+ svcauth_gssapi_set_log_badverf_func(log_badverf, NULL);
+ svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL);
+
+ svcauth_gss_set_log_badauth2_func(log_badauth, NULL);
+ svcauth_gss_set_log_badverf_func(log_badverf, NULL);
+ svcauth_gss_set_log_miscerr_func(log_miscerr, NULL);
+
+ if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE)
+ fail_to_start(0, _("Cannot initialize GSSAPI service name"));
+
+ ret = kadm5int_acl_init(context, 0, params.acl_file);
+ if (ret)
+ fail_to_start(ret, _("initializing ACL file"));
+
+ if (!nofork && daemon(0, 0) != 0)
+ fail_to_start(errno, _("spawning daemon process"));
+ if (pid_file != NULL) {
+ ret = write_pid_file(pid_file);
+ if (ret)
+ fail_to_start(ret, _("creating PID file"));
+ }
+
+ krb5_klog_syslog(LOG_INFO, _("Seeding random number generator"));
+ ret = krb5_c_random_os_entropy(context, strong_random, NULL);
+ if (ret)
+ fail_to_start(ret, _("getting random seed"));
+
+ if (params.iprop_enabled == TRUE) {
+ ulog_set_role(context, IPROP_MASTER);
+
+ ret = ulog_map(context, params.iprop_logfile, params.iprop_ulogsize);
+ if (ret)
+ fail_to_start(ret, _("mapping update log"));
+
+ if (nofork) {
+ fprintf(stderr,
+ _("%s: create IPROP svc (PROG=%d, VERS=%d)\n"),
+ progname, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+ }
+ }
+
+ if (kprop_port == NULL)
+ kprop_port = getenv("KPROP_PORT");
+
+ krb5_klog_syslog(LOG_INFO, _("starting"));
+ if (nofork)
+ fprintf(stderr, _("%s: starting...\n"), progname);
+
+ verto_run(vctx);
+ krb5_klog_syslog(LOG_INFO, _("finished, exiting"));
+
+ /* Clean up memory, etc */
+ svcauth_gssapi_unset_names();
+ kadm5_destroy(global_server_handle);
+ loop_free(vctx);
+ kadm5int_acl_finish(context, 0);
+ (void)gss_release_name(&minor_status, &gss_changepw_name);
+ (void)gss_release_name(&minor_status, &gss_oldchangepw_name);
+ for (i = 0; i < 4; i++)
+ free(names[i].name);
+
+ krb5_klog_close(context);
+ krb5_free_context(context);
+ exit(2);
+}
diff --git a/src/kadmin/server/schpw.c b/src/kadmin/server/schpw.c
new file mode 100644
index 000000000000..900adf7a0997
--- /dev/null
+++ b/src/kadmin/server/schpw.c
@@ -0,0 +1,487 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include "k5-int.h"
+#include <kadm5/admin.h>
+#include <syslog.h>
+#include <adm_proto.h> /* krb5_klog_syslog */
+#include <stdio.h>
+#include <errno.h>
+
+#include "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */
+
+#include "misc.h"
+
+#ifndef GETSOCKNAME_ARG3_TYPE
+#define GETSOCKNAME_ARG3_TYPE int
+#endif
+
+#define RFC3244_VERSION 0xff80
+
+static krb5_error_code
+process_chpw_request(krb5_context context, void *server_handle, char *realm,
+ krb5_keytab keytab, const krb5_fulladdr *local_faddr,
+ const krb5_fulladdr *remote_faddr, krb5_data *req,
+ krb5_data *rep)
+{
+ krb5_error_code ret;
+ char *ptr;
+ unsigned int plen, vno;
+ krb5_data ap_req, ap_rep = empty_data();
+ krb5_data cipher = empty_data(), clear = empty_data();
+ krb5_auth_context auth_context = NULL;
+ krb5_principal changepw = NULL;
+ krb5_principal client, target = NULL;
+ krb5_ticket *ticket = NULL;
+ krb5_replay_data replay;
+ krb5_error krberror;
+ int numresult;
+ char strresult[1024];
+ char *clientstr = NULL, *targetstr = NULL;
+ const char *errmsg = NULL;
+ size_t clen;
+ char *cdots;
+ struct sockaddr_storage ss;
+ socklen_t salen;
+ char addrbuf[100];
+ krb5_address *addr = remote_faddr->address;
+
+ *rep = empty_data();
+
+ if (req->length < 4) {
+ /* either this, or the server is printing bad messages,
+ or the caller passed in garbage */
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ numresult = KRB5_KPASSWD_MALFORMED;
+ strlcpy(strresult, "Request was truncated", sizeof(strresult));
+ goto bailout;
+ }
+
+ ptr = req->data;
+
+ /* verify length */
+
+ plen = (*ptr++ & 0xff);
+ plen = (plen<<8) | (*ptr++ & 0xff);
+
+ if (plen != req->length) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ numresult = KRB5_KPASSWD_MALFORMED;
+ strlcpy(strresult, "Request length was inconsistent",
+ sizeof(strresult));
+ goto bailout;
+ }
+
+ /* verify version number */
+
+ vno = (*ptr++ & 0xff) ;
+ vno = (vno<<8) | (*ptr++ & 0xff);
+
+ if (vno != 1 && vno != RFC3244_VERSION) {
+ ret = KRB5KDC_ERR_BAD_PVNO;
+ numresult = KRB5_KPASSWD_BAD_VERSION;
+ snprintf(strresult, sizeof(strresult),
+ "Request contained unknown protocol version number %d", vno);
+ goto bailout;
+ }
+
+ /* read, check ap-req length */
+
+ ap_req.length = (*ptr++ & 0xff);
+ ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);
+
+ if (ptr + ap_req.length >= req->data + req->length) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ numresult = KRB5_KPASSWD_MALFORMED;
+ strlcpy(strresult, "Request was truncated in AP-REQ",
+ sizeof(strresult));
+ goto bailout;
+ }
+
+ /* verify ap_req */
+
+ ap_req.data = ptr;
+ ptr += ap_req.length;
+
+ ret = krb5_auth_con_init(context, &auth_context);
+ if (ret) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult, "Failed initializing auth context",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ ret = krb5_auth_con_setflags(context, auth_context,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+ if (ret) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult, "Failed initializing auth context",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
+ "kadmin", "changepw", NULL);
+ if (ret) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult, "Failed building kadmin/changepw principal",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
+ NULL, &ticket);
+
+ if (ret) {
+ numresult = KRB5_KPASSWD_AUTHERROR;
+ strlcpy(strresult, "Failed reading application request",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ /* construct the ap-rep */
+
+ ret = krb5_mk_rep(context, auth_context, &ap_rep);
+ if (ret) {
+ numresult = KRB5_KPASSWD_AUTHERROR;
+ strlcpy(strresult, "Failed replying to application request",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ /* decrypt the ChangePasswdData */
+
+ cipher.length = (req->data + req->length) - ptr;
+ cipher.data = ptr;
+
+ /*
+ * Don't set a remote address in auth_context before calling krb5_rd_priv,
+ * so that we can work against clients behind a NAT. Reflection attacks
+ * aren't a concern since we use sequence numbers and since our requests
+ * don't look anything like our responses. Also don't set a local address,
+ * since we don't know what interface the request was received on.
+ */
+
+ ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
+ if (ret) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult, "Failed decrypting request", sizeof(strresult));
+ goto chpwfail;
+ }
+
+ client = ticket->enc_part2->client;
+
+ /* decode ChangePasswdData for setpw requests */
+ if (vno == RFC3244_VERSION) {
+ krb5_data *clear_data;
+
+ ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
+ if (ret != 0) {
+ numresult = KRB5_KPASSWD_MALFORMED;
+ strlcpy(strresult, "Failed decoding ChangePasswdData",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ zapfree(clear.data, clear.length);
+
+ clear = *clear_data;
+ free(clear_data);
+
+ if (target != NULL) {
+ ret = krb5_unparse_name(context, target, &targetstr);
+ if (ret != 0) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult, "Failed unparsing target name for log",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+ }
+ }
+
+ ret = krb5_unparse_name(context, client, &clientstr);
+ if (ret) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult, "Failed unparsing client name for log",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ /* for cpw, verify that this is an AS_REQ ticket */
+ if (vno == 1 &&
+ (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
+ numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
+ strlcpy(strresult, "Ticket must be derived from a password",
+ sizeof(strresult));
+ goto chpwfail;
+ }
+
+ /* change the password */
+
+ ptr = k5memdup0(clear.data, clear.length, &ret);
+ ret = schpw_util_wrapper(server_handle, client, target,
+ (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0,
+ ptr, NULL, strresult, sizeof(strresult));
+ if (ret)
+ errmsg = krb5_get_error_message(context, ret);
+
+ /* zap the password */
+ zapfree(clear.data, clear.length);
+ zapfree(ptr, clear.length);
+ clear = empty_data();
+
+ clen = strlen(clientstr);
+ trunc_name(&clen, &cdots);
+
+ switch (addr->addrtype) {
+ case ADDRTYPE_INET: {
+ struct sockaddr_in *sin = ss2sin(&ss);
+
+ sin->sin_family = AF_INET;
+ memcpy(&sin->sin_addr, addr->contents, addr->length);
+ sin->sin_port = htons(remote_faddr->port);
+ salen = sizeof(*sin);
+ break;
+ }
+ case ADDRTYPE_INET6: {
+ struct sockaddr_in6 *sin6 = ss2sin6(&ss);
+
+ sin6->sin6_family = AF_INET6;
+ memcpy(&sin6->sin6_addr, addr->contents, addr->length);
+ sin6->sin6_port = htons(remote_faddr->port);
+ salen = sizeof(*sin6);
+ break;
+ }
+ default: {
+ struct sockaddr *sa = ss2sa(&ss);
+
+ sa->sa_family = AF_UNSPEC;
+ salen = sizeof(*sa);
+ break;
+ }
+ }
+
+ if (getnameinfo(ss2sa(&ss), salen,
+ addrbuf, sizeof(addrbuf), NULL, 0,
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf));
+
+ if (vno == RFC3244_VERSION) {
+ size_t tlen;
+ char *tdots;
+ const char *targetp;
+
+ if (target == NULL) {
+ tlen = clen;
+ tdots = cdots;
+ targetp = targetstr;
+ } else {
+ tlen = strlen(targetstr);
+ trunc_name(&tlen, &tdots);
+ targetp = clientstr;
+ }
+
+ krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for "
+ "%.*s%s: %s"), addrbuf, (int) clen,
+ clientstr, cdots, (int) tlen, targetp, tdots,
+ errmsg ? errmsg : "success");
+ } else {
+ krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"),
+ addrbuf, (int) clen, clientstr, cdots,
+ errmsg ? errmsg : "success");
+ }
+ switch (ret) {
+ case KADM5_AUTH_CHANGEPW:
+ numresult = KRB5_KPASSWD_ACCESSDENIED;
+ break;
+ case KADM5_PASS_Q_TOOSHORT:
+ case KADM5_PASS_REUSE:
+ case KADM5_PASS_Q_CLASS:
+ case KADM5_PASS_Q_DICT:
+ case KADM5_PASS_Q_GENERIC:
+ case KADM5_PASS_TOOSOON:
+ numresult = KRB5_KPASSWD_SOFTERROR;
+ break;
+ case 0:
+ numresult = KRB5_KPASSWD_SUCCESS;
+ strlcpy(strresult, "", sizeof(strresult));
+ break;
+ default:
+ numresult = KRB5_KPASSWD_HARDERROR;
+ break;
+ }
+
+chpwfail:
+
+ ret = alloc_data(&clear, 2 + strlen(strresult));
+ if (ret)
+ goto bailout;
+
+ ptr = clear.data;
+
+ *ptr++ = (numresult>>8) & 0xff;
+ *ptr++ = numresult & 0xff;
+
+ memcpy(ptr, strresult, strlen(strresult));
+
+ cipher = empty_data();
+
+ if (ap_rep.length) {
+ ret = krb5_auth_con_setaddrs(context, auth_context,
+ local_faddr->address, NULL);
+ if (ret) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult,
+ "Failed storing client and server internet addresses",
+ sizeof(strresult));
+ } else {
+ ret = krb5_mk_priv(context, auth_context, &clear, &cipher,
+ &replay);
+ if (ret) {
+ numresult = KRB5_KPASSWD_HARDERROR;
+ strlcpy(strresult, "Failed encrypting reply",
+ sizeof(strresult));
+ }
+ }
+ }
+
+ /* if no KRB-PRIV was constructed, then we need a KRB-ERROR.
+ if this fails, just bail. there's nothing else we can do. */
+
+ if (cipher.length == 0) {
+ /* clear out ap_rep now, so that it won't be inserted in the
+ reply */
+
+ if (ap_rep.length) {
+ free(ap_rep.data);
+ ap_rep = empty_data();
+ }
+
+ krberror.ctime = 0;
+ krberror.cusec = 0;
+ krberror.susec = 0;
+ ret = krb5_timeofday(context, &krberror.stime);
+ if (ret)
+ goto bailout;
+
+ /* this is really icky. but it's what all the other callers
+ to mk_error do. */
+ krberror.error = ret;
+ krberror.error -= ERROR_TABLE_BASE_krb5;
+ if (krberror.error < 0 || krberror.error > KRB_ERR_MAX)
+ krberror.error = KRB_ERR_GENERIC;
+
+ krberror.client = NULL;
+
+ ret = krb5_build_principal(context, &krberror.server,
+ strlen(realm), realm,
+ "kadmin", "changepw", NULL);
+ if (ret)
+ goto bailout;
+ krberror.text.length = 0;
+ krberror.e_data = clear;
+
+ ret = krb5_mk_error(context, &krberror, &cipher);
+
+ krb5_free_principal(context, krberror.server);
+
+ if (ret)
+ goto bailout;
+ }
+
+ /* construct the reply */
+
+ ret = alloc_data(rep, 6 + ap_rep.length + cipher.length);
+ if (ret)
+ goto bailout;
+ ptr = rep->data;
+
+ /* length */
+
+ *ptr++ = (rep->length>>8) & 0xff;
+ *ptr++ = rep->length & 0xff;
+
+ /* version == 0x0001 big-endian */
+
+ *ptr++ = 0;
+ *ptr++ = 1;
+
+ /* ap_rep length, big-endian */
+
+ *ptr++ = (ap_rep.length>>8) & 0xff;
+ *ptr++ = ap_rep.length & 0xff;
+
+ /* ap-rep data */
+
+ if (ap_rep.length) {
+ memcpy(ptr, ap_rep.data, ap_rep.length);
+ ptr += ap_rep.length;
+ }
+
+ /* krb-priv or krb-error */
+
+ memcpy(ptr, cipher.data, cipher.length);
+
+bailout:
+ krb5_auth_con_free(context, auth_context);
+ krb5_free_principal(context, changepw);
+ krb5_free_ticket(context, ticket);
+ free(ap_rep.data);
+ free(clear.data);
+ free(cipher.data);
+ krb5_free_principal(context, target);
+ krb5_free_unparsed_name(context, targetstr);
+ krb5_free_unparsed_name(context, clientstr);
+ krb5_free_error_message(context, errmsg);
+ return ret;
+}
+
+/* Dispatch routine for set/change password */
+void
+dispatch(void *handle, struct sockaddr *local_saddr,
+ const krb5_fulladdr *remote_faddr, krb5_data *request, int is_tcp,
+ verto_ctx *vctx, loop_respond_fn respond, void *arg)
+{
+ krb5_error_code ret;
+ krb5_keytab kt = NULL;
+ kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle;
+ krb5_fulladdr local_faddr;
+ krb5_address **local_kaddrs = NULL, local_kaddr_buf;
+ krb5_data *response = NULL;
+
+ if (local_saddr == NULL) {
+ ret = krb5_os_localaddr(server_handle->context, &local_kaddrs);
+ if (ret != 0)
+ goto egress;
+
+ local_faddr.address = local_kaddrs[0];
+ local_faddr.port = 0;
+ } else {
+ local_faddr.address = &local_kaddr_buf;
+ init_addr(&local_faddr, local_saddr);
+ }
+
+ ret = krb5_kt_resolve(server_handle->context, "KDB:", &kt);
+ if (ret != 0) {
+ krb5_klog_syslog(LOG_ERR, _("chpw: Couldn't open admin keytab %s"),
+ krb5_get_error_message(server_handle->context, ret));
+ goto egress;
+ }
+
+ response = k5alloc(sizeof(krb5_data), &ret);
+ if (response == NULL)
+ goto egress;
+
+ ret = process_chpw_request(server_handle->context,
+ handle,
+ server_handle->params.realm,
+ kt,
+ &local_faddr,
+ remote_faddr,
+ request,
+ response);
+egress:
+ if (ret)
+ krb5_free_data(server_handle->context, response);
+ krb5_free_addresses(server_handle->context, local_kaddrs);
+ krb5_kt_close(server_handle->context, kt);
+ (*respond)(arg, ret, ret == 0 ? response : NULL);
+}
diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c
new file mode 100644
index 000000000000..86c162590e4b
--- /dev/null
+++ b/src/kadmin/server/server_stubs.c
@@ -0,0 +1,1707 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved
+ *
+ */
+
+#include <k5-platform.h>
+#include <socket-utils.h>
+#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 <adm_proto.h> /* krb5_klog_syslog */
+#include "misc.h"
+
+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_rel_1(acceptor_name(rqstp->rq_svccred), gss_changepw_name) | \
+ (gss_oldchangepw_name && \
+ cmp_gss_names_rel_1(acceptor_name(rqstp->rq_svccred), \
+ gss_oldchangepw_name)))
+
+
+static int gss_to_krb5_name(kadm5_server_handle_t handle,
+ gss_name_t gss_name, krb5_principal *princ);
+
+static int gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str);
+
+static gss_name_t acceptor_name(gss_ctx_id_t context);
+
+gss_name_t rqst2name(struct svc_req *rqstp);
+
+static int cmp_gss_names(gss_name_t n1, gss_name_t n2)
+{
+ OM_uint32 emin;
+ int equal;
+
+ if (GSS_ERROR(gss_compare_name(&emin, n1, n2, &equal)))
+ return(0);
+
+ return(equal);
+}
+
+/* Does a comparison of the names and then releases the first entity */
+/* For use above in CHANGEPW_SERVICE */
+static int cmp_gss_names_rel_1(gss_name_t n1, gss_name_t n2)
+{
+ OM_uint32 min_stat;
+ int ret;
+
+ ret = cmp_gss_names(n1, n2);
+ if (n1) (void) gss_release_name(&min_stat, &n1);
+ return ret;
+}
+
+/*
+ * 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;
+
+ *out_handle = NULL;
+
+ 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, rqst2name(rqstp),
+ &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)
+{
+ if (!handle)
+ return;
+ krb5_free_principal(handle->context, handle->current_caller);
+ free(handle);
+}
+
+/* Result is stored in a static buffer and is invalidated by the next call. */
+const char *
+client_addr(SVCXPRT *xprt)
+{
+ static char abuf[128];
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+ const char *p = NULL;
+
+ if (getpeername(xprt->xp_sock, ss2sa(&ss), &len) != 0)
+ return "(unknown)";
+ if (ss2sa(&ss)->sa_family == AF_INET)
+ p = inet_ntop(AF_INET, &ss2sin(&ss)->sin_addr, abuf, sizeof(abuf));
+ else if (ss2sa(&ss)->sa_family == AF_INET6)
+ p = inet_ntop(AF_INET6, &ss2sin6(&ss)->sin6_addr, abuf, sizeof(abuf));
+ return (p == NULL) ? "(unknown)" : p;
+}
+
+/*
+ * 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(rqst2name(rqstp), 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);
+ gss_release_name(&min_stat, &server_gss_name);
+ return -1;
+ }
+ if (gss_name_to_string(server_gss_name, server_name) != 0) {
+ gss_release_buffer(&min_stat, client_name);
+ gss_release_name(&min_stat, &server_gss_name);
+ return -1;
+ }
+ gss_release_name(&min_stat, &server_gss_name);
+ return 0;
+}
+
+static 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;
+}
+
+static int cmp_gss_krb5_name(kadm5_server_handle_t handle,
+ gss_name_t gss_name, krb5_principal princ)
+{
+ krb5_principal princ2;
+ int status;
+
+ if (! gss_to_krb5_name(handle, gss_name, &princ2))
+ return 0;
+ status = krb5_principal_compare(handle->context, princ, princ2);
+ krb5_free_principal(handle->context, princ2);
+ return status;
+}
+
+static int gss_to_krb5_name(kadm5_server_handle_t handle,
+ gss_name_t gss_name, krb5_principal *princ)
+{
+ OM_uint32 minor_stat;
+ gss_buffer_desc gss_str;
+ int success;
+ char *s;
+
+ if (gss_name_to_string(gss_name, &gss_str) != 0)
+ return 0;
+ if (asprintf(&s, "%.*s", (int)gss_str.length, (char *)gss_str.value) < 0) {
+ gss_release_buffer(&minor_stat, &gss_str);
+ return 0;
+ }
+ success = (krb5_parse_name(handle->context, s, princ) == 0);
+ free(s);
+ gss_release_buffer(&minor_stat, &gss_str);
+ return success;
+}
+
+static int
+gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str)
+{
+ OM_uint32 status, minor_stat;
+ gss_OID gss_type;
+ const char pref[] = KRB5_WELLKNOWN_NAMESTR "/" KRB5_ANONYMOUS_PRINCSTR "@";
+ const size_t preflen = sizeof(pref) - 1;
+
+ status = gss_display_name(&minor_stat, gss_name, str, &gss_type);
+ if (status != GSS_S_COMPLETE)
+ return 1;
+ if (gss_oid_equal(gss_type, GSS_C_NT_ANONYMOUS)) {
+ /* Guard against non-krb5 mechs with different anonymous displays. */
+ if (str->length < preflen || memcmp(str->value, pref, preflen) != 0)
+ return 1;
+ } else if (!gss_oid_equal(gss_type, GSS_KRB5_NT_PRINCIPAL_NAME)) {
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Perform common initialization for server stub functions. A subset of the
+ * output arguments may be set on failure; the caller is responsible for
+ * initializing outputs and calling stub_cleanup() on success or failure.
+ * princ and princ_str_out may be NULL to omit unparsing a principal name.
+ */
+static kadm5_ret_t
+stub_setup(krb5_ui_4 api_version, struct svc_req *rqstp, krb5_principal princ,
+ kadm5_server_handle_t *handle_out, krb5_ui_4 *api_version_out,
+ gss_buffer_t client_name_out, gss_buffer_t service_name_out,
+ char **princ_str_out)
+{
+ kadm5_ret_t ret;
+
+ ret = new_server_handle(api_version, rqstp, handle_out);
+ if (ret)
+ return ret;
+
+ ret = check_handle(*handle_out);
+ if (ret)
+ return ret;
+
+ *api_version_out = (*handle_out)->api_version;
+
+ if (setup_gss_names(rqstp, client_name_out, service_name_out) < 0)
+ return KADM5_FAILURE;
+
+ if (princ_str_out != NULL) {
+ if (princ == NULL)
+ return KADM5_BAD_PRINCIPAL;
+ if (krb5_unparse_name((*handle_out)->context, princ, princ_str_out))
+ return KADM5_BAD_PRINCIPAL;
+ }
+
+ return KADM5_OK;
+}
+
+/* Perform common cleanup for server stub functions. */
+static void
+stub_cleanup(kadm5_server_handle_t handle, char *princ_str,
+ gss_buffer_t client_name, gss_buffer_t service_name)
+{
+ OM_uint32 minor_stat;
+
+ free_server_handle(handle);
+ free(princ_str);
+ gss_release_buffer(&minor_stat, client_name);
+ gss_release_buffer(&minor_stat, service_name);
+}
+
+static int
+log_unauth(
+ char *op,
+ char *target,
+ gss_buffer_t client,
+ gss_buffer_t server,
+ struct svc_req *rqstp)
+{
+ size_t tlen, clen, slen;
+ char *tdots, *cdots, *sdots;
+
+ tlen = strlen(target);
+ trunc_name(&tlen, &tdots);
+ clen = client->length;
+ trunc_name(&clen, &cdots);
+ slen = server->length;
+ trunc_name(&slen, &sdots);
+
+ /* okay to cast lengths to int because trunc_name limits max value */
+ return krb5_klog_syslog(LOG_NOTICE,
+ _("Unauthorized request: %s, %.*s%s, "
+ "client=%.*s%s, service=%.*s%s, addr=%s"),
+ op, (int)tlen, target, tdots,
+ (int)clen, (char *)client->value, cdots,
+ (int)slen, (char *)server->value, sdots,
+ client_addr(rqstp->rq_xprt));
+}
+
+static int
+log_done(
+ char *op,
+ char *target,
+ const char *errmsg,
+ gss_buffer_t client,
+ gss_buffer_t server,
+ struct svc_req *rqstp)
+{
+ size_t tlen, clen, slen;
+ char *tdots, *cdots, *sdots;
+
+ if (errmsg == NULL)
+ errmsg = _("success");
+ tlen = strlen(target);
+ trunc_name(&tlen, &tdots);
+ clen = client->length;
+ trunc_name(&clen, &cdots);
+ slen = server->length;
+ trunc_name(&slen, &sdots);
+
+ /* okay to cast lengths to int because trunc_name limits max value */
+ return krb5_klog_syslog(LOG_NOTICE,
+ _("Request: %s, %.*s%s, %s, "
+ "client=%.*s%s, service=%.*s%s, addr=%s"),
+ op, (int)tlen, target, tdots, errmsg,
+ (int)clen, (char *)client->value, cdots,
+ (int)slen, (char *)server->value, sdots,
+ client_addr(rqstp->rq_xprt));
+}
+
+bool_t
+create_principal_2_svc(cprinc_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ restriction_t *rp;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
+ &handle, &ret->api_version, &client_name,
+ &service_name, &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ if (CHANGEPW_SERVICE(rqstp)
+ || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_ADD,
+ arg->rec.principal, &rp)
+ || kadm5int_acl_impose_restrictions(handle->context,
+ &arg->rec, &arg->mask, rp)) {
+ ret->code = KADM5_AUTH_ADD;
+ log_unauth("kadm5_create_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_create_principal(handle, &arg->rec, arg->mask,
+ arg->passwd);
+
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_create_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+create_principal3_2_svc(cprinc3_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ restriction_t *rp;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
+ &handle, &ret->api_version, &client_name,
+ &service_name, &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ if (CHANGEPW_SERVICE(rqstp)
+ || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_ADD,
+ arg->rec.principal, &rp)
+ || kadm5int_acl_impose_restrictions(handle->context,
+ &arg->rec, &arg->mask, rp)) {
+ ret->code = KADM5_AUTH_ADD;
+ log_unauth("kadm5_create_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_create_principal_3(handle, &arg->rec, arg->mask,
+ arg->n_ks_tuple, arg->ks_tuple,
+ arg->passwd);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_create_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+/* Return KADM5_PROTECT_KEYS if KRB5_KDB_LOCKDOWN_KEYS is set for princ. */
+static kadm5_ret_t
+check_lockdown_keys(kadm5_server_handle_t handle, krb5_principal princ)
+{
+ kadm5_principal_ent_rec rec;
+ kadm5_ret_t ret;
+
+ ret = kadm5_get_principal(handle, princ, &rec, KADM5_ATTRIBUTES);
+ if (ret)
+ return ret;
+ ret = (rec.attributes & KRB5_KDB_LOCKDOWN_KEYS) ? KADM5_PROTECT_KEYS : 0;
+ kadm5_free_principal_ent(handle, &rec);
+ return ret;
+}
+
+bool_t
+delete_principal_2_svc(dprinc_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ if (CHANGEPW_SERVICE(rqstp)
+ || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_DELETE,
+ arg->princ, NULL)) {
+ ret->code = KADM5_AUTH_DELETE;
+ log_unauth("kadm5_delete_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_delete_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_DELETE;
+ }
+ }
+
+ if (ret->code == KADM5_OK)
+ ret->code = kadm5_delete_principal(handle, arg->princ);
+ if (ret->code != KADM5_AUTH_DELETE) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_delete_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ restriction_t *rp;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
+ &handle, &ret->api_version, &client_name,
+ &service_name, &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ if (CHANGEPW_SERVICE(rqstp)
+ || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY,
+ arg->rec.principal, &rp)
+ || kadm5int_acl_impose_restrictions(handle->context,
+ &arg->rec, &arg->mask, rp)) {
+ ret->code = KADM5_AUTH_MODIFY;
+ log_unauth("kadm5_modify_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else if ((arg->mask & KADM5_ATTRIBUTES) &&
+ (!(arg->rec.attributes & KRB5_KDB_LOCKDOWN_KEYS))) {
+ ret->code = check_lockdown_keys(handle, arg->rec.principal);
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_modify_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_MODIFY;
+ }
+ }
+
+ if (ret->code == KADM5_OK) {
+ ret->code = kadm5_modify_principal(handle, &arg->rec, arg->mask);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_modify_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+rename_principal_2_svc(rprinc_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg1 = NULL, *prime_arg2 = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ restriction_t *rp;
+ const char *errmsg = NULL;
+ size_t tlen1, tlen2, clen, slen;
+ char *tdots1, *tdots2, *cdots, *sdots;
+
+ ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+ &ret->api_version, &client_name, &service_name,
+ NULL);
+ if (ret->code)
+ goto exit_func;
+
+ if (krb5_unparse_name(handle->context, arg->src, &prime_arg1) ||
+ krb5_unparse_name(handle->context, arg->dest, &prime_arg2)) {
+ ret->code = KADM5_BAD_PRINCIPAL;
+ goto exit_func;
+ }
+ tlen1 = strlen(prime_arg1);
+ trunc_name(&tlen1, &tdots1);
+ tlen2 = strlen(prime_arg2);
+ trunc_name(&tlen2, &tdots2);
+ clen = client_name.length;
+ trunc_name(&clen, &cdots);
+ slen = service_name.length;
+ trunc_name(&slen, &sdots);
+
+ ret->code = KADM5_OK;
+ if (! CHANGEPW_SERVICE(rqstp)) {
+ if (!kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_DELETE, arg->src, NULL))
+ ret->code = KADM5_AUTH_DELETE;
+ /* any restrictions at all on the ADD kills the RENAME */
+ if (!kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_ADD, arg->dest, &rp) || rp) {
+ if (ret->code == KADM5_AUTH_DELETE)
+ ret->code = KADM5_AUTH_INSUFFICIENT;
+ else
+ ret->code = KADM5_AUTH_ADD;
+ }
+ if (ret->code == KADM5_OK) {
+ ret->code = check_lockdown_keys(handle, arg->src);
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_rename_principal", prime_arg1, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_DELETE;
+ }
+ }
+ } else
+ ret->code = KADM5_AUTH_INSUFFICIENT;
+ if (ret->code != KADM5_OK) {
+ /* okay to cast lengths to int because trunc_name limits max value */
+ krb5_klog_syslog(LOG_NOTICE,
+ _("Unauthorized request: kadm5_rename_principal, "
+ "%.*s%s to %.*s%s, "
+ "client=%.*s%s, service=%.*s%s, addr=%s"),
+ (int)tlen1, prime_arg1, tdots1,
+ (int)tlen2, prime_arg2, tdots2,
+ (int)clen, (char *)client_name.value, cdots,
+ (int)slen, (char *)service_name.value, sdots,
+ client_addr(rqstp->rq_xprt));
+ } else {
+ ret->code = kadm5_rename_principal(handle, arg->src, arg->dest);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ /* okay to cast lengths to int because trunc_name limits max value */
+ krb5_klog_syslog(LOG_NOTICE,
+ _("Request: kadm5_rename_principal, "
+ "%.*s%s to %.*s%s, %s, "
+ "client=%.*s%s, service=%.*s%s, addr=%s"),
+ (int)tlen1, prime_arg1, tdots1,
+ (int)tlen2, prime_arg2, tdots2,
+ errmsg ? errmsg : _("success"),
+ (int)clen, (char *)client_name.value, cdots,
+ (int)slen, (char *)service_name.value, sdots,
+ client_addr(rqstp->rq_xprt));
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+
+ }
+exit_func:
+ free(prime_arg1);
+ free(prime_arg2);
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+get_principal_2_svc(gprinc_arg *arg, gprinc_ret *ret, struct svc_req *rqstp)
+{
+ char *funcname, *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ funcname = "kadm5_get_principal";
+
+ if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
+ (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_INQUIRE,
+ arg->princ,
+ NULL))) {
+ ret->code = KADM5_AUTH_GET;
+ log_unauth(funcname, prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_get_principal(handle, arg->princ, &ret->rec,
+ arg->mask);
+
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done(funcname, prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+get_princs_2_svc(gprincs_arg *arg, gprincs_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+ &ret->api_version, &client_name, &service_name,
+ NULL);
+ if (ret->code)
+ goto exit_func;
+
+ prime_arg = arg->exp;
+ if (prime_arg == NULL)
+ prime_arg = "*";
+
+ if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_LIST,
+ NULL,
+ NULL)) {
+ ret->code = KADM5_AUTH_LIST;
+ log_unauth("kadm5_get_principals", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_get_principals(handle, arg->exp, &ret->princs,
+ &ret->count);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_get_principals", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+
+ }
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+chpass_principal_2_svc(chpass_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code != KADM5_OK) {
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_CHANGEPW;
+ }
+ } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
+ ret->code = chpass_principal_wrapper_3(handle, arg->princ, FALSE, 0,
+ NULL, arg->pass);
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_CHANGEPW, arg->princ, NULL)) {
+ ret->code = kadm5_chpass_principal(handle, arg->princ, arg->pass);
+ } else {
+ log_unauth("kadm5_chpass_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_CHANGEPW;
+ }
+
+ if (ret->code != KADM5_AUTH_CHANGEPW) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_chpass_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+chpass_principal3_2_svc(chpass3_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code != KADM5_OK) {
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_CHANGEPW;
+ }
+ } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
+ ret->code = chpass_principal_wrapper_3(handle, arg->princ,
+ arg->keepold, arg->n_ks_tuple,
+ arg->ks_tuple, arg->pass);
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_CHANGEPW, arg->princ, NULL)) {
+ ret->code = kadm5_chpass_principal_3(handle, arg->princ, arg->keepold,
+ arg->n_ks_tuple, arg->ks_tuple,
+ arg->pass);
+ } else {
+ log_unauth("kadm5_chpass_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_CHANGEPW;
+ }
+
+ if (ret->code != KADM5_AUTH_CHANGEPW) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_chpass_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+setv4key_principal_2_svc(setv4key_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code != KADM5_OK) {
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_setv4key_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_SETKEY, arg->princ, NULL)) {
+ ret->code = kadm5_setv4key_principal(handle, arg->princ,
+ arg->keyblock);
+ } else {
+ log_unauth("kadm5_setv4key_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+
+ if (ret->code != KADM5_AUTH_SETKEY) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_setv4key_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+
+bool_t
+setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code != KADM5_OK) {
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_SETKEY, arg->princ, NULL)) {
+ ret->code = kadm5_setkey_principal(handle, arg->princ, arg->keyblocks,
+ arg->n_keys);
+ } else {
+ log_unauth("kadm5_setkey_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+
+ if (ret->code != KADM5_AUTH_SETKEY) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_setkey_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code != KADM5_OK) {
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_SETKEY, arg->princ, NULL)) {
+ ret->code = kadm5_setkey_principal_3(handle, arg->princ, arg->keepold,
+ arg->n_ks_tuple, arg->ks_tuple,
+ arg->keyblocks, arg->n_keys);
+ } else {
+ log_unauth("kadm5_setkey_principal", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+
+ if (ret->code != KADM5_AUTH_SETKEY) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_setkey_principal", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code != KADM5_OK) {
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_SETKEY, arg->princ, NULL)) {
+ ret->code = kadm5_setkey_principal_4(handle, arg->princ, arg->keepold,
+ arg->key_data, arg->n_key_data);
+ } else {
+ log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+ &service_name, rqstp);
+ ret->code = KADM5_AUTH_SETKEY;
+ }
+
+ if (ret->code != KADM5_AUTH_SETKEY) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_setkey_principal", prime_arg, errmsg, &client_name,
+ &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+/* Empty out *keys/*nkeys if princ is protected with the lockdown attribute, or
+ * if we fail to check. */
+static kadm5_ret_t
+chrand_check_lockdown(kadm5_server_handle_t handle, krb5_principal princ,
+ krb5_keyblock **keys, int *nkeys)
+{
+ kadm5_ret_t ret;
+ int i;
+
+ ret = check_lockdown_keys(handle, princ);
+ if (!ret)
+ return 0;
+
+ for (i = 0; i < *nkeys; i++)
+ krb5_free_keyblock_contents(handle->context, &((*keys)[i]));
+ free(*keys);
+ *keys = NULL;
+ *nkeys = 0;
+ return (ret == KADM5_PROTECT_KEYS) ? KADM5_OK : ret;
+}
+
+bool_t
+chrand_principal_2_svc(chrand_arg *arg, chrand_ret *ret, struct svc_req *rqstp)
+{
+ char *funcname, *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ krb5_keyblock *k;
+ int nkeys;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ funcname = "kadm5_randkey_principal";
+
+ if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
+ ret->code = randkey_principal_wrapper_3(handle, arg->princ, FALSE, 0,
+ NULL, &k, &nkeys);
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_CHANGEPW, arg->princ, NULL)) {
+ ret->code = kadm5_randkey_principal(handle, arg->princ, &k, &nkeys);
+ } else {
+ log_unauth(funcname, prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_CHANGEPW;
+ }
+
+ if (ret->code == KADM5_OK) {
+ ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
+ if (ret->code == KADM5_PROTECT_KEYS)
+ ret->code = KADM5_OK;
+ ret->keys = k;
+ ret->n_keys = nkeys;
+ }
+
+ if (ret->code != KADM5_AUTH_CHANGEPW) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done(funcname, prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *funcname, *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ krb5_keyblock *k;
+ int nkeys;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ funcname = "kadm5_randkey_principal";
+
+ if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
+ ret->code = randkey_principal_wrapper_3(handle, arg->princ,
+ arg->keepold, arg->n_ks_tuple,
+ arg->ks_tuple, &k, &nkeys);
+ } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_CHANGEPW, arg->princ, NULL)) {
+ ret->code = kadm5_randkey_principal_3(handle, arg->princ, arg->keepold,
+ arg->n_ks_tuple, arg->ks_tuple,
+ &k, &nkeys);
+ } else {
+ log_unauth(funcname, prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_CHANGEPW;
+ }
+
+ if (ret->code == KADM5_OK) {
+ ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
+ if (ret->code == KADM5_PROTECT_KEYS)
+ ret->code = KADM5_OK;
+ ret->keys = k;
+ ret->n_keys = nkeys;
+ }
+
+ if (ret->code != KADM5_AUTH_CHANGEPW) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done(funcname, prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+create_policy_2_svc(cpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+ &ret->api_version, &client_name, &service_name,
+ NULL);
+ if (ret->code)
+ goto exit_func;
+
+ prime_arg = arg->rec.policy;
+
+ if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_ADD, NULL, NULL)) {
+ ret->code = KADM5_AUTH_ADD;
+ log_unauth("kadm5_create_policy", prime_arg,
+ &client_name, &service_name, rqstp);
+
+ } else {
+ ret->code = kadm5_create_policy(handle, &arg->rec, arg->mask);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_create_policy",
+ ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+delete_policy_2_svc(dpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+ &ret->api_version, &client_name, &service_name,
+ NULL);
+ if (ret->code)
+ goto exit_func;
+
+ prime_arg = arg->name;
+
+ if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_DELETE, NULL, NULL)) {
+ log_unauth("kadm5_delete_policy", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_DELETE;
+ } else {
+ ret->code = kadm5_delete_policy(handle, arg->name);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_delete_policy",
+ ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+modify_policy_2_svc(mpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+ &ret->api_version, &client_name, &service_name,
+ NULL);
+ if (ret->code)
+ goto exit_func;
+
+ prime_arg = arg->rec.policy;
+
+ if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_MODIFY, NULL, NULL)) {
+ log_unauth("kadm5_modify_policy", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_MODIFY;
+ } else {
+ ret->code = kadm5_modify_policy(handle, &arg->rec, arg->mask);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_modify_policy",
+ ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+get_policy_2_svc(gpol_arg *arg, gpol_ret *ret, struct svc_req *rqstp)
+{
+ char *funcname, *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_ret_t ret2;
+ kadm5_principal_ent_rec caller_ent;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+ &ret->api_version, &client_name, &service_name,
+ NULL);
+ if (ret->code)
+ goto exit_func;
+
+ funcname = "kadm5_get_policy";
+
+ prime_arg = arg->name;
+
+ ret->code = KADM5_AUTH_GET;
+ if (!CHANGEPW_SERVICE(rqstp) && kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_INQUIRE, NULL, 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) {
+ ret->code = kadm5_get_policy(handle, arg->name, &ret->rec);
+
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done(funcname,
+ ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
+ &client_name, &service_name, rqstp);
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+
+ } else {
+ log_unauth(funcname, prime_arg,
+ &client_name, &service_name, rqstp);
+ }
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+get_pols_2_svc(gpols_arg *arg, gpols_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+ &ret->api_version, &client_name, &service_name,
+ NULL);
+ if (ret->code)
+ goto exit_func;
+
+ prime_arg = arg->exp;
+ if (prime_arg == NULL)
+ prime_arg = "*";
+
+ if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_LIST, NULL, NULL)) {
+ ret->code = KADM5_AUTH_LIST;
+ log_unauth("kadm5_get_policies", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_get_policies(handle, arg->exp, &ret->pols,
+ &ret->count);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_get_policies", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+get_privs_2_svc(krb5_ui_4 *arg, getprivs_ret *ret, struct svc_req *rqstp)
+{
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version,
+ &client_name, &service_name, NULL);
+ if (ret->code)
+ goto exit_func;
+
+ ret->code = kadm5_get_privs(handle, &ret->privs);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_get_privs", client_name.value, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+purgekeys_2_svc(purgekeys_arg *arg, generic_ret *ret, struct svc_req *rqstp)
+{
+ char *funcname, *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ funcname = "kadm5_purgekeys";
+
+ if (!cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
+ (CHANGEPW_SERVICE(rqstp)
+ || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY,
+ arg->princ, NULL))) {
+ ret->code = KADM5_AUTH_MODIFY;
+ log_unauth(funcname, prime_arg, &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_purgekeys(handle, arg->princ, arg->keepkvno);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done(funcname, prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+get_strings_2_svc(gstrings_arg *arg, gstrings_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
+ (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
+ rqst2name(rqstp),
+ ACL_INQUIRE,
+ arg->princ,
+ NULL))) {
+ ret->code = KADM5_AUTH_GET;
+ log_unauth("kadm5_get_strings", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_get_strings(handle, arg->princ, &ret->strings,
+ &ret->count);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_get_strings", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+set_string_2_svc(sstring_arg *arg, generic_ret *ret, struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ if (CHANGEPW_SERVICE(rqstp)
+ || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY,
+ arg->princ, NULL)) {
+ ret->code = KADM5_AUTH_MODIFY;
+ log_unauth("kadm5_mod_strings", prime_arg,
+ &client_name, &service_name, rqstp);
+ } else {
+ ret->code = kadm5_set_string(handle, arg->princ, arg->key, arg->value);
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_mod_strings", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
+
+bool_t
+init_2_svc(krb5_ui_4 *arg, generic_ret *ret, struct svc_req *rqstp)
+{
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+ size_t clen, slen;
+ char *cdots, *sdots;
+
+ ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version,
+ &client_name, &service_name, NULL);
+ if (ret->code)
+ goto exit_func;
+
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ clen = client_name.length;
+ trunc_name(&clen, &cdots);
+ slen = service_name.length;
+ trunc_name(&slen, &sdots);
+ /* okay to cast lengths to int because trunc_name limits max value */
+ krb5_klog_syslog(LOG_NOTICE, _("Request: kadm5_init, %.*s%s, %s, "
+ "client=%.*s%s, service=%.*s%s, addr=%s, "
+ "vers=%d, flavor=%d"),
+ (int)clen, (char *)client_name.value, cdots,
+ errmsg ? errmsg : _("success"),
+ (int)clen, (char *)client_name.value, cdots,
+ (int)slen, (char *)service_name.value, sdots,
+ client_addr(rqstp->rq_xprt),
+ ret->api_version & ~(KADM5_API_VERSION_MASK),
+ rqstp->rq_cred.oa_flavor);
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+
+exit_func:
+ stub_cleanup(handle, NULL, &client_name, &service_name);
+ return TRUE;
+}
+
+gss_name_t
+rqst2name(struct svc_req *rqstp)
+{
+
+ if (rqstp->rq_cred.oa_flavor == RPCSEC_GSS)
+ return rqstp->rq_clntname;
+ else
+ return rqstp->rq_clntcred;
+}
+
+bool_t
+get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret,
+ struct svc_req *rqstp)
+{
+ char *prime_arg = NULL;
+ gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER;
+ kadm5_server_handle_t handle;
+ const char *errmsg = NULL;
+
+ ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+ &ret->api_version, &client_name, &service_name,
+ &prime_arg);
+ if (ret->code)
+ goto exit_func;
+
+ if (!(CHANGEPW_SERVICE(rqstp)) &&
+ kadm5int_acl_check(handle->context, rqst2name(rqstp),
+ ACL_EXTRACT, arg->princ, NULL)) {
+ ret->code = kadm5_get_principal_keys(handle, arg->princ, arg->kvno,
+ &ret->key_data, &ret->n_key_data);
+ } else {
+ log_unauth("kadm5_get_principal_keys", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_EXTRACT;
+ }
+
+ if (ret->code == KADM5_OK) {
+ ret->code = check_lockdown_keys(handle, arg->princ);
+ if (ret->code != KADM5_OK) {
+ kadm5_free_kadm5_key_data(handle->context, ret->n_key_data,
+ ret->key_data);
+ ret->key_data = NULL;
+ ret->n_key_data = 0;
+ }
+ if (ret->code == KADM5_PROTECT_KEYS) {
+ log_unauth("kadm5_get_principal_keys", prime_arg,
+ &client_name, &service_name, rqstp);
+ ret->code = KADM5_AUTH_EXTRACT;
+ }
+ }
+
+ if (ret->code != KADM5_AUTH_EXTRACT) {
+ if (ret->code != 0)
+ errmsg = krb5_get_error_message(handle->context, ret->code);
+
+ log_done("kadm5_get_principal_keys", prime_arg, errmsg,
+ &client_name, &service_name, rqstp);
+
+ if (errmsg != NULL)
+ krb5_free_error_message(handle->context, errmsg);
+ }
+
+exit_func:
+ stub_cleanup(handle, prime_arg, &client_name, &service_name);
+ return TRUE;
+}
diff --git a/src/kadmin/testing/Makefile.in b/src/kadmin/testing/Makefile.in
new file mode 100644
index 000000000000..e5bcc743419c
--- /dev/null
+++ b/src/kadmin/testing/Makefile.in
@@ -0,0 +1,8 @@
+mydir=kadmin$(S)testing
+BUILDTOP=$(REL)..$(S)..
+SUBDIRS = scripts util
+
+all:
+
+clean:
+ -$(RM) -r krb5-test-root admin_* init-* kadmin_* kdc_rcache.* ovsec-*
diff --git a/src/kadmin/testing/deps b/src/kadmin/testing/deps
new file mode 100644
index 000000000000..2feac3c9d388
--- /dev/null
+++ b/src/kadmin/testing/deps
@@ -0,0 +1 @@
+# No dependencies here.
diff --git a/src/kadmin/testing/proto/kdc.conf.proto b/src/kadmin/testing/proto/kdc.conf.proto
new file mode 100644
index 000000000000..61283ac77550
--- /dev/null
+++ b/src/kadmin/testing/proto/kdc.conf.proto
@@ -0,0 +1,16 @@
+[kdcdefaults]
+ kdc_listen = 1750
+ kdc_tcp_listen = 1750
+
+[realms]
+ __REALM__ = {
+ profile = __K5ROOT__/krb5.conf
+ database_name = __K5ROOT__/kdb5
+ key_stash_file = __K5ROOT__/.k5.__REALM__
+ acl_file = __K5ROOT__/ovsec_adm.acl
+ dict_file = __K5ROOT__/ovsec_adm.dict
+ kadmind_port = 1751
+ kpasswd_port = 1752
+ master_key_type = des3-hmac-sha1
+ supported_enctypes = des3-hmac-sha1:normal des-cbc-crc:normal des-cbc-crc:v4 des-cbc-md5:normal des-cbc-raw:normal
+ }
diff --git a/src/kadmin/testing/proto/krb5.conf.proto b/src/kadmin/testing/proto/krb5.conf.proto
new file mode 100644
index 000000000000..00c44297806d
--- /dev/null
+++ b/src/kadmin/testing/proto/krb5.conf.proto
@@ -0,0 +1,31 @@
+[libdefaults]
+ default_realm = __REALM__
+ default_keytab_name = FILE:__K5ROOT__/v5srvtab
+ dns_fallback = no
+ plugin_base_dir = __PLUGIN_DIR__
+ allow_weak_crypto = true
+
+[realms]
+ __REALM__ = {
+ kdc = __KDCHOST__:1750
+ admin_server = __KDCHOST__:1751
+ database_module = foobar_db2_module_blah
+ }
+
+[domain_realm]
+ __LOCALHOST__ = __REALM__
+ __KDCHOST__ = __REALM__
+
+[logging]
+ admin_server = FILE:__K5ROOT__/syslog
+ kdc = FILE:__K5ROOT__/syslog
+ default = FILE:__K5ROOT__/syslog
+
+
+# THIS SHOULD BE IN KDC.CONF INSTEAD!
+[dbmodules]
+ db_module_dir = __MODDIR__
+ foobar_db2_module_blah = {
+ db_library = db2
+ database_name = __K5ROOT__/kdb5
+ }
diff --git a/src/kadmin/testing/proto/ovsec_adm.dict b/src/kadmin/testing/proto/ovsec_adm.dict
new file mode 100644
index 000000000000..b54e3a85e105
--- /dev/null
+++ b/src/kadmin/testing/proto/ovsec_adm.dict
@@ -0,0 +1,3 @@
+Abyssinia
+Discordianism
+foo
diff --git a/src/kadmin/testing/scripts/Makefile.in b/src/kadmin/testing/scripts/Makefile.in
new file mode 100644
index 000000000000..8c0d2362a12d
--- /dev/null
+++ b/src/kadmin/testing/scripts/Makefile.in
@@ -0,0 +1,31 @@
+mydir=kadmin$(S)testing$(S)scripts
+BUILDTOP=$(REL)..$(S)..$(S)..
+PERL_PATH=@PERL_PATH@
+
+.SUFFIXES: .plin .pl
+
+GEN_SCRIPTS = compare_dump.pl make-host-keytab.pl \
+ simple_dump.pl verify_xrunner_report.pl qualname.pl
+
+all: env-setup.sh $(GEN_SCRIPTS)
+
+# Should only rebuild env_setup.sh here (use CONFIG_FILES=), but the weird krb5
+# makefile post-processing is unconditional and would trash the makefile.
+env-setup.sh: env-setup.stamp
+env-setup.stamp: $(srcdir)/env-setup.shin $(BUILDTOP)/config.status \
+ Makefile
+ (cd $(BUILDTOP) && \
+ CONFIG_FILES=$(mydir)/env-setup.sh:$(mydir)/env-setup.shin $(SHELL) \
+ config.status)
+ chmod +x env-setup.sh
+ touch env-setup.stamp
+
+.plin.pl:
+ -rm -f $@.tmp
+ echo "#!$(PERL_PATH)" > $@.tmp
+ sed 1d $< >> $@.tmp
+ chmod +x $@.tmp
+ mv $@.tmp $@
+
+clean:
+ -rm -f $(GEN_SCRIPTS) *.tmp env-setup.sh env-setup.stamp
diff --git a/src/kadmin/testing/scripts/compare_dump.plin b/src/kadmin/testing/scripts/compare_dump.plin
new file mode 100755
index 000000000000..df93df4a0014
--- /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/deps b/src/kadmin/testing/scripts/deps
new file mode 100644
index 000000000000..2feac3c9d388
--- /dev/null
+++ b/src/kadmin/testing/scripts/deps
@@ -0,0 +1 @@
+# No dependencies here.
diff --git a/src/kadmin/testing/scripts/env-setup.shin b/src/kadmin/testing/scripts/env-setup.shin
new file mode 100755
index 000000000000..c8d866f153f0
--- /dev/null
+++ b/src/kadmin/testing/scripts/env-setup.shin
@@ -0,0 +1,111 @@
+#!/bin/sh
+#
+# The KADM5 unit tests were developed to work under gmake. As a
+# result, they expect to inherit a number of environment variables.
+# Rather than rewrite the tests, we simply use this script as an
+# execution wrapper that sets all the necessary environment variables
+# before running the program specified on its command line.
+#
+# The variable settings all came from OV's config.mk.
+#
+# Usage: env-setup.sh <command line>
+#
+
+TOP=@RBUILD@/kadmin
+STOP=@S_TOP@/kadmin
+export TOP
+export STOP
+# These two may be needed in case $libdir references them.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@ ; eval "libdir=$libdir"; export libdir
+
+# The shared library run time setup
+TOPLIBD=@RBUILD@/lib
+PROG_LIBPATH=-L@RBUILD@/lib
+BUILDTOP=@RBUILD@
+# XXX kludge!
+PROG_RPATH=@RBUILD@/lib
+# This converts $(TOPLIBD) to $TOPLIBD
+cat > /tmp/env_setup$$ <<\EOF
+@KRB5_RUN_ENV@
+EOF
+
+foo=`sed -e 's/(//g' -e 's/)//g' -e 's/\\\$\\\$/\$/g' /tmp/env_setup$$`
+eval $foo
+export @KRB5_RUN_VARS@
+
+# This will get put in setup.csh for convenience
+KRB5_RUN_ENV_CSH=`eval echo "$foo" | \
+ sed -e 's/\([^=]*\)=\(.*\)/setenv \1 \2/g'`
+export KRB5_RUN_ENV_CSH
+rm /tmp/env_setup$$
+
+TESTDIR=$TOP/testing; export TESTDIR
+STESTDIR=$STOP/testing; export STESTDIR
+if [ "$K5ROOT" = "" ]; then
+ K5ROOT="`cd $TESTDIR; pwd`/krb5-test-root"
+ export K5ROOT
+fi
+
+# If $VERBOSE_TEST is non-null, enter verbose mode. Set $VERBOSE to
+# true or false so its exit status identifies the mode.
+if test x$VERBOSE_TEST = x; then
+ VERBOSE=false
+else
+ VERBOSE=true
+fi
+export VERBOSE
+
+REALM=SECURE-TEST.OV.COM; export REALM
+
+if test x$EXPECT = x; then
+ EXPECT=@EXPECT@; export EXPECT
+fi
+
+COMPARE_DUMP=$TESTDIR/scripts/compare_dump.pl; export COMPARE_DUMP
+INITDB=$STESTDIR/scripts/init_db; export INITDB
+MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl; export MAKE_KEYTAB
+LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl
+export LOCAL_MAKE_KEYTAB
+SIMPLE_DUMP=$TESTDIR/scripts/simple_dump.pl; export SIMPLE_DUMP
+QUALNAME=$TESTDIR/scripts/qualname.pl; export QUALNAME
+TCLUTIL=$STESTDIR/tcl/util.t; export TCLUTIL
+BSDDB_DUMP=$TESTDIR/util/bsddb_dump; export BSDDB_DUMP
+CLNTTCL=$TESTDIR/util/kadm5_clnt_tcl; export CLNTTCL
+SRVTCL=$TESTDIR/util/kadm5_srv_tcl; export SRVTCL
+
+KRB5_CONFIG=$K5ROOT/krb5.conf; export KRB5_CONFIG
+KRB5_KDC_PROFILE=$K5ROOT/kdc.conf; export KRB5_KDC_PROFILE
+KRB5_KTNAME=$K5ROOT/ovsec_adm.srvtab; export KRB5_KTNAME
+KRB5_CLIENT_KTNAME=$K5ROOT/client_keytab; export KRB5_CLIENT_KTNAME
+KRB5CCNAME=$K5ROOT/krb5cc_unit-test; export KRB5CCNAME
+
+# Make sure we don't get confused by translated messages
+# or localized times.
+LC_ALL=C; export LC_ALL
+
+if [ "$TEST_SERVER" != "" ]; then
+ MAKE_KEYTAB="$MAKE_KEYTAB -server $TEST_SERVER"
+fi
+if [ "$TEST_PATH" != "" ]; then
+ MAKE_KEYTAB="$MAKE_KEYTAB -top $TEST_PATH"
+fi
+
+if [ "x$PS_ALL" = "x" ]; then
+ if ps auxww >/dev/null 2>&1; then
+ PS_ALL="ps auxww"
+ PS_PID="ps uwwp"
+ elif ps -ef >/dev/null 2>&1; then
+ PS_ALL="ps -ef"
+ PS_PID="ps -fp"
+ else
+ PS_ALL="ps auxww"
+ PS_PID="ps uwwp"
+ echo "WARNING! Cannot auto-detect ps type, assuming BSD."
+ fi
+
+ export PS_ALL PS_PID
+fi
+
+exec ${1+"$@"}
diff --git a/src/kadmin/testing/scripts/find-make.sh b/src/kadmin/testing/scripts/find-make.sh
new file mode 100755
index 000000000000..904730dfa0da
--- /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/init_db b/src/kadmin/testing/scripts/init_db
new file mode 100755
index 000000000000..cd71656288cb
--- /dev/null
+++ b/src/kadmin/testing/scripts/init_db
@@ -0,0 +1,232 @@
+#!/bin/sh
+
+if $VERBOSE; then
+ REDIRECT=
+else
+ REDIRECT='>/dev/null'
+fi
+
+# Requires that $K5ROOT, /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
+
+if [ "$STOP" = "" ]; then
+ echo "init_db: Environment variable \$STOP must point to top of source tree" 1>&2
+ exit 1
+fi
+
+if [ "$libdir" = "" ]; then
+ echo "init_db: Environment variable \$libdir must point to library install directory" 1>&2
+ exit 1
+fi
+
+IROOT=$TOP/..
+ADMIN=$TOP/dbutil
+BIN=$IROOT/bin
+ETC=$IROOT/etc
+MODDIR=$TOP/../plugins/kdb
+SBIN=$TOP/keytab:$TOP/server
+DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM
+
+if [ ! -d $MODDIR ]; then
+ echo "+++" 1>&2
+ echo "+++ Error! $MODDIR does not exist!" 1>&2
+ echo "+++ The MODDIR variable should point to the directory in which" 1>&2
+ echo "+++ database modules have been installed for testing." 1>&2
+ echo "+++" 1>&2
+ exit 1
+fi
+
+DUMMY=${TESTDIR=$TOP/testing}; export TESTDIR
+DUMMY=${STESTDIR=$STOP/testing}
+DUMMY=${SRVTCL=$TESTDIR/util/kadm5_srv_tcl}; export SRVTCL
+DUMMY=${TCLUTIL=$STESTDIR/tcl/util.t}; export TCLUTIL
+DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl}
+
+PATH=$ADMIN:$BIN:$ETC:$SBIN:$PATH; export PATH
+
+if [ ! -x $SRVTCL ]; then
+ echo "+++" 1>&2
+ echo "+++ Error! $SRVTCL does not exist!" 1>&2
+ echo "+++ It was probably not compiled because TCL was not available. If you" 1>&2
+ echo "+++ now have TCL installed, cd into that directory, re-run configure" 1>&2
+ echo "+++ with the --with-tcl option, and then re-run make." 1>&2
+ echo "+++" 1>&2
+
+ exit 1
+fi
+
+rm -rf $K5ROOT/*
+if [ -d $K5ROOT ]; then
+ true
+else
+ mkdir $K5ROOT
+fi
+
+# touch $K5ROOT/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
+
+qualname=`$QUALNAME`
+
+sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \
+ -e "s/__KDCHOST__/$qualname/g" \
+ -e "s/__LOCALHOST__/$qualname/g" \
+ -e "s#__MODDIR__#$MODDIR#g" \
+ < $STESTDIR/proto/krb5.conf.proto > $K5ROOT/krb5.conf
+sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \
+ < $STESTDIR/proto/kdc.conf.proto > $K5ROOT/kdc.conf
+
+eval kdb5_util -r $REALM create -W -P mrroot -s $REDIRECT || exit 1
+
+cp $STESTDIR/proto/ovsec_adm.dict $K5ROOT/ovsec_adm.dict
+
+cat - > /tmp/init_db$$ <<\EOF
+source $env(TCLUTIL)
+set r $env(REALM)
+if {[info exists env(USER)]} {
+ set whoami $env(USER)
+} else {
+ set whoami [exec whoami]
+}
+
+set cmds {
+ {kadm5_init $env(SRVTCL) mrroot null \
+ [config_params {KADM5_CONFIG_REALM} $r] $KADM5_STRUCT_VERSION \
+ $KADM5_API_VERSION_3 server_handle}
+
+ {kadm5_create_policy $server_handle "test-pol 0 10000 8 2 3 0 2 90 180" \
+ {KADM5_POLICY KADM5_PW_MIN_LENGTH KADM5_PW_MIN_CLASSES KADM5_PW_MAX_LIFE KADM5_PW_HISTORY_NUM KADM5_PW_MAX_FAILURE KADM5_PW_FAILURE_COUNT_INTERVAL KADM5_PW_LOCKOUT_DURATION}}
+ {kadm5_create_policy $server_handle "once-a-min 10 0 0 0 0 0 0 0 0" \
+ {KADM5_POLICY KADM5_PW_MIN_LIFE}}
+ {kadm5_create_policy $server_handle "dict-only 0 0 0 0 0 0 0 0 0" \
+ {KADM5_POLICY}}
+ {kadm5_create_policy $server_handle [simple_policy test-pol-nopw] \
+ {KADM5_POLICY}}
+
+ {kadm5_create_principal $server_handle \
+ [simple_principal testuser@$r] {KADM5_PRINCIPAL} notathena}
+ {kadm5_create_principal $server_handle \
+ [simple_principal test1@$r] {KADM5_PRINCIPAL} test1}
+ {kadm5_create_principal $server_handle \
+ [simple_principal test2@$r] {KADM5_PRINCIPAL} test2}
+ {kadm5_create_principal $server_handle \
+ [simple_principal test3@$r] {KADM5_PRINCIPAL} test3}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/get@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/modify@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/delete@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/add@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/none@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/rename@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/mod-add@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/mod-delete@$r] {KADM5_PRINCIPAL} \
+ admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/get-add@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/get-delete@$r] {KADM5_PRINCIPAL} \
+ admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/get-mod@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/no-add@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [simple_principal admin/no-delete@$r] {KADM5_PRINCIPAL} admin}
+ {kadm5_create_principal $server_handle \
+ [princ_w_pol pol1@$r test-pol] {KADM5_PRINCIPAL \
+ KADM5_POLICY} pol111111}
+ {kadm5_create_principal $server_handle \
+ [princ_w_pol pol2@$r once-a-min] {KADM5_PRINCIPAL \
+ KADM5_POLICY} pol222222}
+ {kadm5_create_principal $server_handle \
+ [princ_w_pol pol3@$r dict-only] {KADM5_PRINCIPAL \
+ KADM5_POLICY} pol333333}
+ {kadm5_create_principal $server_handle \
+ [princ_w_pol admin/get-pol@$r test-pol-nopw] \
+ {KADM5_PRINCIPAL KADM5_POLICY} StupidAdmin}
+ {kadm5_create_principal $server_handle \
+ [princ_w_pol admin/pol@$r test-pol-nopw] {KADM5_PRINCIPAL \
+ KADM5_POLICY} StupidAdmin}
+
+ {kadm5_create_principal $server_handle \
+ [simple_principal changepw/kerberos] \
+ {KADM5_PRINCIPAL} {XXX THIS IS WRONG}}
+
+ {kadm5_create_principal $server_handle \
+ [simple_principal $whoami] \
+ {KADM5_PRINCIPAL} $whoami}
+
+ {kadm5_create_principal $server_handle \
+ [simple_principal testkeys@$r] {KADM5_PRINCIPAL} testkeys}
+
+ {kadm5_destroy $server_handle}
+}
+
+foreach cmd $cmds {
+ if {[catch $cmd output]} {
+ puts stderr "Error! Command: $cmd\nError: $output"
+ exit 1
+ } else {
+ puts stdout $output
+ }
+}
+EOF
+eval "$SRVTCL < /tmp/init_db$$ $REDIRECT"
+rm /tmp/init_db$$
+
+if [ $? -ne 0 ]; then
+ echo "Error in $SRVTCL!" 1>&2
+ exit 1
+fi
+
+cat > $K5ROOT/ovsec_adm.acl <<EOF
+admin@$REALM admcilse
+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 $K5ROOT/ovsec_adm.srvtab $REDIRECT
+
+# Create $K5ROOT/setup.csh to make it easy to run other programs against
+# the test db
+cat > $K5ROOT/setup.csh <<EOF
+setenv KRB5_CONFIG $KRB5_CONFIG
+setenv KRB5_KDC_PROFILE $KRB5_KDC_PROFILE
+setenv KRB5_KTNAME $KRB5_KTNAME
+setenv KRB5_CLIENT_KTNAME $KRB5_CLIENT_KTNAME
+$KRB5_RUN_ENV_CSH
+EOF
+
diff --git a/src/kadmin/testing/scripts/make-host-keytab.plin b/src/kadmin/testing/scripts/make-host-keytab.plin
new file mode 100755
index 000000000000..dfe0b3a01380
--- /dev/null
+++ b/src/kadmin/testing/scripts/make-host-keytab.plin
@@ -0,0 +1,144 @@
+#!/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);
+($canonhost2) = gethostbyaddr($addrs[0],$addrtype);
+if ($canonhost2) { $canonhost = $canonhost2; }
+
+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/kadm5_srv_tcl");
+$TCLUTIL = ($ENV{'TCLUTIL'} || "$TESTDIR/tcl/util.t");
+# This'll be wrong sometimes
+$RSH_CMD = ($ENV{'RSH_CMD'} || '/usr/ucb/rsh');
+$KADMIN = ($ENV{'KADMIN'} || "$top/cli/kadmin.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);
+
+ # We can ignore errors here, because the ktadd below will fail if
+ # this fails for any reason other than "principal exists"
+ for (@princs) {
+ next if (/^kadmin/);
+ $cmd = "$KADMIN -q 'ank -randkey $_' $redirect 2>&1";
+ system($cmd);
+ }
+
+ $cmd = "$KADMIN -q 'ktadd -k $ktfile ";
+ $cmd .= " -q " if (! $verbose);
+ $cmd .= "@princs' $redirect";
+ 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.plin b/src/kadmin/testing/scripts/qualname.plin
new file mode 100755
index 000000000000..b712d89738eb
--- /dev/null
+++ b/src/kadmin/testing/scripts/qualname.plin
@@ -0,0 +1,19 @@
+#!/afs/athena/contrib/perl/p
+
+if ($#ARGV == -1) {
+ chop($hostname = `hostname`);
+} else {
+ $hostname = $ARGV[0];
+}
+
+if (! (($name,$type,$addr) = (gethostbyname($hostname))[0,2,4])) {
+ print STDERR "No such host: $hostname\n";
+ exit(1);
+}
+if (! ($qualname = (gethostbyaddr($addr,$type))[0])) {
+ $qualname = $name;
+}
+
+$qualname =~ tr/A-Z/a-z/; # lowercase our name for keytab use.
+print "$qualname\n";
+
diff --git a/src/kadmin/testing/scripts/simple_dump.plin b/src/kadmin/testing/scripts/simple_dump.plin
new file mode 100755
index 000000000000..ea94ab2d1dde
--- /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 100755
index 000000000000..87bd52fc0ba6
--- /dev/null
+++ b/src/kadmin/testing/scripts/start_servers
@@ -0,0 +1,71 @@
+#!/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=${STESTDIR=$STOP/testing}
+DUMMY=${START_SERVERS_LOCAL=$STESTDIR/scripts/start_servers_local}
+# This'll be wrong sometimes
+DUMMY=${RSH_CMD=rsh}
+
+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
+
+ # Fix up the local krb5.conf to point to the remote
+ localname=`$QUALNAME`
+ sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \
+ -e "s/__KDCHOST__/$hostname/g" \
+ -e "s/__LOCALHOST__/$localname/g" \
+ -e "s#__MODDIR__#$TOP/../plugins/kdb#g"\
+ -e "s#__PLUGIN_DIR__#$TOP/../plugins#g"\
+ < $STESTDIR/proto/krb5.conf.proto > $K5ROOT/krb5.conf
+
+# 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 -n \
+ \"sh -c 'VERBOSE_TEST=$VERBOSE_TEST TOP=$rempath \
+ $rempath/testing/scripts/env-setup.sh \
+ $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 100755
index 000000000000..0cbed462d1ad
--- /dev/null
+++ b/src/kadmin/testing/scripts/start_servers_local
@@ -0,0 +1,161 @@
+#!/bin/sh
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${STESTDIR=$STOP/testing}
+DUMMY=${INITDB=$STESTDIR/scripts/init_db}
+DUMMY=${SRVTCL=$TESTDIR/util/kadm5_srv_tcl}; export SRVTCL
+DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl}
+DUMMY=${STOP_SERVERS_LOCAL=$STESTDIR/scripts/stop_servers_local}
+DUMMY=${KRB5RCACHEDIR=$TESTDIR} ; export KRB5RCACHEDIR
+
+if [ -d /usr/tmp ]; then
+ usrtmp=/usr/tmp
+else
+ usrtmp=/var/tmp
+fi
+
+$STOP_SERVERS_LOCAL -start_servers
+
+if $VERBOSE; then
+ REDIRECT=
+else
+ REDIRECT='>/dev/null'
+fi
+
+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
+ ;;
+ *)
+ 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
+
+# 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" < $K5ROOT/kdc.conf > $K5ROOT/kdc.conf.new
+ mv $K5ROOT/kdc.conf.new $K5ROOT/kdc.conf
+fi
+if [ "$kdcport" != "" ] ; then
+ sedcmd="s/\(kdc_ports = .*\)[ ]*/\1, $kdcport/"
+ sed -e "$sedcmd" < $K5ROOT/kdc.conf > $K5ROOT/kdc.conf.new
+ mv $K5ROOT/kdc.conf.new $K5ROOT/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.pl $hostname`; export QUALNAME
+
+cat - > /tmp/start_servers_local$$ <<\EOF
+if { [catch {
+ source $env(STOP)/testing/tcl/util.t
+ set r $env(REALM)
+ set q $env(QUALNAME)
+ puts stdout [kadm5_init $env(SRVTCL) mrroot null \
+ [config_params {KADM5_CONFIG_REALM} $r] \
+ $KADM5_STRUCT_VERSION $KADM5_API_VERSION_3 server_handle]
+ puts stdout [kadm5_create_principal $server_handle \
+ [simple_principal host/$q@$r] {KADM5_PRINCIPAL} notathena]
+ puts stdout [kadm5_destroy $server_handle]
+} err]} {
+ puts stderr "initialization error: $err"
+ exit 1
+}
+exit 0
+EOF
+eval "$SRVTCL < /tmp/start_servers_local$$ $REDIRECT"
+x=$?
+rm /tmp/start_servers_local$$
+if test $x != 0 ; then exit 1 ; fi
+
+# 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
+
+if test "x$USER" = x ; then
+ USER=$LOGNAME ; export USER
+fi
+
+kdc_args="-R dfl:kdc_rcache.$USER"
+
+(trap "" 2; cd $TOP/../kdc; ./krb5kdc $kdc_args; touch $kdc_start_file) \
+ < /dev/null > $usrtmp/kdc-log.$USER 2>&1 &
+
+s=1
+max_s=60
+sofar_s=0
+timewait_s=300
+
+ovadm_args=-W
+
+rm -f $adm_start_file
+
+(sleep 1; cd $TOP/server; ./kadmind $ovadm_args; \
+ touch $adm_start_file) < /dev/null > $usrtmp/kadm-log.$USER 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 -gt $max_s ]; then
+ echo "Admin server or KDC failed to start after $sofar_s" \
+ "seconds." 1>&2
+ if [ ! -f $adm_start_file ]; then
+ echo " No admin server start file $adm_start_file." 1>&2
+ fi
+ if [ ! -f $kdc_start_file ]; then
+ echo " No KDC start file $adm_start_file." 1>&2
+ fi
+ exit 1
+fi
+
+rm -f $kdc_start_file $adm_start_file
diff --git a/src/kadmin/testing/scripts/stop_servers b/src/kadmin/testing/scripts/stop_servers
new file mode 100755
index 000000000000..b7f8384caccc
--- /dev/null
+++ b/src/kadmin/testing/scripts/stop_servers
@@ -0,0 +1,60 @@
+#!/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=${STESTDIR=$STOP/testing}
+DUMMY=${STOP_SERVERS_LOCAL=$STESTDIR/scripts/stop_servers_local}
+# This'll be wrong sometimes
+DUMMY=${RSH_CMD=rsh}
+
+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
+
+ STOP_SERVERS_LOCAL=`echo $STOP_SERVERS_LOCAL | sed "s%$TOP%$rempath%"`
+ CMD="$RSH_CMD $hostname -n \
+ \"sh -c 'VERBOSE_TEST=$VERBOSE_TEST TOP=$rempath \
+ $rempath/testing/scripts/env-setup.sh \
+ $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
+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 100755
index 000000000000..24a9de7b39da
--- /dev/null
+++ b/src/kadmin/testing/scripts/stop_servers_local
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+DUMMY=${TESTDIR=$TOP/testing}
+DUMMY=${KRB5RCACHEDIR=$TESTDIR}
+
+while [ $# -gt 0 ] ; do
+ case $1 in
+ -start_servers)
+ start_servers=$1
+ ;;
+ *)
+ 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
+
+# Destroy the kdc replay cache so we don't lose if we try to run the
+# KDC as another unix user.
+if test "x$USER" = x ; then
+ USER=$LOGNAME
+fi
+rm -f $KRB5RCACHEDIR/krb5kdc_rcache.$USER
+
+exit 0
diff --git a/src/kadmin/testing/scripts/verify_xrunner_report.plin b/src/kadmin/testing/scripts/verify_xrunner_report.plin
new file mode 100755
index 000000000000..9d83c3ea247d
--- /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 000000000000..6751f89e63da
--- /dev/null
+++ b/src/kadmin/testing/tcl/util.t
@@ -0,0 +1,58 @@
+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 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 {}]
+ 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_KADMIND_PORT" {
+ set params [lreplace $params 2 2 $value]}
+ "KADM5_CONFIG_ADMIN_SERVER" {
+ set params [lreplace $params 3 3 $value]}
+ "KADM5_CONFIG_DBNAME" {set params [lreplace $params 4 4 $value]}
+ "KADM5_CONFIG_ADBNAME" {set params [lreplace $params 5 5 $value]}
+ "KADM5_CONFIG_ADB_LOCKFILE" {
+ set params [lreplace $params 6 6 $value]}
+ "KADM5_CONFIG_ACL_FILE" {set params [lreplace $params 8 8 $value]}
+ "KADM5_CONFIG_DICT_FILE" {
+ set params [lreplace $params 9 9 $value]}
+ "KADM5_CONFIG_MKEY_FROM_KBD" {
+ set params [lreplace $params 10 10 $value]}
+ "KADM5_CONFIG_STASH_FILE" {
+ set params [lreplace $params 11 11 $value]}
+ "KADM5_CONFIG_MKEY_NAME" {
+ set params [lreplace $params 12 12 $value]}
+ "KADM5_CONFIG_ENCTYPE" {set params [lreplace $params 13 13 $value]}
+ "KADM5_CONFIG_MAX_LIFE" {
+ set params [lreplace $params 14 14 $value]}
+ "KADM5_CONFIG_MAX_RLIFE" {
+ set params [lreplace $params 15 15 $value]}
+ "KADM5_CONFIG_EXPIRATION" {
+ set params [lreplace $params 16 16 $value]}
+ "KADM5_CONFIG_FLAGS" {set params [lreplace $params 17 17 $value]}
+ "KADM5_CONFIG_ENCTYPES" {
+ set params [lreplace $params 18 19 [llength $value] $value]}
+ "*" {error "config_params: unknown mask $mask"}
+ }
+ }
+ return $params
+}
+
+
+
diff --git a/src/kadmin/testing/util/Makefile.in b/src/kadmin/testing/util/Makefile.in
new file mode 100644
index 000000000000..7785c742ea3d
--- /dev/null
+++ b/src/kadmin/testing/util/Makefile.in
@@ -0,0 +1,42 @@
+mydir=kadmin$(S)testing$(S)util
+BUILDTOP=$(REL)..$(S)..$(S)..
+LOCALINCLUDES = $(TCL_INCLUDES) -I$(BUILDTOP)/lib/kdb/
+# Force Tcl headers to use stdarg.h, because krb5 does too, and if
+# Tcl uses varargs.h it'll just mess things up.
+DEFINES= -DHAS_STDARG
+KRB5_PTHREAD_LIB=$(THREAD_LINKOPTS)
+
+PROG_LIBPATH=-L$(TOPLIBD) $(TCL_LIBPATH)
+PROG_RPATH=$(KRB5_LIBDIR)$(TCL_RPATH)
+
+SRCS = $(srcdir)/tcl_kadm5.c $(srcdir)/test.c
+OBJS = tcl_kadm5.o test.o
+
+CLNTPROG= kadm5_clnt_tcl
+SRVPROG = kadm5_srv_tcl
+
+DO_ALL=@DO_ALL@
+
+all: all-$(DO_ALL)
+
+all-:
+ @echo "+++"
+ @echo "+++ WARNING: Tcl not available. The kadm5 tests will not be run."
+ @echo "+++"
+ @echo 'Skipped kadm5 tests: Tcl not found' >> $(SKIPTESTS)
+
+all-tcl: $(CLNTPROG) $(SRVPROG)
+
+$(SRVPROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $(SRVPROG) $(OBJS) $(TCL_MAYBE_RPATH) \
+ $(KADMSRV_LIBS) $(KRB5_PTHREAD_LIB) $(KRB5_BASE_LIBS) $(TCL_LIBS)
+
+$(CLNTPROG): $(OBJS) $(KADMCLNT_DEPLIBS) $(KRB5_BASE_DEPLIBS)
+ $(CC_LINK) -o $(CLNTPROG) $(OBJS) $(TCL_MAYBE_RPATH) \
+ $(KRB5_PTHREAD_LIB) $(KADMCLNT_LIBS) $(KRB5_BASE_LIBS) $(TCL_LIBS)
+
+bsddb_dump: bsddb_dump.o
+ $(CC_LINK) -o bsddb_dump bsddb_dump.o $(KADMSRV_LIBS)
+
+clean:
+ $(RM) $(CLNTPROG) $(SRVPROG)
diff --git a/src/kadmin/testing/util/bsddb_dump.c b/src/kadmin/testing/util/bsddb_dump.c
new file mode 100644
index 000000000000..5dbe7ae9c83b
--- /dev/null
+++ b/src/kadmin/testing/util/bsddb_dump.c
@@ -0,0 +1,65 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * $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/deps b/src/kadmin/testing/util/deps
new file mode 100644
index 000000000000..ca828a85cde3
--- /dev/null
+++ b/src/kadmin/testing/util/deps
@@ -0,0 +1,16 @@
+#
+# Generated makefile dependencies follow.
+#
+$(OUTPRE)tcl_kadm5.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+ $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+ $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+ $(BUILDTOP)/lib/kdb/adb_err.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+ $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+ $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+ $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+ $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+ $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/kdb.h \
+ $(top_srcdir)/include/krb5.h tcl_kadm5.c tcl_kadm5.h
+$(OUTPRE)test.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+ tcl_kadm5.h test.c
diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c
new file mode 100644
index 000000000000..a4997c60ca04
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_kadm5.c
@@ -0,0 +1,2568 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include "autoconf.h"
+#include <stdio.h>
+#include <string.h>
+#if HAVE_TCL_H
+#include <tcl.h>
+#elif HAVE_TCL_TCL_H
+#include <tcl/tcl.h>
+#endif
+#define USE_KADM5_API_VERSION 2
+#include <kadm5/admin.h>
+#include <com_err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <adb_err.h>
+#include "tcl_kadm5.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},
+ {"KADM5_PW_MAX_FAILURE", KADM5_PW_MAX_FAILURE},
+ {"KADM5_PW_FAILURE_COUNT_INTERVAL", KADM5_PW_FAILURE_COUNT_INTERVAL},
+ {"KADM5_PW_LOCKOUT_DURATION", KADM5_PW_LOCKOUT_DURATION},
+};
+
+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_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_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 {
+ 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, const char *name,
+ void **handle)
+{
+ Tcl_HashEntry *entry;
+
+ if(!strcasecmp(name, "null"))
+ *handle = 0;
+ else {
+ if (! (struct_table &&
+ (entry = Tcl_FindHashEntry(struct_table, name)))) {
+ 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, const 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; \
+ const char *whoami = argv[0]; \
+ argv++, argc--; \
+ if (argc != num_args + 1) { \
+ Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \
+ return TCL_ERROR; \
+ } \
+ { \
+ int ltcl_ret; \
+ if ((ltcl_ret = get_server_handle(interp, argv[0], &server_handle)) \
+ != TCL_OK) { \
+ return ltcl_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, const char *in_str, char **out_str)
+{
+ if (! in_str) {
+ *out_str = 0;
+ }
+ else if (! strcasecmp(in_str, "null")) {
+ *out_str = 0;
+ }
+ else {
+ *out_str = (char *) 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;
+ const char *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 KADM5_BAD_TL_TYPE: code_string = "KADM5_BAD_TL_TYPE"; break;
+ case KADM5_MISSING_CONF_PARAMS:
+ code_string = "KADM5_MISSING_CONF_PARAMS"; break;
+ case KADM5_BAD_SERVER_NAME:
+ code_string = "KADM5_BAD_SERVER_NAME"; break;
+ case KADM5_MISSING_KRB5_CONF_PARAMS:
+ code_string = "KADM5_MISSING_KRB5_CONF_PARAMS"; break;
+ case KADM5_XDR_FAILURE: code_string = "KADM5_XDR_FAILURE"; break;
+ case KADM5_CANT_RESOLVE: code_string = "KADM5_CANT_RESOLVE"; 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 KRB5_CC_NOTFOUND: code_string = "KRB5_CC_NOTFOUND"; break;
+ case KRB5_FCC_NOFILE: code_string = "KRB5_FCC_NOFILE"; break;
+
+ case EINVAL: code_string = "EINVAL"; break;
+ case ENOENT: code_string = "ENOENT"; break;
+
+ default:
+ fprintf(stderr, "**** CODE %ld (%s) ***\n", (long) code,
+ error_message (code));
+ code_string = "UNKNOWN";
+ break;
+ }
+
+ error_string = 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;
+ char buf[2048];
+
+ if (! (str = malloc(sizeof(*str)))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+
+ Tcl_DStringInit(str);
+ Tcl_DStringStartSublist(str);
+ for (; tl_data; tl_data = tl_data->tl_data_next) {
+ Tcl_DStringStartSublist(str);
+ sprintf(buf, "%d", tl_data->tl_data_type);
+ Tcl_DStringAppendElement(str, buf);
+ sprintf(buf, "%d", tl_data->tl_data_length);
+ Tcl_DStringAppendElement(str, buf);
+ Tcl_DStringAppend(str, " ", 1);
+ Tcl_DStringAppend(str, (char *) tl_data->tl_data_contents,
+ tl_data->tl_data_length);
+ Tcl_DStringEndSublist(str);
+ }
+ Tcl_DStringEndSublist(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, const char *str,
+ krb5_flags *flags)
+{
+ int tmp, argc, i, retcode = TCL_OK;
+ const char **argv;
+ Tcl_HashEntry *entry;
+
+ if (Tcl_GetInt(interp, str, &tmp) == TCL_OK) {
+ *flags = tmp;
+ return TCL_OK;
+ }
+ Tcl_ResetResult(interp);
+
+ if (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);
+ }
+
+ Tcl_Free((char *) 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, const 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, const 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, const 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, const 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,
+ krb5_int32 mask)
+{
+ 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 (mask & KADM5_PRINCIPAL) {
+ krb5_ret = krb5_unparse_name(context, princ->principal, &tmp);
+ if (krb5_ret) {
+ /* XXX Do we want to return an error? Not sure. */
+ Tcl_DStringAppendElement(str, "[unparseable principal]");
+ }
+ else {
+ Tcl_DStringAppendElement(str, tmp);
+ free(tmp);
+ }
+ } else
+ Tcl_DStringAppendElement(str, "null");
+
+ 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 (mask & KADM5_MOD_NAME) {
+ if ((krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp))) {
+ /* XXX */
+ Tcl_DStringAppendElement(str, "[unparseable principal]");
+ }
+ else {
+ Tcl_DStringAppendElement(str, tmp);
+ free(tmp);
+ }
+ } else
+ Tcl_DStringAppendElement(str, "null");
+
+ sprintf(buf, "%d", princ->mod_date);
+ Tcl_DStringAppendElement(str, buf);
+
+ if (mask & KADM5_ATTRIBUTES) {
+ tmp_dstring = unparse_krb5_flags(princ->attributes);
+ Tcl_DStringAppendElement(str, tmp_dstring->string);
+ Tcl_DStringFree(tmp_dstring);
+ free(tmp_dstring);
+ } else
+ Tcl_DStringAppendElement(str, "null");
+
+ 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, const char *list,
+ krb5_key_salt_tuple **keysalts,
+ int num_keysalts)
+{
+ const 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) {
+ Tcl_SetResult(interp, "wrong number of keysalts", TCL_STATIC);
+ 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) {
+ Tcl_SetResult(interp, "wrong # of fields in keysalt", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ /* XXX this used to be argv1[1] too! */
+ if ((retcode = Tcl_GetInt(interp, argv1[0], &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;
+
+ Tcl_Free((char *) argv1);
+ argv1 = NULL;
+ }
+
+finished:
+ if (argv1) {
+ Tcl_Free((char *) argv1);
+ }
+ Tcl_Free((char *) argv);
+ return retcode;
+}
+
+static int parse_key_data(Tcl_Interp *interp, const char *list,
+ krb5_key_data **key_data,
+ int n_key_data)
+{
+ const char **argv = NULL;
+ int argc, retcode;
+
+ *key_data = NULL;
+ if (list == NULL) {
+ if (n_key_data != 0) {
+ Tcl_SetResult(interp, "wrong number of key_datas", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ } else
+ return TCL_OK;
+ }
+
+ if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return retcode;
+ }
+ if (argc != n_key_data) {
+ Tcl_SetResult(interp, "wrong number of key_datas", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if (argc != 0) {
+ Tcl_SetResult(interp, "cannot parse key_data yet", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+finished:
+ Tcl_Free((char *) argv);
+ return retcode;
+}
+
+static int parse_tl_data(Tcl_Interp *interp, const char *list,
+ krb5_tl_data **tlp,
+ int n_tl_data)
+{
+ krb5_tl_data *tl, *tl2;
+ const char **argv = NULL, **argv1 = NULL;
+ int i, tmp, argc, argc1, retcode;
+
+ *tlp = NULL;
+ if (list == NULL) {
+ if (n_tl_data != 0) {
+ Tcl_SetResult(interp, "wrong number of tl_datas", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ } else
+ return TCL_OK;
+ }
+
+ if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) {
+ return retcode;
+ }
+ if (argc != n_tl_data) {
+ Tcl_SetResult(interp, "wrong number of tl_datas", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ tl = tl2 = NULL;
+ for (i = 0; i < n_tl_data; i++) {
+ tl2 = (krb5_tl_data *) malloc(sizeof(krb5_tl_data));
+ memset(tl2, 0, sizeof(krb5_tl_data));
+ tl2->tl_data_next = tl;
+ tl = tl2;
+ }
+ tl2 = tl;
+
+ for (i = 0; i < n_tl_data; i++) {
+ if ((retcode = Tcl_SplitList(interp, argv[i], &argc1, &argv1)) !=
+ TCL_OK) {
+ goto finished;
+ }
+ if (argc1 != 3) {
+ Tcl_SetResult(interp, "wrong # of fields in tl_data", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = Tcl_GetInt(interp, argv1[0], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing tl_data_type");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ tl->tl_data_type = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing tl_data_length");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ tl->tl_data_length = tmp;
+ if (tl->tl_data_length != strlen(argv1[2])) {
+ Tcl_SetResult(interp, "length != string length", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ tl->tl_data_contents = (krb5_octet *) strdup(argv1[2]);
+
+ Tcl_Free((char *) argv1);
+ argv1 = NULL;
+ tl = tl->tl_data_next;
+ }
+ if (tl != NULL) {
+ Tcl_SetResult(interp, "tl is not NULL!", TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ *tlp = tl2;
+
+finished:
+ if (argv1) {
+ Tcl_Free((char *) argv1);
+ }
+ Tcl_Free((char *) argv);
+ return retcode;
+}
+
+static int parse_config_params(Tcl_Interp *interp, char *list,
+ kadm5_config_params *params)
+{
+ static Tcl_HashTable *table = 0;
+ const 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 != 20) {
+ Tcl_SetResult(interp, "wrong # args in config params structure",
+ TCL_STATIC);
+ 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 = Tcl_GetInt(interp, argv[2], &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[3], &params->admin_server))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing profile name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[4], &params->dbname)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing profile name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ /* Ignore argv[5], which used to set the admin_dbname field. */
+ /* Ignore argv[6], which used to set the admin_lockfile field. */
+ /* Ignore argv[7], which used to set the admin_keytab field. */
+ if ((retcode = parse_str(interp, argv[8], &params->acl_file)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing acl_file name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[9], &params->dict_file)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing dict_file name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = Tcl_GetInt(interp, argv[10], &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[11], &params->stash_file)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing stash_file name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = parse_str(interp, argv[12], &params->mkey_name)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing mkey_name name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if ((retcode = Tcl_GetInt(interp, argv[13], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing enctype");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->enctype = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv[14], &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[15], &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[16], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing expiration");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->expiration = tmp;
+ if ((retcode = parse_krb5_flags(interp, argv[17], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing flags");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ params->flags = tmp;
+ if ((retcode = Tcl_GetInt(interp, argv[18], &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[19], &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 = 0;
+ krb5_error_code krb5_ret;
+ int tcl_ret;
+ int argc;
+ const 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 && argc != 20) {
+ Tcl_SetResult(interp, "wrong # args in principal structure",
+ TCL_STATIC);
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if (! (princ = malloc(sizeof *princ))) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(1); /* XXX */
+ }
+ memset(princ, 0, sizeof(*princ));
+
+ 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;
+ }
+
+ if (argc == 12) goto finished;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[12], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing max_renewable_life");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->max_renewable_life = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[13], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing last_success");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->last_success = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[14], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing last_failed");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->last_failed = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[15], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing fail_auth_count");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->fail_auth_count = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[16], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing n_key_data");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->n_key_data = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[17], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing n_tl_data");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->n_tl_data = tmp;
+
+ if ((tcl_ret = parse_key_data(interp, argv[18],
+ &princ->key_data,
+ princ->n_key_data)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing key_data");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+ if ((tcl_ret = parse_tl_data(interp, argv[19],
+ &princ->tl_data,
+ princ->n_tl_data)) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing tl_data");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ princ->n_tl_data = tmp;
+
+finished:
+ Tcl_Free((char *) 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)->policy);
+ 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, "%ld", policy->pw_min_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%ld", policy->pw_max_life);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%ld", policy->pw_min_length);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%ld", policy->pw_min_classes);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%ld", policy->pw_history_num);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%ld", policy->policy_refcnt);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_max_fail);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_failcnt_interval);
+ Tcl_DStringAppendElement(str, buf);
+
+ sprintf(buf, "%d", policy->pw_lockout_duration);
+ 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 = 0;
+ int tcl_ret;
+ int argc;
+ const 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 && argc != 10) {
+ Tcl_SetResult(interp, "wrong # args in policy structure", TCL_STATIC);
+ 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;
+
+ if (argc == 7) goto finished;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[7], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_max_fail");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_max_fail = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_failcnt_interval");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_failcnt_interval = tmp;
+
+ if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp))
+ != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing pw_lockout_duration");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ policy->pw_lockout_duration = tmp;
+
+finished:
+ Tcl_Free((char *) argv);
+ *out_policy = policy;
+ return retcode;
+}
+
+
+static void free_policy_ent(kadm5_policy_ent_t *policy)
+{
+ free((*policy)->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;
+ unsigned int i;
+ int 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 };
+
+static int _tcl_kadm5_init_any(enum init_type init_type, ClientData clientData,
+ Tcl_Interp *interp, int argc, const char *argv[])
+{
+ kadm5_ret_t ret;
+ char *client_name, *pass, *service_name;
+ int tcl_ret;
+ krb5_ui_4 struct_version, api_version;
+ const char *handle_var;
+ void *server_handle;
+ char *handle_name, *params_str;
+ const char *whoami = argv[0];
+ kadm5_config_params params;
+
+ argv++, argc--;
+
+ kadm5_init_krb5_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(context, client_name, cc, service_name,
+ &params, struct_version,
+ api_version, NULL, &server_handle);
+
+ (void) krb5_cc_close(context, cc);
+ } else
+ ret = kadm5_init(context, client_name, pass, service_name, &params,
+ struct_version, api_version, NULL, &server_handle);
+
+ /* The string fields of params are aliases into argv[3], but
+ * params.keysalts is allocated, so clean it up. */
+ free(params.keysalts);
+
+ 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;
+}
+
+static int tcl_kadm5_init(ClientData clientData, Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ return _tcl_kadm5_init_any(INIT_PASS, clientData, interp, argc, argv);
+}
+
+static int tcl_kadm5_init_with_creds(ClientData clientData, Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ return _tcl_kadm5_init_any(INIT_CREDS, clientData, interp, argc, argv);
+}
+
+static int tcl_kadm5_destroy(ClientData clientData, Tcl_Interp *interp,
+ int argc, const 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;
+}
+
+static int tcl_kadm5_create_principal(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const 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;
+}
+
+
+
+static int tcl_kadm5_delete_principal(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const 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;
+ }
+}
+
+
+
+static int tcl_kadm5_modify_principal(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const 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;
+}
+
+
+static int tcl_kadm5_rename_principal(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const 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)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing source");
+ return TCL_ERROR;
+ }
+
+ if ((krb5_ret = krb5_parse_name(context, argv[1], &target)) != 0) {
+ 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;
+}
+
+
+
+static int tcl_kadm5_chpass_principal(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ krb5_principal princ;
+ char *pw;
+#ifdef OVERRIDE
+ int override_qual;
+#endif
+ krb5_error_code krb5_ret;
+ int retcode = TCL_OK;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(2, 0);
+
+ if ((krb5_ret = krb5_parse_name(context, argv[0], &princ)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if (parse_str(interp, argv[1], &pw) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing password");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+
+#ifdef OVERRIDE
+ if (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;
+}
+
+
+
+static int tcl_kadm5_chpass_principal_util(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const 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;
+ kadm5_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(4, 0);
+
+ if ((krb5_ret = krb5_parse_name(context, argv[0], &princ)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if (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_GetBoolean(interp, argv[2], &override_qual) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing override_qual");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+#endif
+ if (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 (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,
+ msg_ret_var ? sizeof(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;
+}
+
+
+
+static int tcl_kadm5_randkey_principal(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const 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 retcode = TCL_OK;
+
+ GET_HANDLE(3, 0);
+
+ if ((krb5_ret = krb5_parse_name(context, argv[0], &princ)) != 0) {
+ stash_error(interp, krb5_ret);
+ Tcl_AppendElement(interp, "while parsing principal name");
+ return TCL_ERROR;
+ }
+
+ if (parse_str(interp, argv[1], &keyblock_var) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing keyblock variable name");
+ retcode = TCL_ERROR;
+ goto finished;
+ }
+ if (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_keys);
+
+ 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;
+}
+
+
+
+static int tcl_kadm5_get_principal(ClientData clientData, Tcl_Interp *interp,
+ int argc, const 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)) != 0) {
+ stash_error(interp, krb5_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, mask);
+ 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;
+}
+
+static int tcl_kadm5_create_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, const 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;
+}
+
+
+
+static int tcl_kadm5_delete_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ kadm5_ret_t ret;
+ char *policy;
+
+ GET_HANDLE(1, 0);
+
+ if (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;
+ }
+}
+
+
+
+static int tcl_kadm5_modify_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, const 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;
+}
+
+
+static int tcl_kadm5_get_policy(ClientData clientData, Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ kadm5_policy_ent_rec ent;
+ Tcl_DString *ent_dstring = 0;
+ char *policy;
+ char *ent_var;
+ kadm5_ret_t ret;
+ int retcode = TCL_OK;
+
+ GET_HANDLE(2, 1);
+
+ if (parse_str(interp, argv[0], &policy) != TCL_OK) {
+ Tcl_AppendElement(interp, "while parsing policy name");
+ return TCL_ERROR;
+ }
+
+ if (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;
+}
+
+
+
+static int tcl_kadm5_free_principal_ent(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ char *ent_name;
+ kadm5_principal_ent_t ent;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(1, 0);
+
+ if (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 = (kadm5_principal_ent_t) Tcl_GetHashValue(entry);
+
+ ret = kadm5_free_principal_ent(server_handle, ent);
+ if (ret != KADM5_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ Tcl_DeleteHashEntry(entry);
+ }
+ set_ok(interp, "Principal freed.");
+ return TCL_OK;
+}
+
+
+static int tcl_kadm5_free_policy_ent(ClientData clientData,
+ Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ char *ent_name;
+ kadm5_policy_ent_t ent;
+ kadm5_ret_t ret;
+
+ GET_HANDLE(1, 0);
+
+ if (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 = (kadm5_policy_ent_t) Tcl_GetHashValue(entry);
+
+ if ((ret = kadm5_free_policy_ent(server_handle, ent)) != KADM5_OK) {
+ stash_error(interp, ret);
+ return TCL_ERROR;
+ }
+ Tcl_DeleteHashEntry(entry);
+ }
+ set_ok(interp, "Policy freed.");
+ return TCL_OK;
+}
+
+
+static int tcl_kadm5_get_privs(ClientData clientData, Tcl_Interp *interp,
+ int argc, const char *argv[])
+{
+ const char *set_ret;
+ kadm5_ret_t ret;
+ char *priv_var;
+ long privs;
+
+ GET_HANDLE(1, 0);
+
+ if (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_2);
+ Tcl_SetVar(interp, "KADM5_API_VERSION_2", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", KADM5_API_VERSION_3);
+ Tcl_SetVar(interp, "KADM5_API_VERSION_3", buf, TCL_GLOBAL_ONLY);
+ (void) sprintf(buf, "%d", KADM5_API_VERSION_4);
+ Tcl_SetVar(interp, "KADM5_API_VERSION_4", 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_kadm5.h b/src/kadmin/testing/util/tcl_kadm5.h
new file mode 100644
index 000000000000..1f91a11a1601
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_kadm5.h
@@ -0,0 +1,3 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+void Tcl_kadm5_init(Tcl_Interp *interp);
diff --git a/src/kadmin/testing/util/tcl_kadm5_syntax b/src/kadmin/testing/util/tcl_kadm5_syntax
new file mode 100644
index 000000000000..5f16e58e0d1f
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_kadm5_syntax
@@ -0,0 +1,57 @@
+Here's a brief summary of the syntax of the tcl versions of the
+kadm5 functions:
+
+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/tcl_krb5_hash.c b/src/kadmin/testing/util/tcl_krb5_hash.c
new file mode 100644
index 000000000000..35c6bb0b37e5
--- /dev/null
+++ b/src/kadmin/testing/util/tcl_krb5_hash.c
@@ -0,0 +1,167 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * 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).
+ */
+
+#if HAVE_TCL_H
+#include <tcl.h>
+#elif HAVE_TCL_TCL_H
+#include <tcl/tcl.h>
+#endif
+#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/test.c b/src/kadmin/testing/util/test.c
new file mode 100644
index 000000000000..37e49d680c46
--- /dev/null
+++ b/src/kadmin/testing/util/test.c
@@ -0,0 +1,38 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+#include "autoconf.h"
+#if HAVE_TCL_H
+#include <tcl.h>
+#elif HAVE_TCL_TCL_H
+#include <tcl/tcl.h>
+#endif
+#include "tcl_kadm5.h"
+
+#define _TCL_MAIN ((TCL_MAJOR_VERSION * 100 + TCL_MINOR_VERSION) >= 704)
+
+#if _TCL_MAIN
+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_kadm5_init(interp);
+
+ return(TCL_OK);
+}