diff options
| author | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
|---|---|---|
| committer | Cy Schubert <cy@FreeBSD.org> | 2017-07-07 17:03:42 +0000 |
| commit | 33a9b234e7087f573ef08cd7318c6497ba08b439 (patch) | |
| tree | d0ea40ad3bf5463a3c55795977c71bcb7d781b4b /src/kadmin/cli | |
Notes
Diffstat (limited to 'src/kadmin/cli')
| -rw-r--r-- | src/kadmin/cli/Makefile.in | 39 | ||||
| -rw-r--r-- | src/kadmin/cli/deps | 63 | ||||
| -rw-r--r-- | src/kadmin/cli/getdate.y | 1050 | ||||
| -rwxr-xr-x | src/kadmin/cli/k5srvutil.sh | 123 | ||||
| -rw-r--r-- | src/kadmin/cli/kadmin.c | 1988 | ||||
| -rw-r--r-- | src/kadmin/cli/kadmin.h | 88 | ||||
| -rw-r--r-- | src/kadmin/cli/kadmin_ct.ct | 100 | ||||
| -rw-r--r-- | src/kadmin/cli/keytab.c | 505 | ||||
| -rw-r--r-- | src/kadmin/cli/keytab_local.c | 10 | ||||
| -rw-r--r-- | src/kadmin/cli/ss_wrapper.c | 77 | ||||
| -rw-r--r-- | src/kadmin/cli/strftime.c | 465 |
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(¶ms, 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, + ¶ms.keysalts, + ¶ms.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, ¶ms, + 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, ¶ms, + 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, + ¶ms, 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, + ¶ms, 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); + } +} |
