diff options
Diffstat (limited to 'src/kadmin')
228 files changed, 40100 insertions, 2 deletions
diff --git a/src/kadmin/ChangeLog b/src/kadmin/ChangeLog index 4dbf1ae4c..5408cd354 100644 --- a/src/kadmin/ChangeLog +++ b/src/kadmin/ChangeLog @@ -1,3 +1,13 @@ +Fri Jul 12 14:38:30 1996 Marc Horowitz <marc@mit.edu> + + * configure.in (CONFIG_DIRS): ktutil is still useful + functionality; add it back to the build. + +Wed Jul 10 16:27:11 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: kdbkeys is no longer necessary. + * configure.in (CONFIG_DIRS): added dbutil + Thu Aug 24 19:21:14 1995 Theodore Y. Ts'o <tytso@dcl> * .Sanitize: Add ktutil directory diff --git a/src/kadmin/Makefile.ov b/src/kadmin/Makefile.ov new file mode 100644 index 000000000..1b5cb8e39 --- /dev/null +++ b/src/kadmin/Makefile.ov @@ -0,0 +1,11 @@ +# +# $Id$ +# + +TOP = . +include $(TOP)/config.mk/template + +SUBDIRS = ../lib/kadm5 server create passwd import export v4server \ + keytab cli testing dbutil + +expand SubdirTarget diff --git a/src/kadmin/cli/ChangeLog b/src/kadmin/cli/ChangeLog new file mode 100644 index 000000000..a69639b86 --- /dev/null +++ b/src/kadmin/cli/ChangeLog @@ -0,0 +1,28 @@ +Fri Jul 19 16:10:39 1996 Marc Horowitz <marc@mit.edu> + + * ss_wrapper.c (main): ss_execute_line was being called with three + args. There are only two, so no error was ever being returned. + +Thu Jul 18 19:14:51 1996 Marc Horowitz <marc@mit.edu> + + * attic/configure.in: removed SS_RULES + + * keytab.c (etype_string): ifdef out des3 reference + + * configure.in: removed SS_RULES + +Mon Jul 15 16:56:43 1996 Barry Jaspan <bjaspan@mit.edu> + + * kadmin.1, keytab.c (kadmin_keytab_add): change ktadd usage to + accept -glob + +Tue Jul 9 16:15:46 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.in: complete rewrite + * configure.in: add the necessary USE_*_LIBRARY macros + +Mon Jul 8 16:45:20 1996 Barry Jaspan <bjaspan@mit.edu> + + * kadmin.1: Update man page for kadm5 changes and functionality. + + diff --git a/src/kadmin/cli/Makefile.in b/src/kadmin/cli/Makefile.in new file mode 100644 index 000000000..c605dd81f --- /dev/null +++ b/src/kadmin/cli/Makefile.in @@ -0,0 +1,19 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kadmin +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab.o + +all:: $(PROG).local $(PROG) + +$(PROG).local: $(OBJS) $(SRVDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS) + +$(PROG): $(OBJS) $(CLNTDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG).local $(PROG) $(OBJS) diff --git a/src/kadmin/cli/Makefile.ov b/src/kadmin/cli/Makefile.ov new file mode 100644 index 000000000..5163bebb8 --- /dev/null +++ b/src/kadmin/cli/Makefile.ov @@ -0,0 +1,28 @@ +TOP = .. +include $(TOP)/config.mk/template + +PROG = kadmin +SRCS = kadmin.c kadmin_ct.c ss_wrapper.c getdate.c keytab.c +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o keytab.o +LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5_ALL) \ + $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) $(BSDLIB) $(NETLIB) +DEPENDS = kadmin_ct.c getdate.c + +expand NormalProgram + +PROG = kadmin.local +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKDB5) \ + $(LIBKRB5_ALL) $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) \ + $(BSDLIB) $(NETLIB) $(REGEXLIB) + +expand NormalProgram + +depend:: $(SRCS) +clean:: ; -rm -f getdate.c y.tab.h kadmin_ct.c + +install:: + $(INSTCMD) kadmin.1 $(INSTALL_MANDIR)/cat1/kadmin.1 + +# needed until we run makedepend +kadmin_ct.c: kadmin_ct.ct +kadmin_ct.o: kadmin_ct.c diff --git a/src/kadmin/cli/attic/Makefile b/src/kadmin/cli/attic/Makefile new file mode 100644 index 000000000..79e432fe4 --- /dev/null +++ b/src/kadmin/cli/attic/Makefile @@ -0,0 +1,11 @@ +TOP = ../.. +include $(TOP)/config.mk/template + +SRCS = kadmin.c kadmin_ct.ct ss_wrapper.c getdate.y +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o +PROG = cli_secure_admin + +LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5_ALL) \ + $(LIBSS) $(LIBDYN) $(LIBDB) $(NDBMLIB) $(BSDLIB) $(NETLIB) + +expand NormalProgram diff --git a/src/kadmin/cli/attic/Makefile.in b/src/kadmin/cli/attic/Makefile.in new file mode 100644 index 000000000..160016af9 --- /dev/null +++ b/src/kadmin/cli/attic/Makefile.in @@ -0,0 +1,45 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) $(OVSECINC) +OVSECROOT=/home/tlyu/ovsecure +OVSECSTAGE=/afs/dev.mit.edu/reference/ovsecure/sunos/stage +OVSECINC=-I$(OVSECROOT)/include -I$(OVSECSTAGE)/include +OVSECLIB=-L$(OVSECSTAGE)/lib -lclient -lcommon -lrpclib -ldyn +LDFLAGS = -g +LIBOBJS=@LIBOBJS@ +ISODELIB=$(OVSECROOT)/lib/libisode.a +COMERRLIB=$(OVSECROOT)/lib/libcom_err.a +SSLIB=$(OVSECSTAGE)/lib/libss.a +DBMLIB=$(OVSECROOT)/lib/libdb.a +KDBLIB=$(OVSECROOT)/lib/libkdb5.a + +all:: + +KLIB = $(OVSECROOT)/lib/libgssapi_krb5.a $(OVSECROOT)/lib/libkrb5.a $(OVSECROOT)/lib/libcrypto.a $(ISODELIB) $(SSLIB) $(COMERRLIB) $(DBMLIB) +DEPKLIB = $(TOPLIBD)/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COMERRLIB) $(DBMLIB) + +SRCS = + +OBJS = kadmin.o kadmin_ct.o ss_wrapper.o getdate.o $(LIBOBJS) + +all:: kadmin +kadmin.o: + $(CC) -c $(CCOPTS) $(OVSECINC) $(DEFS) kadmin.c +getdate.c getdate.h: getdate.y + $(RM) getdate.c getdate.h y.tab.* + $(YACC) -d $(srcdir)/getdate.y + $(MV) y.tab.c getdate.c + $(MV) y.tab.h getdate.h + +kadmin: $(OBJS) + $(CC) -o kadmin $(CFLAGS) $(OBJS) $(OVSECLIB) $(KLIB) $(LIBS) + +# needed until we run makedepend +kadmin_ct.c: kadmin_ct.ct + +kadmin_ct.o: kadmin_ct.c + +clean:: + $(RM) kadmin $(OBJS) kadmin_ct.c getdate.c getdate.h y.tab.c y.tab.h + +# testing rule for building getdate +getdate: getdate.c + $(CC) -o getdate $(CFLAGS) -DTEST getdate.c diff --git a/src/kadmin/cli/attic/configure.in b/src/kadmin/cli/attic/configure.in new file mode 100644 index 000000000..8cca51316 --- /dev/null +++ b/src/kadmin/cli/attic/configure.in @@ -0,0 +1,15 @@ +AC_INIT(getdate.y) +WITH_CCOPTS +CONFIG_RULES +AC_SET_BUILDTOP +AC_PROG_INSTALL +AC_PROG_YACC +AC_HAVE_HEADERS(unistd.h sys/timeb.h alloca.h) +AC_HAVE_FUNCS(ftime timezone) +AC_CHECK_LIB(ndbm,main) +AC_CHECK_LIB(dbm,main) +AC_REPLACE_FUNCS([setenv memmove]) +KRB_INCLUDE +ISODE_INCLUDE +WITH_KRB5ROOT +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/cli/attic/getdate.y b/src/kadmin/cli/attic/getdate.y new file mode 100644 index 000000000..6b03e73bb --- /dev/null +++ b/src/kadmin/cli/attic/getdate.y @@ -0,0 +1,1006 @@ +%{ +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; +** send any email to Rich. +** +** This grammar has nine shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#ifdef HAVE_CONFIG_H +#if defined (emacs) || defined (CONFIG_BROKETS) +#include <config.h> +#else +#include "config.h" +#endif +#endif + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +#undef static +#endif + +/* The following block of alloca-related preprocessor directives is here + solely to allow compilation by non GNU-C compilers of the C parser + produced from this file by old versions of bison. Newer versions of + bison include a block similar to this one in bison.simple. */ + +#ifdef __GNUC__ +#undef alloca +#define alloca __builtin_alloca +#else +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#else +#ifdef _AIX /* for Bison */ + #pragma alloca +#else +void *alloca (); +#endif +#endif +#endif + +#include <stdio.h> +#include <ctype.h> + +/* The code at the top of get_date which figures out the offset of the + current time zone checks various CPP symbols to see if special + tricks are need, but defaults to using the gettimeofday system call. + Include <sys/time.h> if that will be used. */ + +#if defined(vms) + +#include <types.h> +#include <time.h> + +#else + +#include <sys/types.h> + +#ifdef TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#ifdef timezone +#undef timezone /* needed for sgi */ +#endif + +#if defined(HAVE_SYS_TIMEB_H) +#include <sys/timeb.h> +#else +/* +** We use the obsolete `struct timeb' as part of our interface! +** Since the system doesn't have it, we define it here; +** our callers must do likewise. +*/ +struct timeb { + time_t time; /* Seconds since the epoch */ + unsigned short millitm; /* Field not used */ + short timezone; /* Minutes west of GMT */ + short dstflag; /* Field not used */ +}; +#endif /* defined(HAVE_SYS_TIMEB_H) */ + +#endif /* defined(vms) */ + +#if defined (STDC_HEADERS) || defined (USG) +#include <string.h> +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +extern struct tm *gmtime(); +extern struct tm *localtime(); + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yylex (); +static int yyerror (); + +#if !defined(lint) && !defined(SABER) +static char RCS[] = + "$Header$"; +#endif /* !defined(lint) && !defined(SABER) */ + + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type <Meridian> tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL } +}; + + + + +/* ARGSUSED */ +static int +yyerror(s) + char *s; +{ + return 0; +} + + +static time_t +ToSeconds(Hours, Minutes, Seconds, Meridian) + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +static time_t +Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) + time_t Month; + time_t Day; + time_t Year; + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; + DSTMODE DSTmode; +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + if (Year < EPOCH || Year > 1999 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(Start, Future) + time_t Start; + time_t Future; +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(Start, DayOrdinal, DayNumber) + time_t Start; + time_t DayOrdinal; + time_t DayNumber; +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(Start, RelMonth) + time_t Start; + time_t RelMonth; +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(buff) + char *buff; +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static time_t +difftm(a, b) + struct tm *a, *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + return + ( + ( + ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (time_t)(ay-by) * 365 + )*24 + (a->tm_hour - b->tm_hour) + )*60 + (a->tm_min - b->tm_min) + )*60 + (a->tm_sec - b->tm_sec); +} + +time_t +get_date(p, now) + char *p; + struct timeb *now; +{ + struct tm *tm, gmt; + struct timeb ftz; + time_t Start; + time_t tod; + + yyInput = p; + if (now == NULL) { + now = &ftz; + (void)time(&ftz.time); + + if (! (tm = gmtime (&ftz.time))) + return -1; + gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ + ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60; + } + + tm = localtime(&now->time); + yyYear = tm->tm_year; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = now->timezone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = now->time; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff, (struct timeb *)NULL); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/src/kadmin/cli/attic/kadmin.c b/src/kadmin/cli/attic/kadmin.c new file mode 100644 index 000000000..91d2a71e4 --- /dev/null +++ b/src/kadmin/cli/attic/kadmin.c @@ -0,0 +1,958 @@ +/* + * Copyright 1994 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * kadmin.c: base functions for a kadmin command line interface using + * the OVSecure library + */ + +#include <krb5/krb5.h> +#include <krb5/los-proto.h> +#include <krb5/ext-proto.h> +#include <krb5/kdb.h> +#include <ovsec_admin/admin.h> +#include <stdio.h> +#include <sys/types.h> +#include <math.h> +#include <unistd.h> +#include <pwd.h> +#include <sys/timeb.h> + +/* special struct to convert flag names for principals + to actual krb5_flags for a principal */ +struct pflag { + char *flagname; /* name of flag as typed to CLI */ + int flaglen; /* length of string (not counting -,+) */ + krb5_flags theflag; /* actual principal flag to set/clear */ + int set; /* 0 means clear, 1 means set (on '-') */ +}; + +static struct pflag flags[] = { +{"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1}, +{"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1}, +{"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1}, +{"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1}, +{"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1}, +{"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1}, +{"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1}, +{"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0}, +{"requres_hwauth", 14, KRB5_KDB_REQUIRES_HW_AUTH, 0}, +{"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0}, +{"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, +{"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 } +}; + +static char *prflags[] = { + "DISALLOW_POSTDATED", /* 0x00000001 */ + "DISALLOW_FORWARDABLE", /* 0x00000002 */ + "DISALLOW_TGT_BASED", /* 0x00000004 */ + "DISALLOW_RENEWABLE", /* 0x00000008 */ + "DISALLOW_PROXIABLE", /* 0x00000010 */ + "DISALLOW_DUP_SKEY", /* 0x00000020 */ + "DISALLOW_ALL_TIX", /* 0x00000040 */ + "REQUIRES_PRE_AUTH", /* 0x00000080 */ + "REQUIRES_HW_AUTH", /* 0x00000100 */ + "REQUIRES_PWCHANGE", /* 0x00000200 */ + "UNKNOWN_0x00000400", /* 0x00000400 */ + "UNKNOWN_0x00000800", /* 0x00000800 */ + "DISALLOW_SVR", /* 0x00001000 */ + "PWCHANGE_SERVICE" /* 0x00002000 */ +}; + +char *getenv(); +struct passwd *getpwuid(); +int exit_status = 0; +char *def_realm = NULL; + +void *ovsec_hndl = NULL; + +void usage() +{ + fprintf(stderr, + "usage: kadmin [-r realm] [-p principal] [-k keytab] [-q query]\n"); + exit(1); +} + +/* this is a wrapper to go around krb5_parse_principal so we can set + the default realm up properly */ +krb5_error_code kadmin_parse_name(name, principal) + char *name; + krb5_principal *principal; +{ + char *cp, *fullname; + krb5_error_code retval; + + /* assumes def_realm is initialized! */ + fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1); + if (fullname == NULL) + return ENOMEM; + strcpy(fullname, name); + cp = strchr(fullname, '@'); + while (cp) { + if (cp - fullname && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '@'); + } + if (cp == NULL) { + strcat(fullname, "@"); + strcat(fullname, def_realm); + } + retval = krb5_parse_name(fullname, principal); + free(fullname); + return retval; +} + +char *kadmin_startup(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + char *princstr = NULL, *keytab = NULL, *query = NULL; + char *luser, *canon, *cp; + int optchar, freeprinc = 0; + struct passwd *pw; + ovsec_kadm_ret_t retval; + krb5_ccache cc; + krb5_principal princ; + + while ((optchar = getopt(argc, argv, "r:p:k:q:")) != EOF) { + switch (optchar) { + case 'r': + def_realm = optarg; + break; + case 'p': + princstr = optarg; + break; + case 'k': + fprintf(stderr, "kadmin: -k not supported yet\n"); + exit(1); + break; + case 'q': + query = optarg; + break; + default: + usage(); + } + } + if (def_realm == NULL && krb5_get_default_realm(&def_realm)) { + if (freeprinc) + free(princstr); + fprintf(stderr, "kadmin: unable to get default realm\n"); + exit(1); + } + if (princstr == NULL) { + if (!krb5_cc_default(&cc) && !krb5_cc_get_principal(cc, &princ)) { + char *realm = NULL; + if (krb5_unparse_name(princ, &canon)) { + fprintf(stderr, + "kadmin: unable to canonicalize principal\n"); + krb5_free_principal(princ); + exit(1); + } + /* strip out realm of principal if it's there */ + realm = strchr(canon, '@'); + while (realm) { + if (realm - canon && *(realm - 1) != '\\') + break; + else + realm = strchr(realm, '@'); + } + if (realm) + *realm++ = '\0'; + cp = strchr(canon, '/'); + while (cp) { + if (cp - canon && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '/'); + } + if (cp != NULL) + *cp = '\0'; + princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ + + (realm ? 1 + strlen(realm) : 0) + 1); + if (princstr == NULL) { + fprintf(stderr, "kadmin: out of memory\n"); + exit(1); + } + strcpy(princstr, canon); + strcat(princstr, "/admin"); + if (realm) { + strcat(princstr, "@"); + strcat(princstr, realm); + } + free(canon); + krb5_free_principal(princ); + freeprinc++; + } else if (luser = getenv("USER")) { + princstr = malloc(strlen(luser) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "kadmin: out of memory\n"); + exit(1); + } + strcpy(princstr, luser); + strcat(princstr, "/admin"); + strcat(princstr, "@"); + strcat(princstr, def_realm); + freeprinc++; + } else if (pw = getpwuid(getuid())) { + princstr = malloc(strlen(pw->pw_name) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "kadmin: out of memory\n"); + exit(1); + } + strcpy(princstr, pw->pw_name); + strcat(princstr, "/admin@"); + strcat(princstr, def_realm); + freeprinc++; + } else { + fprintf(stderr, "kadmin: unable to figure out a principal name\n"); + exit(1); + } + } + retval = ovsec_kadm_init_with_password(princstr, NULL, + OVSEC_KADM_ADMIN_SERVICE, + def_realm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_hndl); + if (freeprinc) + free(princstr); + if (retval) { /* assume kadm_init does init_ets() */ + com_err("kadmin", retval, "while initializing kadmin interface"); + exit(1); + } + return query; +} + +int quit() +{ + ovsec_kadm_destroy(ovsec_hndl); + /* insert more random cleanup here */ +} + +void kadmin_delprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_ret_t retval; + krb5_principal princ; + char *canon; + char reply[5]; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "delete_principal: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) { + fprintf(stderr, "delete_principal: bad arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("delete_principal", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(princ, &canon); + if (retval) { + com_err("delete_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(princ); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the principal \"%s\"? (yes/no): ", canon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Principal \"%s\" not deleted\n", canon); + free(canon); + krb5_free_principal(princ); + return; + } + } + retval = ovsec_kadm_delete_principal(ovsec_hndl, princ); + krb5_free_principal(princ); + if (retval) { + com_err("delete_principal", retval, + "while deleteing principal \"%s\"", canon); + free(canon); + return; + } + printf("Principal \"%s\" deleted.\nMake sure that you have removed this principal from all ACLs before reusing.\n", canon); + free(canon); + return; +} + +void kadmin_renprinc(argc, argv) + int argc; + char *argv[]; +{ + krb5_principal oldprinc, newprinc; + char *oldcanon, *newcanon; + char reply[5]; + ovsec_kadm_ret_t retval; + + if (argc < 3 || argc > 4) { + fprintf(stderr, "rename_principal: wrong number of arguments\n"); + return; + } + if (argc == 4 && + (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) { + fprintf(stderr, "rename_principal: bad arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 2], &oldprinc); + if (retval) { + com_err("rename_principal", retval, "while parsing old principal"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &newprinc); + if (retval) { + krb5_free_principal(oldprinc); + com_err("rename_principal", retval, "while parsing new principal"); + return; + } + retval = krb5_unparse_name(oldprinc, &oldcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing old principal"); + krb5_free_principal(newprinc); + krb5_free_principal(oldprinc); + return; + } + retval = krb5_unparse_name(newprinc, &newcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing new principal"); + free(oldcanon); + krb5_free_principal(newprinc); + krb5_free_principal(oldprinc); + return; + } + if (argc == 3) { + printf("Are you sure you want to rename the principal \"%s\" to \"%s\"? (yes/no): ", + oldcanon, newcanon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, + "rename_principal: \"%s\" NOT renamed to \"%s\".\n", + oldcanon, newcanon); + free(newcanon); + free(oldcanon); + krb5_free_principal(newprinc); + krb5_free_principal(oldprinc); + return; + } + } + retval = ovsec_kadm_rename_principal(ovsec_hndl, oldprinc, newprinc); + krb5_free_principal(oldprinc); + krb5_free_principal(newprinc); + if (retval) { + com_err("rename_principal", retval, + "while renaming \"%s\" to \"%s\".", oldcanon, + newcanon); + free(newcanon); + free(oldcanon); + return; + } + printf("Principal \"%s\" renamed to \"%s\".\nMake sure that you have removed \"%s\" from all ACLs before reusing.\n", + oldcanon, newcanon, newcanon); + return; +} + +void kadmin_cpw(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_ret_t retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + char *canon; + krb5_principal princ; + + if (argc < 2 || argc > 4) { + fprintf(stderr, "change_password: too many arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("change_password", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(princ, &canon); + if (retval) { + com_err("change_password", retval, "while canonicalizing principal"); + krb5_free_principal(princ); + return; + } + if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) { + retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, argv[2]); + krb5_free_principal(princ); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } else if ((argc == 3) && (strlen(argv[1]) == 8) && + !strcmp("-randkey", argv[1])) { + krb5_keyblock *newkey = NULL; + retval = ovsec_kadm_randkey_principal(ovsec_hndl, princ, &newkey); + krb5_free_principal(princ); + if (retval) { + com_err("change_password", retval, + "while randomizing key for \"%s\".", canon); + free(canon); + return; + } + memset(newkey->contents, 0, newkey->length); + printf("Key for \"%s\" randomized.\n", canon); + free(canon); + return; + } else if (argc == 2) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + argv[1]); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + argv[1]); + retval = krb5_read_password(prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("change_password", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(princ); + return; + } + retval = ovsec_kadm_chpass_principal(ovsec_hndl, princ, newpw); + krb5_free_principal(princ); + memset(newpw, 0, sizeof (newpw)); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } + fprintf(stderr, "change_password: bad arguments\n"); + free(canon); + krb5_free_principal(princ); + return; +} + +int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, caller) + int argc; + char *argv[]; + ovsec_kadm_principal_ent_t oprinc; + u_int32 *mask; + char **pass, *caller; +{ + int i, j; + struct timeb now; + krb5_error_code retval; + + *mask = 0; + *pass = NULL; + ftime(&now); + for (i = 1; i < argc - 1; i++) { + if (strlen(argv[i]) == 7 && + !strcmp("-expire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->princ_expire_time = get_date(argv[i], now); + *mask |= OVSEC_KADM_PRINC_EXPIRE_TIME; + continue; + } + } + if (strlen(argv[i]) == 9 && + !strcmp("-pwexpire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->pw_expiration = get_date(argv[i], now); + *mask |= OVSEC_KADM_PW_EXPIRATION; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-maxlife", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->max_life = get_date(argv[i], now) - now.time; + *mask |= OVSEC_KADM_MAX_LIFE; + continue; + } + } + if (strlen(argv[i]) == 5 && + !strcmp("-kvno", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->kvno = atoi(argv[i]); + *mask |= OVSEC_KADM_KVNO; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-policy", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->policy = argv[i]; + *mask |= OVSEC_KADM_POLICY; + continue; + } + } + if (strlen(argv[i]) == 12 && + !strcmp("-clearpolicy", argv[i])) { + oprinc->policy = NULL; + *mask |= OVSEC_KADM_POLICY_CLR; + continue; + } + if (strlen(argv[i]) == 3 && + !strcmp("-pw", argv[i])) { + if (++i > argc - 2) + return -1; + else { + *pass = argv[i]; + continue; + } + } + for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) { + if (strlen(argv[i]) == flags[j].flaglen + 1 && + !strcmp(flags[j].flagname, + &argv[i][1] /* strip off leading + or - */)) { + if (flags[j].set && argv[i][0] == '-' || + !flags[j].set && argv[i][0] == '+') { + oprinc->attributes |= flags[j].theflag; + *mask |= OVSEC_KADM_ATTRIBUTES; + break; + } else if (flags[j].set && argv[i][0] == '+' || + !flags[j].set && argv[i][0] == '-') { + oprinc->attributes &= ~flags[j].theflag; + *mask |= OVSEC_KADM_ATTRIBUTES; + break; + } else { + return -1; + } + } + } + return -1; + } + if (i != argc - 1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } + retval = kadmin_parse_name(argv[i], &oprinc->principal); + if (retval) { + com_err(caller, retval, "while parsing principal"); + return -1; + } + return 0; +} + +void kadmin_addprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_principal_ent_rec princ; + u_int32 mask; + char *pass, *canon; + krb5_error_code retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + + princ.attributes = 0; + if (kadmin_parse_princ_args(argc, argv, + &princ, &mask, &pass, "add_principal")) { + fprintf(stderr, "add_principal: bad arguments\n"); + return; + } + retval = krb5_unparse_name(princ.principal, &canon); + if (retval) { + com_err("add_principal", + retval, "while canonicalizing principal"); + krb5_free_principal(princ.principal); + return; + } + if (pass == NULL) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + argv[1]); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + argv[1]); + retval = krb5_read_password(prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("add_principal", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(princ.principal); + return; + } + pass = newpw; + } + mask |= OVSEC_KADM_PRINCIPAL; + retval = ovsec_kadm_create_principal(ovsec_hndl, &princ, mask, pass); + krb5_free_principal(princ.principal); + if (retval) { + com_err("add_principal", retval, "while creating \"%s\".", + canon); + free(canon); + return; + } + printf("Principal \"%s\" created.\n", canon); + free(canon); +} + +void kadmin_modprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_principal_ent_rec princ; + u_int32 mask; + krb5_error_code retval; + char *pass, *canon; + + princ.attributes = 0; + if (kadmin_parse_princ_args(argc, argv, + &princ, &mask, &pass, "modify_principal")) { + fprintf(stderr, "modify_principal: bad arguments\n"); + return; + } + retval = krb5_unparse_name(princ.principal, &canon); + if (retval) { + com_err("modify_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(princ.principal); + return; + } + retval = ovsec_kadm_modify_principal(ovsec_hndl, &princ, mask); + if (retval) { + com_err("modify_principal", retval, "while modifying \"%s\".", + argv[argc - 1]); + return; + } +} + +void kadmin_getprinc(argc, argv) + int argc; + char *argv[]; +{ + ovsec_kadm_principal_ent_t dprinc; + krb5_principal princ; + krb5_error_code retval; + char *canon, *modcanon; + int i; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "get_principal: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-terse", argv[1]) : 1)) { + fprintf(stderr, "get_principal: bad arguments\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("get_principal", retval, "while parsing principal"); + return; + } + retval = krb5_unparse_name(princ, &canon); + if (retval) { + com_err("get_principal", retval, "while canonicalizing principal"); + krb5_free_principal(princ); + return; + } + retval = ovsec_kadm_get_principal(ovsec_hndl, princ, &dprinc); + krb5_free_principal(princ); + if (retval) { + com_err("get_principal", retval, "while retrieving \"%s\".", canon); + free(canon); + return; + } + retval = krb5_unparse_name(dprinc->mod_name, &modcanon); + if (retval) { + com_err("get_principal", retval, "while unparsing modname"); + ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc); + free(canon); + return; + } + if (argc == 2) { + printf("Principal: %s\n", canon); + printf("Expiration date: %d\n", dprinc->princ_expire_time); + printf("Last password change: %d\n", dprinc->last_pwd_change); + printf("Password expiration date: %d\n", dprinc->pw_expiration); + printf("Maximum life: %d\n", dprinc->max_life); + printf("Last modified: by %s\n\ton %d\n", + modcanon, dprinc->mod_date); + printf("Attributes: "); + for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { + if (dprinc->attributes & (krb5_flags) 1 << i) + printf(" %s", prflags[i]); + } + printf("\n"); + printf("Key version: %d\n", dprinc->kvno); + printf("Master key version: %d\n", dprinc->mkvno); + printf("Policy: %s\n", dprinc->policy); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\n", + canon, dprinc->princ_expire_time, dprinc->last_pwd_change, + dprinc->pw_expiration, dprinc->max_life, modcanon, + dprinc->mod_date, dprinc->attributes, dprinc->kvno, + dprinc->mkvno, dprinc->policy); + } + free(modcanon); + ovsec_kadm_free_principal_ent(ovsec_hndl, dprinc); + free(canon); +} + +int kadmin_parse_policy_args(argc, argv, policy, mask, caller) + int argc; + char *argv[]; + ovsec_kadm_policy_ent_t policy; + u_int32 *mask; + char *caller; +{ + int i; + struct timeb now; + krb5_error_code retval; + + ftime(&now); + *mask = 0; + for (i = 1; i < argc - 1; i++) { + if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-maxlife")) { + if (++i > argc -2) + return -1; + else { + policy->pw_max_life = get_date(argv[i], now) - now.time; + *mask |= OVSEC_KADM_PW_MAX_LIFE; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-minlife")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_life = get_date(argv[i], now) - now.time; + *mask |= OVSEC_KADM_PW_MIN_LIFE; + continue; + } + } else if (strlen(argv[i]) == 10 && + !strcmp(argv[i], "-minlength")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_length = atoi(argv[i]); + *mask |= OVSEC_KADM_PW_MIN_LENGTH; + continue; + } + } else if (strlen(argv[i]) == 11 && + !strcmp(argv[i], "-minclasses")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_classes = atoi(argv[i]); + *mask |= OVSEC_KADM_PW_MIN_CLASSES; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-history")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_history_num = atoi(argv[i]); + *mask |= OVSEC_KADM_PW_HISTORY_NUM; + continue; + } + } else + return -1; + } + if (i != argc -1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } else + return 0; +} + +void kadmin_addpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + u_int32 mask; + ovsec_kadm_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) { + fprintf(stderr, "add_policy: bad arguments\n"); + return; + } else { + policy.policy = argv[argc - 1]; + mask |= OVSEC_KADM_POLICY; + retval = ovsec_kadm_create_policy(ovsec_hndl, &policy, mask); + if (retval) { + com_err("add_policy", retval, "while creating policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_modpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + u_int32 mask; + ovsec_kadm_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, + "modify_policy")) { + fprintf(stderr, "modify_policy: bad arguments\n"); + return; + } else { + policy.policy = argv[argc - 1]; + retval = ovsec_kadm_modify_policy(ovsec_hndl, &policy, mask); + if (retval) { + com_err("modify_policy", retval, "while modifying policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_delpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char reply[5]; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "delete_policy: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-force", argv[1]) : 1)) { + fprintf(stderr, "delete_policy: bad arguments\n"); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the policy \"%s\"? (yes/no): ", argv[1]); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Policy \"%s\" not deleted.\n", argv[1]); + return; + } + } + retval = ovsec_kadm_delete_policy(ovsec_hndl, argv[argc - 1]); + if (retval) { + com_err("delete_policy:", retval, "while deleting policy \"%s\"", + argv[argc - 1]); + return; + } + return; +} + +void kadmin_getpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + ovsec_kadm_policy_ent_t policy; + + if (argc < 2 || argc > 3) { + fprintf(stderr, "get_policy: wrong number of arguments\n"); + return; + } + if (argc == 3 && + (strlen(argv[1]) == 6 ? strcmp("-terse", argv[1]) : 1)) { + fprintf(stderr, "get_policy: bad arguments\n"); + return; + } + retval = ovsec_kadm_get_policy(ovsec_hndl, argv[argc - 1], &policy); + if (retval) { + com_err("get_policy", retval, "while retrieving policy \"%s\".", + argv[argc - 1]); + return; + } + if (argc == 2) { + printf("Policy: %s\n", policy->policy); + printf("Maximum password life: %d\n", policy->pw_max_life); + printf("Minimum password life: %d\n", policy->pw_min_life); + printf("Minimum password length: %d\n", policy->pw_min_length); + printf("Minimum number of password character classes: %d\n", + policy->pw_min_classes); + printf("Number of old keys kept: %d\n", policy->pw_history_num); + printf("Reference count: %d\n", policy->policy_refcnt); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n", + policy->policy, policy->pw_max_life, policy->pw_min_life, + policy->pw_min_length, policy->pw_min_classes, + policy->pw_history_num, policy->policy_refcnt); + } + ovsec_kadm_free_policy_ent(ovsec_hndl, policy); + return; +} + +kadmin_getprivs(argc, argv) + int argc; + char *argv[]; +{ + static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE"}; + krb5_error_code retval; + int i; + u_int32 plist; + + if (argc != 1) { + fprintf(stderr, "get_privs: bad arguments\n"); + return; + } + retval = ovsec_kadm_get_privs(ovsec_hndl, &plist); + if (retval) { + com_err("get_privs", retval, "while retrieving privileges"); + return; + } + printf("current privileges:"); + for (i = 0; i < sizeof (privs) / sizeof (char *); i++) { + if (plist & 1 << i) + printf(" %s", privs[i]); + } + printf("\n"); + return; +} diff --git a/src/kadmin/cli/attic/kadmin_ct.ct b/src/kadmin/cli/attic/kadmin_ct.ct new file mode 100644 index 000000000..f5a67ed53 --- /dev/null +++ b/src/kadmin/cli/attic/kadmin_ct.ct @@ -0,0 +1,67 @@ +# Copyright 1994 by the Massachusetts Institute of Technology. +# All Rights Reserved. +# +# Export of this software from the United States of America may +# require a specific license from the United States Government. +# It is the responsibility of any person or organization contemplating +# export to obtain such a license before exporting. +# +# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and +# distribute this software and its documentation for any purpose and +# without fee is hereby granted, provided that the above copyright +# notice appear in all copies and that both that copyright notice and +# this permission notice appear in supporting documentation, and that +# the name of M.I.T. not be used in advertising or publicity pertaining +# to distribution of the software without specific, written prior +# permission. M.I.T. makes no representations about the suitability of +# this software for any purpose. It is provided "as is" without express +# or implied warranty. +# +# +# Command table for kadmin CLI for OVSecure +# + +command_table kadmin_cmds; + +request kadmin_addprinc, "Add principal", + add_prinicpal, addprinc, ank; + +request kadmin_delprinc, "Delete principal", + delete_principal, delprinc; + +request kadmin_modprinc, "Modify principal", + modify_principal, modprinc; + +request kadmin_renprinc, "Rename principal", + rename_principal, renprinc; + +request kadmin_cpw, "Change password", + change_password, cpw; + +request kadmin_getprinc, "Get principal", + get_principal, getprinc; + +request kadmin_addpol, "Add policy", + add_policy, addpol; + +request kadmin_modpol, "Modify policy", + modify_policy, modpol; + +request kadmin_delpol, "Delete policy", + delete_policy, delpol; + +request kadmin_getpol, "Get policy", + get_policy, getpol; + +request kadmin_getprivs, "Get privileges", + get_privs, getprivs; + +# list_requests is generic -- unrelated to Kerberos +request ss_list_requests, "List available requests.", + list_requests, lr, "?"; + +request ss_quit, "Exit program.", + quit, exit, q; + +end; + diff --git a/src/kadmin/cli/attic/memmove.c b/src/kadmin/cli/attic/memmove.c new file mode 100644 index 000000000..abc91e923 --- /dev/null +++ b/src/kadmin/cli/attic/memmove.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define MEMMOVE + +/* based on @(#)bcopy.c 5.11 (Berkeley) 6/21/91 */ + +#include <krb5/osconf.h> +#include <krb5/config.h> +#ifdef USE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +/* + * sizeof(word) MUST BE A POWER OF TWO + * SO THAT wmask BELOW IS ALL ONES + */ +typedef int word; /* "word" used for optimal copy speed */ + +#define wsize sizeof(word) +#define wmask (wsize - 1) + +/* + * Copy a block of memory, handling overlap. + * This is the routine that actually implements + * (the portable versions of) bcopy, memcpy, and memmove. + */ +#ifdef MEMCOPY +void * +memcpy(dst0, src0, length) +#else +#ifdef MEMMOVE +void * +memmove(dst0, src0, length) +#else +void +bcopy(src0, dst0, length) +#endif +#endif + void *dst0; + const void *src0; + register size_t length; +{ + register char *dst = dst0; + register const char *src = src0; + register size_t t; + + if (length == 0 || dst == src) /* nothing to do */ + goto done; + + /* + * Macros: loop-t-times; and loop-t-times, t>0 + */ +#define TLOOP(s) if (t) TLOOP1(s) +#define TLOOP1(s) do { s; } while (--t) + + if ((unsigned long)dst < (unsigned long)src) { + /* + * Copy forward. + */ + t = (int)src; /* only need low bits */ + if ((t | (int)dst) & wmask) { + /* + * Try to align operands. This cannot be done + * unless the low bits match. + */ + if ((t ^ (int)dst) & wmask || length < wsize) + t = length; + else + t = wsize - (t & wmask); + length -= t; + TLOOP1(*dst++ = *src++); + } + /* + * Copy whole words, then mop up any trailing bytes. + */ + t = length / wsize; + TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); + t = length & wmask; + TLOOP(*dst++ = *src++); + } else { + /* + * Copy backwards. Otherwise essentially the same. + * Alignment works as before, except that it takes + * (t&wmask) bytes to align, not wsize-(t&wmask). + */ + src += length; + dst += length; + t = (int)src; + if ((t | (int)dst) & wmask) { + if ((t ^ (int)dst) & wmask || length <= wsize) + t = length; + else + t &= wmask; + length -= t; + TLOOP1(*--dst = *--src); + } + t = length / wsize; + TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); + t = length & wmask; + TLOOP(*--dst = *--src); + } +done: +#if defined(MEMCOPY) || defined(MEMMOVE) + return (dst0); +#else + return; +#endif +} diff --git a/src/kadmin/cli/attic/setenv.c b/src/kadmin/cli/attic/setenv.c new file mode 100644 index 000000000..a2432c3d6 --- /dev/null +++ b/src/kadmin/cli/attic/setenv.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* based on @(#)setenv.c 8.1 (Berkeley) 6/4/93 */ +/* based on @(#)getenv.c 8.1 (Berkeley) 6/4/93 */ + +#ifndef __STDC__ +#define const +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#ifndef __P +#define __P(x) () +#endif +char *__findenv __P((const char *, int *)); + +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(name, value, rewrite) + register const char *name; + register const char *value; + int rewrite; +{ + extern char **environ; + static int alloced; /* if allocated space before */ + register char *c; + int l_value, offset; + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + if ((c = __findenv(name, &offset))) { /* find if already exists */ + if (!rewrite) + return (0); + if (strlen(c) >= l_value) { /* old larger; copy over */ + while (*c++ = *value++); + return (0); + } + } else { /* create new slot */ + register int cnt; + register char **p; + + for (p = environ, cnt = 0; *p; ++p, ++cnt); + if (alloced) { /* just increase size */ + environ = (char **)realloc((char *)environ, + (size_t)(sizeof(char *) * (cnt + 2))); + if (!environ) + return (-1); + } + else { /* get new space */ + alloced = 1; /* copy old entries into it */ + p = (char **)malloc((size_t)(sizeof(char *) * (cnt + 2))); + if (!p) + return (-1); + memcpy(p, environ, cnt * sizeof(char *)); + environ = p; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */ + if (!(environ[offset] = /* name + `=' + value */ + malloc((size_t)((int)(c - name) + l_value + 2)))) + return (-1); + for (c = environ[offset]; (*c = *name++) && *c != '='; ++c); + for (*c++ = '='; *c++ = *value++;); + return (0); +} + +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(name) + const char *name; +{ + extern char **environ; + register char **p; + int offset; + + while (__findenv(name, &offset)) /* if set multiple times */ + for (p = &environ[offset];; ++p) + if (!(*p = *(p + 1))) + break; +} + +/* + * getenv -- + * Returns ptr to value associated with name, if any, else NULL. + */ +char * +getenv(name) + const char *name; +{ + int offset; + + return (__findenv(name, &offset)); +} + +/* + * __findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + */ +static char * +__findenv(name, offset) + register const char *name; + int *offset; +{ + extern char **environ; + register int len; + register const char *np; + register char **p, *c; + + if (name == NULL || environ == NULL) + return (NULL); + for (np = name; *np && *np != '='; ++np) + continue; + len = np - name; + for (p = environ; (c = *p) != NULL; ++p) + if (strncmp(c, name, len) == 0 && c[len] == '=') { + *offset = p - environ; + return (c + len + 1); + } + return (NULL); +} diff --git a/src/kadmin/cli/attic/ss_wrapper.c b/src/kadmin/cli/attic/ss_wrapper.c new file mode 100644 index 000000000..f7bbda516 --- /dev/null +++ b/src/kadmin/cli/attic/ss_wrapper.c @@ -0,0 +1,56 @@ +/* + * Copyright 1994 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * ss wrapper for kadmin + */ + +#include <krb5/krb5.h> +#include <ss/ss.h> +#include <stdio.h> + +extern ss_request_table kadmin_cmds; +extern int exit_status; + +int main(argc, argv) + int argc; + char *argv[]; +{ + char *request; + krb5_error_code retval; + int sci_idx, code = 0; + + request = kadmin_startup(argc, argv); + sci_idx = ss_create_invocation("kadmin", "5.0", (char *) NULL, + &kadmin_cmds, &retval); + if (retval) { + ss_perror(sci_idx, retval, "creating invocation"); + exit(1); + } + if (request) { + (void) ss_execute_line(sci_idx, request, &code); + if (code != 0) { + ss_perror(sci_idx, code, request); + exit_status++; + } + } else + ss_listen(sci_idx, &retval); + return quit() ? 1 : exit_status; +} diff --git a/src/kadmin/cli/configure.in b/src/kadmin/cli/configure.in new file mode 100644 index 000000000..713d7d212 --- /dev/null +++ b/src/kadmin/cli/configure.in @@ -0,0 +1,20 @@ +AC_INIT(getdate.y) +WITH_CCOPTS +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_YACC +AC_HAVE_HEADERS(unistd.h sys/timeb.h alloca.h) +AC_HAVE_FUNCS(ftime timezone) +AC_CHECK_LIB(ndbm,main) +AC_CHECK_LIB(dbm,main) +AC_REPLACE_FUNCS([setenv memmove strftime]) +KRB_INCLUDE +USE_KADMCLNT_LIBRARY +USE_GSSAPI_LIBRARY +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +USE_SS_LIBRARY +KRB5_LIBRARIES +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/cli/dump.c b/src/kadmin/cli/dump.c new file mode 100644 index 000000000..2c5e4e753 --- /dev/null +++ b/src/kadmin/cli/dump.c @@ -0,0 +1,1485 @@ +/* + * admin/edit/dump.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * Dump a KDC database. This file was originally written to be part + * of kdb5_edit but has now been adapted for kadmin. + */ + +#include <stdio.h> +#include <k5-int.h> +#include <kadm5/admin.h> + +struct dump_args { + char *programname; + FILE *ofile; + krb5_context context; + int verbose; +}; + +/* External data */ +extern int exit_status; +extern krb5_context context; +extern void *handle; + +/* Strings */ + +static const char k5beta5_dump_header[] = "kdb5_edit load_dump version 2.0\n"; +static const char k5_dump_header[] = "kdb5_edit load_dump version 3.0\n"; +static const char kadm5_dump_header[] = "kadm5 load_dump version 4.0\n"; + +static const char null_mprinc_name[] = "kdb5_dump@MISSING"; + +/* Message strings */ +static const char regex_err[] = "%s: regular expression error - %s\n"; +static const char regex_merr[] = "%s: regular expression match error - %s\n"; +static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n"; +static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n"; +static const char nokeys_err[] = "%s: cannot find any standard key for %s\n"; +static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n"; +static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n"; +static const char oflock_error[] = "%s: cannot lock %s (%s)\n"; +static const char dumprec_err[] = "%s: error performing %s dump (%s)\n"; +static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n"; +static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: "; +static const char read_name_string[] = "name string"; +static const char read_key_type[] = "key type"; +static const char read_key_data[] = "key data"; +static const char read_pr_data1[] = "first set of principal attributes"; +static const char read_mod_name[] = "modifier name"; +static const char read_pr_data2[] = "second set of principal attributes"; +static const char read_salt_data[] = "salt data"; +static const char read_akey_type[] = "alternate key type"; +static const char read_akey_data[] = "alternate key data"; +static const char read_asalt_type[] = "alternate salt type"; +static const char read_asalt_data[] = "alternate salt data"; +static const char read_exp_data[] = "expansion data"; +static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n"; +static const char add_princ_fmt[] = "%s\n"; +static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n"; +static const char read_err_fmt[] = "%s(%d): cannot read %s\n"; +static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n"; +static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n"; +static const char err_line_fmt[] = "%s: error processing line %d of %s\n"; +static const char head_bad_fmt[] = "%s: dump header bad in %s\n"; +static const char read_bytecnt[] = "record byte count"; +static const char read_encdata[] = "encoded data"; +static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n"; +static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n"; +static const char read_nint_data[] = "principal static attributes"; +static const char read_tcontents[] = "tagged data contents"; +static const char read_ttypelen[] = "tagged data type and length"; +static const char read_kcontents[] = "key data contents"; +static const char read_ktypelen[] = "key data type and length"; +static const char read_econtents[] = "extra data contents"; +static const char k5beta5_fmt_name[] = "Kerberos version 5 old format"; +static const char k5beta6_fmt_name[] = "Kerberos version 5 beta 6 format"; +static const char lusage_err_fmt[] = "%s: usage is %s [%s] [%s] [%s] filename dbname\n"; +static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n"; +static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n"; +static const char stdin_name[] = "standard input"; +static const char restfail_fmt[] = "%s: %s restore failed\n"; +static const char close_err_fmt[] = "%s: cannot close database (%s)\n"; +static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n"; +static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n"; +static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n"; +static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n"; +static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n"; +static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n"; + +static const char oldoption[] = "-old"; +static const char verboseoption[] = "-verbose"; +static const char updateoption[] = "-update"; +static const char dump_tmptrail[] = "~"; + +/* Can't use krb5_dbe_find_enctype because we have a */ +/* kadm5_principal_ent_t and not a krb5_db_entry */ +static krb5_error_code +find_enctype(dbentp, enctype, salttype, kentp) + kadm5_principal_ent_rec *dbentp; + krb5_enctype enctype; + krb5_int32 salttype; + krb5_key_data **kentp; +{ + int i; + int maxkvno; + krb5_key_data *datap; + + maxkvno = -1; + datap = (krb5_key_data *) NULL; + for (i=0; i<dbentp->n_key_data; i++) { + if ((dbentp->key_data[i].key_data_type[0] == enctype) && + ((dbentp->key_data[i].key_data_type[1] == salttype) || + (salttype < 0))) { + maxkvno = dbentp->key_data[i].key_data_kvno; + datap = &dbentp->key_data[i]; + } + } + if (maxkvno >= 0) { + *kentp = datap; + return(0); + } + return(ENOENT); +} + + +/* + * dump_k5beta5_header() - Make a dump header that is recognizable by Kerberos + * Version 5 Beta 5 and previous releases. + */ +static krb5_error_code +dump_k5beta5_header(arglist) + struct dump_args *arglist; +{ + /* The old header consists of the leading string */ + fprintf(arglist->ofile, k5beta5_dump_header); + return(0); +} + + +/* + * dump_k5beta5_iterator() - Dump an entry in a format that is usable + * by Kerberos Version 5 Beta 5 and previous + * releases. + */ +static krb5_error_code +dump_k5beta5_iterator(ptr, name, entry) + krb5_pointer ptr; + char *name; + kadm5_principal_ent_rec *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *mod_name; + krb5_tl_data *pwchg; + krb5_key_data *pkey, *akey, nullkey; + int i; + + /* Initialize */ + arg = (struct dump_args *) ptr; + mod_name = (char *) NULL; + memset(&nullkey, 0, sizeof(nullkey)); + + /* + * Deserialize the modifier record. + */ + mod_name = (char *) NULL; + pkey = akey = (krb5_key_data *) NULL; + + /* + * Flatten the modifier name. + */ + if ((retval = krb5_unparse_name(arg->context, + entry->mod_name, + &mod_name))) + fprintf(stderr, mname_unp_err, arg->programname, + error_message(retval)); + + /* + * Find the 'primary' key and the 'alternate' key. + */ + if ((retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_NORMAL, + &pkey)) && + (retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + &akey))) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + return(retval); + } + + /* If we only have one type, then ship it out as the primary. */ + if (!pkey && akey) { + pkey = akey; + akey = &nullkey; + } + else { + if (!akey) + akey = &nullkey; + } + + /* + * First put out strings representing the length of the variable + * length data in this record, then the name and the primary key type. + */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name), + strlen(mod_name), + (krb5_int32) pkey->key_data_length[0], + (krb5_int32) akey->key_data_length[0], + (krb5_int32) pkey->key_data_length[1], + (krb5_int32) akey->key_data_length[1], + name, + (krb5_int32) pkey->key_data_type[0]); + for (i=0; i<pkey->key_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]); + } + /* + * Second, print out strings representing the standard integer + * data in this record. + */ + fprintf(arg->ofile, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t", + (krb5_int32) pkey->key_data_kvno, + entry->max_life, entry->max_renewable_life, + 1 /* Fake mkvno */, entry->princ_expire_time, entry->pw_expiration, + entry->last_pwd_change, entry->last_success, entry->last_failed, + entry->fail_auth_count, mod_name, entry->mod_date, + entry->attributes, pkey->key_data_type[1]); + + /* Pound out the salt data, if present. */ + for (i=0; i<pkey->key_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]); + } + /* Pound out the alternate key type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]); + for (i=0; i<akey->key_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]); + } + /* Pound out the alternate salt type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]); + for (i=0; i<akey->key_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]); + } + /* Pound out the expansion data. (is null) */ + for (i=0; i < 8; i++) { + fprintf(arg->ofile, "\t%u", 0); + } + fprintf(arg->ofile, ";\n"); + /* If we're blabbing, do it */ + if (arg->verbose) + fprintf(stderr, "%s\n", name); + krb5_xfree(mod_name); + + return(0); +} + + +/* + * dump_k5beta6_header() - Output the k5beta6 dump header. + */ +static krb5_error_code +dump_k5beta6_header(arglist) + struct dump_args *arglist; +{ + /* The k5beta6 header consists of the leading string */ + fprintf(arglist->ofile, k5_dump_header); + return(0); +} + + +/* + * dump_k5beta6_iterator() - Output a dump record in k5beta6 format. + */ +static krb5_error_code +dump_k5beta6_iterator(ptr, name, entry) + krb5_pointer ptr; + char *name; + kadm5_principal_ent_rec *entry; +{ + krb5_error_code retval = 0; + struct dump_args *arg; + krb5_tl_data *tlp, *etl; + krb5_key_data *kdata; + int counter, i, j; + + /* Initialize */ + arg = (struct dump_args *) ptr; + + /* + * We'd like to just blast out the contents as they would appear in + * the database so that we can just suck it back in, but it doesn't + * lend itself to easy editing. + */ + + /* + * The dump format is as follows: + * len strlen(name) n_tl_data n_key_data e_length + * name + * attributes max_life max_renewable_life expiration + * pw_expiration last_success last_failed fail_auth_count + * n_tl_data*[type length <contents>] + * n_key_data*[ver kvno ver*(type length <contents>)] + * <e_data> + * Fields which are not encapsulated by angle-brackets are to appear + * verbatim. Bracketed fields absence is indicated by a -1 in its + * place + */ + + /* + * Make sure that the tagged list is reasonably correct, and find + * E_DATA while we're at it. + */ + counter = 0; + etl = NULL; + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA) + etl = tlp; + counter++; + } + + if (counter == entry->n_tl_data) { + /* Pound out header */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", + KRB5_KDB_V1_BASE_LENGTH + (etl ? etl->tl_data_length : 0), + strlen(name), + (int) entry->n_tl_data, + (int) entry->n_key_data, + etl ? etl->tl_data_length : 0, + name); + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + entry->attributes, + entry->max_life, + entry->max_renewable_life, + entry->princ_expire_time, + entry->pw_expiration, + entry->last_success, + entry->last_failed, + entry->fail_auth_count); + /* Pound out tagged data. */ + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + /* skip E_DATA since it is included later */ + if (tlp->tl_data_type == KRB5_TL_KADM5_E_DATA) + continue; + + fprintf(arg->ofile, "%d\t%d\t", + (int) tlp->tl_data_type, + (int) tlp->tl_data_length); + if (tlp->tl_data_length) + for (i=0; i<tlp->tl_data_length; i++) + fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + + /* Pound out key data */ + for (counter=0; counter<entry->n_key_data; counter++) { + kdata = &entry->key_data[counter]; + fprintf(arg->ofile, "%d\t%d\t", + (int) kdata->key_data_ver, + (int) kdata->key_data_kvno); + for (i=0; i<kdata->key_data_ver; i++) { + fprintf(arg->ofile, "%d\t%d\t", + kdata->key_data_type[i], + kdata->key_data_length[i]); + if (kdata->key_data_length[i]) + for (j=0; j<kdata->key_data_length[i]; j++) + fprintf(arg->ofile, "%02x", + kdata->key_data_contents[i][j]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + } + + /* Pound out extra data */ + if (etl && etl->tl_data_length) + for (i=0; i<etl->tl_data_length; i++) + fprintf(arg->ofile, "%02x", etl->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + fprintf(stderr, "%s\n", name); + } + else { + fprintf(stderr, sdump_tl_inc_err, + arg->programname, name, counter, (int) entry->n_tl_data); + retval = EINVAL; + } + return(retval); +} + + +/* + * usage is: + * dump_db [-old] [-verbose] [filename|- [principals...]] + */ +void dump_db(argc, argv) + int argc; + char **argv; +{ + FILE *f; + struct dump_args arglist; + int error; + char *programname; + char *ofile; + krb5_error_code kret; + krb5_error_code (*dump_iterator) PROTOTYPE((krb5_pointer, + char *, + kadm5_principal_ent_rec *)); + krb5_error_code (*dump_header) PROTOTYPE((struct dump_args *)); + const char * dump_name; + int aindex, num, i; + krb5_boolean locked; + char **princs; + kadm5_principal_ent_rec princ_ent; + krb5_principal princ; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + ofile = (char *) NULL; + error = 0; + dump_iterator = dump_k5beta6_iterator; + dump_header = dump_k5beta6_header; + dump_name = k5beta6_fmt_name; + arglist.verbose = 0; + + /* + * Parse the qualifiers. + */ + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) { + dump_iterator = dump_k5beta5_iterator; + dump_header = dump_k5beta5_header; + dump_name = k5beta5_fmt_name; + } + else if (!strcmp(argv[aindex], verboseoption)) { + arglist.verbose++; + } + else + break; + } + + if (aindex < argc) { + ofile = argv[aindex]; + aindex++; + } + + /* this works because of the way aindex and argc are used below */ + if (aindex == argc) { + argv[aindex] = "*"; + argc++; + } + + locked = 0; + if (ofile) { + /* + * Make sure that we don't open and truncate on the fopen, + * since that may hose an on-going kprop process. + * + * We could also control this by opening for read and + * write, doing an flock with LOCK_EX, and then + * truncating the file once we have gotten the lock, + * but that would involve more OS dependencies than I + * want to get into. + */ + unlink(ofile); + if (!(f = fopen(ofile, "w"))) { + fprintf(stderr, ofopen_error, + programname, ofile, error_message(errno)); + exit_status++; + goto cleanup; + } + if ((kret = krb5_lock_file(context, + fileno(f), + KRB5_LOCKMODE_EXCLUSIVE))) { + fprintf(stderr, oflock_error, + programname, ofile, error_message(kret)); + exit_status++; + goto cleanup; + } + else + locked = 1; + } else { + f = stdout; + } + + arglist.programname = programname; + arglist.ofile = f; + arglist.context = context; + + if (kret = (*dump_header)(&arglist)) { + fprintf(stderr, dumphdr_err, + programname, dump_name, error_message(kret)); + exit_status++; + goto cleanup; + } + + while (aindex < argc) { + if (kret = kadm5_get_principals(handle, argv[aindex], + &princs, &num)) { + fprintf(stderr, "%s: error retrieving principals " + "matching %s: (%s)\n", programname, + argv[aindex], error_message(kret)); + exit_status++; + goto cleanup; + } + + for (i = 0; i < num; i++) { + if (kret = krb5_parse_name(context, princs[i], + &princ)) { + com_err(programname, kret, + "while parsing principal name"); + exit_status++; + break; + } + if (kret = kadm5_get_principal(handle, princ, + &princ_ent, + KADM5_PRINCIPAL_NORMAL_MASK | + KADM5_KEY_DATA|KADM5_TL_DATA)){ + com_err(programname, kret, + "while retrieving principal entry"); + krb5_free_principal(context, princ); + exit_status++; + break; + } + if (kret = (*dump_iterator)(&arglist, princs[i], &princ_ent)) { + exit_status++; + krb5_free_principal(context, princ); + kadm5_free_principal_ent(handle, &princ_ent); + break; + } + + krb5_free_principal(context, princ); + kadm5_free_principal_ent(handle, &princ_ent); + } + + kadm5_free_name_list(handle, princs, num); + aindex++; + if (kret) + goto cleanup; + } + +cleanup: + if (ofile) + fclose(f); + + if (locked) + (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK); +} + + +/* + * Read a string of bytes while counting the number of lines passed. + */ +static int +read_string(f, buf, len, lp) + FILE *f; + char *buf; + int len; + int *lp; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; i<len; i++) { + c = (char) fgetc(f); + if (c < 0) { + retval = 1; + break; + } + if (c == '\n') + (*lp)++; + buf[i] = (char) c; + } + buf[len] = '\0'; + return(retval); +} + +/* + * Read a string of two character representations of bytes. + */ +static int +read_octet_string(f, buf, len) + FILE *f; + krb5_octet *buf; + int len; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; i<len; i++) { + if (fscanf(f, "%02x", &c) != 1) { + retval = 1; + break; + } + buf[i] = (krb5_octet) c; + } + return(retval); +} + +/* + * Find the end of an old format record. + */ +static void +find_record_end(f, fn, lineno) + FILE *f; + char *fn; + int lineno; +{ + int ch; + + if (((ch = fgetc(f)) != ';') || ((ch = fgetc(f)) != '\n')) { + fprintf(stderr, trash_end_fmt, fn, lineno); + while (ch != '\n') { + putc(ch, stderr); + ch = fgetc(f); + } + putc(ch, stderr); + } +} + +#if 0 +/* + * process_k5beta5_record() - Handle a dump record in old format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta5_record(fname, context, filep, verbose, linenop) + char *fname; + krb5_context context; + FILE *filep; + int verbose; + int *linenop; +{ + int nmatched; + int retval; + krb5_db_entry dbent; + int name_len, mod_name_len, key_len; + int alt_key_len, salt_len, alt_salt_len; + char *name; + char *mod_name; + int tmpint1, tmpint2, tmpint3; + int error; + const char *try2read; + int i; + krb5_key_data *pkey, *akey; + krb5_timestamp last_pwd_change, mod_date; + krb5_principal mod_princ; + krb5_error_code kret; + + try2read = (char *) NULL; + (*linenop)++; + retval = 1; + memset((char *)&dbent, 0, sizeof(dbent)); + + /* Make sure we've got key_data entries */ + if (krb5_dbe_create_key_data(context, &dbent) || + krb5_dbe_create_key_data(context, &dbent)) { + krb5_db_free_principal(context, &dbent, 1); + return(1); + } + pkey = &dbent.key_data[0]; + akey = &dbent.key_data[1]; + + /* + * Match the sizes. 6 tokens to match. + */ + nmatched = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t", + &name_len, &mod_name_len, &key_len, + &alt_key_len, &salt_len, &alt_salt_len); + if (nmatched == 6) { + pkey->key_data_length[0] = key_len; + akey->key_data_length[0] = alt_key_len; + pkey->key_data_length[1] = salt_len; + akey->key_data_length[1] = alt_salt_len; + name = (char *) NULL; + mod_name = (char *) NULL; + /* + * Get the memory for the variable length fields. + */ + if ((name = (char *) malloc((size_t) (name_len + 1))) && + (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) && + (!key_len || + (pkey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (key_len + 1)))) && + (!alt_key_len || + (akey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) && + (!salt_len || + (pkey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (salt_len + 1)))) && + (!alt_salt_len || + (akey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (alt_salt_len + 1)))) + ) { + error = 0; + + /* Read the principal name */ + if (read_string(filep, name, name_len, linenop)) { + try2read = read_name_string; + error++; + } + /* Read the key type */ + if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) { + try2read = read_key_type; + error++; + } + pkey->key_data_type[0] = tmpint1; + /* Read the old format key */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[0], + pkey->key_data_length[0])) { + try2read = read_key_data; + error++; + } + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((pkey->key_data_length[0] > 4) + && (pkey->key_data_contents[0][0] == 0) + && (pkey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = pkey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = pkey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + pkey->key_data_length[0] = shortlen; + pkey->key_data_contents[0] = shortcopy; + } + + /* Read principal attributes */ + if (!error && (fscanf(filep, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t", + &tmpint1, &dbent.max_life, + &dbent.max_renewable_life, + &tmpint2, &dbent.expiration, + &dbent.pw_expiration, &last_pwd_change, + &dbent.last_success, &dbent.last_failed, + &tmpint3) != 10)) { + try2read = read_pr_data1; + error++; + } + pkey->key_data_kvno = tmpint1; + dbent.fail_auth_count = tmpint3; + /* Read modifier name */ + if (!error && read_string(filep, + mod_name, + mod_name_len, + linenop)) { + try2read = read_mod_name; + error++; + } + /* Read second set of attributes */ + if (!error && (fscanf(filep, "\t%u\t%u\t%u\t", + &mod_date, &dbent.attributes, + &tmpint1) != 3)) { + try2read = read_pr_data2; + error++; + } + pkey->key_data_type[1] = tmpint1; + /* Read salt data */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[1], + pkey->key_data_length[1])) { + try2read = read_salt_data; + error++; + } + /* Read alternate key type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_akey_type; + error++; + } + akey->key_data_type[0] = tmpint1; + /* Read alternate key */ + if (!error && read_octet_string(filep, + akey->key_data_contents[0], + akey->key_data_length[0])) { + try2read = read_akey_data; + error++; + } + + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((akey->key_data_length[0] > 4) + && (akey->key_data_contents[0][0] == 0) + && (akey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = akey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = akey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + akey->key_data_length[0] = shortlen; + akey->key_data_contents[0] = shortcopy; + } + + /* Read alternate salt type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_asalt_type; + error++; + } + akey->key_data_type[1] = tmpint1; + /* Read alternate salt data */ + if (!error && read_octet_string(filep, + akey->key_data_contents[1], + akey->key_data_length[1])) { + try2read = read_asalt_data; + error++; + } + /* Read expansion data - discard it */ + if (!error) { + for (i=0; i<8; i++) { + if (fscanf(filep, "\t%u", &tmpint1) != 1) { + try2read = read_exp_data; + error++; + break; + } + } + if (!error) + find_record_end(filep, fname, *linenop); + } + + /* + * If no error, then we're done reading. Now parse the names + * and store the database dbent. + */ + if (!error) { + if (!(kret = krb5_parse_name(context, + name, + &dbent.princ))) { + if (!(kret = krb5_parse_name(context, + mod_name, + &mod_princ))) { + if (!(kret = + krb5_dbe_update_mod_princ_data(context, + &dbent, + mod_date, + mod_princ)) && + !(kret = + krb5_dbe_update_last_pwd_change(context, + &dbent, + last_pwd_change))) { + int one = 1; + + dbent.len = KRB5_KDB_V1_BASE_LENGTH; + pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ? + 2 : 1; + akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ? + 2 : 1; + if ((pkey->key_data_type[0] == + akey->key_data_type[0]) && + (pkey->key_data_type[1] == + akey->key_data_type[1])) + dbent.n_key_data--; + else if ((akey->key_data_type[0] == 0) + && (akey->key_data_length[0] == 0) + && (akey->key_data_type[1] == 0) + && (akey->key_data_length[1] == 0)) + dbent.n_key_data--; + if ((kret = krb5_db_put_principal(context, + &dbent, + &one)) || + (one != 1)) { + fprintf(stderr, store_err_fmt, + fname, *linenop, name, + error_message(kret)); + error++; + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + dbent.n_key_data = 2; + } + krb5_free_principal(context, mod_princ); + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, mod_name, + error_message(kret)); + error++; + } + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + error++; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + + krb5_db_free_principal(context, &dbent, 1); + if (mod_name) + free(mod_name); + if (name) + free(name); + } + else { + if (nmatched != EOF) + fprintf(stderr, rhead_err_fmt, fname, *linenop); + else + retval = -1; + } + return(retval); +} + + +/* + * process_k5_record() - Handle a dump record in new format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5_record(fname, context, filep, verbose, linenop) + char *fname; + krb5_context context; + FILE *filep; + int verbose; + int *linenop; +{ + int retval; + krb5_db_entry dbentry; + krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9; + int nread; + int error; + int i, j, one; + char *name; + krb5_key_data *kp, *kdatap; + krb5_tl_data **tlp, *tl; + krb5_octet *op; + krb5_error_code kret; + const char *try2read; + + try2read = (char *) NULL; + memset((char *) &dbentry, 0, sizeof(dbentry)); + (*linenop)++; + retval = 1; + name = (char *) NULL; + kp = (krb5_key_data *) NULL; + op = (krb5_octet *) NULL; + error = 0; + kret = 0; + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5); + if (nread == 5) { + /* Get memory for flattened principal name */ + if (!(name = (char *) malloc((size_t) t2 + 1))) + error++; + + /* Get memory for and form tagged data linked list */ + tlp = &dbentry.tl_data; + for (i=0; i<t3; i++) { + if ((*tlp = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) { + memset(*tlp, 0, sizeof(krb5_tl_data)); + tlp = &((*tlp)->tl_data_next); + dbentry.n_tl_data++; + } + else { + error++; + break; + } + } + + /* Get memory for key list */ + if (t4 && !(kp = (krb5_key_data *) malloc((size_t) + (t4*sizeof(krb5_key_data))))) + error++; + + /* Get memory for extra data */ + if (t5 && !(op = (krb5_octet *) malloc((size_t) t5))) + error++; + + if (!error) { + dbentry.len = t1; + dbentry.n_key_data = t4; + dbentry.e_length = t5; + if (kp) { + memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data))); + dbentry.key_data = kp; + kp = (krb5_key_data *) NULL; + } + if (op) { + memset(op, 0, (size_t) t5); + dbentry.e_data = op; + op = (krb5_octet *) NULL; + } + + /* Read in and parse the principal name */ + if (!read_string(filep, name, t2, linenop) && + !(kret = krb5_parse_name(context, name, &dbentry.princ))) { + + /* Get the fixed principal attributes */ + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9); + if (nread == 8) { + dbentry.attributes = (krb5_flags) t2; + dbentry.max_life = (krb5_deltat) t3; + dbentry.max_renewable_life = (krb5_deltat) t4; + dbentry.expiration = (krb5_timestamp) t5; + dbentry.pw_expiration = (krb5_timestamp) t6; + dbentry.last_success = (krb5_timestamp) t7; + dbentry.last_failed = (krb5_timestamp) t8; + dbentry.fail_auth_count = (krb5_kvno) t9; + } else { + try2read = read_nint_data; + error++; + } + + /* Get the tagged data */ + if (!error && dbentry.n_tl_data) { + for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + tl->tl_data_type = (krb5_int16) t1; + tl->tl_data_length = (krb5_int16) t2; + if (tl->tl_data_length) { + if (!(tl->tl_data_contents = + (krb5_octet *) malloc((size_t) t2+1)) || + read_octet_string(filep, + tl->tl_data_contents, + t2)) { + try2read = read_tcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_tcontents; + break; + } + } + } + else { + try2read = read_ttypelen; + error++; + break; + } + } + } + + /* Get the key data */ + if (!error && dbentry.n_key_data) { + for (i=0; !error && (i<dbentry.n_key_data); i++) { + kdatap = &dbentry.key_data[i]; + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + kdatap->key_data_ver = (krb5_int16) t1; + kdatap->key_data_kvno = (krb5_int16) t2; + + for (j=0; j<t1; j++) { + nread = fscanf(filep, "%d\t%d\t", &t3, &t4); + if (nread == 2) { + kdatap->key_data_type[j] = t3; + kdatap->key_data_length[j] = t4; + if (t4) { + if (!(kdatap->key_data_contents[j] = + (krb5_octet *) + malloc((size_t) t4+1)) || + read_octet_string(filep, + kdatap->key_data_contents[j], + t4)) { + try2read = read_kcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_kcontents; + break; + } + } + } + else { + try2read = read_ktypelen; + error++; + break; + } + } + } + } + } + + /* Get the extra data */ + if (!error && dbentry.e_length) { + if (read_octet_string(filep, + dbentry.e_data, + (int) dbentry.e_length)) { + try2read = read_econtents; + error++; + } + } + else { + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_econtents; + } + } + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + one = 1; + if ((kret = krb5_db_put_principal(context, + &dbentry, + &one))) { + fprintf(stderr, store_err_fmt, + fname, *linenop, + name, error_message(kret)); + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + if (kret) + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + else + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + } + else { + fprintf(stderr, rhead_err_fmt, fname, *linenop); + } + + if (op) + free(op); + if (kp) + free(kp); + if (name) + free(name); + krb5_db_free_principal(context, &dbentry, 1); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + + +/* + * restore_k5beta5_compat() - Restore the database from a K5 Beta + * format dump file. + */ +static int +restore_k5beta5_compat(programname, context, dumpfile, f, verbose) + const char *programname; + krb5_context context; + const char *dumpfile; + FILE *f; + int verbose; +{ + int error; + int lineno; + char buf[2*sizeof(k5beta5_dump_header)]; + + /* + * Get/check the header. + */ + error = 0; + fgets(buf, sizeof(buf), f); + if (!strcmp(buf, k5beta5_dump_header)) { + lineno = 1; + /* + * Process the records. + */ + while (!(error = process_k5beta5_record(dumpfile, + context, + f, + verbose, + &lineno))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + /* + * Close the input file. + */ + if (f != stdin) + fclose(f); + } + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + error++; + } + return(error); +} + + +/* + * restore_dump() - Restore the database from a standard dump file. + */ +static int +restore_dump(programname, context, dumpfile, f, verbose) + const char *programname; + krb5_context context; + const char *dumpfile; + FILE *f; + int verbose; +{ + int error; + int lineno; + char buf[2*sizeof(k5_dump_header)]; + + /* + * Get/check the header. + */ + error = 0; + fgets(buf, sizeof(buf), f); + if (!strcmp(buf, k5_dump_header)) { + lineno = 1; + /* + * Process the records. + */ + while (!(error = process_k5_record(dumpfile, + context, + f, + verbose, + &lineno))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + /* + * Close the input file. + */ + if (f != stdin) + fclose(f); + } + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + error++; + } + return(error); +} + +/* + * Usage is + * load_db [-old] [-verbose] [-update] filename dbname + */ +void +load_db(argc, argv) + int argc; + char **argv; +{ + krb5_error_code kret; + krb5_context context; + FILE *f; + extern char *optarg; + extern int optind; + const char *programname; + const char *dumpfile; + char *dbname; + char *dbname_tmp; + int (*restore_function) PROTOTYPE((const char *, + krb5_context, + const char *, + FILE *, + int)); + const char * restore_name; + int update, verbose; + int aindex; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + dumpfile = (char *) NULL; + dbname = (char *) NULL; + restore_function = restore_dump; + restore_name = standard_fmt_name; + update = 0; + verbose = 0; + exit_status = 0; + dbname_tmp = (char *) NULL; + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) { + restore_function = restore_k5beta5_compat; + restore_name = k5beta5_fmt_name; + } + else if (!strcmp(argv[aindex], verboseoption)) { + verbose = 1; + } + else if (!strcmp(argv[aindex], updateoption)) { + update = 1; + } + else + break; + } + if ((argc - aindex) != 2) { + fprintf(stderr, lusage_err_fmt, argv[0], argv[0], + oldoption, verboseoption, updateoption); + exit_status++; + return; + } + + dumpfile = argv[aindex]; + dbname = argv[aindex+1]; + if (!(dbname_tmp = (char *) malloc(strlen(dbname)+ + strlen(dump_tmptrail)+1))) { + fprintf(stderr, no_name_mem_fmt, argv[0]); + exit_status++; + return; + } + strcpy(dbname_tmp, dbname); + strcat(dbname_tmp, dump_tmptrail); + + /* + * Initialize the Kerberos context and error tables. + */ + if ((kret = krb5_init_context(&context))) { + fprintf(stderr, ctx_err_fmt, programname); + free(dbname_tmp); + exit_status++; + return; + } + krb5_init_ets(context); + + /* + * Open the dumpfile + */ + if (dumpfile) { + if ((f = fopen(dumpfile, "r+"))) { + kret = krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_SHARED); + } + } + else { + f = stdin; + } + if (f && !kret) { + /* + * Create the new database if not an update restoration. + */ + if (update || !(kret = krb5_db_create(context, dbname_tmp))) { + /* + * Point ourselves at it. + */ + if (!(kret = krb5_db_set_name(context, + (update) ? dbname : dbname_tmp))) { + /* + * Initialize the database. + */ + if (!(kret = krb5_db_init(context))) { + if ((*restore_function)(programname, + context, + (dumpfile) ? dumpfile : stdin_name, + f, + verbose)) { + fprintf(stderr, restfail_fmt, + programname, restore_name); + exit_status++; + } + if ((kret = krb5_db_fini(context))) { + fprintf(stderr, close_err_fmt, + programname, error_message(kret)); + exit_status++; + } + } + else { + fprintf(stderr, dbinit_err_fmt, + programname, error_message(kret)); + exit_status++; + } + } + else { + fprintf(stderr, dbname_err_fmt, + programname, + (update) ? dbname : dbname_tmp, error_message(kret)); + exit_status++; + } + /* + * If there was an error and this is not an update, then + * destroy the database. + */ + if (!update) { + if (exit_status) { + if ((kret = kdb5_db_destroy(context, dbname))) { + fprintf(stderr, dbdelerr_fmt, + programname, dbname_tmp, error_message(kret)); + exit_status++; + } + } + else { + if ((kret = krb5_db_rename(context, + dbname_tmp, + dbname))) { + fprintf(stderr, dbrenerr_fmt, + programname, dbname_tmp, dbname, + error_message(kret)); + exit_status++; + } + } + } + } + else { + fprintf(stderr, dbcreaterr_fmt, + programname, dbname, error_message(kret)); + exit_status++; + } + if (dumpfile) { + (void) krb5_lock_file(context, fileno(f), KRB5_LOCKMODE_UNLOCK); + fclose(f); + } + } + else { + fprintf(stderr, dfile_err_fmt, dumpfile, error_message(errno)); + exit_status++; + } + free(dbname_tmp); + krb5_free_context(context); +} +#endif diff --git a/src/kadmin/cli/getdate.y b/src/kadmin/cli/getdate.y new file mode 100644 index 000000000..975a819f5 --- /dev/null +++ b/src/kadmin/cli/getdate.y @@ -0,0 +1,1009 @@ +%{ +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; +** send any email to Rich. +** +** This grammar has nine shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unusd static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#ifdef HAVE_CONFIG_H +#if defined (emacs) || defined (CONFIG_BROKETS) +#include <config.h> +#else +#include "config.h" +#endif +#endif +#include <string.h> + +/* Since the code of getdate.y is not included in the Emacs executable + itself, there is no need to #define static in this file. Even if + the code were included in the Emacs executable, it probably + wouldn't do any harm to #undef it here; this will only cause + problems if we try to write to a static variable, which I don't + think this code needs to do. */ +#ifdef emacs +#undef static +#endif + +/* The following block of alloca-related preprocessor directives is here + solely to allow compilation by non GNU-C compilers of the C parser + produced from this file by old versions of bison. Newer versions of + bison include a block similar to this one in bison.simple. */ + +#ifdef __GNUC__ +#undef alloca +#define alloca __builtin_alloca +#else +#ifdef HAVE_ALLOCA_H +#include <alloca.h> +#else +#ifdef _AIX /* for Bison */ + #pragma alloca +#else +void *alloca (); +#endif +#endif +#endif + +#include <stdio.h> +#include <ctype.h> + +/* The code at the top of get_date which figures out the offset of the + current time zone checks various CPP symbols to see if special + tricks are need, but defaults to using the gettimeofday system call. + Include <sys/time.h> if that will be used. */ + +#if defined(vms) + +#include <types.h> +#include <time.h> + +#else + +#include <sys/types.h> + +#ifdef TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#ifdef timezone +#undef timezone /* needed for sgi */ +#endif + +#if defined(HAVE_SYS_TIMEB_H) +#include <sys/timeb.h> +#else +/* +** We use the obsolete `struct timeb' as part of our interface! +** Since the system doesn't have it, we define it here; +** our callers must do likewise. +*/ +struct timeb { + time_t time; /* Seconds since the epoch */ + unsigned short millitm; /* Field not used */ + short timezone; /* Minutes west of GMT */ + short dstflag; /* Field not used */ +}; +#endif /* defined(HAVE_SYS_TIMEB_H) */ + +#endif /* defined(vms) */ + +#if defined (STDC_HEADERS) || defined (USG) +#include <string.h> +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +extern struct tm *gmtime(); +extern struct tm *localtime(); + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yylex (); +static int yyerror (); + +#if !defined(lint) && !defined(SABER) +static char RCS[] = + "$Header$"; +#endif /* !defined(lint) && !defined(SABER) */ + + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type <Meridian> tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ + { "kst", tZONE, -HOUR(9) }, /* Korean Standard */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "kdt", tZONE, -HOUR(10) }, /* Korean Daylight */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL } +}; + + + + +/* ARGSUSED */ +static int +yyerror(s) + char *s; +{ + return 0; +} + + +static time_t +ToSeconds(Hours, Minutes, Seconds, Meridian) + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +static time_t +Convert(Month, Day, Year, Hours, Minutes, Seconds, Meridian, DSTmode) + time_t Month; + time_t Day; + time_t Year; + time_t Hours; + time_t Minutes; + time_t Seconds; + MERIDIAN Meridian; + DSTMODE DSTmode; +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + if (Year < EPOCH || Year > 1999 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(Start, Future) + time_t Start; + time_t Future; +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(Start, DayOrdinal, DayNumber) + time_t Start; + time_t DayOrdinal; + time_t DayNumber; +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(Start, RelMonth) + time_t Start; + time_t RelMonth; +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * tm->tm_year + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(buff) + char *buff; +{ + register char *p; + register char *q; + register const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex() +{ + register char c; + register char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static time_t +difftm(a, b) + struct tm *a, *b; +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + return + ( + ( + ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (time_t)(ay-by) * 365 + )*24 + (a->tm_hour - b->tm_hour) + )*60 + (a->tm_min - b->tm_min) + )*60 + (a->tm_sec - b->tm_sec); +} + +time_t +get_date(p, now) + char *p; + struct timeb *now; +{ + struct tm *tm, gmt; + struct timeb ftz; + time_t Start; + time_t tod; + + yyInput = p; + if (now == NULL) { + now = &ftz; + (void)time(&ftz.time); + + if (! (tm = gmtime (&ftz.time))) + return -1; + gmt = *tm; /* Make a copy, in case localtime modifies *tm. */ + ftz.timezone = difftm (&gmt, localtime (&ftz.time)) / 60; + } + + tm = localtime(&now->time); + yyYear = tm->tm_year; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = now->timezone; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = now->time; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +main(ac, av) + int ac; + char *av[]; +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff, (struct timeb *)NULL); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/src/kadmin/cli/kadmin.1 b/src/kadmin/cli/kadmin.1 new file mode 100644 index 000000000..a8db58334 --- /dev/null +++ b/src/kadmin/cli/kadmin.1 @@ -0,0 +1,473 @@ +KADMIN(8) USER_COMMANDS KADMIN(8) + +NAME + kadmin - a command line interface to the Kerberos KADM5 + administration system + +SYNOPSIS + kadmin [-r realm] [-p principal] [-q query] [clnt|local args] + clnt args: [-p principal] [[-c ccache]|[-k [-t keytab]]] + [-w] [-s admin_server[:port]] + local args: [-d dbname] [-e \"enc:salt ...\"] [-m] + +DESCRIPTION + kadmin is a command-line interface to the Kerberos KADM5 + administration system. It provides for the maintainance of + Kerberos principals, KADM5 policies, and service key tables + (keytabs). It exists as both a remote client, using Kerberos + authentication and an encrypted RPC to operate securely from + anywhere on the network, and as a local client intended to run + directly on the KDC without Kerberos authentication. The + local version provides all of the functionality of the now + obsolete kdb5_edit(8) except for database dump and load, which + is now provided by the kdb5_util(8) utility. + +COMMAND LINE ARGUMENTS + If -r is specified, then kadmin will use the specified realm + as the default database realm rather than the default realm + for the local machine. + + The -q option allows the passing of a request directly to + kadmin, which will then exit. This can be useful for writing + scripts. + + The remote version authenticates to the KADM5 server using the + service kadmin/admin, and therefore needs a client Kerberos + principal name as which to authenticate. The -p, -c, and -k + are designed to work together to specify which principal as + which to authenticate and where the service ticket or + password/key for that principal should be obtained. If given + the -p option, kadmin will use the specified principal to + authenticate. Otherwise, if given -c option then the primary + principal name of the ccache is used. Otherwise, if given the + -k option, the principal name host/<hostname> is used. + Otherwise, kadmin will append "/admin" to the primary + principal name of the default ccache, the value of the USER + environment variable, or the username as obtained with + getpwuid, in order of preference. + + Once kadmin knows the principal name as which to authenticate, + it needs to acquire a Kerberos service ticket for the KADM5 + server. If the -c ccache argument is specified, the ccache + should contain a service ticket for the kadmin/admin service; + it can be acquired with the kinit(1) program. Otherwise, + kadmin requests a new service ticket from the KDC and stores + it in its own temporary ccache. If the -k keytab argument is + specified, the keytab is used to decrypt the KDC response; + otherwise, a password is required. By default, the user is + prompted for the password on the TTY. However, if given the + -w option, kadmin will use the password provided on the + command line instead of prompting for one on the TTY. + WARNING! Placing the password for a Kerberos principal with + administration access into a shell script is EXTREMELY + DANGEROUS and should only be done if you are highly sure that + the script will not fall into the wrong hands. + + If given the -d argument, kadmin will use the specified + database name instead of the default defined in kdc.conf. + Note that specifying a different KDC database name also + specifies a different name for the KADM5 policy database and + lock file. + + If given the -e argument, kadmin will use the specified list + of encryption and salt type tuples instead of the values + specified in kdc.conf. This is useful, for example, if you + want to create a single principal with a particular key/salt + type without affecting any other principals. + + If given the -m argument, kadmin will prompt for the Kerberos + master password on the command line instead of attempting to + use the stash file. + +DATE FORMAT + Various commands in kadmin can take a variety of + date formats, specifying durations or absolute times. + Examples of valid formats are: + + 1 month ago + 2 hours ago + 400000 seconds ago + last year + last Monday + yesterday + a fortnight ago + 3/31/92 10:00:07 PST + January 23, 1987 10:05pm + 22:00 GMT + + Dates which do not have the "ago" specifier default to being + absolute dates, unless they appear in a field where a duration + is expected. In that case the time specifier will be + interpreted as relative. Specifying "ago" on a duration may + result in unexpected behaviour. + +COMMAND DESCRIPTIONS + +add_principal [options] _newprinc_ + creates the principal _newprinc_, prompting twice for a + password. This command requires the "add" privilege. This + command has the aliases "addprinc", "ank". + + OPTIONS + -salt _salttype_ + uses the specified salt instead of the default V5 salt + for generating the key. Valid values for _salttype_ + are: + full_name (aliases "v5_salt", "normal") + name_only + realm_only + no_salt (alias "v4_salt") + + -expire _expdate_ + expiration date of the principal + + -pwexpire _pwexpdate_ + password expiration date + + -maxlife _maxlife_ + maximum ticket life of the principal + + -kvno _kvno_ + explicity set the key version number. This is not + recommended. + + -policy _policy_ + policy used by this principal. If no policy is + supplied, the principal will default to having no + policy, and a warning message will be printed. + + {-|+}allow_tgs_req + "-allow_tgs_req" specifies that a TGS request for a + ticket for a service ticket for this principal is not + permitted. This option is useless for most things. + "+allow_tgs_req" clears this flag. The default is + "+allow_tgs_req". In effect, "-allow_tgs_req" sets + the KRB5_KDB_DISALLOW_TGT_BASED flag on the principal + in the database. + + {-|+}allow_tix + "-allow_tix" forbids the issuance of any tickets for + this principal. "+allow_tix" clears this flag. The + default is "+allow_tix". In effect, "-allow_tix" sets + the KRB5_KDB_DISALLOW_ALL_TIX flag on the principal in + the database. + + {-|+}needchange + "+needchange" sets a flag in attributes field to force + a password change; "-needchange" clears it. The + default is "-needchange". In effect, "+needchange" + sets the KRB5_KDB_REQUIRES_PWCHANGE flag on the + principal in the database. + + {-|+}password_changing_service + "+password_changing_service" sets a flag in the + attributes field marking this as a password change + service principal (useless for most things). + "-password_changing_service" clears the flag. This + flag intentionally has a long name. The default is + "-password_changing_service". In effect, + "+password_changing_service" sets the + KRB5_KDB_PWCHANGE_SERVICE flag on the principal in the + database. + + -randpass + sets the key of the principal to a random value + + -pw _password_ + sets the key of the principal to the specified string + and does not prompt for a password. This is not + recommended. + + EXAMPLE + kadmin: addprinc tlyu/deity + WARNING: no policy specified for "tlyu/deity@ATHENA.MIT.EDU"; + defaulting to no policy. + Enter password for principal tlyu/deity@ATHENA.MIT.EDU: + Re-enter password for principal tlyu/deity@ATHENA.MIT.EDU: + Principal "tlyu/deity@ATHENA.MIT.EDU" created. + kadmin: + + ERRORS + KADM5_AUTH_ADD (requires "add" privilege) + KADM5_BAD_MASK (shouldn't happen) + KADM5_DUP (principal exists already) + KADM5_UNK_POLICY (policy does not exist) + KADM5_PASS_Q_* (password quality violations) + +delete_principal [-force] _principal_ + deletes the specified principal from the database. This + command prompts for deletion, unless the "-force" option is + given. This command requires the "delete" privilege. Aliased + to "delprinc". + + EXAMPLE + kadmin: delprinc mwm_user + Are you sure you want to delete the principal + "mwm_user@ATHENA.MIT.EDU"? (yes/no): yes + Principal "mwm_user@ATHENA.MIT.EDU" deleted. + Make sure that you have removed this principal from + all ACLs before reusing. + kadmin: + + ERRORS + KADM5_AUTH_DELETE (reequires "delete" privilege) + KADM5_UNK_PRINC (principal does not exist) + +modify_principal [options] _principal_ + modifies the specified principal, changing the fields as + specified. The options are as above for "add_principal", + except that password changing is forbidden by this command. + In addition, the option "-clearpolicy" will remove clear the + current policy of a principal. This command requires the + "modify" privilege. Aliased to "modprinc". + + ERRORS + KADM5_AUTH_MODIFY (requires "modify" privilege) + KADM5_UNK_PRINC (principal does not exist) + KADM5_UNK_POLICY (policy does not exist) + KADM5_BAD_MASK (shouldn't happen) + +rename_principal [-force] _old_ _new_ + rename the principal _old_ to _new_. Prompts for + confirmation, unless the "-force" option is given. Requires + both the "add" and "delete" privileges. Aliased to + "renprinc". + + EXAMPLE + kadmin: renprinc tlyutest test0 + Are you sure you want to rename the principal + "tlyutest@ATHENA.MIT.EDU" to + "test0@ATHENA.MIT.EDU"? (yes/no): yes + Principal "tlyutest@ATHENA.MIT.EDU" renamed to + "test0@ATHENA.MIT.EDU". + Make sure that you have removed "tlyutest@ATHENA.MIT.EDU" from + all ACLs before reusing. + kadmin: + + ERRORS + KADM5_AUTH_ADD (requires "add" privilege) + KADM5_AUTH_DELETE (requires "delete" privilege) + KADM5_UNK_PRINC (source principal does not exist) + KADM5_DUP (target principal already exists) + +change_password [options] _principal_ + changes the password of _principal_. Prompts for a new + password if neither -randpass or -pw is specified. Requires + the "modify" privilege, or that the principal that is running + the program to be the same as the one changed. Aliased to + "cpw". + + OPTIONS + -salt _salttype_ + uses the specified salt instead of the default V5 salt + for generating the key. Options are the same as for + add_principal. + + -randpass + sets the key of the principal to a random value + + -pw _password_ + set the password to the specified string. Not + recommended. + + EXAMPLE + kadmin: cpw systest + Enter password for principal systest@ATHENA.MIT.EDU: + Re-enter password for principal systest@ATHENA.MIT.EDU: + Password for systest@ATHENA.MIT.EDU changed. + kadmin: + + ERRORS + KADM5_AUTH_MODIFY (requires the modify privilege) + KADM5_UNK_PRINC (principal does not exist) + KADM5_PASS_Q_* (password policy violation errors) + KADM5_PADD_REUSE (password is in principal's password istory) + KADM5_PASS_TOOSOON (current password minimum life not xpired) + +get_principal [-terse] _principal_ + gets the attributes of _principal_. Requires the "get" + privilege, or that the principal that is running the the + program to be the same as the one being listed. With the + "-terse" option, outputs fields as a quoted tab-separated + strings. Alias "getprinc". + + EXAMPLES + kadmin: getprinc tlyu/deity + Principal: tlyu/deity@ATHENA.MIT.EDU + Key version: 3 + Maximum life: 1 day 00:00:00 + Maximum renewable life: 7 days 00:00:00 + Master key version: 1 + Expires: Mon Jan 18 22:14:07 EDT 2038 + Password expires: Mon Sep 19 14:40:00 EDT 1994 + Password last changed: Mon Jan 31 02:06:40 EDT 1994 + Last modified: by tlyu/admin@ATHENA.MIT.EDU + on Wed Jul 13 18:27:08 EDT 1994 + Attributes: DISALLOW_FORWARDABLE, DISALLOW_PROXIABLE, + REQUIRES_HW_AUTH + Salt type: DEFAULT + kadmin: getprinc systest + systest@ATHENA.MIT.EDU 3 86400 604800 1 + 785926535 753241234 785900000 + tlyu/admin@ATHENA.MIT.EDU 786100034 0 + 0 + kadmin: + + ERRORS + KADM5_AUTH_GET (requires the get privilege) + KADM5_UNK_PRINC (principal does not exist) + +get_principals [expression] + Retrieves all or some principal names. _expression_ is a + shell-style glob expression that can contain the wild-card + characters ?, *, and []'s. All principal names matching the + expression are printed. If no expression is provided, the + expression "*" is assumed. If the expression does not contain + an "@" character, an "@" character followed by the local realm + is appended to the expression. Requires the "list" priviledge. + Alias "getprincs". + + EXAMPLES + kadmin: getprincs test* + test3@SECURE-TEST.OV.COM + test2@SECURE-TEST.OV.COM + test1@SECURE-TEST.OV.COM + testuser@SECURE-TEST.OV.COM + kadmin: + +add_policy [options] _policy_ + adds the named policy to the policy database. Requires the + "add" privilege. Aliased to "addpol". + + OPTIONS + -maxlife _time_ + sets the maximum lifetime of a password + + -minlife _time_ + sets the minimum lifetime of a password + + -minlength _length_ + sets the minimum length of a password + + -minclasses _number_ + sets the minimum number of character classes allowed + in a password + + -history _number_ + sets the number of past keys kept for a principal + + ERRORS + KADM5_AUTH_ADD (requires the add privilege) + KADM5_DUP (policy already exists) + +delete_policy _policy_ + deletes the named policy. Prompts for confirmation before + deletion. The command will fail if the policy is in use by + any principals. Requires the "delete" privilege. Alias + "delpol". + + EXAMPLE + kadmin: del_policy guests + Are you sure you want to delete the policy "guests"? + (yes/no): yes + Policy "guests" deleted. + kadmin: + + ERRORS + KADM5_AUTH_DELETE (requires the delete privilege) + KADM5_UNK_POLICY (policy does not exist) + KADM5_POLICY_REF (reference count on policy is not zero) + +modify_policy [options] _policy_ + modifies the named policy. Options are as above for + "add_policy". Requires the "modify" privilege". Alias + "modpol". + + ERRORS + KADM5_AUTH_MODIFY (requires the modify privilege) + KADM5_UNK_POLICY (policy does not exist) + +get_policy [-terse] _policy_ + displays the values of the named policy. Requires the "get" + privilege. With the "-terse" flag, outputs the fields as + quoted strings separated by tabs. Alias "getpol". + + EXAMPLES + kadmin: get_policy admin + Policy: admin + Maximum password life: 180 days 00:00:00 + Minimum password life: 00:00:00 + Minimum password length: 6 + Minimum number of password character classes: 2 + Number of old keys kept: 5 + Reference count: 17 + kadmin: get_policy -terse admin + admin 15552000 0 6 2 5 17 + kadmin: + + ERRORS + KADM5_AUTH_GET (requires the get privilege) + KADM5_UNK_POLICY (policy does not exist) + +get_policies [expression] + Retrieves all or some policy names. _expression_ is a + shell-style glob expression that can contain the wild-card + characters ?, *, and []'s. All policy names matching the + expression are printed. If no expression is provided, the + expression "*" is assumed. Requires the "list" priviledge. + Alias "getpols". + + EXAMPLES + kadmin: getpols + test-pol + dict-only + once-a-min + test-pol-nopw + kadmin: getpols t* + test-pol + test-pol-nopw + kadmin: + +ktadd [-k keytab] [-q] [principal | -glob princ-exp] [...] + Adds principal or all principals matching princ-exp to a + keytab. princ-exp follows the same rules described for the + get_principals command. An entry for each of the principal's + unique encryption types is added, ignoring multiple keys with + the same encryption type but different salt types. If the -k + argument is not specified, the default keytab /etc/v5srvtab is + used. If the -q option is specified, less verbose status + information is displayed. + + The -glob option requires the "list" privilege. + + EXAMPLES + kadmin% ktadd -k /krb5/kadmind.keytab kadmin/admin kadmin/changepw + kadmin: Entry for principal kadmin/admin@ATHENA.MIT.EDU with + kvno 3, encryption type DES-CBC-CRC added to keytab + WRFILE:/krb5/kadmind.keytab. + kadmin: Entry for principal kadmin/changepw@ATHENA.MIT.EDU + with kvno 3, encryption type DES-CBC-CRC added to keytab + WRFILE:/krb5/kadmind.keytab. + kadmin: + +ktremove [-k keytab] [-q] principal [kvno|"all"|"old"] + Removes entries for the specified principal from a keytab. If + the string "all" is specified, all entries for that principal + are removed; if the string "old" is specified, all entries for + that principal except those with the highest kvno are removed. + Otherwise, the value specified is parsed as an integer, and + all entries whose kvno match that integer are removed. If the + -k argument is not specifeid, the default keytab /etc/v5srvtab + is used. If the -q is specified, less verbose status + information is displayed. + + EXAMPLES + kadmin: ktremove -k /krb5/kadmind.keytab kadmin/admin + kadmin: Entry for principal kadmin/admin with kvno 3 removed + from keytab WRFILE:/krb5/kadmind.keytab. + kadmin: + +SEE ALSO + kerberos(1), kdb5_util(8) + + diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c new file mode 100644 index 000000000..e19383dd9 --- /dev/null +++ b/src/kadmin/cli/kadmin.c @@ -0,0 +1,1322 @@ +/* + * Copyright 1994 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * kadmin.c: base functions for a kadmin command line interface using + * the OVSecure library + */ + +#include <krb5.h> +#include <k5-int.h> +#include <kadm5/admin.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <math.h> +#include <unistd.h> +#include <pwd.h> +/* #include <sys/timeb.h> */ +#include <time.h> + +/* special struct to convert flag names for principals + to actual krb5_flags for a principal */ +struct pflag { + char *flagname; /* name of flag as typed to CLI */ + int flaglen; /* length of string (not counting -,+) */ + krb5_flags theflag; /* actual principal flag to set/clear */ + int set; /* 0 means clear, 1 means set (on '-') */ +}; + +static struct pflag flags[] = { +{"allow_postdated", 15, KRB5_KDB_DISALLOW_POSTDATED, 1}, +{"allow_forwardable", 17, KRB5_KDB_DISALLOW_FORWARDABLE, 1}, +{"allow_tgs_req", 13, KRB5_KDB_DISALLOW_TGT_BASED, 1}, +{"allow_renewable", 15, KRB5_KDB_DISALLOW_RENEWABLE, 1}, +{"allow_proxiable", 15, KRB5_KDB_DISALLOW_PROXIABLE, 1}, +{"allow_dup_skey", 14, KRB5_KDB_DISALLOW_DUP_SKEY, 1}, +{"allow_tix", 9, KRB5_KDB_DISALLOW_ALL_TIX, 1}, +{"requires_preauth", 16, KRB5_KDB_REQUIRES_PRE_AUTH, 0}, +{"requires_hwauth", 15, KRB5_KDB_REQUIRES_HW_AUTH, 0}, +{"needchange", 10, KRB5_KDB_REQUIRES_PWCHANGE, 0}, +{"allow_svr", 9, KRB5_KDB_DISALLOW_SVR, 1}, +{"password_changing_service", 25, KRB5_KDB_PWCHANGE_SERVICE, 0 } +}; + +static char *prflags[] = { + "DISALLOW_POSTDATED", /* 0x00000001 */ + "DISALLOW_FORWARDABLE", /* 0x00000002 */ + "DISALLOW_TGT_BASED", /* 0x00000004 */ + "DISALLOW_RENEWABLE", /* 0x00000008 */ + "DISALLOW_PROXIABLE", /* 0x00000010 */ + "DISALLOW_DUP_SKEY", /* 0x00000020 */ + "DISALLOW_ALL_TIX", /* 0x00000040 */ + "REQUIRES_PRE_AUTH", /* 0x00000080 */ + "REQUIRES_HW_AUTH", /* 0x00000100 */ + "REQUIRES_PWCHANGE", /* 0x00000200 */ + "UNKNOWN_0x00000400", /* 0x00000400 */ + "UNKNOWN_0x00000800", /* 0x00000800 */ + "DISALLOW_SVR", /* 0x00001000 */ + "PWCHANGE_SERVICE" /* 0x00002000 */ +}; + +char *getenv(); +struct passwd *getpwuid(); +int exit_status = 0; +char *def_realm = NULL; +char *whoami = NULL; +time_t get_date(); + +void *handle = NULL; +krb5_context context; +char *ccache_name = NULL; + +void usage() +{ + fprintf(stderr, + "Usage: %s [-r realm] [-p principal] [-q query] [clnt|local args]\n" + "\tclnt args: [-s admin_server[:port]] [[-c ccache]|[-k [-t keytab]]]\n" + "\tlocal args: [-d dbname] [-e \"enc:salt ...\"] [-m]\n", whoami); + exit(1); +} + +char *strdur(duration) + time_t duration; +{ + static char out[50]; + int days, hours, minutes, seconds; + + days = duration / (24 * 3600); + duration %= 24 * 3600; + hours = duration / 3600; + duration %= 3600; + minutes = duration / 60; + duration %= 60; + seconds = duration; + sprintf(out, "%d %s %02d:%02d:%02d", days, days == 1 ? "day" : "days", + hours, minutes, seconds); + return out; +} + +char *strdate(when) + krb5_timestamp when; +{ + struct tm *tm; + static char out[30]; + + time_t lcltim = when; + tm = localtime(&lcltim); + strftime(out, 30, "%a %b %d %H:%M:%S %Z %Y", tm); + return out; +} + +/* this is a wrapper to go around krb5_parse_principal so we can set + the default realm up properly */ +krb5_error_code kadmin_parse_name(name, principal) + char *name; + krb5_principal *principal; +{ + char *cp, *fullname; + krb5_error_code retval; + + /* assumes def_realm is initialized! */ + fullname = (char *)malloc(strlen(name) + 1 + strlen(def_realm) + 1); + if (fullname == NULL) + return ENOMEM; + strcpy(fullname, name); + cp = strchr(fullname, '@'); + while (cp) { + if (cp - fullname && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '@'); + } + if (cp == NULL) { + strcat(fullname, "@"); + strcat(fullname, def_realm); + } + retval = krb5_parse_name(context, fullname, principal); + free(fullname); + return retval; +} + +char *kadmin_startup(argc, argv) + int argc; + char *argv[]; +{ + extern krb5_kt_ops krb5_ktf_writable_ops; + extern char *optarg; + char *princstr = NULL, *keytab_name = NULL, *query = NULL; + char *password = NULL; + char *luser, *canon, *cp; + int optchar, freeprinc = 0, use_keytab = 0; + struct passwd *pw; + kadm5_ret_t retval; + krb5_ccache cc; + krb5_principal princ; + kadm5_config_params params; + + memset((char *) ¶ms, 0, sizeof(params)); + + if (retval = krb5_init_context(&context)) { + com_err(whoami, retval, "while initializing krb5 library"); + exit(1); + } + krb5_init_ets(context); + + while ((optchar = getopt(argc, argv, "r:p:kq:w:d:s:m:c:t:e:")) != EOF) { + switch (optchar) { + case 'r': + def_realm = optarg; + break; + case 'p': + princstr = optarg; + break; + case 'c': + ccache_name = optarg; + break; + case 'k': + use_keytab++; + break; + case 't': + keytab_name = optarg; + break; + case 'w': + password = optarg; + break; + case 'q': + query = optarg; + break; + case 'd': + params.dbname = optarg; + params.mask |= KADM5_CONFIG_DBNAME; + break; + case 's': + params.admin_server = optarg; + params.mask |= KADM5_CONFIG_ADMIN_SERVER; + break; + case 'm': + params.mkey_from_kbd = 1; + params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + break; + case 'e': + retval = krb5_string_to_keysalts(optarg, + ", \t", + ":.-", + 0, + ¶ms.keysalts, + ¶ms.num_keysalts); + if (retval) { + com_err(whoami, retval, "while parsing keysalts %s", optarg); + exit(1); + } + params.mask |= KADM5_CONFIG_ENCTYPES; + break; + default: + usage(); + } + } + if ((ccache_name && use_keytab) || + (keytab_name && !use_keytab)) + usage(); + + if (def_realm == NULL && krb5_get_default_realm(context, &def_realm)) { + if (freeprinc) + free(princstr); + fprintf(stderr, "%s: unable to get default realm\n", whoami); + exit(1); + } + + params.mask |= KADM5_CONFIG_REALM; + params.realm = def_realm; + + /* + * Set cc to an open credentials cache, either specified by the -c + * argument or the default. + */ + if (ccache_name == NULL) { + if (retval = krb5_cc_default(context, &cc)) { + com_err(whoami, retval, + "while opening default credentials cache"); + exit(1); + } + } else { + if (retval = krb5_cc_resolve(context, ccache_name, &cc)) { + com_err(whoami, retval, + "while opening credentials cache %s", ccache_name); + exit(1); + } + } + + /* + * If no principal name is specified: If a ccache was specified + * and its primary principal name can be read, it is used, else if + * a keytab was specified, the principal name is host/hostname, + * otherwise append "/admin" to the primary name of the default + * ccache, $USER, or pw_name. + * + * Gee, 100+ lines to figure out the client principal name. This + * should be compressed... + */ + + if (princstr == NULL) { + if (ccache_name != NULL && + !krb5_cc_get_principal(context, cc, &princ)) { + if (retval = krb5_unparse_name(context, princ, &princstr)) { + com_err(whoami, retval, + "while canonicalizing principal name"); + krb5_free_principal(context, princ); + exit(1); + } + krb5_free_principal(context, princ); + freeprinc++; + } else if (use_keytab != NULL) { + if (retval = krb5_sname_to_principal(context, NULL, + "host", + KRB5_NT_SRV_HST, + &princ)) { + com_err(whoami, retval, + "creating host service principal"); + exit(1); + } + if (retval = krb5_unparse_name(context, princ, &princstr)) { + com_err(whoami, retval, + "while canonicalizing principal name"); + krb5_free_principal(context, princ); + exit(1); + } + krb5_free_principal(context, princ); + freeprinc++; + } else if (!krb5_cc_get_principal(context, cc, &princ)) { + char *realm = NULL; + if (krb5_unparse_name(context, princ, &canon)) { + fprintf(stderr, + "%s: unable to canonicalize principal\n", whoami); + krb5_free_principal(context, princ); + exit(1); + } + /* strip out realm of principal if it's there */ + realm = strchr(canon, '@'); + while (realm) { + if (realm - canon && *(realm - 1) != '\\') + break; + else + realm = strchr(realm, '@'); + } + if (realm) + *realm++ = '\0'; + cp = strchr(canon, '/'); + while (cp) { + if (cp - canon && *(cp - 1) != '\\') + break; + else + cp = strchr(cp, '/'); + } + if (cp != NULL) + *cp = '\0'; + princstr = (char*)malloc(strlen(canon) + 6 /* "/admin" */ + + (realm ? 1 + strlen(realm) : 0) + 1); + if (princstr == NULL) { + fprintf(stderr, "%s: out of memory\n", whoami); + exit(1); + } + strcpy(princstr, canon); + strcat(princstr, "/admin"); + if (realm) { + strcat(princstr, "@"); + strcat(princstr, realm); + } + free(canon); + krb5_free_principal(context, princ); + freeprinc++; + } else if (luser = getenv("USER")) { + princstr = (char *) malloc(strlen(luser) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "%s: out of memory\n", whoami); + exit(1); + } + strcpy(princstr, luser); + strcat(princstr, "/admin"); + strcat(princstr, "@"); + strcat(princstr, def_realm); + freeprinc++; + } else if (pw = getpwuid(getuid())) { + princstr = (char *) malloc(strlen(pw->pw_name) + 7 /* "/admin@" */ + + strlen(def_realm) + 1); + if (princstr == NULL) { + fprintf(stderr, "%s: out of memory\n", whoami); + exit(1); + } + strcpy(princstr, pw->pw_name); + strcat(princstr, "/admin@"); + strcat(princstr, def_realm); + freeprinc++; + } else { + fprintf(stderr, "%s: unable to figure out a principal name\n", + whoami); + exit(1); + } + } + + /* + * Initialize the kadm5 connection. If we were given a ccache, + * use it. Otherwise, use/prompt for the password. + */ + if (ccache_name) + retval = kadm5_init_with_creds(princstr, cc, + KADM5_ADMIN_SERVICE, + ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + else if (use_keytab) + retval = kadm5_init_with_skey(princstr, keytab_name, + KADM5_ADMIN_SERVICE, + ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + else + retval = kadm5_init_with_password(princstr, password, + KADM5_ADMIN_SERVICE, + ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + if (retval) { + com_err(whoami, retval, "while initializing %s interface", whoami); + if (retval == KADM5_BAD_CLIENT_PARAMS || + retval == KADM5_BAD_SERVER_PARAMS) + usage(); + exit(1); + } + if (freeprinc) + free(princstr); + + if (retval = krb5_cc_close(context, cc)) { + com_err(whoami, retval, "while closing ccache %s", + ccache_name); + exit(1); + } + + /* register the WRFILE keytab type and set it as the default */ + if (retval = krb5_kt_register(context, &krb5_ktf_writable_ops)) { + com_err(whoami, retval, + "while registering writable key table functions"); + exit(1); + } + { +#define DEFAULT_KEYTAB "WRFILE:/etc/v5srvtab" + extern char *krb5_defkeyname; + krb5_defkeyname = DEFAULT_KEYTAB; + } + + return query; +} + +int quit() +{ + krb5_ccache cc; + int retval; + + kadm5_destroy(handle); + if (ccache_name != NULL) { + fprintf(stderr, + "\n\a\a\aAdministration credentials NOT DESTROYED.\n"); + } + + /* insert more random cleanup here */ +} + +void kadmin_delprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_ret_t retval; + krb5_principal princ; + char *canon; + char reply[5]; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-force", argv[1])))) { + fprintf(stderr, "usage: delete_principal [-force] principal\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("delete_principal", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(context, princ, &canon); + if (retval) { + com_err("delete_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(context, princ); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the principal \"%s\"? (yes/no): ", canon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Principal \"%s\" not deleted\n", canon); + free(canon); + krb5_free_principal(context, princ); + return; + } + } + retval = kadm5_delete_principal(handle, princ); + krb5_free_principal(context, princ); + if (retval) { + com_err("delete_principal", retval, + "while deleteing principal \"%s\"", canon); + free(canon); + return; + } + printf("Principal \"%s\" deleted.\nMake sure that you have removed this principal from all ACLs before reusing.\n", canon); + free(canon); + return; +} + +void kadmin_renprinc(argc, argv) + int argc; + char *argv[]; +{ + krb5_principal oldprinc, newprinc; + char *oldcanon, *newcanon; + char reply[5]; + kadm5_ret_t retval; + + if (! (argc == 3 || + (argc == 4 && !strcmp("-force", argv[1])))) { + fprintf(stderr, "usage: rename_principal [-force] old new\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 2], &oldprinc); + if (retval) { + com_err("rename_principal", retval, "while parsing old principal"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &newprinc); + if (retval) { + krb5_free_principal(context, oldprinc); + com_err("rename_principal", retval, "while parsing new principal"); + return; + } + retval = krb5_unparse_name(context, oldprinc, &oldcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing old principal"); + krb5_free_principal(context, newprinc); + krb5_free_principal(context, oldprinc); + return; + } + retval = krb5_unparse_name(context, newprinc, &newcanon); + if (retval) { + com_err("rename_principal", retval, + "while canonicalizing new principal"); + free(oldcanon); + krb5_free_principal(context, newprinc); + krb5_free_principal(context, oldprinc); + return; + } + if (argc == 3) { + printf("Are you sure you want to rename the principal \"%s\" to \"%s\"? (yes/no): ", + oldcanon, newcanon); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, + "rename_principal: \"%s\" NOT renamed to \"%s\".\n", + oldcanon, newcanon); + free(newcanon); + free(oldcanon); + krb5_free_principal(context, newprinc); + krb5_free_principal(context, oldprinc); + return; + } + } + retval = kadm5_rename_principal(handle, oldprinc, newprinc); + krb5_free_principal(context, oldprinc); + krb5_free_principal(context, newprinc); + if (retval) { + com_err("rename_principal", retval, + "while renaming \"%s\" to \"%s\".", oldcanon, + newcanon); + free(newcanon); + free(oldcanon); + return; + } + printf("Principal \"%s\" renamed to \"%s\".\nMake sure that you have removed \"%s\" from all ACLs before reusing.\n", + oldcanon, newcanon, newcanon); + return; +} + +void kadmin_cpw(argc, argv) + int argc; + char *argv[]; +{ + kadm5_ret_t retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + char *canon; + krb5_principal princ; + + if (argc < 2) { + goto usage; + } + + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("change_password", retval, "while parsing principal name"); + return; + } + retval = krb5_unparse_name(context, princ, &canon); + if (retval) { + com_err("change_password", retval, "while canonicalizing principal"); + krb5_free_principal(context, princ); + return; + } + if ((argc == 4) && (strlen(argv[1]) == 3) && !strcmp("-pw", argv[1])) { + retval = kadm5_chpass_principal(handle, princ, argv[2]); + krb5_free_principal(context, princ); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } else if ((argc == 3) && (strlen(argv[1]) == 8) && + !strcmp("-randkey", argv[1])) { + retval = kadm5_randkey_principal(handle, princ, NULL, NULL); + krb5_free_principal(context, princ); + if (retval) { + com_err("change_password", retval, + "while randomizing key for \"%s\".", canon); + free(canon); + return; + } + printf("Key for \"%s\" randomized.\n", canon); + free(canon); + return; + } else if (argc == 2) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + argv[1]); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + argv[1]); + retval = krb5_read_password(context, prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("change_password", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(context, princ); + return; + } + retval = kadm5_chpass_principal(handle, princ, newpw); + krb5_free_principal(context, princ); + memset(newpw, 0, sizeof (newpw)); + if (retval) { + com_err("change_password", retval, + "while changing password for \"%s\".", canon); + free(canon); + return; + } + printf("Password for \"%s\" changed.\n", canon); + free(canon); + return; + } else { + free(canon); + krb5_free_principal(context, princ); + usage: + fprintf(stderr, + "usage: change_password [-randpass] [-pw passowrd] " + "principal\n"); + return; + } +} + +int kadmin_parse_princ_args(argc, argv, oprinc, mask, pass, randkey, caller) + int argc; + char *argv[]; + kadm5_principal_ent_t oprinc; + long *mask; + char **pass; + int *randkey; + char *caller; +{ + int i, j, attrib_set; + time_t date; + time_t now; + krb5_error_code retval; + + *mask = 0; + *pass = NULL; + time(&now); + *randkey = 0; + for (i = 1; i < argc - 1; i++) { + attrib_set = 0; + if (strlen(argv[i]) == 7 && + !strcmp("-expire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + date = get_date(argv[i], NULL); + oprinc->princ_expire_time = date == (time_t)-1 ? 0 : date; + *mask |= KADM5_PRINC_EXPIRE_TIME; + continue; + } + } + if (strlen(argv[i]) == 9 && + !strcmp("-pwexpire", argv[i])) { + if (++i > argc - 2) + return -1; + else { + date = get_date(argv[i], NULL); + oprinc->pw_expiration = date == (time_t)-1 ? 0 : date; + *mask |= KADM5_PW_EXPIRATION; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-maxlife", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->max_life = get_date(argv[i], NULL) - now; + *mask |= KADM5_MAX_LIFE; + continue; + } + } + if (strlen(argv[i]) == 5 && + !strcmp("-kvno", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->kvno = atoi(argv[i]); + *mask |= KADM5_KVNO; + continue; + } + } + if (strlen(argv[i]) == 7 && + !strcmp("-policy", argv[i])) { + if (++i > argc - 2) + return -1; + else { + oprinc->policy = argv[i]; + *mask |= KADM5_POLICY; + continue; + } + } + if (strlen(argv[i]) == 12 && + !strcmp("-clearpolicy", argv[i])) { + oprinc->policy = NULL; + *mask |= KADM5_POLICY_CLR; + continue; + } + if (strlen(argv[i]) == 3 && + !strcmp("-pw", argv[i])) { + if (++i > argc - 2) + return -1; + else { + *pass = argv[i]; + continue; + } + } + if (strlen(argv[i]) == 8 && + !strcmp("-randkey", argv[i])) { + ++*randkey; + continue; + } + for (j = 0; j < sizeof (flags) / sizeof (struct pflag); j++) { + if (strlen(argv[i]) == flags[j].flaglen + 1 && + !strcmp(flags[j].flagname, + &argv[i][1] /* strip off leading + or - */)) { + if (flags[j].set && argv[i][0] == '-' || + !flags[j].set && argv[i][0] == '+') { + oprinc->attributes |= flags[j].theflag; + *mask |= KADM5_ATTRIBUTES; + attrib_set++; + break; + } else if (flags[j].set && argv[i][0] == '+' || + !flags[j].set && argv[i][0] == '-') { + oprinc->attributes &= ~flags[j].theflag; + *mask |= KADM5_ATTRIBUTES; + attrib_set++; + break; + } else { + return -1; + } + } + } + if (!attrib_set) + return -1; /* nothing was parsed */ + } + if (i != argc - 1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } + retval = kadmin_parse_name(argv[i], &oprinc->principal); + if (retval) { + com_err(caller, retval, "while parsing principal"); + return -1; + } + return 0; +} + +void kadmin_addmodprinc_usage(func) + char *func; +{ + fprintf(stderr, "usage: %s [options] principal\n", func); + fprintf(stderr, "\toptions are:\n"); + fprintf(stderr, "\t\t[-salt salttype] [-expire expdate] [-pwexpire pwexpdate]\n\t\t[-maxlife maxtixlife] [-kvno kvno] [-policy policy]\n\t\t[-randkey] [-pw password] [{+|-}attribute]\n"); + fprintf(stderr, "\tattributes are:\n"); + fprintf(stderr, "\t\tallow_tgs_req, allow_tix, needchange, password_changing_service\n"); +} + +void kadmin_addprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_principal_ent_rec princ; + long mask; + int randkey = 0; + char *pass, *canon; + krb5_error_code retval; + static char newpw[1024]; + static char prompt1[1024], prompt2[1024]; + + princ.attributes = 0; + if (kadmin_parse_princ_args(argc, argv, + &princ, &mask, &pass, &randkey, + "add_principal")) { + kadmin_addmodprinc_usage("add_principal"); + return; + } + retval = krb5_unparse_name(context, princ.principal, &canon); + if (retval) { + com_err("add_principal", + retval, "while canonicalizing principal"); + krb5_free_principal(context, princ.principal); + return; + } + if (randkey) { /* do special stuff if -randkey specified */ + princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; /* set notix */ + mask |= KADM5_ATTRIBUTES; + pass = "dummy"; + } else if (pass == NULL) { + int i = sizeof (newpw) - 1; + + sprintf(prompt1, "Enter password for principal \"%.900s\": ", + canon); + sprintf(prompt2, + "Re-enter password for principal \"%.900s\": ", + canon); + retval = krb5_read_password(context, prompt1, prompt2, + newpw, &i); + if (retval) { + com_err("add_principal", retval, + "while reading password for \"%s\".", canon); + free(canon); + krb5_free_principal(context, princ.principal); + return; + } + pass = newpw; + } + mask |= KADM5_PRINCIPAL; + retval = kadm5_create_principal(handle, &princ, mask, pass); + if (retval) { + com_err("add_principal", retval, "while creating \"%s\".", + canon); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + if (randkey) { /* more special stuff for -randkey */ + retval = kadm5_randkey_principal(handle, princ.principal, + NULL, NULL); + if (retval) { + com_err("add_principal", retval, + "while randomizing key for \"%s\".", canon); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + princ.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; /* clear notix */ + mask = KADM5_ATTRIBUTES; + retval = kadm5_modify_principal(handle, &princ, mask); + if (retval) { + com_err("add_principal", retval, + "while clearing DISALLOW_ALL_TIX for \"%s\".", canon); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + } + krb5_free_principal(context, princ.principal); + printf("Principal \"%s\" created.\n", canon); + free(canon); +} + +void kadmin_modprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_principal_ent_rec princ, oldprinc; + krb5_principal kprinc; + long mask; + krb5_error_code retval; + char *pass, *canon; + int randkey = 0; + + if (argc < 2) { + kadmin_addmodprinc_usage("modify_principal"); + return; + } + + retval = kadmin_parse_name(argv[argc - 1], &kprinc); + if (retval) { + com_err("modify_principal", retval, "while parsing principal"); + return; + } + retval = krb5_unparse_name(context, kprinc, &canon); + if (retval) { + com_err("modify_principal", retval, + "while canonicalizing principal"); + krb5_free_principal(context, kprinc); + return; + } + retval = kadm5_get_principal(handle, kprinc, &oldprinc, + KADM5_PRINCIPAL_NORMAL_MASK); + krb5_free_principal(context, kprinc); + if (retval) { + com_err("modify_principal", retval, "while getting \"%s\".", + canon); + free(canon); + return; + } + princ.attributes = oldprinc.attributes; + kadm5_free_principal_ent(handle, &oldprinc); + retval = kadmin_parse_princ_args(argc, argv, + &princ, &mask, + &pass, &randkey, + "modify_principal"); + if (retval) { + kadmin_addmodprinc_usage("modify_principal"); + free(canon); + return; + } + if (randkey) { + fprintf(stderr, "modify_principal: -randkey not allowed\n"); + krb5_free_principal(context, princ.principal); + free(canon); + return; + } + retval = kadm5_modify_principal(handle, &princ, mask); + krb5_free_principal(context, princ.principal); + if (retval) { + com_err("modify_principal", retval, + "while modifying \"%s\".", canon); + free(canon); + return; + } + printf("Principal \"%s\" modified.\n", canon); + free(canon); +} + +void kadmin_getprinc(argc, argv) + int argc; + char *argv[]; +{ + kadm5_principal_ent_rec dprinc; + krb5_principal princ; + krb5_error_code retval; + char *canon, *modcanon; + int i; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-terse", argv[1])))) { + fprintf(stderr, "usage: get_principal [-terse] principal\n"); + return; + } + retval = kadmin_parse_name(argv[argc - 1], &princ); + if (retval) { + com_err("get_principal", retval, "while parsing principal"); + return; + } + retval = krb5_unparse_name(context, princ, &canon); + if (retval) { + com_err("get_principal", retval, "while canonicalizing principal"); + krb5_free_principal(context, princ); + return; + } + retval = kadm5_get_principal(handle, princ, &dprinc, + KADM5_PRINCIPAL_NORMAL_MASK | KADM5_KEY_DATA); + krb5_free_principal(context, princ); + if (retval) { + com_err("get_principal", retval, "while retrieving \"%s\".", canon); + free(canon); + return; + } + retval = krb5_unparse_name(context, dprinc.mod_name, &modcanon); + if (retval) { + com_err("get_principal", retval, "while unparsing modname"); + kadm5_free_principal_ent(handle, &dprinc); + free(canon); + return; + } + if (argc == 2) { + printf("Principal: %s\n", canon); + printf("Expiration date: %s\n", strdate(dprinc.princ_expire_time)); + printf("Last password change: %s\n", + strdate(dprinc.last_pwd_change)); + printf("Password expiration date: %s\n", + dprinc.pw_expiration ? + strdate(dprinc.pw_expiration) : "[none]"); + printf("Maximum ticket life: %s\n", strdur(dprinc.max_life)); + printf("Last modified: by %s\n\ton %s\n", + modcanon, strdate(dprinc.mod_date)); + printf("Last successful authentication: %s\n", + strdate(dprinc.last_success)); + printf("Last failed authentication: %s\n", + strdate(dprinc.last_failed)); + printf("Failed password attempts: %d\n", + dprinc.fail_auth_count); + printf("Number of keys: %d\n", dprinc.n_key_data); + for (i = 0; i < dprinc.n_key_data; i++) { + krb5_key_data *key_data = &dprinc.key_data[i]; + char enctype[BUFSIZ], salttype[BUFSIZ]; + + if (krb5_enctype_to_string(key_data->key_data_type[0], + enctype, sizeof(enctype))) + sprintf(enctype, "<Encryption type 0x%x>", + key_data->key_data_type[0]); + printf("Key: vno %d, %s, ", key_data->key_data_kvno, enctype); + if (key_data->key_data_ver > 1) { + if (krb5_salttype_to_string(key_data->key_data_type[1], + salttype, sizeof(salttype))) + sprintf(salttype, "<Salt type 0x%x>", + key_data->key_data_type[1]); + printf("%s\n", salttype); + } else + printf("no salt\n"); + } + + printf("Attributes:"); + for (i = 0; i < sizeof (prflags) / sizeof (char *); i++) { + if (dprinc.attributes & (krb5_flags) 1 << i) + printf(" %s", prflags[i]); + } + printf("\n"); + printf("Policy: %s\n", dprinc.policy ? dprinc.policy : "[none]"); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"\t%d\t%d\t%d\t%d\t\"%s\"" + "\t%d\t%d\t%d\t%d\t%d", + canon, dprinc.princ_expire_time, dprinc.last_pwd_change, + dprinc.pw_expiration, dprinc.max_life, modcanon, + dprinc.mod_date, dprinc.attributes, dprinc.kvno, + dprinc.mkvno, dprinc.policy, + dprinc.max_renewable_life, dprinc.last_success, + dprinc.last_failed, dprinc.fail_auth_count, + dprinc.n_key_data); + for (i = 0; i < dprinc.n_key_data; i++) + printf("\t%d\t%d\t%d\t%d", + dprinc.key_data[i].key_data_ver, + dprinc.key_data[i].key_data_kvno, + dprinc.key_data[i].key_data_type[0], + dprinc.key_data[i].key_data_type[1]); + printf("\n"); + } + free(modcanon); + kadm5_free_principal_ent(handle, &dprinc); + free(canon); +} + +void kadmin_getprincs(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char *exp, **names; + int i, count; + + exp = NULL; + if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) { + fprintf(stderr, "usage: get_principals [expression]\n"); + return; + } + retval = kadm5_get_principals(handle, exp, &names, &count); + if (retval) { + com_err("get_principals", retval, "while retrieving list."); + return; + } + for (i = 0; i < count; i++) + printf("%s\n", names[i]); + kadm5_free_name_list(handle, names, count); +} + +int kadmin_parse_policy_args(argc, argv, policy, mask, caller) + int argc; + char *argv[]; + kadm5_policy_ent_t policy; + long *mask; + char *caller; +{ + int i; + time_t now; + time_t date; + krb5_error_code retval; + + time(&now); + *mask = 0; + for (i = 1; i < argc - 1; i++) { + if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-maxlife")) { + if (++i > argc -2) + return -1; + else { + date = get_date(argv[i], NULL); + policy->pw_max_life = + (date == (time_t)-1 ? 0 : date) - now; + *mask |= KADM5_PW_MAX_LIFE; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-minlife")) { + if (++i > argc - 2) + return -1; + else { + date = get_date(argv[i], NULL); + policy->pw_min_life = + (date == (time_t)-1 ? 0 : date) - now; + *mask |= KADM5_PW_MIN_LIFE; + continue; + } + } else if (strlen(argv[i]) == 10 && + !strcmp(argv[i], "-minlength")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_length = atoi(argv[i]); + *mask |= KADM5_PW_MIN_LENGTH; + continue; + } + } else if (strlen(argv[i]) == 11 && + !strcmp(argv[i], "-minclasses")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_min_classes = atoi(argv[i]); + *mask |= KADM5_PW_MIN_CLASSES; + continue; + } + } else if (strlen(argv[i]) == 8 && + !strcmp(argv[i], "-history")) { + if (++i > argc - 2) + return -1; + else { + policy->pw_history_num = atoi(argv[i]); + *mask |= KADM5_PW_HISTORY_NUM; + continue; + } + } else + return -1; + } + if (i != argc -1) { + fprintf(stderr, "%s: parser lost count!\n", caller); + return -1; + } else + return 0; +} + +void kadmin_addmodpol_usage(func) + char *func; +{ + fprintf(stderr, "usage; %s [options] policy\n", func); + fprintf(stderr, "\toptions are:\n"); + fprintf(stderr, "\t\t[-maxlife time] [-minlife time] [-minlength length]\n\t\t[-minclasses number] [-history number]\n"); +} + +void kadmin_addpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + long mask; + kadm5_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, "add_policy")) { + kadmin_addmodpol_usage("add_policy"); + return; + } else { + policy.policy = argv[argc - 1]; + mask |= KADM5_POLICY; + retval = kadm5_create_policy(handle, &policy, mask); + if (retval) { + com_err("add_policy", retval, "while creating policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_modpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + long mask; + kadm5_policy_ent_rec policy; + + if (kadmin_parse_policy_args(argc, argv, &policy, &mask, + "modify_policy")) { + kadmin_addmodpol_usage("modify_policy"); + return; + } else { + policy.policy = argv[argc - 1]; + retval = kadm5_modify_policy(handle, &policy, mask); + if (retval) { + com_err("modify_policy", retval, "while modifying policy \"%s\".", + policy.policy); + return; + } + } + return; +} + +void kadmin_delpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char reply[5]; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-force", argv[1])))) { + fprintf(stderr, "usage: delete_policy [-force] policy\n"); + return; + } + if (argc == 2) { + printf("Are you sure you want to delete the policy \"%s\"? (yes/no): ", argv[1]); + fgets(reply, sizeof (reply), stdin); + if (strcmp("yes\n", reply)) { + fprintf(stderr, "Policy \"%s\" not deleted.\n", argv[1]); + return; + } + } + retval = kadm5_delete_policy(handle, argv[argc - 1]); + if (retval) { + com_err("delete_policy:", retval, "while deleting policy \"%s\"", + argv[argc - 1]); + return; + } + return; +} + +void kadmin_getpol(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + kadm5_policy_ent_rec policy; + + if (! (argc == 2 || + (argc == 3 && !strcmp("-terse", argv[1])))) { + fprintf(stderr, "usage: get_policy [-terse] policy\n"); + return; + } + retval = kadm5_get_policy(handle, argv[argc - 1], &policy); + if (retval) { + com_err("get_policy", retval, "while retrieving policy \"%s\".", + argv[argc - 1]); + return; + } + if (argc == 2) { + printf("Policy: %s\n", policy.policy); + printf("Maximum password life: %d\n", policy.pw_max_life); + printf("Minimum password life: %d\n", policy.pw_min_life); + printf("Minimum password length: %d\n", policy.pw_min_length); + printf("Minimum number of password character classes: %d\n", + policy.pw_min_classes); + printf("Number of old keys kept: %d\n", policy.pw_history_num); + printf("Reference count: %d\n", policy.policy_refcnt); + } else { + printf("\"%s\"\t%d\t%d\t%d\t%d\t%d\t%d\n", + policy.policy, policy.pw_max_life, policy.pw_min_life, + policy.pw_min_length, policy.pw_min_classes, + policy.pw_history_num, policy.policy_refcnt); + } + kadm5_free_policy_ent(handle, &policy); + return; +} + +void kadmin_getpols(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + char *exp, **names; + int i, count; + + exp = NULL; + if (! (argc == 1 || (argc == 2 && (exp = argv[1])))) { + fprintf(stderr, "usage: get_policies [expression]\n"); + return; + } + retval = kadm5_get_policies(handle, exp, &names, &count); + if (retval) { + com_err("get_policies", retval, "while retrieving list."); + return; + } + for (i = 0; i < count; i++) + printf("%s\n", names[i]); + kadm5_free_name_list(handle, names, count); +} + +kadmin_getprivs(argc, argv) + int argc; + char *argv[]; +{ + static char *privs[] = {"GET", "ADD", "MODIFY", "DELETE"}; + krb5_error_code retval; + int i; + long plist; + + if (argc != 1) { + fprintf(stderr, "usage: get_privs\n"); + return; + } + retval = kadm5_get_privs(handle, &plist); + if (retval) { + com_err("get_privs", retval, "while retrieving privileges"); + return; + } + printf("current privileges:"); + for (i = 0; i < sizeof (privs) / sizeof (char *); i++) { + if (plist & 1 << i) + printf(" %s", privs[i]); + } + printf("\n"); + return; +} diff --git a/src/kadmin/cli/kadmin_ct.ct b/src/kadmin/cli/kadmin_ct.ct new file mode 100644 index 000000000..d22229bf0 --- /dev/null +++ b/src/kadmin/cli/kadmin_ct.ct @@ -0,0 +1,79 @@ +# Copyright 1994 by the Massachusetts Institute of Technology. +# All Rights Reserved. +# +# Export of this software from the United States of America may +# require a specific license from the United States Government. +# It is the responsibility of any person or organization contemplating +# export to obtain such a license before exporting. +# +# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and +# distribute this software and its documentation for any purpose and +# without fee is hereby granted, provided that the above copyright +# notice appear in all copies and that both that copyright notice and +# this permission notice appear in supporting documentation, and that +# the name of M.I.T. not be used in advertising or publicity pertaining +# to distribution of the software without specific, written prior +# permission. M.I.T. makes no representations about the suitability of +# this software for any purpose. It is provided "as is" without express +# or implied warranty. +# +# +# Command table for kadmin CLI for OVSecure +# + +command_table kadmin_cmds; + +request kadmin_addprinc, "Add principal", + add_prinicpal, addprinc, ank; + +request kadmin_delprinc, "Delete principal", + delete_principal, delprinc; + +request kadmin_modprinc, "Modify principal", + modify_principal, modprinc; + +request kadmin_renprinc, "Rename principal", + rename_principal, renprinc; + +request kadmin_cpw, "Change password", + change_password, cpw; + +request kadmin_getprinc, "Get principal", + get_principal, getprinc; + +request kadmin_getprincs, "Get principals", + get_principals, getprincs; + +request kadmin_addpol, "Add policy", + add_policy, addpol; + +request kadmin_modpol, "Modify policy", + modify_policy, modpol; + +request kadmin_delpol, "Delete policy", + delete_policy, delpol; + +request kadmin_getpol, "Get policy", + get_policy, getpol; + +request kadmin_getpols, "Get policies", + get_policies, getpols; + +request kadmin_getprivs, "Get privileges", + get_privs, getprivs; + +request kadmin_keytab_add, "Add entry(s) to a keytab", + ktadd, xst; + +request kadmin_keytab_remove, "Remove entry(s) from a keytab", + ktremove, ktrem; + +# list_requests is generic -- unrelated to Kerberos +request ss_list_requests, "List available requests.", + list_requests, lr, "?"; + +request ss_quit, "Exit program.", + quit, exit, q; + +end; + diff --git a/src/kadmin/cli/keytab.c b/src/kadmin/cli/keytab.c new file mode 100644 index 000000000..4b51140b5 --- /dev/null +++ b/src/kadmin/cli/keytab.c @@ -0,0 +1,420 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <krb5.h> +#include <k5-int.h> +#include <kadm5/admin.h> + +static int add_principal(void *handle, char *keytab_str, krb5_keytab keytab, + char *princ_str); +static int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str); +static char *etype_string(krb5_enctype enctype); + +extern char *krb5_defkeyname; +extern char *whoami; +extern krb5_context context; +extern void *handle; +static int quiet; + +void add_usage() +{ + fprintf(stderr, "Usage: ktadd [-k[eytab] keytab] [-q] [principal | -glob princ-exp] [...]\n"); +} + +void rem_usage() +{ + fprintf(stderr, "Usage: ktremove [-k[eytab] keytab] [-q] principal [kvno|\"all\"|\"old\"]\n"); +} + +int process_keytab(krb5_context context, char **keytab_str, + krb5_keytab *keytab) +{ + int code; + + if (*keytab_str == NULL) { + if (! (*keytab_str = strdup(krb5_defkeyname))) { + com_err(whoami, ENOMEM, "while creating keytab name"); + return 1; + } + code = krb5_kt_default(context, keytab); + if (code != 0) { + com_err(whoami, code, "while opening default keytab"); + free(*keytab_str); + return 1; + } + } else { + if (strchr(*keytab_str, ':') != NULL) { + *keytab_str = strdup(*keytab_str); + if (*keytab_str == NULL) { + com_err(whoami, ENOMEM, "while creating keytab name"); + return 1; + } + } else { + char *tmp = *keytab_str; + + *keytab_str = (char *) + malloc(strlen("WRFILE:")+strlen(tmp)+1); + if (*keytab_str == NULL) { + com_err(whoami, ENOMEM, "while creating keytab name"); + return 1; + } + sprintf(*keytab_str, "WRFILE:%s", tmp); + } + + code = krb5_kt_resolve(context, *keytab_str, keytab); + if (code != 0) { + com_err(whoami, code, "while resolving keytab %s", *keytab_str); + free(keytab_str); + return 1; + } + } + + return 0; +} + + +void kadmin_keytab_add(int argc, char **argv) +{ + krb5_keytab keytab = 0; + char *princ_str, *keytab_str = NULL, **princs; + int code, num, i; + + argc--; argv++; + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + argc--; argv++; + if (!argc || keytab_str) { + add_usage(); + return; + } + keytab_str = *argv; + } else if (strcmp(*argv, "-q") == 0) { + quiet++; + } else + break; + argc--; argv++; + } + + if (argc == 0) { + add_usage(); + return; + } + + if (process_keytab(context, &keytab_str, &keytab)) + return; + + while (*argv) { + if (strcmp(*argv, "-glob") == 0) { + if (*++argv == NULL) { + add_usage(); + break; + } + + if (code = kadm5_get_principals(handle, *argv, &princs, &num)) { + com_err(whoami, code, "while expanding expression \"%s\".", + *argv); + argv++; + continue; + } + + for (i = 0; i < num; i++) + (void) add_principal(handle, keytab_str, keytab, + princs[i]); + kadm5_free_name_list(handle, princs, num); + } else + (void) add_principal(handle, keytab_str, keytab, *argv); + argv++; + } + + code = krb5_kt_close(context, keytab); + if (code != 0) + com_err(whoami, code, "while closing keytab"); + + free(keytab_str); +} + +void kadmin_keytab_remove(int argc, char **argv) +{ + krb5_keytab keytab = 0; + char *princ_str, *keytab_str = NULL; + int code; + + argc--; argv++; + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + argc--; argv++; + if (!argc || keytab_str) { + rem_usage(); + return; + } + keytab_str = *argv; + } else if (strcmp(*argv, "-q") == 0) { + quiet++; + } else + break; + argc--; argv++; + } + + if (argc != 1 && argc != 2) { + rem_usage(); + return; + } + if (process_keytab(context, &keytab_str, &keytab)) + return; + + (void) remove_principal(keytab_str, keytab, argv[0], argv[1]); + + code = krb5_kt_close(context, keytab); + if (code != 0) + com_err(whoami, code, "while closing keytab"); + + free(keytab_str); +} + +int add_principal(void *handle, char *keytab_str, krb5_keytab keytab, + char *princ_str) +{ + kadm5_principal_ent_rec princ_rec; + krb5_principal princ; + krb5_keytab_entry new_entry; + krb5_keyblock *keys; + int code, code2, mask, nkeys, i; + + (void) memset((char *)&princ_rec, 0, sizeof(princ_rec)); + + princ = NULL; + keys = NULL; + nkeys = 0; + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing -add principal name %s", + princ_str); + goto cleanup; + } + + code = kadm5_randkey_principal(handle, princ, &keys, &nkeys); + if (code != 0) { + if (code == KADM5_UNK_PRINC) { + fprintf(stderr, "%s: Principal %s does not exist.\n", + whoami, princ_str); + } else + com_err(whoami, code, "while changing %s's key", + princ_str); + goto cleanup; + } + + code = kadm5_get_principal(handle, princ, &princ_rec, + KADM5_PRINCIPAL_NORMAL_MASK); + if (code != 0) { + com_err(whoami, code, "while retrieving principal"); + goto cleanup; + } + + for (i = 0; i < nkeys; i++) { + memset((char *) &new_entry, 0, sizeof(new_entry)); + new_entry.principal = princ; + new_entry.key = keys[i]; + new_entry.vno = princ_rec.kvno; + + code = krb5_kt_add_entry(context, keytab, &new_entry); + if (code != 0) { + com_err(whoami, code, "while adding key to keytab"); + (void) kadm5_free_principal_ent(handle, &princ_rec); + goto cleanup; + } + + if (!quiet) + printf("%s: Entry for principal %s with kvno %d, " + "encryption type %s added to keytab %s.\n", + whoami, princ_str, princ_rec.kvno, + etype_string(keys[i].enctype), keytab_str); + } + + code = kadm5_free_principal_ent(handle, &princ_rec); + if (code != 0) { + com_err(whoami, code, "while freeing principal entry"); + goto cleanup; + } + +cleanup: + if (nkeys) { + for (i = 0; i < nkeys; i++) + krb5_free_keyblock(context, &keys[i]); + free(keys); + } + if (princ) + krb5_free_principal(context, princ); + + return code; +} + +int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str) +{ + krb5_principal princ; + krb5_keytab_entry entry; + krb5_kt_cursor cursor; + enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; + int code, kvno, did_something; + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing principal name %s", + princ_str); + return code; + } + + mode = UNDEF; + if (kvno_str == NULL) { + mode = HIGH; + kvno = 0; + } else if (strcmp(kvno_str, "all") == 0) { + mode = ALL; + kvno = 0; + } else if (strcmp(kvno_str, "old") == 0) { + mode = OLD; + kvno = 0; + } else { + mode = SPEC; + kvno = atoi(kvno_str); + } + + /* kvno is set to specified value for SPEC, 0 otherwise */ + code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); + if (code != 0) { + if (code == ENOENT) { + fprintf(stderr, "%s: Keytab %s does not exist.\n", + whoami, keytab_str); + } else if (code == KRB5_KT_NOTFOUND) { + if (mode != SPEC) + fprintf(stderr, "%s: No entry for principal " + "%s exists in keytab %s\n", + whoami, princ_str, keytab_str); + else + fprintf(stderr, "%s: No entry for principal " + "%s with kvno %d exists in keytab " + "%s.\n", whoami, princ_str, kvno, + keytab_str); + } else { + com_err(whoami, code, "while retrieving highest kvno " + "from keytab"); + } + return code; + } + + /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ + kvno = entry.vno; + krb5_kt_free_entry(context, &entry); + + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while starting keytab scan"); + return code; + } + + did_something = 0; + while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { + if (krb5_principal_compare(context, princ, entry.principal) && + ((mode == ALL) || + (mode == SPEC && entry.vno == kvno) || + (mode == OLD && entry.vno != kvno) || + (mode == HIGH && entry.vno == kvno))) { + + /* + * Ack! What a kludge... the scanning functions lock + * the keytab so entries cannot be removed while they + * are operating. + */ + code = krb5_kt_end_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while temporarily ending " + "keytab scan"); + return code; + } + code = krb5_kt_remove_entry(context, keytab, &entry); + if (code != 0) { + com_err(whoami, code, "while deleting entry from keytab"); + return code; + } + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while restarting keytab scan"); + return code; + } + + did_something++; + if (!quiet) + printf("%s: Entry for principal %s with kvno %d " + "removed from keytab %s.\n", whoami, + princ_str, entry.vno, keytab_str); + } + krb5_kt_free_entry(context, &entry); + } + if (code && code != KRB5_KT_END) { + com_err(whoami, code, "while scanning keytab"); + return code; + } + if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) { + com_err(whoami, code, "while ending keytab scan"); + return code; + } + + /* + * If !did_someting then mode must be OLD or we would have + * already returned with an error. But check it anyway just to + * prevent unexpected error messages... + */ + if (!did_something && mode == OLD) { + fprintf(stderr, "%s: There is only one entry for principal " + "%s in keytab %s\n", whoami, princ_str, keytab_str); + return 1; + } + + return 0; +} + +/* + * etype_string(enctype): return a string representation of the + * encryption type. XXX copied from klist.c; this should be a + * library function, or perhaps just #defines + */ +static char *etype_string(enctype) + krb5_enctype enctype; +{ + static char buf[12]; + + switch (enctype) { + case ENCTYPE_DES_CBC_CRC: + return "DES-CBC-CRC"; + break; + case ENCTYPE_DES_CBC_MD4: + return "DES-CBC-MD4"; + break; + case ENCTYPE_DES_CBC_MD5: + return "DES-CBC-MD5"; + break; +#if 0 + case ENCTYPE_DES3_CBC_MD5: + return "DES3-CBC-MD5"; + break; +#endif + default: + sprintf(buf, "etype %d", enctype); + return buf; + break; + } +} diff --git a/src/kadmin/cli/memmove.c b/src/kadmin/cli/memmove.c new file mode 100644 index 000000000..abc91e923 --- /dev/null +++ b/src/kadmin/cli/memmove.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Torek. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define MEMMOVE + +/* based on @(#)bcopy.c 5.11 (Berkeley) 6/21/91 */ + +#include <krb5/osconf.h> +#include <krb5/config.h> +#ifdef USE_STRING_H +#include <string.h> +#else +#include <strings.h> +#endif + +/* + * sizeof(word) MUST BE A POWER OF TWO + * SO THAT wmask BELOW IS ALL ONES + */ +typedef int word; /* "word" used for optimal copy speed */ + +#define wsize sizeof(word) +#define wmask (wsize - 1) + +/* + * Copy a block of memory, handling overlap. + * This is the routine that actually implements + * (the portable versions of) bcopy, memcpy, and memmove. + */ +#ifdef MEMCOPY +void * +memcpy(dst0, src0, length) +#else +#ifdef MEMMOVE +void * +memmove(dst0, src0, length) +#else +void +bcopy(src0, dst0, length) +#endif +#endif + void *dst0; + const void *src0; + register size_t length; +{ + register char *dst = dst0; + register const char *src = src0; + register size_t t; + + if (length == 0 || dst == src) /* nothing to do */ + goto done; + + /* + * Macros: loop-t-times; and loop-t-times, t>0 + */ +#define TLOOP(s) if (t) TLOOP1(s) +#define TLOOP1(s) do { s; } while (--t) + + if ((unsigned long)dst < (unsigned long)src) { + /* + * Copy forward. + */ + t = (int)src; /* only need low bits */ + if ((t | (int)dst) & wmask) { + /* + * Try to align operands. This cannot be done + * unless the low bits match. + */ + if ((t ^ (int)dst) & wmask || length < wsize) + t = length; + else + t = wsize - (t & wmask); + length -= t; + TLOOP1(*dst++ = *src++); + } + /* + * Copy whole words, then mop up any trailing bytes. + */ + t = length / wsize; + TLOOP(*(word *)dst = *(word *)src; src += wsize; dst += wsize); + t = length & wmask; + TLOOP(*dst++ = *src++); + } else { + /* + * Copy backwards. Otherwise essentially the same. + * Alignment works as before, except that it takes + * (t&wmask) bytes to align, not wsize-(t&wmask). + */ + src += length; + dst += length; + t = (int)src; + if ((t | (int)dst) & wmask) { + if ((t ^ (int)dst) & wmask || length <= wsize) + t = length; + else + t &= wmask; + length -= t; + TLOOP1(*--dst = *--src); + } + t = length / wsize; + TLOOP(src -= wsize; dst -= wsize; *(word *)dst = *(word *)src); + t = length & wmask; + TLOOP(*--dst = *--src); + } +done: +#if defined(MEMCOPY) || defined(MEMMOVE) + return (dst0); +#else + return; +#endif +} diff --git a/src/kadmin/cli/setenv.c b/src/kadmin/cli/setenv.c new file mode 100644 index 000000000..a2432c3d6 --- /dev/null +++ b/src/kadmin/cli/setenv.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* based on @(#)setenv.c 8.1 (Berkeley) 6/4/93 */ +/* based on @(#)getenv.c 8.1 (Berkeley) 6/4/93 */ + +#ifndef __STDC__ +#define const +#endif + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#ifndef __P +#define __P(x) () +#endif +char *__findenv __P((const char *, int *)); + +/* + * setenv -- + * Set the value of the environmental variable "name" to be + * "value". If rewrite is set, replace any current value. + */ +setenv(name, value, rewrite) + register const char *name; + register const char *value; + int rewrite; +{ + extern char **environ; + static int alloced; /* if allocated space before */ + register char *c; + int l_value, offset; + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + if ((c = __findenv(name, &offset))) { /* find if already exists */ + if (!rewrite) + return (0); + if (strlen(c) >= l_value) { /* old larger; copy over */ + while (*c++ = *value++); + return (0); + } + } else { /* create new slot */ + register int cnt; + register char **p; + + for (p = environ, cnt = 0; *p; ++p, ++cnt); + if (alloced) { /* just increase size */ + environ = (char **)realloc((char *)environ, + (size_t)(sizeof(char *) * (cnt + 2))); + if (!environ) + return (-1); + } + else { /* get new space */ + alloced = 1; /* copy old entries into it */ + p = (char **)malloc((size_t)(sizeof(char *) * (cnt + 2))); + if (!p) + return (-1); + memcpy(p, environ, cnt * sizeof(char *)); + environ = p; + } + environ[cnt + 1] = NULL; + offset = cnt; + } + for (c = (char *)name; *c && *c != '='; ++c); /* no `=' in name */ + if (!(environ[offset] = /* name + `=' + value */ + malloc((size_t)((int)(c - name) + l_value + 2)))) + return (-1); + for (c = environ[offset]; (*c = *name++) && *c != '='; ++c); + for (*c++ = '='; *c++ = *value++;); + return (0); +} + +/* + * unsetenv(name) -- + * Delete environmental variable "name". + */ +void +unsetenv(name) + const char *name; +{ + extern char **environ; + register char **p; + int offset; + + while (__findenv(name, &offset)) /* if set multiple times */ + for (p = &environ[offset];; ++p) + if (!(*p = *(p + 1))) + break; +} + +/* + * getenv -- + * Returns ptr to value associated with name, if any, else NULL. + */ +char * +getenv(name) + const char *name; +{ + int offset; + + return (__findenv(name, &offset)); +} + +/* + * __findenv -- + * Returns pointer to value associated with name, if any, else NULL. + * Sets offset to be the offset of the name/value combination in the + * environmental array, for use by setenv(3) and unsetenv(3). + * Explicitly removes '=' in argument name. + */ +static char * +__findenv(name, offset) + register const char *name; + int *offset; +{ + extern char **environ; + register int len; + register const char *np; + register char **p, *c; + + if (name == NULL || environ == NULL) + return (NULL); + for (np = name; *np && *np != '='; ++np) + continue; + len = np - name; + for (p = environ; (c = *p) != NULL; ++p) + if (strncmp(c, name, len) == 0 && c[len] == '=') { + *offset = p - environ; + return (c + len + 1); + } + return (NULL); +} diff --git a/src/kadmin/cli/ss_wrapper.c b/src/kadmin/cli/ss_wrapper.c new file mode 100644 index 000000000..89e94b36b --- /dev/null +++ b/src/kadmin/cli/ss_wrapper.c @@ -0,0 +1,61 @@ +/* + * Copyright 1994 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * ss wrapper for kadmin + */ + +#include <krb5.h> +#include <ss/ss.h> +#include <stdio.h> +#include <string.h> + +extern ss_request_table kadmin_cmds; +extern int exit_status; +extern char *kadmin_startup(); +extern char *whoami; + +int main(argc, argv) + int argc; + char *argv[]; +{ + char *request; + krb5_error_code retval; + int sci_idx, code = 0; + + whoami = ((whoami = strrchr(argv[0], '/')) ? whoami+1 : argv[0]); + + request = kadmin_startup(argc, argv); + sci_idx = ss_create_invocation(whoami, "5.0", (char *) NULL, + &kadmin_cmds, &retval); + if (retval) { + ss_perror(sci_idx, retval, "creating invocation"); + exit(1); + } + if (request) { + code = ss_execute_line(sci_idx, request); + if (code != 0) { + ss_perror(sci_idx, code, request); + exit_status++; + } + } else + ss_listen(sci_idx, &retval); + return quit() ? 1 : exit_status; +} diff --git a/src/kadmin/cli/strftime.c b/src/kadmin/cli/strftime.c new file mode 100644 index 000000000..484852a72 --- /dev/null +++ b/src/kadmin/cli/strftime.c @@ -0,0 +1,469 @@ +/* strftime - custom formatting of date and/or time + Copyright (C) 1989, 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* Note: this version of strftime lacks locale support, + but it is standalone. + + Performs `%' substitutions similar to those in printf. Except + where noted, substituted fields have a fixed size; numeric fields are + padded if necessary. Padding is with zeros by default; for fields + that display a single number, padding can be changed or inhibited by + following the `%' with one of the modifiers described below. Unknown + field specifiers are copied as normal characters. All other + characters are copied to the output without change. + + Supports a superset of the ANSI C field specifiers. + + Literal character fields: + % % + n newline + t tab + + Numeric modifiers (a nonstandard extension): + - do not pad the field + _ pad the field with spaces + + Time fields: + %H hour (00..23) + %I hour (01..12) + %k hour ( 0..23) + %l hour ( 1..12) + %M minute (00..59) + %p locale's AM or PM + %r time, 12-hour (hh:mm:ss [AP]M) + %R time, 24-hour (hh:mm) + %s time in seconds since 00:00:00, Jan 1, 1970 (a nonstandard extension) + %S second (00..61) + %T time, 24-hour (hh:mm:ss) + %X locale's time representation (%H:%M:%S) + %Z time zone (EDT), or nothing if no time zone is determinable + + Date fields: + %a locale's abbreviated weekday name (Sun..Sat) + %A locale's full weekday name, variable length (Sunday..Saturday) + %b locale's abbreviated month name (Jan..Dec) + %B locale's full month name, variable length (January..December) + %c locale's date and time (Sat Nov 04 12:02:33 EST 1989) + %C century (00..99) + %d day of month (01..31) + %e day of month ( 1..31) + %D date (mm/dd/yy) + %h same as %b + %j day of year (001..366) + %m month (01..12) + %U week number of year with Sunday as first day of week (00..53) + %w day of week (0..6) + %W week number of year with Monday as first day of week (00..53) + %x locale's date representation (mm/dd/yy) + %y last two digits of year (00..99) + %Y year (1970...) + + David MacKenzie <djm@gnu.ai.mit.edu> */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#if defined(TM_IN_SYS_TIME) || (!defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME)) +#include <sys/time.h> +#else +#include <time.h> +#endif + +#ifndef STDC_HEADERS +time_t mktime (); +#endif + +#if defined(HAVE_TZNAME) +extern char *tzname[2]; +#endif + +/* Types of padding for numbers in date and time. */ +enum padding +{ + none, blank, zero +}; + +static char const* const days[] = +{ + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; + +static char const * const months[] = +{ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + +/* Add character C to STRING and increment LENGTH, + unless LENGTH would exceed MAX. */ + +#define add_char(c) \ + do \ + { \ + if (length + 1 <= max) \ + string[length++] = (c); \ + } \ + while (0) + +/* Add a 2 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num2 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Add a 3 digit number to STRING, padding if specified. + Return the number of characters added, up to MAX. */ + +static int +add_num3 (string, num, max, pad) + char *string; + int num; + int max; + enum padding pad; +{ + int top = num / 100; + int mid = (num - top * 100) / 10; + int length = 0; + + if (top == 0 && pad == blank) + add_char (' '); + else if (top != 0 || pad == zero) + add_char (top + '0'); + if (mid == 0 && top == 0 && pad == blank) + add_char (' '); + else if (mid != 0 || top != 0 || pad == zero) + add_char (mid + '0'); + add_char (num % 10 + '0'); + return length; +} + +/* Like strncpy except return the number of characters copied. */ + +static int +add_str (to, from, max) + char *to; + const char *from; + int max; +{ + int i; + + for (i = 0; from[i] && i <= max; ++i) + to[i] = from[i]; + return i; +} + +static int +add_num_time_t (string, max, num) + char *string; + int max; + time_t num; +{ + /* This buffer is large enough to hold the character representation + (including the trailing NUL) of any unsigned decimal quantity + whose binary representation fits in 128 bits. */ + char buf[40]; + int length; + + if (sizeof (num) > 16) + abort (); + sprintf (buf, "%lu", (unsigned long) num); + length = add_str (string, buf, max); + return length; +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Sundays. */ + +static int +sun_week (tm) + struct tm *tm; +{ + int dl; + + /* Set `dl' to the day in the year of the last day of the week previous + to the one containing the day specified in TM. If the day specified + in TM is in the first week of the year, `dl' will be negative or 0. + Otherwise, calculate the number of complete weeks before our week + (dl / 7) and add any partial week at the start of the year (dl % 7). */ + dl = tm->tm_yday - tm->tm_wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +/* Return the week in the year of the time in TM, with the weeks + starting on Mondays. */ + +static int +mon_week (tm) + struct tm *tm; +{ + int dl, wday; + + if (tm->tm_wday == 0) + wday = 6; + else + wday = tm->tm_wday - 1; + dl = tm->tm_yday - wday; + return dl <= 0 ? 0 : dl / 7 + (dl % 7 != 0); +} + +#if !defined(HAVE_TM_ZONE) && !defined(HAVE_TZNAME) +char * +zone_name (tp) + struct tm *tp; +{ + char *timezone (); + struct timeval tv; + struct timezone tz; + + gettimeofday (&tv, &tz); + return timezone (tz.tz_minuteswest, tp->tm_isdst); +} +#endif + +/* Format the time given in TM according to FORMAT, and put the + results in STRING. + Return the number of characters (not including terminating null) + that were put into STRING, or 0 if the length would have + exceeded MAX. */ + +size_t +strftime (string, max, format, tm) + char *string; + size_t max; + const char *format; + const struct tm *tm; +{ + enum padding pad; /* Type of padding to apply. */ + size_t length = 0; /* Characters put in STRING so far. */ + + for (; *format && length < max; ++format) + { + if (*format != '%') + add_char (*format); + else + { + ++format; + /* Modifiers: */ + if (*format == '-') + { + pad = none; + ++format; + } + else if (*format == '_') + { + pad = blank; + ++format; + } + else + pad = zero; + + switch (*format) + { + /* Literal character fields: */ + case 0: + case '%': + add_char ('%'); + break; + case 'n': + add_char ('\n'); + break; + case 't': + add_char ('\t'); + break; + default: + add_char (*format); + break; + + /* Time fields: */ + case 'H': + case 'k': + length += + add_num2 (&string[length], tm->tm_hour, max - length, + *format == 'H' ? pad : blank); + break; + case 'I': + case 'l': + { + int hour12; + + if (tm->tm_hour == 0) + hour12 = 12; + else if (tm->tm_hour > 12) + hour12 = tm->tm_hour - 12; + else + hour12 = tm->tm_hour; + length += + add_num2 (&string[length], hour12, max - length, + *format == 'I' ? pad : blank); + } + break; + case 'M': + length += + add_num2 (&string[length], tm->tm_min, max - length, pad); + break; + case 'p': + if (tm->tm_hour < 12) + add_char ('A'); + else + add_char ('P'); + add_char ('M'); + break; + case 'r': + length += + strftime (&string[length], max - length, "%I:%M:%S %p", tm); + break; + case 'R': + length += + strftime (&string[length], max - length, "%H:%M", tm); + break; + + case 's': + { + struct tm writable_tm; + writable_tm = *tm; + length += add_num_time_t (&string[length], max - length, + mktime (&writable_tm)); + } + break; + + case 'S': + length += + add_num2 (&string[length], tm->tm_sec, max - length, pad); + break; + case 'T': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'X': + length += + strftime (&string[length], max - length, "%H:%M:%S", tm); + break; + case 'Z': +#ifdef HAVE_TM_ZONE + length += add_str (&string[length], tm->tm_zone, max - length); +#else +#ifdef HAVE_TZNAME + if (tm->tm_isdst && tzname[1] && *tzname[1]) + length += add_str (&string[length], tzname[1], max - length); + else + length += add_str (&string[length], tzname[0], max - length); +#else + length += add_str (&string[length], zone_name (tm), max - length); +#endif +#endif + break; + + /* Date fields: */ + case 'a': + add_char (days[tm->tm_wday][0]); + add_char (days[tm->tm_wday][1]); + add_char (days[tm->tm_wday][2]); + break; + case 'A': + length += + add_str (&string[length], days[tm->tm_wday], max - length); + break; + case 'b': + case 'h': + add_char (months[tm->tm_mon][0]); + add_char (months[tm->tm_mon][1]); + add_char (months[tm->tm_mon][2]); + break; + case 'B': + length += + add_str (&string[length], months[tm->tm_mon], max - length); + break; + case 'c': + length += + strftime (&string[length], max - length, + "%a %b %d %H:%M:%S %Z %Y", tm); + break; + case 'C': + length += + add_num2 (&string[length], (tm->tm_year + 1900) / 100, + max - length, pad); + break; + case 'd': + length += + add_num2 (&string[length], tm->tm_mday, max - length, pad); + break; + case 'e': + length += + add_num2 (&string[length], tm->tm_mday, max - length, blank); + break; + case 'D': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'j': + length += + add_num3 (&string[length], tm->tm_yday + 1, max - length, pad); + break; + case 'm': + length += + add_num2 (&string[length], tm->tm_mon + 1, max - length, pad); + break; + case 'U': + length += + add_num2 (&string[length], sun_week (tm), max - length, pad); + break; + case 'w': + add_char (tm->tm_wday + '0'); + break; + case 'W': + length += + add_num2 (&string[length], mon_week (tm), max - length, pad); + break; + case 'x': + length += + strftime (&string[length], max - length, "%m/%d/%y", tm); + break; + case 'y': + length += + add_num2 (&string[length], tm->tm_year % 100, + max - length, pad); + break; + case 'Y': + add_char ((tm->tm_year + 1900) / 1000 + '0'); + length += + add_num3 (&string[length], + (1900 + tm->tm_year) % 1000, max - length, zero); + break; + } + } + } + add_char (0); + return length - 1; +} diff --git a/src/kadmin/config.mk/ChangeLog b/src/kadmin/config.mk/ChangeLog new file mode 100644 index 000000000..5dd46d71a --- /dev/null +++ b/src/kadmin/config.mk/ChangeLog @@ -0,0 +1,10 @@ +Fri Jul 12 14:39:28 1996 Marc Horowitz <marc@mit.edu> + + * architecture: add uname test for NetBSD + * netbsd1.def: added + +Mon Jul 8 16:39:36 1996 Barry Jaspan <bjaspan@mit.edu> + + * template, site.def, rules: Add SITEMAKEFILES to specify -f + Makefile.ov, add .ct default rule. + diff --git a/src/kadmin/config.mk/aix3.2.def b/src/kadmin/config.mk/aix3.2.def new file mode 100644 index 000000000..44fd07b1f --- /dev/null +++ b/src/kadmin/config.mk/aix3.2.def @@ -0,0 +1,40 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +# Make sure there is a blank space at the end of this line! +export PS_TTY = ps -t +export RSH_CMD = /usr/bin/rsh +export INSTCMD = /usr/local/bin/ginstall -c +export LEX = /usr/local/bin/flex +export AWK_CMD = /usr/local/bin/gawk + +# needed for fd_set to be defined +# Not convinced! Experimenting with disabling _BSD. - jik 3/21/95 +ifndef KRB5B4 +CFLAGS += -D_BSD +endif +CFLAGS += -D_ALL_SOURCE + +PCC_STRUCT_RETURN = -fpcc-struct-return +D_NEEDS_RPCENT = -D_SUN + +D_NEEDS_GETUSERSHELL = -DNEEDS_GETUSERSHELL +D_NEEDS_TREEWALK = -DNEEDS_TREEWALK +D_SETEUID = -DNO_SETEUID -DSETEUID_INCLUDE='<sys/id.h>' \ + -DSETEUIDPRE='setuidx(ID_REAL|ID_EFFECTIVE,' -DSETEUIDPOST=')' +D_NO_SETENV = -DNO_SETENV +D_REGEXP_TYPE = -DPOSIX_REGEXPS + +TERMCAPLIB = -ltermcap + +# extra libraries needed for login +LOGINLIB = -ls -lcfg -lodm + +UTMP_FILE = /etc/utmp +WTMP_FILE = /usr/adm/wtmp + +XINCDIR = /usr/lpp/X11/include +XLIBDIR = /usr/lpp/X11/lib + +OMIT_DOC = true +DONT_STRIP_NXLIBS = true diff --git a/src/kadmin/config.mk/architecture b/src/kadmin/config.mk/architecture new file mode 100644 index 000000000..d976c27b2 --- /dev/null +++ b/src/kadmin/config.mk/architecture @@ -0,0 +1,68 @@ +# $Id$ + +# sample `uname -a` output: +# SunOS dun-dun-n 4.1.3 3 sun4c +# SunOS samosa 5.3 Generic_101318-31 sun4m sparc +# HP-UX strange- A.09.01 A 9000/710 2005970723 two-user license +# AIX krusty 2 3 000131533500 + +# each section here should set ARCH_OS to a symbol specifically describing +# the OS, and also set "relevant" capability symbols + +ifndef ARCH_OS +__UNAME_A := $(shell uname -a) +ifeq "$(strip $(filter SunOS 4.1.3, $(__UNAME_A)))" "SunOS 4.1.3" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 4.1.3C, $(__UNAME_A)))" "SunOS 4.1.3C" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 4.1.3_U1, $(__UNAME_A)))" "SunOS 4.1.3_U1" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 4.1.4, $(__UNAME_A)))" "SunOS 4.1.4" + export ARCH_OS := sunos4.1 +endif +ifeq "$(strip $(filter SunOS 5.3, $(__UNAME_A)))" "SunOS 5.3" + export ARCH_OS := solaris2.3 +endif +# For now, assume that Solaris 2.4 is the same as Solaris 2.3. +ifeq "$(strip $(filter SunOS 5.4, $(__UNAME_A)))" "SunOS 5.4" + export ARCH_OS := solaris2.3 +endif +ifeq "$(strip $(filter HP-UX A.09.01, $(__UNAME_A)))" "HP-UX A.09.01" + export ARCH_OS := hpux9.01 +endif +ifeq "$(strip $(filter AIX 2, $(__UNAME_A)))" "AIX 2" + export ARCH_OS := aix3.2 +endif +ifeq "$(strip $(filter sweet-and-sour-sauce, $(__UNAME_A)))" "sweet-and-sour-sauce" + export ARCH_OS := aix3.2 + d:=$(shell echo "*** WARNING! Used hostname for architecture." 1>&2) +endif +ifeq "$(strip $(filter Linux, $(__UNAME_A)))" "Linux" + export ARCH_OS := linux +endif +ifeq "$(strip $(filter IRIX, $(__UNAME_A)))" "IRIX" + export ARCH_OS := irix5.2 +endif +ifeq "$(strip $(filter NetBSD, $(__UNAME_A)))" "NetBSD NetBSD" + export ARCH_OS := netbsd1 +endif + +ifndef ARCH_OS + d:=$(shell echo "*** WARNING! Unknown architecture: $(__UNAME_A)" 1>&2) + export ARCH_OS := dummy + export ARCH_OS_UNKNOWN := dummy +endif +endif + +ifndef ARCH_OS_UNKNOWN + define IncludeArchFile + include $(CONFDIR)/$(ARCH_OS).def + endef +else + define IncludeArchFile + endef +endif + diff --git a/src/kadmin/config.mk/config b/src/kadmin/config.mk/config new file mode 100644 index 000000000..9bb800e84 --- /dev/null +++ b/src/kadmin/config.mk/config @@ -0,0 +1,141 @@ +# $Id$ +# $Source$ + +# +# local Programs +# + +INSTHDRS = $(TOP)/scripts/inst-hdrs.sh +COMPILE_ET = $(TOP)/../util/et/compile_et +MK_CMDS = $(TOP)/../util/ss/mk_cmds +SYM_RANDOMIZE = $(TOP)/intl/sym-randomize.pl + +# +# Directories +# + +STAGETOP= $(TOP)/.. +STAGE_BINDIR = $(STAGETOP)/bin +STAGE_INCDIR = $(STAGETOP)/include +STAGE_LIBDIR = $(STAGETOP)/lib + +INSTALLTOP= $(TOP)/.. +INSTALL_BINDIR = $(INSTALLTOP)/bin +INSTALL_ETCDIR = $(INSTALLTOP)/etc +INSTALL_INCDIR = $(INSTALLTOP)/include +INSTALL_LIBDIR = $(INSTALLTOP)/lib +INSTALL_CONFDIR = $(INSTALLTOP)/config +INSTALL_PROTODIR = $(INSTALLTOP)/proto +INSTALL_ADMINDIR = $(INSTALLTOP)/admin +INSTALL_SERVERDIR = $(INSTALLTOP)/sbin +INSTALL_INSTDIR = $(INSTALLTOP)/install +INSTALL_DOCDIR = $(INSTALLTOP)/doc +INSTALL_MANDIR = $(INSTALLTOP)/man +INSTALL_SRCDIR = $(INSTALLTOP)/src + +# +# libraries +# + +# system +LIBM = -lm + +# stage +LIBADMCLNT := $(STAGE_LIBDIR)/libkadm5clnt.a +LIBADMSRV := $(STAGE_LIBDIR)/libkadm5srv.a +LIBRPCLIB := $(STAGE_LIBDIR)/libgssrpc.a +LIBDYN := $(STAGE_LIBDIR)/libdyn.a +LIBSS := $(STAGE_LIBDIR)/libss.a +LIBOVSEC_UTIL := $(STAGE_LIBDIR)/libkadm5_util.a +LIBFTPSEC := $(STAGE_LIBDIR)/libftpsec.a + +# install +LIBGSSAPI_KRB5 := $(INSTALL_LIBDIR)/libgssapi_krb5.a +LIBGSSAPI_KRB5_DX := $(INSTALL_LIBDIR)/libgssapi_krb5_dx.a +LIBGSSAPI_KRB5_WX := $(INSTALL_LIBDIR)/libgssapi_krb5_wx.a +LIBGSSAPI_KRB5_NX := $(INSTALL_LIBDIR)/libgssapi_krb5_nx.a +LIBGSSAPI_TRUST := $(INSTALL_LIBDIR)/libgssapi_trust.a +LIBDB := $(INSTALL_LIBDIR)/libdb.a +LIBKRB5 := $(INSTALL_LIBDIR)/libkrb5.a +LIBKDB5 := $(INSTALL_LIBDIR)/libkdb5.a +LIBCRYPTO := $(INSTALL_LIBDIR)/libcrypto.a +ifndef KRB5B4 +LIBISODE := $(INSTALL_LIBDIR)/libisode.a +endif +LIBCOM_ERR := $(INSTALL_LIBDIR)/libcom_err.a +LIBKRB5_ALL := $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) $(LIBCOM_ERR) +LIBKRB := $(INSTALL_LIBDIR)/libkrb4.a +LIBKDB := $(INSTALL_LIBDIR)/libkdb.a +LIBKADM := $(INSTALL_LIBDIR)/libkadm.a +LIBKRB425 := $(INSTALL_LIBDIR)/libkrb425.a +LIBDES425 := $(INSTALL_LIBDIR)/libdes425.a + +# X libraries. XXX this uses -L! but perhaps it doesn't matter, see +# [secure-build/2649] +XLIB = -L$(XLIBDIR) -lXext -lX11 + +# +# library name mangling for export +# + +ADMIN_INTERFACE_SYMBOL_FILES := $(TOP)/intl/adm-export-symbols \ + $(TOP)/intl/misc-export-symbols +OTHER_INTERFACE_SYMBOL_FILES := $(TOP)/intl/gss-export-symbols + +ADMIN_CRYPTO_LIBS := $(LIBADMCLNT) $(LIBADMSRV) $(LIBRPCLIB) \ + $(LIBGSSAPI_KRB5) $(LIBKRB5) $(LIBKDB5) $(LIBCRYPTO) \ + $(LIBISODE) $(LIBKRB) $(LIBKDB) $(LIBDES425) +OTHER_CRYPTO_LIBS := $(LIBGSSAPI_KRB5_DX) $(LIBGSSAPI_KRB5_WX) \ + $(LIBGSSAPI_KRB5_NX) + +# +# compiler/linker flags +# + +CFLAGS := $(CFLAGS) -I$(INSTALL_INCDIR) + +# +# Variables for testing +# +# These are all exported because lots of test scripts (/bin/sh, perl, +# tcl) use them. +# + +export TESTDIR = $(TOP)/testing +export COMPARE_DUMP = $(TESTDIR)/scripts/compare_dump.pl +export FIX_CONF_FILES = $(TESTDIR)/scripts/fixup-conf-files.pl +export INITDB = $(TESTDIR)/scripts/init_db +export MAKE_KEYTAB = $(TESTDIR)/scripts/make-host-keytab.pl +export LOCAL_MAKE_KEYTAB= $(TESTDIR)/scripts/make-host-keytab.pl +export RESTORE_FILES = $(TESTDIR)/scripts/restore_files.sh +export SAVE_FILES = $(TESTDIR)/scripts/save_files.sh +export SIMPLE_DUMP = $(TESTDIR)/scripts/simple_dump.pl +export TCLUTIL = $(TESTDIR)/tcl/util.t +export BSDDB_DUMP = $(TESTDIR)/util/bsddb_dump +export CLNTTCL = $(TESTDIR)/util/ovsec_kadm_clnt_tcl +export SRVTCL = $(TESTDIR)/util/ovsec_kadm_srv_tcl +export QUALNAME = $(TOP)/inst-scripts/qualname + +export START_SERVERS = $(TESTDIR)/scripts/start_servers $(TEST_SERVER) +export START_SERVERS_LOCAL = $(TESTDIR)/scripts/start_servers_local + +export STOP_SERVERS = $(TESTDIR)/scripts/stop_servers $(TEST_SERVER) +export STOP_SERVERS_LOCAL = $(TESTDIR)/scripts/stop_servers_local + +export KRB5_CONFIG = /krb5/krb5.conf +export KRB5_KDC_PROFILE = /krb5/kdc.conf +export KRB5_KTNAME = /krb5/ovsec_adm.srvtab + +ifdef TEST_SERVER +MAKE_KEYTAB += -server $(TEST_SERVER) +ifdef TEST_PATH +MAKE_KEYTAB += -top $(TEST_PATH) +START_SERVERS += $(TEST_PATH) +STOP_SERVERS += $(TEST_PATH) +endif +endif + +export VERBOSE_TEST DEJALFLAGS + +# We're running low on disk space, so strip installed binaries +STRIP_INSTALLED = true diff --git a/src/kadmin/config.mk/hpux9.01.def b/src/kadmin/config.mk/hpux9.01.def new file mode 100644 index 000000000..17da13c52 --- /dev/null +++ b/src/kadmin/config.mk/hpux9.01.def @@ -0,0 +1,32 @@ +export ARCH_SKIP_RANLIB := 1 +export INSTCMD = ginstall -c +export PS_ALL = ps -ef +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps -fp +# Make sure there is a blank space at the end of this line! +export PS_TTY = ps -t +export STATIC_LINK_GUI := 1 +export RSH_CMD = /usr/bin/remsh + +PCC_STRUCT_RETURN = -fpcc-struct-return +# this must always be at the end. +REGEXLIB = -lc -lPW +TERMCAPLIB = -ltermcap + +XINCDIR = /usr/include/X11R5 +XLIBDIR = /usr/lib/X11R5 +NDBMLIB := /usr/lib/libndbm.a +ARCH_CFLAGS = -mpa-risc-1-0 + +CFLAGS := $(ARCH_CFLAGS) $(CFLAGS) + +UTMP_FILE = /etc/utmp +WTMP_FILE = /etc/wtmp + +D_RANDOM = -Dsrandom=srand48 -Drandom=lrand48 +D_SETEUID = -DNO_SETEUID \ + -DSETEUIDPRE='setresuid(-1,' -DSETEUIDPOST=',-1)' +D_FATALINLIBS = -Dfatal=ftpd_fatal +D_NO_SETENV = -DNO_SETENV +D_REGEXP_TYPE = -DPOSIX_REGEXPS + diff --git a/src/kadmin/config.mk/irix5.2.def b/src/kadmin/config.mk/irix5.2.def new file mode 100644 index 000000000..053d9328e --- /dev/null +++ b/src/kadmin/config.mk/irix5.2.def @@ -0,0 +1,22 @@ +export PS_ALL = /usr/bin/ps -ef +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = /usr/bin/ps -fp +# Make sure there is a blank space at the end of this line! +export PS_TTY = /usr/bin/ps -t + +RANLIB = /bin/true +D_NO_SETENV = -DNO_SETENV +D_HAVE_SYSLOG_H = -DHAVE_SYSLOG_H +D_HAVE_STDARG_H = -DHAVE_STDARG_H +D_HAVE_SYSLOG = -DHAVE_SYSLOG +D_HAVE_VSPRINTF = -DHAVE_VSPRINTF +D_HAVE_OPENLOG = -DHAVE_OPENLOG +D_HAVE_CLOSELOG = -DHAVE_CLOSELOG +D_HAVE_STRFTIME = -DHAVE_STRFTIME + +# libgen.a is for regcmp and regex, used in /usr/lib/libXm.a; this is +# a different set of regexp routines than used by svr_iters.c +XM_LIB = /usr/lib/libXm.a /usr/lib/libgen.a +XT_LIB = /usr/lib/libXt.a +X_LIB = /usr/lib/libX11.so + diff --git a/src/kadmin/config.mk/linux.def b/src/kadmin/config.mk/linux.def new file mode 100644 index 000000000..69a0c6287 --- /dev/null +++ b/src/kadmin/config.mk/linux.def @@ -0,0 +1,23 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +export RSH_CMD = /usr/bin/rsh + +XINCDIR = /usr/include +XLIBDIR = /usr/lib + +NDBMLIB = -ldbm + +PERL = /usr/bin/perl +LEX_LIB = -lfl + +OMIT_GUI = true +OMIT_XM_KPASSWD = true +OMIT_DOC = true + +UTMP_FILE = /etc/utmp +WTMP_FILE = /var/adm/wtmp + +LIBTCL = -ltcl + +TERMCAPLIB = -ltermcap diff --git a/src/kadmin/config.mk/netbsd1.def b/src/kadmin/config.mk/netbsd1.def new file mode 100644 index 000000000..2b6090355 --- /dev/null +++ b/src/kadmin/config.mk/netbsd1.def @@ -0,0 +1,22 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_TTY = ps -t +export RSH_CMD = /usr/bin/rsh + +PCC_STRUCT_RETURN = +TERMCAPLIB = -ltermcap + +XINCDIR = /usr/X11/include +XLIBDIR = /usr/X11/lib + +UTMP_FILE = /etc/utmp +WTMP_FILE = /usr/adm/wtmp + +D_NO_SETENV = -DNO_SETENV + +SHLIBCFLAGS := -fpic +SHLIBLDFLAGS := -assert pure-text +SHLIBEXT := so +SHLIBSEP := . diff --git a/src/kadmin/config.mk/rules b/src/kadmin/config.mk/rules new file mode 100644 index 000000000..35b647bda --- /dev/null +++ b/src/kadmin/config.mk/rules @@ -0,0 +1,538 @@ +# +# $Id$ +# $Source$ +# + +# +# DefaultRules --- clean, depend, all, stage, includes, and +# install. +# + +define DefaultRules +clean:: + $(CLEAN) core *.o *~ *.bak #* y.output + +depend:: + +all:: + +includes:: + +stage:: + +install:: + +unit-test:: + +traverse:: + +endef + + +# +# SubdirTarget +# +# SUBDIRS = subdirs to work in +# +define SubdirTarget +__SUBDIR_TARGET := clean +expand _DoSubdir + +__SUBDIR_TARGET := depend +expand _DoSubdir + +__SUBDIR_TARGET := all +expand _DoSubdir + +__SUBDIR_TARGET := includes +expand _DoSubdir + +__SUBDIR_TARGET := stage +expand _DoSubdir + +__SUBDIR_TARGET := install +expand _DoSubdir + +__SUBDIR_TARGET := unit-test +expand _DoSubdir + +__SUBDIR_TARGET := traverse +expand _DoSubdir + +ALL_SUBDIRS_TARGET := everything +expand AllTargetsTarget +endef + +# +# Makes the target $(ALL_SUBDIRS_TARGET) perform the targets includes +# depend stage all install in $(SUBDIRS). +# +define AllTargetsTarget +$(ALL_SUBDIRS_TARGET):: + @cwd=`pwd`; \ + for d in $(SUBDIRS); do \ + echo "--- Making includes depend stage all install in $(CUR_DIR)/$$$$d"; \ + cd $$$$d && \ + $(MAKE) includes depend CUR_DIR=$(CUR_DIR)/$$$$d; \ + $(MAKE) stage all install CUR_DIR=$(CUR_DIR)/$$$$d; \ + cd $$$$cwd; \ + done + +endef + +define _DoSubdir +# $(__SUBDIR_TARGET):: +# @echo "--- SKIPPING $(__SUBDIR_TARGET) in $(SUBDIRS)" +$(__SUBDIR_TARGET):: + @cwd=`pwd`; \ + for d in $(SUBDIRS); do \ + echo "--- Making $(__SUBDIR_TARGET) in $(CUR_DIR)/$$$$d"; \ + cd $$$$d && \ + $(MAKE) $(SITEMAKEFLAGS) $(__SUBDIR_TARGET) CUR_DIR=$(CUR_DIR)/$$$$d; \ + cd $$$$cwd; \ + done +endef + +# +# Program -- compile prog, adds all and clean targets +# +# PROG program name +# OBJS object files +# DEPS additional dependencies (e.g.: libraries) +# LIBS libraries +# LDFLAGS arguments for link stage +# PUREFLAGS arguments for purify +# PURELIBS extra libraries to link in when using purify +# +define Program +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).$(__PID__) + $(CC) $(LDFLAGS) -o /tmp/$(PROG).$(__PID__) $(OBJS) $(LIBS) $(STD_LIBS) + mv /tmp/$(PROG).$(__PID__) $(PROG) +else + $(CC) $(LDFLAGS) -o $(PROG) $(OBJS) $(LIBS) $(STD_LIBS) +endif + +clean:: + $(CLEAN) $(PROG) + +proof:: $(PROG).tc + +$(PROG).tc: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).tc.$(__PID__) + $(PROOF) $(CC) $(LDFLAGS) -o /tmp/$(PROG).tc.$(__PID__) $(OBJS) \ + $(LIBS) $(STD_LIBS) + mv /tmp/$(PROG).tc.$(__PID__) $(PROG).tc +else + $(PROOF) $(CC) $(LDFLAGS) -o $(PROG).tc $(OBJS) $(LIBS) $(STD_LIBS) +endif + +clean:: + $(CLEAN) $(PROG).tc $(PROG).tc.* + +pure:: $(PROG).pure + +$(PROG).pure: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).pure.$(__PID__) + $(PURIFY) $(PUREFLAGS) \ + $(CC) $(LDFLAGS) -o /tmp/$(PROG).pure.$(__PID__) $(OBJS) \ + $(LIBS) $(STD_LIBS) $(PURELIBS) + mv /tmp/$(PROG).pure.$(__PID__) $(PROG).pure +else + $(PURIFY) $(PUREFLAGS) $(CC) $(LDFLAGS) -o $(PROG).pure $(OBJS) $(LIBS) $(STD_LIBS) $(PURELIBS) +endif + +clean:: + $(CLEAN) $(PROG).pure $(PROG).pure.pure_* + +quant:: $(PROG).quant + +$(PROG).quant: $(OBJS) $(DEPS) $(filter-out -L% -l%, $(LIBS)) +ifdef BUILD_IN_TMP + rm -f /tmp/$(PROG).quant.$(__PID__) + $(QUANTIFY) $(QUANTFLAGS) \ + $(CC) $(LDFLAGS) -o /tmp/$(PROG).quant.$(__PID__) $(OBJS) \ + $(LIBS) $(STD_LIBS) $(QUANTLIBS) + mv /tmp/$(PROG).quant.$(__PID__) $(PROG).quant +else + $(QUANTIFY) $(QUANTFLAGS) $(CC) $(LDFLAGS) -o $(PROG).quant $(OBJS) $(LIBS) $(STD_LIBS) $(QUANTLIBS) +endif + +clean:: + $(CLEAN) $(PROG).quant $(PROG).quant.quant.*.qv +endef + +# +# InstallProgram -- build program, install in INSTALL_BINDIR, adds +# install target +# +define InstallProgram +__INST_DIR := $(INSTALL_BINDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallUtil -- build program, install in INSTALL_ETCDIR, adds +# install target +# +define InstallUtil +__INST_DIR := $(INSTALL_ETCDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallAdmin -- build program, install in INSTALL_ADMINDIR, adds +# install target +# +define InstallAdmin +__INST_DIR := $(INSTALL_ADMINDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallServer -- build program, install in INSTALL_SERVERDIR, adds +# install target +# +define InstallServer +__INST_DIR := $(INSTALL_SERVERDIR) +expand Program +expand InstallExecutable +endef + +# +# InstallExecuteable(bin, as, dir) +# +define InstallExecutable +ifndef INST_NAME +install:: $(PROG) + rm -f $(__INST_DIR)/$(PROG) + $(INSTCMD) $(INST_PROG_FLAGS) $(PROG) $(__INST_DIR)/$(PROG) +else +install:: $(PROG) + rm -f $(__INST_DIR)/$(INST_NAME) + $(INSTCMD) $(INST_PROG_FLAGS) $(PROG) $(__INST_DIR)/$(INST_NAME) +endif +endef + +# +# Library -- create library from object files and ranlib it, adds all +# and clean targets +# +# LIB name of library (e.g. libfoo.a) +# OBJS object files in library +# +define Library +all:: $(LIB) + +$(LIB): $(OBJS) $(OTHER_OBJS) + $(RM) -f $(LIB) + $(AR) $(ARFLAGS) $(LIB) $(OBJS) +ifndef ARCH_SKIP_RANLIB + $(RANLIB) $(LIB) +endif + +clean:: + $(CLEAN) $(LIB) +endef + +# +# SharedLibrary -- create shared library from object files, adds all +# and clean targets +# +# LIB basename of library (e.g. libfoo) +# (this will be basenamified, anyway) +# OBJS object files in library +# VERSION version of shared library +# +define SharedLibrary +ifdef DO_SHARED_LIBRARIES +ifdef SHLIBLDFLAGS +ifdef SHLIBSEP +__LIB := $(basename $(LIB)).$(SHLIBEXT)$(SHLIBSEP)$(VERSION) +else +__LIB := $(basename $(LIB)).$(SHLIBEXT) +endif +__LIBNV := $(basename $(LIB)).$(SHLIBEXT) +expand SharedLibrary_1 +endif +endif +endef + +define SharedLibrary_1 +ifdef DO_SHARED_LIBRARIES +all:: $(__LIB) + +$(__LIB): $(OBJS) $(OTHER_OBJS) + $(RM) -f $(__LIB) $(__LIBNV) + $(LD) -o $(__LIB) $(SHLIBLDFLAGS) $(OBJS) + $(LNSOFT) $(__LIB) $(__LIBNV) +clean:: + $(CLEAN) $(__LIB) $(__LIBNV) +endif +endef + +# +# StageLibrary -- build library, install in STAGE_LIBDIR, adds stage target +# +define StageLibrary +expand Library + +stage:: $(STAGE_LIBDIR)/$(LIB) + +$(STAGE_LIBDIR)/$(LIB): $(LIB) + $(INSTCMD) $(LIB) $(STAGE_LIBDIR)/$(LIB) +ifndef ARCH_SKIP_RANLIB + $(RANLIB) $(STAGE_LIBDIR)/$(LIB) +endif +endef + +# +# StageSharedLibrary -- build library, install in STAGE_LIBDIR, adds +# stage target +# +define StageSharedLibrary +ifdef DO_SHARED_LIBRARIES +expand SharedLibrary +expand StageSharedLibrary_1 +endif +endef + +define StageSharedLibrary_1 +ifdef DO_SHARED_LIBRARIES +ifdef __LIB +stage:: $(STAGE_LIBDIR)/$(__LIB) + +$(STAGE_LIBDIR)/$(__LIB): $(__LIB) + $(INSTCMD) $(__LIB) $(STAGE_LIBDIR)/$(__LIB) +endif +endif +endef + +# +# InstallLibrary -- build library, install in INSTALL_LIBDIR, +# adds install target +# +define InstallLibrary +expand Library + +install:: $(INSTALL_LIBDIR)/$(LIB) + +$(INSTALL_LIBDIR)/$(LIB): $(LIB) + $(INSTCMD) $(LIB) $(INSTALL_LIBDIR)/$(LIB) +ifndef ARCH_SKIP_RANLIB + $(RANLIB) $(INSTALL_LIBDIR)/$(LIB) +endif +endef + +# +# InstallSharedLibrary -- build library, install in INSTALL_LIBDIR, +# adds install target +# +define InstallSharedLibrary +ifdef DO_SHARED_LIBRARIES +expand SharedLibrary +expand InstallSharedLibrary_1 +endif +endef + +define InstallSharedLibrary_1 +ifdef DO_SHARED_LIBRARIES +ifdef __LIB +install:: $(INSTALL_LIBDIR)/$(__LIB) + +$(INSTALL_LIBDIR)/$(__LIB): $(__LIB) + $(INSTCMD) $(__LIB) $(INSTALL_LIBDIR)/$(__LIB) +endif +endif +endef + +# +# StageFiles(files, dir) +# +define StageFiles +stage:: $(FILES) + $(INSTCMD) $(FILES) $(DIR) +endef + +# +# StageIncludes -- copy include files to staging area, adds includes +# and stage target +# +# HDRS header files to copy +# HDRS_DIR subdir of STAGE_INCDIR to copy to +# +define StageIncludes +includes:: $(HDRS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(HDRS) + +stage:: $(HDRS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(HDRS) +endef + +# +# InstallIncludes -- copy include files to staging area, adds includes +# and install target +# +# HDRS header files to copy +# HDRS_DIR subdir of INSTALL_INCDIR to copy to +# +define InstallIncludes +includes:: $(HDRS) + $(INSTHDRS) $(INSTALL_INCDIR)/$(HDRS_DIR) $(HDRS) + +install:: $(HDRS) + $(INSTHDRS) $(INSTALL_INCDIR)/$(HDRS_DIR) $(HDRS) +endef + +# +# Depend -- run makedepend +# +# SRCS source files to generate dependencies for +# DEPENDS dependencies for "make depend" +# ETABLES error tables whose .c and .h files are implicitly +# dependencies of make depend +# +define Depend +depend:: $(DEPENDS) $(addsuffix .c,$(basename $(ETABLES))) $(addsuffix .h,$(basename $(ETABLES))) + +ifeq (,$(findstring -a,$(__MDFLAGS))) + @rm -f Makefile.depend; touch Makefile.depend +endif + $(MAKEDEPEND) $(__MDFLAGS) $(MDFLAGS) -fMakefile.depend -- $(CFLAGS) -- $(SRCS) + +clean:: + $(CLEAN) Makefile.depend + +ifeq (,$(findstring -a,$(__MDFLAGS))) +__MDFLAGS += -a +endif +endef + +# +# NormalProgram -- everything for a single program +# +# PROG = program name +# SRCS = list of .c sources +# HDRS = list of .h sources +# OBJS = list of .o files to depend and link against +# STAGELIBS = foo.a within the stage area tree to link against +# INSTALLLIBS = foo.a within the install area tree to link against +# LIBS = system libraries to link against +# +# STAGELIBS And INSTALLLIBS are added to the dependencies for PROG. +# +define NormalProgram +expand SaveStuff +LIBS := $(addprefix $(STAGE_LIBDIR)/,$(STAGELIBS)) \ + $(addprefix $(INSTALL_LIBDIR)/,$(INSTALLLIBS)) \ + $(LIBS) +DEPS := $(addprefix $(STAGE_LIBDIR)/,$(STAGELIBS)) \ + $(addprefix $(INSTALL_LIBDIR)/,$(INSTALLLIBS)) \ + $(DEPS) + +clean:: + $(CLEAN) $(OBJS) + +expand InstallProgram +expand Depend +expand RestoreStuff +endef + +define NormalLibrary +expand Library + +clean:: + $(CLEAN) $(OBJS) + +expand Depend +endef + +# +# ErrorTables -- compile an error table with compile_et +# +# ETABLES list of .et files +# CFLAGS for saber target +# +# +define ErrorTables +__ETABLE_HS := $(addsuffix .h,$(basename $(ETABLES))) +__ETABLE_CS := $(addsuffix .c,$(basename $(ETABLES))) +expand ErrorTables_1 +endef + +define ErrorTables_1 + +saber:: + #load $(CFLAGS) $(__ETABLE_CS) + +clean:: + $(CLEAN) $(__ETABLE_HS) $(__ETABLE_CS) +endef + +# +# StageErrorTables -- copy generated include file into staging area. +# +# ETABLES list of .et file +# HDRS_DIR subdir of STAGE_INCDIR to copy to +# +define StageErrorTables +expand ErrorTables +expand StageErrorTables_1 +endef + +define StageErrorTables_1 +includes:: $(__ETABLE_HS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(__ETABLE_HS) + +stage:: $(__ETABLE_HS) + $(INSTHDRS) $(STAGE_INCDIR)/$(HDRS_DIR) $(__ETABLE_HS) +endef + +define SaveStuff +__LIBS := $(LIBS) +__DEPS := $(DEPS) +endef + +define RestoreStuff +LIBS := $(__LIBS) +DEPS := $(__DEPS) +endef + +# +# XDR -- generate .c and .h from .x +# +# XDRS list of .x files +# +define XDR +__XDR_HS := $(addsuffix .h,$(basename $(XDRS))) +__XDR_CS := $(addsuffix .c,$(basename $(XDRS))) +expand XDR_1 +endef + +define XDR_1 +saber:: + #load $(CFLAGS) $(__XDR_CS) + +clean:: + $(CLEAN) $(__XDR_HS) $(__XDR_CS) +endef + + + +# +# Saber -- load files into saber +# +# SRCS list of .c files +# +define Saber +saber:: + #load $(CFLAGS) $(SRCS) +endef diff --git a/src/kadmin/config.mk/site.def b/src/kadmin/config.mk/site.def new file mode 100644 index 000000000..be7fe9e25 --- /dev/null +++ b/src/kadmin/config.mk/site.def @@ -0,0 +1,52 @@ +# $Id$ + +# XXXX this file will probably have lots of ARCH_OS defines in it. + +# +# Misc settings +# + +# +# site-specific compiler/linker flags +# + +CFLAGS := $(CFLAGS) -I$(TOP)/../../src/include \ + -I$(TOP)/../../src/include/krb5 \ + -I$(TOP)/../include \ + -I$(TOP)/../include/krb5 \ + $(PCC_STRUCT_RETURN) -g + +SITEMAKEFLAGS := -f Makefile.ov + +# +# Packages whose locations we need to know +# + +ifeq ($(shell ls -d /.afs/gza.com/product/secure 2>/dev/null),/.afs/gza.com/product/secure) +AFS_ROOT=/.afs +else +AFS_ROOT=/afs +endif + +PERL = /afs/athena/contrib/perl/p +# /afs/sipb/project/tcl/lib/libtcl.a +LIBTCL = /mit/gnu/lib/libtcl.a +# /afs/sipb/project/tcl/include +TCLINC = /mit/gnu/include + +# +# Default locations +# + +YACC = bison -y + +# +# I'm not really sure where this should go, but it is often useful to +# be able to set up a test environment from anywhere in the build +# tree. +# +start-servers: + $(START_SERVERS) + +stop-servers: + $(STOP_SERVERS) diff --git a/src/kadmin/config.mk/solaris2.3.def b/src/kadmin/config.mk/solaris2.3.def new file mode 100644 index 000000000..d18e0e164 --- /dev/null +++ b/src/kadmin/config.mk/solaris2.3.def @@ -0,0 +1,39 @@ +export PS_ALL = /usr/bin/ps -ef +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = /usr/bin/ps -fp +# Make sure there is a blank space at the end of this line! +export PS_TTY = /usr/bin/ps -t +export RSH_CMD = /usr/ucb/rsh +export INSTCMD = /usr/ucb/install +export PATH := $(PATH):/usr/ucb:/usr/ccs/bin + +PCC_STRUCT_RETURN = -fpcc-struct-return +D_NEEDS_RPCENT = -DNEEDS_RPCENT +D_SYSV = -DSYSV +D_POSIX = -DPOSIX +D_NO_SETENV = -DNO_SETENV +D_POSIX_SIGNALS = -DPOSIX_SIGNALS +D_RANDOM = -Dsrandom=srand48 -Drandom=lrand48 +D_REGEXP_TYPE = -DSOLARIS_REGEXPS +NETLIB = -lsocket -lnsl +#BSDLIB = /usr/ucblib/libucb.a +TERMCAPLIB = -lcurses -ltermcap +REGEXLIB = -lgen +LOGINLIB = -lcmd + +XINCDIR = /usr/openwin/include +XLIBDIR = /usr/openwin/lib +RANLIB = /bin/true + +OMIT_GUI = true + +# These are used by admin/v4server/Makefile. They are determined +# automatically by the krb5 beta 4 auto-configure, but we're not using +# that right now. +WAIT_USES_INT = true +OPEN_NEEDS_FCNTL = true + +UTMP_FILE = /var/adm/utmp +WTMP_FILE = /var/adm/wtmp +UTMPX_FILE = /var/adm/utmpx +WTMPX_FILE = /var/adm/wtmpx diff --git a/src/kadmin/config.mk/sunos4.1.def b/src/kadmin/config.mk/sunos4.1.def new file mode 100644 index 000000000..942615511 --- /dev/null +++ b/src/kadmin/config.mk/sunos4.1.def @@ -0,0 +1,22 @@ +export PS_ALL = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_PID = ps auxww +# Make sure there's no extra whitespace at the end of this line! +export PS_TTY = ps -t +export RSH_CMD = /usr/ucb/rsh + +PCC_STRUCT_RETURN = -fpcc-struct-return +TERMCAPLIB = -ltermcap + +XINCDIR = /usr/openwin/include +XLIBDIR = /usr/openwin/lib + +UTMP_FILE = /etc/utmp +WTMP_FILE = /usr/adm/wtmp + +D_NO_SETENV = -DNO_SETENV + +SHLIBCFLAGS := -fpic +SHLIBLDFLAGS := -assert pure-text +SHLIBEXT := so +SHLIBSEP := . diff --git a/src/kadmin/config.mk/template b/src/kadmin/config.mk/template new file mode 100644 index 000000000..caf3a2d9d --- /dev/null +++ b/src/kadmin/config.mk/template @@ -0,0 +1,142 @@ +# $Id$ +# $Source$ + +export TOP + +KRB5B4 = true +CONFDIR = $(TOP)/config.mk + +ifndef CUR_DIR +CUR_DIR = . +endif + +# +# get the os name +# + +include $(CONFDIR)/architecture + +# +# Programs +# +IMAKE = imake +# The purpose of this variable setting is to prevent -w from being +# passed down via environment variables into sub-makes that use SunOS +# Make rather than GNU make. +ifndef MAKE_PRINT_DIRECTORY +MAKE := $(MAKE) --no-print-directory MAKEFLAGS=$(MAKEFLAGS) MFLAGS=$(MFLAGS) +endif +CC = gcc +AR = ar +RANLIB = ranlib +LD = ld +RM = rm +CLEAN = rm -f +MV = mv +LN = ln +LNSOFT = $(LN) -s +MAKEDEPEND = makedepend +RPCGEN = rpcgen +PERL = /usr/local/bin/perl +DUMPPERL = /usr/local/bin/perl.static +UNDUMP = undump +YACC = $(TOP)/scripts/newyacc.sh +GENPROT = $(TOP)/scripts/genproto.sh +INSTCMD = install -c +export INSTCMD +PURIFY = purify +PROOF = proof +QUANTIFY = quantify +LEX_LIB = -ll +PERL = /usr/local/bin/perl +OBJDUMP = /usr/local/bin/gobjdump +OBJCOPY = /usr/local/bin/gobjcopy + +# Dejagnu variables + +# We have to set the host with --host so that setup_xfail will work. +# If we don't set it, then the host type used is "native", which +# doesn't match "*-*-*". + +DEJAFLAGS := $(DEJALFLAGS) $(CLFLAGS) --debug --host \ + unknown-$(shell uname -m)-$(shell uname -s)$(shell uname -r) +ifdef VERBOSE_TEST +DEJAFLAGS += --verbose +endif + +RUNTEST := runtest $(DEJAFLAGS) + +# +# Flags. Since this is the initial setting, don't preserve current +# values; otherwise, recursive makes will get the sum of everything. +# +YFLAGS = -d +ARFLAGS = cru +CFLAGS := $(CLFLAGS) +LDFLAGS := $(CLFLAGS) + +# +# The default target is "all". Put this before any includes, in case +# the includes define new targets. Or perhaps they should be allowed +# to define a new default target... +# +all:: + +# +# Get a unique number for files built in /tmp +# +__PID__ := $(shell echo $$$$) + +include $(CONFDIR)/config + +include $(CONFDIR)/rules + +expand IncludeArchFile + +include $(CONFDIR)/site.def + +ifdef STRIP_INSTALLED +INST_PROG_FLAGS = -s +endif + +# avoid makefiles from failing on default rules +expand DefaultRules + +# include dependencies +ifeq ($(shell [ -r Makefile.depend ] && echo yes),yes) +include Makefile.depend +endif + +# disable RCS frobbing +% :: RCS/%,v + +# fix lex rule +.l.c: + $(RM) -f $@ + $(LEX) $(LFLAGS) -t $< > $@ + +# error table rule +.SUFFIXES: .et +.et.c: + $(COMPILE_ET) $< +.et.h: + $(COMPILE_ET) $< + +# rpcgen rule +.SUFFIXES: .x +.x.c: + $(RPCGEN) -o $@ -c $< +.x.h: + $(RPCGEN) -o $@ -h $< + +# command table rule +.SUFFIXES: .ct +.ct.c: + $(MK_CMDS) $< +.ct.h: + $(MK_CMDS) $< + +CMD="echo 'You must specify CMD to use the 'execute' rule.'; exit 1" + +execute: + @eval $(CMD) diff --git a/src/kadmin/configure.in b/src/kadmin/configure.in index 12d4f04ef..959626b36 100644 --- a/src/kadmin/configure.in +++ b/src/kadmin/configure.in @@ -1,5 +1,6 @@ AC_INIT(configure.in) CONFIG_RULES -CONFIG_DIRS(kpasswd v5server v5client ktutil) +dnl CONFIG_DIRS(kpasswd v5server v5client) +CONFIG_DIRS(create export import keytab cli dbutil passwd ktutil server v4server) DO_SUBDIRS V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/create/ChangeLog b/src/kadmin/create/ChangeLog new file mode 100644 index 000000000..0c724422d --- /dev/null +++ b/src/kadmin/create/ChangeLog @@ -0,0 +1,9 @@ +Fri Jul 12 14:43:56 1996 Marc Horowitz <marc@mit.edu> + + * configure.in (USE_GSSAPI_LIBRARY): shared libraries require that + all symbols be resolved, even if they are not used by the + executeable. Thus, create needs to link against gssapi + +Wed Jul 10 01:24:29 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.in, configure.in: added autoconf support diff --git a/src/kadmin/create/Makefile.in b/src/kadmin/create/Makefile.in new file mode 100644 index 000000000..547bd39c6 --- /dev/null +++ b/src/kadmin/create/Makefile.in @@ -0,0 +1,15 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kdb5_create +OBJS = kdb5_create.o kadm5_create.o string_table.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/create/Makefile.ov b/src/kadmin/create/Makefile.ov new file mode 100644 index 000000000..cdec414b7 --- /dev/null +++ b/src/kadmin/create/Makefile.ov @@ -0,0 +1,12 @@ +TOP = .. +include $(TOP)/config.mk/template + +PROG = kadmin_create +SRCS = kdb5_create.c kadm5_create.c string_table.c +OBJS = kdb5_create.o kadm5_create.o string_table.o + +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) \ + $(LIBDYN) $(NDBMLIB) $(LIBDB) $(BSDLIB) $(NETLIB) + +expand InstallAdmin +expand Depend diff --git a/src/kadmin/create/attic/Makefile.in b/src/kadmin/create/attic/Makefile.in new file mode 100644 index 000000000..f7bd9ca38 --- /dev/null +++ b/src/kadmin/create/attic/Makefile.in @@ -0,0 +1,20 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +all:: + +SRCS = $(srcdir)/ovsec_adm_create.c \ + $(srcdir)/string_table.c + +OBJS = ovsec_adm_create.o \ + string_table.o + +all:: ovsec_adm_create + +ovsec_adm_create: $(OBJS) $(DEPLIBS) + $(LD) $(LDFLAGS) $(LDARGS) -o ovsec_adm_create $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) ./ovsec_adm_create ${DESTDIR}$(SERVER_BINDIR)/kadmind5 + +clean:: + $(RM) ovsec_adm_create diff --git a/src/kadmin/create/attic/configure.in b/src/kadmin/create/attic/configure.in new file mode 100644 index 000000000..67b8f7c52 --- /dev/null +++ b/src/kadmin/create/attic/configure.in @@ -0,0 +1,12 @@ +AC_INIT(ovsec_adm_create.c) +CONFIG_RULES +AC_PROG_INSTALL +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_KDB5_LIBRARY +USE_DYN_LIBRARY +USE_DB_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/create/attic/make_extern b/src/kadmin/create/attic/make_extern new file mode 100644 index 000000000..5432edf66 --- /dev/null +++ b/src/kadmin/create/attic/make_extern @@ -0,0 +1,16 @@ +#!/bin/csh + +echo '/*' +echo ' * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved.' +echo ' * ' +echo ' * $Header$' +echo ' *' +echo ' */' +echo ' ' +echo '#ifndef _OVSEC_ADM_STRINGS_' +echo ' ' + +cat $1 | grep -v rcsid | grep ^char | awk '{printf "extern %s %s;\n",$1,$2}' + +echo ' ' +echo '#endif /* _OVSEC_ADM_STRINGS_ */' diff --git a/src/kadmin/create/attic/ovsec_adm_create.c b/src/kadmin/create/attic/ovsec_adm_create.c new file mode 100644 index 000000000..90be0c406 --- /dev/null +++ b/src/kadmin/create/attic/ovsec_adm_create.c @@ -0,0 +1,663 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.23 1996/07/22 20:24:35 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.22.4.1 1996/07/18 03:01:22 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.22.2.1 1996/06/20 21:44:55 marc + * File added to the repository on a branch + * + * Revision 1.22 1996/06/19 15:09:32 bjaspan + * changes to work in mit tree + * + * Revision 1.21 1995/11/07 23:27:28 grier + * Add stdlib.h + * Add string.h + * + * Revision 1.20 1995/08/13 16:41:11 jik + * Fix a nonsensical comment about the iterator() function. See PR + * secure-admin/470. + * + * Revision 1.19 1995/07/02 19:55:13 jik + * Key version numbers should start out at 1, not 0. + * Should get the master key version number from the master_db entry in + * server_kdb.c, rather than assuming that the master key version number + * is 0. + * + * Revision 1.18 1995/03/14 16:58:50 jik + * Use krb5_xfree instead of xfree if KRB5B4 is defined. + * + * Revision 1.17 1994/03/11 19:37:34 bjaspan + * [secure-admin/1593: ovsec_adm_create non-error messages go to stderr] + * [secure-releng/1608: audit secure-admin/1593: ovsec_adm_create non-error messages go to stderr] + * + * Sandbox: + * + * Normal messages should be printed to stdout rather than displayed + * using com_err, which will cause then to go to stderr. + * + * Revision 1.17 1994/03/09 22:21:33 jik + * Normal messages should be printed to stdout rather than displayed + * using com_err, which will cause then to go to stderr. + * + * Revision 1.16 1993/12/21 20:26:34 marc + * create new principals with policy NULL, not "" + * + * Revision 1.15 1993/12/14 22:51:35 marc + * missing * in call to krb5_random_key + * + * Revision 1.14 1993/11/27 20:42:32 bjaspan + * fix secure/621: coredumps with default realm + * + * Revision 1.13 1993/11/19 20:03:51 shanzer + * osa_adb_open_T takes a file name argument. + * + * Revision 1.12 1993/11/10 21:30:24 bjaspan + * move init code to main, accept -m + * + * Revision 1.11 1993/11/10 04:33:35 bjaspan + * rewrote adding principals to kdb, and set lifetimes + * + * Revision 1.10 1993/11/06 00:08:44 bjaspan + * use new OVSEC_KADM_* names, use correct realm + * + * Revision 1.9 1993/11/05 05:05:35 bjaspan + * added -r realm argument + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "string_table.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ovsec_admin/adb.h> +#include <ovsec_admin/admin.h> + +#include <krb5.h> +#include <krb5/kdb.h> + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime); + +#define ERR 1 +#define OK 0 + +#define ADMIN_LIFETIME 60*60*3 /* 3 hours */ +#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ + +char *whoami; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_db_entry master_db; + +/* + * Function: main + * + * Purpose: create admin principals, create and populate admin dbs + * + * Arguments: + * + * input none + * <return value> exit status 1 for error 0 for success + * + * Requires: + * + * + * Effects: + * + * + * Modifies: + * + */ + +void usage() +{ + fprintf(stderr, "%s\n", str_PROG_CREATE_USAGE); + exit(1); +} + +void main(int argc, char **argv) +{ + char *realm = NULL; + int freerealm = 0; + int retval, from_keyboard = 0; + krb5_principal creator = NULL; + void *handle; + krb5_context context; + + whoami = str_PROG_NAME_CREATE; + + argc--; argv++; + while (argc) { + if (strcmp(*argv, "-r") == 0) { + argc--; argv++; + if (!argc) + usage(); + realm = *argv; + } else if (strcmp(*argv, "-m") == 0) { + from_keyboard = 1; + } else + break; + argc--; argv++; + } + + if (argc != 0) + usage(); + + if (retval = krb5_init_context(&context)) + exit(ERR); + + if (realm == NULL) { + if ((retval = krb5_get_default_realm(context, &realm)) != 0) + exit(retval); + freerealm = 1; + } + + if ((retval = ovsec_kadm_init(whoami, from_keyboard?"non-null":NULL, + NULL, realm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &handle))) { + com_err(whoami, retval, str_INITING_KCONTEXT); + + krb5_free_context(context); + exit(ERR); + } + + retval = add_admin_princs(handle, context, realm); + + ovsec_kadm_destroy(handle); + krb5_free_context(context); + + if (retval) + exit(retval); + + exit(0); +} + +/* + * Function: build_name_with_realm + * + * Purpose: concatenate a name and a realm to form a krb5 name + * + * Arguments: + * + * name (input) the name + * realm (input) the realm + * + * Returns: + * + * pointer to name@realm, in allocated memory, or NULL if it + * cannot be allocated + * + * Requires: both strings are null-terminated + */ +char *build_name_with_realm(char *name, char *realm) +{ + char *n; + + n = (char *) malloc(strlen(name) + strlen(realm) + 2); + sprintf(n, "%s@%s", name, realm); + return n; +} + +/* + * Function: add_admin_princs + * + * Purpose: create admin principals + * + * Arguments: + * + * rseed (input) random seed + * realm (input) realm, or NULL for default realm + * <return value> (output) status, 0 for success, 1 for serious error + * + * Requires: + * + * Effects: + * + * add_admin_princs creates OVSEC_KADM_ADMIN_SERVICE, + * OVSEC_KADM_CHANGEPW_SERVICE, and OVSEC_KADM_HIST_PRINCIPAL. If any + * of these exist a message is printed. If any of these existing + * principal do not have the proper attributes, a warning message is + * printed. + */ +int add_admin_princs(void *handle, krb5_context context, char *realm) +{ + krb5_error_code ret = 0; + + if ((ret = add_admin_princ(handle, context, + OVSEC_KADM_ADMIN_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + OVSEC_KADM_CHANGEPW_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_PWCHANGE_SERVICE, + CHANGEPW_LIFETIME))) + goto clean_and_exit; + +#if 0 + /* this is now done inside kdb_init_hist in the admin server */ + + if ((ret = add_admin_princ(handle, context, + OVSEC_KADM_HIST_PRINCIPAL, realm, + KRB5_KDB_DISALLOW_ALL_TIX, + 0))) + goto clean_and_exit; +#endif + +clean_and_exit: + + return ret; +} + +/* + * Function: add_admin_princ + * + * Arguments: + * + * creator (r) principal to use as "mod_by" + * rseed (r) seed for random key generator + * name (r) principal name + * realm (r) realm name for principal + * attrs (r) principal's attributes + * lifetime (r) principal's max life, or 0 + * not_unique (r) error message for multiple entries, never used + * exists (r) warning message for principal exists + * wrong_attrs (r) warning message for wrong attributes + * + * Returns: + * + * OK on success + * ERR on serious errors + * + * Effects: + * + * If the principal is not unique, not_unique is printed (but this + * never happens). If the principal exists, then exists is printed + * and if the principals attributes != attrs, wrong_attrs is printed. + * Otherwise, the principal is created with mod_by creator and + * attributes attrs and max life of lifetime (if not zero). + */ + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime) +{ + char *fullname; + int nprincs; + krb5_error_code ret; + ovsec_kadm_principal_ent_rec ent; + + memset(&ent, 0, sizeof(ent)); + + fullname = build_name_with_realm(name, realm); + if (ret = krb5_parse_name(context, fullname, &ent.principal)) { + com_err(whoami, ret, str_PARSE_NAME); + return(ERR); + } + ent.max_life = lifetime; + ent.attributes = attrs; + + if (ret = ovsec_kadm_create_principal(handle, &ent, + (OVSEC_KADM_PRINCIPAL | + OVSEC_KADM_MAX_LIFE | + OVSEC_KADM_ATTRIBUTES), + "to-be-random")) { + if (ret == OVSEC_KADM_DUP) + ret = ovsec_kadm_modify_principal(handle, &ent, + (OVSEC_KADM_PRINCIPAL | + OVSEC_KADM_MAX_LIFE | + OVSEC_KADM_ATTRIBUTES)); + + if (ret) { + com_err(whoami, ret, str_PUT_PRINC, fullname); + krb5_free_principal(context, ent.principal); + free(fullname); + return ERR; + } + } + + ret = ovsec_kadm_randkey_principal(handle, ent.principal, NULL); + + krb5_free_principal(context, ent.principal); + free(fullname); + + if (ret) { + com_err(whoami, ret, str_RANDOM_KEY, fullname); + return ERR; + } + + return OK; +} + +#if 0 +/* + * Function: main + * + * Purpose: Return "garbage" if the caller asks for it. + * + * Arguments: + * + * input (input) A null-terminated string, + * or NULL. + * delay (input/output) The number of seconds the + * function should delay before returning. + * <return value> (output) A string. + * + * Requires: + * + * "input" must either be NULL or point to an address in the + * program's address space. "delay" must point to an address in + * the program's address space. + * + * Effects: + * + * The function first sleeps for approximately the number of + * seconds specified in "delay". + * + * Then, if "input" is non-NULL and points to a null-terminated + * string which is equal to "garbage", the function sets "delay" + * to 42 and returns a string allocated with malloc(3) containing + * "more-garbage". + * + * If "input" is NULL or does not contain "garbage", the function + * returns NULL without modifying "delay". + * + * If "<return value>" is non-NULL, the caller should deallocate + * the string in it (with free(3)) when it is no longer needed. + * + * Modifies: + * + * May allocate a new block of memory in the malloc(3) arena. + * May change the value in the memory location pointed to by + * "delay". + */ + +krb5_error_code add_random_princ(princ_str, princ, attrs, lifetime, + creator, rseed) + char *princ_str; + krb5_principal princ, creator; + krb5_flags attrs; + int lifetime; + krb5_pointer *rseed; +{ + krb5_db_entry entry; + krb5_error_code ret; + krb5_encrypted_keyblock ekey; + krb5_keyblock *rkey; + int nentries = 1; + + memset((char *) &entry, 0, sizeof(entry)); + entry.principal = princ; + entry.kvno = 1; + entry.max_life = KRB5_KDB_MAX_LIFE; + entry.max_renewable_life = 0; + entry.mkvno = master_db.mkvno; + entry.expiration = KRB5_KDB_EXPIRATION; + entry.mod_name = creator; + if (lifetime != 0) + entry.max_life = lifetime; + + if (ret = krb5_timeofday(&entry.mod_date)) + return(ret); + + entry.attributes = attrs; + + ret = krb5_random_key(&master_encblock, *rseed, &rkey); + if (ret != 0) { + com_err(whoami, ret, str_RANDOM_KEY, princ_str); + return (ERR); + } + + + ret = krb5_kdb_encrypt_key(&master_encblock, rkey, &ekey); + krb5_free_keyblock(rkey); + if (ret != 0) { + com_err(whoami, ret, str_ENCRYPT_KEY, princ_str); + return (ERR); + } + + entry.key = ekey; + entry.salt_type = KRB5_KDB_SALTTYPE_NORMAL; + entry.salt_length = 0; + entry.salt = 0; + + ret = krb5_db_put_principal(&entry, &nentries); + if (ret != 0) + com_err(whoami, ret, str_PUT_PRINC, princ_str); +#ifdef KRB5B4 + krb5_xfree(ekey.contents); +#else + xfree(ekey.contents); +#endif + + if (ret) return(ERR); + + printf(str_CREATED_PRINC, whoami, princ_str); + + return(OK); +} + +/* + * Function: create_admin_policy_db + * + * Purpose: Return "garbage" if the caller asks for it. + * + * Arguments: + * + * input (input) A null-terminated string, + * or NULL. + * delay (input/output) The number of seconds the + * function should delay before returning. + * <return value> (output) A string. + * + * Requires: + * + * "input" must either be NULL or point to an address in the + * program's address space. "delay" must point to an address in + * the program's address space. + * + * Effects: + * + * The function first sleeps for approximately the number of + * seconds specified in "delay". + * + * Then, if "input" is non-NULL and points to a null-terminated + * string which is equal to "garbage", the function sets "delay" + * to 42 and returns a string allocated with malloc(3) containing + * "more-garbage". + * + * If "input" is NULL or does not contain "garbage", the function + * returns NULL without modifying "delay". + * + * If "<return value>" is non-NULL, the caller should deallocate + * the string in it (with free(3)) when it is no longer needed. + * + * Modifies: + * + * May allocate a new block of memory in the malloc(3) arena. + * May change the value in the memory location pointed to by + * "delay". + */ + +int create_admin_policy_db() +{ + /* We don't have a create/destroy routine, so opening the db and + closing it will have to do. */ + osa_adb_policy_t policy_db = NULL; + osa_adb_ret_t ret; + + ret = osa_adb_open_policy(&policy_db, POLICY_DB); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CREATING_POLICY_DB); + return(-1); + } + + /* Should create sample policies here */ + + ret = osa_adb_close_policy(policy_db); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CLOSING_POLICY_DB); + return(-1); + } + + printf(str_CREATED_POLICY_DB, whoami); + + return(OK); +} + +/* + + * Function: iterator(ptr, entry) + * + * Purpose: + * + * Creates an entry in the Admin database corresponding to the + * specified entry in the Kerberos database. + * + * Arguments: + * + * ptr (input) Actually of type osa_adb_princ_t, + * represents the Admin database in which to + * create the principal. + * entry (input) The entry in the Kerberos database for + * which to create an entry in the Admin + * database. + * + * Requires: + * + * "ptr" represents a valid, open Admin principal database. + * "entry" represents a valid, decoded Kerberos database + * principal entry. + * + * Effects: + * + * Modifies the Admin principal database by creating a principal + * in the database with the same name as "entry" and no other + * information. + * + * Modifies: + * + * Does not modify any global memory. Modifies the Admin + * principal database whose handle is passed into it. + */ + +krb5_error_code +iterator(ptr, entry) +krb5_pointer ptr; +krb5_db_entry *entry; +{ + osa_adb_ret_t retval; + krb5_error_code retval2; + char *princ_str = NULL; + osa_princ_ent_rec osa_princ; + + /* Zero the whole struct, and fill in the princ name */ + memset(&osa_princ, 0, sizeof(osa_princ_ent_rec)); + + osa_princ.name = entry->principal; + osa_princ.policy = NULL; + + retval = osa_adb_create_princ((osa_adb_princ_t) ptr, &osa_princ); + if (retval != OSA_ADB_OK) { + if (retval2 = krb5_unparse_name(entry->principal, &princ_str)) { + com_err(whoami, retval2, str_UNPARSE_PRINC); + } + com_err(whoami, retval, str_CREATING_PRINC_ENTRY, + (princ_str ? princ_str : str_A_PRINC)); + if (princ_str) free(princ_str); + } + return (0); +} + +/* + * Function: create_and_populate_admin_princ_db + * + * Purpose: Return "garbage" if the caller asks for it. + * + * Arguments: + * + * input (input) A null-terminated string, + * or NULL. + * delay (input/output) The number of seconds the + * function should delay before returning. + * <return value> (output) A string. + * + * Requires: + * + * "input" must either be NULL or point to an address in the + * program's address space. "delay" must point to an address in + * the program's address space. + * + * Effects: + * + * The function first sleeps for approximately the number of + * seconds specified in "delay". + * + * Then, if "input" is non-NULL and points to a null-terminated + * string which is equal to "garbage", the function sets "delay" + * to 42 and returns a string allocated with malloc(3) containing + * "more-garbage". + * + * If "input" is NULL or does not contain "garbage", the function + * returns NULL without modifying "delay". + * + * If "<return value>" is non-NULL, the caller should deallocate + * the string in it (with free(3)) when it is no longer needed. + * + * Modifies: + * + * May allocate a new block of memory in the malloc(3) arena. + * May change the value in the memory location pointed to by + * "delay". + */ + +int create_and_populate_admin_princ_db() +{ + osa_adb_princ_t princ_db = NULL; + osa_adb_ret_t ret; + + /* We don't have a create/destroy routine, so opening the db and + closing it will have to do. */ + + ret = osa_adb_open_princ(&princ_db, PRINCIPAL_DB); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CREATING_PRINC_DB); + return(-1); + } + + printf(str_CREATED_PRINC_DB, whoami); + + (void) krb5_db_iterate(iterator, princ_db); + + ret = osa_adb_close_princ(princ_db); + if (ret != OSA_ADB_OK) { + com_err (whoami, ret, str_CLOSING_PRINC_DB); + return(-1); + } + + + return(OK); +} + +#endif diff --git a/src/kadmin/create/configure.in b/src/kadmin/create/configure.in new file mode 100644 index 000000000..403034263 --- /dev/null +++ b/src/kadmin/create/configure.in @@ -0,0 +1,11 @@ +AC_INIT(kdb5_create.c) +CONFIG_RULES +AC_PROG_INSTALL +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/create/kadm5_create.c b/src/kadmin/create/kadm5_create.c new file mode 100644 index 000000000..33b30ec9c --- /dev/null +++ b/src/kadmin/create/kadm5_create.c @@ -0,0 +1,241 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "string_table.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <kadm5/adb.h> +#include <kadm5/admin.h> + +#include <krb5.h> +#include <krb5/kdb.h> + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime); + +#define ERR 1 +#define OK 0 + +#define ADMIN_LIFETIME 60*60*3 /* 3 hours */ +#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ + +extern char *whoami; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_db_entry master_db; + +/* + * Function: kadm5_create + * + * Purpose: create admin principals in KDC database + * + * Arguments: params (r) configuration parameters to use + * + * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE + * principals in the KDC database and sets their attributes + * appropriately. + */ +void kadm5_create(kadm5_config_params *params) +{ + int retval; + void *handle; + krb5_context context; + FILE *f; + + + if (retval = krb5_init_context(&context)) + exit(ERR); + + /* + * The lock file has to exist before calling kadm5_init, but + * params->admin_lockfile may not be set yet... + */ + if (retval = kadm5_get_config_params(context, NULL, NULL, + params, params)) { + com_err(whoami, retval, str_INITING_KCONTEXT); + exit(1); + } + + if (retval = osa_adb_create_policy_db(params)) { + com_err(whoami, retval, str_CREATING_POLICY_DB); + exit(1); + } + + if ((retval = kadm5_init(whoami, NULL, NULL, params, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle))) { + com_err(whoami, retval, str_INITING_KCONTEXT); + + krb5_free_context(context); + exit(ERR); + } + + retval = add_admin_princs(handle, context, params->realm); + + kadm5_destroy(handle); + krb5_free_context(context); + + if (retval) + exit(retval); + + exit(0); +} + +/* + * Function: build_name_with_realm + * + * Purpose: concatenate a name and a realm to form a krb5 name + * + * Arguments: + * + * name (input) the name + * realm (input) the realm + * + * Returns: + * + * pointer to name@realm, in allocated memory, or NULL if it + * cannot be allocated + * + * Requires: both strings are null-terminated + */ +char *build_name_with_realm(char *name, char *realm) +{ + char *n; + + n = (char *) malloc(strlen(name) + strlen(realm) + 2); + sprintf(n, "%s@%s", name, realm); + return n; +} + +/* + * Function: add_admin_princs + * + * Purpose: create admin principals + * + * Arguments: + * + * rseed (input) random seed + * realm (input) realm, or NULL for default realm + * <return value> (output) status, 0 for success, 1 for serious error + * + * Requires: + * + * Effects: + * + * add_admin_princs creates KADM5_ADMIN_SERVICE, + * KADM5_CHANGEPW_SERVICE. If any of these exist a message is + * printed. If any of these existing principal do not have the proper + * attributes, a warning message is printed. + */ +int add_admin_princs(void *handle, krb5_context context, char *realm) +{ + krb5_error_code ret = 0; + + if ((ret = add_admin_princ(handle, context, + KADM5_ADMIN_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + KADM5_CHANGEPW_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_PWCHANGE_SERVICE, + CHANGEPW_LIFETIME))) + goto clean_and_exit; + +clean_and_exit: + + return ret; +} + +/* + * Function: add_admin_princ + * + * Arguments: + * + * creator (r) principal to use as "mod_by" + * rseed (r) seed for random key generator + * name (r) principal name + * realm (r) realm name for principal + * attrs (r) principal's attributes + * lifetime (r) principal's max life, or 0 + * not_unique (r) error message for multiple entries, never used + * exists (r) warning message for principal exists + * wrong_attrs (r) warning message for wrong attributes + * + * Returns: + * + * OK on success + * ERR on serious errors + * + * Effects: + * + * If the principal is not unique, not_unique is printed (but this + * never happens). If the principal exists, then exists is printed + * and if the principals attributes != attrs, wrong_attrs is printed. + * Otherwise, the principal is created with mod_by creator and + * attributes attrs and max life of lifetime (if not zero). + */ + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime) +{ + char *fullname; + int nprincs; + krb5_error_code ret; + kadm5_principal_ent_rec ent; + + memset(&ent, 0, sizeof(ent)); + + fullname = build_name_with_realm(name, realm); + if (ret = krb5_parse_name(context, fullname, &ent.principal)) { + com_err(whoami, ret, str_PARSE_NAME); + return(ERR); + } + ent.max_life = lifetime; + ent.attributes = attrs; + + if (ret = kadm5_create_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES), + "to-be-random")) { + if (ret == KADM5_DUP) + ret = kadm5_modify_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES)); + + if (ret) { + com_err(whoami, ret, str_PUT_PRINC, fullname); + krb5_free_principal(context, ent.principal); + free(fullname); + return ERR; + } + } + + ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL); + + krb5_free_principal(context, ent.principal); + free(fullname); + + if (ret) { + com_err(whoami, ret, str_RANDOM_KEY, fullname); + return ERR; + } + + return OK; +} diff --git a/src/kadmin/create/kdb5_create.c b/src/kadmin/create/kdb5_create.c new file mode 100644 index 000000000..8b167b6b9 --- /dev/null +++ b/src/kadmin/create/kdb5_create.c @@ -0,0 +1,536 @@ +/* + * admin/create/kdb5_create.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Generate (from scratch) a Kerberos KDC database. + */ + +#include <stdio.h> +#include <k5-int.h> +#include <kadm5/admin.h> + +enum ap_op { + NULL_KEY, /* setup null keys */ + MASTER_KEY, /* use master key as new key */ + TGT_KEY /* special handling for tgt key */ +}; + +krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL }; + +struct realm_info { + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_encrypt_block *eblock; + krb5_pointer rseed; + krb5_int32 nkslist; + krb5_key_salt_tuple *kslist; +} rblock = { /* XXX */ + KRB5_KDB_MAX_LIFE, + KRB5_KDB_MAX_RLIFE, + KRB5_KDB_EXPIRATION, + KRB5_KDB_DEF_FLAGS, + (krb5_encrypt_block *) NULL, + (krb5_pointer) NULL, + 1, + &def_kslist +}; + +struct iterate_args { + krb5_context ctx; + struct realm_info *rblock; + krb5_db_entry *dbentp; +}; + +static krb5_error_code add_principal + PROTOTYPE((krb5_context, + krb5_principal, + enum ap_op, + struct realm_info *)); + +/* + * Steps in creating a database: + * + * 1) use the db calls to open/create a new database + * + * 2) get a realm name for the new db + * + * 3) get a master password for the new db; convert to an encryption key. + * + * 4) create various required entries in the database + * + * 5) close & exit + */ + +static void +usage(who, status) +char *who; +int status; +{ + fprintf(stderr, "usage: %s [-d dbpathname] [-r realmname] [-k enctype]\n\ +\t[-M mkeyname]\n", + who); + exit(status); +} + +krb5_keyblock master_keyblock; +krb5_principal master_princ; +krb5_encrypt_block master_encblock; +krb5_data master_salt; + +krb5_data tgt_princ_entries[] = { + {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, + {0, 0, 0} }; + +krb5_data db_creator_entries[] = { + {0, sizeof("db_creation")-1, "db_creation"} }; + +/* XXX knows about contents of krb5_principal, and that tgt names + are of form TGT/REALM@REALM */ +krb5_principal_data tgt_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + tgt_princ_entries, /* krb5_data *data */ + 2, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +krb5_principal_data db_create_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + db_creator_entries, /* krb5_data *data */ + 1, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +char *mkey_password = 0; +char *whoami; + +void +main(argc, argv) +int argc; +char *argv[]; +{ + extern char *optarg; + int optchar; + + krb5_error_code retval; + char *dbname = (char *) NULL; + char *realm = 0; + char *mkey_name = 0; + char *mkey_fullname; + char *defrealm; + char *pw_str = 0; + char *keyfile = 0; + int pw_size = 0; + int enctypedone = 0; + int do_stash = 0; + krb5_data pwd; + krb5_context context; + krb5_realm_params *rparams; + kadm5_config_params kadm5_params; + + memset(&kadm5_params, 0, sizeof(kadm5_params)); + + krb5_init_context(&context); + krb5_init_ets(context); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + whoami = argv[0]; + + kadm5_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + kadm5_params.mkey_from_kbd = 1; + + while ((optchar = getopt(argc, argv, "d:r:k:M:e:P:sf:")) != EOF) { + switch(optchar) { + case 'd': /* set db name */ + kadm5_params.dbname = dbname = optarg; + kadm5_params.mask |= KADM5_CONFIG_DBNAME; + break; + case 'r': + kadm5_params.realm = realm = optarg; + kadm5_params.mask |= KADM5_CONFIG_REALM; + break; + case 'k': + if (!krb5_string_to_enctype(optarg, &master_keyblock.enctype)){ + enctypedone++; + kadm5_params.enctype = master_keyblock.enctype; + kadm5_params.mask |= KADM5_CONFIG_ENCTYPE; + } + else + com_err(argv[0], 0, "%s is an invalid enctype", optarg); + break; + case 's': + do_stash++; + kadm5_params.mkey_from_kbd = 0; + break; + case 'f': + kadm5_params.stash_file = keyfile = optarg; + kadm5_params.mask |= KADM5_CONFIG_STASH_FILE; + break; + case 'M': /* master key name in DB */ + kadm5_params.mkey_name = mkey_name = optarg; + kadm5_params.mask |= KADM5_CONFIG_MKEY_NAME; + break; + case 'P': /* Only used for testing!!! */ + mkey_password = optarg; + break; + case '?': + default: + usage(argv[0], 1); + /*NOTREACHED*/ + } + } + + /* + * Attempt to read the KDC profile. If we do, then read appropriate values + * from it and augment values supplied on the command line. + */ + if (!(retval = krb5_read_realm_params(context, + realm, + (char *) NULL, + (char *) NULL, + &rparams))) { + /* Get the value for the database */ + if (rparams->realm_dbname && !dbname) + dbname = strdup(rparams->realm_dbname); + + /* Get the value for the master key name */ + if (rparams->realm_mkey_name && !mkey_name) + mkey_name = strdup(rparams->realm_mkey_name); + + /* Get the value for the master key type */ + if (rparams->realm_enctype_valid && !enctypedone) { + master_keyblock.enctype = rparams->realm_enctype; + enctypedone++; + } + + /* Get the value for maximum ticket lifetime. */ + if (rparams->realm_max_life_valid) + rblock.max_life = rparams->realm_max_life; + + /* Get the value for maximum renewable ticket lifetime. */ + if (rparams->realm_max_rlife_valid) + rblock.max_rlife = rparams->realm_max_rlife; + + /* Get the value for the default principal expiration */ + if (rparams->realm_expiration_valid) + rblock.expiration = rparams->realm_expiration; + + /* Get the value for the default principal flags */ + if (rparams->realm_flags_valid) + rblock.flags = rparams->realm_flags; + + /* Get the value of the supported key/salt pairs */ + if (rparams->realm_num_keysalts) { + rblock.nkslist = rparams->realm_num_keysalts; + rblock.kslist = rparams->realm_keysalts; + rparams->realm_num_keysalts = 0; + rparams->realm_keysalts = (krb5_key_salt_tuple *) NULL; + } + + /* Get the value for the stash file */ + if (rparams->realm_stash_file && !keyfile) + keyfile = strdup(rparams->realm_stash_file); + + krb5_free_realm_params(context, rparams); + } + + if (!dbname) + dbname = DEFAULT_KDB_FILE; + + if (!enctypedone) + master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, tmp, sizeof(tmp))) + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit(1); + } + + krb5_use_enctype(context, &master_encblock, master_keyblock.enctype); + + retval = krb5_db_set_name(context, dbname); + if (!retval) retval = EEXIST; + + if (retval == EEXIST || retval == EACCES || retval == EPERM) { + /* it exists ! */ + com_err(argv[0], 0, "The database '%s' appears to already exist", + dbname); + exit(1); + } + if (!realm) { + if ((retval = krb5_get_default_realm(context, &defrealm))) { + com_err(argv[0], retval, "while retrieving default realm name"); + exit(1); + } + realm = defrealm; + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(context, mkey_name, realm, + &mkey_fullname, &master_princ))) { + com_err(argv[0], retval, "while setting up master key name"); + exit(1); + } + + krb5_princ_set_realm_data(context, &db_create_princ, realm); + krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm)); + krb5_princ_set_realm_data(context, &tgt_princ, realm); + krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm)); + krb5_princ_component(context, &tgt_princ,1)->data = realm; + krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm); + + printf("Initializing database '%s' for realm '%s',\n\ +master key name '%s'\n", + dbname, realm, mkey_fullname); + + if (!mkey_password) { + printf("You will be prompted for the database Master Password.\n"); + printf("It is important that you NOT FORGET this password.\n"); + fflush(stdout); + + pw_size = 1024; + pw_str = malloc(pw_size); + + retval = krb5_read_password(context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, + pw_str, &pw_size); + if (retval) { + com_err(argv[0], retval, "while reading master key from keyboard"); + exit(1); + } + mkey_password = pw_str; + } + + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(context, master_princ, &master_salt); + if (retval) { + com_err(argv[0], retval, "while calculated master key salt"); + exit(1); + } + if (retval = krb5_string_to_key(context, &master_encblock, + &master_keyblock, &pwd, &master_salt)) { + com_err(argv[0], retval, "while transforming master key from password"); + exit(1); + } + + if ((retval = krb5_process_key(context, &master_encblock, + &master_keyblock))) { + com_err(argv[0], retval, "while processing master key"); + exit(1); + } + + rblock.eblock = &master_encblock; + if ((retval = krb5_init_random_key(context, &master_encblock, + &master_keyblock, &rblock.rseed))) { + com_err(argv[0], retval, "while initializing random key generator"); + (void) krb5_finish_key(context, &master_encblock); + exit(1); + } + if ((retval = krb5_db_create(context, dbname))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while creating database '%s'", + dbname); + exit(1); + } + if ((retval = krb5_db_set_name(context, dbname))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while setting active database to '%s'", + dbname); + exit(1); + } + if ((retval = krb5_db_init(context))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while initializing the database '%s'", + dbname); + exit(1); + } + + if ((retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) || + (retval = add_principal(context, &tgt_princ, TGT_KEY, &rblock))) { + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while adding entries to the database"); + exit(1); + } + if (do_stash && + ((retval = krb5_db_store_mkey(context, keyfile, master_princ, + &master_keyblock)))) { + com_err(argv[0], errno, "while storing key"); + printf("Warning: couldn't stash master key.\n"); + } + /* clean up */ + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + free(master_keyblock.contents); + if (pw_str) { + memset(pw_str, 0, pw_size); + free(pw_str); + } + free(master_salt.data); + + kadm5_create(&kadm5_params); + + exit(0); + +} + +static krb5_error_code +tgt_keysalt_iterate(ksent, ptr) + krb5_key_salt_tuple *ksent; + krb5_pointer ptr; +{ + krb5_context context; + krb5_error_code kret; + struct iterate_args *iargs; + krb5_keyblock random_keyblock, *key; + krb5_int32 ind; + krb5_encrypt_block random_encblock; + krb5_pointer rseed; + krb5_data pwd; + + iargs = (struct iterate_args *) ptr; + kret = 0; + + context = iargs->ctx; + + /* + * Convert the master key password into a key for this particular + * encryption system. + */ + krb5_use_enctype(context, &random_encblock, ksent->ks_enctype); + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + if (kret = krb5_string_to_key(context, &random_encblock, &random_keyblock, + &pwd, &master_salt)) + return kret; + if ((kret = krb5_init_random_key(context, &random_encblock, + &random_keyblock, &rseed))) + return kret; + + if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { + ind = iargs->dbentp->n_key_data-1; + if (!(kret = krb5_random_key(context, + &random_encblock, rseed, + &key))) { + kret = krb5_dbekd_encrypt_key_data(context, + iargs->rblock->eblock, + key, + NULL, + 1, + &iargs->dbentp->key_data[ind]); + krb5_free_keyblock(context, key); + } + } + memset((char *)random_keyblock.contents, 0, random_keyblock.length); + free(random_keyblock.contents); + (void) krb5_finish_random_key(context, &random_encblock, &rseed); + return(kret); +} + +static krb5_error_code +add_principal(context, princ, op, pblock) + krb5_context context; + krb5_principal princ; + enum ap_op op; + struct realm_info *pblock; +{ + krb5_error_code retval; + krb5_db_entry entry; + + krb5_timestamp now; + struct iterate_args iargs; + + int nentries = 1; + + memset((char *) &entry, 0, sizeof(entry)); + + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.attributes = pblock->flags; + entry.max_life = pblock->max_life; + entry.max_renewable_life = pblock->max_rlife; + entry.expiration = pblock->expiration; + + if ((retval = krb5_copy_principal(context, princ, &entry.princ))) + goto error_out; + + if ((retval = krb5_timeofday(context, &now))) + goto error_out; + + if ((retval = krb5_dbe_update_mod_princ_data(context, &entry, + now, &db_create_princ))) + goto error_out; + + switch (op) { + case MASTER_KEY: + if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) + == NULL) + goto error_out; + memset((char *) entry.key_data, 0, sizeof(krb5_key_data)); + entry.n_key_data = 1; + + entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; + if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + &master_keyblock, NULL, + 1, entry.key_data))) + return retval; + break; + case TGT_KEY: + iargs.ctx = context; + iargs.rblock = pblock; + iargs.dbentp = &entry; + /* + * Iterate through the key/salt list, ignoring salt types. + */ + if ((retval = krb5_keysalt_iterate(pblock->kslist, + pblock->nkslist, + 1, + tgt_keysalt_iterate, + (krb5_pointer) &iargs))) + return retval; + break; + case NULL_KEY: + return EOPNOTSUPP; + default: + break; + } + + retval = krb5_db_put_principal(context, &entry, &nentries); + +error_out:; + krb5_dbe_free_contents(context, &entry); + return retval; +} diff --git a/src/kadmin/create/string_table.c b/src/kadmin/create/string_table.c new file mode 100644 index 000000000..b9f86a363 --- /dev/null +++ b/src/kadmin/create/string_table.c @@ -0,0 +1,91 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +/* String table of messages for kadm5_create */ + +char *str_INITING_KCONTEXT = "while initializing the kerberos context"; + +char *str_PARSE_NAME = "while parsing admin principal name."; + +char *str_HISTORY_PARSE_NAME = "while parsing admin history principal name."; + +char *str_ADMIN_PRINC_EXISTS = "Warning! Admin principal already exists."; + +char *str_CHANGEPW_PRINC_EXISTS = "Warning! Changepw principal already exists."; + +char *str_HISTORY_PRINC_EXISTS = "Warning! Admin history principal already exists."; + +char *str_ADMIN_PRINC_WRONG_ATTRS = + "Warning! Admin principal has incorrect attributes.\n" + "\tDISALLOW_TGT should be set, and max_life should be three hours.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_CHANGEPW_PRINC_WRONG_ATTRS = + "Warning! Changepw principal has incorrect attributes.\n" + "\tDISALLOW_TGT and PW_CHANGE_SERVICE should both be set, and " + "max_life should be five minutes.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_HISTORY_PRINC_WRONG_ATTRS = + "Warning! Admin history principal has incorrect attributes.\n" + "\tDISALLOW_ALL_TIX should be set.\n" + "\tThis program will leave it as-is, but beware!."; + +char *str_CREATED_PRINC_DB = + "%s: Admin principal database created (or it already existed).\n"; /* whoami */ + +char *str_CREATED_POLICY_DB = + "%s: Admin policy database created (or it already existed).\n"; /* whoami */ + +char *str_RANDOM_KEY = + "while calling random key for %s."; /* principal name */ + +char *str_ENCRYPT_KEY = + "while calling encrypt key for %s."; /* principal name */ + +char *str_PUT_PRINC = + "while calling storing %s in Kerberos database."; /* principal name */ + +char *str_CREATING_POLICY_DB = "while creating/opening admin policy database."; + +char *str_CLOSING_POLICY_DB = "while closing admin policy database."; + +char *str_CREATING_PRINC_DB = "while creating/opening admin principal database."; + +char *str_CLOSING_PRINC_DB = "while closing admin principal database."; + +char *str_CREATING_PRINC_ENTRY = + "while creating admin principal database entry for %s."; /* princ_name */ + +char *str_A_PRINC = "a principal"; + +char *str_UNPARSE_PRINC = "while unparsing principal."; + +char *str_CREATED_PRINC = "%s: Created %s principal.\n"; /* whoami, princ_name */ + +char *str_INIT_KDB = "while initializing kdb."; + +char *str_NO_KDB = +"while initializing kdb.\nThe Kerberos KDC database needs to exist in /krb5.\n\ +If you haven't run kdb5_create you need to do so before running this command."; + + +char *str_INIT_RANDOM_KEY = "while initializing random key generator."; + +char *str_TOO_MANY_ADMIN_PRINC = + "while fetching admin princ. Can only have one admin principal."; + +char *str_TOO_MANY_CHANGEPW_PRINC = + "while fetching changepw princ. Can only have one changepw principal."; + +char *str_TOO_MANY_HIST_PRINC = + "while fetching history princ. Can only have one history principal."; + +char *str_WHILE_DESTROYING_ADMIN_SESSION = "while closing session with admin server and destroying tickets."; diff --git a/src/kadmin/create/string_table.h b/src/kadmin/create/string_table.h new file mode 100644 index 000000000..e8cb45367 --- /dev/null +++ b/src/kadmin/create/string_table.h @@ -0,0 +1,40 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + */ + +#ifndef _OVSEC_ADM_STRINGS_ + +extern char *str_INITING_KCONTEXT; +extern char *str_PARSE_NAME; +extern char *str_HISTORY_PARSE_NAME; +extern char *str_ADMIN_PRINC_EXISTS; +extern char *str_CHANGEPW_PRINC_EXISTS; +extern char *str_HISTORY_PRINC_EXISTS; +extern char *str_ADMIN_PRINC_WRONG_ATTRS; +extern char *str_CHANGEPW_PRINC_WRONG_ATTRS; +extern char *str_HISTORY_PRINC_WRONG_ATTRS; +extern char *str_CREATED_PRINC_DB; +extern char *str_CREATED_POLICY_DB; +extern char *str_RANDOM_KEY; +extern char *str_ENCRYPT_KEY; +extern char *str_PUT_PRINC; +extern char *str_CREATING_POLICY_DB; +extern char *str_CLOSING_POLICY_DB; +extern char *str_CREATING_PRINC_DB; +extern char *str_CLOSING_PRINC_DB; +extern char *str_CREATING_PRINC_ENTRY; +extern char *str_A_PRINC; +extern char *str_UNPARSE_PRINC; +extern char *str_CREATED_PRINC; +extern char *str_INIT_KDB; +extern char *str_NO_KDB; +extern char *str_INIT_RANDOM_KEY; +extern char *str_TOO_MANY_ADMIN_PRINC; +extern char *str_TOO_MANY_CHANGEPW_PRINC; +extern char *str_TOO_MANY_HIST_PRINC; +extern char *str_WHILE_DESTROYING_ADMIN_SESSION; + +#endif /* _OVSEC_ADM_STRINGS_ */ diff --git a/src/kadmin/dbutil/ChangeLog b/src/kadmin/dbutil/ChangeLog new file mode 100644 index 000000000..7719cc749 --- /dev/null +++ b/src/kadmin/dbutil/ChangeLog @@ -0,0 +1,440 @@ +Thu Jul 18 19:22:04 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed SS_RULES + +Wed Jul 10 19:43:22 1996 Marc Horowitz <marc@mit.edu> + + * dumpv4.c (configure.in, Makefile.in): make autoconf work after + barry's carnage + +Sun May 12 00:27:44 1996 Marc Horowitz <marc@mit.edu> + + * loadv4.c (enter_in_v5_db, add_principal), kdb5_edit.c + (create_db_entry, modent), dumpv4.c (dump_v4_iterator), dump.c + (dump_k5beta_iterator, process_k5beta_record): convert to use new + krb5_dbe_* tl_data functions. + + * cpw.c (enter_pwd_key): krb5_dbe_cpw() takes a kvno now. + +Tue May 7 23:16:57 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: USE_KADM_LIBRARY replaced by USE_KADMSRV_LIBRARY + +Thu Apr 11 19:32:36 1996 Richard Basch <basch@lehman.com> + + * kdb5_edit.c (extract_v4_srvtab): Use the matching key_data's kvno; + don't assume that key_data[0]'s kvno is necessarily the matching + key_data's kvno. + +Wed Apr 10 19:17:58 1996 Richard Basch <basch@lehman.com> + + * kdb5_edit.c (extract_v4_srvtab): Translate the principal name to + the common V4 name. + +Tue Mar 19 18:00:58 1996 Richard Basch <basch@lehman.com> + + * kdb5_edit.c (extract_v4_srvtab): do not test to make sure we + fetched a key of enctype 1 (des-cbc-crc), since we may have gotten + another des key from the database, which is just as useful in a + v4 srvtab + + * dumpv4.c (dump_v4_iterator): use krb5_524_conv_principal to do the + v5 to v4 principal translation, instead of having yet another + hard-coded table. + +Wed Mar 6 16:17:20 1996 Richard Basch <basch@lehman.com> + + * dumpv4.c: The V4 master key & schedule was never initialized, + so the dump created by dump_v4db was garbage. Read the V4 + master key from /.k or prompt for the V4 master key password. + If there is no V4-salt key in the database, but there is a DES + key, include it in the V4 dump, in case it is merely a random + service key for which there is no associated password. + Skip over K/M in the V5 database (use the entered V4 master key). + Both krbtgt and afs keys often have domain-qualifed instances. + +Tue Mar 5 12:18:22 1996 Richard Basch <basch@lehman.com> + + * dump.c: POSIX locking requires that the file be opened read-write. + +Mon Feb 26 22:42:09 1996 Mark Eichin <eichin@cygnus.com> + + * kdb5_edit.c: new command line option -f stashfile. + * kdb5_edit.M: document stashfile option. + +Mon Feb 26 22:13:45 1996 Mark Eichin <eichin@cygnus.com> + + * dump.c (process_k5beta_record): since V4 salt type has no data + either, only set key_data_ver to 1 for data_type 0 with 0-length + salt. Also, don't include alternate key if akey has all-zero type + and length in both fields. + +Sat Feb 24 04:02:18 1996 Mark W. Eichin <eichin@cygnus.com> + + * dump.c (process_k5beta_record): encrypted keys used to have 4 + byte lengths in MSB order, need to convert to 2 byte LSB order + lengths before storing. Handle primary key and alternate key. + +Fri Feb 23 18:44:10 1996 Mark Eichin <eichin@cygnus.com> + + * kdb5_edit.c (kdb5_edit_Init): set manual_mkey for testing with -P + +Wed Feb 14 09:52:18 1996 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kdb5_edit.c (enter_master_key, set_dbname_help): If master key + enctype is unknown, set to DEFAULT_KDC_ENCTYPE. + +Tue Feb 13 16:08:07 1996 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kdb5_edit.c (extract_v4_srvtab): krb5_dbekd_decrypt_key_data + takes krb5_key_data *, not **. + +Tue Jan 30 18:28:57 1996 Mark Eichin <eichin@cygnus.com> + + * dump.c (load_db): dbrenerr_fmt prints "from" first, so pass it + to fprintf correctly. + +Sun Jan 28 14:31:47 1996 Mark Eichin <eichin@cygnus.com> + + * dump.c (process_k5_record): t2..t9 is only 8 vars, not 9. + +Thu Jan 25 16:07:42 1996 Sam Hartman <hartmans@tertius.mit.edu> + + * kdb5_edit.c (extract_srvtab): Extract *all* the keys in a + dbentry, not the first one. + (extract_v4_srvtab): Attempt to find the right v4 keys. + +Wed Jan 24 18:48:38 1996 Tom Yu <tlyu@dragons-lair.MIT.EDU> + + * Makefile.in: Remove spurious @DEFS@ + + +Wed Dec 13 03:44:58 1995 Chris Provenzano (proven@mit.edu) + + * dump.c, dumpv4.c, kdb5_edit.c, loadv4.c : + Remove mkvno from krb5_db_entry. + +Sun Dec 10 11:07:51 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kdb5_edit.M: Document that modent exists + + * kdb5_edit.c (modent): Add usage as suggested by jhawk@mit.edu. + +Thu Nov 09 17:05:57 1995 Chris Provenzano (proven@mit.edu) + + * kdb5_edit.c : Remove krb5_enctype from krb5_string_to_key() args. + +Fri Oct 27 13:37:04 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * dump.c (process_k5_record): Fix off by one in malloc. + +Mon Oct 9 16:35:19 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kdb5_edit.c (extract_v4_srvtab): Extract a one byte version + number for v4 srvtabs (from warlord). + +Thu Oct 5 10:35:35 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * cpw.c: Declare std_ks_tuple as extern. + * kdb5_edit.h: Remove std_ks_tuple declaration as not all sources + include adm.h for structures + +Tue Oct 3 23:10:57 1995 Theodore Y. Ts'o <tytso@dcl> + + * cpw.c (enter_rnd_key, enter_pwd_key): + * kdb5_edit.c (kdb5_edit_Init): Use the kdc.conf file to determine + the default list of keysalt tuples to be used. This is + stored in std_ks_tuple, and is used by cpw.c for random + keys and when a list of keysalts is not specified. + +Mon Sep 18 03:59:47 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kdb5_edit.c (show_principal): Show key version and last password + change. + + * cpw.c: Fix typo in below change in which list was terminated + after third entry. (extra } removed) + +Fri Sep 15 14:21:25 1995 Theodore Y. Ts'o <tytso@dcl> + + * cpw.c: Add DES_CBC_MD5 and DES_CBC_CRC with the V4 salt as + default key/salt tuples to be added. (Once proven's DES_* + folding code is implemented, we can shorten this list.) + Eventually, this list should be read in from kdc.conf. + +Thu Sep 7 20:41:24 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * loadv4.c (load_v4db): Provide a dummy routine if krb4 + compatibility is not compiled in. + +Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c, dump.c, dumpv4.c, kdb5_edit.c, loadv4.c : + s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g + +Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c, dump.c, dumpv4.c, kdb5_edit.c, loadv4.c : Remove krb5_enctype + references, and replace with krb5_keytype where appropriate. + +Fri Aug 25 17:37:33 EDT 1995 Paul Park (pjpark@mit.edu) + * dumpv4.c - Fix handle_keys(). It was trying to recreate work that + has already been done. + * Makefile.in, .Sanitize, loadv4.c, kdb5_ed_ct.ct - Add lddb4, the + command to load a v4 dump file. This is basically, kdb5_ + convert reconstituted to fit within the framework of kdb5_edit. + +Thu Aug 24 19:28:39 1995 Theodore Y. Ts'o <tytso@dcl> + + * .Sanitize: Update file list + +Mon Aug 21 16:45:39 EDT 1995 Paul Park (pjpark@mit.edu) + * dump.c - Completely rework this logic to support old (e.g. Beta 5 + and previous) dump format and new dump format using the same + commands. This is differentiated by using the "-old" command + qualifier. + + * kdb5_edit.M - Add description of -R and -s. Remove "ascii represen- + tation of a decimal number". Remove "Bugs". + +Fri Aug 18 17:06:06 EDT 1995 Paul Park (pjpark@mit.edu) + + * ss_wrapper.c - Change sense of fgets() check so scripts work. + + +Tue Aug 15 14:22:50 EDT 1995 Paul Park (pjpark@mit.edu) + + * kdb5_edit.c, ss_wrapper.c, cpw.c, kdb5_edit.h - Add support for + -s scriptfile and fix up assorted gcc -Wall complaints. + + +Mon Aug 7 17:32:31 EDT 1995 Paul Park (pjpark@mit.edu) + * cpw.c - Use krb5_string_to_keysalts() to generate a list of unique + key/salt pairs supplied in argv. + + +Mon Aug 07 11:16:03 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c : Uses new kdb change password routines for ank, ark, cpw, + and crk. Also remove v4 variants of ank and cpw. + * krb5_edit.c : Deleted old variants of rotuines now in cpw.c + * kdb5_ed_ct.ct, kdb5_edit.M, tcl_wrapper.c: + Removed references to v4 variants of ank and cpw. + * kdb5_edit.h (enter_pwd_key()) : Removed proto, it's nolonger + necessary as it's a static routine in cpw.c + +Thu Aug 03 12:13:50 1995 Chris Provenzano (proven@mit.edu) + + * cpw.c : New change password code for kdb5_edit. + * dumpv4.c : Get it to compile with new kdb format. + +Mon Jul 31 15:47:30 EDT 1995 Paul Park (pjpark@mit.edu) + * kdb5_edit.c - Use libkadm string conversion routines. These are + shared by all utilities. + * Makefile.in - Remove getdate.y. + * configure.in - Remove getdate.y dependency checks. + * getdate.y - Sayonara. + + +Thu Jul 27 15:01:01 EDT 1995 Paul Park (pjpark@mit.edu) + * configure.in - Add --with-dbm and check for already checking for dbm. + + +Thu Jul 27 02:59:05 1995 Chris Provenzano (proven@mit.edu) + + * dump.c kdb5_edit.c kdb5_edit.h util.c : Use new kdb format. + +Mon Jul 17 15:00:08 EDT 1995 Paul Park (pjpark@mit.edu) + * configure.in - Add KADM library. + * dumpv4.c - Change calling sequence to krb5_db_fetch_mkey(). + * kdb5_edit.c - Change calling sequence to krb5_db_fetch_mkey() which + uses the stash file. Add KDC profile reading/handling as a + supplement to command line supplied arguments. + + +Wed Jul 12 12:01:04 EDT 1995 Paul Park (pjpark@mit.edu) + * configure.in - Temporarily add --with-kdb4 option. Default is without + kdb4. Without kdb4 enables a define. With kdb4 uses -lkdb4 and + -l[n]dbm libraries. + * dumpv4.c - Conditionalize references to kdb4 routines with + KDB4_DISABLE. Replace two required routines: + kdb_encrypt_key -> pcbc_encrypt + kdb_get_master_key -> des_read_password/printf/key_sched + + +Fri Jul 7 15:38:00 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Remove all explicit library handling and LDFLAGS. + * configure.in - Add USE_<mumble> and KRB5_LIBRARIES. + + +Thu Jun 15 15:34:59 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Change explicit library names to -l<lib> form, and + change target link line to use $(LD) and associated flags. + Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these wer + split out. + * configure.in - Add shared library usage check. + +Fri Jun 9 18:14:43 1995 <tytso@rsx-11.mit.edu> + + * configure.in: Remove standardized set of autoconf macros, which + are now handled by CONFIG_RULES. + + * dumpv4.c: Change name of controlling #ifdef to be + KRB5_KRB4_COMPAT instead of KRB4. + +Sun May 21 14:20:32 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * dumpv4.c: Include k5-int.h before krb.h so that PROTOTYPE is not + redefined. + +Sun May 7 13:46:30 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * configure.in: Add AC_HEADER_STDC to define STDC_HEADERS for + getdate.y. + +Mon May 1 13:36:41 1995 Theodore Y. Ts'o (tytso@dcl) + + * kdb5_edit.c (kdb5_edit_Init): Check the return code from + kdb5_init_context(). + +Fri Apr 28 18:04:26 1995 Mark Eichin <eichin@cygnus.com> + + * Makefile.in (LOCAL_LIBRARIES): put KRB4_LIB inside KLIB, and put + KDB4_LIB ahead of them both. + +Thu Apr 27 13:47:23 1995 Mark Eichin <eichin@cygnus.com> + + * Makefile.in (LOCAL_LIBRARIES): use KRB4_LIB and KDB4_LIB + directly. + * configure.in: just use WITH_KRB4. + +Wed Apr 19 13:59:47 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kdb5_edit.c (kdb5_edit_Init): If a default realm is specified + (with -r), use krb5_set_default_realm so that created keys + will have the correct realm. + +Thu Mar 23 23:28:26 1995 Theodore Y. Ts'o <tytso@dcl> + + * kdb5_edit.c (show_principal, parse_princ_args): Add + "support_desmd5" flag. + +Tue Mar 14 16:29:05 1995 <tytso@rsx-11.mit.edu> + + * ss_wrapper.c (main): Set the return code from ss_execute_line(), + so that appropriate error checking is done. + +Thu Mar 2 12:18:57 1995 Theodore Y. Ts'o <tytso@dcl> + + * Makefile.in (ISODELIB): Remove reference to $(ISODELIB). + +Wed Mar 1 11:53:02 1995 Theodore Y. Ts'o <tytso@dcl> + + * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket + and -lnsl with WITH_NETLIB check. + +Tue Feb 28 02:06:26 1995 John Gilmore (gnu at toad.com) + + * dump.c, dumpv4.c, kdb5_edit.c, ss_wrapper.c, tcl_wrapper.c, + util.c: Avoid <krb5/...> includes. + +Thu Feb 23 19:52:35 1995 Mark Eichin (eichin@cygnus.com) + + * kdb5_edit.c: add struct timeb and sys/timeb includes from + getdate.y. + (ftime): new function, in case we don't HAVE_FTIME. + +Tue Feb 14 17:55:47 1995 Tom Yu (tlyu@dragons-lair) + + * kdb5_edit.c: add modent + * getdate.y: import get_date + * kdbt_ed_ct.ct: add modent + * configure.in: + * Makefile.in: support for getdate.y + +Wed Feb 8 20:08:36 1995 Tom Yu (tlyu@dragons-lair) + + * kdb5_edit.c (show_principal): make sane and print all useful + fields + +Wed Jan 25 16:54:40 1995 Chris Provenzano (proven@mit.edu) + + * Removed all narrow types and references to wide.h and narrow.h + +Fri Jan 13 15:23:47 1995 Chris Provenzano (proven@mit.edu) + + * Added krb5_context to all krb5_routines + +Mon Dec 19 18:04:11 1994 Theodore Y. Ts'o (tytso@dcl) + + * configure.in: + * Makefile.in: + * dumpv4.c (dump_v4db): Do the right thing if we are compiling + without V4 support. (The dump_v4db command is disabled.) + +Wed Dec 7 00:07:46 1994 <tytso@rsx-11.mit.edu> + + * dumpv4.c (v4_print_time): gmtime expects a pointer to a time_t, + not a long. On most systems these are the same, on + others.... + +Wed Nov 16 01:03:42 1994 Mark Eichin (eichin@cygnus.com) + + * dumpv4.c: new file. New command dump_v4db which creates a v4 + slave dump out of a v5 database, leaving out any keys which aren't + using v4 salt, and any keys that aren't for the current + realm. Reencrypts using v4 master key, synthesizes arbitrary + master key version number. + * configure.in: use WITH_KRB4 for dump support. + * kdb5_ed_ct.ct: add new dump_v4 command. + * Makefile.in: link in dumpv4. + +Fri Oct 14 23:31:49 1994 Theodore Y. Ts'o (tytso@dcl) + + * dump.c (load_db): When scanning a database entry, read + fail_auth_count into a temporary integer variable, and + then copy that into entry.fail_auth_count, which is a + char. + +Fri Oct 7 00:01:40 1994 Theodore Y. Ts'o (tytso@dcl) + + * kdb5_edit.c (kdb5_edit_Init): Don't let errors in + set_dbname_help initially cause the exit status to be set. + Commands like load_db don't need a valid database to be + opened. + + * ss_wrapper.c (main): Clear code before ss_execute_line, since + ss_execute_line doesn't set code to 0 if there are no + problems. + + * kdb5_edit.c (kdb5_edit_Init): Add a new option so that the + master key password can be entered on the command line --- + for testing only; not documented!! + +Mon Oct 3 19:10:47 1994 Theodore Y. Ts'o (tytso@dcl) + + * Makefile.in: Use $(srcdir) to find manual page for make install. + +Thu Sep 29 15:52:22 1994 Theodore Y. Ts'o (tytso@dcl) + + * dump.c (update_ok_file): Make sure mod time on the dump_ok file + is updated. (Some systems don't update the mod-time when + a file is opened for writing.) + + * Makefile.in: Relink executable when libraries change. + + * kdb5_edit.c (show_principal): Pass variable with correct type to + ctime(). + + * tcl_wrapper.c (doquit): + ss_wrapper.c (main): + kdb5_edit.c: + dump.c: Exit with a non-zero exit status if there was an error + in a executed command. + +Thu Sep 15 11:00:30 1994 Theodore Y. Ts'o (tytso@dcl) + + * dump.c (load_db): Fix error string on failed fopen. ("for + writing" -> "for reading") + + diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in new file mode 100644 index 000000000..b5bba09d9 --- /dev/null +++ b/src/kadmin/dbutil/Makefile.in @@ -0,0 +1,18 @@ +CFLAGS = $(CCOPTS) $(DEFS) -DKDB4_DISABLE $(LOCALINCLUDE) @KRB4_INCLUDES@ + +PROG = kdb5_util +OBJS = kdb5_create.o kadm5_create.o string_table.o +OBJS = kdb5_util.o kdb5_util_ct.o dump.o dumpv4.o loadv4.o ss_wrapper.o \ + kdb5_create.o kadm5_create.o string_table.o kdb5_stash.o \ + kdb5_destroy.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/dbutil/Makefile.ov b/src/kadmin/dbutil/Makefile.ov new file mode 100644 index 000000000..f762282f2 --- /dev/null +++ b/src/kadmin/dbutil/Makefile.ov @@ -0,0 +1,19 @@ +TOP = .. +include $(TOP)/config.mk/template + +CFLAGS += -I$(TOP)/../include/kerberosIV + +PROG = kdb5_util +SRCS = kdb5_util.c kdb5_util_ct.c dump.c ss_wrapper.c dumpv4.c loadv4.c \ + kdb5_create.c kadm5_create.c string_table.c kdb5_stash.c \ + kdb5_destroy.c +OBJS = kdb5_util.o kdb5_util_ct.o dump.o dumpv4.o loadv4.o ss_wrapper.o \ + kdb5_create.o kadm5_create.o string_table.o kdb5_stash.o \ + kdb5_destroy.o +ETABLES = kdb5_util_ct.ct + +LIBS = $(LIBADMSRV) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBRPCLIB) $(LIBDYN) \ + $(LIBSS) $(LIBDB) + +expand NormalProgram +expand ErrorTables diff --git a/src/kadmin/dbutil/configure.in b/src/kadmin/dbutil/configure.in new file mode 100644 index 000000000..c9234524e --- /dev/null +++ b/src/kadmin/dbutil/configure.in @@ -0,0 +1,13 @@ +AC_INIT(kdb5_create.c) +CONFIG_RULES +WITH_KRB4 +AC_PROG_INSTALL +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_KDB5_LIBRARY +USE_DYN_LIBRARY +USE_SS_LIBRARY +USE_KRB4_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c new file mode 100644 index 000000000..8bd54ca4d --- /dev/null +++ b/src/kadmin/dbutil/dump.c @@ -0,0 +1,1957 @@ +/* + * admin/edit/dump.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Dump a KDC database + */ + +#include <stdio.h> +#include <k5-int.h> +#include <kadm5/admin.h> +#include <kadm5/adb.h> +#include <com_err.h> +#include "kdb5_util.h" +#if HAVE_REGEX_H +#include <regex.h> +#endif /* HAVE_REGEX_H */ + +/* + * Use compile(3) if no regcomp present. + */ +#if !defined(HAVE_REGCOMP) && defined(HAVE_REGEXP_H) +#define INIT char *sp = instring; +#define GETC() (*sp++) +#define PEEKC() (*sp) +#define UNGETC(c) (--sp) +#define RETURN(c) return(c) +#define ERROR(c) +#define RE_BUF_SIZE 1024 +#include <regexp.h> +#endif /* !HAVE_REGCOMP && HAVE_REGEXP_H */ + +struct dump_args { + char *programname; + FILE *ofile; + krb5_context kcontext; + char **names; + int nnames; + int verbose; +}; + +static krb5_error_code dump_k5beta_iterator PROTOTYPE((krb5_pointer, + krb5_db_entry *)); +static krb5_error_code dump_k5beta6_iterator PROTOTYPE((krb5_pointer, + krb5_db_entry *)); +static krb5_error_code dump_k5beta7_princ PROTOTYPE((krb5_pointer, + krb5_db_entry *)); +static void dump_k5beta7_policy PROTOTYPE((void *, osa_policy_ent_t)); + +typedef krb5_error_code (*dump_func)PROTOTYPE((krb5_pointer, + krb5_db_entry *)); + +static int process_k5beta_record PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); +static int process_k5beta6_record PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); +static int process_k5beta7_record PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); +typedef krb5_error_code (*load_func)PROTOTYPE((char *, krb5_context, + FILE *, int, int *, void *)); + +typedef struct _dump_version { + char *name; + char *header; + dump_func dump_princ; + osa_adb_iter_policy_func dump_policy; + load_func load_record; +} dump_version; + +dump_version old_version = { + "Kerberos version 5 old format", + "kdb5_edit load_dump version 2.0\n", + dump_k5beta_iterator, + NULL, + process_k5beta_record, +}; +dump_version beta6_version = { + "Kerberos version 5 beta 6 format", + "kdb5_edit load_dump version 3.0\n", + dump_k5beta6_iterator, + NULL, + process_k5beta6_record, +}; +dump_version beta7_version = { + "Kerberos version 5", + "kdb5_util load_dump version 4\n", + dump_k5beta7_princ, + dump_k5beta7_policy, + process_k5beta7_record, +}; + +/* External data */ +extern char *current_dbname; +extern krb5_boolean dbactive; +extern int exit_status; +extern krb5_context util_context; +extern kadm5_config_params global_params; + +/* Strings */ + +static const char k5beta_dump_header[] = "kdb5_edit load_dump version 2.0\n"; +static const char k5beta6_dump_header[] = "kdb5_edit load_dump version 3.0\n"; +static const char k5beta7_dump_header[] = "kdb5_edit load_dump version 4\n"; + +static const char null_mprinc_name[] = "kdb5_dump@MISSING"; + +/* Message strings */ +static const char regex_err[] = "%s: regular expression error - %s\n"; +static const char regex_merr[] = "%s: regular expression match error - %s\n"; +static const char pname_unp_err[] = "%s: cannot unparse principal name (%s)\n"; +static const char mname_unp_err[] = "%s: cannot unparse modifier name (%s)\n"; +static const char nokeys_err[] = "%s: cannot find any standard key for %s\n"; +static const char sdump_tl_inc_err[] = "%s: tagged data list inconsistency for %s (counted %d, stored %d)\n"; +static const char stand_fmt_name[] = "Kerberos version 5"; +static const char old_fmt_name[] = "Kerberos version 5 old format"; +static const char b6_fmt_name[] = "Kerberos version 5 beta 6 format"; +static const char ofopen_error[] = "%s: cannot open %s for writing (%s)\n"; +static const char oflock_error[] = "%s: cannot lock %s (%s)\n"; +static const char dumprec_err[] = "%s: error performing %s dump (%s)\n"; +static const char dumphdr_err[] = "%s: error dumping %s header (%s)\n"; +static const char trash_end_fmt[] = "%s(%d): ignoring trash at end of line: "; +static const char read_name_string[] = "name string"; +static const char read_key_type[] = "key type"; +static const char read_key_data[] = "key data"; +static const char read_pr_data1[] = "first set of principal attributes"; +static const char read_mod_name[] = "modifier name"; +static const char read_pr_data2[] = "second set of principal attributes"; +static const char read_salt_data[] = "salt data"; +static const char read_akey_type[] = "alternate key type"; +static const char read_akey_data[] = "alternate key data"; +static const char read_asalt_type[] = "alternate salt type"; +static const char read_asalt_data[] = "alternate salt data"; +static const char read_exp_data[] = "expansion data"; +static const char store_err_fmt[] = "%s(%d): cannot store %s(%s)\n"; +static const char add_princ_fmt[] = "%s\n"; +static const char parse_err_fmt[] = "%s(%d): cannot parse %s (%s)\n"; +static const char read_err_fmt[] = "%s(%d): cannot read %s\n"; +static const char no_mem_fmt[] = "%s(%d): no memory for buffers\n"; +static const char rhead_err_fmt[] = "%s(%d): cannot match size tokens\n"; +static const char err_line_fmt[] = "%s: error processing line %d of %s\n"; +static const char head_bad_fmt[] = "%s: dump header bad in %s\n"; +static const char read_bytecnt[] = "record byte count"; +static const char read_encdata[] = "encoded data"; +static const char n_name_unp_fmt[] = "%s(%s): cannot unparse name\n"; +static const char n_dec_cont_fmt[] = "%s(%s): cannot decode contents\n"; +static const char read_nint_data[] = "principal static attributes"; +static const char read_tcontents[] = "tagged data contents"; +static const char read_ttypelen[] = "tagged data type and length"; +static const char read_kcontents[] = "key data contents"; +static const char read_ktypelen[] = "key data type and length"; +static const char read_econtents[] = "extra data contents"; +static const char k5beta_fmt_name[] = "Kerberos version 5 old format"; +static const char standard_fmt_name[] = "Kerberos version 5 format"; +static const char lusage_err_fmt[] = "%s: usage is %s [%s] [%s] [%s] filename dbname [admin_dbname]\n"; +static const char no_name_mem_fmt[] = "%s: cannot get memory for temporary name\n"; +static const char ctx_err_fmt[] = "%s: cannot initialize Kerberos context\n"; +static const char stdin_name[] = "standard input"; +static const char restfail_fmt[] = "%s: %s restore failed\n"; +static const char close_err_fmt[] = "%s: cannot close database (%s)\n"; +static const char dbinit_err_fmt[] = "%s: cannot initialize database (%s)\n"; +static const char dbname_err_fmt[] = "%s: cannot set database name to %s (%s)\n"; +static const char dbdelerr_fmt[] = "%s: cannot delete bad database %s (%s)\n"; +static const char dbrenerr_fmt[] = "%s: cannot rename database %s to %s (%s)\n"; +static const char dbcreaterr_fmt[] = "%s: cannot create database %s (%s)\n"; +static const char dfile_err_fmt[] = "%s: cannot open %s (%s)\n"; + +static const char oldoption[] = "-old"; +static const char b6option[] = "-b6"; +static const char verboseoption[] = "-verbose"; +static const char updateoption[] = "-update"; +static const char dump_tmptrail[] = "~"; + +/* + * Update the "ok" file. + */ +void update_ok_file (file_name) + char *file_name; +{ + /* handle slave locking/failure stuff */ + char *file_ok; + int fd; + static char ok[]=".dump_ok"; + + if ((file_ok = (char *)malloc(strlen(file_name) + strlen(ok) + 1)) + == NULL) { + com_err(progname, ENOMEM, + "while allocating filename for update_ok_file"); + exit_status++; + return; + } + strcpy(file_ok, file_name); + strcat(file_ok, ok); + if ((fd = open(file_ok, O_WRONLY|O_CREAT|O_TRUNC, 0600)) < 0) { + com_err(progname, errno, "while creating 'ok' file, '%s'", + file_ok); + exit_status++; + free(file_ok); + return; + } + if (write(fd, "", 1) != 1) { + com_err(progname, errno, "while writing to 'ok' file, '%s'", + file_ok); + exit_status++; + free(file_ok); + return; + } + + free(file_ok); + close(fd); + return; +} + +/* + * name_matches() - See if a principal name matches a regular expression + * or string. + */ +static int +name_matches(name, arglist) + char *name; + struct dump_args *arglist; +{ +#if HAVE_REGCOMP + regex_t match_exp; + regmatch_t match_match; + int match_error; + char match_errmsg[BUFSIZ]; + size_t errmsg_size; +#elif HAVE_REGEXP_H + char regexp_buffer[RE_BUF_SIZE]; +#elif HAVE_RE_COMP + extern char *re_comp(); + char *re_result; +#endif /* HAVE_RE_COMP */ + int i, match; + + /* + * Plow, brute force, through the list of names/regular expressions. + */ + match = (arglist->nnames) ? 0 : 1; + for (i=0; i<arglist->nnames; i++) { +#if HAVE_REGCOMP + /* + * Compile the regular expression. + */ + if (match_error = regcomp(&match_exp, + arglist->names[i], + REG_EXTENDED)) { + errmsg_size = regerror(match_error, + &match_exp, + match_errmsg, + sizeof(match_errmsg)); + fprintf(stderr, regex_err, arglist->programname, match_errmsg); + break; + } + /* + * See if we have a match. + */ + if (match_error = regexec(&match_exp, name, 1, &match_match, 0)) { + if (match_error != REG_NOMATCH) { + errmsg_size = regerror(match_error, + &match_exp, + match_errmsg, + sizeof(match_errmsg)); + fprintf(stderr, regex_merr, + arglist->programname, match_errmsg); + break; + } + } + else { + /* + * We have a match. See if it matches the whole + * name. + */ + if ((match_match.rm_so == 0) && + (match_match.rm_eo == strlen(name))) + match = 1; + } + regfree(&match_exp); +#elif HAVE_REGEXP_H + /* + * Compile the regular expression. + */ + compile(arglist->names[i], + regexp_buffer, + ®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. + */ + if (re_result = re_comp(arglist->names[i])) { + fprintf(stderr, regex_err, arglist->programname, re_result); + break; + } + if (re_exec(name)) + match = 1; +#else /* HAVE_RE_COMP */ + /* + * If no regular expression support, then just compare the strings. + */ + if (!strcmp(arglist->names[i], name)) + match = 1; +#endif /* HAVE_REGCOMP */ + if (match) + break; + } + return(match); +} + +static krb5_error_code +find_enctype(dbentp, enctype, salttype, kentp) + krb5_db_entry *dbentp; + krb5_enctype enctype; + krb5_int32 salttype; + krb5_key_data **kentp; +{ + int i; + int maxkvno; + krb5_key_data *datap; + + maxkvno = -1; + datap = (krb5_key_data *) NULL; + for (i=0; i<dbentp->n_key_data; i++) { + if ((dbentp->key_data[i].key_data_type[0] == enctype) && + ((dbentp->key_data[i].key_data_type[1] == salttype) || + (salttype < 0))) { + maxkvno = dbentp->key_data[i].key_data_kvno; + datap = &dbentp->key_data[i]; + } + } + if (maxkvno >= 0) { + *kentp = datap; + return(0); + } + return(ENOENT); +} + +/* + * dump_k5beta_header() - Make a dump header that is recognizable by Kerberos + * Version 5 Beta 5 and previous releases. + */ +static krb5_error_code +dump_k5beta_header(arglist) + struct dump_args *arglist; +{ + /* The old header consists of the leading string */ + fprintf(arglist->ofile, k5beta_dump_header); + return(0); +} + +/* + * dump_k5beta_iterator() - Dump an entry in a format that is usable + * by Kerberos Version 5 Beta 5 and previous + * releases. + */ +static krb5_error_code +dump_k5beta_iterator(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name, *mod_name; + krb5_principal mod_princ; + krb5_tl_data *pwchg; + krb5_key_data *pkey, *akey, nullkey; + krb5_timestamp mod_date, last_pwd_change; + int i; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + mod_name = (char *) NULL; + memset(&nullkey, 0, sizeof(nullkey)); + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + fprintf(stderr, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + /* + * Deserialize the modifier record. + */ + mod_name = (char *) NULL; + mod_princ = NULL; + last_pwd_change = mod_date = 0; + pkey = akey = (krb5_key_data *) NULL; + if (!(retval = krb5_dbe_lookup_mod_princ_data(arg->kcontext, + entry, + &mod_date, + &mod_princ))) { + if (mod_princ) { + /* + * Flatten the modifier name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + mod_princ, + &mod_name))) + fprintf(stderr, mname_unp_err, arg->programname, + error_message(retval)); + krb5_free_principal(arg->kcontext, mod_princ); + } + } + if (!mod_name) + mod_name = strdup(null_mprinc_name); + + /* + * Find the last password change record and set it straight. + */ + if (retval = + krb5_dbe_lookup_last_pwd_change(arg->kcontext, entry, + &last_pwd_change)) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + krb5_xfree(name); + return(retval); + } + + /* + * Find the 'primary' key and the 'alternate' key. + */ + if ((retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_NORMAL, + &pkey)) && + (retval = find_enctype(entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + &akey))) { + fprintf(stderr, nokeys_err, arg->programname, name); + krb5_xfree(mod_name); + krb5_xfree(name); + return(retval); + } + + /* If we only have one type, then ship it out as the primary. */ + if (!pkey && akey) { + pkey = akey; + akey = &nullkey; + } + else { + if (!akey) + akey = &nullkey; + } + + /* + * First put out strings representing the length of the variable + * length data in this record, then the name and the primary key type. + */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%s\t%d\t", strlen(name), + strlen(mod_name), + (krb5_int32) pkey->key_data_length[0], + (krb5_int32) akey->key_data_length[0], + (krb5_int32) pkey->key_data_length[1], + (krb5_int32) akey->key_data_length[1], + name, + (krb5_int32) pkey->key_data_type[0]); + for (i=0; i<pkey->key_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[0][i]); + } + /* + * Second, print out strings representing the standard integer + * data in this record. + */ + fprintf(arg->ofile, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%s\t%u\t%u\t%u\t", + (krb5_int32) pkey->key_data_kvno, + entry->max_life, entry->max_renewable_life, + 1 /* Fake mkvno */, entry->expiration, entry->pw_expiration, + last_pwd_change, entry->last_success, entry->last_failed, + entry->fail_auth_count, mod_name, mod_date, + entry->attributes, pkey->key_data_type[1]); + + /* Pound out the salt data, if present. */ + for (i=0; i<pkey->key_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", pkey->key_data_contents[1][i]); + } + /* Pound out the alternate key type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[0]); + for (i=0; i<akey->key_data_length[0]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[0][i]); + } + /* Pound out the alternate salt type and contents */ + fprintf(arg->ofile, "\t%u\t", akey->key_data_type[1]); + for (i=0; i<akey->key_data_length[1]; i++) { + fprintf(arg->ofile, "%02x", akey->key_data_contents[1][i]); + } + /* Pound out the expansion data. (is null) */ + for (i=0; i < 8; i++) { + fprintf(arg->ofile, "\t%u", 0); + } + fprintf(arg->ofile, ";\n"); + /* If we're blabbing, do it */ + if (arg->verbose) + fprintf(stderr, "%s\n", name); + krb5_xfree(mod_name); + } + krb5_xfree(name); + return(0); +} + +/* + * dump_k5beta6_iterator() - Output a dump record in krb5b6 format. + */ +static krb5_error_code +dump_k5beta6_iterator(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name; + krb5_tl_data *tlp; + krb5_key_data *kdata; + int counter, i, j; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + fprintf(stderr, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + /* + * We'd like to just blast out the contents as they would appear in + * the database so that we can just suck it back in, but it doesn't + * lend itself to easy editing. + */ + + /* + * The dump format is as follows: + * len strlen(name) n_tl_data n_key_data e_length + * name + * attributes max_life max_renewable_life expiration + * pw_expiration last_success last_failed fail_auth_count + * n_tl_data*[type length <contents>] + * n_key_data*[ver kvno ver*(type length <contents>)] + * <e_data> + * Fields which are not encapsulated by angle-brackets are to appear + * verbatim. Bracketed fields absence is indicated by a -1 in its + * place + */ + + /* + * Make sure that the tagged list is reasonably correct. + */ + counter = 0; + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) + counter++; + if (counter == entry->n_tl_data) { + /* Pound out header */ + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%s\t", + (int) entry->len, + strlen(name), + (int) entry->n_tl_data, + (int) entry->n_key_data, + (int) entry->e_length, + name); + fprintf(arg->ofile, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + entry->attributes, + entry->max_life, + entry->max_renewable_life, + entry->expiration, + entry->pw_expiration, + entry->last_success, + entry->last_failed, + entry->fail_auth_count); + /* Pound out tagged data. */ + for (tlp = entry->tl_data; tlp; tlp = tlp->tl_data_next) { + fprintf(arg->ofile, "%d\t%d\t", + (int) tlp->tl_data_type, + (int) tlp->tl_data_length); + if (tlp->tl_data_length) + for (i=0; i<tlp->tl_data_length; i++) + fprintf(arg->ofile, "%02x", tlp->tl_data_contents[i]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + + /* Pound out key data */ + for (counter=0; counter<entry->n_key_data; counter++) { + kdata = &entry->key_data[counter]; + fprintf(arg->ofile, "%d\t%d\t", + (int) kdata->key_data_ver, + (int) kdata->key_data_kvno); + for (i=0; i<kdata->key_data_ver; i++) { + fprintf(arg->ofile, "%d\t%d\t", + kdata->key_data_type[i], + kdata->key_data_length[i]); + if (kdata->key_data_length[i]) + for (j=0; j<kdata->key_data_length[i]; j++) + fprintf(arg->ofile, "%02x", + kdata->key_data_contents[i][j]); + else + fprintf(arg->ofile, "%d", -1); + fprintf(arg->ofile, "\t"); + } + } + + /* Pound out extra data */ + if (entry->e_length) + for (i=0; i<entry->e_length; i++) + fprintf(arg->ofile, "%02x", entry->e_data[i]); + else + fprintf(arg->ofile, "%d", -1); + + /* Print trailer */ + fprintf(arg->ofile, ";\n"); + + if (arg->verbose) + fprintf(stderr, "%s\n", name); + } + else { + fprintf(stderr, sdump_tl_inc_err, + arg->programname, name, counter, (int) entry->n_tl_data); + retval = EINVAL; + } + } + krb5_xfree(name); + return(retval); +} + +/* + * dump_k5beta7_iterator() - Output a dump record in krb5b7 format. + */ +static krb5_error_code +dump_k5beta7_princ(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + krb5_error_code retval; + struct dump_args *arg; + char *name; + int tmp_nnames; + + /* Initialize */ + arg = (struct dump_args *) ptr; + name = (char *) NULL; + + /* + * Flatten the principal name. + */ + if ((retval = krb5_unparse_name(arg->kcontext, + entry->princ, + &name))) { + fprintf(stderr, pname_unp_err, + arg->programname, error_message(retval)); + return(retval); + } + /* + * If we don't have any match strings, or if our name matches, then + * proceed with the dump, otherwise, just forget about it. + */ + if (!arg->nnames || name_matches(name, arg)) { + fprintf(arg->ofile, "princ\t"); + + /* save the callee from matching the name again */ + tmp_nnames = arg->nnames; + arg->nnames = 0; + retval = dump_k5beta6_iterator(ptr, entry); + arg->nnames = tmp_nnames; + } + + free(name); + return retval; +} + +void dump_k5beta7_policy(void *data, osa_policy_ent_t entry) +{ + struct dump_args *arg; + + arg = (struct dump_args *) data; + fprintf(arg->ofile, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt); +} + +/* + * usage is: + * dump_db [-old] [-b6] [-verbose] [filename [principals...]] + */ +void +dump_db(argc, argv) + int argc; + char **argv; +{ + FILE *f; + struct dump_args arglist; + int error; + char *programname; + char *ofile; + krb5_error_code kret; + dump_version *dump; + int aindex; + krb5_boolean locked; + extern osa_adb_policy_t policy_db; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + ofile = (char *) NULL; + error = 0; + dump = &beta7_version; + arglist.verbose = 0; + + /* + * Parse the qualifiers. + */ + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) + dump = &old_version; + else if (!strcmp(argv[aindex], b6option)) + dump = &beta6_version; + else if (!strcmp(argv[aindex], verboseoption)) + arglist.verbose++; + else + break; + } + + arglist.names = (char **) NULL; + arglist.nnames = 0; + if (aindex < argc) { + ofile = argv[aindex]; + aindex++; + if (aindex < argc) { + arglist.names = &argv[aindex]; + arglist.nnames = argc - aindex; + } + } + + /* + * Attempt to open the database. The policy database only has to + * be opened if we try a dump that uses it. + */ + if (!dbactive || (dump->dump_policy != NULL && policy_db == NULL)) { + com_err(argv[0], 0, Err_no_database); + exit_status++; + return; + } + + kret = 0; + locked = 0; + if (ofile && strcmp(ofile, "-")) { + /* + * Make sure that we don't open and truncate on the fopen, + * since that may hose an on-going kprop process. + * + * We could also control this by opening for read and + * write, doing an flock with LOCK_EX, and then + * truncating the file once we have gotten the lock, + * but that would involve more OS dependencies than I + * want to get into. + */ + unlink(ofile); + if (!(f = fopen(ofile, "w"))) { + fprintf(stderr, ofopen_error, + programname, ofile, error_message(errno)); + exit_status++; + } + if ((kret = krb5_lock_file(util_context, + fileno(f), + KRB5_LOCKMODE_EXCLUSIVE))) { + fprintf(stderr, oflock_error, + programname, ofile, error_message(kret)); + exit_status++; + } + else + locked = 1; + } else { + f = stdout; + } + if (f && !(kret)) { + arglist.programname = programname; + arglist.ofile = f; + arglist.kcontext = util_context; + fprintf(arglist.ofile, "%s", dump->header); + if ((kret = krb5_db_iterate(util_context, + dump->dump_princ, + (krb5_pointer) &arglist))) { + fprintf(stderr, dumprec_err, + programname, dump->name, error_message(kret)); + exit_status++; + } + if (dump->dump_policy && + (kret = osa_adb_iter_policy(policy_db, dump->dump_policy, + &arglist))) { + fprintf(stderr, dumprec_err, programname, dump->name, + error_message(kret)); + exit_status++; + } + if (ofile && !exit_status) { + fclose(f); + update_ok_file(ofile); + } + } + if (locked) + (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK); +} + +/* + * Read a string of bytes while counting the number of lines passed. + */ +static int +read_string(f, buf, len, lp) + FILE *f; + char *buf; + int len; + int *lp; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; i<len; i++) { + c = (char) fgetc(f); + if (c < 0) { + retval = 1; + break; + } + if (c == '\n') + (*lp)++; + buf[i] = (char) c; + } + buf[len] = '\0'; + return(retval); +} + +/* + * Read a string of two character representations of bytes. + */ +static int +read_octet_string(f, buf, len) + FILE *f; + krb5_octet *buf; + int len; +{ + int c; + int i, retval; + + retval = 0; + for (i=0; i<len; i++) { + if (fscanf(f, "%02x", &c) != 1) { + retval = 1; + break; + } + buf[i] = (krb5_octet) c; + } + return(retval); +} + +/* + * Find the end of an old format record. + */ +static void +find_record_end(f, fn, lineno) + FILE *f; + char *fn; + int lineno; +{ + int ch; + + if (((ch = fgetc(f)) != ';') || ((ch = fgetc(f)) != '\n')) { + fprintf(stderr, trash_end_fmt, fn, lineno); + while (ch != '\n') { + putc(ch, stderr); + ch = fgetc(f); + } + putc(ch, stderr); + } +} + +#if 0 +/* + * update_tl_data() - Generate the tl_data entries. + */ +static krb5_error_code +update_tl_data(kcontext, dbentp, mod_name, mod_date, last_pwd_change) + krb5_context kcontext; + krb5_db_entry *dbentp; + krb5_principal mod_name; + krb5_timestamp mod_date; + krb5_timestamp last_pwd_change; +{ + krb5_error_code kret; + + kret = 0 ; + + /* + * Handle modification principal. + */ + if (mod_name) { + krb5_tl_mod_princ mprinc; + + memset(&mprinc, 0, sizeof(mprinc)); + if (!(kret = krb5_copy_principal(kcontext, + mod_name, + &mprinc.mod_princ))) { + mprinc.mod_date = mod_date; + kret = krb5_dbe_encode_mod_princ_data(kcontext, + &mprinc, + dbentp); + } + if (mprinc.mod_princ) + krb5_free_principal(kcontext, mprinc.mod_princ); + } + + /* + * Handle last password change. + */ + if (!kret) { + krb5_tl_data *pwchg; + krb5_boolean linked; + + /* Find a previously existing entry */ + for (pwchg = dbentp->tl_data; + (pwchg) && (pwchg->tl_data_type != KRB5_TL_LAST_PWD_CHANGE); + pwchg = pwchg->tl_data_next); + + /* Check to see if we found one. */ + linked = 0; + if (!pwchg) { + /* No, allocate a new one */ + if ((pwchg = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) { + memset(pwchg, 0, sizeof(krb5_tl_data)); + if (!(pwchg->tl_data_contents = + (krb5_octet *) malloc(sizeof(krb5_timestamp)))) { + free(pwchg); + pwchg = (krb5_tl_data *) NULL; + } + else { + pwchg->tl_data_type = KRB5_TL_LAST_PWD_CHANGE; + pwchg->tl_data_length = + (krb5_int16) sizeof(krb5_timestamp); + } + } + } + else + linked = 1; + + /* Do we have an entry? */ + if (pwchg && pwchg->tl_data_contents) { + /* Encode it */ + krb5_kdb_encode_int32(last_pwd_change, pwchg->tl_data_contents); + /* Link it in if necessary */ + if (!linked) { + pwchg->tl_data_next = dbentp->tl_data; + dbentp->tl_data = pwchg; + dbentp->n_tl_data++; + } + } + else + kret = ENOMEM; + } + + return(kret); +} +#endif + +/* + * process_k5beta_record() - Handle a dump record in old format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta_record(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int nmatched; + int retval; + krb5_db_entry dbent; + int name_len, mod_name_len, key_len; + int alt_key_len, salt_len, alt_salt_len; + char *name; + char *mod_name; + int tmpint1, tmpint2, tmpint3; + int error; + const char *try2read; + int i; + krb5_key_data *pkey, *akey; + krb5_timestamp last_pwd_change, mod_date; + krb5_principal mod_princ; + krb5_error_code kret; + + try2read = (char *) NULL; + (*linenop)++; + retval = 1; + memset((char *)&dbent, 0, sizeof(dbent)); + + /* Make sure we've got key_data entries */ + if (krb5_dbe_create_key_data(kcontext, &dbent) || + krb5_dbe_create_key_data(kcontext, &dbent)) { + krb5_db_free_principal(kcontext, &dbent, 1); + return(1); + } + pkey = &dbent.key_data[0]; + akey = &dbent.key_data[1]; + + /* + * Match the sizes. 6 tokens to match. + */ + nmatched = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t", + &name_len, &mod_name_len, &key_len, + &alt_key_len, &salt_len, &alt_salt_len); + if (nmatched == 6) { + pkey->key_data_length[0] = key_len; + akey->key_data_length[0] = alt_key_len; + pkey->key_data_length[1] = salt_len; + akey->key_data_length[1] = alt_salt_len; + name = (char *) NULL; + mod_name = (char *) NULL; + /* + * Get the memory for the variable length fields. + */ + if ((name = (char *) malloc((size_t) (name_len + 1))) && + (mod_name = (char *) malloc((size_t) (mod_name_len + 1))) && + (!key_len || + (pkey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (key_len + 1)))) && + (!alt_key_len || + (akey->key_data_contents[0] = + (krb5_octet *) malloc((size_t) (alt_key_len + 1)))) && + (!salt_len || + (pkey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (salt_len + 1)))) && + (!alt_salt_len || + (akey->key_data_contents[1] = + (krb5_octet *) malloc((size_t) (alt_salt_len + 1)))) + ) { + error = 0; + + /* Read the principal name */ + if (read_string(filep, name, name_len, linenop)) { + try2read = read_name_string; + error++; + } + /* Read the key type */ + if (!error && (fscanf(filep, "\t%d\t", &tmpint1) != 1)) { + try2read = read_key_type; + error++; + } + pkey->key_data_type[0] = tmpint1; + /* Read the old format key */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[0], + pkey->key_data_length[0])) { + try2read = read_key_data; + error++; + } + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((pkey->key_data_length[0] > 4) + && (pkey->key_data_contents[0][0] == 0) + && (pkey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = pkey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = pkey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + pkey->key_data_length[0] = shortlen; + pkey->key_data_contents[0] = shortcopy; + } + + /* Read principal attributes */ + if (!error && (fscanf(filep, + "\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t%u\t", + &tmpint1, &dbent.max_life, + &dbent.max_renewable_life, + &tmpint2, &dbent.expiration, + &dbent.pw_expiration, &last_pwd_change, + &dbent.last_success, &dbent.last_failed, + &tmpint3) != 10)) { + try2read = read_pr_data1; + error++; + } + pkey->key_data_kvno = tmpint1; + dbent.fail_auth_count = tmpint3; + /* Read modifier name */ + if (!error && read_string(filep, + mod_name, + mod_name_len, + linenop)) { + try2read = read_mod_name; + error++; + } + /* Read second set of attributes */ + if (!error && (fscanf(filep, "\t%u\t%u\t%u\t", + &mod_date, &dbent.attributes, + &tmpint1) != 3)) { + try2read = read_pr_data2; + error++; + } + pkey->key_data_type[1] = tmpint1; + /* Read salt data */ + if (!error && read_octet_string(filep, + pkey->key_data_contents[1], + pkey->key_data_length[1])) { + try2read = read_salt_data; + error++; + } + /* Read alternate key type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_akey_type; + error++; + } + akey->key_data_type[0] = tmpint1; + /* Read alternate key */ + if (!error && read_octet_string(filep, + akey->key_data_contents[0], + akey->key_data_length[0])) { + try2read = read_akey_data; + error++; + } + + /* convert to a new format key */ + /* the encrypted version is stored as the unencrypted key length + (4 bytes, MSB first) followed by the encrypted key. */ + if ((akey->key_data_length[0] > 4) + && (akey->key_data_contents[0][0] == 0) + && (akey->key_data_contents[0][1] == 0)) { + /* this really does look like an old key, so drop and swap */ + /* the *new* length is 2 bytes, LSB first, sigh. */ + size_t shortlen = akey->key_data_length[0]-4+2; + char *shortcopy = (krb5_octet *) malloc(shortlen); + char *origdata = akey->key_data_contents[0]; + shortcopy[0] = origdata[3]; + shortcopy[1] = origdata[2]; + memcpy(shortcopy+2,origdata+4,shortlen-2); + free(origdata); + akey->key_data_length[0] = shortlen; + akey->key_data_contents[0] = shortcopy; + } + + /* Read alternate salt type */ + if (!error && (fscanf(filep, "\t%u\t", &tmpint1) != 1)) { + try2read = read_asalt_type; + error++; + } + akey->key_data_type[1] = tmpint1; + /* Read alternate salt data */ + if (!error && read_octet_string(filep, + akey->key_data_contents[1], + akey->key_data_length[1])) { + try2read = read_asalt_data; + error++; + } + /* Read expansion data - discard it */ + if (!error) { + for (i=0; i<8; i++) { + if (fscanf(filep, "\t%u", &tmpint1) != 1) { + try2read = read_exp_data; + error++; + break; + } + } + if (!error) + find_record_end(filep, fname, *linenop); + } + + /* + * If no error, then we're done reading. Now parse the names + * and store the database dbent. + */ + if (!error) { + if (!(kret = krb5_parse_name(kcontext, + name, + &dbent.princ))) { + if (!(kret = krb5_parse_name(kcontext, + mod_name, + &mod_princ))) { + if (!(kret = + krb5_dbe_update_mod_princ_data(kcontext, + &dbent, + mod_date, + mod_princ)) && + !(kret = + krb5_dbe_update_last_pwd_change(kcontext, + &dbent, + last_pwd_change))) { + int one = 1; + + dbent.len = KRB5_KDB_V1_BASE_LENGTH; + pkey->key_data_ver = (pkey->key_data_type[1] || pkey->key_data_length[1]) ? + 2 : 1; + akey->key_data_ver = (akey->key_data_type[1] || akey->key_data_length[1]) ? + 2 : 1; + if ((pkey->key_data_type[0] == + akey->key_data_type[0]) && + (pkey->key_data_type[1] == + akey->key_data_type[1])) + dbent.n_key_data--; + else if ((akey->key_data_type[0] == 0) + && (akey->key_data_length[0] == 0) + && (akey->key_data_type[1] == 0) + && (akey->key_data_length[1] == 0)) + dbent.n_key_data--; + if ((kret = krb5_db_put_principal(kcontext, + &dbent, + &one)) || + (one != 1)) { + fprintf(stderr, store_err_fmt, + fname, *linenop, name, + error_message(kret)); + error++; + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + dbent.n_key_data = 2; + } + krb5_free_principal(kcontext, mod_princ); + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, mod_name, + error_message(kret)); + error++; + } + } + else { + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + error++; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + + krb5_db_free_principal(kcontext, &dbent, 1); + if (mod_name) + free(mod_name); + if (name) + free(name); + } + else { + if (nmatched != EOF) + fprintf(stderr, rhead_err_fmt, fname, *linenop); + else + retval = -1; + } + return(retval); +} + +/* + * process_k5beta6_record() - Handle a dump record in krb5b6 format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta6_record(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int retval; + krb5_db_entry dbentry; + krb5_int32 t1, t2, t3, t4, t5, t6, t7, t8, t9; + int nread; + int error; + int i, j, one; + char *name; + krb5_key_data *kp, *kdatap; + krb5_tl_data **tlp, *tl; + krb5_octet *op; + krb5_error_code kret; + const char *try2read; + + try2read = (char *) NULL; + memset((char *) &dbentry, 0, sizeof(dbentry)); + (*linenop)++; + retval = 1; + name = (char *) NULL; + kp = (krb5_key_data *) NULL; + op = (krb5_octet *) NULL; + error = 0; + kret = 0; + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t", &t1, &t2, &t3, &t4, &t5); + if (nread == 5) { + /* Get memory for flattened principal name */ + if (!(name = (char *) malloc((size_t) t2 + 1))) + error++; + + /* Get memory for and form tagged data linked list */ + tlp = &dbentry.tl_data; + for (i=0; i<t3; i++) { + if ((*tlp = (krb5_tl_data *) malloc(sizeof(krb5_tl_data)))) { + memset(*tlp, 0, sizeof(krb5_tl_data)); + tlp = &((*tlp)->tl_data_next); + dbentry.n_tl_data++; + } + else { + error++; + break; + } + } + + /* Get memory for key list */ + if (t4 && !(kp = (krb5_key_data *) malloc((size_t) + (t4*sizeof(krb5_key_data))))) + error++; + + /* Get memory for extra data */ + if (t5 && !(op = (krb5_octet *) malloc((size_t) t5))) + error++; + + if (!error) { + dbentry.len = t1; + dbentry.n_key_data = t4; + dbentry.e_length = t5; + if (kp) { + memset(kp, 0, (size_t) (t4*sizeof(krb5_key_data))); + dbentry.key_data = kp; + kp = (krb5_key_data *) NULL; + } + if (op) { + memset(op, 0, (size_t) t5); + dbentry.e_data = op; + op = (krb5_octet *) NULL; + } + + /* Read in and parse the principal name */ + if (!read_string(filep, name, t2, linenop) && + !(kret = krb5_parse_name(kcontext, name, &dbentry.princ))) { + + /* Get the fixed principal attributes */ + nread = fscanf(filep, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t", + &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9); + if (nread == 8) { + dbentry.attributes = (krb5_flags) t2; + dbentry.max_life = (krb5_deltat) t3; + dbentry.max_renewable_life = (krb5_deltat) t4; + dbentry.expiration = (krb5_timestamp) t5; + dbentry.pw_expiration = (krb5_timestamp) t6; + dbentry.last_success = (krb5_timestamp) t7; + dbentry.last_failed = (krb5_timestamp) t8; + dbentry.fail_auth_count = (krb5_kvno) t9; + } else { + try2read = read_nint_data; + error++; + } + + /* Get the tagged data */ + if (!error && dbentry.n_tl_data) { + for (tl = dbentry.tl_data; tl; tl = tl->tl_data_next) { + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + tl->tl_data_type = (krb5_int16) t1; + tl->tl_data_length = (krb5_int16) t2; + if (tl->tl_data_length) { + if (!(tl->tl_data_contents = + (krb5_octet *) malloc((size_t) t2+1)) || + read_octet_string(filep, + tl->tl_data_contents, + t2)) { + try2read = read_tcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_tcontents; + break; + } + } + } + else { + try2read = read_ttypelen; + error++; + break; + } + } + } + + /* Get the key data */ + if (!error && dbentry.n_key_data) { + for (i=0; !error && (i<dbentry.n_key_data); i++) { + kdatap = &dbentry.key_data[i]; + nread = fscanf(filep, "%d\t%d\t", &t1, &t2); + if (nread == 2) { + kdatap->key_data_ver = (krb5_int16) t1; + kdatap->key_data_kvno = (krb5_int16) t2; + + for (j=0; j<t1; j++) { + nread = fscanf(filep, "%d\t%d\t", &t3, &t4); + if (nread == 2) { + kdatap->key_data_type[j] = t3; + kdatap->key_data_length[j] = t4; + if (t4) { + if (!(kdatap->key_data_contents[j] = + (krb5_octet *) + malloc((size_t) t4+1)) || + read_octet_string(filep, + kdatap->key_data_contents[j], + t4)) { + try2read = read_kcontents; + error++; + break; + } + } + else { + /* Should be a null field */ + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_kcontents; + break; + } + } + } + else { + try2read = read_ktypelen; + error++; + break; + } + } + } + } + } + + /* Get the extra data */ + if (!error && dbentry.e_length) { + if (read_octet_string(filep, + dbentry.e_data, + (int) dbentry.e_length)) { + try2read = read_econtents; + error++; + } + } + else { + nread = fscanf(filep, "%d", &t9); + if ((nread != 1) || (t9 != -1)) { + error++; + try2read = read_econtents; + } + } + + /* Finally, find the end of the record. */ + if (!error) + find_record_end(filep, fname, *linenop); + + /* + * We have either read in all the data or choked. + */ + if (!error) { + one = 1; + if ((kret = krb5_db_put_principal(kcontext, + &dbentry, + &one))) { + fprintf(stderr, store_err_fmt, + fname, *linenop, + name, error_message(kret)); + } + else { + if (verbose) + fprintf(stderr, add_princ_fmt, name); + retval = 0; + } + } + else { + fprintf(stderr, read_err_fmt, fname, *linenop, try2read); + } + } + else { + if (kret) + fprintf(stderr, parse_err_fmt, + fname, *linenop, name, error_message(kret)); + else + fprintf(stderr, no_mem_fmt, fname, *linenop); + } + } + else { + fprintf(stderr, rhead_err_fmt, fname, *linenop); + } + + if (op) + free(op); + if (kp) + free(kp); + if (name) + free(name); + krb5_db_free_principal(kcontext, &dbentry, 1); + } + else { + if (nread == EOF) + retval = -1; + } + return(retval); +} + +int process_k5beta7_policy(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + osa_policy_ent_rec rec; + char namebuf[1024]; + int nread, ret; + + (*linenop)++; + rec.name = namebuf; + + nread = fscanf(filep, "%1024s\t%d\t%d\t%d\t%d\t%d\t%d", rec.name, + &rec.pw_min_life, &rec.pw_max_life, + &rec.pw_min_length, &rec.pw_min_classes, + &rec.pw_history_num, &rec.policy_refcnt); + if (nread == EOF) + return -1; + else if (nread != 7) { + fprintf(stderr, "cannot parse policy on line %d (%d read)\n", + *linenop, nread); + return 1; + } + + if (ret = osa_adb_create_policy(pol_db, &rec)) { + fprintf(stderr, "cannot create policy on line %d: %s\n", + *linenop, error_message(ret)); + return 1; + } + if (verbose) + fprintf(stderr, "created policy %s\n", rec.name); + + return 0; +} + + +/* + * process_k5beta7_record() - Handle a dump record in krb5b6 format. + * + * Returns -1 for end of file, 0 for success and 1 for failure. + */ +static int +process_k5beta7_record(fname, kcontext, filep, verbose, linenop, pol_db) + char *fname; + krb5_context kcontext; + FILE *filep; + int verbose; + int *linenop; + void *pol_db; +{ + int nread; + char rectype[100]; + + nread = fscanf(filep, "%100s\t", rectype); + if (nread == EOF) + return -1; + else if (nread != 1) + return 1; + if (strcmp(rectype, "princ") == 0) + process_k5beta6_record(fname, kcontext, filep, verbose, + linenop, pol_db); + else if (strcmp(rectype, "policy") == 0) + process_k5beta7_policy(fname, kcontext, filep, verbose, + linenop, pol_db); + else { + fprintf(stderr, "unknown record type \"%s\" on line %d\n", + rectype, *linenop); + return 1; + } + + return 0; +} + +/* + * restore_dump() - Restore the database from any version dump file. + */ +static int +restore_dump(programname, kcontext, dumpfile, f, verbose, dump, pol_db) + char *programname; + krb5_context kcontext; + char *dumpfile; + FILE *f; + int verbose; + dump_version *dump; + osa_adb_policy_t pol_db; +{ + int error; + int lineno; + + error = 0; + lineno = 1; + + /* + * Process the records. + */ + while (!(error = (*dump->load_record)(dumpfile, + kcontext, + f, + verbose, + &lineno, + pol_db))) + ; + if (error != -1) + fprintf(stderr, err_line_fmt, programname, lineno, dumpfile); + else + error = 0; + + return(error); +} + +/* + * Usage is + * load_db [-old] [-verbose] [-update] filename dbname + */ +void +load_db(argc, argv) + int argc; + char **argv; +{ + kadm5_config_params newparams; + osa_adb_policy_t pol_db; + krb5_error_code kret; + krb5_context kcontext; + FILE *f; + extern char *optarg; + extern int optind; + char *programname; + char *dumpfile; + char *dbname, *adbname; + char *dbname_tmp, *adbname_real; + char buf[BUFSIZ]; + dump_version *load; + int update, verbose; + int aindex; + + /* + * Parse the arguments. + */ + programname = argv[0]; + if (strrchr(programname, (int) '/')) + programname = strrchr(argv[0], (int) '/') + 1; + dumpfile = (char *) NULL; + dbname = (char *) NULL; + load = NULL; + update = 0; + verbose = 0; + exit_status = 0; + dbname_tmp = (char *) NULL; + for (aindex = 1; aindex < argc; aindex++) { + if (!strcmp(argv[aindex], oldoption)) + load = &old_version; + else if (!strcmp(argv[aindex], b6option)) + load = &beta6_version; + else if (!strcmp(argv[aindex], verboseoption)) + verbose = 1; + else if (!strcmp(argv[aindex], updateoption)) + update = 1; + else + break; + } + if ((argc - aindex) != 2 && (argc - aindex) != 3) { + fprintf(stderr, lusage_err_fmt, argv[0], argv[0], + oldoption, verboseoption, updateoption); + exit_status++; + return; + } + + dumpfile = argv[aindex]; + dbname = argv[aindex+1]; + adbname = argv[aindex+2]; + if (!(dbname_tmp = (char *) malloc(strlen(dbname)+ + strlen(dump_tmptrail)+1))) { + fprintf(stderr, no_name_mem_fmt, argv[0]); + exit_status++; + return; + } + strcpy(dbname_tmp, dbname); + strcat(dbname_tmp, dump_tmptrail); + + /* + * Initialize the Kerberos context and error tables. + */ + if ((kret = krb5_init_context(&kcontext))) { + fprintf(stderr, ctx_err_fmt, programname); + free(dbname_tmp); + exit_status++; + return; + } + krb5_init_ets(kcontext); + + /* + * Open the dumpfile + */ + if (dumpfile) { + if ((f = fopen(dumpfile, "r+")) == NULL) { + fprintf(stderr, dfile_err_fmt, programname, dumpfile, + error_message(errno)); + exit_status++; + return; + } + if (kret = krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_SHARED)) { + fprintf(stderr, "%s: Cannot lock %s: %s\n", programname, + dumpfile, error_message(errno)); + exit_status++; + return; + } + } else + f = stdin; + + /* + * Auto-detect dump version if we weren't told, verify if we + * were told. + */ + fgets(buf, sizeof(buf), f); + if (load) { + if (strcmp(buf, load->header) != 0) { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + exit_status++; + if (dumpfile) fclose(f); + return; + } + } else { + /* perhaps this should be in an array, but so what? */ + if (strcmp(buf, old_version.header) == 0) + load = &old_version; + else if (strcmp(buf, beta6_version.header) == 0) + load = &beta6_version; + else if (strcmp(buf, beta7_version.header) == 0) + load = &beta7_version; + else { + fprintf(stderr, head_bad_fmt, programname, dumpfile); + exit_status++; + if (dumpfile) fclose(f); + return; + } + + if (load != &beta7_version && adbname != NULL) { + fprintf(stderr, lusage_err_fmt, argv[0], argv[0], + oldoption, verboseoption, updateoption); + exit_status++; + return; + } + } + + /* + * Cons up config params for new policy database. Use adbname is + * specified, otherwise let the policy dbname key off the dbname. + * However, after the name is retrieved, change the actual file + * name to a temp name that we'll rename later (but use the + * correct lock file). + */ + newparams = global_params; + newparams.mask &= ~(KADM5_CONFIG_ADBNAME | KADM5_CONFIG_ADB_LOCKFILE); + newparams.dbname = dbname; + newparams.mask |= KADM5_CONFIG_DBNAME; + if (adbname) { + newparams.admin_dbname = adbname; + newparams.mask |= KADM5_CONFIG_ADBNAME; + } + if (kret = kadm5_get_config_params(kcontext, NULL, NULL, &newparams, + &newparams)) { + fprintf(stderr, "%s while retrieiving configuration " + "parameters.\n", error_message(kret)); + if (dumpfile) fclose(f); + exit_status++; + return; + } + adbname_real = newparams.admin_dbname; + newparams.admin_dbname = (char *) malloc(strlen(adbname_real) + + strlen(dump_tmptrail) + 1); + strcpy(newparams.admin_dbname, adbname_real); + strcat(newparams.admin_dbname, dump_tmptrail); + + /* + * Create the new database if not an update restoration. Always + * create the policy db, even if we are not loading a dump file + * with policy info, because they may be loading an old dump + * intending to use it with the new kadm5 system (ie: using load + * as create). + */ + if (!update && (kret = krb5_db_create(kcontext, dbname_tmp))) { + fprintf(stderr, dbcreaterr_fmt, + programname, dbname, error_message(kret)); + exit_status++; + kadm5_free_config_params(kcontext, &newparams); + if (dumpfile) fclose(f); + return; + } + if (!update && (kret = osa_adb_create_policy_db(&newparams))) { + fprintf(stderr, "%s: %s while creating policy database\n", + programname, error_message(kret)); + kadm5_free_config_params(kcontext, &newparams); + if (dumpfile) fclose(f); + return; + } + + /* + * Point ourselves at the new databases. + */ + if (kret = krb5_db_set_name(kcontext, + (update) ? dbname : dbname_tmp)) { + fprintf(stderr, dbname_err_fmt, + programname, + (update) ? dbname : dbname_tmp, error_message(kret)); + exit_status++; + goto error; + } + if (kret = osa_adb_open_policy(&pol_db, &newparams)) { + fprintf(stderr, "%s: %s while opening policy database\n", + programname, error_message(kret)); + exit_status++; + goto error; + } + + /* + * Initialize the database. + */ + if (kret = krb5_db_init(kcontext)) { + fprintf(stderr, dbinit_err_fmt, + programname, error_message(kret)); + exit_status++; + goto error; + } + + if (restore_dump(programname, kcontext, (dumpfile) ? dumpfile : stdin_name, + f, verbose, load, pol_db)) { + fprintf(stderr, restfail_fmt, + programname, load->name); + exit_status++; + } + + if ((kret = krb5_db_fini(kcontext)) || + (kret = osa_adb_close_policy(pol_db))) { + fprintf(stderr, close_err_fmt, + programname, error_message(kret)); + exit_status++; + } + +error: + /* + * If there was an error and this is not an update, then + * destroy the database. + */ + if (!update) { + if (exit_status) { + if ((kret = kdb5_db_destroy(kcontext, dbname))) { + fprintf(stderr, dbdelerr_fmt, + programname, dbname_tmp, error_message(kret)); + exit_status++; + } + } + else { + if ((kret = krb5_db_rename(kcontext, + dbname_tmp, + dbname))) { + fprintf(stderr, dbrenerr_fmt, + programname, dbname_tmp, dbname, + error_message(kret)); + exit_status++; + } + + if (kret = osa_adb_get_lock(pol_db, OSA_ADB_PERMANENT)) { + fprintf(stderr, + "%s: %s while getting permanent lock\n", + programname, error_message(kret)); + exit_status++; + } else if (rename(newparams.admin_dbname, + adbname_real) < 0) { + fprintf(stderr, "%s: %s while renaming %s to %s\n", + programname, error_message(kret), + newparams.admin_dbname, adbname_real); + exit_status++; + } else if (kret = osa_adb_release_lock(pol_db)) { + fprintf(stderr, "%s: %s while unlocking %s\n", + programname, error_message(kret), + adbname_real); + exit_status++; + } + } + } + + if (dumpfile) { + (void) krb5_lock_file(kcontext, fileno(f), KRB5_LOCKMODE_UNLOCK); + fclose(f); + } + + free(newparams.admin_dbname); + newparams.admin_dbname = adbname_real; + kadm5_free_config_params(kcontext, &newparams); + free(dbname_tmp); + krb5_free_context(kcontext); +} diff --git a/src/kadmin/dbutil/dumpv4.c b/src/kadmin/dbutil/dumpv4.c new file mode 100644 index 000000000..a51626db3 --- /dev/null +++ b/src/kadmin/dbutil/dumpv4.c @@ -0,0 +1,411 @@ +/* + * admin/edit/dumpv4.c + * + * Copyright 1990,1991, 1994 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Dump a KDC database into a V4 slave dump. + */ + +#ifdef KRB5_KRB4_COMPAT + +#include "k5-int.h" +#include "com_err.h" + +#include <des.h> +#include <krb.h> +#include <krb_db.h> +/* MKEYFILE is now defined in kdc.h */ +#include <kdc.h> + +#include <stdio.h> +#include "kdb5_util.h" + +struct dump_record { + char *comerr_name; + FILE *f; + krb5_encrypt_block *v5master; + C_Block v4_master_key; + Key_schedule v4_master_key_schedule; + long master_key_version; + char *realm; +}; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern krb5_boolean dbactive; +extern int exit_status; +extern krb5_context util_context; + +void update_ok_file(); + +#define ANAME_SZ 40 +#define INST_SZ 40 + +static char *v4_mkeyfile = "/.k"; + +static int +v4init(arg, manual) + struct dump_record *arg; + int manual; +{ + int fd; + int ok = 0; + + if (!manual) { + fd = open(v4_mkeyfile, O_RDONLY, 0600); + if (fd >= 0) { + if (read(fd,arg->v4_master_key,sizeof(C_Block)) == sizeof(C_Block)) + ok = 1; + close(fd); + } + } + if (!ok) { + des_read_password(arg->v4_master_key, "V4 Kerberos master key: ", 1); + printf("\n"); + } + arg->master_key_version = 1; + key_sched(arg->v4_master_key, arg->v4_master_key_schedule); + + return 0; +} + +v4_print_time(file, timeval) + FILE *file; + unsigned long timeval; +{ + struct tm *tm; + struct tm *gmtime(); + tm = gmtime((time_t *)&timeval); + fprintf(file, " %04d%02d%02d%02d%02d", + tm->tm_year < 1900 ? tm->tm_year + 1900: tm->tm_year, + tm->tm_mon + 1, + tm->tm_mday, + tm->tm_hour, + tm->tm_min); +} + + + +krb5_error_code +dump_v4_iterator(ptr, entry) + krb5_pointer ptr; + krb5_db_entry *entry; +{ + struct dump_record *arg = (struct dump_record *) ptr; + krb5_principal mod_princ; + krb5_timestamp mod_time; + krb5_error_code retval; + int i, max_kvno, ok_key; + + struct v4princ { + char name[ANAME_SZ+1]; + char instance[INST_SZ+1]; + char realm[REALM_SZ+1]; + int max_life; + int kdc_key_ver, key_version, attributes; + char mod_name[ANAME_SZ+1]; + char mod_instance[INST_SZ+1]; + char mod_realm[REALM_SZ+1]; + } v4princ, *principal; + des_cblock v4key; + + principal = &v4princ; + + if (strcmp(krb5_princ_realm(util_context, entry->princ)->data, arg->realm)) + /* skip this because it's a key for a different realm, probably + * a paired krbtgt key */ + return 0; + + retval = krb5_524_conv_principal(util_context, entry->princ, + principal->name, principal->instance, + principal->realm); + if (retval) + /* Skip invalid V4 principals */ + return 0; + + if (!strcmp(principal->name, "K") && !strcmp(principal->instance, "M")) + /* The V4 master key is handled specially */ + return 0; + + if (! principal->name[0]) + return 0; + if (! principal->instance[0]) + strcpy(principal->instance, "*"); + + /* Now move to mod princ */ + if (retval = krb5_dbe_lookup_mod_princ_data(util_context,entry, + &mod_time, &mod_princ)){ + com_err(arg->comerr_name, retval, "while unparsing db entry"); + exit_status++; + return retval; + } + retval = krb5_524_conv_principal(util_context, mod_princ, + principal->mod_name, principal->mod_instance, + principal->mod_realm); + if (retval) { + /* Invalid V4 mod principal */ + principal->mod_name[0] = '\0'; + principal->mod_instance[0] = '\0'; + } + + if (! principal->mod_name[0]) + strcpy(principal->mod_name, "*"); + if (! principal->mod_instance[0]) + strcpy(principal->mod_instance, "*"); + + /* OK deal with the key now. */ + for (max_kvno = i = 0; i < entry->n_key_data; i++) { + if (max_kvno < entry->key_data[i].key_data_kvno) { + max_kvno = entry->key_data[i].key_data_kvno; + ok_key = i; + } + } + + i = ok_key; + while (ok_key < entry->n_key_data) { + if (max_kvno == entry->key_data[ok_key].key_data_kvno) { + if (entry->key_data[ok_key].key_data_type[1] + == KRB5_KDB_SALTTYPE_V4) { + goto found_one; + } + } + ok_key++; + } + + /* See if there are any DES keys that may be suitable */ + ok_key = i; + while (ok_key < entry->n_key_data) { + if (max_kvno == entry->key_data[ok_key].key_data_kvno) { + krb5_enctype enctype = entry->key_data[ok_key].key_data_type[0]; + if ((enctype == ENCTYPE_DES_CBC_CRC) || + (enctype == ENCTYPE_DES_CBC_MD5) || + (enctype == ENCTYPE_DES_CBC_RAW)) + goto found_one; + } + ok_key++; + } + /* skip this because it's a new style key and we can't help it */ + return 0; + +found_one:; + principal->key_version = max_kvno; + if ((principal->max_life = entry->max_life / (60 * 5)) > 255) + principal->max_life = 255; + principal->kdc_key_ver = arg->master_key_version; + principal->attributes = 0; /* ??? not preserved either */ + + fprintf(arg->f, "%s %s %d %d %d %d ", + principal->name, + principal->instance, + principal->max_life, + principal->kdc_key_ver, + principal->key_version, + principal->attributes); + + handle_one_key(arg, arg->v5master, &entry->key_data[ok_key], v4key); + + for (i = 0; i < 8; i++) { + fprintf(arg->f, "%02x", ((unsigned char*)v4key)[i]); + if (i == 3) fputc(' ', arg->f); + } + + v4_print_time(arg->f, entry->expiration); + v4_print_time(arg->f, mod_time); + + fprintf(arg->f, " %s %s\n", principal->mod_name, principal->mod_instance); + return 0; +} + +/*ARGSUSED*/ +void dump_v4db(argc, argv) + int argc; + char **argv; +{ + FILE *f; + struct dump_record arg; + + if (argc > 2) { + com_err(argv[0], 0, "Usage: %s filename", argv[0]); + exit_status++; + return; + } + if (!dbactive) { + com_err(argv[0], 0, Err_no_database); + exit_status++; + return; + } + if (argc == 2) { + /* + * Make sure that we don't open and truncate on the fopen, + * since that may hose an on-going kprop process. + * + * We could also control this by opening for read and + * write, doing an flock with LOCK_EX, and then + * truncating the file once we have gotten the lock, + * but that would involve more OS dependancies than I + * want to get into. + */ + unlink(argv[1]); + if (!(f = fopen(argv[1], "w"))) { + com_err(argv[0], errno, + "While opening file %s for writing", argv[1]); + exit_status++; + return; + } + } else { + f = stdout; + } + + arg.comerr_name = argv[0]; + arg.f = f; + v4init(&arg, 0); + handle_keys(&arg); + + /* special handling for K.M since it isn't preserved */ + { + des_cblock v4key; + int i; + + /* assume: + max lifetime (255) + key version == 1 (actually, should be whatever the v5 one is) + master key version == key version + args == 0 (none are preserved) + expiration date is the default 2000 + last mod time is near zero (arbitrarily.) + creator is db_creation * + */ + + fprintf(f,"K M 255 1 1 0 "); + +#ifndef KDB4_DISABLE + kdb_encrypt_key (arg.v4_master_key, v4key, + arg.v4_master_key, arg.v4_master_key_schedule, + ENCRYPT); +#else /* KDB4_DISABLE */ + pcbc_encrypt((C_Block *) arg.v4_master_key, + (C_Block *) v4key, + (long) sizeof(C_Block), + arg.v4_master_key_schedule, + (C_Block *) arg.v4_master_key, + ENCRYPT); +#endif /* KDB4_DISABLE */ + + for (i=0; i<8; i++) { + fprintf(f, "%02x", ((unsigned char*)v4key)[i]); + if (i == 3) fputc(' ', f); + } + fprintf(f," 200001010459 197001020000 db_creation *\n"); + } + + (void) krb5_db_iterate(util_context, dump_v4_iterator, + (krb5_pointer) &arg); + if (argc == 2) + fclose(f); + if (argv[1]) + update_ok_file(argv[1]); +} + +int handle_keys(arg) + struct dump_record *arg; +{ + krb5_error_code retval; + char *defrealm; + char *mkey_name = 0; + char *mkey_fullname; + krb5_principal master_princ; + + if (retval = krb5_get_default_realm(util_context, &defrealm)) { + com_err(arg->comerr_name, retval, + "while retrieving default realm name"); + exit(1); + } + arg->realm = defrealm; + + /* assemble & parse the master key name */ + + if (retval = krb5_db_setup_mkey_name(util_context, mkey_name, arg->realm, + &mkey_fullname, &master_princ)) { + com_err(arg->comerr_name, retval, "while setting up master key name"); + exit(1); + } + + krb5_use_enctype(util_context, &master_encblock, DEFAULT_KDC_ENCTYPE); + if (retval = krb5_db_fetch_mkey(util_context, master_princ, + &master_encblock, 0, + 0, (char *) NULL, 0, &master_keyblock)) { + com_err(arg->comerr_name, retval, "while reading master key"); + exit(1); + } + if (retval = krb5_process_key(util_context, &master_encblock, + &master_keyblock)) { + com_err(arg->comerr_name, retval, "while processing master key"); + exit(1); + } + arg->v5master = &master_encblock; + return(0); +} + +handle_one_key(arg, v5master, v5key, v4key) + struct dump_record *arg; + krb5_encrypt_block *v5master; + krb5_key_data *v5key; + des_cblock v4key; +{ + krb5_error_code retval; + + krb5_keyblock v4v5key; + krb5_keyblock v5plainkey; + /* v4key is the actual v4 key from the file. */ + + if (retval = krb5_dbekd_decrypt_key_data(util_context, v5master, v5key, + &v5plainkey, NULL)) + return retval; + + /* v4v5key.contents = (krb5_octet *)v4key; */ + /* v4v5key.enctype = ENCTYPE_DES; */ + /* v4v5key.length = sizeof(v4key); */ + + memcpy(v4key, v5plainkey.contents, sizeof(des_cblock)); +#ifndef KDB4_DISABLE + kdb_encrypt_key (v4key, v4key, + arg->v4_master_key, arg->v4_master_key_schedule, + ENCRYPT); +#else /* KDB4_DISABLE */ + pcbc_encrypt((C_Block *) v4key, + (C_Block *) v4key, + (long) sizeof(C_Block), + arg->v4_master_key_schedule, + (C_Block *) arg->v4_master_key, + ENCRYPT); +#endif /* KDB4_DISABLE */ + return 0; +} + +#else /* KRB5_KRB4_COMPAT */ +void dump_v4db(argc, argv) + int argc; + char **argv; +{ + printf("This version of krb5_edit does not support the V4 dump command.\n"); +} +#endif /* KRB5_KRB4_COMPAT */ diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c new file mode 100644 index 000000000..d31ce3319 --- /dev/null +++ b/src/kadmin/dbutil/kadm5_create.c @@ -0,0 +1,241 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include "string_table.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <kadm5/adb.h> +#include <kadm5/admin.h> + +#include <krb5.h> +#include <krb5/kdb.h> + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime); + +#define ERR 1 +#define OK 0 + +#define ADMIN_LIFETIME 60*60*3 /* 3 hours */ +#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */ + +extern char *whoami; + +extern krb5_encrypt_block master_encblock; +extern krb5_keyblock master_keyblock; +extern krb5_db_entry master_db; + +/* + * Function: kadm5_create + * + * Purpose: create admin principals in KDC database + * + * Arguments: params (r) configuration parameters to use + * + * Effects: Creates KADM5_ADMIN_SERVICE and KADM5_CHANGEPW_SERVICE + * principals in the KDC database and sets their attributes + * appropriately. + */ +int kadm5_create(kadm5_config_params *params) +{ + int retval; + void *handle; + krb5_context context; + FILE *f; + + + if (retval = krb5_init_context(&context)) + exit(ERR); + + /* + * The lock file has to exist before calling kadm5_init, but + * params->admin_lockfile may not be set yet... + */ + if (retval = kadm5_get_config_params(context, NULL, NULL, + params, params)) { + com_err(whoami, retval, str_INITING_KCONTEXT); + return 1; + } + + if (retval = osa_adb_create_policy_db(params)) { + com_err(whoami, retval, str_CREATING_POLICY_DB); + return 1; + } + + if ((retval = kadm5_init(whoami, NULL, NULL, params, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle))) { + com_err(whoami, retval, str_INITING_KCONTEXT); + + krb5_free_context(context); + exit(ERR); + } + + retval = add_admin_princs(handle, context, params->realm); + + kadm5_destroy(handle); + krb5_free_context(context); + + if (retval) + exit(retval); + + return 0; +} + +/* + * Function: build_name_with_realm + * + * Purpose: concatenate a name and a realm to form a krb5 name + * + * Arguments: + * + * name (input) the name + * realm (input) the realm + * + * Returns: + * + * pointer to name@realm, in allocated memory, or NULL if it + * cannot be allocated + * + * Requires: both strings are null-terminated + */ +char *build_name_with_realm(char *name, char *realm) +{ + char *n; + + n = (char *) malloc(strlen(name) + strlen(realm) + 2); + sprintf(n, "%s@%s", name, realm); + return n; +} + +/* + * Function: add_admin_princs + * + * Purpose: create admin principals + * + * Arguments: + * + * rseed (input) random seed + * realm (input) realm, or NULL for default realm + * <return value> (output) status, 0 for success, 1 for serious error + * + * Requires: + * + * Effects: + * + * add_admin_princs creates KADM5_ADMIN_SERVICE, + * KADM5_CHANGEPW_SERVICE. If any of these exist a message is + * printed. If any of these existing principal do not have the proper + * attributes, a warning message is printed. + */ +int add_admin_princs(void *handle, krb5_context context, char *realm) +{ + krb5_error_code ret = 0; + + if ((ret = add_admin_princ(handle, context, + KADM5_ADMIN_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED, + ADMIN_LIFETIME))) + goto clean_and_exit; + + if ((ret = add_admin_princ(handle, context, + KADM5_CHANGEPW_SERVICE, realm, + KRB5_KDB_DISALLOW_TGT_BASED | + KRB5_KDB_PWCHANGE_SERVICE, + CHANGEPW_LIFETIME))) + goto clean_and_exit; + +clean_and_exit: + + return ret; +} + +/* + * Function: add_admin_princ + * + * Arguments: + * + * creator (r) principal to use as "mod_by" + * rseed (r) seed for random key generator + * name (r) principal name + * realm (r) realm name for principal + * attrs (r) principal's attributes + * lifetime (r) principal's max life, or 0 + * not_unique (r) error message for multiple entries, never used + * exists (r) warning message for principal exists + * wrong_attrs (r) warning message for wrong attributes + * + * Returns: + * + * OK on success + * ERR on serious errors + * + * Effects: + * + * If the principal is not unique, not_unique is printed (but this + * never happens). If the principal exists, then exists is printed + * and if the principals attributes != attrs, wrong_attrs is printed. + * Otherwise, the principal is created with mod_by creator and + * attributes attrs and max life of lifetime (if not zero). + */ + +int add_admin_princ(void *handle, krb5_context context, + char *name, char *realm, int attrs, int lifetime) +{ + char *fullname; + int nprincs; + krb5_error_code ret; + kadm5_principal_ent_rec ent; + + memset(&ent, 0, sizeof(ent)); + + fullname = build_name_with_realm(name, realm); + if (ret = krb5_parse_name(context, fullname, &ent.principal)) { + com_err(whoami, ret, str_PARSE_NAME); + return(ERR); + } + ent.max_life = lifetime; + ent.attributes = attrs; + + if (ret = kadm5_create_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES), + "to-be-random")) { + if (ret == KADM5_DUP) + ret = kadm5_modify_principal(handle, &ent, + (KADM5_PRINCIPAL | + KADM5_MAX_LIFE | + KADM5_ATTRIBUTES)); + + if (ret) { + com_err(whoami, ret, str_PUT_PRINC, fullname); + krb5_free_principal(context, ent.principal); + free(fullname); + return ERR; + } + } + + ret = kadm5_randkey_principal(handle, ent.principal, NULL, NULL); + + krb5_free_principal(context, ent.principal); + free(fullname); + + if (ret) { + com_err(whoami, ret, str_RANDOM_KEY, fullname); + return ERR; + } + + return OK; +} diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c new file mode 100644 index 000000000..b7520d98d --- /dev/null +++ b/src/kadmin/dbutil/kdb5_create.c @@ -0,0 +1,449 @@ +/* + * admin/create/kdb5_create.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Generate (from scratch) a Kerberos KDC database. + */ + +#include <stdio.h> +#include <k5-int.h> +#include <kadm5/admin.h> +#include <kadm5/adb.h> + +enum ap_op { + NULL_KEY, /* setup null keys */ + MASTER_KEY, /* use master key as new key */ + TGT_KEY /* special handling for tgt key */ +}; + +krb5_key_salt_tuple def_kslist = { ENCTYPE_DES_CBC_CRC, KRB5_KDB_SALTTYPE_NORMAL }; + +struct realm_info { + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_encrypt_block *eblock; + krb5_pointer rseed; + krb5_int32 nkslist; + krb5_key_salt_tuple *kslist; +} rblock = { /* XXX */ + KRB5_KDB_MAX_LIFE, + KRB5_KDB_MAX_RLIFE, + KRB5_KDB_EXPIRATION, + KRB5_KDB_DEF_FLAGS, + (krb5_encrypt_block *) NULL, + (krb5_pointer) NULL, + 1, + &def_kslist +}; + +struct iterate_args { + krb5_context ctx; + struct realm_info *rblock; + krb5_db_entry *dbentp; +}; + +static krb5_error_code add_principal + PROTOTYPE((krb5_context, + krb5_principal, + enum ap_op, + struct realm_info *)); + +/* + * Steps in creating a database: + * + * 1) use the db calls to open/create a new database + * + * 2) get a realm name for the new db + * + * 3) get a master password for the new db; convert to an encryption key. + * + * 4) create various required entries in the database + * + * 5) close & exit + */ + +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern krb5_encrypt_block master_encblock; +krb5_data master_salt; + +krb5_data tgt_princ_entries[] = { + {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, + {0, 0, 0} }; + +krb5_data db_creator_entries[] = { + {0, sizeof("db_creation")-1, "db_creation"} }; + +/* XXX knows about contents of krb5_principal, and that tgt names + are of form TGT/REALM@REALM */ +krb5_principal_data tgt_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + tgt_princ_entries, /* krb5_data *data */ + 2, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +krb5_principal_data db_create_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + db_creator_entries, /* krb5_data *data */ + 1, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +static char *mkey_password = 0; +char *whoami; + +extern int exit_status; +extern osa_adb_policy_t policy_db; +extern kadm5_config_params global_params; +extern krb5_context util_context; + +static void usage() +{ + fprintf(stderr, "usage: %s [-s]\n", whoami); + exit_status++; +} + +void kdb5_create(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int optchar; + + krb5_error_code retval; + char *mkey_fullname; + char *pw_str = 0; + int pw_size = 0; + int do_stash = 0; + krb5_data pwd; + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + whoami = argv[0]; + + mkey_password = NULL; + optind = 1; + while ((optchar = getopt(argc, argv, "P:s")) != EOF) { + switch(optchar) { + case 's': + do_stash++; + break; + case 'P': /* Only used for testing!!! */ + mkey_password = optarg; + break; + case '?': + default: + usage(); + return; + } + } + + rblock.max_life = global_params.max_life; + rblock.max_rlife = global_params.max_rlife; + rblock.expiration = global_params.expiration; + rblock.flags = global_params.flags; + rblock.nkslist = global_params.num_keysalts; + rblock.kslist = global_params.keysalts; + + krb5_use_enctype(util_context, &master_encblock, master_keyblock.enctype); + + retval = krb5_db_set_name(util_context, global_params.dbname); + if (!retval) retval = EEXIST; + + if (retval == EEXIST || retval == EACCES || retval == EPERM) { + /* it exists ! */ + com_err(argv[0], 0, "The database '%s' appears to already exist", + global_params.dbname); + exit_status++; return; + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(util_context, + global_params.mkey_name, + global_params.realm, + &mkey_fullname, &master_princ))) { + com_err(argv[0], retval, "while setting up master key name"); + exit_status++; return; + } + + krb5_princ_set_realm_data(util_context, &db_create_princ, global_params.realm); + krb5_princ_set_realm_length(util_context, &db_create_princ, strlen(global_params.realm)); + krb5_princ_set_realm_data(util_context, &tgt_princ, global_params.realm); + krb5_princ_set_realm_length(util_context, &tgt_princ, strlen(global_params.realm)); + krb5_princ_component(util_context, &tgt_princ,1)->data = global_params.realm; + krb5_princ_component(util_context, &tgt_princ,1)->length = strlen(global_params.realm); + + printf("Initializing database '%s' for realm '%s',\n\ +master key name '%s'\n", + global_params.dbname, global_params.realm, mkey_fullname); + + if (!mkey_password) { + printf("You will be prompted for the database Master Password.\n"); + printf("It is important that you NOT FORGET this password.\n"); + fflush(stdout); + + pw_size = 1024; + pw_str = malloc(pw_size); + + retval = krb5_read_password(util_context, KRB5_KDC_MKEY_1, KRB5_KDC_MKEY_2, + pw_str, &pw_size); + if (retval) { + com_err(argv[0], retval, "while reading master key from keyboard"); + exit_status++; return; + } + mkey_password = pw_str; + } + + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(util_context, master_princ, &master_salt); + if (retval) { + com_err(argv[0], retval, "while calculated master key salt"); + exit_status++; return; + } + if (retval = krb5_string_to_key(util_context, &master_encblock, + &master_keyblock, &pwd, &master_salt)) { + com_err(argv[0], retval, "while transforming master key from password"); + exit_status++; return; + } + + if ((retval = krb5_process_key(util_context, &master_encblock, + &master_keyblock))) { + com_err(argv[0], retval, "while processing master key"); + exit_status++; return; + } + + rblock.eblock = &master_encblock; + if ((retval = krb5_init_random_key(util_context, &master_encblock, + &master_keyblock, &rblock.rseed))) { + com_err(argv[0], retval, "while initializing random key generator"); + (void) krb5_finish_key(util_context, &master_encblock); + exit_status++; return; + } + if ((retval = krb5_db_create(util_context, global_params.dbname))) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while creating database '%s'", + global_params.dbname); + exit_status++; return; + } + if (retval = krb5_db_fini(util_context)) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, + &rblock.rseed); + com_err(argv[0], retval, "while closing current database"); + exit_status++; return; + } + if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while setting active database to '%s'", + global_params.dbname); + exit_status++; return; + } + if ((retval = krb5_db_init(util_context))) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while initializing the database '%s'", + global_params.dbname); + exit_status++; return; + } + + if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) || + (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) { + (void) krb5_db_fini(util_context); + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + com_err(argv[0], retval, "while adding entries to the database"); + exit_status++; return; + } + /* + * Always stash the master key so kadm5_create does not prompt for + * it; delete the file below if it was not requested. DO NOT EXIT + * BEFORE DELETING THE KEYFILE if do_stash is not set. + */ + if (retval = krb5_db_store_mkey(util_context, + global_params.stash_file, + master_princ, + &master_keyblock)) { + com_err(argv[0], errno, "while storing key"); + printf("Warning: couldn't stash master key.\n"); + } + /* clean up */ + (void) krb5_db_fini(util_context); + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, &rblock.rseed); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + free(master_keyblock.contents); + if (pw_str) { + memset(pw_str, 0, pw_size); + free(pw_str); + } + free(master_salt.data); + + if (kadm5_create(&global_params)) { + if (!do_stash) unlink(global_params.stash_file); + exit_status++; + return; + } + if (!do_stash) unlink(global_params.stash_file); + + /* now open the database */ + open_db_and_mkey(); + + return; + +} + +static krb5_error_code +tgt_keysalt_iterate(ksent, ptr) + krb5_key_salt_tuple *ksent; + krb5_pointer ptr; +{ + krb5_context context; + krb5_error_code kret; + struct iterate_args *iargs; + krb5_keyblock random_keyblock, *key; + krb5_int32 ind; + krb5_encrypt_block random_encblock; + krb5_pointer rseed; + krb5_data pwd; + + iargs = (struct iterate_args *) ptr; + kret = 0; + + context = iargs->ctx; + + /* + * Convert the master key password into a key for this particular + * encryption system. + */ + krb5_use_enctype(context, &random_encblock, ksent->ks_enctype); + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + if (kret = krb5_string_to_key(context, &random_encblock, &random_keyblock, + &pwd, &master_salt)) + return kret; + if ((kret = krb5_init_random_key(context, &random_encblock, + &random_keyblock, &rseed))) + return kret; + + if (!(kret = krb5_dbe_create_key_data(iargs->ctx, iargs->dbentp))) { + ind = iargs->dbentp->n_key_data-1; + if (!(kret = krb5_random_key(context, + &random_encblock, rseed, + &key))) { + kret = krb5_dbekd_encrypt_key_data(context, + iargs->rblock->eblock, + key, + NULL, + 1, + &iargs->dbentp->key_data[ind]); + krb5_free_keyblock(context, key); + } + } + memset((char *)random_keyblock.contents, 0, random_keyblock.length); + free(random_keyblock.contents); + (void) krb5_finish_random_key(context, &random_encblock, &rseed); + return(kret); +} + +static krb5_error_code +add_principal(context, princ, op, pblock) + krb5_context context; + krb5_principal princ; + enum ap_op op; + struct realm_info *pblock; +{ + krb5_error_code retval; + krb5_db_entry entry; + + krb5_timestamp now; + struct iterate_args iargs; + + int nentries = 1; + + memset((char *) &entry, 0, sizeof(entry)); + + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.attributes = pblock->flags; + entry.max_life = pblock->max_life; + entry.max_renewable_life = pblock->max_rlife; + entry.expiration = pblock->expiration; + + if ((retval = krb5_copy_principal(context, princ, &entry.princ))) + goto error_out; + + if ((retval = krb5_timeofday(context, &now))) + goto error_out; + + if ((retval = krb5_dbe_update_mod_princ_data(context, &entry, + now, &db_create_princ))) + goto error_out; + + switch (op) { + case MASTER_KEY: + if ((entry.key_data=(krb5_key_data*)malloc(sizeof(krb5_key_data))) + == NULL) + goto error_out; + memset((char *) entry.key_data, 0, sizeof(krb5_key_data)); + entry.n_key_data = 1; + + entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; + if ((retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + &master_keyblock, NULL, + 1, entry.key_data))) + return retval; + break; + case TGT_KEY: + iargs.ctx = context; + iargs.rblock = pblock; + iargs.dbentp = &entry; + /* + * Iterate through the key/salt list, ignoring salt types. + */ + if ((retval = krb5_keysalt_iterate(pblock->kslist, + pblock->nkslist, + 1, + tgt_keysalt_iterate, + (krb5_pointer) &iargs))) + return retval; + break; + case NULL_KEY: + return EOPNOTSUPP; + default: + break; + } + + retval = krb5_db_put_principal(context, &entry, &nentries); + +error_out:; + krb5_dbe_free_contents(context, &entry); + return retval; +} diff --git a/src/kadmin/dbutil/kdb5_destroy.c b/src/kadmin/dbutil/kdb5_destroy.c new file mode 100644 index 000000000..7c6873df7 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_destroy.c @@ -0,0 +1,117 @@ +/* + * admin/destroy/kdb5_destroy.c + * + * Copyright 1990 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * kdb_dest(roy): destroy the named database. + * + * This version knows about DBM format databases. + */ + +#include "k5-int.h" +#include <stdio.h> +#include "com_err.h" +#include <kadm5/admin.h> +#include <kadm5/adb.h> + +extern int errno; +extern int exit_status; +extern krb5_boolean dbactive; +extern kadm5_config_params global_params; + +char *yes = "yes\n"; /* \n to compare against result of + fgets */ + +static void +usage(who, status) + char *who; + int status; +{ + fprintf(stderr, "usage: %s [-f]\n", who); +} + +void +kdb5_destroy(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + extern int optind; + int optchar; + char *dbname; + char buf[5]; + char dbfilename[MAXPATHLEN]; + krb5_error_code retval, retval1, retval2; + krb5_context context; + int force = 0; + + krb5_init_context(&context); + krb5_init_ets(context); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + dbname = global_params.dbname; + + optind = 1; + while ((optchar = getopt(argc, argv, "f")) != EOF) { + switch(optchar) { + case 'f': + force++; + break; + case '?': + default: + usage(argv[0], 1); + return; + /*NOTREACHED*/ + } + } + if (!force) { + printf("Deleting KDC database stored in '%s', are you sure?\n", dbname); + printf("(type 'yes' to confirm)? "); + if (fgets(buf, sizeof(buf), stdin) == NULL) { + exit_status++; return; + } + if (strcmp(buf, yes)) { + exit_status++; return; + } + printf("OK, deleting database '%s'...\n", dbname); + } + + if (retval = krb5_db_set_name(context, dbname)) { + com_err(argv[0], retval, "'%s'",dbname); + exit_status++; return; + } + retval1 = kdb5_db_destroy(context, dbname); + retval2 = osa_adb_destroy_policy_db(&global_params); + if (retval1) { + com_err(argv[0], retval1, "deleting database '%s'",dbname); + exit_status++; return; + } + if (retval2) { + com_err(argv[0], retval2, "destroying policy database"); + exit_status++; return; + } + + dbactive = FALSE; + printf("** Database '%s' destroyed.\n", dbname); + return; +} diff --git a/src/kadmin/dbutil/kdb5_edit.M b/src/kadmin/dbutil/kdb5_edit.M new file mode 100644 index 000000000..8405c01cd --- /dev/null +++ b/src/kadmin/dbutil/kdb5_edit.M @@ -0,0 +1,179 @@ +.\" admin/edit/kdb5_edit.M +.\" +.\" Copyright 1990 by the Massachusetts Institute of Technology. +.\" +.\" Export of this software from the United States of America may +.\" require a specific license from the United States Government. +.\" It is the responsibility of any person or organization contemplating +.\" export to obtain such a license before exporting. +.\" +.\" WITHIN THAT CONSTRAINT, permission to use, copy, modify, and +.\" distribute this software and its documentation for any purpose and +.\" without fee is hereby granted, provided that the above copyright +.\" notice appear in all copies and that both that copyright notice and +.\" this permission notice appear in supporting documentation, and that +.\" the name of M.I.T. not be used in advertising or publicity pertaining +.\" to distribution of the software without specific, written prior +.\" permission. M.I.T. makes no representations about the suitability of +.\" this software for any purpose. It is provided "as is" without express +.\" or implied warranty. +.\" +.\" +.TH KDB5_EDIT 8 "Kerberos Version 5.0" "MIT Project Athena" +.SH NAME +kdb5_edit \- edit a Kerberos V5 principal database +.SH SYNOPSIS +.B kdb5_edit +[ +.B \-r +.I realm +] [ +.B \-d +.I dbname +] [ +.B \-k +.I keytype +] [ +.B \-M +.I mkeyname +] [ +.B \-e +.I enctype +] [ +.B \-m +] [ +.B \-R +.I command +] [ +.B \-s +.I script +] [ +.B \-f +.I stashfile +] +.br +.SH DESCRIPTION +.I kdb5_edit +allows an administrator to add, delete, and edit entries in a Kerberos +version 5 principal database. +After themaster key is verified, commands are to +.I kdb5_edit +are issued using one of three mechanisms. If a single command is supplied +using the +.B \-R +.I command +argument, then that single command is processed and execution ceases. If a +script file is provided using the +.B \-s +.I script +argument, then commands are read from this file until either an error occurs +or an end of file is detected. Finally, if neither a command or a script is +specified, the invoker is placed into a shell-like command loop, from which +[s]he may issue commands to modify the +database. +.PP +The +.B \-r +.I realm +option specifies the realm of the database; +by default the realm returned by +.IR krb5_default_local_realm (3) +is used. +.PP +The +.B \-d +.I dbname +option specifies the name under which the principal database is stored; +by default the database is in DEFAULT_DBM_FILE (defined in <krb5/osconf.h>). +.PP +The +.B \-k +.I keytype +option specifies the key type of the master key in the database; the default is +the string representation of DEFAULT_KDC_KEYTYPE (defined in <krb5/osconf.h>). +.PP +The +.B \-f +.I stashfile +option specifies the filename of the stashed V5 master key. The default is +defined as DEFAULT_KEYFILE_STUB in <krb5/osconf.h> and is +typically $(prefix)/lib/krb5kdc/.k5.REALMNAME. (In previous +releases, this would have been /.k5.REALMNAME.) +.PP +The +.B \-M +.I mkeyname +option specifies the principal name for the master key in the database; +the default is KRB5_KDB_M_NAME (defined in <krb5/kdb.h>). +.PP +The +.B \-e +.I enctype +option specifies the encryption type to be used when placing entries in +the database; the default is the string representation of DEFAULT_KDC_ETYPE +(defined in <krb5/osconf.h>). +.PP +The +.B \-m +option specifies that the master database password should be fetched +from the keyboard rather than from a file on disk. +.SH AVAILABLE COMMANDS + +The following is a list of commands and their aliases that the system +administrator may use to manipulate the database: + +.IP add_new_key,ank +Add new entry to Kerberos database (prompting for password) + +.IP change_pwd_key,cpw +Change key of an entry in the Kerberos database (prompting for password) + +.IP add_rnd_key,ark +Add new entry to Kerberos database, using a random key + +.IP change_rnd_key,crk +Change key of an entry in the Kerberos database (select a new random key) + +.IP delete_entry,delent,del +Delete an entry from the database + +.IP extract_srvtab,xst,ex_st +Extract service key table + +.IP extract_v4_srvtab,xst4 +Extract service key table + +.IP modify_entry,modent +Modify entry + +.IP list_db,ldb +List database entries + +.IP dump_db,ddb +Dump database entries to a file + +.IP load_db,lddb +Load database entries from a file + +.IP set_dbname,sdbn +Change database name + +.IP enter_master_key,emk +Enter the master key for a database + +.IP change_working_directory,cwd,cd +Change working directory + +.IP print_working_direcotry,pwd +Print working directory + +.IP list_requests,lr,? +List available requests. + +.IP quit,exit,q +Exit program. + +.SH SEE ALSO +krb5(3), krb5kdc(8), ss(3) +.SH BUGS + diff --git a/src/kadmin/dbutil/kdb5_stash.c b/src/kadmin/dbutil/kdb5_stash.c new file mode 100644 index 000000000..6952db7ec --- /dev/null +++ b/src/kadmin/dbutil/kdb5_stash.c @@ -0,0 +1,153 @@ +/* + * admin/stash/kdb5_stash.c + * + * Copyright 1990 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Store the master database key in a file. + */ + +#include "k5-int.h" +#include "com_err.h" +#include <kadm5/admin.h> +#include <stdio.h> + +extern int errno; + +extern krb5_keyblock master_keyblock; +extern krb5_principal master_princ; +extern krb5_encrypt_block master_encblock; +extern kadm5_config_params global_params; + +extern int exit_status; + +static void +usage(who, status) +char *who; +int status; +{ + fprintf(stderr, "usage: %s [-f keyfile]\n", who); + exit_status++; return; +} + + +void +kdb5_stash(argc, argv) +int argc; +char *argv[]; +{ + extern char *optarg; + int optchar; + krb5_error_code retval; + char *dbname = (char *) NULL; + char *realm = 0; + char *mkey_name = 0; + char *mkey_fullname; + char *keyfile = 0; + krb5_context context; + krb5_realm_params *rparams; + + int enctypedone = 0; + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + krb5_init_context(&context); + krb5_init_ets(context); + + dbname = global_params.dbname; + realm = global_params.realm; + mkey_name = global_params.mkey_name; + keyfile = global_params.stash_file; + + optind = 1; + while ((optchar = getopt(argc, argv, "f:")) != EOF) { + switch(optchar) { + case 'f': + keyfile = optarg; + break; + case '?': + default: + usage(argv[0], 1); + return; + } + } + + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, tmp, sizeof(tmp))) + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit_status++; return; + } + + krb5_use_enctype(context, &master_encblock, master_keyblock.enctype); + + if (retval = krb5_db_set_name(context, dbname)) { + com_err(argv[0], retval, "while setting active database to '%s'", + dbname); + exit_status++; return; + } + + /* assemble & parse the master key name */ + + if (retval = krb5_db_setup_mkey_name(context, mkey_name, realm, + &mkey_fullname, &master_princ)) { + com_err(argv[0], retval, "while setting up master key name"); + exit_status++; return; + } + + if (retval = krb5_db_init(context)) { + com_err(argv[0], retval, "while initializing the database '%s'", + dbname); + exit_status++; return; + } + + /* TRUE here means read the keyboard, but only once */ + if (retval = krb5_db_fetch_mkey(context, master_princ, &master_encblock, + TRUE, FALSE, (char *) NULL, + 0, &master_keyblock)) { + com_err(argv[0], retval, "while reading master key"); + (void) krb5_db_fini(context); + exit_status++; return; + } + if (retval = krb5_db_verify_master_key(context, master_princ, + &master_keyblock,&master_encblock)) { + com_err(argv[0], retval, "while verifying master key"); + (void) krb5_db_fini(context); + exit_status++; return; + } + if (retval = krb5_db_store_mkey(context, keyfile, master_princ, + &master_keyblock)) { + com_err(argv[0], errno, "while storing key"); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + (void) krb5_db_fini(context); + exit_status++; return; + } + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + if (retval = krb5_db_fini(context)) { + com_err(argv[0], retval, "closing database '%s'", dbname); + exit_status++; return; + } + + return; +} diff --git a/src/kadmin/dbutil/kdb5_util.M b/src/kadmin/dbutil/kdb5_util.M new file mode 100644 index 000000000..746e018c0 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.M @@ -0,0 +1,122 @@ +KDB5_UTIL(8) + +NAME + kdb5_util - Kerberos database maintainance utility + +SYNOPSIS + kdb5_util [-d dbpathname ] [-r realmname] [-R request ] + [-s scriptfile] [-k enctype] [-M mkeyname] + [-f stashfile] + +DESCRIPTION + kdb5_util allows an administrator to perform low-level + maintainance procedures on the Kerberos and KADM5 database. + Databases can be created, destroyed, and dumped to and loaded + from ASCII files. Additionally, kdb5_util can create a + Kerberos master key stash file. kdb5_util subsumes the + functionality of and makes obsolete the previous database + maintainance programs kdb5_create, kdb5_edit, kdb5_destroy, + and kdb5_stash. + + When the program is first run, it attempts to acquire the + master key and open the database. Execution continues whether + or not it is successful, however, because the database may not + exist yet or the stash file may be corrupt. Commands can be + issued using one of three mechanisms. If a single command is + supplied using the request argument, then that single command + is processed and execution ceases. If a script file is + provided using the -s script argument, then commands are read + from this file until either an error occurs or an end of file + is detected. Finally, if neither a command or a script is + specified, the invoker is placed into a shell-like command + loop, from which commands may be executed. + + The -r realm option specifies the realm of the database; by + default the realm returned by krb5_default_local_realm(3) is + used. + + The -d dbname option specifies the name under which the + principal database is stored; by default the database is + controlled by kdc.conf. The KADM5 policy database and lock + file are also derived from this value. + + The -k keytype option specifies the key type of the master key + in the database; the default is controlled by kdc.conf. + + The -f stashfile option specifies the filename of the stashed + V5 master key. The default is controlled by kdc.conf and is + typically <krb5-prefix>/lib/krb5kdc/.k5.REALMNAME. (In + previous releases, this would have been /.k5.REALMNAME.) + + The -M mkeyname option specifies the principal name for the + master key in the database; the default is controlled by + kdc.conf. + + The -m option specifies that the master database password + should be fetched from the keyboard rather than from a file on + disk. + +AVAILABLE COMMANDS + create_db [-s] + + Alias: create. Creates a new database. If the -s option is + specified, the stash file is also created. This command fails + if the database already exists. If the command is successful, + the database is opened just as if it had already existed when + the program was first run. + + destroy_db [-f] + + Alias: destroy. Destroys the database, first overwriting the + disk sectors and then unlinking the files, after prompting the + user for confirmation. With the -f argument, does not prompt + the user. + + stash_mkey [-f keyfile] + + Alias: stash. Stores the master principal's keys in a stash + file. The -f argument can be used to override the keyfile + specified at startup. + + dump_db [-old] [-b6] [-verbose] [filename [principals...]] + + Alias: ddb. Dumps the current Kerberos and KADM5 database + into an ASCII file. By default, the database is dumped in + current format, "kdb5_util load_dump version 4". The -b6 + argument causes the dump to be in the Kerberos 5 Beta 6 format + ("kdb5_edit load_dump version 3.0"). The -old argument causes + the dump to be in the Kerberos 5 Beta 5 and earlier dump + format ("kdb5_edit load_dump version 2.0"). The -verbose + option causes the name of each principal and policy to be + printed as it is dumped. + + load_db [-old] [-b6] [-verbose] [-update] filename dbname + [admin_dbname] + + Alias: lddb. Loads a database dump from the named file into + the named database. The -old and -b6 options require the dump + to be in the specified format (see dump_db); otherwise, the + format of the dump file is detected automatically and handled + as appropriate. If the -update argument is specified, records + from the dump file are merely added to or updated in the + existing database; otherwise, a new database is created + containing only what is in the dump file and the old one + destroyed on a successful completion. The dbname argument is + required (XXX probably shouldn't be) and overrides the value + specified on the command line or the default. The + admin_dbname is optional and is derived from dbname if not + specified. + + dump_v4db [filename] + + Alias: d4db. Dumps the current database into the Kerberos 4 + database dump format. + + load_v4db [-d v5dbpathname] [-t] [-n] [-r realmname] [-K] + [-k enctype] [-M mkeyname] -f inputfile + + Alias: lddb4. Loads a Kerberos 4 database dump file. XXX Not + sure what all the arguments mean. + +SEE ALSO + kadm5_export(8), kadm5_import(8) diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c new file mode 100644 index 000000000..3f31fcb14 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.c @@ -0,0 +1,416 @@ +/* + * admin/edit/kdb5_edit.c + * + * (C) Copyright 1990,1991, 1996 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Edit a KDC database. + */ + +#include <stdio.h> +#include <k5-int.h> +#include <kadm5/admin.h> +#include <kadm5/adb.h> +#include <time.h> +#include "kdb5_util.h" + +char *Err_no_master_msg = "Master key not entered!\n"; +char *Err_no_database = "Database not currently opened!\n"; + +/* + * XXX Ick, ick, ick. These global variables shouldn't be global.... + */ +static char *mkey_password = 0; + +/* + * I can't figure out any way for this not to be global, given how ss + * works. + */ + +int exit_status = 0; +krb5_context util_context; +osa_adb_policy_t policy_db; +kadm5_config_params global_params; + +/* + * Script input, specified by -s. + */ +FILE *scriptfile = (FILE *) NULL; + +static void +usage(who, status) + char *who; + int status; +{ + fprintf(stderr, + "usage: %s [-d dbpathname ] [-r realmname] [-R request ]\n", + who); + fprintf(stderr, "\t [-k enctype] [-M mkeyname] [-f stashfile]\n"); + exit(status); +} + +krb5_keyblock master_keyblock; +krb5_principal master_princ; +krb5_db_entry master_entry; +krb5_encrypt_block master_encblock; +krb5_pointer master_random; +int valid_master_key = 0; + +char *progname; +krb5_boolean manual_mkey = FALSE; +krb5_boolean dbactive = FALSE; + +char *kdb5_util_Init(argc, argv) + int argc; + char *argv[]; +{ + extern char *optarg; + int optchar; + krb5_error_code retval; + char *request = NULL; + + retval = krb5_init_context(&util_context); + if (retval) { + fprintf(stderr, "krb5_init_context failed with error #%ld\n", + (long) retval); + exit(1); + } + krb5_init_ets(util_context); + initialize_adb_error_table(); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + progname = argv[0]; + + while ((optchar = getopt(argc, argv, "P:d:a:r:R:k:M:e:ms:f:")) != EOF) { + switch(optchar) { + case 'P': /* Only used for testing!!! */ + mkey_password = optarg; + manual_mkey = TRUE; + break; + case 'd': + global_params.dbname = optarg; + global_params.mask |= KADM5_CONFIG_DBNAME; + break; + case 'a': + global_params.admin_dbname = optarg; + global_params.mask |= KADM5_CONFIG_ADBNAME; + break; + case 'r': + global_params.realm = optarg; + global_params.mask |= KADM5_CONFIG_REALM; + /* not sure this is really necessary */ + if ((retval = krb5_set_default_realm(util_context, + global_params.realm))) { + com_err(progname, retval, "while setting default realm name"); + exit(1); + } + break; + case 'R': + request = optarg; + break; + case 'k': + if (krb5_string_to_enctype(optarg, &global_params.enctype)) + com_err(argv[0], 0, "%s is an invalid enctype", optarg); + global_params.mask |= KADM5_CONFIG_ENCTYPE; + break; + case 'M': /* master key name in DB */ + global_params.mkey_name = optarg; + global_params.mask |= KADM5_CONFIG_MKEY_NAME; + break; + case 'm': + manual_mkey = TRUE; + global_params.mkey_from_kbd = 1; + global_params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + break; + case 's': + /* Open the script file */ + if (!(scriptfile = fopen(optarg, "r"))) { + com_err(argv[0], errno, "while opening script file %s", + optarg); + exit(1); + } + break; + case 'f': + global_params.stash_file = optarg; + global_params.mask |= KADM5_CONFIG_STASH_FILE; + break; + case '?': + default: + usage(progname, 1); + /*NOTREACHED*/ + } + } + + if (retval = kadm5_get_config_params(util_context, NULL, NULL, + &global_params, &global_params)) { + com_err(argv[0], retval, "while retreiving configuration parameters"); + exit(1); + } + + /* + * Dump creates files which should not be world-readable. It is + * easiest to do a single umask call here; any shells run by the + * ss command interface will have umask = 77 but that is not a + * serious problem. + */ + (void) umask(077); + + master_keyblock.enctype = global_params.enctype; + if (master_keyblock.enctype != ENCTYPE_UNKNOWN) { + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, + tmp, sizeof(tmp))) + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(argv[0], KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit(1); + } + krb5_use_enctype(util_context, &master_encblock, + master_keyblock.enctype); + } + + + open_db_and_mkey(); + + exit_status = 0; /* It's OK if we get errors in open_db_and_mkey */ + return request; +} + +#if 0 +/* + * This function is no longer used in kdb5_util (and it would no + * longer work, anyway). + */ +void set_dbname(argc, argv) + int argc; + char *argv[]; +{ + krb5_error_code retval; + + if (argc < 3) { + com_err(argv[0], 0, "Too few arguments"); + com_err(argv[0], 0, "Usage: %s dbpathname realmname", argv[0]); + exit_status++; + return; + } + if (dbactive) { + if ((retval = krb5_db_fini(util_context)) && retval!= KRB5_KDB_DBNOTINITED) { + com_err(argv[0], retval, "while closing previous database"); + exit_status++; + return; + } + if (valid_master_key) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, + &master_random); + memset((char *)master_keyblock.contents, 0, + master_keyblock.length); + krb5_xfree(master_keyblock.contents); + master_keyblock.contents = NULL; + valid_master_key = 0; + } + krb5_free_principal(util_context, master_princ); + dbactive = FALSE; + } + + (void) set_dbname_help(argv[0], argv[1]); + return; +} +#endif + +/* + * open_db_and_mkey: Opens the KDC and policy database, and sets the + * global master_* variables. Sets dbactive to TRUE if the databases + * are opened, and valid_master_key to 1 if the global master + * variables are set properly. Returns 0 on success, and 1 on + * failure, but it is not considered a failure if the master key + * cannot be fetched (the master key stash file may not exist when the + * program is run). + */ +int open_db_and_mkey() +{ + krb5_error_code retval; + int nentries, i; + krb5_boolean more; + krb5_data scratch, pwd; + + dbactive = FALSE; + valid_master_key = 0; + + if ((retval = krb5_db_set_name(util_context, global_params.dbname))) { + com_err(progname, retval, "while setting active database to '%s'", + global_params.dbname); + exit_status++; + return(1); + } + if ((retval = krb5_db_init(util_context))) { + com_err(progname, retval, "while initializing database"); + exit_status++; + return(1); + } + if (retval = osa_adb_open_policy(&policy_db, &global_params)) { + com_err(progname, retval, "opening policy database"); + exit_status++; + return (1); + } + + /* assemble & parse the master key name */ + + if ((retval = krb5_db_setup_mkey_name(util_context, + global_params.mkey_name, + global_params.realm, + 0, &master_princ))) { + com_err(progname, retval, "while setting up master key name"); + exit_status++; + return(1); + } + nentries = 1; + if ((retval = krb5_db_get_principal(util_context, master_princ, + &master_entry, &nentries, &more))) { + com_err(progname, retval, "while retrieving master entry"); + exit_status++; + (void) krb5_db_fini(util_context); + return(1); + } else if (more) { + com_err(progname, KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE, + "while retrieving master entry"); + exit_status++; + (void) krb5_db_fini(util_context); + return(1); + } else if (!nentries) { + com_err(progname, KRB5_KDB_NOENTRY, "while retrieving master entry"); + exit_status++; + (void) krb5_db_fini(util_context); + return(1); + } + + krb5_db_free_principal(util_context, &master_entry, nentries); + + /* the databases are now open, and the master principal exists */ + dbactive = TRUE; + + if (mkey_password) { + pwd.data = mkey_password; + pwd.length = strlen(mkey_password); + retval = krb5_principal2salt(util_context, master_princ, &scratch); + if (retval) { + com_err(progname, retval, "while calculated master key salt"); + return(1); + } + + /* If no encryption type is set, use the default */ + if (master_keyblock.enctype == ENCTYPE_UNKNOWN) { + master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + if (!valid_enctype(master_keyblock.enctype)) { + char tmp[32]; + if (krb5_enctype_to_string(master_keyblock.enctype, + tmp, sizeof(tmp))) + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + else + com_err(progname, KRB5_PROG_KEYTYPE_NOSUPP, tmp); + exit(1); + } + krb5_use_enctype(util_context, &master_encblock, + master_keyblock.enctype); + } + + retval = krb5_string_to_key(util_context, &master_encblock, + &master_keyblock, &pwd, &scratch); + if (retval) { + com_err(progname, retval, + "while transforming master key from password"); + return(1); + } + free(scratch.data); + mkey_password = 0; + } else if ((retval = krb5_db_fetch_mkey(util_context, master_princ, + &master_encblock, manual_mkey, + FALSE, global_params.stash_file, + 0, &master_keyblock))) { + com_err(progname, retval, "while reading master key"); + com_err(progname, 0, "Warning: proceeding without master key"); + exit_status++; + return(0); + } + if ((retval = krb5_db_verify_master_key(util_context, master_princ, + &master_keyblock,&master_encblock)) + ) { + com_err(progname, retval, "while verifying master key"); + exit_status++; + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_xfree(master_keyblock.contents); + return(1); + } + if ((retval = krb5_process_key(util_context, &master_encblock, + &master_keyblock))) { + com_err(progname, retval, "while processing master key"); + exit_status++; + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_xfree(master_keyblock.contents); + return(1); + } + if ((retval = krb5_init_random_key(util_context, &master_encblock, + &master_keyblock, + &master_random))) { + com_err(progname, retval, "while initializing random key generator"); + exit_status++; + (void) krb5_finish_key(util_context, &master_encblock); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_xfree(master_keyblock.contents); + return(1); + } + + valid_master_key = 1; + dbactive = TRUE; + return 0; +} + +#ifdef HAVE_GETCWD +#undef getwd +#endif + +int +quit() +{ + krb5_error_code retval; + static krb5_boolean finished = 0; + + if (finished) + return 0; + if (valid_master_key) { + (void) krb5_finish_key(util_context, &master_encblock); + (void) krb5_finish_random_key(util_context, &master_encblock, + &master_random); + } + retval = krb5_db_fini(util_context); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + finished = TRUE; + if (retval && retval != KRB5_KDB_DBNOTINITED) { + com_err(progname, retval, "while closing database"); + exit_status++; + return 1; + } + return 0; +} diff --git a/src/kadmin/dbutil/kdb5_util.h b/src/kadmin/dbutil/kdb5_util.h new file mode 100644 index 000000000..b580e2f6a --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util.h @@ -0,0 +1,49 @@ +/* + * admin/edit/kdb5_edit.h + * + * Copyright 1992 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +#define REALM_SEP '@' +#define REALM_SEP_STR "@" + +extern char *progname; +extern char *Err_no_database; + +void add_key + PROTOTYPE((char const *, char const *, + krb5_const_principal, const krb5_keyblock *, + krb5_kvno, krb5_keysalt *)); +int set_dbname_help + PROTOTYPE((char *, char *)); + +char *kdb5_util_Init PROTOTYPE((int, char **)); + +int quit(); + +int check_for_match + PROTOTYPE((char *, int, krb5_db_entry *, int, int)); + +void parse_token + PROTOTYPE((char *, int *, int *, char *)); + +int create_db_entry + PROTOTYPE((krb5_principal, krb5_db_entry *)); diff --git a/src/kadmin/dbutil/kdb5_util_ct.ct b/src/kadmin/dbutil/kdb5_util_ct.ct new file mode 100644 index 000000000..bac1df125 --- /dev/null +++ b/src/kadmin/dbutil/kdb5_util_ct.ct @@ -0,0 +1,56 @@ +# admin/edit/kdb5_ed_ct.ct +# +# Copyright 1990 by the Massachusetts Institute of Technology. +# All Rights Reserved. +# +# Export of this software from the United States of America may +# require a specific license from the United States Government. +# It is the responsibility of any person or organization contemplating +# export to obtain such a license before exporting. +# +# WITHIN THAT CONSTRAINT, permission to use, copy, modify, and +# distribute this software and its documentation for any purpose and +# without fee is hereby granted, provided that the above copyright +# notice appear in all copies and that both that copyright notice and +# this permission notice appear in supporting documentation, and that +# the name of M.I.T. not be used in advertising or publicity pertaining +# to distribution of the software without specific, written prior +# permission. M.I.T. makes no representations about the suitability of +# this software for any purpose. It is provided "as is" without express +# or implied warranty. +# +# +# Command table for Kerberos administration edit +# + +command_table kdb5_edit_cmds; + +request kdb5_create, "Create a new Kerberos database", + create_db, create; + +request kdb5_destroy, "Destroy a Kerberos database", + destroy_db, destroy; + +request kdb5_stash, "Stash the Kerberos master key", + stash_mkey, stash; + +request dump_db, "Dump database entries to a file", + dump_db, ddb; + +request dump_v4db, "Dump database entries to a V4 slave dump file", + dump_v4db, d4db; + +request load_db, "Load database entries from a file", + load_db, lddb; + +request load_v4db, "Load database entries from a V4 slave dump file", + load_v4db, lddb4; + +# list_requests is generic -- unrelated to Kerberos +request ss_list_requests, "List available requests.", + list_requests, lr, "?"; + +request ss_quit, "Exit program.", + quit, exit, q; + +end; diff --git a/src/kadmin/dbutil/loadv4.c b/src/kadmin/dbutil/loadv4.c new file mode 100644 index 000000000..a1d37edc7 --- /dev/null +++ b/src/kadmin/dbutil/loadv4.c @@ -0,0 +1,881 @@ +/* + * admin/edit/loadv4.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Generate (from scratch) a Kerberos V5 KDC database, filling it in with the + * entries from a V4 database. + */ + +#ifdef KRB5_KRB4_COMPAT + +#include <des.h> +#include <krb.h> +#include <krb_db.h> +/* MKEYFILE is now defined in kdc.h */ +#include <kdc.h> + +static C_Block master_key; +static Key_schedule master_key_schedule; +static long master_key_version; + +static char *v4_mkeyfile = "/.k"; + +#include "k5-int.h" +#include "com_err.h" +#include "adm.h" +#include "adm_proto.h" +#include <stdio.h> + +#include <netinet/in.h> /* ntohl */ + +#define PROGNAME argv[0] + +enum ap_op { + NULL_KEY, /* setup null keys */ + MASTER_KEY, /* use master key as new key */ + RANDOM_KEY /* choose a random key */ +}; + +struct realm_info { + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_encrypt_block *eblock; + krb5_pointer rseed; +}; + +static struct realm_info rblock = { /* XXX */ + KRB5_KDB_MAX_LIFE, + KRB5_KDB_MAX_RLIFE, + KRB5_KDB_EXPIRATION, + KRB5_KDB_DEF_FLAGS, + 0 +}; + +static int verbose = 0; + +static krb5_error_code add_principal + PROTOTYPE((krb5_context, + krb5_principal, + enum ap_op, + struct realm_info *)); + +static int v4init PROTOTYPE((char *, char *, int, char *)); +static krb5_error_code enter_in_v5_db PROTOTYPE((krb5_context, + char *, Principal *)); +static krb5_error_code process_v4_dump PROTOTYPE((krb5_context, char *, + char *)); +static krb5_error_code fixup_database PROTOTYPE((krb5_context, char *)); + +static int create_local_tgt = 0; + +static void +usage(who, status) +char *who; +int status; +{ + fprintf(stderr, "usage: %s [-d v5dbpathname] [-t] [-n] [-r realmname] [-K] [-k enctype]\n\ +\t[-M mkeyname] -f inputfile\n", + who); + return; +} + +static krb5_keyblock master_keyblock; +static krb5_principal master_princ; +static krb5_encrypt_block master_encblock; + +static krb5_data tgt_princ_entries[] = { + {0, KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME}, + {0, 0, 0} }; + +static krb5_data db_creator_entries[] = { + {0, sizeof("db_creation")-1, "db_creation"} }; + +/* XXX knows about contents of krb5_principal, and that tgt names + are of form TGT/REALM@REALM */ +static krb5_principal_data tgt_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + tgt_princ_entries, /* krb5_data *data */ + 2, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + +static krb5_principal_data db_create_princ = { + 0, /* magic number */ + {0, 0, 0}, /* krb5_data realm */ + db_creator_entries, /* krb5_data *data */ + 1, /* int length */ + KRB5_NT_SRV_INST /* int type */ +}; + + +void +load_v4db(argc, argv) +int argc; +char *argv[]; +{ + krb5_error_code retval; + /* The kdb library will default to this, but it is convenient to + make it explicit (error reporting and temporary filename generation + use it). */ + char *dbname = DEFAULT_KDB_FILE; + char *v4dbname = 0; + char *v4dumpfile = 0; + char *realm = 0; + char *mkey_name = 0; + char *mkey_fullname; + char *defrealm; + int enctypedone = 0; + int v4manual = 0; + int read_mkey = 0; + int tempdb = 0; + char *tempdbname; + krb5_context context; + char *stash_file = (char *) NULL; + krb5_realm_params *rparams; + int persist, op_ind; + + krb5_init_context(&context); + + krb5_init_ets(context); + + if (strrchr(argv[0], '/')) + argv[0] = strrchr(argv[0], '/')+1; + + persist = 1; + op_ind = 1; + while (persist && (op_ind < argc)) { + if (!strcmp(argv[op_ind], "-d") && ((argc - op_ind) >= 2)) { + dbname = argv[op_ind+1]; + op_ind++; + } + else if (!strcmp(argv[op_ind], "-T")) { + create_local_tgt = 1; + } + else if (!strcmp(argv[op_ind], "-t")) { + tempdb = 1; + } + else if (!strcmp(argv[op_ind], "-r") && ((argc - op_ind) >= 2)) { + realm = argv[op_ind+1]; + op_ind++; + } + else if (!strcmp(argv[op_ind], "-K")) { + read_mkey = 1; + } + else if (!strcmp(argv[op_ind], "-v")) { + verbose = 1; + } + else if (!strcmp(argv[op_ind], "-k") && ((argc - op_ind) >= 2)) { + if (!krb5_string_to_enctype(argv[op_ind+1], + &master_keyblock.enctype)) + enctypedone++; + else + com_err(argv[0], 0, "%s is an invalid enctype", + argv[op_ind+1]); + op_ind++; + } + else if (!strcmp(argv[op_ind], "-M") && ((argc - op_ind) >= 2)) { + mkey_name = argv[op_ind+1]; + op_ind++; + } + else if (!strcmp(argv[op_ind], "-n")) { + v4manual++; + } + else if (!strcmp(argv[op_ind], "-f") && ((argc - op_ind) >= 2)) { + if (v4dbname) { + usage(PROGNAME, 1); + return; + } + v4dumpfile = argv[op_ind+1]; + op_ind++; + } + else + persist = 0; + op_ind++; + } + + /* + * Attempt to read the KDC profile. If we do, then read appropriate values + * from it and augment values supplied on the command line. + */ + if (!(retval = krb5_read_realm_params(context, + realm, + (char *) NULL, + (char *) NULL, + &rparams))) { + /* Get the value for the database */ + if (rparams->realm_dbname && !dbname) + dbname = strdup(rparams->realm_dbname); + + /* Get the value for the master key name */ + if (rparams->realm_mkey_name && !mkey_name) + mkey_name = strdup(rparams->realm_mkey_name); + + /* Get the value for the master key type */ + if (rparams->realm_enctype_valid && !enctypedone) { + master_keyblock.enctype = rparams->realm_enctype; + enctypedone++; + } + + /* Get the value for the stashfile */ + if (rparams->realm_stash_file) + stash_file = strdup(rparams->realm_stash_file); + + /* Get the value for maximum ticket lifetime. */ + if (rparams->realm_max_life_valid) + rblock.max_life = rparams->realm_max_life; + + /* Get the value for maximum renewable ticket lifetime. */ + if (rparams->realm_max_rlife_valid) + rblock.max_rlife = rparams->realm_max_rlife; + + /* Get the value for the default principal expiration */ + if (rparams->realm_expiration_valid) + rblock.expiration = rparams->realm_expiration; + + /* Get the value for the default principal flags */ + if (rparams->realm_flags_valid) + rblock.flags = rparams->realm_flags; + + krb5_free_realm_params(context, rparams); + } + + if (!v4dumpfile) { + usage(PROGNAME, 1); + return; + } + + if (!enctypedone) + master_keyblock.enctype = DEFAULT_KDC_ENCTYPE; + + if (!valid_enctype(master_keyblock.enctype)) { + com_err(PROGNAME, KRB5_PROG_KEYTYPE_NOSUPP, + "while setting up enctype %d", master_keyblock.enctype); + return; + } + + krb5_use_enctype(context, &master_encblock, master_keyblock.enctype); + + /* If the user has not requested locking, don't modify an existing database. */ + if (! tempdb) { + retval = krb5_db_set_name(context, dbname); + if (retval != ENOENT) { + fprintf(stderr, + "%s: The v5 database appears to already exist.\n", + PROGNAME); + return; + } + tempdbname = dbname; + } else { + int dbnamelen = strlen(dbname); + tempdbname = malloc(dbnamelen + 2); + if (tempdbname == 0) { + com_err(PROGNAME, ENOMEM, "allocating temporary filename"); + return; + } + strcpy(tempdbname, dbname); + tempdbname[dbnamelen] = '~'; + tempdbname[dbnamelen+1] = 0; + (void) kdb5_db_destroy(context, tempdbname); + } + + + if (!realm) { + if (retval = krb5_get_default_realm(context, &defrealm)) { + com_err(PROGNAME, retval, "while retrieving default realm name"); + return; + } + realm = defrealm; + } + + /* assemble & parse the master key name */ + + if (retval = krb5_db_setup_mkey_name(context, mkey_name, realm, + &mkey_fullname, &master_princ)) { + com_err(PROGNAME, retval, "while setting up master key name"); + return; + } + + krb5_princ_set_realm_data(context, &db_create_princ, realm); + krb5_princ_set_realm_length(context, &db_create_princ, strlen(realm)); + krb5_princ_set_realm_data(context, &tgt_princ, realm); + krb5_princ_set_realm_length(context, &tgt_princ, strlen(realm)); + krb5_princ_component(context, &tgt_princ,1)->data = realm; + krb5_princ_component(context, &tgt_princ,1)->length = strlen(realm); + + printf("Initializing database '%s' for realm '%s',\n\ +master key name '%s'\n", + dbname, realm, mkey_fullname); + + if (read_mkey) { + puts("You will be prompted for the version 5 database Master Password."); + puts("It is important that you NOT FORGET this password."); + fflush(stdout); + } + + if (retval = krb5_db_fetch_mkey(context, master_princ, &master_encblock, + read_mkey, read_mkey, stash_file, 0, + &master_keyblock)) { + com_err(PROGNAME, retval, "while reading master key"); + return; + } + if (retval = krb5_process_key(context, &master_encblock, &master_keyblock)) { + com_err(PROGNAME, retval, "while processing master key"); + return; + } + + rblock.eblock = &master_encblock; + if (retval = krb5_init_random_key(context, &master_encblock, + &master_keyblock, &rblock.rseed)) { + com_err(PROGNAME, retval, "while initializing random key generator"); + (void) krb5_finish_key(context, &master_encblock); + return; + } + if (retval = krb5_db_create(context, tempdbname)) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while creating %sdatabase '%s'", + tempdb ? "temporary " : "", tempdbname); + return; + } + if (retval = krb5_db_set_name(context, tempdbname)) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while setting active database to '%s'", + tempdbname); + return; + } + if (v4init(PROGNAME, v4dbname, v4manual, v4dumpfile)) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + return; + } + if ((retval = krb5_db_init(context)) || + (retval = krb5_dbm_db_open_database(context))) { + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while initializing the database '%s'", + tempdbname); + return; + } + + if (retval = add_principal(context, master_princ, MASTER_KEY, &rblock)) { + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while adding K/M to the database"); + return; + } + + if (create_local_tgt && + (retval = add_principal(context, &tgt_princ, RANDOM_KEY, &rblock))) { + (void) krb5_db_fini(context); + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + (void) krb5_dbm_db_destroy(context, tempdbname); + com_err(PROGNAME, retval, "while adding TGT service to the database"); + return; + } + + retval = process_v4_dump(context, v4dumpfile, realm); + putchar('\n'); + if (retval) + com_err(PROGNAME, retval, "while translating entries to the database"); + else { + retval = fixup_database(context, realm); + } + + /* clean up; rename temporary database if there were no errors */ + if (retval == 0) { + if (retval = krb5_db_fini (context)) + com_err(PROGNAME, retval, "while shutting down database"); + else if (tempdb && (retval = krb5_dbm_db_rename(context, tempdbname, + dbname))) + com_err(PROGNAME, retval, "while renaming temporary database"); + } else { + (void) krb5_db_fini (context); + if (tempdb) + (void) krb5_dbm_db_destroy (context, tempdbname); + } + (void) krb5_finish_key(context, &master_encblock); + (void) krb5_finish_random_key(context, &master_encblock, &rblock.rseed); + memset((char *)master_keyblock.contents, 0, master_keyblock.length); + krb5_free_context(context); + return; +} + +static int +v4init(pname, name, manual, dumpfile) +char *pname, *name; +int manual; +char *dumpfile; +{ + int fd; + int ok = 0; + + if (!manual) { + fd = open(v4_mkeyfile, O_RDONLY, 0600); + if (fd >= 0) { + if (read(fd, master_key, sizeof(master_key)) == sizeof(master_key)) + ok = 1; + close(fd); + } + } + if (!ok) { + des_read_password(master_key, "V4 Kerberos master key: ", 0); + printf("\n"); + } + key_sched(master_key, master_key_schedule); + return 0; +} + +static krb5_error_code +enter_in_v5_db(context, realm, princ) +krb5_context context; +char *realm; +Principal *princ; +{ + krb5_db_entry entry; + krb5_error_code retval; + krb5_keyblock v4v5key; + int nentries = 1; + des_cblock v4key; + char *name; + krb5_timestamp mod_time; + krb5_principal mod_princ; + krb5_keysalt keysalt; + + /* don't convert local TGT if we created a TGT already.... */ + if (create_local_tgt && !strcmp(princ->name, "krbtgt") && + !strcmp(princ->instance, realm)) { + if (verbose) + printf("\nignoring local TGT: '%s.%s' ...", + princ->name, princ->instance); + return 0; + } + if (!strcmp(princ->name, KERB_M_NAME) && + !strcmp(princ->instance, KERB_M_INST)) { + des_cblock key_from_db; + int val; + + /* here's our chance to verify the master key */ + /* + * use the master key to decrypt the key in the db, had better + * be the same! + */ + memcpy(key_from_db, (char *)&princ->key_low, 4); + memcpy(((char *) key_from_db) + 4, (char *)&princ->key_high, 4); + pcbc_encrypt((C_Block *) &key_from_db, + (C_Block *) &key_from_db, + (long) sizeof(C_Block), + master_key_schedule, + (C_Block *) master_key, + DECRYPT); + val = memcmp((char *) master_key, (char *) key_from_db, + sizeof(master_key)); + memset((char *)key_from_db, 0, sizeof(key_from_db)); + if (val) { + return KRB5_KDB_BADMASTERKEY; + } + if (verbose) + printf("\nignoring '%s.%s' ...", princ->name, princ->instance); + return 0; + } + memset((char *) &entry, 0, sizeof(entry)); + if (retval = krb5_425_conv_principal(context, princ->name, princ->instance, + realm, &entry.princ)) + return retval; + if (verbose) { + if (retval = krb5_unparse_name(context, entry.princ, &name)) + name = strdup("<not unparsable name!>"); + if (verbose) + printf("\ntranslating %s...", name); + free(name); + } + + if (retval = krb5_build_principal(context, &mod_princ, + strlen(realm), + realm, princ->mod_name, + princ->mod_instance[0] ? princ->mod_instance : 0, + 0)) { + krb5_free_principal(context, entry.princ); + return retval; + } + mod_time = princ->mod_date; + + entry.max_life = princ->max_life * 60 * 5; + entry.max_renewable_life = rblock.max_rlife; + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.expiration = princ->exp_date; + entry.attributes = rblock.flags; /* XXX is there a way to convert + the old attrs? */ + + memcpy((char *)v4key, (char *)&(princ->key_low), 4); + memcpy((char *) (((char *) v4key) + 4), (char *)&(princ->key_high), 4); + pcbc_encrypt((C_Block *) &v4key, + (C_Block *) &v4key, + (long) sizeof(C_Block), + master_key_schedule, + (C_Block *) master_key, + DECRYPT); + + v4v5key.magic = KV5M_KEYBLOCK; + v4v5key.contents = (krb5_octet *)v4key; + v4v5key.enctype = ENCTYPE_DES_CBC_CRC; + v4v5key.length = sizeof(v4key); + + retval = krb5_dbe_create_key_data(context, &entry); + if (retval) { + krb5_free_principal(context, entry.princ); + krb5_free_principal(context, mod_princ); + return retval; + } + + keysalt.type = KRB5_KDB_SALTTYPE_V4; + keysalt.data.length = 0; + keysalt.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(context, rblock.eblock, + &v4v5key, &keysalt, + princ->key_version, + &entry.key_data[0]); + if (!retval) + retval = krb5_dbe_update_mod_princ_data(context, &entry, + mod_time, mod_princ); + if (retval) { + krb5_db_free_principal(context, &entry, 1); + krb5_free_principal(context, mod_princ); + return retval; + } + memset((char *)v4key, 0, sizeof(v4key)); + + retval = krb5_db_put_principal(context, &entry, &nentries); + + if (!retval && !strcmp(princ->name, "krbtgt") && + strcmp(princ->instance, realm) && princ->instance[0]) { + krb5_free_principal(context, entry.princ); + if (retval = krb5_build_principal(context, &entry.princ, + strlen(princ->instance), + princ->instance, + "krbtgt", realm, 0)) + return retval; + retval = krb5_db_put_principal(context, &entry, &nentries); + } + + krb5_db_free_principal(context, &entry, 1); + krb5_free_principal(context, mod_princ); + + return retval; +} + +static krb5_error_code +add_principal(context, princ, op, pblock) +krb5_context context; +krb5_principal princ; +enum ap_op op; +struct realm_info *pblock; +{ + krb5_db_entry entry; + krb5_error_code retval; + krb5_keyblock *rkey; + int nentries = 1; + krb5_timestamp mod_time; + krb5_principal mod_princ; + + memset((char *) &entry, 0, sizeof(entry)); + if (retval = krb5_copy_principal(context, princ, &entry.princ)) + return(retval); + entry.max_life = pblock->max_life; + entry.max_renewable_life = pblock->max_rlife; + entry.len = KRB5_KDB_V1_BASE_LENGTH; + entry.expiration = pblock->expiration; + + if ((retval = krb5_timeofday(context, &mod_time))) { + krb5_db_free_principal(context, &entry, 1); + return retval; + } + entry.attributes = pblock->flags; + + if (retval = krb5_dbe_create_key_data(context, &entry)) { + krb5_db_free_principal(context, &entry, 1); + return(retval); + } + + switch (op) { + case MASTER_KEY: + entry.attributes |= KRB5_KDB_DISALLOW_ALL_TIX; + if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + &master_keyblock, + (krb5_keysalt *) NULL, 1, + &entry.key_data[0])) { + krb5_db_free_principal(context, &entry, 1); + return retval; + } + break; + case RANDOM_KEY: + if (retval = krb5_random_key(context, pblock->eblock, pblock->rseed, + &rkey)) { + krb5_db_free_principal(context, &entry, 1); + return retval; + } + if (retval = krb5_dbekd_encrypt_key_data(context, pblock->eblock, + rkey, + (krb5_keysalt *) NULL, 1, + &entry.key_data[0])) { + krb5_db_free_principal(context, &entry, 1); + return(retval); + } + krb5_free_keyblock(context, rkey); + break; + case NULL_KEY: + return EOPNOTSUPP; + default: + break; + } + + retval = krb5_dbe_update_mod_princ_data(context, &entry, + mod_time, &db_create_princ); + if (!retval) + retval = krb5_db_put_principal(context, &entry, &nentries); + krb5_db_free_principal(context, &entry, 1); + return retval; +} + +/* + * Convert a struct tm * to a UNIX time. + */ + + +#define daysinyear(y) (((y) % 4) ? 365 : (((y) % 100) ? 366 : (((y) % 400) ? 365 : 366))) + +#define SECSPERDAY 24*60*60 +#define SECSPERHOUR 60*60 +#define SECSPERMIN 60 + +static int cumdays[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + 365}; + +static int leapyear[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +static int nonleapyear[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static long +maketime(tp, local) +register struct tm *tp; +int local; +{ + register long retval; + int foo; + int *marray; + + if (tp->tm_mon < 0 || tp->tm_mon > 11 || + tp->tm_hour < 0 || tp->tm_hour > 23 || + tp->tm_min < 0 || tp->tm_min > 59 || + tp->tm_sec < 0 || tp->tm_sec > 59) /* out of range */ + return 0; + + retval = 0; + if (tp->tm_year < 1900) + foo = tp->tm_year + 1900; + else + foo = tp->tm_year; + + if (foo < 1901 || foo > 2038) /* year is too small/large */ + return 0; + + if (daysinyear(foo) == 366) { + if (tp->tm_mon > 1) + retval+= SECSPERDAY; /* add leap day */ + marray = leapyear; + } else + marray = nonleapyear; + + if (tp->tm_mday < 0 || tp->tm_mday > marray[tp->tm_mon]) + return 0; /* out of range */ + + while (--foo >= 1970) + retval += daysinyear(foo) * SECSPERDAY; + + retval += cumdays[tp->tm_mon] * SECSPERDAY; + retval += (tp->tm_mday-1) * SECSPERDAY; + retval += tp->tm_hour * SECSPERHOUR + tp->tm_min * SECSPERMIN + tp->tm_sec; + + if (local) { + /* need to use local time, so we retrieve timezone info */ + struct timezone tz; + struct timeval tv; + if (gettimeofday(&tv, &tz) < 0) { + /* some error--give up? */ + return(retval); + } + retval += tz.tz_minuteswest * SECSPERMIN; + } + return(retval); +} + +static long +time_explode(cp) +register char *cp; +{ + char wbuf[5]; + struct tm tp; + int local; + + memset((char *)&tp, 0, sizeof(tp)); + + if (strlen(cp) > 10) { /* new format */ + (void) strncpy(wbuf, cp, 4); + wbuf[4] = 0; + tp.tm_year = atoi(wbuf); + cp += 4; /* step over the year */ + local = 0; /* GMT */ + } else { /* old format: local time, + year is 2 digits, assuming 19xx */ + wbuf[0] = *cp++; + wbuf[1] = *cp++; + wbuf[2] = 0; + tp.tm_year = 1900 + atoi(wbuf); + local = 1; /* local */ + } + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + wbuf[2] = 0; + tp.tm_mon = atoi(wbuf)-1; + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + tp.tm_mday = atoi(wbuf); + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + tp.tm_hour = atoi(wbuf); + + wbuf[0] = *cp++; + wbuf[1] = *cp++; + tp.tm_min = atoi(wbuf); + + + return(maketime(&tp, local)); +} + +static krb5_error_code +process_v4_dump(context, dumpfile, realm) +krb5_context context; +char *dumpfile; +char *realm; +{ + krb5_error_code retval; + FILE *input_file; + Principal aprinc; + char exp_date_str[50]; + char mod_date_str[50]; + int temp1, temp2, temp3; + long time_explode(); + + input_file = fopen(dumpfile, "r"); + if (!input_file) + return errno; + + for (;;) { /* explicit break on eof from fscanf */ + int nread; + + memset((char *)&aprinc, 0, sizeof(aprinc)); + nread = fscanf(input_file, + "%s %s %d %d %d %hd %x %x %s %s %s %s\n", + aprinc.name, + aprinc.instance, + &temp1, + &temp2, + &temp3, + &aprinc.attributes, + &aprinc.key_low, + &aprinc.key_high, + exp_date_str, + mod_date_str, + aprinc.mod_name, + aprinc.mod_instance); + if (nread != 12) { + retval = nread == EOF ? 0 : KRB5_KDB_DB_CORRUPT; + break; + } + aprinc.key_low = ntohl (aprinc.key_low); + aprinc.key_high = ntohl (aprinc.key_high); + aprinc.max_life = (unsigned char) temp1; + aprinc.kdc_key_ver = (unsigned char) temp2; + aprinc.key_version = (unsigned char) temp3; + aprinc.exp_date = time_explode(exp_date_str); + aprinc.mod_date = time_explode(mod_date_str); + if (aprinc.instance[0] == '*') + aprinc.instance[0] = '\0'; + if (aprinc.mod_name[0] == '*') + aprinc.mod_name[0] = '\0'; + if (aprinc.mod_instance[0] == '*') + aprinc.mod_instance[0] = '\0'; + if (retval = enter_in_v5_db(context, realm, &aprinc)) + break; + } + (void) fclose(input_file); + return retval; +} + +static krb5_error_code fixup_database(context, realm) + krb5_context context; + char * realm; +{ + krb5_db_entry entry; + krb5_error_code retval; + int nprincs; + krb5_boolean more; + + nprincs = 1; + if (retval = krb5_db_get_principal(context, &tgt_princ, &entry, + &nprincs, &more)) + return retval; + + if (nprincs == 0) + return 0; + + entry.attributes |= KRB5_KDB_SUPPORT_DESMD5; + + retval = krb5_db_put_principal(context, &entry, &nprincs); + + if (nprincs) + krb5_db_free_principal(context, &entry, nprincs); + + return retval; +} + +#else /* KRB5_KRB4_COMPAT */ +void +load_v4db(argc, argv) + int argc; + char *argv[]; +{ + printf("This version of krb5_edit does not support the V4 load command.\n"); +} +#endif /* KRB5_KRB4_COMPAT */ diff --git a/src/kadmin/dbutil/ss_wrapper.c b/src/kadmin/dbutil/ss_wrapper.c new file mode 100644 index 000000000..ada85efc9 --- /dev/null +++ b/src/kadmin/dbutil/ss_wrapper.c @@ -0,0 +1,85 @@ +/* + * admin/edit/ss_wrapper.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * ss wrapper for kdb5_edit + */ + +#include <k5-int.h> +#include "kdb5_util.h" +#include <ss/ss.h> +#include <stdio.h> + +extern ss_request_table kdb5_edit_cmds; +extern int exit_status; +extern FILE *scriptfile; + +int main(argc, argv) + int argc; + char *argv[]; +{ + char *request; + krb5_error_code retval; + int sci_idx, code = 0; + + request = kdb5_util_Init(argc, argv); + sci_idx = ss_create_invocation("kdb5_util", "5.0", (char *) NULL, + &kdb5_edit_cmds, &retval); + if (retval) { + ss_perror(sci_idx, retval, "creating invocation"); + exit(1); + } + + if (request) { + code = ss_execute_line(sci_idx, request, &code); + if (code != 0) { + ss_perror(sci_idx, code, request); + exit_status++; + } + } else if (scriptfile) { + char *command; + int nread; + + /* Get a buffer */ + if ((command = (char *) malloc(BUFSIZ))) { + /* Process commands from the script until end-of-file or error */ + while (!feof(scriptfile) && + (fgets(command, BUFSIZ, scriptfile))) { + + /* Strip trailing newline */ + if (command[strlen(command)-1] == '\n') + command[strlen(command)-1] = '\0'; + + /* Execute the command */ + code = ss_execute_line(sci_idx, command, &code); + if (code != 0) { + ss_perror(sci_idx, code, command); + exit_status++; + break; + } + } + free(command); + } + } else + ss_listen(sci_idx, &retval); + return quit() ? 1 : exit_status; +} diff --git a/src/kadmin/dbutil/string_table.c b/src/kadmin/dbutil/string_table.c new file mode 100644 index 000000000..b9f86a363 --- /dev/null +++ b/src/kadmin/dbutil/string_table.c @@ -0,0 +1,91 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +/* String table of messages for kadm5_create */ + +char *str_INITING_KCONTEXT = "while initializing the kerberos context"; + +char *str_PARSE_NAME = "while parsing admin principal name."; + +char *str_HISTORY_PARSE_NAME = "while parsing admin history principal name."; + +char *str_ADMIN_PRINC_EXISTS = "Warning! Admin principal already exists."; + +char *str_CHANGEPW_PRINC_EXISTS = "Warning! Changepw principal already exists."; + +char *str_HISTORY_PRINC_EXISTS = "Warning! Admin history principal already exists."; + +char *str_ADMIN_PRINC_WRONG_ATTRS = + "Warning! Admin principal has incorrect attributes.\n" + "\tDISALLOW_TGT should be set, and max_life should be three hours.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_CHANGEPW_PRINC_WRONG_ATTRS = + "Warning! Changepw principal has incorrect attributes.\n" + "\tDISALLOW_TGT and PW_CHANGE_SERVICE should both be set, and " + "max_life should be five minutes.\n" + "\tThis program will leave them as-is, but beware!."; + +char *str_HISTORY_PRINC_WRONG_ATTRS = + "Warning! Admin history principal has incorrect attributes.\n" + "\tDISALLOW_ALL_TIX should be set.\n" + "\tThis program will leave it as-is, but beware!."; + +char *str_CREATED_PRINC_DB = + "%s: Admin principal database created (or it already existed).\n"; /* whoami */ + +char *str_CREATED_POLICY_DB = + "%s: Admin policy database created (or it already existed).\n"; /* whoami */ + +char *str_RANDOM_KEY = + "while calling random key for %s."; /* principal name */ + +char *str_ENCRYPT_KEY = + "while calling encrypt key for %s."; /* principal name */ + +char *str_PUT_PRINC = + "while calling storing %s in Kerberos database."; /* principal name */ + +char *str_CREATING_POLICY_DB = "while creating/opening admin policy database."; + +char *str_CLOSING_POLICY_DB = "while closing admin policy database."; + +char *str_CREATING_PRINC_DB = "while creating/opening admin principal database."; + +char *str_CLOSING_PRINC_DB = "while closing admin principal database."; + +char *str_CREATING_PRINC_ENTRY = + "while creating admin principal database entry for %s."; /* princ_name */ + +char *str_A_PRINC = "a principal"; + +char *str_UNPARSE_PRINC = "while unparsing principal."; + +char *str_CREATED_PRINC = "%s: Created %s principal.\n"; /* whoami, princ_name */ + +char *str_INIT_KDB = "while initializing kdb."; + +char *str_NO_KDB = +"while initializing kdb.\nThe Kerberos KDC database needs to exist in /krb5.\n\ +If you haven't run kdb5_create you need to do so before running this command."; + + +char *str_INIT_RANDOM_KEY = "while initializing random key generator."; + +char *str_TOO_MANY_ADMIN_PRINC = + "while fetching admin princ. Can only have one admin principal."; + +char *str_TOO_MANY_CHANGEPW_PRINC = + "while fetching changepw princ. Can only have one changepw principal."; + +char *str_TOO_MANY_HIST_PRINC = + "while fetching history princ. Can only have one history principal."; + +char *str_WHILE_DESTROYING_ADMIN_SESSION = "while closing session with admin server and destroying tickets."; diff --git a/src/kadmin/dbutil/string_table.h b/src/kadmin/dbutil/string_table.h new file mode 100644 index 000000000..e8cb45367 --- /dev/null +++ b/src/kadmin/dbutil/string_table.h @@ -0,0 +1,40 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + */ + +#ifndef _OVSEC_ADM_STRINGS_ + +extern char *str_INITING_KCONTEXT; +extern char *str_PARSE_NAME; +extern char *str_HISTORY_PARSE_NAME; +extern char *str_ADMIN_PRINC_EXISTS; +extern char *str_CHANGEPW_PRINC_EXISTS; +extern char *str_HISTORY_PRINC_EXISTS; +extern char *str_ADMIN_PRINC_WRONG_ATTRS; +extern char *str_CHANGEPW_PRINC_WRONG_ATTRS; +extern char *str_HISTORY_PRINC_WRONG_ATTRS; +extern char *str_CREATED_PRINC_DB; +extern char *str_CREATED_POLICY_DB; +extern char *str_RANDOM_KEY; +extern char *str_ENCRYPT_KEY; +extern char *str_PUT_PRINC; +extern char *str_CREATING_POLICY_DB; +extern char *str_CLOSING_POLICY_DB; +extern char *str_CREATING_PRINC_DB; +extern char *str_CLOSING_PRINC_DB; +extern char *str_CREATING_PRINC_ENTRY; +extern char *str_A_PRINC; +extern char *str_UNPARSE_PRINC; +extern char *str_CREATED_PRINC; +extern char *str_INIT_KDB; +extern char *str_NO_KDB; +extern char *str_INIT_RANDOM_KEY; +extern char *str_TOO_MANY_ADMIN_PRINC; +extern char *str_TOO_MANY_CHANGEPW_PRINC; +extern char *str_TOO_MANY_HIST_PRINC; +extern char *str_WHILE_DESTROYING_ADMIN_SESSION; + +#endif /* _OVSEC_ADM_STRINGS_ */ diff --git a/src/kadmin/dbutil/tcl_wrapper.c b/src/kadmin/dbutil/tcl_wrapper.c new file mode 100644 index 000000000..d527fa0d1 --- /dev/null +++ b/src/kadmin/dbutil/tcl_wrapper.c @@ -0,0 +1,235 @@ +/* + * admin/edit/tcl_wrapper.c + * + * Copyright 1990,1991 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * + * Tcl wrapper for kdb5_edit + */ + +#include "k5-int.h" +#include "kdb5_edit.h" +#include <tcl.h> + +#define CMDDECL(x) int x(clientData, interp, argc, argv)\ + ClientData clientData;\ + Tcl_Interp * interp;\ + int argc;\ + char ** argv; +#define CMDPROTO(x) int x PROTOTYPE((ClientData, Tcl_Interp,\ + int, char **)) +#define MKCMD(name,cmd) Tcl_CreateCommand(interp, name, cmd,\ + (ClientData)NULL,\ + (Tcl_CmdDeleteProc *)NULL) + +extern int main(); +int *tclDummyMainPtr = (int *) main; /* force ld to suck in main() + from libtcl.a */ +extern Tcl_Interp *interp; /* XXX yes, this is gross, + but we do need it for some things */ +extern int exit_status; + +void show_principal PROTOTYPE((int, char **)); +void add_new_key PROTOTYPE((int, char **)); +void change_pwd_key PROTOTYPE((int, char **)); +void add_rnd_key PROTOTYPE((int, char **)); +void change_rnd_key PROTOTYPE((int, char **)); +void delete_entry PROTOTYPE((int, char **)); +void extract_srvtab PROTOTYPE((krb5_context, int, char **)); +void extract_v4_srvtab PROTOTYPE((int, char **)); +void list_db PROTOTYPE((int, char **)); +void dump_db PROTOTYPE((int, char **)); +void load_db PROTOTYPE((int, char **)); +void set_dbname PROTOTYPE((krb5_context, int, char **)); +void enter_master_key PROTOTYPE((krb5_context, int, char **)); + +/* + * this is mostly stolen from tcl_ExitCmd() + * we need to do a few extra things, though... + */ +int doquit(clientData, interp, argc, argv) + ClientData clientData; + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + int value; + + if ((argc != 1) && (argc != 2)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], + " ?returnCode?\"", (char *) NULL); + return TCL_ERROR; + } + if (argc == 1) { + exit(quit() ? 1 : exit_status); + } + if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) { + return TCL_ERROR; + } + (void)quit(); + exit(value); + /*NOTREACHED*/ + return TCL_OK; /* Better not ever reach this! */ +} + +int list_requests(clientData, interp, argc, argv) + ClientData clientData; + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + Tcl_SetResult(interp, "show_principal, show: Show the Kerberos database entry for a principal\nadd_new_key, ank: Add new entry to the Kerberos database (prompting for password\nchange_pwd_key, cpw: Change key of an entry in the Kerberos database (prompting for password)\nadd_rnd_key, ark: Add new entry to Kerberos database, using a random key\nchange_rnd_key, crk: Change key of an entry in the Kerberos database (select a random key)\ndelete_entry, delent: Delete an entry from the database\nextract_srvtab, xst, ex_st: Extract service key table\nextract_v4_srvtab, xst4: Extract service key table\nlist_db, ldb: List database entries\nset_dbname, sdbn: Change database name\nenter_master_key, emk: Enter the master key for a database\nchange_working_directory, cwd, cd: Change working directory\nprint_working_directory, pwd: Print working directory\nlist_requests, lr: List available requests\nquit, exit: Exit program", TCL_STATIC); + return TCL_OK; +} + +int wrapper(func, interp, argc, argv) + void (*func)(); + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + (*func)(argc, argv); + return TCL_OK; +} + +int Tcl_AppInit(interp) + Tcl_Interp *interp; +{ + int argc; + char **argv, **mostly_argv; + char *interp_argv, *interp_argv0, *request; + Tcl_CmdInfo cmdInfo; + + if (Tcl_Init(interp) == TCL_ERROR) + return TCL_ERROR; + /* + * the following is, admittedly, sorta gross, but the only way + * to grab the original argc, argv once the interpreter is running + */ + interp_argv = Tcl_GetVar(interp, "argv", 0); + if (interp_argv == NULL) + return TCL_ERROR; + else if (Tcl_SplitList(interp, interp_argv, + &argc, &mostly_argv) != TCL_OK) + return TCL_ERROR; + interp_argv0 = Tcl_GetVar(interp, "argv0", 0); + if (interp_argv0 == NULL) + return TCL_ERROR; + if ((argv = (char **)malloc((argc + 1) * sizeof (char *))) == NULL) + return TCL_ERROR; + argv[0] = interp_argv0; + memcpy(argv + 1, mostly_argv, argc++ * sizeof (char *)); + /* + * set up a prompt + */ + if (Tcl_SetVar(interp, "tcl_prompt1", + "puts -nonewline \"kdb5_edit: \"", 0) == NULL) + return TCL_ERROR; + /* + * we don't want arbitrary programs to get exec'd by accident + */ + if (Tcl_SetVar(interp, "auto_noexec", "{}", 0) == NULL) + return TCL_ERROR; + request = kdb5_edit_Init(argc, argv); + Tcl_CallWhenDeleted(interp, doquit, + (ClientData)0); + Tcl_CreateCommand(interp, "quit", doquit, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "exit", doquit, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "list_requests", list_requests, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "lr", list_requests, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + if (Tcl_GetCommandInfo(interp, "cd", &cmdInfo)) { + Tcl_CreateCommand(interp, "cwd", cmdInfo.proc, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "change_working_directory", cmdInfo.proc, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + } + if (Tcl_GetCommandInfo(interp, "pwd", &cmdInfo)) { + Tcl_CreateCommand(interp, "print_working_directory", cmdInfo.proc, + (ClientData)0, + (Tcl_CmdDeleteProc *)0); + } + Tcl_CreateCommand(interp, "show_principal", wrapper, show_principal, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "show", wrapper, show_principal, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "add_new_key", wrapper, add_new_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ank", wrapper, add_new_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "change_pwd_key", wrapper, change_pwd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "cpw", wrapper, change_pwd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "add_rnd_key", wrapper, add_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ark", wrapper, add_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "change_rnd_key", wrapper, change_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "crk", wrapper, change_rnd_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "delete_entry", wrapper, delete_entry, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "delent", wrapper, delete_entry, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "extract_srvtab", wrapper, extract_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "xst", wrapper, extract_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ex_st", wrapper, extract_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "extract_v4_srvtab", wrapper, extract_v4_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "xv4st", wrapper, extract_v4_srvtab, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "list_db", wrapper, list_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ldb", wrapper, list_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "dump_db", wrapper, dump_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "ddb", wrapper, dump_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "load_db", wrapper, load_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "lddb", wrapper, load_db, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "set_dbname", wrapper, set_dbname, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "sdbn", wrapper, set_dbname, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "enter_master_key", wrapper, enter_master_key, + (Tcl_CmdDeleteProc *)0); + Tcl_CreateCommand(interp, "emk", wrapper, enter_master_key, + (Tcl_CmdDeleteProc *)0); + if (request && (Tcl_Eval(interp, request) == TCL_ERROR)) + return TCL_ERROR; + return TCL_OK; +} diff --git a/src/kadmin/dbutil/util.c b/src/kadmin/dbutil/util.c new file mode 100644 index 000000000..78de2cd6f --- /dev/null +++ b/src/kadmin/dbutil/util.c @@ -0,0 +1,155 @@ +/* + * admin/edit/util.c + * + * Copyright 1992 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * Utilities for kdb5_edit. + * + * Some routines derived from code contributed by the Sandia National + * Laboratories. Sandia National Laboratories also makes no + * representations about the suitability of the modifications, or + * additions to this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + */ + +#include "k5-int.h" +#include "./kdb5_edit.h" + +#if defined(sysvimp) || ( defined(mips) && defined(SYSTYPE_BSD43)) || (defined(vax) && !defined(ultrix)) +char * +strstr(s1, s2) +char *s1; +char *s2; +{ + int s2len; + int i; + char *temp_ptr; + + temp_ptr = s1; + for ( i = 0; i < strlen(s1); i++) { + if (memcmp(temp_ptr, s2, strlen(s2)) == 0) return(temp_ptr); + temp_ptr += 1; + } + return ((char *) 0); +} +#endif /* sysvimp */ + +void +parse_token(token_in, must_be_first_char, num_tokens, tokens_out) +char *token_in; +int *must_be_first_char; +int *num_tokens; +char *tokens_out; +{ + int i, j; + int token_count = 0; + + i = 0; + j = 0; + + /* Eliminate Up Front Asterisks */ + *must_be_first_char = 1; + for (i = 0; token_in[i] == '*'; i++) { + *must_be_first_char = 0; + } + + if (i == strlen(token_in)) { + *num_tokens = 0; + return; + } + + /* Fill first token_out */ + token_count++; + while ((token_in[i] != '*') && (token_in[i] != '\0')) { + tokens_out[j] = token_in[i]; + j++; + i++; + } + + if (i == strlen(token_in)) { + tokens_out[j] = '\0'; + *num_tokens = token_count; + return; + } + + /* Then All Subsequent Tokens */ + while (i < strlen(token_in)) { + if (token_in[i] == '*') { + token_count++; + tokens_out[j] = '\t'; + } else { + tokens_out[j] = token_in[i]; + } + i++; + j++; + } + tokens_out[j] = '\0'; + + if (tokens_out[j - 1] == '\t') { + token_count--; + tokens_out[j - 1] = '\0'; + } + + *num_tokens = token_count; + return; +} + +int +check_for_match(search_field, must_be_first_character, chk_entry, + num_tokens, type) +int must_be_first_character; +char *search_field; +krb5_db_entry *chk_entry; +int num_tokens; +int type; +{ + char token1[256]; + char *found1; + char token2[256]; + char *found2; + char token3[256]; + char *found3; + char *local_entry; + + local_entry = chk_entry->princ->data[type].data; + + token1[0] = token2[0] = token3[0] = '\0'; + + (void) sscanf(search_field, "%s\t%s\t%s", token1, token2, token3); + + found1 = strstr(local_entry, token1); + + if (must_be_first_character && (found1 != local_entry)) return(0); + + if (found1 && (num_tokens == 1)) return(1); + + if (found1 && (num_tokens > 1)) { + found2 = strstr(local_entry, token2); + if (found2 && (found2 > found1) && (num_tokens == 2)) return(1); + } + + if ((found2 > found1) && (num_tokens == 3)) { + found3 = strstr(local_entry, token3); + if (found3 && (found3 > found2) && (found2 > found1)) return(1); + } + return(0); +} + diff --git a/src/kadmin/export/ChangeLog b/src/kadmin/export/ChangeLog new file mode 100644 index 000000000..97a8078c4 --- /dev/null +++ b/src/kadmin/export/ChangeLog @@ -0,0 +1,19 @@ +Thu Jul 18 20:39:32 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Mon Jul 15 16:51:51 1996 Marc Horowitz <marc@mit.edu> + + * export.c (print_princ): return should return a value. + + * configure.in (USE_GSSAPI_LIBRARY): shared libraries require all + symbols to be resolved, so this needs to be here. + +Wed Jul 10 01:26:18 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.in, configure.in: added autoconf support + +Tue Jul 9 16:45:52 1996 Marc Horowitz <marc@mit.edu> + + * export.c: renamed <ovsec_admin/foo.h> to <kadm5/foo.h> + diff --git a/src/kadmin/export/Makefile.in b/src/kadmin/export/Makefile.in new file mode 100644 index 000000000..5fa282d89 --- /dev/null +++ b/src/kadmin/export/Makefile.in @@ -0,0 +1,20 @@ +CFLAGS = $(CCOPTS) $(DEFS) -I. $(LOCALINCLUDE) + +PROG = kadm5_export +OBJS = ovsec_adm_export.o export.o export_err.o + +all:: $(PROG) + +export_err.c export_err.h: $(srcdir)/export_err.et + +export.o: export_err.h +ovsec_adm_export.o: export_err.h + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/export/Makefile.ov b/src/kadmin/export/Makefile.ov new file mode 100644 index 000000000..83e8c7219 --- /dev/null +++ b/src/kadmin/export/Makefile.ov @@ -0,0 +1,24 @@ +TOP = .. +include $(TOP)/config.mk/template +# CFLAGS := $(CFLAGS) -Wall + +# The next line *shouldn't* work, because the : should be a ::. +# However, it does work, and if I change it to :: gmake does really +# weird things. +ovsec_adm_export: export_err.h + +depend:: export_err.h + +PROG := kadm5_export +OBJS := ovsec_adm_export.o export.o export_err.o +SRCS := ovsec_adm_export.c export.c export_err.et +ETABLES := export_err.et + +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDYN) $(LIBDB) + +expand ErrorTables +expand InstallAdmin +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/export/configure.in b/src/kadmin/export/configure.in new file mode 100644 index 000000000..992d591a1 --- /dev/null +++ b/src/kadmin/export/configure.in @@ -0,0 +1,12 @@ +AC_INIT(ovsec_adm_export.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/export/export.c b/src/kadmin/export/export.c new file mode 100644 index 000000000..3d41c4d9d --- /dev/null +++ b/src/kadmin/export/export.c @@ -0,0 +1,242 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <sys/time.h> +#include <stdio.h> +#include <malloc.h> +#include <unistd.h> + +#include <kadm5/adb.h> +#include "export_err.h" +#include "local.h" + +extern int errno; + +void print_key_data(FILE *f, krb5_key_data *key_data) +{ + int c; + + fprintf(f, "%d\t%d\t", key_data->key_data_type[0], + key_data->key_data_length[0]); + for(c = 0; c < key_data->key_data_length[0]; c++) + fprintf(f, "%02x ", + key_data->key_data_contents[0][c]); +} + +/* + * Function: print_princ + * + * Purpose: output osa_adb_princ_ent data in a human + * readable format (which is a format suitable for + * ovsec_adm_import consumption) + * + * Arguments: + * data (input) pointer to a structure containing a FILE * + * and a record counter. + * entry (input) entry to get dumped. + * <return value> void + * + * Requires: + * nuttin + * + * Effects: + * writes data to the specified file pointerp. + * + * Modifies: + * nuttin + * + */ +krb5_error_code print_princ(krb5_pointer data, krb5_db_entry *kdb) +{ + char *princstr; + int x, y, foundcrc, ret; + struct retdata *d; + krb5_tl_data tl_data; + osa_princ_ent_rec adb; + XDR xdrs; + + d = (struct retdata *) data; + + /* + * XXX Currently, lookup_tl_data always returns zero; it sets + * tl_data->tl_data_length to zero if the type isn't found. + * This should be fixed... + */ + /* + * XXX Should this function do nothing for a principal with no + * admin data, or print a record of "default" values? See + * comment in server_kdb.c to help decide. + */ + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + if ((ret = krb5_dbe_lookup_tl_data(d->context, kdb, &tl_data)) + || (tl_data.tl_data_length == 0)) + return(0); + + memset(&adb, 0, sizeof(adb)); + xdrmem_create(&xdrs, tl_data.tl_data_contents, + tl_data.tl_data_length, XDR_DECODE); + if (! xdr_osa_princ_ent_rec(&xdrs, &adb)) { + xdr_destroy(&xdrs); + return(OSA_ADB_XDR_FAILURE); + } + xdr_destroy(&xdrs); + + krb5_unparse_name(d->context, kdb->princ, &princstr); + fprintf(d->fp, "princ\t%s\t", princstr); + if(adb.policy == NULL) + fputc('\t', d->fp); + else + fprintf(d->fp, "%s\t", adb.policy); + fprintf(d->fp, "%x\t%d\t%d\t%d", adb.aux_attributes, + adb.old_key_len,adb.old_key_next, adb.admin_history_kvno); + + for (x = 0; x < adb.old_key_len; x++) { + if (! d->ovsec_compat) + fprintf(d->fp, "\t%d", adb.old_keys[x].n_key_data); + + foundcrc = 0; + for (y = 0; y < adb.old_keys[x].n_key_data; y++) { + krb5_key_data *key_data = &adb.old_keys[x].key_data[y]; + + if (d->ovsec_compat) { + if (key_data->key_data_type[0] != ENCTYPE_DES_CBC_CRC) + continue; + if (foundcrc) { + fprintf(stderr, error_message(EXPORT_DUP_DESCRC), + princstr); + continue; + } + foundcrc++; + } + fputc('\t', d->fp); + print_key_data(d->fp, key_data); + } + if (d->ovsec_compat && !foundcrc) + fprintf(stderr, error_message(EXPORT_NO_DESCRC), princstr); + } + + d->count++; + fputc('\n', d->fp); + free(princstr); + return(0); +} + +/* + * Function: print_policy + * + * Purpose: Print the contents of a policy entry in a human readable format. + * This format is also suitable for consumption for dbimport. + * + * Arguments: + * data (input) a pointer to a structure containing a FILE * + * and a record counter. + * entry (input) policy entry + * <return value> void + * + * Requires: + * nuttin + * + * Effects: + * writes data to file + * + * Modifies: + * nuttin + * + */ + +void +print_policy(void *data, osa_policy_ent_t entry) +{ + struct retdata *d; + + d = (struct retdata *) data; + fprintf(d->fp, "policy\t%s\t%d\t%d\t%d\t%d\t%d\t%d\n", entry->name, + entry->pw_min_life, entry->pw_max_life, entry->pw_min_length, + entry->pw_min_classes, entry->pw_history_num, + entry->policy_refcnt); + d->count++; + return; +} + +/* + * Function: export_principal + * + * Purpose: interates through the principal database with the + * osa_adb_iter_princ function which calls the print_princ + * routine with the FILE * of our filename. If the file + * name that gets passed in is NULL then we use stdout. + * + * Arguments: + * d (input) pointer to retdata. + * <return value> error code. 0 if sucsessful. + * + * Requires: + * nuttin + * + * Effects: + * calls osa_adb_iter_princ which calls print_princ + * + * Modifies: + * nuttin + * + */ +osa_adb_ret_t +export_principal(struct retdata *d, kadm5_config_params *params) +{ + int ret; + + if (ret = krb5_db_set_name(d->context, params->dbname)) + return ret; + + if (ret = krb5_db_init(d->context)) + return ret; + + if (ret = krb5_dbm_db_iterate(d->context, print_princ, d)) + return ret; + + if (ret = krb5_db_fini(d->context)) + return ret; + + return 0; +} + +/* + * Function: export_policy + * + * Purpose: iterates through the policy database with the + * osa_adb_iter_policy function which calls the print_policy + * routine with the FILE * of our filename. If the file name + * that gets passed in is NULL then we use stdout. + * + * Arguments: + * d (input) a pointer to retdata + * <return value> error code 0 if sucsessfull. + * + * Requires: + * nuttin + * + * Effects: + * calls osa_adb_iter_policy which calls print_policy + * + * Modifies: + * nuttin + * + */ +osa_adb_ret_t +export_policy(struct retdata *d, osa_adb_policy_t db) +{ + osa_adb_ret_t ret; + + if((ret = osa_adb_iter_policy(db, print_policy, (void *) d)) + != OSA_ADB_OK) { + return ret; + } + return OSA_ADB_OK; +} diff --git a/src/kadmin/export/export_err.et b/src/kadmin/export/export_err.et new file mode 100644 index 000000000..6c99a47b0 --- /dev/null +++ b/src/kadmin/export/export_err.et @@ -0,0 +1,19 @@ +error_table exp +error_code EXPORT_NO_ERR, "Database export complete, %d record%s processed.\n" +error_code EXPORT_UNK_OPTION, "Unknown Option\nUsage: ovsec_adm_export [filename]" +error_code EXPORT_OUTPUT_OPEN, "while opening output file" +error_code EXPORT_OUTPUT_CHMOD, "while changing mode of file" +error_code EXPORT_OUTPUT_STAT, "while trying to stat file" +error_code EXPORT_DATABASE_OPEN, "while opening database" +error_code EXPORT_PRINCIPAL, "while exporting principal database" +error_code EXPORT_POLICY, "while exporting policy database" +error_code EXPORT_LOCK, "while locking database" +error_code EXPORT_UNLOCK, "while unlocking database" +error_code EXPORT_CLOSE, "while closing database" +error_code EXPORT_SINGLE_RECORD, "" +error_code EXPORT_PLURAL_RECORDS, "s" +error_code EXPORT_NO_DESCRC, "Warning! No DES-CBC-CRC key for principal %s, cannot generate ovsec_adm_export-compatible record; skipping." +error_code EXPORT_DUP_DESCRC, "Warning! Multiple DES-CBC-CRC keys for principal %s; skipping duplicates." +error_code EXPORT_GET_CONFIG, "while retrieving configuration parameters" +end + diff --git a/src/kadmin/export/local.h b/src/kadmin/export/local.h new file mode 100644 index 000000000..3ec895ab2 --- /dev/null +++ b/src/kadmin/export/local.h @@ -0,0 +1,15 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +struct retdata { + krb5_context context; + FILE *fp; + int count; + int ovsec_compat; +}; + +osa_adb_ret_t export_principal(struct retdata *, kadm5_config_params *); +osa_adb_ret_t export_policy(struct retdata *d, osa_adb_policy_t); diff --git a/src/kadmin/export/ovsec_adm_export.c b/src/kadmin/export/ovsec_adm_export.c new file mode 100644 index 000000000..ded21ba55 --- /dev/null +++ b/src/kadmin/export/ovsec_adm_export.c @@ -0,0 +1,159 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <kadm5/adb.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include "export_err.h" +#include "local.h" + +int +main(int argc, char *argv[]) +{ + char *filename; + struct retdata d; + struct stat statb; + int ret, fd; + time_t now; + char *whoami = argv[0]; + osa_adb_policy_t policy_db; + kadm5_config_params params; + + memset(¶ms, 0, sizeof(params)); + memset(&d, 0, sizeof(d)); + + filename = NULL; + initialize_exp_error_table(); + initialize_adb_error_table(); + krb5_init_context(&d.context); + krb5_init_ets(d.context); + + while(--argc) { + if(*++argv == NULL) + break; + if(!strcmp(*argv, "-princ")) { + params.dbname = *++argv; + params.mask |= KADM5_CONFIG_DBNAME; + continue; + } + if(!strcmp(*argv, "-policy")) { + params.admin_dbname = *++argv; + params.mask |= KADM5_CONFIG_ADBNAME; + continue; + } + if(!strcmp(*argv, "-ovsec")) { + d.ovsec_compat++; + continue; + } + if (*argv[0] == '-') { + com_err(whoami, EXPORT_UNK_OPTION, NULL); + exit(2); + } + if(filename == NULL) + filename = *argv; + else { + com_err(whoami, EXPORT_UNK_OPTION, NULL); + exit(2); + } + } + + if (ret = kadm5_get_config_params(d.context, NULL, NULL, ¶ms, + ¶ms)) { + com_err(whoami, ret, error_message(EXPORT_GET_CONFIG)); + exit(2); + } +#define REQUIRED_MASK (KADM5_CONFIG_DBNAME | \ + KADM5_CONFIG_ADBNAME) + if ((params.mask & REQUIRED_MASK) != REQUIRED_MASK) { + com_err(whoami, KADM5_BAD_SERVER_PARAMS, + error_message(EXPORT_GET_CONFIG)); + exit(2); + } + + if(filename != NULL) { + if((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0400)) == -1) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_OPEN), filename); + exit(2); + } + if(fstat(fd, &statb) == -1) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_STAT), filename); + exit(2); + } + if(S_ISREG(statb.st_mode)) { + int mask = umask(0); + (void) umask(mask); + if (fchmod(fd, (0400 & ~mask)) == -1) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_CHMOD), filename); + exit(2); + } + } + if ((d.fp = fdopen(fd, "w")) == NULL) { + com_err(whoami, errno, "%s (%s)", + error_message(EXPORT_OUTPUT_OPEN), filename); + exit(2); + } + } else d.fp = stdout; + + if((ret = osa_adb_open_policy(&policy_db, ¶ms)) != OSA_ADB_OK) { + com_err(argv[0], ret, error_message(EXPORT_DATABASE_OPEN)); + exit(2); + } + if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_SHARED) != OSA_ADB_OK)) { + com_err(argv[0], ret, error_message(EXPORT_LOCK)); + exit(2); + } + + d.count = 0; + + now = time(NULL); + if (d.ovsec_compat) + fprintf(d.fp, "OpenV*Secure V1.0\t%s", ctime(&now)); + else + fprintf(d.fp, "Kerberos KADM5 database V2.0\t%s", + ctime(&now)); + + if ((ret = export_policy(&d, policy_db)) != OSA_ADB_OK) { + com_err(whoami, ret, "%s (%s)", error_message(EXPORT_POLICY), + params.admin_dbname); + exit(2); + } + if ((ret = export_principal(&d, ¶ms)) != + OSA_ADB_OK) { + com_err(whoami, ret, "%s (%s)", error_message(EXPORT_PRINCIPAL), + params.dbname); + exit(2); + } + fprintf(d.fp, "End of Database\t%d\trecords\n", d.count); + + if ((ret = osa_adb_release_lock(policy_db)) != OSA_ADB_OK) { + com_err(argv[0], ret, error_message(EXPORT_UNLOCK)); + exit(2); + } + if ((ret = osa_adb_close_policy(policy_db)) != OSA_ADB_OK) { + com_err(argv[0], ret, error_message(EXPORT_CLOSE)); + exit(2); + } + + fprintf(stderr, error_message(EXPORT_NO_ERR), d.count, + (d.count == 1) ? error_message(EXPORT_SINGLE_RECORD) : + error_message(EXPORT_PLURAL_RECORDS)); + exit(0); +} + + + diff --git a/src/kadmin/export/unit-test/ChangeLog b/src/kadmin/export/unit-test/ChangeLog new file mode 100644 index 000000000..5db33c7c5 --- /dev/null +++ b/src/kadmin/export/unit-test/ChangeLog @@ -0,0 +1,5 @@ +Mon Jul 15 16:55:03 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.ov (unit-test-body), dotest.sh: ovsec_adm_*port is now + kadm5_*port + diff --git a/src/kadmin/export/unit-test/Makefile.ov b/src/kadmin/export/unit-test/Makefile.ov new file mode 100644 index 000000000..25b1bf6c7 --- /dev/null +++ b/src/kadmin/export/unit-test/Makefile.ov @@ -0,0 +1,19 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(SAVE_FILES) + $(FIX_CONF_FILES) + $(INITDB) + +unit-test-body:: + $(RUNTEST) EXPORT=../kadm5_export --tool export + +unit-test-cleanup:: + $(RESTORE_FILES) diff --git a/src/kadmin/export/unit-test/add-to-db.sh b/src/kadmin/export/unit-test/add-to-db.sh new file mode 100644 index 000000000..c50541546 --- /dev/null +++ b/src/kadmin/export/unit-test/add-to-db.sh @@ -0,0 +1,55 @@ +#!/bin/sh + +REALM=SECURE-TEST.OV.COM; export REALM +DUMMY=${TESTDIR=$TOP/testing}; export TESTDIR +DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL +DUMMY=${TCLUTIL=$TESTDIR/tcl/util.t}; export TCLUTIL + +$SRVTCL <<'EOF' +global r + +source $env(TCLUTIL) +set r $env(REALM) + +proc newpol { pname } { + puts stdout [ovsec_kadm_create_policy $server_handle [simple_policy "$pname"] {OVSEC_KADM_POLICY}] +} + +proc newprinc { name } { + global r + puts stdout [ovsec_kadm_create_principal $server_handle [simple_principal "$name@$r"] {OVSEC_KADM_PRINCIPAL} $name] +} + +proc chpass { princ pass } { + global server_handle + puts stdout [ovsec_kadm_chpass_principal $server_handle "$princ" "$pass"] +} + +puts stdout [ovsec_kadm_init $env(SRVTCL) mrroot null $r $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle] + +puts stdout [ovsec_kadm_create_policy $server_handle "export_pwhist 0 0 0 0 10 0" {OVSEC_KADM_POLICY OVSEC_KADM_PW_HISTORY_NUM}] + +### Commented out since this isn't going to work for the december beta +#newprinc "export_with space" +#newprinc "export_with\"dquote" +#newprinc "export_with\nnewline" + +puts stdout [ovsec_kadm_create_principal $server_handle [princ_w_pol export_hist1@$r export_pwhist] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} hist1] + +chpass export_hist1@$r hist1_a + +puts stdout [ovsec_kadm_create_principal $server_handle [princ_w_pol export_hist10@$r export_pwhist] {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} hist10] + +chpass export_hist10@$r hist10_a +chpass export_hist10@$r hist10_b +chpass export_hist10@$r hist10_c +chpass export_hist10@$r hist10_d +chpass export_hist10@$r hist10_e +chpass export_hist10@$r hist10_f +chpass export_hist10@$r hist10_g +chpass export_hist10@$r hist10_h +chpass export_hist10@$r hist10_i + +puts stdout [ovsec_kadm_destroy $server_handle] + +EOF diff --git a/src/kadmin/export/unit-test/config/unix.exp b/src/kadmin/export/unit-test/config/unix.exp new file mode 100644 index 000000000..e8d852f89 --- /dev/null +++ b/src/kadmin/export/unit-test/config/unix.exp @@ -0,0 +1,36 @@ +# +# export_version -- extract and print the version number of export +# + +proc export_version {} { + global EXPORT + set tmp [exec ident $EXPORT] + if [regexp {Header: .*export.c,v ([0-9]+\.[0-9]+)} $tmp \ + dummy version] then { + clone_output "$EXPORT version $version\n" + } else { + clone_output "$EXPORT version <unknown>\n" + } +} +# +# export_load -- loads the program +# +proc export_load {} { + # +} + +# export_exit -- clean up and exit +proc export_exit {} { + # +} + +# +# export_start -- start export running +# +proc export_start { args } { + global EXPORT + global spawn_id + + verbose "% $EXPORT $args" 1 + eval spawn $EXPORT $args +} diff --git a/src/kadmin/export/unit-test/dotest.sh b/src/kadmin/export/unit-test/dotest.sh new file mode 100644 index 000000000..53d4fe0ab --- /dev/null +++ b/src/kadmin/export/unit-test/dotest.sh @@ -0,0 +1,75 @@ +#!/bin/sh + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${BSDDB_DUMP=$TESTDIR/util/bsddb_dump} +DUMMY=${KDB5_EDIT=$TOP/../admin/edit/kdb5_edit} + +DPRINC=/tmp/dbdump.princ +DPOL=/tmp/dbdump.policy + +DPRINC1=$DPRINC.1 +DPRINC2=$DPRINC.2 + +DPOL1=$DPOL.1 +DPOL2=$DPOL.2 + +DEXPORT=/tmp/dbexport + +./add-to-db.sh + +rm -f $DEXPORT +../kadm5_export > $DEXPORT + +if $KDB5_EDIT -R ddb | sort > $DPRINC1; then + : +else + echo "error dumping princ.1" +fi +if $BSDDB_DUMP /krb5/kadb5 | sort > $DPOL1; then + : +else + echo "error dumping policy.1" +fi + +rm -f /krb5/kadb5* +touch /krb5/ovsec_adm.lock + +../../import/kadm5_import < $DEXPORT + +if $KDB5_EDIT -R ddb | sort > $DPRINC2; then + : +else + echo "error dumping princ.2" +fi +if $BSDDB_DUMP /krb5/kadb5 | sort > $DPOL2; then + : +else + echo "error dumping policy.2" +fi + + +status=0 + +if test -s $DPRINC1 && \ + test -s $DPRINC2 && \ + cmp -s $DPRINC1 $DPRINC2; then + echo "export/import principal db succeeded" +else + echo "export/import principal db failed" + status=1 +fi + +if test -s $DPOL1 && \ + test -s $DPOL2 && \ + cmp -s $DPOL1 $DPOL2; then + echo "export/import policy db succeeded" +else + echo "export/import policy db failed" + status=1 +fi + +if [ $status -eq 0 ]; then + rm -f $DPRINC* $DPOL* $DEXPORT +fi + +exit $status diff --git a/src/kadmin/export/unit-test/export.0/dotest.exp b/src/kadmin/export/unit-test/export.0/dotest.exp new file mode 100644 index 000000000..93ac21250 --- /dev/null +++ b/src/kadmin/export/unit-test/export.0/dotest.exp @@ -0,0 +1,29 @@ +# +# $Id$ +# + +verbose "starting test: dotest.sh" + +spawn ./dotest.sh + +set timeout 60 + +expect { + -re "error dumping (princ|policy)\.(\[12\])" + { fail $expect_out(0,string); exp_continue } + -re "export/import (principal|policy) db (failed|succeeded)" + { + if {![string compare $expect_out(2,string) failed]} { + fail $expect_out(0,string) + } else { + pass $expect_out(0,string) + } + exp_continue + } + eof break + timeout { fail "timeout"; close } +} + +set w [wait] + +verbose "% Exit $w" diff --git a/src/kadmin/export/unit-test/export.0/output.exp b/src/kadmin/export/unit-test/export.0/output.exp new file mode 100644 index 000000000..6e0d4144b --- /dev/null +++ b/src/kadmin/export/unit-test/export.0/output.exp @@ -0,0 +1,43 @@ +# +# $Id$ +# + +set timeout 30 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +exec rm -f /tmp/dbexport + +export_win "B.25: General success" /tmp/dbexport + +check_mode "B.26" /tmp/dbexport 0400 + +if {[catch "exec chmod 666 /tmp/dbexport" output]} { + unresolved "B.27: can't chmod /tmp/dbexport: $output" +} else { + export_win "prep for B.27" /tmp/dbexport + check_mode "B.27" /tmp/dbexport 0400 + exec rm -f /tmp/dbexport +} + +proc test28 {} { + if {[catch "file stat /dev/null stats" output]} { + unresolved "B.28: can't stat /dev/null: $output" + return + } + set stats(mode) [expr $stats(mode) & 07777] + if {$stats(mode) == [expr 0400]} { + if {[catch "exec chmod 666 /dev/null" output]} { + unresolved "B.28: can't chmod /dev/null: $output" + return + } + set stats(mode) [expr 0666] + } + export_win "prep for B.28" /dev/null + check_mode "B.28" /dev/null $stats(mode) +} +test28 diff --git a/src/kadmin/export/unit-test/export.0/usage.exp b/src/kadmin/export/unit-test/export.0/usage.exp new file mode 100644 index 000000000..9a592c9b8 --- /dev/null +++ b/src/kadmin/export/unit-test/export.0/usage.exp @@ -0,0 +1,25 @@ +# +# $Id$ +# + +set timeout 30 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +export_lose "A.9: output file not writable" /foo/bar/baz \ + "No such file or directory while opening output file" + +export_lose "A.10: two arguments" {foo bar} \ + "Usage:" + +# XXX this depends on this being the last test run + +system {rm /krb5/kadb5} + +export_lose "A.2: /krb5 doesn't exist" /tmp/dbexport \ + "No such file or directory while opening database" + diff --git a/src/kadmin/export/unit-test/helpers.exp b/src/kadmin/export/unit-test/helpers.exp new file mode 100644 index 000000000..c53630f4b --- /dev/null +++ b/src/kadmin/export/unit-test/helpers.exp @@ -0,0 +1,126 @@ +# +# $Id$ +# + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + +proc myfail { comment } { + global mytest_name + global mytest_status + wait + fail "$mytest_name: $comment" + set mytest_status 1 +} + +proc mypass {} { +} + +## +## When you expect on an id, and eof is detected, the spawn_id is closed. +## It may be waited for, but calling expect or close on this id is an ERROR! +## + +proc mytest { name kpargs status args } { + global spawn_id + global timeout + global mytest_name + global mytest_status + global wait_error_index wait_errno_index wait_status_index + + verbose "starting test: $name" + + set mytest_name "$name" + + eval export_start $kpargs + + # at the end, eof is success + + lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + + # for each test argument.... + # rep invariant: when this foreach ends, the id is close'd, but + # not wait'ed. + + foreach test $args { + set mytest_status 0 + + # treat the arg as an expect parameter + # if failure, the process will be closed and waited. + + uplevel 1 "expect { + $test + timeout { close; myfail \"timeout\"} + eof { myfail \"eof read before expected message string\" } + }" + + if {$mytest_status == 1} { return } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 1 + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { ((![string compare $status zero]) && + ([lindex $ret $wait_status_index] == 0)) || + ((![string compare $status nonzero]) && + ([lindex $ret $wait_status_index] != 0)) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } +} + +proc export_win { name args } { + mytest "$name" "$args" zero { + -re "Database export complete, \[0-9\]+ records processed." + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + +proc export_lose { name args error } { + mytest "$name" "$args" nonzero { + -re "Database export complete, \[0-9\]+ records processed." + { close; myfail "unexpected success" } + -re "ovsec_adm_export: .*$error" + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + +proc check_mode { test file mode } { + if {[catch "file stat $file stats" output]} { + unresolved "$test: can't stat $file: $output" + } else { + set stats(mode) [format "%o" [expr $stats(mode) & 07777]] + set mode [format "%o" [expr $mode]] + if {$stats(mode) != $mode} { + fail "$test: wrong mode ($stats(mode) should be $mode)" + } else { + verbose "$test: file $file has mode $mode" + pass $test + } + } +} diff --git a/src/kadmin/import/ChangeLog b/src/kadmin/import/ChangeLog new file mode 100644 index 000000000..ca7c0bc2d --- /dev/null +++ b/src/kadmin/import/ChangeLog @@ -0,0 +1,17 @@ +Thu Jul 18 20:40:32 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Mon Jul 15 16:58:08 1996 Marc Horowitz <marc@mit.edu> + + * configure.in (USE_GSSAPI_LIBRARY): shared libraries require all + symbols to be resolved, so this needs to be here. + +Wed Jul 10 01:26:47 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.in, configure.in: added autoconf support + +Tue Jul 9 17:12:08 1996 Marc Horowitz <marc@mit.edu> + + * ovsec_adm_import.c, import.c: renamed <ovsec_admin/foo.h> to + <kadm5/foo.h> diff --git a/src/kadmin/import/Makefile.in b/src/kadmin/import/Makefile.in new file mode 100644 index 000000000..2f15e706f --- /dev/null +++ b/src/kadmin/import/Makefile.in @@ -0,0 +1,18 @@ +CFLAGS = $(CCOPTS) $(DEFS) -I. $(LOCALINCLUDE) + +PROG = kadm5_import +OBJS = ovsec_adm_import.o import.o import_err.o strtok.o misc.o + +all:: $(PROG) + +import_err.c import_err.h: $(srcdir)/import_err.et +$(OBJS): import_err.h + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/import/Makefile.ov b/src/kadmin/import/Makefile.ov new file mode 100644 index 000000000..f2750313d --- /dev/null +++ b/src/kadmin/import/Makefile.ov @@ -0,0 +1,24 @@ +TOP = .. +include $(TOP)/config.mk/template +CFLAGS := $(CFLAGS) + +# The next line *shouldn't* work, because the : should be a ::. +# However, it does work, and if I change it to :: gmake does really +# weird things. +ovsec_adm_import: import_err.h + +depend:: import_err.h + +PROG := kadm5_import +OBJS := ovsec_adm_import.o import_err.o import.o strtok.o misc.o +SRCS := ovsec_adm_import.c import.c import_err.et strtok.c misc.c +ETABLES := import_err.et + +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDYN) $(LIBDB) + +expand ErrorTables +expand InstallAdmin +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/import/configure.in b/src/kadmin/import/configure.in new file mode 100644 index 000000000..ad66ebd7c --- /dev/null +++ b/src/kadmin/import/configure.in @@ -0,0 +1,12 @@ +AC_INIT(ovsec_adm_import.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/import/import.c b/src/kadmin/import/import.c new file mode 100644 index 000000000..b1a8c0a76 --- /dev/null +++ b/src/kadmin/import/import.c @@ -0,0 +1,421 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <memory.h> + +#include <kadm5/adb.h> +#include "import_err.h" +#include "import.h" + +#define LINESIZE 32768 /* XXX */ +#define PLURAL(count) (((count) == 1) ? error_message(IMPORT_SINGLE_RECORD) : error_message(IMPORT_PLURAL_RECORDS)) + +int parse_pw_hist_ent(current, hist, ovsec_compat) + char *current; + osa_pw_hist_ent *hist; + int ovsec_compat; +{ + int tmp, i, j, ret; + char *cp; + + ret = 0; + if (!ovsec_compat) { + if ((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + return IMPORT_FAILED; + } + hist->n_key_data = atoi(cp); + } else + hist->n_key_data = 1; + + hist->key_data = (krb5_key_data *) malloc(hist->n_key_data * + sizeof(krb5_key_data)); + if (hist->key_data == NULL) + return ENOMEM; + memset(hist->key_data, 0, sizeof(krb5_key_data)*hist->n_key_data); + + for (i = 0; i < hist->n_key_data; i++) { + krb5_key_data *key_data = &hist->key_data[i]; + + key_data->key_data_ver = 1; + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_type[0] = atoi(cp); + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_length[0] = atoi(cp); + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + if(!(key_data->key_data_contents[0] = + (krb5_octet *) malloc(key_data->key_data_length[0]+1))) { + ret = ENOMEM; + goto done; + } + for(j = 0; j < key_data->key_data_length[0]; j++) { + if(sscanf(cp, "%02x", &tmp) != 1) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + key_data->key_data_contents[0][j] = tmp; + cp = strchr(cp, ' ') + 1; + } + } + +done: + return ret; +} + + + +/* + * Function: parse_principal + * + * Purpose: parse principal line in db dump file + * + * Arguments: + * <return value> 0 on sucsess, error code on failure + * + * Requires: + * principal database to be opened. + * nstrtok(3) to have a valid buffer in memory. + * + * Effects: + * [effects] + * + * Modifies: + * [modifies] + * + */ +int parse_principal(context, ovsec_compat) + krb5_context context; + int ovsec_compat; +{ + XDR xdrs; + osa_princ_ent_t rec; + osa_adb_ret_t ret; + krb5_tl_data tl_data; + krb5_principal princ; + krb5_db_entry kdb; + char *current; + char *cp; + int tmp, x, i, one, more; + + if((cp = nstrtok((char *) NULL, "\t")) == NULL) + return IMPORT_BAD_FILE; + if((rec = (osa_princ_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL) + return ENOMEM; + memset(rec, 0, sizeof(osa_princ_ent_rec)); + if((ret = krb5_parse_name(context, cp, &princ))) + goto done; + krb5_unparse_name(context, princ, ¤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 = (char *) malloc(strlen(cp)+1)) == NULL) { + ret = ENOMEM; + goto done; + } + strcpy(rec->policy, cp); + } else rec->policy = NULL; + } + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->aux_attributes = strtol(cp, (char **)NULL, 16); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->old_key_len = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->old_key_next = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", current); + ret = IMPORT_FAILED; + goto done; + } + rec->admin_history_kvno = atoi(cp); + if (! rec->old_key_len) { + rec->old_keys = NULL; + } else { + if(!(rec->old_keys = (osa_pw_hist_ent *) + malloc(sizeof(osa_pw_hist_ent) * rec->old_key_len))) { + ret = ENOMEM; + goto done; + } + memset(rec->old_keys,0, + sizeof(osa_pw_hist_ent) * rec->old_key_len); + for(x = 0; x < rec->old_key_len; x++) + parse_pw_hist_ent(current, &rec->old_keys[x], ovsec_compat); + } + + xdralloc_create(&xdrs, XDR_ENCODE); + if (! xdr_osa_princ_ent_rec(&xdrs, rec)) { + xdr_destroy(&xdrs); + ret = OSA_ADB_XDR_FAILURE; + goto done; + } + + tl_data.tl_data_type = KRB5_TL_KADM_DATA; + tl_data.tl_data_length = xdr_getpos(&xdrs); + tl_data.tl_data_contents = xdralloc_getdata(&xdrs); + + one = 1; + ret = krb5_db_get_principal(context, princ, &kdb, &one, + &more); + if (ret) + goto done; + + if (ret = krb5_dbe_update_tl_data(context, &kdb, + &tl_data)) + goto done; + + if (ret = krb5_db_put_principal(context, &kdb, &one)) + goto done; + + xdr_destroy(&xdrs); + +done: + free(current); + krb5_free_principal(context, princ); + osa_free_princ_ent(rec); + return ret; +} + +/* + * Function: parse-policy + * + * Purpose: parse the ascii text of a dump file and turn it into + * a policy_ent_rec. + * + * Arguments: + * <return value> 0 on sucsess, error code on failure; + * + * Requires: + * nstrtok to have a buffer in memory + * + * Effects: + * write data out to db. + * + * Modifies: + * policy db. + * + */ +int +parse_policy(pol_db) + osa_adb_policy_t pol_db; +{ + osa_policy_ent_t rec; + char *cp; + osa_adb_ret_t ret; + + if((rec = (osa_policy_ent_t) malloc(sizeof(osa_princ_ent_rec))) == NULL) + return ENOMEM; + memset(rec, 0, sizeof(osa_princ_ent_rec)); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + ret = IMPORT_BAD_FILE; + goto done; + } + if((rec->name = (char *) malloc(strlen(cp) + 1)) == NULL) { + ret = ENOMEM; + goto done; + } + strcpy(rec->name, cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_min_life = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_max_life = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_min_length = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_min_classes = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->pw_history_num = atoi(cp); + if((cp = nstrtok((char *) NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_RECORD, "%s", rec->name); + ret = IMPORT_FAILED; + goto done; + } + rec->policy_refcnt = atoi(cp); + ret = osa_adb_create_policy(pol_db, rec); +done: + osa_free_policy_ent(rec); + return ret; +} + +/* + * Function: import-file + * + * Purpose: import a flat ascii file and convert it to a db file. + * + * Arguments: + * fp (input) file pointer to read db. + * <return value> 0 or error code on error. + * + * Requires: + * fp be valid + * + * Effects: + * calls appropriate routine to write out db files. + * + * Modifies: + * database file. + * + */ + +int import_file(krb5_context context, FILE *fp, int merge_princs, + osa_adb_policy_t pol_db) +{ + + int count = 0; + int errcnt = 0; + int ret = 0; + int found_footer = 0; + int file_count; + int ovsec_compat; + char line[LINESIZE]; + char version[BUFSIZ]; + char date[BUFSIZ]; + char *cp; + + if(fgets(line, LINESIZE, fp) == NULL) + return IMPORT_BAD_FILE; + if ((sscanf(line, "%[^\t]\t %[^\t]", version, date)) != 2) + return IMPORT_BAD_FILE; + if(!strcmp(version, VERSION_OVSEC_10)) + ovsec_compat++; + else if (strcmp(version, VERSION_KADM5_20)) + return IMPORT_BAD_VERSION; + + while(fgets(line, LINESIZE, fp) != (char *) NULL) { + if(found_footer) { + com_err(NULL, IMPORT_EXTRA_DATA, NULL); + break; + } + cp = nstrtok(line, "\t"); + if(!strcasecmp(cp, "princ")) { + if(merge_princs && + (ret = parse_principal(context, ovsec_compat)) != OSA_ADB_OK) { + if(ret == IMPORT_FAILED) { + if(!confirm()) + break; + else { + errcnt++; + continue; + } + } else break; + } else { + count++; + continue; + } + } + if(!strcasecmp(cp, "policy")) { + if((ret = parse_policy(pol_db)) != OSA_ADB_OK) { + if(ret == IMPORT_FAILED) { + if(!confirm()) + break; + else { + errcnt++; + continue; + } + } else break; + } else { + count++; + continue; + } + } + if(!strcasecmp(cp, "end of database")) { + found_footer = 1; + } else { + com_err(NULL, IMPORT_BAD_TOKEN, "%s", cp); + if(!confirm()) { + ret = IMPORT_BAD_FILE; + break; + } else { + errcnt++; + continue; + } + } + } + if(ret == OSA_ADB_OK && found_footer) { + if((cp = nstrtok(NULL, "\t")) == NULL) { + com_err(NULL, IMPORT_BAD_FOOTER, NULL); + if(!confirm()) + ret = IMPORT_BAD_FOOTER; + else + ret = OSA_ADB_OK; + } else + file_count = atoi(cp); + if(file_count != (count + errcnt)) { + fprintf(stderr, error_message(IMPORT_COUNT_MESSAGE), file_count, + PLURAL(file_count), count, PLURAL(count)); + if(!confirm()) + ret = IMPORT_MISMATCH_COUNT; + else + ret = OSA_ADB_OK; + } else fprintf(stderr, error_message(IMPORT_NO_ERR), count, + PLURAL(count)); + } else if(ret == OSA_ADB_OK && !found_footer) { + com_err(NULL, IMPORT_BAD_FOOTER, NULL); + if(!confirm()) + ret = IMPORT_BAD_FOOTER; + else + ret = OSA_ADB_OK; + } + + return ret; +} + diff --git a/src/kadmin/import/import.h b/src/kadmin/import/import.h new file mode 100644 index 000000000..e0c3fedf9 --- /dev/null +++ b/src/kadmin/import/import.h @@ -0,0 +1,40 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.3 1996/07/22 20:26:27 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.2.4.1 1996/07/18 03:02:23 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.2.2.1 1996/06/20 21:48:24 marc + * File added to the repository on a branch + * + * Revision 1.2 1996/06/05 20:52:28 bjaspan + * initial hack at porting to mit kerberos + * + * Revision 1.1 1993/11/17 06:13:23 shanzer + * Initial revision + * + */ + +#include <stdio.h> + +/* + * XXX These should be defined somewhere so import and export get the + * same value. + */ +#define VERSION_OVSEC_10 "OpenV*Secure V1.0" +#define VERSION_KADM5_20 "Kerberos KADM5 database V2.0" + +int import_file(krb5_context context, FILE *fp, int merge_princs, + osa_adb_policy_t pol_db); +int confirm(void); +char *nstrtok(char *str, char *delim); diff --git a/src/kadmin/import/import_err.et b/src/kadmin/import/import_err.et new file mode 100644 index 000000000..e091fe33c --- /dev/null +++ b/src/kadmin/import/import_err.et @@ -0,0 +1,26 @@ +error_table imp +error_code IMPORT_NO_ERR, "Successfully imported %d record%s.\n" +error_code IMPORT_BAD_FILE, "Input not recognized as database dump" +error_code IMPORT_BAD_TOKEN, "Bad token in dump file." +error_code IMPORT_BAD_VERSION, "Bad version in dump file" +error_code IMPORT_BAD_RECORD, "Defective record encountered: " +error_code IMPORT_BAD_FOOTER, "Truncated input file detected." +error_code IMPORT_FAILED, "Import of dump failed" +error_code IMPORT_COUNT_MESSAGE, "Mismatched record count: %d record%s indicated, %d record%s scanned.\n" +error_code IMPORT_MISMATCH_COUNT, "Number of records imported does not match count" +error_code IMPORT_UNK_OPTION, "Unknown command line option.\nUsage: ovsec_adm_import [filename]" +error_code IMPORT_WARN_DB, "Warning -- continuing to import will overwrite existing databases!" +error_code IMPORT_RENAME_FAILED, "Database rename Failed!!" +error_code IMPORT_EXTRA_DATA, "Extra data after footer is ignored." +error_code IMPORT_CONFIRM, "Proceed <y|n>?" +error_code IMPORT_OPEN_DUMP, "while opening input file" +error_code IMPORT_IMPORT, "while importing databases" +error_code IMPORT_TTY, "cannot open /dev/tty!!" +error_code IMPORT_RENAME_OPEN, "while opening databases" +error_code IMPORT_RENAME_LOCK, "while acquiring permanent lock" +error_code IMPORT_RENAME_UNLOCK, "while releasing permanent lock" +error_code IMPORT_RENAME_CLOSE, "while closing databases" +error_code IMPORT_SINGLE_RECORD, "" +error_code IMPORT_PLURAL_RECORDS, "s" +error_code IMPORT_GET_PARAMS, "while retrieving configuration parameters" +end diff --git a/src/kadmin/import/misc.c b/src/kadmin/import/misc.c new file mode 100644 index 000000000..bc58c7e7f --- /dev/null +++ b/src/kadmin/import/misc.c @@ -0,0 +1,95 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.4 1996/07/22 20:26:31 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.3.4.1 1996/07/18 03:02:26 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.3.2.1 1996/06/20 21:48:39 marc + * File added to the repository on a branch + * + * Revision 1.3 1994/04/11 23:52:10 jik + * Sandbox: + * + * Include <com_err.h> to get the declaration of error_message. + * + * Revision 1.3 1994/03/29 21:18:54 jik + * Include <com_err.h> to get the declaration of error_message. + * + * Revision 1.2 1993/12/21 18:59:25 shanzer + * make sure we prompt for input from /dev/tty + * + * Revision 1.1 1993/11/14 23:51:04 shanzer + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <stdio.h> +#include <com_err.h> /* for error_message() */ +#include "import_err.h" + +#ifndef TRUE +#define TRUE (1); +#endif +#ifndef FALSE +#define FALSE (0); +#endif + +/* + * Function: confirm + * + * Purpose: ask a yes or no question you must answer + * with a 'y|n|Y|n' + * + * Arguments: + * (input) none + * <return value> 1 if answered yes. 0 if no. + * + * Requires: + * IMPORT_CONFIRM be be defined. and com_err be init. + * + * Effects: + * none + * + * Modifies: + * nuttin + * + */ + +int +confirm(void) +{ + char buf[BUFSIZ]; /* can we say overkill ... */ + FILE *fp; + + if ((fp = fopen("/dev/tty", "r")) == NULL) { + fprintf(stderr, error_message(IMPORT_TTY)); + return FALSE; + } + while(1) { + fprintf(stderr, error_message(IMPORT_CONFIRM)); + fgets(buf, BUFSIZ, fp); + if(buf[0] == 'y' || buf[0] == 'Y') { + fclose(fp); + return TRUE; + } + if(buf[0] == 'n' || buf[0] == 'N') { + fclose(fp); + return FALSE; + } + } +} + diff --git a/src/kadmin/import/ovsec_adm_import.c b/src/kadmin/import/ovsec_adm_import.c new file mode 100644 index 000000000..4ca920434 --- /dev/null +++ b/src/kadmin/import/ovsec_adm_import.c @@ -0,0 +1,164 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <kadm5/adb.h> +#include "import_err.h" +#include "import.h" + +#define TMP_POLICY_FMT "/krb5/#ovsec_import_policy.%d" + +int +main(int argc, char *argv[]) +{ + char *filename, + *whoami; + int ret, merge_princs; + FILE *fp; + osa_adb_policy_t policy_db; + char pol_dbfile[BUFSIZ]; + kadm5_config_params params; + krb5_context context; + + filename = NULL; + initialize_imp_error_table(); + initialize_adb_error_table(); + krb5_init_context(&context); + krb5_init_ets(context); + + whoami = argv[0]; + merge_princs = 0; + while(--argc) { + if(*++argv == NULL) + break; + if (!strcmp(*argv, "-merge_princs")) { + merge_princs++; + continue; + } + if (*argv[0] == '-') { + com_err(whoami, IMPORT_UNK_OPTION, NULL); + exit(2); + } + if(filename == NULL) + filename = *argv; + else { + com_err(whoami, IMPORT_UNK_OPTION, NULL); + exit(2); + } + } + if(filename != NULL) { + if ((fp = fopen(filename, "r")) == NULL) { + com_err(whoami, errno, "%s (%s)", error_message(IMPORT_OPEN_DUMP), + filename); + exit(2); + } + } else fp = stdin; + + sprintf(pol_dbfile, TMP_POLICY_FMT, getpid()); + if(access(pol_dbfile, F_OK) == 0) { + if(unlink(pol_dbfile) != 0) + return errno; + } + + params.mask = 0; + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + com_err(whoami, ret, error_message(IMPORT_GET_PARAMS)); + exit(2); + } +#define REQUIRED_MASK (KADM5_CONFIG_DBNAME | \ + KADM5_CONFIG_ADBNAME) + if ((params.mask & REQUIRED_MASK) != REQUIRED_MASK) { + com_err(whoami, KADM5_BAD_SERVER_PARAMS, + error_message(IMPORT_GET_PARAMS)); + exit(2); + } + /* + * This trick lets me use the temporary policy db name but the + * standard policy db lockfile, thus ensuring that no one changes + * the policy while this program is working. + */ + params.admin_dbname = pol_dbfile; + + if((ret = osa_adb_open_policy(&policy_db, ¶ms)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN)); + exit(2); + } + if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_PERMANENT) != OSA_ADB_OK)) { + com_err(whoami, ret, error_message(IMPORT_RENAME_LOCK)); + exit(2); + } + if (merge_princs) { + if ((ret = krb5_db_set_name(context, params.dbname)) || + (ret = krb5_db_init(context))) { + com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN)); + exit(2); + } + } + + if((ret = import_file(context, fp, merge_princs, policy_db)) != + OSA_ADB_OK) { + unlink(pol_dbfile); + com_err(whoami, ret, error_message(IMPORT_IMPORT)); + exit(2); + } + + if (merge_princs && (ret = krb5_db_fini(context))) { + com_err(whoami, ret, error_message(IMPORT_RENAME_CLOSE)); + exit(2); + } + + kadm5_free_config_params(context, ¶ms); + params.mask = 0; + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + com_err(whoami, ret, error_message(IMPORT_GET_PARAMS)); + exit(2); + } + + if (access(params.admin_dbname, F_OK) == 0) { + puts(error_message(IMPORT_WARN_DB)); + if(!confirm()) { + com_err(whoami, IMPORT_FAILED, NULL); + exit(2); + } + } + + if((ret = osa_adb_open_policy(&policy_db, ¶ms)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_OPEN)); + exit(2); + } + if ((ret = osa_adb_get_lock(policy_db, OSA_ADB_PERMANENT) != OSA_ADB_OK)) { + com_err(whoami, ret, error_message(IMPORT_RENAME_LOCK)); + exit(2); + } + if (rename(pol_dbfile, params.admin_dbname) != 0) { + com_err(whoami, IMPORT_RENAME_FAILED, NULL); + + /* WARNING! Permanent lock is not replaced. This will */ + /* require manual administrative action! */ + exit(2); + } + if ((ret = osa_adb_release_lock(policy_db)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_UNLOCK)); + + /* WARNING! Permanent lock is not replaced. This will */ + /* require manual administrative action! */ + exit(2); + } + if ((ret = osa_adb_close_policy(policy_db)) != OSA_ADB_OK) { + com_err(whoami, ret, error_message(IMPORT_RENAME_CLOSE)); + exit(2); + } + exit(0); +} diff --git a/src/kadmin/import/strtok.c b/src/kadmin/import/strtok.c new file mode 100644 index 000000000..7b7155786 --- /dev/null +++ b/src/kadmin/import/strtok.c @@ -0,0 +1,131 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.2 1996/07/22 20:26:35 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.1.4.1 1996/07/18 03:02:29 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.1.2.1 1996/06/20 21:49:01 marc + * File added to the repository on a branch + * + * Revision 1.1 1993/11/14 23:51:23 shanzer + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + + +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)strtok.c 5.7 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include <stddef.h> +#include <string.h> + +/* + * Function: nstrtok + * + * Purpose: the same as strtok ... just different. does not deal with + * multiple tokens in row. + * + * Arguments: + * s (input) string to scan + * delim (input) list of delimiters + * <return value> string or null on error. + * + * Requires: + * nuttin + * + * Effects: + * sets last to string + * + * Modifies: + * last + * + */ + +char * +nstrtok(s, delim) + register char *s, *delim; +{ + register char *spanp; + register int c, sc; + char *tok; + static char *last; + + + if (s == NULL && (s = last) == NULL) + return (NULL); + + /* + * Skip (span) leading delimiters (s += strspn(s, delim), sort of). + */ +#ifdef OLD +cont: + c = *s++; + for (spanp = delim; (sc = *spanp++) != 0;) { + if (c == sc) + goto cont; + } + + if (c == 0) { /* no non-delimiter characters */ + last = NULL; + return (NULL); + } + tok = s - 1; +#else + tok = s; +#endif + + /* + * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). + * Note that delim must have one NUL; we stop if we see that, too. + */ + for (;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + last = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + diff --git a/src/kadmin/import/unit-test/Makefile.ov b/src/kadmin/import/unit-test/Makefile.ov new file mode 100644 index 000000000..c7ac33c0b --- /dev/null +++ b/src/kadmin/import/unit-test/Makefile.ov @@ -0,0 +1,9 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: + $(RUNTEST) IMPORT=../ovsec_adm_import --tool import diff --git a/src/kadmin/import/unit-test/config/unix.exp b/src/kadmin/import/unit-test/config/unix.exp new file mode 100644 index 000000000..af4ff443e --- /dev/null +++ b/src/kadmin/import/unit-test/config/unix.exp @@ -0,0 +1,36 @@ +# +# import_version -- extract and print the version number of import +# + +proc import_version {} { + global IMPORT + set tmp [exec ident $IMPORT] + if [regexp {Header: .*import.c,v ([0-9]+\.[0-9]+)} $tmp \ + dummy version] then { + clone_output "$IMPORT version $version\n" + } else { + clone_output "$IMPORT version <unknown>\n" + } +} +# +# import_load -- loads the program +# +proc import_load {} { + # +} + +# import_exit -- clean up and exit +proc import_exit {} { + # +} + +# +# import_start -- start import running +# +proc import_start { args } { + global IMPORT + global spawn_id + + verbose "% $IMPORT $args" 1 + eval spawn $IMPORT $args +} diff --git a/src/kadmin/import/unit-test/helpers.exp b/src/kadmin/import/unit-test/helpers.exp new file mode 100644 index 000000000..5905614e7 --- /dev/null +++ b/src/kadmin/import/unit-test/helpers.exp @@ -0,0 +1,93 @@ +# +# $Id$ +# + +proc myfail { comment } { + global mytest_name + global mytest_status + wait + fail "$mytest_name: $comment" + set mytest_status 1 +} + +proc mypass {} { +} + +## +## When you expect on an id, and eof is detected, the spawn_id is closed. +## It may be waited for, but calling expect or close on this id is an ERROR! +## + +proc mytest { name kpargs status args } { + global spawn_id + global timeout + global mytest_name + global mytest_status + + verbose "starting test: $name" + + set mytest_name "$name" + + eval import_start $kpargs + + # at the end, eof is success + + lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + + # for each test argument.... + # rep invariant: when this foreach ends, the id is close'd, but + # not wait'ed. + + foreach test $args { + set mytest_status 0 + + # treat the arg as an expect parameter + # if failure, the process will be closed and waited. + + uplevel 1 "expect { + $test + timeout { close; myfail \"timeout\"} + eof { myfail \"eof read before expected message string\" } + }" + + if {$mytest_status == 1} { return } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 1 + if {[lindex $ret 0] == -1} { + fail "$name: wait returned error [lindex $ret 1]" + } else { + if { ((![string compare $status zero]) && + ([lindex $ret 1] == 0)) || + ((![string compare $status nonzero]) && + ([lindex $ret 1] != 0)) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret 1], should be $status" + } + } +} + +proc import_win { name args } { + mytest "$name" "$args" zero { + -re "Successfully imported \[0-9\]+ records." + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + +proc import_lose { name args error } { + mytest "$name" "$args" nonzero { + -re "Successfully imported \[0-9\]+ records." + { myfail "unexpected success" } + -re "ovsec_adm_import: .*$error" + { mypass } + eof + { myfail "error: $expect_out(buffer)" } + } +} + diff --git a/src/kadmin/import/unit-test/import.0/usage.exp b/src/kadmin/import/unit-test/import.0/usage.exp new file mode 100644 index 000000000..8e82f5a21 --- /dev/null +++ b/src/kadmin/import/unit-test/import.0/usage.exp @@ -0,0 +1,23 @@ +# +# $Id$ +# + +set timeout 5 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +import_lose "C.6: input file not readable" /foo/bar/baz \ + "No such file or directory while opening input file" + +import_lose "C.7: two arguments" {foo bar} \ + "Usage:" + +system {rm -rf /krb5} + +import_lose "C.2: /krb5 doesn't exist" ./valid_export_file \ + "Secure administration database lock file missing while importing" + diff --git a/src/kadmin/import/unit-test/valid_export_file b/src/kadmin/import/unit-test/valid_export_file new file mode 100644 index 000000000..dad19c874 --- /dev/null +++ b/src/kadmin/import/unit-test/valid_export_file @@ -0,0 +1,27 @@ +OpenV*Secure V1.0 Tue Dec 21 14:18:18 1993 +policy test-pol 0 10000 8 2 3 1 +policy dict-only 0 0 1 1 1 1 +policy once-a-min 30 0 1 1 1 1 +policy test-pol-nopw 0 0 1 1 1 2 +princ admin/delete@SECURE-TEST.OV.COM 0 0 0 0 +princ test3@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/modify@SECURE-TEST.OV.COM 0 0 0 0 +princ ovsec_adm/changepw@SECURE-TEST.OV.COM 0 0 0 0 +princ test2@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/pol@SECURE-TEST.OV.COM test-pol-nopw 800 0 0 0 +princ admin/rename@SECURE-TEST.OV.COM 0 0 0 0 +princ test1@SECURE-TEST.OV.COM 0 0 0 0 +princ krbtgt/SECURE-TEST.OV.COM@SECURE-TEST.OV.COM 0 0 0 0 +princ pol3@SECURE-TEST.OV.COM dict-only 800 0 0 0 +princ admin/get-pol@SECURE-TEST.OV.COM test-pol-nopw 800 0 0 0 +princ admin/none@SECURE-TEST.OV.COM 0 0 0 0 +princ testuser@SECURE-TEST.OV.COM 0 0 0 0 +princ pol2@SECURE-TEST.OV.COM once-a-min 800 0 0 0 +princ K/M@SECURE-TEST.OV.COM 0 0 0 0 +princ pol1@SECURE-TEST.OV.COM test-pol 800 0 0 0 +princ ovsec_adm/history@SECURE-TEST.OV.COM 0 0 0 0 +princ admin@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/add@SECURE-TEST.OV.COM 0 0 0 0 +princ admin/get@SECURE-TEST.OV.COM 0 0 0 0 +princ ovsec_adm/admin@SECURE-TEST.OV.COM 0 0 0 0 +End of Database 25 records diff --git a/src/kadmin/kdbkeys/ChangeLog b/src/kadmin/kdbkeys/ChangeLog new file mode 100644 index 000000000..206aea9be --- /dev/null +++ b/src/kadmin/kdbkeys/ChangeLog @@ -0,0 +1,16 @@ +Thu Jul 18 19:44:10 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Wed Jul 10 01:00:49 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.in, configure.in: added autoconf support + + * kdbkeys.c: rename <ovsec_adm/foo.h> to <kadm5/foo.h>, rename + <krb5/krb5.h to <krb5.h> + +Tue Jul 9 13:25:00 1996 Barry Jaspan <bjaspan@mit.edu> + + * do-test.pl: rewrite to use kdb5_util instead of kdbkeys + + diff --git a/src/kadmin/kdbkeys/Makefile.in b/src/kadmin/kdbkeys/Makefile.in new file mode 100644 index 000000000..5672bd0bf --- /dev/null +++ b/src/kadmin/kdbkeys/Makefile.in @@ -0,0 +1,15 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kdbkeys +OBJS = kdbkeys.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/kdbkeys/Makefile.ov b/src/kadmin/kdbkeys/Makefile.ov new file mode 100644 index 000000000..72c11695a --- /dev/null +++ b/src/kadmin/kdbkeys/Makefile.ov @@ -0,0 +1,21 @@ +# +# $Id$ +# + +TOP = .. +include $(TOP)/config.mk/template + +# Need $(STOP_SERVERS_LOCAL) because any running servers need to be +# killed so that they won't keep the database open so that we can't +# blow it away. + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(START_SERVERS_LOCAL) + $(STOP_SERVERS_LOCAL) + +unit-test-body:: + $(PERL) ./do-test.pl 10 + +unit-test-cleanup:: diff --git a/src/kadmin/kdbkeys/configure.in b/src/kadmin/kdbkeys/configure.in new file mode 100644 index 000000000..6c027fc27 --- /dev/null +++ b/src/kadmin/kdbkeys/configure.in @@ -0,0 +1,11 @@ +AC_INIT(kdbkeys.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/kdbkeys/do-test.pl b/src/kadmin/kdbkeys/do-test.pl new file mode 100644 index 000000000..7acb425f7 --- /dev/null +++ b/src/kadmin/kdbkeys/do-test.pl @@ -0,0 +1,56 @@ +#!/afs/athena/contrib/perl/p + +# +# $Id$ +# + +$debug = $ARGV[1] || $ENV{'VERBOSE_TEST'}; + +die "Need a number.\n" if !$ARGV[0]; + +die "Neither \$TOP nor \$TESTDIR is set.\n" + if (! ($ENV{'TOP'} || $ENV{'TESTDIR'})); + +$TESTDIR = ($ENV{'TESTDIR'} || "$ENV{'TOP'}/testing"); +$INITDB = ($ENV{'INITDB'} || "$TESTDIR/scripts/init_db"); + +for ($i=0; $i<$ARGV[0]; $i++) { + print "Trial $i\n" if $debug; + + system("$INITDB > /dev/null 2>&1") && + die "Error in init_db\n"; + + open(KEYS,"../dbutil/kdb5_util -R dump_db|") || + die "Couldn't run kdb5_util: $!\n"; + chop($header = <KEYS>); + if ($header ne "kdb5_util load_dump version 4") { + die "Cannot operate on dump version \"$header\"; version 4 required."; + } + while(<KEYS>) { + next if ((!/^princ.*kadmin\//) && (!/^princ.*krbtgt/)); + + print if $debug > 1; + + split; + + $princ = $_[6]; + $nkeys = $_[4]; + $ntls = $_[3]; + print "$princ: nkeys $nkeys, ntls $ntls\n" if $debug; + for ($j = 15 + $ntls*3; $nkeys > 0; $nkeys--) { + $ver = $_[$j++]; + $kvno = $_[$j++]; + $keytype = $_[$j++]; + $keylen = $_[$j++]; + $keydata = $_[$j++]; + $j += 3 if ($ver > 1); + + print "$princ, ver $ver, kvno $kvno, type $keytype, len $keylen, " + . "data $keydata\n" if $debug; + + die "Duplicated key $princ = $keydata\n" if + $keys{$keydata}++; + } + } + close(KEYS); +} diff --git a/src/kadmin/keytab/ChangeLog b/src/kadmin/keytab/ChangeLog new file mode 100644 index 000000000..5aeb2a44e --- /dev/null +++ b/src/kadmin/keytab/ChangeLog @@ -0,0 +1,13 @@ +Thu Jul 18 20:41:14 1996 Marc Horowitz <marc@mit.edu> + + * keytab.c (etype_string): ifdef'd out reference to des3 + +Mon Jul 15 16:59:35 1996 Marc Horowitz <marc@mit.edu> + + * keytab.c (main): the default keytab name logic was inappropriate + for beta6, since it ignores the env vars, etc. This version still + has the problem that the default keytab probably isn't writeable. + +Wed Jul 10 01:27:44 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.in, configure.in: added autoconf support diff --git a/src/kadmin/keytab/Makefile.in b/src/kadmin/keytab/Makefile.in new file mode 100644 index 000000000..74426a51f --- /dev/null +++ b/src/kadmin/keytab/Makefile.in @@ -0,0 +1,19 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kadm5_keytab +OBJS = keytab.o + +all:: $(PROG).local $(PROG) + +$(PROG).local: $(OBJS) $(SRVDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS) + +$(PROG): $(OBJS) $(CLNTDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG).local $(PROG) $(OBJS) diff --git a/src/kadmin/keytab/Makefile.ov b/src/kadmin/keytab/Makefile.ov new file mode 100644 index 000000000..15cd6d5d9 --- /dev/null +++ b/src/kadmin/keytab/Makefile.ov @@ -0,0 +1,30 @@ +# +# Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. +# +# $Id$ +# $Source$ +# + +TOP = .. +include $(TOP)/config.mk/template + +SRCS = keytab.c +OBJS = keytab.o +PROG = ovsec_edit_keytab + +LIBS = $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBGSSAPI_KRB5) $(LIBRPCLIB) \ + $(LIBDYN) $(LIBDB) $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) \ + $(BSDLIB) $(NETLIB) + +expand InstallAdmin + +PROG = ovsec_edit_keytab.local +LIBS = $(LIBADMSRV) $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5) $(NDBMLIB) \ + $(LIBRPCLIB) $(LIBDYN) $(LIBDB) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBCOM_ERR) $(BSDLIB) $(NETLIB) + +expand InstallAdmin + +SUBDIRS = unit-test +expand SubdirTarget + diff --git a/src/kadmin/keytab/configure.in b/src/kadmin/keytab/configure.in new file mode 100644 index 000000000..0e043f7e5 --- /dev/null +++ b/src/kadmin/keytab/configure.in @@ -0,0 +1,12 @@ +AC_INIT(keytab.c) +CONFIG_RULES +AC_PROG_INSTALL +USE_KADMCLNT_LIBRARY +USE_GSSAPI_LIBRARY +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/keytab/keytab.c b/src/kadmin/keytab/keytab.c new file mode 100644 index 000000000..2561252ef --- /dev/null +++ b/src/kadmin/keytab/keytab.c @@ -0,0 +1,528 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <krb5.h> +#include <k5-int.h> +#include <kadm5/admin.h> + +int add_principal(char *keytab_str, krb5_keytab keytab, char *me_str, + char *princ_str, int create); +int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str); +static char *etype_string(krb5_enctype enctype); + +krb5_context context; +char *whoami; +int quiet; + +void usage() +{ + fprintf(stderr, "Usage: ovsec_edit_keytab [-k[eytab] keytab] [-q] cmd\n"); + fprintf(stderr, " cmds are:\t-a[dd] [-c[reate] [-p principal] principal\n"); + fprintf(stderr, "\t\t-c[hange] [-p principal] principal\n"); + fprintf(stderr, "\t\t-r[emove] principal [kvno|\"all\"|\"old\"]\n"); + exit(1); +} + +main(int argc0, char **argv0) +{ + extern krb5_kt_ops krb5_ktf_writable_ops; + krb5_keytab keytab = 0; + char *me_str, *princ_str, *keytab_str, *kvno_str; + char keytab_buf[1024]; + int argc, code, did_something, create; + char **argv; + + whoami = strrchr(argv0[0], '/') ? strrchr(argv0[0], '/') + 1 : argv0[0]; + + if (code = krb5_init_context(&context)) { + com_err(whoami, code, "while initializing krb5 context"); + exit(1); + } + + krb5_init_ets(context); + + /* register the WRFILE keytab type and set it as the default */ + if (code = krb5_kt_register(context, &krb5_ktf_writable_ops)) { + com_err(whoami, code, + "while registering writable key table functions"); + exit(1); + } + + /* process non-action arguments first */ + argc = argc0-1; + argv = argv0+1; + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + argc--; argv++; + if (!argc) usage(); + + if (keytab == NULL) { + if (strchr(*argv, ':') != NULL) { + keytab_str = strdup(*argv); + if (keytab_str == NULL) { + com_err(whoami, ENOMEM, + "while creating keytab name"); + exit(1); + } + } else { + keytab_str = (char *) + malloc(strlen("WRFILE:")+strlen(*argv)+1); + if (keytab_str == NULL) { + com_err(whoami, ENOMEM, + "while creating keytab name"); + exit(1); + } + sprintf(keytab_str, "WRFILE:%s", *argv); + } + + code = krb5_kt_resolve(context, keytab_str, &keytab); + if (code != 0) { + com_err(whoami, code, "while resolving keytab %s", + keytab_str); + exit(1); + } + } else { + usage(); + } + } else if (strcmp(*argv, "-q") == 0) { + quiet++; + } + /* otherwise ignore the argument, for now */ + argc--; argv++; + } + + if (keytab == NULL) { + code = krb5_kt_default(context, &keytab); + if (code != 0) { + com_err(whoami, code, "while opening default keytab"); + exit(1); + } + code = krb5_kt_get_name(context, keytab, + keytab_buf, sizeof(keytab_buf)); + keytab_str = keytab_buf; + } + + argc = argc0-1; + argv = argv0+1; + + did_something = 0; + + /* now process the action arguments */ + while (argc) { + if (strncmp(*argv, "-k", 2) == 0) { + /* if there is no keytab argument the previous loop */ + /* would have called usage(), so just skip it */ + argc--; argv++; + } else if (strcmp(*argv, "-q") == 0) { + /* skip it */ + } else if (strncmp(*argv, "-a", 2) == 0 || + strncmp(*argv, "-c", 2) == 0) { + did_something++; + + argc--; argv++; + if (!argc) usage(); + + me_str = NULL; + create = 0; + while (argc) { + if (strcmp(*argv, "-p") == 0) { + argc--; argv++; + if (argc < 1) usage(); + + me_str = *argv; + } else if (strncmp(*argv, "-c", 2) == 0) { + create++; + } else + break; + argc--; argv++; + } + if (argc != 1) usage(); + + code = add_principal(keytab_str, keytab, me_str ? me_str : + *argv, *argv, create); + break; + } else if (strncmp(*argv, "-r", 2) == 0) { + did_something++; + + argc--; argv++; + if (!argc) usage(); + princ_str = *argv; + if (argc > 0) { + argc--; + argv++; + kvno_str = *argv; + } else + kvno_str = NULL; + + code = remove_principal(keytab_str, keytab, princ_str, + kvno_str); + break; + } else { + fprintf(stderr, "%s: Unknown command line option %s.\n", + whoami, *argv); + usage(); + } + + argc--; argv++; + } + + /* argv ends up pointing at the last recognized argument */ + if (!did_something || argc > 1) + usage(); + + /* use argc as temp */ + argc = krb5_kt_close(context, keytab); + if (argc != 0) { + com_err(whoami, argc, "while closing keytab"); + code = argc; + } + + free(keytab_str); + + return (code != 0); +} + +int add_principal(char *keytab_str, krb5_keytab keytab, char *me_str, + char *princ_str, int create) +{ + kadm5_principal_ent_rec princ_rec; + krb5_principal me, princ; + krb5_keytab_entry new_entry; + krb5_keyblock *keys; + void *handle; + int code, code2, mask, nkeys, i; + + (void) memset((char *)&princ_rec, 0, sizeof(princ_rec)); + + me = princ = NULL; + handle = NULL; + keys = NULL; + nkeys = 0; + + code = krb5_parse_name(context, me_str, &me); + if (code != 0) { + com_err(whoami, code, "while parsing -p principal name %s", + me_str); + goto cleanup; + } + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing -add principal name %s", + princ_str); + goto cleanup; + } + + /* first try using the keytab */ + code = kadm5_init_with_skey(me_str, keytab_str, + KADM5_ADMIN_SERVICE, + NULL, /* default configuration */ + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, &handle); + if (code != 0) { + /* KRB5_KT_NOTFOUND and ENOENT are not "errors" because this */ + /* program does not require the keytab entry to exist */ + if (code != KRB5_KT_NOTFOUND && code != ENOENT) { + if (code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN) + fprintf(stderr, "%s: Principal %s does not exist.\n", + whoami, me_str); + else + com_err(whoami, code, "while authenticating as principal " + "%s from keytab", me_str); + } + + code2 = kadm5_init_with_password(me_str, NULL, + KADM5_ADMIN_SERVICE, + NULL, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &handle); + if (code2 != 0) { + if (code2 != code) /* don't dup error messages */ { + com_err(whoami, code2, "while authenticating as " + "principal %s from password", me_str); + } + goto cleanup; + } + } + + if (create) { + /* always try to create and just ignore dup errors because it */ + /* reduces duplicate code... and how often will this happen? */ + + /* be sure to create the principal with the secure sequence */ + /* of events as specified in the functional spec */ + + princ_rec.principal = princ; + princ_rec.attributes = KRB5_KDB_DISALLOW_ALL_TIX; + mask = KADM5_PRINCIPAL | KADM5_ATTRIBUTES; + code = kadm5_create_principal(handle, &princ_rec, + mask, "dummy"); + if (code == KADM5_DUP) { + printf("%s: Principal %s already exists.\n", + whoami, princ_str); + } else if (code != 0) { + if (code == KADM5_AUTH_ADD) { + fprintf(stderr, "%s: Operation requires " + "``add'' and ``modify'' privileges while creating " + "principal.\n", whoami); + } else { + com_err(whoami, code, "while creating " + "principal %s.", princ_str); + } + goto cleanup; + } else if (!quiet) + printf("%s: Created principal %s.\n", whoami, princ_str); + } + + code = kadm5_randkey_principal(handle, princ, &keys, &nkeys); + if (code != 0) { + if (code == KADM5_UNK_PRINC) { + fprintf(stderr, "%s: Principal %s does not exist.\n", + whoami, princ_str); + } else + com_err(whoami, code, "while changing %s's key", + princ_str); + goto cleanup; + } + + code = kadm5_get_principal(handle, princ, &princ_rec, + KADM5_PRINCIPAL_NORMAL_MASK); + if (code != 0) { + com_err(whoami, code, "while retrieving principal"); + goto cleanup; + } + + if (create) { + /* complete the secure principal-creation sequence */ + princ_rec.attributes &= ~KRB5_KDB_DISALLOW_ALL_TIX; + mask = KADM5_ATTRIBUTES; + code = kadm5_modify_principal(handle, &princ_rec, mask); + if (code != 0) { + if (code == KADM5_AUTH_ADD) { + fprintf(stderr, "%s: Operation requires " + "``add'' and ``modify'' privileges while creating " + "principal.\n", whoami); + } else + com_err(whoami, code, "while modifying newly created " + "principal"); + (void) kadm5_free_principal_ent(handle, &princ_rec); + goto cleanup; + } + } + + for (i = 0; i < nkeys; i++) { + memset((char *) &new_entry, 0, sizeof(new_entry)); + new_entry.principal = princ; + new_entry.key = keys[i]; + new_entry.vno = princ_rec.kvno; + + code = krb5_kt_add_entry(context, keytab, &new_entry); + if (code != 0) { + com_err(whoami, code, "while adding key to keytab"); + (void) kadm5_free_principal_ent(handle, &princ_rec); + goto cleanup; + } + + if (!quiet) + printf("%s: Entry for principal %s with kvno %d, " + "encryption type %s added to keytab %s.\n", + whoami, princ_str, princ_rec.kvno, + etype_string(keys[i].enctype), keytab_str); + } + + code = kadm5_free_principal_ent(handle, &princ_rec); + if (code != 0) { + com_err(whoami, code, "while freeing principal entry"); + goto cleanup; + } + +cleanup: + if (handle) { + code2 = kadm5_destroy(handle); + if (code2 != 0) { + com_err(whoami, code2, "while closing admin server connection"); + } + } + if (nkeys) { + for (i = 0; i < nkeys; i++) + krb5_free_keyblock(context, &keys[i]); + free(keys); + } + if (me) + krb5_free_principal(context, me); + if (princ) + krb5_free_principal(context, princ); + + return code; +} + +int remove_principal(char *keytab_str, krb5_keytab keytab, char + *princ_str, char *kvno_str) +{ + krb5_principal princ; + krb5_keytab_entry entry; + krb5_kt_cursor cursor; + enum { UNDEF, SPEC, HIGH, ALL, OLD } mode; + int code, kvno, did_something; + + code = krb5_parse_name(context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, "while parsing principal name %s", + princ_str); + return code; + } + + mode = UNDEF; + if (kvno_str == NULL) { + mode = HIGH; + kvno = 0; + } else if (strcmp(kvno_str, "all") == 0) { + mode = ALL; + kvno = 0; + } else if (strcmp(kvno_str, "old") == 0) { + mode = OLD; + kvno = 0; + } else { + mode = SPEC; + kvno = atoi(kvno_str); + } + + /* kvno is set to specified value for SPEC, 0 otherwise */ + code = krb5_kt_get_entry(context, keytab, princ, kvno, 0, &entry); + if (code != 0) { + if (code == ENOENT) { + fprintf(stderr, "%s: Keytab %s does not exist.\n", + whoami, keytab_str); + } else if (code == KRB5_KT_NOTFOUND) { + if (mode != SPEC) + fprintf(stderr, "%s: No entry for principal " + "%s exists in keytab %s\n", + whoami, princ_str, keytab_str); + else + fprintf(stderr, "%s: No entry for principal " + "%s with kvno %d exists in keytab " + "%s.\n", whoami, princ_str, kvno, + keytab_str); + } else { + com_err(whoami, code, "while retrieving highest kvno " + "from keytab"); + } + return code; + } + + /* set kvno to spec'ed value for SPEC, highest kvno otherwise */ + kvno = entry.vno; + krb5_kt_free_entry(context, &entry); + + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while starting keytab scan"); + return code; + } + + did_something = 0; + while ((code = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0) { + if (krb5_principal_compare(context, princ, entry.principal) && + ((mode == ALL) || + (mode == SPEC && entry.vno == kvno) || + (mode == OLD && entry.vno != kvno) || + (mode == HIGH && entry.vno == kvno))) { + + /* + * Ack! What a kludge... the scanning functions lock + * the keytab so entries cannot be removed while they + * are operating. + */ + code = krb5_kt_end_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while temporarily ending " + "keytab scan"); + return code; + } + code = krb5_kt_remove_entry(context, keytab, &entry); + if (code != 0) { + com_err(whoami, code, "while deleting entry from keytab"); + return code; + } + code = krb5_kt_start_seq_get(context, keytab, &cursor); + if (code != 0) { + com_err(whoami, code, "while restarting keytab scan"); + return code; + } + + did_something++; + if (!quiet) + printf("%s: Entry for principal %s with kvno %d " + "removed from keytab %s.\n", whoami, + princ_str, entry.vno, keytab_str); + } + krb5_kt_free_entry(context, &entry); + } + if (code && code != KRB5_KT_END) { + com_err(whoami, code, "while scanning keytab"); + return code; + } + if (code = krb5_kt_end_seq_get(context, keytab, &cursor)) { + com_err(whoami, code, "while ending keytab scan"); + return code; + } + + /* + * If !did_someting then mode must be OLD or we would have + * already returned with an error. But check it anyway just to + * prevent unexpected error messages... + */ + if (!did_something && mode == OLD) { + fprintf(stderr, "%s: There is only one entry for principal " + "%s in keytab %s\n", whoami, princ_str, keytab_str); + return 1; + } + + return 0; +} + +/* + * etype_string(enctype): return a string representation of the + * encryption type. XXX copied from klist.c; this should be a + * library function, or perhaps just #defines + */ +static char *etype_string(enctype) + krb5_enctype enctype; +{ + static char buf[12]; + + switch (enctype) { + case ENCTYPE_DES_CBC_CRC: + return "DES-CBC-CRC"; + break; + case ENCTYPE_DES_CBC_MD4: + return "DES-CBC-MD4"; + break; + case ENCTYPE_DES_CBC_MD5: + return "DES-CBC-MD5"; + break; +#if 0 + case ENCTYPE_DES3_CBC_MD5: + return "DES3-CBC-MD5"; + break; +#endif + default: + sprintf(buf, "etype %d", enctype); + return buf; + break; + } +} diff --git a/src/kadmin/keytab/unit-test/ChangeLog b/src/kadmin/keytab/unit-test/ChangeLog new file mode 100644 index 000000000..69df6be92 --- /dev/null +++ b/src/kadmin/keytab/unit-test/ChangeLog @@ -0,0 +1,4 @@ +Mon Jul 15 17:03:28 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.ov (unit-test-body): ovsec_adm_keytab is now + kadm5_keytab diff --git a/src/kadmin/keytab/unit-test/Makefile.ov b/src/kadmin/keytab/unit-test/Makefile.ov new file mode 100644 index 000000000..1b3366d8b --- /dev/null +++ b/src/kadmin/keytab/unit-test/Makefile.ov @@ -0,0 +1,21 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-body:: + $(CLNTTCL) ./del-princs.tcl + $(RUNTEST) KEYTAB=../kadm5_keytab \ + KLIST=../../../clients/klist/klist \ + QUALNAME=../../testing/scripts/qualname --tool keytab + +unit-test-setup:: + $(START_SERVERS) + $(CLNTTCL) ./add-princs.tcl + +unit-test-cleanup:: + $(STOP_SERVERS) diff --git a/src/kadmin/keytab/unit-test/add-princs.tcl b/src/kadmin/keytab/unit-test/add-princs.tcl new file mode 100644 index 000000000..247a4382b --- /dev/null +++ b/src/kadmin/keytab/unit-test/add-princs.tcl @@ -0,0 +1,12 @@ +source $env(TCLUTIL) + +ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle + +ovsec_kadm_create_principal $server_handle [simple_principal kttest1] \ + {OVSEC_KADM_PRINCIPAL} kttest1 + +ovsec_kadm_create_principal $server_handle [simple_principal kttest2] \ + {OVSEC_KADM_PRINCIPAL} kttest2 + +ovsec_kadm_destroy $server_handle diff --git a/src/kadmin/keytab/unit-test/config/unix.exp b/src/kadmin/keytab/unit-test/config/unix.exp new file mode 100644 index 000000000..bc07d4f6e --- /dev/null +++ b/src/kadmin/keytab/unit-test/config/unix.exp @@ -0,0 +1,46 @@ +set klist $KLIST +set hostname hostname +set qualname $QUALNAME + +set rm /bin/rm + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + +proc keytab_version {} { + global KEYTAB + puts "$KEYTAB version unknown" +} + +proc keytab_load {} { + # +} + +proc keytab_exit {} { + # +} + +proc keytab_start { args } { + global KEYTAB + global spawn_id + + verbose "% $KEYTAB $args" 1 + eval spawn $KEYTAB $args +} + + diff --git a/src/kadmin/keytab/unit-test/del-princs.tcl b/src/kadmin/keytab/unit-test/del-princs.tcl new file mode 100644 index 000000000..9b9cab5c4 --- /dev/null +++ b/src/kadmin/keytab/unit-test/del-princs.tcl @@ -0,0 +1,24 @@ +source $env(TCLUTIL) + +proc check_err {error} { + if {! [string match {*OVSEC_KADM_UNK_PRINC*} $error]} { + error $error + } +} + +proc delprinc {princ} { + global server_handle + + catch {ovsec_kadm_delete_principal $server_handle $princ} + if {[info exists errorInfo]} { + check_err $errorInfo + } +} + +ovsec_kadm_init admin admin $OVSEC_KADM_ADMIN_SERVICE null \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle + +delprinc dne1 +delprinc dne2 + +ovsec_kadm_destroy $server_handle diff --git a/src/kadmin/keytab/unit-test/helpers.exp b/src/kadmin/keytab/unit-test/helpers.exp new file mode 100644 index 000000000..a9f7ca402 --- /dev/null +++ b/src/kadmin/keytab/unit-test/helpers.exp @@ -0,0 +1,132 @@ +# +# $Id$ +# + +# +# Create a keytab "name" with an entry for each element in the array +# "entries". If "name" already exists it is destroyed. Connections +# to the admin server are made as the principal "admin" with the +# password "password". +# +proc setup_keytab { testname ktname admin password entries } { + global klist rm + global wait_error_index wait_status_index + global verbose + + verbose "setting up test: $testname" 1 + + if {[regexp {(.+):(.+)} $ktname dummy type filename] == 0} { + set filename $ktname + } + + if {[file exists $filename] && [catch "exec $rm $filename"] != 0} { + error "$testname: cannot delete keytab file $filename"; + } + + if {$type == "WRFILE"} { + set type "FILE" + } + + foreach entry $entries { + keytab_run "$testname setup" \ + "-k $ktname -a -p $admin $entry" 0 { + "Enter password:" { + send "$password\n" + } + } + # if "Enter password:" needs to be optional: + # { timeout { } } + } + + if {$verbose > 1} { + if {[file exists $filename]} { + puts "% exec $klist -k $type:$filename\n" + if {[catch "exec $klist -k $type:$filename"] != 0} { + error "$testname: $klist failed" + } + } + } +} + +# +# Run $KEYTAB with args ktargs. Each element of args is treated as an +# expect block for the process, in turn. If all elements match and +# then eof occurs with exit status status, the test passes; otherwise +# it fails. +# +proc keytab_run { testname ktargs status args } { + global spawn_id timeout + global wait_error_index wait_status_index + global progname + + verbose "running $progname for test: $testname" 2 + + eval keytab_start $ktargs + + # wait for eof after exps + lappend args { eof { verbose $expect_out(buffer) 2 } } + + foreach exp $args { + uplevel 1 "expect { + $exp + timeout { close; fail \"$testname: timeout\"; return } + eof { fail \"$testname: eof before expected message\"; return } + }" + } + + set ret [wait] + verbose "% Exit $ret" 2 + + if {[lindex $ret $wait_error_index] == -1} { + fail "$testname: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$testname" + } else { + fail "$testname: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } +} + + +proc klist_check { testname ktname args } { + global klist + + if {[regexp {(.+):(.+)} $ktname dummy type filename] == 0} { + set filename $ktname + } + + set lines [list "^Keytab name: (WR)?FILE:$filename" \ + "^KVNO Principal" "^---- -------"] + + foreach entry $args { + if {[lindex $entry 1] == 0} { + set line "^ *\[0-9\]+ [lindex $entry 0]" + } else { + set line "^ *[lindex $entry 1] [lindex $entry 0]" + } + lappend lines $line + } + + set kl [open "|$klist -k FILE:$filename" r] + + while {[gets $kl line] >= 0} { + if {([llength $lines] == 0) || + ([regexp [lindex $lines 0] $line] == 0)} { + fail "$testname: klist check: \ + [lindex $lines 0] does not match $line" + } + set lines [lrange $lines 1 end] + } + if {[catch "close $kl" msg] != 0} { + fail "$testname: klist: $msg" + return + } + + if {[llength $lines] == 0} { + pass "$testname: klist check" + } else { + fail "$testname: klist check: too few entries in keytab" + } +} diff --git a/src/kadmin/keytab/unit-test/keytab.0/ChangeLog b/src/kadmin/keytab/unit-test/keytab.0/ChangeLog new file mode 100644 index 000000000..1b807089a --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/ChangeLog @@ -0,0 +1,4 @@ +Mon Jul 15 17:09:01 1996 Marc Horowitz <marc@mit.edu> + + * keytab-spec.exp: use /krb5/v5srvtab, since /krb5 is the test + dir. diff --git a/src/kadmin/keytab/unit-test/keytab.0/adding.exp b/src/kadmin/keytab/unit-test/keytab.0/adding.exp new file mode 100644 index 000000000..159ac638b --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/adding.exp @@ -0,0 +1,119 @@ +# +# $Id$ +# + +set timeout 20 + +load_lib "helpers.exp" + +if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} { + error "cannot set progname from $KEYTAB" +} + +set ktscratch_file /tmp/keytab_test +set ktscratch WRFILE:$ktscratch_file +set ktarg "-k $ktscratch" +set add_admin "$ktarg -a -p admin" +set pwprompt { "Enter password:" { send "admin\n" } } + +setup_keytab "A1,A6" $ktscratch admin admin {} +keytab_run "A1,A6" "$add_admin kttest1" 0 "$pwprompt" { + -re \ + "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." {} +} +klist_check "A1,A6" $ktscratch {kttest1 0} + +setup_keytab "A2" $ktscratch admin admin {} +keytab_run "A2" "-q $add_admin kttest1" 0 "$pwprompt" { + -re + "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." { + close; fail "A2: -q"; return } + eof { break } +} +klist_check "A2" $ktscratch {kttest1 0} + +setup_keytab "A3" $ktscratch admin admin {kttest1 kttest1} +set kvno_key1 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest1/ && NR==4 {print \$1 \" \" \$3}"] +set kvno_key2 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest1/ && NR==5 {print \$1 \" \" \$3}"] +if {[lindex $kvno_key1 1] == [lindex $kvno_key2 1]} { + fail "A3: key compare" +} else { + klist_check "A3" $ktscratch "kttest1 [lindex $kvno_key1 0]" \ + "kttest1 [expr [lindex $kvno_key1 0]+1]" +} + +setup_keytab "A7" $ktscratch admin admin {} +keytab_run "A7" "$add_admin does-not-exist" 1 "$pwprompt" { + -re + "$progname: Principal does-not-exist does not exist." {} +} + +setup_keytab "A4,A10,A11,A13,A15" $ktscratch admin admin { kttest1 kttest2} +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest1" 0 +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest2" 0 +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest1" 0 +keytab_run "A4,A10,A11,A13,A15" "$ktarg -a kttest2" 0 +klist_check "A4,A10,A11,A13,A15" $ktscratch { kttest1 0 } \ + { kttest2 0 } { kttest1 0 } { kttest2 0 } { kttest1 0 } { kttest2 0 } + +setup_keytab "A12" $ktscratch admin admin {} +keytab_run "A12" "$ktarg -a -p admin/get-add kttest1" 1 "$pwprompt" { + "Operation requires ``change-password'' privilege while changing" {} +} + +setup_keytab "A14" $ktscratch admin admin {} +# assume the exit status won't be -1, so if the password prompt +# doesn't appear the test will fail +keytab_run "A14" "$ktarg -a kttest1" -1 { + "Enter password:" { send "\n"; expect eof; pass "A14: no -p"; return } +} + +setup_keytab "A16" $ktscratch admin admin {} +keytab_run "A16" "$ktarg -a -p does-not-exist kttest1" 1 { + "$progname: Principal does-not-exist does not exist." {} +} + +setup_keytab "A17" $ktscratch admin admin { kttest1 kttest2} +keytab_run "A17" "$ktarg -a -p kttest2 kttest1" 1 { + "Enter password:" { close; fail "A17: no password prompt"; return } + default { break } +} + +setup_keytab "A18" $ktscratch admin admin { } +keytab_run "A18" "$ktarg -a -c -p admin dne1" 0 "$pwprompt" { + "$progname: Created principal dne1" {} +} { + -re + "$progname: Entry.*dne1.*kvno \[0-9\]+.*keytab $ktscratch." {} +} +klist_check "A18" $ktscratch {dne1 0} + +setup_keytab "A19" $ktscratch admin admin {} +keytab_run "A9" "-q $ktarg -a -c -p admin dne2" 0 "$pwprompt" { + "$progname: Created principal dne2" { close; fail "A19: -q"; return } + eof { break } +} +klist_check "A19" $ktscratch {dne2 0} + +setup_keytab "A21" $ktscratch admin admin {} +keytab_run "A21" "$ktarg -a -c -p admin kttest1" 0 "$pwprompt" { + "$progname: Principal kttest1 already exists." {} +} { + -re \ + "$progname: Entry.*kttest1.*kvno \[0-9\]+.*keytab $ktscratch." {} +} +klist_check "A21" $ktscratch {kttest1 0} + +setup_keytab "A22" $ktscratch admin admin {} +keytab_run "A22" "$ktarg -a -c -p admin/modify kttest1" 1 "$pwprompt" { + "Operation requires ``add'' and ``modify'' privileges while creating" {} +} + +setup_keytab "A23" $ktscratch admin admin {} +keytab_run "A23" "$ktarg -a -c -p admin/get kttest1" 1 "$pwprompt" { + "Operation requires ``add'' and ``modify'' privileges while creating" {} +} + +exec rm -f $ktscratch_file diff --git a/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp b/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp new file mode 100644 index 000000000..8da80eb69 --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/keytab-spec.exp @@ -0,0 +1,47 @@ +# +# $Id$ +# + +set timeout 10 + +load_lib "helpers.exp" + +if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} { + error "cannot set progname from $KEYTAB" +} + +set hname [exec $hostname] +set qname [exec $qualname $hname] + +set testfile1 /tmp/keytab-test1 +set testfile2 /tmp/keytab-test2 + +if {[info exists env(KRB5_KTNAME)]} { + set ktname_orig $env(KRB5_KTNAME) + unset env(KRB5_KTNAME) +} + +setup_keytab "K1" WRFILE:/krb5/v5srvtab admin admin "host/$qname" +klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0" +keytab_run "K1" "-a host/$qname" 0 +klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0" "host/$qname 0" +keytab_run "K1" "-r host/$qname old" 0 +klist_check "K1" FILE:/krb5/v5srvtab "host/$qname 0" + +if {[info exists ktname_orig]} { + set env(KRB5_KTNAME) $ktname_orig +} + +setup_keytab "K2" WRFILE:$testfile1 admin admin {} +keytab_run "K2" "-k WRFILE:$testfile1 -a -p admin kttest1" 0 { + "Enter password:" { send "admin\n" } +} +klist_check "K2" FILE:$testfile1 "kttest1 0" + +setup_keytab "K2" WRFILE:$testfile2 admin admin {} +keytab_run "K3" "-k $testfile2 -a -p admin kttest1" 0 { + "Enter password:" { send "admin\n" } +} +klist_check "K3" FILE:$testfile2 "kttest1 0" + +exec rm -f $testfile1 $testfile2 diff --git a/src/kadmin/keytab/unit-test/keytab.0/removing.exp b/src/kadmin/keytab/unit-test/keytab.0/removing.exp new file mode 100644 index 000000000..a7a50f045 --- /dev/null +++ b/src/kadmin/keytab/unit-test/keytab.0/removing.exp @@ -0,0 +1,125 @@ +# +# $Id$ +# + +set timeout 10 + +load_lib "helpers.exp" + +if {[regexp {(.*/)?([^/]*)} $KEYTAB dummy dir progname] == 0} { + error "cannot set progname from $KEYTAB" +} + +set ktscratch_file /tmp/keytab_test +set ktscratch WRFILE:$ktscratch_file +set ktarg "-k $ktscratch" + +# Get the kvnos we will need later +setup_keytab "setup" $ktscratch admin admin { kttest1 kttest2 } +set kvno1 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest1/ {print \$1}"] +set kvno2 [exec $klist -k -K FILE:$ktscratch_file | \ + awk "/kttest2/ {print \$1}"] + +setup_keytab "R1" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R1" "$ktarg -r kttest1" 0 { + -re + "$progname: Entry for principal kttest1 with kvno \[0-9\]+\ + removed from keytab $ktscratch" {} +} +klist_check "R1" $ktscratch + +setup_keytab "R2" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R2" "$ktarg -q -r kttest1" 0 { + -re + "$progname: Entry for principal kttest1 with kvno \[0-9\]+\ + removed from keytab $ktscratch" { close; fail "R2: -q"; return } + eof { break } +} +klist_check "R2" $ktscratch + +setup_keytab "R3" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +klist_check "R3" $ktscratch "kttest1 $kvno1" +keytab_run "R3" "$ktarg -r kttest1 $kvno1" 0 +klist_check "R3" $ktscratch + +setup_keytab "R4" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +klist_check "R4" $ktscratch "kttest1 [expr $kvno1-2]" \ + "kttest1 [expr $kvno1-1]" "kttest1 $kvno1" +keytab_run "R4" "$ktarg -r kttest1" 0 +klist_check "R4" $ktscratch "kttest1 [expr $kvno1-2]" \ + "kttest1 [expr $kvno1-1]" + +setup_keytab "R5" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +keytab_run "R5" "$ktarg -r kttest1 old" 0 +klist_check "R5" $ktscratch "kttest1 $kvno1" + +setup_keytab "R6" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +keytab_run "R6" "$ktarg -r kttest1 old" 0 +klist_check "R6" $ktscratch "kttest1 $kvno1" + +setup_keytab "R7" $ktscratch admin admin { kttest1 kttest1 kttest1 } +set kvno1 [expr $kvno1+3] +keytab_run "R7" "$ktarg -r kttest1 all" 0 { + "$progname: Entry for principal kttest1" {} +} { + "$progname: Entry for principal kttest1" {} +} { + "$progname: Entry for principal kttest1" {} +} +klist_check "R7" $ktscratch + +setup_keytab "R8" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R8" "$ktarg -r kttest2" 1 { + "$progname: No entry for principal kttest2 exists in keytab" {} +} +klist_check "R8" $ktscratch "kttest1 $kvno1" + +setup_keytab "R9" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R9" "$ktarg -r kttest2 1" 1 { + "$progname: No entry for principal kttest2 with kvno 1 exists in keytab" {} +} +klist_check "R9" $ktscratch "kttest1 $kvno1" + +setup_keytab "R10" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R10" "$ktarg -r kttest2 all" 1 { + "$progname: No entry for principal kttest2 exists in keytab" {} +} +klist_check "R10" $ktscratch "kttest1 $kvno1" + +setup_keytab "R11" $ktscratch admin admin { kttest1 } +set kvno1 [expr $kvno1+1] +keytab_run "R11" "$ktarg -r kttest1 old" 1 { + "$progname: There is only one entry for principal kttest1 in keytab" {} +} +klist_check "R11" $ktscratch "kttest1 $kvno1" + +setup_keytab "R13" $ktscratch admin admin { kttest1 kttest2 kttest1 } +set kvno1 [expr $kvno1+2] +set kvno2 [expr $kvno2+1] +keytab_run "R13" "$ktarg -r kttest2 $kvno2" 0 +klist_check "R13" $ktscratch "kttest1 [expr $kvno1-1]" "kttest1 $kvno1" + +setup_keytab "R14" $ktscratch admin admin { kttest1 kttest2 kttest1 kttest2 } +set kvno1 [expr $kvno1+2] +set kvno2 [expr $kvno2+2] +keytab_run "R14" "$ktarg -r kttest1 all" 0 +klist_check "R14" $ktscratch "kttest2 [expr $kvno2-1]" "kttest2 $kvno2" + +setup_keytab "R15" $ktscratch admin admin { kttest1 kttest2 kttest1 kttest2 } +set kvno1 [expr $kvno1+2] +set kvno2 [expr $kvno2+2] +keytab_run "R15" "$ktarg -r kttest1 old" 0 +klist_check "R15" $ktscratch "kttest2 [expr $kvno2-1]" \ + "kttest1 $kvno1" "kttest2 $kvno2" + +exec rm -f $ktscratch_file diff --git a/src/kadmin/ktutil/ChangeLog b/src/kadmin/ktutil/ChangeLog index 7c8582fd1..6ca64712d 100644 --- a/src/kadmin/ktutil/ChangeLog +++ b/src/kadmin/ktutil/ChangeLog @@ -2,6 +2,10 @@ Thu Jun 13 21:42:11 1996 Tom Yu <tlyu@voltage-multiplier.mit.edu> * configure.in: remove ref to SS_RULES +Fri Jul 12 14:37:47 1996 Marc Horowitz <marc@mit.edu> + + * configure.in (USE_KADM_LIBRARY): removed. it wasn't needed. + Tue Mar 19 19:41:31 1996 Richard Basch <basch@lehman.com> * ktutil_funcs.c (ktutil_write_srvtab): use any type of des key diff --git a/src/kadmin/ktutil/configure.in b/src/kadmin/ktutil/configure.in index 6be5dc2aa..7e9f3e1a0 100644 --- a/src/kadmin/ktutil/configure.in +++ b/src/kadmin/ktutil/configure.in @@ -1,7 +1,6 @@ AC_INIT(ktutil.c) CONFIG_RULES AC_PROG_INSTALL -USE_KADM_LIBRARY USE_KRB4_LIBRARY USE_SS_LIBRARY KRB5_LIBRARIES diff --git a/src/kadmin/passwd/ChangeLog b/src/kadmin/passwd/ChangeLog new file mode 100644 index 000000000..d0f4cbafc --- /dev/null +++ b/src/kadmin/passwd/ChangeLog @@ -0,0 +1,20 @@ +Mon Jul 22 04:07:02 1996 Marc Horowitz <marc@mit.edu> + + * tty_kpasswd.c: main returns int, not void + +Thu Jul 18 19:46:24 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Wed Jul 10 01:28:12 1996 Marc Horowitz <marc@mit.edu> + + * Makefile.in, configure.in: added autoconf support + +Tue Jul 9 15:03:13 1996 Marc Horowitz <marc@mit.edu> + + * kpasswd.c, tty_kpasswd.c, xm_kpasswd.c: renamed + <ovsec_admin/foo.h> to <kadm5/foo.h> + + * configure.in (CONFIG_DIRS): build the subdirs for the new admin + system, not the old one. + diff --git a/src/kadmin/passwd/Kpasswd b/src/kadmin/passwd/Kpasswd new file mode 100644 index 000000000..a7ec03161 --- /dev/null +++ b/src/kadmin/passwd/Kpasswd @@ -0,0 +1,46 @@ +*xm_ovpasswd.title: PW-CHG-GUI +*form.shadowThickness: 2 + +*foreground: black +*background: grey80 +*topShadowColor: grey95 +*bottomShadowColor: grey20 +*fontList: -*-helvetica-medium-r-*-*-14-* +*main_lbl.fontList: -*-helvetica-bold-r-*-*-14-* +*XmForm.Spacing: 5 + +*main_lbl.labelString: Changing password. +*old_lbl.labelString: Old password: +*new_lbl.labelString: New password: +*again_lbl.labelString: New password (again): +*sep.leftOffset: 0 +*sep.rightOffset: 0 +*Quit.labelString: Quit +*Help.labelString: Help + +*main_lbl.alignment: ALIGNMENT_CENTER +*lbl_form*alignment: ALIGNMENT_END +*scroll_win.shadowThickness: 0 + +*scroll_text.value: \ +Enter your old password below, and press return. You will not be able to see what you\n\ +are typing. After correctly entering your old password, you will be prompted twice for\n\ +your new password. Other messages and directions will appear in this space as necessary. +*scroll_text.rows: 5 +*scroll_text.columns: 66 +*scroll_text.scrollHorizontal: FALSE +*scroll_text.cursorPositionVisible: FALSE + +*help_dlg_popup.title: PW-CHG-GUI Help +*help_dlg.messageString: \ +Welcome to the Kerberos password changing GUI.\n\ +\n\ +In the main window, enter your old password when prompted. After verifying\n\ +your old password, the policy governing your password will be displayed, and\n\ +you will be prompted for a new password. You will then be asked to enter it\n\ +a second time, to make sure you have not made any typos. Assuming that\n\ +your new password complies with your password policy, you should receive\n\ +an acknowledgement that your password has been changed.\n\ +\n\ +If an error occurs, the process will start over from the beginning. You may\n\ +exit the application at any time by pressing the "Quit" button. diff --git a/src/kadmin/passwd/Makefile.in b/src/kadmin/passwd/Makefile.in new file mode 100644 index 000000000..46e962070 --- /dev/null +++ b/src/kadmin/passwd/Makefile.in @@ -0,0 +1,23 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) -I. -DUSE_KADM5_API_VERSION=1 + +PROG = kpasswd +OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o + +all:: $(PROG).local $(PROG) + +kpasswd_strings.c kpasswd_strings.h: $(srcdir)/kpasswd_strings.et + +$(OBJS): kpasswd_strings.h + +$(PROG).local: $(OBJS) $(SRVDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG).local $(OBJS) $(SRVLIBS) + +$(PROG): $(OBJS) $(CLNTDEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(CLNTLIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG).local ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) kpasswd_strings.c kpasswd_strings.h $(PROG).local $(PROG) $(OBJS) diff --git a/src/kadmin/passwd/Makefile.ov b/src/kadmin/passwd/Makefile.ov new file mode 100644 index 000000000..8841a4762 --- /dev/null +++ b/src/kadmin/passwd/Makefile.ov @@ -0,0 +1,34 @@ +TOP = .. +include $(TOP)/config.mk/template + +CFLAGS := $(CFLAGS) -DUSE_KADM5_API_VERSION=1 + +# This used as a string table, not an error table +ETABLES = kpasswd_strings.et +expand ErrorTables + +kpasswd.o: kpasswd_strings.h +depend:: kpasswd_strings.h + +PROG = kpasswd +SRCS = tty_kpasswd.c kpasswd.c kpasswd_strings.c +OBJS = tty_kpasswd.o kpasswd.o kpasswd_strings.o +LIBS = $(LIBADMCLNT) $(LIBCOM_ERR) $(LIBGSSAPI_KRB5) $(LIBRPCLIB) \ + $(LIBDYN) $(LIBDB) $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBISODE) $(NDBMLIB) $(BSDLIB) $(NETLIB) + +expand NormalProgram + +ifndef OMIT_XM_KPASSWD +PROG = xm_kpasswd +CFLAGS := -I$(XM_INC) -I$(XT_INC) -I$(X_INC) $(CFLAGS) +SRCS := xm_kpasswd.c kpasswd.c kpasswd_strings.c +OBJS := xm_kpasswd.o kpasswd.o kpasswd_strings.o +LIBS := $(LIBS) $(XM_LIB) $(XT_LIB) $(X_LIB) $(REGEXLIB) + +expand NormalProgram +endif + +SUBDIRS = unit-test + +expand SubdirTarget diff --git a/src/kadmin/passwd/configure.in b/src/kadmin/passwd/configure.in new file mode 100644 index 000000000..fe1389a83 --- /dev/null +++ b/src/kadmin/passwd/configure.in @@ -0,0 +1,13 @@ +AC_INIT(kpasswd.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_PROG_AWK +USE_KADMCLNT_LIBRARY +USE_GSSAPI_LIBRARY +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/passwd/kpasswd.c b/src/kadmin/passwd/kpasswd.c new file mode 100644 index 000000000..f87ad1cb0 --- /dev/null +++ b/src/kadmin/passwd/kpasswd.c @@ -0,0 +1,280 @@ +/* + * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * + */ + +static char rcsid[] = "$Id$"; + +#include <kadm5/admin.h> +#include <krb5.h> + +#include "kpasswd_strings.h" +#define string_text error_message +#define initialize_kpasswd_strings initialize_kpws_error_table + +#include <stdio.h> +#include <pwd.h> +#include <string.h> + +extern char *whoami; + +extern void display_intro_message(); +extern long read_old_password(); +extern long read_new_password(); + +#define MISC_EXIT_STATUS 6 + +/* + * Function: kpasswd + * + * Purpose: Initialize and call lower level routines to change a password + * + * Arguments: + * + * context (r) krb5_context to use + * argc/argv (r) principal name to use, optional + * read_old_password (f) function to read old password + * read_new_password (f) function to read new and change password + * display_intro_message (f) function to display intro message + * whoami (extern) argv[0] + * + * Returns: + * exit status of 0 for success + * 1 principal unknown + * 2 old password wrong + * 3 cannot initialize admin server session + * 4 new passwd mismatch or error trying to change pw + * 5 password not typed + * 6 misc error + * 7 incorrect usage + * + * Requires: + * Passwords cannot be more than 255 characters long. + * + * Effects: + * + * If argc is 2, the password for the principal specified in argv[1] + * is changed; otherwise, the principal of the default credential + * cache or username is used. display_intro_message is called with + * the arguments KPW_STR_CHANGING_PW_FOR and the principal name. + * read_old_password is then called to prompt for the old password. + * The admin system is then initialized, the principal's policy + * retrieved and explained, if appropriate, and finally + * read_new_password is called to read the new password and change the + * principal's password (presumably ovsec_kadm_chpass_principal). + * admin system is de-initialized before the function returns. + * + * Modifies: + * + * Changes the principal's password. + * + */ +int +kpasswd(context, argc, argv) + krb5_context context; + int argc; + char *argv[]; +{ + int code; + krb5_ccache ccache = NULL; + krb5_principal princ = 0; + char *princ_str; + struct passwd *pw = 0; + int pwsize; + char password[255]; /* I don't really like 255 but that's what kinit uses */ + char msg_ret[1024], admin_realm[1024]; + ovsec_kadm_principal_ent_t principal_entry = NULL; + ovsec_kadm_policy_ent_t policy_entry = NULL; + void *server_handle; + + if (argc > 2) { + com_err(whoami, KPW_STR_USAGE, 0); + return(7); + /*NOTREACHED*/ + } + + krb5_init_ets(context); + + /************************************ + * Get principal name to change * + ************************************/ + + /* Look on the command line first, followed by the default credential + cache, followed by defaulting to the Unix user name */ + + if (argc == 2) + princ_str = strdup(argv[1]); + else { + code = krb5_cc_default(context, &ccache); + /* If we succeed, find who is in the credential cache */ + if (code == 0) { + /* Get default principal from cache if one exists */ + code = krb5_cc_get_principal(context, ccache, &princ); + /* if we got a principal, unparse it, otherwise get out of the if + with an error code */ + (void) krb5_cc_close(context, ccache); + if (code == 0) { + code = krb5_unparse_name(context, princ, &princ_str); + if (code != 0) { + com_err(whoami, code, string_text(KPW_STR_UNPARSE_NAME)); + return(MISC_EXIT_STATUS); + } + } + } + + /* this is a crock.. we want to compare against */ + /* "KRB5_CC_DOESNOTEXIST" but there is no such error code, and */ + /* both the file and stdio types return FCC_NOFILE. If there is */ + /* ever another ccache type (or if the error codes are ever */ + /* fixed), this code will have to be updated. */ + if (code && code != KRB5_FCC_NOFILE) { + com_err(whoami, code, string_text(KPW_STR_WHILE_LOOKING_AT_CC)); + return(MISC_EXIT_STATUS); + } + + /* if either krb5_cc failed check the passwd file */ + if (code != 0) { + pw = getpwuid((int) getuid()); + if (pw == NULL) { + com_err(whoami, 0, string_text(KPW_STR_NOT_IN_PASSWD_FILE)); + return(MISC_EXIT_STATUS); + } + princ_str = strdup(pw->pw_name); + } + } + + display_intro_message(string_text(KPW_STR_CHANGING_PW_FOR), princ_str); + + /* Need to get a krb5_principal, unless we started from with one from + the credential cache */ + + if (! princ) { + code = krb5_parse_name (context, princ_str, &princ); + if (code != 0) { + com_err(whoami, code, string_text(KPW_STR_PARSE_NAME), princ_str); + free(princ_str); + return(MISC_EXIT_STATUS); + } + } + + pwsize = sizeof(password); + code = read_old_password(context, password, &pwsize); + + if (code != 0) { + memset(password, 0, sizeof(password)); + com_err(whoami, code, string_text(KPW_STR_WHILE_READING_PASSWORD)); + krb5_free_principal(context, princ); + free(princ_str); + return(MISC_EXIT_STATUS); + } + if (pwsize == 0) { + memset(password, 0, sizeof(password)); + com_err(whoami, 0, string_text(KPW_STR_NO_PASSWORD_READ)); + krb5_free_principal(context, princ); + free(princ_str); + return(5); + } + + admin_realm[0] = '\0'; + strncat(admin_realm, krb5_princ_realm(context, princ)->data, + krb5_princ_realm(context, princ)->length); + + code = ovsec_kadm_init(princ_str, password, OVSEC_KADM_CHANGEPW_SERVICE, + admin_realm /* we probably should take a -r */ + /* someday */, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &server_handle); + if (code != 0) { + if (code == OVSEC_KADM_BAD_PASSWORD) + com_err(whoami, 0, string_text(KPW_STR_OLD_PASSWORD_INCORRECT)); + else + com_err(whoami, 0, string_text(KPW_STR_CANT_OPEN_ADMIN_SERVER), admin_realm, + error_message(code)); + krb5_free_principal(context, princ); + free(princ_str); + return((code == OVSEC_KADM_BAD_PASSWORD)?2:3); + } + + /* Explain policy restrictions on new password if any. */ + /* Note: copy of this exists in login (kverify.c/get_verified_in_tkt). */ + + code = ovsec_kadm_get_principal(server_handle, princ, &principal_entry); + if (code != 0) { + com_err(whoami, 0, + string_text((code == OVSEC_KADM_UNK_PRINC) + ? KPW_STR_PRIN_UNKNOWN : KPW_STR_CANT_GET_POLICY_INFO), + princ_str); + krb5_free_principal(context, princ); + free(princ_str); + (void) ovsec_kadm_destroy(server_handle); + return((code == OVSEC_KADM_UNK_PRINC) ? 1 : MISC_EXIT_STATUS); + } + if ((principal_entry->aux_attributes & OVSEC_KADM_POLICY) != 0) { + code = ovsec_kadm_get_policy(server_handle, + principal_entry->policy, &policy_entry); + if (code != 0) { + /* doesn't matter which error comes back, there's no nice recovery + or need to differentiate to the user */ + com_err(whoami, 0, + string_text(KPW_STR_CANT_GET_POLICY_INFO), princ_str); + (void) ovsec_kadm_free_principal_ent(server_handle, principal_entry); + krb5_free_principal(context, princ); + free(princ_str); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + com_err(whoami, 0, string_text(KPW_STR_POLICY_EXPLANATION), + princ_str, principal_entry->policy, + policy_entry->pw_min_length, policy_entry->pw_min_classes); + if (code = ovsec_kadm_free_principal_ent(server_handle, principal_entry)) { + (void) ovsec_kadm_free_policy_ent(server_handle, policy_entry); + krb5_free_principal(context, princ); + free(princ_str); + com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + if (code = ovsec_kadm_free_policy_ent(server_handle, policy_entry)) { + krb5_free_principal(context, princ); + free(princ_str); + com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_POLICY)); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + } + else { + /* kpasswd *COULD* output something here to encourage the choice + of good passwords, in the absence of an enforced policy. */ + if (code = ovsec_kadm_free_principal_ent(server_handle, + principal_entry)) { + krb5_free_principal(context, princ); + free(princ_str); + com_err(whoami, code, string_text(KPW_STR_WHILE_FREEING_PRINCIPAL)); + (void) ovsec_kadm_destroy(server_handle); + return(MISC_EXIT_STATUS); + } + } + + pwsize = sizeof(password); + code = read_new_password(server_handle, password, &pwsize, msg_ret, princ); + memset(password, 0, sizeof(password)); + + if (code) + com_err(whoami, 0, msg_ret); + + krb5_free_principal(context, princ); + free(princ_str); + + (void) ovsec_kadm_destroy(server_handle); + + if (code == KRB5_LIBOS_CANTREADPWD) + return(5); + else if (code) + return(4); + else + return(0); +} diff --git a/src/kadmin/passwd/kpasswd_strings.et b/src/kadmin/passwd/kpasswd_strings.et new file mode 100644 index 000000000..b78aa9d6f --- /dev/null +++ b/src/kadmin/passwd/kpasswd_strings.et @@ -0,0 +1,76 @@ +# +# Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. +# +# String table of messages for kpasswd + + +error_table kpws + +# /* M1 */ +error_code KPW_STR_USAGE, "Usage: kpasswd [principal_name]." + +error_code KPW_STR_PRIN_UNKNOWN, + "Kerberos principal name %s is not recognized." +# /* <name> */ + +# /* M2 */ +error_code KPW_STR_WHILE_LOOKING_AT_CC, + "while reading principal name from credential cache." + +# /* M4 */ +error_code KPW_STR_OLD_PASSWORD_INCORRECT, + "Old Kerberos password is incorrect. Please try again." + +# /* M5 */ +error_code KPW_STR_CANT_OPEN_ADMIN_SERVER, +"Cannot establish a session with the Kerberos administrative server for\n\ +realm %s. %s." +# /* <realm-name>, <Specific error message from admin server library>. */ + +# /* M6 */ +error_code KPW_STR_NEW_PASSWORD_MISMATCH, + "New passwords do not match - password not changed.\n" + +# /* M7 */ +error_code KPW_STR_PASSWORD_CHANGED, "Kerberos password changed.\n" + +# /* M13 */ +error_code KPW_STR_PASSWORD_NOT_CHANGED, "Password not changed." + +error_code KPW_STR_PARSE_NAME, "when parsing name %s." +error_code KPW_STR_UNPARSE_NAME, "when unparsing name." +error_code KPW_STR_NOT_IN_PASSWD_FILE, "Unable to identify user from password file." + +# /* M3 */ +error_code KPW_STR_CHANGING_PW_FOR, "Changing password for %s." +# /* principal@realm */ + +error_code KPW_STR_OLD_PASSWORD_PROMPT, "Old password:" +error_code KPW_STR_WHILE_READING_PASSWORD, "while reading new password." + +# /* M4 */ +error_code KPW_STR_NO_PASSWORD_READ, +"You must type a password. Passwords must be at least one character long." + +# /* M14 */ +error_code KPW_STR_WHILE_TRYING_TO_CHANGE, "while trying to change password." + +error_code KPW_STR_WHILE_DESTROYING_ADMIN_SESSION, +"while closing session with admin server and destroying tickets." + +error_code KPW_STR_WHILE_FREEING_PRINCIPAL, +"while freeing admin principal entry" + +error_code KPW_STR_WHILE_FREEING_POLICY, +"while freeing admin policy entry" + +error_code KPW_STR_CANT_GET_POLICY_INFO, +"Could not get password policy information for principal %s." +# /* principal@realm */ + +error_code KPW_STR_POLICY_EXPLANATION, +"%s's password is controlled by the policy %s, which\nrequires a minimum of %u characters from at least %u classes (the five classes\nare lowercase, uppercase, numbers, punctuation, and all other characters)." +# /* principal_name policy_name min_length min_classes */ + +end + diff --git a/src/kadmin/passwd/tty_kpasswd.c b/src/kadmin/passwd/tty_kpasswd.c new file mode 100644 index 000000000..6d865a020 --- /dev/null +++ b/src/kadmin/passwd/tty_kpasswd.c @@ -0,0 +1,80 @@ +/* + * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * + */ + +static char rcsid[] = "$Id$"; + +#include <kadm5/admin.h> +#include <krb5.h> + +#include "kpasswd_strings.h" +#define string_text error_message +#define initialize_kpasswd_strings initialize_kpws_error_table + +#include <stdio.h> +#include <pwd.h> +#include <string.h> + +char *whoami; + +void display_intro_message(fmt_string, arg_string) + char *fmt_string; + char *arg_string; +{ + com_err(whoami, 0, fmt_string, arg_string); +} + +long read_old_password(context, password, pwsize) + krb5_context context; + char *password; + int *pwsize; +{ + long code = krb5_read_password(context, + (char *)string_text(KPW_STR_OLD_PASSWORD_PROMPT), + 0, password, pwsize); + return code; +} + +long read_new_password(server_handle, password, pwsize, msg_ret, princ) + void *server_handle; + char *password; + int *pwsize; + char *msg_ret; + krb5_principal princ; +{ + return (ovsec_kadm_chpass_principal_util(server_handle, princ, NULL, + NULL /* don't need new pw back */, + msg_ret)); +} + + +/* + * main() for tty version of kpasswd.c + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + krb5_context context; + int retval; + + initialize_kpasswd_strings(); + + whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0]; + + if (retval = krb5_init_context(&context)) { + com_err(whoami, retval, "initializing krb5 context"); + exit(retval); + } + retval = kpasswd(context, argc, argv); + + if (!retval) + printf(string_text(KPW_STR_PASSWORD_CHANGED)); + + exit(retval); +} diff --git a/src/kadmin/passwd/unit-test/Makefile.ov b/src/kadmin/passwd/unit-test/Makefile.ov new file mode 100644 index 000000000..db042720b --- /dev/null +++ b/src/kadmin/passwd/unit-test/Makefile.ov @@ -0,0 +1,23 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +USER = root + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-body:: + $(RUNTEST) KPASSWD=../kpasswd \ + KINIT=$(TOP)/../clients/kinit/kinit \ + KDESTROY=$(TOP)/../clients/kdestroy/kdestroy \ + USER=$(USER) --tool kpasswd + +unit-test-setup:: + $(START_SERVERS) + echo "source $(TCLUTIL); catch {ovsec_kadm_init admin admin \$$OVSEC_KADM_ADMIN_SERVICE null \$$OVSEC_KADM_STRUCT_VERSION \$$OVSEC_KADM_API_VERSION_1 server_handle; ovsec_kadm_create_principal \$$server_handle [simple_principal $(USER)] {OVSEC_KADM_PRINCIPAL} $(USER); ovsec_kadm_destroy \$$server_handle;}; if {[info exists errorInfo]} { puts stderr \$$errorInfo; exit 1; }" | $(CLNTTCL) + +unit-test-cleanup:: + $(STOP_SERVERS) diff --git a/src/kadmin/passwd/unit-test/config/unix.exp b/src/kadmin/passwd/unit-test/config/unix.exp new file mode 100644 index 000000000..c77aa016a --- /dev/null +++ b/src/kadmin/passwd/unit-test/config/unix.exp @@ -0,0 +1,36 @@ +# +# kpasswd_version -- extract and print the version number of kpasswd +# + +proc kpasswd_version {} { + global KPASSWD + catch "exec ident $KPASSWD" tmp + if [regexp {Id: kpasswd.c,v ([0-9]+\.[0-9]+)} $tmp \ + dummy version] then { + clone_output "$KPASSWD version $version\n" + } else { + clone_output "$KPASSWD version <unknown>\n" + } +} +# +# kpasswd_load -- loads the program +# +proc kpasswd_load {} { + # +} + +# kpasswd_exit -- clean up and exit +proc kpasswd_exit {} { + # +} + +# +# kpasswd_start -- start kpasswd running +# +proc kpasswd_start { args } { + global KPASSWD + global spawn_id + + verbose "% $KPASSWD $args" 1 + eval spawn $KPASSWD $args +} diff --git a/src/kadmin/passwd/unit-test/helpers.exp b/src/kadmin/passwd/unit-test/helpers.exp new file mode 100644 index 000000000..9dcfbcf01 --- /dev/null +++ b/src/kadmin/passwd/unit-test/helpers.exp @@ -0,0 +1,217 @@ +# +# $Id$ +# + +global s +set s "\[\r\n\t\ \]" + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} + +proc myfail { comment } { + global mytest_name + global mytest_status + wait + fail "$mytest_name: $comment" + set mytest_status 1 +} + +proc mypass {} { +} + +## +## When you expect on an id, and eof is detected, the spawn_id is closed. +## It may be waited for, but calling expect or close on this id is an ERROR! +## + +proc mytest { name kpargs status args } { + global spawn_id + global timeout + global mytest_name + global mytest_status + global wait_error_index wait_errno_index wait_status_index + + verbose "starting test: $name" + + set mytest_name "$name" + + eval kpasswd_start $kpargs + + # at the end, eof is success + + lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + + # for each test argument.... + # rep invariant: when this foreach ends, the id is close'd, but + # not wait'ed. + + foreach test $args { + set mytest_status 0 + + # treat the arg as an expect parameter + # if failure, the process will be closed and waited. + + uplevel 1 "expect { + $test + timeout { close; myfail \"timeout\"} + eof { myfail \"eof read before expected message string\" } + }" + + if {$mytest_status == 1} { return } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 1 + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } +} + +proc kinit { princ pass } { + global env; + global KINIT + spawn -noecho $KINIT $princ; + + expect { + -re {Password for .*: $} + {send "$pass\n"} + timeout {puts "Timeout waiting for prompt" ; close } + } + + # this necessary so close(1) in the child will not sleep waiting for + # the parent, which is us, to read pending data. + + expect { + eof {} + } + wait +} + +proc kdestroy {} { + global KDESTROY + global errorCode errorInfo + global env + + if {[info exists errorCode]} { + set saveErrorCode $errorCode + } + if {[info exists errorInfo]} { + set saveErrorInfo $errorInfo + } + catch "system $KDESTROY 2>/dev/null" + if {[info exists saveErrorCode]} { + set errorCode $saveErrorCode + } elseif {[info exists errorCode]} { + unset errorCode + } + if {[info exists saveErrorInfo]} { + set errorInfo $saveErrorInfo + } elseif {[info exists errorInfo]} { + unset errorInfo + } +} + +global initerr_str +global initerr_regexp +set initerr_str "Cannot establish a session with the Kerberos administrative server for realm \[^\r\n\]*\\. " +set initerr_regexp "Cannot establish a session with the Kerberos administrative server for$s+realm \[^\r\n\]*\\.$s+" + +proc test_win { args name princ pass1 { pass2 "\001\001" } } { + global s + global initerr_regexp + + if { $pass2 == "\001\001" } { set pass2 "$pass1" } + + mytest "$name" $args 0 { + -re "Changing password for $princ.*\\.$s+Old password:" + { send "$pass1\n" } + } { + -re "Old Kerberos password is incorrect. Please try again." + { close; myfail "Old password incorrect" } + -re "${initerr_regexp}(.+\[^\r\n\t\ \])\r\n" + { close; myfail "init error: $expect_out(1,string)" } + -re "^$s+New password:" + { send "$pass2\n" } + -re "^$s+.*$s+.*$s+.*$s+New password:" + { send "$pass2\n" } + } { + -re "^$s+New password \\(again\\):" + { send "$pass2\n" } + } { + -re "^$s+Kerberos password changed." + { mypass } + -re "^$s+Password changed." + { close; myfail "Wrong message on success." } + } +} + +proc test_initerr { args name princ pass status err } { + global s + global initerr_regexp + + regsub -all "$s+" $err "$s+" err2 + + mytest "$name" $args $status { + -re "Changing password for $princ.*\\.$s+Old password:" + { send "$pass\n" } + } { + -re "$err2" + { mypass } + -re "Old Kerberos password is incorrect. Please try again." + { close; myfail "Old password incorrect" } + -re "${initerr_regexp}(.+)\r\n" + { close; myfail "init error: $expect_out(1,string)" } + } +} + +proc test_3pass { args name princ pass1 pass2 pass3 status err } { + global s + global initerr_regexp + + regsub -all "$s+" $err "$s+" err2 + + mytest "$name" $args $status { + -re "Changing password for $princ.*\\.$s+Old password:" + { send "$pass1\n" } + } { + -re "Old Kerberos password is incorrect. Please try again." + { close; myfail "Old password incorrect" } + -re "${initerr_regexp}(.+)\r\n" + { close; myfail "init error: $expect_out(1,string)" } + -re "^$s+New password:" + { send "$pass2\n" } + -re "^$s+.*$s+.*$s+.*$s+New password:" + { send "$pass2\n" } + } { + -re "^$s+New password \\(again\\):" + { send "$pass3\n" } + } { + -re "$s+$err2" + { mypass } + } +} + diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp new file mode 100644 index 000000000..4f0354c63 --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/changing.exp @@ -0,0 +1,102 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +if [info exist env(DEBUG)] { debug 1 } + +# +# Here are the tests +# + +test_3pass {test2} {D.5: different new passwords} test2 test2 test2 foobar \ + 4 {New passwords do not match - password not changed.} + +test_3pass {test2} {D.7.5: empty/empty} test2 test2 {} {} \ + 5 {You must type a password. Passwords must be at least one character long.} + +test_3pass {test2} {D.6: empty/non-empty} test2 test2 {} test2 \ + 4 {New passwords do not match - password not changed.} + +test_3pass {test2} {D.7: non-empty/empty} test2 test2 test2 {} \ + 4 {New passwords do not match - password not changed.} + + +test_win {test1} {D.8: change password} test1 test1 newpass + +test_win {test1} {D.9: test changed password} test1 newpass test1 + +mytest "D.22: No policy description was shown" test1 4 { + -re "Changing password for test1.*\\.$s+Old password:" + { send "test1\n" } +} { + -re "$s+.*$s+.*$s+.*char.*classes.*" + { myfail "policy description displayed" } + timeout { mypass } +} { + -re "^$s+New password:" + { send "newpass\n" } +} { + -re "^$s+New password \\(again\\):" + { send "ssapwen\n" } +} { + -re "$s+New passwords do not match - password not changed." + { mypass } +} + +test_3pass {pol1} {D.10: new password too short} pol1 pol111111 que que \ + 4 {New password is too short. Please choose a password which is at least [0-9]+ characters long.} + +test_3pass {pol1} {D.13: too few char classes in new password} pol1 \ + pol111111 123456789 123456789 \ + 4 {New password does not have enough character classes. The character classes are: - lower-case letters, - upper-case letters, - digits, - punctuation, and - all other characters \(e.g., control characters\). Please choose a password with at least [0-9]+ character classes.} + +test_3pass {pol1} {D.14: new password in dictionary} pol1 \ + pol111111 Discordianism Discordianism \ + 4 {New password was found in a dictionary of possible passwords and therefore may be easily guessed. Please choose another password. See the ovpasswd man page for help in choosing a good password.} + +test_win {pol1} {successful change} pol1 pol111111 polAAAAAA +# fail "successful change: XXXX password history is majorly broken" + +test_3pass {pol1} {D.11: new password same as old} pol1 \ + polAAAAAA polAAAAAA polAAAAAA \ + 4 {New password was used previously. Please choose a different password.} + +test_3pass {pol1} {D.12: new password in history} pol1 \ + polAAAAAA pol111111 pol111111 \ + 4 {New password was used previously. Please choose a different password.} + +mytest "D.18: Policy description was shown" pol1 4 { + -re "Changing password for pol1.*\\.$s+Old password:" + { send "polAAAAAA\n" } +} { + -re "$s+.*$s+.*$s+.*8 char.*2 classes.*$s+New password:" + { send "newpass1234\n" } +} { + -re "^$s+New password \\(again\\):" + { send "newpass4321\n" } +} { + -re "$s+New passwords do not match - password not changed." + { mypass } +} + +# restore pol1's password to its initial value; see discussion in +# secure-kpasswd/2204 about secure-releng/2191 if you are confused +test_win {pol1} {successful change} pol1 polAAAAAA polBBBBBB +test_win {pol1} {successful change} pol1 polBBBBBB polCCCCCC +test_win {pol1} {successful change} pol1 polCCCCCC pol111111 + +test_win {pol2} {successful change} pol2 pol222222 polbbbbbb + +test_3pass {pol2} {D.15: too soon to change password} pol2 \ + polbbbbbb pol222222 pol222222 \ + 4 {Password cannot be changed because it was changed too recently. Please wait until .*199[0-9] before you change it. If you need to change your password before then, contact your system security administrator.} + +verbose "(sleeping 30 seconds)" +catch "exec sleep 30" + +test_win {pol2} {password min life passed} pol2 polbbbbbb pol222222 + diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp new file mode 100644 index 000000000..2cda17a6a --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/connecting.exp @@ -0,0 +1,29 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +if [info exist env(DEBUG)] { debug 1 } + +# +# Here are the tests +# + +test_initerr {test2} {C.4: empty old password (XXXX)} test2 {} \ + 5 {You must type a password. Passwords must be at least one character long.} + +test_initerr {test2} {C.5: incorrect old password} test2 foobar \ + 2 "Old Kerberos password is incorrect. Please try again." + +# set timeout 60 +# +#test_initerr {test2@SECURE-TEST-DEAD.OV.COM} {C.8: server up, daemon down} \ +# test2 test2 \ +# 3 "" +# +#test_initerr {test2@SECURE-TEST-DOWN.OV.COM} {C.8.5: server down} \ +# test2 test2 \ +# 3 "${initerr_str}Cannot contact any KDC for requested realm" diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp new file mode 100644 index 000000000..e2bc20569 --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/principal.exp @@ -0,0 +1,55 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +if [info exist env(DEBUG)] { debug 1 } + +# +# Here are the tests +# + +if {[info exists env(KRB5CCNAME)]} { + unset env(KRB5CCNAME) +} +kdestroy + +#### no principal specified + +set whoami $USER +test_win {} {B.7: default nonexisting ccache(1)} $whoami $whoami newpass +test_win {} {B.7: default nonexisting ccache(2)} $whoami newpass $whoami + +kinit test2 test2 +test_win {} {B.4: default existing cache containing existing principal} \ + test2 test2 newpass +kdestroy + +set env(KRB5CCNAME) FILE:/tmp/ovsec_adm_test_ccache +kinit test2 newpass +test_win {} {B.3: specified existing cache containing existing principal} \ + test2 newpass test2 +kdestroy +unset env(KRB5CCNAME) + +#### principal on command line + +# +test_win {test2} {B.14: existing principal, no realm} test2 test2 newpass + +# +test_initerr {bogus} {B.15, C.6: non-existent principal, no realm} bogus bogus \ + 3 "${initerr_str}Client not found in Kerberos database" + +# +test_win {test2@SECURE-TEST.OV.COM} {B.16: existing principal, with realm} \ + test2 newpass test2 + +# +test_initerr {bogus@SECURE-TEST.OV.COM} \ + {B.17: non-existent principal, with realm} \ + bogus bogus \ + 3 "${initerr_str}Client not found in Kerberos database" diff --git a/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp new file mode 100644 index 000000000..e132bab2f --- /dev/null +++ b/src/kadmin/passwd/unit-test/kpasswd.0/usage.exp @@ -0,0 +1,26 @@ +# +# $Id$ +# + +set timeout 15 + +load_lib "helpers.exp" + +# +# Here are the tests +# + +mytest {A.1: two args} {foo bar} 7 { + -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass } +} + +mytest {A.2: three args} {foo bar baz} 7 { + -re {[a-z./]+passwd: Usage: [a-z./]+passwd \[principal_name\]} { mypass } +} + +set env(KRB5CCNAME) bogus_type:bogus_ccname +mytest {B.5: malformed ccache name} {} 6 { + -re {[a-z./]+passwd: Unknown credential cache type while reading principal name from credential cache} { mypass } +} +unset env(KRB5CCNAME) + diff --git a/src/kadmin/passwd/xm_kpasswd.c b/src/kadmin/passwd/xm_kpasswd.c new file mode 100644 index 000000000..0db1111c6 --- /dev/null +++ b/src/kadmin/passwd/xm_kpasswd.c @@ -0,0 +1,450 @@ +/* + * Copyright 1993-1994 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Header$ + * + * + */ + +static char rcsid_2[] = "$Id$"; + +#include <kadm5/admin.h> +#include <krb5.h> + +#include "kpasswd_strings.h" +#define string_text error_message +#define initialize_kpasswd_strings initialize_kpws_error_table + +#include <stdio.h> +#include <pwd.h> +#include <string.h> + +char *whoami; + +#include <Xm/Xm.h> +#include <Xm/MessageB.h> +#include <Xm/ScrolledW.h> +#include <Xm/Form.h> +#include <Xm/Text.h> +#include <Xm/PushB.h> +#include <Xm/Label.h> +#include <Xm/Separator.h> +#include <X11/cursorfont.h> +#include <X11/Shell.h> + +Widget toplevel, scroll_text, prompt_text; +Widget quit_btn, help_btn, old_lbl, new_lbl, again_lbl, main_lbl; +XtAppContext app_con; +int looping; +int retval=0; + + +/*************************************************************************** + * + * A few utility functions for setting/unsetting the busy cursor + * (i.e. the watch cursor). + */ +static void +SetCursor(w,c) + Widget w; + Cursor c; +{ + while (XtIsSubclass(w, shellWidgetClass) != True) + w = XtParent(w); + + XDefineCursor(XtDisplay(w), XtWindow(w), c); + XFlush(XtDisplay(w)); +} + + +static void +SetStandardCursor() +{ + static Cursor ArrowCursor = (Cursor)NULL; + + if (ArrowCursor == (Cursor)NULL) + ArrowCursor = XCreateFontCursor(XtDisplay(toplevel), XC_top_left_arrow); + SetCursor(toplevel, ArrowCursor); +} + + +static void +SetWatchCursor() +{ + static Cursor WatchCursor = (Cursor)NULL; + + if (WatchCursor == (Cursor)NULL) + WatchCursor = XCreateFontCursor(XtDisplay(toplevel), XC_watch); + SetCursor(toplevel, WatchCursor); +} + + +/*************************************************************************** + * + * Set up a com_err hook, for displaying to a motif scrolling widget. + */ + +#if __STDC__ +# include <stdarg.h> +#else /* varargs: not STDC or no <stdarg> */ + /* Non-ANSI, always take <varargs.h> path. */ +# undef VARARGS +# define VARARGS 1 +# include <varargs.h> +#endif /* varargs */ + +static void +#ifdef __STDC__ +motif_com_err (const char *whoami, long code, const char *fmt, va_list args) +#else +motif_com_err (whoami, code, fmt, args) + const char *whoami; + long code; + const char *fmt; + va_list args; +#endif +{ + XEvent event; + char buf[2048]; + + buf[0] = '\0'; + + if (whoami) + { + strcpy(buf, whoami); + strcat(buf, ": "); + } + if (code) + { + strcat(buf, error_message(code)); + strcat(buf, " "); + } + if (fmt) + { + vsprintf(buf + strlen(buf), fmt, args); + } + + XtVaSetValues(scroll_text, XmNvalue, buf, NULL); + + for (; XtAppPending(app_con); ) + { + XtAppNextEvent(app_con, &event); + XtDispatchEvent(&event); + } +} + + +/*************************************************************************** + * + * Function to display help widget. + */ +static void +help() +{ + static Widget help_dlg = NULL; + + if (!help_dlg) + { + help_dlg = XmCreateInformationDialog(toplevel, "help_dlg", NULL, + 0); + XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_CANCEL_BUTTON)); + XtUnmanageChild(XmMessageBoxGetChild(help_dlg, XmDIALOG_HELP_BUTTON)); + } + XtManageChild(help_dlg); +} + + +/*************************************************************************** + * + * Unset the global "looping" when we want to get out of reading a + * password. + */ +static void +unset_looping() +{ + looping = 0; +} + + +/*************************************************************************** + * + * Function to exit the gui. Callback on the "Exit" button. + */ +static void +quit() +{ + exit(retval); +} + + +/*************************************************************************** + * + * Set up motif widgets, callbacks, etc. + */ +static void +create_widgets(argc, argv) + int *argc; + char *argv[]; +{ + Widget form, lbl_form, + sep, + scroll_win; + Pixel bg; + + toplevel = XtAppInitialize(&app_con, "Kpasswd", NULL, 0, argc, argv, + NULL, NULL, 0); + form = XtCreateManagedWidget("form", xmFormWidgetClass, toplevel, NULL, 0); + quit_btn = XtVaCreateManagedWidget("Quit", xmPushButtonWidgetClass, + form, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + XtAddCallback(quit_btn, XmNactivateCallback, quit, 0); + help_btn = XtVaCreateManagedWidget("Help", xmPushButtonWidgetClass, + form, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + /* XmNshowAsDefault, TRUE, */ + NULL); + XtAddCallback(help_btn, XmNactivateCallback, help, 0); + sep = XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, + form, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, quit_btn, + NULL); + lbl_form = XtVaCreateManagedWidget("lbl_form", xmFormWidgetClass, + form, + XmNspacing, 0, + XmNleftAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, sep, + NULL); + old_lbl = XtVaCreateManagedWidget("old_lbl", xmLabelWidgetClass, + lbl_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + new_lbl = XtVaCreateManagedWidget("new_lbl", xmLabelWidgetClass, + lbl_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + again_lbl = XtVaCreateManagedWidget("again_lbl", xmLabelWidgetClass, + lbl_form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_FORM, + NULL); + prompt_text = XtVaCreateManagedWidget("prompt_text", xmTextWidgetClass, + form, + XmNeditMode, XmSINGLE_LINE_EDIT, + XmNleftAttachment, XmATTACH_WIDGET, + XmNleftWidget, lbl_form, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, sep, + NULL); + XtAddCallback(prompt_text, XmNactivateCallback, unset_looping, 0); + XtVaGetValues(prompt_text, XmNbackground, &bg, NULL); + XtVaSetValues(prompt_text, XmNforeground, bg, NULL); + + main_lbl = XtVaCreateWidget("main_lbl", xmLabelWidgetClass, + form, + XmNtopAttachment, XmATTACH_FORM, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + NULL); + scroll_win = XtVaCreateManagedWidget("scroll_win", + xmScrolledWindowWidgetClass, + form, + XmNscrollingPolicy, XmAPPLICATION_DEFINED, + XmNscrollBarDisplayPolicy, XmSTATIC, + XmNtopAttachment, XmATTACH_WIDGET, + XmNtopWidget, main_lbl, + XmNleftAttachment, XmATTACH_FORM, + XmNrightAttachment, XmATTACH_FORM, + XmNbottomAttachment, XmATTACH_WIDGET, + XmNbottomWidget, prompt_text, + NULL); + scroll_text = XtVaCreateManagedWidget("scroll_text", xmTextWidgetClass, + scroll_win, + XmNeditMode, XmMULTI_LINE_EDIT, + XmNeditable, FALSE, + NULL); + XtRealizeWidget(toplevel); +} + + +/*************************************************************************** + * + * + */ +static long +read_password(password, pwsize) + char *password; + int *pwsize; +{ + XEvent event; + char *text_val; + + /* OK, this next part is gross... but this is due to the fact that */ + /* this is not your traditional X program, which would be event */ + /* driven. Instead, this program is more 'CLI' in nature, so we */ + /* handle the dialogs synchronously... */ + + XtVaSetValues(prompt_text, XmNmaxLength, *pwsize, XmNvalue, "", NULL); + for (looping=1; looping; ) + { + XtAppNextEvent(app_con, &event); + XtDispatchEvent(&event); + } + XtVaGetValues(prompt_text, XmNvalue, &text_val, NULL); + *pwsize = strlen(text_val); + strcpy(password, text_val); + memset(text_val, 0, *pwsize); + XtVaSetValues(prompt_text, XmNvalue, text_val, NULL); + return(0); +} + + +/*************************************************************************** + * + * + */ +void +display_intro_message(fmt_string, arg_string) + char *fmt_string; + char *arg_string; +{ + XmString xmstr; + char buf[1024]; + + sprintf(buf, fmt_string, arg_string); + + xmstr = XmStringCreateLtoR(buf, XmSTRING_DEFAULT_CHARSET); + XtVaSetValues(main_lbl, XmNlabelString, xmstr, NULL); + XmStringFree(xmstr); + XtManageChild(main_lbl); +} + + +long +read_old_password(context, password, pwsize) + krb5_context context; + char *password; + int *pwsize; +{ + long code; + + XtManageChild(old_lbl); + code = read_password(password, pwsize); + SetWatchCursor(); + return code; +} + +long +read_new_password(server_handle, password, pwsize, msg_ret, princ) + void *server_handle; + char *password; + int *pwsize; + char *msg_ret; + krb5_principal princ; +{ + char *password2 = (char *) malloc(*pwsize * sizeof(char)); + int pwsize2 = *pwsize; + + SetStandardCursor(); + + if (password2 == NULL) + { + strcpy(msg_ret, error_message(ENOMEM)); + SetWatchCursor(); + return(ENOMEM); + } + + XtManageChild(new_lbl); XtUnmanageChild(old_lbl); + read_password(password, pwsize); + XtManageChild(again_lbl); XtUnmanageChild(new_lbl); + read_password(password2, &pwsize2); + + if (strcmp(password, password2)) + { + memset(password, 0, *pwsize); + + memset(password2, 0, pwsize2); + free(password2); + + strcpy(msg_ret, string_text(CHPASS_UTIL_NEW_PASSWORD_MISMATCH)); + SetWatchCursor(); + return(KRB5_LIBOS_BADPWDMATCH); + } + + memset(password2, 0, pwsize2); + free(password2); + + SetWatchCursor(); + return (ovsec_kadm_chpass_principal_util(server_handle, princ, password, + NULL /* don't need new pw back */, + msg_ret)); +} + + +/*************************************************************************** + * + * + */ +void +main(argc, argv) + int argc; + char *argv[]; +{ + krb5_context context; + int code; + + initialize_kpasswd_strings(); + + whoami = (whoami = strrchr(argv[0], '/')) ? whoami + 1 : argv[0]; + + (void) set_com_err_hook(motif_com_err); + + create_widgets(&argc, argv); + XmProcessTraversal(prompt_text, XmTRAVERSE_CURRENT); + + if (retval = krb5_init_context(&context)) { + com_err(whoami, retval, "initializing krb5 context"); + exit(retval); + } + + while (1) + { + retval = kpasswd(context, argc, argv); + SetStandardCursor(); + + if (!retval) + com_err(0, 0, string_text(KPW_STR_PASSWORD_CHANGED)); + + if (retval == 0) /* 0 is success, so presumably the user */ + /* is done. */ + XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT); + + if ((retval == 1) || /* the rest are "fatal", so we should */ + (retval == 3) || /* "force" the user to quit... */ + (retval == 6) || + (retval == 7)) + { + XtSetSensitive(prompt_text, FALSE); + XmProcessTraversal(quit_btn, XmTRAVERSE_CURRENT); + XtAppMainLoop(app_con); + } + } + + /* NOTREACHED */ + exit(retval); +} diff --git a/src/kadmin/scripts/inst-hdrs.sh b/src/kadmin/scripts/inst-hdrs.sh new file mode 100644 index 000000000..242be89e9 --- /dev/null +++ b/src/kadmin/scripts/inst-hdrs.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +dir=$1; shift +while [ $# -gt 0 ]; do + file=$1 + cmp -s $file $dir/$file + if [ $? != 0 ]; then + echo "+ rm $dir/$file" + rm -f $dir/$file + echo "+ cp $file $dir/$file" + cp $file $dir/$file + fi + shift +done diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in new file mode 100644 index 000000000..41bea4f45 --- /dev/null +++ b/src/kadmin/server/Makefile.in @@ -0,0 +1,15 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) + +PROG = kadmind +OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o misc.o server_glue_v1.o + +all:: $(PROG) + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/server/Makefile.ov b/src/kadmin/server/Makefile.ov new file mode 100644 index 000000000..4f8e489fb --- /dev/null +++ b/src/kadmin/server/Makefile.ov @@ -0,0 +1,39 @@ +TOP = .. +include $(TOP)/config.mk/template +CFLAGS := $(CFLAGS) + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 +endif + +PROG := kadmind +SRCS := kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c misc.c server_glue_v1.c +OBJS := kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o misc.o server_glue_v1.o +LIBS := $(LIBADMSRV) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBDYN) \ + $(LIBKDB5) $(LIBKRB5_ALL) $(LIBDB) \ + $(NDBMLIB) $(NETLIB) $(BSDLIB) $(REGEXLIB) + +expand InstallServer +expand Depend + +clean:: + $(CLEAN) acls.c + +expand Saber + +SABER_LIBS := $(LIBDB) $(LIBGSSAPI_KRB5) $(LIBDYN) $(LIBKDB5) \ + $(LIBKRB5) $(LIBCRYPTO) $(LIBISODE) $(LIBCOM_ERR) +SABER_FLAGS := -G + +saber:: + #cd ../../rpc + #make saber + #cd ../admin/lib/adb + #make saber + #cd ../common + #make saber + #cd ../server + #make saber + #cd ../../server + #load /usr/local/lib/gcc-lib/sparc-sun-sunos4.1.3/2.4.5/libgcc.a + #load $(SABER_FLAGS) $(LDFLAGS) $(GSSLIB) $(SABER_LIBS) diff --git a/src/kadmin/server/acls.l b/src/kadmin/server/acls.l new file mode 100644 index 000000000..aee4801e9 --- /dev/null +++ b/src/kadmin/server/acls.l @@ -0,0 +1,190 @@ +%{ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.3 1996/07/22 20:28:49 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.2.4.1 1996/07/18 03:03:31 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.2.2.1 1996/06/20 21:56:31 marc + * File added to the repository on a branch + * + * Revision 1.2 1993/11/05 07:47:46 bjaspan + * add and use cmp_gss_names, fix regexp bug + * + * Revision 1.1 1993/11/05 07:08:48 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +enum tokens { + NEWLINE = 257, + COMMA, + SEMI, + + GET = 300, + ADD, + MODIFY, + DELETE, + + ID = 350, +}; + +typedef union { + char *s; +} toktype; + +toktype tokval; +int acl_lineno = 0; + +%} + +%% + +\n acl_lineno++; +[ \t]* ; +[ ]*#.* ; +"," return (COMMA); +";" return (SEMI); +"get" return (GET); +"add" return (ADD); +"modify" return (MODIFY); +"delete" return (DELETE); +^[^ \t\n]+ { tokval.s = yytext; return (ID); } + +%% + +#include <string.h> +#include <syslog.h> +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_krb5.h> +#include <ovsec_admin/admin.h> + +typedef struct _entry { + gss_name_t gss_name; + char *name; + u_int privs; + struct _entry *next; +} acl_entry; + +static acl_entry *acl_head = NULL; + +static void error(char *msg); + +int parse_aclfile(FILE *acl_file) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc in_buf; + acl_entry *entry; + enum tokens tok; + + yyin = acl_file; + + acl_lineno = 1; + while ((tok = yylex()) != 0) { + if (tok != ID) { + error("expected identifier"); + goto error; + } + + entry = (acl_entry *) malloc(sizeof(acl_entry)); + if (entry == NULL) { + error("out of memory"); + goto error; + } + entry->name = strdup(tokval.s); + entry->privs = 0; + while (1) { + switch (tok = yylex()) { + case GET: + entry->privs |= OVSEC_KADM_PRIV_GET; + break; + case ADD: + entry->privs |= OVSEC_KADM_PRIV_ADD; + break; + case MODIFY: + entry->privs |= OVSEC_KADM_PRIV_MODIFY; + break; + case DELETE: + entry->privs |= OVSEC_KADM_PRIV_DELETE; + break; + default: + error("expected privilege"); + goto error; + } + tok = yylex(); + if (tok == COMMA) + continue; + else if (tok == SEMI) + break; + else { + error("expected comma or semicolon"); + goto error; + } + } + + in_buf.value = entry->name; + in_buf.length = strlen(entry->name) + 1; + gssstat = gss_import_name(&minor_stat, &in_buf, + gss_nt_krb5_name, &entry->gss_name); + if (gssstat != GSS_S_COMPLETE) { + error("invalid name"); + goto error; + } + + if (acl_head == NULL) { + entry->next = NULL; + acl_head = entry; + } else { + entry->next = acl_head; + acl_head = entry; + } + } + return 0; + +error: + return 1; +} + +int acl_check(gss_name_t caller, int priv) +{ + acl_entry *entry; + + entry = acl_head; + while (entry) { + if (cmp_gss_names(entry->gss_name, caller) && entry->privs & priv) + return 1; + entry = entry->next; + } + return 0; +} + +int cmp_gss_names(gss_name_t name1, gss_name_t name2) +{ + OM_uint32 minor_stat; + int eq; + (void) gss_compare_name(&minor_stat, name1, name2, &eq); + return eq; +} + +static void error(char *msg) +{ + syslog(LOG_ERR, "Error while parsing acl file, line %d: %s\n", + acl_lineno, msg); +} + +yywrap() { return(1); } diff --git a/src/kadmin/server/configure.in b/src/kadmin/server/configure.in new file mode 100644 index 000000000..98492f909 --- /dev/null +++ b/src/kadmin/server/configure.in @@ -0,0 +1,17 @@ +AC_INIT(ovsec_kadmd.c) +CONFIG_RULES +AC_PROG_INSTALL +dnl AC_CHECK_FUNCS(waitpid vsprintf) +dnl AC_CHECK_HEADERS(sys/select.h) +dnl CHECK_SIGNALS +dnl CHECK_SETJMP +dnl CHECK_WAIT_TYPE +dnl ET_RULES +USE_KADMSRV_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_KDB5_LIBRARY +USE_DYN_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c new file mode 100644 index 000000000..9128821d5 --- /dev/null +++ b/src/kadmin/server/kadm_rpc_svc.c @@ -0,0 +1,248 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. + * + * $Id$ + * $Source$ + * + * $Log$ + * Revision 1.12 1996/07/22 20:28:53 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.11.4.1 1996/07/18 03:03:35 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.11.2.2 1996/07/09 20:07:57 marc + * * kadm_rpc_svc.c: renamed <ovsec_admin/foo.h> to <kadm5/foo.h> + * + * Revision 1.11.2.1 1996/06/20 21:56:44 marc + * File added to the repository on a branch + * + * Revision 1.11 1996/06/17 19:49:28 bjaspan + * use krb5_klog_syslog + * + * Revision 1.10 1996/05/29 21:07:53 bjaspan + * be a bit more loud when warning, and don't exit when args can't be freed + * + * Revision 1.9 1996/05/20 21:34:56 bjaspan + * log an error when sendreply fails + * + * Revision 1.8 1996/05/12 07:06:23 marc + * - fixup includes to match beta6 + * + * Revision 1.7 1995/08/01 19:25:59 bjaspan + * [secure/1318] allow retrieval of some/all principal/policy names + * + * Revision 1.6 1994/09/20 16:25:33 bjaspan + * [secure-admin/2436: API versioning fixes to various admin files] + * [secure-releng/2502: audit secure-admin/2436: random API versioning fixes] + * + * Sandbox: + * + * More API versioning stuff -- need to add api_version field to RPC + * return structures in addition to calling structures. + * + * Revision 1.6 1994/09/12 20:19:16 jik + * More API versioning stuff -- need to add api_version field to RPC + * return structures in addition to calling structures. + * + * Revision 1.5 1994/08/16 18:55:46 jik + * Versioning changes. + * + * Revision 1.4 1994/04/25 17:05:05 bjaspan + * [secure-admin/1832] accept old gssapi number, log error when number + * is wrong + * + * Revision 1.3 1993/11/15 02:30:54 shanzer + * added funky procedure header comments. + * + * Revision 1.2 1993/11/10 23:11:21 bjaspan + * added getprivs + * + * Revision 1.1 1993/11/05 07:09:00 bjaspan + * Initial revision + * + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <stdio.h> +#include <rpc/rpc.h> +#include <syslog.h> +#include <memory.h> +#include <kadm5/kadm_rpc.h> +#include <krb5.h> +#include <kadm5/admin.h> + +/* + * Function: kadm_1 + * + * Purpose: RPC proccessing procedure. + * originally generated from rpcgen + * + * Arguments: + * rqstp (input) rpc request structure + * transp (input) rpc transport structure + * (input/output) + * <return value> + * + * Requires: + * Effects: + * Modifies: + */ + +void kadm_1(rqstp, transp) + struct svc_req *rqstp; + register SVCXPRT *transp; +{ + union { + cprinc_arg create_principal_1_arg; + dprinc_arg delete_principal_1_arg; + mprinc_arg modify_principal_1_arg; + rprinc_arg rename_principal_1_arg; + gprinc_arg get_principal_1_arg; + chpass_arg chpass_principal_1_arg; + chrand_arg chrand_principal_1_arg; + cpol_arg create_policy_1_arg; + dpol_arg delete_policy_1_arg; + mpol_arg modify_policy_1_arg; + gpol_arg get_policy_1_arg; + } argument; + char *result; + bool_t (*xdr_argument)(), (*xdr_result)(); + char *(*local)(); + + if (rqstp->rq_cred.oa_flavor != AUTH_GSSAPI && + rqstp->rq_cred.oa_flavor != AUTH_GSSAPI_COMPAT) { + krb5_klog_syslog(LOG_ERR, "Authentication attempt failed: %s, invalid " + "RPC authentication flavor %d", + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr), + rqstp->rq_cred.oa_flavor); + svcerr_weakauth(transp); + return; + } + + switch (rqstp->rq_proc) { + case NULLPROC: + (void) svc_sendreply(transp, xdr_void, (char *)NULL); + return; + + case CREATE_PRINCIPAL: + xdr_argument = xdr_cprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) create_principal_1; + break; + + case DELETE_PRINCIPAL: + xdr_argument = xdr_dprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) delete_principal_1; + break; + + case MODIFY_PRINCIPAL: + xdr_argument = xdr_mprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) modify_principal_1; + break; + + case RENAME_PRINCIPAL: + xdr_argument = xdr_rprinc_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) rename_principal_1; + break; + + case GET_PRINCIPAL: + xdr_argument = xdr_gprinc_arg; + xdr_result = xdr_gprinc_ret; + local = (char *(*)()) get_principal_1; + break; + + case GET_PRINCS: + xdr_argument = xdr_gprincs_arg; + xdr_result = xdr_gprincs_ret; + local = (char *(*)()) get_princs_1; + break; + + case CHPASS_PRINCIPAL: + xdr_argument = xdr_chpass_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) chpass_principal_1; + break; + + case CHRAND_PRINCIPAL: + xdr_argument = xdr_chrand_arg; + xdr_result = xdr_chrand_ret; + local = (char *(*)()) chrand_principal_1; + break; + + case CREATE_POLICY: + xdr_argument = xdr_cpol_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) create_policy_1; + break; + + case DELETE_POLICY: + xdr_argument = xdr_dpol_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) delete_policy_1; + break; + + case MODIFY_POLICY: + xdr_argument = xdr_mpol_arg; + xdr_result = xdr_generic_ret; + local = (char *(*)()) modify_policy_1; + break; + + case GET_POLICY: + xdr_argument = xdr_gpol_arg; + xdr_result = xdr_gpol_ret; + local = (char *(*)()) get_policy_1; + break; + + case GET_POLS: + xdr_argument = xdr_gpols_arg; + xdr_result = xdr_gpols_ret; + local = (char *(*)()) get_pols_1; + break; + + case GET_PRIVS: + xdr_argument = xdr_u_int32; + xdr_result = xdr_getprivs_ret; + local = (char *(*)()) get_privs_1; + break; + + case INIT: + xdr_argument = xdr_u_int32; + xdr_result = xdr_generic_ret; + local = (char *(*)()) init_1; + break; + + default: + krb5_klog_syslog(LOG_ERR, "Invalid OVSEC_KADM procedure number: %s, %d", + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr), + rqstp->rq_proc); + svcerr_noproc(transp); + return; + } + memset((char *)&argument, 0, sizeof(argument)); + if (!svc_getargs(transp, xdr_argument, &argument)) { + svcerr_decode(transp); + return; + } + result = (*local)(&argument, rqstp); + if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { + krb5_klog_syslog(LOG_ERR, "WARNING! Unable to send function results, " + "continuing."); + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, xdr_argument, &argument)) { + krb5_klog_syslog(LOG_ERR, "WARNING! Unable to free arguments, " + "continuing."); + } + return; +} diff --git a/src/kadmin/server/misc.c b/src/kadmin/server/misc.c new file mode 100644 index 000000000..9dc3d9d28 --- /dev/null +++ b/src/kadmin/server/misc.c @@ -0,0 +1,138 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <kadm5/adb.h> +#include <kadm5/server_internal.h> +#include <krb5/kdb.h> +#include "misc.h" + +/* + * Function: chpass_principal_wrapper + * + * Purpose: wrapper to kadm5_chpass_principal that checks to see if + * pw_min_life has been reached. if not it returns an error. + * otherwise it calls kadm5_chpass_principal + * + * Arguments: + * principal (input) krb5_principals whose password we are + * changing + * passoword (input) passowrd we are going to change to. + * <return value> 0 on sucsess error code on failure. + * + * Requires: + * kadm5_init to have been run. + * + * Effects: + * calls kadm5_chpass_principal which changes the kdb and the + * the admin db. + * + */ +kadm5_ret_t +chpass_principal_wrapper(void *server_handle, + krb5_principal principal, char *password) +{ + krb5_int32 now; + kadm5_ret_t ret; + kadm5_policy_ent_rec pol; + kadm5_principal_ent_rec princ; + kadm5_server_handle_t handle = server_handle; + + if (ret = krb5_timeofday(handle->context, &now)) + return ret; + + if((ret = kadm5_get_principal(handle->lhandle, principal, + &princ, + KADM5_PRINCIPAL_NORMAL_MASK)) != + KADM5_OK) + return ret; + if(princ.aux_attributes & KADM5_POLICY) { + if((ret=kadm5_get_policy(handle->lhandle, + princ.policy, &pol)) != KADM5_OK) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + if((now - princ.last_pwd_change) < pol.pw_min_life && + !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) kadm5_free_policy_ent(handle->lhandle, &pol); + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return KADM5_PASS_TOOSOON; + } + if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + } + if (ret = kadm5_free_principal_ent(handle->lhandle, &princ)) + return ret; + + return kadm5_chpass_principal(server_handle, principal, password); +} + + +/* + * Function: randkey_principal_wrapper + * + * Purpose: wrapper to kadm5_randkey_principal which checks the + passwords min. life. + * + * Arguments: + * principal (input) krb5_principal whose password we are + * changing + * key (output) new random key + * <return value> 0, error code on error. + * + * Requires: + * kadm5_init needs to be run + * + * Effects: + * calls kadm5_randkey_principal + * + */ +kadm5_ret_t +randkey_principal_wrapper(void *server_handle, + krb5_principal principal, + krb5_keyblock **keys, int *n_keys) +{ + + krb5_int32 now; + kadm5_ret_t ret; + kadm5_policy_ent_rec pol; + kadm5_principal_ent_rec princ; + kadm5_server_handle_t handle = server_handle; + + if (ret = krb5_timeofday(handle->context, &now)) + return ret; + + if((ret = kadm5_get_principal(handle->lhandle, + principal, &princ, + KADM5_PRINCIPAL_NORMAL_MASK)) != + OSA_ADB_OK) + return ret; + if(princ.aux_attributes & KADM5_POLICY) { + if((ret=kadm5_get_policy(handle->lhandle, + princ.policy, &pol)) != KADM5_OK) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + if((now - princ.last_pwd_change) < pol.pw_min_life && + !(princ.attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) kadm5_free_policy_ent(handle->lhandle, &pol); + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return KADM5_PASS_TOOSOON; + } + if (ret = kadm5_free_policy_ent(handle->lhandle, &pol)) { + (void) kadm5_free_principal_ent(handle->lhandle, &princ); + return ret; + } + } + if (ret = kadm5_free_principal_ent(handle->lhandle, &princ)) + return ret; + return kadm5_randkey_principal(server_handle, principal, keys, n_keys); +} diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h new file mode 100644 index 000000000..c92f5fe32 --- /dev/null +++ b/src/kadmin/server/misc.h @@ -0,0 +1,53 @@ +/* + * Copyright 1994 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + * + * $Log$ + * Revision 1.6 1996/07/22 20:28:56 marc + * this commit includes all the changes on the OV_9510_INTEGRATION and + * OV_MERGE branches. This includes, but is not limited to, the new openvision + * admin system, and major changes to gssapi to add functionality, and bring + * the implementation in line with rfc1964. before committing, the + * code was built and tested for netbsd and solaris. + * + * Revision 1.5.4.1 1996/07/18 03:03:40 marc + * merged in changes from OV_9510_BP to OV_9510_FINAL1 + * + * Revision 1.5.2.1 1996/06/20 21:57:20 marc + * File added to the repository on a branch + * + * Revision 1.5 1996/05/30 21:13:24 bjaspan + * kadm5_get_principal_v1 takes a kadm5_principal_ent_t_v1 + * add kadm5_get_policy_v1 + * + * Revision 1.4 1996/05/20 21:39:05 bjaspan + * rename to kadm5 + * add kadm5_get_principal_v1 + * + * Revision 1.3 1994/09/13 18:24:41 jik + * Back out randkey changes. + * + * Revision 1.2 1994/09/12 20:26:12 jik + * randkey_principal_wrapper now takes a new_kvno option. + * + * Revision 1.1 1994/08/11 17:00:44 jik + * Initial revision + * + */ + +kadm5_ret_t chpass_principal_wrapper(void *server_handle, + krb5_principal principal, + char *password); + +kadm5_ret_t randkey_principal_wrapper(void *server_handle, + krb5_principal principal, + krb5_keyblock **key, + int *n_keys); + +kadm5_ret_t kadm5_get_principal_v1(void *server_handle, + krb5_principal principal, + kadm5_principal_ent_t_v1 *ent); + +kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name, + kadm5_policy_ent_t *ent); diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c new file mode 100644 index 000000000..34f233893 --- /dev/null +++ b/src/kadmin/server/ovsec_kadmd.c @@ -0,0 +1,762 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <stdio.h> +#include <signal.h> +#include <syslog.h> +#include <sys/types.h> +#ifdef _AIX +#include <sys/select.h> +#endif +#include <sys/time.h> +#include <sys/socket.h> +#include <unistd.h> +#include <netinet/in.h> +#include <arpa/inet.h> /* inet_ntoa */ +#include <netdb.h> +#include <rpc/rpc.h> +#include <gssapi/gssapi_krb5.h> +#include <rpc/auth_gssapi.h> +#include <kadm5/admin.h> +#include <kadm5/kadm_rpc.h> +#include <string.h> + +#ifdef PURIFY +#include "purify.h" + +int signal_pure_report = 0; +int signal_pure_clear = 0; +void request_pure_report(int); +void request_pure_clear(int); +#endif /* PURIFY */ + +int signal_request_exit = 0; +int signal_request_reset = 0; +void request_exit(int); +void request_reset_db(int); +void reset_db(void); +void sig_pipe(int); +void kadm_svc_run(void); + +#define TIMEOUT 15 + +gss_name_t gss_changepw_name = NULL, gss_oldchangepw_name = NULL; +void *global_server_handle; + +/* + * This is a kludge, but the server needs these constants to be + * compatible with old clients. They are defined in <kadm5/admin.h>, + * but only if USE_KADM5_API_VERSION == 1. + */ +#define OVSEC_KADM_ADMIN_SERVICE "ovsec_adm/admin" +#define OVSEC_KADM_CHANGEPW_SERVICE "ovsec_adm/changepw" + +/* + * This enables us to set the keytab that gss_acquire_cred uses, but + * it also restricts us to linking against the Kv5 GSS-API library. + * Since this is *k*admind, that shouldn't be a problem. + */ +extern char *krb5_defkeyname; + +char *build_princ_name(char *name, char *realm); +void log_badauth(OM_uint32 major, OM_uint32 minor, + struct sockaddr_in *addr, char *data); +void log_badverf(gss_name_t client_name, gss_name_t server_name, + struct svc_req *rqst, struct rpc_msg *msg, + char *data); +void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, char + *error, char *data); +void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor); +void log_badauth_display_status_1(char *m, OM_uint32 code, int type, + int rec); + + +/* + * Function: usage + * + * Purpose: print out the server usage message + * + * Arguments: + * Requires: + * Effects: + * Modifies: + */ + +void usage() +{ + fprintf(stderr, "Usage: kadmind [-r realm] [-m] [-nofork] " + "[-port port-number]\n"); + exit(1); +} + +/* XXX yuck. the signal handlers need this */ +static krb5_context context; + +int main(int argc, char *argv[]) +{ + void kadm_1(struct svc_req *, SVCXPRT *); + register SVCXPRT *transp; + extern char *optarg; + extern int optind, opterr; + int ret, rlen, nofork, oldnames = 0; + OM_uint32 OMret; + char *whoami; + FILE *acl_file; + gss_buffer_desc in_buf; + struct servent *srv; + struct sockaddr_in addr; + int s; + short port = 0; + auth_gssapi_name names[4]; + kadm5_config_params params; + + names[0].name = names[1].name = names[2].name = names[3].name = NULL; + names[0].type = names[1].type = names[2].type = names[3].type = + gss_nt_krb5_name; + +#ifdef PURIFY + purify_start_batch(); +#endif /* PURIFY */ + whoami = argv[0]; + + nofork = 0; + + memset((char *) ¶ms, 0, sizeof(params)); + + argc--; argv++; + while (argc) { + if (strcmp(*argv, "-r") == 0) { + argc--; argv++; + if (!argc) + usage(); + params.realm = *argv; + params.mask |= KADM5_CONFIG_REALM; + argc--; argv++; + continue; + } else if (strcmp(*argv, "-m") == 0) { + params.mkey_from_kbd = 1; + params.mask |= KADM5_CONFIG_MKEY_FROM_KBD; + } else if (strcmp(*argv, "-nofork") == 0) { + nofork = 1; + } else if(strcmp(*argv, "-port") == 0) { + argc--; argv++; + if(!argc) + usage(); + params.kadmind_port = atoi(*argv); + params.mask |= KADM5_CONFIG_KADMIND_PORT; + } else + break; + argc--; argv++; + } + + if (argc != 0) + usage(); + + if (ret = krb5_init_context(&context)) { + fprintf(stderr, "%s: %s while initializing context, aborting\n", + whoami, error_message(ret)); + exit(1); + } + + krb5_klog_init(context, "admin_server", whoami, 1); + + if((ret = kadm5_init("kadmind", NULL, + NULL, ¶ms, + KADM5_STRUCT_VERSION, + KADM5_API_VERSION_2, + &global_server_handle)) != + KADM5_OK) { + krb5_klog_syslog(LOG_ERR, "%s while initializing, aborting", + error_message(ret)); + fprintf(stderr, "%s: %s while initializing, aborting\n", + whoami, error_message(ret)); + krb5_klog_close(); + exit(1); + } + + if (ret = kadm5_get_config_params(context, NULL, NULL, ¶ms, + ¶ms)) { + krb5_klog_syslog(LOG_ERR, "%s: %s while initializing, aborting\n", + whoami, error_message(ret)); + fprintf(stderr, "%s: %s while initializing, aborting\n", + whoami, error_message(ret)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + +#define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_ACL_FILE | \ + KADM5_CONFIG_ADMIN_KEYTAB) + + if ((params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { + krb5_klog_syslog(LOG_ERR, "%s: Missing required configuration values " + "(%x) while initializing, aborting\n", whoami, + (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); + fprintf(stderr, "%s: Missing required configuration values " + "(%x) while initializing, aborting\n", whoami, + (params.mask & REQUIRED_PARAMS) ^ REQUIRED_PARAMS); + krb5_klog_close(); + kadm5_destroy(global_server_handle); + exit(1); + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(params.kadmind_port); + + if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s", + error_message(errno)); + fprintf(stderr, "Cannot create TCP socket: %s", + error_message(errno)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + #ifdef SO_REUSEADDR + /* the old admin server turned on SO_REUSEADDR for non-default + port numbers. this was necessary, on solaris, for the tests + to work. jhawk argues that the debug and production modes + should be the same. I think I agree, so I'm always going to set + SO_REUSEADDR. The other option is to have the unit tests wait + until the port is useable, or use a different port each time. + --marc */ + + { + int allowed; + + allowed = 1; + if (setsockopt(s, + SOL_SOCKET, + SO_REUSEADDR, + (char *) &allowed, + sizeof(allowed)) < 0) { + krb5_klog_syslog(LOG_ERR, "Cannot set SO_REUSEADDR: %s", + error_message(errno)); + fprintf(stderr, "Cannot set SO_REUSEADDR: %s", + error_message(errno)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + } + #endif + if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + int oerrno = errno; + fprintf(stderr, "%s: Cannot bind socket.\n", whoami); + fprintf(stderr, "bind: %s\n", error_message(oerrno)); + errno = oerrno; + krb5_klog_syslog(LOG_ERR, "Cannot bind socket: %m"); + if(oerrno == EADDRINUSE) { + char *w = strrchr(whoami, '/'); + if (w) { + w++; + } + else { + w = whoami; + } + fprintf(stderr, +"This probably means that another %s process is already\n" +"running, or that another program is using the server port (number %d)\n" +"after being assigned it by the RPC portmap deamon. If another\n" +"%s is already running, you should kill it before\n" +"restarting the server. If, on the other hand, another program is\n" +"using the server port, you should kill it before running\n" +"%s, and ensure that the conflict does not occur in the\n" +"future by making sure that %s is started on reboot\n" + "before portmap.\n", w, ntohs(addr.sin_port), w, w, w); + krb5_klog_syslog(LOG_ERR, "Check for already-running %s or for " + "another process using port %d", w, + htons(addr.sin_port)); + } + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + transp = svctcp_create(s, 0, 0); + if(transp == NULL) { + fprintf(stderr, "%s: Cannot create RPC service.\n", whoami); + krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %m"); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + if(!svc_register(transp, KADM, KADMVERS, kadm_1, 0)) { + fprintf(stderr, "%s: Cannot register RPC service.\n", whoami); + krb5_klog_syslog(LOG_ERR, "Cannot register RPC service, failing."); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm); + names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm); + names[2].name = build_princ_name(OVSEC_KADM_ADMIN_SERVICE, params.realm); + names[3].name = build_princ_name(OVSEC_KADM_CHANGEPW_SERVICE, + params.realm); + if (names[0].name == NULL || names[1].name == NULL || + names[2].name == NULL || names[3].name == NULL) { + krb5_klog_syslog(LOG_ERR, "Cannot initialize GSS-API authentication, " + "failing."); + fprintf(stderr, "%s: Cannot initialize GSS-API authentication.\n", + whoami); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + krb5_defkeyname = params.admin_keytab; + + /* + * Try to acquire creds for the old OV services as well as the + * new names, but if that fails just fall back on the new names. + */ + if (_svcauth_gssapi_set_names(names, 4) == TRUE) + oldnames++; + if (!oldnames && _svcauth_gssapi_set_names(names, 2) == FALSE) { + krb5_klog_syslog(LOG_ERR, "Cannot initialize GSS-API authentication, " + "failing."); + fprintf(stderr, "%s: Cannot initialize GSS-API authentication.\n", + whoami); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + /* if set_names succeeded, this will too */ + in_buf.value = names[1].name; + in_buf.length = strlen(names[1].name) + 1; + (void) gss_import_name(&OMret, &in_buf, gss_nt_krb5_name, + &gss_changepw_name); + if (oldnames) { + in_buf.value = names[3].name; + in_buf.length = strlen(names[3].name) + 1; + (void) gss_import_name(&OMret, &in_buf, gss_nt_krb5_name, + &gss_oldchangepw_name); + } + + _svcauth_gssapi_set_log_badauth_func(log_badauth, NULL); + _svcauth_gssapi_set_log_badverf_func(log_badverf, NULL); + _svcauth_gssapi_set_log_miscerr_func(log_miscerr, NULL); + + if (ret = acl_init(context, 0, params.acl_file)) { + krb5_klog_syslog(LOG_ERR, "Cannot initialize acl file: %s", + error_message(ret)); + fprintf(stderr, "%s: Cannot initialize acl file: %s\n", + whoami, error_message(ret)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + if (!nofork && (ret = daemon(0, 0))) { + ret = errno; + krb5_klog_syslog(LOG_ERR, "Cannot detach from tty: %s", error_message(ret)); + fprintf(stderr, "%s: Cannot detach from tty: %s\n", + whoami, error_message(ret)); + kadm5_destroy(global_server_handle); + krb5_klog_close(); + exit(1); + } + + signal(SIGINT, request_exit); + signal(SIGTERM, request_exit); + signal(SIGQUIT, request_exit); + signal(SIGHUP, request_reset_db); + signal(SIGPIPE, sig_pipe); +#ifdef PURIFY + signal(SIGUSR1, request_pure_report); + signal(SIGUSR2, request_pure_clear); +#endif /* PURIFY */ + krb5_klog_syslog(LOG_INFO, "starting"); + + kadm_svc_run(); + krb5_klog_syslog(LOG_INFO, "finished, exiting"); + kadm5_destroy(global_server_handle); + close(s); + krb5_klog_close(); + exit(2); +} + +/* + * Function: kadm_svc_run + * + * Purpose: modified version of sunrpc svc_run. + * which closes the database every TIMEOUT seconds. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + */ + +void kadm_svc_run(void) +{ + fd_set rfd; + int sz = _rpc_dtablesize(); + struct timeval timeout; + + while(signal_request_exit == 0) { + if (signal_request_reset) + reset_db(); +#ifdef PURIFY + if (signal_pure_report) /* check to see if a report */ + /* should be dumped... */ + { + purify_new_reports(); + signal_pure_report = 0; + } + if (signal_pure_clear) /* ...before checking whether */ + /* the info should be cleared. */ + { + purify_clear_new_reports(); + signal_pure_clear = 0; + } +#endif /* PURIFY */ + timeout.tv_sec = TIMEOUT; + timeout.tv_usec = 0; + rfd = svc_fdset; + switch(select(sz, (fd_set *) &rfd, NULL, NULL, &timeout)) { + case -1: + if(errno == EINTR) + continue; + perror("select"); + return; + case 0: + reset_db(); + break; + default: + svc_getreqset(&rfd); + } + } +} + +#ifdef PURIFY +/* + * Function: request_pure_report + * + * Purpose: sets flag saying the server got a signal and that it should + * dump a purify report when convenient. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + * sets signal_pure_report to one + */ + +void request_pure_report(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report"); + signal_pure_report = 1; + return; +} + +/* + * Function: request_pure_clear + * + * Purpose: sets flag saying the server got a signal and that it should + * dump a purify report when convenient, then clear the + * purify tables. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + * sets signal_pure_report to one + * sets signal_pure_clear to one + */ + +void request_pure_clear(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request a Purify report and clear the old Purify info"); + signal_pure_report = 1; + signal_pure_clear = 1; + return; +} +#endif /* PURIFY */ + +/* + * Function: request_reset_db + * + * Purpose: sets flag saying the server got a signal and that it should + * reset the database files when convenient. + * + * Arguments: + * Requires: + * Effects: + * Modifies: + * sets signal_request_reset to one + */ + +void request_reset_db(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request resetting the databases"); + signal_request_reset = 1; + return; +} + +/* + * Function: reset-db + * + * Purpose: flushes the currently opened database files to disk. + * + * Arguments: + * Requires: + * Effects: + * + * Currently, just sets signal_request_reset to 0. The kdb and adb + * libraries used to be sufficiently broken that it was prudent to + * close and reopen the databases periodically. They are no longer + * that broken, so this function is not necessary. + */ +void reset_db(void) +{ +#ifdef notdef + kadm5_ret_t ret; + + if (ret = kadm5_flush(global_server_handle)) { + krb5_klog_syslog(LOG_ERR, "FATAL ERROR! %s while flushing databases. " + "Databases may be corrupt! Aborting.", + error_message(ret)); + krb5_klog_close(); + exit(3); + } +#endif + + signal_request_reset = 0; + return; +} + +/* + * Function: request-exit + * + * Purpose: sets flags saying the server got a signal and that it + * should exit when convient. + * + * Arguments: + * Requires: + * Effects: + * modifies signal_request_exit which ideally makes the server exit + * at some point. + * + * Modifies: + * signal_request_exit + */ + +void request_exit(int signum) +{ + krb5_klog_syslog(LOG_DEBUG, "Got signal to request exit"); + signal_request_exit = 1; + return; +} + +/* + * Function: sig_pipe + * + * Purpose: SIGPIPE handler + * + * Effects: krb5_klog_syslogs a message that a SIGPIPE occurred and returns, + * thus causing the read() or write() to fail and, presumable, the RPC + * to recover. Otherwise, the process aborts. + */ +void sig_pipe(int unused) +{ + krb5_klog_syslog(LOG_NOTICE, "Warning: Received a SIGPIPE; probably a " + "client aborted. Continuing."); + return; +} + +/* + * Function: build_princ_name + * + * Purpose: takes a name and a realm and builds a string that can be + * consumed by krb5_parse_name. + * + * Arguments: + * name (input) name to be part of principal + * realm (input) realm part of principal + * <return value> char * pointing to "name@realm" + * + * Requires: + * name be non-null. + * + * Effects: + * Modifies: + */ + +char *build_princ_name(char *name, char *realm) +{ + char *fullname; + + fullname = (char *) malloc(strlen(name) + 1 + + (realm ? strlen(realm) + 1 : 0)); + if (fullname == NULL) + return NULL; + if (realm) + sprintf(fullname, "%s@%s", name, realm); + else + strcpy(fullname, name); + return fullname; +} + +/* + * Function: log_badverf + * + * Purpose: Call from GSS-API Sun RPC for garbled/forged/replayed/etc + * messages. + * + * Argiments: + * client_name (r) GSS-API client name + * server_name (r) GSS-API server name + * rqst (r) RPC service request + * msg (r) RPC message + * data (r) arbitrary data (NULL), not used + * + * Effects: + * + * Logs the invalid request via krb5_klog_syslog(); see functional spec for + * format. + */ +void log_badverf(gss_name_t client_name, gss_name_t server_name, + struct svc_req *rqst, struct rpc_msg *msg, char + *data) +{ + static const char *const proc_names[] = { + "kadm5_create_principal", + "kadm5_delete_principal", + "kadm5_modify_principal", + "kadm5_rename_principal", + "kadm5_get_principal", + "kadm5_chpass_principal", + "kadm5_randkey_principal", + "kadm5_create_policy", + "kadm5_delete_policy", + "kadm5_modify_policy", + "kadm5_get_policy", + "kadm5_get_privs", + }; + OM_uint32 minor; + gss_buffer_desc client, server; + gss_OID gss_type; + char *a; + + (void) gss_display_name(&minor, client_name, &client, &gss_type); + (void) gss_display_name(&minor, server_name, &server, &gss_type); + a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr); + + krb5_klog_syslog(LOG_NOTICE, "WARNING! Forged/garbled request: %s, " + "claimed client = %s, server = %s, addr = %s", + proc_names[msg->rm_call.cb_proc], client.value, + server.value, a); + + (void) gss_release_buffer(&minor, &client); + (void) gss_release_buffer(&minor, &server); +} + +/* + * Function: log_miscerr + * + * Purpose: Callback from GSS-API Sun RPC for miscellaneous errors + * + * Arguments: + * rqst (r) RPC service request + * msg (r) RPC message + * error (r) error message from RPC + * data (r) arbitrary data (NULL), not used + * + * Effects: + * + * Logs the error via krb5_klog_syslog(); see functional spec for + * format. + */ +void log_miscerr(struct svc_req *rqst, struct rpc_msg *msg, + char *error, char *data) +{ + char *a; + + a = inet_ntoa(rqst->rq_xprt->xp_raddr.sin_addr); + krb5_klog_syslog(LOG_NOTICE, "Miscellaneous RPC error: %s, %s", a, error); +} + + + +/* + * Function: log_badauth + * + * Purpose: Callback from GSS-API Sun RPC for authentication + * failures/errors. + * + * Arguments: + * major (r) GSS-API major status + * minor (r) GSS-API minor status + * addr (r) originating address + * data (r) arbitrary data (NULL), not used + * + * Effects: + * + * Logs the GSS-API error via krb5_klog_syslog(); see functional spec for + * format. + */ +void log_badauth(OM_uint32 major, OM_uint32 minor, + struct sockaddr_in *addr, char *data) +{ + char *a; + + /* Authentication attempt failed: <IP address>, <GSS-API error */ + /* strings> */ + + a = inet_ntoa(addr->sin_addr); + + krb5_klog_syslog(LOG_NOTICE, "Authentication attempt failed: %s, GSS-API " + "error strings are:", a); + log_badauth_display_status(" ", major, minor); + krb5_klog_syslog(LOG_NOTICE, " GSS-API error strings complete.\n"); +} + +void log_badauth_display_status(char *msg, OM_uint32 major, OM_uint32 minor) +{ + log_badauth_display_status_1(msg, major, GSS_C_GSS_CODE, 0); + log_badauth_display_status_1(msg, minor, GSS_C_MECH_CODE, 0); +} + +void log_badauth_display_status_1(char *m, OM_uint32 code, int type, + int rec) +{ + OM_uint32 gssstat, minor_stat; + gss_buffer_desc msg; + int msg_ctx; + + msg_ctx = 0; + while (1) { + gssstat = gss_display_status(&minor_stat, code, + type, GSS_C_NULL_OID, + &msg_ctx, &msg); + if (gssstat != GSS_S_COMPLETE) { + if (!rec) { + log_badauth_display_status_1(m,gssstat,GSS_C_GSS_CODE,1); + log_badauth_display_status_1(m, minor_stat, + GSS_C_MECH_CODE, 1); + } else + krb5_klog_syslog(LOG_ERR, "GSS-API authentication error %s: " + "recursive failure!\n", msg); + return; + } + + krb5_klog_syslog(LOG_NOTICE, "%s %s\n", m, (char *)msg.value); + (void) gss_release_buffer(&minor_stat, &msg); + + if (!msg_ctx) + break; + } +} diff --git a/src/kadmin/server/server_glue_v1.c b/src/kadmin/server/server_glue_v1.c new file mode 100644 index 000000000..c0bec26e6 --- /dev/null +++ b/src/kadmin/server/server_glue_v1.c @@ -0,0 +1,31 @@ +#define USE_KADM5_API_VERSION 1 +#include <kadm5/admin.h> + +/* + * In server_stubs.c, kadmind has to be able to call kadm5 functions + * with the arguments appropriate for any api version. Because of the + * prototypes in admin.h, however, the compiler will only allow one + * set of arguments to be passed. This file exports the old api + * definitions with a different name, so they can be called from + * server_stubs.c, and just passes on the call to the real api + * function; it uses the old api version, however, so it can actually + * call the real api functions whereas server_stubs.c cannot. + * + * This is most useful for functions like kadm5_get_principal that + * take a different number of arguments based on API version. For + * kadm5_get_policy, the same thing could be accomplished with + * typecasts instead. + */ + +kadm5_ret_t kadm5_get_principal_v1(void *server_handle, + krb5_principal principal, + kadm5_principal_ent_t_v1 *ent) +{ + return kadm5_get_principal(server_handle, principal, ent); +} + +kadm5_ret_t kadm5_get_policy_v1(void *server_handle, kadm5_policy_t name, + kadm5_policy_ent_t *ent) +{ + return kadm5_get_policy(server_handle, name, ent); +} diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c new file mode 100644 index 000000000..8107160af --- /dev/null +++ b/src/kadmin/server/server_stubs.c @@ -0,0 +1,1045 @@ +/* + * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved + * + * $Header$ + */ + +#if !defined(lint) && !defined(__CODECENTER__) +static char *rcsid = "$Header$"; +#endif + +#include <gssapi/gssapi.h> +#include <gssapi/gssapi_krb5.h> /* for gss_nt_krb5_name */ +#include <krb5.h> +#include <kadm5/admin.h> +#include <kadm5/kadm_rpc.h> +#include <kadm5/server_internal.h> +#include <kadm5/server_acl.h> +#include <syslog.h> +#include "misc.h" + +#define LOG_UNAUTH "Unauthorized request: %s, %s, client=%s, service=%s, addr=%s" +#define LOG_DONE "Request: %s, %s, %s, client=%s, service=%s, addr=%s" + +extern gss_name_t gss_changepw_name; +extern gss_name_t gss_oldchangepw_name; +extern void * global_server_handle; + +#define CHANGEPW_SERVICE(rqstp) \ + (cmp_gss_names(acceptor_name(rqstp->rq_svccred), gss_changepw_name) |\ + (gss_oldchangepw_name && \ + cmp_gss_names(acceptor_name(rqstp->rq_svccred), \ + gss_oldchangepw_name))) + +int cmp_gss_names(gss_name_t n1, gss_name_t n2) +{ + OM_uint32 emaj, emin; + int equal; + + if (GSS_ERROR(emaj = gss_compare_name(&emin, n1, n2, &equal))) + return(0); + + return(equal); +} + +/* + * Function check_handle + * + * Purpose: Check a server handle and return a com_err code if it is + * invalid or 0 if it is valid. + * + * Arguments: + * + * handle The server handle. + */ + +static int check_handle(void *handle) +{ + CHECK_HANDLE(handle); + return 0; +} + +/* + * Function: new_server_handle + * + * Purpose: Constructs a server handle suitable for passing into the + * server library API functions, by folding the client's API version + * and calling principal into the server handle returned by + * kadm5_init. + * + * Arguments: + * api_version (input) The API version specified by the client + * rqstp (input) The RPC request + * handle (output) The returned handle + * <return value> (output) An error code, or 0 if no error occurred + * + * Effects: + * Returns a pointer to allocated storage containing the server + * handle. If an error occurs, then no allocated storage is + * returned, and the return value of the function will be a + * non-zero com_err code. + * + * The allocated storage for the handle should be freed with + * free_server_handle (see below) when it is no longer needed. + */ + +static kadm5_ret_t new_server_handle(krb5_ui_4 api_version, + struct svc_req *rqstp, + kadm5_server_handle_t + *out_handle) +{ + kadm5_server_handle_t handle; + + if (! (handle = (kadm5_server_handle_t) + malloc(sizeof(*handle)))) + return ENOMEM; + + *handle = *(kadm5_server_handle_t)global_server_handle; + handle->api_version = api_version; + + if (! gss_to_krb5_name(handle, rqstp->rq_clntcred, + &handle->current_caller)) { + free(handle); + return KADM5_FAILURE; + } + + *out_handle = handle; + return 0; +} + +/* + * Function: free_server_handle + * + * Purpose: Free handle memory allocated by new_server_handle + * + * Arguments: + * handle (input/output) The handle to free + */ +static void free_server_handle(kadm5_server_handle_t handle) +{ + krb5_free_principal(handle->context, handle->current_caller); + free(handle); +} + +/* + * Function: setup_gss_names + * + * Purpose: Create printable representations of the client and server + * names. + * + * Arguments: + * rqstp (r) the RPC request + * client_name (w) the gss_buffer_t for the client name + * server_name (w) the gss_buffer_t for the server name + * + * Effects: + * + * Unparses the client and server names into client_name and + * server_name, both of which must be freed by the caller. Returns 0 + * on success and -1 on failure. + */ +int setup_gss_names(struct svc_req *rqstp, + gss_buffer_desc *client_name, + gss_buffer_desc *server_name) +{ + OM_uint32 maj_stat, min_stat; + gss_name_t server_gss_name; + + if (gss_name_to_string(rqstp->rq_clntcred, client_name) != 0) + return -1; + maj_stat = gss_inquire_context(&min_stat, rqstp->rq_svccred, NULL, + &server_gss_name, NULL, NULL, NULL, + NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) { + gss_release_buffer(&min_stat, client_name); + return -1; + } + if (gss_name_to_string(server_gss_name, server_name) != 0) { + gss_release_buffer(&min_stat, client_name); + return -1; + } + return 0; +} + +gss_name_t acceptor_name(gss_ctx_id_t context) +{ + OM_uint32 maj_stat, min_stat; + gss_name_t name; + + maj_stat = gss_inquire_context(&min_stat, context, NULL, &name, + NULL, NULL, NULL, NULL, NULL); + if (maj_stat != GSS_S_COMPLETE) + return NULL; + return name; +} + +int cmp_gss_krb5_name(kadm5_server_handle_t handle, + gss_name_t gss_name, krb5_principal princ) +{ + krb5_principal princ2; + int stat; + + if (! gss_to_krb5_name(handle, gss_name, &princ2)) + return 0; + stat = krb5_principal_compare(handle->context, princ, princ2); + krb5_free_principal(handle->context, princ2); + return stat; +} + +int gss_to_krb5_name(kadm5_server_handle_t handle, + gss_name_t gss_name, krb5_principal *princ) +{ + OM_uint32 stat, minor_stat; + gss_buffer_desc gss_str; + gss_OID gss_type; + int success; + + stat = gss_display_name(&minor_stat, gss_name, &gss_str, &gss_type); + if ((stat != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name)) + return 0; + success = (krb5_parse_name(handle->context, gss_str.value, princ) == 0); + gss_release_buffer(&minor_stat, &gss_str); + return success; +} +int +gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str) +{ + OM_uint32 stat, minor_stat; + gss_OID gss_type; + int ret; + + stat = gss_display_name(&minor_stat, gss_name, str, &gss_type); + if ((stat != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name)) + return 1; + return 0; +} + +generic_ret * +create_principal_1(cprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg); + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_ADD, + arg->rec.principal)) { + ret.code = KADM5_AUTH_ADD; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_create_principal((void *)handle, + &arg->rec, arg->mask, + arg->passwd); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_create_principal", + prime_arg,((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +delete_principal_1(dprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_DELETE, + arg->princ)) { + ret.code = KADM5_AUTH_DELETE; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_delete_principal((void *)handle, arg->princ); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_delete_principal", prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free(prime_arg); + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +modify_principal_1(mprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg); + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_MODIFY, + arg->rec.principal)) { + ret.code = KADM5_AUTH_MODIFY; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_modify_principal((void *)handle, &arg->rec, + arg->mask); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_modify_principal", + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +rename_principal_1(rprinc_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg1, + *prime_arg2; + char prime_arg[BUFSIZ]; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->src, &prime_arg1); + krb5_unparse_name(handle->context, arg->dest, &prime_arg2); + sprintf(prime_arg, "%s to %s", prime_arg1, prime_arg2); + + ret.code = KADM5_OK; + if (! CHANGEPW_SERVICE(rqstp)) { + if (!acl_check(handle->context, rqstp->rq_clntcred, + ACL_DELETE, arg->src)) + ret.code = KADM5_AUTH_DELETE; + if (!acl_check(handle->context, rqstp->rq_clntcred, + ACL_ADD, arg->dest)) { + if (ret.code == KADM5_AUTH_DELETE) + ret.code = KADM5_AUTH_INSUFFICIENT; + else + ret.code = KADM5_AUTH_ADD; + } + } else + ret.code = KADM5_AUTH_INSUFFICIENT; + if (ret.code != KADM5_OK) { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_rename_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_rename_principal((void *)handle, arg->src, + arg->dest); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_rename_principal", + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg1); + free(prime_arg2); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +gprinc_ret * +get_principal_1(gprinc_arg *arg, struct svc_req *rqstp) +{ + static gprinc_ret ret; + kadm5_principal_ent_t_v1 e; + char *prime_arg, *funcname; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_gprinc_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + funcname = handle->api_version == KADM5_API_VERSION_1 ? + "kadm5_get_principal (V1)" : "kadm5_get_principal"; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (! cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ) && + (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_INQUIRE, + arg->princ))) { + ret.code = KADM5_AUTH_GET; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname, + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + if (handle->api_version == KADM5_API_VERSION_1) { + ret.code = kadm5_get_principal_v1((void *)handle, + arg->princ, &e); + if(ret.code == KADM5_OK) { + memcpy(&ret.rec, e, sizeof(kadm5_principal_ent_rec_v1)); + free(e); + } + } else { + ret.code = kadm5_get_principal((void *)handle, + arg->princ, &ret.rec, + arg->mask); + } + + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname, + prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +gprincs_ret * +get_princs_1(gprincs_arg *arg, struct svc_req *rqstp) +{ + static gprincs_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_gprincs_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->exp; + if (prime_arg == NULL) + prime_arg = "*"; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_LIST, + NULL)) { + ret.code = KADM5_AUTH_LIST; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_principals", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_get_principals((void *)handle, + arg->exp, &ret.princs, + &ret.count); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_principals", + prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +chpass_principal_1(chpass_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) { + ret.code = chpass_principal_wrapper((void *)handle, arg->princ, + arg->pass); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + acl_check(handle->context, rqstp->rq_clntcred, + ACL_CHANGEPW, arg->princ)) { + ret.code = kadm5_chpass_principal((void *)handle, arg->princ, + arg->pass); + } else { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_chpass_principal", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_CHANGEPW; + } + + if(ret.code != KADM5_AUTH_CHANGEPW) { + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_chpass_principal", + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +chrand_ret * +chrand_principal_1(chrand_arg *arg, struct svc_req *rqstp) +{ + static chrand_ret ret; + krb5_keyblock *k; + int nkeys; + char *prime_arg, *funcname; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_chrand_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + funcname = handle->api_version == KADM5_API_VERSION_1 ? + "kadm5_randkey_principal (V1)" : "kadm5_randkey_principal"; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + krb5_unparse_name(handle->context, arg->princ, &prime_arg); + + if (cmp_gss_krb5_name(handle, rqstp->rq_clntcred, arg->princ)) { + ret.code = randkey_principal_wrapper((void *)handle, + arg->princ, &k, &nkeys); + } else if (!(CHANGEPW_SERVICE(rqstp)) && + acl_check(handle->context, rqstp->rq_clntcred, + ACL_CHANGEPW, arg->princ)) { + ret.code = kadm5_randkey_principal((void *)handle, arg->princ, + &k, &nkeys); + } else { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname, + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_CHANGEPW; + } + + if(ret.code == KADM5_OK) { + if (handle->api_version == KADM5_API_VERSION_1) { + krb5_copy_keyblock_contents(handle->context, k, &ret.key); + krb5_free_keyblock(handle->context, k); + } else { + ret.keys = k; + ret.n_keys = nkeys; + } + } + + if(ret.code != KADM5_AUTH_CHANGEPW) { + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname, + prime_arg, ((ret.code == 0) ? "success" : + error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + free(prime_arg); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +create_policy_1(cpol_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->rec.policy; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_ADD, NULL)) { + ret.code = KADM5_AUTH_ADD; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_create_policy", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + + } else { + ret.code = kadm5_create_policy((void *)handle, &arg->rec, + arg->mask); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_create_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +delete_policy_1(dpol_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->name; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_DELETE, NULL)) { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_delete_policy", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_DELETE; + } else { + ret.code = kadm5_delete_policy((void *)handle, arg->name); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_delete_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret * +modify_policy_1(mpol_arg *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->rec.policy; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_MODIFY, NULL)) { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_modify_policy", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + ret.code = KADM5_AUTH_MODIFY; + } else { + ret.code = kadm5_modify_policy((void *)handle, &arg->rec, + arg->mask); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_modify_policy", + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +gpol_ret * +get_policy_1(gpol_arg *arg, struct svc_req *rqstp) +{ + static gpol_ret ret; + kadm5_ret_t ret2; + char *prime_arg, *funcname; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_policy_ent_t e; + kadm5_principal_ent_rec caller_ent; + krb5_principal caller; + kadm5_server_handle_t handle; + + xdr_free(xdr_gpol_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + funcname = handle->api_version == KADM5_API_VERSION_1 ? + "kadm5_get_policy (V1)" : "kadm5_get_policy"; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->name; + + ret.code = KADM5_AUTH_GET; + if (!CHANGEPW_SERVICE(rqstp) && acl_check(handle->context, + rqstp->rq_clntcred, + ACL_INQUIRE, NULL)) + ret.code = KADM5_OK; + else { + ret.code = kadm5_get_principal(handle->lhandle, + handle->current_caller, + &caller_ent, + KADM5_PRINCIPAL_NORMAL_MASK); + if (ret.code == KADM5_OK) { + if (caller_ent.aux_attributes & KADM5_POLICY && + strcmp(caller_ent.policy, arg->name) == 0) { + ret.code = KADM5_OK; + } else ret.code = KADM5_AUTH_GET; + ret2 = kadm5_free_principal_ent(handle->lhandle, + &caller_ent); + ret.code = ret.code ? ret.code : ret2; + } + } + + if (ret.code == KADM5_OK) { + if (handle->api_version == KADM5_API_VERSION_1) { + ret.code = kadm5_get_policy_v1((void *)handle, arg->name, &e); + if(ret.code == KADM5_OK) { + memcpy(&ret.rec, e, sizeof(kadm5_policy_ent_rec)); + free(e); + } + } else { + ret.code = kadm5_get_policy((void *)handle, arg->name, + &ret.rec); + } + + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, funcname, + ((prime_arg == NULL) ? "(null)" : prime_arg), + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, funcname, + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; + +} + +gpols_ret * +get_pols_1(gpols_arg *arg, struct svc_req *rqstp) +{ + static gpols_ret ret; + char *prime_arg; + gss_buffer_desc client_name, + service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_gpols_ret, &ret); + + if (ret.code = new_server_handle(arg->api_version, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + prime_arg = arg->exp; + if (prime_arg == NULL) + prime_arg = "*"; + + if (CHANGEPW_SERVICE(rqstp) || !acl_check(handle->context, + rqstp->rq_clntcred, + ACL_LIST, NULL)) { + ret.code = KADM5_AUTH_LIST; + krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, "kadm5_get_policies", + prime_arg, client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } else { + ret.code = kadm5_get_policies((void *)handle, + arg->exp, &ret.pols, + &ret.count); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_policies", + prime_arg, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + } + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +getprivs_ret * get_privs_1(krb5_ui_4 *arg, struct svc_req *rqstp) +{ + static getprivs_ret ret; + char *prime_arg; + gss_buffer_desc client_name, service_name; + OM_uint32 minor_stat; + kadm5_server_handle_t handle; + + xdr_free(xdr_getprivs_ret, &ret); + + if (ret.code = new_server_handle(*arg, rqstp, &handle)) + return &ret; + + if (ret.code = check_handle((void *)handle)) { + free_server_handle(handle); + return &ret; + } + + ret.api_version = handle->api_version; + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + + ret.code = kadm5_get_privs((void *)handle, &ret.privs); + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, "kadm5_get_privs", + client_name.value, + ((ret.code == 0) ? "success" : error_message(ret.code)), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + free_server_handle(handle); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + return &ret; +} + +generic_ret *init_1(krb5_ui_4 *arg, struct svc_req *rqstp) +{ + static generic_ret ret; + gss_buffer_desc client_name, + service_name; + kadm5_server_handle_t handle; + OM_uint32 minor_stat; + + xdr_free(xdr_generic_ret, &ret); + + if (ret.code = new_server_handle(*arg, rqstp, &handle)) + return &ret; + if (! (ret.code = check_handle((void *)handle))) { + ret.api_version = handle->api_version; + } + + free_server_handle(handle); + + if (setup_gss_names(rqstp, &client_name, &service_name) < 0) { + ret.code = KADM5_FAILURE; + return &ret; + } + + krb5_klog_syslog(LOG_NOTICE, LOG_DONE, + (ret.api_version == KADM5_API_VERSION_1 ? + "kadm5_init (V1)" : "kadm5_init"), + client_name.value, + (ret.code == 0) ? "success" : error_message(ret.code), + client_name.value, service_name.value, + inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr)); + gss_release_buffer(&minor_stat, &client_name); + gss_release_buffer(&minor_stat, &service_name); + + return(&ret); +} diff --git a/src/kadmin/testing/Makefile.ov b/src/kadmin/testing/Makefile.ov new file mode 100644 index 000000000..206bd8551 --- /dev/null +++ b/src/kadmin/testing/Makefile.ov @@ -0,0 +1,8 @@ +# $Id$ + +TOP = .. +include $(TOP)/config.mk/template + +SUBDIRS=util scripts + +expand SubdirTarget diff --git a/src/kadmin/testing/proto/ChangeLog b/src/kadmin/testing/proto/ChangeLog new file mode 100644 index 000000000..71959a3b0 --- /dev/null +++ b/src/kadmin/testing/proto/ChangeLog @@ -0,0 +1,9 @@ +Mon Jul 15 17:11:35 1996 Marc Horowitz <marc@mit.edu> + + * krb5.conf.proto: specify a default_keytab_name in /krb5 + +Fri Jul 12 14:46:17 1996 Marc Horowitz <marc@mit.edu> + + * kdc.conf.proto: put the stash file in /krb5, so that the root + dir does not need to be writeable. also, the admin system + requires a reference in the conf file to admin_keytab diff --git a/src/kadmin/testing/proto/kdc.conf.proto b/src/kadmin/testing/proto/kdc.conf.proto new file mode 100644 index 000000000..798d6c51b --- /dev/null +++ b/src/kadmin/testing/proto/kdc.conf.proto @@ -0,0 +1,20 @@ +[kdcdefaults] + kdc_ports = 1750 + +[realms] + __REALM__ = { + profile = /krb5/krb5.conf + database_name = /krb5/kdb5 + admin_database_name = /krb5/kadb5 + admin_database_lockfile = /krb5/ovsec_adm.lock + admin_keytab = /krb5/ovsec_adm.srvtab + key_stash_file = /krb5/.k5.__REALM__ + acl_file = /krb5/ovsec_adm.acl + dict_file = /krb5/ovsec_adm.dict + kadmind_port = 1751 + max_life = 10h 0m 0s + max_renewable_life = 7d 0h 0m 0s + master_key_type = des-cbc-crc + supported_enctypes = des-cbc-crc:normal des-cbc-crc:v4 + } + diff --git a/src/kadmin/testing/proto/krb5.conf.proto b/src/kadmin/testing/proto/krb5.conf.proto new file mode 100644 index 000000000..6e0c4687d --- /dev/null +++ b/src/kadmin/testing/proto/krb5.conf.proto @@ -0,0 +1,17 @@ +[libdefaults] + default_realm = __REALM__ + default_keytab_name = FILE:/krb5/v5srvtab + +[realms] + __REALM__ = { + kdc = localhost:1750 + admin_server = localhost:1751 + } + +[domain_realm] + localhost = __REALM__ + +[logging] + admin_server = SYSLOG=ERR:LOCAL6 + kdc = SYSLOG=ERR:LOCAL6 + default = SYSLOG=ERR:LOCAL6 diff --git a/src/kadmin/testing/proto/ovsec_adm.dict b/src/kadmin/testing/proto/ovsec_adm.dict new file mode 100644 index 000000000..b54e3a85e --- /dev/null +++ b/src/kadmin/testing/proto/ovsec_adm.dict @@ -0,0 +1,3 @@ +Abyssinia +Discordianism +foo diff --git a/src/kadmin/testing/scripts/ChangeLog b/src/kadmin/testing/scripts/ChangeLog new file mode 100644 index 000000000..5d7069186 --- /dev/null +++ b/src/kadmin/testing/scripts/ChangeLog @@ -0,0 +1,15 @@ +Fri Jul 12 14:48:20 1996 Marc Horowitz <marc@mit.edu> + + * stop_servers_local (true, false): use the path to find these, + instead of looking in /bin explicitly. + + * start_servers_local (/usr/tmp): /usr/tmp doesn't exist on some + systems. Check for that and /var/tmp, and use the one which + exists. (true, false): use the path to find these, instead of + looking in /bin explicitly. + + * make-host-keytab.pl.in: perl5 requires that @ in strings be + backwhacked. (EDIT_KEYTAB): ovsec_adm_keytab is now kadm5_keytab. + + * init_db: kadmin_create should be kdb5_create + diff --git a/src/kadmin/testing/scripts/Makefile.ov b/src/kadmin/testing/scripts/Makefile.ov new file mode 100644 index 000000000..335b636e7 --- /dev/null +++ b/src/kadmin/testing/scripts/Makefile.ov @@ -0,0 +1,19 @@ +# $Id$ + +TOP = ../.. +include $(TOP)/config.mk/template + +GEN_SCRIPTS = compare_dump.pl fixup-conf-files.pl make-host-keytab.pl \ + simple_dump.pl verify_xrunner_report.pl + +all:: $(GEN_SCRIPTS) + +%.pl: %.pl.in + -rm -f $@.tmp + echo "#!$(PERL)" > $@.tmp + sed 1d $@.in >> $@.tmp + chmod +x $@.tmp + mv $@.tmp $@ + +clean:: + -rm -f $(GEN_SCRIPTS) *.tmp diff --git a/src/kadmin/testing/scripts/compare_dump.pl.in b/src/kadmin/testing/scripts/compare_dump.pl.in new file mode 100644 index 000000000..df93df4a0 --- /dev/null +++ b/src/kadmin/testing/scripts/compare_dump.pl.in @@ -0,0 +1,242 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +# $debug = 1; + +sub usage { die "usage: $0 before after changes\n";} + +sub unique { + local(@list) = @_; + local(%ary); + + print "unique? ",join(" ",@list),"\n" if $debug; + + foreach (@list) { + return(0) if $ary{$_}++; + } + + 1; +} + +$before = shift(@ARGV) || &usage; +$debug++ if $before =~ /^-d/; +$before = shift(@ARGV) || &usage if $debug; +$after = shift(@ARGV) || &usage; +$changes = shift(@ARGV) || &usage; +@ARGV && &usage; + +%policy = + ( + "FIRST",2, + "pw_min_life",2, + "pw_max_life",3, + "pw_min_length",4, + "pw_min_classes",5, + "pw_history_num",6, + "policy_refcnt",7, + "LAST",7, + ); + +%princ = + ( + "FIRST",2, + "kvno",2, + "mod_name",3, + "max_life",4, + "princ_expire_time",5, + "expiration",5, + "pw_expiration",6, + "attributes",7, + "policy",8, + "aux_attributes",9, + "LAST",9, + ); + +%keytab = + ( + "LAST",-1, + ); + +sub re { # @_ = ($cnt, $line) + local($cnt, $line) = @_; + local(@fields) = split(' ',$line); + + @list = ('\S+') x $cnt; + for $f (@fields[3..$#fields]) { + ($f =~ /=/) || die "Bad field: $f in $_"; + if (!defined($this{$`})) { die "Bad parameter $` in $_"; } + + if (($list[$this{$`}] = $') eq '\S+') { + $list[$this{$`}] = '[^\s]+'; + } + } + + join('\s+',@list)."\$"; +} + +open(CHANGES, $changes) || die "Couldn't open $changes: $!\n"; + +while(<CHANGES>) { + next if s/^\s*\#\#\!\s*\#//; + next if !s/^\s*\#\#\!\s*//; + + split; + + if ($_[1] =~ /princ/) { + %this = %princ; + $this = "princ"; + } elsif ($_[1] =~ /policy/) { + %this = %policy; + $this = "policy"; + } elsif ($_[1] =~ /keytab/) { + %this = %keytab; + $this = $_[1]; + } else { + die "Bad line: $_"; + } + + $cnt = $this{"LAST"}+1; + + if ($_[0] =~ /add/) { + $diff{"+$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /delete/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changefrom/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changeto/) { + $ndiff{"-$this\t$_[2]"} = &re($cnt,$_); + } else { + die "Bad line: $_"; + } +} + +close(CHANGES); + +if ($debug) { + for (keys %diff) { + print " %diff: \"$_\" /$diff{$_}/\n"; + } + + for (keys %ndiff) { + print "%ndiff: \"$_\" /$ndiff{$_}/\n"; + } + + print "\n"; +} + +open(DIFF,"gdiff -u0 $before $after|") || die "Couldn't diff: $!\n"; + +$warnings = 0; + +while(<DIFF>) { + next if /^\+{3}/; + next if /^\-{3}/; + next if /^@@/; + + print "LINE: $_" if $debug; + + split; + + $key = "$_[0]\t$_[1]"; + $re = $diff{$key}; + + delete $diff{$key}; + + print "%diff: \"$key\" /$re/\n" if $debug; + + if (!$re) { + warn "Unexpected: \"$key\"\n"; + $warnings++; + next; + } + + if (!/$re/) { + warn "Failed: $key\n"; + $warnings++; + next; + } + + if ($new = $ndiff{$key}) { + delete $ndiff{$key}; + + @new = split(/\\s\+/, $new); + for ($i=1;$i<@new;$i++) { + print "NEW: $new[$i]\n" if $debug; + + if ($new[$i] ne '\S+') { + $_[$i] = $new[$i]; + } + } + $_[0] =~ s/^\-//; + $key =~ s/^\-/\+/; + + $diff{$key} = join("\t",@_); + } +} + +close(DIFF); + +open(BEFORE, $before) || die "Couldn't open $before: $!\n"; + +while(<BEFORE>) { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(BEFORE); + +open(AFTER, $after) || die "Couldn't open $after: $!\n"; + +while(<AFTER>) { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(AFTER); + +for (keys %diff) { + warn "Unseen: \"$_\" /$diff{$_}/\n"; + $warnings++; +} + +for (keys %ndiff) { + warn "Unseen changes: \"$_\" /$ndiff{$_}/\n"; + $warnings++; +} + +for (keys %ktkeys) { + if (!&unique(split(' ',$ktkeys{$_}))) { + warn "Some keys not unique for $_\n"; + $warnings++; + } +} + +for (keys %kttimes) { + if (!&unique(split(' ',$kttimes{$_}))) { + warn "Some timestamps not unique for $_\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/scripts/compare_dump.plin b/src/kadmin/testing/scripts/compare_dump.plin new file mode 100644 index 000000000..df93df4a0 --- /dev/null +++ b/src/kadmin/testing/scripts/compare_dump.plin @@ -0,0 +1,242 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +# $debug = 1; + +sub usage { die "usage: $0 before after changes\n";} + +sub unique { + local(@list) = @_; + local(%ary); + + print "unique? ",join(" ",@list),"\n" if $debug; + + foreach (@list) { + return(0) if $ary{$_}++; + } + + 1; +} + +$before = shift(@ARGV) || &usage; +$debug++ if $before =~ /^-d/; +$before = shift(@ARGV) || &usage if $debug; +$after = shift(@ARGV) || &usage; +$changes = shift(@ARGV) || &usage; +@ARGV && &usage; + +%policy = + ( + "FIRST",2, + "pw_min_life",2, + "pw_max_life",3, + "pw_min_length",4, + "pw_min_classes",5, + "pw_history_num",6, + "policy_refcnt",7, + "LAST",7, + ); + +%princ = + ( + "FIRST",2, + "kvno",2, + "mod_name",3, + "max_life",4, + "princ_expire_time",5, + "expiration",5, + "pw_expiration",6, + "attributes",7, + "policy",8, + "aux_attributes",9, + "LAST",9, + ); + +%keytab = + ( + "LAST",-1, + ); + +sub re { # @_ = ($cnt, $line) + local($cnt, $line) = @_; + local(@fields) = split(' ',$line); + + @list = ('\S+') x $cnt; + for $f (@fields[3..$#fields]) { + ($f =~ /=/) || die "Bad field: $f in $_"; + if (!defined($this{$`})) { die "Bad parameter $` in $_"; } + + if (($list[$this{$`}] = $') eq '\S+') { + $list[$this{$`}] = '[^\s]+'; + } + } + + join('\s+',@list)."\$"; +} + +open(CHANGES, $changes) || die "Couldn't open $changes: $!\n"; + +while(<CHANGES>) { + next if s/^\s*\#\#\!\s*\#//; + next if !s/^\s*\#\#\!\s*//; + + split; + + if ($_[1] =~ /princ/) { + %this = %princ; + $this = "princ"; + } elsif ($_[1] =~ /policy/) { + %this = %policy; + $this = "policy"; + } elsif ($_[1] =~ /keytab/) { + %this = %keytab; + $this = $_[1]; + } else { + die "Bad line: $_"; + } + + $cnt = $this{"LAST"}+1; + + if ($_[0] =~ /add/) { + $diff{"+$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /delete/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changefrom/) { + $diff{"-$this\t$_[2]"} = &re($cnt,$_); + } elsif ($_[0] =~ /changeto/) { + $ndiff{"-$this\t$_[2]"} = &re($cnt,$_); + } else { + die "Bad line: $_"; + } +} + +close(CHANGES); + +if ($debug) { + for (keys %diff) { + print " %diff: \"$_\" /$diff{$_}/\n"; + } + + for (keys %ndiff) { + print "%ndiff: \"$_\" /$ndiff{$_}/\n"; + } + + print "\n"; +} + +open(DIFF,"gdiff -u0 $before $after|") || die "Couldn't diff: $!\n"; + +$warnings = 0; + +while(<DIFF>) { + next if /^\+{3}/; + next if /^\-{3}/; + next if /^@@/; + + print "LINE: $_" if $debug; + + split; + + $key = "$_[0]\t$_[1]"; + $re = $diff{$key}; + + delete $diff{$key}; + + print "%diff: \"$key\" /$re/\n" if $debug; + + if (!$re) { + warn "Unexpected: \"$key\"\n"; + $warnings++; + next; + } + + if (!/$re/) { + warn "Failed: $key\n"; + $warnings++; + next; + } + + if ($new = $ndiff{$key}) { + delete $ndiff{$key}; + + @new = split(/\\s\+/, $new); + for ($i=1;$i<@new;$i++) { + print "NEW: $new[$i]\n" if $debug; + + if ($new[$i] ne '\S+') { + $_[$i] = $new[$i]; + } + } + $_[0] =~ s/^\-//; + $key =~ s/^\-/\+/; + + $diff{$key} = join("\t",@_); + } +} + +close(DIFF); + +open(BEFORE, $before) || die "Couldn't open $before: $!\n"; + +while(<BEFORE>) { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(BEFORE); + +open(AFTER, $after) || die "Couldn't open $after: $!\n"; + +while(<AFTER>) { + next if !/^keytab/; + + split; + + if (!$seen{$key = $_[0]." ".$_[1]}++) { + $key =~ s/-\d+$//; + $ktkeys{$key} .= " ".$_[2]; + $kttimes{$key} .= " ".$_[3]; + } +} + +close(AFTER); + +for (keys %diff) { + warn "Unseen: \"$_\" /$diff{$_}/\n"; + $warnings++; +} + +for (keys %ndiff) { + warn "Unseen changes: \"$_\" /$ndiff{$_}/\n"; + $warnings++; +} + +for (keys %ktkeys) { + if (!&unique(split(' ',$ktkeys{$_}))) { + warn "Some keys not unique for $_\n"; + $warnings++; + } +} + +for (keys %kttimes) { + if (!&unique(split(' ',$kttimes{$_}))) { + warn "Some timestamps not unique for $_\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/scripts/find-make.sh b/src/kadmin/testing/scripts/find-make.sh new file mode 100644 index 000000000..904730dfa --- /dev/null +++ b/src/kadmin/testing/scripts/find-make.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +POSSIBILITIES=' +/usr/local/bin/gmake +/usr/local/bin/make +' + +for file in $POSSIBILITIES; do + if [ -f $file ]; then + echo $file + exit 0 + fi +done + +echo gmake +echo '$0 could not find make!' 1>&2 +exit 1 + diff --git a/src/kadmin/testing/scripts/fixup-conf-files.pl.in b/src/kadmin/testing/scripts/fixup-conf-files.pl.in new file mode 100644 index 000000000..d7834d1c7 --- /dev/null +++ b/src/kadmin/testing/scripts/fixup-conf-files.pl.in @@ -0,0 +1,344 @@ +#!/usr/local/bin/perl +# +# Usage: fixup-conf-files.pl [-server hostname] + +$verbose = $ENV{'VERBOSE_TEST'}; +$archos = $ENV{'ARCH_OS'}; + +$REALM = "SECURE-TEST.OV.COM"; + +sub replace { + local($old, $new, $backup) = @_; + local($dev, $ino, $mode); + + $new = $old.".new" if !$new; + $backup = $old.".bak" if !$backup; + + chmod($mode,$new) if (($dev, $ino, $mode) = stat($old)); + + unlink($backup); + link($old, $backup) || die "couldn't make backup link: $backup: $!\n" + if -e $old; + rename($new, $old) || die "couldn't rename $old to $new: $!\n"; +} + +if (@ARGV == 2 && $ARGV[0] eq "-server") { + $servername = $ARGV[1]; +} elsif (@ARGV != 0) { + print STDERR "Usage: $0 fixup-conf-files.pl [-server hostname]\n"; +} + +sub canonicalize_name { + local($hostname) = @_; + local($d, $addr, $addrtype); + + ($host,$d,$addrtype,$d,$addr) = gethostbyname($hostname); + die "couldn't get hostname $hostname\n" if !$host; + ($host) = gethostbyaddr($addr,$addrtype); + die "couldn't reverse-resolve $hostname\n" if !$host; + return $host; +} + +## Get server's canonical hostname. +if ($servername) { + $serverhost = $servername; +} else { + chop ($serverhost = `hostname`); +} +$serverhost = &canonicalize_name($serverhost); + +## Get local canonical hostname +chop($localhost=`hostname`); +$localhost = &canonicalize_name($localhost); + +## parse krb.conf + +if (open(KCONF, "/etc/athena/krb.conf")) { + chop($hrealm = <KCONF>); + + $confok = 0; + + while(<KCONF>) { + $confs .= $_ if !/^$REALM\s+/o; + $confok = 1 if /^$REALM\s+$serverhost\s+admin\s+server$/oi; + } + + close(KCONF); +} + +## rewrite krb.conf if necessary. + +if (($hrealm ne $REALM) || !$confok) { + print "Rewriting /etc/athena/krb.conf...\n" if $verbose; + + open(KCONF, ">/etc/athena/krb.conf.new") || + die "couldn't open /etc/athena/krb.conf.new: $!\n"; + + print KCONF "$REALM\n"; + print KCONF "$REALM $serverhost admin server\n"; + print KCONF $confs; + + close(KCONF); + + &replace("/etc/athena/krb.conf"); +} + +## parse krb.realms + +if (open(KREALMS, "/etc/athena/krb.realms")) { + $serverrealmok = 0; + $localrealmok = 0; + + while(<KREALMS>) { + $realms .= $_ + if !/^$serverhost\s+$REALM$/oi && !/^$localhost\s+$REALM$/oi; + $serverrealmok = 1 if /^$serverhost\s+$REALM$/oi; + $localrealmok = 1 if /^$localhost\s+$REALM$/oi; + } + + close(KREALMS); +} + +## rewrite krb.realms if necessary. + +if (!$serverrealmok || !$localrealmok) { + print "Rewriting /etc/athean/krb.realms...\n" if $verbose; + + open(KREALMS, ">/etc/athena/krb.realms.new") || + die "couldn't open /etc/athena/krb.realms.new: $!\n"; + + print KREALMS "$serverhost $REALM\n"; + print KREALMS "$localhost $REALM\n" if ($localhost ne $serverhost); + print KREALMS $realms; + + close(KREALMS); + + &replace("/etc/athena/krb.realms"); +} + +# ## read /etc/passwd +# +# open(PASSWD, "/etc/passwd") || die "couldn't open /etc/passwd: $!\n"; +# +# $passok = 0; +# +# if ($archos ne "solaris2.3") { +# %mypass = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } else { +# %mypass = +# ( +# "root", "x", +# "testenc", "x", +# "testuser", "x", +# "pol1", "x", +# "pol2", "x", +# "pol3", "x", +# ); +# %myshadow = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } +# +# $chpw = 0; +# +# while(<PASSWD>) { +# if (/^([^:]+):([^:]+):/ && $mypass{$1}) { +# $users{$1}++; +# if ($2 ne $mypass{$1}) { +# s/^([^:]+):([^:]+):/$1:$mypass{$1}:/; +# $chpw++; +# } +# } +# $pass .= $_; +# } +# +# $passok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $pass .= "$_:$mypass{$_}:32765:101::/tmp:/bin/csh\n"; +# $passok = 0; +# } +# } +# close(PASSWD); +# +# ## rewrite passwd if necessary. +# +# if ($chpw || !$passok) { +# print "Rewriting /etc/passwd...\n" if $verbose; +# +# open(PASSWD, ">/etc/passwd.new") || +# die "couldn't open /etc/passwd.new: $!\n"; +# +# print PASSWD $pass; +# +# close(PASSWD); +# +# &replace("/etc/passwd"); +# } +# +# if ($archos eq "solaris2.3") { +# +# ## read /etc/shadow +# +# open(SHADOW, "/etc/shadow") || die "couldn't open /etc/shadow: $!\n"; +# +# $shadowok = 0; +# $chpw = 0; +# %users = (); +# +# while(<SHADOW>) { +# if (/^([^:]+):([^:]+):/ && $myshadow{$1}) { +# $users{$1}++; +# if ($2 ne $myshadow{$1}) { +# s/^([^:]+):([^:]+):/$1:$myshadow{$1}:/; +# $chpw++; +# } +# } +# $shadow .= $_; +# } +# +# $shadowok = 1; +# +# for (keys %myshadow) { +# if (!$users{$_}) { +# $shadow .= "$_:$myshadow{$_}:6445::::::\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if ($chpw || !$shadowok) { +# print "Rewriting /etc/shadow...\n" if $verbose; +# +# open(SHADOW, ">/etc/shadow.new") || +# die "couldn't open /etc/shadow.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/shadow"); +# } +# } +# +# if ($archos eq "aix3.2") { +# +# ## read /etc/security/passwd +# +# open(SHADOW, "/etc/security/passwd") || die "couldn't open /etc/security/passwd: $!\n"; +# +# $shadowok = 0; +# %users = (); +# +# while(<SHADOW>) { +# if (/^([^:]+):\s*$/ && $mypass{$1}) { +# $user = $1; +# $users{$user}++; +# # arrange for the user to have a password entry and none other +# while (<SHADOW>) { +# last if (!/=/); +# } +# $shadow .= "$user:\n\tpassword = KERBEROS5\n\n"; +# } else { +# $shadow .= $_; +# } +# } +# +# $shadowok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $shadow .= "$_:\n\tpassword = KERBEROS5\n\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if (!$shadowok) { +# print "Rewriting /etc/security/passwd...\n" if $verbose; +# +# open(SHADOW, ">/etc/security/passwd.new") || +# die "couldn't open /etc/security/passwd.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/security/passwd"); +# } +# } +# +# open(SERVICES, "/etc/services") || die "couldn't open /etc/services: $!\n"; +# open(NEW_SERVICES, ">/etc/services.new") || +# die "couldn't open /etc/services.new: $!\n"; +# +# print "Rewriting /etc/services...\n" if $verbose; +# +# @needed_services = ('klogin', 'kshell', 'kerberos', 'kerberos-sec', +# 'kerberos5', 'kerberos4', 'kerberos_master', +# 'passwd_server', 'eklogin', 'krb5_prop', +# 'kerberos_adm', 'kerberos-adm'); +# for (@needed_services) { +# $needed_services{$_}++; +# } +# +# while (<SERVICES>) { +# m/^\s*([^\#\s][^\s]+)/; +# if ($needed_services{$1}) { +# print "+ Commenting out old entry: $1\n" if $verbose; +# print NEW_SERVICES "# $_"; +# } else { +# print NEW_SERVICES $_; +# } +# } +# +# close(SERVICES); +# +# print NEW_SERVICES <<EOF || die "writing to /etc/services.new: $!\n"; +# +# klogin 543/tcp # Kerberos authenticated rlogin +# kshell 544/tcp cmd # and remote shell +# kerberos 88/udp kdc # Kerberos authentication--udp +# kerberos 88/tcp kdc # Kerberos authentication--tcp +# kerberos-sec 750/udp # Kerberos authentication--udp +# kerberos-sec 750/tcp # Kerberos authentication--tcp +# kerberos5 88/udp kdc # Kerberos authentication--udp +# kerberos5 88/tcp kdc # Kerberos authentication--tcp +# kerberos4 750/udp # Kerberos authentication--udp +# kerberos4 750/tcp # Kerberos authentication--tcp +# kerberos_master 751/udp # Kerberos authentication +# kerberos_master 751/tcp # Kerberos authentication +# passwd_server 752/udp # Kerberos passwd server +# eklogin 2105/tcp # Kerberos encrypted rlogin +# krb5_prop 754/tcp # Kerberos slave propagation +# kerberos_adm 752/tcp # Kerberos 5 admin/changepw +# kerberos-adm 752/tcp # Kerberos 5 admin/changepw +# EOF +# +# close(NEW_SERVICES) || die "error closing /etc/services.new: $!\n"; +# +# rename("/etc/services", "/etc/services.old") || +# die "couldn't rename /etc/services to /etc/services.old: $!\n"; +# rename("/etc/services.new", "/etc/services") || +# die "couldn't rename /etc/services.new to /etc/services: $!\n"; +# unlink("/etc/services.old") || die "couldn't unlink /etc/services: $!\n"; +# diff --git a/src/kadmin/testing/scripts/fixup-conf-files.plin b/src/kadmin/testing/scripts/fixup-conf-files.plin new file mode 100644 index 000000000..d7834d1c7 --- /dev/null +++ b/src/kadmin/testing/scripts/fixup-conf-files.plin @@ -0,0 +1,344 @@ +#!/usr/local/bin/perl +# +# Usage: fixup-conf-files.pl [-server hostname] + +$verbose = $ENV{'VERBOSE_TEST'}; +$archos = $ENV{'ARCH_OS'}; + +$REALM = "SECURE-TEST.OV.COM"; + +sub replace { + local($old, $new, $backup) = @_; + local($dev, $ino, $mode); + + $new = $old.".new" if !$new; + $backup = $old.".bak" if !$backup; + + chmod($mode,$new) if (($dev, $ino, $mode) = stat($old)); + + unlink($backup); + link($old, $backup) || die "couldn't make backup link: $backup: $!\n" + if -e $old; + rename($new, $old) || die "couldn't rename $old to $new: $!\n"; +} + +if (@ARGV == 2 && $ARGV[0] eq "-server") { + $servername = $ARGV[1]; +} elsif (@ARGV != 0) { + print STDERR "Usage: $0 fixup-conf-files.pl [-server hostname]\n"; +} + +sub canonicalize_name { + local($hostname) = @_; + local($d, $addr, $addrtype); + + ($host,$d,$addrtype,$d,$addr) = gethostbyname($hostname); + die "couldn't get hostname $hostname\n" if !$host; + ($host) = gethostbyaddr($addr,$addrtype); + die "couldn't reverse-resolve $hostname\n" if !$host; + return $host; +} + +## Get server's canonical hostname. +if ($servername) { + $serverhost = $servername; +} else { + chop ($serverhost = `hostname`); +} +$serverhost = &canonicalize_name($serverhost); + +## Get local canonical hostname +chop($localhost=`hostname`); +$localhost = &canonicalize_name($localhost); + +## parse krb.conf + +if (open(KCONF, "/etc/athena/krb.conf")) { + chop($hrealm = <KCONF>); + + $confok = 0; + + while(<KCONF>) { + $confs .= $_ if !/^$REALM\s+/o; + $confok = 1 if /^$REALM\s+$serverhost\s+admin\s+server$/oi; + } + + close(KCONF); +} + +## rewrite krb.conf if necessary. + +if (($hrealm ne $REALM) || !$confok) { + print "Rewriting /etc/athena/krb.conf...\n" if $verbose; + + open(KCONF, ">/etc/athena/krb.conf.new") || + die "couldn't open /etc/athena/krb.conf.new: $!\n"; + + print KCONF "$REALM\n"; + print KCONF "$REALM $serverhost admin server\n"; + print KCONF $confs; + + close(KCONF); + + &replace("/etc/athena/krb.conf"); +} + +## parse krb.realms + +if (open(KREALMS, "/etc/athena/krb.realms")) { + $serverrealmok = 0; + $localrealmok = 0; + + while(<KREALMS>) { + $realms .= $_ + if !/^$serverhost\s+$REALM$/oi && !/^$localhost\s+$REALM$/oi; + $serverrealmok = 1 if /^$serverhost\s+$REALM$/oi; + $localrealmok = 1 if /^$localhost\s+$REALM$/oi; + } + + close(KREALMS); +} + +## rewrite krb.realms if necessary. + +if (!$serverrealmok || !$localrealmok) { + print "Rewriting /etc/athean/krb.realms...\n" if $verbose; + + open(KREALMS, ">/etc/athena/krb.realms.new") || + die "couldn't open /etc/athena/krb.realms.new: $!\n"; + + print KREALMS "$serverhost $REALM\n"; + print KREALMS "$localhost $REALM\n" if ($localhost ne $serverhost); + print KREALMS $realms; + + close(KREALMS); + + &replace("/etc/athena/krb.realms"); +} + +# ## read /etc/passwd +# +# open(PASSWD, "/etc/passwd") || die "couldn't open /etc/passwd: $!\n"; +# +# $passok = 0; +# +# if ($archos ne "solaris2.3") { +# %mypass = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } else { +# %mypass = +# ( +# "root", "x", +# "testenc", "x", +# "testuser", "x", +# "pol1", "x", +# "pol2", "x", +# "pol3", "x", +# ); +# %myshadow = +# ( +# "root", crypt("testroot","St"), +# "testenc", crypt("notath","HJ"), +# "testuser", "KERBEROS5", +# "pol1", "KERBEROS5", +# "pol2", "KERBEROS5", +# "pol3", "KERBEROS5", +# ); +# } +# +# $chpw = 0; +# +# while(<PASSWD>) { +# if (/^([^:]+):([^:]+):/ && $mypass{$1}) { +# $users{$1}++; +# if ($2 ne $mypass{$1}) { +# s/^([^:]+):([^:]+):/$1:$mypass{$1}:/; +# $chpw++; +# } +# } +# $pass .= $_; +# } +# +# $passok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $pass .= "$_:$mypass{$_}:32765:101::/tmp:/bin/csh\n"; +# $passok = 0; +# } +# } +# close(PASSWD); +# +# ## rewrite passwd if necessary. +# +# if ($chpw || !$passok) { +# print "Rewriting /etc/passwd...\n" if $verbose; +# +# open(PASSWD, ">/etc/passwd.new") || +# die "couldn't open /etc/passwd.new: $!\n"; +# +# print PASSWD $pass; +# +# close(PASSWD); +# +# &replace("/etc/passwd"); +# } +# +# if ($archos eq "solaris2.3") { +# +# ## read /etc/shadow +# +# open(SHADOW, "/etc/shadow") || die "couldn't open /etc/shadow: $!\n"; +# +# $shadowok = 0; +# $chpw = 0; +# %users = (); +# +# while(<SHADOW>) { +# if (/^([^:]+):([^:]+):/ && $myshadow{$1}) { +# $users{$1}++; +# if ($2 ne $myshadow{$1}) { +# s/^([^:]+):([^:]+):/$1:$myshadow{$1}:/; +# $chpw++; +# } +# } +# $shadow .= $_; +# } +# +# $shadowok = 1; +# +# for (keys %myshadow) { +# if (!$users{$_}) { +# $shadow .= "$_:$myshadow{$_}:6445::::::\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if ($chpw || !$shadowok) { +# print "Rewriting /etc/shadow...\n" if $verbose; +# +# open(SHADOW, ">/etc/shadow.new") || +# die "couldn't open /etc/shadow.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/shadow"); +# } +# } +# +# if ($archos eq "aix3.2") { +# +# ## read /etc/security/passwd +# +# open(SHADOW, "/etc/security/passwd") || die "couldn't open /etc/security/passwd: $!\n"; +# +# $shadowok = 0; +# %users = (); +# +# while(<SHADOW>) { +# if (/^([^:]+):\s*$/ && $mypass{$1}) { +# $user = $1; +# $users{$user}++; +# # arrange for the user to have a password entry and none other +# while (<SHADOW>) { +# last if (!/=/); +# } +# $shadow .= "$user:\n\tpassword = KERBEROS5\n\n"; +# } else { +# $shadow .= $_; +# } +# } +# +# $shadowok = 1; +# +# for (keys %mypass) { +# if (!$users{$_}) { +# $shadow .= "$_:\n\tpassword = KERBEROS5\n\n"; +# $shadowok = 0; +# } +# } +# close(SHADOW); +# +# ## rewrite shadow if necessary. +# +# if (!$shadowok) { +# print "Rewriting /etc/security/passwd...\n" if $verbose; +# +# open(SHADOW, ">/etc/security/passwd.new") || +# die "couldn't open /etc/security/passwd.new: $!\n"; +# +# print SHADOW $shadow; +# +# close(SHADOW); +# +# &replace("/etc/security/passwd"); +# } +# } +# +# open(SERVICES, "/etc/services") || die "couldn't open /etc/services: $!\n"; +# open(NEW_SERVICES, ">/etc/services.new") || +# die "couldn't open /etc/services.new: $!\n"; +# +# print "Rewriting /etc/services...\n" if $verbose; +# +# @needed_services = ('klogin', 'kshell', 'kerberos', 'kerberos-sec', +# 'kerberos5', 'kerberos4', 'kerberos_master', +# 'passwd_server', 'eklogin', 'krb5_prop', +# 'kerberos_adm', 'kerberos-adm'); +# for (@needed_services) { +# $needed_services{$_}++; +# } +# +# while (<SERVICES>) { +# m/^\s*([^\#\s][^\s]+)/; +# if ($needed_services{$1}) { +# print "+ Commenting out old entry: $1\n" if $verbose; +# print NEW_SERVICES "# $_"; +# } else { +# print NEW_SERVICES $_; +# } +# } +# +# close(SERVICES); +# +# print NEW_SERVICES <<EOF || die "writing to /etc/services.new: $!\n"; +# +# klogin 543/tcp # Kerberos authenticated rlogin +# kshell 544/tcp cmd # and remote shell +# kerberos 88/udp kdc # Kerberos authentication--udp +# kerberos 88/tcp kdc # Kerberos authentication--tcp +# kerberos-sec 750/udp # Kerberos authentication--udp +# kerberos-sec 750/tcp # Kerberos authentication--tcp +# kerberos5 88/udp kdc # Kerberos authentication--udp +# kerberos5 88/tcp kdc # Kerberos authentication--tcp +# kerberos4 750/udp # Kerberos authentication--udp +# kerberos4 750/tcp # Kerberos authentication--tcp +# kerberos_master 751/udp # Kerberos authentication +# kerberos_master 751/tcp # Kerberos authentication +# passwd_server 752/udp # Kerberos passwd server +# eklogin 2105/tcp # Kerberos encrypted rlogin +# krb5_prop 754/tcp # Kerberos slave propagation +# kerberos_adm 752/tcp # Kerberos 5 admin/changepw +# kerberos-adm 752/tcp # Kerberos 5 admin/changepw +# EOF +# +# close(NEW_SERVICES) || die "error closing /etc/services.new: $!\n"; +# +# rename("/etc/services", "/etc/services.old") || +# die "couldn't rename /etc/services to /etc/services.old: $!\n"; +# rename("/etc/services.new", "/etc/services") || +# die "couldn't rename /etc/services.new to /etc/services: $!\n"; +# unlink("/etc/services.old") || die "couldn't unlink /etc/services: $!\n"; +# diff --git a/src/kadmin/testing/scripts/init_db b/src/kadmin/testing/scripts/init_db new file mode 100644 index 000000000..c53ff96c1 --- /dev/null +++ b/src/kadmin/testing/scripts/init_db @@ -0,0 +1,181 @@ +#!/bin/sh + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +if $VERBOSE; then + REDIRECT= +else + REDIRECT='>/dev/null' +fi + +# Requires that /krb5, /etc/krb.conf, and .k5.$REALM be world-writeable. + +if [ "$TOP" = "" ]; then + echo "init_db: Environment variable \$TOP must point to top of build tree" 1>&2 + exit 1 +fi + +IROOT=$TOP/.. +ADMIN=$TOP/create:$IROOT/admin/stash:$IROOT/admin/destroy +BIN=$IROOT/bin +ETC=$IROOT/etc +SBIN=$TOP/keytab:$TOP/server +DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM + +DUMMY=${TESTDIR=$TOP/testing}; export TESTDIR +DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL +DUMMY=${TCLUTIL=$TESTDIR/tcl/util.t}; export TCLUTIL +DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl} + +PATH=$ADMIN:$BIN:$ETC:$SBIN:$PATH; export PATH + +rm -rf /krb5/* +if [ -d /krb5 ]; then + true +else + mkdir /krb5 +fi + +# touch /krb5/syslog +# for pid in `$PS_ALL | awk '/syslogd/ && !/awk/ {print $2}'` ; do +# case "$pid" in +# xxx) ;; +# *) +# if $VERBOSE; then $PS_PID$pid | grep -v COMMAND; fi +# kill -1 $pid +# ;; +# esac +# done + +sed -e "s/__REALM__/$REALM/" < $TESTDIR/proto/krb5.conf.proto > /krb5/krb5.conf +sed -e "s/__REALM__/$REALM/" < $TESTDIR/proto/kdc.conf.proto > /krb5/kdc.conf + +kdb5_create -P mrroot -s -r $REALM $REDIRECT + +cp $TESTDIR/proto/ovsec_adm.dict /krb5/ovsec_adm.dict + +eval $SRVTCL <<'EOF' $REDIRECT +source $env(TCLUTIL) +set r $env(REALM) + +set cmds { + {ovsec_kadm_init $env(SRVTCL) mrroot null $r $OVSEC_KADM_STRUCT_VERSION \ + $OVSEC_KADM_API_VERSION_1 server_handle} + + {ovsec_kadm_create_policy $server_handle "test-pol 0 10000 8 2 3 0" \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LENGTH OVSEC_KADM_PW_MIN_CLASSES OVSEC_KADM_PW_MAX_LIFE OVSEC_KADM_PW_HISTORY_NUM}} + {ovsec_kadm_create_policy $server_handle "once-a-min 30 0 0 0 0 0" \ + {OVSEC_KADM_POLICY OVSEC_KADM_PW_MIN_LIFE}} + {ovsec_kadm_create_policy $server_handle "dict-only 0 0 0 0 0 0" \ + {OVSEC_KADM_POLICY}} + {ovsec_kadm_create_policy $server_handle [simple_policy test-pol-nopw] \ + {OVSEC_KADM_POLICY}} + + {ovsec_kadm_create_principal $server_handle \ + [simple_principal testuser@$r] {OVSEC_KADM_PRINCIPAL} notathena} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal test1@$r] {OVSEC_KADM_PRINCIPAL} test1} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal test2@$r] {OVSEC_KADM_PRINCIPAL} test2} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal test3@$r] {OVSEC_KADM_PRINCIPAL} test3} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/modify@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/delete@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/none@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/rename@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/mod-add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/mod-delete@$r] {OVSEC_KADM_PRINCIPAL} \ + admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get-add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get-delete@$r] {OVSEC_KADM_PRINCIPAL} \ + admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/get-mod@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/no-add@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [simple_principal admin/no-delete@$r] {OVSEC_KADM_PRINCIPAL} admin} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol pol1@$r test-pol] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} pol111111} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol pol2@$r once-a-min] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} pol222222} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol pol3@$r dict-only] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} pol333333} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol admin/get-pol@$r test-pol-nopw] \ + {OVSEC_KADM_PRINCIPAL OVSEC_KADM_POLICY} StupidAdmin} + {ovsec_kadm_create_principal $server_handle \ + [princ_w_pol admin/pol@$r test-pol-nopw] {OVSEC_KADM_PRINCIPAL \ + OVSEC_KADM_POLICY} StupidAdmin} + + {ovsec_kadm_create_principal $server_handle \ + [simple_principal changepw/kerberos] \ + {OVSEC_KADM_PRINCIPAL} {XXX THIS IS WRONG}} + + {ovsec_kadm_destroy $server_handle} +} + +foreach cmd $cmds { + if {[catch $cmd output]} { + puts stderr "Error! Command: $cmd\nError: $output" + exit 1 + } else { + puts stdout $output + } +} +EOF + +if [ $? -ne 0 ]; then + echo "Error in $SRVTCL!" 1>&2 + exit 1 +fi + +cat > /krb5/ovsec_adm.acl <<EOF +admin@$REALM admcil +admin/get@$REALM il +admin/modify@$REALM mc +admin/delete@$REALM d +admin/add@$REALM a +admin/get-pol@$REALM il +admin/rename@$REALM adil +admin/mod-add@$REALM amc +admin/mod-delete@$REALM mcd +admin/get-add@$REALM ail +admin/get-delete@$REALM ild +admin/get-mod@$REALM ilmc +admin/no-add@$REALM mcdil +admin/no-delete@$REALM amcil +changepw/kerberos@$REALM cil + +EOF + +eval $LOCAL_MAKE_KEYTAB -princ kadmin/admin -princ kadmin/changepw -princ ovsec_adm/admin -princ ovsec_adm/changepw /krb5/ovsec_adm.srvtab $REDIRECT + +# Create /krb5/setup.csh to make it easy to run other programs against +# the test db +cat > /krb5/setup.csh <<EOF +setenv KRB5_CONFIG $KRB5_CONFIG +setenv KRB5_KDC_PROFILE $KRB5_KDC_PROFILE +setenv KRB5_KTNAME $KRB5_KTNAME +EOF + diff --git a/src/kadmin/testing/scripts/make-host-keytab.pl.in b/src/kadmin/testing/scripts/make-host-keytab.pl.in new file mode 100644 index 000000000..14d7b10b5 --- /dev/null +++ b/src/kadmin/testing/scripts/make-host-keytab.pl.in @@ -0,0 +1,138 @@ +#!/usr/local/bin/perl + +$server = undef; +@princs = (); +$top = undef; + +($whoami = $0) =~ s,.*/,,; +$usage = "Usage: $whoami [ -server server ] [ -princ principal ] + [ -top dirname ] [ -verbose ] filename + Server defaults to the local host. + Default principals are host/hostname\@SECURE-TEST.OV.COM and + test/hostname\@SECURE-TEST.OV.COM. + If any principals are specified, the default principals are + not added to the srvtab. + The string \"xCANONHOSTx\" in a principal specification will be + replaced by the canonical host name of the local host."; + +@ORIG_ARGV = @ARGV; + +while (($_ = $ARGV[0]) && /^-/) { + shift; + if (/^-server$/) { + ($server = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-princ$/) { + ($princ = shift) || die "Missing argument to $_ option.\n$usage\n"; + push(@princs, $princ); + } + elsif (/^-top$/) { + ($top = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-verbose$/) { + $verbose++; + } + elsif (/^--$/) { + last; + } + else { + die "Unknown option $_.\n$usage\n"; + } +} + +@princs = ("host/xCANONHOSTx\@SECURE-TEST.OV.COM", + "test/xCANONHOSTx\@SECURE-TEST.OV.COM") + if (! @princs); + +$ktfile = shift(@ARGV) || die "need a keytab file\n"; + +$verbose++ if ($ENV{'VERBOSE_TEST'}); + +print "In $0 @ORIG_ARGV...\n" if ($verbose); + +chop ($canonhost = `hostname`); + +($canonhost,$aliases,$addrtype,$length,@addrs) = gethostbyname($canonhost); +die "couldn't get canonical hostname\n" if !($canonhost && @addrs); +($canonhost) = gethostbyaddr($addrs[0],$addrtype); +die "couldn't get canonical hostname\n" if (!$canonhost); + +for (@princs) { + s/xCANONHOSTx/$canonhost/g; +} + +die "Neither \$TOP nor \$TESTDIR is set, and -top not specified.\n" + if (! ($top || $ENV{'TOP'} || $ENV{'TESTDIR'})); + +$top = $ENV{'TOP'} if (! $top); +$TESTDIR = ($ENV{'TESTDIR'} || "$top/testing"); +$MAKE_KEYTAB = ($ENV{'MAKE_KEYTAB'} || "$TESTDIR/scripts/$whoami"); +$SRVTCL = ($ENV{'SRVTCL'} || "$TESTDIR/util/ovsec_kadm_srv_tcl"); +$TCLUTIL = ($ENV{'TCLUTIL'} || "$TESTDIR/tcl/util.t"); +# This'll be wrong sometimes +$RSH_CMD = ($ENV{'RSH_CMD'} || '/usr/ucb/rsh'); +$EDIT_KEYTAB = ($ENV{'EDIT_KEYTAB'} || "$top/keytab/kadm5_keytab.local"); + +if ($server) { +# XXX Using /usr/ucb/rsh for now. + +# Strip command line options because we're adding our own. + + $MAKE_KEYTAB =~ s/ .*//; + + if ($ENV{'TOP'} && ($top ne $ENV{'TOP'})) { +# Replace the old TOP with the new one where necessary + for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { + eval "\$$_ =~ s/^\$ENV{'TOP'}/\$top/;"; + } + +# Make the paths as short as possible so our command line isn't too long. +# for ('SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$TESTDIR/\\\\\\\$TESTDIR/;"; +# } +# for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$top/\\\\\\\$TOP/;"; +# } + } + + $cmd = "cd $top; \\`testing/scripts/find-make.sh\\` execute TOP=$top "; + $cmd .= "VERBOSE_TEST=$verbose " if ($verbose); + $cmd .= "TESTDIR=$TESTDIR "; + $cmd .= "SRVTCL=$SRVTCL "; + $cmd .= "TCLUTIL=$TCLUTIL "; + + $cmd .= "CMD='$MAKE_KEYTAB "; + for (@princs) { + $cmd .= "-princ $_ "; + } + $cmd .= " /tmp/make-keytab.$canonhost.$$'";#'; + + $cmd = "$RSH_CMD $server -l root -n \"$cmd\""; + + $cmd2 = "$RSH_CMD $server -l root -n \"cat /tmp/make-keytab.$canonhost.$$\" > $ktfile"; + + $cmd3 = "$RSH_CMD $server -l root -n \"rm /tmp/make-keytab.$canonhost.$$\""; + + for ($cmd, $cmd2, $cmd3) { + print "$_\n" if ($verbose); + + system($_) && die "Couldn't run $_: $!.\n"; + } +} +else { + $redirect = "> /dev/null" if (! $verbose); + + $cmd = "$EDIT_KEYTAB -k $ktfile"; + $cmd .= " -q" if (! $verbose); + $cmd .= " -a -c"; + for (@princs) { + if (system "$cmd $_") { + sleep(1); + die "Error in system($cmd $_)\n"; + } + } +} + +if (! -f $ktfile) { + die "$ktfile not created.\n"; +} diff --git a/src/kadmin/testing/scripts/make-host-keytab.plin b/src/kadmin/testing/scripts/make-host-keytab.plin new file mode 100644 index 000000000..14d7b10b5 --- /dev/null +++ b/src/kadmin/testing/scripts/make-host-keytab.plin @@ -0,0 +1,138 @@ +#!/usr/local/bin/perl + +$server = undef; +@princs = (); +$top = undef; + +($whoami = $0) =~ s,.*/,,; +$usage = "Usage: $whoami [ -server server ] [ -princ principal ] + [ -top dirname ] [ -verbose ] filename + Server defaults to the local host. + Default principals are host/hostname\@SECURE-TEST.OV.COM and + test/hostname\@SECURE-TEST.OV.COM. + If any principals are specified, the default principals are + not added to the srvtab. + The string \"xCANONHOSTx\" in a principal specification will be + replaced by the canonical host name of the local host."; + +@ORIG_ARGV = @ARGV; + +while (($_ = $ARGV[0]) && /^-/) { + shift; + if (/^-server$/) { + ($server = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-princ$/) { + ($princ = shift) || die "Missing argument to $_ option.\n$usage\n"; + push(@princs, $princ); + } + elsif (/^-top$/) { + ($top = shift) || die "Missing argument to $_ option.\n$usage\n"; + } + elsif (/^-verbose$/) { + $verbose++; + } + elsif (/^--$/) { + last; + } + else { + die "Unknown option $_.\n$usage\n"; + } +} + +@princs = ("host/xCANONHOSTx\@SECURE-TEST.OV.COM", + "test/xCANONHOSTx\@SECURE-TEST.OV.COM") + if (! @princs); + +$ktfile = shift(@ARGV) || die "need a keytab file\n"; + +$verbose++ if ($ENV{'VERBOSE_TEST'}); + +print "In $0 @ORIG_ARGV...\n" if ($verbose); + +chop ($canonhost = `hostname`); + +($canonhost,$aliases,$addrtype,$length,@addrs) = gethostbyname($canonhost); +die "couldn't get canonical hostname\n" if !($canonhost && @addrs); +($canonhost) = gethostbyaddr($addrs[0],$addrtype); +die "couldn't get canonical hostname\n" if (!$canonhost); + +for (@princs) { + s/xCANONHOSTx/$canonhost/g; +} + +die "Neither \$TOP nor \$TESTDIR is set, and -top not specified.\n" + if (! ($top || $ENV{'TOP'} || $ENV{'TESTDIR'})); + +$top = $ENV{'TOP'} if (! $top); +$TESTDIR = ($ENV{'TESTDIR'} || "$top/testing"); +$MAKE_KEYTAB = ($ENV{'MAKE_KEYTAB'} || "$TESTDIR/scripts/$whoami"); +$SRVTCL = ($ENV{'SRVTCL'} || "$TESTDIR/util/ovsec_kadm_srv_tcl"); +$TCLUTIL = ($ENV{'TCLUTIL'} || "$TESTDIR/tcl/util.t"); +# This'll be wrong sometimes +$RSH_CMD = ($ENV{'RSH_CMD'} || '/usr/ucb/rsh'); +$EDIT_KEYTAB = ($ENV{'EDIT_KEYTAB'} || "$top/keytab/kadm5_keytab.local"); + +if ($server) { +# XXX Using /usr/ucb/rsh for now. + +# Strip command line options because we're adding our own. + + $MAKE_KEYTAB =~ s/ .*//; + + if ($ENV{'TOP'} && ($top ne $ENV{'TOP'})) { +# Replace the old TOP with the new one where necessary + for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { + eval "\$$_ =~ s/^\$ENV{'TOP'}/\$top/;"; + } + +# Make the paths as short as possible so our command line isn't too long. +# for ('SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$TESTDIR/\\\\\\\$TESTDIR/;"; +# } +# for ('TESTDIR', 'SRVTCL', 'TCLUTIL', 'MAKE_KEYTAB') { +# eval "\$$_ =~ s/^\$top/\\\\\\\$TOP/;"; +# } + } + + $cmd = "cd $top; \\`testing/scripts/find-make.sh\\` execute TOP=$top "; + $cmd .= "VERBOSE_TEST=$verbose " if ($verbose); + $cmd .= "TESTDIR=$TESTDIR "; + $cmd .= "SRVTCL=$SRVTCL "; + $cmd .= "TCLUTIL=$TCLUTIL "; + + $cmd .= "CMD='$MAKE_KEYTAB "; + for (@princs) { + $cmd .= "-princ $_ "; + } + $cmd .= " /tmp/make-keytab.$canonhost.$$'";#'; + + $cmd = "$RSH_CMD $server -l root -n \"$cmd\""; + + $cmd2 = "$RSH_CMD $server -l root -n \"cat /tmp/make-keytab.$canonhost.$$\" > $ktfile"; + + $cmd3 = "$RSH_CMD $server -l root -n \"rm /tmp/make-keytab.$canonhost.$$\""; + + for ($cmd, $cmd2, $cmd3) { + print "$_\n" if ($verbose); + + system($_) && die "Couldn't run $_: $!.\n"; + } +} +else { + $redirect = "> /dev/null" if (! $verbose); + + $cmd = "$EDIT_KEYTAB -k $ktfile"; + $cmd .= " -q" if (! $verbose); + $cmd .= " -a -c"; + for (@princs) { + if (system "$cmd $_") { + sleep(1); + die "Error in system($cmd $_)\n"; + } + } +} + +if (! -f $ktfile) { + die "$ktfile not created.\n"; +} diff --git a/src/kadmin/testing/scripts/qualname b/src/kadmin/testing/scripts/qualname new file mode 100644 index 000000000..3d047c550 --- /dev/null +++ b/src/kadmin/testing/scripts/qualname @@ -0,0 +1,18 @@ +#!/afs/athena/contrib/perl/p + +if ($#ARGV == -1) { + chop($hostname = `hostname`); +} else { + $hostname = $ARGV[0]; +} + +if (! (($type,$addr) = (gethostbyname($hostname))[2,4])) { + print STDERR "No such host: $hostname\n"; + exit(1); +} +if (! ($qualname = (gethostbyaddr($addr,$type))[0])) { + print STDERR "No address information for host $hostname\n"; + exit(1); +} +print "$qualname\n"; + diff --git a/src/kadmin/testing/scripts/save_files.sh b/src/kadmin/testing/scripts/save_files.sh new file mode 100644 index 000000000..b9fc37319 --- /dev/null +++ b/src/kadmin/testing/scripts/save_files.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +while [ $# -gt 0 ] ; do + case $1 in + -start_servers) + start_servers=$1 + ;; + esac + shift +done + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +# files="/etc/inetd.conf /etc/syslog.conf /etc/krb.conf \ +# /etc/krb.realms /etc/passwd /etc/services /etc/v5srvtab \ +# /etc/rc.local /etc/shadow /etc/security/passwd /.k5login \ +# /.secure/etc/passwd /etc/athena/inetd.conf" + +files="/etc/krb.conf /etc/krb.realms /etc/athena/krb.conf \ + /etc/athena/krb.realms /etc/v5srvtab" + +name=`basename $0` + +make_dne_name() +{ + dne_name="/tmp/"`echo $1 | sed -e 's,/,#,g'`".did-not-exist" +} + +for f in $files ; do + if [ "$name" = "save_files.sh" ]; then + if [ -f $f.pre-secure ]; then + if $VERBOSE; then + echo "Warning! $f.pre-secure exists, not saving." + fi + elif [ ! -f $f ]; then + make_dne_name $f + cp /dev/null $dne_name + else + cp $f $f.pre-secure + fi + else + make_dne_name $f + if [ -f $dne_name ]; then + rm -f $f $dne_name + elif [ ! -f $f.pre-secure ]; then + if [ "x$start_servers" = "x" ]; then + echo "Warning! $f.pre-secure does not exist!" 1>&2 + fi + else + if cp $f.pre-secure $f; then + rm $f.pre-secure + else + echo "Warning! cp failed!" 1>&2 + fi + fi + fi +done + +# DUMMY=${INETD:=/etc/inetd} +# if $VERBOSE; then +# echo "Killing and restarting $INETD" +# fi +# kill `$PS_ALL | awk '/inetd/ && !/awk/ {print $2}'` +# $INETD diff --git a/src/kadmin/testing/scripts/simple_dump.pl.in b/src/kadmin/testing/scripts/simple_dump.pl.in new file mode 100644 index 000000000..ea94ab2d1 --- /dev/null +++ b/src/kadmin/testing/scripts/simple_dump.pl.in @@ -0,0 +1,88 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +## ovsec_adm_export format +## [0]"policy" [1]name [2]pw_min_life [3]pw_max_life [4]pw_min_length [5]pw_min_classes [6]pw_history_num [7]policy_refcnt +## [0]"princ" [1]name [2]policy [3]aux_attributes [4]old_key_len [5]admin_history_kvno [6..]old_keys +$oaevers = "1.0"; + +open(SORT, "|sort") || die "Couldn't open pipe to sort for output: $!\n"; + +open(OAE, "$ENV{'TOP'}/install/admin/ovsec_adm_export|") || + die "Couldn't get oae: $!\n"; + +$header = <OAE>; + +die "Not ovsec_adm_export output\n" + if ($header !~ /^OpenV\*Secure V(\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected oae version $oaevers, got $stdinvers instead.\n" + if $stdinvers ne $oaevers; + +while(<OAE>) { + if (/^End of Database/) { + last; + } elsif (/^policy/) { + print SORT; + } elsif (/^princ/) { + split(/\t/); + + $_[2] = "\"\"" if !$_[2]; + + $_[3] = hex("0x".$_[3]); + + $princ{$_[1]} = sprintf("%s\t0x%04x",@_[2,3]); + } +} + +## kdb_edit ddb format +## [0]strlen(principal) [1]strlen(mod_name) [2]key.length [3]alt_key.length [4]salt_length [5]alt_salt_length [6]principal [7]key.key_type [8]key.contents [9]kvno [10]max_life [11]max_renewable_life [12]mkvno [13]expiration [14]pw_expiration [15]last_pwd_change [16]last_success [17]last_failed [18]fail_auth_count [19]mod_name [20]mod_date [21]attributes [22]salt_type [23]salt [24]alt_key.contents [25]alt_salt [26..33]expansion*8; +$ddbvers = "2.0"; + +open(DDB, "$ENV{'TOP'}/install/admin/kdb5_edit -r SECURE-TEST.OV.COM -R ddb|") || + die "Couldn't get ddb: $!\n"; + +$header = <DDB>; + +die "Not a kdb5_edit ddb\n" + if ($header !~ /^kdb5_edit load_dump version (\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected ddb version $ddbvers, got $stdinvers instead.\n" + if $stdinvers ne $ddbvers; + +## [6]principal [9]kvno [19]mod_name [10]max_life [13]expiration [14]pw_expiration [21]attributes // [2]policy [3]aux_attributes + +while(<DDB>) { + split; + + print SORT join("\t","princ",(@_)[6,9,19,10,13,14], + sprintf("0x%04x",$_[21]), + $princ{$_[6]}),"\n"; +} + +close(DDB); + +for $keytab (@ARGV) { + open(KLIST, "$ENV{'TOP'}/install/bin/klist -k -t -K FILE:$keytab|") || + die "Couldn't list $keytab: $!\n"; + + $dummy = <KLIST>; + $dummy = <KLIST>; + $dummy = <KLIST>; + + while(<KLIST>) { + s/^\s+//; + split; + printf(SORT "keytab:FILE:%s\t%s-%s\t%s\t%s,%s\n",$keytab, + @_[3,0,4,1,2]); + } +} + +close(SORT); diff --git a/src/kadmin/testing/scripts/simple_dump.plin b/src/kadmin/testing/scripts/simple_dump.plin new file mode 100644 index 000000000..ea94ab2d1 --- /dev/null +++ b/src/kadmin/testing/scripts/simple_dump.plin @@ -0,0 +1,88 @@ +#!/usr/local/bin/perl + +# +# $Id$ +# + +## ovsec_adm_export format +## [0]"policy" [1]name [2]pw_min_life [3]pw_max_life [4]pw_min_length [5]pw_min_classes [6]pw_history_num [7]policy_refcnt +## [0]"princ" [1]name [2]policy [3]aux_attributes [4]old_key_len [5]admin_history_kvno [6..]old_keys +$oaevers = "1.0"; + +open(SORT, "|sort") || die "Couldn't open pipe to sort for output: $!\n"; + +open(OAE, "$ENV{'TOP'}/install/admin/ovsec_adm_export|") || + die "Couldn't get oae: $!\n"; + +$header = <OAE>; + +die "Not ovsec_adm_export output\n" + if ($header !~ /^OpenV\*Secure V(\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected oae version $oaevers, got $stdinvers instead.\n" + if $stdinvers ne $oaevers; + +while(<OAE>) { + if (/^End of Database/) { + last; + } elsif (/^policy/) { + print SORT; + } elsif (/^princ/) { + split(/\t/); + + $_[2] = "\"\"" if !$_[2]; + + $_[3] = hex("0x".$_[3]); + + $princ{$_[1]} = sprintf("%s\t0x%04x",@_[2,3]); + } +} + +## kdb_edit ddb format +## [0]strlen(principal) [1]strlen(mod_name) [2]key.length [3]alt_key.length [4]salt_length [5]alt_salt_length [6]principal [7]key.key_type [8]key.contents [9]kvno [10]max_life [11]max_renewable_life [12]mkvno [13]expiration [14]pw_expiration [15]last_pwd_change [16]last_success [17]last_failed [18]fail_auth_count [19]mod_name [20]mod_date [21]attributes [22]salt_type [23]salt [24]alt_key.contents [25]alt_salt [26..33]expansion*8; +$ddbvers = "2.0"; + +open(DDB, "$ENV{'TOP'}/install/admin/kdb5_edit -r SECURE-TEST.OV.COM -R ddb|") || + die "Couldn't get ddb: $!\n"; + +$header = <DDB>; + +die "Not a kdb5_edit ddb\n" + if ($header !~ /^kdb5_edit load_dump version (\d+\.\d+)/); + +$stdinvers = $1; + +die "Expected ddb version $ddbvers, got $stdinvers instead.\n" + if $stdinvers ne $ddbvers; + +## [6]principal [9]kvno [19]mod_name [10]max_life [13]expiration [14]pw_expiration [21]attributes // [2]policy [3]aux_attributes + +while(<DDB>) { + split; + + print SORT join("\t","princ",(@_)[6,9,19,10,13,14], + sprintf("0x%04x",$_[21]), + $princ{$_[6]}),"\n"; +} + +close(DDB); + +for $keytab (@ARGV) { + open(KLIST, "$ENV{'TOP'}/install/bin/klist -k -t -K FILE:$keytab|") || + die "Couldn't list $keytab: $!\n"; + + $dummy = <KLIST>; + $dummy = <KLIST>; + $dummy = <KLIST>; + + while(<KLIST>) { + s/^\s+//; + split; + printf(SORT "keytab:FILE:%s\t%s-%s\t%s\t%s,%s\n",$keytab, + @_[3,0,4,1,2]); + } +} + +close(SORT); diff --git a/src/kadmin/testing/scripts/start_servers b/src/kadmin/testing/scripts/start_servers new file mode 100644 index 000000000..2e395faf8 --- /dev/null +++ b/src/kadmin/testing/scripts/start_servers @@ -0,0 +1,70 @@ +#!/bin/sh +# +# Usage: start_servers [hostname [path]] +# +# This script turns a host into a OpenV*Secure primary server for the +# realm SECURE-TEST.OV.COM. If no arguments are specified, +# the local host is affected. Otherwise, the host hostname is +# affected; the path argument is the top of the Secure install tree on +# that host, and if it is not specified the current canonical value of +# TOP is used. + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${SAVE_FILES=$TESTDIR/scripts/save_files.sh} +DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl} +DUMMY=${START_SERVERS_LOCAL=$TESTDIR/scripts/start_servers_local} +# This'll be wrong sometimes +DUMMY=${RSH_CMD=/usr/ucb/rsh} + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +local=1 + +if [ $# -gt 0 ]; then + if [ $# != 1 -a $# != 2 ]; then + echo "Usage: $0 [hostname [path]]" 1>&2 + exit 1 + fi + + local=0 + hostname=$1 + if [ $# = 1 ]; then + rempath=`sh -c "cd $TOP && pwd"` + else + rempath=$2 + fi +fi + +if [ $local = 0 ]; then + $SAVE_FILES || exit 1 + $FIX_CONF_FILES -server $hostname || exit 1 + +# Using /usr/ucb/rsh and getting rid of "-k $REALM" until we get +# around to fixing the fact that Kerberos rsh doesn't strip out "-k +# REALM" when falling back. + + START_SERVERS_LOCAL=`echo $START_SERVERS_LOCAL|sed "s%$TOP%$rempath%"` + CMD="$RSH_CMD $hostname -l root -n \ + \"cd $rempath; \\\`testing/scripts/find-make.sh\\\` execute VERBOSE_TEST=$VERBOSE_TEST \ + TOP=$rempath \ + CMD='$START_SERVERS_LOCAL $rempath'\"" + + if $VERBOSE; then + echo "+++" + echo "+++ Begin execution of start_servers_local on $hostname" + echo "+++" + echo $CMD + fi + eval $CMD + if $VERBOSE; then + echo "+++" + echo "+++ End execution of start_servers_local on $hostname" + echo "+++" + fi +else + $START_SERVERS_LOCAL +fi + diff --git a/src/kadmin/testing/scripts/start_servers_local b/src/kadmin/testing/scripts/start_servers_local new file mode 100644 index 000000000..a9c8e7957 --- /dev/null +++ b/src/kadmin/testing/scripts/start_servers_local @@ -0,0 +1,196 @@ +#!/bin/sh + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${SAVE_FILES=$TESTDIR/scripts/save_files.sh} +DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl} +DUMMY=${INITDB=$TESTDIR/scripts/init_db} +DUMMY=${SRVTCL=$TESTDIR/util/ovsec_kadm_srv_tcl}; export SRVTCL +DUMMY=${LOCAL_MAKE_KEYTAB=$TESTDIR/scripts/make-host-keytab.pl} +DUMMY=${STOP_SERVERS_LOCAL=$TESTDIR/scripts/stop_servers_local} + +if [ -d /usr/tmp ]; then + usrtmp=/usr/tmp +else + usrtmp=/var/tmp +fi + +$STOP_SERVERS_LOCAL -start_servers + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +if $VERBOSE; then + REDIRECT= +else + REDIRECT='>/dev/null' +fi + +v4files=false +while :; do + case $1 in + -keysalt) + shift + if [ $# -gt 0 ]; then + keysalts="$keysalts $1" + else + break + fi + ;; + -kdcport) + shift + if [ $# -gt 0 ]; then + kdcport=$1 + else + break + fi + ;; + -v4files) + if [ "`whoami`" != "root" ]; then + echo "You must be root to use -v4files!" 1>&2 + exit 1 + fi + v4files=true + ;; + *) + break + ;; + esac + shift +done + +if [ $# -gt 1 ]; then + echo "Usage: $0 [-kdcport port] [-keysalts tuple] ... [top]" 1>&2 + exit 1 +elif [ $# = 1 ]; then + TOP=$1 + export TOP +fi + +# fixup the system config files +if $v4files; then + $SAVE_FILES || exit 1 + $FIX_CONF_FILES || exit 1 +fi + +# create a fresh db + +$INITDB "$keysalts" || exit 1 + +# Post-process the config files based on our arguments +if [ "$keysalts" != "" ]; then + sedcmd="s/\([ ]*supported_enctypes =\).*/\1 $keysalts/" + sed -e "$sedcmd" < /krb5/kdc.conf > /krb5/kdc.conf.new + mv /krb5/kdc.conf.new /krb5/kdc.conf +fi +if [ "$kdcport" != "" ] ; then + sedcmd="s/\(kdc_ports = .*\)[ ]*/\1, $kdcport/" + sed -e "$sedcmd" < /krb5/kdc.conf > /krb5/kdc.conf.new + mv /krb5/kdc.conf.new /krb5/kdc.conf +fi + +# allow admin to krlogin as root (for cleanup) +DUMMY=${REALM=SECURE-TEST.OV.COM}; export REALM +hostname=`hostname` +QUALNAME=`$TOP/testing/scripts/qualname $hostname`; export QUALNAME + +eval $SRVTCL <<'EOF' $REDIRECT +source $env(TOP)/testing/tcl/util.t +set r $env(REALM) +set q $env(QUALNAME) +puts stdout [ovsec_kadm_init $env(SRVTCL) mrroot null $r \ + $OVSEC_KADM_STRUCT_VERSION $OVSEC_KADM_API_VERSION_1 server_handle] +puts stdout [ovsec_kadm_create_principal $server_handle \ + [simple_principal host/$q@$r] {OVSEC_KADM_PRINCIPAL} notathena] +puts stdout [ovsec_kadm_destroy $server_handle] +EOF + +# rm -f /etc/v5srvtab +# eval $LOCAL_MAKE_KEYTAB -princ host/xCANONHOSTx /etc/v5srvtab $REDIRECT + +# run the servers (from the build tree) + +adm_start_file=/tmp/adm_server_start.$$ +kdc_start_file=/tmp/kdc_server_start.$$ + +rm -f $kdc_start_file + +(trap "" 2; cd $TOP/../kdc; ./krb5kdc; touch $kdc_start_file) \ + < /dev/null > $usrtmp/kdc-log 2>&1 & + +s=10 +max_s=60 +sofar_s=0 +timewait_s=300 + +while true; do + rm -f $adm_start_file + + (sleep 5; cd $TOP/server; ./kadmind $ovadm_args; \ + touch $adm_start_file) < /dev/null > $usrtmp/kadm-log 2>&1 & + + # wait until they start + + while [ $sofar_s -le $max_s ]; do + if $VERBOSE; then + echo "Sleeping for $s seconds to allow servers" \ + "to start..." + fi + + sofar_s=`expr $sofar_s + $s` + + sleep $s + + if [ -f $adm_start_file -a -f $kdc_start_file ]; then + break + fi + + done + + if [ $sofar_s -le $max_s ]; then + if $VERBOSE; then + LOG_USER='log_user 1' + else + LOG_USER='log_user 0' + fi + if expect <<EOF + $LOG_USER + spawn telnet localhost 1751 + expect { + "Connection refused" { + close + wait + exit 1 + } + "Connected" { + send "close\n" + close + wait + exit 0 + } + default { + catch {close} + wait + exit 1 + } + } +EOF + then + rm -f $kdc_start_file $adm_start_file + break + else + if $VERBOSE; then + echo "Could not connect to Admin server;" \ + "attempting restart ($sofar_s" \ + "seconds so far)." + fi + max_s=$timewait_s + continue + fi + else + echo "Admin server or KDC failed to start after $sofar_s" \ + "seconds." 1>&2 + exit 1 + fi +done diff --git a/src/kadmin/testing/scripts/stop_servers b/src/kadmin/testing/scripts/stop_servers new file mode 100644 index 000000000..fc5372dd4 --- /dev/null +++ b/src/kadmin/testing/scripts/stop_servers @@ -0,0 +1,84 @@ +#!/bin/sh +# +# Usage: stop_servers [hostname [path]] +# +# This script turns a host into a OpenV*Secure primary server for the +# realm SECURE-TEST.OV.COM. If no arguments are specified, +# the local host is affected. Otherwise, the host hostname is +# affected; the path argument is the top of the Secure install tree on +# that host, and if it is not specified the current canonical value of +# TOP is used. + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${FIX_CONF_FILES=$TESTDIR/scripts/fixup-conf-files.pl} +DUMMY=${STOP_SERVERS_LOCAL=$TESTDIR/scripts/stop_servers_local} +# This'll be wrong sometimes +DUMMY=${RSH_CMD=/usr/ucb/rsh} +DUMMY=${RESTORE_FILES=$TESTDIR/scripts/restore_files.sh} + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +local=1 + +if [ $# -gt 0 ]; then + if [ $# != 1 -a $# != 2 ]; then + echo "Usage: $0 [hostname [path]]" 1>&2 + exit 1 + fi + + local=0 + hostname=$1 + if [ $# = 1 ]; then + rempath=`sh -c "cd $TOP && pwd"` + else + rempath=$2 + fi +fi + +if [ $local = 0 ]; then + if $VERBOSE; then + echo "+++ Stopping servers on remote host $hostname..." + fi + +# $FIX_CONF_FILES -server $hostname +# +# KRB5CCNAME=FILE:/tmp/krb5cc_stop_servers; export KRB5CCNAME +# +# expect <<EOF +#spawn kinit admin +#expect { +# -re "Password for admin@SECURE-TEST.OV.COM" { +# send "admin\n" +# } +#} +#expect { eof { } } +#EOF + +# Using /usr/ucb/rsh and getting rid of "-k REALM" until we get around +# to fixing the fact that Kerberos rsh doesn't strip out "-k REALM" +# when falling back. + + STOP_SERVERS_LOCAL=`echo $STOP_SERVERS_LOCAL | sed "s%$TOP%$rempath%"` + CMD="$RSH_CMD $hostname -l root -n\ + \"cd $rempath; \\\`testing/scripts/find-make.sh\\\` execute VERBOSE_TEST=$VERBOSE_TEST \ + TOP=$rempath \ + CMD='$STOP_SERVERS_LOCAL $rempath'\"" + if $VERBOSE; then + echo "+++" + echo "+++ Begin execution of stop_servers_local on $hostname" + echo "+++" + echo $CMD + fi + eval $CMD + if $VERBOSE; then + echo "+++" + echo "+++ End execution of stop_servers_local on $hostname" + echo "+++" + fi + $RESTORE_FILES +else + $STOP_SERVERS_LOCAL +fi diff --git a/src/kadmin/testing/scripts/stop_servers_local b/src/kadmin/testing/scripts/stop_servers_local new file mode 100644 index 000000000..c0a97ef27 --- /dev/null +++ b/src/kadmin/testing/scripts/stop_servers_local @@ -0,0 +1,49 @@ +#!/bin/sh + +DUMMY=${TESTDIR=$TOP/testing} +DUMMY=${RESTORE_FILES=$TESTDIR/scripts/restore_files.sh} + +# If it's set, set it to true +VERBOSE=${VERBOSE_TEST:+true} +# Otherwise, set it to false +DUMMY=${VERBOSE:=false} + +v4files=false +while [ $# -gt 0 ] ; do + case $1 in + -start_servers) + start_servers=$1 + ;; + -v4files) + v4files=true + ;; + *) + TOP=$1 + export TOP + ;; + esac + shift +done + +# kill any running servers. + +if $VERBOSE; then echo "Killing servers:"; fi + +for pid in xxx \ + `$PS_ALL | grep krb5kdc | grep -v grep | awk '{print $2}'` \ + `$PS_ALL | grep kadmind | grep -v grep | awk '{print $2}'` \ + ; do + case "$pid" in + xxx) + ;; + *) + if $VERBOSE; then $PS_PID$pid | grep -v COMMAND; fi + kill $pid + ;; + esac +done + +# restore saved system config files +if $v4files; then + $RESTORE_FILES $start_servers +fi diff --git a/src/kadmin/testing/scripts/verify_xrunner_report.pl.in b/src/kadmin/testing/scripts/verify_xrunner_report.pl.in new file mode 100644 index 000000000..9d83c3ea2 --- /dev/null +++ b/src/kadmin/testing/scripts/verify_xrunner_report.pl.in @@ -0,0 +1,38 @@ +#!/usr/local/bin/perl + +sub usage { die "usage: $0 reportfile\n"; } + +$report = shift(@ARGV) || die &usage; + +open(REPORT, $report) || die "Couldn't open $report: $!\n"; + +while(<REPORT>) { + if (/Process termination:/ && !/\bOK\b/) { + warn "Process termination not OK\n"; + $warnings++; + } elsif (/Number of detected mismatches:\s*(\d+)/ && ($1 ne "0")) { + warn "Number of detected mismatches = $1\n"; + $warnings++; + } elsif (/Detailed Results Description/) { + break; + } +} + +while(<REPORT>) { + next if !/^\d+\s+/; + + split; + + if (($_[2] ne "run") && + ($_[2] ne "OK") && + ($_[2] ne "end-of-test")) { + warn "Unexpected result code $_[2] from test $_[4]\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/scripts/verify_xrunner_report.plin b/src/kadmin/testing/scripts/verify_xrunner_report.plin new file mode 100644 index 000000000..9d83c3ea2 --- /dev/null +++ b/src/kadmin/testing/scripts/verify_xrunner_report.plin @@ -0,0 +1,38 @@ +#!/usr/local/bin/perl + +sub usage { die "usage: $0 reportfile\n"; } + +$report = shift(@ARGV) || die &usage; + +open(REPORT, $report) || die "Couldn't open $report: $!\n"; + +while(<REPORT>) { + if (/Process termination:/ && !/\bOK\b/) { + warn "Process termination not OK\n"; + $warnings++; + } elsif (/Number of detected mismatches:\s*(\d+)/ && ($1 ne "0")) { + warn "Number of detected mismatches = $1\n"; + $warnings++; + } elsif (/Detailed Results Description/) { + break; + } +} + +while(<REPORT>) { + next if !/^\d+\s+/; + + split; + + if (($_[2] ne "run") && + ($_[2] ne "OK") && + ($_[2] ne "end-of-test")) { + warn "Unexpected result code $_[2] from test $_[4]\n"; + $warnings++; + } +} + +if ($warnings) { + warn "$warnings warnings.\n"; +} + +exit($warnings); diff --git a/src/kadmin/testing/tcl/util.t b/src/kadmin/testing/tcl/util.t new file mode 100644 index 000000000..f4688aeee --- /dev/null +++ b/src/kadmin/testing/tcl/util.t @@ -0,0 +1,61 @@ +proc simple_principal {name} { + return "{$name} 0 0 0 0 {$name} 0 0 0 0 null 0" +} + +proc princ_w_pol {name policy} { + return "{$name} 0 0 0 0 {$name} 0 0 0 0 {$policy} 0" +} + +proc simple_policy {name} { + return "{$name} 0 0 0 0 0 0" +} + +proc config_params {masks values} { + if {[llength $masks] != [llength $values]} { + error "config_params: length of mask and values differ" + } + + set params [list $masks 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 {}] + for {set i 0} {$i < [llength $masks]} {incr i} { + set mask [lindex $masks $i] + set value [lindex $values $i] + switch -glob -- $mask { + "KADM5_CONFIG_REALM" {set params [lreplace $params 1 1 $value]} + "KADM5_CONFIG_PROFILE" {set params [lreplace $params 2 2 $value]} + "KADM5_CONFIG_KADMIND_PORT" { + set params [lreplace $params 3 3 $value]} + "KADM5_CONFIG_ADMIN_SERVER" { + set params [lreplace $params 4 4 $value]} + "KADM5_CONFIG_DBNAME" {set params [lreplace $params 5 5 $value]} + "KADM5_CONFIG_ADBNAME" {set params [lreplace $params 6 6 $value]} + "KADM5_CONFIG_ADB_LOCKFILE" { + set params [lreplace $params 7 7 $value]} + "KADM5_CONFIG_ADMIN_KEYTAB" { + set params [lreplace $params 8 8 $value]} + "KADM5_CONFIG_ACL_FILE" {set params [lreplace $params 9 9 $value]} + "KADM5_CONFIG_DICT_FILE" { + set params [lreplace $params 10 10 $value]} + "KADM5_CONFIG_MKEY_FROM_KBD" { + set params [lreplace $params 11 11 $value]} + "KADM5_CONFIG_STASH_FILE" { + set params [lreplace $params 12 12 $value]} + "KADM5_CONFIG_MKEY_NAME" { + set params [lreplace $params 13 13 $value]} + "KADM5_CONFIG_ENCTYPE" {set params [lreplace $params 14 14 $value]} + "KADM5_CONFIG_MAX_LIFE" { + set params [lreplace $params 15 15 $value]} + "KADM5_CONFIG_MAX_RLIFE" { + set params [lreplace $params 16 16 $value]} + "KADM5_CONFIG_EXPIRATION" { + set params [lreplace $params 17 17 $value]} + "KADM5_CONFIG_FLAGS" {set params [lreplace $params 18 18 $value]} + "KADM5_CONFIG_ENCTYPES" { + set params [lreplace $params 19 20 [llength $value] $value]} + "*" {error "config_params: unknown mask $mask"} + } + } + return $params +} + + + diff --git a/src/kadmin/testing/util/ChangeLog b/src/kadmin/testing/util/ChangeLog new file mode 100644 index 000000000..36df4616d --- /dev/null +++ b/src/kadmin/testing/util/ChangeLog @@ -0,0 +1,3 @@ +Fri Jul 12 15:04:52 1996 Marc Horowitz <marc@mit.edu> + + * tcl_ovsec_kadm.c: renamed <ovsec_admin/foo.h> to <kadm5/foo.h> diff --git a/src/kadmin/testing/util/Makefile.ov b/src/kadmin/testing/util/Makefile.ov new file mode 100644 index 000000000..6d0159092 --- /dev/null +++ b/src/kadmin/testing/util/Makefile.ov @@ -0,0 +1,34 @@ +# $Id$ + +TOP = ../.. +include $(TOP)/config.mk/template + +CFLAGS += -I$(TCLINC) + +SRCS = tcl_ovsec_kadm.c tcl_kadm5.c test.c +OBJS = tcl_ovsec_kadm.o tcl_kadm5.o test.o + +PROG = ovsec_kadm_srv_tcl +LIBS = $(LIBADMSRV) $(LIBRPCLIB) $(LIBDYN) $(LIBGSSAPI_KRB5) \ + $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBISODE) $(LIBTCL) $(LIBM) $(LIBDB) $(LIBCOM_ERR) \ + $(NDBMLIB) $(NETLIB) + +expand Program +expand Depend + +PROG = ovsec_kadm_clnt_tcl +LIBS = $(LIBADMCLNT) $(LIBRPCLIB) $(LIBDYN) $(LIBGSSAPI_KRB5) \ + $(LIBKDB5) $(LIBKRB5) $(LIBCRYPTO) \ + $(LIBISODE) $(LIBTCL) $(LIBM) $(LIBDB) $(LIBCOM_ERR) \ + $(NDBMLIB) $(BSDLIB) $(NETLIB) + +expand Program + +PROG = bsddb_dump +SRCS = bsddb_dump.c +OBJS = bsddb_dump.o +LIBS = $(LIBDB) + +expand Program +expand Depend diff --git a/src/kadmin/testing/util/bsddb_dump.c b/src/kadmin/testing/util/bsddb_dump.c new file mode 100644 index 000000000..ba69b8461 --- /dev/null +++ b/src/kadmin/testing/util/bsddb_dump.c @@ -0,0 +1,64 @@ +/* + * $Id$ + */ + +#include <sys/file.h> +#include <fcntl.h> +#include <db.h> +#include <stdio.h> + +main(int argc, char *argv[]) +{ + char *file; + DB *db; + DBT dbkey, dbdata; + int code, i; + + HASHINFO info; + + info.hash = NULL; + info.bsize = 256; + info.ffactor = 8; + info.nelem = 25000; + info.lorder = 0; + + if (argc != 2) { + fprintf(stderr, "usage: argv[0] dbfile\n"); + exit(2); + } + + file = argv[1]; + + if((db = dbopen(file, O_RDWR, 0666, DB_HASH, &info)) == NULL) { + perror("Opening db file"); + exit(1); + } + + if ((code = (*db->seq)(db, &dbkey, &dbdata, R_FIRST)) == -1) { + perror("starting db iteration"); + exit(1); + } + + while (code == 0) { + for (i=0; i<dbkey.size; i++) + printf("%02x", (int) ((unsigned char *) dbkey.data)[i]); + printf("\t"); + for (i=0; i<dbdata.size; i++) + printf("%02x", (int) ((unsigned char *) dbdata.data)[i]); + printf("\n"); + + code = (*db->seq)(db, &dbkey, &dbdata, R_NEXT); + } + + if (code == -1) { + perror("during db iteration"); + exit(1); + } + + if ((*db->close)(db) == -1) { + perror("closing db"); + exit(1); + } + + exit(0); +} diff --git a/src/kadmin/testing/util/tcl_kadm5.c b/src/kadmin/testing/util/tcl_kadm5.c new file mode 100644 index 000000000..b102cdfc2 --- /dev/null +++ b/src/kadmin/testing/util/tcl_kadm5.c @@ -0,0 +1,2312 @@ +#include <stdio.h> +#include <string.h> +#include <tcl.h> +#define USE_KADM5_API_VERSION 2 +#include <kadm5/admin.h> +#include <com_err.h> +#include <malloc.h> +#include <k5-int.h> +#include <errno.h> +#include <stdlib.h> + +struct flagval { + char *name; + krb5_flags val; +}; + +/* XXX This should probably be in the hash table like server_handle */ +static krb5_context context; + +static struct flagval krb5_flags_array[] = { + {"KRB5_KDB_DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED}, + {"KRB5_KDB_DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE}, + {"KRB5_KDB_DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED}, + {"KRB5_KDB_DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE}, + {"KRB5_KDB_DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE}, + {"KRB5_KDB_DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY}, + {"KRB5_KDB_DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX}, + {"KRB5_KDB_REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH}, + {"KRB5_KDB_REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH}, + {"KRB5_KDB_REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE}, + {"KRB5_KDB_DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR}, + {"KRB5_KDB_PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE} +}; + +static struct flagval aux_attributes[] = { + {"KADM5_POLICY", KADM5_POLICY} +}; + +static struct flagval principal_mask_flags[] = { + {"KADM5_PRINCIPAL", KADM5_PRINCIPAL}, + {"KADM5_PRINC_EXPIRE_TIME", KADM5_PRINC_EXPIRE_TIME}, + {"KADM5_PW_EXPIRATION", KADM5_PW_EXPIRATION}, + {"KADM5_LAST_PWD_CHANGE", KADM5_LAST_PWD_CHANGE}, + {"KADM5_ATTRIBUTES", KADM5_ATTRIBUTES}, + {"KADM5_MAX_LIFE", KADM5_MAX_LIFE}, + {"KADM5_MOD_TIME", KADM5_MOD_TIME}, + {"KADM5_MOD_NAME", KADM5_MOD_NAME}, + {"KADM5_KVNO", KADM5_KVNO}, + {"KADM5_MKVNO", KADM5_MKVNO}, + {"KADM5_AUX_ATTRIBUTES", KADM5_AUX_ATTRIBUTES}, + {"KADM5_POLICY", KADM5_POLICY}, + {"KADM5_POLICY_CLR", KADM5_POLICY_CLR}, + {"KADM5_MAX_RLIFE", KADM5_MAX_RLIFE}, + {"KADM5_LAST_SUCCESS", KADM5_LAST_SUCCESS}, + {"KADM5_LAST_FAILED", KADM5_LAST_FAILED}, + {"KADM5_FAIL_AUTH_COUNT", KADM5_FAIL_AUTH_COUNT}, + {"KADM5_KEY_DATA", KADM5_KEY_DATA}, + {"KADM5_TL_DATA", KADM5_TL_DATA}, + {"KADM5_PRINCIPAL_NORMAL_MASK", KADM5_PRINCIPAL_NORMAL_MASK} +}; + +static struct flagval policy_mask_flags[] = { + {"KADM5_POLICY", KADM5_POLICY}, + {"KADM5_PW_MAX_LIFE", KADM5_PW_MAX_LIFE}, + {"KADM5_PW_MIN_LIFE", KADM5_PW_MIN_LIFE}, + {"KADM5_PW_MIN_LENGTH", KADM5_PW_MIN_LENGTH}, + {"KADM5_PW_MIN_CLASSES", KADM5_PW_MIN_CLASSES}, + {"KADM5_PW_HISTORY_NUM", KADM5_PW_HISTORY_NUM}, + {"KADM5_REF_COUNT", KADM5_REF_COUNT} +}; + +static struct flagval config_mask_flags[] = { + {"KADM5_CONFIG_REALM", KADM5_CONFIG_REALM}, + {"KADM5_CONFIG_DBNAME", KADM5_CONFIG_DBNAME}, + {"KADM5_CONFIG_MKEY_NAME", KADM5_CONFIG_MKEY_NAME}, + {"KADM5_CONFIG_MAX_LIFE", KADM5_CONFIG_MAX_LIFE}, + {"KADM5_CONFIG_MAX_RLIFE", KADM5_CONFIG_MAX_RLIFE}, + {"KADM5_CONFIG_EXPIRATION", KADM5_CONFIG_EXPIRATION}, + {"KADM5_CONFIG_FLAGS", KADM5_CONFIG_FLAGS}, + {"KADM5_CONFIG_ADMIN_KEYTAB", KADM5_CONFIG_ADMIN_KEYTAB}, + {"KADM5_CONFIG_STASH_FILE", KADM5_CONFIG_STASH_FILE}, + {"KADM5_CONFIG_ENCTYPE", KADM5_CONFIG_ENCTYPE}, + {"KADM5_CONFIG_ADBNAME", KADM5_CONFIG_ADBNAME}, + {"KADM5_CONFIG_ADB_LOCKFILE", KADM5_CONFIG_ADB_LOCKFILE}, + {"KADM5_CONFIG_PROFILE", KADM5_CONFIG_PROFILE}, + {"KADM5_CONFIG_ACL_FILE", KADM5_CONFIG_ACL_FILE}, + {"KADM5_CONFIG_KADMIND_PORT", KADM5_CONFIG_KADMIND_PORT}, + {"KADM5_CONFIG_ENCTYPES", KADM5_CONFIG_ENCTYPES}, + {"KADM5_CONFIG_ADMIN_SERVER", KADM5_CONFIG_ADMIN_SERVER}, + {"KADM5_CONFIG_DICT_FILE", KADM5_CONFIG_DICT_FILE}, + {"KADM5_CONFIG_MKEY_FROM_KBD", KADM5_CONFIG_MKEY_FROM_KBD}, +}; + +static struct flagval priv_flags[] = { + {"KADM5_PRIV_GET", KADM5_PRIV_GET}, + {"KADM5_PRIV_ADD", KADM5_PRIV_ADD}, + {"KADM5_PRIV_MODIFY", KADM5_PRIV_MODIFY}, + {"KADM5_PRIV_DELETE", KADM5_PRIV_DELETE} +}; + + +static char *arg_error = "wrong # args"; + +static Tcl_HashTable *struct_table = 0; + +static int put_server_handle(Tcl_Interp *interp, void *handle, char **name) +{ + int i = 1, newPtr = 0; + static char buf[20]; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + /* + * Handles from ovsec_kadm_init() and kadm5_init() should not + * be mixed during unit tests, but the API would happily + * accept them. Making the hash entry names different in + * tcl_kadm.c and tcl_ovsec_kadm.c ensures that GET_HANDLE + * will fail if presented a handle from the other API. + */ + sprintf(buf, "kadm5_handle%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, handle); + + *name = buf; + + return TCL_OK; +} + +static int get_server_handle(Tcl_Interp *interp, char *name, void **handle) +{ + Tcl_HashEntry *entry; + + if(!strcasecmp(name, "null")) + *handle = 0; + else { + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + if (strncmp(name, "ovsec_kadm_handle", 17) == 0) + Tcl_AppendResult(interp, "ovsec_kadm handle " + "specified for kadm5 api: ", name, 0); + else + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + *handle = (void *) Tcl_GetHashValue(entry); + } + return TCL_OK; +} + +static int remove_server_handle(Tcl_Interp *interp, char *name) +{ + Tcl_HashEntry *entry; + + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + + Tcl_SetHashValue(entry, NULL); + return TCL_OK; +} + +#define GET_HANDLE(num_args, ignored) \ + void *server_handle; \ + char *whoami = argv[0]; \ + argv++, argc--; \ + if (argc != num_args + 1) { \ + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \ + return TCL_ERROR; \ + } \ + { \ + int tcl_ret; \ + if ((tcl_ret = get_server_handle(interp, argv[0], &server_handle)) \ + != TCL_OK) { \ + return tcl_ret; \ + } \ + } \ + argv++, argc--; + +static Tcl_HashTable *create_flag_table(struct flagval *flags, int size) +{ + Tcl_HashTable *table; + Tcl_HashEntry *entry; + int i; + + if (! (table = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_InitHashTable(table, TCL_STRING_KEYS); + + for (i = 0; i < size; i++) { + int newPtr; + + if (! (entry = Tcl_CreateHashEntry(table, flags[i].name, &newPtr))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_SetHashValue(entry, &flags[i].val); + } + + return table; +} + + +static Tcl_DString *unparse_str(char *in_str) +{ + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + if (! in_str) { + Tcl_DStringAppend(str, "null", -1); + } + else { + Tcl_DStringAppend(str, in_str, -1); + } + + return str; +} + + + +static int parse_str(Tcl_Interp *interp, char *in_str, char **out_str) +{ + if (! in_str) { + *out_str = 0; + } + else if (! strcasecmp(in_str, "null")) { + *out_str = 0; + } + else { + *out_str = in_str; + } + return TCL_OK; +} + + +static void set_ok(Tcl_Interp *interp, char *string) +{ + Tcl_SetResult(interp, "OK", TCL_STATIC); + Tcl_AppendElement(interp, "KADM5_OK"); + Tcl_AppendElement(interp, string); +} + + + +static Tcl_DString *unparse_err(kadm5_ret_t code) +{ + char *code_string, *error_string; + Tcl_DString *dstring; + + switch (code) { + case KADM5_FAILURE: code_string = "KADM5_FAILURE"; break; + case KADM5_AUTH_GET: code_string = "KADM5_AUTH_GET"; break; + case KADM5_AUTH_ADD: code_string = "KADM5_AUTH_ADD"; break; + case KADM5_AUTH_MODIFY: + code_string = "KADM5_AUTH_MODIFY"; break; + case KADM5_AUTH_DELETE: + code_string = "KADM5_AUTH_DELETE"; break; + case KADM5_AUTH_INSUFFICIENT: + code_string = "KADM5_AUTH_INSUFFICIENT"; break; + case KADM5_BAD_DB: code_string = "KADM5_BAD_DB"; break; + case KADM5_DUP: code_string = "KADM5_DUP"; break; + case KADM5_RPC_ERROR: code_string = "KADM5_RPC_ERROR"; break; + case KADM5_NO_SRV: code_string = "KADM5_NO_SRV"; break; + case KADM5_BAD_HIST_KEY: + code_string = "KADM5_BAD_HIST_KEY"; break; + case KADM5_NOT_INIT: code_string = "KADM5_NOT_INIT"; break; + case KADM5_INIT: code_string = "KADM5_INIT"; break; + case KADM5_BAD_PASSWORD: + code_string = "KADM5_BAD_PASSWORD"; break; + case KADM5_UNK_PRINC: code_string = "KADM5_UNK_PRINC"; break; + case KADM5_UNK_POLICY: code_string = "KADM5_UNK_POLICY"; break; + case KADM5_BAD_MASK: code_string = "KADM5_BAD_MASK"; break; + case KADM5_BAD_CLASS: code_string = "KADM5_BAD_CLASS"; break; + case KADM5_BAD_LENGTH: code_string = "KADM5_BAD_LENGTH"; break; + case KADM5_BAD_POLICY: code_string = "KADM5_BAD_POLICY"; break; + case KADM5_BAD_HISTORY: code_string = "KADM5_BAD_HISTORY"; break; + case KADM5_BAD_PRINCIPAL: + code_string = "KADM5_BAD_PRINCIPAL"; break; + case KADM5_BAD_AUX_ATTR: + code_string = "KADM5_BAD_AUX_ATTR"; break; + case KADM5_PASS_Q_TOOSHORT: + code_string = "KADM5_PASS_Q_TOOSHORT"; break; + case KADM5_PASS_Q_CLASS: + code_string = "KADM5_PASS_Q_CLASS"; break; + case KADM5_PASS_Q_DICT: + code_string = "KADM5_PASS_Q_DICT"; break; + case KADM5_PASS_REUSE: code_string = "KADM5_PASS_REUSE"; break; + case KADM5_PASS_TOOSOON: + code_string = "KADM5_PASS_TOOSOON"; break; + case KADM5_POLICY_REF: + code_string = "KADM5_POLICY_REF"; break; + case KADM5_PROTECT_PRINCIPAL: + code_string = "KADM5_PROTECT_PRINCIPAL"; break; + case KADM5_BAD_SERVER_HANDLE: + code_string = "KADM5_BAD_SERVER_HANDLE"; break; + case KADM5_BAD_STRUCT_VERSION: + code_string = "KADM5_BAD_STRUCT_VERSION"; break; + case KADM5_OLD_STRUCT_VERSION: + code_string = "KADM5_OLD_STRUCT_VERSION"; break; + case KADM5_NEW_STRUCT_VERSION: + code_string = "KADM5_NEW_STRUCT_VERSION"; break; + case KADM5_BAD_API_VERSION: + code_string = "KADM5_BAD_API_VERSION"; break; + case KADM5_OLD_LIB_API_VERSION: + code_string = "KADM5_OLD_LIB_API_VERSION"; break; + case KADM5_OLD_SERVER_API_VERSION: + code_string = "KADM5_OLD_SERVER_API_VERSION"; break; + case KADM5_NEW_LIB_API_VERSION: + code_string = "KADM5_NEW_LIB_API_VERSION"; break; + case KADM5_NEW_SERVER_API_VERSION: + code_string = "KADM5_NEW_SERVER_API_VERSION"; break; + case KADM5_SECURE_PRINC_MISSING: + code_string = "KADM5_SECURE_PRINC_MISSING"; break; + case KADM5_NO_RENAME_SALT: + code_string = "KADM5_NO_RENAME_SALT"; break; + case KADM5_BAD_CLIENT_PARAMS: + code_string = "KADM5_BAD_CLIENT_PARAMS"; break; + case KADM5_BAD_SERVER_PARAMS: + code_string = "KADM5_BAD_SERVER_PARAMS"; break; + case KADM5_AUTH_LIST: + code_string = "KADM5_AUTH_LIST"; break; + case KADM5_AUTH_CHANGEPW: + code_string = "KADM5_AUTH_CHANGEPW"; break; + case KADM5_GSS_ERROR: code_string = "KADM5_GSS_ERROR"; break; + case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break; + case OSA_ADB_NOENT: code_string = "ENOENT"; break; + case OSA_ADB_DBINIT: code_string = "OSA_ADB_DBINIT"; break; + case OSA_ADB_BAD_POLICY: code_string = "Bad policy name"; break; + case OSA_ADB_BAD_PRINC: code_string = "Bad principal name"; break; + case OSA_ADB_BAD_DB: code_string = "Invalid database."; break; + case OSA_ADB_XDR_FAILURE: code_string = "OSA_ADB_XDR_FAILURE"; break; + case OSA_ADB_BADLOCKMODE: code_string = "OSA_ADB_BADLOCKMODE"; break; + case OSA_ADB_CANTLOCK_DB: code_string = "OSA_ADB_CANTLOCK_DB"; break; + case OSA_ADB_NOTLOCKED: code_string = "OSA_ADB_NOTLOCKED"; break; + case OSA_ADB_NOLOCKFILE: code_string = "OSA_ADB_NOLOCKFILE"; break; + case OSA_ADB_NOEXCL_PERM: code_string = "OSA_ADB_NOEXCL_PERM"; break; + case KRB5_KDB_INUSE: code_string = "KRB5_KDB_INUSE"; break; + case KRB5_KDB_UK_SERROR: code_string = "KRB5_KDB_UK_SERROR"; break; + case KRB5_KDB_UK_RERROR: code_string = "KRB5_KDB_UK_RERROR"; break; + case KRB5_KDB_UNAUTH: code_string = "KRB5_KDB_UNAUTH"; break; + case KRB5_KDB_NOENTRY: code_string = "KRB5_KDB_NOENTRY"; break; + case KRB5_KDB_ILL_WILDCARD: code_string = "KRB5_KDB_ILL_WILDCARD"; break; + case KRB5_KDB_DB_INUSE: code_string = "KRB5_KDB_DB_INUSE"; break; + case KRB5_KDB_DB_CHANGED: code_string = "KRB5_KDB_DB_CHANGED"; break; + case KRB5_KDB_TRUNCATED_RECORD: + code_string = "KRB5_KDB_TRUNCATED_RECORD"; break; + case KRB5_KDB_RECURSIVELOCK: + code_string = "KRB5_KDB_RECURSIVELOCK"; break; + case KRB5_KDB_NOTLOCKED: code_string = "KRB5_KDB_NOTLOCKED"; break; + case KRB5_KDB_BADLOCKMODE: code_string = "KRB5_KDB_BADLOCKMODE"; break; + case KRB5_KDB_DBNOTINITED: code_string = "KRB5_KDB_DBNOTINITED"; break; + case KRB5_KDB_DBINITED: code_string = "KRB5_KDB_DBINITED"; break; + case KRB5_KDB_ILLDIRECTION: code_string = "KRB5_KDB_ILLDIRECTION"; break; + case KRB5_KDB_NOMASTERKEY: code_string = "KRB5_KDB_NOMASTERKEY"; break; + case KRB5_KDB_BADMASTERKEY: code_string = "KRB5_KDB_BADMASTERKEY"; break; + case KRB5_KDB_INVALIDKEYSIZE: + code_string = "KRB5_KDB_INVALIDKEYSIZE"; break; + case KRB5_KDB_CANTREAD_STORED: + code_string = "KRB5_KDB_CANTREAD_STORED"; break; + case KRB5_KDB_BADSTORED_MKEY: + code_string = "KRB5_KDB_BADSTORED_MKEY"; break; + case KRB5_KDB_CANTLOCK_DB: code_string = "KRB5_KDB_CANTLOCK_DB"; break; + case KRB5_KDB_DB_CORRUPT: code_string = "KRB5_KDB_DB_CORRUPT"; break; + case KRB5_PARSE_ILLCHAR: code_string = "KRB5_PARSE_ILLCHAR"; break; + case KRB5_PARSE_MALFORMED: code_string = "KRB5_PARSE_MALFORMED"; break; + case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN"; break; + case KRB5_REALM_UNKNOWN: code_string = "KRB5_REALM_UNKNOWN"; break; + case KRB5_KDC_UNREACH: code_string = "KRB5_KDC_UNREACH"; break; + case KRB5_KDCREP_MODIFIED: code_string = "KRB5_KDCREP_MODIFIED"; break; + case KRB5KRB_AP_ERR_BAD_INTEGRITY: code_string = "KRB5KRB_AP_ERR_BAD_INTEGRITY"; break; + case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"; break; + case KRB5_CONFIG_BADFORMAT: code_string = "KRB5_CONFIG_BADFORMAT"; break; + case EINVAL: code_string = "EINVAL"; break; + case ENOENT: code_string = "ENOENT"; break; + default: fprintf(stderr, "**** CODE %d ***\n", code); code_string = "UNKNOWN"; break; + } + + error_string = (char *) error_message(code); + + if (! (dstring = (Tcl_DString *) malloc(sizeof(Tcl_DString)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX Do we really want to exit? Ok if this is */ + /* just a test program, but what about if it gets */ + /* used for other things later? */ + } + + Tcl_DStringInit(dstring); + + if (! (Tcl_DStringAppendElement(dstring, "ERROR") && + Tcl_DStringAppendElement(dstring, code_string) && + Tcl_DStringAppendElement(dstring, error_string))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + return dstring; +} + + + +static void stash_error(Tcl_Interp *interp, krb5_error_code code) +{ + Tcl_DString *dstring = unparse_err(code); + Tcl_DStringResult(interp, dstring); + Tcl_DStringFree(dstring); + free(dstring); +} + +static Tcl_DString *unparse_key_data(krb5_key_data *key_data, int n_key_data) +{ + Tcl_DString *str; + char buf[2048]; + int i, j; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + for (i = 0; i < n_key_data; i++) { + krb5_key_data *key = &key_data[i]; + + Tcl_DStringStartSublist(str); + sprintf(buf, "%d", key->key_data_type[0]); + Tcl_DStringAppendElement(str, buf); + sprintf(buf, "%d", key->key_data_ver > 1 ? + key->key_data_type[1] : -1); + Tcl_DStringAppendElement(str, buf); + if (key->key_data_contents[0]) { + sprintf(buf, "0x"); + for (j = 0; j < key->key_data_length[0]; j++) { + sprintf(buf + 2*(j+1), "%02x", + key->key_data_contents[0][j]); + } + } else *buf = '\0'; + Tcl_DStringAppendElement(str, buf); + Tcl_DStringEndSublist(str); + } + + return str; +} + +static Tcl_DString *unparse_tl_data(krb5_tl_data *tl_data, int n_tl_data) +{ + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + if (n_tl_data > 0 && tl_data->tl_data_contents) + Tcl_DStringAppendElement(str, "[cannot unparse tl data yet]"); + else + Tcl_DStringAppendElement(str, ""); + + return str; +} + +static Tcl_DString *unparse_flags(struct flagval *array, int size, + krb5_int32 flags) +{ + int i; + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (i = 0; i < size; i++) { + if (flags & array[i].val) { + Tcl_DStringAppendElement(str, array[i].name); + } + } + + return str; +} + + +static int parse_flags(Tcl_Interp *interp, Tcl_HashTable *table, + struct flagval *array, int size, char *str, + krb5_flags *flags) +{ + int tcl_ret, tmp, argc, i, retcode = TCL_OK; + char **argv; + Tcl_HashEntry *entry; + + if ((tcl_ret = Tcl_GetInt(interp, str, &tmp)) == TCL_OK) { + *flags = tmp; + return TCL_OK; + } + Tcl_ResetResult(interp); + + if ((tcl_ret = Tcl_SplitList(interp, str, &argc, &argv)) != TCL_OK) { + return TCL_ERROR; + } + + if (! table) { + table = create_flag_table(array, size); + } + + *flags = 0; + + for (i = 0; i < argc; i++) { + if (! (entry = Tcl_FindHashEntry(table, argv[i]))) { + Tcl_AppendResult(interp, "unknown krb5 flag ", argv[i], 0); + retcode = TCL_ERROR; + break; + } + *flags |= *(krb5_flags *) Tcl_GetHashValue(entry); + } + + free(argv); + return(retcode); +} + +static Tcl_DString *unparse_privs(krb5_flags flags) +{ + return unparse_flags(priv_flags, sizeof(priv_flags) / + sizeof(struct flagval), flags); +} + + +static Tcl_DString *unparse_krb5_flags(krb5_flags flags) +{ + return unparse_flags(krb5_flags_array, sizeof(krb5_flags_array) / + sizeof(struct flagval), flags); +} + +static int parse_krb5_flags(Tcl_Interp *interp, char *str, krb5_flags *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, krb5_flags_array, + sizeof(krb5_flags_array) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static Tcl_DString *unparse_aux_attributes(krb5_int32 flags) +{ + return unparse_flags(aux_attributes, sizeof(aux_attributes) / + sizeof(struct flagval), flags); +} + + +static int parse_aux_attributes(Tcl_Interp *interp, char *str, long *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, aux_attributes, + sizeof(aux_attributes) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_principal_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, principal_mask_flags, + sizeof(principal_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_policy_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, policy_mask_flags, + sizeof(policy_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + + +static Tcl_DString *unparse_principal_ent(kadm5_principal_ent_t princ) +{ + Tcl_DString *str, *tmp_dstring; + char *tmp; + char buf[20]; + krb5_error_code krb5_ret; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp = 0; /* It looks to me from looking at the library source */ + /* code for krb5_parse_name that the pointer passed into */ + /* it should be initialized to 0 if I want it do be */ + /* allocated automatically. */ + if (krb5_ret = krb5_unparse_name(context, princ->principal, &tmp)) { + /* XXX Do we want to return an error? Not sure. */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->princ_expire_time); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_pwd_change); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->pw_expiration); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->max_life); + Tcl_DStringAppendElement(str, buf); + + tmp = 0; + if (krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp)) { + /* XXX */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->mod_date); + Tcl_DStringAppendElement(str, buf); + + tmp_dstring = unparse_krb5_flags(princ->attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", princ->kvno); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->mkvno); + Tcl_DStringAppendElement(str, buf); + + /* XXX This may be dangerous, because the contents of the policy */ + /* field are undefined if the POLICY bit isn't set. However, I */ + /* think it's a bug for the field not to be null in that case */ + /* anyway, so we should assume that it will be null so that we'll */ + /* catch it if it isn't. */ + + tmp_dstring = unparse_str(princ->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_aux_attributes(princ->aux_attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", princ->max_renewable_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_success); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_failed); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->fail_auth_count); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->n_key_data); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->n_tl_data); + Tcl_DStringAppendElement(str, buf); + + tmp_dstring = unparse_key_data(princ->key_data, princ->n_key_data); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_tl_data(princ->tl_data, princ->n_tl_data); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + return str; +} + +static int parse_keysalts(Tcl_Interp *interp, char *list, + krb5_key_salt_tuple **keysalts, + int num_keysalts) +{ + char **argv, **argv1 = NULL; + int i, tmp, argc, argc1, retcode; + + *keysalts = NULL; + if (list == NULL) + return TCL_OK; + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + if (argc != num_keysalts) { + sprintf(interp->result, "%d keysalts specified, " + "but num_keysalts is %d", argc, num_keysalts); + retcode = TCL_ERROR; + goto finished; + } + *keysalts = (krb5_key_salt_tuple *) + malloc(sizeof(krb5_key_salt_tuple)*num_keysalts); + for (i = 0; i < num_keysalts; i++) { + if ((retcode = Tcl_SplitList(interp, argv[i], &argc1, &argv1)) != + TCL_OK) { + goto finished; + } + if (argc1 != 2) { + sprintf(interp->result, "wrong # fields in keysalt " + "(%d should be 2)", argc1); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing ks_enctype"); + retcode = TCL_ERROR; + goto finished; + } + (*keysalts)[i].ks_enctype = tmp; + if ((retcode = Tcl_GetInt(interp, argv1[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing ks_salttype"); + goto finished; + } + (*keysalts)[i].ks_salttype = tmp; + + free(argv1); + } + +finished: + if (argv1) + free(argv1); + if (*keysalts) + free(*keysalts); + free(argv); + return retcode; +} + +static int parse_config_params(Tcl_Interp *interp, char *list, + kadm5_config_params *params) +{ + static Tcl_HashTable *table = 0; + char **argv = NULL; + int tmp, argc, retcode; + + memset(params, 0, sizeof(kadm5_config_params)); + if (list == NULL) + return TCL_OK; + + if ((retcode = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return retcode; + } + + if (argc != 21) { + sprintf(interp->result, + "wrong # args in config params structure (%d should be 21)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if ((retcode = parse_flags(interp, table, config_mask_flags, + sizeof(config_mask_flags) / + sizeof(struct flagval), + argv[0], &tmp)) != TCL_OK) { + goto finished; + } + params->mask = tmp; + + if ((retcode = parse_str(interp, argv[1], ¶ms->realm)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing realm name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[2], ¶ms->profile)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kadmind_port"); + retcode = TCL_ERROR; + goto finished; + } + params->kadmind_port = tmp; + if ((retcode = parse_str(interp, argv[4], ¶ms->admin_server)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[5], ¶ms->dbname)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing profile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[6], ¶ms->admin_dbname)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing admin_dbname name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[7], ¶ms->admin_lockfile)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing admin_lockfile name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[8], ¶ms->admin_keytab)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing admin_keytab name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[9], ¶ms->acl_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing acl_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[10], ¶ms->dict_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing dict_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[11], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkey_from_kbd"); + retcode = TCL_ERROR; + goto finished; + } + params->mkey_from_kbd = tmp; + if ((retcode = parse_str(interp, argv[12], ¶ms->stash_file)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing stash_file name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = parse_str(interp, argv[13], ¶ms->mkey_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkey_name name"); + retcode = TCL_ERROR; + goto finished; + } + if ((retcode = Tcl_GetInt(interp, argv[14], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing enctype"); + retcode = TCL_ERROR; + goto finished; + } + params->enctype = tmp; + if ((retcode = Tcl_GetInt(interp, argv[15], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + params->max_life = tmp; + if ((retcode = Tcl_GetInt(interp, argv[16], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_rlife"); + retcode = TCL_ERROR; + goto finished; + } + params->max_rlife = tmp; + if ((retcode = Tcl_GetInt(interp, argv[17], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing expiration"); + retcode = TCL_ERROR; + goto finished; + } + params->expiration = tmp; + if ((retcode = parse_krb5_flags(interp, argv[18], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing flags"); + retcode = TCL_ERROR; + goto finished; + } + params->flags = tmp; + if ((retcode = Tcl_GetInt(interp, argv[19], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing num_keysalts"); + retcode = TCL_ERROR; + goto finished; + } + params->num_keysalts = tmp; + if ((retcode = parse_keysalts(interp, argv[20], ¶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; + krb5_error_code krb5_ret; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 12) { + sprintf(interp->result, "wrong # args in principal structure (%d should be 12)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (princ = malloc(sizeof *princ))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ->principal)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + retcode = TCL_ERROR; + goto finished; + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing princ_expire_time"); + retcode = TCL_ERROR; + goto finished; + } + princ->princ_expire_time = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing last_pwd_change"); + retcode = TCL_ERROR; + goto finished; + } + princ->last_pwd_change = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_expiration"); + retcode = TCL_ERROR; + goto finished; + } + princ->pw_expiration = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + princ->max_life = tmp; + + if ((krb5_ret = krb5_parse_name(context, argv[5], &princ->mod_name)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing mod_name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mod_date"); + retcode = TCL_ERROR; + goto finished; + } + princ->mod_date = tmp; + + if ((tcl_ret = parse_krb5_flags(interp, argv[7], &princ->attributes)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing attributes"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->kvno = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->mkvno = tmp; + + if ((tcl_ret = parse_str(interp, argv[10], &princ->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + retcode = TCL_ERROR; + goto finished; + } + if(princ->policy != NULL) { + if(!(princ->policy = strdup(princ->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + } + + if ((tcl_ret = parse_aux_attributes(interp, argv[11], + &princ->aux_attributes)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing aux_attributes"); + retcode = TCL_ERROR; + goto finished; + } + +finished: + free(argv); + *out_princ = princ; + return retcode; +} + + +static void free_principal_ent(kadm5_principal_ent_t *princ) +{ + krb5_free_principal(context, (*princ)->principal); + krb5_free_principal(context, (*princ)->mod_name); + free(*princ); + *princ = 0; +} + +static Tcl_DString *unparse_policy_ent(kadm5_policy_ent_t policy) +{ + Tcl_DString *str, *tmp_dstring; + char buf[20]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp_dstring = unparse_str(policy->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", policy->pw_min_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_max_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_length); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_classes); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_history_num); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->policy_refcnt); + Tcl_DStringAppendElement(str, buf); + + return str; +} + + + +static int parse_policy_ent(Tcl_Interp *interp, char *list, + kadm5_policy_ent_t *out_policy) +{ + kadm5_policy_ent_t policy; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 7) { + sprintf(interp->result, "wrong # args in policy structure (%d should be 7)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (policy = malloc(sizeof *policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((tcl_ret = parse_str(interp, argv[0], &policy->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + retcode = TCL_ERROR; + goto finished; + } + + if(policy->policy != NULL) { + if (! (policy->policy = strdup(policy->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_max_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_max_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_length"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_length = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_classes"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_classes = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[5], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_history_num"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_history_num = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy_refcnt"); + retcode = TCL_ERROR; + goto finished; + } + policy->policy_refcnt = tmp; + +finished: + free(argv); + *out_policy = policy; + return retcode; +} + + +static void free_policy_ent(kadm5_policy_ent_t *policy) +{ + free(*policy); + *policy = 0; +} + +static Tcl_DString *unparse_keytype(krb5_enctype enctype) +{ + Tcl_DString *str; + char buf[50]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + switch (enctype) { + /* XXX is this right? */ + case ENCTYPE_NULL: Tcl_DStringAppend(str, "ENCTYPE_NULL", -1); break; + case ENCTYPE_DES_CBC_CRC: + Tcl_DStringAppend(str, "ENCTYPE_DES_CBC_CRC", -1); break; + default: + sprintf(buf, "UNKNOWN KEYTYPE (0x%x)", enctype); + Tcl_DStringAppend(str, buf, -1); + break; + } + + return str; +} + + +static Tcl_DString *unparse_keyblocks(krb5_keyblock *keyblocks, int num_keys) +{ + Tcl_DString *str; + Tcl_DString *keytype; + int i, j; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (j = 0; j < num_keys; j++) { + krb5_keyblock *keyblock = &keyblocks[j]; + + Tcl_DStringStartSublist(str); + + keytype = unparse_keytype(keyblock->enctype); + Tcl_DStringAppendElement(str, keytype->string); + Tcl_DStringFree(keytype); + free(keytype); + if (keyblock->length == 0) { + Tcl_DStringAppendElement(str, "0x00"); + } + else { + Tcl_DStringAppendElement(str, "0x"); + for (i = 0; i < keyblock->length; i++) { + char buf[3]; + sprintf(buf, "%02x", (int) keyblock->contents[i]); + Tcl_DStringAppend(str, buf, -1); + } + } + + Tcl_DStringEndSublist(str); + } + + + return str; +} + +enum init_type { INIT_NONE, INIT_PASS, INIT_CREDS }; + +int _tcl_kadm5_init_any(enum init_type init_type, ClientData clientData, + Tcl_Interp *interp, int argc, char *argv[]) +{ + kadm5_ret_t ret; + char *client_name, *pass, *service_name, *realm; + int tcl_ret; + krb5_ui_4 struct_version, api_version; + char *handle_var; + void *server_handle; + char *handle_name, *params_str; + char *whoami = argv[0]; + kadm5_config_params params; + + argv++, argc--; + + krb5_init_context(&context); + + if (argc != 7) { + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); + return TCL_ERROR; + } + + if (((tcl_ret = parse_str(interp, argv[0], &client_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[1], &pass)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[2], &service_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[3], ¶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(client_name, cc, service_name, + ¶ms, struct_version, + api_version, &server_handle); + + (void) krb5_cc_close(context, cc); + } else + ret = kadm5_init(client_name, pass, service_name, ¶ms, + struct_version, api_version, &server_handle); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = put_server_handle(interp, server_handle, &handle_name)) + != TCL_OK) { + return tcl_ret; + } + + if (! Tcl_SetVar(interp, handle_var, handle_name, TCL_LEAVE_ERR_MSG)) { + return TCL_ERROR; + } + + set_ok(interp, "KADM5 API initialized."); + return TCL_OK; +} + +int tcl_kadm5_init(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + return _tcl_kadm5_init_any(INIT_PASS, clientData, interp, argc, argv); +} + +int tcl_kadm5_init_with_creds(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + return _tcl_kadm5_init_any(INIT_CREDS, clientData, interp, argc, argv); +} + +int tcl_kadm5_destroy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + kadm5_ret_t ret; + int tcl_ret; + + GET_HANDLE(0, 0); + + ret = kadm5_destroy(server_handle); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = remove_server_handle(interp, argv[-1])) != TCL_OK) { + return tcl_ret; + } + + set_ok(interp, "KADM5 API deinitialized."); + return TCL_OK; +} + +int tcl_kadm5_create_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + char *princ_string; + kadm5_principal_ent_t princ = 0; + krb5_int32 mask; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + + GET_HANDLE(3, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[2], &pw)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[3], &override_qual)) != + TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#endif + +#ifdef OVERRIDE + ret = kadm5_create_principal(server_handle, princ, mask, pw, + override_qual); +#else + ret = kadm5_create_principal(server_handle, princ, mask, pw); +#endif + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Principal created."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + + +int tcl_kadm5_delete_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int tcl_ret; + char *name; + + GET_HANDLE(1, 0); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + return TCL_ERROR; + } + } else princ = NULL; + ret = kadm5_delete_principal(server_handle, princ); + + if(princ != NULL) + krb5_free_principal(context, princ); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Principal deleted."); + return TCL_OK; + } +} + + + +int tcl_kadm5_modify_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *princ_string; + kadm5_principal_ent_t princ = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_modify_principal(server_handle, princ, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Principal modified."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + +int tcl_kadm5_rename_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal source, target; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &source)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing source"); + return TCL_ERROR; + } + + if (krb5_ret = krb5_parse_name(context, argv[1], &target)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing target"); + krb5_free_principal(context, source); + return TCL_ERROR; + } + + ret = kadm5_rename_principal(server_handle, source, target); + + if (ret == KADM5_OK) { + set_ok(interp, "Principal renamed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + + krb5_free_principal(context, source); + krb5_free_principal(context, target); + return retcode; +} + + + +int tcl_kadm5_chpass_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + krb5_error_code krb5_ret; + int tcl_ret; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing password"); + retcode = TCL_ERROR; + goto finished; + } + +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_chpass_principal(server_handle, + princ, pw, override_qual); +#else + ret = kadm5_chpass_principal(server_handle, princ, pw); +#endif + + if (ret == KADM5_OK) { + set_ok(interp, "Password changed."); + goto finished; + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_kadm5_chpass_principal_util(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *new_pw; +#ifdef OVERRIDE + int override_qual; +#endif + char *pw_ret, *pw_ret_var; + char msg_ret[1024], *msg_ret_var; + krb5_error_code krb5_ret; + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(4, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &new_pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing new password"); + retcode = TCL_ERROR; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } +#endif + if ((tcl_ret = parse_str(interp, argv[3], &pw_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[4], &msg_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing msg_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_chpass_principal_util(server_handle, princ, new_pw, +#ifdef OVERRIDE + override_qual, +#endif + pw_ret_var ? &pw_ret : 0, + msg_ret_var ? msg_ret : 0); + + if (ret == KADM5_OK) { + if (pw_ret_var && + (! Tcl_SetVar(interp, pw_ret_var, pw_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, "while setting pw_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + if (msg_ret_var && + (! Tcl_SetVar(interp, msg_ret_var, msg_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, + "while setting msg_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Password changed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_kadm5_randkey_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_keyblock *keyblocks; + int num_keys; + char *keyblock_var, *num_var, buf[50]; + Tcl_DString *keyblock_dstring = 0; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + int tcl_ret; + int retcode = TCL_OK; + + GET_HANDLE(3, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &keyblock_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } + if ((tcl_ret = parse_str(interp, argv[2], &num_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_randkey_principal(server_handle, + princ, keyblock_var ? &keyblocks : 0, + num_var ? &num_keys : 0); + + if (ret == KADM5_OK) { + if (keyblock_var) { + keyblock_dstring = unparse_keyblocks(keyblocks, num_keys); + if (! Tcl_SetVar(interp, keyblock_var, + keyblock_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting keyblock variable"); + retcode = TCL_ERROR; + goto finished; + } + } + if (num_var) { + sprintf(buf, "%d", num_keys); + if (! Tcl_SetVar(interp, num_var, buf, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting num_keys variable"); + } + } + set_ok(interp, "Key randomized."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + if (keyblock_dstring) { + Tcl_DStringFree(keyblock_dstring); + free(keyblock_dstring); + } + return retcode; +} + + + +int tcl_kadm5_get_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + kadm5_principal_ent_rec ent; + Tcl_DString *ent_dstring = 0; + char *ent_var; + char *name; + krb5_error_code krb5_ret; + int tcl_ret; + kadm5_ret_t ret = -1; + krb5_int32 mask; + int retcode = TCL_OK; + + GET_HANDLE(3, 1); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + } else princ = NULL; + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + retcode = TCL_ERROR; + goto finished; + } + if ((tcl_ret = parse_principal_mask(interp, argv[2], &mask)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal mask"); + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_get_principal(server_handle, princ, ent_var ? &ent : 0, + mask); + + if (ret == KADM5_OK) { + if (ent_var) { + ent_dstring = unparse_principal_ent(&ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Principal retrieved."); + } + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if(princ != NULL) + krb5_free_principal(context, princ); + if (ret == KADM5_OK && ent_var && + (ret = kadm5_free_principal_ent(server_handle, &ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + return retcode; +} + +int tcl_kadm5_create_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + char *policy_string; + kadm5_policy_ent_t policy = 0; + krb5_int32 mask; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + ret = kadm5_create_policy(server_handle, policy, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Policy created."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + + +int tcl_kadm5_delete_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + kadm5_ret_t ret; + char *policy; + int tcl_ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + ret = kadm5_delete_policy(server_handle, policy); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Policy deleted."); + return TCL_OK; + } +} + + + +int tcl_kadm5_modify_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *policy_string; + kadm5_policy_ent_t policy = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + kadm5_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = kadm5_modify_policy(server_handle, policy, mask); + + if (ret != KADM5_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Policy modified."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + +int tcl_kadm5_get_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + kadm5_policy_ent_rec ent; + Tcl_DString *ent_dstring = 0; + char *policy; + char *ent_var; + int tcl_ret; + kadm5_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 1); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + return TCL_ERROR; + } + + ret = kadm5_get_policy(server_handle, policy, ent_var ? &ent : 0); + + if (ret == KADM5_OK) { + if (ent_var) { + ent_dstring = unparse_policy_ent(&ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Policy retrieved."); + } + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if (ent_var && ret == KADM5_OK && + (ret = kadm5_free_policy_ent(server_handle, &ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + return retcode; +} + + + +int tcl_kadm5_free_principal_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + kadm5_principal_ent_t ent; + int tcl_ret; + kadm5_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = kadm5_free_principal_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "principal", sizeof("principal")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "principal handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = kadm5_free_principal_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Principal freed."); + return TCL_OK; +} + + +int tcl_kadm5_free_policy_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + kadm5_policy_ent_t ent; + int tcl_ret; + kadm5_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = kadm5_free_policy_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "policy", sizeof("policy")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "policy handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = kadm5_free_policy_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Policy freed."); + return TCL_OK; +} + + +int tcl_kadm5_get_privs(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + char *set_ret; + kadm5_ret_t ret; + char *priv_var; + long privs; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &priv_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing privs variable name"); + return TCL_ERROR; + } + + ret = kadm5_get_privs(server_handle, priv_var ? &privs : 0); + + if (ret == KADM5_OK) { + if (priv_var) { + Tcl_DString *str = unparse_privs(privs); + set_ret = Tcl_SetVar(interp, priv_var, str->string, + TCL_LEAVE_ERR_MSG); + Tcl_DStringFree(str); + free(str); + if (! set_ret) { + Tcl_AppendElement(interp, "while setting priv variable"); + return TCL_ERROR; + } + } + set_ok(interp, "Privileges retrieved."); + return TCL_OK; + } + else { + stash_error(interp, ret); + return TCL_ERROR; + } +} + + +void Tcl_kadm5_init(Tcl_Interp *interp) +{ + char buf[20]; + + Tcl_SetVar(interp, "KADM5_ADMIN_SERVICE", + KADM5_ADMIN_SERVICE, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "KADM5_CHANGEPW_SERVICE", + KADM5_CHANGEPW_SERVICE, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION); + Tcl_SetVar(interp, "KADM5_STRUCT_VERSION", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_1); + Tcl_SetVar(interp, "KADM5_API_VERSION_1", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_2); + Tcl_SetVar(interp, "KADM5_API_VERSION_2", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_API_VERSION_MASK); + Tcl_SetVar(interp, "KADM5_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", KADM5_STRUCT_VERSION_MASK); + Tcl_SetVar(interp, "KADM5_STRUCT_VERSION_MASK", buf, + TCL_GLOBAL_ONLY); + + Tcl_CreateCommand(interp, "kadm5_init", tcl_kadm5_init, 0, 0); + Tcl_CreateCommand(interp, "kadm5_init_with_creds", + tcl_kadm5_init_with_creds, 0, 0); + Tcl_CreateCommand(interp, "kadm5_destroy", tcl_kadm5_destroy, 0, + 0); + Tcl_CreateCommand(interp, "kadm5_create_principal", + tcl_kadm5_create_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_delete_principal", + tcl_kadm5_delete_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_modify_principal", + tcl_kadm5_modify_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_rename_principal", + tcl_kadm5_rename_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_chpass_principal", + tcl_kadm5_chpass_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_chpass_principal_util", + tcl_kadm5_chpass_principal_util, 0, 0); + Tcl_CreateCommand(interp, "kadm5_randkey_principal", + tcl_kadm5_randkey_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_principal", + tcl_kadm5_get_principal, 0, 0); + Tcl_CreateCommand(interp, "kadm5_create_policy", + tcl_kadm5_create_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_delete_policy", + tcl_kadm5_delete_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_modify_policy", + tcl_kadm5_modify_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_policy", + tcl_kadm5_get_policy, 0, 0); + Tcl_CreateCommand(interp, "kadm5_free_principal_ent", + tcl_kadm5_free_principal_ent, 0, 0); + Tcl_CreateCommand(interp, "kadm5_free_policy_ent", + tcl_kadm5_free_policy_ent, 0, 0); + Tcl_CreateCommand(interp, "kadm5_get_privs", + tcl_kadm5_get_privs, 0, 0); +} diff --git a/src/kadmin/testing/util/tcl_krb5_hash.c b/src/kadmin/testing/util/tcl_krb5_hash.c new file mode 100644 index 000000000..95a3451d9 --- /dev/null +++ b/src/kadmin/testing/util/tcl_krb5_hash.c @@ -0,0 +1,163 @@ +/* + * All of the TCL krb5 functions which return (or place into output + * variables) structures or pointers to structures that can't be + * represented as tcl native types, do so by returning a handle for + * the appropriate structure. The handle is a string of the form + * "type$id", where "type" is the type of datum represented by the + * handle and "id" is a unique identifier for it. This handle can + * then be used later by the caller to refer to the object, and + * internally to retrieve the actually datum from the appropriate hash + * table. + * + * The functions in this file do four things: + * + * 1) Given a pointer to a datum and a string representing the type of + * datum to which the pointer refers, create a new handle for the + * datum, store the datum in the hash table using the new handle as + * its key, and return the new handle. + * + * 2) Given a handle, locate and return the appropriate hash table + * datum. + * + * 3) Given a handle, look through a table of types and unparse + * functions to figure out what function to call to get a string + * representation of the datum, call it with the appropriate pointer + * (obtained from the hash table) as an argument, and return the + * resulting string as the unparsed form of the datum. + * + * 4) Given a handle, remove that handle and its associated datum from + * the hash table (but don't free it -- it's assumed to have already + * been freed by the caller). + */ + +#include <tcl.h> +#include <assert.h> + +#define SEP_STR "$" + +static char *memory_error = "out of memory"; + +/* + * Right now, we're only using one hash table. However, at some point + * in the future, we might decide to use a separate hash table for + * every type. Therefore, I'm putting this function in as an + * abstraction so it's the only thing we'll have to change if we + * decide to do that. + * + * Also, this function allows us to put in just one place the code for + * checking to make sure that the hash table exists and initializing + * it if it doesn't. + */ + +static TclHashTable *get_hash_table(Tcl_Interp *interp, + char *type) +{ + static Tcl_HashTable *hash_table = 0; + + if (! hash_table) { + if (! (hash_table = malloc(sizeof(*hash_table)))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + Tcl_InitHashTable(hash_table, TCL_STRING_KEYS); + } + return hash_table; +} + +#define MAX_ID 999999999 +#define ID_BUF_SIZE 10 + +static Tcl_HashEntry *get_new_handle(Tcl_Interp *interp, + char *type) +{ + static unsigned long int id_counter = 0; + Tcl_DString *handle; + char int_buf[ID_BUF_SIZE]; + + if (! (handle = malloc(sizeof(*handle)))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + Tcl_DStringInit(handle); + + assert(id_counter <= MAX_ID); + + sprintf(int_buf, "%d", id_counter++); + + Tcl_DStringAppend(handle, type, -1); + Tcl_DStringAppend(handle, SEP_STR, -1); + Tcl_DStringAppend(handle, int_buf, -1); + + return handle; +} + + +Tcl_DString *tcl_krb5_create_object(Tcl_Interp *interp, + char *type, + ClientData datum) +{ + Tcl_HashTable *table; + Tcl_DString *handle; + Tcl_HashEntry *entry; + int entry_created = 0; + + if (! (table = get_hash_table(interp, type))) { + return 0; + } + + if (! (handle = get_new_handle(interp, type))) { + return 0; + } + + if (! (entry = Tcl_CreateHashEntry(table, handle, &entry_created))) { + Tcl_SetResult(interp, "error creating hash entry", TCL_STATIC); + Tcl_DStringFree(handle); + return TCL_ERROR; + } + + assert(entry_created); + + Tcl_SetHashValue(entry, datum); + + return handle; +} + +ClientData tcl_krb5_get_object(Tcl_Interp *interp, + char *handle) +{ + char *myhandle, *id_ptr; + Tcl_HashTable *table; + Tcl_HashEntry *entry; + + if (! (myhandle = strdup(handle))) { + Tcl_SetResult(interp, memory_error, TCL_STATIC); + return 0; + } + + if (! (id_ptr = index(myhandle, *SEP_STR))) { + free(myhandle); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "malformatted handle \"", handle, + "\"", 0); + return 0; + } + + *id_ptr = '\0'; + + if (! (table = get_hash_table(interp, myhandle))) { + free(myhandle); + return 0; + } + + free(myhandle); + + if (! (entry = Tcl_FindHashEntry(table, handle))) { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "no object corresponding to handle \"", + handle, "\"", 0); + return 0; + } + + return(Tcl_GetHashValue(entry)); +} + diff --git a/src/kadmin/testing/util/tcl_ovsec_kadm.c b/src/kadmin/testing/util/tcl_ovsec_kadm.c new file mode 100644 index 000000000..0c6aaac9c --- /dev/null +++ b/src/kadmin/testing/util/tcl_ovsec_kadm.c @@ -0,0 +1,2016 @@ +#include <stdio.h> +#include <string.h> +#include <tcl.h> +#define USE_KADM5_API_VERSION 1 +#include <kadm5/admin.h> +#include <com_err.h> +#include <malloc.h> +#include <k5-int.h> +#include <errno.h> +#include <stdlib.h> + +struct flagval { + char *name; + krb5_flags val; +}; + +/* XXX This should probably be in the hash table like server_handle */ +static krb5_context context; + +struct flagval krb5_flags_array[] = { + {"KRB5_KDB_DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED}, + {"KRB5_KDB_DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE}, + {"KRB5_KDB_DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED}, + {"KRB5_KDB_DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE}, + {"KRB5_KDB_DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE}, + {"KRB5_KDB_DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY}, + {"KRB5_KDB_DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX}, + {"KRB5_KDB_REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH}, + {"KRB5_KDB_REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH}, + {"KRB5_KDB_REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE}, + {"KRB5_KDB_DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR}, + {"KRB5_KDB_PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE} +}; + +struct flagval aux_attributes[] = { + {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY} +}; + +struct flagval principal_mask_flags[] = { + {"OVSEC_KADM_PRINCIPAL", OVSEC_KADM_PRINCIPAL}, + {"OVSEC_KADM_PRINC_EXPIRE_TIME", OVSEC_KADM_PRINC_EXPIRE_TIME}, + {"OVSEC_KADM_PW_EXPIRATION", OVSEC_KADM_PW_EXPIRATION}, + {"OVSEC_KADM_LAST_PWD_CHANGE", OVSEC_KADM_LAST_PWD_CHANGE}, + {"OVSEC_KADM_ATTRIBUTES", OVSEC_KADM_ATTRIBUTES}, + {"OVSEC_KADM_MAX_LIFE", OVSEC_KADM_MAX_LIFE}, + {"OVSEC_KADM_MOD_TIME", OVSEC_KADM_MOD_TIME}, + {"OVSEC_KADM_MOD_NAME", OVSEC_KADM_MOD_NAME}, + {"OVSEC_KADM_KVNO", OVSEC_KADM_KVNO}, + {"OVSEC_KADM_MKVNO", OVSEC_KADM_MKVNO}, + {"OVSEC_KADM_AUX_ATTRIBUTES", OVSEC_KADM_AUX_ATTRIBUTES}, + {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY}, + {"OVSEC_KADM_POLICY_CLR", OVSEC_KADM_POLICY_CLR} +}; + +struct flagval policy_mask_flags[] = { + {"OVSEC_KADM_POLICY", OVSEC_KADM_POLICY}, + {"OVSEC_KADM_PW_MAX_LIFE", OVSEC_KADM_PW_MAX_LIFE}, + {"OVSEC_KADM_PW_MIN_LIFE", OVSEC_KADM_PW_MIN_LIFE}, + {"OVSEC_KADM_PW_MIN_LENGTH", OVSEC_KADM_PW_MIN_LENGTH}, + {"OVSEC_KADM_PW_MIN_CLASSES", OVSEC_KADM_PW_MIN_CLASSES}, + {"OVSEC_KADM_PW_HISTORY_NUM", OVSEC_KADM_PW_HISTORY_NUM}, + {"OVSEC_KADM_REF_COUNT", OVSEC_KADM_REF_COUNT} +}; + +struct flagval priv_flags[] = { + {"OVSEC_KADM_PRIV_GET", OVSEC_KADM_PRIV_GET}, + {"OVSEC_KADM_PRIV_ADD", OVSEC_KADM_PRIV_ADD}, + {"OVSEC_KADM_PRIV_MODIFY", OVSEC_KADM_PRIV_MODIFY}, + {"OVSEC_KADM_PRIV_DELETE", OVSEC_KADM_PRIV_DELETE} +}; + + +static char *arg_error = "wrong # args"; + +static Tcl_HashTable *struct_table = 0; + +static int put_server_handle(Tcl_Interp *interp, void *handle, char **name) +{ + int i = 1, newPtr = 0; + static char buf[20]; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + /* + * Handles from ovsec_kadm_init() and kadm5_init() should not + * be mixed during unit tests, but the API would happily + * accept them. Making the hash entry names different in + * tcl_kadm.c and tcl_ovsec_kadm.c ensures that GET_HANDLE + * will fail if presented a handle from the other API. + */ + sprintf(buf, "ovsec_kadm_handle%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, handle); + + *name = buf; + + return TCL_OK; +} + +static int get_server_handle(Tcl_Interp *interp, char *name, void **handle) +{ + Tcl_HashEntry *entry; + + if(!strcasecmp(name, "null")) + *handle = 0; + else { + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + if (strncmp(name, "kadm5_handle", 12) == 0) + Tcl_AppendResult(interp, "kadm5 handle specified " + "for ovsec_kadm api: ", name, 0); + else + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + *handle = (void *) Tcl_GetHashValue(entry); + } + return TCL_OK; +} + +static int remove_server_handle(Tcl_Interp *interp, char *name) +{ + Tcl_HashEntry *entry; + + if (! (struct_table && + (entry = Tcl_FindHashEntry(struct_table, name)))) { + Tcl_AppendResult(interp, "unknown server handle ", name, 0); + return TCL_ERROR; + } + + Tcl_DeleteHashEntry(entry); + return TCL_OK; +} + +#define GET_HANDLE(num_args, do_dostruct) \ + void *server_handle; \ + int dostruct = 0; \ + char *whoami = argv[0]; \ + argv++, argc--; \ + if ((argc > 0) && (! strcmp(argv[0], "-struct"))) { \ + if (! do_dostruct) { \ + Tcl_AppendResult(interp, "-struct isn't a valid option for ", \ + whoami, 0); \ + return TCL_ERROR; \ + } \ + dostruct++; \ + argv++, argc--; \ + } \ + if (argc != num_args + 1) { \ + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); \ + return TCL_ERROR; \ + } \ + { \ + int tcl_ret; \ + if ((tcl_ret = get_server_handle(interp, argv[0], &server_handle)) \ + != TCL_OK) { \ + return tcl_ret; \ + } \ + } \ + argv++, argc--; + +static Tcl_HashTable *create_flag_table(struct flagval *flags, int size) +{ + Tcl_HashTable *table; + Tcl_HashEntry *entry; + int i; + + if (! (table = (Tcl_HashTable *) malloc(sizeof(Tcl_HashTable)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_InitHashTable(table, TCL_STRING_KEYS); + + for (i = 0; i < size; i++) { + int newPtr; + + if (! (entry = Tcl_CreateHashEntry(table, flags[i].name, &newPtr))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_SetHashValue(entry, &flags[i].val); + } + + return table; +} + + +static Tcl_DString *unparse_str(char *in_str) +{ + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + if (! in_str) { + Tcl_DStringAppend(str, "null", -1); + } + else { + Tcl_DStringAppend(str, in_str, -1); + } + + return str; +} + + + +static int parse_str(Tcl_Interp *interp, char *in_str, char **out_str) +{ + if (! in_str) { + *out_str = 0; + } + else if (! strcasecmp(in_str, "null")) { + *out_str = 0; + } + else { + *out_str = in_str; + } + return TCL_OK; +} + + +static void set_ok(Tcl_Interp *interp, char *string) +{ + Tcl_SetResult(interp, "OK", TCL_STATIC); + Tcl_AppendElement(interp, "OVSEC_KADM_OK"); + Tcl_AppendElement(interp, string); +} + + + +static Tcl_DString *unparse_err(ovsec_kadm_ret_t code) +{ + char *code_string, *error_string; + Tcl_DString *dstring; + + switch (code) { + case OVSEC_KADM_FAILURE: code_string = "OVSEC_KADM_FAILURE"; break; + case OVSEC_KADM_AUTH_GET: code_string = "OVSEC_KADM_AUTH_GET"; break; + case OVSEC_KADM_AUTH_ADD: code_string = "OVSEC_KADM_AUTH_ADD"; break; + case OVSEC_KADM_AUTH_MODIFY: + code_string = "OVSEC_KADM_AUTH_MODIFY"; break; + case OVSEC_KADM_AUTH_DELETE: + code_string = "OVSEC_KADM_AUTH_DELETE"; break; + case OVSEC_KADM_AUTH_INSUFFICIENT: + code_string = "OVSEC_KADM_AUTH_INSUFFICIENT"; break; + case OVSEC_KADM_BAD_DB: code_string = "OVSEC_KADM_BAD_DB"; break; + case OVSEC_KADM_DUP: code_string = "OVSEC_KADM_DUP"; break; + case OVSEC_KADM_RPC_ERROR: code_string = "OVSEC_KADM_RPC_ERROR"; break; + case OVSEC_KADM_NO_SRV: code_string = "OVSEC_KADM_NO_SRV"; break; + case OVSEC_KADM_BAD_HIST_KEY: + code_string = "OVSEC_KADM_BAD_HIST_KEY"; break; + case OVSEC_KADM_NOT_INIT: code_string = "OVSEC_KADM_NOT_INIT"; break; + case OVSEC_KADM_INIT: code_string = "OVSEC_KADM_INIT"; break; + case OVSEC_KADM_BAD_PASSWORD: + code_string = "OVSEC_KADM_BAD_PASSWORD"; break; + case OVSEC_KADM_UNK_PRINC: code_string = "OVSEC_KADM_UNK_PRINC"; break; + case OVSEC_KADM_UNK_POLICY: code_string = "OVSEC_KADM_UNK_POLICY"; break; + case OVSEC_KADM_BAD_MASK: code_string = "OVSEC_KADM_BAD_MASK"; break; + case OVSEC_KADM_BAD_CLASS: code_string = "OVSEC_KADM_BAD_CLASS"; break; + case OVSEC_KADM_BAD_LENGTH: code_string = "OVSEC_KADM_BAD_LENGTH"; break; + case OVSEC_KADM_BAD_POLICY: code_string = "OVSEC_KADM_BAD_POLICY"; break; + case OVSEC_KADM_BAD_HISTORY: code_string = "OVSEC_KADM_BAD_HISTORY"; break; + case OVSEC_KADM_BAD_PRINCIPAL: + code_string = "OVSEC_KADM_BAD_PRINCIPAL"; break; + case OVSEC_KADM_BAD_AUX_ATTR: + code_string = "OVSEC_KADM_BAD_AUX_ATTR"; break; + case OVSEC_KADM_PASS_Q_TOOSHORT: + code_string = "OVSEC_KADM_PASS_Q_TOOSHORT"; break; + case OVSEC_KADM_PASS_Q_CLASS: + code_string = "OVSEC_KADM_PASS_Q_CLASS"; break; + case OVSEC_KADM_PASS_Q_DICT: + code_string = "OVSEC_KADM_PASS_Q_DICT"; break; + case OVSEC_KADM_PASS_REUSE: code_string = "OVSEC_KADM_PASS_REUSE"; break; + case OVSEC_KADM_PASS_TOOSOON: + code_string = "OVSEC_KADM_PASS_TOOSOON"; break; + case OVSEC_KADM_POLICY_REF: + code_string = "OVSEC_KADM_POLICY_REF"; break; + case OVSEC_KADM_PROTECT_PRINCIPAL: + code_string = "OVSEC_KADM_PROTECT_PRINCIPAL"; break; + case OVSEC_KADM_BAD_SERVER_HANDLE: + code_string = "OVSEC_KADM_BAD_SERVER_HANDLE"; break; + case OVSEC_KADM_BAD_STRUCT_VERSION: + code_string = "OVSEC_KADM_BAD_STRUCT_VERSION"; break; + case OVSEC_KADM_OLD_STRUCT_VERSION: + code_string = "OVSEC_KADM_OLD_STRUCT_VERSION"; break; + case OVSEC_KADM_NEW_STRUCT_VERSION: + code_string = "OVSEC_KADM_NEW_STRUCT_VERSION"; break; + case OVSEC_KADM_BAD_API_VERSION: + code_string = "OVSEC_KADM_BAD_API_VERSION"; break; + case OVSEC_KADM_OLD_LIB_API_VERSION: + code_string = "OVSEC_KADM_OLD_LIB_API_VERSION"; break; + case OVSEC_KADM_OLD_SERVER_API_VERSION: + code_string = "OVSEC_KADM_OLD_SERVER_API_VERSION"; break; + case OVSEC_KADM_NEW_LIB_API_VERSION: + code_string = "OVSEC_KADM_NEW_LIB_API_VERSION"; break; + case OVSEC_KADM_NEW_SERVER_API_VERSION: + code_string = "OVSEC_KADM_NEW_SERVER_API_VERSION"; break; + case OVSEC_KADM_SECURE_PRINC_MISSING: + code_string = "OVSEC_KADM_SECURE_PRINC_MISSING"; break; + case KADM5_NO_RENAME_SALT: + code_string = "KADM5_NO_RENAME_SALT"; break; + case KADM5_BAD_CLIENT_PARAMS: + code_string = "KADM5_BAD_CLIENT_PARAMS"; break; + case KADM5_BAD_SERVER_PARAMS: + code_string = "KADM5_BAD_SERVER_PARAMS"; break; + case KADM5_AUTH_LIST: + code_string = "KADM5_AUTH_LIST"; break; + case KADM5_AUTH_CHANGEPW: + code_string = "KADM5_AUTH_CHANGEPW"; break; + case OSA_ADB_DUP: code_string = "OSA_ADB_DUP"; break; + case OSA_ADB_NOENT: code_string = "ENOENT"; break; + case OSA_ADB_DBINIT: code_string = "OSA_ADB_DBINIT"; break; + case OSA_ADB_BAD_POLICY: code_string = "Bad policy name"; break; + case OSA_ADB_BAD_PRINC: code_string = "Bad principal name"; break; + case OSA_ADB_BAD_DB: code_string = "Invalid database."; break; + case OSA_ADB_XDR_FAILURE: code_string = "OSA_ADB_XDR_FAILURE"; break; + case KRB5_KDB_INUSE: code_string = "KRB5_KDB_INUSE"; break; + case KRB5_KDB_UK_SERROR: code_string = "KRB5_KDB_UK_SERROR"; break; + case KRB5_KDB_UK_RERROR: code_string = "KRB5_KDB_UK_RERROR"; break; + case KRB5_KDB_UNAUTH: code_string = "KRB5_KDB_UNAUTH"; break; + case KRB5_KDB_NOENTRY: code_string = "KRB5_KDB_NOENTRY"; break; + case KRB5_KDB_ILL_WILDCARD: code_string = "KRB5_KDB_ILL_WILDCARD"; break; + case KRB5_KDB_DB_INUSE: code_string = "KRB5_KDB_DB_INUSE"; break; + case KRB5_KDB_DB_CHANGED: code_string = "KRB5_KDB_DB_CHANGED"; break; + case KRB5_KDB_TRUNCATED_RECORD: + code_string = "KRB5_KDB_TRUNCATED_RECORD"; break; + case KRB5_KDB_RECURSIVELOCK: + code_string = "KRB5_KDB_RECURSIVELOCK"; break; + case KRB5_KDB_NOTLOCKED: code_string = "KRB5_KDB_NOTLOCKED"; break; + case KRB5_KDB_BADLOCKMODE: code_string = "KRB5_KDB_BADLOCKMODE"; break; + case KRB5_KDB_DBNOTINITED: code_string = "KRB5_KDB_DBNOTINITED"; break; + case KRB5_KDB_DBINITED: code_string = "KRB5_KDB_DBINITED"; break; + case KRB5_KDB_ILLDIRECTION: code_string = "KRB5_KDB_ILLDIRECTION"; break; + case KRB5_KDB_NOMASTERKEY: code_string = "KRB5_KDB_NOMASTERKEY"; break; + case KRB5_KDB_BADMASTERKEY: code_string = "KRB5_KDB_BADMASTERKEY"; break; + case KRB5_KDB_INVALIDKEYSIZE: + code_string = "KRB5_KDB_INVALIDKEYSIZE"; break; + case KRB5_KDB_CANTREAD_STORED: + code_string = "KRB5_KDB_CANTREAD_STORED"; break; + case KRB5_KDB_BADSTORED_MKEY: + code_string = "KRB5_KDB_BADSTORED_MKEY"; break; + case KRB5_KDB_CANTLOCK_DB: code_string = "KRB5_KDB_CANTLOCK_DB"; break; + case KRB5_KDB_DB_CORRUPT: code_string = "KRB5_KDB_DB_CORRUPT"; break; + case KRB5_PARSE_ILLCHAR: code_string = "KRB5_PARSE_ILLCHAR"; break; + case KRB5_PARSE_MALFORMED: code_string = "KRB5_PARSE_MALFORMED"; break; + case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN"; break; + case KRB5_REALM_UNKNOWN: code_string = "KRB5_REALM_UNKNOWN"; break; + case KRB5_KDC_UNREACH: code_string = "KRB5_KDC_UNREACH"; break; + case KRB5_KDCREP_MODIFIED: code_string = "KRB5_KDCREP_MODIFIED"; break; + case KRB5KRB_AP_ERR_BAD_INTEGRITY: code_string = "KRB5KRB_AP_ERR_BAD_INTEGRITY"; break; + case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: code_string = "KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN"; break; + case EINVAL: code_string = "EINVAL"; break; + case ENOENT: code_string = "ENOENT"; break; + default: fprintf(stderr, "**** CODE %d ***\n", code); code_string = "UNKNOWN"; break; + } + + error_string = (char *) error_message(code); + + if (! (dstring = (Tcl_DString *) malloc(sizeof(Tcl_DString)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX Do we really want to exit? Ok if this is */ + /* just a test program, but what about if it gets */ + /* used for other things later? */ + } + + Tcl_DStringInit(dstring); + + if (! (Tcl_DStringAppendElement(dstring, "ERROR") && + Tcl_DStringAppendElement(dstring, code_string) && + Tcl_DStringAppendElement(dstring, error_string))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + return dstring; +} + + + +static void stash_error(Tcl_Interp *interp, krb5_error_code code) +{ + Tcl_DString *dstring = unparse_err(code); + Tcl_DStringResult(interp, dstring); + Tcl_DStringFree(dstring); + free(dstring); +} + + + +static Tcl_DString *unparse_flags(struct flagval *array, int size, + krb5_int32 flags) +{ + int i; + Tcl_DString *str; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + for (i = 0; i < size; i++) { + if (flags & array[i].val) { + Tcl_DStringAppendElement(str, array[i].name); + } + } + + return str; +} + + +static int parse_flags(Tcl_Interp *interp, Tcl_HashTable *table, + struct flagval *array, int size, char *str, + krb5_flags *flags) +{ + int tcl_ret, tmp, argc, i, retcode = TCL_OK; + char **argv; + Tcl_HashEntry *entry; + + if ((tcl_ret = Tcl_GetInt(interp, str, &tmp)) == TCL_OK) { + *flags = tmp; + return TCL_OK; + } + Tcl_ResetResult(interp); + + if ((tcl_ret = Tcl_SplitList(interp, str, &argc, &argv)) != TCL_OK) { + return TCL_ERROR; + } + + if (! table) { + table = create_flag_table(array, size); + } + + *flags = 0; + + for (i = 0; i < argc; i++) { + if (! (entry = Tcl_FindHashEntry(table, argv[i]))) { + Tcl_AppendResult(interp, "unknown krb5 flag ", argv[i], 0); + retcode = TCL_ERROR; + break; + } + *flags |= *(krb5_flags *) Tcl_GetHashValue(entry); + } + + free(argv); + return(retcode); +} + +static Tcl_DString *unparse_privs(krb5_flags flags) +{ + return unparse_flags(priv_flags, sizeof(priv_flags) / + sizeof(struct flagval), flags); +} + + +static Tcl_DString *unparse_krb5_flags(krb5_flags flags) +{ + return unparse_flags(krb5_flags_array, sizeof(krb5_flags_array) / + sizeof(struct flagval), flags); +} + +static int parse_krb5_flags(Tcl_Interp *interp, char *str, krb5_flags *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, krb5_flags_array, + sizeof(krb5_flags_array) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static Tcl_DString *unparse_aux_attributes(krb5_int32 flags) +{ + return unparse_flags(aux_attributes, sizeof(aux_attributes) / + sizeof(struct flagval), flags); +} + + +static int parse_aux_attributes(Tcl_Interp *interp, char *str, long *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, aux_attributes, + sizeof(aux_attributes) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + +static int parse_principal_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, principal_mask_flags, + sizeof(principal_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + + +static int parse_policy_mask(Tcl_Interp *interp, char *str, krb5_int32 *flags) +{ + krb5_flags tmp; + static Tcl_HashTable *table = 0; + int tcl_ret; + + if ((tcl_ret = parse_flags(interp, table, policy_mask_flags, + sizeof(policy_mask_flags) / + sizeof(struct flagval), + str, &tmp)) != TCL_OK) { + return tcl_ret; + } + + *flags = tmp; + return TCL_OK; +} + + +static Tcl_DString *unparse_principal_ent(ovsec_kadm_principal_ent_t princ) +{ + Tcl_DString *str, *tmp_dstring; + char *tmp; + char buf[20]; + krb5_error_code krb5_ret; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp = 0; /* It looks to me from looking at the library source */ + /* code for krb5_parse_name that the pointer passed into */ + /* it should be initialized to 0 if I want it do be */ + /* allocated automatically. */ + if (krb5_ret = krb5_unparse_name(context, princ->principal, &tmp)) { + /* XXX Do we want to return an error? Not sure. */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->princ_expire_time); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->last_pwd_change); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->pw_expiration); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->max_life); + Tcl_DStringAppendElement(str, buf); + + tmp = 0; + if (krb5_ret = krb5_unparse_name(context, princ->mod_name, &tmp)) { + /* XXX */ + Tcl_DStringAppendElement(str, "[unparseable principal]"); + } + else { + Tcl_DStringAppendElement(str, tmp); + free(tmp); + } + + sprintf(buf, "%d", princ->mod_date); + Tcl_DStringAppendElement(str, buf); + + tmp_dstring = unparse_krb5_flags(princ->attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", princ->kvno); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", princ->mkvno); + Tcl_DStringAppendElement(str, buf); + + /* XXX This may be dangerous, because the contents of the policy */ + /* field are undefined if the POLICY bit isn't set. However, I */ + /* think it's a bug for the field not to be null in that case */ + /* anyway, so we should assume that it will be null so that we'll */ + /* catch it if it isn't. */ + + tmp_dstring = unparse_str(princ->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + tmp_dstring = unparse_aux_attributes(princ->aux_attributes); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + return str; +} + + + +static int parse_principal_ent(Tcl_Interp *interp, char *list, + ovsec_kadm_principal_ent_t *out_princ) +{ + ovsec_kadm_principal_ent_t princ; + krb5_error_code krb5_ret; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 12) { + sprintf(interp->result, "wrong # args in principal structure (%d should be 12)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (princ = malloc(sizeof *princ))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((krb5_ret = krb5_parse_name(context, argv[0], &princ->principal)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + retcode = TCL_ERROR; + goto finished; + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing princ_expire_time"); + retcode = TCL_ERROR; + goto finished; + } + princ->princ_expire_time = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing last_pwd_change"); + retcode = TCL_ERROR; + goto finished; + } + princ->last_pwd_change = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_expiration"); + retcode = TCL_ERROR; + goto finished; + } + princ->pw_expiration = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing max_life"); + retcode = TCL_ERROR; + goto finished; + } + princ->max_life = tmp; + + if ((krb5_ret = krb5_parse_name(context, argv[5], &princ->mod_name)) != 0) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing mod_name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mod_date"); + retcode = TCL_ERROR; + goto finished; + } + princ->mod_date = tmp; + + if ((tcl_ret = parse_krb5_flags(interp, argv[7], &princ->attributes)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing attributes"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = Tcl_GetInt(interp, argv[8], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing kvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->kvno = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[9], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing mkvno"); + retcode = TCL_ERROR; + goto finished; + } + princ->mkvno = tmp; + + if ((tcl_ret = parse_str(interp, argv[10], &princ->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + retcode = TCL_ERROR; + goto finished; + } + if(princ->policy != NULL) { + if(!(princ->policy = strdup(princ->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); + } + } + + if ((tcl_ret = parse_aux_attributes(interp, argv[11], + &princ->aux_attributes)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing aux_attributes"); + retcode = TCL_ERROR; + goto finished; + } + +finished: + free(argv); + *out_princ = princ; + return retcode; +} + + +static void free_principal_ent(ovsec_kadm_principal_ent_t *princ) +{ + krb5_free_principal(context, (*princ)->principal); + krb5_free_principal(context, (*princ)->mod_name); + free(*princ); + *princ = 0; +} + +static Tcl_DString *unparse_policy_ent(ovsec_kadm_policy_ent_t policy) +{ + Tcl_DString *str, *tmp_dstring; + char buf[20]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + tmp_dstring = unparse_str(policy->policy); + Tcl_DStringAppendElement(str, tmp_dstring->string); + Tcl_DStringFree(tmp_dstring); + free(tmp_dstring); + + sprintf(buf, "%d", policy->pw_min_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_max_life); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_length); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_min_classes); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->pw_history_num); + Tcl_DStringAppendElement(str, buf); + + sprintf(buf, "%d", policy->policy_refcnt); + Tcl_DStringAppendElement(str, buf); + + return str; +} + + + +static int parse_policy_ent(Tcl_Interp *interp, char *list, + ovsec_kadm_policy_ent_t *out_policy) +{ + ovsec_kadm_policy_ent_t policy; + int tcl_ret; + int argc; + char **argv; + int tmp; + int retcode = TCL_OK; + + if ((tcl_ret = Tcl_SplitList(interp, list, &argc, &argv)) != TCL_OK) { + return tcl_ret; + } + + if (argc != 7) { + sprintf(interp->result, "wrong # args in policy structure (%d should be 7)", + argc); + retcode = TCL_ERROR; + goto finished; + } + + if (! (policy = malloc(sizeof *policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + if ((tcl_ret = parse_str(interp, argv[0], &policy->policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + retcode = TCL_ERROR; + goto finished; + } + + if(policy->policy != NULL) { + if (! (policy->policy = strdup(policy->policy))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + } + + /* + * All of the numerical values parsed here are parsed into an + * "int" and then assigned into the structure in case the actual + * width of the field in the Kerberos structure is different from + * the width of an integer. + */ + + if ((tcl_ret = Tcl_GetInt(interp, argv[1], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[2], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_max_life"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_max_life = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[3], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_length"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_length = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[4], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_min_classes"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_min_classes = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[5], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_history_num"); + retcode = TCL_ERROR; + goto finished; + } + policy->pw_history_num = tmp; + + if ((tcl_ret = Tcl_GetInt(interp, argv[6], &tmp)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy_refcnt"); + retcode = TCL_ERROR; + goto finished; + } + policy->policy_refcnt = tmp; + +finished: + free(argv); + *out_policy = policy; + return retcode; +} + + +static void free_policy_ent(ovsec_kadm_policy_ent_t *policy) +{ + free(*policy); + *policy = 0; +} + +static Tcl_DString *unparse_keytype(krb5_enctype enctype) +{ + Tcl_DString *str; + char buf[50]; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + switch (enctype) { + /* XXX is this right? */ + case ENCTYPE_NULL: Tcl_DStringAppend(str, "ENCTYPE_NULL", -1); break; + case ENCTYPE_DES_CBC_CRC: + Tcl_DStringAppend(str, "ENCTYPE_DES_CBC_CRC", -1); break; + default: + sprintf(buf, "UNKNOWN KEYTYPE (0x%x)", enctype); + Tcl_DStringAppend(str, buf, -1); + break; + } + + return str; +} + + +static Tcl_DString *unparse_keyblock(krb5_keyblock *keyblock) +{ + Tcl_DString *str; + Tcl_DString *keytype; + int i; + + if (! (str = malloc(sizeof(*str)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + + Tcl_DStringInit(str); + + keytype = unparse_keytype(keyblock->enctype); + Tcl_DStringAppendElement(str, keytype->string); + Tcl_DStringFree(keytype); + free(keytype); + if (keyblock->length == 0) { + Tcl_DStringAppendElement(str, "0x00"); + } + else { + Tcl_DStringAppendElement(str, "0x"); + for (i = 0; i < keyblock->length; i++) { + char buf[3]; + sprintf(buf, "%02x", (int) keyblock->contents[i]); + Tcl_DStringAppend(str, buf, -1); + } + } + + return str; +} + + + +int tcl_ovsec_kadm_init(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + ovsec_kadm_ret_t ret; + char *client_name, *pass, *service_name, *realm; + int tcl_ret; + krb5_ui_4 struct_version, api_version; + char *handle_var; + void *server_handle; + char *handle_name; + char *whoami = argv[0]; + + argv++, argc--; + + krb5_init_context(&context); + + if (argc != 7) { + Tcl_AppendResult(interp, whoami, ": ", arg_error, 0); + return TCL_ERROR; + } + + if (((tcl_ret = parse_str(interp, argv[0], &client_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[1], &pass)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[2], &service_name)) != TCL_OK) || + ((tcl_ret = parse_str(interp, argv[3], &realm)) != TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[4], (int *) &struct_version)) != + TCL_OK) || + ((tcl_ret = Tcl_GetInt(interp, argv[5], (int *) &api_version)) != + TCL_OK)) { + return tcl_ret; + } + + handle_var = argv[6]; + + if (! (handle_var && *handle_var)) { + Tcl_SetResult(interp, "must specify server handle variable name", + TCL_STATIC); + return TCL_ERROR; + } + + ret = ovsec_kadm_init(client_name, pass, service_name, realm, + struct_version, api_version, &server_handle); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = put_server_handle(interp, server_handle, &handle_name)) + != TCL_OK) { + return tcl_ret; + } + + if (! Tcl_SetVar(interp, handle_var, handle_name, TCL_LEAVE_ERR_MSG)) { + return TCL_ERROR; + } + + set_ok(interp, "OV Admin system initialized."); + return TCL_OK; +} + + + +int tcl_ovsec_kadm_destroy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + ovsec_kadm_ret_t ret; + int tcl_ret; + + GET_HANDLE(0, 0); + + ret = ovsec_kadm_destroy(server_handle); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + + if ((tcl_ret = remove_server_handle(interp, argv[-1])) != TCL_OK) { + return tcl_ret; + } + + set_ok(interp, "OV Admin system deinitialized."); + return TCL_OK; +} + +int tcl_ovsec_kadm_create_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + char *princ_string; + ovsec_kadm_principal_ent_t princ = 0; + krb5_int32 mask; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + + GET_HANDLE(3, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[2], &pw)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[3], &override_qual)) != + TCL_OK) { + retcode = tcl_ret; + goto finished; + } +#endif + +#ifdef OVERRIDE + ret = ovsec_kadm_create_principal(server_handle, princ, mask, pw, + override_qual); +#else + ret = ovsec_kadm_create_principal(server_handle, princ, mask, pw); +#endif + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Principal created."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + + +int tcl_ovsec_kadm_delete_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + int tcl_ret; + char *name; + + GET_HANDLE(1, 0); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal"); + return TCL_ERROR; + } + } else princ = NULL; + ret = ovsec_kadm_delete_principal(server_handle, princ); + + if(princ != NULL) + krb5_free_principal(context, princ); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Principal deleted."); + return TCL_OK; + } +} + + + +int tcl_ovsec_kadm_modify_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *princ_string; + ovsec_kadm_principal_ent_t princ = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + ovsec_kadm_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &princ_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing principal"); + return tcl_ret; + } + + if (princ_string && + ((tcl_ret = parse_principal_ent(interp, princ_string, &princ)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_principal_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_modify_principal(server_handle, princ, mask); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Principal modified."); + } + +finished: + if (princ) { + free_principal_ent(&princ); + } + return retcode; +} + + +int tcl_ovsec_kadm_rename_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal source, target; + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &source)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing source"); + return TCL_ERROR; + } + + if (krb5_ret = krb5_parse_name(context, argv[1], &target)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing target"); + krb5_free_principal(context, source); + return TCL_ERROR; + } + + ret = ovsec_kadm_rename_principal(server_handle, source, target); + + if (ret == OVSEC_KADM_OK) { + set_ok(interp, "Principal renamed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + + krb5_free_principal(context, source); + krb5_free_principal(context, target); + return retcode; +} + + + +int tcl_ovsec_kadm_chpass_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *pw; +#ifdef OVERRIDE + int override_qual; +#endif + krb5_error_code krb5_ret; + int tcl_ret; + int retcode = TCL_OK; + ovsec_kadm_ret_t ret; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing password"); + retcode = TCL_ERROR; + goto finished; + } + +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_chpass_principal(server_handle, + princ, pw, override_qual); +#else + ret = ovsec_kadm_chpass_principal(server_handle, princ, pw); +#endif + + if (ret == OVSEC_KADM_OK) { + set_ok(interp, "Password changed."); + goto finished; + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_ovsec_kadm_chpass_principal_util(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + char *new_pw; +#ifdef OVERRIDE + int override_qual; +#endif + char *pw_ret, *pw_ret_var; + char msg_ret[1024], *msg_ret_var; + krb5_error_code krb5_ret; + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(4, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &new_pw)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing new password"); + retcode = TCL_ERROR; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } +#endif + if ((tcl_ret = parse_str(interp, argv[3], &pw_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing pw_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + if ((tcl_ret = parse_str(interp, argv[4], &msg_ret_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing msg_ret variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_chpass_principal_util(server_handle, princ, new_pw, +#ifdef OVERRIDE + override_qual, +#endif + pw_ret_var ? &pw_ret : 0, + msg_ret_var ? msg_ret : 0); + + if (ret == OVSEC_KADM_OK) { + if (pw_ret_var && + (! Tcl_SetVar(interp, pw_ret_var, pw_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, "while setting pw_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + if (msg_ret_var && + (! Tcl_SetVar(interp, msg_ret_var, msg_ret, + TCL_LEAVE_ERR_MSG))) { + Tcl_AppendElement(interp, + "while setting msg_ret variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Password changed."); + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + return retcode; +} + + + +int tcl_ovsec_kadm_randkey_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_keyblock *keyblock; + char *keyblock_var; + Tcl_DString *keyblock_dstring = 0; +#ifdef OVERRIDE + int override_qual; +#endif + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + int tcl_ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 0); + + if (krb5_ret = krb5_parse_name(context, argv[0], &princ)) { + stash_error(interp, krb5_ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &keyblock_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing keyblock variable name"); + retcode = TCL_ERROR; + goto finished; + } +#ifdef OVERRIDE + if ((tcl_ret = Tcl_GetBoolean(interp, argv[2], &override_qual)) + != TCL_OK) { + Tcl_AppendElement(interp, "while parsing override_qual"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_randkey_principal(server_handle, + princ, keyblock_var ? &keyblock : 0, + override_qual); +#else + ret = ovsec_kadm_randkey_principal(server_handle, + princ, keyblock_var ? &keyblock : 0); +#endif + + if (ret == OVSEC_KADM_OK) { + if (keyblock_var) { + keyblock_dstring = unparse_keyblock(keyblock); + if (! Tcl_SetVar(interp, keyblock_var, + keyblock_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting keyblock variable"); + retcode = TCL_ERROR; + goto finished; + } + } + set_ok(interp, "Key randomized."); + + } + else { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + krb5_free_principal(context, princ); + if (keyblock_dstring) { + Tcl_DStringFree(keyblock_dstring); + free(keyblock_dstring); + } + return retcode; +} + + + +int tcl_ovsec_kadm_get_principal(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + ovsec_kadm_principal_ent_t ent; + Tcl_DString *ent_dstring = 0; + char *ent_var; + char *name; + krb5_error_code krb5_ret; + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 1); + + if((tcl_ret = parse_str(interp, argv[0], &name)) != TCL_OK) + return tcl_ret; + if(name != NULL) { + if (krb5_ret = krb5_parse_name(context, name, &princ)) { + stash_error(interp, ret); + Tcl_AppendElement(interp, "while parsing principal name"); + return TCL_ERROR; + } + } else princ = NULL; + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_get_principal(server_handle, princ, ent_var ? &ent : 0); + + if (ret == OVSEC_KADM_OK) { + if (ent_var) { + if (dostruct) { + char buf[20]; + int i = 1, newPtr = 0; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + sprintf(buf, "principal%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, + &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, ent); + if (! Tcl_SetVar(interp, ent_var, buf, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + Tcl_DeleteHashEntry(entry); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Principal structure retrieved."); + } + else { + ent_dstring = unparse_principal_ent(ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Principal retrieved."); + } + } + } + else { + ent = 0; + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if(princ != NULL) + krb5_free_principal(context, princ); + if (ent && ((! dostruct) || (retcode != TCL_OK))) { + if ((ret = ovsec_kadm_free_principal_ent(server_handle, ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + } + return retcode; +} + +int tcl_ovsec_kadm_create_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + char *policy_string; + ovsec_kadm_policy_ent_t policy = 0; + krb5_int32 mask; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = tcl_ret; + goto finished; + } + + ret = ovsec_kadm_create_policy(server_handle, policy, mask); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + goto finished; + } + else { + set_ok(interp, "Policy created."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + + +int tcl_ovsec_kadm_delete_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + krb5_principal princ; + krb5_error_code krb5_ret; + ovsec_kadm_ret_t ret; + char *policy; + int tcl_ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + ret = ovsec_kadm_delete_policy(server_handle, policy); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + set_ok(interp, "Policy deleted."); + return TCL_OK; + } +} + + + +int tcl_ovsec_kadm_modify_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *policy_string; + ovsec_kadm_policy_ent_t policy = 0; + int tcl_ret; + krb5_int32 mask; + int retcode = TCL_OK; + ovsec_kadm_ret_t ret; + + GET_HANDLE(2, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &policy_string)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy"); + return tcl_ret; + } + + if (policy_string && + ((tcl_ret = parse_policy_ent(interp, policy_string, &policy)) + != TCL_OK)) { + return tcl_ret; + } + + if ((tcl_ret = parse_policy_mask(interp, argv[1], &mask)) != TCL_OK) { + retcode = TCL_ERROR; + goto finished; + } + + ret = ovsec_kadm_modify_policy(server_handle, policy, mask); + + if (ret != OVSEC_KADM_OK) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + else { + set_ok(interp, "Policy modified."); + } + +finished: + if (policy) { + free_policy_ent(&policy); + } + return retcode; +} + + +int tcl_ovsec_kadm_get_policy(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + ovsec_kadm_policy_ent_t ent; + Tcl_DString *ent_dstring = 0; + char *policy; + char *ent_var; + int tcl_ret; + ovsec_kadm_ret_t ret; + int retcode = TCL_OK; + + GET_HANDLE(2, 1); + + if ((tcl_ret = parse_str(interp, argv[0], &policy)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing policy name"); + return TCL_ERROR; + } + + if ((tcl_ret = parse_str(interp, argv[1], &ent_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry variable name"); + return TCL_ERROR; + } + + ret = ovsec_kadm_get_policy(server_handle, policy, ent_var ? &ent : 0); + + if (ret == OVSEC_KADM_OK) { + if (ent_var) { + if (dostruct) { + char buf[20]; + int i = 1, newPtr = 0; + Tcl_HashEntry *entry; + + if (! struct_table) { + if (! (struct_table = + malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + do { + sprintf(buf, "policy%d", i); + entry = Tcl_CreateHashEntry(struct_table, buf, + &newPtr); + i++; + } while (! newPtr); + + Tcl_SetHashValue(entry, ent); + if (! Tcl_SetVar(interp, ent_var, buf, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + Tcl_DeleteHashEntry(entry); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Policy structure retrieved."); + } + else { + ent_dstring = unparse_policy_ent(ent); + if (! Tcl_SetVar(interp, ent_var, ent_dstring->string, + TCL_LEAVE_ERR_MSG)) { + Tcl_AppendElement(interp, + "while setting entry variable"); + retcode = TCL_ERROR; + goto finished; + } + set_ok(interp, "Policy retrieved."); + } + } + } + else { + ent = 0; + stash_error(interp, ret); + retcode = TCL_ERROR; + } + +finished: + if (ent_dstring) { + Tcl_DStringFree(ent_dstring); + free(ent_dstring); + } + if (ent && ((! dostruct) || (retcode != TCL_OK))) { + if ((ret = ovsec_kadm_free_policy_ent(server_handle, ent)) && + (retcode == TCL_OK)) { + stash_error(interp, ret); + retcode = TCL_ERROR; + } + } + return retcode; +} + + + +int tcl_ovsec_kadm_free_principal_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + ovsec_kadm_principal_ent_t ent; + int tcl_ret; + ovsec_kadm_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = ovsec_kadm_free_principal_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "principal", sizeof("principal")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "principal handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = ovsec_kadm_free_principal_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Principal freed."); + return TCL_OK; +} + + +int tcl_ovsec_kadm_free_policy_ent(ClientData clientData, + Tcl_Interp *interp, + int argc, char *argv[]) +{ + char *ent_name; + ovsec_kadm_policy_ent_t ent; + int tcl_ret; + ovsec_kadm_ret_t ret; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &ent_name)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing entry name"); + return TCL_ERROR; + } + + if ((! ent_name) && + (ret = ovsec_kadm_free_policy_ent(server_handle, 0))) { + stash_error(interp, ret); + return TCL_ERROR; + } + else { + Tcl_HashEntry *entry; + + if (strncmp(ent_name, "policy", sizeof("policy")-1)) { + Tcl_AppendResult(interp, "invalid principal handle \"", + ent_name, "\"", 0); + return TCL_ERROR; + } + if (! struct_table) { + if (! (struct_table = malloc(sizeof(*struct_table)))) { + fprintf(stderr, "Out of memory!\n"); + exit(1); /* XXX */ + } + Tcl_InitHashTable(struct_table, TCL_STRING_KEYS); + } + + if (! (entry = Tcl_FindHashEntry(struct_table, ent_name))) { + Tcl_AppendResult(interp, "policy handle \"", ent_name, + "\" not found", 0); + return TCL_ERROR; + } + + ent = Tcl_GetHashValue(entry); + + if (ret = ovsec_kadm_free_policy_ent(server_handle, ent)) { + stash_error(interp, ret); + return TCL_ERROR; + } + Tcl_DeleteHashEntry(entry); + } + set_ok(interp, "Policy freed."); + return TCL_OK; +} + + +int tcl_ovsec_kadm_get_privs(ClientData clientData, Tcl_Interp *interp, + int argc, char *argv[]) +{ + int tcl_ret; + char *set_ret; + ovsec_kadm_ret_t ret; + char *priv_var; + long privs; + + GET_HANDLE(1, 0); + + if ((tcl_ret = parse_str(interp, argv[0], &priv_var)) != TCL_OK) { + Tcl_AppendElement(interp, "while parsing privs variable name"); + return TCL_ERROR; + } + + ret = ovsec_kadm_get_privs(server_handle, priv_var ? &privs : 0); + + if (ret == OVSEC_KADM_OK) { + if (priv_var) { + Tcl_DString *str = unparse_privs(privs); + set_ret = Tcl_SetVar(interp, priv_var, str->string, + TCL_LEAVE_ERR_MSG); + Tcl_DStringFree(str); + free(str); + if (! set_ret) { + Tcl_AppendElement(interp, "while setting priv variable"); + return TCL_ERROR; + } + } + set_ok(interp, "Privileges retrieved."); + return TCL_OK; + } + else { + stash_error(interp, ret); + return TCL_ERROR; + } +} + + +void Tcl_ovsec_kadm_init(Tcl_Interp *interp) +{ + char buf[20]; + + Tcl_SetVar(interp, "OVSEC_KADM_ADMIN_SERVICE", + OVSEC_KADM_ADMIN_SERVICE, TCL_GLOBAL_ONLY); + Tcl_SetVar(interp, "OVSEC_KADM_CHANGEPW_SERVICE", + OVSEC_KADM_CHANGEPW_SERVICE, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_STRUCT_VERSION); + Tcl_SetVar(interp, "OVSEC_KADM_STRUCT_VERSION", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_API_VERSION_1); + Tcl_SetVar(interp, "OVSEC_KADM_API_VERSION_1", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_API_VERSION_MASK); + Tcl_SetVar(interp, "OVSEC_KADM_API_VERSION_MASK", buf, TCL_GLOBAL_ONLY); + (void) sprintf(buf, "%d", OVSEC_KADM_STRUCT_VERSION_MASK); + Tcl_SetVar(interp, "OVSEC_KADM_STRUCT_VERSION_MASK", buf, + TCL_GLOBAL_ONLY); + + Tcl_CreateCommand(interp, "ovsec_kadm_init", tcl_ovsec_kadm_init, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_destroy", tcl_ovsec_kadm_destroy, 0, + 0); + Tcl_CreateCommand(interp, "ovsec_kadm_create_principal", + tcl_ovsec_kadm_create_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_delete_principal", + tcl_ovsec_kadm_delete_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_modify_principal", + tcl_ovsec_kadm_modify_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_rename_principal", + tcl_ovsec_kadm_rename_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_chpass_principal", + tcl_ovsec_kadm_chpass_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_chpass_principal_util", + tcl_ovsec_kadm_chpass_principal_util, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_randkey_principal", + tcl_ovsec_kadm_randkey_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_get_principal", + tcl_ovsec_kadm_get_principal, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_create_policy", + tcl_ovsec_kadm_create_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_delete_policy", + tcl_ovsec_kadm_delete_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_modify_policy", + tcl_ovsec_kadm_modify_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_get_policy", + tcl_ovsec_kadm_get_policy, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_free_principal_ent", + tcl_ovsec_kadm_free_principal_ent, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_free_policy_ent", + tcl_ovsec_kadm_free_policy_ent, 0, 0); + Tcl_CreateCommand(interp, "ovsec_kadm_get_privs", + tcl_ovsec_kadm_get_privs, 0, 0); +} diff --git a/src/kadmin/testing/util/tcl_ovsec_kadm_syntax b/src/kadmin/testing/util/tcl_ovsec_kadm_syntax new file mode 100644 index 000000000..3fc77fbcb --- /dev/null +++ b/src/kadmin/testing/util/tcl_ovsec_kadm_syntax @@ -0,0 +1,57 @@ +Here's a brief summary of the syntax of the tcl versions of the +ovsec_kadm commands: + +string Can be a string or "null" which will turn into a null pointer +principal_ent A 12-field list in the order of the principal_ent + structure: {string number number number number string + number mask number number string mask} + It can also be "null", like a string, to indicate that + a null structure pointer should be used. +mask Either a number, representing the actual value of the + mask, or a sequence of symbols in a list. Example: + {PRINCIPAL ATTRIBUTES} is a valid principal mask. +boolean "1", "0", "true", "false", etc. +varname The name of a Tcl variable, or "null" to not assign. +policy_ent Similar to principal_ent, but with seven fields, + instead of 12. The first is a string, and the rest + are numbers. + +init + client_name:string pass:string service_name:string + realm:string struct_version:int api_version:int + server_handle_ret:varname +destroy + server_handle:string +create_principal + server_handle:string principal:principal_ent + mask:principal_mask password:string +delete_principal + server_handle:string name:string +modify_principal + server_handle:string principal_principal_ent + mask:principal_mask +rename_principal + server_handle:string source:string target:string +chpass_principal + server_handle:string name:string password:string +chpass_principal_util + server_handle:string name:string password:string + pw_ret:varname msg_ret:varname +randkey_principal + server_handle:string name:string keyblock_var:varname +get_principal [-struct] + server_handle:string name:string princ_var:varname +create_policy + server_handle:string policy:policy_ent mask:policy_mask +delete_policy + server_handle:string name:string +modify_policy + server_handle:string policy:policy_ent mask:policy_mask +get_policy [-struct] + server_handle:string name:string policy_var:varname +free_principal_ent + server_handle:string handle:string +free_policy_ent + server_handle:string handle:string +get_privs + server_handle:string privs:priv_var diff --git a/src/kadmin/testing/util/test.c b/src/kadmin/testing/util/test.c new file mode 100644 index 000000000..75a0fc25f --- /dev/null +++ b/src/kadmin/testing/util/test.c @@ -0,0 +1,32 @@ +#include <tcl.h> + +#define IS_TCL_7_5 ((TCL_MAJOR_VERSION * 100 + TCL_MINOR_VERSION) >= 705) + +#if IS_TCL_7_5 +int +main(argc, argv) + int argc; /* Number of command-line arguments. */ + char **argv; /* Values of command-line arguments. */ +{ + Tcl_Main(argc, argv, Tcl_AppInit); + return 0; /* Needed only to prevent compiler warning. */ +} +#else +/* + * The following variable is a special hack that allows applications + * to be linked using the procedure "main" from the Tcl library. The + * variable generates a reference to "main", which causes main to + * be brought in from the library (and all of Tcl with it). + */ + +extern int main(); +int *tclDummyMainPtr = (int *) main; +#endif + +int Tcl_AppInit(Tcl_Interp *interp) +{ + Tcl_ovsec_kadm_init(interp); + Tcl_kadm5_init(interp); + + return(TCL_OK); +} diff --git a/src/kadmin/v4server/ChangeLog b/src/kadmin/v4server/ChangeLog new file mode 100644 index 000000000..604014849 --- /dev/null +++ b/src/kadmin/v4server/ChangeLog @@ -0,0 +1,249 @@ +Thu Jul 18 19:46:49 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Tue Jul 9 17:18:56 1996 Marc Horowitz <marc@mit.edu> + + * kadm_stream.c: rename HAS_STDLIB_H to HAVE_STDLIB_H to conform + to the autoconf convention + * configure.in: the old configure.in seemed to be written for some + other directory. Now it's right. + * admin_server.c, kadm_ser_wrap.c, kadm_server.c: renamed + <ovsec_admin/foo.h> to <kadm5/foo.h> + * Makefile.in: complete rewrite. + +Thu Mar 21 20:33:43 1996 Richard Basch <basch@lehman.com> + + * kadm_funcs.c: new principals were being created with two keys, + one of which the key_data_ver=0 and had no valid data. + +Tue Mar 19 19:42:37 1996 Richard Basch <basch@lehman.com> + + * kadm_funcs.c: + changed all references of des-cbc-md5 to des-cbc-crc + fixed uninitialized variable + set kvno modulo 256 in database + +Wed Feb 21 23:34:31 1996 Richard Basch <basch@lehman.com> + + * kadm_funcs.c: Initialize the length element of the krb5_db_entry + structure in kadm_princ2entry (add_entry was failing). + +Wed Dec 13 03:51:53 1995 Chris Provenzano (proven@mit.edu) + + * kadm_funcs.c : Remove mkvno for krb5_db_entry + +Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu) + + * admin_server.c, kadm_funcs.c kadm_ser_wrap.c : + s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g + +Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu) + + * admin_server.c, kadm_funcs.c, kadm_ser_wrap.c : Remove krb5_enctype + references, and replace with krb5_keytype where appropriate. + +Tue Aug 15 14:31:37 EDT 1995 Paul Park (pjpark@mit.edu) + * admin_server,kadm_funcs,kadm_ser_wrap.c - Replace kadm_find_keytype() + with krb5_dbe_find_keytype(). + + +Thu Aug 10 14:48:26 EDT 1995 Paul Park (pjpark@mit.edu) + * kadm_funcs.c - Add kadm_find_keytype() to find a particular key/salt + pair. Use this to find keys instead of assuming that the + right one's in the first slot. + Fix transposed arguments to strncpy(). + Handle mod_princ_data stuff. + Supply saltblock to encrypt_key_data(). + * admin_server, kadm_ser_wrap.c - Use kadm_find_keytype() to find keys. + + +Mon Aug 7 13:30:46 EDT 1995 Paul Park (pjpark@mit.edu) + * admin_server,kadm_funcs,kadm_ser_wrap.c - Brute force substitutions + to get this to compile. + + +Mon Jul 17 15:12:30 EDT 1995 Paul Park (pjpark@mit.edu) + * kadm_ser_wrap.c - Add NULL stash file argument to krb5_db_fetch_mkey. + + +Fri Jul 7 16:05:11 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Remove all explicit library handling and LDFLAGS. + * configure.in - Add USE_<mumble> and KRB5_LIBRARIES. + + +Tue Jun 27 16:05:27 EDT 1995 Paul Park (pjpark@mit.edu) + * acl_files.c - Change check for return value from fputs(3) from NULL + to EOF. That's what's returned on error. + * admin_server.c - Cast 4th argument of setsockopt(2) to be const char * + * kadm_funcs.c - Cast argument to ctime(3) + * kadm_server.c - Cast first argument to strcpy(3) and strcat(3). + +Tue Jun 20 14:44:54 1995 Tom Yu (tlyu@dragons-lair) + + * configure.in: add tests for TIME_WITH_SYS_TIME and sys/time.h + +Thu Jun 15 17:52:29 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Change explicit library names to -l<lib> form, and + change target link line to use $(LD) and associated flags. + Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these were + split out. + * configure.in - Add shared library usage check. + +Fri Jun 9 19:07:25 1995 <tytso@rsx-11.mit.edu> + + * configure.in: Remove standardized set of autoconf macros, which + are now handled by CONFIG_RULES. + +Fri Jun 9 06:49:36 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kadm_stream.c (vts_long, stv_long): Change u_long to krb5_ui_4 + + * kadm_server.c (kadm_ser_ckpw): Change u_long to krb5_ui_4 + + * kadm_ser_wrap.c (errpkt, kadm_ser_in): Change u_long to krb5_ui_4 + + * kadm_funcs.c (kadm_add_entry): Change u_long to krb5_ui_4 + + * admin_server.c (process_client): Change u_long to krb5_ui_4 + +Sat May 20 22:33:58 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kadm_stream.c: Based on presence of stdlib.h, include or declare + malloc. + + * configure.in: Check for stdlib.h + +Sun May 7 13:49:54 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * admin_server.c: Avoid warning of redeclaring POSIX_SIGNALS if + already defined. + +Sat Apr 29 00:34:01 1995 Theodore Y. Ts'o <tytso@dcl> + + * admin_server.c (kadm_listen): Use Posix sigaction() instead of + signal() to set signal handlers. This allows us not to + worry about System V signal semantics. Make the code use + POSIX_SIGNALS by default. + +Fri Apr 28 18:08:05 1995 Mark Eichin <eichin@cygnus.com> + + * Makefile.in (KLIB): put KRB4_LIB inside KLIB. + +Thu Apr 27 13:53:41 1995 Mark Eichin <eichin@cygnus.com> + + * Makefile.in (v4kadmind): use KRB4_LIB directly. + +Thu Apr 20 23:21:42 1995 Theodore Y. Ts'o (tytso@dcl) + + * kadm_funcs.c: Don't #include <ndbm.h>, since that's + automatically included by k5-config.h + +Thu Apr 20 15:26:48 1995 Ezra Peisach (epeisach@kangaroo.mit.edu) + + * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): krb_int32 should be + krb5_int32. + + * acl_files.c: Declare acl_abort as static at top of file. + +Sun Apr 16 19:10:17 1995 Mark Eichin <eichin@cygnus.com> + + * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): use krb_int32, not + long, for network 4 byte quantities. Should get rid of the + use of memcpy at some point. + +Sat Mar 25 16:59:55 1995 Mark Eichin <eichin@cygnus.com> + + * kadm_funcs.c (kadm_entry2princ): pass kadm_context in to + krb5_524_conv_principal. + +Tue Mar 14 16:45:18 1995 <tytso@rsx-11.mit.edu> + + * Makefile.in: Don't link in the V4 DES library; use the des425 + library to avoid linking the DES code in twice. + +Thu Mar 2 12:25:13 1995 Theodore Y. Ts'o <tytso@dcl> + + * Makefile.in (ISODELIB): Remove reference to $(ISODELIB). + +Wed Mar 1 16:30:08 1995 Theodore Y. Ts'o <tytso@dcl> + + * kadm_server.c: Remove declataions of malloc(); should be done by + header files. + + * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket + and -lnsl with WITH_NETLIB check. + +Tue Feb 28 02:24:56 1995 John Gilmore (gnu at toad.com) + + * admin_server.c, kadm_server.c, kadm-server.h: Avoid + <krb5/...> includes. + +Tue Feb 7 16:42:54 1995 Mark Eichin <eichin@cygnus.com> + + * kadm_funcs.c (kadm_del_entry): fixed call to db_delete_principal. + +Wed Jan 25 18:42:42 1995 Mark Eichin (eichin@tweedledumber.cygnus.com) + + * kadm_server.h (DEL_ACL_FILE): new define, acl file for V4 delete + function. + * kadm_server.c (kadm_ser_add): new function, wrapper for V4 delete. + * kadm_funcs.c (check_access): declare int; add DEL. + (kadm_del_entry): new function, V4 delete from CNS. + (failadd): fix spelling error in log entry. + +Mon Dec 12 13:21:48 1994 Mark Eichin (eichin@cygnus.com) + + * kadm_funcs.c (kadm_entry2princ, kadm_princ2entry, + kadm_chg_srvtab): V4 and V5 max_life are in *different units* so + use the 60*5 conversion factor. + +Fri Nov 18 15:51:11 1994 Theodore Y. Ts'o (tytso@dcl) + + * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change): Add + magic numbers of keyblock structre. + +Fri Nov 18 01:11:58 1994 Mark Eichin <eichin@cygnus.com> + + * configure.in: use CHECK_SIGNALS instead of expansion (from + epeisach). + +Wed Oct 19 18:53:45 1994 Theodore Y. Ts'o (tytso@dcl) + + * kadm_ser_wrap.c (kadm_ser_init): Use krb5_use_cstype() to + initialize the master_encblock structure. + +Thu Sep 29 22:41:20 1994 Theodore Y. Ts'o (tytso@dcl) + + * Makefile.in: relink executable if libraries change + +Thu Sep 15 10:53:37 1994 Theodore Y. Ts'o (tytso@dcl) + + * admin_server.c (close_syslog, byebye): Move these two functions + before main(), so that they get declared properly. Otherwise + suncc will refuse to compile the file. + + * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change, + kadm_chg_srvtab): use krb5_timeofday instead of time(0). + +Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair) + + * admin_server.c: pick up <sys/time.h> (needed to get FD_SET, + etc.) + +Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair) + + * Makefile.in: no longer trying to install v4kadmind as krb5kdc + :-) + * configure.in: another try at making dbm libs dtrt + +Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair) + + * admin_server.c: fixed calls that should have invoked + krb5_init_ets + +Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair) + + * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to + krb5_build_principal + diff --git a/src/kadmin/v4server/Makefile.in b/src/kadmin/v4server/Makefile.in new file mode 100644 index 000000000..01500167b --- /dev/null +++ b/src/kadmin/v4server/Makefile.in @@ -0,0 +1,23 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) \ + -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 -DNEED_SOCKETS + +LOCALINCLUDE = -I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I. + +PROG = kadmind4 +OBJS = kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o kadm_err.o + +all:: $(PROG) + +kadm_err.c kadm_err.h: $(srcdir)/kadm_err.et + +$(OBJS): kadm_err.h + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/v4server/Makefile.ov b/src/kadmin/v4server/Makefile.ov new file mode 100644 index 000000000..a365e8ea7 --- /dev/null +++ b/src/kadmin/v4server/Makefile.ov @@ -0,0 +1,42 @@ +TOP = .. +include $(TOP)/config.mk/template + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS) +endif + +ETABLES = kadm_err.et +expand ErrorTables + +depend:: kadm_err.h + +PROG := ovsec_v4adm_server + +SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \ + kadm_stream.c kadm_supp.c acl_files.c kadm_err.c + +OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o kadm_err.o + +LIBS := $(LIBADMCLNT) $(LIBRPCLIB) \ + $(LIBKADM) $(LIBKRB) $(LIBDES425) \ + $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5_ALL) \ + $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB) + +ifdef WAIT_USES_INT +WAIT_FLAGS = -DWAIT_USES_INT +endif +ifdef OPEN_NEEDS_FCNTL +FCNTL_FLAGS = -DNEED_SYS_FCNTL_H +endif + +CFLAGS := -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 \ + $(WAIT_FLAGS) $(FCNLT_FLAGS) -I. \ + -I$(TOP)/../include/kerberosIV -I$(TOP)/../../src/include/kerberosIV \ + $(CFLAGS) + +expand InstallServer +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/v4server/acl_files.c b/src/kadmin/v4server/acl_files.c new file mode 100644 index 000000000..ae3b0c6bf --- /dev/null +++ b/src/kadmin/v4server/acl_files.c @@ -0,0 +1,536 @@ +/* + * kadmin/v4server/acl_files.c + * + * Copyright 1987,1989 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + */ + + +/*** Routines for manipulating access control list files ***/ + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <ctype.h> +#include <fcntl.h> +#include "krb.h" + +#ifndef KRB_REALM +#define KRB_REALM "ATHENA.MIT.EDU" +#endif + +/* "aname.inst@realm" */ +#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) +#define INST_SEP '.' +#define REALM_SEP '@' + +#define LINESIZE 2048 /* Maximum line length in an acl file */ + +#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ +#define WAIT_TIME 300 /* Maximum time allowed write acl file */ + +#define CACHED_ACLS 8 /* How many acls to cache */ + /* Each acl costs 1 open file descriptor */ +#define ACL_LEN 16 /* Twice a reasonable acl length */ + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define COR(a,b) ((a!=NULL)?(a):(b)) + +extern int errno; + +extern char *malloc(), *calloc(); +extern time_t time(); + +static int acl_abort(); + +/* Canonicalize a principal name */ +/* If instance is missing, it becomes "" */ +/* If realm is missing, it becomes the local realm */ +/* Canonicalized form is put in canon, which must be big enough to hold + MAX_PRINCIPAL_SIZE characters */ +void acl_canonicalize_principal(principal, canon) +char *principal; +char *canon; +{ + char *dot, *atsign, *end; + int len; + + dot = strchr(principal, INST_SEP); + atsign = strchr(principal, REALM_SEP); + + /* Maybe we're done already */ + if(dot != NULL && atsign != NULL) { + if(dot < atsign) { + /* It's for real */ + /* Copy into canon */ + strncpy(canon, principal, MAX_PRINCIPAL_SIZE); + canon[MAX_PRINCIPAL_SIZE-1] = '\0'; + return; + } else { + /* Nope, it's part of the realm */ + dot = NULL; + } + } + + /* No such luck */ + end = principal + strlen(principal); + + /* Get the principal name */ + len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); + strncpy(canon, principal, len); + canon += len; + + /* Add INST_SEP */ + *canon++ = INST_SEP; + + /* Get the instance, if it exists */ + if(dot != NULL) { + ++dot; + len = MIN(INST_SZ, COR(atsign, end) - dot); + strncpy(canon, dot, len); + canon += len; + } + + /* Add REALM_SEP */ + *canon++ = REALM_SEP; + + /* Get the realm, if it exists */ + /* Otherwise, default to local realm */ + if(atsign != NULL) { + ++atsign; + len = MIN(REALM_SZ, end - atsign); + strncpy(canon, atsign, len); + canon += len; + *canon++ = '\0'; + } else if(krb_get_lrealm(canon, 1) != KSUCCESS) { + strcpy(canon, KRB_REALM); + } +} + +/* Get a lock to modify acl_file */ +/* Return new FILE pointer */ +/* or NULL if file cannot be modified */ +/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ +static FILE *acl_lock_file(acl_file) +char *acl_file; +{ + struct stat s; + char new[LINESIZE]; + int nfd; + FILE *nf; + int mode; + + if(stat(acl_file, &s) < 0) return(NULL); + mode = s.st_mode; + sprintf(new, NEW_FILE, acl_file); + for(;;) { + /* Open the new file */ + if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { + if(errno == EEXIST) { + /* Maybe somebody got here already, maybe it's just old */ + if(stat(new, &s) < 0) return(NULL); + if(time(0) - s.st_ctime > WAIT_TIME) { + /* File is stale, kill it */ + unlink(new); + continue; + } else { + /* Wait and try again */ + sleep(1); + continue; + } + } else { + /* Some other error, we lose */ + return(NULL); + } + } + + /* If we got to here, the lock file is ours and ok */ + /* Reopen it under stdio */ + if((nf = fdopen(nfd, "w")) == NULL) { + /* Oops, clean up */ + unlink(new); + } + return(nf); + } +} + +/* Commit changes to acl_file written onto FILE *f */ +/* Returns zero if successful */ +/* Returns > 0 if lock was broken */ +/* Returns < 0 if some other error occurs */ +/* Closes f */ +static int acl_commit(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + sprintf(new, NEW_FILE, acl_file); + if(fflush(f) < 0 + || fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + acl_abort(acl_file, f); + return(-1); + } + + ret = rename(new, acl_file); + fclose(f); + return(ret); +} + +/* Abort changes to acl_file written onto FILE *f */ +/* Returns 0 if successful, < 0 otherwise */ +/* Closes f */ +static int acl_abort(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + /* make sure we aren't nuking someone else's file */ + if(fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + fclose(f); + return(-1); + } else { + sprintf(new, NEW_FILE, acl_file); + ret = unlink(new); + fclose(f); + return(ret); + } +} + +/* Initialize an acl_file */ +/* Creates the file with permissions perm if it does not exist */ +/* Erases it if it does */ +/* Returns return value of acl_commit */ +int acl_initialize(acl_file, perm) +char *acl_file; +int perm; +{ + FILE *new; + int fd; + + /* Check if the file exists already */ + if((new = acl_lock_file(acl_file)) != NULL) { + return(acl_commit(acl_file, new)); + } else { + /* File must be readable and writable by owner */ + if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { + return(-1); + } else { + close(fd); + return(0); + } + } +} + +/* Eliminate all whitespace character in buf */ +/* Modifies its argument */ +static nuke_whitespace(buf) +char *buf; +{ + register char *pin, *pout; + + for(pin = pout = buf; *pin != '\0'; pin++) + if(!isspace(*pin)) *pout++ = *pin; + *pout = '\0'; /* Terminate the string */ +} + +/* Hash table stuff */ + +struct hashtbl { + int size; /* Max number of entries */ + int entries; /* Actual number of entries */ + char **tbl; /* Pointer to start of table */ +}; + +/* Make an empty hash table of size s */ +static struct hashtbl *make_hash(size) +int size; +{ + struct hashtbl *h; + + if(size < 1) size = 1; + h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); + h->size = size; + h->entries = 0; + h->tbl = (char **) calloc(size, sizeof(char *)); + return(h); +} + +/* Destroy a hash table */ +static destroy_hash(h) +struct hashtbl *h; +{ + int i; + + for(i = 0; i < h->size; i++) { + if(h->tbl[i] != NULL) free(h->tbl[i]); + } + free(h->tbl); + free(h); +} + +/* Compute hash value for a string */ +static unsigned hashval(s) +register char *s; +{ + register unsigned hv; + + for(hv = 0; *s != '\0'; s++) { + hv ^= ((hv << 3) ^ *s); + } + return(hv); +} + +/* Add an element to a hash table */ +static add_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + char *s; + char **old; + int i; + + /* Make space if it isn't there already */ + if(h->entries + 1 > (h->size >> 1)) { + old = h->tbl; + h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); + for(i = 0; i < h->size; i++) { + if(old[i] != NULL) { + hv = hashval(old[i]) % (h->size << 1); + while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); + h->tbl[hv] = old[i]; + } + } + h->size = h->size << 1; + free(old); + } + + hv = hashval(el) % h->size; + while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; + s = malloc(strlen(el)+1); + strcpy(s, el); + h->tbl[hv] = s; + h->entries++; +} + +/* Returns nonzero if el is in h */ +static check_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + + for(hv = hashval(el) % h->size; + h->tbl[hv] != NULL; + hv = (hv + 1) % h->size) { + if(!strcmp(h->tbl[hv], el)) return(1); + } + return(0); +} + +struct acl { + char filename[LINESIZE]; /* Name of acl file */ + int fd; /* File descriptor for acl file */ + struct stat status; /* File status at last read */ + struct hashtbl *acl; /* Acl entries */ +}; + +static struct acl acl_cache[CACHED_ACLS]; + +static int acl_cache_count = 0; +static int acl_cache_next = 0; + +/* Returns < 0 if unsuccessful in loading acl */ +/* Returns index into acl_cache otherwise */ +/* Note that if acl is already loaded, this is just a lookup */ +static int acl_load(name) +char *name; +{ + int i; + FILE *f; + struct stat s; + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) { + if(!strcmp(acl_cache[i].filename, name) + && acl_cache[i].fd >= 0) goto got_it; + } + + /* It isn't, load it in */ + /* maybe there's still room */ + if(acl_cache_count < CACHED_ACLS) { + i = acl_cache_count++; + } else { + /* No room, clean one out */ + i = acl_cache_next; + acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; + close(acl_cache[i].fd); + if(acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + } + + /* Set up the acl */ + strcpy(acl_cache[i].filename, name); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + /* Force reload */ + acl_cache[i].acl = (struct hashtbl *) 0; + + got_it: + /* + * See if the stat matches + * + * Use stat(), not fstat(), as the file may have been re-created by + * acl_add or acl_delete. If this happens, the old inode will have + * no changes in the mod-time and the following test will fail. + */ + if(stat(acl_cache[i].filename, &s) < 0) return(-1); + if(acl_cache[i].acl == (struct hashtbl *) 0 + || s.st_nlink != acl_cache[i].status.st_nlink + || s.st_mtime != acl_cache[i].status.st_mtime + || s.st_ctime != acl_cache[i].status.st_ctime) { + /* Gotta reload */ + if(acl_cache[i].fd >= 0) close(acl_cache[i].fd); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1); + if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = make_hash(ACL_LEN); + while(fgets(buf, sizeof(buf), f) != NULL) { + nuke_whitespace(buf); + acl_canonicalize_principal(buf, canon); + add_hash(acl_cache[i].acl, canon); + } + fclose(f); + acl_cache[i].status = s; + } + return(i); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Principal is not canonicalized, and no wildcarding is done */ +acl_exact_match(acl, principal) +char *acl; +char *principal; +{ + int idx; + + return((idx = acl_load(acl)) >= 0 + && check_hash(acl_cache[idx].acl, principal)); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Recognizes wildcards in acl of the form + name.*@realm, *.*@realm, and *.*@* */ +acl_check(acl, principal) +char *acl; +char *principal; +{ + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + char *realm, *tmp; + + acl_canonicalize_principal(principal, canon); + + /* Is it there? */ + if(acl_exact_match(acl, canon)) return(1); + + /* Try the wildcards */ + realm = strchr(canon, REALM_SEP); + tmp = strchr(canon, INST_SEP); + *tmp = '\0'; /* Chuck the instance */ + + sprintf(buf, "%s.*%s", canon, realm); + if(acl_exact_match(acl, buf)) return(1); + + sprintf(buf, "*.*%s", realm); + if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); + + return(0); +} + +/* Adds principal to acl */ +/* Wildcards are interpreted literally */ +acl_add(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL) { + if(fputs(acl_cache[idx].acl->tbl[i], new) == EOF + || putc('\n', new) != '\n') { + acl_abort(acl, new); + return(-1); + } + } + } + fputs(canon, new); + putc('\n', new); + return(acl_commit(acl, new)); +} + +/* Removes principal from acl */ +/* Wildcards are interpreted literally */ +acl_delete(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((!acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL + && strcmp(acl_cache[idx].acl->tbl[i], canon)) { + fputs(acl_cache[idx].acl->tbl[i], new); + putc('\n', new); + } + } + return(acl_commit(acl, new)); +} + diff --git a/src/kadmin/v4server/acl_files.doc b/src/kadmin/v4server/acl_files.doc new file mode 100644 index 000000000..78c448a6d --- /dev/null +++ b/src/kadmin/v4server/acl_files.doc @@ -0,0 +1,107 @@ +PROTOTYPE ACL LIBRARY + +Introduction + +An access control list (ACL) is a list of principals, where each +principal is is represented by a text string which cannot contain +whitespace. The library allows application programs to refer to named +access control lists to test membership and to atomically add and +delete principals using a natural and intuitive interface. At +present, the names of access control lists are required to be Unix +filenames, and refer to human-readable Unix files; in the future, when +a networked ACL server is implemented, the names may refer to a +different namespace specific to the ACL service. + + +Usage + +cc <files> -lacl -lkrb. + + + +Principal Names + +Principal names have the form + +<name>[.<instance>][@<realm>] + +e.g. + +asp +asp.root +asp@ATHENA.MIT.EDU +asp.@ATHENA.MIT.EDU +asp.root@ATHENA.MIT.EDU + +It is possible for principals to be underspecified. If instance is +missing, it is assumed to be "". If realm is missing, it is assumed +to be local_realm. The canonical form contains all of name, instance, +and realm; the acl_add and acl_delete routines will always +leave the file in that form. Note that the canonical form of +asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU. + + +Routines + +acl_canonicalize_principal(principal, buf) +char *principal; +char *buf; /*RETVAL*/ + +Store the canonical form of principal in buf. Buf must contain enough +space to store a principal, given the limits on the sizes of name, +instance, and realm specified in /usr/include/krb.h. + +acl_check(acl, principal) +char *acl; +char *principal; + +Returns nonzero if principal appears in acl. Returns 0 if principal +does not appear in acl, or if an error occurs. Canonicalizes +principal before checking, and allows the ACL to contain wildcards. + +acl_exact_match(acl, principal) +char *acl; +char *principal; + +Like acl_check, but does no canonicalization or wildcarding. + +acl_add(acl, principal) +char *acl; +char *principal; + +Atomically adds principal to acl. Returns 0 if successful, nonzero +otherwise. It is considered a failure if principal is already in acl. +This routine will canonicalize principal, but will treat wildcards +literally. + +acl_delete(acl, principal) +char *acl; +char *principal; + +Atomically deletes principal from acl. Returns 0 if successful, +nonzero otherwise. It is consider a failure if principal is not +already in acl. This routine will canonicalize principal, but will +treat wildcards literally. + +acl_initialize(acl, mode) +char *acl; +int mode; + +Initialize acl. If acl file does not exist, creates it with mode +mode. If acl exists, removes all members. Returns 0 if successful, +nonzero otherwise. WARNING: Mode argument is likely to change with +the eventual introduction of an ACL service. + + +Known problems + +In the presence of concurrency, there is a very small chance that +acl_add or acl_delete could report success even though it would have +had no effect. This is a necessary side effect of using lock files +for concurrency control rather than flock(2), which is not supported +by NFS. + +The current implementation caches ACLs in memory in a hash-table +format for increased efficiency in checking membership; one effect of +the caching scheme is that one file descriptor will be kept open for +each ACL cached, up to a maximum of 8. diff --git a/src/kadmin/v4server/admin_server.c b/src/kadmin/v4server/admin_server.c new file mode 100644 index 000000000..7a207d7c5 --- /dev/null +++ b/src/kadmin/v4server/admin_server.c @@ -0,0 +1,684 @@ +/* + * kadmin/v4server/admin_server.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Top-level loop of the kerberos Administration server + */ + +#include <mit-copyright.h> +/* + admin_server.c + this holds the main loop and initialization and cleanup code for the server +*/ + +#ifdef _AIX +#include <sys/select.h> +#endif + +/* define it for now */ +#ifndef POSIX_SIGNALS +#define POSIX_SIGNALS +#endif + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifndef POSIX_SIGNALS +#ifndef sigmask +#define sigmask(m) (1 <<((m)-1)) +#endif +#endif /* POSIX_SIGNALS */ +#include <sys/wait.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <syslog.h> + +#ifdef OVSEC_KADM +#include <kadm5/admin.h> +void *ovsec_handle; +kadm5_config_params params; +#endif + +#include "k5-int.h" +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include "com_err.h" +#include "kadm_server.h" + +#ifdef POSIX_SIGTYPE +#define SIGNAL_RETURN return +#else +#define SIGNAL_RETURN return(0) +#endif + +/* Almost all procs and such need this, so it is global */ +admin_params prm; /* The command line parameters struct */ + +char prog[32]; /* WHY IS THIS NEEDED??????? */ +char *progname = prog; +char *acldir = DEFAULT_ACL_DIR; +char krbrlm[REALM_SZ]; +extern Kadm_Server server_parm; +krb5_context kadm_context; +int debug; + +/* close the system log file */ +void close_syslog() +{ + syslog(LOG_INFO, "Shutting down V4 admin server"); +} + +void byebye() /* say goodnight gracie */ +{ + printf("Admin Server (kadm server) has completed operation.\n"); +} + +/* +** Main does the logical thing, it sets up the database and RPC interface, +** as well as handling the creation and maintenance of the syslog file... +*/ +main(argc, argv) /* admin_server main routine */ +int argc; +char *argv[]; +{ + int errval; + int c; + char *lrealm; + extern char *optarg; + extern int fascist_cpw; + +#ifdef OVSEC_KADM + memset(¶ms, 0, sizeof(params)); +#endif + + krb5_init_context(&kadm_context); + krb5_init_ets(kadm_context); + initialize_kadm_error_table(); + prog[sizeof(prog)-1]='\0'; /* Terminate... */ + (void) strncpy(prog, argv[0], sizeof(prog)-1); + + /* initialize the admin_params structure */ + prm.sysfile = KADM_SYSLOG; /* default file name */ + prm.inter = 1; + + memset(krbrlm, 0, sizeof(krbrlm)); + + fascist_cpw = 1; /* by default, enable fascist mode */ + while ((c = getopt(argc, argv, "Df:hnd:a:r:FN")) != EOF) + switch(c) { + case 'D': + debug++; + break; + case 'f': /* Syslog file name change */ + prm.sysfile = optarg; + break; + case 'n': + prm.inter = 0; + break; + case 'a': /* new acl directory */ + acldir = optarg; + break; + case 'd': +#ifdef OVSEC_KADM + params.dbname = optarg; + params.mask |= KADM5_CONFIG_DBNAME; +#else + if (errval = krb5_db_set_name(kadm_context, optarg)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } +#endif + break; + case 'F': + fascist_cpw++; + break; + case 'N': + fascist_cpw = 0; + break; + case 'r': + (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1); + break; + case 'h': /* get help on using admin_server */ + default: + printf("Usage: admin_server [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n"); + exit(-1); /* failure */ + } + + if (krbrlm[0] == 0) { + if (errval = krb5_get_default_realm(kadm_context, &lrealm)) { + com_err(argv[0], errval, "while attempting to get local realm"); + exit(1); + } + (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1); + } + +#ifdef OVSEC_KADM + params.realm = krbrlm; + params.mask |= KADM5_CONFIG_REALM; + + if (errval = kadm5_get_config_params(kadm_context, NULL, NULL, + ¶ms, ¶ms)) { + com_err(argv[0], errval, "while retrieving kadm5 params"); + exit(1); + } + if (errval = krb5_db_set_name(kadm_context, params.dbname)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } +#endif /* OVSEC_KADM */ + + printf("KADM Server %s initializing\n",KADM_VERSTR); + printf("Please do not use 'kill -9' to kill this job, use a\n"); + printf("regular kill instead\n\n"); + +#ifdef OVSEC_KADM + printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n", + debug ? "" : "parent ", getpid()); +#else + printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST"); +#endif + + openlog(argv[0], LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */ + syslog(LOG_INFO, "V4 admin server starting"); + + errval = krb5_db_init(kadm_context); /* Open the Kerberos database */ + if (errval) { + fprintf(stderr, "error: krb5_db_init() failed"); + close_syslog(); + byebye(); + exit(1); + } + if (errval = krb5_db_set_lockmode(kadm_context, TRUE)) { + com_err(argv[0], errval, "while setting db to nonblocking"); + close_syslog(); + byebye(); + exit(1); + } + /* set up the server_parm struct */ + if ((errval = kadm_ser_init(prm.inter, krbrlm +#ifdef OVSEC_KADM + , ¶ms +#endif + ))==KADM_SUCCESS) { + krb5_db_fini(kadm_context); /* Close the Kerberos database-- + will re-open later */ + errval = kadm_listen(); /* listen for calls to server from + clients */ + } + if (errval != KADM_SUCCESS) { + fprintf(stderr,"error: %s\n",error_message(errval)); + krb5_db_fini(kadm_context); /* Close if error */ + } + close_syslog(); /* Close syslog file, print + closing note */ + byebye(); /* Say bye bye on the terminal + in use */ + return 0; +} /* procedure main */ + + +static void clear_secrets() +{ + krb5_finish_key(kadm_context, &server_parm.master_encblock); + memset((char *)&server_parm.master_encblock, 0, + sizeof (server_parm.master_encblock)); + memset((char *)server_parm.master_keyblock.contents, 0, + server_parm.master_keyblock.length); + server_parm.mkvno = 0L; + return; +} + +static exit_now = 0; + +krb5_sigtype +doexit(sig) + int sig; +{ + exit_now = 1; + SIGNAL_RETURN; +} + +unsigned pidarraysize = 0; +int *pidarray = (int *)0; +int unknown_child = 0; + +/* +kadm_listen +listen on the admin servers port for a request +*/ +kadm_listen() +{ + extern int errno; + int found; + int admin_fd; + int peer_fd; + fd_set mask, readfds; + struct sockaddr_in peer; + int addrlen; + void process_client(), kill_children(); + int pid; + krb5_sigtype do_child(); +#ifdef POSIX_SIGNALS + struct sigaction new_act; + + new_act.sa_handler = doexit; + sigemptyset(&new_act.sa_mask); + sigaction(SIGINT, &new_act, 0); + sigaction(SIGTERM, &new_act, 0); + sigaction(SIGHUP, &new_act, 0); + sigaction(SIGQUIT, &new_act, 0); + sigaction(SIGALRM, &new_act, 0); + new_act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &new_act, 0); + new_act.sa_handler = do_child; + sigaction(SIGCHLD, &new_act, 0); +#else + signal(SIGINT, doexit); + signal(SIGTERM, doexit); + signal(SIGHUP, doexit); + signal(SIGQUIT, doexit); + signal(SIGPIPE, SIG_IGN); /* get errors on write() */ + signal(SIGALRM, doexit); + signal(SIGCHLD, do_child); +#endif + + if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return KADM_NO_SOCK; + if (debug) { + int one = 1; + if (setsockopt(admin_fd, SOL_SOCKET, SO_REUSEADDR, &one, + sizeof(int)) < 0) { + syslog(LOG_ERR, "setsockopt: %m"); + return KADM_NO_BIND; + } + } + if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr, + sizeof(struct sockaddr_in)) < 0) + return KADM_NO_BIND; + (void) listen(admin_fd, 1); + FD_ZERO(&mask); + FD_SET(admin_fd, &mask); + + for (;;) { /* loop nearly forever */ + if (exit_now) { + clear_secrets(); + kill_children(); + return(0); + } + readfds = mask; + if ((found = select(admin_fd+1,&readfds,(fd_set *)0, + (fd_set *)0, (struct timeval *)0)) == 0) + continue; /* no things read */ + if (found < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "select: %s", error_message(errno)); + continue; + } + if (FD_ISSET(admin_fd, &readfds)) { + /* accept the conn */ + addrlen = sizeof(peer); + if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer, + &addrlen)) < 0) { + syslog(LOG_ERR, "accept: %s", error_message(errno)); + continue; + } + + if (debug) { + process_client(peer_fd, &peer); + } else if (pid = fork()) { + /* parent */ + if (pid < 0) { + syslog(LOG_ERR, "fork: %s", error_message(errno)); + (void) close(peer_fd); + continue; + } + /* fork succeeded: keep tabs on child */ + (void) close(peer_fd); + if (unknown_child != pid) { + if (pidarray) { + pidarray = (int *)realloc((char *)pidarray, + (++pidarraysize * sizeof(int))); + pidarray[pidarraysize-1] = pid; + } else { + pidarray = (int *)malloc((pidarraysize = 1) * sizeof(int)); + pidarray[0] = pid; + } + } /* End if unknown_child != pid.*/ + } else { + /* child */ + (void) close(admin_fd); + process_client(peer_fd, &peer); + } + } else { + syslog(LOG_ERR, "something else woke me up!"); + return(0); + } + } + /*NOTREACHED*/ +} + +void process_client(fd, who) + int fd; + struct sockaddr_in *who; +{ + u_char *dat; + int dat_len; + u_short dlen; + int retval; + int on = 1; + int nentries = 1; + krb5_db_entry sprinc_entries; + krb5_boolean more; + krb5_keyblock cpw_skey; + krb5_key_data *kdatap; + int status; + +#ifdef OVSEC_KADM +#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab" + char *service_name; + + service_name = (char *) malloc(strlen(server_parm.sname) + + strlen(server_parm.sinst) + + strlen(server_parm.krbrlm) + 3); + if (service_name == NULL) { + syslog(LOG_ERR, "error: out of memory allocating service name"); + cleanexit(1); + } + sprintf(service_name, "%s/%s@%s", server_parm.sname, + server_parm.sinst, server_parm.krbrlm); + + retval = ovsec_kadm_init_with_skey(service_name, + OVSEC_KADM_SRVTAB, + OVSEC_KADM_ADMIN_SERVICE, krbrlm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_handle); + if (retval) { + syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s", + error_message(retval)); + cleanexit(1); + } + free(service_name); + + if (retval = krb5_db_set_name(kadm_context, params.dbname)) { + syslog(LOG_ERR, "%s while setting dbname", error_message(retval)); + cleanexit(1); + } +#endif + +#ifndef NOENCRYPTION + /* Must do it here, since this is after the fork() call */ + des_init_random_number_generator(server_parm.master_keyblock.contents); +#endif /* NOENCRYPTION */ + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const char *) &on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt keepalive: %d", errno); + + server_parm.recv_addr = *who; + + if (retval = krb5_db_init(kadm_context)) { /* Open as client */ + syslog(LOG_ERR, "can't open krb db: %s", error_message(retval)); + cleanexit(1); + } + /* need to set service key to changepw.KRB_MASTER */ + + status = krb5_db_get_principal(kadm_context, server_parm.sprinc, + &sprinc_entries, + &nentries, &more); + /* ugh... clean this up later */ + if (status == KRB5_KDB_DB_INUSE) { + /* db locked */ + krb5_ui_4 retcode = KADM_DB_INUSE; + char *pdat; + + dat_len = KADM_VERSIZE + sizeof(krb5_ui_4); + dat = (u_char *) malloc((unsigned)dat_len); + pdat = (char *) dat; + retcode = htonl((krb5_ui_4) KADM_DB_INUSE); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + goto out; + } else if (!nentries) { + syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst); + cleanexit(2); + } else if (status) { + syslog(LOG_ERR, error_message(status)); + cleanexit(2); + } + + status = krb5_dbe_find_enctype(kadm_context, + &sprinc_entries, + ENCTYPE_DES_CBC_MD5, + -1, + -1, + &kdatap); + if (status) { + syslog(LOG_ERR, "find enctype failed: %s", error_message(status)); + cleanexit(1); + } + + status = krb5_dbekd_decrypt_key_data(kadm_context, + &server_parm.master_encblock, + kdatap, + &cpw_skey, + (krb5_keysalt *) NULL); + if (status) { + syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status)); + cleanexit(1); + } + /* if error, will show up when rd_req fails */ + (void) krb_set_key((char *)cpw_skey.contents, 0); + while (1) { + if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) != + sizeof(u_short)) { + if (retval < 0) + syslog(LOG_ERR, "dlen read: %s", error_message(errno)); + else if (retval) + syslog(LOG_ERR, "short dlen read: %d", retval); + (void) close(fd); +#ifdef OVSEC_KADM + (void) ovsec_kadm_destroy(ovsec_handle); +#endif + cleanexit(retval ? 3 : 0); + } + if (exit_now) { + cleanexit(0); + } + dat_len = (int) ntohs(dlen); + dat = (u_char *) malloc((unsigned)dat_len); + if (!dat) { + syslog(LOG_ERR, "malloc: No memory"); + (void) close(fd); + cleanexit(4); + } + if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) { + if (retval < 0) + syslog(LOG_ERR, "data read: %s", error_message(errno)); + else + syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval); + (void) close(fd); + cleanexit(5); + } + if (exit_now) { + cleanexit(0); + } + if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS) + syslog(LOG_ERR, "processing request: %s", error_message(retval)); + + /* kadm_ser_in did the processing and returned stuff in + dat & dat_len , return the appropriate data */ + + out: + dlen = (u_short) dat_len; + + if (dat_len != (int)dlen) { + clear_secrets(); + abort(); /* XXX */ + } + dlen = htons(dlen); + + if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) { + syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(6); + } + + if (krb_net_write(fd, (char *)dat, dat_len) < 0) { + syslog(LOG_ERR, "writing to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(7); + } + free((char *)dat); + } + /*NOTREACHED*/ +} + +krb5_sigtype +do_child(sig) + int sig; +{ + /* SIGCHLD brings us here */ + int pid; + register int i, j; + +#ifdef WAIT_USES_INT + int status; +#else + union wait status; +#endif + + pid = wait(&status); + + for (i = 0; i < pidarraysize; i++) + if (pidarray[i] == pid) { + /* found it */ + for (j = i; j < pidarraysize-1; j++) + /* copy others down */ + pidarray[j] = pidarray[j+1]; + pidarraysize--; +#ifdef WAIT_USES_INT + if (WIFEXITED(status) || WIFSIGNALED(status)) + if (WTERMSIG(status) || WEXITSTATUS(status)) + syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + if (status.w_retcode || status.w_coredump || status.w_termsig) + syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + SIGNAL_RETURN; + } + unknown_child = pid; +#ifdef WAIT_USES_INT + syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + SIGNAL_RETURN; +} + +cleanexit(val) +{ + krb5_db_fini(kadm_context); + clear_secrets(); + exit(val); +} + +void +kill_children() +{ + register int i; +#ifdef POSIX_SIGNALS + sigset_t oldmask, igmask; +#else + int osigmask; +#endif + +#ifdef POSIX_SIGNALS + sigemptyset(&igmask); + sigaddset(&igmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &igmask, &oldmask); +#else + osigmask = sigblock(sigmask(SIGCHLD)); +#endif + + for (i = 0; i < pidarraysize; i++) { + kill(pidarray[i], SIGINT); + syslog(LOG_ERR, "killing child %d", pidarray[i]); + } +#ifdef POSIX_SIGNALS + sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0); +#else + sigsetmask(osigmask); +#endif + return; +} + +#ifdef OVSEC_KADM +krb5_ui_4 convert_ovsec_to_kadm(val) + krb5_ui_4 val; +{ + switch (val) { + case KADM5_AUTH_GET: + case KADM5_AUTH_ADD: + case KADM5_AUTH_MODIFY: + case KADM5_AUTH_DELETE: + case KADM5_AUTH_INSUFFICIENT: + case KADM5_AUTH_LIST: + case KADM5_AUTH_CHANGEPW: + return KADM_UNAUTH; + case KADM5_BAD_DB: + return KADM_UK_RERROR; + case KADM5_DUP: + case KADM5_POLICY_REF: + return KADM_INUSE; + case KADM5_RPC_ERROR: + return KADM_NO_CONN; + case KADM5_NO_SRV: + return KADM_NO_HOST; + case KADM5_UNK_PRINC: + case KADM5_UNK_POLICY: + return KADM_NOENTRY; + case KADM5_PASS_Q_TOOSHORT: + case KADM5_PASS_Q_CLASS: + case KADM5_PASS_Q_DICT: + case KADM5_PASS_REUSE: + case KADM5_PASS_TOOSOON: + case CHPASS_UTIL_PASSWORD_TOO_SOON: + return KADM_INSECURE_PW; + case KADM5_BAD_PASSWORD: + return KADM_NO_CRED; + case KADM5_PROTECT_PRINCIPAL: + return KADM_NO_OPCODE; + case KADM5_NOT_INIT: + case KADM5_BAD_HIST_KEY: + case KADM5_BAD_MASK: + case KADM5_BAD_CLASS: + case KADM5_BAD_LENGTH: + case KADM5_BAD_POLICY: + case KADM5_BAD_PRINCIPAL: + case KADM5_BAD_AUX_ATTR: + case KADM5_BAD_HISTORY: + case KADM5_BAD_MIN_PASS_LIFE: + return -1; + } + return val; +} +#endif diff --git a/src/kadmin/v4server/attic/ChangeLog b/src/kadmin/v4server/attic/ChangeLog new file mode 100644 index 000000000..6eefc24c7 --- /dev/null +++ b/src/kadmin/v4server/attic/ChangeLog @@ -0,0 +1,25 @@ +Thu Jul 18 19:47:58 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair) + + * admin_server.c: pick up <sys/time.h> (needed to get FD_SET, + etc.) + +Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair) + + * Makefile.in: no longer trying to install v4kadmind as krb5kdc + :-) + * configure.in: another try at making dbm libs dtrt + +Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair) + + * admin_server.c: fixed calls that should have invoked + krb5_init_ets + +Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair) + + * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to + krb5_build_principal + diff --git a/src/kadmin/v4server/attic/Imakefile b/src/kadmin/v4server/attic/Imakefile new file mode 100644 index 000000000..e1449ef32 --- /dev/null +++ b/src/kadmin/v4server/attic/Imakefile @@ -0,0 +1,49 @@ +# $Source$ +# $Author$ +# $Header$ +# +# Copyright 1989 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, +# please see the file <mit-copyright.h>. +# +# Imakefile for Kerberos admin server library. + +DEFINES = $(KRB4DEF) +INCLUDES = $(KRB4INCLUDES) -I. +SRCS = \ + kadm_server.c \ + kadm_funcs.c \ + admin_server.c \ + kadm_ser_wrap.c \ + kadm_stream.c \ + kadm_supp.c \ + kadm_err.c \ + acl_files.c +OBJS = \ + kadm_server.o \ + kadm_funcs.o \ + admin_server.o \ + kadm_ser_wrap.o \ + kadm_stream.o \ + kadm_supp.o \ + kadm_err.o \ + acl_files.o + +ErrorTableObjectRule() + +all:: v4kadmind + +depend:: kadm_err.c + +kadm_err.c: kadm_err.et + +NormalProgramTarget(v4kadmind,$(OBJS),$(KDBDEPLIB) $(DEPKLIB), \ + $(KDBLIB) $(KRB4LIB) $(KLIB) ,) + +Krb5InstallServerProgram(v4kadmind) + +clean:: + $(RM) kadm_err.c kadm_err.h + +DependTarget() diff --git a/src/kadmin/v4server/attic/Makefile b/src/kadmin/v4server/attic/Makefile new file mode 100644 index 000000000..d0acac021 --- /dev/null +++ b/src/kadmin/v4server/attic/Makefile @@ -0,0 +1,39 @@ +TOP = ../.. +include $(TOP)/config.mk/template + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS) +endif + +PROG := ovsec_v4adm_server + +SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \ + kadm_stream.c kadm_supp.c acl_files.c + +OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o + +LIBS := $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5) \ + $(LIBKADM) $(LIBKRB) $(LIBDES425) $(LIBKDB5) \ + $(LIBCRYPTO) $(LIBISODE) \ + $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB) + +ifdef WAIT_USES_INT +WAIT_FLAGS = -DWAIT_USES_INT +endif +ifdef OPEN_NEEDS_FCNTL +FCNTL_FLAGS = -DNEED_SYS_FCNTL_H +endif + +# XXX the -D's should probably be moved somewhere; in krb5.4.2 they +# are in osconf.h +CFLAGS := -DOVSEC_KADM \ + -DKADM_SYSLOG="\"/krb5/admin_server.syslog\"" \ + -DDEFAULT_ACL_DIR="\"/krb5\"" $(WAIT_FLAGS) $(FCNTL_FLAGS) \ + $(CFLAGS) + +expand InstallServer +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/v4server/attic/Makefile.in b/src/kadmin/v4server/attic/Makefile.in new file mode 100644 index 000000000..f5206aa66 --- /dev/null +++ b/src/kadmin/v4server/attic/Makefile.in @@ -0,0 +1,53 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) +LDFLAGS = -g + +ISODELIB=@ISODELIB@ +COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a +DBMLIB= +KDBLIB=$(TOPLIBD)/libkdb5.a + +KRB4LIB = $(KRB4)/lib/libkrb.a $(TOPLIBD)/libdes425.a + +KLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(ISODELIB) $(COMERRLIB) $(DBMLIB) + +LOCALINCLUDE=-I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I. + +SRCS = \ + $(srcdir)/kadm_server.c \ + $(srcdir)/kadm_funcs.c \ + $(srcdir)/admin_server.c \ + $(srcdir)/kadm_ser_wrap.c \ + $(srcdir)/kadm_stream.c \ + $(srcdir)/kadm_supp.c \ + $(srcdir)/kadm_err.c \ + $(srcdir)/acl_files.c +OBJS = \ + kadm_server.o \ + kadm_funcs.o \ + admin_server.o \ + kadm_ser_wrap.o \ + kadm_stream.o \ + kadm_supp.o \ + kadm_err.o \ + acl_files.o + +all:: kadm_err.h v4kadmind + +depend:: kadm_err.c + +kadm_err.c: kadm_err.et + +kadm_err.h: kadm_err.et + +v4kadmind: $(OBJS) $(KDBDEPLIB) $(DEPKLIB) + $(CC) $(CFLAGS) -o v4kadmind $(OBJS) $(KDBLIB) $(KLIB) $(KRB4LIB) $(LIBS) $(KRB4)/lib/libdes.a + +install:: + $(INSTALL_PROGRAM) v4kadmind ${DESTDIR}$(SERVER_BINDIR)/v4kadmind + +clean:: + $(RM) kadm_err.h kadm_err.c + +clean:: + $(RM) v4kadmind + diff --git a/src/kadmin/v4server/attic/acl_files.c b/src/kadmin/v4server/attic/acl_files.c new file mode 100644 index 000000000..81275ae26 --- /dev/null +++ b/src/kadmin/v4server/attic/acl_files.c @@ -0,0 +1,541 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1987,1989 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + */ + +#ifndef lint +static char rcsid_acl_files_c[] = "$Id$"; +#endif lint + + +/*** Routines for manipulating access control list files ***/ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <ctype.h> +#ifdef NEED_SYS_FCNTL_H +#include <sys/fcntl.h> +#endif +#include "krb.h" +#include <krb5/krb5.h> + +#ifndef KRB_REALM +#define KRB_REALM "ATHENA.MIT.EDU" +#endif + +/* "aname.inst@realm" */ +#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) +#define INST_SEP '.' +#define REALM_SEP '@' + +#define LINESIZE 2048 /* Maximum line length in an acl file */ + +#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ +#define WAIT_TIME 300 /* Maximum time allowed write acl file */ + +#define CACHED_ACLS 8 /* How many acls to cache */ + /* Each acl costs 1 open file descriptor */ +#define ACL_LEN 16 /* Twice a reasonable acl length */ + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define COR(a,b) ((a!=NULL)?(a):(b)) + +extern int errno; + +extern char *malloc(), *calloc(); +extern time_t time(); + +static int acl_abort PROTOTYPE((char *acl_file, FILE *f)); + +/* Canonicalize a principal name */ +/* If instance is missing, it becomes "" */ +/* If realm is missing, it becomes the local realm */ +/* Canonicalized form is put in canon, which must be big enough to hold + MAX_PRINCIPAL_SIZE characters */ +acl_canonicalize_principal(principal, canon) +char *principal; +char *canon; +{ + char *dot, *atsign, *end; + int len; + + dot = strchr(principal, INST_SEP); + atsign = strchr(principal, REALM_SEP); + + /* Maybe we're done already */ + if(dot != NULL && atsign != NULL) { + if(dot < atsign) { + /* It's for real */ + /* Copy into canon */ + strncpy(canon, principal, MAX_PRINCIPAL_SIZE); + canon[MAX_PRINCIPAL_SIZE-1] = '\0'; + return; + } else { + /* Nope, it's part of the realm */ + dot = NULL; + } + } + + /* No such luck */ + end = principal + strlen(principal); + + /* Get the principal name */ + len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); + strncpy(canon, principal, len); + canon += len; + + /* Add INST_SEP */ + *canon++ = INST_SEP; + + /* Get the instance, if it exists */ + if(dot != NULL) { + ++dot; + len = MIN(INST_SZ, COR(atsign, end) - dot); + strncpy(canon, dot, len); + canon += len; + } + + /* Add REALM_SEP */ + *canon++ = REALM_SEP; + + /* Get the realm, if it exists */ + /* Otherwise, default to local realm */ + if(atsign != NULL) { + ++atsign; + len = MIN(REALM_SZ, end - atsign); + strncpy(canon, atsign, len); + canon += len; + *canon++ = '\0'; + } else if(krb_get_lrealm(canon, 1) != KSUCCESS) { + strcpy(canon, KRB_REALM); + } +} + +/* Get a lock to modify acl_file */ +/* Return new FILE pointer */ +/* or NULL if file cannot be modified */ +/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ +static FILE *acl_lock_file(acl_file) +char *acl_file; +{ + struct stat s; + char new[LINESIZE]; + int nfd; + FILE *nf; + int mode; + + if(stat(acl_file, &s) < 0) return(NULL); + mode = s.st_mode; + sprintf(new, NEW_FILE, acl_file); + for(;;) { + /* Open the new file */ + if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { + if(errno == EEXIST) { + /* Maybe somebody got here already, maybe it's just old */ + if(stat(new, &s) < 0) return(NULL); + if(time(0) - s.st_ctime > WAIT_TIME) { + /* File is stale, kill it */ + unlink(new); + continue; + } else { + /* Wait and try again */ + sleep(1); + continue; + } + } else { + /* Some other error, we lose */ + return(NULL); + } + } + + /* If we got to here, the lock file is ours and ok */ + /* Reopen it under stdio */ + if((nf = fdopen(nfd, "w")) == NULL) { + /* Oops, clean up */ + unlink(new); + } + return(nf); + } +} + +/* Commit changes to acl_file written onto FILE *f */ +/* Returns zero if successful */ +/* Returns > 0 if lock was broken */ +/* Returns < 0 if some other error occurs */ +/* Closes f */ +static int acl_commit(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + sprintf(new, NEW_FILE, acl_file); + if(fflush(f) < 0 + || fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + acl_abort(acl_file, f); + return(-1); + } + + ret = rename(new, acl_file); + fclose(f); + return(ret); +} + +/* Abort changes to acl_file written onto FILE *f */ +/* Returns 0 if successful, < 0 otherwise */ +/* Closes f */ +static int acl_abort(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + /* make sure we aren't nuking someone else's file */ + if(fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + fclose(f); + return(-1); + } else { + sprintf(new, NEW_FILE, acl_file); + ret = unlink(new); + fclose(f); + return(ret); + } +} + +/* Initialize an acl_file */ +/* Creates the file with permissions perm if it does not exist */ +/* Erases it if it does */ +/* Returns return value of acl_commit */ +int acl_initialize(acl_file, perm) +char *acl_file; +int perm; +{ + FILE *new; + int fd; + + /* Check if the file exists already */ + if((new = acl_lock_file(acl_file)) != NULL) { + return(acl_commit(acl_file, new)); + } else { + /* File must be readable and writable by owner */ + if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { + return(-1); + } else { + close(fd); + return(0); + } + } +} + +/* Eliminate all whitespace character in buf */ +/* Modifies its argument */ +static nuke_whitespace(buf) +char *buf; +{ + register char *pin, *pout; + + for(pin = pout = buf; *pin != '\0'; pin++) + if(!isspace(*pin)) *pout++ = *pin; + *pout = '\0'; /* Terminate the string */ +} + +/* Hash table stuff */ + +struct hashtbl { + int size; /* Max number of entries */ + int entries; /* Actual number of entries */ + char **tbl; /* Pointer to start of table */ +}; + +/* Make an empty hash table of size s */ +static struct hashtbl *make_hash(size) +int size; +{ + struct hashtbl *h; + + if(size < 1) size = 1; + h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); + h->size = size; + h->entries = 0; + h->tbl = (char **) calloc(size, sizeof(char *)); + return(h); +} + +/* Destroy a hash table */ +static destroy_hash(h) +struct hashtbl *h; +{ + int i; + + for(i = 0; i < h->size; i++) { + if(h->tbl[i] != NULL) free(h->tbl[i]); + } + free(h->tbl); + free(h); +} + +/* Compute hash value for a string */ +static unsigned hashval(s) +register char *s; +{ + register unsigned hv; + + for(hv = 0; *s != '\0'; s++) { + hv ^= ((hv << 3) ^ *s); + } + return(hv); +} + +/* Add an element to a hash table */ +static add_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + char *s; + char **old; + int i; + + /* Make space if it isn't there already */ + if(h->entries + 1 > (h->size >> 1)) { + old = h->tbl; + h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); + for(i = 0; i < h->size; i++) { + if(old[i] != NULL) { + hv = hashval(old[i]) % (h->size << 1); + while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); + h->tbl[hv] = old[i]; + } + } + h->size = h->size << 1; + free(old); + } + + hv = hashval(el) % h->size; + while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; + s = malloc(strlen(el)+1); + strcpy(s, el); + h->tbl[hv] = s; + h->entries++; +} + +/* Returns nonzero if el is in h */ +static check_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + + for(hv = hashval(el) % h->size; + h->tbl[hv] != NULL; + hv = (hv + 1) % h->size) { + if(!strcmp(h->tbl[hv], el)) return(1); + } + return(0); +} + +struct acl { + char filename[LINESIZE]; /* Name of acl file */ + int fd; /* File descriptor for acl file */ + struct stat status; /* File status at last read */ + struct hashtbl *acl; /* Acl entries */ +}; + +static struct acl acl_cache[CACHED_ACLS]; + +static int acl_cache_count = 0; +static int acl_cache_next = 0; + +/* Returns < 0 if unsuccessful in loading acl */ +/* Returns index into acl_cache otherwise */ +/* Note that if acl is already loaded, this is just a lookup */ +static int acl_load(name) +char *name; +{ + int i; + FILE *f; + struct stat s; + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) { + if(!strcmp(acl_cache[i].filename, name) + && acl_cache[i].fd >= 0) goto got_it; + } + + /* It isn't, load it in */ + /* maybe there's still room */ + if(acl_cache_count < CACHED_ACLS) { + i = acl_cache_count++; + } else { + /* No room, clean one out */ + i = acl_cache_next; + acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; + close(acl_cache[i].fd); + if(acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + } + + /* Set up the acl */ + strcpy(acl_cache[i].filename, name); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + /* Force reload */ + acl_cache[i].acl = (struct hashtbl *) 0; + + got_it: + /* + * See if the stat matches + * + * Use stat(), not fstat(), as the file may have been re-created by + * acl_add or acl_delete. If this happens, the old inode will have + * no changes in the mod-time and the following test will fail. + */ + if(stat(acl_cache[i].filename, &s) < 0) return(-1); + if(acl_cache[i].acl == (struct hashtbl *) 0 + || s.st_nlink != acl_cache[i].status.st_nlink + || s.st_mtime != acl_cache[i].status.st_mtime + || s.st_ctime != acl_cache[i].status.st_ctime) { + /* Gotta reload */ + if(acl_cache[i].fd >= 0) close(acl_cache[i].fd); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1); + if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = make_hash(ACL_LEN); + while(fgets(buf, sizeof(buf), f) != NULL) { + nuke_whitespace(buf); + acl_canonicalize_principal(buf, canon); + add_hash(acl_cache[i].acl, canon); + } + fclose(f); + acl_cache[i].status = s; + } + return(i); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Principal is not canonicalized, and no wildcarding is done */ +acl_exact_match(acl, principal) +char *acl; +char *principal; +{ + int idx; + + return((idx = acl_load(acl)) >= 0 + && check_hash(acl_cache[idx].acl, principal)); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Recognizes wildcards in acl of the form + name.*@realm, *.*@realm, and *.*@* */ +acl_check(acl, principal) +char *acl; +char *principal; +{ + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + char *realm, *tmp; + + acl_canonicalize_principal(principal, canon); + + /* Is it there? */ + if(acl_exact_match(acl, canon)) return(1); + + /* Try the wildcards */ + realm = strchr(canon, REALM_SEP); + tmp = strchr(canon, INST_SEP); + *tmp = '\0'; /* Chuck the instance */ + + sprintf(buf, "%s.*%s", canon, realm); + if(acl_exact_match(acl, buf)) return(1); + + sprintf(buf, "*.*%s", realm); + if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); + + return(0); +} + +/* Adds principal to acl */ +/* Wildcards are interpreted literally */ +acl_add(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL) { + if((fputs(acl_cache[idx].acl->tbl[i], new) == EOF) + || (putc('\n', new) != '\n')) { + acl_abort(acl, new); + return(-1); + } + } + } + fputs(canon, new); + putc('\n', new); + return(acl_commit(acl, new)); +} + +/* Removes principal from acl */ +/* Wildcards are interpreted literally */ +acl_delete(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((!acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL + && strcmp(acl_cache[idx].acl->tbl[i], canon)) { + fputs(acl_cache[idx].acl->tbl[i], new); + putc('\n', new); + } + } + return(acl_commit(acl, new)); +} + diff --git a/src/kadmin/v4server/attic/acl_files.doc b/src/kadmin/v4server/attic/acl_files.doc new file mode 100644 index 000000000..78c448a6d --- /dev/null +++ b/src/kadmin/v4server/attic/acl_files.doc @@ -0,0 +1,107 @@ +PROTOTYPE ACL LIBRARY + +Introduction + +An access control list (ACL) is a list of principals, where each +principal is is represented by a text string which cannot contain +whitespace. The library allows application programs to refer to named +access control lists to test membership and to atomically add and +delete principals using a natural and intuitive interface. At +present, the names of access control lists are required to be Unix +filenames, and refer to human-readable Unix files; in the future, when +a networked ACL server is implemented, the names may refer to a +different namespace specific to the ACL service. + + +Usage + +cc <files> -lacl -lkrb. + + + +Principal Names + +Principal names have the form + +<name>[.<instance>][@<realm>] + +e.g. + +asp +asp.root +asp@ATHENA.MIT.EDU +asp.@ATHENA.MIT.EDU +asp.root@ATHENA.MIT.EDU + +It is possible for principals to be underspecified. If instance is +missing, it is assumed to be "". If realm is missing, it is assumed +to be local_realm. The canonical form contains all of name, instance, +and realm; the acl_add and acl_delete routines will always +leave the file in that form. Note that the canonical form of +asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU. + + +Routines + +acl_canonicalize_principal(principal, buf) +char *principal; +char *buf; /*RETVAL*/ + +Store the canonical form of principal in buf. Buf must contain enough +space to store a principal, given the limits on the sizes of name, +instance, and realm specified in /usr/include/krb.h. + +acl_check(acl, principal) +char *acl; +char *principal; + +Returns nonzero if principal appears in acl. Returns 0 if principal +does not appear in acl, or if an error occurs. Canonicalizes +principal before checking, and allows the ACL to contain wildcards. + +acl_exact_match(acl, principal) +char *acl; +char *principal; + +Like acl_check, but does no canonicalization or wildcarding. + +acl_add(acl, principal) +char *acl; +char *principal; + +Atomically adds principal to acl. Returns 0 if successful, nonzero +otherwise. It is considered a failure if principal is already in acl. +This routine will canonicalize principal, but will treat wildcards +literally. + +acl_delete(acl, principal) +char *acl; +char *principal; + +Atomically deletes principal from acl. Returns 0 if successful, +nonzero otherwise. It is consider a failure if principal is not +already in acl. This routine will canonicalize principal, but will +treat wildcards literally. + +acl_initialize(acl, mode) +char *acl; +int mode; + +Initialize acl. If acl file does not exist, creates it with mode +mode. If acl exists, removes all members. Returns 0 if successful, +nonzero otherwise. WARNING: Mode argument is likely to change with +the eventual introduction of an ACL service. + + +Known problems + +In the presence of concurrency, there is a very small chance that +acl_add or acl_delete could report success even though it would have +had no effect. This is a necessary side effect of using lock files +for concurrency control rather than flock(2), which is not supported +by NFS. + +The current implementation caches ACLs in memory in a hash-table +format for increased efficiency in checking membership; one effect of +the caching scheme is that one file descriptor will be kept open for +each ACL cached, up to a maximum of 8. diff --git a/src/kadmin/v4server/attic/aclocal.m4 b/src/kadmin/v4server/attic/aclocal.m4 new file mode 100644 index 000000000..70bf66a7d --- /dev/null +++ b/src/kadmin/v4server/attic/aclocal.m4 @@ -0,0 +1,3 @@ +sinclude([./../../aclocal.m4])dnl +undefine([AC_BUILDTOP])dnl +define(AC_BUILDTOP,[./../..])dnl diff --git a/src/kadmin/v4server/attic/admin_server.c b/src/kadmin/v4server/attic/admin_server.c new file mode 100644 index 000000000..04155bca1 --- /dev/null +++ b/src/kadmin/v4server/attic/admin_server.c @@ -0,0 +1,668 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Top-level loop of the kerberos Administration server + */ + +#include <mit-copyright.h> + +/* + admin_server.c + this holds the main loop and initialization and cleanup code for the server +*/ + +#ifdef _AIX +#include <sys/select.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <signal.h> +#include <string.h> + +#ifndef POSIX_SIGNALS +#ifndef sigmask +#define sigmask(m) (1 <<((m)-1)) +#endif +#endif /* POSIX_SIGNALS */ +#ifdef _AIX +#include <sys/resource.h> +#endif /* _AIX */ +#include <sys/wait.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <syslog.h> + +#include <krb5/krb5.h> +#include <krb5/kdb.h> +#include <krb5/kdb_dbm.h> +#include <krb5/los-proto.h> +#include <krb5/config.h> + +#ifdef OVSEC_KADM +#include <ovsec_admin/admin.h> +void *ovsec_handle; +#endif + +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include "kadm_server.h" + +/* Almost all procs and such need this, so it is global */ +admin_params prm; /* The command line parameters struct */ + +char prog[32]; /* WHY IS THIS NEEDED??????? */ +char *progname = prog; +char *acldir = DEFAULT_ACL_DIR; +char krbrlm[REALM_SZ]; +extern Kadm_Server server_parm; +int des_debug; /* used by the des425 libraries */ +int debug = 0; + +/* +** Main does the logical thing, it sets up the database and RPC interface, +** as well as handling the creation and maintenance of the syslog file... +*/ +main(argc, argv) /* admin_server main routine */ +int argc; +char *argv[]; +{ + int errval; + int c; + char *db_name, *lrealm; + extern char *optarg; + extern int fascist_cpw; + + krb5_init_ets(); + initialize_kadm_error_table(); + prog[sizeof(prog)-1]='\0'; /* Terminate... */ + (void) strncpy(prog, argv[0], sizeof(prog)-1); + + /* initialize the admin_params structure */ + prm.sysfile = KADM_SYSLOG; /* default file name */ + prm.inter = 1; + + memset(krbrlm, 0, sizeof(krbrlm)); + + fascist_cpw = 1; /* by default, enable fascist mode */ + while ((c = getopt(argc, argv, "f:hnd:Da:r:FN")) != EOF) + switch(c) { + case 'd': + if (errval = krb5_db_set_name(optarg)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } + break; + case 'D': + debug++; + break; +#ifndef OVSEC_KADM + case 'f': /* Syslog file name change */ + prm.sysfile = optarg; + break; + case 'F': + fascist_cpw++; + break; + case 'N': + fascist_cpw = 0; + break; +#endif + case 'n': + prm.inter = 0; + break; + case 'a': /* new acl directory */ + acldir = optarg; + break; + case 'r': + (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1); + break; + case 'h': /* get help on using admin_server */ + default: +#ifdef OVSEC_KADM + fprintf(stderr, "Usage: ovsec_v4adm_server [-D] [-h] [-n] [-r realm] [-d dbname] [-a acldir]\n"); + +#else + printf("Usage: admin_server [-D] [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n"); +#endif + exit(-1); /* failure */ + } + + if (krbrlm[0] == 0) { + if (errval = krb5_get_default_realm(&lrealm)) { + com_err(argv[0], errval, "while attempting to get local realm"); + exit(1); + } + (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1); + } + printf("KADM Server %s initializing\n",KADM_VERSTR); + printf("Please do not use 'kill -9' to kill this job, use a\n"); + printf("regular kill instead\n\n"); + +#ifdef OVSEC_KADM + printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n", + debug ? "" : "parent ", getpid()); +#else + printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST"); +#endif + + open_syslog(argv[0], "V4 admin server (parent) starting"); + + errval = krb5_db_init(); /* Open the Kerberos database */ + if (errval) { + fprintf(stderr, "error: krb5_db_init() failed"); + close_syslog(); + byebye(); + exit(1); + } + if (errval = krb5_db_set_lockmode(TRUE)) { + com_err(argv[0], errval, "while setting db to nonblocking"); + close_syslog(); + byebye(); + exit(1); + } + + /* set up the server_parm struct */ + if ((errval = kadm_ser_init(prm.inter, krbrlm)) != KADM_SUCCESS) { + fprintf(stderr, "error initializing: %s\n", error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + + /* detach from the terminal */ + if (!debug) { + if ( +#ifdef KRB5B4 + daemon(0, 0) +#else + errval = krb5_detach_process() +#endif + ) { +#ifdef KRB5B4 + errval = errno; +#endif + fprintf(stderr, "error detaching from terminal: %s\n", + error_message(errval)); + syslog(LOG_ERR, "error detaching from terminal: %s", + error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + open_syslog(argv[0], "V4 admin server (child) starting"); + } + + krb5_db_fini(); + + if (errval = kadm_listen()) { + fprintf(stderr, "error while listening for requests: %s\n", + error_message(errval)); + syslog(LOG_ERR, "error while listening for requests: %s", + error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + + close_syslog(); + byebye(); + exit(0); +} /* procedure main */ + + +/* open the system log file */ +open_syslog(whoami, message) + char *whoami, *message; +{ + static int opened = 0; + + if (opened) { + closelog(); + } + openlog(whoami, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */ + syslog(LOG_INFO, message); + opened++; +} + +/* close the system log file */ +close_syslog() +{ + syslog(LOG_INFO, "Shutting down V4 admin server"); +} + +byebye() /* say goodnight gracie */ +{ + printf("Admin Server (kadm server) has completed operation.\n"); +} + +static clear_secrets() +{ + krb5_finish_key(&server_parm.master_encblock); + memset((char *)&server_parm.master_encblock, 0, + sizeof (server_parm.master_encblock)); + memset((char *)server_parm.master_keyblock.contents, 0, + server_parm.master_keyblock.length); + server_parm.mkvno = 0L; + return; +} + +static exit_now = 0; + +krb5_sigtype doexit() +{ + exit_now = 1; +} + +unsigned pidarraysize = 0; +int *pidarray = (int *)0; +int unknown_child = 0; + +/* +kadm_listen +listen on the admin servers port for a request +*/ +kadm_listen() +{ + extern int errno; + int found; + int admin_fd; + int peer_fd; + fd_set mask, readfds; + struct sockaddr_in peer; + int addrlen; + void process_client(), kill_children(); + int pid; + krb5_sigtype do_child(); + + (void) signal(SIGINT, doexit); + (void) signal(SIGTERM, doexit); + (void) signal(SIGHUP, doexit); + (void) signal(SIGQUIT, doexit); + (void) signal(SIGPIPE, SIG_IGN); /* get errors on write() */ + (void) signal(SIGALRM, doexit); + (void) signal(SIGCHLD, do_child); + + if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return KADM_NO_SOCK; + if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr, + sizeof(struct sockaddr_in)) < 0) + return KADM_NO_BIND; + (void) listen(admin_fd, 1); + FD_ZERO(&mask); + FD_SET(admin_fd, &mask); + + for (;;) { /* loop nearly forever */ + if (exit_now) { + clear_secrets(); + kill_children(); + return(0); + } + readfds = mask; + if ((found = select(admin_fd+1,&readfds,(fd_set *)0, + (fd_set *)0, (struct timeval *)0)) == 0) + continue; /* no things read */ + if (found < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "select: %s", error_message(errno)); + continue; + } + if (FD_ISSET(admin_fd, &readfds)) { + /* accept the conn */ + addrlen = sizeof(peer); + if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer, + &addrlen)) < 0) { + syslog(LOG_ERR, "accept: %s", error_message(errno)); + continue; + } + + if (debug) { + process_client(peer_fd, &peer); + } else if (pid = fork()) { + /* parent */ + if (pid < 0) { + syslog(LOG_ERR, "fork: %s", error_message(errno)); + (void) close(peer_fd); + continue; + } + /* fork succeeded: keep tabs on child */ + (void) close(peer_fd); + if (unknown_child != pid) { + if (pidarray) { + pidarray = (int *)realloc((char *)pidarray, + (++pidarraysize * sizeof(int))); + pidarray[pidarraysize-1] = pid; + } else { + pidarray = (int *)malloc((pidarraysize = 1) * + sizeof(int)); + pidarray[0] = pid; + } + } /* End if unknown_child != pid.*/ + } else { + /* child */ + (void) close(admin_fd); + process_client(peer_fd, &peer); + } + } else { + syslog(LOG_ERR, "something else woke me up!"); + return(0); + } + } + /*NOTREACHED*/ +} + +void process_client(fd, who) + int fd; + struct sockaddr_in *who; +{ + u_char *dat; + int dat_len; + u_short dlen; + int retval; + int on = 1; + Principal service; + des_cblock skey; + int nentries = 1; + krb5_db_entry sprinc_entries; + krb5_boolean more; + krb5_keyblock cpw_skey; + int status; + +#ifdef OVSEC_KADM +#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab" + char *service_name; + + service_name = (char *) malloc(strlen(server_parm.sname) + + strlen(server_parm.sinst) + + strlen(server_parm.krbrlm) + 3); + if (service_name == NULL) { + syslog(LOG_ERR, "error: out of memory allocating service name"); + } + sprintf(service_name, "%s/%s@%s", server_parm.sname, + server_parm.sinst, server_parm.krbrlm); + + retval = ovsec_kadm_init_with_skey(service_name, + OVSEC_KADM_SRVTAB, + OVSEC_KADM_ADMIN_SERVICE, krbrlm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_handle); + if (retval) { + syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s", + error_message(retval)); + cleanexit(1); + } + free(service_name); + +#endif + +#if !defined(NOENCRYPTION) + /* Must do it here, since this is after the fork() call. */ + des_init_random_number_generator(server_parm.master_keyblock.contents); +#endif + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt keepalive: %d", errno); + + server_parm.recv_addr = *who; + + if (krb5_db_init()) { /* Open as client */ + syslog(LOG_ERR, "can't open krb db"); + cleanexit(1); + } + + /* need to set service key to changepw.KRB_MASTER */ + + status = krb5_db_get_principal(server_parm.sprinc, + &sprinc_entries, + &nentries, &more); + /* ugh... clean this up later */ + if (status == KRB5_KDB_DB_INUSE) { + /* db locked */ + krb5_ui_4 retcode = KADM_DB_INUSE; + char *pdat; + + dat_len = KADM_VERSIZE + sizeof(u_int); + dat = (u_char *) malloc((unsigned)dat_len); + pdat = (char *) dat; + /* This must be 32 bit integer due to the htonl */ + retcode = htonl((krb5_ui_4) KADM_DB_INUSE); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + goto out; + } else if (!nentries) { + syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst); + cleanexit(2); + } else if (status) { + syslog(LOG_ERR, error_message(status)); + cleanexit(2); + } + + status = krb5_kdb_decrypt_key(&server_parm.master_encblock, + &sprinc_entries.key, + &cpw_skey); + if (status) { + syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status)); + cleanexit(1); + } + /* if error, will show up when rd_req fails */ + (void) krb_set_key((char *)cpw_skey.contents, 0); +#ifdef KRB5_FREE_KEYBLOCK_CONTENTS_EXISTS + krb5_free_keyblock_contents(&cpw_skey); +#else + memset((char*)cpw_skey.contents, 0, cpw_skey.length); + free(cpw_skey.contents); +#endif + + krb5_dbm_db_free_principal(&sprinc_entries, nentries); + + while (1) { + if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) != + sizeof(u_short)) { + if (retval < 0) + syslog(LOG_ERR, "dlen read: %s", error_message(errno)); + else if (retval) + syslog(LOG_ERR, "short dlen read: %d", retval); + (void) close(fd); +#ifdef OVSEC_KADM + (void) ovsec_kadm_destroy(ovsec_handle); +#endif + if (debug) + return; + else + cleanexit(retval ? 3 : 0); + } + if (exit_now) { + cleanexit(0); + } + dat_len = (int) ntohs(dlen); + dat = (u_char *) malloc((unsigned)dat_len); + if (!dat) { + syslog(LOG_ERR, "malloc: No memory"); + (void) close(fd); + cleanexit(4); + } + if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) { + if (retval < 0) + syslog(LOG_ERR, "data read: %s", error_message(errno)); + else + syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval); + (void) close(fd); + cleanexit(5); + } + if (exit_now) { + cleanexit(0); + } + if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS) + syslog(LOG_ERR, "processing request: %s", error_message(retval)); + + /* kadm_ser_in did the processing and returned stuff in + dat & dat_len , return the appropriate data */ + + out: + dlen = (u_short) dat_len; + + if (dat_len != (int)dlen) { + clear_secrets(); + abort(); /* XXX */ + } + dlen = htons(dlen); + + if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) { + syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(6); + } + + if (krb_net_write(fd, (char *)dat, dat_len) < 0) { + syslog(LOG_ERR, "writing to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(7); + } + free((char *)dat); + } + /*NOTREACHED*/ +} + +krb5_sigtype do_child() +{ + /* SIGCHLD brings us here */ + int pid; + register int i, j; + +#ifdef WAIT_USES_INT + int status; +#else + union wait status; +#endif + + pid = wait(&status); + + for (i = 0; i < pidarraysize; i++) + if (pidarray[i] == pid) { + /* found it */ + for (j = i; j < pidarraysize-1; j++) + /* copy others down */ + pidarray[j] = pidarray[j+1]; + pidarraysize--; +#ifdef WAIT_USES_INT + if (WIFEXITED(status) || WIFSIGNALED(status)) + syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + if (status.w_retcode || status.w_coredump || status.w_termsig) + syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + goto done; /* use goto to avoid figuring out whether to + return a value */ + } + unknown_child = pid; +#ifdef WAIT_USES_INT + syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); +#else + syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); +#endif + + done: +} + +cleanexit(val) +{ + krb5_db_fini(); + clear_secrets(); + exit(val); +} + +void kill_children() +{ + register int i; +#ifdef POSIX_SIGNALS + sigset_t oldmask, igmask; +#else + int osigmask; +#endif + +#ifdef POSIX_SIGNALS + sigemptyset(&igmask); + sigaddset(&igmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &igmask, &oldmask); +#else + osigmask = sigblock(sigmask(SIGCHLD)); +#endif + + for (i = 0; i < pidarraysize; i++) { + kill(pidarray[i], SIGINT); + syslog(LOG_ERR, "killing child %d", pidarray[i]); + } +#ifdef POSIX_SIGNALS + sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0); +#else + sigsetmask(osigmask); +#endif + return; +} + +#ifdef OVSEC_KADM +krb5_ui_4 convert_ovsec_to_kadm(val) + krb5_ui_4 val; +{ + switch (val) { + case OVSEC_KADM_AUTH_GET: + case OVSEC_KADM_AUTH_ADD: + case OVSEC_KADM_AUTH_MODIFY: + case OVSEC_KADM_AUTH_DELETE: + case OVSEC_KADM_AUTH_INSUFFICIENT: + return KADM_UNAUTH; + case OVSEC_KADM_BAD_DB: + return KADM_UK_RERROR; + case OVSEC_KADM_DUP: + case OVSEC_KADM_POLICY_REF: + return KADM_INUSE; + case OVSEC_KADM_RPC_ERROR: + return KADM_NO_CONN; + case OVSEC_KADM_NO_SRV: + return KADM_NO_HOST; + case OVSEC_KADM_UNK_PRINC: + case OVSEC_KADM_UNK_POLICY: + return KADM_NOENTRY; + case OVSEC_KADM_PASS_Q_TOOSHORT: + case OVSEC_KADM_PASS_Q_CLASS: + case OVSEC_KADM_PASS_Q_DICT: + case OVSEC_KADM_PASS_REUSE: + case OVSEC_KADM_PASS_TOOSOON: + case CHPASS_UTIL_PASSWORD_TOO_SOON: + return KADM_INSECURE_PW; + case OVSEC_KADM_BAD_PASSWORD: + return KADM_NO_CRED; + case OVSEC_KADM_PROTECT_PRINCIPAL: + return KADM_NO_OPCODE; + case OVSEC_KADM_NOT_INIT: + case OVSEC_KADM_BAD_HIST_KEY: + case OVSEC_KADM_BAD_MASK: + case OVSEC_KADM_BAD_CLASS: + case OVSEC_KADM_BAD_LENGTH: + case OVSEC_KADM_BAD_POLICY: + case OVSEC_KADM_BAD_PRINCIPAL: + case OVSEC_KADM_BAD_AUX_ATTR: + case OVSEC_KADM_BAD_HISTORY: + case OVSEC_KADM_BAD_MIN_PASS_LIFE: + return -1; + } + return val; +} +#endif diff --git a/src/kadmin/v4server/attic/configure.in b/src/kadmin/v4server/attic/configure.in new file mode 100644 index 000000000..f09ba3a28 --- /dev/null +++ b/src/kadmin/v4server/attic/configure.in @@ -0,0 +1,22 @@ +AC_INIT(admin_server.c) +WITH_CCOPTS +CONFIG_RULES +AC_SET_BUILDTOP +AC_PROG_INSTALL +AC_HAVE_LIBRARY(socket) +AC_HAVE_LIBRARY(nsl) +AC_HAVE_LIBRARY(-lndbm) +AC_HAVE_LIBRARY(-ldbm) +CHECK_WAIT_TYPE +CHECK_FCNTL +AC_FUNC_CHECK(sigprocmask, +AC_COMPILE_CHECK([sigset_t], +[#include <signal.h>], +[sigset_t x], +AC_DEFINE(POSIX_SIGNALS))) +AC_PROG_AWK +KRB_INCLUDE +ISODE_INCLUDE +WITH_KRB4 +WITH_KRB5ROOT +AC_OUTPUT(Makefile,[EXTRA_RULES]) diff --git a/src/kadmin/v4server/attic/kadm_err.et b/src/kadmin/v4server/attic/kadm_err.et new file mode 100644 index 000000000..a19273083 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_err.et @@ -0,0 +1,57 @@ +# kadmin.v4/server/kadm_err.et +# +# Copyright 1988 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, please see the file +# <mit-copyright.h>. +# +# Kerberos administration server error table +# + et kadm + +# KADM_SUCCESS, as all success codes should be, is zero + +ec KADM_RCSID, "$Header$" +# /* Building and unbuilding the packet errors */ +ec KADM_NO_REALM, "Cannot fetch local realm" +ec KADM_NO_CRED, "Unable to fetch credentials" +ec KADM_BAD_KEY, "Bad key supplied" +ec KADM_NO_ENCRYPT, "Can't encrypt data" +ec KADM_NO_AUTH, "Cannot encode/decode authentication info" +ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm" +ec KADM_NO_ROOM, "Packet is too large" +ec KADM_BAD_VER, "Version number is incorrect" +ec KADM_BAD_CHK, "Checksum does not match" +ec KADM_NO_READ, "Unsealing private data failed" +ec KADM_NO_OPCODE, "Unsupported operation" +ec KADM_NO_HOST, "Could not find administrating host" +ec KADM_UNK_HOST, "Administrating host name is unknown" +ec KADM_NO_SERV, "Could not find service name in services database" +ec KADM_NO_SOCK, "Could not create socket" +ec KADM_NO_CONN, "Could not connect to server" +ec KADM_NO_HERE, "Could not fetch local socket address" +ec KADM_NO_MAST, "Could not fetch master key" +ec KADM_NO_VERI, "Could not verify master key" + +# /* From the server side routines */ +ec KADM_INUSE, "Entry already exists in database" +ec KADM_UK_SERROR, "Database store error" +ec KADM_UK_RERROR, "Database read error" +ec KADM_UNAUTH, "Insufficient access to perform requested operation" +# KADM_DATA isn't really an error, but... +ec KADM_DATA, "Data is available for return to client" +ec KADM_NOENTRY, "No such entry in the database" + +ec KADM_NOMEM, "Memory exhausted" +ec KADM_NO_HOSTNAME, "Could not fetch system hostname" +ec KADM_NO_BIND, "Could not bind port" +ec KADM_LENGTH_ERROR, "Length mismatch problem" +ec KADM_ILL_WILDCARD, "Illegal use of wildcard" + +ec KADM_DB_INUSE, "Database locked or in use" + +ec KADM_INSECURE_PW, "Insecure password rejected" +ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match" + +ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request" +end diff --git a/src/kadmin/v4server/attic/kadm_funcs.c b/src/kadmin/v4server/attic/kadm_funcs.c new file mode 100644 index 000000000..7ce9c7b4f --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_funcs.c @@ -0,0 +1,876 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side database manipulation routines + */ + +#ifndef lint +static char rcsid_kadm_funcs_c[] = +"$Id$"; +#endif lint + +#include <mit-copyright.h> +/* +kadm_funcs.c +the actual database manipulation code +*/ + +#include <stdio.h> +#include <sys/param.h> +#include <ndbm.h> +#include <ctype.h> +#include <pwd.h> +#include <sys/file.h> +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include <syslog.h> +#ifdef NEED_SYS_FCNTL_H +#include <sys/fcntl.h> +#endif + +#include "kadm_server.h" + +extern Kadm_Server server_parm; + +krb5_error_code +kadm_entry2princ(entry, princ) + krb5_db_entry entry; + Principal *princ; +{ + char realm[REALM_SZ]; /* dummy values only */ + krb5_error_code retval; + time_t lcltim; + + /* NOTE: does not convert the key */ + memset(princ, 0, sizeof (*princ)); + retval = krb5_524_conv_principal(entry.principal, + princ->name, princ->instance, realm); + if (retval) + return retval; + princ->exp_date = entry.expiration; + lcltim = entry.expiration; + strncpy(princ->exp_date_txt, ctime(&lcltim), + DATE_SZ); + lcltim = princ->mod_date = entry.mod_date; + strncpy(princ->mod_date_txt, ctime(&lcltim), + DATE_SZ); + princ->attributes = entry.attributes; + princ->max_life = entry.max_life; + princ->kdc_key_ver = entry.mkvno; + princ->key_version = entry.kvno; + retval = krb5_524_conv_principal(entry.mod_name, + princ->mod_name, princ->mod_instance, + realm); + if (retval) + return retval; + return 0; +} + +krb5_error_code +kadm_princ2entry(princ, entry) + Principal princ; + krb5_db_entry *entry; +{ + krb5_error_code retval; + + /* NOTE: does not convert the key */ + memset(entry, 0, sizeof (*entry)); + /* yeah yeah stupid v4 database doesn't store realm names */ + retval = krb5_425_conv_principal(princ.name, princ.instance, + server_parm.krbrlm, &entry->principal); + if (retval) + return retval; + entry->kvno = princ.key_version; + entry->max_life = princ.max_life; + entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */ + entry->mkvno = server_parm.mkvno; /* XXX */ + entry->expiration = princ.exp_date; + retval = krb5_425_conv_principal(princ.mod_name, princ.mod_instance, + server_parm.krbrlm, &entry->mod_name); + if (retval) + return retval; + entry->mod_date = princ.mod_date; + entry->attributes = princ.attributes; + entry->salt_type = KRB5_KDB_SALTTYPE_V4; +} + +check_access(pname, pinst, prealm, acltype) +char *pname; +char *pinst; +char *prealm; +enum acl_types acltype; +{ + char checkname[MAX_K_NAME_SZ]; + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm); + + switch (acltype) { + case ADDACL: + (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE); + break; + case GETACL: + (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE); + break; + case MODACL: + (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE); + break; + case STABACL: + (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE); + break; + } + return(acl_check(filename, checkname)); +} + +int +wildcard(str) +char *str; +{ + if (!strcmp(str, WILDCARD_STR)) + return(1); + return(0); +} + +#define failadd(code) { (void) syslog(LOG_ERR, "FAILED addding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_add_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_principal default_princ; + krb5_error_code retval; + krb5_db_entry newentry, tmpentry; + krb5_boolean more; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + int numfound; + + if (!check_access(rname, rinstance, rrealm, ADDACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failadd(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + kadm_vals_to_prin(valsin->fields, &data_i, valsin); + (void) strncpy(data_i.name, valsin->name, ANAME_SZ); + (void) strncpy(data_i.instance, valsin->instance, INST_SZ); + + if (!IS_FIELD(KADM_EXPDATE,valsin->fields)) + data_i.exp_date = server_parm.expiration; + if (!IS_FIELD(KADM_ATTR,valsin->fields)) + data_i.attributes = server_parm.flags; + if (!IS_FIELD(KADM_MAXLIFE,valsin->fields)) + data_i.max_life = server_parm.max_life; + + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) + failadd(KADM_NOMEM); + data_i.key_low = ntohl(data_i.key_low); + data_i.key_high = ntohl(data_i.key_high); + memcpy(newpw.contents, &data_i.key_low, 4); + memcpy((char *)(((krb4_int32 *) newpw.contents) + 1), &data_i.key_high, 4); + newpw.length = 8; + newpw.keytype = KEYTYPE_DES; + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset((char *)newpw.contents, 0, newpw.length); + free(newpw.contents); + if (retval) { + failadd(retval); + } + data_o = data_i; + + retval = kadm_princ2entry(data_i, &newentry); + if (retval) { + memset((char *)encpw.contents, 0, encpw.length); + free(encpw.contents); + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + + newentry.key = encpw; + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, + &tmpentry, &numfound, &more); + + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + krb5_db_free_principal(&tmpentry, numfound); + if (numfound) { + krb5_db_free_principal(&newentry, 1); + failadd(KADM_INUSE); + } else { + newentry.kvno = ++data_i.key_version; + if (retval = krb5_timeofday(&newentry.mod_date)) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + if (newentry.mod_name) + krb5_free_principal(newentry.mod_name); + newentry.mod_name = NULL; /* in case the following breaks */ + retval = krb5_425_conv_principal(rname, rinstance, rrealm, + &newentry.mod_name); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(&newentry, &numfound); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + if (!numfound) { + krb5_db_free_principal(&newentry, 1); + failadd(KADM_UK_SERROR); + } else { + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, + &tmpentry, + &numfound, &more); + krb5_db_free_principal(&newentry, 1); + if (retval) { + failadd(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(&tmpentry, numfound); + failadd(KADM_UK_RERROR); + } + kadm_entry2princ(tmpentry, &data_o); + krb5_db_free_principal(&tmpentry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } +} +#undef failadd + +#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; /* what they wannt to get */ +u_char *flags; /* which fields we want */ +Kadm_vals *valsout; /* what data is there */ +{ + int numfound; /* check how many were returned */ + krb5_boolean more; /* To point to more name.instances */ + Principal data_o; /* Data object to hold Principal */ + krb5_principal inprinc; + krb5_db_entry entry; + krb5_error_code retval; + + if (!check_access(rname, rinstance, rrealm, GETACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failget(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(valsin->name, valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failget(retval); + /* Look up the record in the database */ + numfound = 1; + retval = krb5_db_get_principal(inprinc, &entry, &numfound, &more); + krb5_free_principal(inprinc); + if (retval) { + failget(retval); + } else if (!numfound || more) { + failget(KADM_NOENTRY); + } + retval = kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(&entry, 1); + if (retval) { + failget(retval); + } + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ +} +#undef failget + +#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; } + +kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin1, *valsin2; /* holds the parameters being + passed in */ +Kadm_vals *valsout; /* the actual record which is returned */ +{ + int numfound; + krb5_boolean more; + Principal data_o, temp_key; + u_char fields[4]; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + krb5_error_code retval; + krb5_principal theprinc, rprinc; + krb5_db_entry newentry, odata; + + if (wildcard(valsin1->name) || wildcard(valsin1->instance)) { + failmod(KADM_ILL_WILDCARD); + } + + if (!check_access(rname, rinstance, rrealm, MODACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry", + rname, rinstance, rrealm, valsin1->name, valsin1->instance); + return KADM_UNAUTH; + } + + syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ", + valsin1->name, valsin1->instance, rname, rinstance, rrealm); + krb5_425_conv_principal(valsin1->name, valsin1->instance, + server_parm.krbrlm, &theprinc); + if (retval) + failmod(retval); + numfound = 1; + retval = krb5_db_get_principal(theprinc, &newentry, &numfound, &more); + if (retval) { + krb5_free_principal(theprinc); + failmod(retval); + } else if (numfound == 1) { + kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2); + krb5_free_principal(newentry.principal); + newentry.principal = theprinc; + if (IS_FIELD(KADM_EXPDATE,valsin2->fields)) + newentry.expiration = temp_key.exp_date; + if (IS_FIELD(KADM_ATTR,valsin2->fields)) + newentry.attributes = temp_key.attributes; + if (IS_FIELD(KADM_MAXLIFE,valsin2->fields)) + newentry.max_life = temp_key.max_life; + if (IS_FIELD(KADM_DESKEY,valsin2->fields)) { + newentry.kvno++; + newentry.mkvno = server_parm.mkvno; + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(&newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(KADM_NOMEM); + } + newpw.length = 8; + newpw.keytype = KEYTYPE_DES; + temp_key.key_low = ntohl(temp_key.key_low); + temp_key.key_high = ntohl(temp_key.key_high); + memcpy(newpw.contents, &temp_key.key_low, 4); + memcpy(newpw.contents + 4, &temp_key.key_high, 4); + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset(newpw.contents, 0, newpw.length); + free(newpw.contents); + memset((char *)&temp_key, 0, sizeof(temp_key)); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + if (newentry.key.contents) { + memset((char *)newentry.key.contents, 0, newentry.key.length); + free(newentry.key.contents); + } + newentry.key = encpw; + } + if (retval = krb5_timeofday(&newentry.mod_date)) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + retval = krb5_425_conv_principal(rname, rinstance, rrealm, + &newentry.mod_name); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&newentry, &numfound); + memset((char *)&data_o, 0, sizeof(data_o)); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } else { + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, &odata, + &numfound, &more); + krb5_db_free_principal(&newentry, 1); + if (retval) { + failmod(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(&odata, numfound); + failmod(KADM_UK_RERROR); + } + retval = kadm_entry2princ(odata, &data_o); + krb5_db_free_principal(&odata, 1); + if (retval) + failmod(retval); + memset((char *) fields, 0, sizeof(fields)); + SET_FIELD(KADM_NAME,fields); + SET_FIELD(KADM_INST,fields); + SET_FIELD(KADM_EXPDATE,fields); + SET_FIELD(KADM_ATTR,fields); + SET_FIELD(KADM_MAXLIFE,fields); + kadm_prin_to_vals(fields, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } else { + failmod(KADM_NOENTRY); + } +} +#undef failmod + +#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_change (rname, rinstance, rrealm, newpw) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +{ + int numfound; + krb5_boolean more; + krb5_principal rprinc; + krb5_error_code retval; + krb5_keyblock localpw; + krb5_encrypted_keyblock encpw; + krb5_db_entry odata; + + if (strcmp(server_parm.krbrlm, rrealm)) { + syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n", + rname, rinstance, rrealm); + return(KADM_WRONG_REALM); + } + + if (wildcard(rname) || wildcard(rinstance)) { + failchange(KADM_ILL_WILDCARD); + } + syslog(LOG_INFO, "'%s.%s@%s' wants to change its password", + rname, rinstance, rrealm); + retval = krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &rprinc); + if (retval) + failchange(retval); + if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL) + failchange(KADM_NOMEM); + memcpy(localpw.contents, newpw, 8); + localpw.keytype = KEYTYPE_DES; + localpw.length = 8; + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &localpw, &encpw); + memset((char *)localpw.contents, 0, 8); + free(localpw.contents); + if (retval) { + krb5_free_principal(rprinc); + failchange(retval); + } + numfound = 1; + retval = krb5_db_get_principal(rprinc, &odata, &numfound, &more); + krb5_free_principal(rprinc); + if (retval) { + failchange(retval); + } else if (numfound == 1) { + odata.key = encpw; + odata.kvno++; + odata.mkvno = server_parm.mkvno; + if (retval = krb5_timeofday(&odata.mod_date)) { + krb5_db_free_principal(&odata, 1); + failchange(retval); + } + krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &odata.mod_name); + if (retval) { + krb5_db_free_principal(&odata, 1); + failchange(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&odata, &numfound); + krb5_db_free_principal(&odata, 1); + if (retval) { + failchange(retval); + } else if (more) { + failchange(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, + "'%s.%s@%s' password changed.", rname, rinstance, rrealm); + return KADM_SUCCESS; + } + } + else { + failchange(KADM_NOENTRY); + } +} +#undef failchange + +check_pw(newpw, checkstr) + des_cblock newpw; + char *checkstr; +{ +#ifdef NOENCRYPTION + return 0; +#else /* !NOENCRYPTION */ + des_cblock checkdes; + + (void) des_string_to_key(checkstr, checkdes); + return(!memcmp(checkdes, newpw, sizeof(des_cblock))); +#endif /* NOENCRYPTION */ +} + +char *reverse(str) + char *str; +{ + static char newstr[80]; + char *p, *q; + int i; + + i = strlen(str); + if (i >= sizeof(newstr)) + i = sizeof(newstr)-1; + p = str+i-1; + q = newstr; + q[i]='\0'; + for(; i > 0; i--) + *q++ = *p--; + + return(newstr); +} + +int lower(str) + char *str; +{ + register char *cp; + int effect=0; + + for (cp = str; *cp; cp++) { + if (isupper(*cp)) { + *cp = tolower(*cp); + effect++; + } + } + return(effect); +} + +des_check_gecos(gecos, newpw) + char *gecos; + des_cblock newpw; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + if (lower(cp)) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + } + cp = ncp; + } else + break; + } + return(0); +} + +str_check_gecos(gecos, pwstr) + char *gecos; + char *pwstr; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (!strcasecmp(pwstr, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (!strcasecmp(pwstr, tcp)) + return(KADM_INSECURE_PW); + cp = ncp; + } else + break; + } + return(0); +} + + +kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +char *pwstring; +{ + static DBM *pwfile = NULL; + int retval; + datum passwd, entry; + struct passwd *ent; +#ifdef HESIOD + extern struct passwd *hes_getpwnam(); +#endif + + if (pwstring && !check_pw(newpw, pwstring)) + /* + * Someone's trying to toy with us.... + */ + return(KADM_PW_MISMATCH); + if (pwstring && (strlen(pwstring) < 5)) + return(KADM_INSECURE_PW); + if (!pwfile) { + pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644); + } + if (pwfile) { + passwd.dptr = (char *) newpw; + passwd.dsize = 8; + entry = dbm_fetch(pwfile, passwd); + if (entry.dptr) + return(KADM_INSECURE_PW); + } + if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname))) + return(KADM_INSECURE_PW); +#ifdef HESIOD + ent = hes_getpwnam(rname); +#else + ent = getpwnam(rname); +#endif + if (ent && ent->pw_gecos) { + if (pwstring) + retval = str_check_gecos(ent->pw_gecos, pwstring); + else + retval = des_check_gecos(ent->pw_gecos, newpw); + if (retval) + return(retval); + } + return(0); +} + +/* + * This routine checks to see if a principal should be considered an + * allowable service name which can be changed by kadm_change_srvtab. + * + * We do this check by using the ACL library. This makes the + * (relatively) reasonable assumption that both the name and the + * instance will not contain '.' or '@'. + */ +kadm_check_srvtab(name, instance) + char *name; + char *instance; +{ + FILE *f; + char filename[MAXPATHLEN]; + char buf[ANAME_SZ], *cp; + extern char *acldir; + + (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE); + if (!acl_check(filename, name)) + return(KADM_NOT_SERV_PRINC); + + (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE); + if (acl_check(filename, instance)) + return(KADM_NOT_SERV_PRINC); + return 0; +} + +/* + * Routine to allow some people to change the key of a srvtab + * principal to a random key, which the admin server will return to + * the client. + */ +#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_chg_srvtab(rname, rinstance, rrealm, values) + char *rname; /* requestors name */ + char *rinstance; /* requestors instance */ + char *rrealm; /* requestors realm */ + Kadm_vals *values; +{ + int numfound, ret, isnew = 0; + des_cblock new_key; + Principal principal; + krb5_principal inprinc; + krb5_error_code retval; + krb5_db_entry odata; + krb5_boolean more; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + + if (!check_access(rname, rinstance, rrealm, STABACL)) + failsrvtab(KADM_UNAUTH); + if (wildcard(rname) || wildcard(rinstance)) + failsrvtab(KADM_ILL_WILDCARD); + if (ret = kadm_check_srvtab(values->name, values->instance)) + failsrvtab(ret); + + retval = krb5_425_conv_principal(values->name, values->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failsrvtab(retval); + /* + * OK, get the entry + */ + numfound = 1; + retval = krb5_db_get_principal(inprinc, &odata, &numfound, &more); + if (retval) { + krb5_free_principal(inprinc); + failsrvtab(retval); + } else if (numfound) { + odata.kvno++; + } else { + /* + * This is a new srvtab entry that we're creating + */ + isnew = 1; + memset((char *)&odata, 0, sizeof (odata)); + odata.principal = inprinc; + odata.kvno = 1; + odata.max_life = server_parm.max_life; + odata.max_renewable_life = server_parm.max_rlife; + odata.mkvno = server_parm.mkvno; + odata.expiration = server_parm.expiration; + odata.attributes = 0; + } + +#ifdef NOENCRYPTION + memset(new_key, 0, sizeof(new_key)); + new_key[0] = 127; +#else + des_new_random_key(new_key); +#endif + /* + * Store the new key in the return structure; also fill in the + * rest of the fields. + */ + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(&odata, 1); + failsrvtab(KADM_NOMEM); + } + newpw.keytype = KEYTYPE_DES; + newpw.length = 8; + memcpy((char *)newpw.contents, new_key, 8); + memset((char *)new_key, 0, sizeof (new_key)); + memcpy((char *)&values->key_low, newpw.contents, 4); + memcpy((char *)&values->key_high, newpw.contents + 4, 4); + values->key_low = htonl(values->key_low); + values->key_high = htonl(values->key_high); + values->max_life = odata.kvno; + values->exp_date = odata.expiration; + values->attributes = odata.attributes; + memset(values->fields, 0, sizeof(values->fields)); + SET_FIELD(KADM_NAME, values->fields); + SET_FIELD(KADM_INST, values->fields); + SET_FIELD(KADM_EXPDATE, values->fields); + SET_FIELD(KADM_ATTR, values->fields); + SET_FIELD(KADM_MAXLIFE, values->fields); + SET_FIELD(KADM_DESKEY, values->fields); + + /* + * Encrypt the new key with the master key, and then update + * the database record + */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset((char *)newpw.contents, 0, 8); + free(newpw.contents); + if (odata.key.contents) { + memset((char *)odata.key.contents, 0, odata.key.length); + free(odata.key.contents); + } + odata.key = encpw; + if (retval) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + if (retval = krb5_timeofday(&odata.mod_date)) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + retval = krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &odata.mod_name); + if (retval) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&odata, &numfound); + krb5_db_free_principal(&odata, 1); + if (retval) { + failsrvtab(retval); + } + else if (!numfound) { + failsrvtab(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.", + values->name, values->instance, + numfound ? "changed" : "created", + rname, rinstance, rrealm); + return KADM_DATA; + } +} + +#undef failsrvtab diff --git a/src/kadmin/v4server/attic/kadm_ser_wrap.c b/src/kadmin/v4server/attic/kadm_ser_wrap.c new file mode 100644 index 000000000..bca8b8e05 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_ser_wrap.c @@ -0,0 +1,288 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side support functions + */ + +#ifndef lint +static char rcsid_module_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> +/* +kadm_ser_wrap.c +unwraps wrapped packets and calls the appropriate server subroutine +*/ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <netdb.h> +#include <sys/socket.h> +#include <krb.h> +#include <kadm.h> +#include <kadm_err.h> +#include <krb_err.h> +#include <syslog.h> +#include "kadm_server.h" + +#ifdef OVSEC_KADM +#include <ovsec_admin/admin.h> +extern void *ovsec_handle; +#endif + +Kadm_Server server_parm; + +/* +kadm_ser_init +set up the server_parm structure +*/ +kadm_ser_init(inter, realm) + int inter; /* interactive or from file */ + char realm[]; +{ + struct servent *sep; + struct hostent *hp; + char hostname[MAXHOSTNAMELEN]; + char *mkey_name; + krb5_error_code retval; + int numfound = 1; + krb5_boolean more; + krb5_db_entry master_entry; + + if (gethostname(hostname, sizeof(hostname))) + return KADM_NO_HOSTNAME; + + (void) strcpy(server_parm.sname, PWSERV_NAME); + (void) strcpy(server_parm.sinst, KRB_MASTER); + (void) strcpy(server_parm.krbrlm, realm); + if (krb5_build_principal(&server_parm.sprinc, + strlen(realm), + realm, + PWSERV_NAME, + KRB_MASTER, 0)) + return KADM_NO_MAST; + + /* setting up the addrs */ + server_parm.admin_fd = -1; + if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL) + return KADM_NO_SERV; + memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr)); + server_parm.admin_addr.sin_family = AF_INET; + if ((hp = gethostbyname(hostname)) == NULL) + return KADM_NO_HOSTNAME; + memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr, + hp->h_length); + server_parm.admin_addr.sin_port = sep->s_port; + + /* setting up the database */ + mkey_name = KRB5_KDB_M_NAME; + server_parm.master_keyblock.keytype = KEYTYPE_DES; +#ifdef PROVIDE_DES_CBC_CRC +#ifdef KRB5B4 + server_parm.master_encblock.crypto_entry = krb5_des_cst_entry.system; +#else + server_parm.master_encblock.crypto_entry = &mit_des_cryptosystem_entry; +#endif /* KRB5B4 */ +#else + error(You gotta figure out what cryptosystem to use in the KDC); +#endif + retval = krb5_db_setup_mkey_name(mkey_name, realm, (char **) 0, + &server_parm.master_princ); + if (retval) + return KADM_NO_MAST; + krb5_db_fetch_mkey(server_parm.master_princ, + &server_parm.master_encblock, + (inter == 1), FALSE, NULL, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_MAST; + retval = krb5_db_verify_master_key(server_parm.master_princ, + &server_parm.master_keyblock, + &server_parm.master_encblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_process_key(&server_parm.master_encblock, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_VERI; + + retval = krb5_db_get_principal(server_parm.master_princ, + &master_entry, &numfound, &more); + if (retval || more || !numfound) + return KADM_NO_VERI; + server_parm.max_life = master_entry.max_life; + server_parm.max_rlife = master_entry.max_renewable_life; + server_parm.expiration = master_entry.expiration; + server_parm.mkvno = master_entry.kvno; + /* don't set flags, as master has some extra restrictions + (??? quoted from kdb_edit.c) */ + krb5_db_free_principal(&master_entry, numfound); + return KADM_SUCCESS; +} + + +static void errpkt(dat, dat_len, code) +u_char **dat; +int *dat_len; +int code; +{ + krb4_uint32 retcode; + char *pdat; + + free((char *)*dat); /* free up req */ + *dat_len = KADM_VERSIZE + sizeof(krb4_uint32); + *dat = (u_char *) malloc((unsigned)*dat_len); + if (!(*dat)) { + syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len); + abort(); + } + pdat = (char *) *dat; + retcode = htonl((krb4_uint32) code); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb4_uint32)); + return; +} + +/* +kadm_ser_in +unwrap the data stored in dat, process, and return it. +*/ +kadm_ser_in(dat,dat_len) +u_char **dat; +int *dat_len; +{ + u_char *in_st; /* pointer into the sent packet */ + int in_len,retc; /* where in packet we are, for + returns */ + krb4_uint32 r_len; /* length of the actual packet */ + KTEXT_ST authent; /* the authenticator */ + AUTH_DAT ad; /* who is this, klink */ + krb4_uint32 ncksum; /* checksum of encrypted data */ + des_key_schedule sess_sched; /* our schedule */ + MSG_DAT msg_st; + u_char *retdat, *tmpdat; + int retval, retlen; + + if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) { + errpkt(dat, dat_len, KADM_BAD_VER); + return KADM_BAD_VER; + } + in_len = KADM_VERSIZE; + /* get the length */ + if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0) + return KADM_LENGTH_ERROR; + in_len += retc; + authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb4_uint32); + memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length); + authent.mbz = 0; + /* service key should be set before here */ + if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst, + server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0)) + { + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + +#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));} + + in_st = *dat + *dat_len - r_len; +#ifdef NOENCRYPTION + ncksum = 0; +#else + ncksum = quad_cksum((des_cblock *)in_st, (des_cblock *)0, (krb4_int32) r_len, 0, + (des_cblock *)ad.session); +#endif + if (ncksum!=ad.checksum) { /* yow, are we correct yet */ + clr_cli_secrets(); + errpkt(dat, dat_len,KADM_BAD_CHK); + return KADM_BAD_CHK; + } +#ifdef NOENCRYPTION + memset(sess_sched, 0, sizeof(sess_sched)); +#else + des_key_sched(ad.session, sess_sched); +#endif + if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session, + &server_parm.recv_addr, + &server_parm.admin_addr, &msg_st)) { + clr_cli_secrets(); + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + switch (msg_st.app_data[0]) { + case CHANGE_PW: + retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#ifndef OVSEC_KADM + case ADD_ENT: + retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case GET_ENT: + retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case MOD_ENT: + retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHECK_PW: + retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHG_STAB: + retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#endif /* OVSEC_KADM */ + default: + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_OPCODE); + return KADM_NO_OPCODE; + } + /* Now seal the response back into a priv msg */ + free((char *)*dat); + tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE + + sizeof(krb4_uint32))); + if (!tmpdat) { + clr_cli_secrets(); + syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!", + retlen + KADM_VERSIZE + sizeof(krb4_uint32)); + errpkt(dat, dat_len, KADM_NOMEM); + return KADM_NOMEM; + } + (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE); + retval = htonl((krb4_uint32)retval); + memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb4_uint32)); + if (retlen) { + memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb4_uint32), (char *)retdat, + retlen); + free((char *)retdat); + } + /* slop for mk_priv stuff */ + *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE + + sizeof(krb4_uint32) + 200)); + if ((*dat_len = krb_mk_priv(tmpdat, *dat, + (krb4_uint32) (retlen + KADM_VERSIZE + + sizeof(krb4_uint32)), + sess_sched, + ad.session, &server_parm.admin_addr, + &server_parm.recv_addr)) < 0) { + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_ENCRYPT); + free(tmpdat); + return KADM_NO_ENCRYPT; + } + clr_cli_secrets(); + free(tmpdat); + return KADM_SUCCESS; +} diff --git a/src/kadmin/v4server/attic/kadm_server.c b/src/kadmin/v4server/attic/kadm_server.c new file mode 100644 index 000000000..143af5d4e --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_server.c @@ -0,0 +1,546 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side subroutines + */ + +#ifndef lint +static char rcsid_kadm_server_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> + +#include <krb5/osconf.h> +#include <krb5/wordsize.h> + +#include <stdio.h> +#ifdef USE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#include <string.h> + +#ifdef OVSEC_KADM +#include <com_err.h> +#include <ovsec_admin/admin.h> +#include <ovsec_admin/chpass_util_strings.h> +#include <krb5/kdb.h> +extern void *ovsec_handle; +#endif + +#include <kadm.h> +#include <kadm_err.h> + +int fascist_cpw = 0; /* Be fascist about insecure passwords? */ + +#ifdef OVSEC_KADM +char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n"; + +#else /* !OVSEC_KADM */ + +char bad_pw_err[] = + "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n"; + +char bad_pw_warn[] = + "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n"; + +char check_pw_msg[] = + "You have entered an insecure password. You should choose another.\n\n"; + +char pw_blurb[] = +"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n"; + +#endif /* OVSEC_KADM */ + +/* from V4 month_sname.c -- was not part of API */ +/* + * Given an integer 1-12, month_sname() returns a string + * containing the first three letters of the corresponding + * month. Returns 0 if the argument is out of range. + */ + +static char *month_sname(n) + int n; +{ + static char *name[] = { + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" + }; + return((n < 1 || n > 12) ? 0 : name [n-1]); +} + +/* from V4 log.c -- was not part of API */ + +/* + * krb_log() is used to add entries to the logfile (see krb_set_logfile() + * below). Note that it is probably not portable since it makes + * assumptions about what the compiler will do when it is called + * with less than the correct number of arguments which is the + * way it is usually called. + * + * The log entry consists of a timestamp and the given arguments + * printed according to the given "format". + * + * The log file is opened and closed for each log entry. + * + * The return value is undefined. + */ + +/* static char *log_name = KRBLOG; */ +/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */ +static char *log_name = KADM_SYSLOG; + +static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0) + char *format; + int a1,a2,a3,a4,a5,a6,a7,a8,a9,a0; +{ + FILE *logfile, *fopen(); + time_t now; + struct tm *tm; + + if ((logfile = fopen(log_name,"a")) == NULL) + return; + + (void) time(&now); + tm = localtime(&now); + + fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday, + month_sname(tm->tm_mon + 1),tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0); + fprintf(logfile,"\n"); + (void) fclose(logfile); + return; +} + + +/* +kadm_ser_cpw - the server side of the change_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : caller can change only own password + +Replaces the password (i.e. des key) of the caller with that specified in key. +Returns no actual data from the master server, since this is called by a user +*/ +kadm_ser_cpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int status, stvlen = 0; + int retval; + extern char *malloc(); + extern int kadm_approve_pw(); +#ifdef OVSEC_KADM + ovsec_kadm_principal_ent_t princ_ent; + ovsec_kadm_policy_ent_t pol_ent; + krb5_principal user_princ; + char msg_ret[1024], *time_string, *ptr; + const char *msg_ptr; + krb5_int32 now; + time_t until; +#endif + + /* take key off the stream, and change the database */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((((krb5_int32 *)newkey) + 1), &keyhigh, 4); + memcpy(newkey, &keylow, 4); + +#ifdef OVSEC_KADM + /* we don't use the client-provided key itself */ + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); + + if (no_pword) { + krb_log("Old-style change password request from '%s.%s@%s'!", + ad->pname, ad->pinst, ad->prealm); + *outlen = strlen(pw_required)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, pw_required); + } else { + *outlen = 0; + } + return KADM_INSECURE_PW; + } + + if (krb5_build_principal(&user_princ, + strlen(ad->prealm), + ad->prealm, + ad->pname, + *ad->pinst ? ad->pinst : 0, 0)) + /* this should never happen */ + return KADM_NOENTRY; + + *outlen = 0; + + if (retval = krb5_timeofday(&now)) { + msg_ptr = error_message(retval); + goto send_response; + } + + retval = ovsec_kadm_get_principal(ovsec_handle, user_princ, + &princ_ent); + if (retval != 0) { + msg_ptr = error_message(retval); + goto send_response; + } + + /* + * This daemon necessarily has the modify privilege, so + * ovsec_kadm_chpass_principal will allow it to violate the + * policy's minimum lifetime. Since that's A Bad Thing, we need + * to enforce it ourselves. Unfortunately, this means we are + * duplicating code from both ovsec_adm_server and + * ovsec_kadm_chpass_util(). + */ + if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) { + retval = ovsec_kadm_get_policy(ovsec_handle, + princ_ent->policy, + &pol_ent); + if (retval != 0) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + msg_ptr = error_message(retval); + goto send_response; + } + + /* make "now" a boolean, true == too soon */ + now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life); + + (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent); + + if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + retval = CHPASS_UTIL_PASSWORD_TOO_SOON; + + until = princ_ent->last_pwd_change + pol_ent->pw_min_life; + time_string = ctime(&until); + + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + + sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON), + time_string); + msg_ptr = msg_ret; + + goto send_response; + } + } + + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + + retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ, + pword, NULL, msg_ret); + msg_ptr = msg_ret; + (void) krb5_free_principal(user_princ); + +send_response: + + retval = convert_ovsec_to_kadm(retval); + + if (retval) { + /* don't send message on success because kpasswd.v4 will */ + /* print "password changed" too */ + *outlen = strlen(msg_ptr)+2; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, msg_ptr); + strcat(*datout, "\n"); + } else + *outlen = 0; + } + if (retval == KADM_INSECURE_PW) { + krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + +#else /* !OVSEC_KADM */ + + if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, + newkey, no_pword ? 0 : pword)) { + if (retval == KADM_PW_MISMATCH) { + /* + * Very strange!!! This means that the cleartext + * password which was sent and the DES cblock + * didn't match! + */ + (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } + if (fascist_cpw) { + *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, bad_pw_err); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); +#ifdef notdef + /* For debugging only, probably a bad idea */ + if (!no_pword) + (void) krb_log("The password was %s\n", pword); +#endif + return(retval); + } else { + *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, bad_pw_warn); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' used an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + } else { + *datout = 0; + *outlen = 0; + } + + retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey); + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); +#endif /* OVSEC_KADM */ + + return retval; +} + +/**********************************************************************/ +#ifndef OVSEC_KADM + +/* +kadm_ser_add - the server side of the add_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Adds and entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +kadm_ser_add(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_mod - the server side of the mod_entry routine + recieves : KTEXT, {values, values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Modifies all entries corresponding to the first values so they match the + second values. +returns the values for the changed entries +*/ +kadm_ser_mod(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals vals1, vals2, retvals; + int wh; + int status; + + if ((wh = stream_to_vals(dat, &vals1, len)) < 0) + return KADM_LENGTH_ERROR; + if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0) + return KADM_LENGTH_ERROR; + if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1, + &vals2, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_get + recieves : KTEXT, {values, flags} + returns : CKSUM, RETCODE, {count, values, values, values} + acl : su + +gets the fields requested by flags from all entries matching values +returns this data for each matching recipient, after a count of how many such + matches there were +*/ +kadm_ser_get(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + u_char fl[FLDSZ]; + int loop,wh; + int status; + + if ((wh = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + if (wh + FLDSZ > len) + return KADM_LENGTH_ERROR; + for (loop=FLDSZ-1; loop>=0; loop--) + fl[loop] = dat[wh++]; + if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm, + &values, fl, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_ckpw - the server side of the check_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : none + +Checks to see if the des key passed from the caller is a "secure" password. +*/ +kadm_ser_ckpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int stvlen = 0,status; + int retval; + extern char *malloc(); + extern int kadm_approve_pw(); + + /* take key off the stream, and check it */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + keylow = keyhigh = 0; + retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey, + no_pword ? 0 : pword); + memset(newkey, 0, sizeof(newkey)); + if (retval) { + *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, check_pw_msg); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent an insecure password to be checked", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } else { + *datout = 0; + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent a secure password to be checked", + ad->pname, ad->pinst, ad->prealm); + } + return(0); +} + +/* +kadm_ser_stab - the server side of the change_srvtab routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Creates or modifies the specified service principal to have a random +key, which is sent back to the client. The key version is returned in +the max_life field of the values structure. It's a hack, but it's a +backwards compatible hack.... +*/ +kadm_ser_stab(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values); + if (status == KADM_DATA) { + *outlen = vals_to_stream(&values,datout); + values.key_low = values.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +#endif /* !OVSEC_KADM */ diff --git a/src/kadmin/v4server/attic/kadm_server.h b/src/kadmin/v4server/attic/kadm_server.h new file mode 100644 index 000000000..4e6fd8c46 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_server.h @@ -0,0 +1,64 @@ +/* + * $Source$ + * $Author$ + * $Header$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Definitions for Kerberos administration server & client + */ + +#ifndef KADM_SERVER_DEFS +#define KADM_SERVER_DEFS + +#include <mit-copyright.h> +/* + * kadm_server.h + * Header file for the fourth attempt at an admin server + * Doug Church, December 28, 1989, MIT Project Athena + * ps. Yes that means this code belongs to athena etc... + * as part of our ongoing attempt to copyright all greek names + */ + +#include <sys/types.h> +#include <krb.h> +#include <des.h> + +#include <krb5/krb5.h> +#include <krb5/kdb.h> +#include <krb5/osconf.h> +#include <krb5/config.h> +#ifdef PROVIDE_DES_CBC_CRC +#include <krb5/mit-des.h> +#endif + +typedef struct { + struct sockaddr_in admin_addr; + struct sockaddr_in recv_addr; + int recv_addr_len; + int admin_fd; /* our link to clients */ + char sname[ANAME_SZ]; + char sinst[INST_SZ]; + char krbrlm[REALM_SZ]; + krb5_principal sprinc; + krb5_encrypt_block master_encblock; + krb5_principal master_princ; + krb5_keyblock master_keyblock; + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_kvno mkvno; +} Kadm_Server; + +#define ADD_ACL_FILE "/v4acl.add" +#define GET_ACL_FILE "/v4acl.get" +#define MOD_ACL_FILE "/v4acl.mod" +#define STAB_ACL_FILE "/v4acl.srvtab" +#define STAB_SERVICES_FILE "/v4stab_services" +#define STAB_HOSTS_FILE "/v4stab_bad_hosts" + +#endif /* KADM_SERVER_DEFS */ diff --git a/src/kadmin/v4server/attic/kadm_stream.c b/src/kadmin/v4server/attic/kadm_stream.c new file mode 100644 index 000000000..83aaa295c --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_stream.c @@ -0,0 +1,276 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Stream conversion functions for Kerberos administration server + */ + +#ifndef lint +static char rcsid_kadm_stream_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> +/* + kadm_stream.c + this holds the stream support routines for the kerberos administration server + + vals_to_stream: converts a vals struct to a stream for transmission + internals build_field_header, vts_[string, char, krb4_int32, short] + stream_to_vals: converts a stream to a vals struct + internals check_field_header, stv_[string, char, krb4_int32, short] + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits +*/ + +#include "kadm.h" +#include <string.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* +vals_to_stream + recieves : kadm_vals *, u_char * + returns : a realloced and filled in u_char * + +this function creates a byte-stream representation of the kadm_vals structure +*/ +vals_to_stream(dt_in, dt_out) +Kadm_vals *dt_in; +u_char **dt_out; +{ + int vsloop, stsize; /* loop counter, stream size */ + + stsize = build_field_header(dt_in->fields, dt_out); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_in->fields)) { + switch (vsloop) { + case KADM_NAME: + stsize+=vts_string(dt_in->name, dt_out, stsize); + break; + case KADM_INST: + stsize+=vts_string(dt_in->instance, dt_out, stsize); + break; + case KADM_EXPDATE: + stsize+=vts_long(dt_in->exp_date, dt_out, stsize); + break; + case KADM_ATTR: + stsize+=vts_short(dt_in->attributes, dt_out, stsize); + break; + case KADM_MAXLIFE: + stsize+=vts_char(dt_in->max_life, dt_out, stsize); + break; + case KADM_DESKEY: + stsize+=vts_long(dt_in->key_high, dt_out, stsize); + stsize+=vts_long(dt_in->key_low, dt_out, stsize); + break; + default: + break; + } +} + return(stsize); +} + +build_field_header(cont, st) +u_char *cont; /* container for fields data */ +u_char **st; /* stream */ +{ + *st = (u_char *) malloc (4); + memcpy((void *) *st, (void *) cont, 4); + return 4; /* return pointer to current stream location */ +} + +vts_string(dat, st, loc) +char *dat; /* a string to put on the stream */ +u_char **st; /* base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1)); + memcpy((char *)(*st + loc), dat, strlen(dat)+1); + return strlen(dat)+1; +} + +vts_short(dat, st, loc) +u_short dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + u_short temp; /* to hold the net order short */ + + temp = htons(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short)); + return sizeof(u_short); +} + +vts_long(dat, st, loc) +krb4_uint32 dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + krb4_uint32 temp; /* to hold the net order short */ + + temp = htonl(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb4_uint32))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb4_uint32)); + return sizeof(krb4_uint32); +} + + +vts_char(dat, st, loc) +u_char dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char))); + (*st)[loc] = (u_char) dat; + return 1; +} + +/* +stream_to_vals + recieves : u_char *, kadm_vals * + returns : a kadm_vals filled in according to u_char * + +this decodes a byte stream represntation of a vals struct into kadm_vals +*/ +stream_to_vals(dt_in, dt_out, maxlen) +u_char *dt_in; +Kadm_vals *dt_out; +int maxlen; /* max length to use */ +{ + register int vsloop, stsize; /* loop counter, stream size */ + register int status; + krb4_int32 lcllong; + + memset((char *) dt_out, 0, sizeof(*dt_out)); + + stsize = check_field_header(dt_in, dt_out->fields, maxlen); + if (stsize < 0) + return(-1); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_out->fields)) + switch (vsloop) { + case KADM_NAME: + if ((status = stv_string(dt_in, dt_out->name, stsize, + sizeof(dt_out->name), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_INST: + if ((status = stv_string(dt_in, dt_out->instance, stsize, + sizeof(dt_out->instance), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_EXPDATE: + if ((status = stv_long(dt_in, &lcllong, stsize, + maxlen)) < 0) + return(-1); + dt_out->exp_date = lcllong; + stsize += status; + break; + case KADM_ATTR: + if ((status = stv_short(dt_in, &dt_out->attributes, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_MAXLIFE: + if ((status = stv_char(dt_in, &dt_out->max_life, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_DESKEY: + if ((status = stv_long(dt_in, &dt_out->key_high, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + if ((status = stv_long(dt_in, &dt_out->key_low, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + default: + break; + } + return stsize; +} + +check_field_header(st, cont, maxlen) +u_char *st; /* stream */ +u_char *cont; /* container for fields data */ +int maxlen; +{ + if (4 > maxlen) + return(-1); + memcpy((char *) cont, (char *) st, 4); + return 4; /* return pointer to current stream location */ +} + +stv_string(st, dat, loc, stlen, maxlen) +register u_char *st; /* base pointer to the stream */ +char *dat; /* a string to read from the stream */ +register int loc; /* offset into the stream for current data */ +int stlen; /* max length of string to copy in */ +int maxlen; /* max length of input stream */ +{ + int maxcount; /* max count of chars to copy */ + + maxcount = min(maxlen - loc, stlen); + + (void) strncpy(dat, (char *)st + loc, maxcount); + + if (dat[maxcount-1]) /* not null-term --> not enuf room */ + return(-1); + return strlen(dat)+1; +} + +stv_short(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_short *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + u_short temp; /* to hold the net order short */ + + if (loc + sizeof(u_short) > maxlen) + return(-1); + memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(u_short)); + *dat = ntohs(temp); /* convert to network order */ + return sizeof(u_short); +} + +stv_long(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +krb4_uint32 *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; /* maximum length of st */ +{ + krb4_uint32 temp; /* to hold the net order short */ + + if (loc + sizeof(krb4_uint32) > maxlen) + return(-1); + memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(krb4_uint32)); + *dat = ntohl(temp); /* convert to network order */ + return sizeof(krb4_uint32); +} + +stv_char(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_char *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + if (loc + 1 > maxlen) + return(-1); + *dat = *(st + loc); + return 1; +} + diff --git a/src/kadmin/v4server/attic/kadm_supp.c b/src/kadmin/v4server/attic/kadm_supp.c new file mode 100644 index 000000000..cf4ba40f4 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_supp.c @@ -0,0 +1,114 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Support functions for Kerberos administration server & clients + */ + +#ifndef lint +static char rcsid_kadm_supp_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> +/* + kadm_supp.c + this holds the support routines for the kerberos administration server + + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits + prin_vals: prints out data associated with a Principal in the vals + structure +*/ + +#include "kadm.h" +#include "krb_db.h" + +/* +prin_vals: + recieves : a vals structure +*/ +prin_vals(vals) +Kadm_vals *vals; +{ + printf("Info in Database for %s.%s:\n", vals->name, vals->instance); + printf(" Max Life: %d Exp Date: %s\n",vals->max_life, + asctime(localtime(&vals->exp_date))); + printf(" Attribs: %.2x key: %u %u\n",vals->attributes, + vals->key_low, vals->key_high); +} + +#ifdef notdef +nierror(s) +int s; +{ + extern char *error_message(); + printf("Kerberos admin server loses..... %s\n",error_message(s)); + return(s); +} +#endif + +/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal, + it copies the fields in Principal specified by fields into Kadm_vals, + i.e from old to new */ + +kadm_prin_to_vals(fields, new, old) +u_char fields[FLDSZ]; +Kadm_vals *new; +Principal *old; +{ + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) { + (void) strncpy(new->name, old->name, ANAME_SZ); + SET_FIELD(KADM_NAME, new->fields); + } + if (IS_FIELD(KADM_INST,fields)) { + (void) strncpy(new->instance, old->instance, INST_SZ); + SET_FIELD(KADM_INST, new->fields); + } + if (IS_FIELD(KADM_EXPDATE,fields)) { + new->exp_date = old->exp_date; + SET_FIELD(KADM_EXPDATE, new->fields); + } + if (IS_FIELD(KADM_ATTR,fields)) { + new->attributes = old->attributes; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_MAXLIFE,fields)) { + new->max_life = old->max_life; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + SET_FIELD(KADM_DESKEY, new->fields); + } +} + +kadm_vals_to_prin(fields, new, old) +u_char fields[FLDSZ]; +Principal *new; +Kadm_vals *old; +{ + + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) + (void) strncpy(new->name, old->name, ANAME_SZ); + if (IS_FIELD(KADM_INST,fields)) + (void) strncpy(new->instance, old->instance, INST_SZ); + if (IS_FIELD(KADM_EXPDATE,fields)) + new->exp_date = old->exp_date; + if (IS_FIELD(KADM_ATTR,fields)) + new->attributes = old->attributes; + if (IS_FIELD(KADM_MAXLIFE,fields)) + new->max_life = old->max_life; + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + } +} diff --git a/src/kadmin/v4server/configure.in b/src/kadmin/v4server/configure.in new file mode 100644 index 000000000..9d45c1e16 --- /dev/null +++ b/src/kadmin/v4server/configure.in @@ -0,0 +1,16 @@ +AC_INIT(admin_server.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_CHECK_HEADERS(sys/time.h unistd.h stdlib.h) +CHECK_SIGNALS +CHECK_WAIT_TYPE +AC_PROG_AWK +USE_KADMCLNT_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +USE_KRB4_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/v4server/kadm_err.et b/src/kadmin/v4server/kadm_err.et new file mode 100644 index 000000000..a19273083 --- /dev/null +++ b/src/kadmin/v4server/kadm_err.et @@ -0,0 +1,57 @@ +# kadmin.v4/server/kadm_err.et +# +# Copyright 1988 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, please see the file +# <mit-copyright.h>. +# +# Kerberos administration server error table +# + et kadm + +# KADM_SUCCESS, as all success codes should be, is zero + +ec KADM_RCSID, "$Header$" +# /* Building and unbuilding the packet errors */ +ec KADM_NO_REALM, "Cannot fetch local realm" +ec KADM_NO_CRED, "Unable to fetch credentials" +ec KADM_BAD_KEY, "Bad key supplied" +ec KADM_NO_ENCRYPT, "Can't encrypt data" +ec KADM_NO_AUTH, "Cannot encode/decode authentication info" +ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm" +ec KADM_NO_ROOM, "Packet is too large" +ec KADM_BAD_VER, "Version number is incorrect" +ec KADM_BAD_CHK, "Checksum does not match" +ec KADM_NO_READ, "Unsealing private data failed" +ec KADM_NO_OPCODE, "Unsupported operation" +ec KADM_NO_HOST, "Could not find administrating host" +ec KADM_UNK_HOST, "Administrating host name is unknown" +ec KADM_NO_SERV, "Could not find service name in services database" +ec KADM_NO_SOCK, "Could not create socket" +ec KADM_NO_CONN, "Could not connect to server" +ec KADM_NO_HERE, "Could not fetch local socket address" +ec KADM_NO_MAST, "Could not fetch master key" +ec KADM_NO_VERI, "Could not verify master key" + +# /* From the server side routines */ +ec KADM_INUSE, "Entry already exists in database" +ec KADM_UK_SERROR, "Database store error" +ec KADM_UK_RERROR, "Database read error" +ec KADM_UNAUTH, "Insufficient access to perform requested operation" +# KADM_DATA isn't really an error, but... +ec KADM_DATA, "Data is available for return to client" +ec KADM_NOENTRY, "No such entry in the database" + +ec KADM_NOMEM, "Memory exhausted" +ec KADM_NO_HOSTNAME, "Could not fetch system hostname" +ec KADM_NO_BIND, "Could not bind port" +ec KADM_LENGTH_ERROR, "Length mismatch problem" +ec KADM_ILL_WILDCARD, "Illegal use of wildcard" + +ec KADM_DB_INUSE, "Database locked or in use" + +ec KADM_INSECURE_PW, "Insecure password rejected" +ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match" + +ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request" +end diff --git a/src/kadmin/v4server/kadm_funcs.c b/src/kadmin/v4server/kadm_funcs.c new file mode 100644 index 000000000..5025e3acb --- /dev/null +++ b/src/kadmin/v4server/kadm_funcs.c @@ -0,0 +1,1066 @@ +/* + * kadmin/v4server/kadm_funcs.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side database manipulation routines + */ + + +#include <mit-copyright.h> +/* +kadm_funcs.c +the actual database manipulation code +*/ + +#include <stdio.h> +#include <string.h> +#include <sys/param.h> +/* #include <ndbm.h> Gotten by kadmin_server.h */ +#include <ctype.h> +#include <pwd.h> +#include <sys/file.h> +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include <syslog.h> +#include <fcntl.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif +#else +#include <time.h> +#endif + +#include "kadm_server.h" + +extern Kadm_Server server_parm; + +krb5_error_code +kadm_entry2princ(entry, princ) + krb5_db_entry entry; + Principal *princ; +{ + char realm[REALM_SZ]; /* dummy values only */ + krb5_tl_mod_princ *mprinc; + krb5_key_data *pkey; + krb5_error_code retval; + + /* NOTE: does not convert the key */ + memset(princ, 0, sizeof (*princ)); + retval = krb5_524_conv_principal(kadm_context, entry.princ, + princ->name, princ->instance, realm); + if (retval) + return retval; + princ->exp_date = entry.expiration; + strncpy(princ->exp_date_txt, ctime((const time_t *) &entry.expiration), + DATE_SZ); + princ->attributes = entry.attributes; + princ->max_life = entry.max_life / (60 * 5); + princ->kdc_key_ver = 1; /* entry.mkvno; */ + princ->key_version = entry.key_data[0].key_data_kvno; + + retval = krb5_dbe_decode_mod_princ_data(kadm_context, &entry, &mprinc); + if (retval) + return retval; + princ->mod_date = mprinc->mod_date; + strncpy(princ->mod_date_txt, + ctime((const time_t *) &mprinc->mod_date), + DATE_SZ); + krb5_free_principal(kadm_context, mprinc->mod_princ); + krb5_xfree(mprinc); + + /* Find the V4 key */ + retval = krb5_dbe_find_enctype(kadm_context, + &entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) + return retval; + princ->key_version = pkey->key_data_kvno; + + return 0; +} + +krb5_error_code +kadm_princ2entry(princ, entry) + Principal princ; + krb5_db_entry *entry; +{ + krb5_error_code retval; + krb5_tl_mod_princ mprinc; + krb5_key_data *kdatap; + + /* NOTE: does not convert the key */ + memset(entry, 0, sizeof (*entry)); + /* yeah yeah stupid v4 database doesn't store realm names */ + retval = krb5_425_conv_principal(kadm_context, princ.name, princ.instance, + server_parm.krbrlm, &entry->princ); + if (retval) + return retval; + + entry->len = KRB5_KDB_V1_BASE_LENGTH; + entry->max_life = princ.max_life * (60 * 5); + entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */ + entry->expiration = princ.exp_date; + entry->attributes = princ.attributes; + + retval = krb5_425_conv_principal(kadm_context, princ.mod_name, + princ.mod_instance, + server_parm.krbrlm, &mprinc.mod_princ); + if (retval) + return(retval); + mprinc.mod_date = princ.mod_date; + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, &mprinc, entry); + if (retval) + return(retval); + + if (mprinc.mod_princ) + krb5_free_principal(kadm_context, mprinc.mod_princ); + + if (retval = krb5_dbe_find_enctype(kadm_context, + entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &kdatap)) { + if (!(retval = krb5_dbe_create_key_data(kadm_context, entry))) + kdatap = &entry->key_data[entry->n_key_data-1]; + } + if (kdatap) { + kdatap->key_data_ver = 2; + kdatap->key_data_type[0] = (krb5_int16) ENCTYPE_DES_CBC_CRC; + kdatap->key_data_type[1] = (krb5_int16) KRB5_KDB_SALTTYPE_V4; + kdatap->key_data_kvno = (krb5_int16) princ.key_version; + } + return(retval); +} + +int +check_access(pname, pinst, prealm, acltype) +char *pname; +char *pinst; +char *prealm; +enum acl_types acltype; +{ + char checkname[MAX_K_NAME_SZ]; + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm); + + switch (acltype) { + case ADDACL: + (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE); + break; + case GETACL: + (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE); + break; + case MODACL: + (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE); + break; + case DELACL: + (void) sprintf(filename, "%s%s", acldir, DEL_ACL_FILE); + break; + case STABACL: + (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE); + break; + } + return(acl_check(filename, checkname)); +} + +int +wildcard(str) +char *str; +{ + if (!strcmp(str, WILDCARD_STR)) + return(1); + return(0); +} + +#define failadd(code) { (void) syslog(LOG_ERR, "FAILED adding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_add_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_principal default_princ; + krb5_error_code retval; + krb5_db_entry newentry, tmpentry; + krb5_boolean more; + krb5_keyblock newpw; + krb5_tl_mod_princ mprinc; + krb5_key_data *pkey; + krb5_keysalt sblock; + int numfound; + + if (!check_access(rname, rinstance, rrealm, ADDACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failadd(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + kadm_vals_to_prin(valsin->fields, &data_i, valsin); + (void) strncpy(data_i.name, valsin->name, ANAME_SZ); + (void) strncpy(data_i.instance, valsin->instance, INST_SZ); + + if (!IS_FIELD(KADM_EXPDATE,valsin->fields)) + data_i.exp_date = server_parm.expiration; + if (!IS_FIELD(KADM_ATTR,valsin->fields)) + data_i.attributes = server_parm.flags; + if (!IS_FIELD(KADM_MAXLIFE,valsin->fields)) + data_i.max_life = server_parm.max_life; + + retval = kadm_princ2entry(data_i, &newentry); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + newpw.magic = KV5M_KEYBLOCK; + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) + failadd(KADM_NOMEM); + + retval = krb5_dbe_find_enctype(kadm_context, + &newentry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) + failadd(retval); + + data_i.key_low = ntohl(data_i.key_low); + data_i.key_high = ntohl(data_i.key_high); + memcpy(newpw.contents, &data_i.key_low, 4); + memcpy((char *)(((krb5_int32 *) newpw.contents) + 1), &data_i.key_high, 4); + newpw.length = 8; + newpw.enctype = ENCTYPE_DES_CBC_CRC; + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + /* encrypt new key in master key */ + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + &sblock, + (int) ++data_i.key_version, + pkey); + memset((char *)newpw.contents, 0, newpw.length); + free(newpw.contents); + if (retval) { + failadd(retval); + } + data_o = data_i; + + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, + &tmpentry, &numfound, &more); + + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + if (numfound) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(KADM_INUSE); + } else { + if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + mprinc.mod_princ = NULL; /* in case the following breaks */ + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm, + &mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, + &mprinc, + &newentry); + krb5_free_principal(kadm_context, mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &newentry, &numfound); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + if (!numfound) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(KADM_UK_SERROR); + } else { + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, + &tmpentry, + &numfound, &more); + krb5_db_free_principal(kadm_context, &newentry, 1); + if (retval) { + failadd(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + failadd(KADM_UK_RERROR); + } + kadm_entry2princ(tmpentry, &data_o); + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } +} +#undef failadd + +#define faildel(code) { (void) syslog(LOG_ERR, "FAILED deleting '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_del_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + int numfound; /* check how many we get written */ + krb5_boolean more; /* pointer to more grabbed records */ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_db_entry entry, odata; + krb5_error_code retval; + krb5_principal inprinc; + + if (!check_access(rname, rinstance, rrealm, DELACL)) { + (void) syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to delete an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + faildel(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to delete an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(kadm_context, valsin->name, + valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + faildel(retval); + + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound, + &more); + + if (retval) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(retval); + } else if (!numfound || more) { + faildel(KADM_NOENTRY); + } + + retval = krb5_db_delete_principal(kadm_context, inprinc, &numfound); + if (retval) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(retval); + } + if (!numfound) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(KADM_UK_SERROR); + } else { + if (retval) { + faildel(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(KADM_UK_RERROR); + } + kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(kadm_context, &entry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' deleted.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } +} +#undef faildel + +#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; /* what they wannt to get */ +u_char *flags; /* which fields we want */ +Kadm_vals *valsout; /* what data is there */ +{ + int numfound; /* check how many were returned */ + krb5_boolean more; /* To point to more name.instances */ + Principal data_o; /* Data object to hold Principal */ + krb5_principal inprinc; + krb5_db_entry entry; + krb5_error_code retval; + + if (!check_access(rname, rinstance, rrealm, GETACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failget(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(kadm_context, valsin->name, + valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failget(retval); + /* Look up the record in the database */ + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound, + &more); + krb5_free_principal(kadm_context, inprinc); + if (retval) { + failget(retval); + } else if (!numfound || more) { + failget(KADM_NOENTRY); + } + retval = kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(kadm_context, &entry, 1); + if (retval) { + failget(retval); + } + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ +} +#undef failget + +#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; } + +kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin1, *valsin2; /* holds the parameters being + passed in */ +Kadm_vals *valsout; /* the actual record which is returned */ +{ + int numfound; + krb5_boolean more; + Principal data_o, temp_key; + u_char fields[4]; + krb5_keyblock newpw; + krb5_error_code retval; + krb5_principal theprinc; + krb5_db_entry newentry, odata; + krb5_tl_mod_princ mprinc; + krb5_key_data *pkey; + krb5_keysalt sblock; + + if (wildcard(valsin1->name) || wildcard(valsin1->instance)) { + failmod(KADM_ILL_WILDCARD); + } + + if (!check_access(rname, rinstance, rrealm, MODACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry", + rname, rinstance, rrealm, valsin1->name, valsin1->instance); + return KADM_UNAUTH; + } + + syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ", + valsin1->name, valsin1->instance, rname, rinstance, rrealm); + retval = krb5_425_conv_principal(kadm_context, + valsin1->name, valsin1->instance, + server_parm.krbrlm, &theprinc); + if (retval) + failmod(retval); + numfound = 1; + retval = krb5_db_get_principal(kadm_context, theprinc, &newentry, + &numfound, &more); + if (retval) { + krb5_free_principal(kadm_context, theprinc); + failmod(retval); + } else if (numfound == 1) { + kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2); + krb5_free_principal(kadm_context, newentry.princ); + newentry.princ = theprinc; + if (IS_FIELD(KADM_EXPDATE,valsin2->fields)) + newentry.expiration = temp_key.exp_date; + if (IS_FIELD(KADM_ATTR,valsin2->fields)) + newentry.attributes = temp_key.attributes; + if (IS_FIELD(KADM_MAXLIFE,valsin2->fields)) + newentry.max_life = temp_key.max_life; + if (IS_FIELD(KADM_DESKEY,valsin2->fields)) { + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(kadm_context, &newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(KADM_NOMEM); + } + newpw.magic = KV5M_KEYBLOCK; + newpw.length = 8; + newpw.enctype = ENCTYPE_DES_CBC_CRC; + temp_key.key_low = ntohl(temp_key.key_low); + temp_key.key_high = ntohl(temp_key.key_high); + memcpy(newpw.contents, &temp_key.key_low, 4); + memcpy(newpw.contents + 4, &temp_key.key_high, 4); + if (retval = krb5_dbe_find_enctype(kadm_context, + &newentry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(retval); + } + if (pkey->key_data_contents[0]) { + krb5_xfree(pkey->key_data_contents[0]); + pkey->key_data_contents[0] = (krb5_octet *) NULL; + } + /* encrypt new key in master key */ + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + &sblock, + (int) pkey->key_data_kvno+1, + pkey); + memset(newpw.contents, 0, newpw.length); + free(newpw.contents); + memset((char *)&temp_key, 0, sizeof(temp_key)); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + } + if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm, + &mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, + &mprinc, + &newentry); + krb5_free_principal(kadm_context, mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &newentry, &numfound); + memset((char *)&data_o, 0, sizeof(data_o)); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } else { + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, &odata, + &numfound, &more); + krb5_db_free_principal(kadm_context, &newentry, 1); + if (retval) { + failmod(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &odata, numfound); + failmod(KADM_UK_RERROR); + } + retval = kadm_entry2princ(odata, &data_o); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) + failmod(retval); + memset((char *) fields, 0, sizeof(fields)); + SET_FIELD(KADM_NAME,fields); + SET_FIELD(KADM_INST,fields); + SET_FIELD(KADM_EXPDATE,fields); + SET_FIELD(KADM_ATTR,fields); + SET_FIELD(KADM_MAXLIFE,fields); + kadm_prin_to_vals(fields, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } else { + failmod(KADM_NOENTRY); + } +} +#undef failmod + +#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_change (rname, rinstance, rrealm, newpw) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +{ + int numfound; + krb5_boolean more; + krb5_principal rprinc; + krb5_error_code retval; + krb5_keyblock localpw; + krb5_db_entry odata; + krb5_key_data *pkey; + krb5_keysalt sblock; + + if (strcmp(server_parm.krbrlm, rrealm)) { + syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n", + rname, rinstance, rrealm); + return(KADM_WRONG_REALM); + } + + if (wildcard(rname) || wildcard(rinstance)) { + failchange(KADM_ILL_WILDCARD); + } + syslog(LOG_INFO, "'%s.%s@%s' wants to change its password", + rname, rinstance, rrealm); + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, + server_parm.krbrlm, &rprinc); + if (retval) + failchange(retval); + if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL) + failchange(KADM_NOMEM); + memcpy(localpw.contents, newpw, 8); + localpw.magic = KV5M_KEYBLOCK; + localpw.enctype = ENCTYPE_DES_CBC_CRC; + localpw.length = 8; + numfound = 1; + retval = krb5_db_get_principal(kadm_context, rprinc, &odata, + &numfound, &more); + krb5_free_principal(kadm_context, rprinc); + if (retval) { + memset(localpw.contents, 0, localpw.length); + free(localpw.contents); + failchange(retval); + } else if (numfound == 1) { + if (retval = krb5_dbe_find_enctype(kadm_context, + &odata, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey)) { + failchange(retval); + } + pkey->key_data_kvno++; + pkey->key_data_kvno %= 256; + numfound = 1; + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &localpw, + &sblock, + (int) pkey->key_data_kvno, + pkey); + memset(localpw.contents, 0, localpw.length); + free(localpw.contents); + if (retval) { + failchange(retval); + } + retval = krb5_db_put_principal(kadm_context, &odata, &numfound); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) { + failchange(retval); + } else if (more) { + failchange(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, + "'%s.%s@%s' password changed.", rname, rinstance, rrealm); + return KADM_SUCCESS; + } + } + else { + failchange(KADM_NOENTRY); + } +} +#undef failchange + +check_pw(newpw, checkstr) + des_cblock newpw; + char *checkstr; +{ +#ifdef NOENCRYPTION + return 0; +#else /* !NOENCRYPTION */ + des_cblock checkdes; + + (void) des_string_to_key(checkstr, checkdes); + return(!memcmp(checkdes, newpw, sizeof(des_cblock))); +#endif /* NOENCRYPTION */ +} + +char *reverse(str) + char *str; +{ + static char newstr[80]; + char *p, *q; + int i; + + i = strlen(str); + if (i >= sizeof(newstr)) + i = sizeof(newstr)-1; + p = str+i-1; + q = newstr; + q[i]='\0'; + for(; i > 0; i--) + *q++ = *p--; + + return(newstr); +} + +int lower(str) + char *str; +{ + register char *cp; + int effect=0; + + for (cp = str; *cp; cp++) { + if (isupper(*cp)) { + *cp = tolower(*cp); + effect++; + } + } + return(effect); +} + +des_check_gecos(gecos, newpw) + char *gecos; + des_cblock newpw; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + if (lower(cp)) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + } + cp = ncp; + } else + break; + } + return(0); +} + +str_check_gecos(gecos, pwstr) + char *gecos; + char *pwstr; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (!strcasecmp(pwstr, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (!strcasecmp(pwstr, tcp)) + return(KADM_INSECURE_PW); + cp = ncp; + } else + break; + } + return(0); +} + + +kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +char *pwstring; +{ + static DBM *pwfile = NULL; + int retval; + datum passwd, entry; + struct passwd *ent; +#ifdef HESIOD + extern struct passwd *hes_getpwnam(); +#endif + + if (pwstring && !check_pw(newpw, pwstring)) + /* + * Someone's trying to toy with us.... + */ + return(KADM_PW_MISMATCH); + if (pwstring && (strlen(pwstring) < 5)) + return(KADM_INSECURE_PW); + if (!pwfile) { + pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644); + } + if (pwfile) { + passwd.dptr = (char *) newpw; + passwd.dsize = 8; + entry = dbm_fetch(pwfile, passwd); + if (entry.dptr) + return(KADM_INSECURE_PW); + } + if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname))) + return(KADM_INSECURE_PW); +#ifdef HESIOD + ent = hes_getpwnam(rname); +#else + ent = getpwnam(rname); +#endif + if (ent && ent->pw_gecos) { + if (pwstring) + retval = str_check_gecos(ent->pw_gecos, pwstring); + else + retval = des_check_gecos(ent->pw_gecos, newpw); + if (retval) + return(retval); + } + return(0); +} + +/* + * This routine checks to see if a principal should be considered an + * allowable service name which can be changed by kadm_change_srvtab. + * + * We do this check by using the ACL library. This makes the + * (relatively) reasonable assumption that both the name and the + * instance will not contain '.' or '@'. + */ +kadm_check_srvtab(name, instance) + char *name; + char *instance; +{ + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE); + if (!acl_check(filename, name)) + return(KADM_NOT_SERV_PRINC); + + (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE); + if (acl_check(filename, instance)) + return(KADM_NOT_SERV_PRINC); + return 0; +} + +/* + * Routine to allow some people to change the key of a srvtab + * principal to a random key, which the admin server will return to + * the client. + */ +#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_chg_srvtab(rname, rinstance, rrealm, values) + char *rname; /* requestors name */ + char *rinstance; /* requestors instance */ + char *rrealm; /* requestors realm */ + Kadm_vals *values; +{ + int numfound, ret, isnew = 0; + des_cblock new_key; + krb5_principal inprinc; + krb5_error_code retval; + krb5_db_entry odata; + krb5_boolean more; + krb5_keyblock newpw; + krb5_key_data *pkey; + + if (!check_access(rname, rinstance, rrealm, STABACL)) + failsrvtab(KADM_UNAUTH); + if (wildcard(rname) || wildcard(rinstance)) + failsrvtab(KADM_ILL_WILDCARD); + if (ret = kadm_check_srvtab(values->name, values->instance)) + failsrvtab(ret); + + retval = krb5_425_conv_principal(kadm_context, values->name, + values->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failsrvtab(retval); + /* + * OK, get the entry + */ + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &odata, + &numfound, &more); + if (retval) { + krb5_free_principal(kadm_context, inprinc); + failsrvtab(retval); + } else if (numfound) { + retval = krb5_dbe_find_enctype(kadm_context, + &odata, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) { + krb5_free_principal(kadm_context, inprinc); + failsrvtab(retval); + } + } + else { + /* + * This is a new srvtab entry that we're creating + */ + isnew = 1; + memset((char *)&odata, 0, sizeof (odata)); + odata.princ = inprinc; + odata.max_life = server_parm.max_life; + odata.max_renewable_life = server_parm.max_rlife; + odata.expiration = server_parm.expiration; + odata.attributes = 0; + if (!krb5_dbe_create_key_data(kadm_context, &odata)) { + pkey = &odata.key_data[0]; + memset(pkey, 0, sizeof(*pkey)); + pkey->key_data_ver = 2; + pkey->key_data_type[0] = ENCTYPE_DES_CBC_CRC; + pkey->key_data_type[1] = KRB5_KDB_SALTTYPE_V4; + } + } + pkey->key_data_kvno++; + +#ifdef NOENCRYPTION + memset(new_key, 0, sizeof(new_key)); + new_key[0] = 127; +#else + des_new_random_key(new_key); +#endif + /* + * Store the new key in the return structure; also fill in the + * rest of the fields. + */ + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(kadm_context, &odata, 1); + failsrvtab(KADM_NOMEM); + } + newpw.enctype = ENCTYPE_DES_CBC_CRC; + newpw.length = 8; + memcpy((char *)newpw.contents, new_key, 8); + memset((char *)new_key, 0, sizeof (new_key)); + memcpy((char *)&values->key_low, newpw.contents, 4); + memcpy((char *)&values->key_high, newpw.contents + 4, 4); + values->key_low = htonl(values->key_low); + values->key_high = htonl(values->key_high); + values->max_life = odata.max_life / (60 * 5); + values->exp_date = odata.expiration; + values->attributes = odata.attributes; + memset(values->fields, 0, sizeof(values->fields)); + SET_FIELD(KADM_NAME, values->fields); + SET_FIELD(KADM_INST, values->fields); + SET_FIELD(KADM_EXPDATE, values->fields); + SET_FIELD(KADM_ATTR, values->fields); + SET_FIELD(KADM_MAXLIFE, values->fields); + SET_FIELD(KADM_DESKEY, values->fields); + + /* + * Encrypt the new key with the master key, and then update + * the database record + */ + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + (krb5_keysalt *) NULL, + (int) pkey->key_data_kvno, + pkey); + memset((char *)newpw.contents, 0, 8); + free(newpw.contents); + if (retval) { + krb5_db_free_principal(kadm_context, &odata, 1); + failsrvtab(retval); + } + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &odata, &numfound); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) { + failsrvtab(retval); + } + else if (!numfound) { + failsrvtab(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.", + values->name, values->instance, + numfound ? "changed" : "created", + rname, rinstance, rrealm); + return KADM_DATA; + } +} + +#undef failsrvtab diff --git a/src/kadmin/v4server/kadm_ser_wrap.c b/src/kadmin/v4server/kadm_ser_wrap.c new file mode 100644 index 000000000..5e7f48508 --- /dev/null +++ b/src/kadmin/v4server/kadm_ser_wrap.c @@ -0,0 +1,310 @@ +/* + * kadmin/v4server/kadm_ser_wrap.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side support functions + */ + + +#include <mit-copyright.h> +/* +kadm_ser_wrap.c +unwraps wrapped packets and calls the appropriate server subroutine +*/ + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/types.h> +#include <netdb.h> +#include <sys/socket.h> +#include "kadm_server.h" +#include <kadm.h> +#include <kadm_err.h> +#include <krb_err.h> +#include <syslog.h> + +#ifdef OVSEC_KADM +#include <kadm5/admin.h> +extern void *ovsec_handle; +#endif + +Kadm_Server server_parm; + +/* +kadm_ser_init +set up the server_parm structure +*/ +#ifdef OVSEC_KADM +kadm_ser_init(inter, realm, params) + int inter; /* interactive or from file */ + char realm[]; + kadm5_config_params *params; +#else +kadm_ser_init(inter, realm) + int inter; /* interactive or from file */ + char realm[]; +#endif +{ + struct servent *sep; + struct hostent *hp; + char hostname[MAXHOSTNAMELEN]; + char *mkey_name; + krb5_error_code retval; + int numfound = 1; + krb5_boolean more; + krb5_db_entry master_entry; + krb5_key_data *kdatap; + + if (gethostname(hostname, sizeof(hostname))) + return KADM_NO_HOSTNAME; + + (void) strcpy(server_parm.sname, PWSERV_NAME); + (void) strcpy(server_parm.sinst, KRB_MASTER); + (void) strcpy(server_parm.krbrlm, realm); + if (krb5_build_principal(kadm_context, + &server_parm.sprinc, + strlen(realm), + realm, + PWSERV_NAME, + KRB_MASTER, 0)) + return KADM_NO_MAST; + server_parm.admin_fd = -1; + /* setting up the addrs */ + if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL) + return KADM_NO_SERV; + memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr)); + server_parm.admin_addr.sin_family = AF_INET; + if ((hp = gethostbyname(hostname)) == NULL) + return KADM_NO_HOSTNAME; + memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr, + hp->h_length); + server_parm.admin_addr.sin_port = sep->s_port; + /* setting up the database */ + mkey_name = KRB5_KDB_M_NAME; + +#ifdef OVSEC_KADM + server_parm.master_keyblock.enctype = params->enctype; + krb5_use_enctype(kadm_context, &server_parm.master_encblock, + server_parm.master_keyblock.enctype); +#else + if (inter == 1) { + server_parm.master_keyblock.enctype = ENCTYPE_DES_CBC_MD5; + krb5_use_enctype(kadm_context, &server_parm.master_encblock, + server_parm.master_keyblock.enctype); + } else + server_parm.master_keyblock.enctype = ENCTYPE_UNKNOWN; +#endif + + retval = krb5_db_setup_mkey_name(kadm_context, mkey_name, realm, + (char **) 0, + &server_parm.master_princ); + if (retval) + return KADM_NO_MAST; + krb5_db_fetch_mkey(kadm_context, server_parm.master_princ, + &server_parm.master_encblock, + (inter == 1), FALSE, +#ifdef OVSEC_KADM + params->stash_file, +#else + (char *) NULL, +#endif + NULL, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_MAST; + retval = krb5_db_verify_master_key(kadm_context, server_parm.master_princ, + &server_parm.master_keyblock, + &server_parm.master_encblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_process_key(kadm_context, &server_parm.master_encblock, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_db_get_principal(kadm_context, server_parm.master_princ, + &master_entry, &numfound, &more); + if (retval || more || !numfound) + return KADM_NO_VERI; + + retval = krb5_dbe_find_enctype(kadm_context, + &master_entry, + -1, -1, -1, + &kdatap); + if (retval) + return KRB5_PROG_KEYTYPE_NOSUPP; + server_parm.max_life = master_entry.max_life; + server_parm.max_rlife = master_entry.max_renewable_life; + server_parm.expiration = master_entry.expiration; + server_parm.mkvno = kdatap->key_data_kvno; + /* don't set flags, as master has some extra restrictions + (??? quoted from kdb_edit.c) */ + krb5_db_free_principal(kadm_context, &master_entry, numfound); + return KADM_SUCCESS; +} + + +static void errpkt(dat, dat_len, code) +u_char **dat; +int *dat_len; +int code; +{ + krb5_ui_4 retcode; + char *pdat; + + free((char *)*dat); /* free up req */ + *dat_len = KADM_VERSIZE + sizeof(krb5_ui_4); + *dat = (u_char *) malloc((unsigned)*dat_len); + if (!(*dat)) { + syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len); + abort(); + } + pdat = (char *) *dat; + retcode = htonl((krb5_ui_4) code); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + return; +} + +/* +kadm_ser_in +unwrap the data stored in dat, process, and return it. +*/ +kadm_ser_in(dat,dat_len) +u_char **dat; +int *dat_len; +{ + u_char *in_st; /* pointer into the sent packet */ + int in_len,retc; /* where in packet we are, for + returns */ + krb5_ui_4 r_len; /* length of the actual packet */ + KTEXT_ST authent; /* the authenticator */ + AUTH_DAT ad; /* who is this, klink */ + krb5_ui_4 ncksum; /* checksum of encrypted data */ + des_key_schedule sess_sched; /* our schedule */ + MSG_DAT msg_st; + u_char *retdat, *tmpdat; + int retval, retlen; + + if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) { + errpkt(dat, dat_len, KADM_BAD_VER); + return KADM_BAD_VER; + } + in_len = KADM_VERSIZE; + /* get the length */ + if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0) + return KADM_LENGTH_ERROR; + in_len += retc; + authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb5_ui_4); + memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length); + authent.mbz = 0; + /* service key should be set before here */ + if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst, + server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0)) + { + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + +#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));} + + in_st = *dat + *dat_len - r_len; +#ifdef NOENCRYPTION + ncksum = 0; +#else + ncksum = quad_cksum(in_st, (krb5_ui_4 *)0, (long) r_len, 0, ad.session); +#endif + if (ncksum!=ad.checksum) { /* yow, are we correct yet */ + clr_cli_secrets(); + errpkt(dat, dat_len,KADM_BAD_CHK); + return KADM_BAD_CHK; + } +#ifdef NOENCRYPTION + memset(sess_sched, 0, sizeof(sess_sched)); +#else + des_key_sched(ad.session, sess_sched); +#endif + if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session, + &server_parm.recv_addr, + &server_parm.admin_addr, &msg_st)) { + clr_cli_secrets(); + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + switch (msg_st.app_data[0]) { + case CHANGE_PW: + retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#ifndef OVSEC_KADM + case ADD_ENT: + retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case DEL_ENT: + retval = kadm_ser_del(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case GET_ENT: + retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case MOD_ENT: + retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHECK_PW: + retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHG_STAB: + retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#endif /* OVSEC_KADM */ + default: + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_OPCODE); + return KADM_NO_OPCODE; + } + /* Now seal the response back into a priv msg */ + free((char *)*dat); + tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE + + sizeof(krb5_ui_4))); + if (!tmpdat) { + clr_cli_secrets(); + syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!", + retlen + KADM_VERSIZE + sizeof(krb5_ui_4)); + errpkt(dat, dat_len, KADM_NOMEM); + return KADM_NOMEM; + } + (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE); + retval = htonl((krb5_ui_4)retval); + memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb5_ui_4)); + if (retlen) { + memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb5_ui_4), (char *)retdat, + retlen); + free((char *)retdat); + } + /* slop for mk_priv stuff */ + *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE + + sizeof(krb5_ui_4) + 200)); + if ((*dat_len = krb_mk_priv(tmpdat, *dat, + (u_long) (retlen + KADM_VERSIZE + + sizeof(krb5_ui_4)), + sess_sched, + ad.session, &server_parm.admin_addr, + &server_parm.recv_addr)) < 0) { + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_ENCRYPT); + return KADM_NO_ENCRYPT; + } + clr_cli_secrets(); + return KADM_SUCCESS; +} diff --git a/src/kadmin/v4server/kadm_server.c b/src/kadmin/v4server/kadm_server.c new file mode 100644 index 000000000..81e43f128 --- /dev/null +++ b/src/kadmin/v4server/kadm_server.c @@ -0,0 +1,571 @@ +/* + * kadmin/v4server/kadm_server.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side subroutines + */ + + +#include <mit-copyright.h> + +#include "k5-int.h" + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif +#else +#include <time.h> +#endif + +#ifdef OVSEC_KADM +#include <com_err.h> +#include <kadm5/admin.h> +#include <kadm5/chpass_util_strings.h> +#include <krb5/kdb.h> +extern void *ovsec_handle; +#endif + +#include <kadm.h> +#include <kadm_err.h> + +extern krb5_context kadm_context; +int fascist_cpw = 0; /* Be fascist about insecure passwords? */ + +#ifdef OVSEC_KADM +char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n"; + +#else /* !OVSEC_KADM */ + +char bad_pw_err[] = + "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n"; + +char bad_pw_warn[] = + "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n"; + +char check_pw_msg[] = + "You have entered an insecure password. You should choose another.\n\n"; + +char pw_blurb[] = +"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n"; + +#endif /* OVSEC_KADM */ + +/* from V4 month_sname.c -- was not part of API */ +/* + * Given an integer 1-12, month_sname() returns a string + * containing the first three letters of the corresponding + * month. Returns 0 if the argument is out of range. + */ + +static char *month_sname(n) + int n; +{ + static char *name[] = { + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" + }; + return((n < 1 || n > 12) ? 0 : name [n-1]); +} + +/* from V4 log.c -- was not part of API */ + +/* + * krb_log() is used to add entries to the logfile (see krb_set_logfile() + * below). Note that it is probably not portable since it makes + * assumptions about what the compiler will do when it is called + * with less than the correct number of arguments which is the + * way it is usually called. + * + * The log entry consists of a timestamp and the given arguments + * printed according to the given "format". + * + * The log file is opened and closed for each log entry. + * + * The return value is undefined. + */ + +/* static char *log_name = KRBLOG; */ +/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */ +static char *log_name = KADM_SYSLOG; + +static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0) + char *format; + char *a1,*a2,*a3,*a4,*a5,*a6,*a7,*a8,*a9,*a0; +{ + FILE *logfile; + time_t now; + struct tm *tm; + + if ((logfile = fopen(log_name,"a")) == NULL) + return; + + (void) time(&now); + tm = localtime(&now); + + fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday, + month_sname(tm->tm_mon + 1),tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0); + fprintf(logfile,"\n"); + (void) fclose(logfile); + return; +} + + +/* +kadm_ser_cpw - the server side of the change_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : caller can change only own password + +Replaces the password (i.e. des key) of the caller with that specified in key. +Returns no actual data from the master server, since this is called by a user +*/ +kadm_ser_cpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_int32 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int status, stvlen = 0; + int retval; + extern int kadm_approve_pw(); +#ifdef OVSEC_KADM + ovsec_kadm_principal_ent_t princ_ent; + ovsec_kadm_policy_ent_t pol_ent; + krb5_principal user_princ; + char msg_ret[1024], *time_string, *ptr; + const char *msg_ptr; + krb5_int32 now; + time_t until; +#endif + + /* take key off the stream, and change the database */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + +#ifdef OVSEC_KADM + /* we don't use the client-provided key itself */ + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); + + if (no_pword) { + krb_log("Old-style change password request from '%s.%s@%s'!", + ad->pname, ad->pinst, ad->prealm); + *outlen = strlen(pw_required)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, pw_required); + } else { + *outlen = 0; + } + return KADM_INSECURE_PW; + } + + if (krb5_build_principal(kadm_context, &user_princ, + strlen(ad->prealm), + ad->prealm, + ad->pname, + *ad->pinst ? ad->pinst : 0, 0)) + /* this should never happen */ + return KADM_NOENTRY; + + *outlen = 0; + + if (retval = krb5_timeofday(kadm_context, &now)) { + msg_ptr = error_message(retval); + goto send_response; + } + + retval = ovsec_kadm_get_principal(ovsec_handle, user_princ, + &princ_ent); + if (retval != 0) { + msg_ptr = error_message(retval); + goto send_response; + } + + /* + * This daemon necessarily has the modify privilege, so + * ovsec_kadm_chpass_principal will allow it to violate the + * policy's minimum lifetime. Since that's A Bad Thing, we need + * to enforce it ourselves. Unfortunately, this means we are + * duplicating code from both ovsec_adm_server and + * ovsec_kadm_chpass_util(). + */ + if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) { + retval = ovsec_kadm_get_policy(ovsec_handle, + princ_ent->policy, + &pol_ent); + if (retval != 0) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + msg_ptr = error_message(retval); + goto send_response; + } + + /* make "now" a boolean, true == too soon */ + now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life); + + (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent); + + if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + retval = CHPASS_UTIL_PASSWORD_TOO_SOON; + + until = princ_ent->last_pwd_change + pol_ent->pw_min_life; + time_string = ctime(&until); + + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + + sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON), + time_string); + msg_ptr = msg_ret; + + goto send_response; + } + } + + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + + retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ, + pword, NULL, msg_ret); + msg_ptr = msg_ret; + (void) krb5_free_principal(kadm_context, user_princ); + +send_response: + + retval = convert_ovsec_to_kadm(retval); + + if (retval) { + /* don't send message on success because kpasswd.v4 will */ + /* print "password changed" too */ + *outlen = strlen(msg_ptr)+2; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, msg_ptr); + strcat(*datout, "\n"); + } else + *outlen = 0; + } + if (retval == KADM_INSECURE_PW) { + krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } +#else /* OVSEC_KADM */ + if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, + newkey, no_pword ? 0 : pword)) { + if (retval == KADM_PW_MISMATCH) { + /* + * Very strange!!! This means that the cleartext + * password which was sent and the DES cblock + * didn't match! + */ + (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } + if (fascist_cpw) { + *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, bad_pw_err); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); +#ifdef notdef + /* For debugging only, probably a bad idea */ + if (!no_pword) + (void) krb_log("The password was %s\n", pword); +#endif + return(retval); + } else { + *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, bad_pw_warn); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' used an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + } else { + *datout = 0; + *outlen = 0; + } + + retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey); + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); +#endif /* OVSEC_KADM */ + + return retval; +} + +#ifndef OVSEC_KADM +/* +kadm_ser_add - the server side of the add_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Adds and entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +int +kadm_ser_add(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_del - the server side of the del_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Deletes an entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +kadm_ser_del(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_del_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_mod - the server side of the mod_entry routine + recieves : KTEXT, {values, values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Modifies all entries corresponding to the first values so they match the + second values. +returns the values for the changed entries +*/ +kadm_ser_mod(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals vals1, vals2, retvals; + int wh; + int status; + + if ((wh = stream_to_vals(dat, &vals1, len)) < 0) + return KADM_LENGTH_ERROR; + if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0) + return KADM_LENGTH_ERROR; + if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1, + &vals2, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_get + recieves : KTEXT, {values, flags} + returns : CKSUM, RETCODE, {count, values, values, values} + acl : su + +gets the fields requested by flags from all entries matching values +returns this data for each matching recipient, after a count of how many such + matches there were +*/ +kadm_ser_get(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + u_char fl[FLDSZ]; + int loop,wh; + int status; + + if ((wh = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + if (wh + FLDSZ > len) + return KADM_LENGTH_ERROR; + for (loop=FLDSZ-1; loop>=0; loop--) + fl[loop] = dat[wh++]; + if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm, + &values, fl, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_ckpw - the server side of the check_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : none + +Checks to see if the des key passed from the caller is a "secure" password. +*/ +kadm_ser_ckpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int stvlen = 0,status; + int retval; + extern int kadm_approve_pw(); + + /* take key off the stream, and check it */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + keylow = keyhigh = 0; + retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey, + no_pword ? 0 : pword); + memset(newkey, 0, sizeof(newkey)); + if (retval) { + *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, check_pw_msg); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent an insecure password to be checked", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } else { + *datout = 0; + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent a secure password to be checked", + ad->pname, ad->pinst, ad->prealm); + } + return(0); +} + +/* +kadm_ser_stab - the server side of the change_srvtab routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Creates or modifies the specified service principal to have a random +key, which is sent back to the client. The key version is returned in +the max_life field of the values structure. It's a hack, but it's a +backwards compatible hack.... +*/ +kadm_ser_stab(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values); + if (status == KADM_DATA) { + *outlen = vals_to_stream(&values,datout); + values.key_low = values.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} +#endif /* !OVSEC_KADM */ diff --git a/src/kadmin/v4server/kadm_server.h b/src/kadmin/v4server/kadm_server.h new file mode 100644 index 000000000..d852bcaab --- /dev/null +++ b/src/kadmin/v4server/kadm_server.h @@ -0,0 +1,58 @@ +/* + * kadmin/v4server/kadm_server.h + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Definitions for Kerberos administration server & client + */ + +#ifndef KADM_SERVER_DEFS +#define KADM_SERVER_DEFS + +#include <mit-copyright.h> +/* + * kadm_server.h + * Header file for the fourth attempt at an admin server + * Doug Church, December 28, 1989, MIT Project Athena + * ps. Yes that means this code belongs to athena etc... + * as part of our ongoing attempt to copyright all greek names + */ + +#include <sys/types.h> +#include <krb.h> +#include <des.h> +#include "k5-int.h" + +typedef struct { + struct sockaddr_in admin_addr; + struct sockaddr_in recv_addr; + int recv_addr_len; + int admin_fd; /* our link to clients */ + char sname[ANAME_SZ]; + char sinst[INST_SZ]; + char krbrlm[REALM_SZ]; + krb5_principal sprinc; + krb5_encrypt_block master_encblock; + krb5_principal master_princ; + krb5_keyblock master_keyblock; + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_kvno mkvno; +} Kadm_Server; + +#define ADD_ACL_FILE "/v4acl.add" +#define GET_ACL_FILE "/v4acl.get" +#define MOD_ACL_FILE "/v4acl.mod" +#define DEL_ACL_FILE "/v4acl.del" +#define STAB_ACL_FILE "/v4acl.srvtab" +#define STAB_SERVICES_FILE "/v4stab_services" +#define STAB_HOSTS_FILE "/v4stab_bad_hosts" + +krb5_context kadm_context; + +#endif /* KADM_SERVER_DEFS */ diff --git a/src/kadmin/v4server/kadm_stream.c b/src/kadmin/v4server/kadm_stream.c new file mode 100644 index 000000000..86da6c64f --- /dev/null +++ b/src/kadmin/v4server/kadm_stream.c @@ -0,0 +1,277 @@ +/* + * kadmin/v4server/kadm_stream.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Stream conversion functions for Kerberos administration server + */ + + +#include <mit-copyright.h> +#include <string.h> +#include "k5-int.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#else +extern char *malloc(), *calloc(), *realloc(); +#endif + +/* + kadm_stream.c + this holds the stream support routines for the kerberos administration server + + vals_to_stream: converts a vals struct to a stream for transmission + internals build_field_header, vts_[string, char, long, short] + stream_to_vals: converts a stream to a vals struct + internals check_field_header, stv_[string, char, long, short] + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits +*/ + +#include "kadm.h" + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* +vals_to_stream + recieves : kadm_vals *, u_char * + returns : a realloced and filled in u_char * + +this function creates a byte-stream representation of the kadm_vals structure +*/ +vals_to_stream(dt_in, dt_out) +Kadm_vals *dt_in; +u_char **dt_out; +{ + int vsloop, stsize; /* loop counter, stream size */ + + stsize = build_field_header(dt_in->fields, dt_out); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_in->fields)) { + switch (vsloop) { + case KADM_NAME: + stsize+=vts_string(dt_in->name, dt_out, stsize); + break; + case KADM_INST: + stsize+=vts_string(dt_in->instance, dt_out, stsize); + break; + case KADM_EXPDATE: + stsize+=vts_long(dt_in->exp_date, dt_out, stsize); + break; + case KADM_ATTR: + stsize+=vts_short(dt_in->attributes, dt_out, stsize); + break; + case KADM_MAXLIFE: + stsize+=vts_char(dt_in->max_life, dt_out, stsize); + break; + case KADM_DESKEY: + stsize+=vts_long(dt_in->key_high, dt_out, stsize); + stsize+=vts_long(dt_in->key_low, dt_out, stsize); + break; + default: + break; + } +} + return(stsize); +} + +build_field_header(cont, st) +u_char *cont; /* container for fields data */ +u_char **st; /* stream */ +{ + *st = (u_char *) malloc (4); + memcpy((char *) *st, (char *) cont, 4); + return 4; /* return pointer to current stream location */ +} + +vts_string(dat, st, loc) +char *dat; /* a string to put on the stream */ +u_char **st; /* base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1)); + memcpy((char *)(*st + loc), dat, strlen(dat)+1); + return strlen(dat)+1; +} + +vts_short(dat, st, loc) +u_short dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + u_short temp; /* to hold the net order short */ + + temp = htons(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short)); + return sizeof(u_short); +} + +vts_long(dat, st, loc) +krb5_ui_4 dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + krb5_ui_4 temp; /* to hold the net order short */ + + temp = htonl(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb5_ui_4))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb5_ui_4)); + return sizeof(krb5_ui_4); +} + + +vts_char(dat, st, loc) +u_char dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char))); + (*st)[loc] = (u_char) dat; + return 1; +} + +/* +stream_to_vals + recieves : u_char *, kadm_vals * + returns : a kadm_vals filled in according to u_char * + +this decodes a byte stream represntation of a vals struct into kadm_vals +*/ +stream_to_vals(dt_in, dt_out, maxlen) +u_char *dt_in; +Kadm_vals *dt_out; +int maxlen; /* max length to use */ +{ + register int vsloop, stsize; /* loop counter, stream size */ + register int status; + + memset((char *) dt_out, 0, sizeof(*dt_out)); + + stsize = check_field_header(dt_in, dt_out->fields, maxlen); + if (stsize < 0) + return(-1); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_out->fields)) + switch (vsloop) { + case KADM_NAME: + if ((status = stv_string(dt_in, dt_out->name, stsize, + sizeof(dt_out->name), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_INST: + if ((status = stv_string(dt_in, dt_out->instance, stsize, + sizeof(dt_out->instance), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_EXPDATE: + if ((status = stv_long(dt_in, &dt_out->exp_date, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_ATTR: + if ((status = stv_short(dt_in, &dt_out->attributes, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_MAXLIFE: + if ((status = stv_char(dt_in, &dt_out->max_life, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_DESKEY: + if ((status = stv_long(dt_in, &dt_out->key_high, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + if ((status = stv_long(dt_in, &dt_out->key_low, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + default: + break; + } + return stsize; +} + +check_field_header(st, cont, maxlen) +u_char *st; /* stream */ +u_char *cont; /* container for fields data */ +int maxlen; +{ + if (4 > maxlen) + return(-1); + memcpy((char *) cont, (char *) st, 4); + return 4; /* return pointer to current stream location */ +} + +stv_string(st, dat, loc, stlen, maxlen) +register u_char *st; /* base pointer to the stream */ +char *dat; /* a string to read from the stream */ +register int loc; /* offset into the stream for current data */ +int stlen; /* max length of string to copy in */ +int maxlen; /* max length of input stream */ +{ + int maxcount; /* max count of chars to copy */ + + maxcount = min(maxlen - loc, stlen); + + (void) strncpy(dat, (char *)st + loc, maxcount); + + if (dat[maxcount-1]) /* not null-term --> not enuf room */ + return(-1); + return strlen(dat)+1; +} + +stv_short(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_short *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + u_short temp; /* to hold the net order short */ + + if (loc + sizeof(u_short) > maxlen) + return(-1); + memcpy((char *) &temp, (char *) st+ loc, sizeof(u_short)); + *dat = ntohs(temp); /* convert to network order */ + return sizeof(u_short); +} + +stv_long(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +krb5_ui_4 *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; /* maximum length of st */ +{ + krb5_ui_4 temp; /* to hold the net order short */ + + if (loc + sizeof(krb5_ui_4) > maxlen) + return(-1); + memcpy((char *) &temp, (char *) st + loc, sizeof(krb5_ui_4)); + *dat = ntohl(temp); /* convert to network order */ + return sizeof(krb5_ui_4); +} + +stv_char(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_char *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + if (loc + 1 > maxlen) + return(-1); + *dat = *(st + loc); + return 1; +} + diff --git a/src/kadmin/v4server/kadm_supp.c b/src/kadmin/v4server/kadm_supp.c new file mode 100644 index 000000000..9d2f8deb2 --- /dev/null +++ b/src/kadmin/v4server/kadm_supp.c @@ -0,0 +1,113 @@ +/* + * kadmin/v4server/kadm_supp.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Support functions for Kerberos administration server & clients + */ + + +#include <mit-copyright.h> +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +/* + kadm_supp.c + this holds the support routines for the kerberos administration server + + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits + prin_vals: prints out data associated with a Principal in the vals + structure +*/ + +#include "kadm.h" +#include "krb_db.h" + +/* +prin_vals: + recieves : a vals structure +*/ +void prin_vals(vals) +Kadm_vals *vals; +{ + printf("Info in Database for %s.%s:\n", vals->name, vals->instance); + printf(" Max Life: %d Exp Date: %s\n",vals->max_life, + asctime(localtime((long *)&vals->exp_date))); + printf(" Attribs: %.2x key: %u %u\n",vals->attributes, + vals->key_low, vals->key_high); +} + +#ifdef notdef +nierror(s) +int s; +{ + extern char *error_message(); + printf("Kerberos admin server loses..... %s\n",error_message(s)); + return(s); +} +#endif + +/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal, + it copies the fields in Principal specified by fields into Kadm_vals, + i.e from old to new */ + +kadm_prin_to_vals(fields, new, old) +u_char fields[FLDSZ]; +Kadm_vals *new; +Principal *old; +{ + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) { + (void) strncpy(new->name, old->name, ANAME_SZ); + SET_FIELD(KADM_NAME, new->fields); + } + if (IS_FIELD(KADM_INST,fields)) { + (void) strncpy(new->instance, old->instance, INST_SZ); + SET_FIELD(KADM_INST, new->fields); + } + if (IS_FIELD(KADM_EXPDATE,fields)) { + new->exp_date = old->exp_date; + SET_FIELD(KADM_EXPDATE, new->fields); + } + if (IS_FIELD(KADM_ATTR,fields)) { + new->attributes = old->attributes; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_MAXLIFE,fields)) { + new->max_life = old->max_life; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + SET_FIELD(KADM_DESKEY, new->fields); + } +} + +kadm_vals_to_prin(fields, new, old) +u_char fields[FLDSZ]; +Principal *new; +Kadm_vals *old; +{ + + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) + (void) strncpy(new->name, old->name, ANAME_SZ); + if (IS_FIELD(KADM_INST,fields)) + (void) strncpy(new->instance, old->instance, INST_SZ); + if (IS_FIELD(KADM_EXPDATE,fields)) + new->exp_date = old->exp_date; + if (IS_FIELD(KADM_ATTR,fields)) + new->attributes = old->attributes; + if (IS_FIELD(KADM_MAXLIFE,fields)) + new->max_life = old->max_life; + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + } +} diff --git a/src/kadmin/v4server/unit-test/ChangeLog b/src/kadmin/v4server/unit-test/ChangeLog new file mode 100644 index 000000000..93120b8a4 --- /dev/null +++ b/src/kadmin/v4server/unit-test/ChangeLog @@ -0,0 +1,13 @@ +Mon Jul 15 17:15:51 1996 Marc Horowitz <marc@mit.edu> + + * helpers.exp (exp_prog): the check for non-newline-terminated + stdout was causing failures where there weren't any. Barry + doesn't remember why this was here to begin with. + * Makefile.ov (unit-test-body), helpers.exp: some versions of + runtest do not like digits in command-line variable names. + * Makefile.ov (unit-test-body), helpers.exp: ovsec_v4adm_server + renamed to kadmind4 + * getpid.sh: grep out any programs with expect or kadmind4 in + their names. + + diff --git a/src/kadmin/v4server/unit-test/Makefile.ov b/src/kadmin/v4server/unit-test/Makefile.ov new file mode 100644 index 000000000..3af65607e --- /dev/null +++ b/src/kadmin/v4server/unit-test/Makefile.ov @@ -0,0 +1,19 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(START_SERVERS_LOCAL) -v4files -kdcport 750 -keysalt des-cbc-crc:v4 + $(LOCAL_MAKE_KEYTAB) -princ changepw/kerberos /krb5/ovsec_adm.srvtab + +unit-test-body:: + $(RUNTEST) VFOURSERVER=../kadmind4 --tool v4server \ + KDBFIVE_EDIT=../../../admin/edit/kdb5_edit + +unit-test-cleanup:: + $(STOP_SERVERS_LOCAL) -v4files diff --git a/src/kadmin/v4server/unit-test/config/ChangeLog b/src/kadmin/v4server/unit-test/config/ChangeLog new file mode 100644 index 000000000..aa01abc17 --- /dev/null +++ b/src/kadmin/v4server/unit-test/config/ChangeLog @@ -0,0 +1,7 @@ +Mon Jul 15 17:18:56 1996 Marc Horowitz <marc@mit.edu> + + * unix.exp: some versions of runtest do not like digits in + command-line variable names. ovsec_edit_keytab renamed to + kadm5_keytab + + diff --git a/src/kadmin/v4server/unit-test/config/unix.exp b/src/kadmin/v4server/unit-test/config/unix.exp new file mode 100644 index 000000000..874092311 --- /dev/null +++ b/src/kadmin/v4server/unit-test/config/unix.exp @@ -0,0 +1,42 @@ +global env + +set kill /bin/kill + +if {[file exists /bin/sleep]} { + set sleep /bin/sleep +} else { + set sleep /usr/bin/sleep +} + +set kpasswd_v4 /usr/athena/bin/kpasswd +set ovpasswd $env(TOP)/kpasswd/kpasswd +set kadmin_local $env(TOP)/cli/kadmin.local +set kdb5_edit $KDBFIVE_EDIT +set remove_changepw_perms ./remove_changepw_perms.sh +set getpid ./getpid.sh +set ovsec_adm_server $env(TOP)/server/kadmind +set ovsec_edit_keytab $env(TOP)/keytab/kadm5_keytab +set hostname [exec hostname] + +# change-password.exp sends ^C to kpasswd to kill it; on HP-UX the +# default intr character is DEL, and setting it on all platforms +# won't hurt +set stty_init "intr \\^c" + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} diff --git a/src/kadmin/v4server/unit-test/getpid.sh b/src/kadmin/v4server/unit-test/getpid.sh new file mode 100644 index 000000000..5c1b1a690 --- /dev/null +++ b/src/kadmin/v4server/unit-test/getpid.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# tcl sucks big fat hairy rocks + +$PS_ALL | awk "/$1/"' && !/awk/ && !/getpid/ && !/expect/ && !/kadmind4/ { print $2 }' diff --git a/src/kadmin/v4server/unit-test/helpers.exp b/src/kadmin/v4server/unit-test/helpers.exp new file mode 100644 index 000000000..7f23b65c8 --- /dev/null +++ b/src/kadmin/v4server/unit-test/helpers.exp @@ -0,0 +1,232 @@ +proc server_pids { } { + global env + + return [eval [concat exec $env(PS_ALL) | \ + awk {{/kadmind4/ && !/awk/ && !/expect/ {printf("%d ", $2)}}}]] +} + +proc server_exit { name status } { + global wait_error_index wait_errno_index wait_status_index + global server_id + global kill + + verbose "$name: stopping V4 kpasswd server." 1 + + # We can't know whether the process exists or not, so we have + # to ignore errors. XXX will close ever time out? + catch {close $server_id} + set pids [server_pids] + if {$pids != {}} { + verbose "server_exit killing process(es) $pids" + catch {exec $kill $pids} + } else { + verbose "server_exit: couldn't find running server(s) to kill" + } + + # wait hangs on AIX if the process was killed; since status == -1 + # in that case, solve the problem by not waiting; the zombies will + # be cleaned up when the test finishes + if {$status == -1} { + return 1 + } + + set ret [wait -i $server_id] + verbose "% Exit $ret" 2 + + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + return 0 + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + return 0 + } + } + + return 1 +} + +proc myfail { msg } { + global mytest_status + fail "$msg" + set mytest_status 1 +} + +proc server_start { name cmdline should_listen args } { + global spawn_id server_id + global VFOURSERVER + global mytest_status + global sleep hostname + + set max_tries 60 + + verbose "$name: starting V4 kpasswd server." 1 + + for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} { + if {$num_tries} { + exec $sleep 5 + verbose "Couldn't connect to V4 kpasswd server; retrying ($num_tries so far)." + } + + spawn $VFOURSERVER $cmdline + set server_id $spawn_id + + foreach test $args { + set mytest_status 0 + uplevel 1 "expect { + -i $server_id + $test + timeout { myfail \"$name: timeout\" } + eof { myfail \"$name: eof while expecting string\" } + }" + + if {$mytest_status == 1} { + return 0 + } + } + + set pids [server_pids] + + if {$should_listen} { + exec $sleep 1 + set save_spawn_id $spawn_id + spawn telnet $hostname kerberos_master + expect { + {Connection refused} { + close -i $save_spawn_id + wait -i $save_spawn_id + close + wait + continue + } + {Connected} { + send "close\n" + close + wait + set spawn_id $save_spawn_id + break + } + default { + close -i $save_spawn_id + wait -i $save_spawn_id + catch {close} + wait + continue + } + } + } else { + break + } + } + + if {$pids == {}} { + # Try twice to find the server processes. Not sure why, + # but there seems to be some sort of race condition in the OS. + + verbose "server_start: couldn't find server process(es) -- trying again" + exec $sleep 1 + set pids [server_pids] + } + + if {$num_tries > $max_tries} { + myfail "$name: couldn't connect to V4 kpasswd server" + return 0 + } else { + if {$pids != {}} { + verbose "server_start: server process ID(s) is/are $pids" + } + pass "$name" + return 1 + } +} + +proc exp_prog { name prog cmdline status args } { + global spawn_id spawn_pid + global mytest_status + global wait_error_index wait_errno_index wait_status_index + + verbose "$name: spawning $prog $cmdline" 1 + + set spawn_pid [eval "spawn $prog $cmdline"] + + # at the end, eof is success + +# lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + lappend args { eof {} } + + foreach test $args { + set mytest_status 0 + uplevel 1 "expect { + $test + timeout { close; myfail \"$name: timeout\" } + eof { myfail \"$name: eof while expecting string\" } + }" + + if {$mytest_status == 1} { return 0 } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 2 + + if {$status == -1} { return 1 } + + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } + + return 1 +} + +proc fix_salt { name fullname oldpw newpw } { + global kdb5_edit + + exp_prog "$name: kdb5_edit" $kdb5_edit "" 0 { + "kdb5_edit:" { send "cpw $fullname\n" } + } { + "Enter password:" { send "$newpw\n" } + } { + "Re-enter password for verification:" { send "$newpw\n" } + } { + "kdb5_edit:" { send "quit\n" } + } +} + +proc unexpire { name fullname } { + global kadmin_local + + # While we're at it, make sure they aren't expired. + exp_prog "$name: kadmin.local" $kadmin_local "" 0 { + "kadmin.local:" { + send "modprinc -expire \"May 6, 1999\" $fullname\n" + } + } { + -re "Principal .* modified." { send "quit\n" } + } +} + +proc kpasswd_v4 { name fullname status oldpw newpw args } { + global kpasswd_v4 s + + eval [concat { + exp_prog $name $kpasswd_v4 "-u $fullname" $status { + -re "Old password for $fullname:" { send "$oldpw\n" } + } { + -re "New Password for $fullname:" { send "$newpw\n" } + } { + -re "Verifying, please re-enter New Password for $fullname:" + { send "$newpw\n" } + } + } $args] +} diff --git a/src/kadmin/v4server/unit-test/remove_changepw_perms.sh b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh new file mode 100644 index 000000000..27d026ff3 --- /dev/null +++ b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# tcl sucks big fat hairy rocks + +ed /krb5/ovsec_adm.acl <<EOF >/dev/null 2>&1 +g/changepw\/kerberos/s/^/#/ +w +q +EOF diff --git a/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp new file mode 100644 index 000000000..3c8e181b2 --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp @@ -0,0 +1,11 @@ +load_lib "helpers.exp" + +set timeout 10 + +exp_prog "setup" $ovsec_edit_keytab \ + "-k /krb5/ovsec_adm.srvtab -a -c -p admin changepw/kerberos" \ + 0 { + "Enter password:" { send "admin\n" } +} { + -re "Entry for principal changepw/kerberos .* added to keytab" {} +} diff --git a/src/kadmin/v4server/unit-test/v4server.1/access.exp b/src/kadmin/v4server/unit-test/v4server.1/access.exp new file mode 100644 index 000000000..4d30fc9c7 --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/access.exp @@ -0,0 +1,88 @@ +load_lib "helpers.exp" + +set timeout 30 + +# Setup: make sure the principals we will use have V4 salt +fix_salt "A.setup" testuser notathena notathena +unexpire "A.setup" testuser +unexpire "A.setup" changepw/kerberos + +proc kill_admin_server {} { + global env kill getpid + + set pid [exec $getpid kadmind] + if {$pid != ""} { + exec $kill $pid + } +} + +proc start_admin_server {} { + global ovsec_adm_server sleep + + set max_tries 60 + + for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} { + if {$num_tries} { + exec $sleep 5 + verbose "$ovsec_adm_server couldn't bind; retrying ($num_tries so far)" + } + if {[catch "exec $ovsec_adm_server" msg]} { + if {[regexp {Address already in use} $msg]} { + continue + } + fail "starting $ovsec_adm_server: $msg" + } + return + } + fail "starting $ovsec_adm_server: $msg" +} + +proc remove_changepw_perms {} { + global remove_changepw_perms + + exec $remove_changepw_perms +} + +proc set_changepw_perms { perms } { + remove_changepw_perms + + exec echo "changepw/kerberos@SECURE-TEST.OV.COM $perms" \ + >> /krb5/ovsec_adm.acl +} + +# start off with a dead admin server +kill_admin_server + +set_changepw_perms "i" +start_admin_server +server_start A.1 "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} +kpasswd_v4 A.1 testuser 2 notathena foobar { + "Operation requires ``change-password'' privilege" {} +} { + "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {} +} { + "Password NOT changed." {} +} +server_exit A.1 -1 +kill_admin_server + +set_changepw_perms "c" +start_admin_server +server_start A.2 "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} +kpasswd_v4 A.2 testuser 2 notathena foobar { + "Operation requires ``get'' privilege" {} +} { + "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {} +} { + "Password NOT changed." {} +} +server_exit A.2 -1 +kill_admin_server + +set_changepw_perms "ci" + +start_admin_server diff --git a/src/kadmin/v4server/unit-test/v4server.1/change-password.exp b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp new file mode 100644 index 000000000..62b9ec30a --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp @@ -0,0 +1,59 @@ +load_lib "helpers.exp" + +set timeout 30 + +spawn stty -a +expect { eof {} } +wait + +# Setup: make sure the principals we will use have V4 salt +fix_salt "CPW.setup" testuser notathena notathena +fix_salt "CPW.setup" pol1 pol111111 pol111111 +fix_salt "CPW.setup" pol2 pol222222 pol222222 +unexpire "CPW.setup" testuser +unexpire "CPW.setup" pol1 +unexpire "CPW.setup" pol2 +unexpire "CPW.setup" changepw/kerberos + +server_start "CPW.all" "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} + +kpasswd_v4 CPW.1 testuser 0 notathena foobar { "Password changed." {} } +kpasswd_v4 CPW.1 testuser 0 foobar notathena { "Password changed." {} } + +kpasswd_v4 CPW.3 pol1 -1 pol111111 foo { + "New password is too short." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.4 pol1 -1 pol111111 foooooooo { + "New password does not have enough character classes." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.5 pol1 -1 pol111111 Abyssinia { + "New password was found in a dictionary" {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.6.setup pol1 0 pol111111 polAAAAAA { "Password changed." {} } +kpasswd_v4 CPW.6 pol1 -1 polAAAAAA pol111111 { + "New password was used previously." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +# this relies on the fact that kdb5_edit resets last_pwd_change, which +# it appears to +kpasswd_v4 CPW.7.setup pol2 0 pol222222 polBBBBBB { "Password changed." {} } +kpasswd_v4 CPW.7 pol2 -1 polBBBBBB pol222222 { + "Password cannot be changed because it was changed too recently." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +server_exit "CPW.all" -1 diff --git a/src/kadmin/v4server/unit-test/v4server.1/usage.exp b/src/kadmin/v4server/unit-test/v4server.1/usage.exp new file mode 100644 index 000000000..4d292067a --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/usage.exp @@ -0,0 +1,26 @@ +load_lib "helpers.exp" + +set timeout 10 + +server_start "U.1: -h" "-h" 0 { + -re {Usage: .*} {} +} { + eof {} +} +server_exit "U.1: -h" 255 + +server_start "U.4: -n" "-n" 1 { + "Enter KDC database master key:" { + myfail "unexpected password prompt" + } + "KADM Server starting in the OVSEC_KADM mode" {} +} + +server_exit "U.4: -n" -1 + +server_start "U.5: no -n" "" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} { + "Enter KDC database master key:" { send "mrroot\n" } +} +server_exit "U.5: no -n" -1 |
