diff options
Diffstat (limited to 'src/kadmin')
78 files changed, 20535 insertions, 0 deletions
diff --git a/src/kadmin/Makefile.in b/src/kadmin/Makefile.in new file mode 100644 index 000000000000..f4061f4f7a0c --- /dev/null +++ b/src/kadmin/Makefile.in @@ -0,0 +1,6 @@ +mydir=kadmin +BUILDTOP=$(REL).. +SUBDIRS = cli dbutil ktutil server testing + +all: + diff --git a/src/kadmin/cli/Makefile.in b/src/kadmin/cli/Makefile.in new file mode 100644 index 000000000000..adfea6e2b5ef --- /dev/null +++ b/src/kadmin/cli/Makefile.in @@ -0,0 +1,39 @@ +mydir=kadmin$(S)cli +BUILDTOP=$(REL)..$(S).. +KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS) + +PROG = kadmin +COMMON_OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o +KADMIN_OBJS = $(COMMON_OBJS) keytab.o +LOCAL_OBJS = $(COMMON_OBJS) keytab_local.o + +SRCS = kadmin.c kadmin_ct.c ss_wrapper.c getdate.c keytab.c keytab_local.c + +LOCALINCLUDES=-I$(srcdir) + +all: $(PROG).local $(PROG) + +$(PROG).local: $(LOCAL_OBJS) $(SS_DEPLIB) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $(PROG).local $(LOCAL_OBJS) $(SS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS) + +$(PROG): $(KADMIN_OBJS) $(SS_DEPLIB) $(KADMCLNT_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $(PROG) $(KADMIN_OBJS) $(SS_LIB) $(KADMCLNT_LIBS) $(KRB5_BASE_LIBS) + +kadmin_ct.o: kadmin_ct.c + +install: + $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG).local + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(CLIENT_BINDIR)/$(PROG) + $(INSTALL_SCRIPT) $(srcdir)/k5srvutil.sh ${DESTDIR}$(CLIENT_BINDIR)/k5srvutil + +generate-files-mac: kadmin_ct.c getdate.c + +clean: + $(RM) $(PROG).local $(PROG) $(COMMON_OBJS) $(KADMIN_OBJS) $(LOCAL_OBJS) +clean-unix:: + $(RM) datetest getdate.c kadmin_ct.c + +# for testing getdate.y +# CC_LINK is not meant for compilation and this use may break in the future. +datetest: getdate.c + $(CC_LINK) $(ALL_CFLAGS) -DTEST -o datetest getdate.c diff --git a/src/kadmin/cli/deps b/src/kadmin/cli/deps new file mode 100644 index 000000000000..a9c997b395ab --- /dev/null +++ b/src/kadmin/cli/deps @@ -0,0 +1,63 @@ +# +# Generated makefile dependencies follow. +# +$(OUTPRE)kadmin.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/kdb.h \ + $(top_srcdir)/include/krb5.h kadmin.c kadmin.h +$(OUTPRE)kadmin_ct.$(OBJEXT): $(COM_ERR_DEPS) $(SS_DEPS) \ + kadmin_ct.c +$(OUTPRE)ss_wrapper.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(SS_DEPS) \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \ + kadmin.h ss_wrapper.c +$(OUTPRE)getdate.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \ + getdate.c +$(OUTPRE)keytab.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + kadmin.h keytab.c +$(OUTPRE)keytab_local.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + kadmin.h keytab.c keytab_local.c diff --git a/src/kadmin/cli/getdate.y b/src/kadmin/cli/getdate.y new file mode 100644 index 000000000000..4f0c56f7eb20 --- /dev/null +++ b/src/kadmin/cli/getdate.y @@ -0,0 +1,1050 @@ +%{ +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; +** send any email to Rich. +** +** This grammar has nine shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#include "autoconf.h" +#include <string.h> + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +#undef static +#endif + +/* The following block of alloca-related preprocessor directives is here + solely to allow compilation by non GNU-C compilers of the C parser + produced from this file by old versions of bison. Newer versions of + bison include a block similar to this one in bison.simple. */ + +#ifdef __GNUC__ +#undef alloca +#define alloca __builtin_alloca +#else +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#else +#ifdef _AIX /* for Bison */ + #pragma alloca +#else +void *alloca (); +#endif +#endif +#endif + +#include <stdio.h> +#include <ctype.h> + +#if defined(HAVE_STDLIB_H) +#include <stdlib.h> +#endif + +/* The code at the top of get_date which figures out the offset of the + current time zone checks various CPP symbols to see if special + tricks are need, but defaults to using the gettimeofday system call. + Include <sys/time.h> if that will be used. */ + +#if defined(vms) + +#include <types.h> +#include <time.h> + +#else + +#include <sys/types.h> + +#ifdef TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#ifdef timezone +#undef timezone /* needed for sgi */ +#endif + +/* +** We use the obsolete `struct my_timeb' as part of our interface! +** Since the system doesn't have it, we define it here; +** our callers must do likewise. +*/ +struct my_timeb { + time_t time; /* Seconds since the epoch */ + unsigned short millitm; /* Field not used */ + short timezone; /* Minutes west of GMT */ + short dstflag; /* Field not used */ +}; +#endif /* defined(vms) */ + +#if defined (STDC_HEADERS) || defined (USG) +#include <string.h> +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#ifndef bcopy +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +extern struct tm *gmtime(); +extern struct tm *localtime(); + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int getdate_yylex (void); +static int getdate_yyerror (char *); + + +#define EPOCH 1970 +#define EPOCH_END 2038 /* assumes 32 bits */ +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST tNEVER + +%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type <Meridian> tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + | tNEVER { + yyYear = 1970; + yyMonth = 1; + yyDay = 1; + yyHour = yyMinutes = yySeconds = 0; + yyDSTmode = DSToff; + yyTimezone = 0; /* gmt */ + yyHaveDate++; + } + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { "never", tNEVER, 0 }, + { NULL } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ + { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "kdt", tZONE, -HOUR(10) }, /* Korean Daylight */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* ARGSUSED */ +static int +yyerror(char *s) +{ + return 0; +} + + +static time_t +ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + +/* + * From hh:mm:ss [am|pm] mm/dd/yy [tz], compute and return the number + * of seconds since 00:00:00 1/1/70 GMT. + */ +static time_t +Convert(time_t Month, time_t Day, time_t Year, time_t Hours, time_t Minutes, + time_t Seconds, MERIDIAN Meridian, DSTMODE DSTmode) +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + struct tm *tm; + + if (Year < 0) + Year = -Year; + if (Year < 1900) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + if (Year < EPOCH + || Year > EPOCH_END + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + ((i % 4 == 0) && ((Year % 100 != 0) || + (Year % 400 == 0))); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon) + Julian -= 60 * 60; + else if (DSTmode == DSTmaybe) { + tm = localtime(&Julian); + if (tm == NULL) + return -1; + else if (tm->tm_isdst) + Julian -= 60 * 60; + } + return Julian; +} + + +static time_t +DSTcorrect(time_t Start, time_t Future, int *error) +{ + time_t StartDay; + time_t FutureDay; + struct tm *tm; + + tm = localtime(&Start); + if (tm == NULL) { + *error = 1; + return -1; + } + StartDay = (tm->tm_hour + 1) % 24; + tm = localtime(&Future); + if (tm == NULL) { + *error = 1; + return -1; + } + FutureDay = (tm->tm_hour + 1) % 24; + *error = 0; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber, int *error) +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + if (tm == NULL) { + *error = 1; + return -1; + } + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now, error); +} + + +static time_t +RelativeMonth(time_t Start, time_t RelMonth) +{ + struct tm *tm; + time_t Month; + time_t Year; + time_t ret; + int error; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + if (tm == NULL) + return -1; + Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + ret = Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe); + if (ret == -1) + return ret; + ret = DSTcorrect(Start, ret, &error); + if (error) + return -1; + return ret; +} + + +static int +LookupWord(char *buff) +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper((int) *p)) + *p = tolower((int) *p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace((int) *yyInput)) + yyInput++; + + c = *yyInput; + if (isdigit((int) c) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit((int) (*++yyInput))) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit((int) (c = *yyInput++)); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha((int) c)) { + for (p = buff; isalpha((int) (c = *yyInput++)) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static time_t +difftm(struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + return + ( + ( + ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (time_t)(ay-by) * 365 + )*24 + (a->tm_hour - b->tm_hour) + )*60 + (a->tm_min - b->tm_min) + )*60 + (a->tm_sec - b->tm_sec); +} + +/* For get_date extern declaration compatibility check... yuck. */ +#include <krb5.h> +int yyparse(void); + +time_t get_date_rel(char *, time_t); +time_t get_date(char *); + +time_t +get_date_rel(char *p, time_t nowtime) +{ + struct my_timeb *now = NULL; + struct tm *tm, gmt; + struct my_timeb ftz; + time_t Start; + time_t tod; + time_t delta; + int error; + + yyInput = p; + if (now == NULL) { + now = &ftz; + + ftz.time = nowtime; + + if (! (tm = gmtime (&ftz.time))) + return -1; + gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ + tm = localtime(&ftz.time); + if (tm == NULL) + return -1; + ftz.timezone = difftm (&gmt, tm) / 60; + } + + tm = localtime(&now->time); + if (tm == NULL) + return -1; + yyYear = tm->tm_year; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = now->timezone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + /* + * When yyparse returns, zero or more of yyHave{Time,Zone,Date,Day,Rel} + * will have been incremented. The value is number of items of + * that type that were found; for all but Rel, more than one is + * illegal. + * + * For each yyHave indicator, the following values are set: + * + * yyHaveTime: + * yyHour, yyMinutes, yySeconds: hh:mm:ss specified, initialized + * to zeros above + * yyMeridian: MERam, MERpm, or MER24 + * yyTimeZone: time zone specified in minutes + * yyDSTmode: DSToff if yyTimeZone is set, otherwise unchanged + * (initialized above to DSTmaybe) + * + * yyHaveZone: + * yyTimezone: as above + * yyDSTmode: DSToff if a non-DST zone is specified, otherwise DSTon + * XXX don't understand interaction with yyHaveTime zone info + * + * yyHaveDay: + * yyDayNumber: 0-6 for Sunday-Saturday + * yyDayOrdinal: val specified with day ("second monday", + * Ordinal=2), otherwise 1 + * + * yyHaveDate: + * yyMonth, yyDay, yyYear: mm/dd/yy specified, initialized to + * today above + * + * yyHaveRel: + * yyRelSeconds: seconds specified with MINUTE_UNITs ("3 hours") or + * SEC_UNITs ("30 seconds") + * yyRelMonth: months specified with MONTH_UNITs ("3 months", "1 + * year") + * + * The code following yyparse turns these values into a single + * date stamp. + */ + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + /* + * If an absolute time specified, set Start to the equivalent Unix + * timestamp. Otherwise, set Start to now, and if we do not have + * a relatime time (ie: only yyHaveZone), decrement Start to the + * beginning of today. + * + * By having yyHaveDay in the "absolute" list, "next Monday" means + * midnight next Monday. Otherwise, "next Monday" would mean the + * time right now, next Monday. It's not clear to me why the + * current behavior is preferred. + */ + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = now->time; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + /* + * Add in the relative time specified. RelativeMonth adds in the + * months, accounting for the fact that the actual length of "3 + * months" depends on where you start counting. + * + * XXX By having this separate from the previous block, we are + * allowing dates like "10:00am 3 months", which means 3 months + * from 10:00am today, or even "1/1/99 two days" which means two + * days after 1/1/99. + * + * XXX Shouldn't this only be done if yyHaveRel, just for + * thoroughness? + */ + Start += yyRelSeconds; + delta = RelativeMonth(Start, yyRelMonth); + if (delta == (time_t) -1) + return -1; + Start += delta; + + /* + * Now, if you specified a day of week and counter, add it in. By + * disallowing Date but allowing Time, you can say "5pm next + * monday". + * + * XXX The yyHaveDay && !yyHaveDate restriction should be enforced + * above and be able to cause failure. + */ + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber, &error); + if (error != 0) + return -1; + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +time_t +get_date(char *p) +{ + return get_date_rel(p, time(NULL)); +} + + +#if defined(TEST) + +/* ARGSUSED */ +main(int ac, char *av[]) +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/src/kadmin/cli/k5srvutil.sh b/src/kadmin/cli/k5srvutil.sh new file mode 100755 index 000000000000..050fa8776ff4 --- /dev/null +++ b/src/kadmin/cli/k5srvutil.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +# list_princs keytab +# returns a list of principals in the keytab +# sorted and uniquified +list_princs() { + klist -k $keytab | awk '(NR > 3) {print $2}' | sort | uniq +} + +set_command() { + if [ x$command != x ] ; then + cmd_error Only one command can be specified + usage + exit 1 + fi + command=$1 +} + +#interactive_prompt prompt princ +# If in interactive mode return true if the principal should be acted on +# otherwise return true all the time +interactive_prompt() { + if [ $interactive = 0 ] ; then + return 0 + fi + printf "%s for %s? [yn]" "$1" "$2" + read ans + case $ans in + n*|N*) + return 1 + ;; + esac + return 0 + } + +cmd_error() { + echo $@ 2>&1 + } + +usage() { + echo "Usage: $0 [-i] [-f file] [-e keysalts] list|change|delete|delold" +} + + + +change_key() { + princs=`list_princs ` + for princ in $princs; do + if interactive_prompt "Change key " $princ; then + kadmin -k -t $keytab -p $princ -q \ + "ktadd -k $keytab $keysalts $princ" + fi + done + } + +delete_old_keys() { + princs=`list_princs ` + for princ in $princs; do + if interactive_prompt "Delete old keys " $princ; then + kadmin -k -t $keytab -p $princ -q "ktrem -k $keytab $princ old" + fi + done + } + +delete_keys() { + interactive=1 + princs=`list_princs ` + for princ in $princs; do + if interactive_prompt "Delete all keys " $princ; then + kadmin -p $princ -k -t $keytab -q "ktrem -k $keytab $princ all" + fi + done + } + + +keytab=/etc/krb5.keytab +interactive=0 +keysalts="" + +while [ $# -gt 0 ] ; do + opt=$1 + shift + case $opt in + "-f") + keytab=$1 + shift + ;; + "-i") + interactive=1 + ;; + "-e") + keysalts="$keysalts -e \"$1\"" + shift + ;; + change|delold|delete|list) + set_command $opt + ;; + *) + cmd_error Illegal option: $opt + usage + exit 1 + ;; + esac +done + + +case $command in + change) + change_key + ;; + delold) + delete_old_keys + ;; + delete) + delete_keys + ;; + list) + klist -k $keytab + ;; + *) + usage + ;; + esac diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c new file mode 100644 index 000000000000..c53c677a82d0 --- /dev/null +++ b/src/kadmin/cli/kadmin.c @@ -0,0 +1,1988 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1994, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Base functions for a kadmin command line interface using the OVSecure + * library */ + +/* for "_" macro */ +#include "k5-platform.h" +#include <krb5.h> +#include <kadm5/admin.h> +#include <adm_proto.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <sys/types.h> +#include <math.h> +#include <unistd.h> +#include <pwd.h> +/* #include <sys/timeb.h> */ +#include <time.h> +#include "kadmin.h" + +static krb5_boolean script_mode = FALSE; +int exit_status = 0; +char *def_realm = NULL; +char *whoami = NULL; + +void *handle = NULL; +krb5_context context; +char *ccache_name = NULL; + +int locked = 0; + +static void +info(const char *fmt, ...) +#if !defined(__cplusplus) && (__GNUC__ > 2) + __attribute__((__format__(__printf__, 1, 2))) +#endif + ; + +static void +error(const char *fmt, ...) +#if !defined(__cplusplus) && (__GNUC__ > 2) + __attribute__((__format__(__printf__, 1, 2))) +#endif + ; + +/* Like printf, but suppressed if script_mode is set. */ +static void +info(const char *fmt, ...) +{ + va_list ap; + + if (script_mode) + return; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +/* Like fprintf to stderr; also set exit_status if script_mode is set. */ +static void +error(const char *fmt, ...) +{ + va_list ap; + + if (script_mode) + exit_status = 1; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static void +usage() +{ + error(_("Usage: %s [-r realm] [-p principal] [-q query] " + "[clnt|local args]\n" + " [command args...]\n" + "\tclnt args: [-s admin_server[:port]] " + "[[-c ccache]|[-k [-t keytab]]]|[-n]\n" + "\tlocal args: [-x db_args]* [-d dbname] " + "[-e \"enc:salt ...\"] [-m]" + "where,\n\t[-x db_args]* - any number of database specific " + "arguments.\n" + "\t\t\tLook at each database documentation for supported " + "arguments\n"), whoami); + exit(1); +} + +static char * +strdur(time_t duration) +{ + static char out[50]; + int neg, days, hours, minutes, seconds; + + if (duration < 0) { + duration *= -1; + neg = 1; + } else + neg = 0; + days = duration / (24 * 3600); + duration %= 24 * 3600; + hours = duration / 3600; + duration %= 3600; + minutes = duration / 60; + duration %= 60; + seconds = duration; + snprintf(out, sizeof(out), "%s%d %s %02d:%02d:%02d", neg ? "-" : "", + days, days == 1 ? "day" : "days", + hours, minutes, seconds); + return out; +} + +static char * +strdate(krb5_timestamp when) +{ + struct tm *tm; + static char out[40]; + + time_t lcltim = when; + tm = localtime(&lcltim); + strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm); + return out; +} + +/* Parse a date string using getdate.y. On failure, output an error message + * and return (time_t)-1. */ +static time_t +parse_date(char *str, time_t now) +{ + time_t date; + + date = get_date_rel(str, now); + if (date == (time_t)-1) + error(_("Invalid date specification \"%s\".\n"), str); + return date; +} + +/* + * Parse a time interval. Use krb5_string_to_deltat() if it works; otherwise + * use getdate.y and subtract now, with sanity checks. On failure, output an + * error message and return (time_t)-1. + */ +static time_t +parse_interval(char *str, time_t now) +{ + time_t date; + krb5_deltat delta; + + if (krb5_string_to_deltat(str, &delta) == 0) + return delta; + + date = parse_date(str, now); + if (date == (time_t)-1) + return date; + + /* Interpret an absolute time of 0 (e.g. "never") as an interval of 0. */ + if (date == 0) + return 0; + + /* Don't return a negative interval if the date is in the past. */ + if (date < now) { + error(_("Interval specification \"%s\" is in the past.\n"), str); + return (time_t)-1; + } + + return date - now; +} + +/* this is a wrapper to go around krb5_parse_principal so we can set + the default realm up properly */ +static krb5_error_code +kadmin_parse_name(char *name, krb5_principal *principal) +{ + char *cp, *fullname; + krb5_error_code retval; + int result; + + /* assumes def_realm is initialized! */ + cp = strchr(name, '@'); + while (cp) { + if (cp - name && *(cp - 1) != '\\') + break; + else + cp = strchr(cp + 1, '@'); + } + if (cp == NULL) + result = asprintf(&fullname, "%s@%s", name, def_realm); + else + result = asprintf(&fullname, "%s", name); + if (result < 0) + return ENOMEM; + retval = krb5_parse_name(context, fullname, principal); + free(fullname); + return retval; +} + +static void +extended_com_err_fn(const char *myprog, errcode_t code, + const char *fmt, va_list args) +{ + const char *emsg; + + if (code) { + emsg = krb5_get_error_message(context, code); + error("%s: %s ", myprog, emsg); + krb5_free_error_message(context, emsg); + } else { + error("%s: ", myprog); + } + vfprintf(stderr, fmt, args); + error("\n"); +} + +/* Create a principal using the oldest appropriate kadm5 API. */ +static krb5_error_code +create_princ(kadm5_principal_ent_rec *princ, long mask, int n_ks, + krb5_key_salt_tuple *ks, char *pass) +{ + if (ks) + return kadm5_create_principal_3(handle, princ, mask, n_ks, ks, pass); + else + return kadm5_create_principal(handle, princ, mask, pass); +} + +/* Randomize a principal's password using the appropriate kadm5 API. */ +krb5_error_code +randkey_princ(void *lhandle, krb5_principal princ, krb5_boolean keepold, + int n_ks, krb5_key_salt_tuple *ks, krb5_keyblock **key, + int *n_keys) +{ + krb5_error_code ret; + + /* Try the newer API first, because the Solaris kadmind only creates DES + * keys when the old API is used. */ + ret = kadm5_randkey_principal_3(lhandle, princ, keepold, n_ks, ks, key, + n_keys); + + /* Fall back to the old version if we get an error and aren't using any new + * parameters. */ + if (ret == KADM5_RPC_ERROR && !keepold && ks == NULL) + ret = kadm5_randkey_principal(lhandle, princ, key, n_keys); + + return ret; +} + +static krb5_boolean +policy_exists(const char *name) +{ + kadm5_policy_ent_rec pol; + + if (kadm5_get_policy(handle, (char *)name, &pol) != 0) + return FALSE; + kadm5_free_policy_ent(handle, &pol); + return TRUE; +} + +void +kadmin_startup(int argc, char *argv[], char **request_out, char ***args_out) +{ + extern char *optarg; + char *princstr = NULL, *keytab_name = NULL, *query = NULL; + char *password = NULL; + char *luser, *canon, *cp; + int optchar, freeprinc = 0, use_keytab = 0, use_anonymous = 0; + struct passwd *pw; + kadm5_ret_t retval; + krb5_ccache cc; + krb5_principal princ; + kadm5_config_params params; + char **db_args = NULL; + int db_args_size = 0; + char *db_name = NULL; + char *svcname, *realm; + + memset(¶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); + } +} diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in new file mode 100644 index 000000000000..1fa25d2a1319 --- /dev/null +++ b/src/kadmin/dbutil/Makefile.in @@ -0,0 +1,43 @@ +mydir=kadmin$(S)dbutil +BUILDTOP=$(REL)..$(S).. +LOCALINCLUDES = -I. +KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS) + +PROG = kdb5_util + +SRCS = kdb5_util.c kdb5_create.c kadm5_create.c kdb5_destroy.c \ + kdb5_stash.c import_err.c strtok.c dump.c ovload.c kdb5_mkey.c \ + tabdump.c tdumputil.c +EXTRADEPSRCS = t_tdumputil.c + +OBJS = kdb5_util.o kdb5_create.o kadm5_create.o kdb5_destroy.o \ + kdb5_stash.o import_err.o strtok.o dump.o ovload.o kdb5_mkey.o \ + tabdump.o tdumputil.o + +GETDATE = ../cli/getdate.o + +all: $(PROG) + +$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(GETDATE) + $(CC_LINK) -o $(PROG) $(OBJS) $(GETDATE) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS) + +import_err.c import_err.h: $(srcdir)/import_err.et + +$(OBJS): import_err.h + +install: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean: + $(RM) $(PROG) $(OBJS) import_err.c import_err.h + $(RM) t_tdumputil.o t_tdumputil + +T_TDUMPUTIL_OBJS = t_tdumputil.o tdumputil.o + +t_tdumputil: $(T_TDUMPUTIL_OBJS) $(SUPPORT_DEPLIB) + $(CC_LINK) -o $@ $(T_TDUMPUTIL_OBJS) $(SUPPORT_LIB) + +depend: import_err.h + +check-pytests: t_tdumputil + $(RUNPYTEST) $(srcdir)/t_tdumputil.py $(PYTESTFLAGS) diff --git a/src/kadmin/dbutil/deps b/src/kadmin/dbutil/deps new file mode 100644 index 000000000000..4dcc336283f4 --- /dev/null +++ b/src/kadmin/dbutil/deps @@ -0,0 +1,206 @@ +# +# Generated makefile dependencies follow. +# +$(OUTPRE)kdb5_util.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h kdb5_util.c kdb5_util.h +$(OUTPRE)kdb5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h kdb5_create.c \ + kdb5_util.h +$(OUTPRE)kadm5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/fake-addrinfo.h \ + $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \ + $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \ + $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \ + $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \ + $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \ + $(top_srcdir)/include/iprop.h $(top_srcdir)/include/iprop_hdr.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \ + $(top_srcdir)/include/kdb_log.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + kadm5_create.c kdb5_util.h +$(OUTPRE)kdb5_destroy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h kdb5_destroy.c \ + kdb5_util.h +$(OUTPRE)kdb5_stash.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h kdb5_stash.c kdb5_util.h +$(OUTPRE)import_err.$(OBJEXT): $(COM_ERR_DEPS) import_err.c +$(OUTPRE)strtok.$(OBJEXT): nstrtok.h strtok.c +$(OUTPRE)dump.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h dump.c kdb5_util.h +$(OUTPRE)ovload.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h import_err.h kdb5_util.h \ + nstrtok.h ovload.c +$(OUTPRE)kdb5_mkey.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h kdb5_mkey.c kdb5_util.h +$(OUTPRE)tabdump.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h kdb5_util.h tabdump.c \ + tdumputil.h +$(OUTPRE)tdumputil.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h tdumputil.c tdumputil.h +$(OUTPRE)t_tdumputil.$(OBJEXT): t_tdumputil.c tdumputil.h diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c new file mode 100644 index 000000000000..f7889bd234f5 --- /dev/null +++ b/src/kadmin/dbutil/dump.c @@ -0,0 +1,1717 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/dbutil/dump.c - Dump a KDC database */ +/* + * Copyright 1990,1991,2001,2006,2008,2009,2013 by the Massachusetts Institute + * of Technology. All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <k5-int.h> +#include <kadm5/admin.h> +#include <kadm5/server_internal.h> +#include <kdb.h> +#include <com_err.h> +#include "kdb5_util.h" +#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP) +#include <regex.h> +#endif /* HAVE_REGEX_H */ + +/* Needed for master key conversion. */ +static krb5_boolean mkey_convert; +krb5_keyblock new_master_keyblock; +krb5_kvno new_mkvno; + +#define K5Q1(x) #x +#define K5Q(x) K5Q1(x) +#define K5CONST_WIDTH_SCANF_STR(x) "%" K5Q(x) "s" + +/* Use compile(3) if no regcomp present. */ +#if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H) +#define INIT char *sp = instring; +#define GETC() (*sp++) +#define PEEKC() (*sp) +#define UNGETC(c) (--sp) +#define RETURN(c) return(c) +#define ERROR(c) +#define RE_BUF_SIZE 1024 +#include <regexp.h> +#endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */ + +typedef krb5_error_code (*dump_func)(krb5_context context, + krb5_db_entry *entry, const char *name, + FILE *fp, krb5_boolean verbose, + krb5_boolean omit_nra); +typedef int (*load_func)(krb5_context context, const char *dumpfile, FILE *fp, + krb5_boolean verbose, int *linenop); + +typedef struct _dump_version { + char *name; + char *header; + int updateonly; + int iprop; + int ipropx; + dump_func dump_princ; + osa_adb_iter_policy_func dump_policy; + load_func load_record; +} dump_version; + +struct dump_args { + FILE *ofile; + krb5_context context; + char **names; + int nnames; + krb5_boolean verbose; + krb5_boolean omit_nra; /* omit non-replicated attributes */ + dump_version *dump; +}; + +/* External data */ +extern krb5_db_entry *master_entry; + +/* + * Re-encrypt the key_data with the new master key... + */ +krb5_error_code +master_key_convert(krb5_context context, krb5_db_entry *db_entry) +{ + krb5_error_code retval; + krb5_keyblock v5plainkey, *key_ptr, *tmp_mkey; + krb5_keysalt keysalt; + krb5_key_data new_key_data, *key_data; + krb5_boolean is_mkey; + krb5_kvno kvno; + int i, j; + + is_mkey = krb5_principal_compare(context, master_princ, db_entry->princ); + + if (is_mkey) { + return add_new_mkey(context, db_entry, &new_master_keyblock, + new_mkvno); + } + + for (i = 0; i < db_entry->n_key_data; i++) { + key_data = &db_entry->key_data[i]; + retval = krb5_dbe_find_mkey(context, db_entry, &tmp_mkey); + if (retval) + return retval; + retval = krb5_dbe_decrypt_key_data(context, tmp_mkey, key_data, + &v5plainkey, &keysalt); + if (retval) + return retval; + + memset(&new_key_data, 0, sizeof(new_key_data)); + + key_ptr = &v5plainkey; + kvno = key_data->key_data_kvno; + + retval = krb5_dbe_encrypt_key_data(context, &new_master_keyblock, + key_ptr, &keysalt, kvno, + &new_key_data); + if (retval) + return retval; + krb5_free_keyblock_contents(context, &v5plainkey); + for (j = 0; j < key_data->key_data_ver; j++) { + if (key_data->key_data_length[j]) + free(key_data->key_data_contents[j]); + } + *key_data = new_key_data; + } + assert(new_mkvno > 0); + return krb5_dbe_update_mkvno(context, db_entry, new_mkvno); +} + +/* Create temp file for new dump to be named ofile. */ +static FILE * +create_ofile(char *ofile, char **tmpname) +{ + int fd = -1; + FILE *f; + + *tmpname = NULL; + if (asprintf(tmpname, "%s-XXXXXX", ofile) < 0) + goto error; + + fd = mkstemp(*tmpname); + if (fd == -1) + goto error; + + f = fdopen(fd, "w+"); + if (f != NULL) + return f; + +error: + com_err(progname, errno, _("while allocating temporary filename dump")); + if (fd >= 0) + unlink(*tmpname); + exit(1); +} + +/* Rename new dump file into place. */ +static void +finish_ofile(char *ofile, char **tmpname) +{ + if (rename(*tmpname, ofile) == -1) { + com_err(progname, errno, _("while renaming dump file into place")); + exit(1); + } + free(*tmpname); + *tmpname = NULL; +} + +/* Create the .dump_ok file. */ +static int +prep_ok_file(krb5_context context, char *file_name, int *fd) +{ + static char ok[] = ".dump_ok"; + krb5_error_code retval; + char *file_ok; + + if (asprintf(&file_ok, "%s%s", file_name, ok) < 0) { + com_err(progname, ENOMEM, _("while allocating dump_ok filename")); + exit_status++; + return 0; + } + + *fd = open(file_ok, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (*fd == -1) { + com_err(progname, errno, _("while creating 'ok' file, '%s'"), file_ok); + exit_status++; + free(file_ok); + return 0; + } + retval = krb5_lock_file(context, *fd, KRB5_LOCKMODE_EXCLUSIVE); + if (retval) { + com_err(progname, retval, _("while locking 'ok' file, '%s'"), file_ok); + free(file_ok); + return 0; + } + free(file_ok); + return 1; +} + +/* + * Update the "ok" file. + */ +static void +update_ok_file(krb5_context context, int fd) +{ + write(fd, "", 1); + krb5_lock_file(context, fd, KRB5_LOCKMODE_UNLOCK); + close(fd); +} + +/* Return true if a principal name matches a regular expression or string. */ +static int +name_matches(char *name, struct dump_args *args) +{ +#if HAVE_REGCOMP + regex_t reg; + regmatch_t rmatch; + int st; + char errmsg[BUFSIZ]; +#elif HAVE_REGEXP_H + char regexp_buffer[RE_BUF_SIZE]; +#elif HAVE_RE_COMP + extern char *re_comp(); + char *re_result; +#endif /* HAVE_RE_COMP */ + int i, match; + + /* Check each regular expression in args. */ + match = args->nnames ? 0 : 1; + for (i = 0; i < args->nnames && !match; i++) { +#if HAVE_REGCOMP + /* Compile the regular expression. */ + st = regcomp(®, args->names[i], REG_EXTENDED); + if (st) { + regerror(st, ®, errmsg, sizeof(errmsg)); + fprintf(stderr, _("%s: regular expression error: %s\n"), progname, + errmsg); + break; + } + /* See if we have a match. */ + st = regexec(®, name, 1, &rmatch, 0); + if (st == 0) { + /* See if it matches the whole name. */ + if (rmatch.rm_so == 0 && (size_t)rmatch.rm_eo == strlen(name)) + match = 1; + } else if (st != REG_NOMATCH) { + regerror(st, ®, errmsg, sizeof(errmsg)); + fprintf(stderr, _("%s: regular expression match error: %s\n"), + progname, errmsg); + break; + } + regfree(®); +#elif HAVE_REGEXP_H + /* Compile the regular expression. */ + compile(args->names[i], regexp_buffer, ®exp_buffer[RE_BUF_SIZE], + '\0'); + if (step(name, regexp_buffer)) { + if (loc1 == name && loc2 == &name[strlen(name)]) + match = 1; + } +#elif HAVE_RE_COMP + /* Compile the regular expression. */ + re_result = re_comp(args->names[i]); + if (re_result) { + fprintf(stderr, _("%s: regular expression error: %s\n"), progname, + re_result); + break; + } + if (re_exec(name)) + match = 1; +#else /* HAVE_RE_COMP */ + /* If no regular expression support, then just compare the strings. */ + if (!strcmp(args->names[i], name)) + match = 1; +#endif /* HAVE_REGCOMP */ + } + return match; +} + +/* Output "-1" if len is 0; otherwise output len bytes of data in hex. */ +static void +dump_octets_or_minus1(FILE *fp, unsigned char *data, size_t len) +{ + if (len > 0) { + for (; len > 0; len--) + fprintf(fp, "%02x", *data++); + } else { + fprintf(fp, "-1"); + } +} + +/* + * Dump TL data; common to principals and policies. + * + * If filter_kadm then the KRB5_TL_KADM_DATA (where a principal's policy + * name is stored) is filtered out. This is for dump formats that don't + * support policies. + */ +static void +dump_tl_data(FILE *ofile, krb5_tl_data *tlp, krb5_boolean filter_kadm) +{ + for (; tlp != NULL; tlp = tlp->tl_data_next) { + if (tlp->tl_data_type == KRB5_TL_KADM_DATA && filter_kadm) + continue; + fprintf(ofile, "\t%d\t%d\t", (int)tlp->tl_data_type, + (int)tlp->tl_data_length); + dump_octets_or_minus1(ofile, tlp->tl_data_contents, + tlp->tl_data_length); + } +} + +/* Dump a principal entry in krb5 beta 7 format. Omit kadmin tl-data if kadm + * is false. */ +static krb5_error_code +k5beta7_common(krb5_context context, krb5_db_entry *entry, + const char *name, FILE *fp, krb5_boolean verbose, + krb5_boolean omit_nra, krb5_boolean kadm) +{ + krb5_tl_data *tlp; + krb5_key_data *kdata; + int counter, skip, i; + + /* + * The dump format is as follows: + * len strlen(name) n_tl_data n_key_data e_length + * name + * attributes max_life max_renewable_life expiration + * pw_expiration last_success last_failed fail_auth_count + * n_tl_data*[type length <contents>] + * n_key_data*[ver kvno ver*(type length <contents>)] + * <e_data> + * Fields which are not encapsulated by angle-brackets are to appear + * verbatim. A bracketed field's absence is indicated by a -1 in its + * place. + */ + + /* Make sure that the tagged list is reasonably correct. */ + counter = skip = 0; + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + /* Don't dump tl data types we know aren't understood by earlier + * versions. */ + if (tlp->tl_data_type == KRB5_TL_KADM_DATA && !kadm) + skip++; + else + counter++; + } + + if (counter + skip != entry->n_tl_data) { + fprintf(stderr, _("%s: tagged data list inconsistency for %s " + "(counted %d, stored %d)\n"), progname, name, + counter + skip, (int)entry->n_tl_data); + return EINVAL; + } + + /* Write out header. */ + fprintf(fp, "princ\t%d\t%lu\t%d\t%d\t%d\t%s\t", (int)entry->len, + (unsigned long)strlen(name), counter, (int)entry->n_key_data, + (int)entry->e_length, name); + fprintf(fp, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d", entry->attributes, + entry->max_life, entry->max_renewable_life, entry->expiration, + entry->pw_expiration, + omit_nra ? 0 : entry->last_success, + omit_nra ? 0 : entry->last_failed, + omit_nra ? 0 : entry->fail_auth_count); + + /* Write out tagged data. */ + dump_tl_data(fp, entry->tl_data, !kadm); + fprintf(fp, "\t"); + + /* Write out key data. */ + for (counter = 0; counter < entry->n_key_data; counter++) { + kdata = &entry->key_data[counter]; + fprintf(fp, "%d\t%d\t", (int)kdata->key_data_ver, + (int)kdata->key_data_kvno); + for (i = 0; i < kdata->key_data_ver; i++) { + fprintf(fp, "%d\t%d\t", kdata->key_data_type[i], + kdata->key_data_length[i]); + dump_octets_or_minus1(fp, kdata->key_data_contents[i], + kdata->key_data_length[i]); + fprintf(fp, "\t"); + } + } + + /* Write out extra data. */ + dump_octets_or_minus1(fp, entry->e_data, entry->e_length); + + /* Write trailer. */ + fprintf(fp, ";\n"); + + if (verbose) + fprintf(stderr, "%s\n", name); + + return 0; +} + +/* Output a dump record in krb5b7 format. */ +static krb5_error_code +dump_k5beta7_princ(krb5_context context, krb5_db_entry *entry, + const char *name, FILE *fp, krb5_boolean verbose, + krb5_boolean omit_nra) +{ + return k5beta7_common(context, entry, name, fp, verbose, omit_nra, FALSE); +} + +static krb5_error_code +dump_k5beta7_princ_withpolicy(krb5_context context, krb5_db_entry *entry, + const char *name, FILE *fp, krb5_boolean verbose, + krb5_boolean omit_nra) +{ + return k5beta7_common(context, entry, name, fp, verbose, omit_nra, TRUE); +} + +static void +dump_k5beta7_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg = data; + + fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, 0); +} + +static void +dump_r1_8_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg = data; + + fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + entry->name, entry->pw_min_life, entry->pw_max_life, + entry->pw_min_length, entry->pw_min_classes, entry->pw_history_num, + 0, entry->pw_max_fail, entry->pw_failcnt_interval, + entry->pw_lockout_duration); +} + +static void +dump_r1_11_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg = data; + + fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t" + "%d\t%d\t%d\t%s\t%d", entry->name, entry->pw_min_life, + entry->pw_max_life, entry->pw_min_length, entry->pw_min_classes, + entry->pw_history_num, 0, entry->pw_max_fail, + entry->pw_failcnt_interval, entry->pw_lockout_duration, + entry->attributes, entry->max_life, entry->max_renewable_life, + entry->allowed_keysalts ? entry->allowed_keysalts : "-", + entry->n_tl_data); + + dump_tl_data(arg->ofile, entry->tl_data, FALSE); + fprintf(arg->ofile, "\n"); +} + +static void +print_key_data(FILE *f, krb5_key_data *kd) +{ + int c; + + fprintf(f, "%d\t%d\t", kd->key_data_type[0], kd->key_data_length[0]); + for (c = 0; c < kd->key_data_length[0]; c++) + fprintf(f, "%02x ", kd->key_data_contents[0][c]); +} + +/* Output osa_adb_princ_ent data in a printable serialized format, suitable for + * ovsec_adm_import consumption. */ +static krb5_error_code +dump_ov_princ(krb5_context context, krb5_db_entry *entry, const char *name, + FILE *fp, krb5_boolean verbose, krb5_boolean omit_nra) +{ + char *princstr; + unsigned int x; + int y, foundcrc; + krb5_tl_data tl_data; + osa_princ_ent_rec adb; + XDR xdrs; + krb5_key_data *key_data; + + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + if (krb5_dbe_lookup_tl_data(context, entry, &tl_data) || + tl_data.tl_data_length == 0) + return 0; + + memset(&adb, 0, sizeof(adb)); + xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents, + tl_data.tl_data_length, XDR_DECODE); + if (!xdr_osa_princ_ent_rec(&xdrs, &adb)) { + xdr_destroy(&xdrs); + return KADM5_XDR_FAILURE; + } + xdr_destroy(&xdrs); + + krb5_unparse_name(context, entry->princ, &princstr); + fprintf(fp, "princ\t%s\t", princstr); + if (adb.policy == NULL) + fputc('\t', fp); + else + fprintf(fp, "%s\t", adb.policy); + fprintf(fp, "%lx\t%d\t%d\t%d", adb.aux_attributes, adb.old_key_len, + adb.old_key_next, adb.admin_history_kvno); + + for (x = 0; x < adb.old_key_len; x++) { + foundcrc = 0; + for (y = 0; y < adb.old_keys[x].n_key_data; y++) { + key_data = &adb.old_keys[x].key_data[y]; + if (key_data->key_data_type[0] != ENCTYPE_DES_CBC_CRC) + continue; + if (foundcrc) { + fprintf(stderr, _("Warning! Multiple DES-CBC-CRC keys for " + "principal %s; skipping duplicates.\n"), + princstr); + continue; + } + foundcrc++; + + fputc('\t', fp); + print_key_data(fp, key_data); + } + if (!foundcrc) { + fprintf(stderr, _("Warning! No DES-CBC-CRC key for principal %s, " + "cannot generate OV-compatible record; " + "skipping\n"), princstr); + } + } + + fputc('\n', fp); + free(princstr); + xdr_free(xdr_osa_princ_ent_rec, &adb); + return 0; +} + +static krb5_error_code +dump_iterator(void *ptr, krb5_db_entry *entry) +{ + krb5_error_code ret; + struct dump_args *args = ptr; + char *name; + + ret = krb5_unparse_name(args->context, entry->princ, &name); + if (ret) { + com_err(progname, ret, _("while unparsing principal name")); + return ret; + } + + /* Re-encode the keys in the new master key, if necessary. */ + if (mkey_convert) { + ret = master_key_convert(args->context, entry); + if (ret) { + com_err(progname, ret, _("while converting %s to new master key"), + name); + goto cleanup; + } + } + + /* Don't dump this entry if we have match strings and it doesn't match. */ + if (args->nnames > 0 && !name_matches(name, args)) + goto cleanup; + + ret = args->dump->dump_princ(args->context, entry, name, args->ofile, + args->verbose, args->omit_nra); + +cleanup: + free(name); + return ret; +} + +static inline void +load_err(const char *fname, int lineno, const char *msg) +{ + fprintf(stderr, _("%s(%d): %s\n"), fname, lineno, msg); +} + +/* Read a string of bytes. Increment *lp for each newline. Return 0 on + * success, 1 on failure. */ +static int +read_string(FILE *f, char *buf, int len, int *lp) +{ + int c, i; + + for (i = 0; i < len; i++) { + c = fgetc(f); + if (c < 0) + return 1; + if (c == '\n') + (*lp)++; + buf[i] = c; + } + buf[len] = '\0'; + return 0; +} + +/* Read a string of two-character representations of bytes. */ +static int +read_octet_string(FILE *f, unsigned char *buf, int len) +{ + int c, i; + + for (i = 0; i < len; i++) { + if (fscanf(f, "%02x", &c) != 1) + return 1; + buf[i] = c; + } + return 0; +} + +/* Read the end of a dumpfile record. */ +static void +read_record_end(FILE *f, const char *fn, int lineno) +{ + int ch; + + if ((ch = fgetc(f)) != ';' || (ch = fgetc(f)) != '\n') { + fprintf(stderr, _("%s(%d): ignoring trash at end of line: "), fn, + lineno); + while (ch != '\n') { + putc(ch, stderr); + ch = fgetc(f); + } + putc(ch, stderr); + } +} + +/* Allocate and form a TL data list of a desired size. */ +static int +alloc_tl_data(krb5_int16 n_tl_data, krb5_tl_data **tldp) +{ + krb5_tl_data **tlp = tldp; + int i; + + for (i = 0; i < n_tl_data; i++) { + *tlp = calloc(1, sizeof(krb5_tl_data)); + if (*tlp == NULL) + return ENOMEM; /* caller cleans up */ + tlp = &((*tlp)->tl_data_next); + } + + return 0; +} + +/* If len is zero, read the string "-1" from fp. Otherwise allocate space and + * read len octets. Return 0 on success, 1 on failure. */ +static int +read_octets_or_minus1(FILE *fp, size_t len, unsigned char **out) +{ + int ival; + unsigned char *buf; + + *out = NULL; + if (len == 0) + return fscanf(fp, "%d", &ival) != 1 || ival != -1; + buf = malloc(len); + if (buf == NULL) + return 1; + if (read_octet_string(fp, buf, len)) { + free(buf); + return 1; + } + *out = buf; + return 0; +} + +/* Read TL data for a principal or policy. Print an error and return -1 on + * failure. */ +static int +process_tl_data(const char *fname, FILE *filep, int lineno, + krb5_tl_data *tl_data) +{ + krb5_tl_data *tl; + int nread, i1; + unsigned int u1; + + for (tl = tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%u\t", &i1, &u1); + if (nread != 2) { + load_err(fname, lineno, + _("cannot read tagged data type and length")); + return EINVAL; + } + tl->tl_data_type = i1; + tl->tl_data_length = u1; + if (read_octets_or_minus1(filep, tl->tl_data_length, + &tl->tl_data_contents)) { + load_err(fname, lineno, _("cannot read tagged data contents")); + return EINVAL; + } + } + + return 0; +} + +/* Read a beta 7 entry and add it to the database. Return -1 for end of file, + * 0 for success and 1 for failure. */ +static int +process_k5beta7_princ(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + int retval, nread, i, j; + krb5_db_entry *dbentry; + int t1, t2, t3, t4, t5, t6, t7; + unsigned int u1, u2, u3, u4, u5; + char *name = NULL; + krb5_key_data *kp = NULL, *kd; + krb5_tl_data *tl; + krb5_error_code ret; + + dbentry = calloc(1, sizeof(*dbentry)); + if (dbentry == NULL) + return 1; + (*linenop)++; + nread = fscanf(filep, "%u\t%u\t%u\t%u\t%u\t", &u1, &u2, &u3, &u4, &u5); + if (nread == EOF) { + retval = -1; + goto cleanup; + } + if (nread != 5) { + load_err(fname, *linenop, _("cannot match size tokens")); + goto fail; + } + + /* Get memory for flattened principal name */ + name = malloc(u2 + 1); + if (name == NULL) + goto fail; + + /* Get memory for and form tagged data linked list */ + if (alloc_tl_data(u3, &dbentry->tl_data)) + goto fail; + dbentry->n_tl_data = u3; + + /* Get memory for key list */ + if (u4 && (kp = calloc(u4, sizeof(krb5_key_data))) == NULL) + goto fail; + + dbentry->len = u1; + dbentry->n_key_data = u4; + dbentry->e_length = u5; + + if (kp != NULL) { + dbentry->key_data = kp; + kp = NULL; + } + + /* Read in and parse the principal name */ + if (read_string(filep, name, u2, linenop)) { + load_err(fname, *linenop, _("cannot read name string")); + goto fail; + } + ret = krb5_parse_name(context, name, &dbentry->princ); + if (ret) { + com_err(progname, ret, _("while parsing name %s"), name); + goto fail; + } + + /* Get the fixed principal attributes */ + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + &t1, &t2, &t3, &t4, &t5, &t6, &t7, &u1); + if (nread != 8) { + load_err(fname, *linenop, _("cannot read principal attributes")); + goto fail; + } + dbentry->attributes = t1; + dbentry->max_life = t2; + dbentry->max_renewable_life = t3; + dbentry->expiration = t4; + dbentry->pw_expiration = t5; + dbentry->last_success = t6; + dbentry->last_failed = t7; + dbentry->fail_auth_count = u1; + dbentry->mask = KADM5_LOAD | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | + KADM5_MAX_LIFE | KADM5_MAX_RLIFE | + KADM5_PRINC_EXPIRE_TIME | KADM5_LAST_SUCCESS | + KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT; + + /* Read tagged data. */ + if (dbentry->n_tl_data) { + if (process_tl_data(fname, filep, *linenop, dbentry->tl_data)) + goto fail; + for (tl = dbentry->tl_data; tl; tl = tl->tl_data_next) { + /* test to set mask fields */ + if (tl->tl_data_type == KRB5_TL_KADM_DATA) { + XDR xdrs; + osa_princ_ent_rec osa_princ_ent; + + /* + * Assuming aux_attributes will always be + * there + */ + dbentry->mask |= KADM5_AUX_ATTRIBUTES; + + /* test for an actual policy reference */ + memset(&osa_princ_ent, 0, sizeof(osa_princ_ent)); + xdrmem_create(&xdrs, (char *)tl->tl_data_contents, + tl->tl_data_length, XDR_DECODE); + if (xdr_osa_princ_ent_rec(&xdrs, &osa_princ_ent)) { + if ((osa_princ_ent.aux_attributes & KADM5_POLICY) && + osa_princ_ent.policy != NULL) + dbentry->mask |= KADM5_POLICY; + kdb_free_entry(NULL, NULL, &osa_princ_ent); + } + xdr_destroy(&xdrs); + } + } + dbentry->mask |= KADM5_TL_DATA; + } + + /* Get the key data. */ + for (i = 0; i < dbentry->n_key_data; i++) { + kd = &dbentry->key_data[i]; + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread != 2) { + load_err(fname, *linenop, _("cannot read key size and version")); + goto fail; + } + + kd->key_data_ver = t1; + kd->key_data_kvno = t2; + + for (j = 0; j < t1; j++) { + nread = fscanf(filep, "%d\t%d\t", &t3, &t4); + if (nread != 2) { + load_err(fname, *linenop, + _("cannot read key type and length")); + goto fail; + } + kd->key_data_type[j] = t3; + kd->key_data_length[j] = t4; + if (read_octets_or_minus1(filep, t4, &kd->key_data_contents[j])) { + load_err(fname, *linenop, _("cannot read key data")); + goto fail; + } + } + } + if (dbentry->n_key_data) + dbentry->mask |= KADM5_KEY_DATA; + + /* Get the extra data */ + if (read_octets_or_minus1(filep, dbentry->e_length, &dbentry->e_data)) { + load_err(fname, *linenop, _("cannot read extra data")); + goto fail; + } + + /* Finally, find the end of the record. */ + read_record_end(filep, fname, *linenop); + + ret = krb5_db_put_principal(context, dbentry); + if (ret) { + com_err(progname, ret, _("while storing %s"), name); + goto fail; + } + + if (verbose) + fprintf(stderr, "%s\n", name); + retval = 0; + +cleanup: + free(kp); + free(name); + krb5_db_free_principal(context, dbentry); + return retval; + +fail: + retval = 1; + goto cleanup; +} + +static int +process_k5beta7_policy(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + osa_policy_ent_rec rec; + char namebuf[1024]; + unsigned int refcnt; + int nread, ret; + + memset(&rec, 0, sizeof(rec)); + + (*linenop)++; + rec.name = namebuf; + + nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u", rec.name, + &rec.pw_min_life, &rec.pw_max_life, &rec.pw_min_length, + &rec.pw_min_classes, &rec.pw_history_num, &refcnt); + if (nread == EOF) + return -1; + if (nread != 7) { + fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); + return 1; + } + + ret = krb5_db_create_policy(context, &rec); + if (ret) + ret = krb5_db_put_policy(context, &rec); + if (ret) { + com_err(progname, ret, _("while creating policy")); + return 1; + } + if (verbose) + fprintf(stderr, _("created policy %s\n"), rec.name); + + return 0; +} + +static int +process_r1_8_policy(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + osa_policy_ent_rec rec; + char namebuf[1024]; + unsigned int refcnt; + int nread, ret; + + memset(&rec, 0, sizeof(rec)); + + (*linenop)++; + rec.name = namebuf; + + nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u", + rec.name, &rec.pw_min_life, &rec.pw_max_life, + &rec.pw_min_length, &rec.pw_min_classes, + &rec.pw_history_num, &refcnt, &rec.pw_max_fail, + &rec.pw_failcnt_interval, &rec.pw_lockout_duration); + if (nread == EOF) + return -1; + if (nread != 10) { + fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); + return 1; + } + + ret = krb5_db_create_policy(context, &rec); + if (ret) + ret = krb5_db_put_policy(context, &rec); + if (ret) { + com_err(progname, ret, _("while creating policy")); + return 1; + } + if (verbose) + fprintf(stderr, "created policy %s\n", rec.name); + + return 0; +} + +static int +process_r1_11_policy(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + osa_policy_ent_rec rec; + krb5_tl_data *tl, *tl_next; + char namebuf[1024]; + char keysaltbuf[KRB5_KDB_MAX_ALLOWED_KS_LEN + 1]; + unsigned int refcnt; + int nread, c, ret = 0; + + memset(&rec, 0, sizeof(rec)); + + (*linenop)++; + rec.name = namebuf; + + /* + * Due to a historical error, iprop dumps use the same version before and + * after the 1.11 policy extensions. So we need to accept both 1.8-format + * and 1.11-format policy entries. Begin by reading the 1.8 fields. + */ + nread = fscanf(filep, "%1023s\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u", + rec.name, &rec.pw_min_life, &rec.pw_max_life, + &rec.pw_min_length, &rec.pw_min_classes, + &rec.pw_history_num, &refcnt, &rec.pw_max_fail, + &rec.pw_failcnt_interval, &rec.pw_lockout_duration); + if (nread == EOF) + return -1; + if (nread != 10) { + fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); + return 1; + } + + /* The next character should be a newline (1.8) or a tab (1.11). */ + c = getc(filep); + if (c == EOF) + return -1; + if (c != '\n') { + /* Read the additional 1.11-format fields. */ + rec.allowed_keysalts = keysaltbuf; + nread = fscanf(filep, "%u\t%u\t%u\t" + K5CONST_WIDTH_SCANF_STR(KRB5_KDB_MAX_ALLOWED_KS_LEN) + "\t%hd", &rec.attributes, &rec.max_life, + &rec.max_renewable_life, rec.allowed_keysalts, + &rec.n_tl_data); + if (nread == EOF) + return -1; + if (nread != 5) { + fprintf(stderr, _("cannot parse policy (%d read)\n"), nread); + return 1; + } + + if (rec.allowed_keysalts && !strcmp(rec.allowed_keysalts, "-")) + rec.allowed_keysalts = NULL; + + /* Get TL data */ + ret = alloc_tl_data(rec.n_tl_data, &rec.tl_data); + if (ret) + goto cleanup; + + ret = process_tl_data(fname, filep, *linenop, rec.tl_data); + if (ret) + goto cleanup; + } + + ret = krb5_db_create_policy(context, &rec); + if (ret) + ret = krb5_db_put_policy(context, &rec); + if (ret) { + com_err(progname, ret, _("while creating policy")); + goto cleanup; + } + if (verbose) + fprintf(stderr, "created policy %s\n", rec.name); + +cleanup: + for (tl = rec.tl_data; tl; tl = tl_next) { + tl_next = tl->tl_data_next; + free(tl->tl_data_contents); + free(tl); + } + return ret ? 1 : 0; +} + +/* Read a record which is tagged with "princ" or "policy", calling princfn + * or policyfn as appropriate. */ +static int +process_tagged(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop, load_func princfn, + load_func policyfn) +{ + int nread; + char rectype[100]; + + nread = fscanf(filep, "%99s\t", rectype); + if (nread == EOF) + return -1; + if (nread != 1) + return 1; + if (strcmp(rectype, "princ") == 0) + return (*princfn)(context, fname, filep, verbose, linenop); + if (strcmp(rectype, "policy") == 0) + return (*policyfn)(context, fname, filep, verbose, linenop); + if (strcmp(rectype, "End") == 0) /* Only expected for OV format */ + return -1; + + fprintf(stderr, _("unknown record type \"%s\"\n"), rectype); + return 1; +} + +static int +process_k5beta7_record(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + return process_tagged(context, fname, filep, verbose, linenop, + process_k5beta7_princ, process_k5beta7_policy); +} + +static int +process_ov_record(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + return process_tagged(context, fname, filep, verbose, linenop, + process_ov_principal, process_k5beta7_policy); +} + +static int +process_r1_8_record(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + return process_tagged(context, fname, filep, verbose, linenop, + process_k5beta7_princ, process_r1_8_policy); +} + +static int +process_r1_11_record(krb5_context context, const char *fname, FILE *filep, + krb5_boolean verbose, int *linenop) +{ + return process_tagged(context, fname, filep, verbose, linenop, + process_k5beta7_princ, process_r1_11_policy); +} + +dump_version beta7_version = { + "Kerberos version 5", + "kdb5_util load_dump version 4\n", + 0, + 0, + 0, + dump_k5beta7_princ, + dump_k5beta7_policy, + process_k5beta7_record, +}; +dump_version ov_version = { + "OpenV*Secure V1.0", + "OpenV*Secure V1.0\t", + 1, + 0, + 0, + dump_ov_princ, + dump_k5beta7_policy, + process_ov_record +}; +dump_version r1_3_version = { + "Kerberos version 5 release 1.3", + "kdb5_util load_dump version 5\n", + 0, + 0, + 0, + dump_k5beta7_princ_withpolicy, + dump_k5beta7_policy, + process_k5beta7_record, +}; +dump_version r1_8_version = { + "Kerberos version 5 release 1.8", + "kdb5_util load_dump version 6\n", + 0, + 0, + 0, + dump_k5beta7_princ_withpolicy, + dump_r1_8_policy, + process_r1_8_record, +}; +dump_version r1_11_version = { + "Kerberos version 5 release 1.11", + "kdb5_util load_dump version 7\n", + 0, + 0, + 0, + dump_k5beta7_princ_withpolicy, + dump_r1_11_policy, + process_r1_11_record, +}; +dump_version iprop_version = { + "Kerberos iprop version", + "iprop", + 0, + 1, + 0, + dump_k5beta7_princ_withpolicy, + dump_k5beta7_policy, + process_k5beta7_record, +}; +dump_version ipropx_1_version = { + "Kerberos iprop extensible version", + "ipropx", + 0, + 1, + 1, + dump_k5beta7_princ_withpolicy, + dump_r1_11_policy, + process_r1_11_record, +}; + +/* Read the dump header. Return 1 on success, 0 if the file is not a + * recognized iprop dump format. */ +static int +parse_iprop_header(char *buf, dump_version **dv, kdb_last_t *last) +{ + char head[128]; + int nread; + uint32_t u[4]; + uint32_t *up = &u[0]; + + nread = sscanf(buf, "%127s %u %u %u %u", head, &u[0], &u[1], &u[2], &u[3]); + if (nread < 1) + return 0; + + if (!strcmp(head, ipropx_1_version.header)) { + if (nread != 5) + return 0; + if (u[0] == IPROPX_VERSION_0) { + *dv = &iprop_version; + } else if (u[0] == IPROPX_VERSION_1) { + *dv = &ipropx_1_version; + } else { + fprintf(stderr, _("%s: Unknown iprop dump version %d\n"), progname, + u[0]); + return 0; + } + up = &u[1]; + } else if (!strcmp(head, iprop_version.header)) { + if (nread != 4) + return 0; + *dv = &iprop_version; + } else { + fprintf(stderr, "Invalid iprop header\n"); + return 0; + } + + last->last_sno = *up++; + last->last_time.seconds = *up++; + last->last_time.useconds = *up++; + return 1; +} + +/* Return true if the serial number and timestamp in an existing dump file is + * in the ulog. */ +static krb5_boolean +current_dump_sno_in_ulog(krb5_context context, const char *ifile) +{ + update_status_t status; + dump_version *junk; + kdb_last_t last; + char buf[BUFSIZ]; + FILE *f; + + f = fopen(ifile, "r"); + if (f == NULL) + return 0; /* aliasing other errors to ENOENT here is OK */ + + if (fgets(buf, sizeof(buf), f) == NULL) + return errno ? -1 : 0; + fclose(f); + + if (!parse_iprop_header(buf, &junk, &last)) + return 0; + + status = ulog_get_sno_status(context, &last); + return status == UPDATE_OK || status == UPDATE_NIL; +} + +/* + * usage is: + * dump_db [-b7] [-ov] [-r13] [-r18] [-verbose] [-mkey_convert] + * [-new_mkey_file mkey_file] [-rev] [-recurse] + * [filename [principals...]] + */ +void +dump_db(int argc, char **argv) +{ + FILE *f; + struct dump_args args; + char *ofile = NULL, *tmpofile = NULL, *new_mkey_file = NULL; + krb5_error_code ret, retval; + dump_version *dump; + int aindex, ok_fd = -1; + bool_t dump_sno = FALSE; + kdb_log_context *log_ctx; + unsigned int ipropx_version = IPROPX_VERSION_0; + krb5_kvno kt_kvno; + krb5_boolean conditional = FALSE; + kdb_last_t last; + krb5_flags iterflags = 0; + + /* Parse the arguments. */ + dump = &r1_11_version; + args.verbose = FALSE; + args.omit_nra = FALSE; + mkey_convert = FALSE; + log_ctx = util_context->kdblog_context; + + /* + * Parse the qualifiers. + */ + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], "-b7")) { + dump = &beta7_version; + } else if (!strcmp(argv[aindex], "-ov")) { + dump = &ov_version; + } else if (!strcmp(argv[aindex], "-r13")) { + dump = &r1_3_version; + } else if (!strcmp(argv[aindex], "-r18")) { + dump = &r1_8_version; + } else if (!strncmp(argv[aindex], "-i", 2)) { + if (log_ctx && log_ctx->iproprole) { + /* ipropx_version is the maximum version acceptable. */ + ipropx_version = atoi(argv[aindex] + 2); + dump = ipropx_version ? &ipropx_1_version : &iprop_version; + /* + * dump_sno is used to indicate if the serial number should be + * populated in the output file to be used later by iprop for + * updating the slave's update log when loading. + */ + dump_sno = TRUE; + /* FLAG_OMIT_NRA is set to indicate that non-replicated + * attributes should be omitted. */ + args.omit_nra = TRUE; + } else { + fprintf(stderr, _("Iprop not enabled\n")); + goto error; + } + } else if (!strcmp(argv[aindex], "-c")) { + conditional = 1; + } else if (!strcmp(argv[aindex], "-verbose")) { + args.verbose = TRUE; + } else if (!strcmp(argv[aindex], "-mkey_convert")) { + mkey_convert = 1; + } else if (!strcmp(argv[aindex], "-new_mkey_file")) { + new_mkey_file = argv[++aindex]; + mkey_convert = 1; + } else if (!strcmp(argv[aindex], "-rev")) { + iterflags |= KRB5_DB_ITER_REV; + } else if (!strcmp(argv[aindex], "-recurse")) { + iterflags |= KRB5_DB_ITER_RECURSE; + } else { + break; + } + } + + args.names = NULL; + args.nnames = 0; + if (aindex < argc) { + ofile = argv[aindex]; + aindex++; + if (aindex < argc) { + args.names = &argv[aindex]; + args.nnames = argc - aindex; + } + } + + /* If a conditional ipropx dump we check if the existing dump is + * good enough. */ + if (ofile != NULL && conditional) { + if (!dump->iprop) { + com_err(progname, 0, + _("Conditional dump is an undocumented option for " + "use only for iprop dumps")); + goto error; + } + if (current_dump_sno_in_ulog(util_context, ofile)) + return; + } + + /* + * Make sure the database is open. The policy database only has + * to be opened if we try a dump that uses it. + */ + if (!dbactive) { + com_err(progname, 0, _("Database not currently opened!")); + goto error; + } + + /* + * If we're doing a master key conversion, set up for it. + */ + if (mkey_convert) { + if (!valid_master_key) { + /* TRUE here means read the keyboard, but only once */ + retval = krb5_db_fetch_mkey(util_context, master_princ, + master_keyblock.enctype, TRUE, FALSE, + NULL, NULL, NULL, &master_keyblock); + if (retval) { + com_err(progname, retval, _("while reading master key")); + exit(1); + } + retval = krb5_db_fetch_mkey_list(util_context, master_princ, + &master_keyblock); + if (retval) { + com_err(progname, retval, _("while verifying master key")); + exit(1); + } + } + new_master_keyblock.enctype = global_params.enctype; + if (new_master_keyblock.enctype == ENCTYPE_UNKNOWN) + new_master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + + if (new_mkey_file) { + if (global_params.mask & KADM5_CONFIG_KVNO) + kt_kvno = global_params.kvno; + else + kt_kvno = IGNORE_VNO; + + retval = krb5_db_fetch_mkey(util_context, master_princ, + new_master_keyblock.enctype, FALSE, + FALSE, new_mkey_file, &kt_kvno, NULL, + &new_master_keyblock); + if (retval) { + com_err(progname, retval, _("while reading new master key")); + exit(1); + } + } else { + printf(_("Please enter new master key....\n")); + retval = krb5_db_fetch_mkey(util_context, master_princ, + new_master_keyblock.enctype, TRUE, + TRUE, NULL, NULL, NULL, + &new_master_keyblock); + if (retval) { + com_err(progname, retval, _("while reading new master key")); + exit(1); + } + } + /* Get new master key vno that will be used to protect princs. */ + new_mkvno = get_next_kvno(util_context, master_entry); + } + + ret = 0; + + if (ofile != NULL && strcmp(ofile, "-")) { + /* Discourage accidental dumping to filenames beginning with '-'. */ + if (ofile[0] == '-') + usage(); + if (!prep_ok_file(util_context, ofile, &ok_fd)) + return; /* prep_ok_file() bumps exit_status */ + f = create_ofile(ofile, &tmpofile); + if (f == NULL) { + com_err(progname, errno, _("while opening %s for writing"), ofile); + goto error; + } + } else { + f = stdout; + } + + args.ofile = f; + args.context = util_context; + args.dump = dump; + fprintf(args.ofile, "%s", dump->header); + + if (dump_sno) { + ret = ulog_get_last(util_context, &last); + if (ret) { + com_err(progname, ret, _("while reading update log header")); + goto error; + } + if (ipropx_version) + fprintf(f, " %u", IPROPX_VERSION); + fprintf(f, " %u", last.last_sno); + fprintf(f, " %u", last.last_time.seconds); + fprintf(f, " %u", last.last_time.useconds); + } + + if (dump->header[strlen(dump->header)-1] != '\n') + fputc('\n', args.ofile); + + ret = krb5_db_iterate(util_context, NULL, dump_iterator, &args, iterflags); + if (ret) { + com_err(progname, ret, _("performing %s dump"), dump->name); + goto error; + } + + if (dump->dump_policy != NULL) { + ret = krb5_db_iter_policy(util_context, "*", dump->dump_policy, &args); + if (ret) { + com_err(progname, ret, _("performing %s dump"), dump->name); + goto error; + } + } + + if (f != stdout) { + fclose(f); + finish_ofile(ofile, &tmpofile); + update_ok_file(util_context, ok_fd); + } + return; + +error: + if (tmpofile != NULL) + unlink(tmpofile); + free(tmpofile); + exit_status++; +} + +/* Restore the database from any version dump file. */ +static int +restore_dump(krb5_context context, char *dumpfile, FILE *f, + krb5_boolean verbose, dump_version *dump) +{ + int err = 0; + int lineno = 1; + + /* Process the records. */ + while (!(err = dump->load_record(context, dumpfile, f, verbose, &lineno))); + if (err != -1) { + fprintf(stderr, _("%s: error processing line %d of %s\n"), progname, + lineno, dumpfile); + return err; + } + return 0; +} + +/* + * Usage: load_db [-ov] [-b7] [-r13] [-verbose] [-update] [-hash] + * filename + */ +void +load_db(int argc, char **argv) +{ + krb5_error_code ret; + FILE *f = NULL; + char *dumpfile = NULL, *dbname, buf[BUFSIZ]; + dump_version *load = NULL; + int aindex; + kdb_log_context *log_ctx; + kdb_last_t last; + krb5_boolean db_locked = FALSE, temp_db_created = FALSE; + krb5_boolean verbose = FALSE, update = FALSE, iprop_load = FALSE; + + /* Parse the arguments. */ + dbname = global_params.dbname; + exit_status = 0; + log_ctx = util_context->kdblog_context; + + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], "-b7")){ + load = &beta7_version; + } else if (!strcmp(argv[aindex], "-ov")) { + load = &ov_version; + } else if (!strcmp(argv[aindex], "-r13")) { + load = &r1_3_version; + } else if (!strcmp(argv[aindex], "-r18")){ + load = &r1_8_version; + } else if (!strcmp(argv[aindex], "-i")) { + if (log_ctx && log_ctx->iproprole) { + load = &iprop_version; + iprop_load = TRUE; + } else { + fprintf(stderr, _("Iprop not enabled\n")); + goto error; + } + } else if (!strcmp(argv[aindex], "-verbose")) { + verbose = TRUE; + } else if (!strcmp(argv[aindex], "-update")){ + update = TRUE; + } else if (!strcmp(argv[aindex], "-hash")) { + if (!add_db_arg("hash=true")) { + com_err(progname, ENOMEM, _("while parsing options")); + goto error; + } + } else { + break; + } + } + if (argc - aindex != 1) + usage(); + dumpfile = argv[aindex]; + + /* Open the dumpfile. */ + if (dumpfile != NULL) { + f = fopen(dumpfile, "r"); + if (f == NULL) { + com_err(progname, errno, _("while opening %s"), dumpfile); + goto error; + } + } else { + f = stdin; + dumpfile = _("standard input"); + } + + /* Auto-detect dump version if we weren't told, or verify if we were. */ + if (fgets(buf, sizeof(buf), f) == NULL) { + fprintf(stderr, _("%s: can't read dump header in %s\n"), progname, + dumpfile); + goto error; + } + if (load) { + /* Only check what we know; some headers only contain a prefix. + * NB: this should work for ipropx even though load is iprop */ + if (strncmp(buf, load->header, strlen(load->header)) != 0) { + fprintf(stderr, _("%s: dump header bad in %s\n"), progname, + dumpfile); + goto error; + } + } else { + if (strcmp(buf, beta7_version.header) == 0) { + load = &beta7_version; + } else if (strcmp(buf, r1_3_version.header) == 0) { + load = &r1_3_version; + } else if (strcmp(buf, r1_8_version.header) == 0) { + load = &r1_8_version; + } else if (strcmp(buf, r1_11_version.header) == 0) { + load = &r1_11_version; + } else if (strncmp(buf, ov_version.header, + strlen(ov_version.header)) == 0) { + load = &ov_version; + } else { + fprintf(stderr, _("%s: dump header bad in %s\n"), progname, + dumpfile); + goto error; + } + } + + if (global_params.iprop_enabled && + ulog_map(util_context, global_params.iprop_logfile, + global_params.iprop_ulogsize)) { + fprintf(stderr, _("Could not open iprop ulog\n")); + goto error; + } + + if (load->updateonly && !update) { + fprintf(stderr, _("%s: dump version %s can only be loaded with the " + "-update flag\n"), progname, load->name); + goto error; + } + + /* If we are not in update mode, we create an alternate database and then + * promote it to be the live db. */ + if (!update) { + if (!add_db_arg("temporary")) { + com_err(progname, ENOMEM, _("computing parameters for database")); + goto error; + } + + if (iprop_load && !add_db_arg("merge_nra")) { + com_err(progname, ENOMEM, _("computing parameters for database")); + goto error; + } + + ret = krb5_db_create(util_context, db5util_db_args); + if (ret) { + com_err(progname, ret, _("while creating database")); + goto error; + } + temp_db_created = TRUE; + } else { + /* Initialize the database. */ + ret = krb5_db_open(util_context, db5util_db_args, + KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN); + if (ret) { + com_err(progname, ret, _("while opening database")); + goto error; + } + + /* Make sure the db is left unusable if the update fails, if the db + * supports locking. */ + ret = krb5_db_lock(util_context, KRB5_DB_LOCKMODE_PERMANENT); + if (ret == 0) { + db_locked = TRUE; + } else if (ret != KRB5_PLUGIN_OP_NOTSUPP) { + com_err(progname, ret, _("while permanently locking database")); + goto error; + } + } + + if (log_ctx != NULL && log_ctx->iproprole && !update) { + /* Don't record updates we are making to the temporary DB. We will + * reinitialize or update the ulog header after promoting it. */ + log_ctx->iproprole = IPROP_SLAVE; + if (iprop_load) { + /* Parse the iprop header information. */ + if (!parse_iprop_header(buf, &load, &last)) + goto error; + } + } + + if (restore_dump(util_context, dumpfile ? dumpfile : _("standard input"), + f, verbose, load)) { + fprintf(stderr, _("%s: %s restore failed\n"), progname, load->name); + goto error; + } + + if (db_locked && (ret = krb5_db_unlock(util_context))) { + com_err(progname, ret, _("while unlocking database")); + goto error; + } + + if (!update) { + /* Initialize the ulog header before promoting so we can't leave behind + * the pre-load ulog state if we are killed just after promoting. */ + if (log_ctx != NULL && log_ctx->iproprole) { + ret = ulog_init_header(util_context); + if (ret) { + com_err(progname, ret, _("while reinitializing update log")); + goto error; + } + } + + ret = krb5_db_promote(util_context, db5util_db_args); + /* Ignore a not supported error since there is nothing to do about it + * anyway. */ + if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) { + com_err(progname, ret, + _("while making newly loaded database live")); + goto error; + } + + if (log_ctx != NULL && log_ctx->iproprole) { + /* Reinitialize the ulog header since we replaced the DB, and + * record the iprop state if we received it. */ + ret = ulog_init_header(util_context); + if (ret) { + com_err(progname, ret, _("while reinitializing update log")); + goto error; + } + if (iprop_load) { + ret = ulog_set_last(util_context, &last); + if (ret) { + com_err(progname, ret, + _("while writing update log header")); + goto error; + } + } + } + } + +cleanup: + /* If we created a temporary DB but didn't succeed, destroy it. */ + if (exit_status && temp_db_created) { + ret = krb5_db_destroy(util_context, db5util_db_args); + /* Ignore a not supported error since there is nothing to do about + * it anyway. */ + if (ret != 0 && ret != KRB5_PLUGIN_OP_NOTSUPP) { + com_err(progname, ret, _("while deleting bad database %s"), + dbname); + } + } + + if (f != NULL && f != stdin) + fclose(f); + + return; + +error: + exit_status++; + goto cleanup; +} diff --git a/src/kadmin/dbutil/import_err.et b/src/kadmin/dbutil/import_err.et new file mode 100644 index 000000000000..48bb8fb9a924 --- /dev/null +++ b/src/kadmin/dbutil/import_err.et @@ -0,0 +1,22 @@ +error_table imp +error_code IMPORT_BAD_FILE, "Input not recognized as database dump" +error_code IMPORT_BAD_TOKEN, "Bad token in dump file." +error_code IMPORT_BAD_VERSION, "Bad version in dump file" +error_code IMPORT_BAD_RECORD, "Defective record encountered: " +error_code IMPORT_BAD_FOOTER, "Truncated input file detected." +error_code IMPORT_FAILED, "Import of dump failed" +error_code IMPORT_MISMATCH_COUNT, "Number of records imported does not match count" +error_code IMPORT_UNK_OPTION, "Unknown command line option.\nUsage: ovsec_adm_import [filename]" +error_code IMPORT_WARN_DB, "Warning -- continuing to import will overwrite existing databases!" +error_code IMPORT_RENAME_FAILED, "Database rename Failed!!" +error_code IMPORT_EXTRA_DATA, "Extra data after footer is ignored." +error_code IMPORT_CONFIRM, "Proceed <y|n>?" +error_code IMPORT_OPEN_DUMP, "while opening input file" +error_code IMPORT_IMPORT, "while importing databases" +error_code IMPORT_TTY, "cannot open /dev/tty!!" +error_code IMPORT_RENAME_OPEN, "while opening databases" +error_code IMPORT_RENAME_LOCK, "while acquiring permanent lock" +error_code IMPORT_RENAME_UNLOCK, "while releasing permanent lock" +error_code IMPORT_RENAME_CLOSE, "while closing databases" +error_code IMPORT_GET_PARAMS, "while retrieving configuration parameters" +end diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c new file mode 100644 index 000000000000..1745a4d62039 --- /dev/null +++ b/src/kadmin/dbutil/kadm5_create.c @@ -0,0 +1,270 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Source$ + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <k5-int.h> +#include <ctype.h> +#include <kdb.h> +#include <kadm5/admin.h> +#include <adm_proto.h> + +#include "fake-addrinfo.h" + + +#include <krb5.h> +#include <kdb.h> +#include "kdb5_util.h" + +static int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime); +static int add_admin_princs(void *handle, krb5_context context, char *realm); + +#define ERR 1 +#define OK 0 + +#define ADMIN_LIFETIME 60*60*3 /* 3 hours */ +#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ + +/* + * Function: kadm5_create + * + * Purpose: create admin principals in KDC database + * + * Arguments: params (r) configuration parameters to use + * + * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE + * principals in the KDC database and sets their attributes + * appropriately. + */ +int kadm5_create(kadm5_config_params *params) +{ + int retval; + krb5_context context; + + kadm5_config_params lparams; + + if ((retval = kadm5_init_krb5_context(&context))) + exit(ERR); + + /* + * The lock file has to exist before calling kadm5_init, but + * params->admin_lockfile may not be set yet... + */ + if ((retval = kadm5_get_config_params(context, 1, + params, &lparams))) { + com_err(progname, retval, _("while looking up the Kerberos " + "configuration")); + return 1; + } + + retval = kadm5_create_magic_princs(&lparams, context); + + kadm5_free_config_params(context, &lparams); + krb5_free_context(context); + + return retval; +} + +int kadm5_create_magic_princs(kadm5_config_params *params, + krb5_context context) +{ + int retval; + void *handle; + + retval = krb5_klog_init(context, "admin_server", progname, 0); + if (retval) + return retval; + if ((retval = kadm5_init(context, progname, NULL, NULL, params, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_4, + db5util_db_args, + &handle))) { + com_err(progname, retval, _("while initializing the Kerberos admin " + "interface")); + return retval; + } + + retval = add_admin_princs(handle, context, params->realm); + + kadm5_destroy(handle); + + krb5_klog_close(context); + + return retval; +} + +/* + * Function: add_admin_princs + * + * Purpose: create admin principals + * + * Arguments: + * + * rseed (input) random seed + * realm (input) realm, or NULL for default realm + * <return value> (output) status, 0 for success, 1 for serious error + * + * Requires: + * + * Effects: + * + * add_admin_princs creates KADM5_ADMIN_SERVICE, + * KADM5_CHANGEPW_SERVICE. If any of these exist a message is + * printed. If any of these existing principal do not have the proper + * attributes, a warning message is printed. + */ +static int add_admin_princs(void *handle, krb5_context context, char *realm) +{ + krb5_error_code ret = 0; + char *service_name = 0, *kiprop_name = 0, *canonhost = 0; + char localname[MAXHOSTNAMELEN]; + + if (gethostname(localname, MAXHOSTNAMELEN)) { + ret = errno; + perror("gethostname"); + goto clean_and_exit; + } + ret = krb5_expand_hostname(context, localname, &canonhost); + if (ret) { + com_err(progname, ret, _("while canonicalizing local hostname")); + goto clean_and_exit; + } + if (asprintf(&service_name, "kadmin/%s", canonhost) < 0) { + ret = ENOMEM; + fprintf(stderr, _("Out of memory\n")); + goto clean_and_exit; + } + if (asprintf(&kiprop_name, "kiprop/%s", canonhost) < 0) { + ret = ENOMEM; + fprintf(stderr, _("Out of memory\n")); + goto clean_and_exit; + } + + if ((ret = add_admin_princ(handle, context, + service_name, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_LOCKDOWN_KEYS, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + KADM5_ADMIN_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_LOCKDOWN_KEYS, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + KADM5_CHANGEPW_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_PWCHANGE_SERVICE | + KRB5_KDB_LOCKDOWN_KEYS, + CHANGEPW_LIFETIME))) + goto clean_and_exit; + + ret = add_admin_princ(handle, context, kiprop_name, realm, 0, 0); + +clean_and_exit: + krb5_free_string(context, canonhost); + free(service_name); + free(kiprop_name); + + return ret; +} + +/* + * Function: add_admin_princ + * + * Arguments: + * + * creator (r) principal to use as "mod_by" + * rseed (r) seed for random key generator + * name (r) principal name + * realm (r) realm name for principal + * attrs (r) principal's attributes + * lifetime (r) principal's max life, or 0 + * not_unique (r) error message for multiple entries, never used + * exists (r) warning message for principal exists + * wrong_attrs (r) warning message for wrong attributes + * + * Returns: + * + * OK on success + * ERR on serious errors + * + * Effects: + * + * If the principal is not unique, not_unique is printed (but this + * never happens). If the principal exists, then exists is printed + * and if the principals attributes != attrs, wrong_attrs is printed. + * Otherwise, the principal is created with mod_by creator and + * attributes attrs and max life of lifetime (if not zero). + */ + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime) +{ + char *fullname; + krb5_error_code ret; + kadm5_principal_ent_rec ent; + long flags; + + memset(&ent, 0, sizeof(ent)); + + if (asprintf(&fullname, "%s@%s", name, realm) < 0) { + com_err(progname, ENOMEM, _("while appending realm to principal")); + return ERR; + } + ret = krb5_parse_name(context, fullname, &ent.principal); + if (ret) { + com_err(progname, ret, _("while parsing admin principal name")); + return(ERR); + } + ent.max_life = lifetime; + ent.attributes = attrs; + + flags = KADM5_PRINCIPAL | KADM5_ATTRIBUTES; + if (lifetime) + flags |= KADM5_MAX_LIFE; + ret = kadm5_create_principal(handle, &ent, flags, NULL); + if (ret && ret != KADM5_DUP) { + com_err(progname, ret, _("while creating principal %s"), fullname); + krb5_free_principal(context, ent.principal); + free(fullname); + return ERR; + } + + krb5_free_principal(context, ent.principal); + free(fullname); + + return OK; +} diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c new file mode 100644 index 000000000000..92bb6f6eee75 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_create.c @@ -0,0 +1,502 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/dbutil/kdb5_create.c - Create a KDC database */ +/* + * Copyright 1990,1991,2001, 2002, 2008 by the Massachusetts Institute of + * Technology. All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <k5-int.h> +#include <kdb.h> +#include <kadm5/server_internal.h> +#include <kadm5/admin.h> +#include <adm_proto.h> +#include "kdb5_util.h" + +enum ap_op { + NULL_KEY, /* setup null keys */ + MASTER_KEY, /* use master key as new key */ + TGT_KEY /* special handling for tgt key */ +}; + +krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL }; + +struct realm_info { + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_keyblock *key; + krb5_int32 nkslist; + krb5_key_salt_tuple *kslist; +} rblock = { /* XXX */ + KRB5_KDB_MAX_LIFE, + KRB5_KDB_MAX_RLIFE, + KRB5_KDB_EXPIRATION, + KRB5_KDB_DEF_FLAGS, + (krb5_keyblock *) NULL, + 1, + &def_kslist +}; + +struct iterate_args { + krb5_context ctx; + struct realm_info *rblock; + krb5_db_entry *dbentp; +}; + +static krb5_error_code add_principal +(krb5_context, + krb5_principal, + enum ap_op, + struct realm_info *); + +/* + * Steps in creating a database: + * + * 1) use the db calls to open/create a new database + * + * 2) get a realm name for the new db + * + * 3) get a master password for the new db; convert to an encryption key. + * + * 4) create various required entries in the database + * + * 5) close & exit + */ + +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern char *mkey_fullname; +krb5_data master_salt; + +krb5_data tgt_princ_entries[] = { + {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, + {0, 0, 0} }; + +krb5_data db_creator_entries[] = { + {0, sizeof("db_creation")-1, "db_creation"} }; + +/* XXX knows about contents of krb5_principal, and that tgt names + are of form TGT/REALM@REALM */ +krb5_principal_data tgt_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + tgt_princ_entries, /* krb5_data *data */ + 2, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +krb5_principal_data db_create_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + db_creator_entries, /* krb5_data *data */ + 1, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +extern char *mkey_password; + +extern char *progname; +extern int exit_status; +extern kadm5_config_params global_params; +extern krb5_context util_context; + +void kdb5_create(argc, argv) + int argc; + char *argv[]; +{ + int optchar; + + krb5_error_code retval; + char *pw_str = 0; + unsigned int pw_size = 0; + int do_stash = 0; + krb5_data pwd, seed; + kdb_log_context *log_ctx; + krb5_kvno mkey_kvno; + int strong_random = 1; + + while ((optchar = getopt(argc, argv, "sW")) != -1) { + switch(optchar) { + case 's': + do_stash++; + break; + case 'h': + if (!add_db_arg("hash=true")) { + com_err(progname, ENOMEM, + _("while parsing command arguments\n")); + exit(1); + } + break; + case 'W': + strong_random = 0; + break; + case '?': + default: + usage(); + return; + } + } + + rblock.max_life = global_params.max_life; + rblock.max_rlife = global_params.max_rlife; + rblock.expiration = global_params.expiration; + rblock.flags = global_params.flags; + rblock.nkslist = global_params.num_keysalts; + rblock.kslist = global_params.keysalts; + + log_ctx = util_context->kdblog_context; + + printf(_("Loading random data\n")); + retval = krb5_c_random_os_entropy (util_context, strong_random, NULL); + if (retval) { + com_err(progname, retval, _("Loading random data")); + exit_status++; return; + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(util_context, + global_params.mkey_name, + global_params.realm, + &mkey_fullname, &master_princ))) { + com_err(progname, retval, _("while setting up master key name")); + exit_status++; return; + } + + krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm); + krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm)); + krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm); + krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm)); + krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm; + krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm); + + printf(_("Initializing database '%s' for realm '%s',\n" + "master key name '%s'\n"), + global_params.dbname, global_params.realm, mkey_fullname); + + if (!mkey_password) { + printf(_("You will be prompted for the database Master Password.\n")); + printf(_("It is important that you NOT FORGET this password.\n")); + fflush(stdout); + + pw_size = 1024; + pw_str = malloc(pw_size); + if (pw_str == NULL) { + com_err(progname, ENOMEM, _("while creating new master key")); + exit_status++; return; + } + + retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, + pw_str, &pw_size); + if (retval) { + com_err(progname, retval, + _("while reading master key from keyboard")); + exit_status++; return; + } + mkey_password = pw_str; + } + + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(util_context, master_princ, &master_salt); + if (retval) { + com_err(progname, retval, _("while calculating master key salt")); + exit_status++; return; + } + + retval = krb5_c_string_to_key(util_context, master_keyblock.enctype, + &pwd, &master_salt, &master_keyblock); + if (retval) { + com_err(progname, retval, + _("while transforming master key from password")); + exit_status++; return; + } + + rblock.key = &master_keyblock; + + seed = make_data(master_keyblock.contents, master_keyblock.length); + + if ((retval = krb5_c_random_seed(util_context, &seed))) { + com_err(progname, retval, + _("while initializing random key generator")); + exit_status++; return; + } + if ((retval = krb5_db_create(util_context, + db5util_db_args))) { + com_err(progname, retval, _("while creating database '%s'"), + global_params.dbname); + exit_status++; return; + } +/* if ((retval = krb5_db_fini(util_context))) { */ +/* com_err(progname, retval, "while closing current database"); */ +/* exit_status++; return; */ +/* } */ +/* if ((retval = krb5_db_open(util_context, db5util_db_args, KRB5_KDB_OPEN_RW))) { */ +/* com_err(progname, retval, "while initializing the database '%s'", */ +/* global_params.dbname); */ +/* exit_status++; return; */ +/* } */ + + if (log_ctx && log_ctx->iproprole) { + retval = ulog_map(util_context, global_params.iprop_logfile, + global_params.iprop_ulogsize); + if (retval) { + com_err(argv[0], retval, _("while creating update log")); + exit_status++; + return; + } + + /* + * We're reinitializing the update log in case one already + * existed, but this should never happen. + */ + retval = ulog_init_header(util_context); + if (retval) { + com_err(argv[0], retval, _("while initializing update log")); + exit_status++; + return; + } + + /* + * Since we're creating a new db we shouldn't worry about + * adding the initial principals since any slave might as well + * do full resyncs from this newly created db. + */ + log_ctx->iproprole = IPROP_NULL; + } + + if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) || + (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) { + com_err(progname, retval, _("while adding entries to the database")); + exit_status++; return; + } + + + + /* + * Always stash the master key so kadm5_create does not prompt for + * it; delete the file below if it was not requested. DO NOT EXIT + * BEFORE DELETING THE KEYFILE if do_stash is not set. + */ + + /* + * Determine the kvno to use, it must be that used to create the master key + * princ. + */ + if (global_params.mask & KADM5_CONFIG_KVNO) + mkey_kvno = global_params.kvno; /* user specified */ + else + mkey_kvno = 1; /* Default */ + + retval = krb5_db_store_master_key(util_context, + global_params.stash_file, + master_princ, + mkey_kvno, + &master_keyblock, + mkey_password); + if (retval) { + com_err(progname, retval, _("while storing key")); + printf(_("Warning: couldn't stash master key.\n")); + } + /* clean up */ + zapfree(pw_str, pw_size); + free(master_salt.data); + + if (kadm5_create(&global_params)) { + if (!do_stash) unlink(global_params.stash_file); + exit_status++; + return; + } + if (!do_stash) unlink(global_params.stash_file); + + return; +} + +static krb5_error_code +tgt_keysalt_iterate(ksent, ptr) + krb5_key_salt_tuple *ksent; + krb5_pointer ptr; +{ + krb5_context context; + krb5_error_code kret; + struct iterate_args *iargs; + krb5_keyblock key; + krb5_int32 ind; + krb5_data pwd; + + iargs = (struct iterate_args *) ptr; + kret = 0; + + context = iargs->ctx; + + /* + * Convert the master key password into a key for this particular + * encryption system. + */ + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + kret = krb5_c_random_seed(context, &pwd); + if (kret) + return kret; + + if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { + ind = iargs->dbentp->n_key_data-1; + if (!(kret = krb5_c_make_random_key(context, ksent->ks_enctype, + &key))) { + kret = krb5_dbe_encrypt_key_data(context, iargs->rblock->key, + &key, NULL, 1, + &iargs->dbentp->key_data[ind]); + krb5_free_keyblock_contents(context, &key); + } + } + + return(kret); +} + +static krb5_error_code +add_principal(context, princ, op, pblock) + krb5_context context; + krb5_principal princ; + enum ap_op op; + struct realm_info *pblock; +{ + krb5_error_code retval; + krb5_db_entry *entry; + krb5_kvno mkey_kvno; + krb5_timestamp now; + struct iterate_args iargs; + krb5_actkvno_node actkvno; + + entry = calloc(1, sizeof(*entry)); + if (entry == NULL) + return ENOMEM; + + entry->len = KRB5_KDB_V1_BASE_LENGTH; + entry->attributes = pblock->flags; + entry->max_life = pblock->max_life; + entry->max_renewable_life = pblock->max_rlife; + entry->expiration = pblock->expiration; + + if ((retval = krb5_copy_principal(context, princ, &entry->princ))) + goto error_out; + + if ((retval = krb5_timeofday(context, &now))) + goto error_out; + + if ((retval = krb5_dbe_update_mod_princ_data(context, entry, + now, &db_create_princ))) + goto error_out; + + switch (op) { + case MASTER_KEY: + if ((entry->key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) + == NULL) + goto error_out; + memset(entry->key_data, 0, sizeof(krb5_key_data)); + entry->n_key_data = 1; + + if (global_params.mask & KADM5_CONFIG_KVNO) + mkey_kvno = global_params.kvno; /* user specified */ + else + mkey_kvno = 1; /* Default */ + entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX; + if ((retval = krb5_dbe_encrypt_key_data(context, pblock->key, + &master_keyblock, NULL, + mkey_kvno, entry->key_data))) + return retval; + /* + * There should always be at least one "active" mkey so creating the + * KRB5_TL_ACTKVNO entry now so the initial mkey is active. + */ + actkvno.next = NULL; + actkvno.act_kvno = mkey_kvno; + /* earliest possible time in case system clock is set back */ + actkvno.act_time = 0; + if ((retval = krb5_dbe_update_actkvno(context, entry, &actkvno))) + return retval; + + /* so getprinc shows the right kvno */ + if ((retval = krb5_dbe_update_mkvno(context, entry, mkey_kvno))) + return retval; + + break; + case TGT_KEY: + iargs.ctx = context; + iargs.rblock = pblock; + iargs.dbentp = entry; + /* + * Iterate through the key/salt list, ignoring salt types. + */ + if ((retval = krb5_keysalt_iterate(pblock->kslist, + pblock->nkslist, + 1, + tgt_keysalt_iterate, + (krb5_pointer) &iargs))) + return retval; + break; + case NULL_KEY: + return EOPNOTSUPP; + default: + break; + } + + entry->mask = (KADM5_KEY_DATA | KADM5_PRINCIPAL | KADM5_ATTRIBUTES | + KADM5_MAX_LIFE | KADM5_MAX_RLIFE | KADM5_TL_DATA | + KADM5_PRINC_EXPIRE_TIME); + entry->attributes |= KRB5_KDB_LOCKDOWN_KEYS; + + retval = krb5_db_put_principal(context, entry); + +error_out: + krb5_db_free_principal(context, entry); + return retval; +} diff --git a/src/kadmin/dbutil/kdb5_destroy.c b/src/kadmin/dbutil/kdb5_destroy.c new file mode 100644 index 000000000000..fffce7429674 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_destroy.c @@ -0,0 +1,94 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/dbutil/kdb5_destroy.c - Destroy a KDC database */ +/* + * Copyright 1990, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include <stdio.h> +#include "com_err.h" +#include <kadm5/admin.h> +#include <kdb.h> +#include "kdb5_util.h" + +extern int exit_status; +extern krb5_boolean dbactive; +extern kadm5_config_params global_params; + +char *yes = "yes\n"; /* \n to compare against result of + fgets */ + +void +kdb5_destroy(argc, argv) + int argc; + char *argv[]; +{ + extern int optind; + int optchar; + char *dbname; + char buf[5]; + krb5_error_code retval1; + int force = 0; + + dbname = global_params.dbname; + + optind = 1; + while ((optchar = getopt(argc, argv, "f")) != -1) { + switch(optchar) { + case 'f': + force++; + break; + case '?': + default: + usage(); + return; + /*NOTREACHED*/ + } + } + if (!force) { + printf(_("Deleting KDC database stored in '%s', are you sure?\n"), + dbname); + printf(_("(type 'yes' to confirm)? ")); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + exit_status++; return; + } + if (strcmp(buf, yes)) { + exit_status++; return; + } + printf(_("OK, deleting database '%s'...\n"), dbname); + } + + retval1 = krb5_db_destroy(util_context, db5util_db_args); + if (retval1) { + com_err(progname, retval1, _("deleting database '%s'"), dbname); + exit_status++; return; + } + + if (global_params.iprop_enabled) { + (void) unlink(global_params.iprop_logfile); + } + + dbactive = FALSE; + printf(_("** Database '%s' destroyed.\n"), dbname); + return; +} diff --git a/src/kadmin/dbutil/kdb5_mkey.c b/src/kadmin/dbutil/kdb5_mkey.c new file mode 100644 index 000000000000..7df8cbc83f21 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_mkey.c @@ -0,0 +1,1333 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <k5-int.h> +#include <kdb.h> +#include <kadm5/server_internal.h> +#include <kadm5/admin.h> +#include <adm_proto.h> +#include "kdb5_util.h" +#include <time.h> + +#if defined(HAVE_COMPILE) && defined(HAVE_STEP) +#define SOLARIS_REGEXPS +#elif defined(HAVE_REGCOMP) && defined(HAVE_REGEXEC) +#define POSIX_REGEXPS +#elif defined(HAVE_RE_COMP) && defined(HAVE_RE_EXEC) +#define BSD_REGEXPS +#else +#error I cannot find any regexp functions +#endif +#ifdef SOLARIS_REGEXPS +#include <regexpr.h> +#endif +#ifdef POSIX_REGEXPS +#include <regex.h> +#endif + +extern krb5_keyblock master_keyblock; /* current mkey */ +extern krb5_kvno master_kvno; +extern krb5_principal master_princ; +extern krb5_data master_salt; +extern char *mkey_fullname; +extern char *mkey_password; +extern char *progname; +extern int exit_status; +extern kadm5_config_params global_params; +extern krb5_context util_context; +extern time_t get_date(char *); + +static char *strdate(krb5_timestamp when) +{ + struct tm *tm; + static char out[40]; + + time_t lcltim = when; + tm = localtime(&lcltim); + strftime(out, sizeof(out), "%a %b %d %H:%M:%S %Z %Y", tm); + return out; +} + +krb5_kvno +get_next_kvno(krb5_context context, krb5_db_entry *entry) +{ + krb5_kvno new_kvno; + + new_kvno = krb5_db_get_key_data_kvno(context, entry->n_key_data, + entry->key_data); + new_kvno++; + /* deal with wrapping */ + if (new_kvno == 0) + new_kvno = 1; /* knvo must not be 0 as this is special value (IGNORE_VNO) */ + + return (new_kvno); +} + +krb5_error_code +add_new_mkey(krb5_context context, krb5_db_entry *master_entry, + krb5_keyblock *new_mkey, krb5_kvno use_mkvno) +{ + krb5_error_code retval = 0; + int old_key_data_count, i; + krb5_kvno new_mkey_kvno; + krb5_key_data tmp_key_data; + krb5_mkey_aux_node *mkey_aux_data_head = NULL, **mkey_aux_data; + krb5_keylist_node *keylist_node; + krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(context); + + /* do this before modifying master_entry key_data */ + new_mkey_kvno = get_next_kvno(context, master_entry); + /* verify the requested mkvno if not 0 is the one that would be used here. */ + if (use_mkvno != 0 && new_mkey_kvno != use_mkvno) + return (KRB5_KDB_KVNONOMATCH); + + old_key_data_count = master_entry->n_key_data; + + /* alloc enough space to hold new and existing key_data */ + /* + * The encrypted key is malloc'ed by krb5_dbe_encrypt_key_data and + * krb5_key_data key_data_contents is a pointer to this key. Using some + * logic from master_key_convert(). + */ + for (i = 0; i < master_entry->n_key_data; i++) + krb5_free_key_data_contents(context, &master_entry->key_data[i]); + free(master_entry->key_data); + master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * + (old_key_data_count + 1)); + if (master_entry->key_data == NULL) + return (ENOMEM); + + memset(master_entry->key_data, 0, + sizeof(krb5_key_data) * (old_key_data_count + 1)); + master_entry->n_key_data = old_key_data_count + 1; + + /* Note, mkey does not have salt */ + /* add new mkey encrypted with itself to mkey princ entry */ + if ((retval = krb5_dbe_encrypt_key_data(context, new_mkey, new_mkey, NULL, + (int) new_mkey_kvno, + &master_entry->key_data[0]))) { + return (retval); + } + /* the mvkno should be that of the newest mkey */ + if ((retval = krb5_dbe_update_mkvno(context, master_entry, new_mkey_kvno))) { + krb5_free_key_data_contents(context, &master_entry->key_data[0]); + return (retval); + } + /* + * Need to decrypt old keys with the current mkey which is in the global + * master_keyblock and encrypt those keys with the latest mkey. And while + * the old keys are being decrypted, use those to create the + * KRB5_TL_MKEY_AUX entries which store the latest mkey encrypted by one of + * the older mkeys. + * + * The new mkey is followed by existing keys. + * + * First, set up for creating a krb5_mkey_aux_node list which will be used + * to update the mkey aux data for the mkey princ entry. + */ + mkey_aux_data_head = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node)); + if (mkey_aux_data_head == NULL) { + retval = ENOMEM; + goto clean_n_exit; + } + memset(mkey_aux_data_head, 0, sizeof(krb5_mkey_aux_node)); + mkey_aux_data = &mkey_aux_data_head; + + for (keylist_node = master_keylist, i = 1; keylist_node != NULL; + keylist_node = keylist_node->next, i++) { + + /* + * Create a list of krb5_mkey_aux_node nodes. One node contains the new + * mkey encrypted by an old mkey and the old mkey's kvno (one node per + * old mkey). + */ + if (*mkey_aux_data == NULL) { + /* *mkey_aux_data points to next field of previous node */ + *mkey_aux_data = (krb5_mkey_aux_node *) malloc(sizeof(krb5_mkey_aux_node)); + if (*mkey_aux_data == NULL) { + retval = ENOMEM; + goto clean_n_exit; + } + memset(*mkey_aux_data, 0, sizeof(krb5_mkey_aux_node)); + } + + memset(&tmp_key_data, 0, sizeof(tmp_key_data)); + /* encrypt the new mkey with the older mkey */ + retval = krb5_dbe_encrypt_key_data(context, &keylist_node->keyblock, + new_mkey, NULL, (int) new_mkey_kvno, + &tmp_key_data); + if (retval) + goto clean_n_exit; + + (*mkey_aux_data)->latest_mkey = tmp_key_data; + (*mkey_aux_data)->mkey_kvno = keylist_node->kvno; + mkey_aux_data = &((*mkey_aux_data)->next); + + /* + * Store old key in master_entry keydata past the new mkey + */ + retval = krb5_dbe_encrypt_key_data(context, new_mkey, + &keylist_node->keyblock, + NULL, (int) keylist_node->kvno, + &master_entry->key_data[i]); + if (retval) + goto clean_n_exit; + } + assert(i == old_key_data_count + 1); + + if ((retval = krb5_dbe_update_mkey_aux(context, master_entry, + mkey_aux_data_head))) { + goto clean_n_exit; + } + master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA; + +clean_n_exit: + krb5_dbe_free_mkey_aux_list(context, mkey_aux_data_head); + return (retval); +} + +void +kdb5_add_mkey(int argc, char *argv[]) +{ + int optchar; + krb5_error_code retval; + char *pw_str = 0; + unsigned int pw_size = 0; + int do_stash = 0; + krb5_data pwd; + krb5_kvno new_mkey_kvno; + krb5_keyblock new_mkeyblock; + krb5_enctype new_master_enctype = ENCTYPE_UNKNOWN; + char *new_mkey_password; + krb5_db_entry *master_entry = NULL; + krb5_timestamp now; + + /* + * The command table entry for this command causes open_db_and_mkey() to be + * called first to open the KDB and get the current mkey. + */ + + memset(&new_mkeyblock, 0, sizeof(new_mkeyblock)); + master_salt.data = NULL; + + while ((optchar = getopt(argc, argv, "e:s")) != -1) { + switch(optchar) { + case 'e': + if (krb5_string_to_enctype(optarg, &new_master_enctype)) { + com_err(progname, EINVAL, _("%s is an invalid enctype"), + optarg); + exit_status++; + return; + } + break; + case 's': + do_stash++; + break; + case '?': + default: + usage(); + return; + } + } + + if (new_master_enctype == ENCTYPE_UNKNOWN) + new_master_enctype = global_params.enctype; + + retval = krb5_db_get_principal(util_context, master_princ, 0, + &master_entry); + if (retval != 0) { + com_err(progname, retval, _("while getting master key principal %s"), + mkey_fullname); + exit_status++; + goto cleanup_return; + } + + printf(_("Creating new master key for master key principal '%s'\n"), + mkey_fullname); + + printf(_("You will be prompted for a new database Master Password.\n")); + printf(_("It is important that you NOT FORGET this password.\n")); + fflush(stdout); + + pw_size = 1024; + pw_str = malloc(pw_size); + if (pw_str == NULL) { + com_err(progname, ENOMEM, _("while creating new master key")); + exit_status++; + goto cleanup_return; + } + + retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, + pw_str, &pw_size); + if (retval) { + com_err(progname, retval, + _("while reading new master key from keyboard")); + exit_status++; + goto cleanup_return; + } + new_mkey_password = pw_str; + + pwd.data = new_mkey_password; + pwd.length = strlen(new_mkey_password); + retval = krb5_principal2salt(util_context, master_princ, &master_salt); + if (retval) { + com_err(progname, retval, _("while calculating master key salt")); + exit_status++; + goto cleanup_return; + } + + retval = krb5_c_string_to_key(util_context, new_master_enctype, + &pwd, &master_salt, &new_mkeyblock); + if (retval) { + com_err(progname, retval, + _("while transforming master key from password")); + exit_status++; + goto cleanup_return; + } + + new_mkey_kvno = get_next_kvno(util_context, master_entry); + retval = add_new_mkey(util_context, master_entry, &new_mkeyblock, + new_mkey_kvno); + if (retval) { + com_err(progname, retval, + _("adding new master key to master principal")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_timeofday(util_context, &now))) { + com_err(progname, retval, _("while getting current time")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry, + now, master_princ))) { + com_err(progname, retval, _("while updating the master key principal " + "modification time")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_db_put_principal(util_context, master_entry))) { + com_err(progname, retval, _("while adding master key entry to the " + "database")); + exit_status++; + goto cleanup_return; + } + + if (do_stash) { + retval = krb5_db_store_master_key(util_context, + global_params.stash_file, + master_princ, + new_mkey_kvno, + &new_mkeyblock, + mkey_password); + if (retval) { + com_err(progname, retval, _("while storing key")); + printf(_("Warning: couldn't stash master key.\n")); + } + } + +cleanup_return: + /* clean up */ + krb5_db_free_principal(util_context, master_entry); + zap((char *)new_mkeyblock.contents, new_mkeyblock.length); + free(new_mkeyblock.contents); + if (pw_str) { + zap(pw_str, pw_size); + free(pw_str); + } + free(master_salt.data); + return; +} + +void +kdb5_use_mkey(int argc, char *argv[]) +{ + krb5_error_code retval; + krb5_kvno use_kvno; + krb5_timestamp now, start_time; + krb5_actkvno_node *actkvno_list = NULL, *new_actkvno = NULL, + *prev_actkvno, *cur_actkvno; + krb5_db_entry *master_entry = NULL; + krb5_keylist_node *keylist_node; + krb5_boolean inserted = FALSE; + krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context); + + if (argc < 2 || argc > 3) { + /* usage calls exit */ + usage(); + } + + use_kvno = atoi(argv[1]); + if (use_kvno == 0) { + com_err(progname, EINVAL, _("0 is an invalid KVNO value")); + exit_status++; + return; + } else { + /* verify use_kvno is valid */ + for (keylist_node = master_keylist; keylist_node != NULL; + keylist_node = keylist_node->next) { + if (use_kvno == keylist_node->kvno) + break; + } + if (!keylist_node) { + com_err(progname, EINVAL, _("%d is an invalid KVNO value"), + use_kvno); + exit_status++; + return; + } + } + + if ((retval = krb5_timeofday(util_context, &now))) { + com_err(progname, retval, _("while getting current time")); + exit_status++; + return; + } + + if (argc == 3) { + time_t t = get_date(argv[2]); + if (t == -1) { + com_err(progname, 0, _("could not parse date-time string '%s'"), + argv[2]); + exit_status++; + return; + } else + start_time = (krb5_timestamp) t; + } else { + start_time = now; + } + + /* + * Need to: + * + * 1. get mkey princ + * 2. get krb5_actkvno_node list + * 3. add use_kvno to actkvno list (sorted in right spot) + * 4. update mkey princ's tl data + * 5. put mkey princ. + */ + + retval = krb5_db_get_principal(util_context, master_princ, 0, + &master_entry); + if (retval != 0) { + com_err(progname, retval, _("while getting master key principal %s"), + mkey_fullname); + exit_status++; + goto cleanup_return; + } + + retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); + if (retval != 0) { + com_err(progname, retval, + _("while looking up active version of master key")); + exit_status++; + goto cleanup_return; + } + + /* + * If an entry already exists with the same kvno either delete it or if it's + * the only entry, just set its active time. + */ + for (prev_actkvno = NULL, cur_actkvno = actkvno_list; + cur_actkvno != NULL; + prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) { + + if (cur_actkvno->act_kvno == use_kvno) { + /* delete it */ + if (prev_actkvno) { + prev_actkvno->next = cur_actkvno->next; + cur_actkvno->next = NULL; + krb5_dbe_free_actkvno_list(util_context, cur_actkvno); + } else { + if (cur_actkvno->next) { + /* delete it from front of list */ + actkvno_list = cur_actkvno->next; + cur_actkvno->next = NULL; + krb5_dbe_free_actkvno_list(util_context, cur_actkvno); + } else { + /* There's only one entry, go ahead and change the time */ + cur_actkvno->act_time = start_time; + inserted = TRUE; + } + } + break; + } + } + + if (!inserted) { + /* alloc enough space to hold new and existing key_data */ + new_actkvno = (krb5_actkvno_node *) malloc(sizeof(krb5_actkvno_node)); + if (new_actkvno == NULL) { + com_err(progname, ENOMEM, _("while adding new master key")); + exit_status++; + goto cleanup_return; + } + memset(new_actkvno, 0, sizeof(krb5_actkvno_node)); + new_actkvno->act_kvno = use_kvno; + new_actkvno->act_time = start_time; + + /* insert new act kvno node */ + + if (actkvno_list == NULL) { + /* new actkvno is the list */ + actkvno_list = new_actkvno; + } else { + for (prev_actkvno = NULL, cur_actkvno = actkvno_list; + cur_actkvno != NULL; + prev_actkvno = cur_actkvno, cur_actkvno = cur_actkvno->next) { + + if (new_actkvno->act_time < cur_actkvno->act_time) { + if (prev_actkvno) { + prev_actkvno->next = new_actkvno; + new_actkvno->next = cur_actkvno; + } else { + new_actkvno->next = actkvno_list; + actkvno_list = new_actkvno; + } + break; + } else if (cur_actkvno->next == NULL) { + /* end of line, just add new node to end of list */ + cur_actkvno->next = new_actkvno; + break; + } + } + } + } + + if (actkvno_list->act_time > now) { + com_err(progname, EINVAL, + _("there must be one master key currently active")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_dbe_update_actkvno(util_context, master_entry, + actkvno_list))) { + com_err(progname, retval, + _("while updating actkvno data for master principal entry")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry, + now, master_princ))) { + com_err(progname, retval, _("while updating the master key principal " + "modification time")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_db_put_principal(util_context, master_entry))) { + com_err(progname, retval, + _("while adding master key entry to the database")); + exit_status++; + goto cleanup_return; + } + +cleanup_return: + /* clean up */ + krb5_db_free_principal(util_context, master_entry); + krb5_dbe_free_actkvno_list(util_context, actkvno_list); + return; +} + +void +kdb5_list_mkeys(int argc, char *argv[]) +{ + krb5_error_code retval; + char *output_str = NULL, enctype[BUFSIZ]; + krb5_kvno act_kvno; + krb5_timestamp act_time; + krb5_actkvno_node *actkvno_list = NULL, *cur_actkvno; + krb5_db_entry *master_entry = NULL; + krb5_keylist_node *cur_kb_node; + krb5_keyblock *act_mkey; + krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context); + + if (master_keylist == NULL) { + com_err(progname, 0, _("master keylist not initialized")); + exit_status++; + return; + } + + retval = krb5_db_get_principal(util_context, master_princ, 0, + &master_entry); + if (retval != 0) { + com_err(progname, retval, _("while getting master key principal %s"), + mkey_fullname); + exit_status++; + goto cleanup_return; + } + + retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); + if (retval != 0) { + com_err(progname, retval, _("while looking up active kvno list")); + exit_status++; + goto cleanup_return; + } + + retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &act_kvno, + &act_mkey); + if (retval != 0) { + com_err(progname, retval, _("while looking up active master key")); + exit_status++; + goto cleanup_return; + } + + printf("Master keys for Principal: %s\n", mkey_fullname); + + for (cur_kb_node = master_keylist; cur_kb_node != NULL; + cur_kb_node = cur_kb_node->next) { + + if ((retval = krb5_enctype_to_name(cur_kb_node->keyblock.enctype, + FALSE, enctype, sizeof(enctype)))) { + com_err(progname, retval, _("while getting enctype description")); + exit_status++; + goto cleanup_return; + } + + act_time = -1; /* assume actkvno entry not found */ + for (cur_actkvno = actkvno_list; cur_actkvno != NULL; + cur_actkvno = cur_actkvno->next) { + if (cur_actkvno->act_kvno == cur_kb_node->kvno) { + act_time = cur_actkvno->act_time; + break; + } + } + + if (cur_kb_node->kvno == act_kvno) { + /* * indicates kvno is currently active */ + retval = asprintf(&output_str, + _("KVNO: %d, Enctype: %s, Active on: %s *\n"), + cur_kb_node->kvno, enctype, strdate(act_time)); + } else { + if (act_time != -1) { + retval = asprintf(&output_str, + _("KVNO: %d, Enctype: %s, Active on: %s\n"), + cur_kb_node->kvno, enctype, strdate(act_time)); + } else { + retval = asprintf(&output_str, + _("KVNO: %d, Enctype: %s, No activate time " + "set\n"), cur_kb_node->kvno, enctype); + } + } + if (retval == -1) { + com_err(progname, ENOMEM, _("asprintf could not allocate enough " + "memory to hold output")); + exit_status++; + goto cleanup_return; + } + printf("%s", output_str); + free(output_str); + output_str = NULL; + } + +cleanup_return: + /* clean up */ + krb5_db_free_principal(util_context, master_entry); + free(output_str); + krb5_dbe_free_actkvno_list(util_context, actkvno_list); + return; +} + +struct update_enc_mkvno { + unsigned int re_match_count; + unsigned int already_current; + unsigned int updated; + unsigned int dry_run : 1; + unsigned int verbose : 1; +#ifdef SOLARIS_REGEXPS + char *expbuf; +#endif +#ifdef POSIX_REGEXPS + regex_t preg; +#endif +#if !defined(SOLARIS_REGEXPS) && !defined(POSIX_REGEXPS) + unsigned char placeholder; +#endif +}; + +/* XXX Duplicated in libkadm5srv! */ +/* + * Function: glob_to_regexp + * + * Arguments: + * + * glob (r) the shell-style glob (?*[]) to convert + * realm (r) the default realm to append, or NULL + * regexp (w) the ed-style regexp created from glob + * + * Effects: + * + * regexp is filled in with allocated memory contained a regular + * expression to be used with re_comp/compile that matches what the + * shell-style glob would match. If glob does not contain an "@" + * character and realm is not NULL, "@*" is appended to the regexp. + * + * Conversion algorithm: + * + * quoted characters are copied quoted + * ? is converted to . + * * is converted to .* + * active characters are quoted: ^, $, . + * [ and ] are active but supported and have the same meaning, so + * they are copied + * other characters are copied + * regexp is anchored with ^ and $ + */ +static int glob_to_regexp(char *glob, char *realm, char **regexp) +{ + int append_realm; + char *p; + + /* validate the glob */ + if (glob[strlen(glob)-1] == '\\') + return EINVAL; + + /* A character of glob can turn into two in regexp, plus ^ and $ */ + /* and trailing null. If glob has no @, also allocate space for */ + /* the realm. */ + append_realm = (realm != NULL) && (strchr(glob, '@') == NULL); + p = (char *) malloc(strlen(glob)*2+ 3 + (append_realm ? 3 : 0)); + if (p == NULL) + return ENOMEM; + *regexp = p; + + *p++ = '^'; + while (*glob) { + switch (*glob) { + case '?': + *p++ = '.'; + break; + case '*': + *p++ = '.'; + *p++ = '*'; + break; + case '.': + case '^': + case '$': + *p++ = '\\'; + *p++ = *glob; + break; + case '\\': + *p++ = '\\'; + *p++ = *++glob; + break; + default: + *p++ = *glob; + break; + } + glob++; + } + + if (append_realm) { + *p++ = '@'; + *p++ = '.'; + *p++ = '*'; + } + + *p++ = '$'; + *p++ = '\0'; + return 0; +} + +static int +update_princ_encryption_1(void *cb, krb5_db_entry *ent) +{ + struct update_enc_mkvno *p = cb; + char *pname = 0; + krb5_error_code retval; + int match; + krb5_timestamp now; + int result; + krb5_kvno old_mkvno; + + retval = krb5_unparse_name(util_context, ent->princ, &pname); + if (retval) { + com_err(progname, retval, + _("getting string representation of principal name")); + goto fail; + } + + if (krb5_principal_compare(util_context, ent->princ, master_princ)) { + goto skip; + } + +#ifdef SOLARIS_REGEXPS + match = (step(pname, p->expbuf) != 0); +#endif +#ifdef POSIX_REGEXPS + match = (regexec(&p->preg, pname, 0, NULL, 0) == 0); +#endif +#ifdef BSD_REGEXPS + match = (re_exec(pname) != 0); +#endif + if (!match) { + goto skip; + } + p->re_match_count++; + retval = krb5_dbe_get_mkvno(util_context, ent, &old_mkvno); + if (retval) { + com_err(progname, retval, + _("determining master key used for principal '%s'"), pname); + goto fail; + } + /* Line up "skip" and "update" messages for viewing. */ + if (old_mkvno == new_mkvno) { + if (p->dry_run && p->verbose) + printf(_("would skip: %s\n"), pname); + else if (p->verbose) + printf(_("skipping: %s\n"), pname); + p->already_current++; + goto skip; + } + if (p->dry_run) { + if (p->verbose) + printf(_("would update: %s\n"), pname); + p->updated++; + goto skip; + } else if (p->verbose) + printf(_("updating: %s\n"), pname); + retval = master_key_convert (util_context, ent); + if (retval) { + com_err(progname, retval, + _("error re-encrypting key for principal '%s'"), pname); + goto fail; + } + if ((retval = krb5_timeofday(util_context, &now))) { + com_err(progname, retval, _("while getting current time")); + goto fail; + } + + if ((retval = krb5_dbe_update_mod_princ_data(util_context, ent, + now, master_princ))) { + com_err(progname, retval, + _("while updating principal '%s' modification time"), pname); + goto fail; + } + + ent->mask |= KADM5_KEY_DATA; + + if ((retval = krb5_db_put_principal(util_context, ent))) { + com_err(progname, retval, _("while updating principal '%s' key data " + "in the database"), pname); + goto fail; + } + p->updated++; +skip: + result = 0; + goto egress; +fail: + exit_status++; + result = 1; +egress: + if (pname) + krb5_free_unparsed_name(util_context, pname); + return result; +} + +extern int are_you_sure (const char *, ...) +#if !defined(__cplusplus) && (__GNUC__ > 2) + __attribute__((__format__(__printf__, 1, 2))) +#endif + ; + +int +are_you_sure (const char *format, ...) +{ + va_list va; + char ansbuf[100]; + + va_start(va, format); + vprintf(format, va); + va_end(va); + printf(_("\n(type 'yes' to confirm)? ")); + fflush(stdout); + if (fgets(ansbuf, sizeof(ansbuf), stdin) == NULL) + return 0; + if (strcmp(ansbuf, "yes\n")) + return 0; + return 1; +} + +void +kdb5_update_princ_encryption(int argc, char *argv[]) +{ + struct update_enc_mkvno data = { 0 }; + char *name_pattern = NULL; + int force = 0; + int optchar; + krb5_error_code retval; + krb5_actkvno_node *actkvno_list = 0; + krb5_db_entry *master_entry = NULL; +#ifdef BSD_REGEXPS + char *msg; +#endif + char *regexp = NULL; + krb5_keyblock *act_mkey; + krb5_keylist_node *master_keylist = krb5_db_mkey_list_alias(util_context); + krb5_flags iterflags = 0; + + while ((optchar = getopt(argc, argv, "fnv")) != -1) { + switch (optchar) { + case 'f': + force = 1; + break; + case 'n': + data.dry_run = 1; + break; + case 'v': + data.verbose = 1; + break; + case '?': + case ':': + default: + usage(); + } + } + if (argv[optind] != NULL) { + name_pattern = argv[optind]; + if (argv[optind+1] != NULL) + usage(); + } + + if (master_keylist == NULL) { + com_err(progname, 0, _("master keylist not initialized")); + exit_status++; + goto cleanup; + } + + /* The glob_to_regexp code only cares if the "realm" parameter is + NULL or not; the string data is irrelevant. */ + if (name_pattern == NULL) + name_pattern = "*"; + if (glob_to_regexp(name_pattern, "hi", ®exp) != 0) { + com_err(progname, ENOMEM, + _("converting glob pattern '%s' to regular expression"), + name_pattern); + exit_status++; + goto cleanup; + } + + if ( +#ifdef SOLARIS_REGEXPS + ((data.expbuf = compile(regexp, NULL, NULL)) == NULL) +#endif +#ifdef POSIX_REGEXPS + ((regcomp(&data.preg, regexp, REG_NOSUB)) != 0) +#endif +#ifdef BSD_REGEXPS + ((msg = (char *) re_comp(regexp)) != NULL) +#endif + ) { + /* XXX syslog msg or regerr(regerrno) */ + com_err(progname, 0, _("error compiling converted regexp '%s'"), + regexp); + exit_status++; + goto cleanup; + } + + retval = krb5_db_get_principal(util_context, master_princ, 0, + &master_entry); + if (retval != 0) { + com_err(progname, retval, _("while getting master key principal %s"), + mkey_fullname); + exit_status++; + goto cleanup; + } + + retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); + if (retval != 0) { + com_err(progname, retval, _("while looking up active kvno list")); + exit_status++; + goto cleanup; + } + + retval = krb5_dbe_find_act_mkey(util_context, actkvno_list, &new_mkvno, + &act_mkey); + if (retval) { + com_err(progname, retval, _("while looking up active master key")); + exit_status++; + goto cleanup; + } + new_master_keyblock = *act_mkey; + + if (!force && + !data.dry_run && + !are_you_sure(_("Re-encrypt all keys not using master key vno %u?"), + new_mkvno)) { + printf(_("OK, doing nothing.\n")); + exit_status++; + goto cleanup; + } + if (data.verbose) { + if (data.dry_run) { + printf(_("Principals whose keys WOULD BE re-encrypted to master " + "key vno %u:\n"), new_mkvno); + } else { + printf(_("Principals whose keys are being re-encrypted to master " + "key vno %u if necessary:\n"), new_mkvno); + } + } + + if (!data.dry_run) { + /* Grab a write lock so we don't have to upgrade to a write lock and + * reopen the DB while iterating. */ + iterflags = KRB5_DB_ITER_WRITE; + } + + retval = krb5_db_iterate(util_context, name_pattern, + update_princ_encryption_1, &data, iterflags); + /* If exit_status is set, then update_princ_encryption_1 already + printed a message. */ + if (retval != 0 && exit_status == 0) { + com_err(progname, retval, _("trying to process principal database")); + exit_status++; + } + if (data.dry_run) { + printf(_("%u principals processed: %u would be updated, %u already " + "current\n"), + data.re_match_count, data.updated, data.already_current); + } else { + printf(_("%u principals processed: %u updated, %u already current\n"), + data.re_match_count, data.updated, data.already_current); + } + +cleanup: + krb5_db_free_principal(util_context, master_entry); + free(regexp); +#ifdef POSIX_REGEXPS + regfree(&data.preg); +#endif + memset(&new_master_keyblock, 0, sizeof(new_master_keyblock)); + krb5_dbe_free_actkvno_list(util_context, actkvno_list); +} + +struct kvnos_in_use { + krb5_kvno kvno; + unsigned int use_count; +}; + +struct purge_args { + krb5_context kcontext; + struct kvnos_in_use *kvnos; + unsigned int num_kvnos; +}; + +static krb5_error_code +find_mkvnos_in_use(krb5_pointer ptr, + krb5_db_entry *entry) +{ + krb5_error_code retval; + struct purge_args * args; + unsigned int i; + krb5_kvno mkvno; + + args = (struct purge_args *) ptr; + + retval = krb5_dbe_get_mkvno(args->kcontext, entry, &mkvno); + if (retval) + return (retval); + + for (i = 0; i < args->num_kvnos; i++) { + if (args->kvnos[i].kvno == mkvno) { + /* XXX do I need to worry about use_count wrapping? */ + args->kvnos[i].use_count++; + break; + } + } + return 0; +} + +void +kdb5_purge_mkeys(int argc, char *argv[]) +{ + int optchar; + krb5_error_code retval; + krb5_timestamp now; + krb5_db_entry *master_entry = NULL; + krb5_boolean force = FALSE, dry_run = FALSE, verbose = FALSE; + struct purge_args args; + char buf[5]; + unsigned int i, j, k, num_kvnos_inuse, num_kvnos_purged; + unsigned int old_key_data_count; + krb5_actkvno_node *actkvno_list = NULL, *actkvno_entry, *prev_actkvno_entry; + krb5_mkey_aux_node *mkey_aux_list = NULL, *mkey_aux_entry, *prev_mkey_aux_entry; + krb5_key_data *old_key_data; + + /* + * Verify that the master key list has been initialized before doing + * anything else. + */ + if (krb5_db_mkey_list_alias(util_context) == NULL) { + com_err(progname, KRB5_KDB_DBNOTINITED, + _("master keylist not initialized")); + exit_status++; + return; + } + + memset(&args, 0, sizeof(args)); + + optind = 1; + while ((optchar = getopt(argc, argv, "fnv")) != -1) { + switch(optchar) { + case 'f': + force = TRUE; + break; + case 'n': + dry_run = TRUE; /* mkey princ will not be modified */ + force = TRUE; /* implied */ + break; + case 'v': + verbose = TRUE; + break; + case '?': + default: + usage(); + return; + } + } + + retval = krb5_db_get_principal(util_context, master_princ, 0, + &master_entry); + if (retval != 0) { + com_err(progname, retval, _("while getting master key principal %s"), + mkey_fullname); + exit_status++; + goto cleanup_return; + } + + if (!force) { + printf(_("Will purge all unused master keys stored in the '%s' " + "principal, are you sure?\n"), mkey_fullname); + printf(_("(type 'yes' to confirm)? ")); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + exit_status++; + goto cleanup_return; + } + if (strcmp(buf, "yes\n")) { + exit_status++; + goto cleanup_return; + } + printf(_("OK, purging unused master keys from '%s'...\n"), + mkey_fullname); + } + + /* save the old keydata */ + old_key_data_count = master_entry->n_key_data; + if (old_key_data_count == 1) { + if (verbose) + printf(_("There is only one master key which can not be " + "purged.\n")); + goto cleanup_return; + } + old_key_data = master_entry->key_data; + + args.kvnos = (struct kvnos_in_use *) malloc(sizeof(struct kvnos_in_use) * old_key_data_count); + if (args.kvnos == NULL) { + retval = ENOMEM; + com_err(progname, ENOMEM, _("while allocating args.kvnos")); + exit_status++; + goto cleanup_return; + } + memset(args.kvnos, 0, sizeof(struct kvnos_in_use) * old_key_data_count); + args.num_kvnos = old_key_data_count; + args.kcontext = util_context; + + /* populate the kvnos array with all the current mkvnos */ + for (i = 0; i < old_key_data_count; i++) + args.kvnos[i].kvno = master_entry->key_data[i].key_data_kvno; + + if ((retval = krb5_db_iterate(util_context, + NULL, + find_mkvnos_in_use, + (krb5_pointer) &args, 0))) { + com_err(progname, retval, _("while finding master keys in use")); + exit_status++; + goto cleanup_return; + } + /* + * args.kvnos has been marked with the mkvno's that are currently protecting + * princ entries + */ + if (dry_run) { + printf(_("Would purge the following master key(s) from %s:\n"), + mkey_fullname); + } else { + printf(_("Purging the following master key(s) from %s:\n"), + mkey_fullname); + } + + /* find # of keys still in use or print out verbose info */ + for (i = num_kvnos_inuse = num_kvnos_purged = 0; i < args.num_kvnos; i++) { + if (args.kvnos[i].use_count > 0) { + num_kvnos_inuse++; + } else { + /* this key would be deleted */ + if (args.kvnos[i].kvno == master_kvno) { + com_err(progname, KRB5_KDB_STORED_MKEY_NOTCURRENT, + _("master key stash file needs updating, command " + "aborting")); + exit_status++; + goto cleanup_return; + } + num_kvnos_purged++; + printf(_("KVNO: %d\n"), args.kvnos[i].kvno); + } + } + /* didn't find any keys to purge */ + if (num_kvnos_inuse == args.num_kvnos) { + printf(_("All keys in use, nothing purged.\n")); + goto cleanup_return; + } + if (dry_run) { + /* bail before doing anything else */ + printf(_("%d key(s) would be purged.\n"), num_kvnos_purged); + goto cleanup_return; + } + + retval = krb5_dbe_lookup_actkvno(util_context, master_entry, &actkvno_list); + if (retval != 0) { + com_err(progname, retval, _("while looking up active kvno list")); + exit_status++; + goto cleanup_return; + } + + retval = krb5_dbe_lookup_mkey_aux(util_context, master_entry, &mkey_aux_list); + if (retval != 0) { + com_err(progname, retval, _("while looking up mkey aux data list")); + exit_status++; + goto cleanup_return; + } + + master_entry->key_data = (krb5_key_data *) malloc(sizeof(krb5_key_data) * num_kvnos_inuse); + if (master_entry->key_data == NULL) { + retval = ENOMEM; + com_err(progname, ENOMEM, _("while allocating key_data")); + exit_status++; + goto cleanup_return; + } + memset(master_entry->key_data, 0, sizeof(krb5_key_data) * num_kvnos_inuse); + master_entry->n_key_data = num_kvnos_inuse; /* there's only 1 mkey per kvno */ + + /* + * Assuming that the latest mkey will not be purged because it will always + * be "in use" so this code will not bother with encrypting keys again. + */ + for (i = k = 0; i < old_key_data_count; i++) { + for (j = 0; j < args.num_kvnos; j++) { + if (args.kvnos[j].kvno == (krb5_kvno) old_key_data[i].key_data_kvno) { + if (args.kvnos[j].use_count != 0) { + master_entry->key_data[k++] = old_key_data[i]; + memset(&old_key_data[i], 0, sizeof(old_key_data[i])); + break; + } else { + /* remove unused mkey */ + /* adjust the actkno data */ + for (prev_actkvno_entry = actkvno_entry = actkvno_list; + actkvno_entry != NULL; + actkvno_entry = actkvno_entry->next) { + + if (actkvno_entry->act_kvno == args.kvnos[j].kvno) { + if (actkvno_entry == actkvno_list) { + /* remove from head */ + actkvno_list = actkvno_entry->next; + prev_actkvno_entry = actkvno_list; + } else if (actkvno_entry->next == NULL) { + /* remove from tail */ + prev_actkvno_entry->next = NULL; + } else { + /* remove in between */ + prev_actkvno_entry->next = actkvno_entry->next; + } + actkvno_entry->next = NULL; + krb5_dbe_free_actkvno_list(util_context, actkvno_entry); + break; /* deleted entry, no need to loop further */ + } else { + prev_actkvno_entry = actkvno_entry; + } + } + /* adjust the mkey aux data */ + for (prev_mkey_aux_entry = mkey_aux_entry = mkey_aux_list; + mkey_aux_entry != NULL; + mkey_aux_entry = mkey_aux_entry->next) { + + if (mkey_aux_entry->mkey_kvno == args.kvnos[j].kvno) { + if (mkey_aux_entry == mkey_aux_list) { + mkey_aux_list = mkey_aux_entry->next; + prev_mkey_aux_entry = mkey_aux_list; + } else if (mkey_aux_entry->next == NULL) { + prev_mkey_aux_entry->next = NULL; + } else { + prev_mkey_aux_entry->next = mkey_aux_entry->next; + } + mkey_aux_entry->next = NULL; + krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_entry); + break; /* deleted entry, no need to loop further */ + } else { + prev_mkey_aux_entry = mkey_aux_entry; + } + } + } + } + } + } + assert(k == num_kvnos_inuse); + + /* Free any key data entries we did not consume in the loop above. */ + for (i = 0; i < old_key_data_count; i++) + krb5_dbe_free_key_data_contents(util_context, &old_key_data[i]); + free(old_key_data); + + if ((retval = krb5_dbe_update_actkvno(util_context, master_entry, + actkvno_list))) { + com_err(progname, retval, + _("while updating actkvno data for master principal entry")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_dbe_update_mkey_aux(util_context, master_entry, + mkey_aux_list))) { + com_err(progname, retval, + _("while updating mkey_aux data for master principal entry")); + exit_status++; + return; + } + + if ((retval = krb5_timeofday(util_context, &now))) { + com_err(progname, retval, _("while getting current time")); + exit_status++; + goto cleanup_return; + } + + if ((retval = krb5_dbe_update_mod_princ_data(util_context, master_entry, + now, master_princ))) { + com_err(progname, retval, _("while updating the master key principal " + "modification time")); + exit_status++; + goto cleanup_return; + } + + master_entry->mask |= KADM5_KEY_DATA | KADM5_TL_DATA; + + if ((retval = krb5_db_put_principal(util_context, master_entry))) { + com_err(progname, retval, + _("while adding master key entry to the database")); + exit_status++; + goto cleanup_return; + } + printf(_("%d key(s) purged.\n"), num_kvnos_purged); + +cleanup_return: + krb5_db_free_principal(util_context, master_entry); + free(args.kvnos); + krb5_dbe_free_actkvno_list(util_context, actkvno_list); + krb5_dbe_free_mkey_aux_list(util_context, mkey_aux_list); + return; +} diff --git a/src/kadmin/dbutil/kdb5_stash.c b/src/kadmin/dbutil/kdb5_stash.c new file mode 100644 index 000000000000..e05944f29005 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_stash.c @@ -0,0 +1,139 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/dbutil/kdb5_stash.c - Store the master database key in a file */ +/* + * Copyright 1990 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include "k5-int.h" +#include "com_err.h" +#include <kadm5/admin.h> +#include <stdio.h> +#include "kdb5_util.h" + +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern kadm5_config_params global_params; + +extern int exit_status; +extern int close_policy_db; + +void +kdb5_stash(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int optchar; + krb5_error_code retval; + char *keyfile = 0; + krb5_kvno mkey_kvno; + + keyfile = global_params.stash_file; + + optind = 1; + while ((optchar = getopt(argc, argv, "f:")) != -1) { + switch(optchar) { + case 'f': + keyfile = optarg; + break; + case '?': + default: + usage(); + return; + } + } + + if (!krb5_c_valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_name(master_keyblock.enctype, FALSE, + tmp, sizeof(tmp))) + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, + _("while setting up enctype %d"), master_keyblock.enctype); + else + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, "%s", tmp); + exit_status++; return; + } + + if (global_params.mask & KADM5_CONFIG_KVNO) + mkey_kvno = global_params.kvno; /* user specified */ + else + mkey_kvno = IGNORE_VNO; /* use whatever krb5_db_fetch_mkey finds */ + + if (!valid_master_key) { + /* TRUE here means read the keyboard, but only once */ + retval = krb5_db_fetch_mkey(util_context, master_princ, + master_keyblock.enctype, + TRUE, FALSE, (char *) NULL, + &mkey_kvno, + NULL, &master_keyblock); + if (retval) { + com_err(progname, retval, _("while reading master key")); + exit_status++; return; + } + + retval = krb5_db_fetch_mkey_list(util_context, master_princ, + &master_keyblock); + if (retval) { + com_err(progname, retval, _("while getting master key list")); + exit_status++; return; + } + } else { + printf(_("Using existing stashed keys to update stash file.\n")); + } + + retval = krb5_db_store_master_key_list(util_context, keyfile, master_princ, + NULL); + if (retval) { + com_err(progname, retval, _("while storing key")); + exit_status++; return; + } + + exit_status = 0; + return; +} diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c new file mode 100644 index 000000000000..000b5595c9fa --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.c @@ -0,0 +1,651 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/dbutil/kdb5_util.c - Administer a KDC database */ +/* + * (C) Copyright 1990,1991, 1996, 2008, 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <k5-int.h> +#include <kadm5/admin.h> +#include <locale.h> +#include <adm_proto.h> +#include <time.h> +#include "kdb5_util.h" + +/* + * XXX Ick, ick, ick. These global variables shouldn't be global.... + */ +char *mkey_password = 0; + +/* + * I can't figure out any way for this not to be global, given how ss + * works. + */ + +int exit_status = 0; +krb5_context util_context; +kadm5_config_params global_params; + +void usage() +{ + fprintf(stderr, + _("Usage: kdb5_util [-x db_args]* [-r realm] [-d dbname] " + "[-k mkeytype] [-M mkeyname]\n" + "\t [-kv mkeyVNO] [-sf stashfilename] [-m] cmd " + "[cmd_options]\n" + "\tcreate [-s]\n" + "\tdestroy [-f]\n" + "\tstash [-f keyfile]\n" + "\tdump [-old|-ov|-b6|-b7|-r13|-r18] [-verbose]\n" + "\t [-mkey_convert] [-new_mkey_file mkey_file]\n" + "\t [-rev] [-recurse] [filename [princs...]]\n" + "\tload [-old|-ov|-b6|-b7|-r13|-r18] [-verbose] [-update] " + "filename\n" + "\tark [-e etype_list] principal\n" + "\tadd_mkey [-e etype] [-s]\n" + "\tuse_mkey kvno [time]\n" + "\tlist_mkeys\n")); + /* avoid a string length compiler warning */ + fprintf(stderr, + _("\tupdate_princ_encryption [-f] [-n] [-v] [princ-pattern]\n" + "\tpurge_mkeys [-f] [-n] [-v]\n" + "\ttabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype\n" + "\nwhere,\n\t[-x db_args]* - any number of database specific " + "arguments.\n" + "\t\t\tLook at each database documentation for supported " + "arguments\n")); + exit(1); +} + +krb5_keyblock master_keyblock; +krb5_kvno master_kvno; /* fetched */ +extern krb5_principal master_princ; +char *mkey_fullname; +krb5_db_entry *master_entry = NULL; +int valid_master_key = 0; + +char *progname; +krb5_boolean manual_mkey = FALSE; +krb5_boolean dbactive = FALSE; + +static int open_db_and_mkey(void); + +static void add_random_key(int, char **); + +typedef void (*cmd_func)(int, char **); + +struct _cmd_table { + char *name; + cmd_func func; + int opendb; +} cmd_table[] = { + {"create", kdb5_create, 0}, + {"destroy", kdb5_destroy, 1}, /* 1 opens the kdb */ + {"stash", kdb5_stash, 1}, + {"dump", dump_db, 1}, + {"load", load_db, 0}, + {"ark", add_random_key, 1}, + {"add_mkey", kdb5_add_mkey, 1}, + {"use_mkey", kdb5_use_mkey, 1}, + {"list_mkeys", kdb5_list_mkeys, 1}, + {"update_princ_encryption", kdb5_update_princ_encryption, 1}, + {"purge_mkeys", kdb5_purge_mkeys, 1}, + {"tabdump", tabdump, 1}, + {NULL, NULL, 0}, +}; + +static struct _cmd_table *cmd_lookup(name) + char *name; +{ + struct _cmd_table *cmd = cmd_table; + while (cmd->name) { + if (strcmp(cmd->name, name) == 0) + return cmd; + else + cmd++; + } + + return NULL; +} + +#define ARG_VAL (--argc > 0 ? (koptarg = *(++argv)) : (char *)(usage(), NULL)) + +char **db5util_db_args = NULL; +int db5util_db_args_size = 0; + +static void extended_com_err_fn (const char *myprog, errcode_t code, + const char *fmt, va_list args) +{ + const char *emsg; + if (code) { + emsg = krb5_get_error_message (util_context, code); + fprintf (stderr, "%s: %s ", myprog, emsg); + krb5_free_error_message (util_context, emsg); + } else { + fprintf (stderr, "%s: ", myprog); + } + vfprintf (stderr, fmt, args); + fprintf (stderr, "\n"); +} + +int add_db_arg(char *arg) +{ + char **temp; + db5util_db_args_size++; + temp = realloc(db5util_db_args, + sizeof(char *) * (db5util_db_args_size + 1)); + if (temp == NULL) + return 0; + db5util_db_args = temp; + db5util_db_args[db5util_db_args_size-1] = arg; + db5util_db_args[db5util_db_args_size] = NULL; + return 1; +} + +int main(argc, argv) + int argc; + char *argv[]; +{ + struct _cmd_table *cmd = NULL; + char *koptarg, **cmd_argv; + char *db_name_tmp = NULL; + int cmd_argc; + krb5_error_code retval; + + setlocale(LC_ALL, ""); + set_com_err_hook(extended_com_err_fn); + + /* + * Ensure that "progname" is set before calling com_err. + */ + progname = (strrchr(argv[0], '/') ? + strrchr(argv[0], '/') + 1 : argv[0]); + + retval = kadm5_init_krb5_context(&util_context); + if (retval) { + com_err (progname, retval, _("while initializing Kerberos code")); + exit(1); + } + + cmd_argv = (char **) malloc(sizeof(char *)*argc); + if (cmd_argv == NULL) { + com_err(progname, ENOMEM, _("while creating sub-command arguments")); + exit(1); + } + memset(cmd_argv, 0, sizeof(char *)*argc); + cmd_argc = 1; + + argv++; argc--; + while (*argv) { + if (strcmp(*argv, "-P") == 0 && ARG_VAL) { + mkey_password = koptarg; + manual_mkey = TRUE; + } else if (strcmp(*argv, "-d") == 0 && ARG_VAL) { + global_params.dbname = koptarg; + global_params.mask |= KADM5_CONFIG_DBNAME; + + if (asprintf(&db_name_tmp, "dbname=%s", global_params.dbname) < 0) + { + com_err(progname, ENOMEM, + _("while parsing command arguments")); + exit(1); + } + + if (!add_db_arg(db_name_tmp)) { + com_err(progname, ENOMEM, + _("while parsing command arguments\n")); + exit(1); + } + + } else if (strcmp(*argv, "-x") == 0 && ARG_VAL) { + if (!add_db_arg(koptarg)) { + com_err(progname, ENOMEM, + _("while parsing command arguments\n")); + exit(1); + } + + } else if (strcmp(*argv, "-r") == 0 && ARG_VAL) { + global_params.realm = koptarg; + global_params.mask |= KADM5_CONFIG_REALM; + /* not sure this is really necessary */ + if ((retval = krb5_set_default_realm(util_context, + global_params.realm))) { + com_err(progname, retval, + _("while setting default realm name")); + exit(1); + } + } else if (strcmp(*argv, "-k") == 0 && ARG_VAL) { + if (krb5_string_to_enctype(koptarg, &global_params.enctype)) { + com_err(progname, EINVAL, _(": %s is an invalid enctype"), + koptarg); + exit(1); + } else + global_params.mask |= KADM5_CONFIG_ENCTYPE; + } else if (strcmp(*argv, "-kv") == 0 && ARG_VAL) { + global_params.kvno = (krb5_kvno) atoi(koptarg); + if (global_params.kvno == IGNORE_VNO) { + com_err(progname, EINVAL, _(": %s is an invalid mkeyVNO"), + koptarg); + exit(1); + } else + global_params.mask |= KADM5_CONFIG_KVNO; + } else if (strcmp(*argv, "-M") == 0 && ARG_VAL) { + global_params.mkey_name = koptarg; + global_params.mask |= KADM5_CONFIG_MKEY_NAME; + } else if (strcmp(*argv, "-sf") == 0 && ARG_VAL) { + global_params.stash_file = koptarg; + global_params.mask |= KADM5_CONFIG_STASH_FILE; + } else if (strcmp(*argv, "-m") == 0) { + manual_mkey = TRUE; + global_params.mkey_from_kbd = 1; + global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + } else if (cmd_lookup(*argv) != NULL) { + if (cmd_argv[0] == NULL) + cmd_argv[0] = *argv; + else + usage(); + } else { + cmd_argv[cmd_argc++] = *argv; + } + argv++; argc--; + } + + if (cmd_argv[0] == NULL) + usage(); + + if( !util_context->default_realm ) + { + char *temp = NULL; + retval = krb5_get_default_realm(util_context, &temp); + if( retval ) + { + com_err(progname, retval, _("while getting default realm")); + exit(1); + } + krb5_free_default_realm(util_context, temp); + } + + retval = kadm5_get_config_params(util_context, 1, + &global_params, &global_params); + if (retval) { + com_err(progname, retval, + _("while retreiving configuration parameters")); + exit(1); + } + + /* + * Dump creates files which should not be world-readable. It is + * easiest to do a single umask call here. + */ + (void) umask(077); + + master_keyblock.enctype = global_params.enctype; + if ((master_keyblock.enctype != ENCTYPE_UNKNOWN) && + (!krb5_c_valid_enctype(master_keyblock.enctype))) { + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + } + + cmd = cmd_lookup(cmd_argv[0]); + if (cmd->opendb && open_db_and_mkey()) + return exit_status; + + if (global_params.iprop_enabled == TRUE) + ulog_set_role(util_context, IPROP_MASTER); + else + ulog_set_role(util_context, IPROP_NULL); + + (*cmd->func)(cmd_argc, cmd_argv); + + if( db_name_tmp ) + free( db_name_tmp ); + + if( db5util_db_args ) + free(db5util_db_args); + + quit(); + kadm5_free_config_params(util_context, &global_params); + krb5_free_context(util_context); + free(cmd_argv); + return exit_status; +} + +#if 0 +/* + * This function is no longer used in kdb5_util (and it would no + * longer work, anyway). + */ +void set_dbname(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc < 3) { + com_err(argv[0], 0, _("Too few arguments")); + com_err(progname, 0, _("Usage: %s dbpathname realmname"), argv[0]); + exit_status++; + return; + } + if (dbactive) { + if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) { + com_err(progname, retval, _("while closing previous database")); + exit_status++; + return; + } + if (valid_master_key) { + krb5_free_keyblock_contents(util_context, &master_keyblock); + master_keyblock.contents = NULL; + valid_master_key = 0; + } + krb5_free_principal(util_context, master_princ); + free(mkey_fullname); + dbactive = FALSE; + } + + (void) set_dbname_help(progname, argv[1]); + return; +} +#endif + +/* + * open_db_and_mkey: Opens the KDC and policy database, and sets the + * global master_* variables. Sets dbactive to TRUE if the databases + * are opened, and valid_master_key to 1 if the global master + * variables are set properly. Returns 0 on success, and 1 on + * failure, but it is not considered a failure if the master key + * cannot be fetched (the master key stash file may not exist when the + * program is run). + */ +static int open_db_and_mkey() +{ + krb5_error_code retval; + krb5_data scratch, pwd, seed; + + dbactive = FALSE; + valid_master_key = 0; + + if ((retval = krb5_db_open(util_context, db5util_db_args, + KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN))) { + com_err(progname, retval, _("while initializing database")); + exit_status++; + return(1); + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(util_context, + global_params.mkey_name, + global_params.realm, + &mkey_fullname, &master_princ))) { + com_err(progname, retval, _("while setting up master key name")); + exit_status++; + return(1); + } + if ((retval = krb5_db_get_principal(util_context, master_princ, 0, + &master_entry))) { + com_err(progname, retval, _("while retrieving master entry")); + exit_status++; + (void) krb5_db_fini(util_context); + return(1); + } + + if (global_params.mask & KADM5_CONFIG_KVNO) + master_kvno = global_params.kvno; /* user specified */ + else + master_kvno = IGNORE_VNO; + + /* the databases are now open, and the master principal exists */ + dbactive = TRUE; + + if (mkey_password) { + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(util_context, master_princ, &scratch); + if (retval) { + com_err(progname, retval, _("while calculated master key salt")); + exit_status++; + return(1); + } + + /* If no encryption type is set, use the default */ + if (master_keyblock.enctype == ENCTYPE_UNKNOWN) + master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + if (!krb5_c_valid_enctype(master_keyblock.enctype)) + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", + master_keyblock.enctype); + + retval = krb5_c_string_to_key(util_context, master_keyblock.enctype, + &pwd, &scratch, &master_keyblock); + if (retval) { + com_err(progname, retval, + _("while transforming master key from password")); + exit_status++; + return(1); + } + free(scratch.data); + mkey_password = 0; + + } else { + if ((retval = krb5_db_fetch_mkey(util_context, master_princ, + master_keyblock.enctype, + manual_mkey, FALSE, + global_params.stash_file, + &master_kvno, + 0, &master_keyblock))) { + com_err(progname, retval, _("while reading master key")); + com_err(progname, 0, _("Warning: proceeding without master key")); + exit_status++; + return(0); + } + } + + if ((retval = krb5_db_fetch_mkey_list(util_context, master_princ, + &master_keyblock))) { + com_err(progname, retval, "while getting master key list"); + com_err(progname, 0, "Warning: proceeding without master key list"); + exit_status++; + return(0); + } + + seed.length = master_keyblock.length; + seed.data = (char *) master_keyblock.contents; + + if ((retval = krb5_c_random_seed(util_context, &seed))) { + com_err(progname, retval, _("while seeding random number generator")); + exit_status++; + memset(master_keyblock.contents, 0, master_keyblock.length); + krb5_free_keyblock_contents(util_context, &master_keyblock); + return(1); + } + + if (global_params.iprop_enabled) { + if (ulog_map(util_context, global_params.iprop_logfile, + global_params.iprop_ulogsize)) { + fprintf(stderr, _("%s: Could not map log\n"), progname); + exit_status++; + return(1); + } + } + + valid_master_key = 1; + dbactive = TRUE; + return 0; +} + +#ifdef HAVE_GETCWD +#undef getwd +#endif + +int +quit() +{ + krb5_error_code retval; + static krb5_boolean finished = 0; + + if (finished) + return 0; + ulog_fini(util_context); + retval = krb5_db_fini(util_context); + zapfree(master_keyblock.contents, master_keyblock.length); + krb5_free_principal(util_context, master_princ); + finished = TRUE; + if (retval && retval != KRB5_KDB_DBNOTINITED) { + com_err(progname, retval, _("while closing database")); + exit_status++; + return 1; + } + return 0; +} + +static void +add_random_key(argc, argv) + int argc; + char **argv; +{ + krb5_error_code ret; + krb5_principal princ; + krb5_db_entry *dbent; + krb5_timestamp now; + + krb5_key_salt_tuple *keysalts = NULL; + krb5_int32 num_keysalts = 0; + + int free_keysalts; + char *me = progname; + char *ks_str = NULL; + char *pr_str; + krb5_keyblock *tmp_mkey; + + if (argc < 2) + usage(); + for (argv++, argc--; *argv; argv++, argc--) { + if (!strcmp(*argv, "-e")) { + argv++; argc--; + ks_str = *argv; + continue; + } else + break; + } + if (argc < 1) + usage(); + pr_str = *argv; + ret = krb5_parse_name(util_context, pr_str, &princ); + if (ret) { + com_err(me, ret, _("while parsing principal name %s"), pr_str); + exit_status++; + return; + } + ret = krb5_db_get_principal(util_context, princ, 0, &dbent); + if (ret) { + com_err(me, ret, _("while fetching principal %s"), pr_str); + exit_status++; + return; + } + ret = krb5_string_to_keysalts(ks_str, + NULL, NULL, 0, + &keysalts, + &num_keysalts); + if (ret) { + com_err(me, ret, _("while parsing keysalts %s"), ks_str); + exit_status++; + return; + } + if (!num_keysalts || keysalts == NULL) { + num_keysalts = global_params.num_keysalts; + keysalts = global_params.keysalts; + free_keysalts = 0; + } else + free_keysalts = 1; + + /* Find the mkey used to protect the existing keys */ + ret = krb5_dbe_find_mkey(util_context, dbent, &tmp_mkey); + if (ret) { + com_err(me, ret, _("while finding mkey")); + krb5_db_free_principal(util_context, dbent); + exit_status++; + return; + } + + ret = krb5_dbe_ark(util_context, tmp_mkey, keysalts, num_keysalts, dbent); + if (free_keysalts) + free(keysalts); + if (ret) { + com_err(me, ret, "while randomizing principal %s", pr_str); + krb5_db_free_principal(util_context, dbent); + exit_status++; + return; + } + dbent->attributes &= ~KRB5_KDB_REQUIRES_PWCHANGE; + ret = krb5_timeofday(util_context, &now); + if (ret) { + com_err(me, ret, _("while getting time")); + krb5_db_free_principal(util_context, dbent); + exit_status++; + return; + } + ret = krb5_dbe_update_last_pwd_change(util_context, dbent, now); + if (ret) { + com_err(me, ret, _("while setting changetime")); + krb5_db_free_principal(util_context, dbent); + exit_status++; + return; + } + ret = krb5_db_put_principal(util_context, dbent); + krb5_db_free_principal(util_context, dbent); + if (ret) { + com_err(me, ret, _("while saving principal %s"), pr_str); + exit_status++; + return; + } + printf(_("%s changed\n"), pr_str); +} diff --git a/src/kadmin/dbutil/kdb5_util.h b/src/kadmin/dbutil/kdb5_util.h new file mode 100644 index 000000000000..69e186d4b21e --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.h @@ -0,0 +1,99 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/dbutil/kdb5_util.h */ +/* + * Copyright 1992, 2008, 2009 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include <kdb_log.h> + +#define REALM_SEP '@' +#define REALM_SEP_STR "@" + +extern char *progname; +#ifndef V4_DECLARES_STATIC +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +#endif +extern krb5_boolean dbactive; +extern int exit_status; +extern krb5_context util_context; +extern kadm5_config_params global_params; +extern int valid_master_key; +extern krb5_db_entry master_db; +extern char **db5util_db_args; +extern int db5util_db_args_size; +extern krb5_kvno new_mkvno; +extern krb5_keyblock new_master_keyblock; +extern int add_db_arg(char *arg); + +extern void usage(void); + +extern void add_key +(char const *, char const *, + krb5_const_principal, const krb5_keyblock *, + krb5_kvno, krb5_keysalt *); +extern int set_dbname_help +(char *, char *); + +extern char *kdb5_util_Init (int, char **); + +extern int quit (void); + +extern int check_for_match +(char *, int, krb5_db_entry *, int, int); + +extern void parse_token +(char *, int *, int *, char *); + +extern int create_db_entry (krb5_principal, krb5_db_entry *); + +extern int kadm5_create_magic_princs (kadm5_config_params *params, + krb5_context context); + +extern int process_ov_principal (krb5_context kcontext, const char *fname, + FILE *filep, krb5_boolean verbose, + int *linenop); + +extern void load_db (int argc, char **argv); +extern void dump_db (int argc, char **argv); +extern void kdb5_create (int argc, char **argv); +extern void kdb5_destroy (int argc, char **argv); +extern void kdb5_stash (int argc, char **argv); +extern void kdb5_add_mkey (int argc, char **argv); +extern void kdb5_use_mkey (int argc, char **argv); +extern void kdb5_list_mkeys (int argc, char **argv); +extern void kdb5_update_princ_encryption (int argc, char **argv); +extern void tabdump (int argc, char **argv); + +extern krb5_error_code master_key_convert(krb5_context context, + krb5_db_entry *db_entry); +extern void kdb5_purge_mkeys (int argc, char **argv); + +extern int kadm5_create (kadm5_config_params *params); + +extern krb5_error_code add_new_mkey(krb5_context, krb5_db_entry *, + krb5_keyblock *, krb5_kvno); + +extern krb5_kvno get_next_kvno(krb5_context, krb5_db_entry *); + +void usage (void); diff --git a/src/kadmin/dbutil/nstrtok.h b/src/kadmin/dbutil/nstrtok.h new file mode 100644 index 000000000000..3ee8f634c05b --- /dev/null +++ b/src/kadmin/dbutil/nstrtok.h @@ -0,0 +1,3 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* Prototype for nstrtok */ +char *nstrtok(char *, const char *delim); diff --git a/src/kadmin/dbutil/ovload.c b/src/kadmin/dbutil/ovload.c new file mode 100644 index 000000000000..15a5ab300521 --- /dev/null +++ b/src/kadmin/dbutil/ovload.c @@ -0,0 +1,205 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include <k5-int.h> +#include <unistd.h> + +#include <kadm5/admin.h> +#include <kadm5/server_internal.h> +#include <kdb.h> +#include "import_err.h" +#include "kdb5_util.h" +#include "nstrtok.h" + +#define LINESIZE 32768 /* XXX */ + +static int parse_pw_hist_ent(current, hist) + char *current; + osa_pw_hist_ent *hist; +{ + int tmp, i, j, ret; + char *cp; + + ret = 0; + hist->n_key_data = 1; + + hist->key_data = (krb5_key_data *) malloc(hist->n_key_data * + sizeof(krb5_key_data)); + if (hist->key_data == NULL) + return ENOMEM; + memset(hist->key_data, 0, sizeof(krb5_key_data)*hist->n_key_data); + + for (i = 0; i < hist->n_key_data; i++) { + krb5_key_data *key_data = &hist->key_data[i]; + + key_data->key_data_ver = 1; + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_type[0] = atoi(cp); + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_length[0] = atoi(cp); + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + if(!(key_data->key_data_contents[0] = + (krb5_octet *) malloc(key_data->key_data_length[0]+1))) { + ret = ENOMEM; + goto done; + } + for(j = 0; j < key_data->key_data_length[0]; j++) { + if(sscanf(cp, "%02x", &tmp) != 1) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_contents[0][j] = tmp; + cp = strchr(cp, ' ') + 1; + } + } + +done: + return ret; +} + +/* + * Function: parse_principal + * + * Purpose: parse principal line in db dump file + * + * Arguments: + * <return value> 0 on success, error code on failure + * + * Requires: + * principal database to be opened. + * nstrtok(3) to have a valid buffer in memory. + * + * Effects: + * [effects] + * + * Modifies: + * [modifies] + * + */ +int process_ov_principal(kcontext, fname, filep, verbose, linenop) + krb5_context kcontext; + const char *fname; + FILE *filep; + krb5_boolean verbose; + int *linenop; +{ + XDR xdrs; + osa_princ_ent_t rec; + krb5_error_code ret; + krb5_tl_data tl_data; + krb5_principal princ; + krb5_db_entry *kdb = NULL; + char *current = 0; + char *cp; + unsigned int x; + char line[LINESIZE]; + + if (fgets(line, LINESIZE, filep) == (char *) NULL) { + return IMPORT_BAD_FILE; + } + if((cp = nstrtok(line, "\t")) == NULL) + return IMPORT_BAD_FILE; + if((rec = (osa_princ_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL) + return ENOMEM; + memset(rec, 0, sizeof(osa_princ_ent_rec)); + if((ret = krb5_parse_name(kcontext, cp, &princ))) + goto done; + krb5_unparse_name(kcontext, princ, ¤t); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } else { + if(strcmp(cp, "")) { + if((rec->policy = strdup(cp)) == NULL) { + ret = ENOMEM; + goto done; + } + } else rec->policy = NULL; + } + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->aux_attributes = strtol(cp, (char **)NULL, 16); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->old_key_len = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->old_key_next = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->admin_history_kvno = atoi(cp); + if (! rec->old_key_len) { + rec->old_keys = NULL; + } else { + if(!(rec->old_keys = (osa_pw_hist_ent *) + malloc(sizeof(osa_pw_hist_ent) * rec->old_key_len))) { + ret = ENOMEM; + goto done; + } + memset(rec->old_keys,0, + sizeof(osa_pw_hist_ent) * rec->old_key_len); + for(x = 0; x < rec->old_key_len; x++) + parse_pw_hist_ent(current, &rec->old_keys[x]); + } + + xdralloc_create(&xdrs, XDR_ENCODE); + if (! xdr_osa_princ_ent_rec(&xdrs, rec)) { + xdr_destroy(&xdrs); + ret = KADM5_XDR_FAILURE; + goto done; + } + + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + tl_data.tl_data_length = xdr_getpos(&xdrs); + tl_data.tl_data_contents = (krb5_octet *) xdralloc_getdata(&xdrs); + + ret = krb5_db_get_principal(kcontext, princ, 0, &kdb); + if (ret) + goto done; + + ret = krb5_dbe_update_tl_data(kcontext, kdb, &tl_data); + if (ret) + goto done; + + ret = krb5_db_put_principal(kcontext, kdb); + if (ret) + goto done; + + xdr_destroy(&xdrs); + + (*linenop)++; + +done: + free(current); + krb5_free_principal(kcontext, princ); + osa_free_princ_ent(rec); + krb5_db_free_principal(kcontext, kdb); + return ret; +} diff --git a/src/kadmin/dbutil/strtok.c b/src/kadmin/dbutil/strtok.c new file mode 100644 index 000000000000..0640c747e44e --- /dev/null +++ b/src/kadmin/dbutil/strtok.c @@ -0,0 +1,105 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + */ + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <stddef.h> +#include <string.h> +#include "nstrtok.h" + +/* + * Function: nstrtok + * + * Purpose: the same as strtok ... just different. does not deal with + * multiple tokens in row. + * + * Arguments: + * s (input) string to scan + * delim (input) list of delimiters + * <return value> string or null on error. + * + * Requires: + * nuttin + * + * Effects: + * sets last to string + * + * Modifies: + * last + * + */ + +char * +nstrtok(s, delim) + register char *s; + register const char *delim; +{ + register const char *spanp; + register int c, sc; + char *tok; + static char *last; + + + if (s == NULL && (s = last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +#ifdef OLD +cont: + c = *s++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + last = NULL; + return (NULL); + } + tok = s - 1; +#else + tok = s; +#endif + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} diff --git a/src/kadmin/dbutil/t_tdumputil.c b/src/kadmin/dbutil/t_tdumputil.c new file mode 100644 index 000000000000..a9ed0e547151 --- /dev/null +++ b/src/kadmin/dbutil/t_tdumputil.c @@ -0,0 +1,115 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kdc/t_tdumputil.c - test tdumputil.c functions */ +/* + * Copyright (C) 2015 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "tdumputil.h" + +static char *argv0; + +static void +usage(void) +{ + fprintf(stderr, + "usage: %s: [-T rectype] [-c] nfields {fieldnames} {fields}\n", + argv0); + exit(1); +} + +int +main(int argc, char **argv) +{ + int ch, csv = 0, i, nf; + char **a, *rectype = NULL; + struct rechandle *h; + + argv0 = argv[0]; + while ((ch = getopt(argc, argv, "T:c")) != -1) { + switch (ch) { + case 'T': + rectype = optarg; + break; + case 'c': + csv = 1; + break; + default: + usage(); + break; + } + } + argc -= optind; + argv += optind; + + if (csv) + h = rechandle_csv(stdout, rectype); + else + h = rechandle_tabsep(stdout, rectype); + if (h == NULL) + exit(1); + + if (*argv == NULL) + usage(); + nf = atoi(*argv); + argc--; + argv++; + a = calloc(nf + 1, sizeof(*a)); + if (a == NULL) + exit(1); + + for (i = 0; argv[i] != NULL && i < nf; i++) + a[i] = argv[i]; + if (i != nf) + usage(); + argv += nf; + a[nf] = NULL; + + if (rectype == NULL && writeheader(h, a) < 0) + exit(1); + free(a); + + while (*argv != NULL) { + if (startrec(h) < 0) + exit(1); + for (i = 0; argv[i] != NULL && i < nf; i++) { + if (writefield(h, "%s", argv[i]) < 0) + exit(1); + } + if (i != nf) + usage(); + argv += nf; + if (endrec(h) < 0) + exit(1); + } + exit(0); +} diff --git a/src/kadmin/dbutil/t_tdumputil.py b/src/kadmin/dbutil/t_tdumputil.py new file mode 100755 index 000000000000..5d7ac38d2fba --- /dev/null +++ b/src/kadmin/dbutil/t_tdumputil.py @@ -0,0 +1,32 @@ +#!/usr/bin/python + +from k5test import * +from subprocess import * + +realm = K5Realm(create_kdb=False) + +def compare(s, expected, msg): + if s == expected: + return + print 'expected:', repr(expected) + print 'got:', repr(s) + fail(msg) + +out = realm.run(['./t_tdumputil', '2', 'field1', 'field2', + 'value1', 'value2']) +expected = 'field1\tfield2\nvalue1\tvalue2\n' +compare(out, expected, 'tab-separated values') + +out = realm.run(['./t_tdumputil', '-c', '2', 'field1', 'field2', + 'space value', 'comma,value', + 'quote"value', 'quotes""value']) +expected = 'field1,field2\nspace value,"comma,value"\n' \ + '"quote""value","quotes""""value"\n' +compare(out, expected, 'comma-separated values') + +out = realm.run(['./t_tdumputil', '-T', 'rectype', '2', 'field1', 'field2', + 'value1', 'value2']) +expected = 'rectype\tvalue1\tvalue2\n' +compare(out, expected, 'rectype prefixed') + +success('tabdump utilities') diff --git a/src/kadmin/dbutil/tabdump.c b/src/kadmin/dbutil/tabdump.c new file mode 100644 index 000000000000..69a3482ec935 --- /dev/null +++ b/src/kadmin/dbutil/tabdump.c @@ -0,0 +1,665 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kdc/tabdump.c - reporting-friendly tabular KDB dumps */ +/* + * Copyright (C) 2015 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <k5-int.h> +#include "k5-platform.h" /* for asprintf */ + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <kadm5/admin.h> +#include <kadm5/server_internal.h> + +#include "adm_proto.h" +#include "kdb5_util.h" +#include "tdumputil.h" + +struct tdopts { + int csv; /* 1 for CSV, 0 for tab-separated */ + int emptyhex_empty; /* print empty hex strings as "" not "-1" */ + int numeric; /* numeric instead of symbolic output */ + int omitheader; /* omit field headers */ + int writerectype; /* write record type prefix */ + char *fname; /* output file name */ +}; + +struct rec_args; + +typedef int (tdump_princ_fn)(struct rec_args *, const char *, krb5_db_entry *); +typedef int (tdump_policy_fn)(struct rec_args *, const char *, + kadm5_policy_ent_t); + +/* Descriptor for a tabdump record type */ +struct tdtype { + const char *rectype; + char * const *fieldnames; + tdump_princ_fn *princ_fn; + tdump_policy_fn *policy_fn; +}; + +static tdump_princ_fn keydata; +static tdump_princ_fn keyinfo; +static tdump_princ_fn princ_flags; +static tdump_princ_fn princ_lockout; +static tdump_princ_fn princ_meta; +static tdump_princ_fn princ_stringattrs; +static tdump_princ_fn princ_tktpolicy; + +static char * const keydata_fields[] = { + "name", "keyindex", "kvno", "enctype", "key", "salttype", "salt", NULL +}; +static char * const keyinfo_fields[] = { + "name", "keyindex", "kvno", "enctype", "salttype", "salt", NULL +}; +static char * const princ_flags_fields[] = { + "name", "flag", "value", NULL +}; +static char * const princ_lockout_fields[] = { + "name", "last_success", "last_failed", "fail_count", NULL +}; +static char * const princ_meta_fields[] = { + "name", "modby", "modtime", "lastpwd", "policy", "mkvno", "hist_kvno", NULL +}; +static char * const princ_stringattrs_fields[] = { + "name", "key", "value", NULL +}; +static char * const princ_tktpolicy_fields[] = { + "name", "expiration", "pw_expiration", "max_life", "max_renew_life", NULL +}; + +/* Lookup table for tabdump record types */ +static struct tdtype tdtypes[] = { + {"keydata", keydata_fields, keydata, NULL}, + {"keyinfo", keyinfo_fields, keyinfo, NULL}, + {"princ_flags", princ_flags_fields, princ_flags, NULL}, + {"princ_lockout", princ_lockout_fields, princ_lockout, NULL}, + {"princ_meta", princ_meta_fields, princ_meta, NULL}, + {"princ_stringattrs", princ_stringattrs_fields, princ_stringattrs, NULL}, + {"princ_tktpolicy", princ_tktpolicy_fields, princ_tktpolicy, NULL}, +}; +#define NTDTYPES (sizeof(tdtypes)/sizeof(tdtypes[0])) + +/* State to pass to KDB iterator */ +struct rec_args { + FILE *f; + struct tdtype *tdtype; + struct rechandle *rh; + struct tdopts *opts; +}; + +/* Decode the KADM_DATA from a DB entry.*/ +static int +get_adb(krb5_db_entry *dbe, osa_princ_ent_rec *adb) +{ + XDR xdrs; + int success; + krb5_tl_data tl_data; + krb5_error_code ret; + + memset(adb, 0, sizeof(*adb)); + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + ret = krb5_dbe_lookup_tl_data(util_context, dbe, &tl_data); + if (ret != 0 || tl_data.tl_data_length == 0) + return 0; + xdrmem_create(&xdrs, (caddr_t)tl_data.tl_data_contents, + tl_data.tl_data_length, XDR_DECODE); + success = xdr_osa_princ_ent_rec(&xdrs, adb); + xdr_destroy(&xdrs); + return success; +} + +/* Write a date field as an ISO 8601 UTC date/time representation. */ +static int +write_date_iso(struct rec_args *args, krb5_timestamp when) +{ + char buf[64]; + time_t t; + struct tm *tm = NULL; + struct rechandle *h = args->rh; + + t = when; + tm = gmtime(&t); + if (tm == NULL) { + errno = EINVAL; + return -1; + } + if (strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%SZ", tm) == 0) { + errno = EINVAL; + return -1; + } + if (writefield(h, "%s", buf) < 0) + return -1; + return 0; +} + +/* Write a date field, optionally as a decimal POSIX timestamp. */ +static int +write_date(struct rec_args *args, krb5_timestamp when) +{ + struct tdopts *opts = args->opts; + struct rechandle *h = args->rh; + + if (opts->numeric) + return writefield(h, "%d", when); + + return write_date_iso(args, when); +} + +/* Write an enctype field, optionally as decimal. */ +static krb5_error_code +write_enctype(struct rec_args *args, krb5_int16 etype) +{ + char buf[256]; + krb5_error_code ret; + struct rechandle *h = args->rh; + struct tdopts *opts = args->opts; + + if (!opts->numeric) { + ret = krb5_enctype_to_name(etype, 0, buf, sizeof(buf)); + if (ret == 0) { + if (writefield(h, "%s", buf) < 0) + return errno; + return ret; + } + } + /* decimal if requested, or if conversion failed */ + if (writefield(h, "%d", etype) < 0) + return errno; + return 0; +} + +/* Write a salttype field, optionally as decimal. */ +static krb5_error_code +write_salttype(struct rec_args *args, krb5_int16 salttype) +{ + char buf[256]; + krb5_error_code ret; + struct rechandle *h = args->rh; + struct tdopts *opts = args->opts; + + if (!opts->numeric) { + ret = krb5_salttype_to_string(salttype, buf, sizeof(buf)); + if (ret == 0) { + if (writefield(h, "%s", buf) < 0) + return errno; + return ret; + } + } + /* decimal if requested, or if conversion failed */ + if (writefield(h, "%d", salttype) < 0) + return errno; + return 0; +} + +/* + * Write a field of bytes from krb5_data as a hexadecimal string. Write empty + * strings as "-1" unless requested. + */ +static int +write_data(struct rec_args *args, krb5_data *data) +{ + int ret; + char *p; + size_t i; + struct k5buf buf; + struct rechandle *h = args->rh; + struct tdopts *opts = args->opts; + + if (data->length == 0 && !opts->emptyhex_empty) { + if (writefield(h, "-1") < 0) + return -1; + return 0; + } + k5_buf_init_dynamic(&buf); + p = data->data; + for (i = 0; i < data->length; i++) + k5_buf_add_fmt(&buf, "%02x", (unsigned char)p[i]); + + if (buf.data == NULL) { + errno = ENOMEM; + return -1; + } + ret = writefield(h, "%s", (char *)buf.data); + k5_buf_free(&buf); + return ret; +} + +/* Write a single record of a keydata/keyinfo key set. */ +static krb5_error_code +keyinfo_rec(struct rec_args *args, const char *name, int i, krb5_key_data *kd, + int dumpkeys) +{ + int ret; + krb5_data data; + struct rechandle *h = args->rh; + + if (startrec(h) < 0) + return errno; + if (writefield(h, "%s", name) < 0) + return errno; + if (writefield(h, "%d", i) < 0) + return errno; + if (writefield(h, "%d", kd->key_data_kvno) < 0) + return errno; + if (write_enctype(args, kd->key_data_type[0]) < 0) + return errno; + if (dumpkeys) { + data.length = kd->key_data_length[0]; + data.data = (void *)kd->key_data_contents[0]; + if (write_data(args, &data) < 0) + return errno; + } + ret = write_salttype(args, kd->key_data_type[1]); + if (ret) + return ret; + data.length = kd->key_data_length[1]; + data.data = (void *)kd->key_data_contents[1]; + if (write_data(args, &data) < 0) + return errno; + if (endrec(h) < 0) + return errno; + return 0; +} + +/* Write out a principal's key set, optionally including actual key data. */ +static krb5_error_code +keyinfo_common(struct rec_args *args, const char *name, krb5_db_entry *entry, + int dumpkeys) +{ + krb5_error_code ret; + krb5_key_data kd; + int i; + + for (i = 0; i < entry->n_key_data; i++) { + kd = entry->key_data[i]; + /* missing salt data -> normal salt */ + if (kd.key_data_ver == 1) { + kd.key_data_ver = 2; + kd.key_data_type[1] = KRB5_KDB_SALTTYPE_NORMAL; + kd.key_data_length[1] = 0; + kd.key_data_contents[1] = NULL; + } + ret = keyinfo_rec(args, name, i, &kd, dumpkeys); + if (ret) + return ret; + } + return 0; +} + +/* Write a principal's key data. */ +static krb5_error_code +keydata(struct rec_args *args, const char *name, krb5_db_entry *dbe) +{ + return keyinfo_common(args, name, dbe, 1); +} + +/* Write a principal's key info (suppressing actual key data). */ +static krb5_error_code +keyinfo(struct rec_args *args, const char *name, krb5_db_entry *dbe) +{ + return keyinfo_common(args, name, dbe, 0); +} + +/* Write a record corresponding to a single principal flag setting. */ +static krb5_error_code +princflag_rec(struct rechandle *h, const char *name, const char *flagname, + int set) +{ + if (startrec(h) < 0) + return errno; + if (writefield(h, "%s", name) < 0) + return errno; + if (writefield(h, "%s", flagname) < 0) + return errno; + if (writefield(h, "%d", set) < 0) + return errno; + if (endrec(h) < 0) + return errno; + return 0; +} + +/* Write a principal's flag settings. */ +static krb5_error_code +princ_flags(struct rec_args *args, const char *name, krb5_db_entry *dbe) +{ + int i; + char *s = NULL; + krb5_flags flags = dbe->attributes; + krb5_error_code ret; + struct tdopts *opts = args->opts; + struct rechandle *h = args->rh; + + for (i = 0; i < 32; i++) { + if (opts->numeric) { + if (asprintf(&s, "0x%08lx", 1UL << i) == -1) + return ENOMEM; + } else { + ret = krb5_flagnum_to_string(i, &s); + if (ret) + return ret; + /* Don't print unknown flags if they're not set and numeric output + * isn't requested. */ + if (!(flags & (1UL << i)) && strncmp(s, "0x", 2) == 0) { + free(s); + continue; + } + } + ret = princflag_rec(h, name, s, ((flags & (1UL << i)) != 0)); + free(s); + if (ret) + return ret; + } + return 0; +} + +/* Write a principal's lockout data. */ +static krb5_error_code +princ_lockout(struct rec_args *args, const char *name, krb5_db_entry *dbe) +{ + struct rechandle *h = args->rh; + + if (startrec(h) < 0) + return errno; + if (writefield(h, "%s", name) < 0) + return errno; + if (write_date(args, dbe->last_success) < 0) + return errno; + if (write_date(args, dbe->last_failed) < 0) + return errno; + if (writefield(h, "%d", dbe->fail_auth_count) < 0) + return errno; + if (endrec(h) < 0) + return errno; + return 0; +} + +/* Write a principal's metadata. */ +static krb5_error_code +princ_meta(struct rec_args *args, const char *name, krb5_db_entry *dbe) +{ + int got_adb = 0; + char *modby; + krb5_kvno mkvno; + const char *policy; + krb5_principal mod_princ = NULL; + krb5_timestamp mod_time, last_pwd; + krb5_error_code ret; + osa_princ_ent_rec adb; + struct rechandle *h = args->rh; + + memset(&adb, 0, sizeof(adb)); + if (startrec(h) < 0) + return errno; + if (writefield(h, "%s", name) < 0) + return errno; + + ret = krb5_dbe_lookup_last_pwd_change(util_context, dbe, &last_pwd); + if (ret) + return ret; + ret = krb5_dbe_get_mkvno(util_context, dbe, &mkvno); + if (ret) + return ret; + + ret = krb5_dbe_lookup_mod_princ_data(util_context, dbe, &mod_time, + &mod_princ); + if (ret) + return ret; + ret = krb5_unparse_name(util_context, mod_princ, &modby); + krb5_free_principal(util_context, mod_princ); + if (ret) + return ret; + ret = writefield(h, "%s", modby); + krb5_free_unparsed_name(util_context, modby); + if (ret < 0) + return errno; + + if (write_date(args, mod_time) < 0) + return errno; + if (write_date(args, last_pwd) < 0) + return errno; + + got_adb = get_adb(dbe, &adb); + if (got_adb && adb.policy != NULL) + policy = adb.policy; + else + policy = ""; + ret = writefield(h, "%s", policy); + if (ret < 0) { + ret = errno; + goto cleanup; + } + if (writefield(h, "%d", mkvno) < 0) { + ret = errno; + goto cleanup; + } + if (writefield(h, "%d", adb.admin_history_kvno) < 0) { + ret = errno; + goto cleanup; + } + if (endrec(h) < 0) + ret = errno; + else + ret = 0; + +cleanup: + kdb_free_entry(NULL, NULL, &adb); + return ret; +} + +/* Write a principal's string attributes. */ +static krb5_error_code +princ_stringattrs(struct rec_args *args, const char *name, krb5_db_entry *dbe) +{ + int i, nattrs; + krb5_error_code ret; + krb5_string_attr *attrs; + struct rechandle *h = args->rh; + + ret = krb5_dbe_get_strings(util_context, dbe, &attrs, &nattrs); + if (ret) + return ret; + for (i = 0; i < nattrs; i++) { + if (startrec(h) < 0) { + ret = errno; + goto cleanup; + } + if (writefield(h, "%s", name) < 0) { + ret = errno; + goto cleanup; + } + if (writefield(h, "%s", attrs[i].key) < 0) { + ret = errno; + goto cleanup; + } + if (writefield(h, "%s", attrs[i].value) < 0) { + ret = errno; + goto cleanup; + } + if (endrec(h) < 0) { + ret = errno; + goto cleanup; + } + } +cleanup: + krb5_dbe_free_strings(util_context, attrs, nattrs); + return ret; +} + +/* Write a principal's ticket policy. */ +static krb5_error_code +princ_tktpolicy(struct rec_args *args, const char *name, krb5_db_entry *dbe) +{ + struct rechandle *h = args->rh; + + if (startrec(h) < 0) + return errno; + if (writefield(h, "%s", name) < 0) + return errno; + if (write_date(args, dbe->expiration) < 0) + return errno; + if (write_date(args, dbe->pw_expiration) < 0) + return errno; + if (writefield(h, "%d", dbe->max_life) < 0) + return errno; + if (writefield(h, "%d", dbe->max_renewable_life) < 0) + return errno; + if (endrec(h) < 0) + return errno; + return 0; +} + +/* Iterator function for krb5_db_iterate() */ +static krb5_error_code +tditer(void *ptr, krb5_db_entry *entry) +{ + krb5_error_code ret; + struct rec_args *args = ptr; + char *name; + + ret = krb5_unparse_name(util_context, entry->princ, &name); + if (ret) { + com_err(progname, ret, _("while unparsing principal name")); + return ret; + } + ret = args->tdtype->princ_fn(args, name, entry); + krb5_free_unparsed_name(util_context, name); + if (ret) + return ret; + return 0; +} + +/* Set up state structure for the iterator. */ +static krb5_error_code +setup_args(struct rec_args *args, struct tdtype *tdtype, + struct tdopts *opts) +{ + FILE *f = NULL; + const char *rectype = NULL; + struct rechandle *rh; + + args->tdtype = tdtype; + args->opts = opts; + if (opts->fname != NULL && strcmp(opts->fname, "-") != 0) { + f = fopen(opts->fname, "w"); + if (f == NULL) { + com_err(progname, errno, _("opening %s for writing"), + opts->fname); + return errno; + } + args->f = f; + } else { + f = stdout; + args->f = NULL; + } + if (opts->writerectype) + rectype = tdtype->rectype; + if (opts->csv) + rh = rechandle_csv(f, rectype); + else + rh = rechandle_tabsep(f, rectype); + if (rh == NULL) + return ENOMEM; + args->rh = rh; + if (!opts->omitheader && writeheader(rh, tdtype->fieldnames) < 0) + return errno; + return 0; +} + +/* Clean up the state structure. */ +static void +cleanup_args(struct rec_args *args) +{ + rechandle_free(args->rh); + if (args->f != NULL) + fclose(args->f); +} + +/* + * Usaage is: + * tabdump [-H] [-c] [-e] [-n] [-o outfile] dumptype + */ +void +tabdump(int argc, char **argv) +{ + int ch; + size_t i; + const char *rectype; + struct rec_args args; + struct tdopts opts; + krb5_error_code ret; + + memset(&opts, 0, sizeof(opts)); + memset(&args, 0, sizeof(args)); + optind = 1; + while ((ch = getopt(argc, argv, "Hceno:")) != -1) { + switch (ch) { + case 'H': + opts.omitheader = 1; + break; + case 'c': + opts.csv = 1; + break; + case 'e': + opts.emptyhex_empty = 1; + break; + case 'n': + opts.numeric = 1; + break; + case 'o': + opts.fname = optarg; + break; + case '?': + default: + usage(); + break; + } + } + if (argc - optind < 1) + usage(); + rectype = argv[optind]; + for (i = 0; i < NTDTYPES; i++) { + if (strcmp(rectype, tdtypes[i].rectype) == 0) { + setup_args(&args, &tdtypes[i], &opts); + break; + } + } + if (i >= NTDTYPES) + usage(); + ret = krb5_db_iterate(util_context, NULL, tditer, &args, 0); + cleanup_args(&args); + if (ret) { + com_err(progname, ret, _("performing tabular dump")); + exit_status++; + } +} diff --git a/src/kadmin/dbutil/tdumputil.c b/src/kadmin/dbutil/tdumputil.c new file mode 100644 index 000000000000..2c14d5670e08 --- /dev/null +++ b/src/kadmin/dbutil/tdumputil.c @@ -0,0 +1,266 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kdc/tdumputil.c - utilities for tab-separated, etc. files */ +/* + * Copyright (C) 2015 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "k5-int.h" +#include "k5-platform.h" /* for vasprintf */ +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "tdumputil.h" + +/* + * Structure describing flavor of a tabular output format. + * + * fieldsep is the field separator + * + * recordsep is the record/line separator + * + * quotechar begins and ends a quoted field. If an instance of quotechar + * occurs within a quoted field value, it is doubled. + * + * Values are only quoted if they contain fieldsep, recordsep, or quotechar. + */ +struct flavor { + int fieldsep; /* field separator */ + int recordsep; /* record separator */ + int quotechar; /* quote character */ +}; + +struct rechandle { + FILE *fh; + const char *rectype; + int do_sep; + struct flavor flavor; +}; + +static const struct flavor tabsep = { + '\t', /* fieldsep */ + '\n', /* recordsep */ + '\0' /* quotechar */ +}; + +static const struct flavor csv = { + ',', /* fieldsep */ + '\n', /* recordsep */ + '"' /* quotechar */ +}; + +/* + * Double any quote characters present in a quoted field. + */ +static char * +qquote(struct flavor *fl, const char *s) +{ + const char *sp; + struct k5buf buf; + + k5_buf_init_dynamic(&buf); + for (sp = s; *sp != '\0'; sp++) { + k5_buf_add_len(&buf, sp, 1); + if (*sp == fl->quotechar) + k5_buf_add_len(&buf, sp, 1); + } + return buf.data; +} + +/* + * Write an optionally quoted field. + */ +static int +writequoted(struct rechandle *h, const char *fmt, va_list ap) +{ + int doquote = 0, ret; + char *s = NULL, *qs = NULL; + struct flavor fl = h->flavor; + + assert(fl.quotechar != '\0'); + ret = vasprintf(&s, fmt, ap); + if (ret < 0) + return ret; + if (strchr(s, fl.fieldsep) != NULL) + doquote = 1; + if (strchr(s, fl.recordsep) != NULL) + doquote = 1; + if (strchr(s, fl.quotechar) != NULL) + doquote = 1; + + if (doquote) { + qs = qquote(&fl, s); + if (qs == NULL) { + ret = -1; + goto cleanup; + } + ret = fprintf(h->fh, "%c%s%c", fl.quotechar, qs, fl.quotechar); + } else { + ret = fprintf(h->fh, "%s", s); + } +cleanup: + free(s); + free(qs); + return ret; +} + +/* + * Return a rechandle with the requested file handle and rectype. + * + * rectype must be a valid pointer for the entire lifetime of the rechandle (or + * null) + */ +static struct rechandle * +rechandle_common(FILE *fh, const char *rectype) +{ + struct rechandle *h = calloc(1, sizeof(*h)); + + if (h == NULL) + return NULL; + h->fh = fh; + h->rectype = rectype; + h->do_sep = 0; + return h; +} + +/* + * Return a rechandle for tab-separated output. + */ +struct rechandle * +rechandle_tabsep(FILE *fh, const char *rectype) +{ + struct rechandle *h = rechandle_common(fh, rectype); + + if (h == NULL) + return NULL; + h->flavor = tabsep; + return h; +} + +/* + * Return a rechandle for CSV output. + */ +struct rechandle * +rechandle_csv(FILE *fh, const char *rectype) +{ + struct rechandle *h = rechandle_common(fh, rectype); + + if (h == NULL) + return NULL; + h->flavor = csv; + return h; +} + +/* + * Free a rechandle. + */ +void +rechandle_free(struct rechandle *h) +{ + free(h); +} + +/* + * Start a record. This includes writing a record type prefix (rectype) if + * specified. + */ +int +startrec(struct rechandle *h) +{ + if (h->rectype == NULL) { + h->do_sep = 0; + return 0; + } + h->do_sep = 1; + return fputs(h->rectype, h->fh); +} + +/* + * Write a single field of a record. This includes writing a separator + * character, if appropriate. + */ +int +writefield(struct rechandle *h, const char *fmt, ...) +{ + int ret = 0; + va_list ap; + struct flavor fl = h->flavor; + + if (h->do_sep) { + ret = fputc(fl.fieldsep, h->fh); + if (ret < 0) + return ret; + } + h->do_sep = 1; + va_start(ap, fmt); + if (fl.quotechar == '\0') + ret = vfprintf(h->fh, fmt, ap); + else + ret = writequoted(h, fmt, ap); + va_end(ap); + return ret; +} + +/* + * Finish a record (line). + */ +int +endrec(struct rechandle *h) +{ + int ret = 0; + struct flavor fl = h->flavor; + + ret = fputc(fl.recordsep, h->fh); + h->do_sep = 0; + return ret; +} + +/* + * Write a header line if h->rectype is null. (If rectype is set, it will be + * prefixed to output lines, most likely in a mixed record type output file, so + * it doesn't make sense to output a header line in that case.) + */ +int +writeheader(struct rechandle *h, char * const *a) +{ + int ret = 0; + char * const *p; + + if (h->rectype != NULL) + return 0; + for (p = a; *p != NULL; p++) { + ret = writefield(h, "%s", *p); + if (ret < 0) + return ret; + } + ret = endrec(h); + return ret; +} diff --git a/src/kadmin/dbutil/tdumputil.h b/src/kadmin/dbutil/tdumputil.h new file mode 100644 index 000000000000..b7437310ce6a --- /dev/null +++ b/src/kadmin/dbutil/tdumputil.h @@ -0,0 +1,51 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kdc/tdumputil.h - utilities for tab-separated, etc. files */ +/* + * Copyright (C) 2015 by the Massachusetts Institute of Technology. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TDUMPUTIL_H +#define TDUMPUTIL_H + +struct rechandle; + +struct rechandle *rechandle_csv(FILE *, const char *); +struct rechandle *rechandle_tabsep(FILE *, const char *); +void rechandle_free(struct rechandle *); + +int startrec(struct rechandle *); +int writefield(struct rechandle *, const char *, ...) +#if !defined(__cplusplus) && (__GNUC__ > 2) + __attribute__((__format__(__printf__, 2, 3))) +#endif + ; +int endrec(struct rechandle *); +int writeheader(struct rechandle *, char * const *); + +#endif /* TDUMPUTIL_H */ diff --git a/src/kadmin/deps b/src/kadmin/deps new file mode 100644 index 000000000000..2feac3c9d388 --- /dev/null +++ b/src/kadmin/deps @@ -0,0 +1 @@ +# No dependencies here. diff --git a/src/kadmin/kdbkeys/Makefile.in b/src/kadmin/kdbkeys/Makefile.in new file mode 100644 index 000000000000..996febd8ea59 --- /dev/null +++ b/src/kadmin/kdbkeys/Makefile.in @@ -0,0 +1,16 @@ +mydir=. +BUILDTOP=$(REL)..$(S).. + +PROG = kdbkeys +OBJS = kdbkeys.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS) + +install: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/kdbkeys/do-test.pl b/src/kadmin/kdbkeys/do-test.pl new file mode 100755 index 000000000000..7acb425f790b --- /dev/null +++ b/src/kadmin/kdbkeys/do-test.pl @@ -0,0 +1,56 @@ +#!/afs/athena/contrib/perl/p + +# +# $Id$ +# + +$debug = $ARGV[1] || $ENV{'VERBOSE_TEST'}; + +die "Need a number.\n" if !$ARGV[0]; + +die "Neither \$TOP nor \$TESTDIR is set.\n" + if (! ($ENV{'TOP'} || $ENV{'TESTDIR'})); + +$TESTDIR = ($ENV{'TESTDIR'} || "$ENV{'TOP'}/testing"); +$INITDB = ($ENV{'INITDB'} || "$TESTDIR/scripts/init_db"); + +for ($i=0; $i<$ARGV[0]; $i++) { + print "Trial $i\n" if $debug; + + system("$INITDB > /dev/null 2>&1") && + die "Error in init_db\n"; + + open(KEYS,"../dbutil/kdb5_util -R dump_db|") || + die "Couldn't run kdb5_util: $!\n"; + chop($header = <KEYS>); + if ($header ne "kdb5_util load_dump version 4") { + die "Cannot operate on dump version \"$header\"; version 4 required."; + } + while(<KEYS>) { + next if ((!/^princ.*kadmin\//) && (!/^princ.*krbtgt/)); + + print if $debug > 1; + + split; + + $princ = $_[6]; + $nkeys = $_[4]; + $ntls = $_[3]; + print "$princ: nkeys $nkeys, ntls $ntls\n" if $debug; + for ($j = 15 + $ntls*3; $nkeys > 0; $nkeys--) { + $ver = $_[$j++]; + $kvno = $_[$j++]; + $keytype = $_[$j++]; + $keylen = $_[$j++]; + $keydata = $_[$j++]; + $j += 3 if ($ver > 1); + + print "$princ, ver $ver, kvno $kvno, type $keytype, len $keylen, " + . "data $keydata\n" if $debug; + + die "Duplicated key $princ = $keydata\n" if + $keys{$keydata}++; + } + } + close(KEYS); +} diff --git a/src/kadmin/ktutil/Makefile.in b/src/kadmin/ktutil/Makefile.in new file mode 100644 index 000000000000..f8f9b4111c32 --- /dev/null +++ b/src/kadmin/ktutil/Makefile.in @@ -0,0 +1,30 @@ +mydir=kadmin$(S)ktutil +BUILDTOP=$(REL)..$(S).. + +OBJS= ktutil.o \ + ktutil_ct.o \ + ktutil_funcs.o + +SRCS= $(srcdir)/ktutil.c \ + ktutil_ct.c \ + $(srcdir)/ktutil_funcs.c + +all: ktutil + +ktutil: ktutil.o $(OBJS) $(SS_DEPLIB) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o ktutil $(OBJS) $(SS_LIB) $(KRB5_BASE_LIBS) + +install: + $(INSTALL_PROGRAM) ktutil ${DESTDIR}$(CLIENT_BINDIR)/ktutil + +generate-files-mac: ktutil_ct.c + +# needed until we run makedepend +ktutil_ct.c: ktutil_ct.ct + +ktutil_ct.o: ktutil_ct.c + +clean: + $(RM) ktutil_ct.c ktutil + +depend: ktutil_ct.c diff --git a/src/kadmin/ktutil/deps b/src/kadmin/ktutil/deps new file mode 100644 index 000000000000..4df399924b56 --- /dev/null +++ b/src/kadmin/ktutil/deps @@ -0,0 +1,26 @@ +# +# Generated makefile dependencies follow. +# +$(OUTPRE)ktutil.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SS_DEPS) \ + $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h ktutil.c ktutil.h +$(OUTPRE)ktutil_ct.$(OBJEXT): $(COM_ERR_DEPS) $(SS_DEPS) \ + ktutil_ct.c +$(OUTPRE)ktutil_funcs.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h ktutil.h ktutil_funcs.c diff --git a/src/kadmin/ktutil/ktutil.c b/src/kadmin/ktutil/ktutil.c new file mode 100644 index 000000000000..ef16d37a5693 --- /dev/null +++ b/src/kadmin/ktutil/ktutil.c @@ -0,0 +1,274 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/ktutil/ktutil.c - SS user interface for ktutil */ +/* + * Copyright 1995, 1996, 2008 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +#include "k5-int.h" +#include "ktutil.h" +#include <com_err.h> +#include <locale.h> +#include "adm_proto.h" +#include <ss/ss.h> +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif + +extern ss_request_table ktutil_cmds; +krb5_context kcontext; +krb5_kt_list ktlist = NULL; + +int main(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + int sci_idx; + + setlocale(LC_ALL, ""); + retval = krb5_init_context(&kcontext); + if (retval) { + com_err(argv[0], retval, _("while initializing krb5")); + exit(1); + } + sci_idx = ss_create_invocation("ktutil", "5.0", (char *)NULL, + &ktutil_cmds, &retval); + if (retval) { + ss_perror(sci_idx, retval, _("creating invocation")); + exit(1); + } + retval = ss_listen(sci_idx); + ktutil_free_kt_list(kcontext, ktlist); + exit(0); +} + +void ktutil_clear_list(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc != 1) { + fprintf(stderr, _("%s: invalid arguments\n"), argv[0]); + return; + } + retval = ktutil_free_kt_list(kcontext, ktlist); + if (retval) + com_err(argv[0], retval, _("while freeing ktlist")); + ktlist = NULL; +} + +void ktutil_read_v5(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc != 2) { + fprintf(stderr, _("%s: must specify keytab to read\n"), argv[0]); + return; + } + retval = ktutil_read_keytab(kcontext, argv[1], &ktlist); + if (retval) + com_err(argv[0], retval, _("while reading keytab \"%s\""), argv[1]); +} + +void ktutil_read_v4(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc != 2) { + fprintf(stderr, _("%s: must specify the srvtab to read\n"), argv[0]); + return; + } + retval = ktutil_read_srvtab(kcontext, argv[1], &ktlist); + if (retval) + com_err(argv[0], retval, _("while reading srvtab \"%s\""), argv[1]); +} + +void ktutil_write_v5(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc != 2) { + fprintf(stderr, _("%s: must specify keytab to write\n"), argv[0]); + return; + } + retval = ktutil_write_keytab(kcontext, ktlist, argv[1]); + if (retval) + com_err(argv[0], retval, _("while writing keytab \"%s\""), argv[1]); +} + +void ktutil_write_v4(argc, argv) + int argc; + char *argv[]; +{ + fprintf(stderr, _("%s: writing srvtabs is no longer supported\n"), + argv[0]); +} + +void ktutil_add_entry(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char *princ = NULL; + char *enctype = NULL; + krb5_kvno kvno = 0; + int use_pass = 0, use_key = 0, i; + + for (i = 1; i < argc; i++) { + if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-p", 2)) { + princ = argv[++i]; + continue; + } + if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-k", 2)) { + kvno = (krb5_kvno) atoi(argv[++i]); + continue; + } + if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-e", 2)) { + enctype = argv[++i]; + continue; + } + if ((strlen(argv[i]) == 9) && !strncmp(argv[i], "-password", 9)) { + use_pass++; + continue; + } + if ((strlen(argv[i]) == 4) && !strncmp(argv[i], "-key", 4)) { + use_key++; + continue; + } + } + + if (argc != 8 || !(princ && kvno && enctype) || (use_pass+use_key != 1)) { + fprintf(stderr, _("usage: %s (-key | -password) -p principal " + "-k kvno -e enctype\n"), argv[0]); + return; + } + + retval = ktutil_add(kcontext, &ktlist, princ, kvno, enctype, use_pass); + if (retval) + com_err(argv[0], retval, _("while adding new entry")); +} + +void ktutil_delete_entry(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc != 2) { + fprintf(stderr, _("%s: must specify entry to delete\n"), argv[0]); + return; + } + retval = ktutil_delete(kcontext, &ktlist, atoi(argv[1])); + if (retval) + com_err(argv[0], retval, _("while deleting entry %d"), atoi(argv[1])); +} + +void ktutil_list(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + krb5_kt_list lp; + int show_time = 0, show_keys = 0, show_enctype = 0; + int i; + unsigned int j; + char *pname; + + for (i = 1; i < argc; i++) { + if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-t", 2)) { + show_time++; + continue; + } + if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-k", 2)) { + show_keys++; + continue; + } + if ((strlen(argv[i]) == 2) && !strncmp(argv[i], "-e", 2)) { + show_enctype++; + continue; + } + + fprintf(stderr, _("%s: usage: %s [-t] [-k] [-e]\n"), argv[0], argv[0]); + return; + } + /* XXX Translating would disturb table alignment; skip for now. */ + if (show_time) { + printf("slot KVNO Timestamp Principal\n"); + printf("---- ---- ----------------- ---------------------------------------------------\n"); + } else { + printf("slot KVNO Principal\n"); + printf("---- ---- ---------------------------------------------------------------------\n"); + } + for (i = 1, lp = ktlist; lp; i++, lp = lp->next) { + retval = krb5_unparse_name(kcontext, lp->entry->principal, &pname); + if (retval) { + com_err(argv[0], retval, "while unparsing principal name"); + return; + } + printf("%4d %4d ", i, lp->entry->vno); + if (show_time) { + char fmtbuf[18]; + char fill; + time_t tstamp; + + tstamp = lp->entry->timestamp; + (void) localtime(&tstamp); + lp->entry->timestamp = tstamp; + fill = ' '; + if (!krb5_timestamp_to_sfstring((krb5_timestamp)lp->entry-> + timestamp, + fmtbuf, + sizeof(fmtbuf), + &fill)) + printf("%s ", fmtbuf); + } + printf("%40s", pname); + if (show_enctype) { + static char buf[256]; + if ((retval = krb5_enctype_to_name(lp->entry->key.enctype, FALSE, + buf, sizeof(buf)))) { + com_err(argv[0], retval, + _("While converting enctype to string")); + return; + } + printf(" (%s) ", buf); + } + + if (show_keys) { + printf(" (0x"); + for (j = 0; j < lp->entry->key.length; j++) + printf("%02x", lp->entry->key.contents[j]); + printf(")"); + } + printf("\n"); + free(pname); + } +} diff --git a/src/kadmin/ktutil/ktutil.h b/src/kadmin/ktutil/ktutil.h new file mode 100644 index 000000000000..c4839ff12aa8 --- /dev/null +++ b/src/kadmin/ktutil/ktutil.h @@ -0,0 +1,69 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/ktutil/ktutil.h */ +/* + * Copyright 1995 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +typedef struct _krb5_kt_list { + struct _krb5_kt_list *next; + krb5_keytab_entry *entry; +} *krb5_kt_list; + +krb5_error_code ktutil_free_kt_list (krb5_context, krb5_kt_list); + +krb5_error_code ktutil_delete (krb5_context, krb5_kt_list *, int); + +krb5_error_code ktutil_add (krb5_context, + krb5_kt_list *, + char *, + krb5_kvno, + char *, + int); + +krb5_error_code ktutil_read_keytab (krb5_context, + char *, + krb5_kt_list *); + +krb5_error_code ktutil_write_keytab (krb5_context, + krb5_kt_list, + char *); + +krb5_error_code ktutil_read_srvtab (krb5_context, + char *, + krb5_kt_list *); + +void ktutil_add_entry (int, char *[]); + +void ktutil_clear_list (int, char *[]); + +void ktutil_read_v5 (int, char *[]); + +void ktutil_read_v4 (int, char *[]); + +void ktutil_write_v5 (int, char *[]); + +void ktutil_write_v4 (int, char *[]); + +void ktutil_delete_entry (int, char *[]); + +void ktutil_list (int, char *[]); diff --git a/src/kadmin/ktutil/ktutil_ct.ct b/src/kadmin/ktutil/ktutil_ct.ct new file mode 100644 index 000000000000..0c7ccb6894a7 --- /dev/null +++ b/src/kadmin/ktutil/ktutil_ct.ct @@ -0,0 +1,59 @@ +# Copyright 1995 by the Massachusetts Institute of Technology. +# All Rights Reserved. +# +# Export of this software from the United States of America may +# require a specific license from the United States Government. +# It is the responsibility of any person or organization contemplating +# export to obtain such a license before exporting. +# +# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and +# distribute this software and its documentation for any purpose and +# without fee is hereby granted, provided that the above copyright +# notice appear in all copies and that both that copyright notice and +# this permission notice appear in supporting documentation, and that +# the name of M.I.T. not be used in advertising or publicity pertaining +# to distribution of the software without specific, written prior +# permission. Furthermore if you modify this software you must label +# your software as modified software and not distribute it in such a +# fashion that it might be confused with the original M.I.T. software. +# M.I.T. makes no representations about the suitability of +# this software for any purpose. It is provided "as is" without express +# or implied warranty. +# +# +# Command table for ktutil +# + +command_table ktutil_cmds; + +request ktutil_clear_list, "Clear the current keylist.", + clear_list, clear; + +request ktutil_read_v5, "Read a krb5 keytab into the current keylist.", + read_kt, rkt; + +request ktutil_read_v4, "Read a krb4 srvtab into the current keylist.", + read_st, rst; + +request ktutil_write_v5, "Write the current keylist to a krb5 keytab.", + write_kt, wkt; + +request ktutil_write_v4, "Write the current keylist to a krb4 srvtab.", + write_st, wst; + +request ktutil_add_entry, "Add an entry to the current keylist.", + add_entry, addent; + +request ktutil_delete_entry, "Delete an entry from the current keylist.", + delete_entry, delent; + +request ktutil_list, "List the current keylist.", + list, l; + +request ss_list_requests, "List available requests.", + list_requests, lr, "?"; + +request ss_quit, "Exit program.", + quit, exit, q; + +end; diff --git a/src/kadmin/ktutil/ktutil_funcs.c b/src/kadmin/ktutil/ktutil_funcs.c new file mode 100644 index 000000000000..20a348c80582 --- /dev/null +++ b/src/kadmin/ktutil/ktutil_funcs.c @@ -0,0 +1,353 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* kadmin/ktutil/ktutil_funcs.c */ +/* + *(C) Copyright 1995, 1996 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* + * Utility functions for ktutil. + */ + +#include "k5-int.h" +#include "ktutil.h" +#include <string.h> +#include <ctype.h> + +/* + * Free a kt_list + */ +krb5_error_code ktutil_free_kt_list(context, list) + krb5_context context; + krb5_kt_list list; +{ + krb5_kt_list lp, prev; + krb5_error_code retval = 0; + + for (lp = list; lp;) { + retval = krb5_kt_free_entry(context, lp->entry); + free(lp->entry); + if (retval) + break; + prev = lp; + lp = lp->next; + free(prev); + } + return retval; +} + +/* + * Delete a numbered entry in a kt_list. Takes a pointer to a kt_list + * in case head gets deleted. + */ +krb5_error_code ktutil_delete(context, list, idx) + krb5_context context; + krb5_kt_list *list; + int idx; +{ + krb5_kt_list lp, prev; + int i; + + for (lp = *list, i = 1; lp; prev = lp, lp = lp->next, i++) { + if (i == idx) { + if (i == 1) + *list = lp->next; + else + prev->next = lp->next; + lp->next = NULL; + return ktutil_free_kt_list(context, lp); + } + } + return EINVAL; +} + +/* + * Create a new keytab entry and add it to the keytab list. + * Based on the value of use_pass, either prompt the user for a + * password or key. If the keytab list is NULL, allocate a new + * one first. + */ +krb5_error_code ktutil_add(context, list, princ_str, kvno, + enctype_str, use_pass) + krb5_context context; + krb5_kt_list *list; + char *princ_str; + krb5_kvno kvno; + char *enctype_str; + int use_pass; +{ + krb5_keytab_entry *entry; + krb5_kt_list lp = NULL, prev = NULL; + krb5_principal princ; + krb5_enctype enctype; + krb5_timestamp now; + krb5_error_code retval; + krb5_data password, salt; + krb5_keyblock key; + char buf[BUFSIZ]; + char promptstr[1024]; + + char *cp; + int i, tmp; + unsigned int pwsize = BUFSIZ; + + retval = krb5_parse_name(context, princ_str, &princ); + if (retval) + return retval; + /* now unparse in order to get the default realm appended + to princ_str, if no realm was specified */ + retval = krb5_unparse_name(context, princ, &princ_str); + if (retval) + return retval; + retval = krb5_string_to_enctype(enctype_str, &enctype); + if (retval) + return KRB5_BAD_ENCTYPE; + retval = krb5_timeofday(context, &now); + if (retval) + return retval; + + if (*list) { + /* point lp at the tail of the list */ + for (lp = *list; lp->next; lp = lp->next); + } + entry = (krb5_keytab_entry *) malloc(sizeof(krb5_keytab_entry)); + if (!entry) { + return ENOMEM; + } + memset(entry, 0, sizeof(*entry)); + + if (!lp) { /* if list is empty, start one */ + lp = (krb5_kt_list) malloc(sizeof(*lp)); + if (!lp) { + return ENOMEM; + } + } else { + lp->next = (krb5_kt_list) malloc(sizeof(*lp)); + if (!lp->next) { + return ENOMEM; + } + prev = lp; + lp = lp->next; + } + lp->next = NULL; + lp->entry = entry; + + if (use_pass) { + password.length = pwsize; + password.data = (char *) malloc(pwsize); + if (!password.data) { + retval = ENOMEM; + goto cleanup; + } + + snprintf(promptstr, sizeof(promptstr), _("Password for %.1000s"), + princ_str); + retval = krb5_read_password(context, promptstr, NULL, password.data, + &password.length); + if (retval) + goto cleanup; + retval = krb5_principal2salt(context, princ, &salt); + if (retval) + goto cleanup; + retval = krb5_c_string_to_key(context, enctype, &password, + &salt, &key); + if (retval) + goto cleanup; + memset(password.data, 0, password.length); + password.length = 0; + lp->entry->key = key; + } else { + printf(_("Key for %s (hex): "), princ_str); + fgets(buf, BUFSIZ, stdin); + /* + * We need to get rid of the trailing '\n' from fgets. + * If we have an even number of hex digits (as we should), + * write a '\0' over the '\n'. If for some reason we have + * an odd number of hex digits, force an even number of hex + * digits by writing a '0' into the last position (the string + * will still be null-terminated). + */ + buf[strlen(buf) - 1] = strlen(buf) % 2 ? '\0' : '0'; + if (strlen(buf) == 0) { + fprintf(stderr, _("addent: Error reading key.\n")); + retval = 0; + goto cleanup; + } + + lp->entry->key.enctype = enctype; + lp->entry->key.contents = (krb5_octet *) malloc((strlen(buf) + 1) / 2); + if (!lp->entry->key.contents) { + retval = ENOMEM; + goto cleanup; + } + + i = 0; + for (cp = buf; *cp; cp += 2) { + if (!isxdigit((int) cp[0]) || !isxdigit((int) cp[1])) { + fprintf(stderr, _("addent: Illegal character in key.\n")); + retval = 0; + goto cleanup; + } + sscanf(cp, "%02x", &tmp); + lp->entry->key.contents[i++] = (krb5_octet) tmp; + } + lp->entry->key.length = i; + } + lp->entry->principal = princ; + lp->entry->vno = kvno; + lp->entry->timestamp = now; + + if (!*list) + *list = lp; + + return 0; + +cleanup: + if (prev) + prev->next = NULL; + ktutil_free_kt_list(context, lp); + return retval; +} + +/* + * Read in a keytab and append it to list. If list starts as NULL, + * allocate a new one if necessary. + */ +krb5_error_code ktutil_read_keytab(context, name, list) + krb5_context context; + char *name; + krb5_kt_list *list; +{ + krb5_kt_list lp = NULL, tail = NULL, back = NULL; + krb5_keytab kt; + krb5_keytab_entry *entry; + krb5_kt_cursor cursor; + krb5_error_code retval = 0; + + if (*list) { + /* point lp at the tail of the list */ + for (lp = *list; lp->next; lp = lp->next); + back = lp; + } + retval = krb5_kt_resolve(context, name, &kt); + if (retval) + return retval; + retval = krb5_kt_start_seq_get(context, kt, &cursor); + if (retval) + goto close_kt; + for (;;) { + entry = (krb5_keytab_entry *)malloc(sizeof (krb5_keytab_entry)); + if (!entry) { + retval = ENOMEM; + break; + } + memset(entry, 0, sizeof (*entry)); + retval = krb5_kt_next_entry(context, kt, entry, &cursor); + if (retval) + break; + + if (!lp) { /* if list is empty, start one */ + lp = (krb5_kt_list)malloc(sizeof (*lp)); + if (!lp) { + retval = ENOMEM; + break; + } + } else { + lp->next = (krb5_kt_list)malloc(sizeof (*lp)); + if (!lp->next) { + retval = ENOMEM; + break; + } + lp = lp->next; + } + if (!tail) + tail = lp; + lp->next = NULL; + lp->entry = entry; + } + if (entry) + free(entry); + if (retval) { + if (retval == KRB5_KT_END) + retval = 0; + else { + ktutil_free_kt_list(context, tail); + tail = NULL; + if (back) + back->next = NULL; + } + } + if (!*list) + *list = tail; + krb5_kt_end_seq_get(context, kt, &cursor); +close_kt: + krb5_kt_close(context, kt); + return retval; +} + +/* + * Takes a kt_list and writes it to the named keytab. + */ +krb5_error_code ktutil_write_keytab(context, list, name) + krb5_context context; + krb5_kt_list list; + char *name; +{ + krb5_kt_list lp; + krb5_keytab kt; + char ktname[MAXPATHLEN+sizeof("WRFILE:")+1]; + krb5_error_code retval = 0; + int result; + + result = snprintf(ktname, sizeof(ktname), "WRFILE:%s", name); + if (SNPRINTF_OVERFLOW(result, sizeof(ktname))) + return ENAMETOOLONG; + retval = krb5_kt_resolve(context, ktname, &kt); + if (retval) + return retval; + for (lp = list; lp; lp = lp->next) { + retval = krb5_kt_add_entry(context, kt, lp->entry); + if (retval) + break; + } + krb5_kt_close(context, kt); + return retval; +} + +/* + * Read in a named krb4 srvtab and append to list. Allocate new list + * if needed. + */ +krb5_error_code ktutil_read_srvtab(context, name, list) + krb5_context context; + char *name; + krb5_kt_list *list; +{ + char *ktname; + krb5_error_code result; + + if (asprintf(&ktname, "SRVTAB:%s", name) < 0) + return ENOMEM; + result = ktutil_read_keytab(context, ktname, list); + free(ktname); + return result; +} diff --git a/src/kadmin/scripts/inst-hdrs.sh b/src/kadmin/scripts/inst-hdrs.sh new file mode 100755 index 000000000000..242be89e9193 --- /dev/null +++ b/src/kadmin/scripts/inst-hdrs.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +dir=$1; shift +while [ $# -gt 0 ]; do + file=$1 + cmp -s $file $dir/$file + if [ $? != 0 ]; then + echo "+ rm $dir/$file" + rm -f $dir/$file + echo "+ cp $file $dir/$file" + cp $file $dir/$file + fi + shift +done diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in new file mode 100644 index 000000000000..3a013a4d4e15 --- /dev/null +++ b/src/kadmin/server/Makefile.in @@ -0,0 +1,22 @@ +mydir=kadmin$(S)server +BUILDTOP=$(REL)..$(S).. +KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS) + +LOCALINCLUDES = -I$(top_srcdir)/lib/gssapi/generic \ + -I$(top_srcdir)/lib/gssapi/krb5 -I$(BUILDTOP)/lib/gssapi/generic \ + -I$(BUILDTOP)/lib/gssapi/krb5 -I$(top_srcdir)/lib/kadm5/srv + +PROG = kadmind +OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o misc.o ipropd_svc.o +SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c ipropd_svc.c + +all: $(PROG) + +$(PROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB) $(VERTO_DEPLIB) + $(CC_LINK) -o $(PROG) $(OBJS) $(APPUTILS_LIB) $(KADMSRV_LIBS) $(KDB_DEP_LIB) $(KRB5_BASE_LIBS) $(VERTO_LIBS) + +install: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(SERVER_BINDIR)/$(PROG) + +clean: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/server/deps b/src/kadmin/server/deps new file mode 100644 index 000000000000..44311af19af7 --- /dev/null +++ b/src/kadmin/server/deps @@ -0,0 +1,135 @@ +# +# Generated makefile dependencies follow. +# +$(OUTPRE)kadm_rpc_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_internal.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \ + $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ + $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ + $(top_srcdir)/include/net-server.h $(top_srcdir)/include/port-sockets.h \ + $(top_srcdir)/include/socket-utils.h kadm_rpc_svc.c \ + misc.h +$(OUTPRE)server_stubs.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_acl.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/adm_proto.h \ + $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \ + $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \ + $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \ + $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \ + $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + misc.h server_stubs.c +$(OUTPRE)ovsec_kadmd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_acl.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(BUILDTOP)/lib/gssapi/generic/gssapi_err_generic.h \ + $(BUILDTOP)/lib/gssapi/krb5/gssapi_err_krb5.h $(COM_ERR_DEPS) \ + $(VERTO_DEPS) $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_gssapi.h \ + $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \ + $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \ + $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \ + $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \ + $(top_srcdir)/include/iprop.h $(top_srcdir)/include/iprop_hdr.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \ + $(top_srcdir)/include/kdb_kt.h $(top_srcdir)/include/kdb_log.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + $(top_srcdir)/lib/gssapi/generic/gssapiP_generic.h \ + $(top_srcdir)/lib/gssapi/generic/gssapi_ext.h $(top_srcdir)/lib/gssapi/generic/gssapi_generic.h \ + $(top_srcdir)/lib/gssapi/krb5/gssapiP_krb5.h $(top_srcdir)/lib/gssapi/krb5/gssapi_krb5.h \ + misc.h ovsec_kadmd.c +$(OUTPRE)schpw.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \ + $(COM_ERR_DEPS) $(VERTO_DEPS) $(top_srcdir)/include/adm_proto.h \ + $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \ + $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \ + $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \ + $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \ + $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + misc.h schpw.c +$(OUTPRE)misc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \ + $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \ + $(BUILDTOP)/include/kadm5/server_acl.h $(BUILDTOP)/include/kadm5/server_internal.h \ + $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ + $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(VERTO_DEPS) \ + $(top_srcdir)/include/gssrpc/auth.h $(top_srcdir)/include/gssrpc/auth_gss.h \ + $(top_srcdir)/include/gssrpc/auth_unix.h $(top_srcdir)/include/gssrpc/clnt.h \ + $(top_srcdir)/include/gssrpc/rename.h $(top_srcdir)/include/gssrpc/rpc.h \ + $(top_srcdir)/include/gssrpc/rpc_msg.h $(top_srcdir)/include/gssrpc/svc.h \ + $(top_srcdir)/include/gssrpc/svc_auth.h $(top_srcdir)/include/gssrpc/xdr.h \ + $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ + $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ + $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/kdb.h \ + $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \ + $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ + misc.c misc.h +$(OUTPRE)ipropd_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \ + $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/kadm5/admin.h \ + $(BUILDTOP)/include/kadm5/admin_internal.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/kadm5/kadm_rpc.h \ + $(BUILDTOP)/include/kadm5/server_internal.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/include/osconf.h $(COM_ERR_DEPS) $(VERTO_DEPS) \ + $(top_srcdir)/include/adm_proto.h $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/iprop.h \ + $(top_srcdir)/include/iprop_hdr.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/kdb.h \ + $(top_srcdir)/include/kdb_log.h $(top_srcdir)/include/krb5.h \ + $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \ + $(top_srcdir)/lib/gssapi/krb5/gssapi_krb5.h $(top_srcdir)/lib/kadm5/srv/server_acl.h \ + ipropd_svc.c misc.h diff --git a/src/kadmin/server/ipropd_svc.c b/src/kadmin/server/ipropd_svc.c new file mode 100644 index 000000000000..bce668f4221a --- /dev/null +++ b/src/kadmin/server/ipropd_svc.c @@ -0,0 +1,646 @@ +/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* #pragma ident "@(#)ipropd_svc.c 1.2 04/02/20 SMI" */ + + +#include "k5-platform.h" +#include <signal.h> +#include <sys/types.h> +#include <sys/resource.h> /* rlimit */ +#include <syslog.h> + +#include <kadm5/admin.h> +#include <kadm5/kadm_rpc.h> +#include <kadm5/server_internal.h> +#include <server_acl.h> +#include <adm_proto.h> +#include <string.h> +#include <gssapi_krb5.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <kdb_log.h> +#include "misc.h" +#include "osconf.h" + +extern gss_name_t rqst2name(struct svc_req *rqstp); + +extern void *global_server_handle; +extern int nofork; +extern short l_port; +extern char *kdb5_util; +extern char *kprop; +extern char *dump_file; +extern char *kprop_port; + +static char *reply_ok_str = "UPDATE_OK"; +static char *reply_err_str = "UPDATE_ERROR"; +static char *reply_fr_str = "UPDATE_FULL_RESYNC_NEEDED"; +static char *reply_busy_str = "UPDATE_BUSY"; +static char *reply_nil_str = "UPDATE_NIL"; +static char *reply_perm_str = "UPDATE_PERM_DENIED"; +static char *reply_unknown_str = "<UNKNOWN_CODE>"; + +#define LOG_UNAUTH _("Unauthorized request: %s, client=%s, service=%s, addr=%s") +#define LOG_DONE _("Request: %s, %s, %s, client=%s, service=%s, addr=%s") + +#ifdef DPRINT +#undef DPRINT +#endif +#ifdef DEBUG +#define DPRINT(...) \ + do { \ + if (nofork) { \ + fprintf(stderr, __VA_ARGS__); \ + fflush(stderr); \ + } \ + } while (0) +#else +#define DPRINT(...) +#endif + +static void +debprret(char *w, update_status_t ret, kdb_sno_t sno) +{ + switch (ret) { + case UPDATE_OK: + printf("%s: end (OK, sno=%u)\n", + w, sno); + break; + case UPDATE_ERROR: + printf("%s: end (ERROR)\n", w); + break; + case UPDATE_FULL_RESYNC_NEEDED: + printf("%s: end (FR NEEDED)\n", w); + break; + case UPDATE_BUSY: + printf("%s: end (BUSY)\n", w); + break; + case UPDATE_NIL: + printf("%s: end (NIL)\n", w); + break; + case UPDATE_PERM_DENIED: + printf("%s: end (PERM)\n", w); + break; + default: + printf("%s: end (UNKNOWN return code (%d))\n", w, ret); + } +} + +static char * +replystr(update_status_t ret) +{ + switch (ret) { + case UPDATE_OK: + return (reply_ok_str); + case UPDATE_ERROR: + return (reply_err_str); + case UPDATE_FULL_RESYNC_NEEDED: + return (reply_fr_str); + case UPDATE_BUSY: + return (reply_busy_str); + case UPDATE_NIL: + return (reply_nil_str); + case UPDATE_PERM_DENIED: + return (reply_perm_str); + default: + return (reply_unknown_str); + } +} + +/* Returns null on allocation failure. + Regardless of success or failure, frees the input buffer. */ +static char * +buf_to_string(gss_buffer_desc *b) +{ + OM_uint32 min_stat; + char *s = malloc(b->length+1); + + if (s) { + memcpy(s, b->value, b->length); + s[b->length] = 0; + } + (void) gss_release_buffer(&min_stat, b); + return s; +} + +kdb_incr_result_t * +iprop_get_updates_1_svc(kdb_last_t *arg, struct svc_req *rqstp) +{ + static kdb_incr_result_t ret; + char *whoami = "iprop_get_updates_1"; + int kret; + kadm5_server_handle_t handle = global_server_handle; + char *client_name = 0, *service_name = 0; + char obuf[256] = {0}; + + /* default return code */ + ret.ret = UPDATE_ERROR; + + DPRINT("%s: start, last_sno=%lu\n", whoami, + (unsigned long)arg->last_sno); + + if (!handle) { + krb5_klog_syslog(LOG_ERR, + _("%s: server handle is NULL"), + whoami); + goto out; + } + + { + gss_buffer_desc client_desc, service_desc; + + if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) { + krb5_klog_syslog(LOG_ERR, + _("%s: setup_gss_names failed"), + whoami); + goto out; + } + client_name = buf_to_string(&client_desc); + service_name = buf_to_string(&service_desc); + if (client_name == NULL || service_name == NULL) { + krb5_klog_syslog(LOG_ERR, + _("%s: out of memory recording principal names"), + whoami); + goto out; + } + } + + DPRINT("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n", whoami, client_name, + service_name); + + if (!kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_IPROP, + NULL, + NULL)) { + ret.ret = UPDATE_PERM_DENIED; + + DPRINT("%s: PERMISSION DENIED: clprinc=`%s'\n\tsvcprinc=`%s'\n", + whoami, client_name, service_name); + + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami, + client_name, service_name, + client_addr(rqstp->rq_xprt)); + goto out; + } + + kret = ulog_get_entries(handle->context, arg, &ret); + + if (ret.ret == UPDATE_OK) { + (void) snprintf(obuf, sizeof (obuf), + _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=%lu"), + replystr(ret.ret), + (unsigned long)arg->last_sno, + (unsigned long)ret.lastentry.last_sno); + } else { + (void) snprintf(obuf, sizeof (obuf), + _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=N/A"), + replystr(ret.ret), + (unsigned long)arg->last_sno); + } + + DPRINT("%s: request %s %s\n\tclprinc=`%s'\n\tsvcprinc=`%s'\n", + whoami, obuf, + ((kret == 0) ? "success" : error_message(kret)), + client_name, service_name); + + krb5_klog_syslog(LOG_NOTICE, + _("Request: %s, %s, %s, client=%s, service=%s, addr=%s"), + whoami, + obuf, + ((kret == 0) ? "success" : error_message(kret)), + client_name, service_name, + client_addr(rqstp->rq_xprt)); + +out: + if (nofork) + debprret(whoami, ret.ret, ret.lastentry.last_sno); + free(client_name); + free(service_name); + return (&ret); +} + + +/* + * Given a client princ (foo/fqdn@R), copy (in arg cl) the fqdn substring. + * Return arg cl str ptr on success, else NULL. + */ +static char * +getclhoststr(const char *clprinc, char *cl, size_t len) +{ + const char *s, *e; + + if ((s = strchr(clprinc, '/')) == NULL || (e = strchr(++s, '@')) == NULL || + (size_t)(e - s) >= len) + return NULL; + memcpy(cl, s, e - s); + cl[e - s] = '\0'; + return (cl); +} + +static kdb_fullresync_result_t * +ipropx_resync(uint32_t vers, struct svc_req *rqstp) +{ + static kdb_fullresync_result_t ret; + char *ubuf = 0; + char clhost[NI_MAXHOST] = {0}; + int pret, fret; + FILE *p; + kadm5_server_handle_t handle = global_server_handle; + OM_uint32 min_stat; + gss_name_t name = NULL; + char *client_name = NULL, *service_name = NULL; + char *whoami = "iprop_full_resync_1"; + + /* + * vers contains the highest version number the client is + * willing to accept. A client can always accept a lower + * version: the version number is indicated in the dump + * header. + */ + + /* default return code */ + ret.ret = UPDATE_ERROR; + + if (!handle) { + krb5_klog_syslog(LOG_ERR, + _("%s: server handle is NULL"), + whoami); + goto out; + } + + DPRINT("%s: start\n", whoami); + + { + gss_buffer_desc client_desc, service_desc; + + if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) { + DPRINT("%s: setup_gss_names failed\n", whoami); + krb5_klog_syslog(LOG_ERR, + _("%s: setup_gss_names failed"), + whoami); + goto out; + } + client_name = buf_to_string(&client_desc); + service_name = buf_to_string(&service_desc); + if (client_name == NULL || service_name == NULL) { + DPRINT("%s: out of memory\n", whoami); + krb5_klog_syslog(LOG_ERR, + _("%s: out of memory recording principal names"), + whoami); + goto out; + } + } + + DPRINT("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n", + whoami, client_name, service_name); + + if (!kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_IPROP, + NULL, + NULL)) { + ret.ret = UPDATE_PERM_DENIED; + + DPRINT("%s: Permission denied\n", whoami); + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami, + client_name, service_name, + client_addr(rqstp->rq_xprt)); + goto out; + } + + if (!getclhoststr(client_name, clhost, sizeof (clhost))) { + krb5_klog_syslog(LOG_ERR, + _("%s: getclhoststr failed"), + whoami); + goto out; + } + + /* + * Note the -i; modified version of kdb5_util dump format + * to include sno (serial number). This argument is now + * versioned (-i0 for legacy dump format, -i1 for ipropx + * version 1 format, etc). + * + * The -c option ("conditional") causes the dump to dump only if no + * dump already exists or that dump is not in ipropx format, or the + * sno and timestamp in the header of that dump are outside the + * ulog. This allows us to share a single global dump with all + * slaves, since it's OK to share an older dump, as long as its sno + * and timestamp are in the ulog (then the slaves can get the + * subsequent updates very iprop). + */ + if (asprintf(&ubuf, "%s -r %s dump -i%d -c %s", kdb5_util, + handle->params.realm, vers, dump_file) < 0) { + krb5_klog_syslog(LOG_ERR, + _("%s: cannot construct kdb5 util dump string too long; out of memory"), + whoami); + goto out; + } + + /* + * Fork to dump the db and xfer it to the slave. + * (the fork allows parent to return quickly and the child + * acts like a callback to the slave). + */ + fret = fork(); + DPRINT("%s: fork=%d (%d)\n", whoami, fret, getpid()); + + switch (fret) { + case -1: /* error */ + if (nofork) { + perror(whoami); + } + DPRINT("%s: fork failed\n", whoami); + krb5_klog_syslog(LOG_ERR, + _("%s: fork failed: %s"), + whoami, + error_message(errno)); + goto out; + + case 0: /* child */ + DPRINT("%s: run `%s' ...\n", whoami, ubuf); + (void) signal(SIGCHLD, SIG_DFL); + /* run kdb5_util(1M) dump for IProp */ + p = popen(ubuf, "w"); + if (p == NULL) { + krb5_klog_syslog(LOG_ERR, + _("%s: popen failed: %s"), + whoami, error_message(errno)); + _exit(1); + } + pret = pclose(p); + DPRINT("%s: pclose=%d\n", whoami, pret); + if (pret != 0) { + /* XXX popen/pclose may not set errno + properly, and the error could be from the + subprocess anyways. */ + if (nofork) { + perror(whoami); + } + krb5_klog_syslog(LOG_ERR, + _("%s: pclose(popen) failed: %s"), + whoami, + error_message(errno)); + _exit(1); + } + + if (kprop_port != NULL) { + DPRINT("%s: exec `kprop -r %s -f %s -P %s %s' ...\n", + whoami, handle->params.realm, dump_file, kprop_port, + clhost); + pret = execl(kprop, "kprop", "-r", handle->params.realm, "-f", + dump_file, "-P", kprop_port, clhost, NULL); + } else { + DPRINT("%s: exec `kprop -r %s -f %s %s' ...\n", + whoami, handle->params.realm, dump_file, clhost); + pret = execl(kprop, "kprop", "-r", handle->params.realm, "-f", + dump_file, clhost, NULL); + } + perror(whoami); + krb5_klog_syslog(LOG_ERR, + _("%s: exec failed: %s"), + whoami, + error_message(errno)); + _exit(1); + + default: /* parent */ + ret.ret = UPDATE_OK; + /* not used by slave (sno is retrieved from kdb5_util dump) */ + ret.lastentry.last_sno = 0; + ret.lastentry.last_time.seconds = 0; + ret.lastentry.last_time.useconds = 0; + + DPRINT("%s: spawned resync process %d, client=%s, " + "service=%s, addr=%s\n", whoami, fret, client_name, + service_name, client_addr(rqstp->rq_xprt)); + krb5_klog_syslog(LOG_NOTICE, + _("Request: %s, spawned resync process %d, client=%s, service=%s, addr=%s"), + whoami, fret, + client_name, service_name, + client_addr(rqstp->rq_xprt)); + + goto out; + } + +out: + if (nofork) + debprret(whoami, ret.ret, 0); + free(client_name); + free(service_name); + if (name) + gss_release_name(&min_stat, &name); + free(ubuf); + return (&ret); +} + +kdb_fullresync_result_t * +iprop_full_resync_1_svc(/* LINTED */ void *argp, struct svc_req *rqstp) +{ + return ipropx_resync(IPROPX_VERSION_0, rqstp); +} + +kdb_fullresync_result_t * +iprop_full_resync_ext_1_svc(uint32_t *argp, struct svc_req *rqstp) +{ + return ipropx_resync(*argp, rqstp); +} + +static int +check_iprop_rpcsec_auth(struct svc_req *rqstp) +{ + /* XXX Since the client can authenticate against any principal in + the database, we need to do a sanity check. Only checking for + "kiprop" now, but that means theoretically the client could be + authenticating to kiprop on some other machine. */ + /* Code taken from kadm_rpc_svc.c, tweaked. */ + + gss_ctx_id_t ctx; + krb5_context kctx; + OM_uint32 maj_stat, min_stat; + gss_name_t name; + krb5_principal princ; + int ret, success; + krb5_data *c1, *realm; + gss_buffer_desc gss_str; + kadm5_server_handle_t handle; + size_t slen; + char *sdots; + + success = 0; + handle = (kadm5_server_handle_t)global_server_handle; + + if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) + return 0; + + ctx = rqstp->rq_svccred; + + maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name, + NULL, NULL, NULL, NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) { + krb5_klog_syslog(LOG_ERR, + _("check_rpcsec_auth: failed inquire_context, " + "stat=%u"), maj_stat); + log_badauth(maj_stat, min_stat, rqstp->rq_xprt, NULL); + goto fail_name; + } + + kctx = handle->context; + ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str); + if (ret == 0) + goto fail_name; + + slen = gss_str.length; + trunc_name(&slen, &sdots); + /* + * Since we accept with GSS_C_NO_NAME, the client can authenticate + * against the entire kdb. Therefore, ensure that the service + * name is something reasonable. + */ + if (krb5_princ_size(kctx, princ) != 2) + goto fail_princ; + + c1 = krb5_princ_component(kctx, princ, 0); + realm = krb5_princ_realm(kctx, princ); + if (strncmp(handle->params.realm, realm->data, realm->length) == 0 + && strncmp("kiprop", c1->data, c1->length) == 0) { + success = 1; + } + +fail_princ: + if (!success) { + krb5_klog_syslog(LOG_ERR, _("bad service principal %.*s%s"), + (int) slen, (char *) gss_str.value, sdots); + } + gss_release_buffer(&min_stat, &gss_str); + krb5_free_principal(kctx, princ); +fail_name: + gss_release_name(&min_stat, &name); + return success; +} + +void +krb5_iprop_prog_1(struct svc_req *rqstp, + register SVCXPRT *transp) +{ + union { + kdb_last_t iprop_get_updates_1_arg; + } argument; + char *result; + bool_t (*_xdr_argument)(), (*_xdr_result)(); + char *(*local)(/* union XXX *, struct svc_req * */); + char *whoami = "krb5_iprop_prog_1"; + + if (!check_iprop_rpcsec_auth(rqstp)) { + krb5_klog_syslog(LOG_ERR, _("authentication attempt failed: %s, RPC " + "authentication flavor %d"), + client_addr(rqstp->rq_xprt), + rqstp->rq_cred.oa_flavor); + svcerr_weakauth(transp); + return; + } + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply(transp, xdr_void, + (char *)NULL); + return; + + case IPROP_GET_UPDATES: + _xdr_argument = xdr_kdb_last_t; + _xdr_result = xdr_kdb_incr_result_t; + local = (char *(*)()) iprop_get_updates_1_svc; + break; + + case IPROP_FULL_RESYNC: + _xdr_argument = xdr_void; + _xdr_result = xdr_kdb_fullresync_result_t; + local = (char *(*)()) iprop_full_resync_1_svc; + break; + + case IPROP_FULL_RESYNC_EXT: + _xdr_argument = xdr_u_int32; + _xdr_result = xdr_kdb_fullresync_result_t; + local = (char *(*)()) iprop_full_resync_ext_1_svc; + break; + + default: + krb5_klog_syslog(LOG_ERR, + _("RPC unknown request: %d (%s)"), + rqstp->rq_proc, whoami); + svcerr_noproc(transp); + return; + } + (void) memset(&argument, 0, sizeof (argument)); + if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) { + krb5_klog_syslog(LOG_ERR, + _("RPC svc_getargs failed (%s)"), + whoami); + svcerr_decode(transp); + return; + } + result = (*local)(&argument, rqstp); + + if (_xdr_result && result != NULL && + !svc_sendreply(transp, _xdr_result, result)) { + krb5_klog_syslog(LOG_ERR, + _("RPC svc_sendreply failed (%s)"), + whoami); + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) { + krb5_klog_syslog(LOG_ERR, + _("RPC svc_freeargs failed (%s)"), + whoami); + + exit(1); + } + + if (rqstp->rq_proc == IPROP_GET_UPDATES) { + /* LINTED */ + kdb_incr_result_t *r = (kdb_incr_result_t *)result; + + if (r->ret == UPDATE_OK) { + ulog_free_entries(r->updates.kdb_ulog_t_val, + r->updates.kdb_ulog_t_len); + r->updates.kdb_ulog_t_val = NULL; + r->updates.kdb_ulog_t_len = 0; + } + } + +} + +#if 0 +/* + * Get the host base service name for the kiprop principal. Returns + * KADM5_OK on success. Caller must free the storage allocated for + * host_service_name. + */ +kadm5_ret_t +kiprop_get_adm_host_srv_name(krb5_context context, + const char *realm, + char **host_service_name) +{ + kadm5_ret_t ret; + char *name; + char *host; + + if (ret = kadm5_get_master(context, realm, &host)) + return (ret); + + if (asprintf(&name, "%s@%s", KIPROP_SVC_NAME, host) < 0) { + free(host); + return (ENOMEM); + } + free(host); + *host_service_name = name; + + return (KADM5_OK); +} +#endif diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c new file mode 100644 index 000000000000..e43ca0d578d8 --- /dev/null +++ b/src/kadmin/server/kadm_rpc_svc.c @@ -0,0 +1,370 @@ +/* -*- mode: c; c-file-style: "bsd"; indent-tabs-mode: t -*- */ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + */ + +#include <k5-int.h> +#include <gssrpc/rpc.h> +#include <gssapi/gssapi_krb5.h> /* for gss_nt_krb5_name */ +#include <syslog.h> +#include <kadm5/kadm_rpc.h> +#include <krb5.h> +#include <kadm5/admin.h> +#include <adm_proto.h> +#include "misc.h" +#include "kadm5/server_internal.h" + +extern void *global_server_handle; + +static int check_rpcsec_auth(struct svc_req *); + +/* + * Function: kadm_1 + * + * Purpose: RPC proccessing procedure. + * originally generated from rpcgen + * + * Arguments: + * rqstp (input) rpc request structure + * transp (input) rpc transport structure + * (input/output) + * <return value> + * + * Requires: + * Effects: + * Modifies: + */ + +void kadm_1(rqstp, transp) + struct svc_req *rqstp; + register SVCXPRT *transp; +{ + union { + cprinc_arg create_principal_2_arg; + dprinc_arg delete_principal_2_arg; + mprinc_arg modify_principal_2_arg; + rprinc_arg rename_principal_2_arg; + gprinc_arg get_principal_2_arg; + chpass_arg chpass_principal_2_arg; + chrand_arg chrand_principal_2_arg; + cpol_arg create_policy_2_arg; + dpol_arg delete_policy_2_arg; + mpol_arg modify_policy_2_arg; + gpol_arg get_policy_2_arg; + setkey_arg setkey_principal_2_arg; + setv4key_arg setv4key_principal_2_arg; + cprinc3_arg create_principal3_2_arg; + chpass3_arg chpass_principal3_2_arg; + chrand3_arg chrand_principal3_2_arg; + setkey3_arg setkey_principal3_2_arg; + setkey4_arg setkey_principal4_2_arg; + getpkeys_arg get_principal_keys_2_arg; + } argument; + union { + generic_ret gen_ret; + gprinc_ret get_principal_2_ret; + chrand_ret chrand_principal_2_ret; + gpol_ret get_policy_2_ret; + getprivs_ret get_privs_2_ret; + gprincs_ret get_princs_2_ret; + gpols_ret get_pols_2_ret; + chrand_ret chrand_principal3_2_ret; + gstrings_ret get_string_2_ret; + getpkeys_ret get_principal_keys_ret; + } result; + bool_t retval; + bool_t (*xdr_argument)(), (*xdr_result)(); + bool_t (*local)(); + + if (rqstp->rq_cred.oa_flavor != AUTH_GSSAPI && + !check_rpcsec_auth(rqstp)) { + krb5_klog_syslog(LOG_ERR, "Authentication attempt failed: %s, " + "RPC authentication flavor %d", + client_addr(rqstp->rq_xprt), + rqstp->rq_cred.oa_flavor); + svcerr_weakauth(transp); + return; + } + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply(transp, xdr_void, (char *)NULL); + return; + + case CREATE_PRINCIPAL: + xdr_argument = xdr_cprinc_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) create_principal_2_svc; + break; + + case DELETE_PRINCIPAL: + xdr_argument = xdr_dprinc_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) delete_principal_2_svc; + break; + + case MODIFY_PRINCIPAL: + xdr_argument = xdr_mprinc_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) modify_principal_2_svc; + break; + + case RENAME_PRINCIPAL: + xdr_argument = xdr_rprinc_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) rename_principal_2_svc; + break; + + case GET_PRINCIPAL: + xdr_argument = xdr_gprinc_arg; + xdr_result = xdr_gprinc_ret; + local = (bool_t (*)()) get_principal_2_svc; + break; + + case GET_PRINCS: + xdr_argument = xdr_gprincs_arg; + xdr_result = xdr_gprincs_ret; + local = (bool_t (*)()) get_princs_2_svc; + break; + + case CHPASS_PRINCIPAL: + xdr_argument = xdr_chpass_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) chpass_principal_2_svc; + break; + + case SETV4KEY_PRINCIPAL: + xdr_argument = xdr_setv4key_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) setv4key_principal_2_svc; + break; + + case SETKEY_PRINCIPAL: + xdr_argument = xdr_setkey_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) setkey_principal_2_svc; + break; + + case CHRAND_PRINCIPAL: + xdr_argument = xdr_chrand_arg; + xdr_result = xdr_chrand_ret; + local = (bool_t (*)()) chrand_principal_2_svc; + break; + + case CREATE_POLICY: + xdr_argument = xdr_cpol_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) create_policy_2_svc; + break; + + case DELETE_POLICY: + xdr_argument = xdr_dpol_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) delete_policy_2_svc; + break; + + case MODIFY_POLICY: + xdr_argument = xdr_mpol_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) modify_policy_2_svc; + break; + + case GET_POLICY: + xdr_argument = xdr_gpol_arg; + xdr_result = xdr_gpol_ret; + local = (bool_t (*)()) get_policy_2_svc; + break; + + case GET_POLS: + xdr_argument = xdr_gpols_arg; + xdr_result = xdr_gpols_ret; + local = (bool_t (*)()) get_pols_2_svc; + break; + + case GET_PRIVS: + xdr_argument = xdr_u_int32; + xdr_result = xdr_getprivs_ret; + local = (bool_t (*)()) get_privs_2_svc; + break; + + case INIT: + xdr_argument = xdr_u_int32; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) init_2_svc; + break; + + case CREATE_PRINCIPAL3: + xdr_argument = xdr_cprinc3_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) create_principal3_2_svc; + break; + + case CHPASS_PRINCIPAL3: + xdr_argument = xdr_chpass3_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) chpass_principal3_2_svc; + break; + + case CHRAND_PRINCIPAL3: + xdr_argument = xdr_chrand3_arg; + xdr_result = xdr_chrand_ret; + local = (bool_t (*)()) chrand_principal3_2_svc; + break; + + case SETKEY_PRINCIPAL3: + xdr_argument = xdr_setkey3_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) setkey_principal3_2_svc; + break; + + case PURGEKEYS: + xdr_argument = xdr_purgekeys_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) purgekeys_2_svc; + break; + + case GET_STRINGS: + xdr_argument = xdr_gstrings_arg; + xdr_result = xdr_gstrings_ret; + local = (bool_t (*)()) get_strings_2_svc; + break; + + case SET_STRING: + xdr_argument = xdr_sstring_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) set_string_2_svc; + break; + + case SETKEY_PRINCIPAL4: + xdr_argument = xdr_setkey4_arg; + xdr_result = xdr_generic_ret; + local = (bool_t (*)()) setkey_principal4_2_svc; + break; + + case EXTRACT_KEYS: + xdr_argument = xdr_getpkeys_arg; + xdr_result = xdr_getpkeys_ret; + local = (bool_t (*)()) get_principal_keys_2_svc; + break; + + default: + krb5_klog_syslog(LOG_ERR, "Invalid KADM5 procedure number: %s, %d", + client_addr(rqstp->rq_xprt), rqstp->rq_proc); + svcerr_noproc(transp); + return; + } + memset(&argument, 0, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + return; + } + memset(&result, 0, sizeof(result)); + retval = (*local)(&argument, &result, rqstp); + if (retval && !svc_sendreply(transp, xdr_result, (void *)&result)) { + krb5_klog_syslog(LOG_ERR, "WARNING! Unable to send function results, " + "continuing."); + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + krb5_klog_syslog(LOG_ERR, "WARNING! Unable to free arguments, " + "continuing."); + } + if (!svc_freeargs(transp, xdr_result, &result)) { + krb5_klog_syslog(LOG_ERR, "WARNING! Unable to free results, " + "continuing."); + } + return; +} + +static int +check_rpcsec_auth(struct svc_req *rqstp) +{ + gss_ctx_id_t ctx; + krb5_context kctx; + OM_uint32 maj_stat, min_stat; + gss_name_t name; + krb5_principal princ; + int ret, success; + krb5_data *c1, *c2, *realm; + gss_buffer_desc gss_str; + kadm5_server_handle_t handle; + size_t slen; + char *sdots; + + success = 0; + handle = (kadm5_server_handle_t)global_server_handle; + + if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) + return 0; + + ctx = rqstp->rq_svccred; + + maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name, + NULL, NULL, NULL, NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) { + krb5_klog_syslog(LOG_ERR, _("check_rpcsec_auth: failed " + "inquire_context, stat=%u"), maj_stat); + log_badauth(maj_stat, min_stat, rqstp->rq_xprt, NULL); + goto fail_name; + } + + kctx = handle->context; + ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str); + if (ret == 0) + goto fail_name; + + slen = gss_str.length; + trunc_name(&slen, &sdots); + /* + * Since we accept with GSS_C_NO_NAME, the client can authenticate + * against the entire kdb. Therefore, ensure that the service + * name is something reasonable. + */ + if (krb5_princ_size(kctx, princ) != 2) + goto fail_princ; + + c1 = krb5_princ_component(kctx, princ, 0); + c2 = krb5_princ_component(kctx, princ, 1); + realm = krb5_princ_realm(kctx, princ); + success = data_eq_string(*realm, handle->params.realm) && + data_eq_string(*c1, "kadmin") && !data_eq_string(*c2, "history"); + +fail_princ: + if (!success) { + krb5_klog_syslog(LOG_ERR, _("bad service principal %.*s%s"), + (int) slen, (char *) gss_str.value, sdots); + } + gss_release_buffer(&min_stat, &gss_str); + krb5_free_principal(kctx, princ); +fail_name: + gss_release_name(&min_stat, &name); + return success; +} + +int +gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name, + krb5_principal *princ, gss_buffer_t gss_str) +{ + OM_uint32 status, minor_stat; + gss_OID gss_type; + char *str; + int success; + + status = gss_display_name(&minor_stat, gss_name, gss_str, &gss_type); + if ((status != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name)) { + krb5_klog_syslog(LOG_ERR, _("gss_to_krb5_name: failed display_name " + "status %d"), status); + log_badauth(status, minor_stat, rqstp->rq_xprt, NULL); + return 0; + } + str = malloc(gss_str->length +1); + if (str == NULL) + return 0; + *str = '\0'; + + strncat(str, gss_str->value, gss_str->length); + success = (krb5_parse_name(ctx, str, princ) == 0); + free(str); + return success; +} diff --git a/src/kadmin/server/misc.c b/src/kadmin/server/misc.c new file mode 100644 index 000000000000..27a6376af6b1 --- /dev/null +++ b/src/kadmin/server/misc.c @@ -0,0 +1,272 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + */ + +#include <k5-int.h> +#include <kdb.h> +#include <kadm5/server_internal.h> +#include <kadm5/server_acl.h> +#include "misc.h" +#include "net-server.h" + +/* + * Function: chpass_principal_wrapper_3 + * + * Purpose: wrapper to kadm5_chpass_principal that checks to see if + * pw_min_life has been reached. if not it returns an error. + * otherwise it calls kadm5_chpass_principal + * + * Arguments: + * principal (input) krb5_principals whose password we are + * changing + * keepold (input) whether to preserve old keys + * n_ks_tuple (input) the number of key-salt tuples in ks_tuple + * ks_tuple (input) array of tuples indicating the caller's + * requested enctypes/salttypes + * password (input) password we are going to change to. + * <return value> 0 on success error code on failure. + * + * Requires: + * kadm5_init to have been run. + * + * Effects: + * calls kadm5_chpass_principal which changes the kdb and the + * the admin db. + * + */ +kadm5_ret_t +chpass_principal_wrapper_3(void *server_handle, + krb5_principal principal, + krb5_boolean keepold, + int n_ks_tuple, + krb5_key_salt_tuple *ks_tuple, + char *password) +{ + kadm5_ret_t ret; + + ret = check_min_life(server_handle, principal, NULL, 0); + if (ret) + return ret; + + return kadm5_chpass_principal_3(server_handle, principal, + keepold, n_ks_tuple, ks_tuple, + password); +} + + +/* + * Function: randkey_principal_wrapper_3 + * + * Purpose: wrapper to kadm5_randkey_principal which checks the + * password's min. life. + * + * Arguments: + * principal (input) krb5_principal whose password we are + * changing + * keepold (input) whether to preserve old keys + * n_ks_tuple (input) the number of key-salt tuples in ks_tuple + * ks_tuple (input) array of tuples indicating the caller's + * requested enctypes/salttypes + * key (output) new random key + * <return value> 0, error code on error. + * + * Requires: + * kadm5_init needs to be run + * + * Effects: + * calls kadm5_randkey_principal + * + */ +kadm5_ret_t +randkey_principal_wrapper_3(void *server_handle, + krb5_principal principal, + krb5_boolean keepold, + int n_ks_tuple, + krb5_key_salt_tuple *ks_tuple, + krb5_keyblock **keys, int *n_keys) +{ + kadm5_ret_t ret; + + ret = check_min_life(server_handle, principal, NULL, 0); + if (ret) + return ret; + return kadm5_randkey_principal_3(server_handle, principal, + keepold, n_ks_tuple, ks_tuple, + keys, n_keys); +} + +kadm5_ret_t +schpw_util_wrapper(void *server_handle, + krb5_principal client, + krb5_principal target, + krb5_boolean initial_flag, + char *new_pw, char **ret_pw, + char *msg_ret, unsigned int msg_len) +{ + kadm5_ret_t ret; + kadm5_server_handle_t handle = server_handle; + krb5_boolean access_granted; + krb5_boolean self; + + /* + * If no target is explicitly provided, then the target principal + * is the client principal. + */ + if (target == NULL) + target = client; + + /* + * A principal can always change its own password, as long as it + * has an initial ticket and meets the minimum password lifetime + * requirement. + */ + self = krb5_principal_compare(handle->context, client, target); + if (self) { + ret = check_min_life(server_handle, target, msg_ret, msg_len); + if (ret != 0) + return ret; + + access_granted = initial_flag; + } else + access_granted = FALSE; + + if (!access_granted && + kadm5int_acl_check_krb(handle->context, client, + ACL_CHANGEPW, target, NULL)) { + /* + * Otherwise, principals with appropriate privileges can change + * any password + */ + access_granted = TRUE; + } + + if (access_granted) { + ret = kadm5_chpass_principal_util(server_handle, + target, + new_pw, ret_pw, + msg_ret, msg_len); + } else { + ret = KADM5_AUTH_CHANGEPW; + strlcpy(msg_ret, "Unauthorized request", msg_len); + } + + return ret; +} + +kadm5_ret_t +check_min_life(void *server_handle, krb5_principal principal, + char *msg_ret, unsigned int msg_len) +{ + krb5_int32 now; + kadm5_ret_t ret; + kadm5_policy_ent_rec pol; + kadm5_principal_ent_rec princ; + kadm5_server_handle_t handle = server_handle; + + if (msg_ret != NULL) + *msg_ret = '\0'; + + ret = krb5_timeofday(handle->context, &now); + if (ret) + return ret; + + ret = kadm5_get_principal(handle->lhandle, principal, + &princ, KADM5_PRINCIPAL_NORMAL_MASK); + if(ret) + return ret; + if(princ.aux_attributes & KADM5_POLICY) { + /* Look up the policy. If it doesn't exist, treat this principal as if + * it had no policy. */ + if((ret=kadm5_get_policy(handle->lhandle, + princ.policy, &pol)) != KADM5_OK) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return (ret == KADM5_UNK_POLICY) ? 0 : ret; + } + if((now - princ.last_pwd_change) < pol.pw_min_life && + !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + if (msg_ret != NULL) { + time_t until; + char *time_string, *ptr; + const char *errstr; + + until = princ.last_pwd_change + pol.pw_min_life; + + time_string = ctime(&until); + errstr = error_message(CHPASS_UTIL_PASSWORD_TOO_SOON); + + if (strlen(errstr) + strlen(time_string) < msg_len) { + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + snprintf(msg_ret, msg_len, errstr, time_string); + } + } + + (void) kadm5_free_policy_ent(handle->lhandle, &pol); + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return KADM5_PASS_TOOSOON; + } + + ret = kadm5_free_policy_ent(handle->lhandle, &pol); + if (ret) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + } + + return kadm5_free_principal_ent(handle->lhandle, &princ); +} + +#define MAXPRINCLEN 125 + +void +trunc_name(size_t *len, char **dots) +{ + *dots = *len > MAXPRINCLEN ? "..." : ""; + *len = *len > MAXPRINCLEN ? MAXPRINCLEN : *len; +} + +krb5_error_code +make_toolong_error (void *handle, krb5_data **out) +{ + krb5_error errpkt; + krb5_error_code retval; + krb5_data *scratch; + kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle; + + retval = krb5_us_timeofday(server_handle->context, &errpkt.stime, &errpkt.susec); + if (retval) + return retval; + errpkt.error = KRB_ERR_FIELD_TOOLONG; + retval = krb5_build_principal(server_handle->context, &errpkt.server, + strlen(server_handle->params.realm), + server_handle->params.realm, + "kadmin", "changepw", NULL); + if (retval) + return retval; + errpkt.client = NULL; + errpkt.cusec = 0; + errpkt.ctime = 0; + errpkt.text.length = 0; + errpkt.text.data = 0; + errpkt.e_data.length = 0; + errpkt.e_data.data = 0; + scratch = malloc(sizeof(*scratch)); + if (scratch == NULL) + return ENOMEM; + retval = krb5_mk_error(server_handle->context, &errpkt, scratch); + if (retval) { + free(scratch); + return retval; + } + + *out = scratch; + return 0; +} + +krb5_context get_context(void *handle) +{ + kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle; + return server_handle->context; +} diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h new file mode 100644 index 000000000000..ea0fc7d2217a --- /dev/null +++ b/src/kadmin/server/misc.h @@ -0,0 +1,71 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1994 OpenVision Technologies, Inc., All Rights Reserved + * + */ + +#ifndef _MISC_H +#define _MISC_H 1 + +#include "net-server.h" /* for krb5_fulladdr */ + +int +setup_gss_names(struct svc_req *, gss_buffer_desc *, + gss_buffer_desc *); + + +kadm5_ret_t +chpass_principal_wrapper_3(void *server_handle, + krb5_principal principal, + krb5_boolean keepold, + int n_ks_tuple, + krb5_key_salt_tuple *ks_tuple, + char *password); + +kadm5_ret_t +randkey_principal_wrapper_3(void *server_handle, + krb5_principal principal, + krb5_boolean keepold, + int n_ks_tuple, + krb5_key_salt_tuple *ks_tuple, + krb5_keyblock **keys, int *n_keys); + +kadm5_ret_t +schpw_util_wrapper(void *server_handle, krb5_principal client, + krb5_principal target, krb5_boolean initial_flag, + char *new_pw, char **ret_pw, + char *msg_ret, unsigned int msg_len); + +kadm5_ret_t check_min_life(void *server_handle, krb5_principal principal, + char *msg_ret, unsigned int msg_len); + +void kadm_1(struct svc_req *, SVCXPRT *); +void krb5_iprop_prog_1(struct svc_req *, SVCXPRT *); + +void trunc_name(size_t *len, char **dots); + +int +gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name, + krb5_principal *princ, gss_buffer_t gss_str); + + +void reset_db(void); + +void log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data); + +const char *client_addr(SVCXPRT *xprt); + +/* network.c */ +#include "net-server.h" + + +void +krb5_iprop_prog_1(struct svc_req *rqstp, SVCXPRT *transp); + +kadm5_ret_t +kiprop_get_adm_host_srv_name(krb5_context, + const char *, + char **); + + +#endif /* _MISC_H */ diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c new file mode 100644 index 000000000000..a3edd3b00169 --- /dev/null +++ b/src/kadmin/server/ovsec_kadmd.c @@ -0,0 +1,562 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + */ + +/* + * Copyright (C) 1998 by the FundsXpress, INC. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of FundsXpress. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. FundsXpress makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include <k5-platform.h> +#include <errno.h> +#include <locale.h> +#include <stdio.h> +#include <signal.h> +#include <syslog.h> +#include <sys/types.h> +#ifdef _AIX +#include <sys/select.h> +#endif +#include <sys/time.h> +#include <sys/socket.h> +#include <unistd.h> +#include <netinet/in.h> +#include <netdb.h> +#include <gssrpc/rpc.h> +#include <gssapi/gssapi.h> +#include "gssapiP_krb5.h" /* for kg_get_context */ +#include <gssrpc/auth_gssapi.h> +#include <kadm5/admin.h> +#include <kadm5/kadm_rpc.h> +#include <kadm5/server_acl.h> +#include <adm_proto.h> +#include "kdb_kt.h" /* for krb5_ktkdb_set_context */ +#include <string.h> +#include "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */ +#include <kdb_log.h> + +#include "misc.h" + +#if defined(NEED_DAEMON_PROTO) +int daemon(int, int); +#endif + +#define TIMEOUT 15 + +gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; +void *global_server_handle; +int nofork = 0; +char *kdb5_util = KPROPD_DEFAULT_KDB5_UTIL; +char *kprop = KPROPD_DEFAULT_KPROP; +char *dump_file = KPROP_DEFAULT_FILE; +char *kprop_port = NULL; + +static krb5_context context; +static char *progname; + +#ifdef USE_PASSWORD_SERVER +void kadm5_set_use_password_server(void); +#endif + +static void +usage() +{ + fprintf(stderr, _("Usage: kadmind [-x db_args]* [-r realm] [-m] [-nofork] " + "[-port port-number]\n" + "\t\t[-proponly] [-p path-to-kdb5_util] [-F dump-file]\n" + "\t\t[-K path-to-kprop] [-k kprop-port] [-P pid_file]\n" + "\nwhere,\n\t[-x db_args]* - any number of database " + "specific arguments.\n" + "\t\t\tLook at each database documentation for " + "supported arguments\n")); + exit(1); +} + +/* + * Output a message to stderr and the admin server log, and exit with status 1. + * msg should not be punctuated. If code is given, msg should indicate what + * operation was taking place in the present progressive. Otherwise msg should + * be capitalized and should indicate what went wrong. + */ +static void +fail_to_start(krb5_error_code code, const char *msg) +{ + const char *errmsg; + + fprintf(stderr, "%s: ", progname); + if (code) { + errmsg = krb5_get_error_message(context, code); + fprintf(stderr, _("%s: %s while %s, aborting\n"), progname, errmsg, + msg); + krb5_klog_syslog(LOG_ERR, _("%s while %s, aborting\n"), errmsg, msg); + } else { + fprintf(stderr, _("%s: %s, aborting\n"), progname, msg); + krb5_klog_syslog(LOG_ERR, _("%s, aborting"), msg); + } + exit(1); +} + +static int +write_pid_file(const char *pid_file) +{ + FILE *file; + unsigned long pid; + int st1, st2; + + file = fopen(pid_file, "w"); + if (file == NULL) + return errno; + pid = (unsigned long)getpid(); + st1 = (fprintf(file, "%ld\n", pid) < 0) ? errno : 0; + st2 = (fclose(file) == EOF) ? errno : 0; + return st1 ? st1 : st2; +} + +/* Set up the main loop. If proponly is set, don't set up ports for kpasswd or + * kadmin. May set *ctx_out even on error. */ +static krb5_error_code +setup_loop(int proponly, verto_ctx **ctx_out) +{ + krb5_error_code ret; + verto_ctx *ctx; + kadm5_server_handle_t handle = global_server_handle; + + *ctx_out = ctx = loop_init(VERTO_EV_TYPE_SIGNAL); + if (ctx == NULL) + return ENOMEM; + ret = loop_setup_signals(ctx, global_server_handle, NULL); + if (ret) + return ret; + if (!proponly) { + ret = loop_add_udp_address(handle->params.kpasswd_port, + handle->params.kpasswd_listen); + if (ret) + return ret; + ret = loop_add_tcp_address(handle->params.kpasswd_port, + handle->params.kpasswd_listen); + if (ret) + return ret; + ret = loop_add_rpc_service(handle->params.kadmind_port, + handle->params.kadmind_listen, + KADM, KADMVERS, kadm_1); + if (ret) + return ret; + } +#ifndef DISABLE_IPROP + if (handle->params.iprop_enabled) { + ret = loop_add_rpc_service(handle->params.iprop_port, + handle->params.iprop_listen, + KRB5_IPROP_PROG, KRB5_IPROP_VERS, + krb5_iprop_prog_1); + if (ret) + return ret; + } +#endif + return loop_setup_network(ctx, global_server_handle, progname, + DEFAULT_TCP_LISTEN_BACKLOG); +} + +/* Point GSSAPI at the KDB keytab so we don't need an actual file keytab. */ +static krb5_error_code +setup_kdb_keytab() +{ + krb5_error_code ret; + + ret = krb5_ktkdb_set_context(context); + if (ret) + return ret; + ret = krb5_db_register_keytab(context); + if (ret) + return ret; + return krb5_gss_register_acceptor_identity("KDB:"); +} + + +/* Return "name@realm". */ +static char * +build_princ_name(char *name, char *realm) +{ + char *fullname; + + if (asprintf(&fullname, "%s@%s", name, realm) < 0) + return NULL; + return fullname; +} + +/* Callback from GSSRPC for garbled/forged/replayed/etc messages. */ +static void +log_badverf(gss_name_t client_name, gss_name_t server_name, + struct svc_req *rqst, struct rpc_msg *msg, char *data) +{ + static const struct { + rpcproc_t proc; + const char *proc_name; + } proc_names[] = { + {1, "CREATE_PRINCIPAL"}, + {2, "DELETE_PRINCIPAL"}, + {3, "MODIFY_PRINCIPAL"}, + {4, "RENAME_PRINCIPAL"}, + {5, "GET_PRINCIPAL"}, + {6, "CHPASS_PRINCIPAL"}, + {7, "CHRAND_PRINCIPAL"}, + {8, "CREATE_POLICY"}, + {9, "DELETE_POLICY"}, + {10, "MODIFY_POLICY"}, + {11, "GET_POLICY"}, + {12, "GET_PRIVS"}, + {13, "INIT"}, + {14, "GET_PRINCS"}, + {15, "GET_POLS"}, + {16, "SETKEY_PRINCIPAL"}, + {17, "SETV4KEY_PRINCIPAL"}, + {18, "CREATE_PRINCIPAL3"}, + {19, "CHPASS_PRINCIPAL3"}, + {20, "CHRAND_PRINCIPAL3"}, + {21, "SETKEY_PRINCIPAL3"}, + {22, "PURGEKEYS"}, + {23, "GET_STRINGS"}, + {24, "SET_STRING"} + }; + OM_uint32 minor; + gss_buffer_desc client, server; + gss_OID gss_type; + const char *a; + rpcproc_t proc; + unsigned int i; + const char *procname; + size_t clen, slen; + char *cdots, *sdots; + + client.length = 0; + client.value = NULL; + server.length = 0; + server.value = NULL; + + (void)gss_display_name(&minor, client_name, &client, &gss_type); + (void)gss_display_name(&minor, server_name, &server, &gss_type); + if (client.value == NULL) { + client.value = "(null)"; + clen = sizeof("(null)") - 1; + } else { + clen = client.length; + } + trunc_name(&clen, &cdots); + if (server.value == NULL) { + server.value = "(null)"; + slen = sizeof("(null)") - 1; + } else { + slen = server.length; + } + trunc_name(&slen, &sdots); + a = client_addr(rqst->rq_xprt); + + proc = msg->rm_call.cb_proc; + procname = NULL; + for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) { + if (proc_names[i].proc == proc) { + procname = proc_names[i].proc_name; + break; + } + } + if (procname != NULL) { + krb5_klog_syslog(LOG_NOTICE, + _("WARNING! Forged/garbled request: %s, claimed " + "client = %.*s%s, server = %.*s%s, addr = %s"), + procname, (int)clen, (char *)client.value, cdots, + (int)slen, (char *)server.value, sdots, a); + } else { + krb5_klog_syslog(LOG_NOTICE, + _("WARNING! Forged/garbled request: %d, claimed " + "client = %.*s%s, server = %.*s%s, addr = %s"), + proc, (int)clen, (char *)client.value, cdots, + (int)slen, (char *)server.value, sdots, a); + } + + (void)gss_release_buffer(&minor, &client); + (void)gss_release_buffer(&minor, &server); +} + +/* Callback from GSSRPC for miscellaneous errors */ +static void +log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char *error, char *data) +{ + krb5_klog_syslog(LOG_NOTICE, _("Miscellaneous RPC error: %s, %s"), + client_addr(rqst->rq_xprt), error); +} + +static void +log_badauth_display_status_1(char *m, OM_uint32 code, int type) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc msg; + OM_uint32 msg_ctx; + + msg_ctx = 0; + while (1) { + gssstat = gss_display_status(&minor_stat, code, type, GSS_C_NULL_OID, + &msg_ctx, &msg); + if (gssstat != GSS_S_COMPLETE) { + krb5_klog_syslog(LOG_ERR, _("%s Cannot decode status %d"), m, + (int)code); + return; + } + + krb5_klog_syslog(LOG_NOTICE, "%s %.*s", m, (int)msg.length, + (char *)msg.value); + (void)gss_release_buffer(&minor_stat, &msg); + + if (!msg_ctx) + break; + } +} + +/* Callback from GSSRPC for authentication failures */ +void +log_badauth(OM_uint32 major, OM_uint32 minor, SVCXPRT *xprt, char *data) +{ + krb5_klog_syslog(LOG_NOTICE, _("Authentication attempt failed: %s, " + "GSS-API error strings are:"), + client_addr(xprt)); + log_badauth_display_status_1(" ", major, GSS_C_GSS_CODE); + log_badauth_display_status_1(" ", minor, GSS_C_MECH_CODE); + krb5_klog_syslog(LOG_NOTICE, _(" GSS-API error strings complete.")); +} + +int +main(int argc, char *argv[]) +{ + OM_uint32 minor_status; + gss_buffer_desc in_buf; + gss_OID nt_krb5_name_oid = (gss_OID)GSS_KRB5_NT_PRINCIPAL_NAME; + auth_gssapi_name names[4]; + kadm5_config_params params; + verto_ctx *vctx; + const char *pid_file = NULL; + char **db_args = NULL, **tmpargs; + int ret, i, db_args_size = 0, strong_random = 1, proponly = 0; + + setlocale(LC_ALL, ""); + setvbuf(stderr, NULL, _IONBF, 0); + + names[0].name = names[1].name = names[2].name = names[3].name = NULL; + names[0].type = names[1].type = names[2].type = names[3].type = + nt_krb5_name_oid; + + progname = (strrchr(argv[0], '/') != NULL) ? strrchr(argv[0], '/') + 1 : + argv[0]; + + memset(¶ms, 0, sizeof(params)); + + argc--, argv++; + while (argc) { + if (strcmp(*argv, "-x") == 0) { + argc--, argv++; + if (!argc) + usage(); + db_args_size++; + tmpargs = realloc(db_args, sizeof(char *) * (db_args_size + 1)); + if (tmpargs == NULL) { + fprintf(stderr, _("%s: cannot initialize. Not enough " + "memory\n"), progname); + exit(1); + } + db_args = tmpargs; + db_args[db_args_size - 1] = *argv; + db_args[db_args_size] = NULL; + } else if (strcmp(*argv, "-r") == 0) { + argc--, argv++; + if (!argc) + usage(); + params.realm = *argv; + params.mask |= KADM5_CONFIG_REALM; + argc--, argv++; + continue; + } else if (strcmp(*argv, "-m") == 0) { + params.mkey_from_kbd = 1; + params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + } else if (strcmp(*argv, "-nofork") == 0) { + nofork = 1; +#ifdef USE_PASSWORD_SERVER + } else if (strcmp(*argv, "-passwordserver") == 0) { + kadm5_set_use_password_server(); +#endif +#ifndef DISABLE_IPROP + } else if (strcmp(*argv, "-proponly") == 0) { + proponly = 1; +#endif + } else if (strcmp(*argv, "-port") == 0) { + argc--, argv++; + if (!argc) + usage(); + params.kadmind_port = atoi(*argv); + params.mask |= KADM5_CONFIG_KADMIND_PORT; + } else if (strcmp(*argv, "-P") == 0) { + argc--, argv++; + if (!argc) + usage(); + pid_file = *argv; + } else if (strcmp(*argv, "-W") == 0) { + strong_random = 0; + } else if (strcmp(*argv, "-p") == 0) { + argc--, argv++; + if (!argc) + usage(); + kdb5_util = *argv; + } else if (strcmp(*argv, "-F") == 0) { + argc--, argv++; + if (!argc) + usage(); + dump_file = *argv; + } else if (strcmp(*argv, "-K") == 0) { + argc--, argv++; + if (!argc) + usage(); + kprop = *argv; + } else if (strcmp(*argv, "-k") == 0) { + argc--, argv++; + if (!argc) + usage(); + kprop_port = *argv; + } else { + break; + } + argc--, argv++; + } + + if (argc != 0) + usage(); + + ret = kadm5_init_krb5_context(&context); + if (ret) { + fprintf(stderr, _("%s: %s while initializing context, aborting\n"), + progname, error_message(ret)); + exit(1); + } + + krb5_klog_init(context, "admin_server", progname, 1); + + ret = kadm5_init(context, "kadmind", NULL, NULL, ¶ms, + KADM5_STRUCT_VERSION, KADM5_API_VERSION_4, db_args, + &global_server_handle); + if (ret) + fail_to_start(ret, _("initializing")); + + ret = kadm5_get_config_params(context, 1, ¶ms, ¶ms); + if (ret) + fail_to_start(ret, _("getting config parameters")); + if (!(params.mask & KADM5_CONFIG_REALM)) + fail_to_start(0, _("Missing required realm configuration")); + if (!(params.mask & KADM5_CONFIG_ACL_FILE)) + fail_to_start(0, _("Missing required ACL file configuration")); + + ret = setup_loop(proponly, &vctx); + if (ret) + fail_to_start(ret, _("initializing network")); + + names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm); + names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm); + if (names[0].name == NULL || names[1].name == NULL) + fail_to_start(0, _("Cannot build GSSAPI auth names")); + + ret = setup_kdb_keytab(); + if (ret) + fail_to_start(0, _("Cannot set up KDB keytab")); + + if (svcauth_gssapi_set_names(names, 2) == FALSE) + fail_to_start(0, _("Cannot set GSSAPI authentication names")); + + /* if set_names succeeded, this will too */ + in_buf.value = names[1].name; + in_buf.length = strlen(names[1].name) + 1; + (void)gss_import_name(&minor_status, &in_buf, nt_krb5_name_oid, + &gss_changepw_name); + + svcauth_gssapi_set_log_badauth2_func(log_badauth, NULL); + svcauth_gssapi_set_log_badverf_func(log_badverf, NULL); + svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL); + + svcauth_gss_set_log_badauth2_func(log_badauth, NULL); + svcauth_gss_set_log_badverf_func(log_badverf, NULL); + svcauth_gss_set_log_miscerr_func(log_miscerr, NULL); + + if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE) + fail_to_start(0, _("Cannot initialize GSSAPI service name")); + + ret = kadm5int_acl_init(context, 0, params.acl_file); + if (ret) + fail_to_start(ret, _("initializing ACL file")); + + if (!nofork && daemon(0, 0) != 0) + fail_to_start(errno, _("spawning daemon process")); + if (pid_file != NULL) { + ret = write_pid_file(pid_file); + if (ret) + fail_to_start(ret, _("creating PID file")); + } + + krb5_klog_syslog(LOG_INFO, _("Seeding random number generator")); + ret = krb5_c_random_os_entropy(context, strong_random, NULL); + if (ret) + fail_to_start(ret, _("getting random seed")); + + if (params.iprop_enabled == TRUE) { + ulog_set_role(context, IPROP_MASTER); + + ret = ulog_map(context, params.iprop_logfile, params.iprop_ulogsize); + if (ret) + fail_to_start(ret, _("mapping update log")); + + if (nofork) { + fprintf(stderr, + _("%s: create IPROP svc (PROG=%d, VERS=%d)\n"), + progname, KRB5_IPROP_PROG, KRB5_IPROP_VERS); + } + } + + if (kprop_port == NULL) + kprop_port = getenv("KPROP_PORT"); + + krb5_klog_syslog(LOG_INFO, _("starting")); + if (nofork) + fprintf(stderr, _("%s: starting...\n"), progname); + + verto_run(vctx); + krb5_klog_syslog(LOG_INFO, _("finished, exiting")); + + /* Clean up memory, etc */ + svcauth_gssapi_unset_names(); + kadm5_destroy(global_server_handle); + loop_free(vctx); + kadm5int_acl_finish(context, 0); + (void)gss_release_name(&minor_status, &gss_changepw_name); + (void)gss_release_name(&minor_status, &gss_oldchangepw_name); + for (i = 0; i < 4; i++) + free(names[i].name); + + krb5_klog_close(context); + krb5_free_context(context); + exit(2); +} diff --git a/src/kadmin/server/schpw.c b/src/kadmin/server/schpw.c new file mode 100644 index 000000000000..900adf7a0997 --- /dev/null +++ b/src/kadmin/server/schpw.c @@ -0,0 +1,487 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include "k5-int.h" +#include <kadm5/admin.h> +#include <syslog.h> +#include <adm_proto.h> /* krb5_klog_syslog */ +#include <stdio.h> +#include <errno.h> + +#include "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */ + +#include "misc.h" + +#ifndef GETSOCKNAME_ARG3_TYPE +#define GETSOCKNAME_ARG3_TYPE int +#endif + +#define RFC3244_VERSION 0xff80 + +static krb5_error_code +process_chpw_request(krb5_context context, void *server_handle, char *realm, + krb5_keytab keytab, const krb5_fulladdr *local_faddr, + const krb5_fulladdr *remote_faddr, krb5_data *req, + krb5_data *rep) +{ + krb5_error_code ret; + char *ptr; + unsigned int plen, vno; + krb5_data ap_req, ap_rep = empty_data(); + krb5_data cipher = empty_data(), clear = empty_data(); + krb5_auth_context auth_context = NULL; + krb5_principal changepw = NULL; + krb5_principal client, target = NULL; + krb5_ticket *ticket = NULL; + krb5_replay_data replay; + krb5_error krberror; + int numresult; + char strresult[1024]; + char *clientstr = NULL, *targetstr = NULL; + const char *errmsg = NULL; + size_t clen; + char *cdots; + struct sockaddr_storage ss; + socklen_t salen; + char addrbuf[100]; + krb5_address *addr = remote_faddr->address; + + *rep = empty_data(); + + if (req->length < 4) { + /* either this, or the server is printing bad messages, + or the caller passed in garbage */ + ret = KRB5KRB_AP_ERR_MODIFIED; + numresult = KRB5_KPASSWD_MALFORMED; + strlcpy(strresult, "Request was truncated", sizeof(strresult)); + goto bailout; + } + + ptr = req->data; + + /* verify length */ + + plen = (*ptr++ & 0xff); + plen = (plen<<8) | (*ptr++ & 0xff); + + if (plen != req->length) { + ret = KRB5KRB_AP_ERR_MODIFIED; + numresult = KRB5_KPASSWD_MALFORMED; + strlcpy(strresult, "Request length was inconsistent", + sizeof(strresult)); + goto bailout; + } + + /* verify version number */ + + vno = (*ptr++ & 0xff) ; + vno = (vno<<8) | (*ptr++ & 0xff); + + if (vno != 1 && vno != RFC3244_VERSION) { + ret = KRB5KDC_ERR_BAD_PVNO; + numresult = KRB5_KPASSWD_BAD_VERSION; + snprintf(strresult, sizeof(strresult), + "Request contained unknown protocol version number %d", vno); + goto bailout; + } + + /* read, check ap-req length */ + + ap_req.length = (*ptr++ & 0xff); + ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); + + if (ptr + ap_req.length >= req->data + req->length) { + ret = KRB5KRB_AP_ERR_MODIFIED; + numresult = KRB5_KPASSWD_MALFORMED; + strlcpy(strresult, "Request was truncated in AP-REQ", + sizeof(strresult)); + goto bailout; + } + + /* verify ap_req */ + + ap_req.data = ptr; + ptr += ap_req.length; + + ret = krb5_auth_con_init(context, &auth_context); + if (ret) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, "Failed initializing auth context", + sizeof(strresult)); + goto chpwfail; + } + + ret = krb5_auth_con_setflags(context, auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + if (ret) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, "Failed initializing auth context", + sizeof(strresult)); + goto chpwfail; + } + + ret = krb5_build_principal(context, &changepw, strlen(realm), realm, + "kadmin", "changepw", NULL); + if (ret) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, "Failed building kadmin/changepw principal", + sizeof(strresult)); + goto chpwfail; + } + + ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, + NULL, &ticket); + + if (ret) { + numresult = KRB5_KPASSWD_AUTHERROR; + strlcpy(strresult, "Failed reading application request", + sizeof(strresult)); + goto chpwfail; + } + + /* construct the ap-rep */ + + ret = krb5_mk_rep(context, auth_context, &ap_rep); + if (ret) { + numresult = KRB5_KPASSWD_AUTHERROR; + strlcpy(strresult, "Failed replying to application request", + sizeof(strresult)); + goto chpwfail; + } + + /* decrypt the ChangePasswdData */ + + cipher.length = (req->data + req->length) - ptr; + cipher.data = ptr; + + /* + * Don't set a remote address in auth_context before calling krb5_rd_priv, + * so that we can work against clients behind a NAT. Reflection attacks + * aren't a concern since we use sequence numbers and since our requests + * don't look anything like our responses. Also don't set a local address, + * since we don't know what interface the request was received on. + */ + + ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); + if (ret) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, "Failed decrypting request", sizeof(strresult)); + goto chpwfail; + } + + client = ticket->enc_part2->client; + + /* decode ChangePasswdData for setpw requests */ + if (vno == RFC3244_VERSION) { + krb5_data *clear_data; + + ret = decode_krb5_setpw_req(&clear, &clear_data, &target); + if (ret != 0) { + numresult = KRB5_KPASSWD_MALFORMED; + strlcpy(strresult, "Failed decoding ChangePasswdData", + sizeof(strresult)); + goto chpwfail; + } + + zapfree(clear.data, clear.length); + + clear = *clear_data; + free(clear_data); + + if (target != NULL) { + ret = krb5_unparse_name(context, target, &targetstr); + if (ret != 0) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, "Failed unparsing target name for log", + sizeof(strresult)); + goto chpwfail; + } + } + } + + ret = krb5_unparse_name(context, client, &clientstr); + if (ret) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, "Failed unparsing client name for log", + sizeof(strresult)); + goto chpwfail; + } + + /* for cpw, verify that this is an AS_REQ ticket */ + if (vno == 1 && + (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) { + numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED; + strlcpy(strresult, "Ticket must be derived from a password", + sizeof(strresult)); + goto chpwfail; + } + + /* change the password */ + + ptr = k5memdup0(clear.data, clear.length, &ret); + ret = schpw_util_wrapper(server_handle, client, target, + (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0, + ptr, NULL, strresult, sizeof(strresult)); + if (ret) + errmsg = krb5_get_error_message(context, ret); + + /* zap the password */ + zapfree(clear.data, clear.length); + zapfree(ptr, clear.length); + clear = empty_data(); + + clen = strlen(clientstr); + trunc_name(&clen, &cdots); + + switch (addr->addrtype) { + case ADDRTYPE_INET: { + struct sockaddr_in *sin = ss2sin(&ss); + + sin->sin_family = AF_INET; + memcpy(&sin->sin_addr, addr->contents, addr->length); + sin->sin_port = htons(remote_faddr->port); + salen = sizeof(*sin); + break; + } + case ADDRTYPE_INET6: { + struct sockaddr_in6 *sin6 = ss2sin6(&ss); + + sin6->sin6_family = AF_INET6; + memcpy(&sin6->sin6_addr, addr->contents, addr->length); + sin6->sin6_port = htons(remote_faddr->port); + salen = sizeof(*sin6); + break; + } + default: { + struct sockaddr *sa = ss2sa(&ss); + + sa->sa_family = AF_UNSPEC; + salen = sizeof(*sa); + break; + } + } + + if (getnameinfo(ss2sa(&ss), salen, + addrbuf, sizeof(addrbuf), NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf)); + + if (vno == RFC3244_VERSION) { + size_t tlen; + char *tdots; + const char *targetp; + + if (target == NULL) { + tlen = clen; + tdots = cdots; + targetp = targetstr; + } else { + tlen = strlen(targetstr); + trunc_name(&tlen, &tdots); + targetp = clientstr; + } + + krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for " + "%.*s%s: %s"), addrbuf, (int) clen, + clientstr, cdots, (int) tlen, targetp, tdots, + errmsg ? errmsg : "success"); + } else { + krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"), + addrbuf, (int) clen, clientstr, cdots, + errmsg ? errmsg : "success"); + } + switch (ret) { + case KADM5_AUTH_CHANGEPW: + numresult = KRB5_KPASSWD_ACCESSDENIED; + break; + case KADM5_PASS_Q_TOOSHORT: + case KADM5_PASS_REUSE: + case KADM5_PASS_Q_CLASS: + case KADM5_PASS_Q_DICT: + case KADM5_PASS_Q_GENERIC: + case KADM5_PASS_TOOSOON: + numresult = KRB5_KPASSWD_SOFTERROR; + break; + case 0: + numresult = KRB5_KPASSWD_SUCCESS; + strlcpy(strresult, "", sizeof(strresult)); + break; + default: + numresult = KRB5_KPASSWD_HARDERROR; + break; + } + +chpwfail: + + ret = alloc_data(&clear, 2 + strlen(strresult)); + if (ret) + goto bailout; + + ptr = clear.data; + + *ptr++ = (numresult>>8) & 0xff; + *ptr++ = numresult & 0xff; + + memcpy(ptr, strresult, strlen(strresult)); + + cipher = empty_data(); + + if (ap_rep.length) { + ret = krb5_auth_con_setaddrs(context, auth_context, + local_faddr->address, NULL); + if (ret) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, + "Failed storing client and server internet addresses", + sizeof(strresult)); + } else { + ret = krb5_mk_priv(context, auth_context, &clear, &cipher, + &replay); + if (ret) { + numresult = KRB5_KPASSWD_HARDERROR; + strlcpy(strresult, "Failed encrypting reply", + sizeof(strresult)); + } + } + } + + /* if no KRB-PRIV was constructed, then we need a KRB-ERROR. + if this fails, just bail. there's nothing else we can do. */ + + if (cipher.length == 0) { + /* clear out ap_rep now, so that it won't be inserted in the + reply */ + + if (ap_rep.length) { + free(ap_rep.data); + ap_rep = empty_data(); + } + + krberror.ctime = 0; + krberror.cusec = 0; + krberror.susec = 0; + ret = krb5_timeofday(context, &krberror.stime); + if (ret) + goto bailout; + + /* this is really icky. but it's what all the other callers + to mk_error do. */ + krberror.error = ret; + krberror.error -= ERROR_TABLE_BASE_krb5; + if (krberror.error < 0 || krberror.error > KRB_ERR_MAX) + krberror.error = KRB_ERR_GENERIC; + + krberror.client = NULL; + + ret = krb5_build_principal(context, &krberror.server, + strlen(realm), realm, + "kadmin", "changepw", NULL); + if (ret) + goto bailout; + krberror.text.length = 0; + krberror.e_data = clear; + + ret = krb5_mk_error(context, &krberror, &cipher); + + krb5_free_principal(context, krberror.server); + + if (ret) + goto bailout; + } + + /* construct the reply */ + + ret = alloc_data(rep, 6 + ap_rep.length + cipher.length); + if (ret) + goto bailout; + ptr = rep->data; + + /* length */ + + *ptr++ = (rep->length>>8) & 0xff; + *ptr++ = rep->length & 0xff; + + /* version == 0x0001 big-endian */ + + *ptr++ = 0; + *ptr++ = 1; + + /* ap_rep length, big-endian */ + + *ptr++ = (ap_rep.length>>8) & 0xff; + *ptr++ = ap_rep.length & 0xff; + + /* ap-rep data */ + + if (ap_rep.length) { + memcpy(ptr, ap_rep.data, ap_rep.length); + ptr += ap_rep.length; + } + + /* krb-priv or krb-error */ + + memcpy(ptr, cipher.data, cipher.length); + +bailout: + krb5_auth_con_free(context, auth_context); + krb5_free_principal(context, changepw); + krb5_free_ticket(context, ticket); + free(ap_rep.data); + free(clear.data); + free(cipher.data); + krb5_free_principal(context, target); + krb5_free_unparsed_name(context, targetstr); + krb5_free_unparsed_name(context, clientstr); + krb5_free_error_message(context, errmsg); + return ret; +} + +/* Dispatch routine for set/change password */ +void +dispatch(void *handle, struct sockaddr *local_saddr, + const krb5_fulladdr *remote_faddr, krb5_data *request, int is_tcp, + verto_ctx *vctx, loop_respond_fn respond, void *arg) +{ + krb5_error_code ret; + krb5_keytab kt = NULL; + kadm5_server_handle_t server_handle = (kadm5_server_handle_t)handle; + krb5_fulladdr local_faddr; + krb5_address **local_kaddrs = NULL, local_kaddr_buf; + krb5_data *response = NULL; + + if (local_saddr == NULL) { + ret = krb5_os_localaddr(server_handle->context, &local_kaddrs); + if (ret != 0) + goto egress; + + local_faddr.address = local_kaddrs[0]; + local_faddr.port = 0; + } else { + local_faddr.address = &local_kaddr_buf; + init_addr(&local_faddr, local_saddr); + } + + ret = krb5_kt_resolve(server_handle->context, "KDB:", &kt); + if (ret != 0) { + krb5_klog_syslog(LOG_ERR, _("chpw: Couldn't open admin keytab %s"), + krb5_get_error_message(server_handle->context, ret)); + goto egress; + } + + response = k5alloc(sizeof(krb5_data), &ret); + if (response == NULL) + goto egress; + + ret = process_chpw_request(server_handle->context, + handle, + server_handle->params.realm, + kt, + &local_faddr, + remote_faddr, + request, + response); +egress: + if (ret) + krb5_free_data(server_handle->context, response); + krb5_free_addresses(server_handle->context, local_kaddrs); + krb5_kt_close(server_handle->context, kt); + (*respond)(arg, ret, ret == 0 ? response : NULL); +} diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c new file mode 100644 index 000000000000..86c162590e4b --- /dev/null +++ b/src/kadmin/server/server_stubs.c @@ -0,0 +1,1707 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + */ + +#include <k5-platform.h> +#include <socket-utils.h> +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_krb5.h> /* for gss_nt_krb5_name */ +#include <krb5.h> +#include <kadm5/admin.h> +#include <kadm5/kadm_rpc.h> +#include <kadm5/server_internal.h> +#include <kadm5/server_acl.h> +#include <syslog.h> +#include <adm_proto.h> /* krb5_klog_syslog */ +#include "misc.h" + +extern gss_name_t gss_changepw_name; +extern gss_name_t gss_oldchangepw_name; +extern void * global_server_handle; + +#define CHANGEPW_SERVICE(rqstp) \ + (cmp_gss_names_rel_1(acceptor_name(rqstp->rq_svccred), gss_changepw_name) | \ + (gss_oldchangepw_name && \ + cmp_gss_names_rel_1(acceptor_name(rqstp->rq_svccred), \ + gss_oldchangepw_name))) + + +static int gss_to_krb5_name(kadm5_server_handle_t handle, + gss_name_t gss_name, krb5_principal *princ); + +static int gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str); + +static gss_name_t acceptor_name(gss_ctx_id_t context); + +gss_name_t rqst2name(struct svc_req *rqstp); + +static int cmp_gss_names(gss_name_t n1, gss_name_t n2) +{ + OM_uint32 emin; + int equal; + + if (GSS_ERROR(gss_compare_name(&emin, n1, n2, &equal))) + return(0); + + return(equal); +} + +/* Does a comparison of the names and then releases the first entity */ +/* For use above in CHANGEPW_SERVICE */ +static int cmp_gss_names_rel_1(gss_name_t n1, gss_name_t n2) +{ + OM_uint32 min_stat; + int ret; + + ret = cmp_gss_names(n1, n2); + if (n1) (void) gss_release_name(&min_stat, &n1); + return ret; +} + +/* + * Function check_handle + * + * Purpose: Check a server handle and return a com_err code if it is + * invalid or 0 if it is valid. + * + * Arguments: + * + * handle The server handle. + */ + +static int check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} + +/* + * Function: new_server_handle + * + * Purpose: Constructs a server handle suitable for passing into the + * server library API functions, by folding the client's API version + * and calling principal into the server handle returned by + * kadm5_init. + * + * Arguments: + * api_version (input) The API version specified by the client + * rqstp (input) The RPC request + * handle (output) The returned handle + * <return value> (output) An error code, or 0 if no error occurred + * + * Effects: + * Returns a pointer to allocated storage containing the server + * handle. If an error occurs, then no allocated storage is + * returned, and the return value of the function will be a + * non-zero com_err code. + * + * The allocated storage for the handle should be freed with + * free_server_handle (see below) when it is no longer needed. + */ + +static kadm5_ret_t new_server_handle(krb5_ui_4 api_version, + struct svc_req *rqstp, + kadm5_server_handle_t + *out_handle) +{ + kadm5_server_handle_t handle; + + *out_handle = NULL; + + if (! (handle = (kadm5_server_handle_t) + malloc(sizeof(*handle)))) + return ENOMEM; + + *handle = *(kadm5_server_handle_t)global_server_handle; + handle->api_version = api_version; + + if (! gss_to_krb5_name(handle, rqst2name(rqstp), + &handle->current_caller)) { + free(handle); + return KADM5_FAILURE; + } + + *out_handle = handle; + return 0; +} + +/* + * Function: free_server_handle + * + * Purpose: Free handle memory allocated by new_server_handle + * + * Arguments: + * handle (input/output) The handle to free + */ +static void free_server_handle(kadm5_server_handle_t handle) +{ + if (!handle) + return; + krb5_free_principal(handle->context, handle->current_caller); + free(handle); +} + +/* Result is stored in a static buffer and is invalidated by the next call. */ +const char * +client_addr(SVCXPRT *xprt) +{ + static char abuf[128]; + struct sockaddr_storage ss; + socklen_t len = sizeof(ss); + const char *p = NULL; + + if (getpeername(xprt->xp_sock, ss2sa(&ss), &len) != 0) + return "(unknown)"; + if (ss2sa(&ss)->sa_family == AF_INET) + p = inet_ntop(AF_INET, &ss2sin(&ss)->sin_addr, abuf, sizeof(abuf)); + else if (ss2sa(&ss)->sa_family == AF_INET6) + p = inet_ntop(AF_INET6, &ss2sin6(&ss)->sin6_addr, abuf, sizeof(abuf)); + return (p == NULL) ? "(unknown)" : p; +} + +/* + * Function: setup_gss_names + * + * Purpose: Create printable representations of the client and server + * names. + * + * Arguments: + * rqstp (r) the RPC request + * client_name (w) the gss_buffer_t for the client name + * server_name (w) the gss_buffer_t for the server name + * + * Effects: + * + * Unparses the client and server names into client_name and + * server_name, both of which must be freed by the caller. Returns 0 + * on success and -1 on failure. + */ +int setup_gss_names(struct svc_req *rqstp, + gss_buffer_desc *client_name, + gss_buffer_desc *server_name) +{ + OM_uint32 maj_stat, min_stat; + gss_name_t server_gss_name; + + if (gss_name_to_string(rqst2name(rqstp), client_name) != 0) + return -1; + maj_stat = gss_inquire_context(&min_stat, rqstp->rq_svccred, NULL, + &server_gss_name, NULL, NULL, NULL, + NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) { + gss_release_buffer(&min_stat, client_name); + gss_release_name(&min_stat, &server_gss_name); + return -1; + } + if (gss_name_to_string(server_gss_name, server_name) != 0) { + gss_release_buffer(&min_stat, client_name); + gss_release_name(&min_stat, &server_gss_name); + return -1; + } + gss_release_name(&min_stat, &server_gss_name); + return 0; +} + +static gss_name_t acceptor_name(gss_ctx_id_t context) +{ + OM_uint32 maj_stat, min_stat; + gss_name_t name; + + maj_stat = gss_inquire_context(&min_stat, context, NULL, &name, + NULL, NULL, NULL, NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) + return NULL; + return name; +} + +static int cmp_gss_krb5_name(kadm5_server_handle_t handle, + gss_name_t gss_name, krb5_principal princ) +{ + krb5_principal princ2; + int status; + + if (! gss_to_krb5_name(handle, gss_name, &princ2)) + return 0; + status = krb5_principal_compare(handle->context, princ, princ2); + krb5_free_principal(handle->context, princ2); + return status; +} + +static int gss_to_krb5_name(kadm5_server_handle_t handle, + gss_name_t gss_name, krb5_principal *princ) +{ + OM_uint32 minor_stat; + gss_buffer_desc gss_str; + int success; + char *s; + + if (gss_name_to_string(gss_name, &gss_str) != 0) + return 0; + if (asprintf(&s, "%.*s", (int)gss_str.length, (char *)gss_str.value) < 0) { + gss_release_buffer(&minor_stat, &gss_str); + return 0; + } + success = (krb5_parse_name(handle->context, s, princ) == 0); + free(s); + gss_release_buffer(&minor_stat, &gss_str); + return success; +} + +static int +gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str) +{ + OM_uint32 status, minor_stat; + gss_OID gss_type; + const char pref[] = KRB5_WELLKNOWN_NAMESTR "/" KRB5_ANONYMOUS_PRINCSTR "@"; + const size_t preflen = sizeof(pref) - 1; + + status = gss_display_name(&minor_stat, gss_name, str, &gss_type); + if (status != GSS_S_COMPLETE) + return 1; + if (gss_oid_equal(gss_type, GSS_C_NT_ANONYMOUS)) { + /* Guard against non-krb5 mechs with different anonymous displays. */ + if (str->length < preflen || memcmp(str->value, pref, preflen) != 0) + return 1; + } else if (!gss_oid_equal(gss_type, GSS_KRB5_NT_PRINCIPAL_NAME)) { + return 1; + } + return 0; +} + +/* + * Perform common initialization for server stub functions. A subset of the + * output arguments may be set on failure; the caller is responsible for + * initializing outputs and calling stub_cleanup() on success or failure. + * princ and princ_str_out may be NULL to omit unparsing a principal name. + */ +static kadm5_ret_t +stub_setup(krb5_ui_4 api_version, struct svc_req *rqstp, krb5_principal princ, + kadm5_server_handle_t *handle_out, krb5_ui_4 *api_version_out, + gss_buffer_t client_name_out, gss_buffer_t service_name_out, + char **princ_str_out) +{ + kadm5_ret_t ret; + + ret = new_server_handle(api_version, rqstp, handle_out); + if (ret) + return ret; + + ret = check_handle(*handle_out); + if (ret) + return ret; + + *api_version_out = (*handle_out)->api_version; + + if (setup_gss_names(rqstp, client_name_out, service_name_out) < 0) + return KADM5_FAILURE; + + if (princ_str_out != NULL) { + if (princ == NULL) + return KADM5_BAD_PRINCIPAL; + if (krb5_unparse_name((*handle_out)->context, princ, princ_str_out)) + return KADM5_BAD_PRINCIPAL; + } + + return KADM5_OK; +} + +/* Perform common cleanup for server stub functions. */ +static void +stub_cleanup(kadm5_server_handle_t handle, char *princ_str, + gss_buffer_t client_name, gss_buffer_t service_name) +{ + OM_uint32 minor_stat; + + free_server_handle(handle); + free(princ_str); + gss_release_buffer(&minor_stat, client_name); + gss_release_buffer(&minor_stat, service_name); +} + +static int +log_unauth( + char *op, + char *target, + gss_buffer_t client, + gss_buffer_t server, + struct svc_req *rqstp) +{ + size_t tlen, clen, slen; + char *tdots, *cdots, *sdots; + + tlen = strlen(target); + trunc_name(&tlen, &tdots); + clen = client->length; + trunc_name(&clen, &cdots); + slen = server->length; + trunc_name(&slen, &sdots); + + /* okay to cast lengths to int because trunc_name limits max value */ + return krb5_klog_syslog(LOG_NOTICE, + _("Unauthorized request: %s, %.*s%s, " + "client=%.*s%s, service=%.*s%s, addr=%s"), + op, (int)tlen, target, tdots, + (int)clen, (char *)client->value, cdots, + (int)slen, (char *)server->value, sdots, + client_addr(rqstp->rq_xprt)); +} + +static int +log_done( + char *op, + char *target, + const char *errmsg, + gss_buffer_t client, + gss_buffer_t server, + struct svc_req *rqstp) +{ + size_t tlen, clen, slen; + char *tdots, *cdots, *sdots; + + if (errmsg == NULL) + errmsg = _("success"); + tlen = strlen(target); + trunc_name(&tlen, &tdots); + clen = client->length; + trunc_name(&clen, &cdots); + slen = server->length; + trunc_name(&slen, &sdots); + + /* okay to cast lengths to int because trunc_name limits max value */ + return krb5_klog_syslog(LOG_NOTICE, + _("Request: %s, %.*s%s, %s, " + "client=%.*s%s, service=%.*s%s, addr=%s"), + op, (int)tlen, target, tdots, errmsg, + (int)clen, (char *)client->value, cdots, + (int)slen, (char *)server->value, sdots, + client_addr(rqstp->rq_xprt)); +} + +bool_t +create_principal_2_svc(cprinc_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + restriction_t *rp; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal, + &handle, &ret->api_version, &client_name, + &service_name, &prime_arg); + if (ret->code) + goto exit_func; + + if (CHANGEPW_SERVICE(rqstp) + || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_ADD, + arg->rec.principal, &rp) + || kadm5int_acl_impose_restrictions(handle->context, + &arg->rec, &arg->mask, rp)) { + ret->code = KADM5_AUTH_ADD; + log_unauth("kadm5_create_principal", prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_create_principal(handle, &arg->rec, arg->mask, + arg->passwd); + + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_create_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +create_principal3_2_svc(cprinc3_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + restriction_t *rp; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal, + &handle, &ret->api_version, &client_name, + &service_name, &prime_arg); + if (ret->code) + goto exit_func; + + if (CHANGEPW_SERVICE(rqstp) + || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_ADD, + arg->rec.principal, &rp) + || kadm5int_acl_impose_restrictions(handle->context, + &arg->rec, &arg->mask, rp)) { + ret->code = KADM5_AUTH_ADD; + log_unauth("kadm5_create_principal", prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_create_principal_3(handle, &arg->rec, arg->mask, + arg->n_ks_tuple, arg->ks_tuple, + arg->passwd); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_create_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +/* Return KADM5_PROTECT_KEYS if KRB5_KDB_LOCKDOWN_KEYS is set for princ. */ +static kadm5_ret_t +check_lockdown_keys(kadm5_server_handle_t handle, krb5_principal princ) +{ + kadm5_principal_ent_rec rec; + kadm5_ret_t ret; + + ret = kadm5_get_principal(handle, princ, &rec, KADM5_ATTRIBUTES); + if (ret) + return ret; + ret = (rec.attributes & KRB5_KDB_LOCKDOWN_KEYS) ? KADM5_PROTECT_KEYS : 0; + kadm5_free_principal_ent(handle, &rec); + return ret; +} + +bool_t +delete_principal_2_svc(dprinc_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + if (CHANGEPW_SERVICE(rqstp) + || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_DELETE, + arg->princ, NULL)) { + ret->code = KADM5_AUTH_DELETE; + log_unauth("kadm5_delete_principal", prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_delete_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_DELETE; + } + } + + if (ret->code == KADM5_OK) + ret->code = kadm5_delete_principal(handle, arg->princ); + if (ret->code != KADM5_AUTH_DELETE) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_delete_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + restriction_t *rp; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal, + &handle, &ret->api_version, &client_name, + &service_name, &prime_arg); + if (ret->code) + goto exit_func; + + if (CHANGEPW_SERVICE(rqstp) + || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY, + arg->rec.principal, &rp) + || kadm5int_acl_impose_restrictions(handle->context, + &arg->rec, &arg->mask, rp)) { + ret->code = KADM5_AUTH_MODIFY; + log_unauth("kadm5_modify_principal", prime_arg, + &client_name, &service_name, rqstp); + } else if ((arg->mask & KADM5_ATTRIBUTES) && + (!(arg->rec.attributes & KRB5_KDB_LOCKDOWN_KEYS))) { + ret->code = check_lockdown_keys(handle, arg->rec.principal); + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_modify_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_MODIFY; + } + } + + if (ret->code == KADM5_OK) { + ret->code = kadm5_modify_principal(handle, &arg->rec, arg->mask); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_modify_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +rename_principal_2_svc(rprinc_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg1 = NULL, *prime_arg2 = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + restriction_t *rp; + const char *errmsg = NULL; + size_t tlen1, tlen2, clen, slen; + char *tdots1, *tdots2, *cdots, *sdots; + + ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle, + &ret->api_version, &client_name, &service_name, + NULL); + if (ret->code) + goto exit_func; + + if (krb5_unparse_name(handle->context, arg->src, &prime_arg1) || + krb5_unparse_name(handle->context, arg->dest, &prime_arg2)) { + ret->code = KADM5_BAD_PRINCIPAL; + goto exit_func; + } + tlen1 = strlen(prime_arg1); + trunc_name(&tlen1, &tdots1); + tlen2 = strlen(prime_arg2); + trunc_name(&tlen2, &tdots2); + clen = client_name.length; + trunc_name(&clen, &cdots); + slen = service_name.length; + trunc_name(&slen, &sdots); + + ret->code = KADM5_OK; + if (! CHANGEPW_SERVICE(rqstp)) { + if (!kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_DELETE, arg->src, NULL)) + ret->code = KADM5_AUTH_DELETE; + /* any restrictions at all on the ADD kills the RENAME */ + if (!kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_ADD, arg->dest, &rp) || rp) { + if (ret->code == KADM5_AUTH_DELETE) + ret->code = KADM5_AUTH_INSUFFICIENT; + else + ret->code = KADM5_AUTH_ADD; + } + if (ret->code == KADM5_OK) { + ret->code = check_lockdown_keys(handle, arg->src); + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_rename_principal", prime_arg1, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_DELETE; + } + } + } else + ret->code = KADM5_AUTH_INSUFFICIENT; + if (ret->code != KADM5_OK) { + /* okay to cast lengths to int because trunc_name limits max value */ + krb5_klog_syslog(LOG_NOTICE, + _("Unauthorized request: kadm5_rename_principal, " + "%.*s%s to %.*s%s, " + "client=%.*s%s, service=%.*s%s, addr=%s"), + (int)tlen1, prime_arg1, tdots1, + (int)tlen2, prime_arg2, tdots2, + (int)clen, (char *)client_name.value, cdots, + (int)slen, (char *)service_name.value, sdots, + client_addr(rqstp->rq_xprt)); + } else { + ret->code = kadm5_rename_principal(handle, arg->src, arg->dest); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + /* okay to cast lengths to int because trunc_name limits max value */ + krb5_klog_syslog(LOG_NOTICE, + _("Request: kadm5_rename_principal, " + "%.*s%s to %.*s%s, %s, " + "client=%.*s%s, service=%.*s%s, addr=%s"), + (int)tlen1, prime_arg1, tdots1, + (int)tlen2, prime_arg2, tdots2, + errmsg ? errmsg : _("success"), + (int)clen, (char *)client_name.value, cdots, + (int)slen, (char *)service_name.value, sdots, + client_addr(rqstp->rq_xprt)); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + + } +exit_func: + free(prime_arg1); + free(prime_arg2); + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +get_principal_2_svc(gprinc_arg *arg, gprinc_ret *ret, struct svc_req *rqstp) +{ + char *funcname, *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + funcname = "kadm5_get_principal"; + + if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) && + (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_INQUIRE, + arg->princ, + NULL))) { + ret->code = KADM5_AUTH_GET; + log_unauth(funcname, prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_get_principal(handle, arg->princ, &ret->rec, + arg->mask); + + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done(funcname, prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +get_princs_2_svc(gprincs_arg *arg, gprincs_ret *ret, struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle, + &ret->api_version, &client_name, &service_name, + NULL); + if (ret->code) + goto exit_func; + + prime_arg = arg->exp; + if (prime_arg == NULL) + prime_arg = "*"; + + if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_LIST, + NULL, + NULL)) { + ret->code = KADM5_AUTH_LIST; + log_unauth("kadm5_get_principals", prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_get_principals(handle, arg->exp, &ret->princs, + &ret->count); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_get_principals", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + + } + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +chpass_principal_2_svc(chpass_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code != KADM5_OK) { + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_chpass_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_CHANGEPW; + } + } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) { + ret->code = chpass_principal_wrapper_3(handle, arg->princ, FALSE, 0, + NULL, arg->pass); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_CHANGEPW, arg->princ, NULL)) { + ret->code = kadm5_chpass_principal(handle, arg->princ, arg->pass); + } else { + log_unauth("kadm5_chpass_principal", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_CHANGEPW; + } + + if (ret->code != KADM5_AUTH_CHANGEPW) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_chpass_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +chpass_principal3_2_svc(chpass3_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code != KADM5_OK) { + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_chpass_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_CHANGEPW; + } + } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) { + ret->code = chpass_principal_wrapper_3(handle, arg->princ, + arg->keepold, arg->n_ks_tuple, + arg->ks_tuple, arg->pass); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_CHANGEPW, arg->princ, NULL)) { + ret->code = kadm5_chpass_principal_3(handle, arg->princ, arg->keepold, + arg->n_ks_tuple, arg->ks_tuple, + arg->pass); + } else { + log_unauth("kadm5_chpass_principal", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_CHANGEPW; + } + + if (ret->code != KADM5_AUTH_CHANGEPW) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_chpass_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +setv4key_principal_2_svc(setv4key_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code != KADM5_OK) { + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_setv4key_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_SETKEY, arg->princ, NULL)) { + ret->code = kadm5_setv4key_principal(handle, arg->princ, + arg->keyblock); + } else { + log_unauth("kadm5_setv4key_principal", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + + if (ret->code != KADM5_AUTH_SETKEY) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_setv4key_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + + +bool_t +setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code != KADM5_OK) { + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_setkey_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_SETKEY, arg->princ, NULL)) { + ret->code = kadm5_setkey_principal(handle, arg->princ, arg->keyblocks, + arg->n_keys); + } else { + log_unauth("kadm5_setkey_principal", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + + if (ret->code != KADM5_AUTH_SETKEY) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_setkey_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code != KADM5_OK) { + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_setkey_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_SETKEY, arg->princ, NULL)) { + ret->code = kadm5_setkey_principal_3(handle, arg->princ, arg->keepold, + arg->n_ks_tuple, arg->ks_tuple, + arg->keyblocks, arg->n_keys); + } else { + log_unauth("kadm5_setkey_principal", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + + if (ret->code != KADM5_AUTH_SETKEY) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_setkey_principal", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code != KADM5_OK) { + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_setkey_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_SETKEY, arg->princ, NULL)) { + ret->code = kadm5_setkey_principal_4(handle, arg->princ, arg->keepold, + arg->key_data, arg->n_key_data); + } else { + log_unauth("kadm5_setkey_principal", prime_arg, &client_name, + &service_name, rqstp); + ret->code = KADM5_AUTH_SETKEY; + } + + if (ret->code != KADM5_AUTH_SETKEY) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_setkey_principal", prime_arg, errmsg, &client_name, + &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +/* Empty out *keys/*nkeys if princ is protected with the lockdown attribute, or + * if we fail to check. */ +static kadm5_ret_t +chrand_check_lockdown(kadm5_server_handle_t handle, krb5_principal princ, + krb5_keyblock **keys, int *nkeys) +{ + kadm5_ret_t ret; + int i; + + ret = check_lockdown_keys(handle, princ); + if (!ret) + return 0; + + for (i = 0; i < *nkeys; i++) + krb5_free_keyblock_contents(handle->context, &((*keys)[i])); + free(*keys); + *keys = NULL; + *nkeys = 0; + return (ret == KADM5_PROTECT_KEYS) ? KADM5_OK : ret; +} + +bool_t +chrand_principal_2_svc(chrand_arg *arg, chrand_ret *ret, struct svc_req *rqstp) +{ + char *funcname, *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + krb5_keyblock *k; + int nkeys; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + funcname = "kadm5_randkey_principal"; + + if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) { + ret->code = randkey_principal_wrapper_3(handle, arg->princ, FALSE, 0, + NULL, &k, &nkeys); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_CHANGEPW, arg->princ, NULL)) { + ret->code = kadm5_randkey_principal(handle, arg->princ, &k, &nkeys); + } else { + log_unauth(funcname, prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_CHANGEPW; + } + + if (ret->code == KADM5_OK) { + ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys); + if (ret->code == KADM5_PROTECT_KEYS) + ret->code = KADM5_OK; + ret->keys = k; + ret->n_keys = nkeys; + } + + if (ret->code != KADM5_AUTH_CHANGEPW) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done(funcname, prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret, + struct svc_req *rqstp) +{ + char *funcname, *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + krb5_keyblock *k; + int nkeys; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + funcname = "kadm5_randkey_principal"; + + if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) { + ret->code = randkey_principal_wrapper_3(handle, arg->princ, + arg->keepold, arg->n_ks_tuple, + arg->ks_tuple, &k, &nkeys); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_CHANGEPW, arg->princ, NULL)) { + ret->code = kadm5_randkey_principal_3(handle, arg->princ, arg->keepold, + arg->n_ks_tuple, arg->ks_tuple, + &k, &nkeys); + } else { + log_unauth(funcname, prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_CHANGEPW; + } + + if (ret->code == KADM5_OK) { + ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys); + if (ret->code == KADM5_PROTECT_KEYS) + ret->code = KADM5_OK; + ret->keys = k; + ret->n_keys = nkeys; + } + + if (ret->code != KADM5_AUTH_CHANGEPW) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done(funcname, prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +create_policy_2_svc(cpol_arg *arg, generic_ret *ret, struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle, + &ret->api_version, &client_name, &service_name, + NULL); + if (ret->code) + goto exit_func; + + prime_arg = arg->rec.policy; + + if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_ADD, NULL, NULL)) { + ret->code = KADM5_AUTH_ADD; + log_unauth("kadm5_create_policy", prime_arg, + &client_name, &service_name, rqstp); + + } else { + ret->code = kadm5_create_policy(handle, &arg->rec, arg->mask); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_create_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +delete_policy_2_svc(dpol_arg *arg, generic_ret *ret, struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle, + &ret->api_version, &client_name, &service_name, + NULL); + if (ret->code) + goto exit_func; + + prime_arg = arg->name; + + if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_DELETE, NULL, NULL)) { + log_unauth("kadm5_delete_policy", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_DELETE; + } else { + ret->code = kadm5_delete_policy(handle, arg->name); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_delete_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +modify_policy_2_svc(mpol_arg *arg, generic_ret *ret, struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle, + &ret->api_version, &client_name, &service_name, + NULL); + if (ret->code) + goto exit_func; + + prime_arg = arg->rec.policy; + + if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_MODIFY, NULL, NULL)) { + log_unauth("kadm5_modify_policy", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_MODIFY; + } else { + ret->code = kadm5_modify_policy(handle, &arg->rec, arg->mask); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_modify_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +get_policy_2_svc(gpol_arg *arg, gpol_ret *ret, struct svc_req *rqstp) +{ + char *funcname, *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_ret_t ret2; + kadm5_principal_ent_rec caller_ent; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle, + &ret->api_version, &client_name, &service_name, + NULL); + if (ret->code) + goto exit_func; + + funcname = "kadm5_get_policy"; + + prime_arg = arg->name; + + ret->code = KADM5_AUTH_GET; + if (!CHANGEPW_SERVICE(rqstp) && kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_INQUIRE, NULL, NULL)) + ret->code = KADM5_OK; + else { + ret->code = kadm5_get_principal(handle->lhandle, + handle->current_caller, &caller_ent, + KADM5_PRINCIPAL_NORMAL_MASK); + if (ret->code == KADM5_OK) { + if (caller_ent.aux_attributes & KADM5_POLICY && + strcmp(caller_ent.policy, arg->name) == 0) { + ret->code = KADM5_OK; + } else { + ret->code = KADM5_AUTH_GET; + } + ret2 = kadm5_free_principal_ent(handle->lhandle, + &caller_ent); + ret->code = ret->code ? ret->code : ret2; + } + } + + if (ret->code == KADM5_OK) { + ret->code = kadm5_get_policy(handle, arg->name, &ret->rec); + + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done(funcname, + ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg, + &client_name, &service_name, rqstp); + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + + } else { + log_unauth(funcname, prime_arg, + &client_name, &service_name, rqstp); + } + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +get_pols_2_svc(gpols_arg *arg, gpols_ret *ret, struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle, + &ret->api_version, &client_name, &service_name, + NULL); + if (ret->code) + goto exit_func; + + prime_arg = arg->exp; + if (prime_arg == NULL) + prime_arg = "*"; + + if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_LIST, NULL, NULL)) { + ret->code = KADM5_AUTH_LIST; + log_unauth("kadm5_get_policies", prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_get_policies(handle, arg->exp, &ret->pols, + &ret->count); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_get_policies", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +get_privs_2_svc(krb5_ui_4 *arg, getprivs_ret *ret, struct svc_req *rqstp) +{ + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version, + &client_name, &service_name, NULL); + if (ret->code) + goto exit_func; + + ret->code = kadm5_get_privs(handle, &ret->privs); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_get_privs", client_name.value, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +bool_t +purgekeys_2_svc(purgekeys_arg *arg, generic_ret *ret, struct svc_req *rqstp) +{ + char *funcname, *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + funcname = "kadm5_purgekeys"; + + if (!cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) && + (CHANGEPW_SERVICE(rqstp) + || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY, + arg->princ, NULL))) { + ret->code = KADM5_AUTH_MODIFY; + log_unauth(funcname, prime_arg, &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_purgekeys(handle, arg->princ, arg->keepkvno); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done(funcname, prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +get_strings_2_svc(gstrings_arg *arg, gstrings_ret *ret, struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) && + (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context, + rqst2name(rqstp), + ACL_INQUIRE, + arg->princ, + NULL))) { + ret->code = KADM5_AUTH_GET; + log_unauth("kadm5_get_strings", prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_get_strings(handle, arg->princ, &ret->strings, + &ret->count); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_get_strings", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +set_string_2_svc(sstring_arg *arg, generic_ret *ret, struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + if (CHANGEPW_SERVICE(rqstp) + || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY, + arg->princ, NULL)) { + ret->code = KADM5_AUTH_MODIFY; + log_unauth("kadm5_mod_strings", prime_arg, + &client_name, &service_name, rqstp); + } else { + ret->code = kadm5_set_string(handle, arg->princ, arg->key, arg->value); + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_mod_strings", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} + +bool_t +init_2_svc(krb5_ui_4 *arg, generic_ret *ret, struct svc_req *rqstp) +{ + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + size_t clen, slen; + char *cdots, *sdots; + + ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version, + &client_name, &service_name, NULL); + if (ret->code) + goto exit_func; + + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + clen = client_name.length; + trunc_name(&clen, &cdots); + slen = service_name.length; + trunc_name(&slen, &sdots); + /* okay to cast lengths to int because trunc_name limits max value */ + krb5_klog_syslog(LOG_NOTICE, _("Request: kadm5_init, %.*s%s, %s, " + "client=%.*s%s, service=%.*s%s, addr=%s, " + "vers=%d, flavor=%d"), + (int)clen, (char *)client_name.value, cdots, + errmsg ? errmsg : _("success"), + (int)clen, (char *)client_name.value, cdots, + (int)slen, (char *)service_name.value, sdots, + client_addr(rqstp->rq_xprt), + ret->api_version & ~(KADM5_API_VERSION_MASK), + rqstp->rq_cred.oa_flavor); + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + +exit_func: + stub_cleanup(handle, NULL, &client_name, &service_name); + return TRUE; +} + +gss_name_t +rqst2name(struct svc_req *rqstp) +{ + + if (rqstp->rq_cred.oa_flavor == RPCSEC_GSS) + return rqstp->rq_clntname; + else + return rqstp->rq_clntcred; +} + +bool_t +get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret, + struct svc_req *rqstp) +{ + char *prime_arg = NULL; + gss_buffer_desc client_name = GSS_C_EMPTY_BUFFER; + gss_buffer_desc service_name = GSS_C_EMPTY_BUFFER; + kadm5_server_handle_t handle; + const char *errmsg = NULL; + + ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle, + &ret->api_version, &client_name, &service_name, + &prime_arg); + if (ret->code) + goto exit_func; + + if (!(CHANGEPW_SERVICE(rqstp)) && + kadm5int_acl_check(handle->context, rqst2name(rqstp), + ACL_EXTRACT, arg->princ, NULL)) { + ret->code = kadm5_get_principal_keys(handle, arg->princ, arg->kvno, + &ret->key_data, &ret->n_key_data); + } else { + log_unauth("kadm5_get_principal_keys", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_EXTRACT; + } + + if (ret->code == KADM5_OK) { + ret->code = check_lockdown_keys(handle, arg->princ); + if (ret->code != KADM5_OK) { + kadm5_free_kadm5_key_data(handle->context, ret->n_key_data, + ret->key_data); + ret->key_data = NULL; + ret->n_key_data = 0; + } + if (ret->code == KADM5_PROTECT_KEYS) { + log_unauth("kadm5_get_principal_keys", prime_arg, + &client_name, &service_name, rqstp); + ret->code = KADM5_AUTH_EXTRACT; + } + } + + if (ret->code != KADM5_AUTH_EXTRACT) { + if (ret->code != 0) + errmsg = krb5_get_error_message(handle->context, ret->code); + + log_done("kadm5_get_principal_keys", prime_arg, errmsg, + &client_name, &service_name, rqstp); + + if (errmsg != NULL) + krb5_free_error_message(handle->context, errmsg); + } + +exit_func: + stub_cleanup(handle, prime_arg, &client_name, &service_name); + return TRUE; +} diff --git a/src/kadmin/testing/Makefile.in b/src/kadmin/testing/Makefile.in new file mode 100644 index 000000000000..e5bcc743419c --- /dev/null +++ b/src/kadmin/testing/Makefile.in @@ -0,0 +1,8 @@ +mydir=kadmin$(S)testing +BUILDTOP=$(REL)..$(S).. +SUBDIRS = scripts util + +all: + +clean: + -$(RM) -r krb5-test-root admin_* init-* kadmin_* kdc_rcache.* ovsec-* diff --git a/src/kadmin/testing/deps b/src/kadmin/testing/deps new file mode 100644 index 000000000000..2feac3c9d388 --- /dev/null +++ b/src/kadmin/testing/deps @@ -0,0 +1 @@ +# No dependencies here. diff --git a/src/kadmin/testing/proto/kdc.conf.proto b/src/kadmin/testing/proto/kdc.conf.proto new file mode 100644 index 000000000000..61283ac77550 --- /dev/null +++ b/src/kadmin/testing/proto/kdc.conf.proto @@ -0,0 +1,16 @@ +[kdcdefaults] + kdc_listen = 1750 + kdc_tcp_listen = 1750 + +[realms] + __REALM__ = { + profile = __K5ROOT__/krb5.conf + database_name = __K5ROOT__/kdb5 + key_stash_file = __K5ROOT__/.k5.__REALM__ + acl_file = __K5ROOT__/ovsec_adm.acl + dict_file = __K5ROOT__/ovsec_adm.dict + kadmind_port = 1751 + kpasswd_port = 1752 + master_key_type = des3-hmac-sha1 + supported_enctypes = des3-hmac-sha1:normal des-cbc-crc:normal des-cbc-crc:v4 des-cbc-md5:normal des-cbc-raw:normal + } diff --git a/src/kadmin/testing/proto/krb5.conf.proto b/src/kadmin/testing/proto/krb5.conf.proto new file mode 100644 index 000000000000..00c44297806d --- /dev/null +++ b/src/kadmin/testing/proto/krb5.conf.proto @@ -0,0 +1,31 @@ +[libdefaults] + default_realm = __REALM__ + default_keytab_name = FILE:__K5ROOT__/v5srvtab + dns_fallback = no + plugin_base_dir = __PLUGIN_DIR__ + allow_weak_crypto = true + +[realms] + __REALM__ = { + kdc = __KDCHOST__:1750 + admin_server = __KDCHOST__:1751 + database_module = foobar_db2_module_blah + } + +[domain_realm] + __LOCALHOST__ = __REALM__ + __KDCHOST__ = __REALM__ + +[logging] + admin_server = FILE:__K5ROOT__/syslog + kdc = FILE:__K5ROOT__/syslog + default = FILE:__K5ROOT__/syslog + + +# THIS SHOULD BE IN KDC.CONF INSTEAD! +[dbmodules] + db_module_dir = __MODDIR__ + foobar_db2_module_blah = { + db_library = db2 + database_name = __K5ROOT__/kdb5 + } diff --git a/src/kadmin/testing/proto/ovsec_adm.dict b/src/kadmin/testing/proto/ovsec_adm.dict new file mode 100644 index 000000000000..b54e3a85e105 --- /dev/null +++ b/src/kadmin/testing/proto/ovsec_adm.dict @@ -0,0 +1,3 @@ +Abyssinia +Discordianism +foo diff --git a/src/kadmin/testing/scripts/Makefile.in b/src/kadmin/testing/scripts/Makefile.in new file mode 100644 index 000000000000..8c0d2362a12d --- /dev/null +++ b/src/kadmin/testing/scripts/Makefile.in @@ -0,0 +1,31 @@ +mydir=kadmin$(S)testing$(S)scripts +BUILDTOP=$(REL)..$(S)..$(S).. +PERL_PATH=@PERL_PATH@ + +.SUFFIXES: .plin .pl + +GEN_SCRIPTS = compare_dump.pl make-host-keytab.pl \ + simple_dump.pl verify_xrunner_report.pl qualname.pl + +all: env-setup.sh $(GEN_SCRIPTS) + +# Should only rebuild env_setup.sh here (use CONFIG_FILES=), but the weird krb5 +# makefile post-processing is unconditional and would trash the makefile. +env-setup.sh: env-setup.stamp +env-setup.stamp: $(srcdir)/env-setup.shin $(BUILDTOP)/config.status \ + Makefile + (cd $(BUILDTOP) && \ + CONFIG_FILES=$(mydir)/env-setup.sh:$(mydir)/env-setup.shin $(SHELL) \ + config.status) + chmod +x env-setup.sh + touch env-setup.stamp + +.plin.pl: + -rm -f $@.tmp + echo "#!$(PERL_PATH)" > $@.tmp + sed 1d $< >> $@.tmp + chmod +x $@.tmp + mv $@.tmp $@ + +clean: + -rm -f $(GEN_SCRIPTS) *.tmp env-setup.sh env-setup.stamp diff --git a/src/kadmin/testing/scripts/compare_dump.plin b/src/kadmin/testing/scripts/compare_dump.plin new file mode 100755 index 000000000000..df93df4a0014 --- /dev/null +++ b/src/kadmin/testing/scripts/compare_dump.plin @@ -0,0 +1,242 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +# $debug = 1; + +sub usage { die "usage: $0 before after changes\n";} + +sub unique { + local(@list) = @_; + local(%ary); + + print "unique? ",join(" ",@list),"\n" if $debug; + + foreach (@list) { + return(0) if $ary{$_}++; + } + + 1; +} + +$before = shift(@ARGV) || &usage; +$debug++ if $before =~ /^-d/; +$before = shift(@ARGV) || &usage if $debug; +$after = shift(@ARGV) || &usage; +$changes = shift(@ARGV) || &usage; +@ARGV && &usage; + +%policy = + ( + "FIRST",2, + "pw_min_life",2, + "pw_max_life",3, + "pw_min_length",4, + "pw_min_classes",5, + "pw_history_num",6, + "policy_refcnt",7, + "LAST",7, + ); + +%princ = + ( + "FIRST",2, + "kvno",2, + "mod_name",3, + "max_life",4, + "princ_expire_time",5, + "expiration",5, + "pw_expiration",6, + "attributes",7, + "policy",8, + "aux_attributes",9, + "LAST",9, + ); + +%keytab = + ( + "LAST",-1, + ); + +sub re { # @_ = ($cnt, $line) + local($cnt, $line) = @_; + local(@fields) = split(' ',$line); + + @list = ('\S+') x $cnt; + for $f (@fields[3..$#fields]) { + ($f =~ /=/) || die "Bad field: $f in $_"; + if (!defined($this{$`})) { die "Bad parameter $` in $_"; } + + if (($list[$this{$`}] = $') eq '\S+') { + $list[$this{$`}] = '[^\s]+'; + } + } + + join('\s+',@list)."\$"; +} + +open(CHANGES, $changes) || die "Couldn't open $changes: $!\n"; + +while(<CHANGES>) { + next if s/^\s*\#\#\!\s*\#//; + next if !s/^\s*\#\#\!\s*//; + + split; + + if ($_[1] =~ /princ/) { + %this = %princ; + $this = "princ"; + } elsif ($_[1] =~ /policy/) { + %this = %policy; + $this = "policy"; + } elsif ($_[1] =~ /keytab/) { + %this = %keytab; + $this = $_[1]; + } else { + die "Bad line: $_"; + } + + $cnt = $this{"LAST"}+1; + + if ($_[0] =~ /add/) { + $diff{"+$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /delete/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changefrom/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changeto/) { + $ndiff{"-$this\t$_[2]"} = &re($cnt,$_); + } else { + die "Bad line: $_"; + } +} + +close(CHANGES); + +if ($debug) { + for (keys %diff) { + print " %diff: \"$_\" /$diff{$_}/\n"; + } + + for (keys %ndiff) { + print "%ndiff: \"$_\" /$ndiff{$_}/\n"; + } + + print "\n"; +} + +open(DIFF,"gdiff -u0 $before $after|") || die "Couldn't diff: $!\n"; + +$warnings = 0; + +while(<DIFF>) { + next if /^\+{3}/; + next if /^\-{3}/; + next if /^@@/; + + print "LINE: $_" if $debug; + + split; + + $key = "$_[0]\t$_[1]"; + $re = $diff{$key}; + + delete $diff{$key}; + + print "%diff: \"$key\" /$re/\n" if $debug; + + if (!$re) { + warn "Unexpected: \"$key\"\n"; + $warnings++; + next; + } + + if (!/$re/) { + warn "Failed: $key\n"; + $warnings++; + next; + } + + if ($new = $ndiff{$key}) { + delete $ndiff{$key}; + + @new = split(/\\s\+/, $new); + for ($i=1;$i<@new;$i++) { + print "NEW: $new[$i]\n" if $debug; + + if ($new[$i] ne '\S+') { + $_[$i] = $new[$i]; + } + } + $_[0] =~ s/^\-//; + $key =~ s/^\-/\+/; + + $diff{$key} = join("\t",@_); + } +} + +close(DIFF); + +open(BEFORE, $before) || die "Couldn't open $before: $!\n"; + +while(<BEFORE>) { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(BEFORE); + +open(AFTER, $after) || die "Couldn't open $after: $!\n"; + +while(<AFTER>) { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(AFTER); + +for (keys %diff) { + warn "Unseen: \"$_\" /$diff{$_}/\n"; + $warnings++; +} + +for (keys %ndiff) { + warn "Unseen changes: \"$_\" /$ndiff{$_}/\n"; + $warnings++; +} + +for (keys %ktkeys) { + if (!&unique(split(' ',$ktkeys{$_}))) { + warn "Some keys not unique for $_\n"; + $warnings++; + } +} + +for (keys %kttimes) { + if (!&unique(split(' ',$kttimes{$_}))) { + warn "Some timestamps not unique for $_\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/scripts/deps b/src/kadmin/testing/scripts/deps new file mode 100644 index 000000000000..2feac3c9d388 --- /dev/null +++ b/src/kadmin/testing/scripts/deps @@ -0,0 +1 @@ +# No dependencies here. diff --git a/src/kadmin/testing/scripts/env-setup.shin b/src/kadmin/testing/scripts/env-setup.shin new file mode 100755 index 000000000000..c8d866f153f0 --- /dev/null +++ b/src/kadmin/testing/scripts/env-setup.shin @@ -0,0 +1,111 @@ +#!/bin/sh +# +# The KADM5 unit tests were developed to work under gmake. As a +# result, they expect to inherit a number of environment variables. +# Rather than rewrite the tests, we simply use this script as an +# execution wrapper that sets all the necessary environment variables +# before running the program specified on its command line. +# +# The variable settings all came from OV's config.mk. +# +# Usage: env-setup.sh <command line> +# + +TOP=@RBUILD@/kadmin +STOP=@S_TOP@/kadmin +export TOP +export STOP +# These two may be needed in case $libdir references them. +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ ; eval "libdir=$libdir"; export libdir + +# The shared library run time setup +TOPLIBD=@RBUILD@/lib +PROG_LIBPATH=-L@RBUILD@/lib +BUILDTOP=@RBUILD@ +# XXX kludge! +PROG_RPATH=@RBUILD@/lib +# This converts $(TOPLIBD) to $TOPLIBD +cat > /tmp/env_setup$$ <<\EOF +@KRB5_RUN_ENV@ +EOF + +foo=`sed -e 's/(//g' -e 's/)//g' -e 's/\\\$\\\$/\$/g' /tmp/env_setup$$` +eval $foo +export @KRB5_RUN_VARS@ + +# This will get put in setup.csh for convenience +KRB5_RUN_ENV_CSH=`eval echo "$foo" | \ + sed -e 's/\([^=]*\)=\(.*\)/setenv \1 \2/g'` +export KRB5_RUN_ENV_CSH +rm /tmp/env_setup$$ + +TESTDIR=$TOP/testing; export TESTDIR +STESTDIR=$STOP/testing; export STESTDIR +if [ "$K5ROOT" = "" ]; then + K5ROOT="`cd $TESTDIR; pwd`/krb5-test-root" + export K5ROOT +fi + +# If $VERBOSE_TEST is non-null, enter verbose mode. Set $VERBOSE to +# true or false so its exit status identifies the mode. +if test x$VERBOSE_TEST = x; then + VERBOSE=false +else + VERBOSE=true +fi +export VERBOSE + +REALM=SECURE-TEST.OV.COM; export REALM + +if test x$EXPECT = x; then + EXPECT=@EXPECT@; export EXPECT +fi + +COMPARE_DUMP=$TESTDIR/scripts/compare_dump.pl; export COMPARE_DUMP +INITDB=$STESTDIR/scripts/init_db; export INITDB +MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl; export MAKE_KEYTAB +LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl +export LOCAL_MAKE_KEYTAB +SIMPLE_DUMP=$TESTDIR/scripts/simple_dump.pl; export SIMPLE_DUMP +QUALNAME=$TESTDIR/scripts/qualname.pl; export QUALNAME +TCLUTIL=$STESTDIR/tcl/util.t; export TCLUTIL +BSDDB_DUMP=$TESTDIR/util/bsddb_dump; export BSDDB_DUMP +CLNTTCL=$TESTDIR/util/kadm5_clnt_tcl; export CLNTTCL +SRVTCL=$TESTDIR/util/kadm5_srv_tcl; export SRVTCL + +KRB5_CONFIG=$K5ROOT/krb5.conf; export KRB5_CONFIG +KRB5_KDC_PROFILE=$K5ROOT/kdc.conf; export KRB5_KDC_PROFILE +KRB5_KTNAME=$K5ROOT/ovsec_adm.srvtab; export KRB5_KTNAME +KRB5_CLIENT_KTNAME=$K5ROOT/client_keytab; export KRB5_CLIENT_KTNAME +KRB5CCNAME=$K5ROOT/krb5cc_unit-test; export KRB5CCNAME + +# Make sure we don't get confused by translated messages +# or localized times. +LC_ALL=C; export LC_ALL + +if [ "$TEST_SERVER" != "" ]; then + MAKE_KEYTAB="$MAKE_KEYTAB -server $TEST_SERVER" +fi +if [ "$TEST_PATH" != "" ]; then + MAKE_KEYTAB="$MAKE_KEYTAB -top $TEST_PATH" +fi + +if [ "x$PS_ALL" = "x" ]; then + if ps auxww >/dev/null 2>&1; then + PS_ALL="ps auxww" + PS_PID="ps uwwp" + elif ps -ef >/dev/null 2>&1; then + PS_ALL="ps -ef" + PS_PID="ps -fp" + else + PS_ALL="ps auxww" + PS_PID="ps uwwp" + echo "WARNING! Cannot auto-detect ps type, assuming BSD." + fi + + export PS_ALL PS_PID +fi + +exec ${1+"$@"} diff --git a/src/kadmin/testing/scripts/find-make.sh b/src/kadmin/testing/scripts/find-make.sh new file mode 100755 index 000000000000..904730dfa0da --- /dev/null +++ b/src/kadmin/testing/scripts/find-make.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +POSSIBILITIES=' +/usr/local/bin/gmake +/usr/local/bin/make +' + +for file in $POSSIBILITIES; do + if [ -f $file ]; then + echo $file + exit 0 + fi +done + +echo gmake +echo '$0 could not find make!' 1>&2 +exit 1 + diff --git a/src/kadmin/testing/scripts/init_db b/src/kadmin/testing/scripts/init_db new file mode 100755 index 000000000000..cd71656288cb --- /dev/null +++ b/src/kadmin/testing/scripts/init_db @@ -0,0 +1,232 @@ +#!/bin/sh + +if $VERBOSE; then + REDIRECT= +else + REDIRECT='>/dev/null' +fi + +# Requires that $K5ROOT, /etc/krb.conf, and .k5.$REALM be world-writeable. + +if [ "$TOP" = "" ]; then + echo "init_db: Environment variable \$TOP must point to top of build tree" 1>&2 + exit 1 +fi + +if [ "$STOP" = "" ]; then + echo "init_db: Environment variable \$STOP must point to top of source tree" 1>&2 + exit 1 +fi + +if [ "$libdir" = "" ]; then + echo "init_db: Environment variable \$libdir must point to library install directory" 1>&2 + exit 1 +fi + +IROOT=$TOP/.. +ADMIN=$TOP/dbutil +BIN=$IROOT/bin +ETC=$IROOT/etc +MODDIR=$TOP/../plugins/kdb +SBIN=$TOP/keytab:$TOP/server +DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM + +if [ ! -d $MODDIR ]; then + echo "+++" 1>&2 + echo "+++ Error! $MODDIR does not exist!" 1>&2 + echo "+++ The MODDIR variable should point to the directory in which" 1>&2 + echo "+++ database modules have been installed for testing." 1>&2 + echo "+++" 1>&2 + exit 1 +fi + +DUMMY=${TESTDIR=$TOP/testing}; export TESTDIR +DUMMY=${STESTDIR=$STOP/testing} +DUMMY=${SRVTCL=$TESTDIR/util/kadm5_srv_tcl}; export SRVTCL +DUMMY=${TCLUTIL=$STESTDIR/tcl/util.t}; export TCLUTIL +DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl} + +PATH=$ADMIN:$BIN:$ETC:$SBIN:$PATH; export PATH + +if [ ! -x $SRVTCL ]; then + echo "+++" 1>&2 + echo "+++ Error! $SRVTCL does not exist!" 1>&2 + echo "+++ It was probably not compiled because TCL was not available. If you" 1>&2 + echo "+++ now have TCL installed, cd into that directory, re-run configure" 1>&2 + echo "+++ with the --with-tcl option, and then re-run make." 1>&2 + echo "+++" 1>&2 + + exit 1 +fi + +rm -rf $K5ROOT/* +if [ -d $K5ROOT ]; then + true +else + mkdir $K5ROOT +fi + +# touch $K5ROOT/syslog +# for pid in `$PS_ALL | awk '/syslogd/ && !/awk/ {print $2}'` ; do +# case "$pid" in +# xxx) ;; +# *) +# if $VERBOSE; then $PS_PID$pid | grep -v COMMAND; fi +# kill -1 $pid +# ;; +# esac +# done + +qualname=`$QUALNAME` + +sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \ + -e "s/__KDCHOST__/$qualname/g" \ + -e "s/__LOCALHOST__/$qualname/g" \ + -e "s#__MODDIR__#$MODDIR#g" \ + < $STESTDIR/proto/krb5.conf.proto > $K5ROOT/krb5.conf +sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \ + < $STESTDIR/proto/kdc.conf.proto > $K5ROOT/kdc.conf + +eval kdb5_util -r $REALM create -W -P mrroot -s $REDIRECT || exit 1 + +cp $STESTDIR/proto/ovsec_adm.dict $K5ROOT/ovsec_adm.dict + +cat - > /tmp/init_db$$ <<\EOF +source $env(TCLUTIL) +set r $env(REALM) +if {[info exists env(USER)]} { + set whoami $env(USER) +} else { + set whoami [exec whoami] +} + +set cmds { + {kadm5_init $env(SRVTCL) mrroot null \ + [config_params {KADM5_CONFIG_REALM} $r] $KADM5_STRUCT_VERSION \ + $KADM5_API_VERSION_3 server_handle} + + {kadm5_create_policy $server_handle "test-pol 0 10000 8 2 3 0 2 90 180" \ + {KADM5_POLICY KADM5_PW_MIN_LENGTH KADM5_PW_MIN_CLASSES KADM5_PW_MAX_LIFE KADM5_PW_HISTORY_NUM KADM5_PW_MAX_FAILURE KADM5_PW_FAILURE_COUNT_INTERVAL KADM5_PW_LOCKOUT_DURATION}} + {kadm5_create_policy $server_handle "once-a-min 10 0 0 0 0 0 0 0 0" \ + {KADM5_POLICY KADM5_PW_MIN_LIFE}} + {kadm5_create_policy $server_handle "dict-only 0 0 0 0 0 0 0 0 0" \ + {KADM5_POLICY}} + {kadm5_create_policy $server_handle [simple_policy test-pol-nopw] \ + {KADM5_POLICY}} + + {kadm5_create_principal $server_handle \ + [simple_principal testuser@$r] {KADM5_PRINCIPAL} notathena} + {kadm5_create_principal $server_handle \ + [simple_principal test1@$r] {KADM5_PRINCIPAL} test1} + {kadm5_create_principal $server_handle \ + [simple_principal test2@$r] {KADM5_PRINCIPAL} test2} + {kadm5_create_principal $server_handle \ + [simple_principal test3@$r] {KADM5_PRINCIPAL} test3} + {kadm5_create_principal $server_handle \ + [simple_principal admin@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/get@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/modify@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/delete@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/add@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/none@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/rename@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/mod-add@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/mod-delete@$r] {KADM5_PRINCIPAL} \ + admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/get-add@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/get-delete@$r] {KADM5_PRINCIPAL} \ + admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/get-mod@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/no-add@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [simple_principal admin/no-delete@$r] {KADM5_PRINCIPAL} admin} + {kadm5_create_principal $server_handle \ + [princ_w_pol pol1@$r test-pol] {KADM5_PRINCIPAL \ + KADM5_POLICY} pol111111} + {kadm5_create_principal $server_handle \ + [princ_w_pol pol2@$r once-a-min] {KADM5_PRINCIPAL \ + KADM5_POLICY} pol222222} + {kadm5_create_principal $server_handle \ + [princ_w_pol pol3@$r dict-only] {KADM5_PRINCIPAL \ + KADM5_POLICY} pol333333} + {kadm5_create_principal $server_handle \ + [princ_w_pol admin/get-pol@$r test-pol-nopw] \ + {KADM5_PRINCIPAL KADM5_POLICY} StupidAdmin} + {kadm5_create_principal $server_handle \ + [princ_w_pol admin/pol@$r test-pol-nopw] {KADM5_PRINCIPAL \ + KADM5_POLICY} StupidAdmin} + + {kadm5_create_principal $server_handle \ + [simple_principal changepw/kerberos] \ + {KADM5_PRINCIPAL} {XXX THIS IS WRONG}} + + {kadm5_create_principal $server_handle \ + [simple_principal $whoami] \ + {KADM5_PRINCIPAL} $whoami} + + {kadm5_create_principal $server_handle \ + [simple_principal testkeys@$r] {KADM5_PRINCIPAL} testkeys} + + {kadm5_destroy $server_handle} +} + +foreach cmd $cmds { + if {[catch $cmd output]} { + puts stderr "Error! Command: $cmd\nError: $output" + exit 1 + } else { + puts stdout $output + } +} +EOF +eval "$SRVTCL < /tmp/init_db$$ $REDIRECT" +rm /tmp/init_db$$ + +if [ $? -ne 0 ]; then + echo "Error in $SRVTCL!" 1>&2 + exit 1 +fi + +cat > $K5ROOT/ovsec_adm.acl <<EOF +admin@$REALM admcilse +admin/get@$REALM il +admin/modify@$REALM mc +admin/delete@$REALM d +admin/add@$REALM a +admin/get-pol@$REALM il +admin/rename@$REALM adil +admin/mod-add@$REALM amc +admin/mod-delete@$REALM mcd +admin/get-add@$REALM ail +admin/get-delete@$REALM ild +admin/get-mod@$REALM ilmc +admin/no-add@$REALM mcdil +admin/no-delete@$REALM amcil +changepw/kerberos@$REALM cil + +EOF + +eval $LOCAL_MAKE_KEYTAB -princ kadmin/admin -princ kadmin/changepw -princ ovsec_adm/admin -princ ovsec_adm/changepw $K5ROOT/ovsec_adm.srvtab $REDIRECT + +# Create $K5ROOT/setup.csh to make it easy to run other programs against +# the test db +cat > $K5ROOT/setup.csh <<EOF +setenv KRB5_CONFIG $KRB5_CONFIG +setenv KRB5_KDC_PROFILE $KRB5_KDC_PROFILE +setenv KRB5_KTNAME $KRB5_KTNAME +setenv KRB5_CLIENT_KTNAME $KRB5_CLIENT_KTNAME +$KRB5_RUN_ENV_CSH +EOF + diff --git a/src/kadmin/testing/scripts/make-host-keytab.plin b/src/kadmin/testing/scripts/make-host-keytab.plin new file mode 100755 index 000000000000..dfe0b3a01380 --- /dev/null +++ b/src/kadmin/testing/scripts/make-host-keytab.plin @@ -0,0 +1,144 @@ +#!/usr/local/bin/perl + +$server = undef; +@princs = (); +$top = undef; + +($whoami = $0) =~ s,.*/,,; +$usage = "Usage: $whoami [ -server server ] [ -princ principal ] + [ -top dirname ] [ -verbose ] filename + Server defaults to the local host. + Default principals are host/hostname\@SECURE-TEST.OV.COM and + test/hostname\@SECURE-TEST.OV.COM. + If any principals are specified, the default principals are + not added to the srvtab. + The string \"xCANONHOSTx\" in a principal specification will be + replaced by the canonical host name of the local host."; + +@ORIG_ARGV = @ARGV; + +while (($_ = $ARGV[0]) && /^-/) { + shift; + if (/^-server$/) { + ($server = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-princ$/) { + ($princ = shift) || die "Missing argument to $_ option.\n$usage\n"; + push(@princs, $princ); + } + elsif (/^-top$/) { + ($top = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-verbose$/) { + $verbose++; + } + elsif (/^--$/) { + last; + } + else { + die "Unknown option $_.\n$usage\n"; + } +} + +@princs = ("host/xCANONHOSTx\@SECURE-TEST.OV.COM", + "test/xCANONHOSTx\@SECURE-TEST.OV.COM") + if (! @princs); + +$ktfile = shift(@ARGV) || die "need a keytab file\n"; + +$verbose++ if ($ENV{'VERBOSE_TEST'}); + +print "In $0 @ORIG_ARGV...\n" if ($verbose); + +chop ($canonhost = `hostname`); + +($canonhost,$aliases,$addrtype,$length,@addrs) = gethostbyname($canonhost); +die "couldn't get canonical hostname\n" if !($canonhost && @addrs); +($canonhost2) = gethostbyaddr($addrs[0],$addrtype); +if ($canonhost2) { $canonhost = $canonhost2; } + +for (@princs) { + s/xCANONHOSTx/$canonhost/g; +} + +die "Neither \$TOP nor \$TESTDIR is set, and -top not specified.\n" + if (! ($top || $ENV{'TOP'} || $ENV{'TESTDIR'})); + +$top = $ENV{'TOP'} if (! $top); +$TESTDIR = ($ENV{'TESTDIR'} || "$top/testing"); +$MAKE_KEYTAB = ($ENV{'MAKE_KEYTAB'} || "$TESTDIR/scripts/$whoami"); +$SRVTCL = ($ENV{'SRVTCL'} || "$TESTDIR/util/kadm5_srv_tcl"); +$TCLUTIL = ($ENV{'TCLUTIL'} || "$TESTDIR/tcl/util.t"); +# This'll be wrong sometimes +$RSH_CMD = ($ENV{'RSH_CMD'} || '/usr/ucb/rsh'); +$KADMIN = ($ENV{'KADMIN'} || "$top/cli/kadmin.local"); + +if ($server) { +# XXX Using /usr/ucb/rsh for now. + +# Strip command line options because we're adding our own. + + $MAKE_KEYTAB =~ s/ .*//; + + if ($ENV{'TOP'} && ($top ne $ENV{'TOP'})) { +# Replace the old TOP with the new one where necessary + for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { + eval "\$$_ =~ s/^\$ENV{'TOP'}/\$top/;"; + } + +# Make the paths as short as possible so our command line isn't too long. +# for ('SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$TESTDIR/\\\\\\\$TESTDIR/;"; +# } +# for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$top/\\\\\\\$TOP/;"; +# } + } + + $cmd = "cd $top; \\`testing/scripts/find-make.sh\\` execute TOP=$top "; + $cmd .= "VERBOSE_TEST=$verbose " if ($verbose); + $cmd .= "TESTDIR=$TESTDIR "; + $cmd .= "SRVTCL=$SRVTCL "; + $cmd .= "TCLUTIL=$TCLUTIL "; + + $cmd .= "CMD='$MAKE_KEYTAB "; + for (@princs) { + $cmd .= "-princ $_ "; + } + $cmd .= " /tmp/make-keytab.$canonhost.$$'";#'; + + $cmd = "$RSH_CMD $server -l root -n \"$cmd\""; + + $cmd2 = "$RSH_CMD $server -l root -n \"cat /tmp/make-keytab.$canonhost.$$\" > $ktfile"; + + $cmd3 = "$RSH_CMD $server -l root -n \"rm /tmp/make-keytab.$canonhost.$$\""; + + for ($cmd, $cmd2, $cmd3) { + print "$_\n" if ($verbose); + + system($_) && die "Couldn't run $_: $!.\n"; + } +} +else { + $redirect = "> /dev/null" if (! $verbose); + + # We can ignore errors here, because the ktadd below will fail if + # this fails for any reason other than "principal exists" + for (@princs) { + next if (/^kadmin/); + $cmd = "$KADMIN -q 'ank -randkey $_' $redirect 2>&1"; + system($cmd); + } + + $cmd = "$KADMIN -q 'ktadd -k $ktfile "; + $cmd .= " -q " if (! $verbose); + $cmd .= "@princs' $redirect"; + if (system "$cmd") { + sleep(1); + die "Error in system($cmd)\n"; + } +} + +if (! -f $ktfile) { + die "$ktfile not created.\n"; +} diff --git a/src/kadmin/testing/scripts/qualname.plin b/src/kadmin/testing/scripts/qualname.plin new file mode 100755 index 000000000000..b712d89738eb --- /dev/null +++ b/src/kadmin/testing/scripts/qualname.plin @@ -0,0 +1,19 @@ +#!/afs/athena/contrib/perl/p + +if ($#ARGV == -1) { + chop($hostname = `hostname`); +} else { + $hostname = $ARGV[0]; +} + +if (! (($name,$type,$addr) = (gethostbyname($hostname))[0,2,4])) { + print STDERR "No such host: $hostname\n"; + exit(1); +} +if (! ($qualname = (gethostbyaddr($addr,$type))[0])) { + $qualname = $name; +} + +$qualname =~ tr/A-Z/a-z/; # lowercase our name for keytab use. +print "$qualname\n"; + diff --git a/src/kadmin/testing/scripts/simple_dump.plin b/src/kadmin/testing/scripts/simple_dump.plin new file mode 100755 index 000000000000..ea94ab2d1dde --- /dev/null +++ b/src/kadmin/testing/scripts/simple_dump.plin @@ -0,0 +1,88 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +## ovsec_adm_export format +## [0]"policy" [1]name [2]pw_min_life [3]pw_max_life [4]pw_min_length [5]pw_min_classes [6]pw_history_num [7]policy_refcnt +## [0]"princ" [1]name [2]policy [3]aux_attributes [4]old_key_len [5]admin_history_kvno [6..]old_keys +$oaevers = "1.0"; + +open(SORT, "|sort") || die "Couldn't open pipe to sort for output: $!\n"; + +open(OAE, "$ENV{'TOP'}/install/admin/ovsec_adm_export|") || + die "Couldn't get oae: $!\n"; + +$header = <OAE>; + +die "Not ovsec_adm_export output\n" + if ($header !~ /^OpenV\*Secure V(\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected oae version $oaevers, got $stdinvers instead.\n" + if $stdinvers ne $oaevers; + +while(<OAE>) { + if (/^End of Database/) { + last; + } elsif (/^policy/) { + print SORT; + } elsif (/^princ/) { + split(/\t/); + + $_[2] = "\"\"" if !$_[2]; + + $_[3] = hex("0x".$_[3]); + + $princ{$_[1]} = sprintf("%s\t0x%04x",@_[2,3]); + } +} + +## kdb_edit ddb format +## [0]strlen(principal) [1]strlen(mod_name) [2]key.length [3]alt_key.length [4]salt_length [5]alt_salt_length [6]principal [7]key.key_type [8]key.contents [9]kvno [10]max_life [11]max_renewable_life [12]mkvno [13]expiration [14]pw_expiration [15]last_pwd_change [16]last_success [17]last_failed [18]fail_auth_count [19]mod_name [20]mod_date [21]attributes [22]salt_type [23]salt [24]alt_key.contents [25]alt_salt [26..33]expansion*8; +$ddbvers = "2.0"; + +open(DDB, "$ENV{'TOP'}/install/admin/kdb5_edit -r SECURE-TEST.OV.COM -R ddb|") || + die "Couldn't get ddb: $!\n"; + +$header = <DDB>; + +die "Not a kdb5_edit ddb\n" + if ($header !~ /^kdb5_edit load_dump version (\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected ddb version $ddbvers, got $stdinvers instead.\n" + if $stdinvers ne $ddbvers; + +## [6]principal [9]kvno [19]mod_name [10]max_life [13]expiration [14]pw_expiration [21]attributes // [2]policy [3]aux_attributes + +while(<DDB>) { + split; + + print SORT join("\t","princ",(@_)[6,9,19,10,13,14], + sprintf("0x%04x",$_[21]), + $princ{$_[6]}),"\n"; +} + +close(DDB); + +for $keytab (@ARGV) { + open(KLIST, "$ENV{'TOP'}/install/bin/klist -k -t -K FILE:$keytab|") || + die "Couldn't list $keytab: $!\n"; + + $dummy = <KLIST>; + $dummy = <KLIST>; + $dummy = <KLIST>; + + while(<KLIST>) { + s/^\s+//; + split; + printf(SORT "keytab:FILE:%s\t%s-%s\t%s\t%s,%s\n",$keytab, + @_[3,0,4,1,2]); + } +} + +close(SORT); diff --git a/src/kadmin/testing/scripts/start_servers b/src/kadmin/testing/scripts/start_servers new file mode 100755 index 000000000000..87bd52fc0ba6 --- /dev/null +++ b/src/kadmin/testing/scripts/start_servers @@ -0,0 +1,71 @@ +#!/bin/sh +# +# Usage: start_servers [hostname [path]] +# +# This script turns a host into a OpenV*Secure primary server for the +# realm SECURE-TEST.OV.COM. If no arguments are specified, +# the local host is affected. Otherwise, the host hostname is +# affected; the path argument is the top of the Secure install tree on +# that host, and if it is not specified the current canonical value of +# TOP is used. + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${STESTDIR=$STOP/testing} +DUMMY=${START_SERVERS_LOCAL=$STESTDIR/scripts/start_servers_local} +# This'll be wrong sometimes +DUMMY=${RSH_CMD=rsh} + +local=1 + +if [ $# -gt 0 ]; then + if [ $# != 1 -a $# != 2 ]; then + echo "Usage: $0 [hostname [path]]" 1>&2 + exit 1 + fi + + local=0 + hostname=$1 + if [ $# = 1 ]; then + rempath=`sh -c "cd $TOP && pwd"` + else + rempath=$2 + fi +fi + +if [ $local = 0 ]; then + + # Fix up the local krb5.conf to point to the remote + localname=`$QUALNAME` + sed -e "s/__REALM__/$REALM/g" -e "s#__K5ROOT__#$K5ROOT#g" \ + -e "s/__KDCHOST__/$hostname/g" \ + -e "s/__LOCALHOST__/$localname/g" \ + -e "s#__MODDIR__#$TOP/../plugins/kdb#g"\ + -e "s#__PLUGIN_DIR__#$TOP/../plugins#g"\ + < $STESTDIR/proto/krb5.conf.proto > $K5ROOT/krb5.conf + +# Using /usr/ucb/rsh and getting rid of "-k $REALM" until we get +# around to fixing the fact that Kerberos rsh doesn't strip out "-k +# REALM" when falling back. + + START_SERVERS_LOCAL=`echo $START_SERVERS_LOCAL|sed "s%$TOP%$rempath%"` + CMD="$RSH_CMD $hostname -n \ + \"sh -c 'VERBOSE_TEST=$VERBOSE_TEST TOP=$rempath \ + $rempath/testing/scripts/env-setup.sh \ + $START_SERVERS_LOCAL $rempath'\"" + + if $VERBOSE; then + echo "+++" + echo "+++ Begin execution of start_servers_local on $hostname" + echo "+++" + echo $CMD + fi + eval $CMD + if $VERBOSE; then + echo "+++" + echo "+++ End execution of start_servers_local on $hostname" + echo "+++" + fi +else + $START_SERVERS_LOCAL +fi + diff --git a/src/kadmin/testing/scripts/start_servers_local b/src/kadmin/testing/scripts/start_servers_local new file mode 100755 index 000000000000..0cbed462d1ad --- /dev/null +++ b/src/kadmin/testing/scripts/start_servers_local @@ -0,0 +1,161 @@ +#!/bin/sh + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${STESTDIR=$STOP/testing} +DUMMY=${INITDB=$STESTDIR/scripts/init_db} +DUMMY=${SRVTCL=$TESTDIR/util/kadm5_srv_tcl}; export SRVTCL +DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl} +DUMMY=${STOP_SERVERS_LOCAL=$STESTDIR/scripts/stop_servers_local} +DUMMY=${KRB5RCACHEDIR=$TESTDIR} ; export KRB5RCACHEDIR + +if [ -d /usr/tmp ]; then + usrtmp=/usr/tmp +else + usrtmp=/var/tmp +fi + +$STOP_SERVERS_LOCAL -start_servers + +if $VERBOSE; then + REDIRECT= +else + REDIRECT='>/dev/null' +fi + +while :; do + case $1 in + -keysalt) + shift + if [ $# -gt 0 ]; then + keysalts="$keysalts $1" + else + break + fi + ;; + -kdcport) + shift + if [ $# -gt 0 ]; then + kdcport=$1 + else + break + fi + ;; + *) + break + ;; + esac + shift +done + +if [ $# -gt 1 ]; then + echo "Usage: $0 [-kdcport port] [-keysalts tuple] ... [top]" 1>&2 + exit 1 +elif [ $# = 1 ]; then + TOP=$1 + export TOP +fi + +# create a fresh db + +$INITDB "$keysalts" || exit 1 + +# Post-process the config files based on our arguments +if [ "$keysalts" != "" ]; then + sedcmd="s/\([ ]*supported_enctypes =\).*/\1 $keysalts/" + sed -e "$sedcmd" < $K5ROOT/kdc.conf > $K5ROOT/kdc.conf.new + mv $K5ROOT/kdc.conf.new $K5ROOT/kdc.conf +fi +if [ "$kdcport" != "" ] ; then + sedcmd="s/\(kdc_ports = .*\)[ ]*/\1, $kdcport/" + sed -e "$sedcmd" < $K5ROOT/kdc.conf > $K5ROOT/kdc.conf.new + mv $K5ROOT/kdc.conf.new $K5ROOT/kdc.conf +fi + +# allow admin to krlogin as root (for cleanup) +DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM +hostname=`hostname` +QUALNAME=`$TOP/testing/scripts/qualname.pl $hostname`; export QUALNAME + +cat - > /tmp/start_servers_local$$ <<\EOF +if { [catch { + source $env(STOP)/testing/tcl/util.t + set r $env(REALM) + set q $env(QUALNAME) + puts stdout [kadm5_init $env(SRVTCL) mrroot null \ + [config_params {KADM5_CONFIG_REALM} $r] \ + $KADM5_STRUCT_VERSION $KADM5_API_VERSION_3 server_handle] + puts stdout [kadm5_create_principal $server_handle \ + [simple_principal host/$q@$r] {KADM5_PRINCIPAL} notathena] + puts stdout [kadm5_destroy $server_handle] +} err]} { + puts stderr "initialization error: $err" + exit 1 +} +exit 0 +EOF +eval "$SRVTCL < /tmp/start_servers_local$$ $REDIRECT" +x=$? +rm /tmp/start_servers_local$$ +if test $x != 0 ; then exit 1 ; fi + +# rm -f /etc/v5srvtab +# eval $LOCAL_MAKE_KEYTAB -princ host/xCANONHOSTx /etc/v5srvtab $REDIRECT + +# run the servers (from the build tree) + +adm_start_file=/tmp/adm_server_start.$$ +kdc_start_file=/tmp/kdc_server_start.$$ + +rm -f $kdc_start_file + +if test "x$USER" = x ; then + USER=$LOGNAME ; export USER +fi + +kdc_args="-R dfl:kdc_rcache.$USER" + +(trap "" 2; cd $TOP/../kdc; ./krb5kdc $kdc_args; touch $kdc_start_file) \ + < /dev/null > $usrtmp/kdc-log.$USER 2>&1 & + +s=1 +max_s=60 +sofar_s=0 +timewait_s=300 + +ovadm_args=-W + +rm -f $adm_start_file + +(sleep 1; cd $TOP/server; ./kadmind $ovadm_args; \ + touch $adm_start_file) < /dev/null > $usrtmp/kadm-log.$USER 2>&1 & + +# wait until they start + +while [ $sofar_s -le $max_s ]; do + if $VERBOSE; then + echo "Sleeping for $s seconds to allow servers" \ + "to start..." + fi + + sofar_s=`expr $sofar_s + $s` + + sleep $s + + if [ -f $adm_start_file -a -f $kdc_start_file ]; then + break + fi +done + +if [ $sofar_s -gt $max_s ]; then + echo "Admin server or KDC failed to start after $sofar_s" \ + "seconds." 1>&2 + if [ ! -f $adm_start_file ]; then + echo " No admin server start file $adm_start_file." 1>&2 + fi + if [ ! -f $kdc_start_file ]; then + echo " No KDC start file $adm_start_file." 1>&2 + fi + exit 1 +fi + +rm -f $kdc_start_file $adm_start_file diff --git a/src/kadmin/testing/scripts/stop_servers b/src/kadmin/testing/scripts/stop_servers new file mode 100755 index 000000000000..b7f8384caccc --- /dev/null +++ b/src/kadmin/testing/scripts/stop_servers @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Usage: stop_servers [hostname [path]] +# +# This script turns a host into a OpenV*Secure primary server for the +# realm SECURE-TEST.OV.COM. If no arguments are specified, +# the local host is affected. Otherwise, the host hostname is +# affected; the path argument is the top of the Secure install tree on +# that host, and if it is not specified the current canonical value of +# TOP is used. + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${STESTDIR=$STOP/testing} +DUMMY=${STOP_SERVERS_LOCAL=$STESTDIR/scripts/stop_servers_local} +# This'll be wrong sometimes +DUMMY=${RSH_CMD=rsh} + +local=1 + +if [ $# -gt 0 ]; then + if [ $# != 1 -a $# != 2 ]; then + echo "Usage: $0 [hostname [path]]" 1>&2 + exit 1 + fi + + local=0 + hostname=$1 + if [ $# = 1 ]; then + rempath=`sh -c "cd $TOP && pwd"` + else + rempath=$2 + fi +fi + +if [ $local = 0 ]; then + if $VERBOSE; then + echo "+++ Stopping servers on remote host $hostname..." + fi + + STOP_SERVERS_LOCAL=`echo $STOP_SERVERS_LOCAL | sed "s%$TOP%$rempath%"` + CMD="$RSH_CMD $hostname -n \ + \"sh -c 'VERBOSE_TEST=$VERBOSE_TEST TOP=$rempath \ + $rempath/testing/scripts/env-setup.sh \ + $STOP_SERVERS_LOCAL $rempath'\"" + + if $VERBOSE; then + echo "+++" + echo "+++ Begin execution of stop_servers_local on $hostname" + echo "+++" + echo $CMD + fi + eval $CMD + if $VERBOSE; then + echo "+++" + echo "+++ End execution of stop_servers_local on $hostname" + echo "+++" + fi +else + $STOP_SERVERS_LOCAL +fi diff --git a/src/kadmin/testing/scripts/stop_servers_local b/src/kadmin/testing/scripts/stop_servers_local new file mode 100755 index 000000000000..24a9de7b39da --- /dev/null +++ b/src/kadmin/testing/scripts/stop_servers_local @@ -0,0 +1,44 @@ +#!/bin/sh + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${KRB5RCACHEDIR=$TESTDIR} + +while [ $# -gt 0 ] ; do + case $1 in + -start_servers) + start_servers=$1 + ;; + *) + TOP=$1 + export TOP + ;; + esac + shift +done + +# kill any running servers. + +if $VERBOSE; then echo "Killing servers:"; fi + +for pid in xxx \ + `$PS_ALL | grep krb5kdc | grep -v grep | awk '{print $2}'` \ + `$PS_ALL | grep kadmind | grep -v grep | awk '{print $2}'` \ + ; do + case "$pid" in + xxx) + ;; + *) + if $VERBOSE; then $PS_PID$pid | grep -v COMMAND; fi + kill $pid + ;; + esac +done + +# Destroy the kdc replay cache so we don't lose if we try to run the +# KDC as another unix user. +if test "x$USER" = x ; then + USER=$LOGNAME +fi +rm -f $KRB5RCACHEDIR/krb5kdc_rcache.$USER + +exit 0 diff --git a/src/kadmin/testing/scripts/verify_xrunner_report.plin b/src/kadmin/testing/scripts/verify_xrunner_report.plin new file mode 100755 index 000000000000..9d83c3ea247d --- /dev/null +++ b/src/kadmin/testing/scripts/verify_xrunner_report.plin @@ -0,0 +1,38 @@ +#!/usr/local/bin/perl + +sub usage { die "usage: $0 reportfile\n"; } + +$report = shift(@ARGV) || die &usage; + +open(REPORT, $report) || die "Couldn't open $report: $!\n"; + +while(<REPORT>) { + if (/Process termination:/ && !/\bOK\b/) { + warn "Process termination not OK\n"; + $warnings++; + } elsif (/Number of detected mismatches:\s*(\d+)/ && ($1 ne "0")) { + warn "Number of detected mismatches = $1\n"; + $warnings++; + } elsif (/Detailed Results Description/) { + break; + } +} + +while(<REPORT>) { + next if !/^\d+\s+/; + + split; + + if (($_[2] ne "run") && + ($_[2] ne "OK") && + ($_[2] ne "end-of-test")) { + warn "Unexpected result code $_[2] from test $_[4]\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/tcl/util.t b/src/kadmin/testing/tcl/util.t new file mode 100644 index 000000000000..6751f89e63da --- /dev/null +++ b/src/kadmin/testing/tcl/util.t @@ -0,0 +1,58 @@ +proc simple_principal {name} { + return "{$name} 0 0 0 0 {$name} 0 0 0 0 null 0" +} + +proc princ_w_pol {name policy} { + return "{$name} 0 0 0 0 {$name} 0 0 0 0 {$policy} 0" +} + +proc simple_policy {name} { + return "{$name} 0 0 0 0 0 0 0 0 0" +} + +proc config_params {masks values} { + if {[llength $masks] != [llength $values]} { + error "config_params: length of mask and values differ" + } + + set params [list $masks 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 {}] + for {set i 0} {$i < [llength $masks]} {incr i} { + set mask [lindex $masks $i] + set value [lindex $values $i] + switch -glob -- $mask { + "KADM5_CONFIG_REALM" {set params [lreplace $params 1 1 $value]} + "KADM5_CONFIG_KADMIND_PORT" { + set params [lreplace $params 2 2 $value]} + "KADM5_CONFIG_ADMIN_SERVER" { + set params [lreplace $params 3 3 $value]} + "KADM5_CONFIG_DBNAME" {set params [lreplace $params 4 4 $value]} + "KADM5_CONFIG_ADBNAME" {set params [lreplace $params 5 5 $value]} + "KADM5_CONFIG_ADB_LOCKFILE" { + set params [lreplace $params 6 6 $value]} + "KADM5_CONFIG_ACL_FILE" {set params [lreplace $params 8 8 $value]} + "KADM5_CONFIG_DICT_FILE" { + set params [lreplace $params 9 9 $value]} + "KADM5_CONFIG_MKEY_FROM_KBD" { + set params [lreplace $params 10 10 $value]} + "KADM5_CONFIG_STASH_FILE" { + set params [lreplace $params 11 11 $value]} + "KADM5_CONFIG_MKEY_NAME" { + set params [lreplace $params 12 12 $value]} + "KADM5_CONFIG_ENCTYPE" {set params [lreplace $params 13 13 $value]} + "KADM5_CONFIG_MAX_LIFE" { + set params [lreplace $params 14 14 $value]} + "KADM5_CONFIG_MAX_RLIFE" { + set params [lreplace $params 15 15 $value]} + "KADM5_CONFIG_EXPIRATION" { + set params [lreplace $params 16 16 $value]} + "KADM5_CONFIG_FLAGS" {set params [lreplace $params 17 17 $value]} + "KADM5_CONFIG_ENCTYPES" { + set params [lreplace $params 18 19 [llength $value] $value]} + "*" {error "config_params: unknown mask $mask"} + } + } + return $params +} + + + diff --git a/src/kadmin/testing/util/Makefile.in b/src/kadmin/testing/util/Makefile.in new file mode 100644 index 000000000000..7785c742ea3d --- /dev/null +++ b/src/kadmin/testing/util/Makefile.in @@ -0,0 +1,42 @@ +mydir=kadmin$(S)testing$(S)util +BUILDTOP=$(REL)..$(S)..$(S).. +LOCALINCLUDES = $(TCL_INCLUDES) -I$(BUILDTOP)/lib/kdb/ +# Force Tcl headers to use stdarg.h, because krb5 does too, and if +# Tcl uses varargs.h it'll just mess things up. +DEFINES= -DHAS_STDARG +KRB5_PTHREAD_LIB=$(THREAD_LINKOPTS) + +PROG_LIBPATH=-L$(TOPLIBD) $(TCL_LIBPATH) +PROG_RPATH=$(KRB5_LIBDIR)$(TCL_RPATH) + +SRCS = $(srcdir)/tcl_kadm5.c $(srcdir)/test.c +OBJS = tcl_kadm5.o test.o + +CLNTPROG= kadm5_clnt_tcl +SRVPROG = kadm5_srv_tcl + +DO_ALL=@DO_ALL@ + +all: all-$(DO_ALL) + +all-: + @echo "+++" + @echo "+++ WARNING: Tcl not available. The kadm5 tests will not be run." + @echo "+++" + @echo 'Skipped kadm5 tests: Tcl not found' >> $(SKIPTESTS) + +all-tcl: $(CLNTPROG) $(SRVPROG) + +$(SRVPROG): $(OBJS) $(KADMSRV_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $(SRVPROG) $(OBJS) $(TCL_MAYBE_RPATH) \ + $(KADMSRV_LIBS) $(KRB5_PTHREAD_LIB) $(KRB5_BASE_LIBS) $(TCL_LIBS) + +$(CLNTPROG): $(OBJS) $(KADMCLNT_DEPLIBS) $(KRB5_BASE_DEPLIBS) + $(CC_LINK) -o $(CLNTPROG) $(OBJS) $(TCL_MAYBE_RPATH) \ + $(KRB5_PTHREAD_LIB) $(KADMCLNT_LIBS) $(KRB5_BASE_LIBS) $(TCL_LIBS) + +bsddb_dump: bsddb_dump.o + $(CC_LINK) -o bsddb_dump bsddb_dump.o $(KADMSRV_LIBS) + +clean: + $(RM) $(CLNTPROG) $(SRVPROG) diff --git a/src/kadmin/testing/util/bsddb_dump.c b/src/kadmin/testing/util/bsddb_dump.c new file mode 100644 index 000000000000..5dbe7ae9c83b --- /dev/null +++ b/src/kadmin/testing/util/bsddb_dump.c @@ -0,0 +1,65 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * $Id$ + */ + +#include <sys/file.h> +#include <fcntl.h> +#include <db.h> +#include <stdio.h> + +main(int argc, char *argv[]) +{ + char *file; + DB *db; + DBT dbkey, dbdata; + int code, i; + + HASHINFO info; + + info.hash = NULL; + info.bsize = 256; + info.ffactor = 8; + info.nelem = 25000; + info.lorder = 0; + + if (argc != 2) { + fprintf(stderr, "usage: argv[0] dbfile\n"); + exit(2); + } + + file = argv[1]; + + if((db = dbopen(file, O_RDWR, 0666, DB_HASH, &info)) == NULL) { + perror("Opening db file"); + exit(1); + } + + if ((code = (*db->seq)(db, &dbkey, &dbdata, R_FIRST)) == -1) { + perror("starting db iteration"); + exit(1); + } + + while (code == 0) { + for (i=0; i<dbkey.size; i++) + printf("%02x", (int) ((unsigned char *) dbkey.data)[i]); + printf("\t"); + for (i=0; i<dbdata.size; i++) + printf("%02x", (int) ((unsigned char *) dbdata.data)[i]); + printf("\n"); + + code = (*db->seq)(db, &dbkey, &dbdata, R_NEXT); + } + + if (code == -1) { + perror("during db iteration"); + exit(1); + } + + if ((*db->close)(db) == -1) { + perror("closing db"); + exit(1); + } + + exit(0); +} diff --git a/src/kadmin/testing/util/deps b/src/kadmin/testing/util/deps new file mode 100644 index 000000000000..ca828a85cde3 --- /dev/null +++ b/src/kadmin/testing/util/deps @@ -0,0 +1,16 @@ +# +# Generated makefile dependencies follow. +# +$(OUTPRE)tcl_kadm5.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \ + $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \ + $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \ + $(BUILDTOP)/lib/kdb/adb_err.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \ + $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \ + $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \ + $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \ + $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \ + $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/kdb.h \ + $(top_srcdir)/include/krb5.h tcl_kadm5.c tcl_kadm5.h +$(OUTPRE)test.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + tcl_kadm5.h test.c diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c new file mode 100644 index 000000000000..a4997c60ca04 --- /dev/null +++ b/src/kadmin/testing/util/tcl_kadm5.c @@ -0,0 +1,2568 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include "autoconf.h" +#include <stdio.h> +#include <string.h> +#if HAVE_TCL_H +#include <tcl.h> +#elif HAVE_TCL_TCL_H +#include <tcl/tcl.h> +#endif +#define USE_KADM5_API_VERSION 2 +#include <kadm5/admin.h> +#include <com_err.h> +#include <errno.h> +#include <stdlib.h> +#include <adb_err.h> +#include "tcl_kadm5.h" + +struct flagval { + char *name; + krb5_flags val; +}; + +/* XXX This should probably be in the hash table like server_handle */ +static krb5_context context; + +static struct flagval krb5_flags_array[] = { + {"KRB5_KDB_DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED}, + {"KRB5_KDB_DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE}, + {"KRB5_KDB_DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED}, + {"KRB5_KDB_DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE}, + {"KRB5_KDB_DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE}, + {"KRB5_KDB_DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY}, + {"KRB5_KDB_DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX}, + {"KRB5_KDB_REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH}, + {"KRB5_KDB_REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH}, + {"KRB5_KDB_REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE}, + {"KRB5_KDB_DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR}, + {"KRB5_KDB_PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE} +}; + +static struct flagval aux_attributes[] = { + {"KADM5_POLICY", KADM5_POLICY} +}; + +static struct flagval principal_mask_flags[] = { + {"KADM5_PRINCIPAL", KADM5_PRINCIPAL}, + {"KADM5_PRINC_EXPIRE_TIME", KADM5_PRINC_EXPIRE_TIME}, + {"KADM5_PW_EXPIRATION", KADM5_PW_EXPIRATION}, + {"KADM5_LAST_PWD_CHANGE", KADM5_LAST_PWD_CHANGE}, + {"KADM5_ATTRIBUTES", KADM5_ATTRIBUTES}, + {"KADM5_MAX_LIFE", KADM5_MAX_LIFE}, + {"KADM5_MOD_TIME", KADM5_MOD_TIME}, + {"KADM5_MOD_NAME", KADM5_MOD_NAME}, + {"KADM5_KVNO", KADM5_KVNO}, + {"KADM5_MKVNO", KADM5_MKVNO}, + {"KADM5_AUX_ATTRIBUTES", KADM5_AUX_ATTRIBUTES}, + {"KADM5_POLICY", KADM5_POLICY}, + {"KADM5_POLICY_CLR", KADM5_POLICY_CLR}, + {"KADM5_MAX_RLIFE", KADM5_MAX_RLIFE}, + {"KADM5_LAST_SUCCESS", KADM5_LAST_SUCCESS}, + {"KADM5_LAST_FAILED", KADM5_LAST_FAILED}, + {"KADM5_FAIL_AUTH_COUNT", KADM5_FAIL_AUTH_COUNT}, + {"KADM5_KEY_DATA", KADM5_KEY_DATA}, + {"KADM5_TL_DATA", KADM5_TL_DATA}, + {"KADM5_PRINCIPAL_NORMAL_MASK", KADM5_PRINCIPAL_NORMAL_MASK} +}; + +static struct flagval policy_mask_flags[] = { + {"KADM5_POLICY", KADM5_POLICY}, + {"KADM5_PW_MAX_LIFE", KADM5_PW_MAX_LIFE}, + {"KADM5_PW_MIN_LIFE", KADM5_PW_MIN_LIFE}, + {"KADM5_PW_MIN_LENGTH", KADM5_PW_MIN_LENGTH}, + {"KADM5_PW_MIN_CLASSES", KADM5_PW_MIN_CLASSES}, + {"KADM5_PW_HISTORY_NUM", KADM5_PW_HISTORY_NUM}, + {"KADM5_REF_COUNT", KADM5_REF_COUNT}, + {"KADM5_PW_MAX_FAILURE", KADM5_PW_MAX_FAILURE}, + {"KADM5_PW_FAILURE_COUNT_INTERVAL", KADM5_PW_FAILURE_COUNT_INTERVAL}, + {"KADM5_PW_LOCKOUT_DURATION", KADM5_PW_LOCKOUT_DURATION}, +}; + +static struct flagval config_mask_flags[] = { + {"KADM5_CONFIG_REALM", KADM5_CONFIG_REALM}, + {"KADM5_CONFIG_DBNAME", KADM5_CONFIG_DBNAME}, + {"KADM5_CONFIG_MKEY_NAME", KADM5_CONFIG_MKEY_NAME}, + {"KADM5_CONFIG_MAX_LIFE", KADM5_CONFIG_MAX_LIFE}, + {"KADM5_CONFIG_MAX_RLIFE", KADM5_CONFIG_MAX_RLIFE}, + {"KADM5_CONFIG_EXPIRATION", KADM5_CONFIG_EXPIRATION}, + {"KADM5_CONFIG_FLAGS", KADM5_CONFIG_FLAGS}, + {"KADM5_CONFIG_STASH_FILE", KADM5_CONFIG_STASH_FILE}, + {"KADM5_CONFIG_ENCTYPE", KADM5_CONFIG_ENCTYPE}, + {"KADM5_CONFIG_ADBNAME", KADM5_CONFIG_ADBNAME}, + {"KADM5_CONFIG_ADB_LOCKFILE", KADM5_CONFIG_ADB_LOCKFILE}, + {"KADM5_CONFIG_ACL_FILE", KADM5_CONFIG_ACL_FILE}, + {"KADM5_CONFIG_KADMIND_PORT", KADM5_CONFIG_KADMIND_PORT}, + {"KADM5_CONFIG_ENCTYPES", KADM5_CONFIG_ENCTYPES}, + {"KADM5_CONFIG_ADMIN_SERVER", KADM5_CONFIG_ADMIN_SERVER}, + {"KADM5_CONFIG_DICT_FILE", KADM5_CONFIG_DICT_FILE}, + {"KADM5_CONFIG_MKEY_FROM_KBD", KADM5_CONFIG_MKEY_FROM_KBD}, +}; + +static struct flagval priv_flags[] = { + {"KADM5_PRIV_GET", KADM5_PRIV_GET}, + {"KADM5_PRIV_ADD", KADM5_PRIV_ADD}, + {"KADM5_PRIV_MODIFY", KADM5_PRIV_MODIFY}, + {"KADM5_PRIV_DELETE", KADM5_PRIV_DELETE} +}; + + +static char *arg_error = "wrong # args"; + +static Tcl_HashTable *struct_table = 0; + +static int put_server_handle(Tcl_Interp *interp, void *handle, char **name) +{ + int i = 1, newPtr = 0; + static char buf[20]; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + sprintf(buf, "kadm5_handle%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, handle); + + *name = buf; + + return TCL_OK; +} + +static int get_server_handle(Tcl_Interp *interp, const char *name, + void **handle) +{ + Tcl_HashEntry *entry; + + if(!strcasecmp(name, "null")) + *handle = 0; + else { + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + *handle = (void *) Tcl_GetHashValue(entry); + } + return TCL_OK; +} + +static int remove_server_handle(Tcl_Interp *interp, const char *name) +{ + Tcl_HashEntry *entry; + + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + + Tcl_SetHashValue(entry, NULL); + return TCL_OK; +} + +#define GET_HANDLE(num_args, ignored) \ + void *server_handle; \ + const char *whoami = argv[0]; \ + argv++, argc--; \ + if (argc != num_args + 1) { \ + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \ + return TCL_ERROR; \ + } \ + { \ + int ltcl_ret; \ + if ((ltcl_ret = get_server_handle(interp, argv[0], &server_handle)) \ + != TCL_OK) { \ + return ltcl_ret; \ + } \ + } \ + argv++, argc--; + +static Tcl_HashTable *create_flag_table(struct flagval *flags, int size) +{ + Tcl_HashTable *table; + Tcl_HashEntry *entry; + int i; + + if (! (table = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_InitHashTable(table, TCL_STRING_KEYS); + + for (i = 0; i < size; i++) { + int newPtr; + + if (! (entry = Tcl_CreateHashEntry(table, flags[i].name, &newPtr))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_SetHashValue(entry, &flags[i].val); + } + + return table; +} + + +static Tcl_DString *unparse_str(char *in_str) +{ + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + if (! in_str) { + Tcl_DStringAppend(str, "null", -1); + } + else { + Tcl_DStringAppend(str, in_str, -1); + } + + return str; +} + + + +static int parse_str(Tcl_Interp *interp, const char *in_str, char **out_str) +{ + if (! in_str) { + *out_str = 0; + } + else if (! strcasecmp(in_str, "null")) { + *out_str = 0; + } + else { + *out_str = (char *) in_str; + } + return TCL_OK; +} + + +static void set_ok(Tcl_Interp *interp, char *string) +{ + Tcl_SetResult(interp, "OK", TCL_STATIC); + Tcl_AppendElement(interp, "KADM5_OK"); + Tcl_AppendElement(interp, string); +} + + + +static Tcl_DString *unparse_err(kadm5_ret_t code) +{ + char *code_string; + const char *error_string; + Tcl_DString *dstring; + + switch (code) { + case KADM5_FAILURE: code_string = "KADM5_FAILURE"; break; + case KADM5_AUTH_GET: code_string = "KADM5_AUTH_GET"; break; + case KADM5_AUTH_ADD: code_string = "KADM5_AUTH_ADD"; break; + case KADM5_AUTH_MODIFY: + code_string = "KADM5_AUTH_MODIFY"; break; + case KADM5_AUTH_DELETE: + code_string = "KADM5_AUTH_DELETE"; break; + case KADM5_AUTH_INSUFFICIENT: + code_string = "KADM5_AUTH_INSUFFICIENT"; break; + case KADM5_BAD_DB: code_string = "KADM5_BAD_DB"; break; + case KADM5_DUP: code_string = "KADM5_DUP"; break; + case KADM5_RPC_ERROR: code_string = "KADM5_RPC_ERROR"; break; + case KADM5_NO_SRV: code_string = "KADM5_NO_SRV"; break; + case KADM5_BAD_HIST_KEY: + code_string = "KADM5_BAD_HIST_KEY"; break; + case KADM5_NOT_INIT: code_string = "KADM5_NOT_INIT"; break; + case KADM5_INIT: code_string = "KADM5_INIT"; break; + case KADM5_BAD_PASSWORD: + code_string = "KADM5_BAD_PASSWORD"; break; + case KADM5_UNK_PRINC: code_string = "KADM5_UNK_PRINC"; break; + case KADM5_UNK_POLICY: code_string = "KADM5_UNK_POLICY"; break; + case KADM5_BAD_MASK: code_string = "KADM5_BAD_MASK"; break; + case KADM5_BAD_CLASS: code_string = "KADM5_BAD_CLASS"; break; + case KADM5_BAD_LENGTH: code_string = "KADM5_BAD_LENGTH"; break; + case KADM5_BAD_POLICY: code_string = "KADM5_BAD_POLICY"; break; + case KADM5_BAD_HISTORY: code_string = "KADM5_BAD_HISTORY"; break; + case KADM5_BAD_PRINCIPAL: + code_string = "KADM5_BAD_PRINCIPAL"; break; + case KADM5_BAD_AUX_ATTR: + code_string = "KADM5_BAD_AUX_ATTR"; break; + case KADM5_PASS_Q_TOOSHORT: + code_string = "KADM5_PASS_Q_TOOSHORT"; break; + case KADM5_PASS_Q_CLASS: + code_string = "KADM5_PASS_Q_CLASS"; break; + case KADM5_PASS_Q_DICT: + code_string = "KADM5_PASS_Q_DICT"; break; + case KADM5_PASS_REUSE: code_string = "KADM5_PASS_REUSE"; break; + case KADM5_PASS_TOOSOON: + code_string = "KADM5_PASS_TOOSOON"; break; + case KADM5_POLICY_REF: + code_string = "KADM5_POLICY_REF"; break; + case KADM5_PROTECT_PRINCIPAL: + code_string = "KADM5_PROTECT_PRINCIPAL"; break; + case KADM5_BAD_SERVER_HANDLE: + code_string = "KADM5_BAD_SERVER_HANDLE"; break; + case KADM5_BAD_STRUCT_VERSION: + code_string = "KADM5_BAD_STRUCT_VERSION"; break; + case KADM5_OLD_STRUCT_VERSION: + code_string = "KADM5_OLD_STRUCT_VERSION"; break; + case KADM5_NEW_STRUCT_VERSION: + code_string = "KADM5_NEW_STRUCT_VERSION"; break; + case KADM5_BAD_API_VERSION: + code_string = "KADM5_BAD_API_VERSION"; break; + case KADM5_OLD_LIB_API_VERSION: + code_string = "KADM5_OLD_LIB_API_VERSION"; break; + case KADM5_OLD_SERVER_API_VERSION: + code_string = "KADM5_OLD_SERVER_API_VERSION"; break; + case KADM5_NEW_LIB_API_VERSION: + code_string = "KADM5_NEW_LIB_API_VERSION"; break; + case KADM5_NEW_SERVER_API_VERSION: + code_string = "KADM5_NEW_SERVER_API_VERSION"; break; + case KADM5_SECURE_PRINC_MISSING: + code_string = "KADM5_SECURE_PRINC_MISSING"; break; + case KADM5_NO_RENAME_SALT: + code_string = "KADM5_NO_RENAME_SALT"; break; + case KADM5_BAD_CLIENT_PARAMS: + code_string = "KADM5_BAD_CLIENT_PARAMS"; break; + case KADM5_BAD_SERVER_PARAMS: + code_string = "KADM5_BAD_SERVER_PARAMS"; break; + case KADM5_AUTH_LIST: + code_string = "KADM5_AUTH_LIST"; break; + case KADM5_AUTH_CHANGEPW: + code_string = "KADM5_AUTH_CHANGEPW"; break; + case KADM5_GSS_ERROR: code_string = "KADM5_GSS_ERROR"; break; + case KADM5_BAD_TL_TYPE: code_string = "KADM5_BAD_TL_TYPE"; break; + case KADM5_MISSING_CONF_PARAMS: + code_string = "KADM5_MISSING_CONF_PARAMS"; break; + case KADM5_BAD_SERVER_NAME: + code_string = "KADM5_BAD_SERVER_NAME"; break; + case KADM5_MISSING_KRB5_CONF_PARAMS: + code_string = "KADM5_MISSING_KRB5_CONF_PARAMS"; break; + case KADM5_XDR_FAILURE: code_string = "KADM5_XDR_FAILURE"; break; + case KADM5_CANT_RESOLVE: code_string = "KADM5_CANT_RESOLVE"; break; + + + case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break; + case OSA_ADB_NOENT: code_string = "ENOENT"; break; + case OSA_ADB_DBINIT: code_string = "OSA_ADB_DBINIT"; break; + case OSA_ADB_BAD_POLICY: code_string = "Bad policy name"; break; + case OSA_ADB_BAD_PRINC: code_string = "Bad principal name"; break; + case OSA_ADB_BAD_DB: code_string = "Invalid database."; break; + case OSA_ADB_XDR_FAILURE: code_string = "OSA_ADB_XDR_FAILURE"; break; + case OSA_ADB_BADLOCKMODE: code_string = "OSA_ADB_BADLOCKMODE"; break; + case OSA_ADB_CANTLOCK_DB: code_string = "OSA_ADB_CANTLOCK_DB"; break; + case OSA_ADB_NOTLOCKED: code_string = "OSA_ADB_NOTLOCKED"; break; + case OSA_ADB_NOLOCKFILE: code_string = "OSA_ADB_NOLOCKFILE"; break; + case OSA_ADB_NOEXCL_PERM: code_string = "OSA_ADB_NOEXCL_PERM"; break; + + case KRB5_KDB_INUSE: code_string = "KRB5_KDB_INUSE"; break; + case KRB5_KDB_UK_SERROR: code_string = "KRB5_KDB_UK_SERROR"; break; + case KRB5_KDB_UK_RERROR: code_string = "KRB5_KDB_UK_RERROR"; break; + case KRB5_KDB_UNAUTH: code_string = "KRB5_KDB_UNAUTH"; break; + case KRB5_KDB_NOENTRY: code_string = "KRB5_KDB_NOENTRY"; break; + case KRB5_KDB_ILL_WILDCARD: code_string = "KRB5_KDB_ILL_WILDCARD"; break; + case KRB5_KDB_DB_INUSE: code_string = "KRB5_KDB_DB_INUSE"; break; + case KRB5_KDB_DB_CHANGED: code_string = "KRB5_KDB_DB_CHANGED"; break; + case KRB5_KDB_TRUNCATED_RECORD: + code_string = "KRB5_KDB_TRUNCATED_RECORD"; break; + case KRB5_KDB_RECURSIVELOCK: + code_string = "KRB5_KDB_RECURSIVELOCK"; break; + case KRB5_KDB_NOTLOCKED: code_string = "KRB5_KDB_NOTLOCKED"; break; + case KRB5_KDB_BADLOCKMODE: code_string = "KRB5_KDB_BADLOCKMODE"; break; + case KRB5_KDB_DBNOTINITED: code_string = "KRB5_KDB_DBNOTINITED"; break; + case KRB5_KDB_DBINITED: code_string = "KRB5_KDB_DBINITED"; break; + case KRB5_KDB_ILLDIRECTION: code_string = "KRB5_KDB_ILLDIRECTION"; break; + case KRB5_KDB_NOMASTERKEY: code_string = "KRB5_KDB_NOMASTERKEY"; break; + case KRB5_KDB_BADMASTERKEY: code_string = "KRB5_KDB_BADMASTERKEY"; break; + case KRB5_KDB_INVALIDKEYSIZE: + code_string = "KRB5_KDB_INVALIDKEYSIZE"; break; + case KRB5_KDB_CANTREAD_STORED: + code_string = "KRB5_KDB_CANTREAD_STORED"; break; + case KRB5_KDB_BADSTORED_MKEY: + code_string = "KRB5_KDB_BADSTORED_MKEY"; break; + case KRB5_KDB_CANTLOCK_DB: code_string = "KRB5_KDB_CANTLOCK_DB"; break; + case KRB5_KDB_DB_CORRUPT: code_string = "KRB5_KDB_DB_CORRUPT"; break; + + case KRB5_PARSE_ILLCHAR: code_string = "KRB5_PARSE_ILLCHAR"; break; + case KRB5_PARSE_MALFORMED: code_string = "KRB5_PARSE_MALFORMED"; break; + case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN"; break; + case KRB5_REALM_UNKNOWN: code_string = "KRB5_REALM_UNKNOWN"; break; + case KRB5_KDC_UNREACH: code_string = "KRB5_KDC_UNREACH"; break; + case KRB5_KDCREP_MODIFIED: code_string = "KRB5_KDCREP_MODIFIED"; break; + case KRB5KRB_AP_ERR_BAD_INTEGRITY: code_string = "KRB5KRB_AP_ERR_BAD_INTEGRITY"; break; + case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"; break; + case KRB5_CONFIG_BADFORMAT: code_string = "KRB5_CONFIG_BADFORMAT"; break; + + case KRB5_CC_NOTFOUND: code_string = "KRB5_CC_NOTFOUND"; break; + case KRB5_FCC_NOFILE: code_string = "KRB5_FCC_NOFILE"; break; + + case EINVAL: code_string = "EINVAL"; break; + case ENOENT: code_string = "ENOENT"; break; + + default: + fprintf(stderr, "**** CODE %ld (%s) ***\n", (long) code, + error_message (code)); + code_string = "UNKNOWN"; + break; + } + + error_string = error_message(code); + + if (! (dstring = (Tcl_DString *) malloc(sizeof(Tcl_DString)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX Do we really want to exit? Ok if this is */ + /* just a test program, but what about if it gets */ + /* used for other things later? */ + } + + Tcl_DStringInit(dstring); + + if (! (Tcl_DStringAppendElement(dstring, "ERROR") && + Tcl_DStringAppendElement(dstring, code_string) && + Tcl_DStringAppendElement(dstring, error_string))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + return dstring; +} + + + +static void stash_error(Tcl_Interp *interp, krb5_error_code code) +{ + Tcl_DString *dstring = unparse_err(code); + Tcl_DStringResult(interp, dstring); + Tcl_DStringFree(dstring); + free(dstring); +} + +static Tcl_DString *unparse_key_data(krb5_key_data *key_data, int n_key_data) +{ + Tcl_DString *str; + char buf[2048]; + int i, j; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + for (i = 0; i < n_key_data; i++) { + krb5_key_data *key = &key_data[i]; + + Tcl_DStringStartSublist(str); + sprintf(buf, "%d", key->key_data_type[0]); + Tcl_DStringAppendElement(str, buf); + sprintf(buf, "%d", key->key_data_ver > 1 ? + key->key_data_type[1] : -1); + Tcl_DStringAppendElement(str, buf); + if (key->key_data_contents[0]) { + sprintf(buf, "0x"); + for (j = 0; j < key->key_data_length[0]; j++) { + sprintf(buf + 2*(j+1), "%02x", + key->key_data_contents[0][j]); + } + } else *buf = '\0'; + Tcl_DStringAppendElement(str, buf); + Tcl_DStringEndSublist(str); + } + + return str; +} + +static Tcl_DString *unparse_tl_data(krb5_tl_data *tl_data, int n_tl_data) +{ + Tcl_DString *str; + char buf[2048]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + Tcl_DStringStartSublist(str); + for (; tl_data; tl_data = tl_data->tl_data_next) { + Tcl_DStringStartSublist(str); + sprintf(buf, "%d", tl_data->tl_data_type); + Tcl_DStringAppendElement(str, buf); + sprintf(buf, "%d", tl_data->tl_data_length); + Tcl_DStringAppendElement(str, buf); + Tcl_DStringAppend(str, " ", 1); + Tcl_DStringAppend(str, (char *) tl_data->tl_data_contents, + tl_data->tl_data_length); + Tcl_DStringEndSublist(str); + } + Tcl_DStringEndSublist(str); + + return str; +} + +static Tcl_DString *unparse_flags(struct flagval *array, int size, + krb5_int32 flags) +{ + int i; + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (i = 0; i < size; i++) { + if (flags & array[i].val) { + Tcl_DStringAppendElement(str, array[i].name); + } + } + + return str; +} + + +static int parse_flags(Tcl_Interp *interp, Tcl_HashTable *table, + struct flagval *array, int size, const char *str, + krb5_flags *flags) +{ + int tmp, argc, i, retcode = TCL_OK; + const char **argv; + Tcl_HashEntry *entry; + + if (Tcl_GetInt(interp, str, &tmp) == TCL_OK) { + *flags = tmp; + return TCL_OK; + } + Tcl_ResetResult(interp); + + if (Tcl_SplitList(interp, str, &argc, &argv) != TCL_OK) { + return TCL_ERROR; + } + + if (! table) { + table = create_flag_table(array, size); + } + + *flags = 0; + + for (i = 0; i < argc; i++) { + if (! (entry = Tcl_FindHashEntry(table, argv[i]))) { + Tcl_AppendResult(interp, "unknown krb5 flag ", argv[i], 0); + retcode = TCL_ERROR; + break; + } + *flags |= *(krb5_flags *) Tcl_GetHashValue(entry); + } + + Tcl_Free((char *) argv); + return(retcode); +} + +static Tcl_DString *unparse_privs(krb5_flags flags) +{ + return unparse_flags(priv_flags, sizeof(priv_flags) / + sizeof(struct flagval), flags); +} + + +static Tcl_DString *unparse_krb5_flags(krb5_flags flags) +{ + return unparse_flags(krb5_flags_array, sizeof(krb5_flags_array) / + sizeof(struct flagval), flags); +} + +static int parse_krb5_flags(Tcl_Interp *interp, const char *str, + krb5_flags *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, krb5_flags_array, + sizeof(krb5_flags_array) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static Tcl_DString *unparse_aux_attributes(krb5_int32 flags) +{ + return unparse_flags(aux_attributes, sizeof(aux_attributes) / + sizeof(struct flagval), flags); +} + + +static int parse_aux_attributes(Tcl_Interp *interp, const char *str, + long *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, aux_attributes, + sizeof(aux_attributes) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_principal_mask(Tcl_Interp *interp, const char *str, + krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, principal_mask_flags, + sizeof(principal_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_policy_mask(Tcl_Interp *interp, const char *str, + krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, policy_mask_flags, + sizeof(policy_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + + +static Tcl_DString *unparse_principal_ent(kadm5_principal_ent_t princ, + krb5_int32 mask) +{ + Tcl_DString *str, *tmp_dstring; + char *tmp; + char buf[20]; + krb5_error_code krb5_ret; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp = 0; /* It looks to me from looking at the library source */ + /* code for krb5_parse_name that the pointer passed into */ + /* it should be initialized to 0 if I want it do be */ + /* allocated automatically. */ + if (mask & KADM5_PRINCIPAL) { + krb5_ret = krb5_unparse_name(context, princ->principal, &tmp); + if (krb5_ret) { + /* XXX Do we want to return an error? Not sure. */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + } else + Tcl_DStringAppendElement(str, "null"); + + sprintf(buf, "%d", princ->princ_expire_time); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_pwd_change); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->pw_expiration); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->max_life); + Tcl_DStringAppendElement(str, buf); + + tmp = 0; + if (mask & KADM5_MOD_NAME) { + if ((krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp))) { + /* XXX */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + } else + Tcl_DStringAppendElement(str, "null"); + + sprintf(buf, "%d", princ->mod_date); + Tcl_DStringAppendElement(str, buf); + + if (mask & KADM5_ATTRIBUTES) { + tmp_dstring = unparse_krb5_flags(princ->attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + } else + Tcl_DStringAppendElement(str, "null"); + + sprintf(buf, "%d", princ->kvno); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->mkvno); + Tcl_DStringAppendElement(str, buf); + + /* XXX This may be dangerous, because the contents of the policy */ + /* field are undefined if the POLICY bit isn't set. However, I */ + /* think it's a bug for the field not to be null in that case */ + /* anyway, so we should assume that it will be null so that we'll */ + /* catch it if it isn't. */ + + tmp_dstring = unparse_str(princ->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_aux_attributes(princ->aux_attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", princ->max_renewable_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_success); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_failed); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->fail_auth_count); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->n_key_data); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->n_tl_data); + Tcl_DStringAppendElement(str, buf); + + tmp_dstring = unparse_key_data(princ->key_data, princ->n_key_data); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_tl_data(princ->tl_data, princ->n_tl_data); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + return str; +} + +static int parse_keysalts(Tcl_Interp *interp, const char *list, + krb5_key_salt_tuple **keysalts, + int num_keysalts) +{ + const char **argv, **argv1 = NULL; + int i, tmp, argc, argc1, retcode; + + *keysalts = NULL; + if (list == NULL) + return TCL_OK; + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + if (argc != num_keysalts) { + Tcl_SetResult(interp, "wrong number of keysalts", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + *keysalts = (krb5_key_salt_tuple *) + malloc(sizeof(krb5_key_salt_tuple)*num_keysalts); + for (i = 0; i < num_keysalts; i++) { + if ((retcode = Tcl_SplitList(interp, argv[i], &argc1, &argv1)) != + TCL_OK) { + goto finished; + } + if (argc1 != 2) { + Tcl_SetResult(interp, "wrong # of fields in keysalt", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + /* XXX this used to be argv1[1] too! */ + if ((retcode = Tcl_GetInt(interp, argv1[0], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing ks_enctype"); + retcode = TCL_ERROR; + goto finished; + } + (*keysalts)[i].ks_enctype = tmp; + if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing ks_salttype"); + goto finished; + } + (*keysalts)[i].ks_salttype = tmp; + + Tcl_Free((char *) argv1); + argv1 = NULL; + } + +finished: + if (argv1) { + Tcl_Free((char *) argv1); + } + Tcl_Free((char *) argv); + return retcode; +} + +static int parse_key_data(Tcl_Interp *interp, const char *list, + krb5_key_data **key_data, + int n_key_data) +{ + const char **argv = NULL; + int argc, retcode; + + *key_data = NULL; + if (list == NULL) { + if (n_key_data != 0) { + Tcl_SetResult(interp, "wrong number of key_datas", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } else + return TCL_OK; + } + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + if (argc != n_key_data) { + Tcl_SetResult(interp, "wrong number of key_datas", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + + if (argc != 0) { + Tcl_SetResult(interp, "cannot parse key_data yet", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + +finished: + Tcl_Free((char *) argv); + return retcode; +} + +static int parse_tl_data(Tcl_Interp *interp, const char *list, + krb5_tl_data **tlp, + int n_tl_data) +{ + krb5_tl_data *tl, *tl2; + const char **argv = NULL, **argv1 = NULL; + int i, tmp, argc, argc1, retcode; + + *tlp = NULL; + if (list == NULL) { + if (n_tl_data != 0) { + Tcl_SetResult(interp, "wrong number of tl_datas", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } else + return TCL_OK; + } + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + if (argc != n_tl_data) { + Tcl_SetResult(interp, "wrong number of tl_datas", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + + tl = tl2 = NULL; + for (i = 0; i < n_tl_data; i++) { + tl2 = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)); + memset(tl2, 0, sizeof(krb5_tl_data)); + tl2->tl_data_next = tl; + tl = tl2; + } + tl2 = tl; + + for (i = 0; i < n_tl_data; i++) { + if ((retcode = Tcl_SplitList(interp, argv[i], &argc1, &argv1)) != + TCL_OK) { + goto finished; + } + if (argc1 != 3) { + Tcl_SetResult(interp, "wrong # of fields in tl_data", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv1[0], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing tl_data_type"); + retcode = TCL_ERROR; + goto finished; + } + tl->tl_data_type = tmp; + if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing tl_data_length"); + retcode = TCL_ERROR; + goto finished; + } + tl->tl_data_length = tmp; + if (tl->tl_data_length != strlen(argv1[2])) { + Tcl_SetResult(interp, "length != string length", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + tl->tl_data_contents = (krb5_octet *) strdup(argv1[2]); + + Tcl_Free((char *) argv1); + argv1 = NULL; + tl = tl->tl_data_next; + } + if (tl != NULL) { + Tcl_SetResult(interp, "tl is not NULL!", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + *tlp = tl2; + +finished: + if (argv1) { + Tcl_Free((char *) argv1); + } + Tcl_Free((char *) argv); + return retcode; +} + +static int parse_config_params(Tcl_Interp *interp, char *list, + kadm5_config_params *params) +{ + static Tcl_HashTable *table = 0; + const char **argv = NULL; + int tmp, argc, retcode; + + memset(params, 0, sizeof(kadm5_config_params)); + if (list == NULL) + return TCL_OK; + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + + if (argc != 20) { + Tcl_SetResult(interp, "wrong # args in config params structure", + TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + + if ((retcode = parse_flags(interp, table, config_mask_flags, + sizeof(config_mask_flags) / + sizeof(struct flagval), + argv[0], &tmp)) != TCL_OK) { + goto finished; + } + params->mask = tmp; + + if ((retcode = parse_str(interp, argv[1], ¶ms->realm)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing realm name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kadmind_port"); + retcode = TCL_ERROR; + goto finished; + } + params->kadmind_port = tmp; + if ((retcode = parse_str(interp, argv[3], ¶ms->admin_server)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[4], ¶ms->dbname)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + /* Ignore argv[5], which used to set the admin_dbname field. */ + /* Ignore argv[6], which used to set the admin_lockfile field. */ + /* Ignore argv[7], which used to set the admin_keytab field. */ + if ((retcode = parse_str(interp, argv[8], ¶ms->acl_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing acl_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[9], ¶ms->dict_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing dict_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[10], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkey_from_kbd"); + retcode = TCL_ERROR; + goto finished; + } + params->mkey_from_kbd = tmp; + if ((retcode = parse_str(interp, argv[11], ¶ms->stash_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing stash_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[12], ¶ms->mkey_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkey_name name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[13], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing enctype"); + retcode = TCL_ERROR; + goto finished; + } + params->enctype = tmp; + if ((retcode = Tcl_GetInt(interp, argv[14], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + params->max_life = tmp; + if ((retcode = Tcl_GetInt(interp, argv[15], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_rlife"); + retcode = TCL_ERROR; + goto finished; + } + params->max_rlife = tmp; + if ((retcode = Tcl_GetInt(interp, argv[16], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing expiration"); + retcode = TCL_ERROR; + goto finished; + } + params->expiration = tmp; + if ((retcode = parse_krb5_flags(interp, argv[17], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing flags"); + retcode = TCL_ERROR; + goto finished; + } + params->flags = tmp; + if ((retcode = Tcl_GetInt(interp, argv[18], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing num_keysalts"); + retcode = TCL_ERROR; + goto finished; + } + params->num_keysalts = tmp; + if ((retcode = parse_keysalts(interp, argv[19], ¶ms->keysalts, + params->num_keysalts)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keysalts"); + retcode = TCL_ERROR; + goto finished; + } + +finished: + return retcode; +} + +static int parse_principal_ent(Tcl_Interp *interp, char *list, + kadm5_principal_ent_t *out_princ) +{ + kadm5_principal_ent_t princ = 0; + krb5_error_code krb5_ret; + int tcl_ret; + int argc; + const char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 12 && argc != 20) { + Tcl_SetResult(interp, "wrong # args in principal structure", + TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + + if (! (princ = malloc(sizeof *princ))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + memset(princ, 0, sizeof(*princ)); + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ->principal)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + retcode = TCL_ERROR; + goto finished; + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing princ_expire_time"); + retcode = TCL_ERROR; + goto finished; + } + princ->princ_expire_time = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing last_pwd_change"); + retcode = TCL_ERROR; + goto finished; + } + princ->last_pwd_change = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_expiration"); + retcode = TCL_ERROR; + goto finished; + } + princ->pw_expiration = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + princ->max_life = tmp; + + if ((krb5_ret = krb5_parse_name(context, argv[5], &princ->mod_name)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing mod_name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mod_date"); + retcode = TCL_ERROR; + goto finished; + } + princ->mod_date = tmp; + + if ((tcl_ret = parse_krb5_flags(interp, argv[7], &princ->attributes)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing attributes"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->kvno = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->mkvno = tmp; + + if ((tcl_ret = parse_str(interp, argv[10], &princ->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + retcode = TCL_ERROR; + goto finished; + } + if(princ->policy != NULL) { + if(!(princ->policy = strdup(princ->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + } + + if ((tcl_ret = parse_aux_attributes(interp, argv[11], + &princ->aux_attributes)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing aux_attributes"); + retcode = TCL_ERROR; + goto finished; + } + + if (argc == 12) goto finished; + + if ((tcl_ret = Tcl_GetInt(interp, argv[12], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_renewable_life"); + retcode = TCL_ERROR; + goto finished; + } + princ->max_renewable_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[13], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing last_success"); + retcode = TCL_ERROR; + goto finished; + } + princ->last_success = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[14], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing last_failed"); + retcode = TCL_ERROR; + goto finished; + } + princ->last_failed = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[15], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing fail_auth_count"); + retcode = TCL_ERROR; + goto finished; + } + princ->fail_auth_count = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[16], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing n_key_data"); + retcode = TCL_ERROR; + goto finished; + } + princ->n_key_data = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[17], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing n_tl_data"); + retcode = TCL_ERROR; + goto finished; + } + princ->n_tl_data = tmp; + + if ((tcl_ret = parse_key_data(interp, argv[18], + &princ->key_data, + princ->n_key_data)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing key_data"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = parse_tl_data(interp, argv[19], + &princ->tl_data, + princ->n_tl_data)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing tl_data"); + retcode = TCL_ERROR; + goto finished; + } + princ->n_tl_data = tmp; + +finished: + Tcl_Free((char *) argv); + *out_princ = princ; + return retcode; +} + + +static void free_principal_ent(kadm5_principal_ent_t *princ) +{ + krb5_free_principal(context, (*princ)->principal); + krb5_free_principal(context, (*princ)->mod_name); + free((*princ)->policy); + free(*princ); + *princ = 0; +} + +static Tcl_DString *unparse_policy_ent(kadm5_policy_ent_t policy) +{ + Tcl_DString *str, *tmp_dstring; + char buf[20]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp_dstring = unparse_str(policy->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%ld", policy->pw_min_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%ld", policy->pw_max_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%ld", policy->pw_min_length); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%ld", policy->pw_min_classes); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%ld", policy->pw_history_num); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%ld", policy->policy_refcnt); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_max_fail); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_failcnt_interval); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_lockout_duration); + Tcl_DStringAppendElement(str, buf); + + return str; +} + + + +static int parse_policy_ent(Tcl_Interp *interp, char *list, + kadm5_policy_ent_t *out_policy) +{ + kadm5_policy_ent_t policy = 0; + int tcl_ret; + int argc; + const char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 7 && argc != 10) { + Tcl_SetResult(interp, "wrong # args in policy structure", TCL_STATIC); + retcode = TCL_ERROR; + goto finished; + } + + if (! (policy = malloc(sizeof *policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((tcl_ret = parse_str(interp, argv[0], &policy->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + retcode = TCL_ERROR; + goto finished; + } + + if(policy->policy != NULL) { + if (! (policy->policy = strdup(policy->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_max_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_max_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_length"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_length = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_classes"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_classes = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[5], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_history_num"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_history_num = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy_refcnt"); + retcode = TCL_ERROR; + goto finished; + } + policy->policy_refcnt = tmp; + + if (argc == 7) goto finished; + + if ((tcl_ret = Tcl_GetInt(interp, argv[7], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_max_fail"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_max_fail = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_failcnt_interval"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_failcnt_interval = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_lockout_duration"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_lockout_duration = tmp; + +finished: + Tcl_Free((char *) argv); + *out_policy = policy; + return retcode; +} + + +static void free_policy_ent(kadm5_policy_ent_t *policy) +{ + free((*policy)->policy); + free(*policy); + *policy = 0; +} + +static Tcl_DString *unparse_keytype(krb5_enctype enctype) +{ + Tcl_DString *str; + char buf[50]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + switch (enctype) { + /* XXX is this right? */ + case ENCTYPE_NULL: Tcl_DStringAppend(str, "ENCTYPE_NULL", -1); break; + case ENCTYPE_DES_CBC_CRC: + Tcl_DStringAppend(str, "ENCTYPE_DES_CBC_CRC", -1); break; + default: + sprintf(buf, "UNKNOWN KEYTYPE (0x%x)", enctype); + Tcl_DStringAppend(str, buf, -1); + break; + } + + return str; +} + + +static Tcl_DString *unparse_keyblocks(krb5_keyblock *keyblocks, int num_keys) +{ + Tcl_DString *str; + Tcl_DString *keytype; + unsigned int i; + int j; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (j = 0; j < num_keys; j++) { + krb5_keyblock *keyblock = &keyblocks[j]; + + Tcl_DStringStartSublist(str); + + keytype = unparse_keytype(keyblock->enctype); + Tcl_DStringAppendElement(str, keytype->string); + Tcl_DStringFree(keytype); + free(keytype); + if (keyblock->length == 0) { + Tcl_DStringAppendElement(str, "0x00"); + } + else { + Tcl_DStringAppendElement(str, "0x"); + for (i = 0; i < keyblock->length; i++) { + char buf[3]; + sprintf(buf, "%02x", (int) keyblock->contents[i]); + Tcl_DStringAppend(str, buf, -1); + } + } + + Tcl_DStringEndSublist(str); + } + + + return str; +} + +enum init_type { INIT_NONE, INIT_PASS, INIT_CREDS }; + +static int _tcl_kadm5_init_any(enum init_type init_type, ClientData clientData, + Tcl_Interp *interp, int argc, const char *argv[]) +{ + kadm5_ret_t ret; + char *client_name, *pass, *service_name; + int tcl_ret; + krb5_ui_4 struct_version, api_version; + const char *handle_var; + void *server_handle; + char *handle_name, *params_str; + const char *whoami = argv[0]; + kadm5_config_params params; + + argv++, argc--; + + kadm5_init_krb5_context(&context); + + if (argc != 7) { + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); + return TCL_ERROR; + } + + if (((tcl_ret = parse_str(interp, argv[0], &client_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[1], &pass)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[2], &service_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[3], ¶ms_str)) != TCL_OK) || + ((tcl_ret = parse_config_params(interp, params_str, ¶ms)) + != TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[4], (int *) &struct_version)) != + TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[5], (int *) &api_version)) != + TCL_OK)) { + return tcl_ret; + } + + handle_var = argv[6]; + + if (! (handle_var && *handle_var)) { + Tcl_SetResult(interp, "must specify server handle variable name", + TCL_STATIC); + return TCL_ERROR; + } + + if (init_type == INIT_CREDS) { + krb5_ccache cc; + + if (pass == NULL) { + if ((ret = krb5_cc_default(context, &cc))) { + stash_error(interp, ret); + return TCL_ERROR; + } + } else { + if ((ret = krb5_cc_resolve(context, pass, &cc))) { + stash_error(interp, ret); + return TCL_ERROR; + } + } + + ret = kadm5_init_with_creds(context, client_name, cc, service_name, + ¶ms, struct_version, + api_version, NULL, &server_handle); + + (void) krb5_cc_close(context, cc); + } else + ret = kadm5_init(context, client_name, pass, service_name, ¶ms, + struct_version, api_version, NULL, &server_handle); + + /* The string fields of params are aliases into argv[3], but + * params.keysalts is allocated, so clean it up. */ + free(params.keysalts); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = put_server_handle(interp, server_handle, &handle_name)) + != TCL_OK) { + return tcl_ret; + } + + if (! Tcl_SetVar(interp, handle_var, handle_name, TCL_LEAVE_ERR_MSG)) { + return TCL_ERROR; + } + + set_ok(interp, "KADM5 API initialized."); + return TCL_OK; +} + +static int tcl_kadm5_init(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + return _tcl_kadm5_init_any(INIT_PASS, clientData, interp, argc, argv); +} + +static int tcl_kadm5_init_with_creds(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + return _tcl_kadm5_init_any(INIT_CREDS, clientData, interp, argc, argv); +} + +static int tcl_kadm5_destroy(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + kadm5_ret_t ret; + int tcl_ret; + + GET_HANDLE(0, 0); + + ret = kadm5_destroy(server_handle); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = remove_server_handle(interp, argv[-1])) != TCL_OK) { + return tcl_ret; + } + + set_ok(interp, "KADM5 API deinitialized."); + return TCL_OK; +} + +static int tcl_kadm5_create_principal(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + char *princ_string; + kadm5_principal_ent_t princ = 0; + krb5_int32 mask; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + + GET_HANDLE(3, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[2], &pw)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[3], &override_qual)) != + TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#endif + +#ifdef OVERRIDE + ret = kadm5_create_principal(server_handle, princ, mask, pw, + override_qual); +#else + ret = kadm5_create_principal(server_handle, princ, mask, pw); +#endif + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Principal created."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + + +static int tcl_kadm5_delete_principal(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int tcl_ret; + char *name; + + GET_HANDLE(1, 0); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if ((krb5_ret = krb5_parse_name(context, name, &princ))) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + return TCL_ERROR; + } + } else princ = NULL; + ret = kadm5_delete_principal(server_handle, princ); + + if(princ != NULL) + krb5_free_principal(context, princ); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Principal deleted."); + return TCL_OK; + } +} + + + +static int tcl_kadm5_modify_principal(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + char *princ_string; + kadm5_principal_ent_t princ = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_modify_principal(server_handle, princ, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Principal modified."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + +static int tcl_kadm5_rename_principal(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + krb5_principal source, target; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 0); + + if ((krb5_ret = krb5_parse_name(context, argv[0], &source)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing source"); + return TCL_ERROR; + } + + if ((krb5_ret = krb5_parse_name(context, argv[1], &target)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing target"); + krb5_free_principal(context, source); + return TCL_ERROR; + } + + ret = kadm5_rename_principal(server_handle, source, target); + + if (ret == KADM5_OK) { + set_ok(interp, "Principal renamed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + + krb5_free_principal(context, source); + krb5_free_principal(context, target); + return retcode; +} + + + +static int tcl_kadm5_chpass_principal(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + krb5_principal princ; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + krb5_error_code krb5_ret; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if (parse_str(interp, argv[1], &pw) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing password"); + retcode = TCL_ERROR; + goto finished; + } + +#ifdef OVERRIDE + if (Tcl_GetBoolean(interp, argv[2], &override_qual) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_chpass_principal(server_handle, + princ, pw, override_qual); +#else + ret = kadm5_chpass_principal(server_handle, princ, pw); +#endif + + if (ret == KADM5_OK) { + set_ok(interp, "Password changed."); + goto finished; + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +static int tcl_kadm5_chpass_principal_util(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + krb5_principal princ; + char *new_pw; +#ifdef OVERRIDE + int override_qual; +#endif + char *pw_ret, *pw_ret_var; + char msg_ret[1024], *msg_ret_var; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(4, 0); + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if (parse_str(interp, argv[1], &new_pw) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing new password"); + retcode = TCL_ERROR; + goto finished; + } +#ifdef OVERRIDE + if (Tcl_GetBoolean(interp, argv[2], &override_qual) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } +#endif + if (parse_str(interp, argv[3], &pw_ret_var) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + if (parse_str(interp, argv[4], &msg_ret_var) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing msg_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_chpass_principal_util(server_handle, princ, new_pw, +#ifdef OVERRIDE + override_qual, +#endif + pw_ret_var ? &pw_ret : 0, + msg_ret_var ? msg_ret : 0, + msg_ret_var ? sizeof(msg_ret) : 0); + + if (ret == KADM5_OK) { + if (pw_ret_var && + (! Tcl_SetVar(interp, pw_ret_var, pw_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, "while setting pw_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + if (msg_ret_var && + (! Tcl_SetVar(interp, msg_ret_var, msg_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, + "while setting msg_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Password changed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +static int tcl_kadm5_randkey_principal(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + krb5_principal princ; + krb5_keyblock *keyblocks; + int num_keys; + char *keyblock_var, *num_var, buf[50]; + Tcl_DString *keyblock_dstring = 0; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(3, 0); + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if (parse_str(interp, argv[1], &keyblock_var) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } + if (parse_str(interp, argv[2], &num_var) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_randkey_principal(server_handle, + princ, keyblock_var ? &keyblocks : 0, + &num_keys); + + if (ret == KADM5_OK) { + if (keyblock_var) { + keyblock_dstring = unparse_keyblocks(keyblocks, num_keys); + if (! Tcl_SetVar(interp, keyblock_var, + keyblock_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting keyblock variable"); + retcode = TCL_ERROR; + goto finished; + } + } + if (num_var) { + sprintf(buf, "%d", num_keys); + if (! Tcl_SetVar(interp, num_var, buf, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting num_keys variable"); + } + } + set_ok(interp, "Key randomized."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + if (keyblock_dstring) { + Tcl_DStringFree(keyblock_dstring); + free(keyblock_dstring); + } + return retcode; +} + + + +static int tcl_kadm5_get_principal(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + krb5_principal princ; + kadm5_principal_ent_rec ent; + Tcl_DString *ent_dstring = 0; + char *ent_var; + char *name; + krb5_error_code krb5_ret; + int tcl_ret; + kadm5_ret_t ret = -1; + krb5_int32 mask; + int retcode = TCL_OK; + + GET_HANDLE(3, 1); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if ((krb5_ret = krb5_parse_name(context, name, &princ)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + } else princ = NULL; + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + retcode = TCL_ERROR; + goto finished; + } + if ((tcl_ret = parse_principal_mask(interp, argv[2], &mask)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal mask"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_get_principal(server_handle, princ, ent_var ? &ent : 0, + mask); + + if (ret == KADM5_OK) { + if (ent_var) { + ent_dstring = unparse_principal_ent(&ent, mask); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Principal retrieved."); + } + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if(princ != NULL) + krb5_free_principal(context, princ); + if (ret == KADM5_OK && ent_var && + (ret = kadm5_free_principal_ent(server_handle, &ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + return retcode; +} + +static int tcl_kadm5_create_policy(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + char *policy_string; + kadm5_policy_ent_t policy = 0; + krb5_int32 mask; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + ret = kadm5_create_policy(server_handle, policy, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Policy created."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + + +static int tcl_kadm5_delete_policy(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + kadm5_ret_t ret; + char *policy; + + GET_HANDLE(1, 0); + + if (parse_str(interp, argv[0], &policy) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + ret = kadm5_delete_policy(server_handle, policy); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Policy deleted."); + return TCL_OK; + } +} + + + +static int tcl_kadm5_modify_policy(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + char *policy_string; + kadm5_policy_ent_t policy = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_modify_policy(server_handle, policy, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Policy modified."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + +static int tcl_kadm5_get_policy(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + kadm5_policy_ent_rec ent; + Tcl_DString *ent_dstring = 0; + char *policy; + char *ent_var; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 1); + + if (parse_str(interp, argv[0], &policy) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + if (parse_str(interp, argv[1], &ent_var) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + return TCL_ERROR; + } + + ret = kadm5_get_policy(server_handle, policy, ent_var ? &ent : 0); + + if (ret == KADM5_OK) { + if (ent_var) { + ent_dstring = unparse_policy_ent(&ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Policy retrieved."); + } + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if (ent_var && ret == KADM5_OK && + (ret = kadm5_free_policy_ent(server_handle, &ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + return retcode; +} + + + +static int tcl_kadm5_free_principal_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + char *ent_name; + kadm5_principal_ent_t ent; + kadm5_ret_t ret; + + GET_HANDLE(1, 0); + + if (parse_str(interp, argv[0], &ent_name) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = kadm5_free_principal_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "principal", sizeof("principal")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "principal handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = (kadm5_principal_ent_t) Tcl_GetHashValue(entry); + + ret = kadm5_free_principal_ent(server_handle, ent); + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Principal freed."); + return TCL_OK; +} + + +static int tcl_kadm5_free_policy_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, const char *argv[]) +{ + char *ent_name; + kadm5_policy_ent_t ent; + kadm5_ret_t ret; + + GET_HANDLE(1, 0); + + if (parse_str(interp, argv[0], &ent_name) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = kadm5_free_policy_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "policy", sizeof("policy")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "policy handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = (kadm5_policy_ent_t) Tcl_GetHashValue(entry); + + if ((ret = kadm5_free_policy_ent(server_handle, ent)) != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Policy freed."); + return TCL_OK; +} + + +static int tcl_kadm5_get_privs(ClientData clientData, Tcl_Interp *interp, + int argc, const char *argv[]) +{ + const char *set_ret; + kadm5_ret_t ret; + char *priv_var; + long privs; + + GET_HANDLE(1, 0); + + if (parse_str(interp, argv[0], &priv_var) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing privs variable name"); + return TCL_ERROR; + } + + ret = kadm5_get_privs(server_handle, priv_var ? &privs : 0); + + if (ret == KADM5_OK) { + if (priv_var) { + Tcl_DString *str = unparse_privs(privs); + set_ret = Tcl_SetVar(interp, priv_var, str->string, + TCL_LEAVE_ERR_MSG); + Tcl_DStringFree(str); + free(str); + if (! set_ret) { + Tcl_AppendElement(interp, "while setting priv variable"); + return TCL_ERROR; + } + } + set_ok(interp, "Privileges retrieved."); + return TCL_OK; + } + else { + stash_error(interp, ret); + return TCL_ERROR; + } +} + + +void Tcl_kadm5_init(Tcl_Interp *interp) +{ + char buf[20]; + + Tcl_SetVar(interp, "KADM5_ADMIN_SERVICE", + KADM5_ADMIN_SERVICE, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "KADM5_CHANGEPW_SERVICE", + KADM5_CHANGEPW_SERVICE, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION); + Tcl_SetVar(interp, "KADM5_STRUCT_VERSION", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_2); + Tcl_SetVar(interp, "KADM5_API_VERSION_2", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_3); + Tcl_SetVar(interp, "KADM5_API_VERSION_3", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_4); + Tcl_SetVar(interp, "KADM5_API_VERSION_4", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_MASK); + Tcl_SetVar(interp, "KADM5_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION_MASK); + Tcl_SetVar(interp, "KADM5_STRUCT_VERSION_MASK", buf, + TCL_GLOBAL_ONLY); + + Tcl_CreateCommand(interp, "kadm5_init", tcl_kadm5_init, 0, 0); + Tcl_CreateCommand(interp, "kadm5_init_with_creds", + tcl_kadm5_init_with_creds, 0, 0); + Tcl_CreateCommand(interp, "kadm5_destroy", tcl_kadm5_destroy, 0, + 0); + Tcl_CreateCommand(interp, "kadm5_create_principal", + tcl_kadm5_create_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_delete_principal", + tcl_kadm5_delete_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_modify_principal", + tcl_kadm5_modify_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_rename_principal", + tcl_kadm5_rename_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_chpass_principal", + tcl_kadm5_chpass_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_chpass_principal_util", + tcl_kadm5_chpass_principal_util, 0, 0); + Tcl_CreateCommand(interp, "kadm5_randkey_principal", + tcl_kadm5_randkey_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_principal", + tcl_kadm5_get_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_create_policy", + tcl_kadm5_create_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_delete_policy", + tcl_kadm5_delete_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_modify_policy", + tcl_kadm5_modify_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_policy", + tcl_kadm5_get_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_free_principal_ent", + tcl_kadm5_free_principal_ent, 0, 0); + Tcl_CreateCommand(interp, "kadm5_free_policy_ent", + tcl_kadm5_free_policy_ent, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_privs", + tcl_kadm5_get_privs, 0, 0); +} diff --git a/src/kadmin/testing/util/tcl_kadm5.h b/src/kadmin/testing/util/tcl_kadm5.h new file mode 100644 index 000000000000..1f91a11a1601 --- /dev/null +++ b/src/kadmin/testing/util/tcl_kadm5.h @@ -0,0 +1,3 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ + +void Tcl_kadm5_init(Tcl_Interp *interp); diff --git a/src/kadmin/testing/util/tcl_kadm5_syntax b/src/kadmin/testing/util/tcl_kadm5_syntax new file mode 100644 index 000000000000..5f16e58e0d1f --- /dev/null +++ b/src/kadmin/testing/util/tcl_kadm5_syntax @@ -0,0 +1,57 @@ +Here's a brief summary of the syntax of the tcl versions of the +kadm5 functions: + +string Can be a string or "null" which will turn into a null pointer +principal_ent A 12-field list in the order of the principal_ent + structure: {string number number number number string + number mask number number string mask} + It can also be "null", like a string, to indicate that + a null structure pointer should be used. +mask Either a number, representing the actual value of the + mask, or a sequence of symbols in a list. Example: + {PRINCIPAL ATTRIBUTES} is a valid principal mask. +boolean "1", "0", "true", "false", etc. +varname The name of a Tcl variable, or "null" to not assign. +policy_ent Similar to principal_ent, but with seven fields, + instead of 12. The first is a string, and the rest + are numbers. + +init + client_name:string pass:string service_name:string + realm:string struct_version:int api_version:int + server_handle_ret:varname +destroy + server_handle:string +create_principal + server_handle:string principal:principal_ent + mask:principal_mask password:string +delete_principal + server_handle:string name:string +modify_principal + server_handle:string principal_principal_ent + mask:principal_mask +rename_principal + server_handle:string source:string target:string +chpass_principal + server_handle:string name:string password:string +chpass_principal_util + server_handle:string name:string password:string + pw_ret:varname msg_ret:varname +randkey_principal + server_handle:string name:string keyblock_var:varname +get_principal [-struct] + server_handle:string name:string princ_var:varname +create_policy + server_handle:string policy:policy_ent mask:policy_mask +delete_policy + server_handle:string name:string +modify_policy + server_handle:string policy:policy_ent mask:policy_mask +get_policy [-struct] + server_handle:string name:string policy_var:varname +free_principal_ent + server_handle:string handle:string +free_policy_ent + server_handle:string handle:string +get_privs + server_handle:string privs:priv_var diff --git a/src/kadmin/testing/util/tcl_krb5_hash.c b/src/kadmin/testing/util/tcl_krb5_hash.c new file mode 100644 index 000000000000..35c6bb0b37e5 --- /dev/null +++ b/src/kadmin/testing/util/tcl_krb5_hash.c @@ -0,0 +1,167 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* + * All of the TCL krb5 functions which return (or place into output + * variables) structures or pointers to structures that can't be + * represented as tcl native types, do so by returning a handle for + * the appropriate structure. The handle is a string of the form + * "type$id", where "type" is the type of datum represented by the + * handle and "id" is a unique identifier for it. This handle can + * then be used later by the caller to refer to the object, and + * internally to retrieve the actually datum from the appropriate hash + * table. + * + * The functions in this file do four things: + * + * 1) Given a pointer to a datum and a string representing the type of + * datum to which the pointer refers, create a new handle for the + * datum, store the datum in the hash table using the new handle as + * its key, and return the new handle. + * + * 2) Given a handle, locate and return the appropriate hash table + * datum. + * + * 3) Given a handle, look through a table of types and unparse + * functions to figure out what function to call to get a string + * representation of the datum, call it with the appropriate pointer + * (obtained from the hash table) as an argument, and return the + * resulting string as the unparsed form of the datum. + * + * 4) Given a handle, remove that handle and its associated datum from + * the hash table (but don't free it -- it's assumed to have already + * been freed by the caller). + */ + +#if HAVE_TCL_H +#include <tcl.h> +#elif HAVE_TCL_TCL_H +#include <tcl/tcl.h> +#endif +#include <assert.h> + +#define SEP_STR "$" + +static char *memory_error = "out of memory"; + +/* + * Right now, we're only using one hash table. However, at some point + * in the future, we might decide to use a separate hash table for + * every type. Therefore, I'm putting this function in as an + * abstraction so it's the only thing we'll have to change if we + * decide to do that. + * + * Also, this function allows us to put in just one place the code for + * checking to make sure that the hash table exists and initializing + * it if it doesn't. + */ + +static TclHashTable *get_hash_table(Tcl_Interp *interp, + char *type) +{ + static Tcl_HashTable *hash_table = 0; + + if (! hash_table) { + if (! (hash_table = malloc(sizeof(*hash_table)))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + Tcl_InitHashTable(hash_table, TCL_STRING_KEYS); + } + return hash_table; +} + +#define MAX_ID 999999999 +#define ID_BUF_SIZE 10 + +static Tcl_HashEntry *get_new_handle(Tcl_Interp *interp, + char *type) +{ + static unsigned long int id_counter = 0; + Tcl_DString *handle; + char int_buf[ID_BUF_SIZE]; + + if (! (handle = malloc(sizeof(*handle)))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + Tcl_DStringInit(handle); + + assert(id_counter <= MAX_ID); + + sprintf(int_buf, "%d", id_counter++); + + Tcl_DStringAppend(handle, type, -1); + Tcl_DStringAppend(handle, SEP_STR, -1); + Tcl_DStringAppend(handle, int_buf, -1); + + return handle; +} + + +Tcl_DString *tcl_krb5_create_object(Tcl_Interp *interp, + char *type, + ClientData datum) +{ + Tcl_HashTable *table; + Tcl_DString *handle; + Tcl_HashEntry *entry; + int entry_created = 0; + + if (! (table = get_hash_table(interp, type))) { + return 0; + } + + if (! (handle = get_new_handle(interp, type))) { + return 0; + } + + if (! (entry = Tcl_CreateHashEntry(table, handle, &entry_created))) { + Tcl_SetResult(interp, "error creating hash entry", TCL_STATIC); + Tcl_DStringFree(handle); + return TCL_ERROR; + } + + assert(entry_created); + + Tcl_SetHashValue(entry, datum); + + return handle; +} + +ClientData tcl_krb5_get_object(Tcl_Interp *interp, + char *handle) +{ + char *myhandle, *id_ptr; + Tcl_HashTable *table; + Tcl_HashEntry *entry; + + if (! (myhandle = strdup(handle))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + + if (! (id_ptr = index(myhandle, *SEP_STR))) { + free(myhandle); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "malformatted handle \"", handle, + "\"", 0); + return 0; + } + + *id_ptr = '\0'; + + if (! (table = get_hash_table(interp, myhandle))) { + free(myhandle); + return 0; + } + + free(myhandle); + + if (! (entry = Tcl_FindHashEntry(table, handle))) { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "no object corresponding to handle \"", + handle, "\"", 0); + return 0; + } + + return(Tcl_GetHashValue(entry)); +} diff --git a/src/kadmin/testing/util/test.c b/src/kadmin/testing/util/test.c new file mode 100644 index 000000000000..37e49d680c46 --- /dev/null +++ b/src/kadmin/testing/util/test.c @@ -0,0 +1,38 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +#include "autoconf.h" +#if HAVE_TCL_H +#include <tcl.h> +#elif HAVE_TCL_TCL_H +#include <tcl/tcl.h> +#endif +#include "tcl_kadm5.h" + +#define _TCL_MAIN ((TCL_MAJOR_VERSION * 100 + TCL_MINOR_VERSION) >= 704) + +#if _TCL_MAIN +int +main(argc, argv) + int argc; /* Number of command-line arguments. */ + char **argv; /* Values of command-line arguments. */ +{ + Tcl_Main(argc, argv, Tcl_AppInit); + return 0; /* Needed only to prevent compiler warning. */ +} +#else +/* + * The following variable is a special hack that allows applications + * to be linked using the procedure "main" from the Tcl library. The + * variable generates a reference to "main", which causes main to + * be brought in from the library (and all of Tcl with it). + */ + +extern int main(); +int *tclDummyMainPtr = (int *) main; +#endif + +int Tcl_AppInit(Tcl_Interp *interp) +{ + Tcl_kadm5_init(interp); + + return(TCL_OK); +} |
