diff options
| author | Marc Horowitz <marc@mit.edu> | 1996-07-22 20:49:46 +0000 |
|---|---|---|
| committer | Marc Horowitz <marc@mit.edu> | 1996-07-22 20:49:46 +0000 |
| commit | edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1 (patch) | |
| tree | 6c2974a97b448c040fa4a31708ec5e02f187526c /src/kadmin | |
| parent | 013bb1391582ed9e653ae706e398ddb8d08cfcc9 (diff) | |
| download | krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.gz krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.tar.xz krb5-edf8b4d8a6a665c2aa150993cd813ea6c5cf12e1.zip | |
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.
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8774 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/kadmin')
228 files changed, 40100 insertions, 2 deletions
diff --git a/src/kadmin/ChangeLog b/src/kadmin/ChangeLog index 4dbf1ae4c6..5408cd3542 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 0000000000..1b5cb8e395 --- /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 0000000000..a69639b86d --- /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 0000000000..c605dd81f6 --- /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 0000000000..5163bebb80 --- /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 0000000000..79e432fe4f --- /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 0000000000..160016af98 --- /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 0000000000..8cca513167 --- /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 0000000000..6b03e73bb7 --- /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 0000000000..91d2a71e4d --- /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 0000000000..f5a67ed530 --- /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 0000000000..abc91e923e --- /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 0000000000..a2432c3d6d --- /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 0000000000..f7bbda5163 --- /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 0000000000..713d7d2123 --- /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 0000000000..2c5e4e753b --- /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 0000000000..975a819f5a --- /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 0000000000..a8db58334a --- /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 0000000000..e19383dd99 --- /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 0000000000..d22229bf0c --- /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 0000000000..4b51140b55 --- /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 0000000000..abc91e923e --- /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 0000000000..a2432c3d6d --- /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 0000000000..89e94b36ba --- /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 0000000000..484852a72d --- /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 0000000000..5dd46d71a1 --- /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 0000000000..44fd07b1f6 --- /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 0000000000..d976c27b2f --- /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 0000000000..9bb800e849 --- /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 0000000000..17da13c525 --- /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 0000000000..053d9328e2 --- /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 0000000000..69a0c62877 --- /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 0000000000..2b6090355d --- /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 0000000000..35b647bdab --- /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 0000000000..be7fe9e25b --- /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 0000000000..d18e0e1640 --- /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 0000000000..942615511f --- /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 0000000000..caf3a2d9d2 --- /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 12d4f04ef2..959626b36d 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 0000000000..0c724422d8 --- /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 0000000000..547bd39c6b --- /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 0000000000..cdec414b73 --- /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 0000000000..f7bd9ca381 --- /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 0000000000..67b8f7c52b --- /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 0000000000..5432edf66a --- /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 0000000000..90be0c406c --- /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 0000000000..4030342632 --- /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 0000000000..33b30ec9c1 --- /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 0000000000..8b167b6b93 --- /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 0000000000..b9f86a3634 --- /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 0000000000..e8cb45367c --- /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 0000000000..7719cc749b --- /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 0000000000..b5bba09d99 --- /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 0000000000..f762282f25 --- /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 0000000000..c9234524ef --- /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 0000000000..8bd54ca4dd --- /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 0000000000..a51626db34 --- /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 0000000000..d31ce33192 --- /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 0000000000..b7520d98d1 --- /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 0000000000..7c6873df7b --- /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 0000000000..8405c01cd1 --- /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 0000000000..6952db7ec1 --- /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 0000000000..746e018c01 --- /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 0000000000..3f31fcb146 --- /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 0000000000..b580e2f6a5 --- /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 0000000000..bac1df125c --- /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 0000000000..a1d37edc77 --- /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 0000000000..ada85efc9a --- /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 0000000000..b9f86a3634 --- /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 0000000000..e8cb45367c --- /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 0000000000..d527fa0d18 --- /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 0000000000..78de2cd6f9 --- /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 0000000000..97a8078c42 --- /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 0000000000..5fa282d89e --- /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 0000000000..83e8c7219d --- /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 0000000000..992d591a11 --- /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 0000000000..3d41c4d9d7 --- /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 0000000000..6c99a47b07 --- /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 0000000000..3ec895ab24 --- /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 0000000000..ded21ba558 --- /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 0000000000..5db33c7c53 --- /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 0000000000..25b1bf6c70 --- /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 0000000000..c505415463 --- /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 0000000000..e8d852f899 --- /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 0000000000..53d4fe0abb --- /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 0000000000..93ac21250f --- /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 0000000000..6e0d4144b7 --- /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 0000000000..9a592c9b8d --- /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 0000000000..c53630f4be --- /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 0000000000..ca7c0bc2db --- /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 0000000000..2f15e706f9 --- /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 0000000000..f2750313de --- /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 0000000000..ad66ebd7c7 --- /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 0000000000..b1a8c0a76c --- /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 0000000000..e0c3fedf91 --- /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 0000000000..e091fe33c7 --- /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 0000000000..bc58c7e7ff --- /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 0000000000..4ca920434e --- /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 0000000000..7b71557860 --- /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 0000000000..c7ac33c0bb --- /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 0000000000..af4ff443ea --- /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 0000000000..5905614e78 --- /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 0000000000..8e82f5a210 --- /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 0000000000..dad19c874c --- /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 0000000000..206aea9beb --- /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 0000000000..5672bd0bf0 --- /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 0000000000..72c11695ad --- /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 0000000000..6c027fc277 --- /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 0000000000..7acb425f79 --- /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 0000000000..5aeb2a44ed --- /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 0000000000..74426a51fb --- /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 0000000000..15cd6d5d9d --- /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 0000000000..0e043f7e51 --- /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 0000000000..2561252ef8 --- /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 0000000000..69df6be92e --- /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 0000000000..1b3366d8bd --- /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 0000000000..247a4382bc --- /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 0000000000..bc07d4f6ec --- /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 0000000000..9b9cab5c4f --- /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 0000000000..a9f7ca4023 --- /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 0000000000..1b807089a9 --- /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 0000000000..159ac638b2 --- /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 0000000000..8da80eb694 --- /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 0000000000..a7a50f045d --- /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 7c8582fd18..6ca64712d0 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 6be5dc2aac..7e9f3e1a0b 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 0000000000..d0f4cbafca --- /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 0000000000..a7ec03161a --- /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 0000000000..46e9620709 --- /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 0000000000..8841a4762b --- /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 0000000000..fe1389a832 --- /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 0000000000..f87ad1cb00 --- /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 0000000000..b78aa9d6f2 --- /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 0000000000..6d865a020f --- /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 0000000000..db042720bf --- /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 0000000000..c77aa016a3 --- /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 0000000000..9dcfbcf01e --- /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 0000000000..4f0354c633 --- /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 0000000000..2cda17a6a3 --- /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 0000000000..e2bc205697 --- /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 0000000000..e132bab2f8 --- /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 0000000000..0db1111c64 --- /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 0000000000..242be89e91 --- /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 0000000000..41bea4f455 --- /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 0000000000..4f8e489fbb --- /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 0000000000..aee4801e99 --- /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 0000000000..98492f909c --- /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 0000000000..9128821d50 --- /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 0000000000..9dc3d9d28f --- /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 0000000000..c92f5fe32f --- /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 0000000000..34f2338934 --- /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 0000000000..c0bec26e61 --- /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 0000000000..8107160afa --- /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 0000000000..206bd85510 --- /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 0000000000..71959a3b05 --- /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 0000000000..798d6c51bd --- /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 0000000000..6e0c4687d6 --- /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 0000000000..b54e3a85e1 --- /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 0000000000..5d7069186e --- /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 0000000000..335b636e7e --- /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 0000000000..df93df4a00 --- /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 0000000000..df93df4a00 --- /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 0000000000..904730dfa0 --- /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 0000000000..d7834d1c74 --- /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 0000000000..d7834d1c74 --- /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 0000000000..c53ff96c1e --- /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 0000000000..14d7b10b54 --- /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 0000000000..14d7b10b54 --- /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 0000000000..3d047c550a --- /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 0000000000..b9fc37319a --- /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 0000000000..ea94ab2d1d --- /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 0000000000..ea94ab2d1d --- /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 0000000000..2e395faf85 --- /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 0000000000..a9c8e79570 --- /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 0000000000..fc5372dd4e --- /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 0000000000..c0a97ef271 --- /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 0000000000..9d83c3ea24 --- /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 0000000000..9d83c3ea24 --- /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 0000000000..f4688aeee3 --- /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 0000000000..36df4616d2 --- /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 0000000000..6d01590923 --- /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 0000000000..ba69b84611 --- /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 0000000000..b102cdfc2a --- /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 0000000000..95a3451d93 --- /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 0000000000..0c6aaac9cb --- /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 0000000000..3fc77fbcbf --- /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 0000000000..75a0fc25f9 --- /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 0000000000..604014849e --- /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 0000000000..01500167b7 --- /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 0000000000..a365e8ea7a --- /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 0000000000..ae3b0c6bff --- /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 0000000000..78c448a6d6 --- /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 0000000000..7a207d7c5e --- /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 0000000000..6eefc24c70 --- /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 0000000000..e1449ef32b --- /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 0000000000..d0acac0219 --- /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 0000000000..f5206aa66f --- /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 0000000000..81275ae26f --- /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 0000000000..78c448a6d6 --- /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 0000000000..70bf66a7d6 --- /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 0000000000..04155bca17 --- /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 0000000000..f09ba3a28c --- /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 0000000000..a192730833 --- /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 0000000000..7ce9c7b4f7 --- /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 0000000000..bca8b8e054 --- /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 0000000000..143af5d4e4 --- /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 0000000000..4e6fd8c468 --- /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 0000000000..83aaa295c7 --- /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 0000000000..cf4ba40f4c --- /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 0000000000..9d45c1e165 --- /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 0000000000..a192730833 --- /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 0000000000..5025e3acb2 --- /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 0000000000..5e7f485083 --- /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 0000000000..81e43f128f --- /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 0000000000..d852bcaabb --- /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 0000000000..86da6c64fe --- /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 0000000000..9d2f8deb29 --- /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 0000000000..93120b8a41 --- /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 0000000000..3af65607ed --- /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 0000000000..aa01abc17f --- /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 0000000000..874092311e --- /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 0000000000..5c1b1a6903 --- /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 0000000000..7f23b65c8f --- /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 0000000000..27d026ff32 --- /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 0000000000..3c8e181b2c --- /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 0000000000..4d30fc9c73 --- /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 0000000000..62b9ec30a8 --- /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 0000000000..4d292067a1 --- /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 |
