summaryrefslogtreecommitdiff
path: root/src/kadmin/cli
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
committerCy Schubert <cy@FreeBSD.org>2017-07-07 17:03:42 +0000
commit33a9b234e7087f573ef08cd7318c6497ba08b439 (patch)
treed0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/kadmin/cli
Notes
Diffstat (limited to 'src/kadmin/cli')
-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
11 files changed, 4508 insertions, 0 deletions
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);
+ }
+}