diff options
Diffstat (limited to 'src/kadmin/v4server')
41 files changed, 8348 insertions, 0 deletions
diff --git a/src/kadmin/v4server/ChangeLog b/src/kadmin/v4server/ChangeLog new file mode 100644 index 000000000..604014849 --- /dev/null +++ b/src/kadmin/v4server/ChangeLog @@ -0,0 +1,249 @@ +Thu Jul 18 19:46:49 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Tue Jul 9 17:18:56 1996 Marc Horowitz <marc@mit.edu> + + * kadm_stream.c: rename HAS_STDLIB_H to HAVE_STDLIB_H to conform + to the autoconf convention + * configure.in: the old configure.in seemed to be written for some + other directory. Now it's right. + * admin_server.c, kadm_ser_wrap.c, kadm_server.c: renamed + <ovsec_admin/foo.h> to <kadm5/foo.h> + * Makefile.in: complete rewrite. + +Thu Mar 21 20:33:43 1996 Richard Basch <basch@lehman.com> + + * kadm_funcs.c: new principals were being created with two keys, + one of which the key_data_ver=0 and had no valid data. + +Tue Mar 19 19:42:37 1996 Richard Basch <basch@lehman.com> + + * kadm_funcs.c: + changed all references of des-cbc-md5 to des-cbc-crc + fixed uninitialized variable + set kvno modulo 256 in database + +Wed Feb 21 23:34:31 1996 Richard Basch <basch@lehman.com> + + * kadm_funcs.c: Initialize the length element of the krb5_db_entry + structure in kadm_princ2entry (add_entry was failing). + +Wed Dec 13 03:51:53 1995 Chris Provenzano (proven@mit.edu) + + * kadm_funcs.c : Remove mkvno for krb5_db_entry + +Wed Sep 06 14:20:57 1995 Chris Provenzano (proven@mit.edu) + + * admin_server.c, kadm_funcs.c kadm_ser_wrap.c : + s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g + +Tue Sep 05 22:10:34 1995 Chris Provenzano (proven@mit.edu) + + * admin_server.c, kadm_funcs.c, kadm_ser_wrap.c : Remove krb5_enctype + references, and replace with krb5_keytype where appropriate. + +Tue Aug 15 14:31:37 EDT 1995 Paul Park (pjpark@mit.edu) + * admin_server,kadm_funcs,kadm_ser_wrap.c - Replace kadm_find_keytype() + with krb5_dbe_find_keytype(). + + +Thu Aug 10 14:48:26 EDT 1995 Paul Park (pjpark@mit.edu) + * kadm_funcs.c - Add kadm_find_keytype() to find a particular key/salt + pair. Use this to find keys instead of assuming that the + right one's in the first slot. + Fix transposed arguments to strncpy(). + Handle mod_princ_data stuff. + Supply saltblock to encrypt_key_data(). + * admin_server, kadm_ser_wrap.c - Use kadm_find_keytype() to find keys. + + +Mon Aug 7 13:30:46 EDT 1995 Paul Park (pjpark@mit.edu) + * admin_server,kadm_funcs,kadm_ser_wrap.c - Brute force substitutions + to get this to compile. + + +Mon Jul 17 15:12:30 EDT 1995 Paul Park (pjpark@mit.edu) + * kadm_ser_wrap.c - Add NULL stash file argument to krb5_db_fetch_mkey. + + +Fri Jul 7 16:05:11 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Remove all explicit library handling and LDFLAGS. + * configure.in - Add USE_<mumble> and KRB5_LIBRARIES. + + +Tue Jun 27 16:05:27 EDT 1995 Paul Park (pjpark@mit.edu) + * acl_files.c - Change check for return value from fputs(3) from NULL + to EOF. That's what's returned on error. + * admin_server.c - Cast 4th argument of setsockopt(2) to be const char * + * kadm_funcs.c - Cast argument to ctime(3) + * kadm_server.c - Cast first argument to strcpy(3) and strcat(3). + +Tue Jun 20 14:44:54 1995 Tom Yu (tlyu@dragons-lair) + + * configure.in: add tests for TIME_WITH_SYS_TIME and sys/time.h + +Thu Jun 15 17:52:29 EDT 1995 Paul Park (pjpark@mit.edu) + * Makefile.in - Change explicit library names to -l<lib> form, and + change target link line to use $(LD) and associated flags. + Also, for K4, use KRB4_LIB and KRB4_CRYPTO_LIB, these were + split out. + * configure.in - Add shared library usage check. + +Fri Jun 9 19:07:25 1995 <tytso@rsx-11.mit.edu> + + * configure.in: Remove standardized set of autoconf macros, which + are now handled by CONFIG_RULES. + +Fri Jun 9 06:49:36 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kadm_stream.c (vts_long, stv_long): Change u_long to krb5_ui_4 + + * kadm_server.c (kadm_ser_ckpw): Change u_long to krb5_ui_4 + + * kadm_ser_wrap.c (errpkt, kadm_ser_in): Change u_long to krb5_ui_4 + + * kadm_funcs.c (kadm_add_entry): Change u_long to krb5_ui_4 + + * admin_server.c (process_client): Change u_long to krb5_ui_4 + +Sat May 20 22:33:58 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * kadm_stream.c: Based on presence of stdlib.h, include or declare + malloc. + + * configure.in: Check for stdlib.h + +Sun May 7 13:49:54 1995 Ezra Peisach <epeisach@kangaroo.mit.edu> + + * admin_server.c: Avoid warning of redeclaring POSIX_SIGNALS if + already defined. + +Sat Apr 29 00:34:01 1995 Theodore Y. Ts'o <tytso@dcl> + + * admin_server.c (kadm_listen): Use Posix sigaction() instead of + signal() to set signal handlers. This allows us not to + worry about System V signal semantics. Make the code use + POSIX_SIGNALS by default. + +Fri Apr 28 18:08:05 1995 Mark Eichin <eichin@cygnus.com> + + * Makefile.in (KLIB): put KRB4_LIB inside KLIB. + +Thu Apr 27 13:53:41 1995 Mark Eichin <eichin@cygnus.com> + + * Makefile.in (v4kadmind): use KRB4_LIB directly. + +Thu Apr 20 23:21:42 1995 Theodore Y. Ts'o (tytso@dcl) + + * kadm_funcs.c: Don't #include <ndbm.h>, since that's + automatically included by k5-config.h + +Thu Apr 20 15:26:48 1995 Ezra Peisach (epeisach@kangaroo.mit.edu) + + * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): krb_int32 should be + krb5_int32. + + * acl_files.c: Declare acl_abort as static at top of file. + +Sun Apr 16 19:10:17 1995 Mark Eichin <eichin@cygnus.com> + + * kadm_server.c (kadm_ser_cpw, kadm_ser_ckpw): use krb_int32, not + long, for network 4 byte quantities. Should get rid of the + use of memcpy at some point. + +Sat Mar 25 16:59:55 1995 Mark Eichin <eichin@cygnus.com> + + * kadm_funcs.c (kadm_entry2princ): pass kadm_context in to + krb5_524_conv_principal. + +Tue Mar 14 16:45:18 1995 <tytso@rsx-11.mit.edu> + + * Makefile.in: Don't link in the V4 DES library; use the des425 + library to avoid linking the DES code in twice. + +Thu Mar 2 12:25:13 1995 Theodore Y. Ts'o <tytso@dcl> + + * Makefile.in (ISODELIB): Remove reference to $(ISODELIB). + +Wed Mar 1 16:30:08 1995 Theodore Y. Ts'o <tytso@dcl> + + * kadm_server.c: Remove declataions of malloc(); should be done by + header files. + + * configure.in: Remove ISODE_INCLUDE, replace check for -lsocket + and -lnsl with WITH_NETLIB check. + +Tue Feb 28 02:24:56 1995 John Gilmore (gnu at toad.com) + + * admin_server.c, kadm_server.c, kadm-server.h: Avoid + <krb5/...> includes. + +Tue Feb 7 16:42:54 1995 Mark Eichin <eichin@cygnus.com> + + * kadm_funcs.c (kadm_del_entry): fixed call to db_delete_principal. + +Wed Jan 25 18:42:42 1995 Mark Eichin (eichin@tweedledumber.cygnus.com) + + * kadm_server.h (DEL_ACL_FILE): new define, acl file for V4 delete + function. + * kadm_server.c (kadm_ser_add): new function, wrapper for V4 delete. + * kadm_funcs.c (check_access): declare int; add DEL. + (kadm_del_entry): new function, V4 delete from CNS. + (failadd): fix spelling error in log entry. + +Mon Dec 12 13:21:48 1994 Mark Eichin (eichin@cygnus.com) + + * kadm_funcs.c (kadm_entry2princ, kadm_princ2entry, + kadm_chg_srvtab): V4 and V5 max_life are in *different units* so + use the 60*5 conversion factor. + +Fri Nov 18 15:51:11 1994 Theodore Y. Ts'o (tytso@dcl) + + * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change): Add + magic numbers of keyblock structre. + +Fri Nov 18 01:11:58 1994 Mark Eichin <eichin@cygnus.com> + + * configure.in: use CHECK_SIGNALS instead of expansion (from + epeisach). + +Wed Oct 19 18:53:45 1994 Theodore Y. Ts'o (tytso@dcl) + + * kadm_ser_wrap.c (kadm_ser_init): Use krb5_use_cstype() to + initialize the master_encblock structure. + +Thu Sep 29 22:41:20 1994 Theodore Y. Ts'o (tytso@dcl) + + * Makefile.in: relink executable if libraries change + +Thu Sep 15 10:53:37 1994 Theodore Y. Ts'o (tytso@dcl) + + * admin_server.c (close_syslog, byebye): Move these two functions + before main(), so that they get declared properly. Otherwise + suncc will refuse to compile the file. + + * kadm_funcs.c (kadm_add_entry, kadm_mod_entry, kadm_change, + kadm_chg_srvtab): use krb5_timeofday instead of time(0). + +Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair) + + * admin_server.c: pick up <sys/time.h> (needed to get FD_SET, + etc.) + +Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair) + + * Makefile.in: no longer trying to install v4kadmind as krb5kdc + :-) + * configure.in: another try at making dbm libs dtrt + +Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair) + + * admin_server.c: fixed calls that should have invoked + krb5_init_ets + +Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair) + + * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to + krb5_build_principal + diff --git a/src/kadmin/v4server/Makefile.in b/src/kadmin/v4server/Makefile.in new file mode 100644 index 000000000..01500167b --- /dev/null +++ b/src/kadmin/v4server/Makefile.in @@ -0,0 +1,23 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) \ + -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 -DNEED_SOCKETS + +LOCALINCLUDE = -I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I. + +PROG = kadmind4 +OBJS = kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o kadm_err.o + +all:: $(PROG) + +kadm_err.c kadm_err.h: $(srcdir)/kadm_err.et + +$(OBJS): kadm_err.h + +$(PROG): $(OBJS) $(DEPLIBS) + $(CC) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS) + +install:: + $(INSTALL_PROGRAM) $(PROG) ${DESTDIR}$(ADMIN_BINDIR)/$(PROG) + +clean:: + $(RM) $(PROG) $(OBJS) diff --git a/src/kadmin/v4server/Makefile.ov b/src/kadmin/v4server/Makefile.ov new file mode 100644 index 000000000..a365e8ea7 --- /dev/null +++ b/src/kadmin/v4server/Makefile.ov @@ -0,0 +1,42 @@ +TOP = .. +include $(TOP)/config.mk/template + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS) +endif + +ETABLES = kadm_err.et +expand ErrorTables + +depend:: kadm_err.h + +PROG := ovsec_v4adm_server + +SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \ + kadm_stream.c kadm_supp.c acl_files.c kadm_err.c + +OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o kadm_err.o + +LIBS := $(LIBADMCLNT) $(LIBRPCLIB) \ + $(LIBKADM) $(LIBKRB) $(LIBDES425) \ + $(LIBGSSAPI_KRB5) $(LIBKDB5) $(LIBKRB5_ALL) \ + $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB) + +ifdef WAIT_USES_INT +WAIT_FLAGS = -DWAIT_USES_INT +endif +ifdef OPEN_NEEDS_FCNTL +FCNTL_FLAGS = -DNEED_SYS_FCNTL_H +endif + +CFLAGS := -DOVSEC_KADM -DUSE_KADM5_API_VERSION=1 \ + $(WAIT_FLAGS) $(FCNLT_FLAGS) -I. \ + -I$(TOP)/../include/kerberosIV -I$(TOP)/../../src/include/kerberosIV \ + $(CFLAGS) + +expand InstallServer +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/v4server/acl_files.c b/src/kadmin/v4server/acl_files.c new file mode 100644 index 000000000..ae3b0c6bf --- /dev/null +++ b/src/kadmin/v4server/acl_files.c @@ -0,0 +1,536 @@ +/* + * kadmin/v4server/acl_files.c + * + * Copyright 1987,1989 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + */ + + +/*** Routines for manipulating access control list files ***/ + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <ctype.h> +#include <fcntl.h> +#include "krb.h" + +#ifndef KRB_REALM +#define KRB_REALM "ATHENA.MIT.EDU" +#endif + +/* "aname.inst@realm" */ +#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) +#define INST_SEP '.' +#define REALM_SEP '@' + +#define LINESIZE 2048 /* Maximum line length in an acl file */ + +#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ +#define WAIT_TIME 300 /* Maximum time allowed write acl file */ + +#define CACHED_ACLS 8 /* How many acls to cache */ + /* Each acl costs 1 open file descriptor */ +#define ACL_LEN 16 /* Twice a reasonable acl length */ + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define COR(a,b) ((a!=NULL)?(a):(b)) + +extern int errno; + +extern char *malloc(), *calloc(); +extern time_t time(); + +static int acl_abort(); + +/* Canonicalize a principal name */ +/* If instance is missing, it becomes "" */ +/* If realm is missing, it becomes the local realm */ +/* Canonicalized form is put in canon, which must be big enough to hold + MAX_PRINCIPAL_SIZE characters */ +void acl_canonicalize_principal(principal, canon) +char *principal; +char *canon; +{ + char *dot, *atsign, *end; + int len; + + dot = strchr(principal, INST_SEP); + atsign = strchr(principal, REALM_SEP); + + /* Maybe we're done already */ + if(dot != NULL && atsign != NULL) { + if(dot < atsign) { + /* It's for real */ + /* Copy into canon */ + strncpy(canon, principal, MAX_PRINCIPAL_SIZE); + canon[MAX_PRINCIPAL_SIZE-1] = '\0'; + return; + } else { + /* Nope, it's part of the realm */ + dot = NULL; + } + } + + /* No such luck */ + end = principal + strlen(principal); + + /* Get the principal name */ + len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); + strncpy(canon, principal, len); + canon += len; + + /* Add INST_SEP */ + *canon++ = INST_SEP; + + /* Get the instance, if it exists */ + if(dot != NULL) { + ++dot; + len = MIN(INST_SZ, COR(atsign, end) - dot); + strncpy(canon, dot, len); + canon += len; + } + + /* Add REALM_SEP */ + *canon++ = REALM_SEP; + + /* Get the realm, if it exists */ + /* Otherwise, default to local realm */ + if(atsign != NULL) { + ++atsign; + len = MIN(REALM_SZ, end - atsign); + strncpy(canon, atsign, len); + canon += len; + *canon++ = '\0'; + } else if(krb_get_lrealm(canon, 1) != KSUCCESS) { + strcpy(canon, KRB_REALM); + } +} + +/* Get a lock to modify acl_file */ +/* Return new FILE pointer */ +/* or NULL if file cannot be modified */ +/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ +static FILE *acl_lock_file(acl_file) +char *acl_file; +{ + struct stat s; + char new[LINESIZE]; + int nfd; + FILE *nf; + int mode; + + if(stat(acl_file, &s) < 0) return(NULL); + mode = s.st_mode; + sprintf(new, NEW_FILE, acl_file); + for(;;) { + /* Open the new file */ + if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { + if(errno == EEXIST) { + /* Maybe somebody got here already, maybe it's just old */ + if(stat(new, &s) < 0) return(NULL); + if(time(0) - s.st_ctime > WAIT_TIME) { + /* File is stale, kill it */ + unlink(new); + continue; + } else { + /* Wait and try again */ + sleep(1); + continue; + } + } else { + /* Some other error, we lose */ + return(NULL); + } + } + + /* If we got to here, the lock file is ours and ok */ + /* Reopen it under stdio */ + if((nf = fdopen(nfd, "w")) == NULL) { + /* Oops, clean up */ + unlink(new); + } + return(nf); + } +} + +/* Commit changes to acl_file written onto FILE *f */ +/* Returns zero if successful */ +/* Returns > 0 if lock was broken */ +/* Returns < 0 if some other error occurs */ +/* Closes f */ +static int acl_commit(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + sprintf(new, NEW_FILE, acl_file); + if(fflush(f) < 0 + || fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + acl_abort(acl_file, f); + return(-1); + } + + ret = rename(new, acl_file); + fclose(f); + return(ret); +} + +/* Abort changes to acl_file written onto FILE *f */ +/* Returns 0 if successful, < 0 otherwise */ +/* Closes f */ +static int acl_abort(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + /* make sure we aren't nuking someone else's file */ + if(fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + fclose(f); + return(-1); + } else { + sprintf(new, NEW_FILE, acl_file); + ret = unlink(new); + fclose(f); + return(ret); + } +} + +/* Initialize an acl_file */ +/* Creates the file with permissions perm if it does not exist */ +/* Erases it if it does */ +/* Returns return value of acl_commit */ +int acl_initialize(acl_file, perm) +char *acl_file; +int perm; +{ + FILE *new; + int fd; + + /* Check if the file exists already */ + if((new = acl_lock_file(acl_file)) != NULL) { + return(acl_commit(acl_file, new)); + } else { + /* File must be readable and writable by owner */ + if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { + return(-1); + } else { + close(fd); + return(0); + } + } +} + +/* Eliminate all whitespace character in buf */ +/* Modifies its argument */ +static nuke_whitespace(buf) +char *buf; +{ + register char *pin, *pout; + + for(pin = pout = buf; *pin != '\0'; pin++) + if(!isspace(*pin)) *pout++ = *pin; + *pout = '\0'; /* Terminate the string */ +} + +/* Hash table stuff */ + +struct hashtbl { + int size; /* Max number of entries */ + int entries; /* Actual number of entries */ + char **tbl; /* Pointer to start of table */ +}; + +/* Make an empty hash table of size s */ +static struct hashtbl *make_hash(size) +int size; +{ + struct hashtbl *h; + + if(size < 1) size = 1; + h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); + h->size = size; + h->entries = 0; + h->tbl = (char **) calloc(size, sizeof(char *)); + return(h); +} + +/* Destroy a hash table */ +static destroy_hash(h) +struct hashtbl *h; +{ + int i; + + for(i = 0; i < h->size; i++) { + if(h->tbl[i] != NULL) free(h->tbl[i]); + } + free(h->tbl); + free(h); +} + +/* Compute hash value for a string */ +static unsigned hashval(s) +register char *s; +{ + register unsigned hv; + + for(hv = 0; *s != '\0'; s++) { + hv ^= ((hv << 3) ^ *s); + } + return(hv); +} + +/* Add an element to a hash table */ +static add_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + char *s; + char **old; + int i; + + /* Make space if it isn't there already */ + if(h->entries + 1 > (h->size >> 1)) { + old = h->tbl; + h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); + for(i = 0; i < h->size; i++) { + if(old[i] != NULL) { + hv = hashval(old[i]) % (h->size << 1); + while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); + h->tbl[hv] = old[i]; + } + } + h->size = h->size << 1; + free(old); + } + + hv = hashval(el) % h->size; + while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; + s = malloc(strlen(el)+1); + strcpy(s, el); + h->tbl[hv] = s; + h->entries++; +} + +/* Returns nonzero if el is in h */ +static check_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + + for(hv = hashval(el) % h->size; + h->tbl[hv] != NULL; + hv = (hv + 1) % h->size) { + if(!strcmp(h->tbl[hv], el)) return(1); + } + return(0); +} + +struct acl { + char filename[LINESIZE]; /* Name of acl file */ + int fd; /* File descriptor for acl file */ + struct stat status; /* File status at last read */ + struct hashtbl *acl; /* Acl entries */ +}; + +static struct acl acl_cache[CACHED_ACLS]; + +static int acl_cache_count = 0; +static int acl_cache_next = 0; + +/* Returns < 0 if unsuccessful in loading acl */ +/* Returns index into acl_cache otherwise */ +/* Note that if acl is already loaded, this is just a lookup */ +static int acl_load(name) +char *name; +{ + int i; + FILE *f; + struct stat s; + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) { + if(!strcmp(acl_cache[i].filename, name) + && acl_cache[i].fd >= 0) goto got_it; + } + + /* It isn't, load it in */ + /* maybe there's still room */ + if(acl_cache_count < CACHED_ACLS) { + i = acl_cache_count++; + } else { + /* No room, clean one out */ + i = acl_cache_next; + acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; + close(acl_cache[i].fd); + if(acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + } + + /* Set up the acl */ + strcpy(acl_cache[i].filename, name); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + /* Force reload */ + acl_cache[i].acl = (struct hashtbl *) 0; + + got_it: + /* + * See if the stat matches + * + * Use stat(), not fstat(), as the file may have been re-created by + * acl_add or acl_delete. If this happens, the old inode will have + * no changes in the mod-time and the following test will fail. + */ + if(stat(acl_cache[i].filename, &s) < 0) return(-1); + if(acl_cache[i].acl == (struct hashtbl *) 0 + || s.st_nlink != acl_cache[i].status.st_nlink + || s.st_mtime != acl_cache[i].status.st_mtime + || s.st_ctime != acl_cache[i].status.st_ctime) { + /* Gotta reload */ + if(acl_cache[i].fd >= 0) close(acl_cache[i].fd); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1); + if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = make_hash(ACL_LEN); + while(fgets(buf, sizeof(buf), f) != NULL) { + nuke_whitespace(buf); + acl_canonicalize_principal(buf, canon); + add_hash(acl_cache[i].acl, canon); + } + fclose(f); + acl_cache[i].status = s; + } + return(i); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Principal is not canonicalized, and no wildcarding is done */ +acl_exact_match(acl, principal) +char *acl; +char *principal; +{ + int idx; + + return((idx = acl_load(acl)) >= 0 + && check_hash(acl_cache[idx].acl, principal)); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Recognizes wildcards in acl of the form + name.*@realm, *.*@realm, and *.*@* */ +acl_check(acl, principal) +char *acl; +char *principal; +{ + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + char *realm, *tmp; + + acl_canonicalize_principal(principal, canon); + + /* Is it there? */ + if(acl_exact_match(acl, canon)) return(1); + + /* Try the wildcards */ + realm = strchr(canon, REALM_SEP); + tmp = strchr(canon, INST_SEP); + *tmp = '\0'; /* Chuck the instance */ + + sprintf(buf, "%s.*%s", canon, realm); + if(acl_exact_match(acl, buf)) return(1); + + sprintf(buf, "*.*%s", realm); + if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); + + return(0); +} + +/* Adds principal to acl */ +/* Wildcards are interpreted literally */ +acl_add(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL) { + if(fputs(acl_cache[idx].acl->tbl[i], new) == EOF + || putc('\n', new) != '\n') { + acl_abort(acl, new); + return(-1); + } + } + } + fputs(canon, new); + putc('\n', new); + return(acl_commit(acl, new)); +} + +/* Removes principal from acl */ +/* Wildcards are interpreted literally */ +acl_delete(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((!acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL + && strcmp(acl_cache[idx].acl->tbl[i], canon)) { + fputs(acl_cache[idx].acl->tbl[i], new); + putc('\n', new); + } + } + return(acl_commit(acl, new)); +} + diff --git a/src/kadmin/v4server/acl_files.doc b/src/kadmin/v4server/acl_files.doc new file mode 100644 index 000000000..78c448a6d --- /dev/null +++ b/src/kadmin/v4server/acl_files.doc @@ -0,0 +1,107 @@ +PROTOTYPE ACL LIBRARY + +Introduction + +An access control list (ACL) is a list of principals, where each +principal is is represented by a text string which cannot contain +whitespace. The library allows application programs to refer to named +access control lists to test membership and to atomically add and +delete principals using a natural and intuitive interface. At +present, the names of access control lists are required to be Unix +filenames, and refer to human-readable Unix files; in the future, when +a networked ACL server is implemented, the names may refer to a +different namespace specific to the ACL service. + + +Usage + +cc <files> -lacl -lkrb. + + + +Principal Names + +Principal names have the form + +<name>[.<instance>][@<realm>] + +e.g. + +asp +asp.root +asp@ATHENA.MIT.EDU +asp.@ATHENA.MIT.EDU +asp.root@ATHENA.MIT.EDU + +It is possible for principals to be underspecified. If instance is +missing, it is assumed to be "". If realm is missing, it is assumed +to be local_realm. The canonical form contains all of name, instance, +and realm; the acl_add and acl_delete routines will always +leave the file in that form. Note that the canonical form of +asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU. + + +Routines + +acl_canonicalize_principal(principal, buf) +char *principal; +char *buf; /*RETVAL*/ + +Store the canonical form of principal in buf. Buf must contain enough +space to store a principal, given the limits on the sizes of name, +instance, and realm specified in /usr/include/krb.h. + +acl_check(acl, principal) +char *acl; +char *principal; + +Returns nonzero if principal appears in acl. Returns 0 if principal +does not appear in acl, or if an error occurs. Canonicalizes +principal before checking, and allows the ACL to contain wildcards. + +acl_exact_match(acl, principal) +char *acl; +char *principal; + +Like acl_check, but does no canonicalization or wildcarding. + +acl_add(acl, principal) +char *acl; +char *principal; + +Atomically adds principal to acl. Returns 0 if successful, nonzero +otherwise. It is considered a failure if principal is already in acl. +This routine will canonicalize principal, but will treat wildcards +literally. + +acl_delete(acl, principal) +char *acl; +char *principal; + +Atomically deletes principal from acl. Returns 0 if successful, +nonzero otherwise. It is consider a failure if principal is not +already in acl. This routine will canonicalize principal, but will +treat wildcards literally. + +acl_initialize(acl, mode) +char *acl; +int mode; + +Initialize acl. If acl file does not exist, creates it with mode +mode. If acl exists, removes all members. Returns 0 if successful, +nonzero otherwise. WARNING: Mode argument is likely to change with +the eventual introduction of an ACL service. + + +Known problems + +In the presence of concurrency, there is a very small chance that +acl_add or acl_delete could report success even though it would have +had no effect. This is a necessary side effect of using lock files +for concurrency control rather than flock(2), which is not supported +by NFS. + +The current implementation caches ACLs in memory in a hash-table +format for increased efficiency in checking membership; one effect of +the caching scheme is that one file descriptor will be kept open for +each ACL cached, up to a maximum of 8. diff --git a/src/kadmin/v4server/admin_server.c b/src/kadmin/v4server/admin_server.c new file mode 100644 index 000000000..7a207d7c5 --- /dev/null +++ b/src/kadmin/v4server/admin_server.c @@ -0,0 +1,684 @@ +/* + * kadmin/v4server/admin_server.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Top-level loop of the kerberos Administration server + */ + +#include <mit-copyright.h> +/* + admin_server.c + this holds the main loop and initialization and cleanup code for the server +*/ + +#ifdef _AIX +#include <sys/select.h> +#endif + +/* define it for now */ +#ifndef POSIX_SIGNALS +#define POSIX_SIGNALS +#endif + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <signal.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifndef POSIX_SIGNALS +#ifndef sigmask +#define sigmask(m) (1 <<((m)-1)) +#endif +#endif /* POSIX_SIGNALS */ +#include <sys/wait.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <syslog.h> + +#ifdef OVSEC_KADM +#include <kadm5/admin.h> +void *ovsec_handle; +kadm5_config_params params; +#endif + +#include "k5-int.h" +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include "com_err.h" +#include "kadm_server.h" + +#ifdef POSIX_SIGTYPE +#define SIGNAL_RETURN return +#else +#define SIGNAL_RETURN return(0) +#endif + +/* Almost all procs and such need this, so it is global */ +admin_params prm; /* The command line parameters struct */ + +char prog[32]; /* WHY IS THIS NEEDED??????? */ +char *progname = prog; +char *acldir = DEFAULT_ACL_DIR; +char krbrlm[REALM_SZ]; +extern Kadm_Server server_parm; +krb5_context kadm_context; +int debug; + +/* close the system log file */ +void close_syslog() +{ + syslog(LOG_INFO, "Shutting down V4 admin server"); +} + +void byebye() /* say goodnight gracie */ +{ + printf("Admin Server (kadm server) has completed operation.\n"); +} + +/* +** Main does the logical thing, it sets up the database and RPC interface, +** as well as handling the creation and maintenance of the syslog file... +*/ +main(argc, argv) /* admin_server main routine */ +int argc; +char *argv[]; +{ + int errval; + int c; + char *lrealm; + extern char *optarg; + extern int fascist_cpw; + +#ifdef OVSEC_KADM + memset(¶ms, 0, sizeof(params)); +#endif + + krb5_init_context(&kadm_context); + krb5_init_ets(kadm_context); + initialize_kadm_error_table(); + prog[sizeof(prog)-1]='\0'; /* Terminate... */ + (void) strncpy(prog, argv[0], sizeof(prog)-1); + + /* initialize the admin_params structure */ + prm.sysfile = KADM_SYSLOG; /* default file name */ + prm.inter = 1; + + memset(krbrlm, 0, sizeof(krbrlm)); + + fascist_cpw = 1; /* by default, enable fascist mode */ + while ((c = getopt(argc, argv, "Df:hnd:a:r:FN")) != EOF) + switch(c) { + case 'D': + debug++; + break; + case 'f': /* Syslog file name change */ + prm.sysfile = optarg; + break; + case 'n': + prm.inter = 0; + break; + case 'a': /* new acl directory */ + acldir = optarg; + break; + case 'd': +#ifdef OVSEC_KADM + params.dbname = optarg; + params.mask |= KADM5_CONFIG_DBNAME; +#else + if (errval = krb5_db_set_name(kadm_context, optarg)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } +#endif + break; + case 'F': + fascist_cpw++; + break; + case 'N': + fascist_cpw = 0; + break; + case 'r': + (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1); + break; + case 'h': /* get help on using admin_server */ + default: + printf("Usage: admin_server [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n"); + exit(-1); /* failure */ + } + + if (krbrlm[0] == 0) { + if (errval = krb5_get_default_realm(kadm_context, &lrealm)) { + com_err(argv[0], errval, "while attempting to get local realm"); + exit(1); + } + (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1); + } + +#ifdef OVSEC_KADM + params.realm = krbrlm; + params.mask |= KADM5_CONFIG_REALM; + + if (errval = kadm5_get_config_params(kadm_context, NULL, NULL, + ¶ms, ¶ms)) { + com_err(argv[0], errval, "while retrieving kadm5 params"); + exit(1); + } + if (errval = krb5_db_set_name(kadm_context, params.dbname)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } +#endif /* OVSEC_KADM */ + + printf("KADM Server %s initializing\n",KADM_VERSTR); + printf("Please do not use 'kill -9' to kill this job, use a\n"); + printf("regular kill instead\n\n"); + +#ifdef OVSEC_KADM + printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n", + debug ? "" : "parent ", getpid()); +#else + printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST"); +#endif + + openlog(argv[0], LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */ + syslog(LOG_INFO, "V4 admin server starting"); + + errval = krb5_db_init(kadm_context); /* Open the Kerberos database */ + if (errval) { + fprintf(stderr, "error: krb5_db_init() failed"); + close_syslog(); + byebye(); + exit(1); + } + if (errval = krb5_db_set_lockmode(kadm_context, TRUE)) { + com_err(argv[0], errval, "while setting db to nonblocking"); + close_syslog(); + byebye(); + exit(1); + } + /* set up the server_parm struct */ + if ((errval = kadm_ser_init(prm.inter, krbrlm +#ifdef OVSEC_KADM + , ¶ms +#endif + ))==KADM_SUCCESS) { + krb5_db_fini(kadm_context); /* Close the Kerberos database-- + will re-open later */ + errval = kadm_listen(); /* listen for calls to server from + clients */ + } + if (errval != KADM_SUCCESS) { + fprintf(stderr,"error: %s\n",error_message(errval)); + krb5_db_fini(kadm_context); /* Close if error */ + } + close_syslog(); /* Close syslog file, print + closing note */ + byebye(); /* Say bye bye on the terminal + in use */ + return 0; +} /* procedure main */ + + +static void clear_secrets() +{ + krb5_finish_key(kadm_context, &server_parm.master_encblock); + memset((char *)&server_parm.master_encblock, 0, + sizeof (server_parm.master_encblock)); + memset((char *)server_parm.master_keyblock.contents, 0, + server_parm.master_keyblock.length); + server_parm.mkvno = 0L; + return; +} + +static exit_now = 0; + +krb5_sigtype +doexit(sig) + int sig; +{ + exit_now = 1; + SIGNAL_RETURN; +} + +unsigned pidarraysize = 0; +int *pidarray = (int *)0; +int unknown_child = 0; + +/* +kadm_listen +listen on the admin servers port for a request +*/ +kadm_listen() +{ + extern int errno; + int found; + int admin_fd; + int peer_fd; + fd_set mask, readfds; + struct sockaddr_in peer; + int addrlen; + void process_client(), kill_children(); + int pid; + krb5_sigtype do_child(); +#ifdef POSIX_SIGNALS + struct sigaction new_act; + + new_act.sa_handler = doexit; + sigemptyset(&new_act.sa_mask); + sigaction(SIGINT, &new_act, 0); + sigaction(SIGTERM, &new_act, 0); + sigaction(SIGHUP, &new_act, 0); + sigaction(SIGQUIT, &new_act, 0); + sigaction(SIGALRM, &new_act, 0); + new_act.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &new_act, 0); + new_act.sa_handler = do_child; + sigaction(SIGCHLD, &new_act, 0); +#else + signal(SIGINT, doexit); + signal(SIGTERM, doexit); + signal(SIGHUP, doexit); + signal(SIGQUIT, doexit); + signal(SIGPIPE, SIG_IGN); /* get errors on write() */ + signal(SIGALRM, doexit); + signal(SIGCHLD, do_child); +#endif + + if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return KADM_NO_SOCK; + if (debug) { + int one = 1; + if (setsockopt(admin_fd, SOL_SOCKET, SO_REUSEADDR, &one, + sizeof(int)) < 0) { + syslog(LOG_ERR, "setsockopt: %m"); + return KADM_NO_BIND; + } + } + if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr, + sizeof(struct sockaddr_in)) < 0) + return KADM_NO_BIND; + (void) listen(admin_fd, 1); + FD_ZERO(&mask); + FD_SET(admin_fd, &mask); + + for (;;) { /* loop nearly forever */ + if (exit_now) { + clear_secrets(); + kill_children(); + return(0); + } + readfds = mask; + if ((found = select(admin_fd+1,&readfds,(fd_set *)0, + (fd_set *)0, (struct timeval *)0)) == 0) + continue; /* no things read */ + if (found < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "select: %s", error_message(errno)); + continue; + } + if (FD_ISSET(admin_fd, &readfds)) { + /* accept the conn */ + addrlen = sizeof(peer); + if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer, + &addrlen)) < 0) { + syslog(LOG_ERR, "accept: %s", error_message(errno)); + continue; + } + + if (debug) { + process_client(peer_fd, &peer); + } else if (pid = fork()) { + /* parent */ + if (pid < 0) { + syslog(LOG_ERR, "fork: %s", error_message(errno)); + (void) close(peer_fd); + continue; + } + /* fork succeeded: keep tabs on child */ + (void) close(peer_fd); + if (unknown_child != pid) { + if (pidarray) { + pidarray = (int *)realloc((char *)pidarray, + (++pidarraysize * sizeof(int))); + pidarray[pidarraysize-1] = pid; + } else { + pidarray = (int *)malloc((pidarraysize = 1) * sizeof(int)); + pidarray[0] = pid; + } + } /* End if unknown_child != pid.*/ + } else { + /* child */ + (void) close(admin_fd); + process_client(peer_fd, &peer); + } + } else { + syslog(LOG_ERR, "something else woke me up!"); + return(0); + } + } + /*NOTREACHED*/ +} + +void process_client(fd, who) + int fd; + struct sockaddr_in *who; +{ + u_char *dat; + int dat_len; + u_short dlen; + int retval; + int on = 1; + int nentries = 1; + krb5_db_entry sprinc_entries; + krb5_boolean more; + krb5_keyblock cpw_skey; + krb5_key_data *kdatap; + int status; + +#ifdef OVSEC_KADM +#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab" + char *service_name; + + service_name = (char *) malloc(strlen(server_parm.sname) + + strlen(server_parm.sinst) + + strlen(server_parm.krbrlm) + 3); + if (service_name == NULL) { + syslog(LOG_ERR, "error: out of memory allocating service name"); + cleanexit(1); + } + sprintf(service_name, "%s/%s@%s", server_parm.sname, + server_parm.sinst, server_parm.krbrlm); + + retval = ovsec_kadm_init_with_skey(service_name, + OVSEC_KADM_SRVTAB, + OVSEC_KADM_ADMIN_SERVICE, krbrlm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_handle); + if (retval) { + syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s", + error_message(retval)); + cleanexit(1); + } + free(service_name); + + if (retval = krb5_db_set_name(kadm_context, params.dbname)) { + syslog(LOG_ERR, "%s while setting dbname", error_message(retval)); + cleanexit(1); + } +#endif + +#ifndef NOENCRYPTION + /* Must do it here, since this is after the fork() call */ + des_init_random_number_generator(server_parm.master_keyblock.contents); +#endif /* NOENCRYPTION */ + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const char *) &on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt keepalive: %d", errno); + + server_parm.recv_addr = *who; + + if (retval = krb5_db_init(kadm_context)) { /* Open as client */ + syslog(LOG_ERR, "can't open krb db: %s", error_message(retval)); + cleanexit(1); + } + /* need to set service key to changepw.KRB_MASTER */ + + status = krb5_db_get_principal(kadm_context, server_parm.sprinc, + &sprinc_entries, + &nentries, &more); + /* ugh... clean this up later */ + if (status == KRB5_KDB_DB_INUSE) { + /* db locked */ + krb5_ui_4 retcode = KADM_DB_INUSE; + char *pdat; + + dat_len = KADM_VERSIZE + sizeof(krb5_ui_4); + dat = (u_char *) malloc((unsigned)dat_len); + pdat = (char *) dat; + retcode = htonl((krb5_ui_4) KADM_DB_INUSE); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + goto out; + } else if (!nentries) { + syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst); + cleanexit(2); + } else if (status) { + syslog(LOG_ERR, error_message(status)); + cleanexit(2); + } + + status = krb5_dbe_find_enctype(kadm_context, + &sprinc_entries, + ENCTYPE_DES_CBC_MD5, + -1, + -1, + &kdatap); + if (status) { + syslog(LOG_ERR, "find enctype failed: %s", error_message(status)); + cleanexit(1); + } + + status = krb5_dbekd_decrypt_key_data(kadm_context, + &server_parm.master_encblock, + kdatap, + &cpw_skey, + (krb5_keysalt *) NULL); + if (status) { + syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status)); + cleanexit(1); + } + /* if error, will show up when rd_req fails */ + (void) krb_set_key((char *)cpw_skey.contents, 0); + while (1) { + if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) != + sizeof(u_short)) { + if (retval < 0) + syslog(LOG_ERR, "dlen read: %s", error_message(errno)); + else if (retval) + syslog(LOG_ERR, "short dlen read: %d", retval); + (void) close(fd); +#ifdef OVSEC_KADM + (void) ovsec_kadm_destroy(ovsec_handle); +#endif + cleanexit(retval ? 3 : 0); + } + if (exit_now) { + cleanexit(0); + } + dat_len = (int) ntohs(dlen); + dat = (u_char *) malloc((unsigned)dat_len); + if (!dat) { + syslog(LOG_ERR, "malloc: No memory"); + (void) close(fd); + cleanexit(4); + } + if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) { + if (retval < 0) + syslog(LOG_ERR, "data read: %s", error_message(errno)); + else + syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval); + (void) close(fd); + cleanexit(5); + } + if (exit_now) { + cleanexit(0); + } + if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS) + syslog(LOG_ERR, "processing request: %s", error_message(retval)); + + /* kadm_ser_in did the processing and returned stuff in + dat & dat_len , return the appropriate data */ + + out: + dlen = (u_short) dat_len; + + if (dat_len != (int)dlen) { + clear_secrets(); + abort(); /* XXX */ + } + dlen = htons(dlen); + + if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) { + syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(6); + } + + if (krb_net_write(fd, (char *)dat, dat_len) < 0) { + syslog(LOG_ERR, "writing to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(7); + } + free((char *)dat); + } + /*NOTREACHED*/ +} + +krb5_sigtype +do_child(sig) + int sig; +{ + /* SIGCHLD brings us here */ + int pid; + register int i, j; + +#ifdef WAIT_USES_INT + int status; +#else + union wait status; +#endif + + pid = wait(&status); + + for (i = 0; i < pidarraysize; i++) + if (pidarray[i] == pid) { + /* found it */ + for (j = i; j < pidarraysize-1; j++) + /* copy others down */ + pidarray[j] = pidarray[j+1]; + pidarraysize--; +#ifdef WAIT_USES_INT + if (WIFEXITED(status) || WIFSIGNALED(status)) + if (WTERMSIG(status) || WEXITSTATUS(status)) + syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + if (status.w_retcode || status.w_coredump || status.w_termsig) + syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + SIGNAL_RETURN; + } + unknown_child = pid; +#ifdef WAIT_USES_INT + syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + SIGNAL_RETURN; +} + +cleanexit(val) +{ + krb5_db_fini(kadm_context); + clear_secrets(); + exit(val); +} + +void +kill_children() +{ + register int i; +#ifdef POSIX_SIGNALS + sigset_t oldmask, igmask; +#else + int osigmask; +#endif + +#ifdef POSIX_SIGNALS + sigemptyset(&igmask); + sigaddset(&igmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &igmask, &oldmask); +#else + osigmask = sigblock(sigmask(SIGCHLD)); +#endif + + for (i = 0; i < pidarraysize; i++) { + kill(pidarray[i], SIGINT); + syslog(LOG_ERR, "killing child %d", pidarray[i]); + } +#ifdef POSIX_SIGNALS + sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0); +#else + sigsetmask(osigmask); +#endif + return; +} + +#ifdef OVSEC_KADM +krb5_ui_4 convert_ovsec_to_kadm(val) + krb5_ui_4 val; +{ + switch (val) { + case KADM5_AUTH_GET: + case KADM5_AUTH_ADD: + case KADM5_AUTH_MODIFY: + case KADM5_AUTH_DELETE: + case KADM5_AUTH_INSUFFICIENT: + case KADM5_AUTH_LIST: + case KADM5_AUTH_CHANGEPW: + return KADM_UNAUTH; + case KADM5_BAD_DB: + return KADM_UK_RERROR; + case KADM5_DUP: + case KADM5_POLICY_REF: + return KADM_INUSE; + case KADM5_RPC_ERROR: + return KADM_NO_CONN; + case KADM5_NO_SRV: + return KADM_NO_HOST; + case KADM5_UNK_PRINC: + case KADM5_UNK_POLICY: + return KADM_NOENTRY; + case KADM5_PASS_Q_TOOSHORT: + case KADM5_PASS_Q_CLASS: + case KADM5_PASS_Q_DICT: + case KADM5_PASS_REUSE: + case KADM5_PASS_TOOSOON: + case CHPASS_UTIL_PASSWORD_TOO_SOON: + return KADM_INSECURE_PW; + case KADM5_BAD_PASSWORD: + return KADM_NO_CRED; + case KADM5_PROTECT_PRINCIPAL: + return KADM_NO_OPCODE; + case KADM5_NOT_INIT: + case KADM5_BAD_HIST_KEY: + case KADM5_BAD_MASK: + case KADM5_BAD_CLASS: + case KADM5_BAD_LENGTH: + case KADM5_BAD_POLICY: + case KADM5_BAD_PRINCIPAL: + case KADM5_BAD_AUX_ATTR: + case KADM5_BAD_HISTORY: + case KADM5_BAD_MIN_PASS_LIFE: + return -1; + } + return val; +} +#endif diff --git a/src/kadmin/v4server/attic/ChangeLog b/src/kadmin/v4server/attic/ChangeLog new file mode 100644 index 000000000..6eefc24c7 --- /dev/null +++ b/src/kadmin/v4server/attic/ChangeLog @@ -0,0 +1,25 @@ +Thu Jul 18 19:47:58 1996 Marc Horowitz <marc@mit.edu> + + * configure.in: removed ET_RULES, replaced with AC_PROG_AWK + +Thu Aug 4 16:37:33 1994 Tom Yu (tlyu@dragons-lair) + + * admin_server.c: pick up <sys/time.h> (needed to get FD_SET, + etc.) + +Sat Jul 16 09:21:22 1994 Tom Yu (tlyu at dragons-lair) + + * Makefile.in: no longer trying to install v4kadmind as krb5kdc + :-) + * configure.in: another try at making dbm libs dtrt + +Wed Jun 29 00:24:28 1994 Tom Yu (tlyu at dragons-lair) + + * admin_server.c: fixed calls that should have invoked + krb5_init_ets + +Sat Jun 25 09:07:48 1994 Tom Yu (tlyu at dragons-lair) + + * kadm_ser_wrap.c: fixed lack of a terminal 0 in a call to + krb5_build_principal + diff --git a/src/kadmin/v4server/attic/Imakefile b/src/kadmin/v4server/attic/Imakefile new file mode 100644 index 000000000..e1449ef32 --- /dev/null +++ b/src/kadmin/v4server/attic/Imakefile @@ -0,0 +1,49 @@ +# $Source$ +# $Author$ +# $Header$ +# +# Copyright 1989 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, +# please see the file <mit-copyright.h>. +# +# Imakefile for Kerberos admin server library. + +DEFINES = $(KRB4DEF) +INCLUDES = $(KRB4INCLUDES) -I. +SRCS = \ + kadm_server.c \ + kadm_funcs.c \ + admin_server.c \ + kadm_ser_wrap.c \ + kadm_stream.c \ + kadm_supp.c \ + kadm_err.c \ + acl_files.c +OBJS = \ + kadm_server.o \ + kadm_funcs.o \ + admin_server.o \ + kadm_ser_wrap.o \ + kadm_stream.o \ + kadm_supp.o \ + kadm_err.o \ + acl_files.o + +ErrorTableObjectRule() + +all:: v4kadmind + +depend:: kadm_err.c + +kadm_err.c: kadm_err.et + +NormalProgramTarget(v4kadmind,$(OBJS),$(KDBDEPLIB) $(DEPKLIB), \ + $(KDBLIB) $(KRB4LIB) $(KLIB) ,) + +Krb5InstallServerProgram(v4kadmind) + +clean:: + $(RM) kadm_err.c kadm_err.h + +DependTarget() diff --git a/src/kadmin/v4server/attic/Makefile b/src/kadmin/v4server/attic/Makefile new file mode 100644 index 000000000..d0acac021 --- /dev/null +++ b/src/kadmin/v4server/attic/Makefile @@ -0,0 +1,39 @@ +TOP = ../.. +include $(TOP)/config.mk/template + +ifdef KRB5B4 +CFLAGS += -DKRB5B4 $(D_POSIX_SIGNALS) +endif + +PROG := ovsec_v4adm_server + +SRCS := kadm_server.c admin_server.c kadm_ser_wrap.c \ + kadm_stream.c kadm_supp.c acl_files.c + +OBJS := kadm_server.o admin_server.o kadm_ser_wrap.o \ + kadm_stream.o kadm_supp.o acl_files.o + +LIBS := $(LIBADMCLNT) $(LIBRPCLIB) $(LIBGSSAPI_KRB5) $(LIBKRB5) \ + $(LIBKADM) $(LIBKRB) $(LIBDES425) $(LIBKDB5) \ + $(LIBCRYPTO) $(LIBISODE) \ + $(LIBDYN) $(LIBDB) $(LIBCOM_ERR) $(NDBMLIB) $(NETLIB) $(BSDLIB) + +ifdef WAIT_USES_INT +WAIT_FLAGS = -DWAIT_USES_INT +endif +ifdef OPEN_NEEDS_FCNTL +FCNTL_FLAGS = -DNEED_SYS_FCNTL_H +endif + +# XXX the -D's should probably be moved somewhere; in krb5.4.2 they +# are in osconf.h +CFLAGS := -DOVSEC_KADM \ + -DKADM_SYSLOG="\"/krb5/admin_server.syslog\"" \ + -DDEFAULT_ACL_DIR="\"/krb5\"" $(WAIT_FLAGS) $(FCNTL_FLAGS) \ + $(CFLAGS) + +expand InstallServer +expand Depend + +SUBDIRS = unit-test +expand SubdirTarget diff --git a/src/kadmin/v4server/attic/Makefile.in b/src/kadmin/v4server/attic/Makefile.in new file mode 100644 index 000000000..f5206aa66 --- /dev/null +++ b/src/kadmin/v4server/attic/Makefile.in @@ -0,0 +1,53 @@ +CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE) +LDFLAGS = -g + +ISODELIB=@ISODELIB@ +COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a +DBMLIB= +KDBLIB=$(TOPLIBD)/libkdb5.a + +KRB4LIB = $(KRB4)/lib/libkrb.a $(TOPLIBD)/libdes425.a + +KLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(ISODELIB) $(COMERRLIB) $(DBMLIB) + +LOCALINCLUDE=-I$(SRCTOP)/include/kerberosIV -I$(BUILDTOP)/include/kerberosIV -I. + +SRCS = \ + $(srcdir)/kadm_server.c \ + $(srcdir)/kadm_funcs.c \ + $(srcdir)/admin_server.c \ + $(srcdir)/kadm_ser_wrap.c \ + $(srcdir)/kadm_stream.c \ + $(srcdir)/kadm_supp.c \ + $(srcdir)/kadm_err.c \ + $(srcdir)/acl_files.c +OBJS = \ + kadm_server.o \ + kadm_funcs.o \ + admin_server.o \ + kadm_ser_wrap.o \ + kadm_stream.o \ + kadm_supp.o \ + kadm_err.o \ + acl_files.o + +all:: kadm_err.h v4kadmind + +depend:: kadm_err.c + +kadm_err.c: kadm_err.et + +kadm_err.h: kadm_err.et + +v4kadmind: $(OBJS) $(KDBDEPLIB) $(DEPKLIB) + $(CC) $(CFLAGS) -o v4kadmind $(OBJS) $(KDBLIB) $(KLIB) $(KRB4LIB) $(LIBS) $(KRB4)/lib/libdes.a + +install:: + $(INSTALL_PROGRAM) v4kadmind ${DESTDIR}$(SERVER_BINDIR)/v4kadmind + +clean:: + $(RM) kadm_err.h kadm_err.c + +clean:: + $(RM) v4kadmind + diff --git a/src/kadmin/v4server/attic/acl_files.c b/src/kadmin/v4server/attic/acl_files.c new file mode 100644 index 000000000..81275ae26 --- /dev/null +++ b/src/kadmin/v4server/attic/acl_files.c @@ -0,0 +1,541 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1987,1989 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + */ + +#ifndef lint +static char rcsid_acl_files_c[] = "$Id$"; +#endif lint + + +/*** Routines for manipulating access control list files ***/ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <sys/errno.h> +#include <ctype.h> +#ifdef NEED_SYS_FCNTL_H +#include <sys/fcntl.h> +#endif +#include "krb.h" +#include <krb5/krb5.h> + +#ifndef KRB_REALM +#define KRB_REALM "ATHENA.MIT.EDU" +#endif + +/* "aname.inst@realm" */ +#define MAX_PRINCIPAL_SIZE (ANAME_SZ + INST_SZ + REALM_SZ + 3) +#define INST_SEP '.' +#define REALM_SEP '@' + +#define LINESIZE 2048 /* Maximum line length in an acl file */ + +#define NEW_FILE "%s.~NEWACL~" /* Format for name of altered acl file */ +#define WAIT_TIME 300 /* Maximum time allowed write acl file */ + +#define CACHED_ACLS 8 /* How many acls to cache */ + /* Each acl costs 1 open file descriptor */ +#define ACL_LEN 16 /* Twice a reasonable acl length */ + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#define COR(a,b) ((a!=NULL)?(a):(b)) + +extern int errno; + +extern char *malloc(), *calloc(); +extern time_t time(); + +static int acl_abort PROTOTYPE((char *acl_file, FILE *f)); + +/* Canonicalize a principal name */ +/* If instance is missing, it becomes "" */ +/* If realm is missing, it becomes the local realm */ +/* Canonicalized form is put in canon, which must be big enough to hold + MAX_PRINCIPAL_SIZE characters */ +acl_canonicalize_principal(principal, canon) +char *principal; +char *canon; +{ + char *dot, *atsign, *end; + int len; + + dot = strchr(principal, INST_SEP); + atsign = strchr(principal, REALM_SEP); + + /* Maybe we're done already */ + if(dot != NULL && atsign != NULL) { + if(dot < atsign) { + /* It's for real */ + /* Copy into canon */ + strncpy(canon, principal, MAX_PRINCIPAL_SIZE); + canon[MAX_PRINCIPAL_SIZE-1] = '\0'; + return; + } else { + /* Nope, it's part of the realm */ + dot = NULL; + } + } + + /* No such luck */ + end = principal + strlen(principal); + + /* Get the principal name */ + len = MIN(ANAME_SZ, COR(dot, COR(atsign, end)) - principal); + strncpy(canon, principal, len); + canon += len; + + /* Add INST_SEP */ + *canon++ = INST_SEP; + + /* Get the instance, if it exists */ + if(dot != NULL) { + ++dot; + len = MIN(INST_SZ, COR(atsign, end) - dot); + strncpy(canon, dot, len); + canon += len; + } + + /* Add REALM_SEP */ + *canon++ = REALM_SEP; + + /* Get the realm, if it exists */ + /* Otherwise, default to local realm */ + if(atsign != NULL) { + ++atsign; + len = MIN(REALM_SZ, end - atsign); + strncpy(canon, atsign, len); + canon += len; + *canon++ = '\0'; + } else if(krb_get_lrealm(canon, 1) != KSUCCESS) { + strcpy(canon, KRB_REALM); + } +} + +/* Get a lock to modify acl_file */ +/* Return new FILE pointer */ +/* or NULL if file cannot be modified */ +/* REQUIRES WRITE PERMISSION TO CONTAINING DIRECTORY */ +static FILE *acl_lock_file(acl_file) +char *acl_file; +{ + struct stat s; + char new[LINESIZE]; + int nfd; + FILE *nf; + int mode; + + if(stat(acl_file, &s) < 0) return(NULL); + mode = s.st_mode; + sprintf(new, NEW_FILE, acl_file); + for(;;) { + /* Open the new file */ + if((nfd = open(new, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0) { + if(errno == EEXIST) { + /* Maybe somebody got here already, maybe it's just old */ + if(stat(new, &s) < 0) return(NULL); + if(time(0) - s.st_ctime > WAIT_TIME) { + /* File is stale, kill it */ + unlink(new); + continue; + } else { + /* Wait and try again */ + sleep(1); + continue; + } + } else { + /* Some other error, we lose */ + return(NULL); + } + } + + /* If we got to here, the lock file is ours and ok */ + /* Reopen it under stdio */ + if((nf = fdopen(nfd, "w")) == NULL) { + /* Oops, clean up */ + unlink(new); + } + return(nf); + } +} + +/* Commit changes to acl_file written onto FILE *f */ +/* Returns zero if successful */ +/* Returns > 0 if lock was broken */ +/* Returns < 0 if some other error occurs */ +/* Closes f */ +static int acl_commit(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + sprintf(new, NEW_FILE, acl_file); + if(fflush(f) < 0 + || fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + acl_abort(acl_file, f); + return(-1); + } + + ret = rename(new, acl_file); + fclose(f); + return(ret); +} + +/* Abort changes to acl_file written onto FILE *f */ +/* Returns 0 if successful, < 0 otherwise */ +/* Closes f */ +static int acl_abort(acl_file, f) +char *acl_file; +FILE *f; +{ + char new[LINESIZE]; + int ret; + struct stat s; + + /* make sure we aren't nuking someone else's file */ + if(fstat(fileno(f), &s) < 0 + || s.st_nlink == 0) { + fclose(f); + return(-1); + } else { + sprintf(new, NEW_FILE, acl_file); + ret = unlink(new); + fclose(f); + return(ret); + } +} + +/* Initialize an acl_file */ +/* Creates the file with permissions perm if it does not exist */ +/* Erases it if it does */ +/* Returns return value of acl_commit */ +int acl_initialize(acl_file, perm) +char *acl_file; +int perm; +{ + FILE *new; + int fd; + + /* Check if the file exists already */ + if((new = acl_lock_file(acl_file)) != NULL) { + return(acl_commit(acl_file, new)); + } else { + /* File must be readable and writable by owner */ + if((fd = open(acl_file, O_CREAT|O_EXCL, perm|0600)) < 0) { + return(-1); + } else { + close(fd); + return(0); + } + } +} + +/* Eliminate all whitespace character in buf */ +/* Modifies its argument */ +static nuke_whitespace(buf) +char *buf; +{ + register char *pin, *pout; + + for(pin = pout = buf; *pin != '\0'; pin++) + if(!isspace(*pin)) *pout++ = *pin; + *pout = '\0'; /* Terminate the string */ +} + +/* Hash table stuff */ + +struct hashtbl { + int size; /* Max number of entries */ + int entries; /* Actual number of entries */ + char **tbl; /* Pointer to start of table */ +}; + +/* Make an empty hash table of size s */ +static struct hashtbl *make_hash(size) +int size; +{ + struct hashtbl *h; + + if(size < 1) size = 1; + h = (struct hashtbl *) malloc(sizeof(struct hashtbl)); + h->size = size; + h->entries = 0; + h->tbl = (char **) calloc(size, sizeof(char *)); + return(h); +} + +/* Destroy a hash table */ +static destroy_hash(h) +struct hashtbl *h; +{ + int i; + + for(i = 0; i < h->size; i++) { + if(h->tbl[i] != NULL) free(h->tbl[i]); + } + free(h->tbl); + free(h); +} + +/* Compute hash value for a string */ +static unsigned hashval(s) +register char *s; +{ + register unsigned hv; + + for(hv = 0; *s != '\0'; s++) { + hv ^= ((hv << 3) ^ *s); + } + return(hv); +} + +/* Add an element to a hash table */ +static add_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + char *s; + char **old; + int i; + + /* Make space if it isn't there already */ + if(h->entries + 1 > (h->size >> 1)) { + old = h->tbl; + h->tbl = (char **) calloc(h->size << 1, sizeof(char *)); + for(i = 0; i < h->size; i++) { + if(old[i] != NULL) { + hv = hashval(old[i]) % (h->size << 1); + while(h->tbl[hv] != NULL) hv = (hv+1) % (h->size << 1); + h->tbl[hv] = old[i]; + } + } + h->size = h->size << 1; + free(old); + } + + hv = hashval(el) % h->size; + while(h->tbl[hv] != NULL && strcmp(h->tbl[hv], el)) hv = (hv+1) % h->size; + s = malloc(strlen(el)+1); + strcpy(s, el); + h->tbl[hv] = s; + h->entries++; +} + +/* Returns nonzero if el is in h */ +static check_hash(h, el) +struct hashtbl *h; +char *el; +{ + unsigned hv; + + for(hv = hashval(el) % h->size; + h->tbl[hv] != NULL; + hv = (hv + 1) % h->size) { + if(!strcmp(h->tbl[hv], el)) return(1); + } + return(0); +} + +struct acl { + char filename[LINESIZE]; /* Name of acl file */ + int fd; /* File descriptor for acl file */ + struct stat status; /* File status at last read */ + struct hashtbl *acl; /* Acl entries */ +}; + +static struct acl acl_cache[CACHED_ACLS]; + +static int acl_cache_count = 0; +static int acl_cache_next = 0; + +/* Returns < 0 if unsuccessful in loading acl */ +/* Returns index into acl_cache otherwise */ +/* Note that if acl is already loaded, this is just a lookup */ +static int acl_load(name) +char *name; +{ + int i; + FILE *f; + struct stat s; + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + + /* See if it's there already */ + for(i = 0; i < acl_cache_count; i++) { + if(!strcmp(acl_cache[i].filename, name) + && acl_cache[i].fd >= 0) goto got_it; + } + + /* It isn't, load it in */ + /* maybe there's still room */ + if(acl_cache_count < CACHED_ACLS) { + i = acl_cache_count++; + } else { + /* No room, clean one out */ + i = acl_cache_next; + acl_cache_next = (acl_cache_next + 1) % CACHED_ACLS; + close(acl_cache[i].fd); + if(acl_cache[i].acl) { + destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = (struct hashtbl *) 0; + } + } + + /* Set up the acl */ + strcpy(acl_cache[i].filename, name); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + /* Force reload */ + acl_cache[i].acl = (struct hashtbl *) 0; + + got_it: + /* + * See if the stat matches + * + * Use stat(), not fstat(), as the file may have been re-created by + * acl_add or acl_delete. If this happens, the old inode will have + * no changes in the mod-time and the following test will fail. + */ + if(stat(acl_cache[i].filename, &s) < 0) return(-1); + if(acl_cache[i].acl == (struct hashtbl *) 0 + || s.st_nlink != acl_cache[i].status.st_nlink + || s.st_mtime != acl_cache[i].status.st_mtime + || s.st_ctime != acl_cache[i].status.st_ctime) { + /* Gotta reload */ + if(acl_cache[i].fd >= 0) close(acl_cache[i].fd); + if((acl_cache[i].fd = open(name, O_RDONLY, 0)) < 0) return(-1); + if((f = fdopen(acl_cache[i].fd, "r")) == NULL) return(-1); + if(acl_cache[i].acl) destroy_hash(acl_cache[i].acl); + acl_cache[i].acl = make_hash(ACL_LEN); + while(fgets(buf, sizeof(buf), f) != NULL) { + nuke_whitespace(buf); + acl_canonicalize_principal(buf, canon); + add_hash(acl_cache[i].acl, canon); + } + fclose(f); + acl_cache[i].status = s; + } + return(i); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Principal is not canonicalized, and no wildcarding is done */ +acl_exact_match(acl, principal) +char *acl; +char *principal; +{ + int idx; + + return((idx = acl_load(acl)) >= 0 + && check_hash(acl_cache[idx].acl, principal)); +} + +/* Returns nonzero if it can be determined that acl contains principal */ +/* Recognizes wildcards in acl of the form + name.*@realm, *.*@realm, and *.*@* */ +acl_check(acl, principal) +char *acl; +char *principal; +{ + char buf[MAX_PRINCIPAL_SIZE]; + char canon[MAX_PRINCIPAL_SIZE]; + char *realm, *tmp; + + acl_canonicalize_principal(principal, canon); + + /* Is it there? */ + if(acl_exact_match(acl, canon)) return(1); + + /* Try the wildcards */ + realm = strchr(canon, REALM_SEP); + tmp = strchr(canon, INST_SEP); + *tmp = '\0'; /* Chuck the instance */ + + sprintf(buf, "%s.*%s", canon, realm); + if(acl_exact_match(acl, buf)) return(1); + + sprintf(buf, "*.*%s", realm); + if(acl_exact_match(acl, buf) || acl_exact_match(acl, "*.*@*")) return(1); + + return(0); +} + +/* Adds principal to acl */ +/* Wildcards are interpreted literally */ +acl_add(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL) { + if((fputs(acl_cache[idx].acl->tbl[i], new) == EOF) + || (putc('\n', new) != '\n')) { + acl_abort(acl, new); + return(-1); + } + } + } + fputs(canon, new); + putc('\n', new); + return(acl_commit(acl, new)); +} + +/* Removes principal from acl */ +/* Wildcards are interpreted literally */ +acl_delete(acl, principal) +char *acl; +char *principal; +{ + int idx; + int i; + FILE *new; + char canon[MAX_PRINCIPAL_SIZE]; + + acl_canonicalize_principal(principal, canon); + + if((new = acl_lock_file(acl)) == NULL) return(-1); + if((!acl_exact_match(acl, canon)) + || (idx = acl_load(acl)) < 0) { + acl_abort(acl, new); + return(-1); + } + /* It isn't there yet, copy the file and put it in */ + for(i = 0; i < acl_cache[idx].acl->size; i++) { + if(acl_cache[idx].acl->tbl[i] != NULL + && strcmp(acl_cache[idx].acl->tbl[i], canon)) { + fputs(acl_cache[idx].acl->tbl[i], new); + putc('\n', new); + } + } + return(acl_commit(acl, new)); +} + diff --git a/src/kadmin/v4server/attic/acl_files.doc b/src/kadmin/v4server/attic/acl_files.doc new file mode 100644 index 000000000..78c448a6d --- /dev/null +++ b/src/kadmin/v4server/attic/acl_files.doc @@ -0,0 +1,107 @@ +PROTOTYPE ACL LIBRARY + +Introduction + +An access control list (ACL) is a list of principals, where each +principal is is represented by a text string which cannot contain +whitespace. The library allows application programs to refer to named +access control lists to test membership and to atomically add and +delete principals using a natural and intuitive interface. At +present, the names of access control lists are required to be Unix +filenames, and refer to human-readable Unix files; in the future, when +a networked ACL server is implemented, the names may refer to a +different namespace specific to the ACL service. + + +Usage + +cc <files> -lacl -lkrb. + + + +Principal Names + +Principal names have the form + +<name>[.<instance>][@<realm>] + +e.g. + +asp +asp.root +asp@ATHENA.MIT.EDU +asp.@ATHENA.MIT.EDU +asp.root@ATHENA.MIT.EDU + +It is possible for principals to be underspecified. If instance is +missing, it is assumed to be "". If realm is missing, it is assumed +to be local_realm. The canonical form contains all of name, instance, +and realm; the acl_add and acl_delete routines will always +leave the file in that form. Note that the canonical form of +asp@ATHENA.MIT.EDU is actually asp.@ATHENA.MIT.EDU. + + +Routines + +acl_canonicalize_principal(principal, buf) +char *principal; +char *buf; /*RETVAL*/ + +Store the canonical form of principal in buf. Buf must contain enough +space to store a principal, given the limits on the sizes of name, +instance, and realm specified in /usr/include/krb.h. + +acl_check(acl, principal) +char *acl; +char *principal; + +Returns nonzero if principal appears in acl. Returns 0 if principal +does not appear in acl, or if an error occurs. Canonicalizes +principal before checking, and allows the ACL to contain wildcards. + +acl_exact_match(acl, principal) +char *acl; +char *principal; + +Like acl_check, but does no canonicalization or wildcarding. + +acl_add(acl, principal) +char *acl; +char *principal; + +Atomically adds principal to acl. Returns 0 if successful, nonzero +otherwise. It is considered a failure if principal is already in acl. +This routine will canonicalize principal, but will treat wildcards +literally. + +acl_delete(acl, principal) +char *acl; +char *principal; + +Atomically deletes principal from acl. Returns 0 if successful, +nonzero otherwise. It is consider a failure if principal is not +already in acl. This routine will canonicalize principal, but will +treat wildcards literally. + +acl_initialize(acl, mode) +char *acl; +int mode; + +Initialize acl. If acl file does not exist, creates it with mode +mode. If acl exists, removes all members. Returns 0 if successful, +nonzero otherwise. WARNING: Mode argument is likely to change with +the eventual introduction of an ACL service. + + +Known problems + +In the presence of concurrency, there is a very small chance that +acl_add or acl_delete could report success even though it would have +had no effect. This is a necessary side effect of using lock files +for concurrency control rather than flock(2), which is not supported +by NFS. + +The current implementation caches ACLs in memory in a hash-table +format for increased efficiency in checking membership; one effect of +the caching scheme is that one file descriptor will be kept open for +each ACL cached, up to a maximum of 8. diff --git a/src/kadmin/v4server/attic/aclocal.m4 b/src/kadmin/v4server/attic/aclocal.m4 new file mode 100644 index 000000000..70bf66a7d --- /dev/null +++ b/src/kadmin/v4server/attic/aclocal.m4 @@ -0,0 +1,3 @@ +sinclude([./../../aclocal.m4])dnl +undefine([AC_BUILDTOP])dnl +define(AC_BUILDTOP,[./../..])dnl diff --git a/src/kadmin/v4server/attic/admin_server.c b/src/kadmin/v4server/attic/admin_server.c new file mode 100644 index 000000000..04155bca1 --- /dev/null +++ b/src/kadmin/v4server/attic/admin_server.c @@ -0,0 +1,668 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Top-level loop of the kerberos Administration server + */ + +#include <mit-copyright.h> + +/* + admin_server.c + this holds the main loop and initialization and cleanup code for the server +*/ + +#ifdef _AIX +#include <sys/select.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <signal.h> +#include <string.h> + +#ifndef POSIX_SIGNALS +#ifndef sigmask +#define sigmask(m) (1 <<((m)-1)) +#endif +#endif /* POSIX_SIGNALS */ +#ifdef _AIX +#include <sys/resource.h> +#endif /* _AIX */ +#include <sys/wait.h> +#include <errno.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <syslog.h> + +#include <krb5/krb5.h> +#include <krb5/kdb.h> +#include <krb5/kdb_dbm.h> +#include <krb5/los-proto.h> +#include <krb5/config.h> + +#ifdef OVSEC_KADM +#include <ovsec_admin/admin.h> +void *ovsec_handle; +#endif + +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include "kadm_server.h" + +/* Almost all procs and such need this, so it is global */ +admin_params prm; /* The command line parameters struct */ + +char prog[32]; /* WHY IS THIS NEEDED??????? */ +char *progname = prog; +char *acldir = DEFAULT_ACL_DIR; +char krbrlm[REALM_SZ]; +extern Kadm_Server server_parm; +int des_debug; /* used by the des425 libraries */ +int debug = 0; + +/* +** Main does the logical thing, it sets up the database and RPC interface, +** as well as handling the creation and maintenance of the syslog file... +*/ +main(argc, argv) /* admin_server main routine */ +int argc; +char *argv[]; +{ + int errval; + int c; + char *db_name, *lrealm; + extern char *optarg; + extern int fascist_cpw; + + krb5_init_ets(); + initialize_kadm_error_table(); + prog[sizeof(prog)-1]='\0'; /* Terminate... */ + (void) strncpy(prog, argv[0], sizeof(prog)-1); + + /* initialize the admin_params structure */ + prm.sysfile = KADM_SYSLOG; /* default file name */ + prm.inter = 1; + + memset(krbrlm, 0, sizeof(krbrlm)); + + fascist_cpw = 1; /* by default, enable fascist mode */ + while ((c = getopt(argc, argv, "f:hnd:Da:r:FN")) != EOF) + switch(c) { + case 'd': + if (errval = krb5_db_set_name(optarg)) { + com_err(argv[0], errval, "while setting dbname"); + exit(1); + } + break; + case 'D': + debug++; + break; +#ifndef OVSEC_KADM + case 'f': /* Syslog file name change */ + prm.sysfile = optarg; + break; + case 'F': + fascist_cpw++; + break; + case 'N': + fascist_cpw = 0; + break; +#endif + case 'n': + prm.inter = 0; + break; + case 'a': /* new acl directory */ + acldir = optarg; + break; + case 'r': + (void) strncpy(krbrlm, optarg, sizeof(krbrlm) - 1); + break; + case 'h': /* get help on using admin_server */ + default: +#ifdef OVSEC_KADM + fprintf(stderr, "Usage: ovsec_v4adm_server [-D] [-h] [-n] [-r realm] [-d dbname] [-a acldir]\n"); + +#else + printf("Usage: admin_server [-D] [-h] [-n] [-F] [-N] [-r realm] [-d dbname] [-f filename] [-a acldir]\n"); +#endif + exit(-1); /* failure */ + } + + if (krbrlm[0] == 0) { + if (errval = krb5_get_default_realm(&lrealm)) { + com_err(argv[0], errval, "while attempting to get local realm"); + exit(1); + } + (void) strncpy(krbrlm, lrealm, sizeof(krbrlm) - 1); + } + printf("KADM Server %s initializing\n",KADM_VERSTR); + printf("Please do not use 'kill -9' to kill this job, use a\n"); + printf("regular kill instead\n\n"); + +#ifdef OVSEC_KADM + printf("KADM Server starting in the OVSEC_KADM mode (%sprocess id %d).\n", + debug ? "" : "parent ", getpid()); +#else + printf("KADM Server starting in %s mode for the purposes for password changing\n\n", fascist_cpw ? "fascist" : "NON-FASCIST"); +#endif + + open_syslog(argv[0], "V4 admin server (parent) starting"); + + errval = krb5_db_init(); /* Open the Kerberos database */ + if (errval) { + fprintf(stderr, "error: krb5_db_init() failed"); + close_syslog(); + byebye(); + exit(1); + } + if (errval = krb5_db_set_lockmode(TRUE)) { + com_err(argv[0], errval, "while setting db to nonblocking"); + close_syslog(); + byebye(); + exit(1); + } + + /* set up the server_parm struct */ + if ((errval = kadm_ser_init(prm.inter, krbrlm)) != KADM_SUCCESS) { + fprintf(stderr, "error initializing: %s\n", error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + + /* detach from the terminal */ + if (!debug) { + if ( +#ifdef KRB5B4 + daemon(0, 0) +#else + errval = krb5_detach_process() +#endif + ) { +#ifdef KRB5B4 + errval = errno; +#endif + fprintf(stderr, "error detaching from terminal: %s\n", + error_message(errval)); + syslog(LOG_ERR, "error detaching from terminal: %s", + error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + open_syslog(argv[0], "V4 admin server (child) starting"); + } + + krb5_db_fini(); + + if (errval = kadm_listen()) { + fprintf(stderr, "error while listening for requests: %s\n", + error_message(errval)); + syslog(LOG_ERR, "error while listening for requests: %s", + error_message(errval)); + krb5_db_fini(); + close_syslog(); + byebye(); + exit(1); + } + + close_syslog(); + byebye(); + exit(0); +} /* procedure main */ + + +/* open the system log file */ +open_syslog(whoami, message) + char *whoami, *message; +{ + static int opened = 0; + + if (opened) { + closelog(); + } + openlog(whoami, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6); /* XXX */ + syslog(LOG_INFO, message); + opened++; +} + +/* close the system log file */ +close_syslog() +{ + syslog(LOG_INFO, "Shutting down V4 admin server"); +} + +byebye() /* say goodnight gracie */ +{ + printf("Admin Server (kadm server) has completed operation.\n"); +} + +static clear_secrets() +{ + krb5_finish_key(&server_parm.master_encblock); + memset((char *)&server_parm.master_encblock, 0, + sizeof (server_parm.master_encblock)); + memset((char *)server_parm.master_keyblock.contents, 0, + server_parm.master_keyblock.length); + server_parm.mkvno = 0L; + return; +} + +static exit_now = 0; + +krb5_sigtype doexit() +{ + exit_now = 1; +} + +unsigned pidarraysize = 0; +int *pidarray = (int *)0; +int unknown_child = 0; + +/* +kadm_listen +listen on the admin servers port for a request +*/ +kadm_listen() +{ + extern int errno; + int found; + int admin_fd; + int peer_fd; + fd_set mask, readfds; + struct sockaddr_in peer; + int addrlen; + void process_client(), kill_children(); + int pid; + krb5_sigtype do_child(); + + (void) signal(SIGINT, doexit); + (void) signal(SIGTERM, doexit); + (void) signal(SIGHUP, doexit); + (void) signal(SIGQUIT, doexit); + (void) signal(SIGPIPE, SIG_IGN); /* get errors on write() */ + (void) signal(SIGALRM, doexit); + (void) signal(SIGCHLD, do_child); + + if ((admin_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + return KADM_NO_SOCK; + if (bind(admin_fd, (struct sockaddr *)&server_parm.admin_addr, + sizeof(struct sockaddr_in)) < 0) + return KADM_NO_BIND; + (void) listen(admin_fd, 1); + FD_ZERO(&mask); + FD_SET(admin_fd, &mask); + + for (;;) { /* loop nearly forever */ + if (exit_now) { + clear_secrets(); + kill_children(); + return(0); + } + readfds = mask; + if ((found = select(admin_fd+1,&readfds,(fd_set *)0, + (fd_set *)0, (struct timeval *)0)) == 0) + continue; /* no things read */ + if (found < 0) { + if (errno != EINTR) + syslog(LOG_ERR, "select: %s", error_message(errno)); + continue; + } + if (FD_ISSET(admin_fd, &readfds)) { + /* accept the conn */ + addrlen = sizeof(peer); + if ((peer_fd = accept(admin_fd, (struct sockaddr *)&peer, + &addrlen)) < 0) { + syslog(LOG_ERR, "accept: %s", error_message(errno)); + continue; + } + + if (debug) { + process_client(peer_fd, &peer); + } else if (pid = fork()) { + /* parent */ + if (pid < 0) { + syslog(LOG_ERR, "fork: %s", error_message(errno)); + (void) close(peer_fd); + continue; + } + /* fork succeeded: keep tabs on child */ + (void) close(peer_fd); + if (unknown_child != pid) { + if (pidarray) { + pidarray = (int *)realloc((char *)pidarray, + (++pidarraysize * sizeof(int))); + pidarray[pidarraysize-1] = pid; + } else { + pidarray = (int *)malloc((pidarraysize = 1) * + sizeof(int)); + pidarray[0] = pid; + } + } /* End if unknown_child != pid.*/ + } else { + /* child */ + (void) close(admin_fd); + process_client(peer_fd, &peer); + } + } else { + syslog(LOG_ERR, "something else woke me up!"); + return(0); + } + } + /*NOTREACHED*/ +} + +void process_client(fd, who) + int fd; + struct sockaddr_in *who; +{ + u_char *dat; + int dat_len; + u_short dlen; + int retval; + int on = 1; + Principal service; + des_cblock skey; + int nentries = 1; + krb5_db_entry sprinc_entries; + krb5_boolean more; + krb5_keyblock cpw_skey; + int status; + +#ifdef OVSEC_KADM +#define OVSEC_KADM_SRVTAB "FILE:/krb5/ovsec_adm.srvtab" + char *service_name; + + service_name = (char *) malloc(strlen(server_parm.sname) + + strlen(server_parm.sinst) + + strlen(server_parm.krbrlm) + 3); + if (service_name == NULL) { + syslog(LOG_ERR, "error: out of memory allocating service name"); + } + sprintf(service_name, "%s/%s@%s", server_parm.sname, + server_parm.sinst, server_parm.krbrlm); + + retval = ovsec_kadm_init_with_skey(service_name, + OVSEC_KADM_SRVTAB, + OVSEC_KADM_ADMIN_SERVICE, krbrlm, + OVSEC_KADM_STRUCT_VERSION, + OVSEC_KADM_API_VERSION_1, + &ovsec_handle); + if (retval) { + syslog(LOG_ERR, "error: ovsec_kadm_init failed: %s", + error_message(retval)); + cleanexit(1); + } + free(service_name); + +#endif + +#if !defined(NOENCRYPTION) + /* Must do it here, since this is after the fork() call. */ + des_init_random_number_generator(server_parm.master_keyblock.contents); +#endif + + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt keepalive: %d", errno); + + server_parm.recv_addr = *who; + + if (krb5_db_init()) { /* Open as client */ + syslog(LOG_ERR, "can't open krb db"); + cleanexit(1); + } + + /* need to set service key to changepw.KRB_MASTER */ + + status = krb5_db_get_principal(server_parm.sprinc, + &sprinc_entries, + &nentries, &more); + /* ugh... clean this up later */ + if (status == KRB5_KDB_DB_INUSE) { + /* db locked */ + krb5_ui_4 retcode = KADM_DB_INUSE; + char *pdat; + + dat_len = KADM_VERSIZE + sizeof(u_int); + dat = (u_char *) malloc((unsigned)dat_len); + pdat = (char *) dat; + /* This must be 32 bit integer due to the htonl */ + retcode = htonl((krb5_ui_4) KADM_DB_INUSE); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + goto out; + } else if (!nentries) { + syslog(LOG_ERR, "no service %s.%s", server_parm.sname, server_parm.sinst); + cleanexit(2); + } else if (status) { + syslog(LOG_ERR, error_message(status)); + cleanexit(2); + } + + status = krb5_kdb_decrypt_key(&server_parm.master_encblock, + &sprinc_entries.key, + &cpw_skey); + if (status) { + syslog(LOG_ERR, "decrypt_key failed: %s", error_message(status)); + cleanexit(1); + } + /* if error, will show up when rd_req fails */ + (void) krb_set_key((char *)cpw_skey.contents, 0); +#ifdef KRB5_FREE_KEYBLOCK_CONTENTS_EXISTS + krb5_free_keyblock_contents(&cpw_skey); +#else + memset((char*)cpw_skey.contents, 0, cpw_skey.length); + free(cpw_skey.contents); +#endif + + krb5_dbm_db_free_principal(&sprinc_entries, nentries); + + while (1) { + if ((retval = krb_net_read(fd, (char *)&dlen, sizeof(u_short))) != + sizeof(u_short)) { + if (retval < 0) + syslog(LOG_ERR, "dlen read: %s", error_message(errno)); + else if (retval) + syslog(LOG_ERR, "short dlen read: %d", retval); + (void) close(fd); +#ifdef OVSEC_KADM + (void) ovsec_kadm_destroy(ovsec_handle); +#endif + if (debug) + return; + else + cleanexit(retval ? 3 : 0); + } + if (exit_now) { + cleanexit(0); + } + dat_len = (int) ntohs(dlen); + dat = (u_char *) malloc((unsigned)dat_len); + if (!dat) { + syslog(LOG_ERR, "malloc: No memory"); + (void) close(fd); + cleanexit(4); + } + if ((retval = krb_net_read(fd, (char *)dat, dat_len)) != dat_len) { + if (retval < 0) + syslog(LOG_ERR, "data read: %s", error_message(errno)); + else + syslog(LOG_ERR, "short read: %d vs. %d", dat_len, retval); + (void) close(fd); + cleanexit(5); + } + if (exit_now) { + cleanexit(0); + } + if ((retval = kadm_ser_in(&dat,&dat_len)) != KADM_SUCCESS) + syslog(LOG_ERR, "processing request: %s", error_message(retval)); + + /* kadm_ser_in did the processing and returned stuff in + dat & dat_len , return the appropriate data */ + + out: + dlen = (u_short) dat_len; + + if (dat_len != (int)dlen) { + clear_secrets(); + abort(); /* XXX */ + } + dlen = htons(dlen); + + if (krb_net_write(fd, (char *)&dlen, sizeof(u_short)) < 0) { + syslog(LOG_ERR, "writing dlen to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(6); + } + + if (krb_net_write(fd, (char *)dat, dat_len) < 0) { + syslog(LOG_ERR, "writing to client: %s", error_message(errno)); + (void) close(fd); + cleanexit(7); + } + free((char *)dat); + } + /*NOTREACHED*/ +} + +krb5_sigtype do_child() +{ + /* SIGCHLD brings us here */ + int pid; + register int i, j; + +#ifdef WAIT_USES_INT + int status; +#else + union wait status; +#endif + + pid = wait(&status); + + for (i = 0; i < pidarraysize; i++) + if (pidarray[i] == pid) { + /* found it */ + for (j = i; j < pidarraysize-1; j++) + /* copy others down */ + pidarray[j] = pidarray[j+1]; + pidarraysize--; +#ifdef WAIT_USES_INT + if (WIFEXITED(status) || WIFSIGNALED(status)) + syslog(LOG_ERR, "child %d: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); + +#else + if (status.w_retcode || status.w_coredump || status.w_termsig) + syslog(LOG_ERR, "child %d: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); + +#endif + goto done; /* use goto to avoid figuring out whether to + return a value */ + } + unknown_child = pid; +#ifdef WAIT_USES_INT + syslog(LOG_ERR, "child %d not in list: termsig %d, retcode %d", pid, + WTERMSIG(status), WEXITSTATUS(status)); +#else + syslog(LOG_ERR, "child %d not in list: termsig %d, coredump %d, retcode %d", + pid, status.w_termsig, status.w_coredump, status.w_retcode); +#endif + + done: +} + +cleanexit(val) +{ + krb5_db_fini(); + clear_secrets(); + exit(val); +} + +void kill_children() +{ + register int i; +#ifdef POSIX_SIGNALS + sigset_t oldmask, igmask; +#else + int osigmask; +#endif + +#ifdef POSIX_SIGNALS + sigemptyset(&igmask); + sigaddset(&igmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &igmask, &oldmask); +#else + osigmask = sigblock(sigmask(SIGCHLD)); +#endif + + for (i = 0; i < pidarraysize; i++) { + kill(pidarray[i], SIGINT); + syslog(LOG_ERR, "killing child %d", pidarray[i]); + } +#ifdef POSIX_SIGNALS + sigprocmask(SIG_SETMASK, &oldmask, (sigset_t*)0); +#else + sigsetmask(osigmask); +#endif + return; +} + +#ifdef OVSEC_KADM +krb5_ui_4 convert_ovsec_to_kadm(val) + krb5_ui_4 val; +{ + switch (val) { + case OVSEC_KADM_AUTH_GET: + case OVSEC_KADM_AUTH_ADD: + case OVSEC_KADM_AUTH_MODIFY: + case OVSEC_KADM_AUTH_DELETE: + case OVSEC_KADM_AUTH_INSUFFICIENT: + return KADM_UNAUTH; + case OVSEC_KADM_BAD_DB: + return KADM_UK_RERROR; + case OVSEC_KADM_DUP: + case OVSEC_KADM_POLICY_REF: + return KADM_INUSE; + case OVSEC_KADM_RPC_ERROR: + return KADM_NO_CONN; + case OVSEC_KADM_NO_SRV: + return KADM_NO_HOST; + case OVSEC_KADM_UNK_PRINC: + case OVSEC_KADM_UNK_POLICY: + return KADM_NOENTRY; + case OVSEC_KADM_PASS_Q_TOOSHORT: + case OVSEC_KADM_PASS_Q_CLASS: + case OVSEC_KADM_PASS_Q_DICT: + case OVSEC_KADM_PASS_REUSE: + case OVSEC_KADM_PASS_TOOSOON: + case CHPASS_UTIL_PASSWORD_TOO_SOON: + return KADM_INSECURE_PW; + case OVSEC_KADM_BAD_PASSWORD: + return KADM_NO_CRED; + case OVSEC_KADM_PROTECT_PRINCIPAL: + return KADM_NO_OPCODE; + case OVSEC_KADM_NOT_INIT: + case OVSEC_KADM_BAD_HIST_KEY: + case OVSEC_KADM_BAD_MASK: + case OVSEC_KADM_BAD_CLASS: + case OVSEC_KADM_BAD_LENGTH: + case OVSEC_KADM_BAD_POLICY: + case OVSEC_KADM_BAD_PRINCIPAL: + case OVSEC_KADM_BAD_AUX_ATTR: + case OVSEC_KADM_BAD_HISTORY: + case OVSEC_KADM_BAD_MIN_PASS_LIFE: + return -1; + } + return val; +} +#endif diff --git a/src/kadmin/v4server/attic/configure.in b/src/kadmin/v4server/attic/configure.in new file mode 100644 index 000000000..f09ba3a28 --- /dev/null +++ b/src/kadmin/v4server/attic/configure.in @@ -0,0 +1,22 @@ +AC_INIT(admin_server.c) +WITH_CCOPTS +CONFIG_RULES +AC_SET_BUILDTOP +AC_PROG_INSTALL +AC_HAVE_LIBRARY(socket) +AC_HAVE_LIBRARY(nsl) +AC_HAVE_LIBRARY(-lndbm) +AC_HAVE_LIBRARY(-ldbm) +CHECK_WAIT_TYPE +CHECK_FCNTL +AC_FUNC_CHECK(sigprocmask, +AC_COMPILE_CHECK([sigset_t], +[#include <signal.h>], +[sigset_t x], +AC_DEFINE(POSIX_SIGNALS))) +AC_PROG_AWK +KRB_INCLUDE +ISODE_INCLUDE +WITH_KRB4 +WITH_KRB5ROOT +AC_OUTPUT(Makefile,[EXTRA_RULES]) diff --git a/src/kadmin/v4server/attic/kadm_err.et b/src/kadmin/v4server/attic/kadm_err.et new file mode 100644 index 000000000..a19273083 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_err.et @@ -0,0 +1,57 @@ +# kadmin.v4/server/kadm_err.et +# +# Copyright 1988 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, please see the file +# <mit-copyright.h>. +# +# Kerberos administration server error table +# + et kadm + +# KADM_SUCCESS, as all success codes should be, is zero + +ec KADM_RCSID, "$Header$" +# /* Building and unbuilding the packet errors */ +ec KADM_NO_REALM, "Cannot fetch local realm" +ec KADM_NO_CRED, "Unable to fetch credentials" +ec KADM_BAD_KEY, "Bad key supplied" +ec KADM_NO_ENCRYPT, "Can't encrypt data" +ec KADM_NO_AUTH, "Cannot encode/decode authentication info" +ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm" +ec KADM_NO_ROOM, "Packet is too large" +ec KADM_BAD_VER, "Version number is incorrect" +ec KADM_BAD_CHK, "Checksum does not match" +ec KADM_NO_READ, "Unsealing private data failed" +ec KADM_NO_OPCODE, "Unsupported operation" +ec KADM_NO_HOST, "Could not find administrating host" +ec KADM_UNK_HOST, "Administrating host name is unknown" +ec KADM_NO_SERV, "Could not find service name in services database" +ec KADM_NO_SOCK, "Could not create socket" +ec KADM_NO_CONN, "Could not connect to server" +ec KADM_NO_HERE, "Could not fetch local socket address" +ec KADM_NO_MAST, "Could not fetch master key" +ec KADM_NO_VERI, "Could not verify master key" + +# /* From the server side routines */ +ec KADM_INUSE, "Entry already exists in database" +ec KADM_UK_SERROR, "Database store error" +ec KADM_UK_RERROR, "Database read error" +ec KADM_UNAUTH, "Insufficient access to perform requested operation" +# KADM_DATA isn't really an error, but... +ec KADM_DATA, "Data is available for return to client" +ec KADM_NOENTRY, "No such entry in the database" + +ec KADM_NOMEM, "Memory exhausted" +ec KADM_NO_HOSTNAME, "Could not fetch system hostname" +ec KADM_NO_BIND, "Could not bind port" +ec KADM_LENGTH_ERROR, "Length mismatch problem" +ec KADM_ILL_WILDCARD, "Illegal use of wildcard" + +ec KADM_DB_INUSE, "Database locked or in use" + +ec KADM_INSECURE_PW, "Insecure password rejected" +ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match" + +ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request" +end diff --git a/src/kadmin/v4server/attic/kadm_funcs.c b/src/kadmin/v4server/attic/kadm_funcs.c new file mode 100644 index 000000000..7ce9c7b4f --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_funcs.c @@ -0,0 +1,876 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side database manipulation routines + */ + +#ifndef lint +static char rcsid_kadm_funcs_c[] = +"$Id$"; +#endif lint + +#include <mit-copyright.h> +/* +kadm_funcs.c +the actual database manipulation code +*/ + +#include <stdio.h> +#include <sys/param.h> +#include <ndbm.h> +#include <ctype.h> +#include <pwd.h> +#include <sys/file.h> +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include <syslog.h> +#ifdef NEED_SYS_FCNTL_H +#include <sys/fcntl.h> +#endif + +#include "kadm_server.h" + +extern Kadm_Server server_parm; + +krb5_error_code +kadm_entry2princ(entry, princ) + krb5_db_entry entry; + Principal *princ; +{ + char realm[REALM_SZ]; /* dummy values only */ + krb5_error_code retval; + time_t lcltim; + + /* NOTE: does not convert the key */ + memset(princ, 0, sizeof (*princ)); + retval = krb5_524_conv_principal(entry.principal, + princ->name, princ->instance, realm); + if (retval) + return retval; + princ->exp_date = entry.expiration; + lcltim = entry.expiration; + strncpy(princ->exp_date_txt, ctime(&lcltim), + DATE_SZ); + lcltim = princ->mod_date = entry.mod_date; + strncpy(princ->mod_date_txt, ctime(&lcltim), + DATE_SZ); + princ->attributes = entry.attributes; + princ->max_life = entry.max_life; + princ->kdc_key_ver = entry.mkvno; + princ->key_version = entry.kvno; + retval = krb5_524_conv_principal(entry.mod_name, + princ->mod_name, princ->mod_instance, + realm); + if (retval) + return retval; + return 0; +} + +krb5_error_code +kadm_princ2entry(princ, entry) + Principal princ; + krb5_db_entry *entry; +{ + krb5_error_code retval; + + /* NOTE: does not convert the key */ + memset(entry, 0, sizeof (*entry)); + /* yeah yeah stupid v4 database doesn't store realm names */ + retval = krb5_425_conv_principal(princ.name, princ.instance, + server_parm.krbrlm, &entry->principal); + if (retval) + return retval; + entry->kvno = princ.key_version; + entry->max_life = princ.max_life; + entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */ + entry->mkvno = server_parm.mkvno; /* XXX */ + entry->expiration = princ.exp_date; + retval = krb5_425_conv_principal(princ.mod_name, princ.mod_instance, + server_parm.krbrlm, &entry->mod_name); + if (retval) + return retval; + entry->mod_date = princ.mod_date; + entry->attributes = princ.attributes; + entry->salt_type = KRB5_KDB_SALTTYPE_V4; +} + +check_access(pname, pinst, prealm, acltype) +char *pname; +char *pinst; +char *prealm; +enum acl_types acltype; +{ + char checkname[MAX_K_NAME_SZ]; + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm); + + switch (acltype) { + case ADDACL: + (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE); + break; + case GETACL: + (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE); + break; + case MODACL: + (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE); + break; + case STABACL: + (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE); + break; + } + return(acl_check(filename, checkname)); +} + +int +wildcard(str) +char *str; +{ + if (!strcmp(str, WILDCARD_STR)) + return(1); + return(0); +} + +#define failadd(code) { (void) syslog(LOG_ERR, "FAILED addding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_add_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_principal default_princ; + krb5_error_code retval; + krb5_db_entry newentry, tmpentry; + krb5_boolean more; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + int numfound; + + if (!check_access(rname, rinstance, rrealm, ADDACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failadd(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + kadm_vals_to_prin(valsin->fields, &data_i, valsin); + (void) strncpy(data_i.name, valsin->name, ANAME_SZ); + (void) strncpy(data_i.instance, valsin->instance, INST_SZ); + + if (!IS_FIELD(KADM_EXPDATE,valsin->fields)) + data_i.exp_date = server_parm.expiration; + if (!IS_FIELD(KADM_ATTR,valsin->fields)) + data_i.attributes = server_parm.flags; + if (!IS_FIELD(KADM_MAXLIFE,valsin->fields)) + data_i.max_life = server_parm.max_life; + + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) + failadd(KADM_NOMEM); + data_i.key_low = ntohl(data_i.key_low); + data_i.key_high = ntohl(data_i.key_high); + memcpy(newpw.contents, &data_i.key_low, 4); + memcpy((char *)(((krb4_int32 *) newpw.contents) + 1), &data_i.key_high, 4); + newpw.length = 8; + newpw.keytype = KEYTYPE_DES; + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset((char *)newpw.contents, 0, newpw.length); + free(newpw.contents); + if (retval) { + failadd(retval); + } + data_o = data_i; + + retval = kadm_princ2entry(data_i, &newentry); + if (retval) { + memset((char *)encpw.contents, 0, encpw.length); + free(encpw.contents); + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + + newentry.key = encpw; + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, + &tmpentry, &numfound, &more); + + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + krb5_db_free_principal(&tmpentry, numfound); + if (numfound) { + krb5_db_free_principal(&newentry, 1); + failadd(KADM_INUSE); + } else { + newentry.kvno = ++data_i.key_version; + if (retval = krb5_timeofday(&newentry.mod_date)) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + if (newentry.mod_name) + krb5_free_principal(newentry.mod_name); + newentry.mod_name = NULL; /* in case the following breaks */ + retval = krb5_425_conv_principal(rname, rinstance, rrealm, + &newentry.mod_name); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(&newentry, &numfound); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failadd(retval); + } + if (!numfound) { + krb5_db_free_principal(&newentry, 1); + failadd(KADM_UK_SERROR); + } else { + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, + &tmpentry, + &numfound, &more); + krb5_db_free_principal(&newentry, 1); + if (retval) { + failadd(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(&tmpentry, numfound); + failadd(KADM_UK_RERROR); + } + kadm_entry2princ(tmpentry, &data_o); + krb5_db_free_principal(&tmpentry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } +} +#undef failadd + +#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; /* what they wannt to get */ +u_char *flags; /* which fields we want */ +Kadm_vals *valsout; /* what data is there */ +{ + int numfound; /* check how many were returned */ + krb5_boolean more; /* To point to more name.instances */ + Principal data_o; /* Data object to hold Principal */ + krb5_principal inprinc; + krb5_db_entry entry; + krb5_error_code retval; + + if (!check_access(rname, rinstance, rrealm, GETACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failget(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(valsin->name, valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failget(retval); + /* Look up the record in the database */ + numfound = 1; + retval = krb5_db_get_principal(inprinc, &entry, &numfound, &more); + krb5_free_principal(inprinc); + if (retval) { + failget(retval); + } else if (!numfound || more) { + failget(KADM_NOENTRY); + } + retval = kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(&entry, 1); + if (retval) { + failget(retval); + } + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ +} +#undef failget + +#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; } + +kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin1, *valsin2; /* holds the parameters being + passed in */ +Kadm_vals *valsout; /* the actual record which is returned */ +{ + int numfound; + krb5_boolean more; + Principal data_o, temp_key; + u_char fields[4]; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + krb5_error_code retval; + krb5_principal theprinc, rprinc; + krb5_db_entry newentry, odata; + + if (wildcard(valsin1->name) || wildcard(valsin1->instance)) { + failmod(KADM_ILL_WILDCARD); + } + + if (!check_access(rname, rinstance, rrealm, MODACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry", + rname, rinstance, rrealm, valsin1->name, valsin1->instance); + return KADM_UNAUTH; + } + + syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ", + valsin1->name, valsin1->instance, rname, rinstance, rrealm); + krb5_425_conv_principal(valsin1->name, valsin1->instance, + server_parm.krbrlm, &theprinc); + if (retval) + failmod(retval); + numfound = 1; + retval = krb5_db_get_principal(theprinc, &newentry, &numfound, &more); + if (retval) { + krb5_free_principal(theprinc); + failmod(retval); + } else if (numfound == 1) { + kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2); + krb5_free_principal(newentry.principal); + newentry.principal = theprinc; + if (IS_FIELD(KADM_EXPDATE,valsin2->fields)) + newentry.expiration = temp_key.exp_date; + if (IS_FIELD(KADM_ATTR,valsin2->fields)) + newentry.attributes = temp_key.attributes; + if (IS_FIELD(KADM_MAXLIFE,valsin2->fields)) + newentry.max_life = temp_key.max_life; + if (IS_FIELD(KADM_DESKEY,valsin2->fields)) { + newentry.kvno++; + newentry.mkvno = server_parm.mkvno; + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(&newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(KADM_NOMEM); + } + newpw.length = 8; + newpw.keytype = KEYTYPE_DES; + temp_key.key_low = ntohl(temp_key.key_low); + temp_key.key_high = ntohl(temp_key.key_high); + memcpy(newpw.contents, &temp_key.key_low, 4); + memcpy(newpw.contents + 4, &temp_key.key_high, 4); + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset(newpw.contents, 0, newpw.length); + free(newpw.contents); + memset((char *)&temp_key, 0, sizeof(temp_key)); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + if (newentry.key.contents) { + memset((char *)newentry.key.contents, 0, newentry.key.length); + free(newentry.key.contents); + } + newentry.key = encpw; + } + if (retval = krb5_timeofday(&newentry.mod_date)) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + retval = krb5_425_conv_principal(rname, rinstance, rrealm, + &newentry.mod_name); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&newentry, &numfound); + memset((char *)&data_o, 0, sizeof(data_o)); + if (retval) { + krb5_db_free_principal(&newentry, 1); + failmod(retval); + } else { + numfound = 1; + retval = krb5_db_get_principal(newentry.principal, &odata, + &numfound, &more); + krb5_db_free_principal(&newentry, 1); + if (retval) { + failmod(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(&odata, numfound); + failmod(KADM_UK_RERROR); + } + retval = kadm_entry2princ(odata, &data_o); + krb5_db_free_principal(&odata, 1); + if (retval) + failmod(retval); + memset((char *) fields, 0, sizeof(fields)); + SET_FIELD(KADM_NAME,fields); + SET_FIELD(KADM_INST,fields); + SET_FIELD(KADM_EXPDATE,fields); + SET_FIELD(KADM_ATTR,fields); + SET_FIELD(KADM_MAXLIFE,fields); + kadm_prin_to_vals(fields, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } else { + failmod(KADM_NOENTRY); + } +} +#undef failmod + +#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_change (rname, rinstance, rrealm, newpw) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +{ + int numfound; + krb5_boolean more; + krb5_principal rprinc; + krb5_error_code retval; + krb5_keyblock localpw; + krb5_encrypted_keyblock encpw; + krb5_db_entry odata; + + if (strcmp(server_parm.krbrlm, rrealm)) { + syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n", + rname, rinstance, rrealm); + return(KADM_WRONG_REALM); + } + + if (wildcard(rname) || wildcard(rinstance)) { + failchange(KADM_ILL_WILDCARD); + } + syslog(LOG_INFO, "'%s.%s@%s' wants to change its password", + rname, rinstance, rrealm); + retval = krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &rprinc); + if (retval) + failchange(retval); + if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL) + failchange(KADM_NOMEM); + memcpy(localpw.contents, newpw, 8); + localpw.keytype = KEYTYPE_DES; + localpw.length = 8; + /* encrypt new key in master key */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &localpw, &encpw); + memset((char *)localpw.contents, 0, 8); + free(localpw.contents); + if (retval) { + krb5_free_principal(rprinc); + failchange(retval); + } + numfound = 1; + retval = krb5_db_get_principal(rprinc, &odata, &numfound, &more); + krb5_free_principal(rprinc); + if (retval) { + failchange(retval); + } else if (numfound == 1) { + odata.key = encpw; + odata.kvno++; + odata.mkvno = server_parm.mkvno; + if (retval = krb5_timeofday(&odata.mod_date)) { + krb5_db_free_principal(&odata, 1); + failchange(retval); + } + krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &odata.mod_name); + if (retval) { + krb5_db_free_principal(&odata, 1); + failchange(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&odata, &numfound); + krb5_db_free_principal(&odata, 1); + if (retval) { + failchange(retval); + } else if (more) { + failchange(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, + "'%s.%s@%s' password changed.", rname, rinstance, rrealm); + return KADM_SUCCESS; + } + } + else { + failchange(KADM_NOENTRY); + } +} +#undef failchange + +check_pw(newpw, checkstr) + des_cblock newpw; + char *checkstr; +{ +#ifdef NOENCRYPTION + return 0; +#else /* !NOENCRYPTION */ + des_cblock checkdes; + + (void) des_string_to_key(checkstr, checkdes); + return(!memcmp(checkdes, newpw, sizeof(des_cblock))); +#endif /* NOENCRYPTION */ +} + +char *reverse(str) + char *str; +{ + static char newstr[80]; + char *p, *q; + int i; + + i = strlen(str); + if (i >= sizeof(newstr)) + i = sizeof(newstr)-1; + p = str+i-1; + q = newstr; + q[i]='\0'; + for(; i > 0; i--) + *q++ = *p--; + + return(newstr); +} + +int lower(str) + char *str; +{ + register char *cp; + int effect=0; + + for (cp = str; *cp; cp++) { + if (isupper(*cp)) { + *cp = tolower(*cp); + effect++; + } + } + return(effect); +} + +des_check_gecos(gecos, newpw) + char *gecos; + des_cblock newpw; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + if (lower(cp)) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + } + cp = ncp; + } else + break; + } + return(0); +} + +str_check_gecos(gecos, pwstr) + char *gecos; + char *pwstr; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (!strcasecmp(pwstr, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (!strcasecmp(pwstr, tcp)) + return(KADM_INSECURE_PW); + cp = ncp; + } else + break; + } + return(0); +} + + +kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +char *pwstring; +{ + static DBM *pwfile = NULL; + int retval; + datum passwd, entry; + struct passwd *ent; +#ifdef HESIOD + extern struct passwd *hes_getpwnam(); +#endif + + if (pwstring && !check_pw(newpw, pwstring)) + /* + * Someone's trying to toy with us.... + */ + return(KADM_PW_MISMATCH); + if (pwstring && (strlen(pwstring) < 5)) + return(KADM_INSECURE_PW); + if (!pwfile) { + pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644); + } + if (pwfile) { + passwd.dptr = (char *) newpw; + passwd.dsize = 8; + entry = dbm_fetch(pwfile, passwd); + if (entry.dptr) + return(KADM_INSECURE_PW); + } + if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname))) + return(KADM_INSECURE_PW); +#ifdef HESIOD + ent = hes_getpwnam(rname); +#else + ent = getpwnam(rname); +#endif + if (ent && ent->pw_gecos) { + if (pwstring) + retval = str_check_gecos(ent->pw_gecos, pwstring); + else + retval = des_check_gecos(ent->pw_gecos, newpw); + if (retval) + return(retval); + } + return(0); +} + +/* + * This routine checks to see if a principal should be considered an + * allowable service name which can be changed by kadm_change_srvtab. + * + * We do this check by using the ACL library. This makes the + * (relatively) reasonable assumption that both the name and the + * instance will not contain '.' or '@'. + */ +kadm_check_srvtab(name, instance) + char *name; + char *instance; +{ + FILE *f; + char filename[MAXPATHLEN]; + char buf[ANAME_SZ], *cp; + extern char *acldir; + + (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE); + if (!acl_check(filename, name)) + return(KADM_NOT_SERV_PRINC); + + (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE); + if (acl_check(filename, instance)) + return(KADM_NOT_SERV_PRINC); + return 0; +} + +/* + * Routine to allow some people to change the key of a srvtab + * principal to a random key, which the admin server will return to + * the client. + */ +#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_chg_srvtab(rname, rinstance, rrealm, values) + char *rname; /* requestors name */ + char *rinstance; /* requestors instance */ + char *rrealm; /* requestors realm */ + Kadm_vals *values; +{ + int numfound, ret, isnew = 0; + des_cblock new_key; + Principal principal; + krb5_principal inprinc; + krb5_error_code retval; + krb5_db_entry odata; + krb5_boolean more; + krb5_keyblock newpw; + krb5_encrypted_keyblock encpw; + + if (!check_access(rname, rinstance, rrealm, STABACL)) + failsrvtab(KADM_UNAUTH); + if (wildcard(rname) || wildcard(rinstance)) + failsrvtab(KADM_ILL_WILDCARD); + if (ret = kadm_check_srvtab(values->name, values->instance)) + failsrvtab(ret); + + retval = krb5_425_conv_principal(values->name, values->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failsrvtab(retval); + /* + * OK, get the entry + */ + numfound = 1; + retval = krb5_db_get_principal(inprinc, &odata, &numfound, &more); + if (retval) { + krb5_free_principal(inprinc); + failsrvtab(retval); + } else if (numfound) { + odata.kvno++; + } else { + /* + * This is a new srvtab entry that we're creating + */ + isnew = 1; + memset((char *)&odata, 0, sizeof (odata)); + odata.principal = inprinc; + odata.kvno = 1; + odata.max_life = server_parm.max_life; + odata.max_renewable_life = server_parm.max_rlife; + odata.mkvno = server_parm.mkvno; + odata.expiration = server_parm.expiration; + odata.attributes = 0; + } + +#ifdef NOENCRYPTION + memset(new_key, 0, sizeof(new_key)); + new_key[0] = 127; +#else + des_new_random_key(new_key); +#endif + /* + * Store the new key in the return structure; also fill in the + * rest of the fields. + */ + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(&odata, 1); + failsrvtab(KADM_NOMEM); + } + newpw.keytype = KEYTYPE_DES; + newpw.length = 8; + memcpy((char *)newpw.contents, new_key, 8); + memset((char *)new_key, 0, sizeof (new_key)); + memcpy((char *)&values->key_low, newpw.contents, 4); + memcpy((char *)&values->key_high, newpw.contents + 4, 4); + values->key_low = htonl(values->key_low); + values->key_high = htonl(values->key_high); + values->max_life = odata.kvno; + values->exp_date = odata.expiration; + values->attributes = odata.attributes; + memset(values->fields, 0, sizeof(values->fields)); + SET_FIELD(KADM_NAME, values->fields); + SET_FIELD(KADM_INST, values->fields); + SET_FIELD(KADM_EXPDATE, values->fields); + SET_FIELD(KADM_ATTR, values->fields); + SET_FIELD(KADM_MAXLIFE, values->fields); + SET_FIELD(KADM_DESKEY, values->fields); + + /* + * Encrypt the new key with the master key, and then update + * the database record + */ + retval = krb5_kdb_encrypt_key(&server_parm.master_encblock, + &newpw, &encpw); + memset((char *)newpw.contents, 0, 8); + free(newpw.contents); + if (odata.key.contents) { + memset((char *)odata.key.contents, 0, odata.key.length); + free(odata.key.contents); + } + odata.key = encpw; + if (retval) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + if (retval = krb5_timeofday(&odata.mod_date)) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + retval = krb5_425_conv_principal(rname, rinstance, + server_parm.krbrlm, &odata.mod_name); + if (retval) { + krb5_db_free_principal(&odata, 1); + failsrvtab(retval); + } + numfound = 1; + retval = krb5_db_put_principal(&odata, &numfound); + krb5_db_free_principal(&odata, 1); + if (retval) { + failsrvtab(retval); + } + else if (!numfound) { + failsrvtab(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.", + values->name, values->instance, + numfound ? "changed" : "created", + rname, rinstance, rrealm); + return KADM_DATA; + } +} + +#undef failsrvtab diff --git a/src/kadmin/v4server/attic/kadm_ser_wrap.c b/src/kadmin/v4server/attic/kadm_ser_wrap.c new file mode 100644 index 000000000..bca8b8e05 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_ser_wrap.c @@ -0,0 +1,288 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side support functions + */ + +#ifndef lint +static char rcsid_module_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> +/* +kadm_ser_wrap.c +unwraps wrapped packets and calls the appropriate server subroutine +*/ + +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <netdb.h> +#include <sys/socket.h> +#include <krb.h> +#include <kadm.h> +#include <kadm_err.h> +#include <krb_err.h> +#include <syslog.h> +#include "kadm_server.h" + +#ifdef OVSEC_KADM +#include <ovsec_admin/admin.h> +extern void *ovsec_handle; +#endif + +Kadm_Server server_parm; + +/* +kadm_ser_init +set up the server_parm structure +*/ +kadm_ser_init(inter, realm) + int inter; /* interactive or from file */ + char realm[]; +{ + struct servent *sep; + struct hostent *hp; + char hostname[MAXHOSTNAMELEN]; + char *mkey_name; + krb5_error_code retval; + int numfound = 1; + krb5_boolean more; + krb5_db_entry master_entry; + + if (gethostname(hostname, sizeof(hostname))) + return KADM_NO_HOSTNAME; + + (void) strcpy(server_parm.sname, PWSERV_NAME); + (void) strcpy(server_parm.sinst, KRB_MASTER); + (void) strcpy(server_parm.krbrlm, realm); + if (krb5_build_principal(&server_parm.sprinc, + strlen(realm), + realm, + PWSERV_NAME, + KRB_MASTER, 0)) + return KADM_NO_MAST; + + /* setting up the addrs */ + server_parm.admin_fd = -1; + if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL) + return KADM_NO_SERV; + memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr)); + server_parm.admin_addr.sin_family = AF_INET; + if ((hp = gethostbyname(hostname)) == NULL) + return KADM_NO_HOSTNAME; + memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr, + hp->h_length); + server_parm.admin_addr.sin_port = sep->s_port; + + /* setting up the database */ + mkey_name = KRB5_KDB_M_NAME; + server_parm.master_keyblock.keytype = KEYTYPE_DES; +#ifdef PROVIDE_DES_CBC_CRC +#ifdef KRB5B4 + server_parm.master_encblock.crypto_entry = krb5_des_cst_entry.system; +#else + server_parm.master_encblock.crypto_entry = &mit_des_cryptosystem_entry; +#endif /* KRB5B4 */ +#else + error(You gotta figure out what cryptosystem to use in the KDC); +#endif + retval = krb5_db_setup_mkey_name(mkey_name, realm, (char **) 0, + &server_parm.master_princ); + if (retval) + return KADM_NO_MAST; + krb5_db_fetch_mkey(server_parm.master_princ, + &server_parm.master_encblock, + (inter == 1), FALSE, NULL, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_MAST; + retval = krb5_db_verify_master_key(server_parm.master_princ, + &server_parm.master_keyblock, + &server_parm.master_encblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_process_key(&server_parm.master_encblock, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_VERI; + + retval = krb5_db_get_principal(server_parm.master_princ, + &master_entry, &numfound, &more); + if (retval || more || !numfound) + return KADM_NO_VERI; + server_parm.max_life = master_entry.max_life; + server_parm.max_rlife = master_entry.max_renewable_life; + server_parm.expiration = master_entry.expiration; + server_parm.mkvno = master_entry.kvno; + /* don't set flags, as master has some extra restrictions + (??? quoted from kdb_edit.c) */ + krb5_db_free_principal(&master_entry, numfound); + return KADM_SUCCESS; +} + + +static void errpkt(dat, dat_len, code) +u_char **dat; +int *dat_len; +int code; +{ + krb4_uint32 retcode; + char *pdat; + + free((char *)*dat); /* free up req */ + *dat_len = KADM_VERSIZE + sizeof(krb4_uint32); + *dat = (u_char *) malloc((unsigned)*dat_len); + if (!(*dat)) { + syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len); + abort(); + } + pdat = (char *) *dat; + retcode = htonl((krb4_uint32) code); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb4_uint32)); + return; +} + +/* +kadm_ser_in +unwrap the data stored in dat, process, and return it. +*/ +kadm_ser_in(dat,dat_len) +u_char **dat; +int *dat_len; +{ + u_char *in_st; /* pointer into the sent packet */ + int in_len,retc; /* where in packet we are, for + returns */ + krb4_uint32 r_len; /* length of the actual packet */ + KTEXT_ST authent; /* the authenticator */ + AUTH_DAT ad; /* who is this, klink */ + krb4_uint32 ncksum; /* checksum of encrypted data */ + des_key_schedule sess_sched; /* our schedule */ + MSG_DAT msg_st; + u_char *retdat, *tmpdat; + int retval, retlen; + + if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) { + errpkt(dat, dat_len, KADM_BAD_VER); + return KADM_BAD_VER; + } + in_len = KADM_VERSIZE; + /* get the length */ + if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0) + return KADM_LENGTH_ERROR; + in_len += retc; + authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb4_uint32); + memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length); + authent.mbz = 0; + /* service key should be set before here */ + if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst, + server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0)) + { + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + +#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));} + + in_st = *dat + *dat_len - r_len; +#ifdef NOENCRYPTION + ncksum = 0; +#else + ncksum = quad_cksum((des_cblock *)in_st, (des_cblock *)0, (krb4_int32) r_len, 0, + (des_cblock *)ad.session); +#endif + if (ncksum!=ad.checksum) { /* yow, are we correct yet */ + clr_cli_secrets(); + errpkt(dat, dat_len,KADM_BAD_CHK); + return KADM_BAD_CHK; + } +#ifdef NOENCRYPTION + memset(sess_sched, 0, sizeof(sess_sched)); +#else + des_key_sched(ad.session, sess_sched); +#endif + if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session, + &server_parm.recv_addr, + &server_parm.admin_addr, &msg_st)) { + clr_cli_secrets(); + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + switch (msg_st.app_data[0]) { + case CHANGE_PW: + retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#ifndef OVSEC_KADM + case ADD_ENT: + retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case GET_ENT: + retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case MOD_ENT: + retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHECK_PW: + retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHG_STAB: + retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#endif /* OVSEC_KADM */ + default: + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_OPCODE); + return KADM_NO_OPCODE; + } + /* Now seal the response back into a priv msg */ + free((char *)*dat); + tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE + + sizeof(krb4_uint32))); + if (!tmpdat) { + clr_cli_secrets(); + syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!", + retlen + KADM_VERSIZE + sizeof(krb4_uint32)); + errpkt(dat, dat_len, KADM_NOMEM); + return KADM_NOMEM; + } + (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE); + retval = htonl((krb4_uint32)retval); + memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb4_uint32)); + if (retlen) { + memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb4_uint32), (char *)retdat, + retlen); + free((char *)retdat); + } + /* slop for mk_priv stuff */ + *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE + + sizeof(krb4_uint32) + 200)); + if ((*dat_len = krb_mk_priv(tmpdat, *dat, + (krb4_uint32) (retlen + KADM_VERSIZE + + sizeof(krb4_uint32)), + sess_sched, + ad.session, &server_parm.admin_addr, + &server_parm.recv_addr)) < 0) { + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_ENCRYPT); + free(tmpdat); + return KADM_NO_ENCRYPT; + } + clr_cli_secrets(); + free(tmpdat); + return KADM_SUCCESS; +} diff --git a/src/kadmin/v4server/attic/kadm_server.c b/src/kadmin/v4server/attic/kadm_server.c new file mode 100644 index 000000000..143af5d4e --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_server.c @@ -0,0 +1,546 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side subroutines + */ + +#ifndef lint +static char rcsid_kadm_server_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> + +#include <krb5/osconf.h> +#include <krb5/wordsize.h> + +#include <stdio.h> +#ifdef USE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#include <string.h> + +#ifdef OVSEC_KADM +#include <com_err.h> +#include <ovsec_admin/admin.h> +#include <ovsec_admin/chpass_util_strings.h> +#include <krb5/kdb.h> +extern void *ovsec_handle; +#endif + +#include <kadm.h> +#include <kadm_err.h> + +int fascist_cpw = 0; /* Be fascist about insecure passwords? */ + +#ifdef OVSEC_KADM +char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n"; + +#else /* !OVSEC_KADM */ + +char bad_pw_err[] = + "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n"; + +char bad_pw_warn[] = + "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n"; + +char check_pw_msg[] = + "You have entered an insecure password. You should choose another.\n\n"; + +char pw_blurb[] = +"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n"; + +#endif /* OVSEC_KADM */ + +/* from V4 month_sname.c -- was not part of API */ +/* + * Given an integer 1-12, month_sname() returns a string + * containing the first three letters of the corresponding + * month. Returns 0 if the argument is out of range. + */ + +static char *month_sname(n) + int n; +{ + static char *name[] = { + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" + }; + return((n < 1 || n > 12) ? 0 : name [n-1]); +} + +/* from V4 log.c -- was not part of API */ + +/* + * krb_log() is used to add entries to the logfile (see krb_set_logfile() + * below). Note that it is probably not portable since it makes + * assumptions about what the compiler will do when it is called + * with less than the correct number of arguments which is the + * way it is usually called. + * + * The log entry consists of a timestamp and the given arguments + * printed according to the given "format". + * + * The log file is opened and closed for each log entry. + * + * The return value is undefined. + */ + +/* static char *log_name = KRBLOG; */ +/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */ +static char *log_name = KADM_SYSLOG; + +static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0) + char *format; + int a1,a2,a3,a4,a5,a6,a7,a8,a9,a0; +{ + FILE *logfile, *fopen(); + time_t now; + struct tm *tm; + + if ((logfile = fopen(log_name,"a")) == NULL) + return; + + (void) time(&now); + tm = localtime(&now); + + fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday, + month_sname(tm->tm_mon + 1),tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0); + fprintf(logfile,"\n"); + (void) fclose(logfile); + return; +} + + +/* +kadm_ser_cpw - the server side of the change_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : caller can change only own password + +Replaces the password (i.e. des key) of the caller with that specified in key. +Returns no actual data from the master server, since this is called by a user +*/ +kadm_ser_cpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int status, stvlen = 0; + int retval; + extern char *malloc(); + extern int kadm_approve_pw(); +#ifdef OVSEC_KADM + ovsec_kadm_principal_ent_t princ_ent; + ovsec_kadm_policy_ent_t pol_ent; + krb5_principal user_princ; + char msg_ret[1024], *time_string, *ptr; + const char *msg_ptr; + krb5_int32 now; + time_t until; +#endif + + /* take key off the stream, and change the database */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((((krb5_int32 *)newkey) + 1), &keyhigh, 4); + memcpy(newkey, &keylow, 4); + +#ifdef OVSEC_KADM + /* we don't use the client-provided key itself */ + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); + + if (no_pword) { + krb_log("Old-style change password request from '%s.%s@%s'!", + ad->pname, ad->pinst, ad->prealm); + *outlen = strlen(pw_required)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, pw_required); + } else { + *outlen = 0; + } + return KADM_INSECURE_PW; + } + + if (krb5_build_principal(&user_princ, + strlen(ad->prealm), + ad->prealm, + ad->pname, + *ad->pinst ? ad->pinst : 0, 0)) + /* this should never happen */ + return KADM_NOENTRY; + + *outlen = 0; + + if (retval = krb5_timeofday(&now)) { + msg_ptr = error_message(retval); + goto send_response; + } + + retval = ovsec_kadm_get_principal(ovsec_handle, user_princ, + &princ_ent); + if (retval != 0) { + msg_ptr = error_message(retval); + goto send_response; + } + + /* + * This daemon necessarily has the modify privilege, so + * ovsec_kadm_chpass_principal will allow it to violate the + * policy's minimum lifetime. Since that's A Bad Thing, we need + * to enforce it ourselves. Unfortunately, this means we are + * duplicating code from both ovsec_adm_server and + * ovsec_kadm_chpass_util(). + */ + if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) { + retval = ovsec_kadm_get_policy(ovsec_handle, + princ_ent->policy, + &pol_ent); + if (retval != 0) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + msg_ptr = error_message(retval); + goto send_response; + } + + /* make "now" a boolean, true == too soon */ + now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life); + + (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent); + + if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + retval = CHPASS_UTIL_PASSWORD_TOO_SOON; + + until = princ_ent->last_pwd_change + pol_ent->pw_min_life; + time_string = ctime(&until); + + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + + sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON), + time_string); + msg_ptr = msg_ret; + + goto send_response; + } + } + + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + + retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ, + pword, NULL, msg_ret); + msg_ptr = msg_ret; + (void) krb5_free_principal(user_princ); + +send_response: + + retval = convert_ovsec_to_kadm(retval); + + if (retval) { + /* don't send message on success because kpasswd.v4 will */ + /* print "password changed" too */ + *outlen = strlen(msg_ptr)+2; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, msg_ptr); + strcat(*datout, "\n"); + } else + *outlen = 0; + } + if (retval == KADM_INSECURE_PW) { + krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + +#else /* !OVSEC_KADM */ + + if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, + newkey, no_pword ? 0 : pword)) { + if (retval == KADM_PW_MISMATCH) { + /* + * Very strange!!! This means that the cleartext + * password which was sent and the DES cblock + * didn't match! + */ + (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } + if (fascist_cpw) { + *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, bad_pw_err); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); +#ifdef notdef + /* For debugging only, probably a bad idea */ + if (!no_pword) + (void) krb_log("The password was %s\n", pword); +#endif + return(retval); + } else { + *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, bad_pw_warn); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' used an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + } else { + *datout = 0; + *outlen = 0; + } + + retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey); + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); +#endif /* OVSEC_KADM */ + + return retval; +} + +/**********************************************************************/ +#ifndef OVSEC_KADM + +/* +kadm_ser_add - the server side of the add_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Adds and entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +kadm_ser_add(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_mod - the server side of the mod_entry routine + recieves : KTEXT, {values, values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Modifies all entries corresponding to the first values so they match the + second values. +returns the values for the changed entries +*/ +kadm_ser_mod(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals vals1, vals2, retvals; + int wh; + int status; + + if ((wh = stream_to_vals(dat, &vals1, len)) < 0) + return KADM_LENGTH_ERROR; + if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0) + return KADM_LENGTH_ERROR; + if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1, + &vals2, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_get + recieves : KTEXT, {values, flags} + returns : CKSUM, RETCODE, {count, values, values, values} + acl : su + +gets the fields requested by flags from all entries matching values +returns this data for each matching recipient, after a count of how many such + matches there were +*/ +kadm_ser_get(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + u_char fl[FLDSZ]; + int loop,wh; + int status; + + if ((wh = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + if (wh + FLDSZ > len) + return KADM_LENGTH_ERROR; + for (loop=FLDSZ-1; loop>=0; loop--) + fl[loop] = dat[wh++]; + if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm, + &values, fl, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_ckpw - the server side of the check_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : none + +Checks to see if the des key passed from the caller is a "secure" password. +*/ +kadm_ser_ckpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int stvlen = 0,status; + int retval; + extern char *malloc(); + extern int kadm_approve_pw(); + + /* take key off the stream, and check it */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + keylow = keyhigh = 0; + retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey, + no_pword ? 0 : pword); + memset(newkey, 0, sizeof(newkey)); + if (retval) { + *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, check_pw_msg); + strcat(*datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent an insecure password to be checked", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } else { + *datout = 0; + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent a secure password to be checked", + ad->pname, ad->pinst, ad->prealm); + } + return(0); +} + +/* +kadm_ser_stab - the server side of the change_srvtab routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Creates or modifies the specified service principal to have a random +key, which is sent back to the client. The key version is returned in +the max_life field of the values structure. It's a hack, but it's a +backwards compatible hack.... +*/ +kadm_ser_stab(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values); + if (status == KADM_DATA) { + *outlen = vals_to_stream(&values,datout); + values.key_low = values.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +#endif /* !OVSEC_KADM */ diff --git a/src/kadmin/v4server/attic/kadm_server.h b/src/kadmin/v4server/attic/kadm_server.h new file mode 100644 index 000000000..4e6fd8c46 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_server.h @@ -0,0 +1,64 @@ +/* + * $Source$ + * $Author$ + * $Header$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Definitions for Kerberos administration server & client + */ + +#ifndef KADM_SERVER_DEFS +#define KADM_SERVER_DEFS + +#include <mit-copyright.h> +/* + * kadm_server.h + * Header file for the fourth attempt at an admin server + * Doug Church, December 28, 1989, MIT Project Athena + * ps. Yes that means this code belongs to athena etc... + * as part of our ongoing attempt to copyright all greek names + */ + +#include <sys/types.h> +#include <krb.h> +#include <des.h> + +#include <krb5/krb5.h> +#include <krb5/kdb.h> +#include <krb5/osconf.h> +#include <krb5/config.h> +#ifdef PROVIDE_DES_CBC_CRC +#include <krb5/mit-des.h> +#endif + +typedef struct { + struct sockaddr_in admin_addr; + struct sockaddr_in recv_addr; + int recv_addr_len; + int admin_fd; /* our link to clients */ + char sname[ANAME_SZ]; + char sinst[INST_SZ]; + char krbrlm[REALM_SZ]; + krb5_principal sprinc; + krb5_encrypt_block master_encblock; + krb5_principal master_princ; + krb5_keyblock master_keyblock; + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_kvno mkvno; +} Kadm_Server; + +#define ADD_ACL_FILE "/v4acl.add" +#define GET_ACL_FILE "/v4acl.get" +#define MOD_ACL_FILE "/v4acl.mod" +#define STAB_ACL_FILE "/v4acl.srvtab" +#define STAB_SERVICES_FILE "/v4stab_services" +#define STAB_HOSTS_FILE "/v4stab_bad_hosts" + +#endif /* KADM_SERVER_DEFS */ diff --git a/src/kadmin/v4server/attic/kadm_stream.c b/src/kadmin/v4server/attic/kadm_stream.c new file mode 100644 index 000000000..83aaa295c --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_stream.c @@ -0,0 +1,276 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Stream conversion functions for Kerberos administration server + */ + +#ifndef lint +static char rcsid_kadm_stream_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> +/* + kadm_stream.c + this holds the stream support routines for the kerberos administration server + + vals_to_stream: converts a vals struct to a stream for transmission + internals build_field_header, vts_[string, char, krb4_int32, short] + stream_to_vals: converts a stream to a vals struct + internals check_field_header, stv_[string, char, krb4_int32, short] + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits +*/ + +#include "kadm.h" +#include <string.h> + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* +vals_to_stream + recieves : kadm_vals *, u_char * + returns : a realloced and filled in u_char * + +this function creates a byte-stream representation of the kadm_vals structure +*/ +vals_to_stream(dt_in, dt_out) +Kadm_vals *dt_in; +u_char **dt_out; +{ + int vsloop, stsize; /* loop counter, stream size */ + + stsize = build_field_header(dt_in->fields, dt_out); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_in->fields)) { + switch (vsloop) { + case KADM_NAME: + stsize+=vts_string(dt_in->name, dt_out, stsize); + break; + case KADM_INST: + stsize+=vts_string(dt_in->instance, dt_out, stsize); + break; + case KADM_EXPDATE: + stsize+=vts_long(dt_in->exp_date, dt_out, stsize); + break; + case KADM_ATTR: + stsize+=vts_short(dt_in->attributes, dt_out, stsize); + break; + case KADM_MAXLIFE: + stsize+=vts_char(dt_in->max_life, dt_out, stsize); + break; + case KADM_DESKEY: + stsize+=vts_long(dt_in->key_high, dt_out, stsize); + stsize+=vts_long(dt_in->key_low, dt_out, stsize); + break; + default: + break; + } +} + return(stsize); +} + +build_field_header(cont, st) +u_char *cont; /* container for fields data */ +u_char **st; /* stream */ +{ + *st = (u_char *) malloc (4); + memcpy((void *) *st, (void *) cont, 4); + return 4; /* return pointer to current stream location */ +} + +vts_string(dat, st, loc) +char *dat; /* a string to put on the stream */ +u_char **st; /* base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1)); + memcpy((char *)(*st + loc), dat, strlen(dat)+1); + return strlen(dat)+1; +} + +vts_short(dat, st, loc) +u_short dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + u_short temp; /* to hold the net order short */ + + temp = htons(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short)); + return sizeof(u_short); +} + +vts_long(dat, st, loc) +krb4_uint32 dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + krb4_uint32 temp; /* to hold the net order short */ + + temp = htonl(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb4_uint32))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb4_uint32)); + return sizeof(krb4_uint32); +} + + +vts_char(dat, st, loc) +u_char dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char))); + (*st)[loc] = (u_char) dat; + return 1; +} + +/* +stream_to_vals + recieves : u_char *, kadm_vals * + returns : a kadm_vals filled in according to u_char * + +this decodes a byte stream represntation of a vals struct into kadm_vals +*/ +stream_to_vals(dt_in, dt_out, maxlen) +u_char *dt_in; +Kadm_vals *dt_out; +int maxlen; /* max length to use */ +{ + register int vsloop, stsize; /* loop counter, stream size */ + register int status; + krb4_int32 lcllong; + + memset((char *) dt_out, 0, sizeof(*dt_out)); + + stsize = check_field_header(dt_in, dt_out->fields, maxlen); + if (stsize < 0) + return(-1); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_out->fields)) + switch (vsloop) { + case KADM_NAME: + if ((status = stv_string(dt_in, dt_out->name, stsize, + sizeof(dt_out->name), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_INST: + if ((status = stv_string(dt_in, dt_out->instance, stsize, + sizeof(dt_out->instance), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_EXPDATE: + if ((status = stv_long(dt_in, &lcllong, stsize, + maxlen)) < 0) + return(-1); + dt_out->exp_date = lcllong; + stsize += status; + break; + case KADM_ATTR: + if ((status = stv_short(dt_in, &dt_out->attributes, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_MAXLIFE: + if ((status = stv_char(dt_in, &dt_out->max_life, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_DESKEY: + if ((status = stv_long(dt_in, &dt_out->key_high, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + if ((status = stv_long(dt_in, &dt_out->key_low, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + default: + break; + } + return stsize; +} + +check_field_header(st, cont, maxlen) +u_char *st; /* stream */ +u_char *cont; /* container for fields data */ +int maxlen; +{ + if (4 > maxlen) + return(-1); + memcpy((char *) cont, (char *) st, 4); + return 4; /* return pointer to current stream location */ +} + +stv_string(st, dat, loc, stlen, maxlen) +register u_char *st; /* base pointer to the stream */ +char *dat; /* a string to read from the stream */ +register int loc; /* offset into the stream for current data */ +int stlen; /* max length of string to copy in */ +int maxlen; /* max length of input stream */ +{ + int maxcount; /* max count of chars to copy */ + + maxcount = min(maxlen - loc, stlen); + + (void) strncpy(dat, (char *)st + loc, maxcount); + + if (dat[maxcount-1]) /* not null-term --> not enuf room */ + return(-1); + return strlen(dat)+1; +} + +stv_short(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_short *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + u_short temp; /* to hold the net order short */ + + if (loc + sizeof(u_short) > maxlen) + return(-1); + memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(u_short)); + *dat = ntohs(temp); /* convert to network order */ + return sizeof(u_short); +} + +stv_long(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +krb4_uint32 *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; /* maximum length of st */ +{ + krb4_uint32 temp; /* to hold the net order short */ + + if (loc + sizeof(krb4_uint32) > maxlen) + return(-1); + memcpy((char *) &temp, (char *)((long)st+(krb4_uint32)loc), sizeof(krb4_uint32)); + *dat = ntohl(temp); /* convert to network order */ + return sizeof(krb4_uint32); +} + +stv_char(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_char *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + if (loc + 1 > maxlen) + return(-1); + *dat = *(st + loc); + return 1; +} + diff --git a/src/kadmin/v4server/attic/kadm_supp.c b/src/kadmin/v4server/attic/kadm_supp.c new file mode 100644 index 000000000..cf4ba40f4 --- /dev/null +++ b/src/kadmin/v4server/attic/kadm_supp.c @@ -0,0 +1,114 @@ +/* + * $Source$ + * $Author$ + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Support functions for Kerberos administration server & clients + */ + +#ifndef lint +static char rcsid_kadm_supp_c[] = +"$Header$"; +#endif lint + +#include <mit-copyright.h> +/* + kadm_supp.c + this holds the support routines for the kerberos administration server + + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits + prin_vals: prints out data associated with a Principal in the vals + structure +*/ + +#include "kadm.h" +#include "krb_db.h" + +/* +prin_vals: + recieves : a vals structure +*/ +prin_vals(vals) +Kadm_vals *vals; +{ + printf("Info in Database for %s.%s:\n", vals->name, vals->instance); + printf(" Max Life: %d Exp Date: %s\n",vals->max_life, + asctime(localtime(&vals->exp_date))); + printf(" Attribs: %.2x key: %u %u\n",vals->attributes, + vals->key_low, vals->key_high); +} + +#ifdef notdef +nierror(s) +int s; +{ + extern char *error_message(); + printf("Kerberos admin server loses..... %s\n",error_message(s)); + return(s); +} +#endif + +/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal, + it copies the fields in Principal specified by fields into Kadm_vals, + i.e from old to new */ + +kadm_prin_to_vals(fields, new, old) +u_char fields[FLDSZ]; +Kadm_vals *new; +Principal *old; +{ + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) { + (void) strncpy(new->name, old->name, ANAME_SZ); + SET_FIELD(KADM_NAME, new->fields); + } + if (IS_FIELD(KADM_INST,fields)) { + (void) strncpy(new->instance, old->instance, INST_SZ); + SET_FIELD(KADM_INST, new->fields); + } + if (IS_FIELD(KADM_EXPDATE,fields)) { + new->exp_date = old->exp_date; + SET_FIELD(KADM_EXPDATE, new->fields); + } + if (IS_FIELD(KADM_ATTR,fields)) { + new->attributes = old->attributes; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_MAXLIFE,fields)) { + new->max_life = old->max_life; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + SET_FIELD(KADM_DESKEY, new->fields); + } +} + +kadm_vals_to_prin(fields, new, old) +u_char fields[FLDSZ]; +Principal *new; +Kadm_vals *old; +{ + + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) + (void) strncpy(new->name, old->name, ANAME_SZ); + if (IS_FIELD(KADM_INST,fields)) + (void) strncpy(new->instance, old->instance, INST_SZ); + if (IS_FIELD(KADM_EXPDATE,fields)) + new->exp_date = old->exp_date; + if (IS_FIELD(KADM_ATTR,fields)) + new->attributes = old->attributes; + if (IS_FIELD(KADM_MAXLIFE,fields)) + new->max_life = old->max_life; + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + } +} diff --git a/src/kadmin/v4server/configure.in b/src/kadmin/v4server/configure.in new file mode 100644 index 000000000..9d45c1e16 --- /dev/null +++ b/src/kadmin/v4server/configure.in @@ -0,0 +1,16 @@ +AC_INIT(admin_server.c) +CONFIG_RULES +AC_PROG_INSTALL +AC_CHECK_HEADERS(sys/time.h unistd.h stdlib.h) +CHECK_SIGNALS +CHECK_WAIT_TYPE +AC_PROG_AWK +USE_KADMCLNT_LIBRARY +USE_GSSRPC_LIBRARY +USE_GSSAPI_LIBRARY +USE_DYN_LIBRARY +USE_KDB5_LIBRARY +USE_KRB4_LIBRARY +KRB5_LIBRARIES +V5_USE_SHARED_LIB +V5_AC_OUTPUT_MAKEFILE diff --git a/src/kadmin/v4server/kadm_err.et b/src/kadmin/v4server/kadm_err.et new file mode 100644 index 000000000..a19273083 --- /dev/null +++ b/src/kadmin/v4server/kadm_err.et @@ -0,0 +1,57 @@ +# kadmin.v4/server/kadm_err.et +# +# Copyright 1988 by the Massachusetts Institute of Technology. +# +# For copying and distribution information, please see the file +# <mit-copyright.h>. +# +# Kerberos administration server error table +# + et kadm + +# KADM_SUCCESS, as all success codes should be, is zero + +ec KADM_RCSID, "$Header$" +# /* Building and unbuilding the packet errors */ +ec KADM_NO_REALM, "Cannot fetch local realm" +ec KADM_NO_CRED, "Unable to fetch credentials" +ec KADM_BAD_KEY, "Bad key supplied" +ec KADM_NO_ENCRYPT, "Can't encrypt data" +ec KADM_NO_AUTH, "Cannot encode/decode authentication info" +ec KADM_WRONG_REALM, "Principal attemping change is in wrong realm" +ec KADM_NO_ROOM, "Packet is too large" +ec KADM_BAD_VER, "Version number is incorrect" +ec KADM_BAD_CHK, "Checksum does not match" +ec KADM_NO_READ, "Unsealing private data failed" +ec KADM_NO_OPCODE, "Unsupported operation" +ec KADM_NO_HOST, "Could not find administrating host" +ec KADM_UNK_HOST, "Administrating host name is unknown" +ec KADM_NO_SERV, "Could not find service name in services database" +ec KADM_NO_SOCK, "Could not create socket" +ec KADM_NO_CONN, "Could not connect to server" +ec KADM_NO_HERE, "Could not fetch local socket address" +ec KADM_NO_MAST, "Could not fetch master key" +ec KADM_NO_VERI, "Could not verify master key" + +# /* From the server side routines */ +ec KADM_INUSE, "Entry already exists in database" +ec KADM_UK_SERROR, "Database store error" +ec KADM_UK_RERROR, "Database read error" +ec KADM_UNAUTH, "Insufficient access to perform requested operation" +# KADM_DATA isn't really an error, but... +ec KADM_DATA, "Data is available for return to client" +ec KADM_NOENTRY, "No such entry in the database" + +ec KADM_NOMEM, "Memory exhausted" +ec KADM_NO_HOSTNAME, "Could not fetch system hostname" +ec KADM_NO_BIND, "Could not bind port" +ec KADM_LENGTH_ERROR, "Length mismatch problem" +ec KADM_ILL_WILDCARD, "Illegal use of wildcard" + +ec KADM_DB_INUSE, "Database locked or in use" + +ec KADM_INSECURE_PW, "Insecure password rejected" +ec KADM_PW_MISMATCH, "Cleartext password and DES key did not match" + +ec KADM_NOT_SERV_PRINC, "Invalid principal for change srvtab request" +end diff --git a/src/kadmin/v4server/kadm_funcs.c b/src/kadmin/v4server/kadm_funcs.c new file mode 100644 index 000000000..5025e3acb --- /dev/null +++ b/src/kadmin/v4server/kadm_funcs.c @@ -0,0 +1,1066 @@ +/* + * kadmin/v4server/kadm_funcs.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side database manipulation routines + */ + + +#include <mit-copyright.h> +/* +kadm_funcs.c +the actual database manipulation code +*/ + +#include <stdio.h> +#include <string.h> +#include <sys/param.h> +/* #include <ndbm.h> Gotten by kadmin_server.h */ +#include <ctype.h> +#include <pwd.h> +#include <sys/file.h> +#include <kadm.h> +#include <kadm_err.h> +#include <krb_db.h> +#include <syslog.h> +#include <fcntl.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif +#else +#include <time.h> +#endif + +#include "kadm_server.h" + +extern Kadm_Server server_parm; + +krb5_error_code +kadm_entry2princ(entry, princ) + krb5_db_entry entry; + Principal *princ; +{ + char realm[REALM_SZ]; /* dummy values only */ + krb5_tl_mod_princ *mprinc; + krb5_key_data *pkey; + krb5_error_code retval; + + /* NOTE: does not convert the key */ + memset(princ, 0, sizeof (*princ)); + retval = krb5_524_conv_principal(kadm_context, entry.princ, + princ->name, princ->instance, realm); + if (retval) + return retval; + princ->exp_date = entry.expiration; + strncpy(princ->exp_date_txt, ctime((const time_t *) &entry.expiration), + DATE_SZ); + princ->attributes = entry.attributes; + princ->max_life = entry.max_life / (60 * 5); + princ->kdc_key_ver = 1; /* entry.mkvno; */ + princ->key_version = entry.key_data[0].key_data_kvno; + + retval = krb5_dbe_decode_mod_princ_data(kadm_context, &entry, &mprinc); + if (retval) + return retval; + princ->mod_date = mprinc->mod_date; + strncpy(princ->mod_date_txt, + ctime((const time_t *) &mprinc->mod_date), + DATE_SZ); + krb5_free_principal(kadm_context, mprinc->mod_princ); + krb5_xfree(mprinc); + + /* Find the V4 key */ + retval = krb5_dbe_find_enctype(kadm_context, + &entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) + return retval; + princ->key_version = pkey->key_data_kvno; + + return 0; +} + +krb5_error_code +kadm_princ2entry(princ, entry) + Principal princ; + krb5_db_entry *entry; +{ + krb5_error_code retval; + krb5_tl_mod_princ mprinc; + krb5_key_data *kdatap; + + /* NOTE: does not convert the key */ + memset(entry, 0, sizeof (*entry)); + /* yeah yeah stupid v4 database doesn't store realm names */ + retval = krb5_425_conv_principal(kadm_context, princ.name, princ.instance, + server_parm.krbrlm, &entry->princ); + if (retval) + return retval; + + entry->len = KRB5_KDB_V1_BASE_LENGTH; + entry->max_life = princ.max_life * (60 * 5); + entry->max_renewable_life = server_parm.max_rlife; /* XXX yeah well */ + entry->expiration = princ.exp_date; + entry->attributes = princ.attributes; + + retval = krb5_425_conv_principal(kadm_context, princ.mod_name, + princ.mod_instance, + server_parm.krbrlm, &mprinc.mod_princ); + if (retval) + return(retval); + mprinc.mod_date = princ.mod_date; + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, &mprinc, entry); + if (retval) + return(retval); + + if (mprinc.mod_princ) + krb5_free_principal(kadm_context, mprinc.mod_princ); + + if (retval = krb5_dbe_find_enctype(kadm_context, + entry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &kdatap)) { + if (!(retval = krb5_dbe_create_key_data(kadm_context, entry))) + kdatap = &entry->key_data[entry->n_key_data-1]; + } + if (kdatap) { + kdatap->key_data_ver = 2; + kdatap->key_data_type[0] = (krb5_int16) ENCTYPE_DES_CBC_CRC; + kdatap->key_data_type[1] = (krb5_int16) KRB5_KDB_SALTTYPE_V4; + kdatap->key_data_kvno = (krb5_int16) princ.key_version; + } + return(retval); +} + +int +check_access(pname, pinst, prealm, acltype) +char *pname; +char *pinst; +char *prealm; +enum acl_types acltype; +{ + char checkname[MAX_K_NAME_SZ]; + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(checkname, "%s.%s@%s", pname, pinst, prealm); + + switch (acltype) { + case ADDACL: + (void) sprintf(filename, "%s%s", acldir, ADD_ACL_FILE); + break; + case GETACL: + (void) sprintf(filename, "%s%s", acldir, GET_ACL_FILE); + break; + case MODACL: + (void) sprintf(filename, "%s%s", acldir, MOD_ACL_FILE); + break; + case DELACL: + (void) sprintf(filename, "%s%s", acldir, DEL_ACL_FILE); + break; + case STABACL: + (void) sprintf(filename, "%s%s", acldir, STAB_ACL_FILE); + break; + } + return(acl_check(filename, checkname)); +} + +int +wildcard(str) +char *str; +{ + if (!strcmp(str, WILDCARD_STR)) + return(1); + return(0); +} + +#define failadd(code) { (void) syslog(LOG_ERR, "FAILED adding '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_add_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_principal default_princ; + krb5_error_code retval; + krb5_db_entry newentry, tmpentry; + krb5_boolean more; + krb5_keyblock newpw; + krb5_tl_mod_princ mprinc; + krb5_key_data *pkey; + krb5_keysalt sblock; + int numfound; + + if (!check_access(rname, rinstance, rrealm, ADDACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to add an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failadd(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to add an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + kadm_vals_to_prin(valsin->fields, &data_i, valsin); + (void) strncpy(data_i.name, valsin->name, ANAME_SZ); + (void) strncpy(data_i.instance, valsin->instance, INST_SZ); + + if (!IS_FIELD(KADM_EXPDATE,valsin->fields)) + data_i.exp_date = server_parm.expiration; + if (!IS_FIELD(KADM_ATTR,valsin->fields)) + data_i.attributes = server_parm.flags; + if (!IS_FIELD(KADM_MAXLIFE,valsin->fields)) + data_i.max_life = server_parm.max_life; + + retval = kadm_princ2entry(data_i, &newentry); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + newpw.magic = KV5M_KEYBLOCK; + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) + failadd(KADM_NOMEM); + + retval = krb5_dbe_find_enctype(kadm_context, + &newentry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) + failadd(retval); + + data_i.key_low = ntohl(data_i.key_low); + data_i.key_high = ntohl(data_i.key_high); + memcpy(newpw.contents, &data_i.key_low, 4); + memcpy((char *)(((krb5_int32 *) newpw.contents) + 1), &data_i.key_high, 4); + newpw.length = 8; + newpw.enctype = ENCTYPE_DES_CBC_CRC; + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + /* encrypt new key in master key */ + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + &sblock, + (int) ++data_i.key_version, + pkey); + memset((char *)newpw.contents, 0, newpw.length); + free(newpw.contents); + if (retval) { + failadd(retval); + } + data_o = data_i; + + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, + &tmpentry, &numfound, &more); + + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + if (numfound) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(KADM_INUSE); + } else { + if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + mprinc.mod_princ = NULL; /* in case the following breaks */ + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm, + &mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, + &mprinc, + &newentry); + krb5_free_principal(kadm_context, mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &newentry, &numfound); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(retval); + } + if (!numfound) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failadd(KADM_UK_SERROR); + } else { + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, + &tmpentry, + &numfound, &more); + krb5_db_free_principal(kadm_context, &newentry, 1); + if (retval) { + failadd(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + failadd(KADM_UK_RERROR); + } + kadm_entry2princ(tmpentry, &data_o); + krb5_db_free_principal(kadm_context, &tmpentry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' added.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } +} +#undef failadd + +#define faildel(code) { (void) syslog(LOG_ERR, "FAILED deleting '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_del_entry (rname, rinstance, rrealm, valsin, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; +Kadm_vals *valsout; +{ + int numfound; /* check how many we get written */ + krb5_boolean more; /* pointer to more grabbed records */ + Principal data_i, data_o; /* temporary principal */ + u_char flags[4]; + krb5_db_entry entry, odata; + krb5_error_code retval; + krb5_principal inprinc; + + if (!check_access(rname, rinstance, rrealm, DELACL)) { + (void) syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to delete an entry for '%s.%s'", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + /* Need to check here for "legal" name and instance */ + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + faildel(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "request to delete an entry for '%s.%s' from '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(kadm_context, valsin->name, + valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + faildel(retval); + + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound, + &more); + + if (retval) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(retval); + } else if (!numfound || more) { + faildel(KADM_NOENTRY); + } + + retval = krb5_db_delete_principal(kadm_context, inprinc, &numfound); + if (retval) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(retval); + } + if (!numfound) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(KADM_UK_SERROR); + } else { + if (retval) { + faildel(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &entry, numfound); + faildel(KADM_UK_RERROR); + } + kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(kadm_context, &entry, numfound); + memset((char *)flags, 0, sizeof(flags)); + SET_FIELD(KADM_NAME,flags); + SET_FIELD(KADM_INST,flags); + SET_FIELD(KADM_EXPDATE,flags); + SET_FIELD(KADM_ATTR,flags); + SET_FIELD(KADM_MAXLIFE,flags); + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' deleted.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } +} +#undef faildel + +#define failget(code) { (void) syslog(LOG_ERR, "FAILED retrieving '%s.%s' (%s)", valsin->name, valsin->instance, error_message(code)); return code; } + +kadm_get_entry (rname, rinstance, rrealm, valsin, flags, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin; /* what they wannt to get */ +u_char *flags; /* which fields we want */ +Kadm_vals *valsout; /* what data is there */ +{ + int numfound; /* check how many were returned */ + krb5_boolean more; /* To point to more name.instances */ + Principal data_o; /* Data object to hold Principal */ + krb5_principal inprinc; + krb5_db_entry entry; + krb5_error_code retval; + + if (!check_access(rname, rinstance, rrealm, GETACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to get '%s.%s's entry", + rname, rinstance, rrealm, valsin->name, valsin->instance); + return KADM_UNAUTH; + } + + if (wildcard(valsin->name) || wildcard(valsin->instance)) { + failget(KADM_ILL_WILDCARD); + } + + syslog(LOG_INFO, "retrieve '%s.%s's entry for '%s.%s@%s'", + valsin->name, valsin->instance, rname, rinstance, rrealm); + + retval = krb5_425_conv_principal(kadm_context, valsin->name, + valsin->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failget(retval); + /* Look up the record in the database */ + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &entry, &numfound, + &more); + krb5_free_principal(kadm_context, inprinc); + if (retval) { + failget(retval); + } else if (!numfound || more) { + failget(KADM_NOENTRY); + } + retval = kadm_entry2princ(entry, &data_o); + krb5_db_free_principal(kadm_context, &entry, 1); + if (retval) { + failget(retval); + } + kadm_prin_to_vals(flags, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' retrieved.", valsin->name, valsin->instance); + return KADM_DATA; /* Set all the appropriate fields */ +} +#undef failget + +#define failmod(code) { (void) syslog(LOG_ERR, "FAILED modifying '%s.%s' (%s)", valsin1->name, valsin1->instance, error_message(code)); return code; } + +kadm_mod_entry (rname, rinstance, rrealm, valsin1, valsin2, valsout) +char *rname; /* requestors name */ +char *rinstance; /* requestors instance */ +char *rrealm; /* requestors realm */ +Kadm_vals *valsin1, *valsin2; /* holds the parameters being + passed in */ +Kadm_vals *valsout; /* the actual record which is returned */ +{ + int numfound; + krb5_boolean more; + Principal data_o, temp_key; + u_char fields[4]; + krb5_keyblock newpw; + krb5_error_code retval; + krb5_principal theprinc; + krb5_db_entry newentry, odata; + krb5_tl_mod_princ mprinc; + krb5_key_data *pkey; + krb5_keysalt sblock; + + if (wildcard(valsin1->name) || wildcard(valsin1->instance)) { + failmod(KADM_ILL_WILDCARD); + } + + if (!check_access(rname, rinstance, rrealm, MODACL)) { + syslog(LOG_WARNING, "WARNING: '%s.%s@%s' tried to change '%s.%s's entry", + rname, rinstance, rrealm, valsin1->name, valsin1->instance); + return KADM_UNAUTH; + } + + syslog(LOG_INFO, "request to modify '%s.%s's entry from '%s.%s@%s' ", + valsin1->name, valsin1->instance, rname, rinstance, rrealm); + retval = krb5_425_conv_principal(kadm_context, + valsin1->name, valsin1->instance, + server_parm.krbrlm, &theprinc); + if (retval) + failmod(retval); + numfound = 1; + retval = krb5_db_get_principal(kadm_context, theprinc, &newentry, + &numfound, &more); + if (retval) { + krb5_free_principal(kadm_context, theprinc); + failmod(retval); + } else if (numfound == 1) { + kadm_vals_to_prin(valsin2->fields, &temp_key, valsin2); + krb5_free_principal(kadm_context, newentry.princ); + newentry.princ = theprinc; + if (IS_FIELD(KADM_EXPDATE,valsin2->fields)) + newentry.expiration = temp_key.exp_date; + if (IS_FIELD(KADM_ATTR,valsin2->fields)) + newentry.attributes = temp_key.attributes; + if (IS_FIELD(KADM_MAXLIFE,valsin2->fields)) + newentry.max_life = temp_key.max_life; + if (IS_FIELD(KADM_DESKEY,valsin2->fields)) { + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(kadm_context, &newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(KADM_NOMEM); + } + newpw.magic = KV5M_KEYBLOCK; + newpw.length = 8; + newpw.enctype = ENCTYPE_DES_CBC_CRC; + temp_key.key_low = ntohl(temp_key.key_low); + temp_key.key_high = ntohl(temp_key.key_high); + memcpy(newpw.contents, &temp_key.key_low, 4); + memcpy(newpw.contents + 4, &temp_key.key_high, 4); + if (retval = krb5_dbe_find_enctype(kadm_context, + &newentry, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + memset((char *)&temp_key, 0, sizeof (temp_key)); + failmod(retval); + } + if (pkey->key_data_contents[0]) { + krb5_xfree(pkey->key_data_contents[0]); + pkey->key_data_contents[0] = (krb5_octet *) NULL; + } + /* encrypt new key in master key */ + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + &sblock, + (int) pkey->key_data_kvno+1, + pkey); + memset(newpw.contents, 0, newpw.length); + free(newpw.contents); + memset((char *)&temp_key, 0, sizeof(temp_key)); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + } + if (retval = krb5_timeofday(kadm_context, &mprinc.mod_date)) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, rrealm, + &mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + + retval = krb5_dbe_encode_mod_princ_data(kadm_context, + &mprinc, + &newentry); + krb5_free_principal(kadm_context, mprinc.mod_princ); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } + + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &newentry, &numfound); + memset((char *)&data_o, 0, sizeof(data_o)); + if (retval) { + krb5_db_free_principal(kadm_context, &newentry, 1); + failmod(retval); + } else { + numfound = 1; + retval = krb5_db_get_principal(kadm_context, newentry.princ, &odata, + &numfound, &more); + krb5_db_free_principal(kadm_context, &newentry, 1); + if (retval) { + failmod(retval); + } else if (numfound != 1 || more) { + krb5_db_free_principal(kadm_context, &odata, numfound); + failmod(KADM_UK_RERROR); + } + retval = kadm_entry2princ(odata, &data_o); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) + failmod(retval); + memset((char *) fields, 0, sizeof(fields)); + SET_FIELD(KADM_NAME,fields); + SET_FIELD(KADM_INST,fields); + SET_FIELD(KADM_EXPDATE,fields); + SET_FIELD(KADM_ATTR,fields); + SET_FIELD(KADM_MAXLIFE,fields); + kadm_prin_to_vals(fields, valsout, &data_o); + syslog(LOG_INFO, "'%s.%s' modified.", valsin1->name, valsin1->instance); + return KADM_DATA; /* Set all the appropriate fields */ + } + } else { + failmod(KADM_NOENTRY); + } +} +#undef failmod + +#define failchange(code) { syslog(LOG_ERR, "FAILED changing key for '%s.%s@%s' (%s)", rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_change (rname, rinstance, rrealm, newpw) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +{ + int numfound; + krb5_boolean more; + krb5_principal rprinc; + krb5_error_code retval; + krb5_keyblock localpw; + krb5_db_entry odata; + krb5_key_data *pkey; + krb5_keysalt sblock; + + if (strcmp(server_parm.krbrlm, rrealm)) { + syslog(LOG_ERR, "change key request from wrong realm, '%s.%s@%s'!\n", + rname, rinstance, rrealm); + return(KADM_WRONG_REALM); + } + + if (wildcard(rname) || wildcard(rinstance)) { + failchange(KADM_ILL_WILDCARD); + } + syslog(LOG_INFO, "'%s.%s@%s' wants to change its password", + rname, rinstance, rrealm); + retval = krb5_425_conv_principal(kadm_context, rname, rinstance, + server_parm.krbrlm, &rprinc); + if (retval) + failchange(retval); + if ((localpw.contents = (krb5_octet *)malloc(8)) == NULL) + failchange(KADM_NOMEM); + memcpy(localpw.contents, newpw, 8); + localpw.magic = KV5M_KEYBLOCK; + localpw.enctype = ENCTYPE_DES_CBC_CRC; + localpw.length = 8; + numfound = 1; + retval = krb5_db_get_principal(kadm_context, rprinc, &odata, + &numfound, &more); + krb5_free_principal(kadm_context, rprinc); + if (retval) { + memset(localpw.contents, 0, localpw.length); + free(localpw.contents); + failchange(retval); + } else if (numfound == 1) { + if (retval = krb5_dbe_find_enctype(kadm_context, + &odata, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey)) { + failchange(retval); + } + pkey->key_data_kvno++; + pkey->key_data_kvno %= 256; + numfound = 1; + sblock.type = KRB5_KDB_SALTTYPE_V4; + sblock.data.length = 0; + sblock.data.data = (char *) NULL; + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &localpw, + &sblock, + (int) pkey->key_data_kvno, + pkey); + memset(localpw.contents, 0, localpw.length); + free(localpw.contents); + if (retval) { + failchange(retval); + } + retval = krb5_db_put_principal(kadm_context, &odata, &numfound); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) { + failchange(retval); + } else if (more) { + failchange(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, + "'%s.%s@%s' password changed.", rname, rinstance, rrealm); + return KADM_SUCCESS; + } + } + else { + failchange(KADM_NOENTRY); + } +} +#undef failchange + +check_pw(newpw, checkstr) + des_cblock newpw; + char *checkstr; +{ +#ifdef NOENCRYPTION + return 0; +#else /* !NOENCRYPTION */ + des_cblock checkdes; + + (void) des_string_to_key(checkstr, checkdes); + return(!memcmp(checkdes, newpw, sizeof(des_cblock))); +#endif /* NOENCRYPTION */ +} + +char *reverse(str) + char *str; +{ + static char newstr[80]; + char *p, *q; + int i; + + i = strlen(str); + if (i >= sizeof(newstr)) + i = sizeof(newstr)-1; + p = str+i-1; + q = newstr; + q[i]='\0'; + for(; i > 0; i--) + *q++ = *p--; + + return(newstr); +} + +int lower(str) + char *str; +{ + register char *cp; + int effect=0; + + for (cp = str; *cp; cp++) { + if (isupper(*cp)) { + *cp = tolower(*cp); + effect++; + } + } + return(effect); +} + +des_check_gecos(gecos, newpw) + char *gecos; + des_cblock newpw; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + if (lower(cp)) { + if (check_pw(newpw, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (check_pw(newpw, tcp)) + return(KADM_INSECURE_PW); + } + cp = ncp; + } else + break; + } + return(0); +} + +str_check_gecos(gecos, pwstr) + char *gecos; + char *pwstr; +{ + char *cp, *ncp, *tcp; + + for (cp = gecos; *cp; ) { + /* Skip past punctuation */ + for (; *cp; cp++) + if (isalnum(*cp)) + break; + /* Skip to the end of the word */ + for (ncp = cp; *ncp; ncp++) + if (!isalnum(*ncp) && *ncp != '\'') + break; + /* Delimit end of word */ + if (*ncp) + *ncp++ = '\0'; + /* Check word to see if it's the password */ + if (*cp) { + if (!strcasecmp(pwstr, cp)) + return(KADM_INSECURE_PW); + tcp = reverse(cp); + if (!strcasecmp(pwstr, tcp)) + return(KADM_INSECURE_PW); + cp = ncp; + } else + break; + } + return(0); +} + + +kadm_approve_pw(rname, rinstance, rrealm, newpw, pwstring) +char *rname; +char *rinstance; +char *rrealm; +des_cblock newpw; +char *pwstring; +{ + static DBM *pwfile = NULL; + int retval; + datum passwd, entry; + struct passwd *ent; +#ifdef HESIOD + extern struct passwd *hes_getpwnam(); +#endif + + if (pwstring && !check_pw(newpw, pwstring)) + /* + * Someone's trying to toy with us.... + */ + return(KADM_PW_MISMATCH); + if (pwstring && (strlen(pwstring) < 5)) + return(KADM_INSECURE_PW); + if (!pwfile) { + pwfile = dbm_open(PW_CHECK_FILE, O_RDONLY, 0644); + } + if (pwfile) { + passwd.dptr = (char *) newpw; + passwd.dsize = 8; + entry = dbm_fetch(pwfile, passwd); + if (entry.dptr) + return(KADM_INSECURE_PW); + } + if (check_pw(newpw, rname) || check_pw(newpw, reverse(rname))) + return(KADM_INSECURE_PW); +#ifdef HESIOD + ent = hes_getpwnam(rname); +#else + ent = getpwnam(rname); +#endif + if (ent && ent->pw_gecos) { + if (pwstring) + retval = str_check_gecos(ent->pw_gecos, pwstring); + else + retval = des_check_gecos(ent->pw_gecos, newpw); + if (retval) + return(retval); + } + return(0); +} + +/* + * This routine checks to see if a principal should be considered an + * allowable service name which can be changed by kadm_change_srvtab. + * + * We do this check by using the ACL library. This makes the + * (relatively) reasonable assumption that both the name and the + * instance will not contain '.' or '@'. + */ +kadm_check_srvtab(name, instance) + char *name; + char *instance; +{ + char filename[MAXPATHLEN]; + extern char *acldir; + + (void) sprintf(filename, "%s%s", acldir, STAB_SERVICES_FILE); + if (!acl_check(filename, name)) + return(KADM_NOT_SERV_PRINC); + + (void) sprintf(filename, "%s%s", acldir, STAB_HOSTS_FILE); + if (acl_check(filename, instance)) + return(KADM_NOT_SERV_PRINC); + return 0; +} + +/* + * Routine to allow some people to change the key of a srvtab + * principal to a random key, which the admin server will return to + * the client. + */ +#define failsrvtab(code) { syslog(LOG_ERR, "change_srvtab: FAILED changing '%s.%s' by '%s.%s@%s' (%s)", values->name, values->instance, rname, rinstance, rrealm, error_message(code)); return code; } + +kadm_chg_srvtab(rname, rinstance, rrealm, values) + char *rname; /* requestors name */ + char *rinstance; /* requestors instance */ + char *rrealm; /* requestors realm */ + Kadm_vals *values; +{ + int numfound, ret, isnew = 0; + des_cblock new_key; + krb5_principal inprinc; + krb5_error_code retval; + krb5_db_entry odata; + krb5_boolean more; + krb5_keyblock newpw; + krb5_key_data *pkey; + + if (!check_access(rname, rinstance, rrealm, STABACL)) + failsrvtab(KADM_UNAUTH); + if (wildcard(rname) || wildcard(rinstance)) + failsrvtab(KADM_ILL_WILDCARD); + if (ret = kadm_check_srvtab(values->name, values->instance)) + failsrvtab(ret); + + retval = krb5_425_conv_principal(kadm_context, values->name, + values->instance, + server_parm.krbrlm, &inprinc); + if (retval) + failsrvtab(retval); + /* + * OK, get the entry + */ + numfound = 1; + retval = krb5_db_get_principal(kadm_context, inprinc, &odata, + &numfound, &more); + if (retval) { + krb5_free_principal(kadm_context, inprinc); + failsrvtab(retval); + } else if (numfound) { + retval = krb5_dbe_find_enctype(kadm_context, + &odata, + ENCTYPE_DES_CBC_CRC, + KRB5_KDB_SALTTYPE_V4, + -1, + &pkey); + if (retval) { + krb5_free_principal(kadm_context, inprinc); + failsrvtab(retval); + } + } + else { + /* + * This is a new srvtab entry that we're creating + */ + isnew = 1; + memset((char *)&odata, 0, sizeof (odata)); + odata.princ = inprinc; + odata.max_life = server_parm.max_life; + odata.max_renewable_life = server_parm.max_rlife; + odata.expiration = server_parm.expiration; + odata.attributes = 0; + if (!krb5_dbe_create_key_data(kadm_context, &odata)) { + pkey = &odata.key_data[0]; + memset(pkey, 0, sizeof(*pkey)); + pkey->key_data_ver = 2; + pkey->key_data_type[0] = ENCTYPE_DES_CBC_CRC; + pkey->key_data_type[1] = KRB5_KDB_SALTTYPE_V4; + } + } + pkey->key_data_kvno++; + +#ifdef NOENCRYPTION + memset(new_key, 0, sizeof(new_key)); + new_key[0] = 127; +#else + des_new_random_key(new_key); +#endif + /* + * Store the new key in the return structure; also fill in the + * rest of the fields. + */ + if ((newpw.contents = (krb5_octet *)malloc(8)) == NULL) { + krb5_db_free_principal(kadm_context, &odata, 1); + failsrvtab(KADM_NOMEM); + } + newpw.enctype = ENCTYPE_DES_CBC_CRC; + newpw.length = 8; + memcpy((char *)newpw.contents, new_key, 8); + memset((char *)new_key, 0, sizeof (new_key)); + memcpy((char *)&values->key_low, newpw.contents, 4); + memcpy((char *)&values->key_high, newpw.contents + 4, 4); + values->key_low = htonl(values->key_low); + values->key_high = htonl(values->key_high); + values->max_life = odata.max_life / (60 * 5); + values->exp_date = odata.expiration; + values->attributes = odata.attributes; + memset(values->fields, 0, sizeof(values->fields)); + SET_FIELD(KADM_NAME, values->fields); + SET_FIELD(KADM_INST, values->fields); + SET_FIELD(KADM_EXPDATE, values->fields); + SET_FIELD(KADM_ATTR, values->fields); + SET_FIELD(KADM_MAXLIFE, values->fields); + SET_FIELD(KADM_DESKEY, values->fields); + + /* + * Encrypt the new key with the master key, and then update + * the database record + */ + retval = krb5_dbekd_encrypt_key_data(kadm_context, + &server_parm.master_encblock, + &newpw, + (krb5_keysalt *) NULL, + (int) pkey->key_data_kvno, + pkey); + memset((char *)newpw.contents, 0, 8); + free(newpw.contents); + if (retval) { + krb5_db_free_principal(kadm_context, &odata, 1); + failsrvtab(retval); + } + numfound = 1; + retval = krb5_db_put_principal(kadm_context, &odata, &numfound); + krb5_db_free_principal(kadm_context, &odata, 1); + if (retval) { + failsrvtab(retval); + } + else if (!numfound) { + failsrvtab(KADM_UK_SERROR); + } else { + syslog(LOG_INFO, "change_srvtab: service '%s.%s' %s by %s.%s@%s.", + values->name, values->instance, + numfound ? "changed" : "created", + rname, rinstance, rrealm); + return KADM_DATA; + } +} + +#undef failsrvtab diff --git a/src/kadmin/v4server/kadm_ser_wrap.c b/src/kadmin/v4server/kadm_ser_wrap.c new file mode 100644 index 000000000..5e7f48508 --- /dev/null +++ b/src/kadmin/v4server/kadm_ser_wrap.c @@ -0,0 +1,310 @@ +/* + * kadmin/v4server/kadm_ser_wrap.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side support functions + */ + + +#include <mit-copyright.h> +/* +kadm_ser_wrap.c +unwraps wrapped packets and calls the appropriate server subroutine +*/ + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/types.h> +#include <netdb.h> +#include <sys/socket.h> +#include "kadm_server.h" +#include <kadm.h> +#include <kadm_err.h> +#include <krb_err.h> +#include <syslog.h> + +#ifdef OVSEC_KADM +#include <kadm5/admin.h> +extern void *ovsec_handle; +#endif + +Kadm_Server server_parm; + +/* +kadm_ser_init +set up the server_parm structure +*/ +#ifdef OVSEC_KADM +kadm_ser_init(inter, realm, params) + int inter; /* interactive or from file */ + char realm[]; + kadm5_config_params *params; +#else +kadm_ser_init(inter, realm) + int inter; /* interactive or from file */ + char realm[]; +#endif +{ + struct servent *sep; + struct hostent *hp; + char hostname[MAXHOSTNAMELEN]; + char *mkey_name; + krb5_error_code retval; + int numfound = 1; + krb5_boolean more; + krb5_db_entry master_entry; + krb5_key_data *kdatap; + + if (gethostname(hostname, sizeof(hostname))) + return KADM_NO_HOSTNAME; + + (void) strcpy(server_parm.sname, PWSERV_NAME); + (void) strcpy(server_parm.sinst, KRB_MASTER); + (void) strcpy(server_parm.krbrlm, realm); + if (krb5_build_principal(kadm_context, + &server_parm.sprinc, + strlen(realm), + realm, + PWSERV_NAME, + KRB_MASTER, 0)) + return KADM_NO_MAST; + server_parm.admin_fd = -1; + /* setting up the addrs */ + if ((sep = getservbyname(KADM_SNAME, "tcp")) == NULL) + return KADM_NO_SERV; + memset((char *)&server_parm.admin_addr, 0,sizeof(server_parm.admin_addr)); + server_parm.admin_addr.sin_family = AF_INET; + if ((hp = gethostbyname(hostname)) == NULL) + return KADM_NO_HOSTNAME; + memcpy((char *) &server_parm.admin_addr.sin_addr.s_addr, hp->h_addr, + hp->h_length); + server_parm.admin_addr.sin_port = sep->s_port; + /* setting up the database */ + mkey_name = KRB5_KDB_M_NAME; + +#ifdef OVSEC_KADM + server_parm.master_keyblock.enctype = params->enctype; + krb5_use_enctype(kadm_context, &server_parm.master_encblock, + server_parm.master_keyblock.enctype); +#else + if (inter == 1) { + server_parm.master_keyblock.enctype = ENCTYPE_DES_CBC_MD5; + krb5_use_enctype(kadm_context, &server_parm.master_encblock, + server_parm.master_keyblock.enctype); + } else + server_parm.master_keyblock.enctype = ENCTYPE_UNKNOWN; +#endif + + retval = krb5_db_setup_mkey_name(kadm_context, mkey_name, realm, + (char **) 0, + &server_parm.master_princ); + if (retval) + return KADM_NO_MAST; + krb5_db_fetch_mkey(kadm_context, server_parm.master_princ, + &server_parm.master_encblock, + (inter == 1), FALSE, +#ifdef OVSEC_KADM + params->stash_file, +#else + (char *) NULL, +#endif + NULL, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_MAST; + retval = krb5_db_verify_master_key(kadm_context, server_parm.master_princ, + &server_parm.master_keyblock, + &server_parm.master_encblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_process_key(kadm_context, &server_parm.master_encblock, + &server_parm.master_keyblock); + if (retval) + return KADM_NO_VERI; + retval = krb5_db_get_principal(kadm_context, server_parm.master_princ, + &master_entry, &numfound, &more); + if (retval || more || !numfound) + return KADM_NO_VERI; + + retval = krb5_dbe_find_enctype(kadm_context, + &master_entry, + -1, -1, -1, + &kdatap); + if (retval) + return KRB5_PROG_KEYTYPE_NOSUPP; + server_parm.max_life = master_entry.max_life; + server_parm.max_rlife = master_entry.max_renewable_life; + server_parm.expiration = master_entry.expiration; + server_parm.mkvno = kdatap->key_data_kvno; + /* don't set flags, as master has some extra restrictions + (??? quoted from kdb_edit.c) */ + krb5_db_free_principal(kadm_context, &master_entry, numfound); + return KADM_SUCCESS; +} + + +static void errpkt(dat, dat_len, code) +u_char **dat; +int *dat_len; +int code; +{ + krb5_ui_4 retcode; + char *pdat; + + free((char *)*dat); /* free up req */ + *dat_len = KADM_VERSIZE + sizeof(krb5_ui_4); + *dat = (u_char *) malloc((unsigned)*dat_len); + if (!(*dat)) { + syslog(LOG_ERR, "malloc(%d) returned null while in errpkt!", *dat_len); + abort(); + } + pdat = (char *) *dat; + retcode = htonl((krb5_ui_4) code); + (void) strncpy(pdat, KADM_ULOSE, KADM_VERSIZE); + memcpy(&pdat[KADM_VERSIZE], (char *)&retcode, sizeof(krb5_ui_4)); + return; +} + +/* +kadm_ser_in +unwrap the data stored in dat, process, and return it. +*/ +kadm_ser_in(dat,dat_len) +u_char **dat; +int *dat_len; +{ + u_char *in_st; /* pointer into the sent packet */ + int in_len,retc; /* where in packet we are, for + returns */ + krb5_ui_4 r_len; /* length of the actual packet */ + KTEXT_ST authent; /* the authenticator */ + AUTH_DAT ad; /* who is this, klink */ + krb5_ui_4 ncksum; /* checksum of encrypted data */ + des_key_schedule sess_sched; /* our schedule */ + MSG_DAT msg_st; + u_char *retdat, *tmpdat; + int retval, retlen; + + if (strncmp(KADM_VERSTR, (char *)*dat, KADM_VERSIZE)) { + errpkt(dat, dat_len, KADM_BAD_VER); + return KADM_BAD_VER; + } + in_len = KADM_VERSIZE; + /* get the length */ + if ((retc = stv_long(*dat, &r_len, in_len, *dat_len)) < 0) + return KADM_LENGTH_ERROR; + in_len += retc; + authent.length = *dat_len - r_len - KADM_VERSIZE - sizeof(krb5_ui_4); + memcpy((char *)authent.dat, (char *)(*dat) + in_len, authent.length); + authent.mbz = 0; + /* service key should be set before here */ + if (retc = krb_rd_req(&authent, server_parm.sname, server_parm.sinst, + server_parm.recv_addr.sin_addr.s_addr, &ad, (char *)0)) + { + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + +#define clr_cli_secrets() {memset((char *)sess_sched, 0, sizeof(sess_sched)); memset((char *)ad.session, 0, sizeof(ad.session));} + + in_st = *dat + *dat_len - r_len; +#ifdef NOENCRYPTION + ncksum = 0; +#else + ncksum = quad_cksum(in_st, (krb5_ui_4 *)0, (long) r_len, 0, ad.session); +#endif + if (ncksum!=ad.checksum) { /* yow, are we correct yet */ + clr_cli_secrets(); + errpkt(dat, dat_len,KADM_BAD_CHK); + return KADM_BAD_CHK; + } +#ifdef NOENCRYPTION + memset(sess_sched, 0, sizeof(sess_sched)); +#else + des_key_sched(ad.session, sess_sched); +#endif + if (retc = (int) krb_rd_priv(in_st, r_len, sess_sched, ad.session, + &server_parm.recv_addr, + &server_parm.admin_addr, &msg_st)) { + clr_cli_secrets(); + errpkt(dat, dat_len,retc + krb_err_base); + return retc + krb_err_base; + } + switch (msg_st.app_data[0]) { + case CHANGE_PW: + retval = kadm_ser_cpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#ifndef OVSEC_KADM + case ADD_ENT: + retval = kadm_ser_add(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case DEL_ENT: + retval = kadm_ser_del(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case GET_ENT: + retval = kadm_ser_get(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case MOD_ENT: + retval = kadm_ser_mod(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHECK_PW: + retval = kadm_ser_ckpw(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; + case CHG_STAB: + retval = kadm_ser_stab(msg_st.app_data+1,(int) msg_st.app_length,&ad, + &retdat, &retlen); + break; +#endif /* OVSEC_KADM */ + default: + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_OPCODE); + return KADM_NO_OPCODE; + } + /* Now seal the response back into a priv msg */ + free((char *)*dat); + tmpdat = (u_char *) malloc((unsigned)(retlen + KADM_VERSIZE + + sizeof(krb5_ui_4))); + if (!tmpdat) { + clr_cli_secrets(); + syslog(LOG_ERR, "malloc(%d) returned null while in kadm_ser_in!", + retlen + KADM_VERSIZE + sizeof(krb5_ui_4)); + errpkt(dat, dat_len, KADM_NOMEM); + return KADM_NOMEM; + } + (void) strncpy((char *)tmpdat, KADM_VERSTR, KADM_VERSIZE); + retval = htonl((krb5_ui_4)retval); + memcpy((char *)tmpdat + KADM_VERSIZE, (char *)&retval, sizeof(krb5_ui_4)); + if (retlen) { + memcpy((char *)tmpdat + KADM_VERSIZE + sizeof(krb5_ui_4), (char *)retdat, + retlen); + free((char *)retdat); + } + /* slop for mk_priv stuff */ + *dat = (u_char *) malloc((unsigned) (retlen + KADM_VERSIZE + + sizeof(krb5_ui_4) + 200)); + if ((*dat_len = krb_mk_priv(tmpdat, *dat, + (u_long) (retlen + KADM_VERSIZE + + sizeof(krb5_ui_4)), + sess_sched, + ad.session, &server_parm.admin_addr, + &server_parm.recv_addr)) < 0) { + clr_cli_secrets(); + errpkt(dat, dat_len, KADM_NO_ENCRYPT); + return KADM_NO_ENCRYPT; + } + clr_cli_secrets(); + return KADM_SUCCESS; +} diff --git a/src/kadmin/v4server/kadm_server.c b/src/kadmin/v4server/kadm_server.c new file mode 100644 index 000000000..81e43f128 --- /dev/null +++ b/src/kadmin/v4server/kadm_server.c @@ -0,0 +1,571 @@ +/* + * kadmin/v4server/kadm_server.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Kerberos administration server-side subroutines + */ + + +#include <mit-copyright.h> + +#include "k5-int.h" + +#include <stdio.h> +#include <string.h> +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#ifdef TIME_WITH_SYS_TIME +#include <time.h> +#endif +#else +#include <time.h> +#endif + +#ifdef OVSEC_KADM +#include <com_err.h> +#include <kadm5/admin.h> +#include <kadm5/chpass_util_strings.h> +#include <krb5/kdb.h> +extern void *ovsec_handle; +#endif + +#include <kadm.h> +#include <kadm_err.h> + +extern krb5_context kadm_context; +int fascist_cpw = 0; /* Be fascist about insecure passwords? */ + +#ifdef OVSEC_KADM +char pw_required[] = "The version of kpasswd that you are using is not compatible with the\nOpenV*Secure V4 Administration Server. Please contact your security\nadministrator.\n\n"; + +#else /* !OVSEC_KADM */ + +char bad_pw_err[] = + "\007\007\007ERROR: Insecure password not accepted. Please choose another.\n\n"; + +char bad_pw_warn[] = + "\007\007\007WARNING: You have chosen an insecure password. You may wish to\nchoose a better password.\n\n"; + +char check_pw_msg[] = + "You have entered an insecure password. You should choose another.\n\n"; + +char pw_blurb[] = +"A good password is something which is easy for you to remember, but that\npeople who know you won't easily guess; so don't use your name, or your\ndog's name, or a word from the dictionary. Passwords should be at least\n6 characters long, and may contain UPPER- and lower-case letters,\nnumbers, or punctuation. A good password can be:\n\n -- some initials, like \"GykoR-66\" for \"Get your kicks on Rte 66.\"\n -- an easily pronounced nonsense word, like \"slaRooBey\" or \"krang-its\"\n -- a mis-spelled phrase, like \"2HotPeetzas\" or \"ItzAGurl\"\n\nPlease Note: It is important that you do not tell ANYONE your password,\nincluding your friends, or even people from Athena or Information\nSystems. Remember, *YOU* are assumed to be responsible for anything\ndone using your password.\n"; + +#endif /* OVSEC_KADM */ + +/* from V4 month_sname.c -- was not part of API */ +/* + * Given an integer 1-12, month_sname() returns a string + * containing the first three letters of the corresponding + * month. Returns 0 if the argument is out of range. + */ + +static char *month_sname(n) + int n; +{ + static char *name[] = { + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" + }; + return((n < 1 || n > 12) ? 0 : name [n-1]); +} + +/* from V4 log.c -- was not part of API */ + +/* + * krb_log() is used to add entries to the logfile (see krb_set_logfile() + * below). Note that it is probably not portable since it makes + * assumptions about what the compiler will do when it is called + * with less than the correct number of arguments which is the + * way it is usually called. + * + * The log entry consists of a timestamp and the given arguments + * printed according to the given "format". + * + * The log file is opened and closed for each log entry. + * + * The return value is undefined. + */ + +/* static char *log_name = KRBLOG; */ +/* KRBLOG is in the V4 klog.h but may not correspond to anything installed. */ +static char *log_name = KADM_SYSLOG; + +static void krb_log(format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0) + char *format; + char *a1,*a2,*a3,*a4,*a5,*a6,*a7,*a8,*a9,*a0; +{ + FILE *logfile; + time_t now; + struct tm *tm; + + if ((logfile = fopen(log_name,"a")) == NULL) + return; + + (void) time(&now); + tm = localtime(&now); + + fprintf(logfile,"%2d-%s-%02d %02d:%02d:%02d ",tm->tm_mday, + month_sname(tm->tm_mon + 1),tm->tm_year, + tm->tm_hour, tm->tm_min, tm->tm_sec); + fprintf(logfile,format,a1,a2,a3,a4,a5,a6,a7,a8,a9,a0); + fprintf(logfile,"\n"); + (void) fclose(logfile); + return; +} + + +/* +kadm_ser_cpw - the server side of the change_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : caller can change only own password + +Replaces the password (i.e. des key) of the caller with that specified in key. +Returns no actual data from the master server, since this is called by a user +*/ +kadm_ser_cpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_int32 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int status, stvlen = 0; + int retval; + extern int kadm_approve_pw(); +#ifdef OVSEC_KADM + ovsec_kadm_principal_ent_t princ_ent; + ovsec_kadm_policy_ent_t pol_ent; + krb5_principal user_princ; + char msg_ret[1024], *time_string, *ptr; + const char *msg_ptr; + krb5_int32 now; + time_t until; +#endif + + /* take key off the stream, and change the database */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((stvlen = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + +#ifdef OVSEC_KADM + /* we don't use the client-provided key itself */ + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); + + if (no_pword) { + krb_log("Old-style change password request from '%s.%s@%s'!", + ad->pname, ad->pinst, ad->prealm); + *outlen = strlen(pw_required)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, pw_required); + } else { + *outlen = 0; + } + return KADM_INSECURE_PW; + } + + if (krb5_build_principal(kadm_context, &user_princ, + strlen(ad->prealm), + ad->prealm, + ad->pname, + *ad->pinst ? ad->pinst : 0, 0)) + /* this should never happen */ + return KADM_NOENTRY; + + *outlen = 0; + + if (retval = krb5_timeofday(kadm_context, &now)) { + msg_ptr = error_message(retval); + goto send_response; + } + + retval = ovsec_kadm_get_principal(ovsec_handle, user_princ, + &princ_ent); + if (retval != 0) { + msg_ptr = error_message(retval); + goto send_response; + } + + /* + * This daemon necessarily has the modify privilege, so + * ovsec_kadm_chpass_principal will allow it to violate the + * policy's minimum lifetime. Since that's A Bad Thing, we need + * to enforce it ourselves. Unfortunately, this means we are + * duplicating code from both ovsec_adm_server and + * ovsec_kadm_chpass_util(). + */ + if (princ_ent->aux_attributes & OVSEC_KADM_POLICY) { + retval = ovsec_kadm_get_policy(ovsec_handle, + princ_ent->policy, + &pol_ent); + if (retval != 0) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + msg_ptr = error_message(retval); + goto send_response; + } + + /* make "now" a boolean, true == too soon */ + now = ((now - princ_ent->last_pwd_change) < pol_ent->pw_min_life); + + (void) ovsec_kadm_free_policy_ent(ovsec_handle, pol_ent); + + if(now && !(princ_ent->attributes & KRB5_KDB_REQUIRES_PWCHANGE)) { + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + retval = CHPASS_UTIL_PASSWORD_TOO_SOON; + + until = princ_ent->last_pwd_change + pol_ent->pw_min_life; + time_string = ctime(&until); + + if (*(ptr = &time_string[strlen(time_string)-1]) == '\n') + *ptr = '\0'; + + sprintf(msg_ret, error_message(CHPASS_UTIL_PASSWORD_TOO_SOON), + time_string); + msg_ptr = msg_ret; + + goto send_response; + } + } + + (void) ovsec_kadm_free_principal_ent(ovsec_handle, princ_ent); + + retval = ovsec_kadm_chpass_principal_util(ovsec_handle, user_princ, + pword, NULL, msg_ret); + msg_ptr = msg_ret; + (void) krb5_free_principal(kadm_context, user_princ); + +send_response: + + retval = convert_ovsec_to_kadm(retval); + + if (retval) { + /* don't send message on success because kpasswd.v4 will */ + /* print "password changed" too */ + *outlen = strlen(msg_ptr)+2; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy(*datout, msg_ptr); + strcat(*datout, "\n"); + } else + *outlen = 0; + } + if (retval == KADM_INSECURE_PW) { + krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } +#else /* OVSEC_KADM */ + if (retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, + newkey, no_pword ? 0 : pword)) { + if (retval == KADM_PW_MISMATCH) { + /* + * Very strange!!! This means that the cleartext + * password which was sent and the DES cblock + * didn't match! + */ + (void) krb_log("'%s.%s@%s' sent a password string which didn't match with the DES key?!?", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } + if (fascist_cpw) { + *outlen = strlen(bad_pw_err)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, bad_pw_err); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' tried to use an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); +#ifdef notdef + /* For debugging only, probably a bad idea */ + if (!no_pword) + (void) krb_log("The password was %s\n", pword); +#endif + return(retval); + } else { + *outlen = strlen(bad_pw_warn) + strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, bad_pw_warn); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' used an insecure password in changepw", + ad->pname, ad->pinst, ad->prealm); + } + } else { + *datout = 0; + *outlen = 0; + } + + retval = kadm_change(ad->pname, ad->pinst, ad->prealm, newkey); + keylow = keyhigh = 0; + memset(newkey, 0, sizeof(newkey)); +#endif /* OVSEC_KADM */ + + return retval; +} + +#ifndef OVSEC_KADM +/* +kadm_ser_add - the server side of the add_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Adds and entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +int +kadm_ser_add(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_add_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_del - the server side of the del_entry routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as alloc) + +Deletes an entry containing values to the database +returns the values of the entry, so if you leave certain fields blank you will + be able to determine the default values they are set to +*/ +kadm_ser_del(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return(KADM_LENGTH_ERROR); + if ((status = kadm_del_entry(ad->pname, ad->pinst, ad->prealm, + &values, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_mod - the server side of the mod_entry routine + recieves : KTEXT, {values, values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Modifies all entries corresponding to the first values so they match the + second values. +returns the values for the changed entries +*/ +kadm_ser_mod(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals vals1, vals2, retvals; + int wh; + int status; + + if ((wh = stream_to_vals(dat, &vals1, len)) < 0) + return KADM_LENGTH_ERROR; + if ((status = stream_to_vals(dat+wh,&vals2, len-wh)) < 0) + return KADM_LENGTH_ERROR; + if ((status = kadm_mod_entry(ad->pname, ad->pinst, ad->prealm, &vals1, + &vals2, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_get + recieves : KTEXT, {values, flags} + returns : CKSUM, RETCODE, {count, values, values, values} + acl : su + +gets the fields requested by flags from all entries matching values +returns this data for each matching recipient, after a count of how many such + matches there were +*/ +kadm_ser_get(dat,len,ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values, retvals; + u_char fl[FLDSZ]; + int loop,wh; + int status; + + if ((wh = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + if (wh + FLDSZ > len) + return KADM_LENGTH_ERROR; + for (loop=FLDSZ-1; loop>=0; loop--) + fl[loop] = dat[wh++]; + if ((status = kadm_get_entry(ad->pname, ad->pinst, ad->prealm, + &values, fl, &retvals)) == KADM_DATA) { + *outlen = vals_to_stream(&retvals,datout); + retvals.key_low = retvals.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} + +/* +kadm_ser_ckpw - the server side of the check_password routine + recieves : KTEXT, {key} + returns : CKSUM, RETCODE + acl : none + +Checks to see if the des key passed from the caller is a "secure" password. +*/ +kadm_ser_ckpw(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + krb5_ui_4 keylow, keyhigh; + char pword[MAX_KPW_LEN]; + int no_pword = 0; + des_cblock newkey; + int stvlen = 0,status; + int retval; + extern int kadm_approve_pw(); + + /* take key off the stream, and check it */ + + if ((status = stv_long(dat, &keyhigh, 0, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_long(dat, &keylow, stvlen, len)) < 0) + return(KADM_LENGTH_ERROR); + stvlen += status; + if ((status = stv_string(dat, pword, stvlen, sizeof(pword), len)) < 0) { + no_pword++; + pword[0]='\0'; + } + stvlen += status; + + keylow = ntohl(keylow); + keyhigh = ntohl(keyhigh); + memcpy((char *)(((krb5_int32 *)newkey) + 1), (char *)&keyhigh, 4); + memcpy((char *)newkey, (char *)&keylow, 4); + keylow = keyhigh = 0; + retval = kadm_approve_pw(ad->pname, ad->pinst, ad->prealm, newkey, + no_pword ? 0 : pword); + memset(newkey, 0, sizeof(newkey)); + if (retval) { + *outlen = strlen(check_pw_msg)+strlen(pw_blurb)+1; + if (*datout = (u_char *) malloc(*outlen)) { + strcpy((char *) *datout, check_pw_msg); + strcat((char *) *datout, pw_blurb); + } else + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent an insecure password to be checked", + ad->pname, ad->pinst, ad->prealm); + return(retval); + } else { + *datout = 0; + *outlen = 0; + (void) krb_log("'%s.%s@%s' sent a secure password to be checked", + ad->pname, ad->pinst, ad->prealm); + } + return(0); +} + +/* +kadm_ser_stab - the server side of the change_srvtab routine + recieves : KTEXT, {values} + returns : CKSUM, RETCODE, {values} + acl : su, sms (as register or dealloc) + +Creates or modifies the specified service principal to have a random +key, which is sent back to the client. The key version is returned in +the max_life field of the values structure. It's a hack, but it's a +backwards compatible hack.... +*/ +kadm_ser_stab(dat, len, ad, datout, outlen) +u_char *dat; +int len; +AUTH_DAT *ad; +u_char **datout; +int *outlen; +{ + Kadm_vals values; + int status; + + if ((status = stream_to_vals(dat, &values, len)) < 0) + return KADM_LENGTH_ERROR; + status = kadm_chg_srvtab(ad->pname, ad->pinst, ad->prealm, &values); + if (status == KADM_DATA) { + *outlen = vals_to_stream(&values,datout); + values.key_low = values.key_high = 0; + return KADM_SUCCESS; + } else { + *outlen = 0; + return status; + } +} +#endif /* !OVSEC_KADM */ diff --git a/src/kadmin/v4server/kadm_server.h b/src/kadmin/v4server/kadm_server.h new file mode 100644 index 000000000..d852bcaab --- /dev/null +++ b/src/kadmin/v4server/kadm_server.h @@ -0,0 +1,58 @@ +/* + * kadmin/v4server/kadm_server.h + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Definitions for Kerberos administration server & client + */ + +#ifndef KADM_SERVER_DEFS +#define KADM_SERVER_DEFS + +#include <mit-copyright.h> +/* + * kadm_server.h + * Header file for the fourth attempt at an admin server + * Doug Church, December 28, 1989, MIT Project Athena + * ps. Yes that means this code belongs to athena etc... + * as part of our ongoing attempt to copyright all greek names + */ + +#include <sys/types.h> +#include <krb.h> +#include <des.h> +#include "k5-int.h" + +typedef struct { + struct sockaddr_in admin_addr; + struct sockaddr_in recv_addr; + int recv_addr_len; + int admin_fd; /* our link to clients */ + char sname[ANAME_SZ]; + char sinst[INST_SZ]; + char krbrlm[REALM_SZ]; + krb5_principal sprinc; + krb5_encrypt_block master_encblock; + krb5_principal master_princ; + krb5_keyblock master_keyblock; + krb5_deltat max_life; + krb5_deltat max_rlife; + krb5_timestamp expiration; + krb5_flags flags; + krb5_kvno mkvno; +} Kadm_Server; + +#define ADD_ACL_FILE "/v4acl.add" +#define GET_ACL_FILE "/v4acl.get" +#define MOD_ACL_FILE "/v4acl.mod" +#define DEL_ACL_FILE "/v4acl.del" +#define STAB_ACL_FILE "/v4acl.srvtab" +#define STAB_SERVICES_FILE "/v4stab_services" +#define STAB_HOSTS_FILE "/v4stab_bad_hosts" + +krb5_context kadm_context; + +#endif /* KADM_SERVER_DEFS */ diff --git a/src/kadmin/v4server/kadm_stream.c b/src/kadmin/v4server/kadm_stream.c new file mode 100644 index 000000000..86da6c64f --- /dev/null +++ b/src/kadmin/v4server/kadm_stream.c @@ -0,0 +1,277 @@ +/* + * kadmin/v4server/kadm_stream.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Stream conversion functions for Kerberos administration server + */ + + +#include <mit-copyright.h> +#include <string.h> +#include "k5-int.h" + +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#else +extern char *malloc(), *calloc(), *realloc(); +#endif + +/* + kadm_stream.c + this holds the stream support routines for the kerberos administration server + + vals_to_stream: converts a vals struct to a stream for transmission + internals build_field_header, vts_[string, char, long, short] + stream_to_vals: converts a stream to a vals struct + internals check_field_header, stv_[string, char, long, short] + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits +*/ + +#include "kadm.h" + +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* +vals_to_stream + recieves : kadm_vals *, u_char * + returns : a realloced and filled in u_char * + +this function creates a byte-stream representation of the kadm_vals structure +*/ +vals_to_stream(dt_in, dt_out) +Kadm_vals *dt_in; +u_char **dt_out; +{ + int vsloop, stsize; /* loop counter, stream size */ + + stsize = build_field_header(dt_in->fields, dt_out); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_in->fields)) { + switch (vsloop) { + case KADM_NAME: + stsize+=vts_string(dt_in->name, dt_out, stsize); + break; + case KADM_INST: + stsize+=vts_string(dt_in->instance, dt_out, stsize); + break; + case KADM_EXPDATE: + stsize+=vts_long(dt_in->exp_date, dt_out, stsize); + break; + case KADM_ATTR: + stsize+=vts_short(dt_in->attributes, dt_out, stsize); + break; + case KADM_MAXLIFE: + stsize+=vts_char(dt_in->max_life, dt_out, stsize); + break; + case KADM_DESKEY: + stsize+=vts_long(dt_in->key_high, dt_out, stsize); + stsize+=vts_long(dt_in->key_low, dt_out, stsize); + break; + default: + break; + } +} + return(stsize); +} + +build_field_header(cont, st) +u_char *cont; /* container for fields data */ +u_char **st; /* stream */ +{ + *st = (u_char *) malloc (4); + memcpy((char *) *st, (char *) cont, 4); + return 4; /* return pointer to current stream location */ +} + +vts_string(dat, st, loc) +char *dat; /* a string to put on the stream */ +u_char **st; /* base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned) (loc + strlen(dat) + 1)); + memcpy((char *)(*st + loc), dat, strlen(dat)+1); + return strlen(dat)+1; +} + +vts_short(dat, st, loc) +u_short dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + u_short temp; /* to hold the net order short */ + + temp = htons(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_short))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(u_short)); + return sizeof(u_short); +} + +vts_long(dat, st, loc) +krb5_ui_4 dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + krb5_ui_4 temp; /* to hold the net order short */ + + temp = htonl(dat); /* convert to network order */ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(krb5_ui_4))); + memcpy((char *)(*st + loc), (char *) &temp, sizeof(krb5_ui_4)); + return sizeof(krb5_ui_4); +} + + +vts_char(dat, st, loc) +u_char dat; /* the attributes field */ +u_char **st; /* a base pointer to the stream */ +int loc; /* offset into the stream for current data */ +{ + *st = (u_char *) realloc ((char *)*st, (unsigned)(loc + sizeof(u_char))); + (*st)[loc] = (u_char) dat; + return 1; +} + +/* +stream_to_vals + recieves : u_char *, kadm_vals * + returns : a kadm_vals filled in according to u_char * + +this decodes a byte stream represntation of a vals struct into kadm_vals +*/ +stream_to_vals(dt_in, dt_out, maxlen) +u_char *dt_in; +Kadm_vals *dt_out; +int maxlen; /* max length to use */ +{ + register int vsloop, stsize; /* loop counter, stream size */ + register int status; + + memset((char *) dt_out, 0, sizeof(*dt_out)); + + stsize = check_field_header(dt_in, dt_out->fields, maxlen); + if (stsize < 0) + return(-1); + for (vsloop=31; vsloop>=0; vsloop--) + if (IS_FIELD(vsloop,dt_out->fields)) + switch (vsloop) { + case KADM_NAME: + if ((status = stv_string(dt_in, dt_out->name, stsize, + sizeof(dt_out->name), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_INST: + if ((status = stv_string(dt_in, dt_out->instance, stsize, + sizeof(dt_out->instance), maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_EXPDATE: + if ((status = stv_long(dt_in, &dt_out->exp_date, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_ATTR: + if ((status = stv_short(dt_in, &dt_out->attributes, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_MAXLIFE: + if ((status = stv_char(dt_in, &dt_out->max_life, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + case KADM_DESKEY: + if ((status = stv_long(dt_in, &dt_out->key_high, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + if ((status = stv_long(dt_in, &dt_out->key_low, stsize, + maxlen)) < 0) + return(-1); + stsize += status; + break; + default: + break; + } + return stsize; +} + +check_field_header(st, cont, maxlen) +u_char *st; /* stream */ +u_char *cont; /* container for fields data */ +int maxlen; +{ + if (4 > maxlen) + return(-1); + memcpy((char *) cont, (char *) st, 4); + return 4; /* return pointer to current stream location */ +} + +stv_string(st, dat, loc, stlen, maxlen) +register u_char *st; /* base pointer to the stream */ +char *dat; /* a string to read from the stream */ +register int loc; /* offset into the stream for current data */ +int stlen; /* max length of string to copy in */ +int maxlen; /* max length of input stream */ +{ + int maxcount; /* max count of chars to copy */ + + maxcount = min(maxlen - loc, stlen); + + (void) strncpy(dat, (char *)st + loc, maxcount); + + if (dat[maxcount-1]) /* not null-term --> not enuf room */ + return(-1); + return strlen(dat)+1; +} + +stv_short(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_short *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + u_short temp; /* to hold the net order short */ + + if (loc + sizeof(u_short) > maxlen) + return(-1); + memcpy((char *) &temp, (char *) st+ loc, sizeof(u_short)); + *dat = ntohs(temp); /* convert to network order */ + return sizeof(u_short); +} + +stv_long(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +krb5_ui_4 *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; /* maximum length of st */ +{ + krb5_ui_4 temp; /* to hold the net order short */ + + if (loc + sizeof(krb5_ui_4) > maxlen) + return(-1); + memcpy((char *) &temp, (char *) st + loc, sizeof(krb5_ui_4)); + *dat = ntohl(temp); /* convert to network order */ + return sizeof(krb5_ui_4); +} + +stv_char(st, dat, loc, maxlen) +u_char *st; /* a base pointer to the stream */ +u_char *dat; /* the attributes field */ +int loc; /* offset into the stream for current data */ +int maxlen; +{ + if (loc + 1 > maxlen) + return(-1); + *dat = *(st + loc); + return 1; +} + diff --git a/src/kadmin/v4server/kadm_supp.c b/src/kadmin/v4server/kadm_supp.c new file mode 100644 index 000000000..9d2f8deb2 --- /dev/null +++ b/src/kadmin/v4server/kadm_supp.c @@ -0,0 +1,113 @@ +/* + * kadmin/v4server/kadm_supp.c + * + * Copyright 1988 by the Massachusetts Institute of Technology. + * + * For copying and distribution information, please see the file + * <mit-copyright.h>. + * + * Support functions for Kerberos administration server & clients + */ + + +#include <mit-copyright.h> +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +/* + kadm_supp.c + this holds the support routines for the kerberos administration server + + error: prints out a kadm error message, returns + fatal: prints out a kadm fatal error message, exits + prin_vals: prints out data associated with a Principal in the vals + structure +*/ + +#include "kadm.h" +#include "krb_db.h" + +/* +prin_vals: + recieves : a vals structure +*/ +void prin_vals(vals) +Kadm_vals *vals; +{ + printf("Info in Database for %s.%s:\n", vals->name, vals->instance); + printf(" Max Life: %d Exp Date: %s\n",vals->max_life, + asctime(localtime((long *)&vals->exp_date))); + printf(" Attribs: %.2x key: %u %u\n",vals->attributes, + vals->key_low, vals->key_high); +} + +#ifdef notdef +nierror(s) +int s; +{ + extern char *error_message(); + printf("Kerberos admin server loses..... %s\n",error_message(s)); + return(s); +} +#endif + +/* kadm_prin_to_vals takes a fields arguments, a Kadm_vals and a Principal, + it copies the fields in Principal specified by fields into Kadm_vals, + i.e from old to new */ + +kadm_prin_to_vals(fields, new, old) +u_char fields[FLDSZ]; +Kadm_vals *new; +Principal *old; +{ + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) { + (void) strncpy(new->name, old->name, ANAME_SZ); + SET_FIELD(KADM_NAME, new->fields); + } + if (IS_FIELD(KADM_INST,fields)) { + (void) strncpy(new->instance, old->instance, INST_SZ); + SET_FIELD(KADM_INST, new->fields); + } + if (IS_FIELD(KADM_EXPDATE,fields)) { + new->exp_date = old->exp_date; + SET_FIELD(KADM_EXPDATE, new->fields); + } + if (IS_FIELD(KADM_ATTR,fields)) { + new->attributes = old->attributes; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_MAXLIFE,fields)) { + new->max_life = old->max_life; + SET_FIELD(KADM_MAXLIFE, new->fields); + } + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + SET_FIELD(KADM_DESKEY, new->fields); + } +} + +kadm_vals_to_prin(fields, new, old) +u_char fields[FLDSZ]; +Principal *new; +Kadm_vals *old; +{ + + memset((char *)new, 0, sizeof(*new)); + if (IS_FIELD(KADM_NAME,fields)) + (void) strncpy(new->name, old->name, ANAME_SZ); + if (IS_FIELD(KADM_INST,fields)) + (void) strncpy(new->instance, old->instance, INST_SZ); + if (IS_FIELD(KADM_EXPDATE,fields)) + new->exp_date = old->exp_date; + if (IS_FIELD(KADM_ATTR,fields)) + new->attributes = old->attributes; + if (IS_FIELD(KADM_MAXLIFE,fields)) + new->max_life = old->max_life; + if (IS_FIELD(KADM_DESKEY,fields)) { + new->key_low = old->key_low; + new->key_high = old->key_high; + } +} diff --git a/src/kadmin/v4server/unit-test/ChangeLog b/src/kadmin/v4server/unit-test/ChangeLog new file mode 100644 index 000000000..93120b8a4 --- /dev/null +++ b/src/kadmin/v4server/unit-test/ChangeLog @@ -0,0 +1,13 @@ +Mon Jul 15 17:15:51 1996 Marc Horowitz <marc@mit.edu> + + * helpers.exp (exp_prog): the check for non-newline-terminated + stdout was causing failures where there weren't any. Barry + doesn't remember why this was here to begin with. + * Makefile.ov (unit-test-body), helpers.exp: some versions of + runtest do not like digits in command-line variable names. + * Makefile.ov (unit-test-body), helpers.exp: ovsec_v4adm_server + renamed to kadmind4 + * getpid.sh: grep out any programs with expect or kadmind4 in + their names. + + diff --git a/src/kadmin/v4server/unit-test/Makefile.ov b/src/kadmin/v4server/unit-test/Makefile.ov new file mode 100644 index 000000000..3af65607e --- /dev/null +++ b/src/kadmin/v4server/unit-test/Makefile.ov @@ -0,0 +1,19 @@ +# +# $Id$ +# + +TOP = ../.. +include $(TOP)/config.mk/template + +unit-test:: unit-test-setup unit-test-body unit-test-cleanup + +unit-test-setup:: + $(START_SERVERS_LOCAL) -v4files -kdcport 750 -keysalt des-cbc-crc:v4 + $(LOCAL_MAKE_KEYTAB) -princ changepw/kerberos /krb5/ovsec_adm.srvtab + +unit-test-body:: + $(RUNTEST) VFOURSERVER=../kadmind4 --tool v4server \ + KDBFIVE_EDIT=../../../admin/edit/kdb5_edit + +unit-test-cleanup:: + $(STOP_SERVERS_LOCAL) -v4files diff --git a/src/kadmin/v4server/unit-test/config/ChangeLog b/src/kadmin/v4server/unit-test/config/ChangeLog new file mode 100644 index 000000000..aa01abc17 --- /dev/null +++ b/src/kadmin/v4server/unit-test/config/ChangeLog @@ -0,0 +1,7 @@ +Mon Jul 15 17:18:56 1996 Marc Horowitz <marc@mit.edu> + + * unix.exp: some versions of runtest do not like digits in + command-line variable names. ovsec_edit_keytab renamed to + kadm5_keytab + + diff --git a/src/kadmin/v4server/unit-test/config/unix.exp b/src/kadmin/v4server/unit-test/config/unix.exp new file mode 100644 index 000000000..874092311 --- /dev/null +++ b/src/kadmin/v4server/unit-test/config/unix.exp @@ -0,0 +1,42 @@ +global env + +set kill /bin/kill + +if {[file exists /bin/sleep]} { + set sleep /bin/sleep +} else { + set sleep /usr/bin/sleep +} + +set kpasswd_v4 /usr/athena/bin/kpasswd +set ovpasswd $env(TOP)/kpasswd/kpasswd +set kadmin_local $env(TOP)/cli/kadmin.local +set kdb5_edit $KDBFIVE_EDIT +set remove_changepw_perms ./remove_changepw_perms.sh +set getpid ./getpid.sh +set ovsec_adm_server $env(TOP)/server/kadmind +set ovsec_edit_keytab $env(TOP)/keytab/kadm5_keytab +set hostname [exec hostname] + +# change-password.exp sends ^C to kpasswd to kill it; on HP-UX the +# default intr character is DEL, and setting it on all platforms +# won't hurt +set stty_init "intr \\^c" + +if {[info commands exp_version] != {}} { + set exp_version_4 [regexp {^4} [exp_version]] +} else { + set exp_version_4 [regexp {^4} [expect_version]] +} + +# Backward compatibility until we're using expect 5 everywhere +if {$exp_version_4} { + global wait_error_index wait_errno_index wait_status_index + set wait_error_index 0 + set wait_errno_index 1 + set wait_status_index 1 +} else { + set wait_error_index 2 + set wait_errno_index 3 + set wait_status_index 3 +} diff --git a/src/kadmin/v4server/unit-test/getpid.sh b/src/kadmin/v4server/unit-test/getpid.sh new file mode 100644 index 000000000..5c1b1a690 --- /dev/null +++ b/src/kadmin/v4server/unit-test/getpid.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +# tcl sucks big fat hairy rocks + +$PS_ALL | awk "/$1/"' && !/awk/ && !/getpid/ && !/expect/ && !/kadmind4/ { print $2 }' diff --git a/src/kadmin/v4server/unit-test/helpers.exp b/src/kadmin/v4server/unit-test/helpers.exp new file mode 100644 index 000000000..7f23b65c8 --- /dev/null +++ b/src/kadmin/v4server/unit-test/helpers.exp @@ -0,0 +1,232 @@ +proc server_pids { } { + global env + + return [eval [concat exec $env(PS_ALL) | \ + awk {{/kadmind4/ && !/awk/ && !/expect/ {printf("%d ", $2)}}}]] +} + +proc server_exit { name status } { + global wait_error_index wait_errno_index wait_status_index + global server_id + global kill + + verbose "$name: stopping V4 kpasswd server." 1 + + # We can't know whether the process exists or not, so we have + # to ignore errors. XXX will close ever time out? + catch {close $server_id} + set pids [server_pids] + if {$pids != {}} { + verbose "server_exit killing process(es) $pids" + catch {exec $kill $pids} + } else { + verbose "server_exit: couldn't find running server(s) to kill" + } + + # wait hangs on AIX if the process was killed; since status == -1 + # in that case, solve the problem by not waiting; the zombies will + # be cleaned up when the test finishes + if {$status == -1} { + return 1 + } + + set ret [wait -i $server_id] + verbose "% Exit $ret" 2 + + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + return 0 + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + return 0 + } + } + + return 1 +} + +proc myfail { msg } { + global mytest_status + fail "$msg" + set mytest_status 1 +} + +proc server_start { name cmdline should_listen args } { + global spawn_id server_id + global VFOURSERVER + global mytest_status + global sleep hostname + + set max_tries 60 + + verbose "$name: starting V4 kpasswd server." 1 + + for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} { + if {$num_tries} { + exec $sleep 5 + verbose "Couldn't connect to V4 kpasswd server; retrying ($num_tries so far)." + } + + spawn $VFOURSERVER $cmdline + set server_id $spawn_id + + foreach test $args { + set mytest_status 0 + uplevel 1 "expect { + -i $server_id + $test + timeout { myfail \"$name: timeout\" } + eof { myfail \"$name: eof while expecting string\" } + }" + + if {$mytest_status == 1} { + return 0 + } + } + + set pids [server_pids] + + if {$should_listen} { + exec $sleep 1 + set save_spawn_id $spawn_id + spawn telnet $hostname kerberos_master + expect { + {Connection refused} { + close -i $save_spawn_id + wait -i $save_spawn_id + close + wait + continue + } + {Connected} { + send "close\n" + close + wait + set spawn_id $save_spawn_id + break + } + default { + close -i $save_spawn_id + wait -i $save_spawn_id + catch {close} + wait + continue + } + } + } else { + break + } + } + + if {$pids == {}} { + # Try twice to find the server processes. Not sure why, + # but there seems to be some sort of race condition in the OS. + + verbose "server_start: couldn't find server process(es) -- trying again" + exec $sleep 1 + set pids [server_pids] + } + + if {$num_tries > $max_tries} { + myfail "$name: couldn't connect to V4 kpasswd server" + return 0 + } else { + if {$pids != {}} { + verbose "server_start: server process ID(s) is/are $pids" + } + pass "$name" + return 1 + } +} + +proc exp_prog { name prog cmdline status args } { + global spawn_id spawn_pid + global mytest_status + global wait_error_index wait_errno_index wait_status_index + + verbose "$name: spawning $prog $cmdline" 1 + + set spawn_pid [eval "spawn $prog $cmdline"] + + # at the end, eof is success + +# lappend args { eof { if {[regexp "\[\r\n\]$" $expect_out(buffer)] == 0} { myfail "final status message not newline-terminated" } } } + lappend args { eof {} } + + foreach test $args { + set mytest_status 0 + uplevel 1 "expect { + $test + timeout { close; myfail \"$name: timeout\" } + eof { myfail \"$name: eof while expecting string\" } + }" + + if {$mytest_status == 1} { return 0 } + } + + # at this point, the id is closed and we can wait on it. + + set ret [wait] + verbose "% Exit $ret" 2 + + if {$status == -1} { return 1 } + + if {[lindex $ret $wait_error_index] == -1} { + fail "$name: wait returned error [lindex $ret $wait_errno_index]" + } else { + if { [lindex $ret $wait_status_index] == $status || + (($status<0) && ([lindex $ret $wait_status_index] == ($status+256))) } { + pass "$name" + } else { + fail "$name: unexpected return status [lindex $ret $wait_status_index], should be $status" + } + } + + return 1 +} + +proc fix_salt { name fullname oldpw newpw } { + global kdb5_edit + + exp_prog "$name: kdb5_edit" $kdb5_edit "" 0 { + "kdb5_edit:" { send "cpw $fullname\n" } + } { + "Enter password:" { send "$newpw\n" } + } { + "Re-enter password for verification:" { send "$newpw\n" } + } { + "kdb5_edit:" { send "quit\n" } + } +} + +proc unexpire { name fullname } { + global kadmin_local + + # While we're at it, make sure they aren't expired. + exp_prog "$name: kadmin.local" $kadmin_local "" 0 { + "kadmin.local:" { + send "modprinc -expire \"May 6, 1999\" $fullname\n" + } + } { + -re "Principal .* modified." { send "quit\n" } + } +} + +proc kpasswd_v4 { name fullname status oldpw newpw args } { + global kpasswd_v4 s + + eval [concat { + exp_prog $name $kpasswd_v4 "-u $fullname" $status { + -re "Old password for $fullname:" { send "$oldpw\n" } + } { + -re "New Password for $fullname:" { send "$newpw\n" } + } { + -re "Verifying, please re-enter New Password for $fullname:" + { send "$newpw\n" } + } + } $args] +} diff --git a/src/kadmin/v4server/unit-test/remove_changepw_perms.sh b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh new file mode 100644 index 000000000..27d026ff3 --- /dev/null +++ b/src/kadmin/v4server/unit-test/remove_changepw_perms.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# tcl sucks big fat hairy rocks + +ed /krb5/ovsec_adm.acl <<EOF >/dev/null 2>&1 +g/changepw\/kerberos/s/^/#/ +w +q +EOF diff --git a/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp new file mode 100644 index 000000000..3c8e181b2 --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.0/setup-srvtab.exp @@ -0,0 +1,11 @@ +load_lib "helpers.exp" + +set timeout 10 + +exp_prog "setup" $ovsec_edit_keytab \ + "-k /krb5/ovsec_adm.srvtab -a -c -p admin changepw/kerberos" \ + 0 { + "Enter password:" { send "admin\n" } +} { + -re "Entry for principal changepw/kerberos .* added to keytab" {} +} diff --git a/src/kadmin/v4server/unit-test/v4server.1/access.exp b/src/kadmin/v4server/unit-test/v4server.1/access.exp new file mode 100644 index 000000000..4d30fc9c7 --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/access.exp @@ -0,0 +1,88 @@ +load_lib "helpers.exp" + +set timeout 30 + +# Setup: make sure the principals we will use have V4 salt +fix_salt "A.setup" testuser notathena notathena +unexpire "A.setup" testuser +unexpire "A.setup" changepw/kerberos + +proc kill_admin_server {} { + global env kill getpid + + set pid [exec $getpid kadmind] + if {$pid != ""} { + exec $kill $pid + } +} + +proc start_admin_server {} { + global ovsec_adm_server sleep + + set max_tries 60 + + for {set num_tries 0} {$num_tries <= $max_tries} {incr num_tries} { + if {$num_tries} { + exec $sleep 5 + verbose "$ovsec_adm_server couldn't bind; retrying ($num_tries so far)" + } + if {[catch "exec $ovsec_adm_server" msg]} { + if {[regexp {Address already in use} $msg]} { + continue + } + fail "starting $ovsec_adm_server: $msg" + } + return + } + fail "starting $ovsec_adm_server: $msg" +} + +proc remove_changepw_perms {} { + global remove_changepw_perms + + exec $remove_changepw_perms +} + +proc set_changepw_perms { perms } { + remove_changepw_perms + + exec echo "changepw/kerberos@SECURE-TEST.OV.COM $perms" \ + >> /krb5/ovsec_adm.acl +} + +# start off with a dead admin server +kill_admin_server + +set_changepw_perms "i" +start_admin_server +server_start A.1 "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} +kpasswd_v4 A.1 testuser 2 notathena foobar { + "Operation requires ``change-password'' privilege" {} +} { + "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {} +} { + "Password NOT changed." {} +} +server_exit A.1 -1 +kill_admin_server + +set_changepw_perms "c" +start_admin_server +server_start A.2 "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} +kpasswd_v4 A.2 testuser 2 notathena foobar { + "Operation requires ``get'' privilege" {} +} { + "$kpasswd_v4: Insufficient access to perform requested operation while attempting to change password." {} +} { + "Password NOT changed." {} +} +server_exit A.2 -1 +kill_admin_server + +set_changepw_perms "ci" + +start_admin_server diff --git a/src/kadmin/v4server/unit-test/v4server.1/change-password.exp b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp new file mode 100644 index 000000000..62b9ec30a --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/change-password.exp @@ -0,0 +1,59 @@ +load_lib "helpers.exp" + +set timeout 30 + +spawn stty -a +expect { eof {} } +wait + +# Setup: make sure the principals we will use have V4 salt +fix_salt "CPW.setup" testuser notathena notathena +fix_salt "CPW.setup" pol1 pol111111 pol111111 +fix_salt "CPW.setup" pol2 pol222222 pol222222 +unexpire "CPW.setup" testuser +unexpire "CPW.setup" pol1 +unexpire "CPW.setup" pol2 +unexpire "CPW.setup" changepw/kerberos + +server_start "CPW.all" "-n" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} + +kpasswd_v4 CPW.1 testuser 0 notathena foobar { "Password changed." {} } +kpasswd_v4 CPW.1 testuser 0 foobar notathena { "Password changed." {} } + +kpasswd_v4 CPW.3 pol1 -1 pol111111 foo { + "New password is too short." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.4 pol1 -1 pol111111 foooooooo { + "New password does not have enough character classes." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.5 pol1 -1 pol111111 Abyssinia { + "New password was found in a dictionary" {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +kpasswd_v4 CPW.6.setup pol1 0 pol111111 polAAAAAA { "Password changed." {} } +kpasswd_v4 CPW.6 pol1 -1 polAAAAAA pol111111 { + "New password was used previously." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +# this relies on the fact that kdb5_edit resets last_pwd_change, which +# it appears to +kpasswd_v4 CPW.7.setup pol2 0 pol222222 polBBBBBB { "Password changed." {} } +kpasswd_v4 CPW.7 pol2 -1 polBBBBBB pol222222 { + "Password cannot be changed because it was changed too recently." {} +} { + "$kpasswd_v4: Insecure password rejected while attempting to change password." { send "\003\n"; close; break } +} + +server_exit "CPW.all" -1 diff --git a/src/kadmin/v4server/unit-test/v4server.1/usage.exp b/src/kadmin/v4server/unit-test/v4server.1/usage.exp new file mode 100644 index 000000000..4d292067a --- /dev/null +++ b/src/kadmin/v4server/unit-test/v4server.1/usage.exp @@ -0,0 +1,26 @@ +load_lib "helpers.exp" + +set timeout 10 + +server_start "U.1: -h" "-h" 0 { + -re {Usage: .*} {} +} { + eof {} +} +server_exit "U.1: -h" 255 + +server_start "U.4: -n" "-n" 1 { + "Enter KDC database master key:" { + myfail "unexpected password prompt" + } + "KADM Server starting in the OVSEC_KADM mode" {} +} + +server_exit "U.4: -n" -1 + +server_start "U.5: no -n" "" 1 { + "KADM Server starting in the OVSEC_KADM mode" {} +} { + "Enter KDC database master key:" { send "mrroot\n" } +} +server_exit "U.5: no -n" -1 |
