Ack - kadm is still used by the Mac and dos build trees
authorEzra Peisach <epeisach@mit.edu>
Sat, 24 Aug 1996 16:03:45 +0000 (16:03 +0000)
committerEzra Peisach <epeisach@mit.edu>
Sat, 24 Aug 1996 16:03:45 +0000 (16:03 +0000)
Everything restored...

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8982 dc483132-0cff-0310-8789-dd5450dbe970

17 files changed:
src/lib/kadm/.Sanitize [new file with mode: 0644]
src/lib/kadm/ChangeLog [new file with mode: 0644]
src/lib/kadm/Makefile.in [new file with mode: 0644]
src/lib/kadm/adm_conn.c [new file with mode: 0644]
src/lib/kadm/adm_kt_dec.c [new file with mode: 0644]
src/lib/kadm/adm_kt_enc.c [new file with mode: 0644]
src/lib/kadm/adm_kw_dec.c [new file with mode: 0644]
src/lib/kadm/adm_kw_enc.c [new file with mode: 0644]
src/lib/kadm/adm_rw.c [new file with mode: 0644]
src/lib/kadm/alt_prof.c [new file with mode: 0644]
src/lib/kadm/configure.in [new file with mode: 0644]
src/lib/kadm/keysalt.c [new file with mode: 0644]
src/lib/kadm/krb5strings.M [new file with mode: 0644]
src/lib/kadm/logger.c [new file with mode: 0644]
src/lib/kadm/str_conv.c [new file with mode: 0644]
src/lib/kadm/t_dbentry.c [new file with mode: 0644]
src/lib/kadm/t_ktentry.c [new file with mode: 0644]

diff --git a/src/lib/kadm/.Sanitize b/src/lib/kadm/.Sanitize
new file mode 100644 (file)
index 0000000..4f33eec
--- /dev/null
@@ -0,0 +1,49 @@
+# Sanitize.in for Kerberos V5
+
+# Each directory to survive it's way into a release will need a file
+# like this one called "./.Sanitize".  All keyword lines must exist,
+# and must exist in the order specified by this file.  Each directory
+# in the tree will be processed, top down, in the following order.
+
+# Hash started lines like this one are comments and will be deleted
+# before anything else is done.  Blank lines will also be squashed
+# out.
+
+# The lines between the "Do-first:" line and the "Things-to-keep:"
+# line are executed as a /bin/sh shell script before anything else is
+# done in this 
+
+Do-first:
+
+# All files listed between the "Things-to-keep:" line and the
+# "Files-to-sed:" line will be kept.  All other files will be removed.
+# Directories listed in this section will have their own Sanitize
+# called.  Directories not listed will be removed in their entirety
+# with rm -rf.
+
+Things-to-keep:
+
+.cvsignore
+ChangeLog
+Makefile.in
+adm_conn.c
+adm_kw_dec.c
+adm_kw_enc.c
+adm_kt_dec.c
+adm_kt_enc.c
+adm_rw.c
+alt_prof.c
+configure.in
+configure
+keysalt.c
+krb5strings.M
+logger.c
+str_conv.c
+t_dbentry.c
+t_ktentry.c
+
+Things-to-lose:
+
+Do-last:
+
+# End of file.
diff --git a/src/lib/kadm/ChangeLog b/src/lib/kadm/ChangeLog
new file mode 100644 (file)
index 0000000..ac07e19
--- /dev/null
@@ -0,0 +1,334 @@
+Thu Jun 13 22:12:21 1996  Tom Yu  <tlyu@voltage-multiplier.mit.edu>
+
+       * configure.in: remove ref to ET_RULES
+
+Mon Jun 10 21:42:26 1996  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+       * adm_conn.c:
+       * adm_kw_dec.c:
+       * adm_kw_enc.c: Change use of _WINDOWS to _MSDOS, and add check
+               for _WIN32
+
+Tue May 21 20:51:06 1996  Sam Hartman  <hartmans@mit.edu>
+
+       * Makefile.in (check-unix):  Use KRB5_RUN_FLAGS
+
+Sun May 12 00:46:57 1996  Marc Horowitz  <marc@mit.edu>
+
+       * alt_prof.c (krb5_read_realm_params): added "acl_file" variable
+       for the admin server.
+
+Wed Mar 13 17:37:00 1996  Ken Raeburn  <raeburn@cygnus.com>
+
+       * configure.in: Use AC_HEADER_STDARG.
+
+Sun Dec 10 11:02:56 1995  Ezra Peisach  <epeisach@kangaroo.mit.edu>
+
+       * str_conv.c (krb5_input_flag_to_string): Add new routine.
+
+Wed Nov  8 02:46:54 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * alt_prof.c (krb5_free_realm_params): Free the realm_kdc_ports
+               element of the structure.
+
+Fri Oct  6 00:30:16 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * Makefile.in: Remove ##DOS!include of config/windows.in.
+               config/windows.in is now included by wconfig.
+
+       * logger.c (klog_vsyslog): Make the logs less verbose, by omitting
+               the hostname, pid, etc. information.
+
+Thu Oct  5 19:46:40 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * alt_prof.c (krb5_read_realm_params): Remove the profile
+               relation, since it's really a bad idea.  Removed the
+               "port" and "secondary_port" relations, and replaced them
+               with the "kdc_port" relation, which takes a list of ports.
+
+Mon Oct  2 15:08:53 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * logger.c (krb5_klog_init): If the log file can't be opened,
+               print an intelligent error message.
+
+Thu Oct  5 12:06:35 1995  Ezra Peisach  <epeisach@kangaroo.mit.edu>
+
+       * alt_prof.c (krb5_read_realm_params): If secure flag is set in
+               context, do not allow for environment variables to specify
+               configuration files.
+
+Tue Sep 26 02:31:38 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * adm_conn.c (kadm_get_creds): zero out creds->server after
+       freeing it to protect later attempts.
+
+Fri Sep 29 17:06:18 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * logger.c: #ifdef the entire file so it's not built under Windows.
+
+Tue Sep 27 12:00:00 1995    <jrivlin@fusion.com>
+
+       * adm_kw_dec.c (krb5_adm_proto_to_dbent): Routine removed for 
+               Windows and Mac to match with the prototype.
+
+       * adm_kw_dec.c (krb5_adm_dbent_to_proto): Routine removed for 
+               Windows and Mac to match with the prototype.
+
+Tue Sep 26 16:24:00 1995    <tytso@rsts-11.mit.edu>
+
+       * alt_prof.c (krb5_read_realm_params): On an error, initialize the
+               returned rparams pointer to NULL.
+
+Mon Sep 25 16:54:18 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * Makefile.in: Removed "foo:: foo-$(WHAT)" lines from the
+               Makefile. 
+
+Fri Sep 22 12:00:00 1995  James Mattly  <mattly@fusion.com>
+
+       * adm_conn.c:  for sockets changed close to closesocket, sockets on
+               macintosh arn't files.
+
+Fri Sep 22 15:44:02 1995  Mark Eichin  <eichin@cygnus.com>
+
+       * logger.c (klog_com_err_proc): pass whoami in failure messages
+       for error log failures to match format string.
+
+Wed Sep 13 10:45:25 1995 Keith Vetter (keithv@fusion.com)
+
+       * keysalt.c: 16/32 bit integer mismatch.
+       * str_conv.c:   sftime_format_table is conditional upon HAVE_STRFTIME,
+               cast some constants to long so that math wouldn't overflow,
+               16/32 bit integer size mismatch.
+
+Wed Sep 13 18:17:30 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * alt_prof.c (krb5_read_realm_params): Fix memory leak.  Free the
+               default realm when we're done.  Remove the "profile"
+               parameter from the kdc.conf file.  This is bad idea,
+               architecturally.
+
+Tue Sep 12 13:18:42 1995  Ezra Peisach  <epeisach@kangaroo.mit.edu>
+
+       * adm_conn.c: For Macintosh hardwire cache name. Various casting
+               fixes. 
+
+Thu Sep  7 17:50:15 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * adm_conn.c (kadm_get_creds): Use KRB5_ADM_SERVICE_INSTANCE for
+               the instance name, instead of KRB5_ADM_SERVICE_NAME.
+
+Wed Sep 06 14:20:57 1995   Chris Provenzano (proven@mit.edu)
+
+        * adm_kt_dec.c, adm_kt_enc.c, alt_prof.c, keysalt.c, str_conv.c : 
+               s/keytype/enctype/g, s/KEYTYPE/ENCTYPE/g
+
+Tue Sep 05 22:10:34 1995   Chris Provenzano (proven@mit.edu)
+
+        * adm_kt_dec.c, adm_kt_enc.c, alt_prof.c, str_conv.c: 
+               Remove krb5_enctype references, and replace 
+               with krb5_keytype where appropriate
+
+Tue Aug 29 15:31:50 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * .Sanitize, krb5strings.M - Add new manpage describing string syntax
+               for common datatypes handled by str_conv.c.
+
+Thu Aug 24 18:53:32 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * .Sanitize: Update file list
+
+Mon Aug 21 17:07:56 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * str_conv.c - Add krb5_timestamp_to_sfstring().  This converts time
+               to a short string, potentially filled.  Use strftime() for
+               krb5_timestamp_to_string() if present so that locale-dependent
+               time format is used.
+
+
+Tue Aug 8 17:35:10 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * str_conv.c - Fix Purify complaint.
+
+
+Mon Aug 7 17:38:45 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * keysalt.c(krb5_string_to_keysalt) - Don't do the silly whitespace
+               filling logic.  If the string has imbedded whitespace, then
+               it's just tough rocks.  Save replaced string separators and
+               terminators so that they string looks the same coming out as
+               going in.
+
+
+Fri Aug 4 16:21:50 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * Makefile.in, .Sanitize, keysalt.c - Add keysalt.c modules.
+
+
+Thu Aug 3 11:51:14 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * alt_prof.c - Actually pass back the parsed string value in krb5_aprof_
+               get_deltat().
+
+
+Mon Jul 31 15:52:40 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * adm_kw_{enc,dec}.c - Add support for new kadmin protocol.
+       * alt_prof.c - Remove string conversion routine, use the ones in str_
+               conv.c.  Convert krb5_read_realm_params() to use these string
+               conversion routines so that the KDC profile is more textual
+               as opposed to being numeric.
+       * str_conv.c - String conversion routines.
+       * Makefile.in, configure.in - Add support for str_conv.c
+       * t_dbentry.c - Test new kadmin protocol.
+
+Mon Jul 17 15:16:26 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * alt_prof.c - Add krb5_{read,free}_realm_params() to read in per-
+               realm KDC data and to free the allocated structure.
+
+Mon Jul 10 17:59:23 1995  Ezra Peisach  <epeisach@kangaroo.mit.edu>
+
+       * adm_kt_dec.c, adm_kt_enc.c, adm_kw_dec.c, adm_kw_enc.c,
+               alt_prof.c, logger.c: Include adm_proto.h for prototypes.
+
+Fri Jul 7 16:23:57 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * Makefile.in - Remove LDFLAGS, it's set by configure.  Find com_err
+               library in TOPLIBD now.
+
+Thu Jul  6 17:34:22 1995  Tom Yu  <tlyu@lothlorien.MIT.EDU>
+
+       * adm_conn.c (kadm_get_creds): Pass kcontext to os_localaddr.
+
+Tue Jun 27 15:50:31 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * alt_prof.c - Change filename and hierarchy lists to be const char.
+
+
+Thu Jun 22 11:56:15 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * alt_prof.c - New jacket routines for handling profiles.  This includes
+               the ability to parse certain string values to appropriate types
+               (e.g. delta time or 32-bit integer).
+       * Makefile.in - Add alt_prof.c
+
+
+Thu Jun 15 18:03:40 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * Makefile.in - Remove explicit copying of archive library to library
+               directory.
+       * configure.in - Create symlink for archive when we build it.
+
+Wed Jun 14 14:36:13 1995  Sam Hartman  <hartmans@tardis.MIT.EDU>
+
+       * t_dbentry.c (main): option should be an int so that comparisons
+        against EOF work when char unsigned.
+
+Tue Jun 13 14:37:25 1995  Sam Hartman  <hartmans@tardis.MIT.EDU>
+
+       * logger.c: Don't check to make sure unix is defined for
+        DEVICE_OPEN et al.  Currently, there are no special cases where
+        special handling is required; if they are, then they should be
+        checked for, and the generic case taken if no special case fits.
+        The previous behavior broke under AIX.
+
+
+Sun Jun 11 09:24:06 1995  Ezra Peisach  <epeisach@kangaroo.mit.edu>
+
+       * Makefile.in (clean-unix): Remove $(UNIX_OBJS)
+
+Sat Jun 10 23:05:23 1995  Tom Yu  (tlyu@dragons-lair)
+
+       * adm_conn.c, adm_rw.c: krb5_auth_context redefinitions
+
+Fri Jun  9 19:26:44 1995    <tytso@rsx-11.mit.edu>
+
+       * configure.in: Remove standardized set of autoconf macros, which
+               are now handled by CONFIG_RULES.
+
+Thu Jun  8 23:32:28 1995  Theodore Y. Ts'o  <tytso@dcl>
+
+       * Makefile.in: $($(WHAT)_OBJS) is not accepted by all Makes!  We
+               assume for now that libkadm.a rule is only used by Unix,
+               which should be a valid assumption, and build handle
+               $(UNIX_OBJS) there.
+
+Thu Jun 8 14:32:33 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * logger.c - New module to provide profile-controlled logging.  This
+               can optionally take over control of com_err(3) output to also
+               direct this output to where the profile specifies.  Also
+               includes a syslog(3) compatible interface which also follows
+               the profile's direction.
+       * Makefile.in, configure.in - update for logger.c
+
+
+Mon Jun 5 14:15:37 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * adm_conn.c - Rework kadm_get_ccache() and kadm_get_creds() so that
+               we can specify differing credentials caches and the lifetime
+               of obtained tickets if not using an existing ccache.  This
+               changes the calling sequence of krb5_adm_connect(), adding
+               two arguments: a ccache name and a (delta) lifetime.  Default
+               ccache name changes formats to tkt_kadm_<pid> and default
+               lifetime is 10 minutes.
+
+Fri Jun 2 17:56:03 1995 Keith Vetter (keithv@fusion.com)
+
+       * adm_conn.c: used SOCKET_ERRNO instead of errno and 
+          added prototypes for the local functions.
+       * adm_rw.c: added prototypes for the local functions so that
+          ints get promoted to longs correctly on the PC.
+
+Wed May 31 08:10:13 1995  Ezra Peisach  <epeisach@kangaroo.mit.edu>
+
+       * Makefile.in (DB_OBJS): Change DBOBJS to DB_OBJS to match rest of
+               Makefile.in 
+
+Tue May 30 17:36:47 1995 Keith Vetter (keithv@fusion.com)
+
+       * adm_rw.c: removed INTERFACE from two routines.
+
+Tue May 30 10:35:07 1995 Keith Vetter (keithv@fusion.com)
+
+       * adm_conn.c: used Windows specific way of creating a temp file.
+       * Makefile.in: PC doesn't need to compile adm_kw_*.c files.
+
+Thu May 25 17:49:06 1995 Keith Vetter (keithv@fusion.com)
+
+       First pass to make the code compile cleanly on the PC.
+       * Makefile.in: made to work on the PC.
+       * adm_conn.c: used atoi instead of sscanf since it can't be
+          used in a windows DLL. Why not? Ask Microsoft.
+       * adm_kt_e.c, adm_kw_e.c, adm_rw.c: made the explicit the
+          cast to a char when pulling out bytes from an int.
+       * adm_kw_d.c: size_t != off_t on the PC. Fixed the equation.
+       * adm_rw.c: two parameters were declared as int but prototyped
+          as krb5_int32.
+
+Tue May 16 13:58:30 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * configure.in  - Check for srand48, srand and srandom along with
+                         network libraries.
+       * t_ktentry.c, t_dbentry.c - Use available random number generator
+                         and also make sure memory is freed so we don't
+                         chew up memory in tester.
+
+
+Tue May 16 13:19:04 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * t_dbentry.c   - Change isset to is_a_set to keep Ultrix happy.  Also
+                         correctly calculate the length of the standard pwd.
+                         so that we don't overwrite the end of the malloc()ed
+                         string.
+       * t_ktentry.c   - Remove isset logic, no differentiation for keytabs.
+
+
+Tue May 16 10:35:54 EDT 1995   Paul Park       (pjpark@mit.edu)
+       * t_dbentry.c, t_ktentry.c - new test modules for encode/decode
+               functions.  These convert to and from krb5_db_entry and
+               krb5_keytab_entry and verify contents.
+       * adm_kt_enc.c  - Fix bug encoding integer values.
+
+
+Tue May 9 15:21:49 EDT 1995    Paul Park       (pjpark@mit.edu)
+       * adm_conn.c    - use profile information to locate the admin_server.
+                         Also, return the correct value from krb5_adm_connect
+                         instead of always returning zero.
+       * adm_{kw,kt}_{enc,dec}.c - New modules to [en/de]code administrative
+                         protocol keyword=value pairs and keytab entries.
+
+
+Fri Apr 28 09:47:29 EDT 1995   Paul Park       (pjpark@mit.edu)
+
+       Create a new library consisting of functions which communicate with
+       the administrative server here.  These modules replace the originals
+       which used to be in libkrb5.
+
+       adm_rw.c        - Remove ntohl/htonl in favor of byte blasting.
+
diff --git a/src/lib/kadm/Makefile.in b/src/lib/kadm/Makefile.in
new file mode 100644 (file)
index 0000000..62cda2c
--- /dev/null
@@ -0,0 +1,92 @@
+CFLAGS = $(CCOPTS) $(DEFS) 
+
+##DOSBUILDTOP = ..\..
+##DOSLIBNAME=kadm.lib
+RUN_SETUP=@KRB5_RUN_ENV@
+BASE_OBJS= adm_conn.$(OBJEXT)  \
+       adm_kt_dec.$(OBJEXT)    \
+       adm_kt_enc.$(OBJEXT)    \
+       adm_rw.$(OBJEXT)        \
+       alt_prof.$(OBJEXT)      \
+       str_conv.$(OBJEXT)      \
+       keysalt.$(OBJEXT)
+
+UNIX_OBJS = logger.$(OBJEXT)
+
+DB_OBJS= adm_kw_dec.$(OBJEXT)  \
+       adm_kw_enc.$(OBJEXT)
+
+OBJS=  $(BASE_OBJS) $(DB_OBJS) 
+
+SRCS=  $(srcdir)/adm_conn.c    \
+       $(srcdir)/adm_kt_dec.c  \
+       $(srcdir)/adm_kt_enc.c  \
+       $(srcdir)/adm_rw.c      \
+       $(srcdir)/adm_kw_dec.c  \
+       $(srcdir)/adm_kw_enc.c  \
+       $(srcdir)/logger.c      \
+       $(srcdir)/alt_prof.c    \
+       $(srcdir)/str_conv.c    \
+       $(srcdir)/keysalt.c
+
+all:: $(BASE_OBJS) 
+
+all-unix:: $(DB_OBJS) $(UNIX_OBJS)
+all-unix:: libkadm.a
+all-mac:: $(DB_OBJS)
+all-windows::
+
+libkadm.a: $(OBJS) $(UNIX_OBJS)
+       $(RM) $@
+       $(ARADD) $@ $(OBJS) $(UNIX_OBJS)
+       $(RANLIB) $@
+
+install:: libkadm.a
+       $(INSTALL_DATA) libkadm.a $(DESTDIR)$(KRB5_LIBDIR)/libkadm.a
+       $(RANLIB) $(DESTDIR)$(KRB5_LIBDIR)/libkadm.a
+
+clean-unix::
+       $(RM) libkadm.$(LIBEXT) $(UNIX_OBJS)
+clean-mac::
+       $(RM) libkadm.$(LIBEXT)
+clean-windows::
+       $(RM) kadm.lib kadm.bak
+
+#
+# t_dbentry
+#
+T_DBENTRY_OBJS =  t_dbentry.$(OBJEXT) \
+               $(TOPLIBD)/libkadm.a $(TOPLIBD)/libkdb5.a \
+               $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a \
+               $(TOPLIBD)/libcom_err.a
+
+t_dbentry:     $(T_DBENTRY_OBJS)
+       $(LD) -o t_dbentry $(T_DBENTRY_OBJS) $(LIBS)
+
+#
+# t_ktentry
+#
+T_KTENTRY_OBJS =  t_ktentry.$(OBJEXT) \
+               $(TOPLIBD)/libkadm.a $(TOPLIBD)/libkrb5.a       \
+               $(TOPLIBD)/libcrypto.a $(TOPLIBD)/libcom_err.a
+
+t_ktentry:     $(T_KTENTRY_OBJS)
+       $(LD) -o t_ktentry $(T_KTENTRY_OBJS) $(LIBS)
+
+TEST_PROGS     = t_dbentry t_ktentry
+
+check-unix::   $(TEST_PROGS)
+       $(RUN_SETUP) ./t_dbentry -r 100
+       $(RUN_SETUP) ./t_ktentry -r 100
+
+check-mac::
+
+check-windows::
+
+clean-unix::
+       $(RM) t_dbentry$(EXEEXT) t_dbentry.$(OBJEXT)
+       $(RM) t_ktentry$(EXEEXT) t_ktentry.$(OBJEXT)
+clean-mac::
+       $(RM) t_dbentry$(EXEEXT) t_dbentry.$(OBJEXT)
+       $(RM) t_ktentry$(EXEEXT) t_ktentry.$(OBJEXT)
+
diff --git a/src/lib/kadm/adm_conn.c b/src/lib/kadm/adm_conn.c
new file mode 100644 (file)
index 0000000..1534b4b
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ * lib/kadm/adm_conn.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+/*
+ * Routines to contact an administrative protocol server.
+ */
+#define        NEED_SOCKETS
+#define        NEED_LOWLEVEL_IO
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+#if    HAVE_PWD_H
+#include <pwd.h>
+#endif /* HAVE_PWD_H */
+
+/* Default ticket life is 10 minutes */
+#define        KADM_DEFAULT_LIFETIME   (10*60)
+
+/*
+ * Strings
+ */
+static char *kadm_cache_name_fmt =     "FILE:/tmp/tkt_kadm_%d";
+
+/*
+ * Prototypes for local functions
+ */
+static krb5_error_code kadm_get_ccache
+       PROTOTYPE((krb5_context,
+                  char *,
+                  char *,
+                  krb5_ccache *,
+                  krb5_principal *));
+static krb5_error_code kadm_get_creds
+       PROTOTYPE((krb5_context,
+               krb5_ccache ,
+               krb5_principal,
+               krb5_creds  *,
+               char *,
+               char *,
+               krb5_timestamp));
+static krb5_error_code kadm_contact_server
+       PROTOTYPE((krb5_context,
+               krb5_data *,
+               int *,
+               krb5_address **,
+               krb5_address **));
+static krb5_error_code kadm_get_auth
+       PROTOTYPE((krb5_context,
+               krb5_auth_context *,
+               krb5_address *,
+               krb5_address *));
+\f
+/*
+ * kadm_get_ccache()   - Initialze a credentials cache.
+ * 
+ * Cleanup after success by calling krb5_cc_destroy() and krb5_free_principal()
+ * Allocates new ccache and client.
+ */
+static krb5_error_code
+kadm_get_ccache(kcontext, user, ccname, ccache, client)
+    krb5_context       kcontext;
+    char               *user;
+    char               *ccname;
+    krb5_ccache                *ccache;
+    krb5_principal     *client;
+{
+    krb5_error_code    kret;
+    char               *name;
+    int                did_malloc = 0;
+    char               new_cache[MAXPATHLEN];
+    krb5_principal     tprinc;
+
+    /* Initialize. */
+    *client = (krb5_principal) NULL;
+    tprinc = (krb5_principal) NULL;
+
+    /*
+     * If a name specified, then use that one, else get it from our
+     * current uid.
+     */
+    if (user) {
+       name = user;
+    }
+    else {
+#if    HAVE_PWD_H
+       struct passwd *pw;
+
+       pw = getpwuid(getuid());
+       if (pw) {
+           name = (char *) malloc(strlen(pw->pw_name)+1);
+           did_malloc = 1;
+           strcpy(name, pw->pw_name);
+       }
+       else {
+           kret = errno;
+           goto cleanup;
+       }
+#else  /* HAVE_PWD_H */
+       kret = ENOENT;
+       goto cleanup;
+#endif /* HAVE_PWD_H */
+    }
+
+    /* Parse the name and form our principal */
+    if (kret = krb5_parse_name(kcontext, name, client))
+       goto cleanup;
+
+    if (!ccname) {
+#if defined(_MSDOS) || defined(_WIN32)
+       strcpy (new_cache, "FILE:");
+       GetTempFileName (0, "tkt", 0, new_cache+5);
+#else
+#ifdef _MACINTOSH
+       (void) sprintf(new_cache, "STDIO:admcc");
+#else
+       (void) sprintf(new_cache, kadm_cache_name_fmt, getpid());
+#endif /* _MACINTOSH */
+#endif /* _MSDOS || _WIN32 */
+    }
+    else
+       sprintf(new_cache, "FILE:%s", ccname);
+
+    /*
+     * We only need to resolve the credentials cache if one hasn't
+     * been supplied to us.
+     */
+    if (!(*ccache) && (kret = krb5_cc_resolve(kcontext, new_cache, ccache)))
+       goto cleanup;
+
+    /* XXX assumes a file ccache */
+    if ((kret = krb5_cc_get_principal(kcontext, *ccache, &tprinc)) ==
+       KRB5_FCC_NOFILE)
+       kret = krb5_cc_initialize(kcontext, *ccache, *client);
+
+
+ cleanup:
+    if (did_malloc)
+       free(name);
+
+    if (tprinc)
+       krb5_free_principal(kcontext, tprinc);
+
+    if (kret) {
+       if (*client)
+           krb5_free_principal(kcontext, *client);
+    }
+
+    return(kret);
+}
+\f
+/*
+ * kadm_get_creds()    - Get initial credentials.
+ *
+ * Cleanup after success by calling krb5_free_principal().
+ * Allocates new principal for creds->server.
+ */
+static krb5_error_code
+kadm_get_creds(kcontext, ccache, client, creds, prompt, oldpw, tlife)
+    krb5_context       kcontext;
+    krb5_ccache                ccache;
+    krb5_principal     client;
+    krb5_creds         *creds;
+    char               *prompt;
+    char               *oldpw;
+    krb5_timestamp     tlife;
+{
+    char               *client_name;
+    krb5_error_code    kret;
+    krb5_address       **my_addresses;
+    int                        old_pwsize;
+    krb5_creds         tcreds;
+
+    /* Initialize */
+    my_addresses = (krb5_address **) NULL;
+    client_name = (char *) NULL;
+
+    /* Get the string form for our principal */
+    if (kret = krb5_unparse_name(kcontext, client, &client_name))
+       return(kret);
+
+    if (kret = krb5_os_localaddr(kcontext, &my_addresses))
+       goto cleanup;
+
+    creds->client = client;
+    /*
+     * Build server principal name:
+     * "changepw" is service
+     * realm name is instance
+     * realm name is realm name
+     */
+    if (kret = krb5_build_principal_ext(kcontext,
+                                       &creds->server,
+                                       client->realm.length,
+                                       client->realm.data,
+                                       strlen(KRB5_ADM_SERVICE_INSTANCE),
+                                       KRB5_ADM_SERVICE_INSTANCE,
+                                       client->realm.length,
+                                       client->realm.data,
+                                       0))
+       goto cleanup;
+
+    /* Attempt to retrieve an appropriate entry from the credentials cache. */
+    if ((kret = krb5_cc_retrieve_cred(kcontext,
+                                     ccache,
+                                     KRB5_TC_MATCH_SRV_NAMEONLY,
+                                     creds,
+                                     &tcreds))
+       == KRB5_CC_NOTFOUND) {
+       krb5_timestamp  jetzt;
+
+       if (prompt != (char *) NULL) {
+           /* Read the password */
+           old_pwsize = KRB5_ADM_MAX_PASSWORD_LEN;
+           if (kret = krb5_read_password(kcontext,
+                                         prompt,
+                                         (char *) NULL,
+                                         oldpw,
+                                         &old_pwsize))
+               goto cleanup;
+       }
+       if (kret = krb5_timeofday(kcontext, &jetzt))
+           goto cleanup;
+       if (tlife > 0)
+           creds->times.endtime = jetzt + tlife;
+       else
+           creds->times.endtime = jetzt + KADM_DEFAULT_LIFETIME;
+
+       /* Get our initial ticket */
+       kret = krb5_get_in_tkt_with_password(kcontext,
+                                            0,
+                                            my_addresses,
+                                            NULL,
+                                            NULL,
+                                            oldpw,
+                                            ccache,
+                                            creds,
+                                            0);
+    }
+    else {
+       krb5_principal sclient, sserver;
+
+       if (!kret) {
+           /*
+            * We found the credentials cache entry - copy it out.
+            *
+            * We'd like to just blast tcreds on top of creds, but we cannot.
+            * other logic uses the client data, and rather than going and
+            * chasing all that logic down, might as well pretend that we just
+            * filled in all the other muck.
+            */
+           sclient = creds->client;
+           sserver = creds->server;
+           memcpy((char *) creds, (char *) &tcreds, sizeof(tcreds));
+           if (creds->client)
+               krb5_free_principal(kcontext, creds->client);
+           if (creds->server)
+               krb5_free_principal(kcontext, creds->server);
+           creds->client = sclient;
+           creds->server = sserver;
+       }
+    }
+
+ cleanup:
+    if (kret) {
+       if (creds->server) {
+           krb5_free_principal(kcontext, creds->server);
+           creds->server = 0;
+       }
+    }
+    if (my_addresses)
+       krb5_free_addresses(kcontext, my_addresses);
+    if (client_name)
+       krb5_xfree(client_name);
+    return(kret);
+}
+\f
+/*
+ * kadm_contact_server()       - Establish a connection to the server.
+ *
+ * Cleanup after success by calling close() and free().
+ * Opens/connects socket *sockp.  Allocates address storage for local/remote.
+ */
+static krb5_error_code
+kadm_contact_server(kcontext, realmp, sockp, local, remote)
+    krb5_context       kcontext;
+    krb5_data          *realmp;
+    int                        *sockp;
+    krb5_address       **local;
+    krb5_address       **remote;
+{
+    struct hostent     *remote_host;
+    struct servent     *service;
+    char               **hostlist;
+    int                        i, count;
+
+    krb5_error_code    kret;
+
+    struct sockaddr_in in_local;
+    struct sockaddr_in in_remote;
+    int                        addr_len;
+
+    const char         *realm_admin_names[4];
+    char               *realm_name;
+    krb5_boolean       found;
+
+    /* Initialize */
+    hostlist = (char **) NULL;
+    *sockp = -1;
+    realm_name = (char *) NULL;
+
+    /*
+     * XXX - only know ADDRTYPE_INET.
+     */
+#ifdef KRB5_USE_INET
+    *local = (krb5_address *) malloc(sizeof(krb5_address));
+    *remote = (krb5_address *) malloc(sizeof(krb5_address));
+    realm_name = (char *) malloc((size_t) realmp->length + 1);
+    if ((*local == (krb5_address *) NULL) ||
+       (*remote == (krb5_address *) NULL) ||
+       (realm_name == (char *) NULL)) {
+       kret = ENOMEM;
+       goto cleanup;
+    }
+    (*local)->addrtype = (*remote)->addrtype = ADDRTYPE_INET;
+    (*local)->length = (*remote)->length = sizeof(struct in_addr);
+    (*local)->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    (*remote)->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    if (((*local)->contents == NULL) || ((*remote)->contents == NULL)) {
+       kret = ENOMEM;
+       goto cleanup;
+    }
+
+    /*
+     * First attempt to find addresses from our config file, if we cannot
+     * find an entry, then try getservbyname().
+     */
+    found = 0;
+#ifndef        OLD_CONFIG_FILES
+    strncpy(realm_name, realmp->data, (size_t) realmp->length);
+    realm_name[realmp->length] = '\0';
+    realm_admin_names[0] = "realms";
+    realm_admin_names[1] = realm_name;
+    realm_admin_names[2] = "admin_server";
+    realm_admin_names[3] = (char *) NULL;
+    if (!(kret = profile_get_values(kcontext->profile,
+                                   realm_admin_names,
+                                   &hostlist))) {
+       int             hi;
+       char            *cport;
+       char            *cp;
+       krb5_int32      pport;
+
+       for (hi = 0; hostlist[hi]; hi++) {
+           /*
+            * This knows a little too much about the format of profile
+            * entries.  Shouldn't it just be some sort of tuple?
+            *
+            * The form is assumed to be:
+            *  admin_server = <hostname>[:<portname>[<whitespace>]]
+            */
+           cport = (char *) NULL;
+           pport = (u_short) KRB5_ADM_DEFAULT_PORT;
+           cp = strchr(hostlist[hi], ' ');
+           if (cp)
+               *cp = '\0';
+           cp = strchr(hostlist[hi], '\t');
+           if (cp)
+               *cp = '\0';
+           cport = strchr(hostlist[hi], ':');
+           if (cport) {
+               *cport = '\0';
+               cport++;
+
+               pport = atoi (cport);
+               if (pport == 0) {
+                   kret = KRB5_CONFIG_BADFORMAT;
+                   goto cleanup;
+               }
+           }
+
+           /*
+            * Now that we have a host name, get the host entry.
+            */
+           remote_host = gethostbyname(hostlist[hi]);
+           if (remote_host == (struct hostent *) NULL) {
+               kret = KRB5_CONFIG_BADFORMAT;
+               goto cleanup;
+           }
+
+           /*
+            * Fill in our address values.
+            */
+           in_remote.sin_family = remote_host->h_addrtype;
+           (void) memcpy((char *) &in_remote.sin_addr,
+                         (char *) remote_host->h_addr,
+                         sizeof(in_remote.sin_addr));
+           in_remote.sin_port = htons((u_short) pport);
+
+           /* Open a tcp socket */
+           *sockp = (int) socket(PF_INET, SOCK_STREAM, 0);
+           if (*sockp < 0) {
+               kret = SOCKET_ERRNO;
+               goto cleanup;
+           }
+           else kret = 0;
+
+           /* Attempt to connect to the remote address. */
+           if (connect(*sockp,
+                       (struct sockaddr *) &in_remote,
+                       sizeof(in_remote)) < 0) {
+               /* Failed, go to next address */
+               kret = SOCKET_ERRNO;
+               closesocket((SOCKET)*sockp);
+               *sockp = -1;
+               continue;
+           }
+
+           /* Find out local address */
+           addr_len = sizeof(in_local);
+           if (getsockname((SOCKET) *sockp,
+                           (struct sockaddr *) &in_local,
+                           &addr_len) < 0) {
+               /* Couldn't get our local address? */
+               kret = SOCKET_ERRNO;
+               goto cleanup;
+           }
+           else {
+               /* Connection established. */
+               memcpy((char *) (*remote)->contents,
+                      (char *) &in_remote.sin_addr,
+                      sizeof(struct in_addr));
+               memcpy((char *) (*local)->contents,
+                      (char *) &in_local.sin_addr,
+                      sizeof(struct in_addr));
+               found = 1;
+               break;
+           }
+       }
+       if (!found) {
+           krb5_xfree(hostlist);
+           hostlist = (char **) NULL;
+       }
+    }
+#endif /* OLD_CONFIG_FILES */
+    if (!found) {
+       /*
+        * Use the old way of finding our administrative server.
+        *
+        * This consists of looking up an entry in /etc/services and if
+        * we don't find it, then we are just out of luck.  Then, we use
+        * that port number along with the address of the kdc.
+        */
+       if ((service = getservbyname(KRB5_ADM_SERVICE_NAME, "tcp")) == NULL) {
+           kret = ENOENT;
+           goto cleanup;
+       }
+       in_remote.sin_port = service->s_port;
+       
+       if (kret = krb5_get_krbhst(kcontext, realmp, &hostlist))
+           goto cleanup;
+       
+       /* Now count the number of hosts in the realm */
+       count = 0;
+       for (i=0; hostlist[i]; i++)
+           count++;
+       if (count == 0) {
+           kret = ENOENT;      /* something better? */
+           goto cleanup;
+       }
+       
+       /* Now find an available host */
+       for (i=0; hostlist[i]; i++) {
+           remote_host = gethostbyname(hostlist[i]);
+           if (remote_host != (struct hostent *) NULL) {
+               in_remote.sin_family = remote_host->h_addrtype;
+               (void) memcpy((char *) &in_remote.sin_addr,
+                             (char *) remote_host->h_addr,
+                             sizeof(in_remote.sin_addr));
+       
+               /* Open a tcp socket */
+               *sockp = (int) socket(PF_INET, SOCK_STREAM, 0);
+               if (*sockp < 0) {
+                   kret = SOCKET_ERRNO;
+                   goto cleanup;
+               }
+               else kret = 0;
+       
+               if (connect(*sockp,
+                           (struct sockaddr *) &in_remote,
+                           sizeof(in_remote)) < 0) {
+                   kret = SOCKET_ERRNO;
+                   closesocket((SOCKET)*sockp);
+                   *sockp = -1;
+                   continue;
+               }
+
+               /* Find out local address */
+               addr_len = sizeof(in_local);
+               if (getsockname((SOCKET)*sockp,
+                               (struct sockaddr *) &in_local,
+                               &addr_len) < 0) {
+                   kret = SOCKET_ERRNO;
+                   goto cleanup;
+               }
+               else {
+                   memcpy((char *) (*remote)->contents,
+                          (char *) &in_remote.sin_addr,
+                          sizeof(struct in_addr));
+       
+                   memcpy((char *) (*local)->contents,
+                          (char *) &in_local.sin_addr,
+                          sizeof(struct in_addr));
+                   found = 1;
+                   break;
+               }
+           }
+       }
+       if (!found)
+           kret = KRB5_SERVICE_UNKNOWN;
+    }
+#else  /* KRB5_USE_INET */
+    kret = ENOENT;
+#endif /* KRB5_USE_INET */
+
+ cleanup:
+    if (kret) {
+       if (*sockp >= 0)
+           closesocket((SOCKET)*sockp);
+       if (*local && (*local)->contents)
+           free((*local)->contents);
+       if (*remote && (*remote)->contents)
+           free((*remote)->contents);
+       if (*local) {
+           memset((char *) (*local), 0, sizeof(krb5_address));
+           free(*local);
+           *local = (krb5_address *) NULL;
+       }
+       if (*remote) {
+           memset((char *) (*remote), 0, sizeof(krb5_address));
+           free(*remote);
+           *remote = (krb5_address *) NULL;
+       }
+    }
+    if (realm_name)
+       free(realm_name);
+    if (hostlist)
+       krb5_xfree(hostlist);
+    return(kret);
+}
+\f
+/*
+ * kadm_get_auth()     - Get authorization context.
+ *
+ * Cleanup after success by calling krb5_xfree().
+ * New krb5_auth_context allocated in *ctxp
+ */
+static krb5_error_code
+kadm_get_auth(kcontext, ctxp, local, remote)
+    krb5_context       kcontext;
+    krb5_auth_context  *ctxp;
+    krb5_address       *local;
+    krb5_address       *remote;
+{
+    krb5_auth_con_init(kcontext, ctxp);
+    krb5_auth_con_setflags(kcontext, *ctxp, 
+                          KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                          KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+    krb5_auth_con_setaddrs(kcontext, *ctxp, local, remote);
+    return(0);
+}
+\f
+/*
+ * krb5_adm_connect()  - Establish the connection to the service.
+ *
+ * If *ccachep is not null, then that ccache is used to establish the identity
+ * of the caller.  (Argument list is ugly, I know)
+ *
+ * Errors are not reported by this routine.
+ * Cleanup after successful invocation must:
+ *     destroy/close ccache.
+ *     free auth_context
+ *     close socket.
+ */
+krb5_error_code INTERFACE
+krb5_adm_connect(kcontext, user, prompt, opassword, sockp, ctxp,
+                ccachep, ccname, tlife)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    char               *user;          /* User specified       (In ) */
+    char               *prompt;        /* Old password prompt  (In ) */
+    char               *opassword;     /* Old Password         (I/O) */
+    int                        *sockp;         /* Socket for conn.     (Out) */
+    krb5_auth_context  *ctxp;          /* Auth context         (Out) */
+    krb5_ccache                *ccachep;       /* Credentials cache    (I/O) */
+    char               *ccname;        /* Cred cache name      (In ) */
+    krb5_timestamp     tlife;          /* Ticket lifetime      (In ) */
+{
+    krb5_error_code    kret;
+    krb5_principal     client;
+    krb5_creds         creds;
+    krb5_data          server_realm;
+    krb5_data          request_data, suppl_data;
+    krb5_data          response_data;
+    krb5_address       *local_addr;
+    krb5_address       *remote_addr;
+    krb5_boolean       ccache_supplied;
+
+    char               *server;
+
+    /* Initialize */
+    memset((char *) &creds, 0, sizeof(krb5_creds));
+    server = (char *) NULL;
+    *sockp = -1;
+    local_addr = remote_addr = (krb5_address *) NULL;
+    client = (krb5_principal) NULL;
+    *ctxp = (krb5_auth_context) NULL;
+    ccache_supplied = (*ccachep != (krb5_ccache) NULL);
+
+    /*
+     * Find the appropriate credentials cache and set up our identity.
+     */
+    if (kret = kadm_get_ccache(kcontext, user, ccname, ccachep, &client))
+       goto cleanup;
+
+    /*
+     * Get initial credentials.
+     */
+    if (kret = kadm_get_creds(kcontext,
+                             *ccachep,
+                             client,
+                             &creds,
+                             prompt,
+                             opassword,
+                             tlife))
+       goto cleanup;
+
+    /*
+     * Establish connection to server.
+     */
+    if ((server_realm.data = (char *) malloc(client->realm.length+1)) ==
+       (char *) NULL)
+       goto cleanup;
+
+    server_realm.length = client->realm.length;
+    memcpy(server_realm.data, client->realm.data, server_realm.length);
+    server_realm.data[server_realm.length] = '\0';
+    if (kret = kadm_contact_server(kcontext,
+                                  &server_realm,
+                                  sockp,
+                                  &local_addr,
+                                  &remote_addr))
+       goto cleanup;
+
+    /*
+     * Obtain our authorization context
+     */
+    if (kret = kadm_get_auth(kcontext, ctxp, local_addr, remote_addr))
+       goto cleanup;
+
+    /*
+     * Format, then send the KRB_AP_REQ
+     */
+    suppl_data.data = NULL;
+    suppl_data.length = 0;
+    if (kret = krb5_mk_req_extended(kcontext,
+                                   ctxp,
+                                   AP_OPTS_MUTUAL_REQUIRED,
+                                   &suppl_data,
+                                   &creds,
+                                   &request_data))
+       goto cleanup;
+
+    if (kret = krb5_write_message(kcontext, sockp, &request_data))
+       goto cleanup;
+
+    /*
+     * Now read back the response.
+     */
+    if (kret = krb5_read_message(kcontext, sockp, &response_data)) {
+       goto cleanup;
+    }
+    else {
+       krb5_ap_rep_enc_part    *reply = NULL;
+
+       kret = krb5_rd_rep(kcontext, *ctxp, &response_data, &reply);
+       if (reply)
+           krb5_free_ap_rep_enc_part(kcontext, reply);
+    }
+ cleanup:
+    if (server)
+       free(server);
+    if (kret) {
+       if (*ctxp) {
+           krb5_xfree(*ctxp);
+           *ctxp = (krb5_auth_context) NULL;
+       }
+       if (*sockp >= 0) {
+           closesocket((SOCKET)*sockp);
+           *sockp = -1;
+       }
+       if (local_addr && local_addr->contents)
+           free(local_addr->contents);
+       if (remote_addr && remote_addr->contents)
+           free(remote_addr->contents);
+       if (local_addr)
+           free(local_addr);
+       if (remote_addr)
+           free(remote_addr);
+       if (creds.server)
+           krb5_free_principal(kcontext, creds.server);
+       if (client)
+           krb5_free_principal(kcontext, client);
+       if (*ccachep && !ccache_supplied) {
+           krb5_cc_destroy(kcontext, *ccachep);
+           *ccachep = (krb5_ccache) NULL;
+       }
+    }
+    return(kret);
+
+}
+\f
+/*
+ * krb5_adm_disconnect()       - Disconnect from the administrative service.
+ *
+ * If ccache is supplied, then it is destroyed.  Otherwise, the ccache is
+ * the caller's responsibility to close.
+ */
+void INTERFACE
+krb5_adm_disconnect(kcontext, socketp, auth_context, ccache)
+    krb5_context       kcontext;
+    int                        *socketp;
+    krb5_auth_context  auth_context;
+    krb5_ccache                ccache;
+{
+    if (ccache)
+       krb5_cc_destroy(kcontext, ccache);
+    if (auth_context)
+       krb5_xfree(auth_context);
+    if (*socketp >= 0)
+       closesocket((SOCKET)*socketp);
+}
+
diff --git a/src/lib/kadm/adm_kt_dec.c b/src/lib/kadm/adm_kt_dec.c
new file mode 100644 (file)
index 0000000..7886a70
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * lib/kadm/adm_kt_dec.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * adm_kt_dec.c        - Decode keytab entry according to protocol.
+ */
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+\f
+/*
+ * krb5_adm_proto_to_ktent()   - Convert a list of reply components to
+ *                               a keytab entry according to procotol.
+ *
+ * Successful callers of this routine should free ktentp->principal
+ * and ktentp->key.contents.
+ */
+krb5_error_code
+krb5_adm_proto_to_ktent(kcontext, ncomp, complist, ktentp)
+    krb5_context       kcontext;
+    krb5_int32         ncomp;
+    krb5_data          *complist;
+    krb5_keytab_entry  *ktentp;
+{
+    krb5_error_code    kret;
+    char               *v;
+
+    /*
+     * Clear out the keytab entry.
+     */
+    memset((char *) ktentp, 0, sizeof(krb5_keytab_entry));
+
+    /*
+     * Figure out how many components we have.  We expect KRB5_ADM_KT_NCOMPS
+     * components.
+     */
+    if (ncomp != KRB5_ADM_KT_NCOMPS)
+       return(EINVAL);
+
+    /* Parse the supplied principal name */
+    if (kret = krb5_parse_name(kcontext,
+                              complist[KRB5_ADM_KT_PRINCIPAL].data,
+                              &ktentp->principal))
+       goto done;
+
+    /* Parse the supplied timestamp */
+    if (complist[KRB5_ADM_KT_TIMESTAMP].length < sizeof(krb5_timestamp)) {
+       kret = EINVAL;
+       goto done;
+    }
+    v = complist[KRB5_ADM_KT_TIMESTAMP].data;
+    ktentp->timestamp = (krb5_timestamp)
+       (((krb5_int32) ((unsigned char) v[0]) << 24) +
+        ((krb5_int32) ((unsigned char) v[1]) << 16) +
+        ((krb5_int32) ((unsigned char) v[2]) << 8) +
+        ((krb5_int32) ((unsigned char) v[3])));
+
+    /* Parse the supplied vno */
+    if (complist[KRB5_ADM_KT_VNO].length < sizeof(krb5_kvno)) {
+       kret = EINVAL;
+       goto done;
+    }
+    v = complist[KRB5_ADM_KT_VNO].data;
+    ktentp->vno = (krb5_kvno)
+       (((krb5_int32) ((unsigned char) v[0]) << 24) +
+        ((krb5_int32) ((unsigned char) v[1]) << 16) +
+        ((krb5_int32) ((unsigned char) v[2]) << 8) +
+        ((krb5_int32) ((unsigned char) v[3])));
+
+    /* Parse the supplied key_enctype */
+    if (complist[KRB5_ADM_KT_KEY_ENCTYPE].length < sizeof(krb5_enctype)) {
+       kret = EINVAL;
+       goto done;
+    }
+    v = complist[KRB5_ADM_KT_KEY_ENCTYPE].data;
+    ktentp->key.enctype = (krb5_enctype)
+       (((krb5_int32) ((unsigned char) v[0]) << 24) +
+        ((krb5_int32) ((unsigned char) v[1]) << 16) +
+        ((krb5_int32) ((unsigned char) v[2]) << 8) +
+        ((krb5_int32) ((unsigned char) v[3])));
+
+    /* Finally, shuck the key contents */
+    if (ktentp->key.contents = (krb5_octet *)
+       malloc((size_t) complist[KRB5_ADM_KT_KEY_KEY].length)) {
+       ktentp->key.length = complist[KRB5_ADM_KT_KEY_KEY].length;
+       memcpy(ktentp->key.contents,
+              complist[KRB5_ADM_KT_KEY_KEY].data,
+              (size_t) ktentp->key.length);
+       kret = 0;
+    }
+    else
+       kret = ENOMEM;
+       
+ done:
+    if (kret) {
+       if (ktentp->principal)
+           krb5_free_principal(kcontext, ktentp->principal);
+       if (ktentp->key.contents) {
+           memset((char *) ktentp->key.contents, 0,
+                  (size_t) ktentp->key.length);
+           krb5_xfree(ktentp->key.contents);
+       }
+       memset((char *) ktentp, 0, sizeof(krb5_keytab_entry));
+    }
+    return(kret);
+}
diff --git a/src/lib/kadm/adm_kt_enc.c b/src/lib/kadm/adm_kt_enc.c
new file mode 100644 (file)
index 0000000..d7dc65a
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * lib/kadm/adm_kt_enc.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * adm_kt_enc.c        - Encode keytab entry according to protocol.
+ */
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+\f
+/*
+ * krb5_adm_ktent_to_proto()   - Convert a keytab entry into an external
+ *                               list of reply components.
+ *
+ * Successful callers must free the storage for complistp and complistp->data
+ * either manually or by using krb5_free_adm_data().
+ */
+krb5_error_code
+krb5_adm_ktent_to_proto(kcontext, ktentp, ncompp, complistp)
+    krb5_context       kcontext;
+    krb5_keytab_entry  *ktentp;
+    krb5_int32         *ncompp;
+    krb5_data          **complistp;
+{
+    krb5_error_code    kret;
+    krb5_data          *clist;
+    krb5_int32         nents;
+
+    kret = ENOMEM;
+    nents = 0;
+    if (clist = (krb5_data *) malloc((size_t) KRB5_ADM_KT_NCOMPS *
+                                    sizeof(krb5_data))) {
+       memset((char *) clist, 0, ((size_t) KRB5_ADM_KT_NCOMPS *
+                                  sizeof(krb5_data)));
+       /*
+        * Fill in the principal field.
+        */
+       if (kret = krb5_unparse_name(kcontext,
+                                    ktentp->principal,
+                                    &clist[KRB5_ADM_KT_PRINCIPAL].data))
+           goto done;
+       clist[KRB5_ADM_KT_PRINCIPAL].length =
+           strlen(clist[KRB5_ADM_KT_PRINCIPAL].data);
+       nents++;
+
+       /*
+        * Fill in timestamp.
+        */
+       if (kret = krb5_timeofday(kcontext, &ktentp->timestamp))
+           goto done;
+       if (clist[KRB5_ADM_KT_TIMESTAMP].data = 
+           (char *) malloc(sizeof(krb5_ui_4))) {
+           clist[KRB5_ADM_KT_TIMESTAMP].length = sizeof(krb5_ui_4);
+           clist[KRB5_ADM_KT_TIMESTAMP].data[0] =
+               (char) ((ktentp->timestamp >> 24) & 0xff);
+           clist[KRB5_ADM_KT_TIMESTAMP].data[1] =
+               (char) ((ktentp->timestamp >> 16) & 0xff);
+           clist[KRB5_ADM_KT_TIMESTAMP].data[2] =
+               (char) ((ktentp->timestamp >> 8) & 0xff);
+           clist[KRB5_ADM_KT_TIMESTAMP].data[3] =
+               (char) (ktentp->timestamp & 0xff);
+           nents++;
+       }
+       else {
+           kret = ENOMEM;
+           goto done;
+       }
+
+       /*
+        * Fill in vno.
+        */
+       if (clist[KRB5_ADM_KT_VNO].data = 
+           (char *) malloc(sizeof(krb5_ui_4))) {
+           clist[KRB5_ADM_KT_VNO].length = sizeof(krb5_ui_4);
+           clist[KRB5_ADM_KT_VNO].data[0] = (ktentp->vno >> 24) & 0xff;
+           clist[KRB5_ADM_KT_VNO].data[1] = (ktentp->vno >> 16) & 0xff;
+           clist[KRB5_ADM_KT_VNO].data[2] = (ktentp->vno >> 8) & 0xff;
+           clist[KRB5_ADM_KT_VNO].data[3] = ktentp->vno & 0xff;
+           nents++;
+       }
+       else {
+           kret = ENOMEM;
+           goto done;
+       }
+
+       /*
+        * Fill in key_enctype.
+        */
+       if (clist[KRB5_ADM_KT_KEY_ENCTYPE].data = 
+           (char *) malloc(sizeof(krb5_ui_4))) {
+           clist[KRB5_ADM_KT_KEY_ENCTYPE].length = sizeof(krb5_ui_4);
+           clist[KRB5_ADM_KT_KEY_ENCTYPE].data[0] =
+               (ktentp->key.enctype >> 24) & 0xff;
+           clist[KRB5_ADM_KT_KEY_ENCTYPE].data[1] =
+               (ktentp->key.enctype >> 16) & 0xff;
+           clist[KRB5_ADM_KT_KEY_ENCTYPE].data[2] =
+               (ktentp->key.enctype >> 8) & 0xff;
+           clist[KRB5_ADM_KT_KEY_ENCTYPE].data[3] =
+               ktentp->key.enctype & 0xff;
+           nents++;
+       }
+       else {
+           kret = ENOMEM;
+           goto done;
+       }
+
+       /*
+        * Fill in key_key.
+        */
+       if (clist[KRB5_ADM_KT_KEY_KEY].data = 
+           (char *) malloc((size_t) ktentp->key.length)) {
+           memcpy(clist[KRB5_ADM_KT_KEY_KEY].data,
+                  ktentp->key.contents,
+                  (size_t) ktentp->key.length);
+           clist[KRB5_ADM_KT_KEY_KEY].length = ktentp->key.length;
+           nents++;
+           kret = 0;
+       }
+       else
+           kret = ENOMEM;
+    }
+ done:
+    if (kret) {
+       if (clist) {
+           int i;
+           for (i=0; i<KRB5_ADM_KT_NCOMPS; i++) {
+               if (clist[i].data) {
+                   memset(clist[i].data, 0, (size_t) clist[i].length);
+                   krb5_xfree(clist[i].data);
+               }
+           }
+           krb5_xfree(clist);
+       }
+       clist = (krb5_data *) NULL;
+       nents = 0;
+    }
+    *complistp = clist;
+    *ncompp = nents;
+    return(kret);
+}
diff --git a/src/lib/kadm/adm_kw_dec.c b/src/lib/kadm/adm_kw_dec.c
new file mode 100644 (file)
index 0000000..3509738
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+ * lib/kadm/adm_kw_dec.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * adm_kw_dec.c        - routines to decode keyword-value pairs.
+ */
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+#define        char2int(c)     ((c) - '0')
+\f
+/*
+ * keyword_value()     - Find index of keyword value if keyword is present.
+ *
+ * If a value is required, then the index of the keyword value is returned,
+ * otherwise the index of the first character past the end of the keyword
+ * string is returned.
+ */
+static off_t
+keyword_value(dataentp, keyword, value_req)
+    krb5_data          *dataentp;
+    char               *keyword;
+    krb5_boolean       value_req;
+{
+    off_t      len_req;
+
+    len_req = strlen(keyword);
+    if (value_req)
+       len_req++;
+    if ((dataentp->length >= len_req) &&
+       (!strncmp(dataentp->data, keyword, strlen(keyword))) &&
+       (!value_req || (dataentp->data[strlen(keyword)] == '=')))
+       return(len_req);
+    else
+       return(-1);
+}
+\f
+/*
+ * decode_kw_string()  - Decode a keyword=<string> pair and return the
+ *                       string value if the pair is present.
+ *
+ * Note: successful callers must free the string storage.
+ */
+static krb5_error_code
+decode_kw_string(dataentp, keyword, stringp)
+    krb5_data  *dataentp;
+    char       *keyword;
+    char       **stringp;
+{
+    krb5_error_code    kret;
+    off_t              valueoff;
+    size_t             len2copy;
+
+    kret = ENOENT;
+    if ((valueoff = keyword_value(dataentp, keyword, 1)) >= 0) {
+       kret = ENOMEM;
+       len2copy = (size_t) ((off_t) dataentp->length - valueoff);
+       *stringp = (char *) malloc(len2copy+1);
+       if (*stringp) {
+           strncpy(*stringp, &dataentp->data[valueoff], len2copy);
+           (*stringp)[len2copy] = '\0';
+           kret = 0;
+       }
+    }
+    return(kret);
+}
+\f
+/*
+ * decode_kw_integer() - Decode a keyword=<integer> pair and return the value
+ *                       if the pair is present.
+ */
+static krb5_error_code
+decode_kw_integer(dataentp, keyword, uintp)
+    krb5_data  *dataentp;
+    char       *keyword;
+    krb5_ui_4  *uintp;
+{
+    krb5_error_code    kret;
+    off_t              voff;
+    size_t             len2copy;
+
+    kret = ENOENT;
+    if ((voff = keyword_value(dataentp, keyword, 1)) >= 0) {
+       kret = EINVAL;
+       len2copy = (size_t) ((off_t) dataentp->length - voff);
+       if (len2copy == sizeof(krb5_ui_4)) {
+           *uintp = (((krb5_int32) ((unsigned char) dataentp->data[voff])
+                      << 24) +
+                     ((krb5_int32) ((unsigned char) dataentp->data[voff+1])
+                      << 16) +
+                     ((krb5_int32) ((unsigned char) dataentp->data[voff+2])
+                      << 8) +
+                     ((krb5_int32) ((unsigned char) dataentp->data[voff+3])));
+           kret = 0;
+       }
+    }
+    return(kret);
+}
+\f
+/*
+ * decode_kw_gentime() - Decode a keyword=<general-time> pair and return the
+ *                       time result if the pair is present.
+ *
+ * XXX - this knows too much about how Kerberos time is encoded.
+ */
+static krb5_error_code
+decode_kw_gentime(dataentp, keyword, gtimep)
+    krb5_data          *dataentp;
+    char               *keyword;
+    krb5_timestamp     *gtimep;
+{
+    krb5_error_code    kret;
+    char               *timestring;
+    struct tm          tval;
+    time_t             temp_time;
+
+    memset((char *) &tval, 0, sizeof(tval));
+    timestring = (char *) NULL;
+    if (!(kret = decode_kw_string(dataentp, keyword, &timestring))) {
+       kret = EINVAL;
+       if ((strlen(timestring) == 15) &&
+           (timestring[14] == 'Z')) {
+           tval.tm_year = 1000*char2int(timestring[0]) +
+               100*char2int(timestring[1]) +
+                   10*char2int(timestring[2]) +
+                       char2int(timestring[3]) - 1900;
+           tval.tm_mon = 10*char2int(timestring[4]) +
+               char2int(timestring[5]) - 1;
+           tval.tm_mday = 10*char2int(timestring[6]) +
+               char2int(timestring[7]);
+           tval.tm_hour = 10*char2int(timestring[8]) +
+               char2int(timestring[9]);
+           tval.tm_min = 10*char2int(timestring[10]) +
+               char2int(timestring[11]);
+           tval.tm_sec = 10*char2int(timestring[12]) +
+               char2int(timestring[13]);
+           tval.tm_isdst = -1;
+           temp_time = gmt_mktime(&tval);
+           if (temp_time >= 0) {
+               kret = 0;
+               *gtimep = (krb5_timestamp) temp_time;
+           }
+       }
+       free(timestring);
+    }
+    return(kret);
+}
+\f
+/*
+ * decode_kw_tagged()  - Decode a keyword=<taglist>...<data> list and return
+ *                       the values of the tags and the data if the list is
+ *                       present.
+ */
+static krb5_error_code
+decode_kw_tagged(dataentp, keyword, ntags, taglist, lenp, datap)
+    krb5_data          *dataentp;
+    char               *keyword;
+    krb5_int32         ntags;
+    krb5_int32         *taglist;
+    size_t             *lenp;
+    krb5_octet         **datap;
+{
+    krb5_error_code    kret;
+    off_t              valueoff;
+    size_t             len2copy;
+    unsigned char      *cp, *ep;
+    int                        i;
+
+    kret = ENOENT;
+    if ((valueoff = keyword_value(dataentp, keyword, 1)) >= 0) {
+       /*
+        * Blast through the tags.
+        */
+       kret = 0;
+       cp = (unsigned char *) &dataentp->data[valueoff];
+       ep = (unsigned char *) &dataentp->data[dataentp->length];
+       for (i=0; i<ntags; i++) {
+           if (&cp[sizeof(krb5_int32)] > ep) {
+               kret = EINVAL;
+               break;
+           }
+           taglist[i] = (((krb5_int32) ((unsigned char) cp[0]) << 24) +
+                         ((krb5_int32) ((unsigned char) cp[1]) << 16) +
+                         ((krb5_int32) ((unsigned char) cp[2]) << 8) +
+                         ((krb5_int32) ((unsigned char) cp[3])));
+           cp += sizeof(krb5_int32);
+       }
+       if (!kret) {
+           /*
+            * If we were successful, copy out the remaining bytes for value.
+            */
+           len2copy = (size_t) (ep - cp);
+           if (len2copy &&
+               (*datap = (krb5_octet *) malloc(len2copy+1))) {
+               memcpy(*datap, cp, len2copy);
+               (*datap)[len2copy] = '\0';
+           }
+           if (len2copy && !*datap)
+               kret = ENOMEM;
+           else
+               *lenp = len2copy;
+       }
+    }
+    return(kret);
+}
+\f
+#if !defined(_MSDOS) && !defined(_WIN32) && !defined(_MACINTOSH)
+/*
+ * krb5_adm_proto_to_dbent()   - Convert external attribute list into a
+ *                               database entry.
+ *
+ * Scan through the keyword=value pairs in "data" until either the end of
+ * the list (as determined from "nent") is reached, or an error occurs.
+ * Return a mask of attributes which are set in "validp", the actual
+ * attribute values in "dbentp" and "pwordp" if a password is specified.
+ *
+ * Successful callers must allocate the storage for "validp", "dbentp" and
+ * must free the storage allocated for "pwordp" if a password is specified
+ * and free the storage allocated for "validp->mod_name" if a modifier name
+ * is specified.
+ */
+krb5_error_code
+krb5_adm_proto_to_dbent(kcontext, nent, data, validp, dbentp, pwordp)
+    krb5_context       kcontext;       /* Kerberos context     */ /* In */
+    krb5_int32         nent;           /* Number of components */ /* In */
+    krb5_data          *data;          /* Component list       */ /* In */
+    krb5_ui_4          *validp;        /* Valid bitmask        */ /* Out */
+    krb5_db_entry      *dbentp;        /* Database entry       */ /* Out */
+    char               **pwordp;       /* Password string      */ /* Out */
+{
+    int                        i;
+    krb5_error_code    retval;
+    krb5_ui_4          parsed_mask;
+    krb5_int32         taglist[4];
+    size_t             data_length;
+    krb5_octet         *tagged_data;
+    struct key_tag_correlator {
+       krb5_int32      key_tag;
+       int             key_data_index;
+    } *correlators, *correlation;
+    int                        ncorrelations;
+
+    /* Initialize */
+    retval = 0;
+    parsed_mask = 0;
+    *pwordp = (char *) NULL;
+    correlators = (struct key_tag_correlator *) NULL;
+    ncorrelations = 0;
+
+    /* Loop through all the specified keyword=value pairs. */
+    for (i=0; i<nent; i++) {
+       /* Check for password */
+       if (!(retval = decode_kw_string(&data[i],
+                                       KRB5_ADM_KW_PASSWORD,
+                                       pwordp))) {
+           parsed_mask |= KRB5_ADM_M_PASSWORD;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for maximum lifetime */
+       if (!(retval = decode_kw_integer(&data[i],
+                                        KRB5_ADM_KW_MAXLIFE,
+                                        (krb5_ui_4 *) &dbentp->max_life))) {
+           parsed_mask |= KRB5_ADM_M_MAXLIFE;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for maximum renewable lifetime */
+       if (!(retval = decode_kw_integer(&data[i],
+                                        KRB5_ADM_KW_MAXRENEWLIFE,
+                                        (krb5_ui_4 *)
+                                        &dbentp->max_renewable_life))) {
+           parsed_mask |= KRB5_ADM_M_MAXRENEWLIFE;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for principal expiration */
+       if (!(retval = decode_kw_gentime(&data[i],
+                                        KRB5_ADM_KW_EXPIRATION,
+                                        &dbentp->expiration))) {
+           parsed_mask |= KRB5_ADM_M_EXPIRATION;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for password expiration */
+       if (!(retval = decode_kw_gentime(&data[i],
+                                        KRB5_ADM_KW_PWEXPIRATION,
+                                        &dbentp->pw_expiration))) {
+           parsed_mask |= KRB5_ADM_M_PWEXPIRATION;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* random key - value optional */
+       if (keyword_value(&data[i],
+                         KRB5_ADM_KW_RANDOMKEY,
+                         0) >= 0) {
+           krb5_ui_4   value;
+
+           if (retval = decode_kw_integer(&data[i],
+                                          KRB5_ADM_KW_RANDOMKEY,
+                                          &value)) {
+               value = 1;
+               retval = 0;
+           }
+           if (value)
+               parsed_mask |= KRB5_ADM_M_RANDOMKEY;
+           else
+               parsed_mask &= ~KRB5_ADM_M_RANDOMKEY;
+           continue;
+       }
+
+       /* Check for flags */
+       if (!(retval = decode_kw_integer(&data[i],
+                                        KRB5_ADM_KW_FLAGS,
+                                        (krb5_ui_4 *) &dbentp->attributes))) {
+           parsed_mask |= KRB5_ADM_M_FLAGS;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for last successful password entry */
+       if (!(retval = decode_kw_gentime(&data[i],
+                                        KRB5_ADM_KW_LASTSUCCESS,
+                                        &dbentp->last_success))) {
+           parsed_mask |= KRB5_ADM_M_LASTSUCCESS;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for last failed entry */
+       if (!(retval = decode_kw_gentime(&data[i],
+                                        KRB5_ADM_KW_LASTFAILED,
+                                        &dbentp->last_failed))) {
+           parsed_mask |= KRB5_ADM_M_LASTFAILED;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for failure count */
+       if (!(retval = decode_kw_integer(&data[i],
+                                        KRB5_ADM_KW_FAILCOUNT,
+                                        (krb5_ui_4 *)
+                                        &dbentp->fail_auth_count))) {
+           parsed_mask |= KRB5_ADM_M_FAILCOUNT;
+           continue;
+       }
+       else {
+           if (retval != ENOENT)
+               break;
+       }
+
+       /* Check for auxiliary data */
+       if (!(retval = decode_kw_tagged(&data[i],
+                                       KRB5_ADM_KW_AUXDATA,
+                                       1,
+                                       taglist,
+                                       &data_length,
+                                       &tagged_data))) {
+           krb5_tl_data        **fixupp;
+           krb5_tl_data        *tl_data, *new_tl;
+
+           /*
+            * We've got a tagged data value here.  We've got to do a little
+            * work to put it in the right place.  First, find the right place.
+            */
+           fixupp = &dbentp->tl_data;
+           for (tl_data = dbentp->tl_data;
+                tl_data; 
+                tl_data = tl_data->tl_data_next)
+               fixupp = &tl_data->tl_data_next;
+
+           /* Get memory */
+           if (new_tl = (krb5_tl_data *) malloc(sizeof(krb5_tl_data))) {
+               /* Fill in the supplied values */
+               new_tl->tl_data_type = (krb5_int16) taglist[0];
+               new_tl->tl_data_length = (krb5_int16) data_length;
+               new_tl->tl_data_contents = tagged_data;
+
+               /* Link in the right place */
+               new_tl->tl_data_next= *fixupp;
+               *fixupp = new_tl;
+
+               /* Update counters and flags */
+               dbentp->n_tl_data++;
+               parsed_mask |= KRB5_ADM_M_AUXDATA;
+           }
+           else {
+               retval = ENOMEM;
+               break;
+           }
+           continue;
+       }
+       else {
+           if ((retval != ENOENT) && (retval != EINVAL))
+               break;
+       }
+
+       /* Check for key data */
+       if (!(retval = decode_kw_tagged(&data[i],
+                                       KRB5_ADM_KW_KEYDATA,
+                                       3,
+                                       taglist,
+                                       &data_length,
+                                       &tagged_data))) {
+           krb5_boolean        corr_found;
+           int                 cindex, kindex;
+           krb5_key_data       *kdata;
+
+           /*
+            * See if we already have a correlation betwen our key-tag and
+            * an index into the key table.
+            */
+           corr_found = 0;
+           for (cindex = 0; cindex < ncorrelations; cindex++) {
+               if (correlators[cindex].key_tag == taglist[0]) {
+                   correlation = &correlators[cindex];
+                   corr_found = 1;
+                   break;
+               }
+           }
+
+           /* If not, then we had better make one up */
+           if (!corr_found) {
+               /* Get a new list */
+               if (correlation = (struct key_tag_correlator *)
+                   malloc((ncorrelations+1)*
+                          sizeof(struct key_tag_correlator))) {
+                   /* Save the contents of the old one. */
+                   if (ncorrelations) {
+                       memcpy(correlation, correlators,
+                              ncorrelations*
+                              sizeof(struct key_tag_correlator));
+                       /* Free the old one */
+                       free(correlators);
+                   }
+                   /* Point us at the new relation */
+                   correlators = correlation;
+                   correlation = &correlators[ncorrelations];
+                   ncorrelations++;
+                   correlation->key_tag = taglist[0];
+                   /* Make a new key data entry */
+                   if (kdata = (krb5_key_data *)
+                       malloc((dbentp->n_key_data+1)*sizeof(krb5_key_data))) {
+                       /* Copy the old list */
+                       if (dbentp->n_key_data) {
+                           memcpy(kdata, dbentp->key_data,
+                                  dbentp->n_key_data*sizeof(krb5_key_data));
+                           free(dbentp->key_data);
+                       }
+                       dbentp->key_data = kdata;
+                       correlation->key_data_index = dbentp->n_key_data;
+                       memset(&kdata[dbentp->n_key_data], 0,
+                              sizeof(krb5_key_data));
+                       kdata[dbentp->n_key_data].key_data_ver = 1;
+                       dbentp->n_key_data++;
+                       corr_found = 1;
+                   }
+                   else
+                       retval = ENOMEM;
+               }
+               else
+                   retval = ENOMEM;
+           }
+
+           /* Check to see if we either found a correlation or made one */
+           if (corr_found) {
+               /* Special case for key version number */
+               if (taglist[1] == -1) {
+                   dbentp->key_data[correlation->key_data_index].
+                       key_data_kvno = taglist[2];
+               }
+               else {
+                   dbentp->key_data[correlation->key_data_index].
+                       key_data_type[taglist[1]] = taglist[2];
+                   dbentp->key_data[correlation->key_data_index].
+                       key_data_length[taglist[1]] = (krb5_int16) data_length;
+                   dbentp->key_data[correlation->key_data_index].
+                       key_data_contents[taglist[1]] = tagged_data;
+               }
+               parsed_mask |= KRB5_ADM_M_KEYDATA;
+           }
+           else
+               break;
+           continue;
+       }
+       else {
+           if ((retval != ENOENT) && (retval != EINVAL))
+               break;
+       }
+
+       /* Check for extra data */
+       if (!(retval = decode_kw_tagged(&data[i],
+                                       KRB5_ADM_KW_EXTRADATA,
+                                       0,
+                                       taglist,
+                                       &data_length,
+                                       &dbentp->e_data))) {
+           dbentp->e_length = (krb5_int16) data_length;
+           parsed_mask |= KRB5_ADM_M_EXTRADATA;
+           continue;
+       }
+       else {
+           if ((retval != ENOENT) && (retval != EINVAL))
+               break;
+       }
+
+       /* If we fall through here, we've got something unrecognized */
+       if (retval) {
+           retval = EINVAL;
+           break;
+       }
+    }
+
+    if (retval) {
+       if (*pwordp) {
+           memset(*pwordp, 0, strlen(*pwordp));
+           free(*pwordp);
+           *pwordp = (char *) NULL;
+       }
+       parsed_mask = 0;
+    }
+    if (correlators)
+       free(correlators);
+    *validp |= parsed_mask;
+    return(retval);
+}
+#endif
diff --git a/src/lib/kadm/adm_kw_enc.c b/src/lib/kadm/adm_kw_enc.c
new file mode 100644 (file)
index 0000000..b98725f
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * lib/kadm/adm_kw_enc.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * adm_kw_enc.c        - routines to encode principal attributes in keyword-value
+ *               pairs.
+ */
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+\f
+/*
+ * format_kw_string()  - Format a keyword=<string> pair.
+ * 
+ * Work routine for other string-based formatters also.
+ */
+static krb5_error_code
+format_kw_string(datap, kwordp, valp)
+    krb5_data  *datap;
+    char       *kwordp;
+    char       *valp;
+{
+    krb5_error_code    retval;
+    char               fbuffer[BUFSIZ];
+
+    retval = ENOMEM;
+    sprintf(fbuffer,"%s=%s", kwordp, valp);
+    datap->data = (char *) malloc(strlen(fbuffer)+1);
+    if (datap->data) {
+       datap->length = strlen(fbuffer);
+       strcpy(datap->data, fbuffer);
+       retval = 0;
+    }
+    return(retval);
+}
+\f
+/*
+ * format_kw_integer() - Format a keyword=<integer> pair.
+ */
+static krb5_error_code
+format_kw_integer(datap, kwordp, val)
+    krb5_data  *datap;
+    char       *kwordp;
+    krb5_ui_4  val;
+{
+    krb5_error_code    retval;
+    char               fbuffer[BUFSIZ];
+
+    retval = ENOMEM;
+    sprintf(fbuffer,"%s=", kwordp);
+    datap->data = (char *) malloc(strlen(fbuffer)+sizeof(krb5_ui_4));
+    if (datap->data) {
+       datap->length = strlen(fbuffer);
+       strcpy(datap->data, fbuffer);
+       datap->data[datap->length]   = (unsigned char) ((val >> 24) & 0xff);
+       datap->data[datap->length+1] = (unsigned char) ((val >> 16) & 0xff);
+       datap->data[datap->length+2] = (unsigned char) ((val >> 8) & 0xff);
+       datap->data[datap->length+3] = (unsigned char) (val & 0xff);
+       datap->length += sizeof(krb5_ui_4);
+       retval = 0;
+    }
+    return(retval);
+}
+\f
+/*
+ * format_kw_gentime() - Format a keyword=<general-time> pair.
+ *
+ * XXX - should this routine know so much about how generaltime is encoded?
+ */
+static krb5_error_code
+format_kw_gentime(datap, kwordp, timep)
+    krb5_data          *datap;
+    char               *kwordp;
+    krb5_timestamp     *timep;
+{
+    krb5_error_code    retval;
+    char               fbuffer[BUFSIZ];
+    time_t             tval;
+    struct tm          *time_gmt;
+
+    retval = EINVAL;
+    tval = (time_t) *timep;
+    time_gmt = gmtime(&tval);
+    if (time_gmt) {
+       sprintf(fbuffer,"%04d%02d%02d%02d%02d%02dZ",
+               time_gmt->tm_year+1900,
+               time_gmt->tm_mon+1,
+               time_gmt->tm_mday,
+               time_gmt->tm_hour,
+               time_gmt->tm_min,
+               time_gmt->tm_sec);
+       retval = format_kw_string(datap, kwordp, fbuffer);
+    }
+    return(retval);
+}
+\f
+/*
+ * format_kw_tagged()  - Format a <tagged>=<taglist>...<value> list.
+ */
+static krb5_error_code
+format_kw_tagged(datap, kwordp, ntags, taglist, vallen, val)
+    krb5_data  *datap;
+    char       *kwordp;
+    const int  ntags;
+    krb5_int32 *taglist;
+    krb5_int32 vallen;
+    krb5_octet *val;
+{
+    krb5_error_code    retval;
+    unsigned char      *cp;
+    int                        i;
+
+    /* Calculate the size required:
+     * strlen(kwordp) + 1 for "kword"=
+     * 4 * ntags for tags
+     * vallen for value;
+     */
+    datap->data = (char *) malloc(strlen(kwordp)+
+                                 1+
+                                 (ntags*sizeof(krb5_int32))+
+                                 vallen+1);
+    if (datap->data) {
+       datap->length = strlen(kwordp)+1+(ntags*sizeof(krb5_int32))+vallen;
+       cp = (unsigned char *) datap->data;
+       cp[datap->length] = '\0';
+       sprintf((char *) cp, "%s=", kwordp);
+       cp += strlen((char *) cp);
+       for (i=0; i<ntags; i++) {
+           cp[0] = (unsigned char) ((taglist[i] >> 24) & 0xff);
+           cp[1] = (unsigned char) ((taglist[i] >> 16) & 0xff);
+           cp[2] = (unsigned char) ((taglist[i] >> 8) & 0xff);
+           cp[3] = (unsigned char) (taglist[i] & 0xff);
+           cp += sizeof(krb5_int32);
+       }
+       if (val && vallen)
+           memcpy(cp, val, vallen);
+       retval = 0;
+    }
+    return(retval);
+}
+\f
+#if !defined(_MSDOS) && !defined(_WIN32) && !defined(_MACINTOSH)
+/*
+ * krb5_adm_dbent_to_proto()   - Convert database a database entry into
+ *                               an external attribute list.
+ *
+ * "valid" controls the generation of "datap" and "nentp".  For each
+ * corresponding bit in "valid" a keyword-value pair is generated from
+ * values in "dbentp" or "password" and put into "datap".  The number of
+ * generated pairs is returned in "nentp".  Additionally, the KRB5_ADM_M_SET
+ * and KRB5_ADM_M_GET bits control whether we are generating attribute lists
+ * for a "set" operation or a "get" operation.  One of these bits must be
+ * specified.
+ *
+ * Successful callers must free the storage for datap and datap->data
+ * either manually or using krb5_free_adm_data().
+ */
+krb5_error_code
+krb5_adm_dbent_to_proto(kcontext, valid, dbentp, password, nentp, datap)
+    krb5_context       kcontext;       /* Kerberos context     */ /* In */
+    krb5_ui_4          valid;          /* Valid bitmask        */ /* In */
+    krb5_db_entry      *dbentp;        /* Database entry       */ /* In */
+    char               *password;      /* New password for set */ /* In */
+    krb5_int32         *nentp;         /* Number of components */ /* Out */
+    krb5_data          **datap;        /* Output list          */ /* Out */
+{
+    krb5_error_code    kret;
+    krb5_data          *outlist;
+    size_t             n2alloc;
+    int                        outindex;
+    krb5_boolean       is_set;
+    krb5_ui_4          tmp;
+    krb5_int32         taglist[4];
+    krb5_tl_data       *tl_data;
+    int                        keyind, attrind;
+
+    kret = 0;
+    /* First check out whether this is a set or get and the mask */
+    is_set = ((valid & KRB5_ADM_M_SET) == KRB5_ADM_M_SET);
+    if ((is_set && ((valid & ~KRB5_ADM_M_SET_VALID) != 0)) ||
+       (!is_set && ((valid & ~KRB5_ADM_M_GET_VALID) != 0)) ||
+       (!is_set && ((valid & KRB5_ADM_M_GET) == 0)))
+       return(EINVAL);
+
+    /*
+     * Compute the number of elements to allocate.  First count set bits.
+     */
+    n2alloc = 0;
+    for (tmp = valid & ~(KRB5_ADM_M_SET|KRB5_ADM_M_GET);
+        tmp;
+        tmp >>= 1) {
+       if (tmp & 1)
+           n2alloc++;
+    }
+    if (valid & KRB5_ADM_M_AUXDATA)
+       n2alloc += (dbentp->n_tl_data - 1);
+    /*
+     * NOTE: If the number of per-key attributes increases, you must increase
+     * the 3 below.  The 3 represents 1 for key version, 1 for key type and
+     * one for salt type.
+     */
+    if (valid & KRB5_ADM_M_KEYDATA)
+       n2alloc += ((dbentp->n_key_data*3)-1);
+
+    n2alloc *= sizeof(krb5_data);
+    outindex = 0;
+    outlist = (krb5_data *) malloc(n2alloc);
+    if (outlist) {
+       /* Clear out the output data list */
+       memset((char *) outlist, 0, n2alloc);
+
+       /* Handle password only for set request */
+       if (is_set &&
+           ((valid & KRB5_ADM_M_PASSWORD) != 0) &&
+           password) {
+           if (kret = format_kw_string(&outlist[outindex],
+                                       KRB5_ADM_KW_PASSWORD,
+                                       password))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle maximum ticket lifetime */
+       if ((valid & KRB5_ADM_M_MAXLIFE) != 0) {
+           if (kret = format_kw_integer(&outlist[outindex],
+                                        KRB5_ADM_KW_MAXLIFE,
+                                        (krb5_ui_4) dbentp->max_life))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle maximum renewable ticket lifetime */
+       if ((valid & KRB5_ADM_M_MAXRENEWLIFE) != 0) {
+           if (kret =
+               format_kw_integer(&outlist[outindex],
+                                 KRB5_ADM_KW_MAXRENEWLIFE,
+                                 (krb5_ui_4) dbentp->max_renewable_life))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle principal expiration */
+       if ((valid & KRB5_ADM_M_EXPIRATION) != 0) {
+           if (kret = format_kw_gentime(&outlist[outindex],
+                                        KRB5_ADM_KW_EXPIRATION,
+                                        &dbentp->expiration))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle password expiration */
+       if ((valid & KRB5_ADM_M_PWEXPIRATION) != 0) {
+           if (kret = format_kw_gentime(&outlist[outindex],
+                                        KRB5_ADM_KW_PWEXPIRATION,
+                                        &dbentp->pw_expiration))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Random key */
+       if ((valid & KRB5_ADM_M_RANDOMKEY) != 0) {
+           if (kret = format_kw_integer(&outlist[outindex],
+                                        KRB5_ADM_KW_RANDOMKEY,
+                                        1))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle flags */
+       if ((valid & KRB5_ADM_M_FLAGS) != 0) {
+           if (kret = format_kw_integer(&outlist[outindex],
+                                        KRB5_ADM_KW_FLAGS,
+                                        (krb5_ui_4) dbentp->attributes))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle last successful password entry */
+       if (!is_set &&
+           ((valid & KRB5_ADM_M_LASTSUCCESS) != 0)) {
+           if (kret = format_kw_gentime(&outlist[outindex],
+                                        KRB5_ADM_KW_LASTSUCCESS,
+                                        &dbentp->last_success))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle last failed password attempt */
+       if (!is_set &&
+           ((valid & KRB5_ADM_M_LASTFAILED) != 0)) {
+           if (kret = format_kw_gentime(&outlist[outindex],
+                                        KRB5_ADM_KW_LASTFAILED,
+                                        &dbentp->last_failed))
+               goto choke;
+           else
+               outindex++;
+       }
+       /* Handle number of failed password attempts */
+       if (!is_set &&
+           ((valid & KRB5_ADM_M_FAILCOUNT) != 0)) {
+           if (kret = format_kw_integer(&outlist[outindex],
+                                        KRB5_ADM_KW_FAILCOUNT,
+                                        (krb5_ui_4) dbentp->fail_auth_count))
+               goto choke;
+           else
+               outindex++;
+       }
+
+       /* Handle the auxiliary data */
+       if ((valid & KRB5_ADM_M_AUXDATA) != 0) {
+           for (tl_data = dbentp->tl_data; tl_data; tl_data =
+                tl_data->tl_data_next) {
+               taglist[0] = (krb5_int32) tl_data->tl_data_type;
+               if (kret = format_kw_tagged(&outlist[outindex],
+                                           KRB5_ADM_KW_AUXDATA,
+                                           1,
+                                           taglist,
+                                           (krb5_int32) tl_data->
+                                               tl_data_length,
+                                           tl_data->tl_data_contents))
+                   goto choke;
+               else
+                   outindex++;
+           }
+       }
+
+       /* Handle the key data */
+       if (!is_set &&
+           ((valid  & KRB5_ADM_M_KEYDATA) != 0)) {
+           for (keyind = 0; keyind < dbentp->n_key_data; keyind++) {
+               /*
+                * First handle kvno
+                */
+               taglist[0] = (krb5_int32) keyind;
+               taglist[1] = (krb5_int32) -1;
+               taglist[2] = (krb5_int32) dbentp->key_data[keyind].
+                   key_data_kvno;
+               if (kret = format_kw_tagged(&outlist[outindex],
+                                           KRB5_ADM_KW_KEYDATA,
+                                           3,
+                                           taglist,
+                                           0,
+                                           (krb5_octet *) NULL))
+                   goto choke;
+               else
+                   outindex++;
+
+               /*
+                * Then each attribute as supported.
+                */
+               for (attrind = 0;
+                    attrind < KRB5_KDB_V1_KEY_DATA_ARRAY;
+                    attrind++) {
+                   taglist[1] = (krb5_int32) attrind;
+                   taglist[2] = (krb5_int32) dbentp->key_data[keyind].
+                       key_data_type[attrind];
+                   if (kret = format_kw_tagged(&outlist[outindex],
+                                               KRB5_ADM_KW_KEYDATA,
+                                               3,
+                                               taglist,
+                                               (krb5_int32) dbentp->
+                                                   key_data[keyind].
+                                                   key_data_length[attrind],
+                                               dbentp->key_data[keyind].
+                                                   key_data_contents[attrind])
+                       )
+                       goto choke;
+                   else
+                       outindex++;
+               }
+           }
+       }
+
+       /* Finally, handle the extra data */
+       if ((valid & KRB5_ADM_M_EXTRADATA) != 0) {
+           if (kret = format_kw_tagged(&outlist[outindex],
+                                       KRB5_ADM_KW_EXTRADATA,
+                                       0,
+                                       (krb5_int32 *) NULL,
+                                       (krb5_int32) dbentp->e_length,
+                                       dbentp->e_data))
+               goto choke;
+           else
+               outindex++;
+       }
+    }
+    else {
+       if (n2alloc)
+           kret = ENOMEM;
+    }
+ choke:
+    if (kret) {
+       if (outlist) {
+           int i;
+           for (i=0; i<outindex; i++) {
+               if (outlist[i].data) {
+                   memset(outlist[i].data, 0, (size_t) outlist[i].length);
+                   free(outlist[i].data);
+               }
+           }
+           free(outlist);
+       }
+       outlist = (krb5_data *) NULL;
+       outindex = 0;
+    }
+    *datap = outlist;
+    *nentp = outindex;
+    return(kret);
+}
+#endif
+
diff --git a/src/lib/kadm/adm_rw.c b/src/lib/kadm/adm_rw.c
new file mode 100644 (file)
index 0000000..bed4e4b
--- /dev/null
@@ -0,0 +1,534 @@
+/*
+ * lib/kadm/adm_rw.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * Routines to engage in the administrative (password changing) protocol.
+ */
+#define NEED_SOCKETS
+#include "k5-int.h"
+#include "adm_proto.h"
+
+/*
+ * Local prototypes (needed or else the PC will pass fail).
+ */
+static void kadm_copyin_int32 PROTOTYPE((char *, krb5_int32 *));
+static void kadm_copyout_int32 PROTOTYPE((krb5_int32, char *));
+
+/*
+ * Routines to [de]serialize integers.
+ *
+ * kadm_copyin_int32   - Move a 32-bit integer fron network byte order to
+ *                       host byte order.
+ * kadm_copyout_int32  - Move a 32-bit integer from host byte order to
+ *                       network byte order.
+ */
+static void
+kadm_copyin_int32(cp, ip)
+    char       *cp;
+    krb5_int32 *ip;
+{
+    *ip = (((krb5_int32) ((unsigned char) cp[0]) << 24) +
+          ((krb5_int32) ((unsigned char) cp[1]) << 16) +
+          ((krb5_int32) ((unsigned char) cp[2]) << 8) +
+          ((krb5_int32) ((unsigned char) cp[3])));
+}
+
+static void
+kadm_copyout_int32(outint, cp)
+    krb5_int32 outint;
+    char       *cp;
+{
+    cp[0] = (char) ((outint >> 24) & 0xff);
+    cp[1] = (char) ((outint >> 16) & 0xff);
+    cp[2] = (char) ((outint >> 8) & 0xff);
+    cp[3] = (char) (outint & 0xff);
+}
+\f
+/*
+ * krb5_free_adm_data()        - Free data blocks allocated by read_adm... routines.
+ */
+void INTERFACE
+krb5_free_adm_data(kcontext, ncomp, datap)
+    krb5_context       kcontext;
+    krb5_int32         ncomp;
+    krb5_data          *datap;
+{
+    int i;
+    
+    if (datap) {
+       for (i=0; i<ncomp; i++)
+           if (datap[i].data && (datap[i].length > 0))
+               krb5_xfree(datap[i].data);
+
+       krb5_xfree(datap);
+    }
+}
+\f
+/*
+ * krb5_send_adm_cmd() - Send an administrative command.
+ *
+ * Send a list of data in a KRB_PRIV message.  Data takes the format:
+ *     nargs (4 octets in network order)
+ *             arg size 1 (4 octets in network order)
+ *             arg data 1 ("arg size 1" octets)
+ *             .
+ *             .
+ *             .
+ */
+krb5_error_code INTERFACE
+krb5_send_adm_cmd(kcontext, sock, ctx, nargs, arglist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to write to   (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32                 nargs;          /* Number of arguments  (In ) */
+    krb5_data          *arglist;       /* Components to write  (In ) */
+{
+    int        writebufsize;
+    int i;
+    char *writebuf;
+    krb5_error_code ret;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    ret = 0;
+    /* Calculate write buffer size */
+    writebufsize = sizeof(krb5_int32);
+    for (i=0; i<nargs; i++) {
+       writebufsize += sizeof(krb5_int32);     /* for argument size */
+       writebufsize += arglist[i].length;      /* for actual arg */
+    }
+
+    if (writebuf = (char *) malloc(writebufsize)) {
+       char                    *curr;
+       krb5_data               write_data, out_data;
+       krb5_replay_data        replay_data;
+
+       /* Serialize into write buffer - first number of arguments */
+       curr = writebuf;
+       kadm_copyout_int32(nargs, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Then the arguments */
+       for (i=0; i<nargs; i++) {
+           kadm_copyout_int32(arglist[i].length, curr);
+           curr += sizeof(krb5_int32);
+           memcpy(curr, arglist[i].data, arglist[i].length);
+           curr += arglist[i].length;
+       }
+
+       /* Set up the message */
+       write_data.length = writebufsize;
+       write_data.data = writebuf;
+
+       /* Generate the message */
+       if (!(ret = krb5_mk_priv(kcontext,
+                                ctx,
+                                &write_data,
+                                &out_data,
+                                &replay_data))) {
+           /* Write the message */
+           if (ret = krb5_write_message(kcontext, sock, &out_data))
+               goto cleanup;
+           krb5_xfree(out_data.data);
+       }
+
+    cleanup:
+       /* Paranoia */
+       memset(writebuf, 0, writebufsize);
+       free(writebuf);
+    }
+    else {
+       /* error */
+       ret = ENOMEM;
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_send_adm_reply()       - Send an administrative reply.
+ *
+ * Send a reply in a KRB_PRIV message.  Data takes the format:
+ *     status (4 octets in network order)
+ *     ncomps (4 octets in network order)
+ *             comp size 1 (4 octets in network order)
+ *             comp data 1 ("comp size 1" octets)
+ *             .
+ *             .
+ *             .
+ */
+krb5_error_code
+krb5_send_adm_reply(kcontext, sock, ctx, cmd_stat, ncomps, complist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to write to   (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32         cmd_stat;       /* Command status       (In ) */
+    krb5_int32                 ncomps;         /* Number of arguments  (In ) */
+    krb5_data          *complist;      /* Components to write  (In ) */
+{
+    int        writebufsize;
+    int i;
+    char *writebuf;
+    krb5_error_code ret;
+    krb5_int32 ac_flags;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    ret = 0;
+    /* Calculate write buffer size */
+    writebufsize = 2 * sizeof(krb5_int32);
+    for (i=0; i<ncomps; i++) {
+       writebufsize += sizeof(krb5_int32);     /* for argument size */
+       writebufsize += complist[i].length;     /* for actual arg */
+    }
+
+    if (writebuf = (char *) malloc(writebufsize)) {
+       char                    *curr;
+       krb5_data               write_data, out_data;
+       krb5_replay_data        replay_data;
+
+       /* Serialize into write buffer - first command status */
+       curr = writebuf;
+       kadm_copyout_int32(cmd_stat, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Now number of reply components */
+       kadm_copyout_int32(ncomps, curr);
+       curr += sizeof(krb5_int32);
+
+       /* Then the arguments */
+       for (i=0; i<ncomps; i++) {
+           kadm_copyout_int32(complist[i].length, curr);
+           curr += sizeof(krb5_int32);
+           memcpy(curr, complist[i].data, complist[i].length);
+           curr += complist[i].length;
+       }
+
+       /* Set up the message */
+       write_data.length = writebufsize;
+       write_data.data = writebuf;
+
+       /* Generate the message */
+       if (!(ret = krb5_mk_priv(kcontext,
+                                ctx,
+                                &write_data,
+                                &out_data,
+                                &replay_data))) {
+           /* Write the message */
+           if (ret = krb5_write_message(kcontext, sock, &out_data))
+               goto cleanup;
+           krb5_xfree(out_data.data);
+       }
+
+    cleanup:
+       /* Paranoia */
+       memset(writebuf, 0, writebufsize);
+       free(writebuf);
+    }
+    else {
+       /* error */
+       ret = ENOMEM;
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_read_adm_cmd() - Read an administrative protocol command.
+ *
+ * Read an administrative command from the socket.  Expect data in the
+ * same format as send_adm_cmd shoots them out in.
+ *
+ * It is the caller's responsibility to free the memory allocated for
+ * the read in argument list.
+ */
+krb5_error_code
+krb5_read_adm_cmd(kcontext, sock, ctx, nargs, arglist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to read from  (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32         *nargs;         /* Number of arguments  (Out) */
+    krb5_data          **arglist;      /* List of arguments    (Out) */
+{
+    krb5_data          read_data;
+    krb5_error_code    ret;
+    krb5_data          msg_data;
+    krb5_replay_data   replay_data;
+    krb5_int32         ac_flags;
+    krb5_int32         len32;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    if (!(ret = krb5_read_message(kcontext, sock, &read_data))) {
+       if (!(ret = krb5_rd_priv(kcontext,
+                                ctx,
+                                &read_data,
+                                &msg_data,
+                                &replay_data))) {
+           char *curr;
+           int replyok;
+           int i;
+
+           replyok = 0;
+           /* We'd better have at least one reply component */
+           if (msg_data.length >= sizeof(krb5_int32)) {
+               curr = msg_data.data;
+               kadm_copyin_int32(curr, nargs);
+               curr += sizeof(krb5_int32);
+
+               /* Are there any components to copy? */
+               if (*nargs > 0) {
+
+                   /* Get the memory for the list */
+                   if (*arglist = (krb5_data *)
+                       malloc((size_t) (*nargs) * sizeof(krb5_data))) {
+                       krb5_data *xarglist;
+
+                       xarglist = *arglist;
+                       memset((char *) (xarglist), 0,
+                               (size_t) (*nargs) * sizeof(krb5_data));
+
+                       replyok = 1;
+                       /* Copy out each list entry */
+                       for (i=0; i<*nargs; i++) {
+
+                           /* First get the length of the reply component */
+                           if (curr + sizeof(krb5_int32) - msg_data.data <=
+                               msg_data.length) {
+
+                               kadm_copyin_int32(curr, &len32);
+                               xarglist[i].length = (int) len32;
+                               curr += sizeof(krb5_int32);
+
+                               /* Then get the memory for the actual data */
+                               if ((curr + xarglist[i].length -
+                                    msg_data.data <= msg_data.length) &&
+                                   (xarglist[i].data = (char *)
+                                    malloc(xarglist[i].length+1))) {
+
+                                   /* Then copy it out */
+                                   memcpy(xarglist[i].data,
+                                          curr,
+                                          xarglist[i].length);
+                                   curr += xarglist[i].length;
+
+                                   /* Null terminate for convenience */
+                                   xarglist[i].data[xarglist[i].length] 
+                                           = '\0';
+                               }
+                               else {
+                                   /* Not enough remaining data. */
+                                   replyok = 0;
+                                   break;
+                               }
+                           }
+                           else {
+                               /* Not enough remaining data */
+                               replyok = 0;
+                               break;
+                           }
+                       }
+                       if (!replyok)
+                           krb5_free_adm_data(kcontext, *nargs, *arglist);
+                   }
+               }
+               else {
+                   if (*nargs == 0) {
+                       *arglist = (krb5_data *) NULL;
+                       replyok = 1;
+                   }
+               }
+           }
+           if (!replyok) {
+               ret = KRB5KRB_AP_ERR_MSG_TYPE;  /* syntax error */
+           }
+           memset(msg_data.data, 0, msg_data.length);
+           krb5_xfree(msg_data.data);
+       }
+       krb5_xfree(read_data.data);
+    }
+    return(ret);
+}
+\f
+/*
+ * krb5_read_adm_reply()       - Read an administrative protocol response.
+ *
+ * Expect to read them out in the same format as send_adm_reply shoots them
+ * in.
+ *
+ * It is the caller's responsibility to free the memory allocated for
+ * the read in component list.
+ */
+krb5_error_code INTERFACE
+krb5_read_adm_reply(kcontext, sock, ctx, cmd_stat, ncomps, complist)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    krb5_pointer       sock;           /* Socket to read from  (In ) */
+    krb5_auth_context  ctx;            /* Auth context         (In ) */
+    krb5_int32         *cmd_stat;      /* Command status       (Out) */
+    krb5_int32         *ncomps;        /* # of reply components(Out) */
+    krb5_data          **complist;     /* List of components   (Out) */
+{
+    krb5_data          read_data;
+    krb5_error_code    ret;
+    krb5_data          msg_data;
+    krb5_replay_data   replay_data;
+    krb5_int32         ac_flags;
+    krb5_int32         len32;
+
+    /*
+     * First check that our auth context has the right flags in it.
+     */
+    if (ret = krb5_auth_con_getflags(kcontext, ctx, &ac_flags))
+       return(ret);
+
+    if ((ac_flags & (KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                    KRB5_AUTH_CONTEXT_DO_SEQUENCE)) !=
+       (KRB5_AUTH_CONTEXT_RET_SEQUENCE|KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
+       /* XXX - need a better error */
+       return(KRB5KRB_AP_ERR_MSG_TYPE);
+    }
+
+    if (!(ret = krb5_read_message(kcontext, sock, &read_data))) {
+       if (!(ret = krb5_rd_priv(kcontext,
+                                ctx,
+                                &read_data,
+                                &msg_data,
+                                &replay_data))) {
+           char *curr;
+           int replyok;
+           int i;
+
+           replyok = 0;
+           /* We'd better have at least two reply components */
+           if (msg_data.length >= (2*sizeof(krb5_int32))) {
+               curr = msg_data.data;
+               kadm_copyin_int32(curr, cmd_stat);
+               curr += sizeof(krb5_int32);
+               kadm_copyin_int32(curr, ncomps);
+               curr += sizeof(krb5_int32);
+
+               /* Are there any components to copy? */
+               if (*ncomps > 0) {
+
+                   /* Get the memory for the list */
+                   if (*complist = (krb5_data *)
+                       malloc((size_t) ((*ncomps) * sizeof(krb5_data)))) {
+                       krb5_data *xcomplist;
+
+                       xcomplist = *complist;
+                       memset((char *) (xcomplist), 0, 
+                              (size_t) ((*ncomps) * sizeof(krb5_data)));
+
+                       replyok = 1;
+                       /* Copy out each list entry */
+                       for (i=0; i<*ncomps; i++) {
+
+                           /* First get the length of the reply component */
+                           if (curr + sizeof(krb5_int32) - msg_data.data <=
+                               msg_data.length) {
+                               kadm_copyin_int32(curr, &len32);
+                               xcomplist[i].length = (int) len32;
+                               curr += sizeof(krb5_int32);
+
+                               /* Then get the memory for the actual data */
+                               if ((curr + xcomplist[i].length -
+                                    msg_data.data <= msg_data.length) &&
+                                   (xcomplist[i].data = (char *)
+                                    malloc(xcomplist[i].length+1))) {
+
+                                   /* Then copy it out */
+                                   memcpy(xcomplist[i].data,
+                                          curr,
+                                          xcomplist[i].length);
+                                   curr += xcomplist[i].length;
+
+                                   /* Null terminate for convenience */
+                                   xcomplist[i].data[xcomplist[i].length] 
+                                           = '\0';
+                               }
+                               else {
+                                   /* Not enough remaining data. */
+                                   replyok = 0;
+                                   break;
+                               }
+                           }
+                           else {
+                               /* Not enough remaining data */
+                               replyok = 0;
+                               break;
+                           }
+                       }
+                       if (!replyok)
+                           krb5_free_adm_data(kcontext, *ncomps, *complist);
+                   }
+               }
+               else {
+                   if (*ncomps == 0) {
+                       *complist = (krb5_data *) NULL;
+                       replyok = 1;
+                   }
+               }
+           }
+           if (!replyok) {
+               ret = KRB5KRB_AP_ERR_MSG_TYPE;  /* syntax error */
+           }
+           memset(msg_data.data, 0, msg_data.length);
+           krb5_xfree(msg_data.data);
+       }
+       krb5_xfree(read_data.data);
+    }
+    return(ret);
+}
diff --git a/src/lib/kadm/alt_prof.c b/src/lib/kadm/alt_prof.c
new file mode 100644 (file)
index 0000000..9556ac4
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * lib/kadm/alt_prof.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * alt_prof.c - Implement alternate profile file handling.
+ */
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * krb5_aprof_init()   - Initialize alternate profile context.
+ *
+ * Parameters:
+ *     fname           - default file name of the profile.
+ *     envname         - environment variable name which can override fname.
+ *     acontextp       - Pointer to opaque context for alternate profile.
+ *
+ * Returns:
+ *     error codes from profile_init()
+ */
+krb5_error_code
+krb5_aprof_init(fname, envname, acontextp)
+    char               *fname;
+    char               *envname;
+    krb5_pointer       *acontextp;
+{
+    krb5_error_code    kret;
+    const char         *namelist[2];
+    profile_t          profile;
+    
+    namelist[1] = (char *) NULL;
+    profile = (profile_t) NULL;
+    if (envname) {
+       if ((namelist[0] = getenv(envname))) {
+           if (!(kret = profile_init(namelist, &profile))) {
+               *acontextp = (krb5_pointer) profile;
+               return(0);
+           }
+       }
+    }
+    namelist[0] = fname;
+    profile = (profile_t) NULL;
+    if (!(kret = profile_init(namelist, &profile))) {
+       *acontextp = (krb5_pointer) profile;
+       return(0);
+    }
+    return(kret);
+}
+
+/*
+ * krb5_aprof_getvals()        - Get values from alternate profile.
+ *
+ * Parameters:
+ *     acontext        - opaque context for alternate profile.
+ *     hierarchy       - hierarchy of value to retrieve.
+ *     retdata         - Returned data values.
+ *
+ * Returns:
+ *     error codes from profile_get_values()
+ */
+krb5_error_code
+krb5_aprof_getvals(acontext, hierarchy, retdata)
+    krb5_pointer       acontext;
+    const char         **hierarchy;
+    char               ***retdata;
+{
+    return(profile_get_values((profile_t) acontext,
+                             hierarchy,
+                             retdata));
+}
+
+/*
+ * krb5_aprof_get_deltat()     - Get a delta time value from the alternate
+ *                               profile.
+ *
+ * Parameters:
+ *     acontext                - opaque context for alternate profile.
+ *     hierarchy               - hierarchy of value to retrieve.
+ *     uselast                 - if true, use last value, otherwise use
+ *                               first value found.
+ *     deltatp                 - returned delta time value.
+ *
+ * Returns:
+ *     error codes from profile_get_values()
+ *     error codes from krb5_string_to_deltat()
+ */
+krb5_error_code
+krb5_aprof_get_deltat(acontext, hierarchy, uselast, deltatp)
+    krb5_pointer       acontext;
+    const char         **hierarchy;
+    krb5_boolean       uselast;
+    krb5_deltat                *deltatp;
+{
+    krb5_error_code    kret;
+    char               **values;
+    char               *valp;
+    int                        index;
+
+    if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) {
+       index = 0;
+       if (uselast) {
+           for (index=0; values[index]; index++);
+           index--;
+       }
+       valp = values[index];
+       kret = krb5_string_to_deltat(valp, deltatp);
+
+       /* Free the string storage */
+       for (index=0; values[index]; index++)
+           krb5_xfree(values[index]);
+       krb5_xfree(values);
+    }
+    return(kret);
+}
+
+/*
+ * krb5_aprof_get_string()     - Get a string value from the alternate
+ *                               profile.
+ *
+ * Parameters:
+ *     acontext                - opaque context for alternate profile.
+ *     hierarchy               - hierarchy of value to retrieve.
+ *     uselast                 - if true, use last value, otherwise use
+ *                               first value found.
+ *     stringp                 - returned string value.
+ *
+ * Returns:
+ *     error codes from profile_get_values()
+ */
+krb5_error_code
+krb5_aprof_get_string(acontext, hierarchy, uselast, stringp)
+    krb5_pointer       acontext;
+    const char         **hierarchy;
+    krb5_boolean       uselast;
+    char               **stringp;
+{
+    krb5_error_code    kret;
+    char               **values;
+    int                        index, i;
+
+    if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) {
+       index = 0;
+       if (uselast) {
+           for (index=0; values[index]; index++);
+           index--;
+       }
+
+       *stringp = values[index];
+
+       /* Free the string storage */
+       for (i=0; values[i]; i++)
+           if (i != index)
+               krb5_xfree(values[i]);
+       krb5_xfree(values);
+    }
+    return(kret);
+}
+
+/*
+ * krb5_aprof_get_int32()      - Get a 32-bit integer value from the alternate
+ *                               profile.
+ *
+ * Parameters:
+ *     acontext                - opaque context for alternate profile.
+ *     hierarchy               - hierarchy of value to retrieve.
+ *     uselast                 - if true, use last value, otherwise use
+ *                               first value found.
+ *     intp                    - returned 32-bit integer value.
+ *
+ * Returns:
+ *     error codes from profile_get_values()
+ *     EINVAL                  - value is not an integer
+ */
+krb5_error_code
+krb5_aprof_get_int32(acontext, hierarchy, uselast, intp)
+    krb5_pointer       acontext;
+    const char         **hierarchy;
+    krb5_boolean       uselast;
+    krb5_int32         *intp;
+{
+    krb5_error_code    kret;
+    char               **values;
+    int                        index;
+
+    if (!(kret = krb5_aprof_getvals(acontext, hierarchy, &values))) {
+       index = 0;
+       if (uselast) {
+           for (index=0; values[index]; index++);
+           index--;
+       }
+
+       if (sscanf(values[index], "%d", intp) != 1)
+           kret = EINVAL;
+
+       /* Free the string storage */
+       for (index=0; values[index]; index++)
+           krb5_xfree(values[index]);
+       krb5_xfree(values);
+    }
+    return(kret);
+}
+
+/*
+ * krb5_aprof_finish() - Finish alternate profile context.
+ *
+ * Parameter:
+ *     acontext        - opaque context for alternate profile.
+ *
+ * Returns:
+ *     0 on success, something else on failure.
+ */
+krb5_error_code
+krb5_aprof_finish(acontext)
+    krb5_pointer       acontext;
+{
+    profile_release(acontext);
+    return(0);
+}
+
+/*
+ * krb5_read_realm_params()    - Read per-realm parameters from KDC
+ *                               alternate profile.
+ */
+krb5_error_code
+krb5_read_realm_params(kcontext, realm, kdcprofile, kdcenv, rparamp)
+    krb5_context       kcontext;
+    char               *realm;
+    char               *kdcprofile;
+    char               *kdcenv;
+    krb5_realm_params  **rparamp;
+{
+    char               *filename;
+    char               *envname;
+    char               *lrealm;
+    krb5_pointer       aprofile = 0;
+    krb5_realm_params  *rparams;
+    const char         *hierarchy[4];
+    char               *svalue;
+    krb5_int32         ivalue;
+    krb5_deltat                dtvalue;
+
+    krb5_error_code    kret;
+
+    filename = (kdcprofile) ? kdcprofile : DEFAULT_KDC_PROFILE;
+    envname = (kdcenv) ? kdcenv : KDC_PROFILE_ENV;
+
+    if (kcontext->profile_secure == TRUE) envname = 0;
+
+    rparams = (krb5_realm_params *) NULL;
+    if (realm)
+       lrealm = strdup(realm);
+    else {
+       kret = krb5_get_default_realm(kcontext, &lrealm);
+       if (kret)
+           goto cleanup;
+    }
+
+    kret = krb5_aprof_init(filename, envname, &aprofile);
+    if (kret)
+       goto cleanup;
+    
+    rparams = (krb5_realm_params *) malloc(sizeof(krb5_realm_params));
+    if (rparams == 0) {
+       kret = ENOMEM;
+       goto cleanup;
+    }
+
+    /* Initialize realm parameters */
+    memset((char *) rparams, 0, sizeof(krb5_realm_params));
+
+    /* Get the value for the database */
+    hierarchy[0] = "realms";
+    hierarchy[1] = lrealm;
+    hierarchy[2] = "database_name";
+    hierarchy[3] = (char *) NULL;
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
+       rparams->realm_dbname = svalue;
+       
+    /* Get the value for the KDC port list */
+    hierarchy[2] = "kdc_ports";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
+       rparams->realm_kdc_ports = svalue;
+           
+    /* Get the name of the acl file */
+    hierarchy[2] = "acl_file";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
+       rparams->realm_acl_file = svalue;
+           
+    /* Get the value for the kadmind port */
+    hierarchy[2] = "kadmind_port";
+    if (!krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) {
+       rparams->realm_kadmind_port = ivalue;
+       rparams->realm_kadmind_port_valid = 1;
+    }
+           
+    /* Get the value for the master key name */
+    hierarchy[2] = "master_key_name";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
+       rparams->realm_mkey_name = svalue;
+           
+    /* Get the value for the master key type */
+    hierarchy[2] = "master_key_type";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
+       if (!krb5_string_to_enctype(svalue, &rparams->realm_enctype))
+           rparams->realm_enctype_valid = 1;
+       krb5_xfree(svalue);
+    }
+           
+    /* Get the value for the stashfile */
+    hierarchy[2] = "key_stash_file";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue))
+       rparams->realm_stash_file = svalue;
+           
+    /* Get the value for maximum ticket lifetime. */
+    hierarchy[2] = "max_life";
+    if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) {
+       rparams->realm_max_life = dtvalue;
+       rparams->realm_max_life_valid = 1;
+    }
+           
+    /* Get the value for maximum renewable ticket lifetime. */
+    hierarchy[2] = "max_renewable_life";
+    if (!krb5_aprof_get_deltat(aprofile, hierarchy, TRUE, &dtvalue)) {
+       rparams->realm_max_rlife = dtvalue;
+       rparams->realm_max_rlife_valid = 1;
+    }
+           
+    /* Get the value for the default principal expiration */
+    hierarchy[2] = "default_principal_expiration";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
+       if (!krb5_string_to_timestamp(svalue,
+                                     &rparams->realm_expiration))
+           rparams->realm_expiration_valid = 1;
+       krb5_xfree(svalue);
+    }
+           
+    /* Get the value for the default principal flags */
+    hierarchy[2] = "default_principal_flags";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
+       char *sp, *ep, *tp;
+
+       sp = svalue;
+       rparams->realm_flags = 0;
+       while (sp) {
+           if ((ep = strchr(sp, (int) ',')) ||
+               (ep = strchr(sp, (int) ' ')) ||
+               (ep = strchr(sp, (int) '\t'))) {
+               /* Fill in trailing whitespace of sp */
+               tp = ep - 1;
+               while (isspace(*tp) && (tp < sp)) {
+                   *tp = '\0';
+                   tp--;
+               }
+               *ep = '\0';
+               ep++;
+               /* Skip over trailing whitespace of ep */
+               while (isspace(*ep) && (*ep)) ep++;
+           }
+           /* Convert this flag */
+           if (krb5_string_to_flags(sp,
+                                    "+",
+                                    "-",
+                                    &rparams->realm_flags))
+               break;
+           sp = ep;
+       }
+       if (!sp)
+           rparams->realm_flags_valid = 1;
+       krb5_xfree(svalue);
+    }
+
+    /* Get the value for the supported enctype/salttype matrix */
+    hierarchy[2] = "supported_enctypes";
+    if (!krb5_aprof_get_string(aprofile, hierarchy, TRUE, &svalue)) {
+       krb5_string_to_keysalts(svalue,
+                               ", \t", /* Tuple separators     */
+                               ":.-",  /* Key/salt separators  */
+                               0,      /* No duplicates        */
+                               &rparams->realm_keysalts,
+                               &rparams->realm_num_keysalts);
+       krb5_xfree(svalue);
+    }
+
+cleanup:
+    if (aprofile)
+       krb5_aprof_finish(aprofile);
+    if (lrealm)
+       free(lrealm);
+    if (kret) {
+       if (rparams)
+           krb5_free_realm_params(kcontext, rparams);
+       rparams = 0;
+    }
+    *rparamp = rparams;
+    return(kret);
+}
+
+/*
+ * krb5_free_realm_params()    - Free data allocated by above.
+ */
+krb5_error_code
+krb5_free_realm_params(kcontext, rparams)
+    krb5_context       kcontext;
+    krb5_realm_params  *rparams;
+{
+    if (rparams) {
+       if (rparams->realm_profile)
+           krb5_xfree(rparams->realm_profile);
+       if (rparams->realm_dbname)
+           krb5_xfree(rparams->realm_dbname);
+       if (rparams->realm_mkey_name)
+           krb5_xfree(rparams->realm_mkey_name);
+       if (rparams->realm_stash_file)
+           krb5_xfree(rparams->realm_stash_file);
+       if (rparams->realm_keysalts)
+           krb5_xfree(rparams->realm_keysalts);
+       if (rparams->realm_kdc_ports)
+           krb5_xfree(rparams->realm_kdc_ports);
+       krb5_xfree(rparams);
+    }
+    return(0);
+}
+
diff --git a/src/lib/kadm/configure.in b/src/lib/kadm/configure.in
new file mode 100644 (file)
index 0000000..a3d4ab5
--- /dev/null
@@ -0,0 +1,16 @@
+AC_INIT(configure.in)
+CONFIG_RULES
+AC_PROG_ARCHIVE
+AC_PROG_ARCHIVE_ADD
+AC_PROG_RANLIB
+AC_PROG_INSTALL
+AC_HEADER_STDARG
+AC_HAVE_HEADERS(pwd.h syslog.h)
+AC_HAVE_FUNCS(srand48 srand srandom syslog openlog closelog)
+AC_FUNC_CHECK(vsprintf,AC_DEFINE(HAVE_VSPRINTF))
+AC_PROG_AWK
+KRB5_RUN_FLAGS
+LinkFileDir(../libkadm.a, libkadm.a, ./kadm)
+AppendRule([all-unix:: ../libkadm.a])
+AppendRule([all:: all-$(WHAT)])
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/lib/kadm/keysalt.c b/src/lib/kadm/keysalt.c
new file mode 100644 (file)
index 0000000..e8b9b4a
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * lib/kadm/keysalt.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * keysalt.c   - Routines to handle key/salt tuples.
+ */
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+static const char default_tupleseps[]  = ", \t";
+static const char default_ksaltseps[]  = ":.";
+
+/*
+ * krb5_keysalt_is_present()   - Determine if a key/salt pair is present
+ *                               in a list of key/salt tuples.
+ *
+ *     Salttype may be negative to indicate a search for only a enctype.
+ */
+krb5_boolean
+krb5_keysalt_is_present(ksaltlist, nksalts, enctype, salttype)
+    krb5_key_salt_tuple        *ksaltlist;
+    krb5_int32         nksalts;
+    krb5_enctype       enctype;
+    krb5_int32         salttype;
+{
+    krb5_boolean       foundit;
+    int                        i;
+
+    foundit = 0;
+    if (ksaltlist) {
+       for (i=0; i<nksalts; i++) {
+           if ((ksaltlist[i].ks_enctype == enctype) &&
+               ((ksaltlist[i].ks_salttype == salttype) ||
+                (salttype < 0))) {
+               foundit = 1;
+               break;
+           }
+       }
+    }
+    return(foundit);
+}
+
+/*
+ * krb5_keysalt_iterate()      - Do something for each unique key/salt
+ *                               combination.
+ *
+ * If ignoresalt set, then salttype is ignored.
+ */
+krb5_error_code
+krb5_keysalt_iterate(ksaltlist, nksalt, ignoresalt, iterator, arg)
+    krb5_key_salt_tuple        *ksaltlist;
+    krb5_int32         nksalt;
+    krb5_boolean       ignoresalt;
+    krb5_error_code    (*iterator) KRB5_NPROTOTYPE((krb5_key_salt_tuple *,
+                                                    krb5_pointer));
+    krb5_pointer       arg;
+{
+    int                        i;
+    krb5_error_code    kret;
+    krb5_key_salt_tuple        scratch;
+
+    kret = 0;
+    for (i=0; i<nksalt; i++) {
+       scratch.ks_enctype = ksaltlist[i].ks_enctype;
+       scratch.ks_salttype = (ignoresalt) ? -1 : ksaltlist[i].ks_salttype;
+       if (!krb5_keysalt_is_present(ksaltlist,
+                                    i,
+                                    scratch.ks_enctype,
+                                    scratch.ks_salttype)) {
+           if (kret = (*iterator)(&scratch, arg))
+               break;
+       }
+    }
+    return(kret);
+}
+
+/*
+ * krb5_string_to_keysalts()   - Convert a string representation to a list
+ *                               of key/salt tuples.
+ */
+krb5_error_code
+krb5_string_to_keysalts(string, tupleseps, ksaltseps, dups, ksaltp, nksaltp)
+    char               *string;
+    const char         *tupleseps;
+    const char         *ksaltseps;
+    krb5_boolean       dups;
+    krb5_key_salt_tuple        **ksaltp;
+    krb5_int32         *nksaltp;
+{
+    krb5_error_code    kret;
+    char               *kp, *sp, *ep;
+    char               sepchar, trailchar;
+    krb5_enctype       ktype;
+    krb5_int32         stype;
+    krb5_key_salt_tuple        *savep;
+    const char         *tseplist;
+    const char         *ksseplist;
+    const char         *septmp;
+    size_t             len;
+    
+    kret = 0;
+    kp = string;
+    tseplist = (tupleseps) ? tupleseps : default_tupleseps;
+    ksseplist = (ksaltseps) ? ksaltseps : default_ksaltseps;
+    while (kp) {
+       /* Attempt to find a separator */
+       ep = (char *) NULL;
+       if (*tseplist) {
+           septmp = tseplist;
+           for (ep = strchr(kp, (int) *septmp);
+                *(++septmp) && !ep;
+                ep = strchr(kp, (int) *septmp));
+       }
+
+       if (ep) {
+           trailchar = *ep;
+           *ep = '\0';
+           ep++;
+       }
+       /*
+        * kp points to something (hopefully) of the form:
+        *      <enctype><ksseplist><salttype>
+        *      or
+        *      <enctype>
+        */
+       sp = (char *) NULL;
+       /* Attempt to find a separator */
+       septmp = ksseplist;
+       for (sp = strchr(kp, (int) *septmp);
+            *(++septmp) && !sp;
+            ep = strchr(kp, (int) *septmp));
+
+       if (sp) {
+           /* Separate enctype from salttype */
+           sepchar = *sp;
+           *sp = '\0';
+           sp++;
+       }
+       else
+           stype = -1;
+
+       /*
+        * Attempt to parse enctype and salttype.  If we parse well
+        * then make sure that it specifies a unique key/salt combo
+        */
+       if (!krb5_string_to_enctype(kp, &ktype) &&
+           (!sp || !krb5_string_to_salttype(sp, &stype)) &&
+           (dups ||
+            !krb5_keysalt_is_present(*ksaltp, *nksaltp, ktype, stype))) {
+
+           /* Squirrel away old keysalt array */
+           savep = *ksaltp;
+           len = (size_t) *nksaltp;
+
+           /* Get new keysalt array */
+           if (*ksaltp = (krb5_key_salt_tuple *)
+               malloc((len + 1) * sizeof(krb5_key_salt_tuple))) {
+
+               /* Copy old keysalt if appropriate */
+               if (savep) {
+                   memcpy(*ksaltp, savep,
+                          len * sizeof(krb5_key_salt_tuple));
+                   krb5_xfree(savep);
+               }
+
+               /* Save our values */
+               (*ksaltp)[(*nksaltp)].ks_enctype = ktype;
+               (*ksaltp)[(*nksaltp)].ks_salttype = stype;
+               (*nksaltp)++;
+           }
+           else {
+               *ksaltp = savep;
+               break;
+           }
+       }
+       if (sp)
+           sp[-1] = sepchar;
+       if (ep)
+           ep[-1] = trailchar;
+       kp = ep;
+    }
+    return(kret);
+}
+
+
diff --git a/src/lib/kadm/krb5strings.M b/src/lib/kadm/krb5strings.M
new file mode 100644 (file)
index 0000000..de118ba
--- /dev/null
@@ -0,0 +1,250 @@
+.\" lib/kadm/krb5strings.M
+.\" Copyright 1995 by the Massachusetts Institute of Technology.
+.\"
+.\" Export of this software from the United States of America may
+.\"   require a specific license from the United States Government.
+.\"   It is the responsibility of any person or organization contemplating
+.\"   export to obtain such a license before exporting.
+.\" 
+.\" WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+.\" distribute this software and its documentation for any purpose and
+.\" without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" the name of M.I.T. not be used in advertising or publicity pertaining
+.\" to distribution of the software without specific, written prior
+.\" permission.  M.I.T. makes no representations about the suitability of
+.\" this software for any purpose.  It is provided "as is" without express
+.\" or implied warranty.
+.\" 
+.\"
+.TH KRB5STRINGS 3 "Kerberos Version 5.0" "MIT Project Athena"
+.SH NAME
+krb5strings \- String representations of Kerberos V5 internal data.
+.SH KEY TYPES
+The following strings specify valid key types for use by Kerberos V5.
+.TP 2i
+.I null
+Specifies KEYTYPE_NULL.
+.TP 2i
+.I des
+Specifies KEYTYPE_DES.
+
+.SH SALT TYPES
+The following strings specify valid salt types for use by Kerberos V5.
+.TP 2i
+.I normal
+Specifies KRB5_KDB_SALTTYPE_NORMAL.
+.TP 2i
+.I v4
+Specifies KRB5_KDB_SALTTYPE_V4.
+.TP 2i
+.I norealm
+Specifies KRB5_KDB_SALTTYPE_NOREALM.
+.TP 2i
+.I onlyrealm
+Specifies KRB5_KDB_SALTTYPE_ONLYREALM.
+.TP 2i
+.I afs3
+Specifies KRB5_KDB_SALTTYPE_AFS3.
+.TP 2i
+.I special
+Specifies KRB5_KDB_SALTTYPE_SPECIAL.
+
+.SH ENCRYPTION TYPES
+The following strings specify valid encryption types for use by Kerberos V5.
+.TP 2i
+.I null
+Specifies ETYPE_NULL.
+.TP 2i
+.I des-cbc-crc
+Specifies ETYPE_DES_CBC_CRC.
+.TP 2i
+.I des-cbc-md4
+Specifies ETYPE_DES_CBC_MD4.
+.TP 2i
+.I des-cbc-md5
+Specifies ETYPE_DES_CBC_MD5.
+.TP 2i
+.I raw-des-cbc
+Specifies ETYPE_RAW_DES_CBC.
+
+.SH CHECKSUM TYPES
+The following strings specify valid checksum types for use by Kerberos V5.
+.TP 2i
+.I crc32
+Specifies CKSUMTYPE_CRC32.
+.TP 2i
+.I md4
+Specifies CKSUMTYPE_RSA_MD4.
+.TP 2i
+.I md4-des
+Specifies CKSUMTYPE_RSA_MD4_DES.
+.TP 2i
+.I des-cbc
+Specifies CKSUMTYPE_DESCBC.
+.TP 2i
+.I md5
+Specifies CKSUMTYPE_RSA_MD5.
+.TP 2i
+.I md5-des
+Specifies CKSUMTYPE_RSA_MD5_DES.
+
+.SH PRINCIPAL FLAGS
+The following strings specify particular principal attributes for use by
+Kerberos V5.
+.TP 2i
+.I postdateable
+In the negative sense, specifies KRB5_KDB_DISALLOW_POSTDATED.
+.TP 2i
+.I forwardable
+In the negative sense, specifies KRB5_KDB_DISALLOW_FORWARDABLE.
+.TP 2i
+.I tgt-based
+In the negative sense, specifies KRB5_KDB_DISALLOW_TGT_BASED.
+.TP 2i
+.I renewable
+In the negative sense, specifies KRB5_KDB_DISALLOW_RENEWABLE.
+.TP 2i
+.I proxiable
+In the negative sense, specifies KRB5_KDB_DISALLOW_PROXIABLE.
+.TP 2i
+.I dup-skey
+In the negative sense, specifies KRB5_KDB_DISALLOW_DUP_SKEY.
+.TP 2i
+.I allow-tickets
+In the negative sense, specifies KRB5_KDB_DISALLOW_ALL_TIX.
+.TP 2i
+.I preauth
+Specifies KRB5_KDB_REQUIRES_PRE_AUTH.
+.TP 2i
+.I hwauth
+Specifies KRB5_KDB_REQUIRES_HW_AUTH.
+.TP 2i
+.I pwchange
+Specifies KRB5_KDB_REQUIRES_PWCHANGE.
+.TP 2i
+.I service
+In the negative sense, specifies KRB5_KDB_DISALLOW_SVR.
+.TP 2i
+.I pwservice
+Specifies KRB5_KDB_PWCHANGE_SERVICE.
+.TP 2i
+.I md5
+Specifies KRB5_KDB_SUPPORT_DESMD5.
+
+.SH ABSOLUTE TIME
+The following formats specify valid absolute time strings for use by Kerberos
+V5.  In the description the following abbreviations are used:
+.in +1i
+.B yy
+denotes the last two digits of the year.
+
+.B mm
+denotes the two digits representing the month (01 = January, 12 = December).
+
+.B dd
+denotes the two digits representing the day of the month.
+
+.B HH
+denotes the two digits representing the hour of the day (24-hour format).
+
+.B MM
+denotes the two digits representing the minute of the hour.
+
+.B SS
+denotes the two digits representing the second of the minute.
+.in -1i
+
+.TP 2i
+.I yymmddHHMMSS
+e.g. 951225093023 specifies 9:30:23 a.m. on 25 December 1995.
+.TP 2i
+.I yy.mm.dd.HH.MM.SS
+e.g. 95.12.25.09.30.23 specifies 9:30:23 a.m. on 25 December 1995.
+.TP 2i
+.I yymmddHHMM
+e.g. 9512250930 specifies 9:30 a.m. on 25 December 1995.
+.TP 2i
+.I HHMMSS
+e.g. 123056 specifies 12:30:56 p.m. today.
+.TP 2i
+.I HHMM
+e.g. 2130 specifies 9:30 p.m. today.
+.TP 2i
+.I HH:MM:SS
+e.g. 12:30:56 specifies 12:30:56 p.m. today.
+.TP 2i
+.I HH:MM
+e.g. 21:30 specifies 9:30 p.m. today.
+.PP
+The following formats are recognized if the target operating system supports
+the
+.B strptime(3)
+function.  See the
+.B strptime(3)
+manual page for a description of the format fields:
+.TP 2i
+.I %x:%X
+Specifies the locale-dependent short date format concatenated by a colon with
+the locale-dependent short time format.
+.TP 2i
+.I %d-%b-%Y:%T
+e.g. 10-January-1995:16:42:23 in U.S. locales.
+.TP 2i
+.I %d-%b-%Y:%R
+e.g. 10-January-1995:16:42 in U.S. locales.
+
+.SH DELTA TIME
+The following formats specify valid delta time strings for use by Kerberos
+V5.  In the description the following abbreviations are used:
+.in +1i
+.B d
+denotes a number of days.
+
+.B h[h]
+denotes one or two digits representing a number of hours.
+
+.B m[m]
+denotes one or two digits representing a number of minutes.
+
+.B s[s]
+denotes one or two digits representing a number of seconds.
+.in -1i
+.TP 2i
+.I d-hh:mm:ss
+e.g. 7-04:30:01 specifies seven days, four hours 30 minutes and one second.
+.TP 2i
+.I ddhhmmss
+e.g. 7d4h30m1s specifies seven days, four hours 30 minutes and one second.
+.TP 2i
+.I h:mm:ss
+e.g. 6:23:16 specifies six hours, 23 minutes and 16 seconds.
+.TP 2i
+.I hhmmss
+e.g. 6h23m16s specifies six hours 23 minutes and 16 seconds.
+.TP 2i
+.I h:mm
+e.g. 2:30 specifies two hours and 30 minutes.
+.TP 2i
+.I hhmm
+e.g. 2h30m specifies two hours and 30 minutes.
+.TP 2i
+.I dd
+e.g. 2d specifies two days.
+.TP 2i
+.I hh
+e.g. 4h specifies four hours.
+.TP 2i
+.I mm
+e.g. 30m specifies 30 minutes.
+.TP 2i
+.I ss
+e.g. 3600s specifies 3600 seconds.
+
+.SH SEE ALSO
+kdc.conf(5), krb5kdc(8), kdb5_edit(8), kadmin5(8), strptime(3)
+
+
+
diff --git a/src/lib/kadm/logger.c b/src/lib/kadm/logger.c
new file mode 100644 (file)
index 0000000..425c1cc
--- /dev/null
@@ -0,0 +1,940 @@
+/*
+ * lib/kadm/logger.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+#if !defined(_MSDOS)
+
+/*
+ * logger.c    - Handle logging functions for those who want it.
+ */
+#include "k5-int.h"
+#include "adm_proto.h"
+#include "com_err.h"
+#include <stdio.h>
+#if    HAVE_SYSLOG_H
+#include <syslog.h>
+#endif /* HAVE_SYSLOG_H */
+#if    HAVE_STDARG_H
+#include <stdarg.h>
+#else  /* HAVE_STDARG_H */
+#include <varargs.h>
+#endif /* HAVE_STDARG_H */
+
+#define        KRB5_KLOG_MAX_ERRMSG_SIZE       1024
+#ifndef        MAXHOSTNAMELEN
+#define        MAXHOSTNAMELEN  256
+#endif /* MAXHOSTNAMELEN */
+
+/* This is to assure that we have at least one match in the syslog stuff */
+#ifndef        LOG_AUTH
+#define        LOG_AUTH        0
+#endif /* LOG_AUTH */
+#ifndef        LOG_ERR
+#define        LOG_ERR         0
+#endif /* LOG_ERR */
+
+static const char lspec_parse_err_1[] =        "%s: cannot parse <%s>\n";
+static const char lspec_parse_err_2[] =        "%s: warning - logging entry syntax error\n";
+static const char log_file_err[] =     "%s: error writing to %s\n";
+static const char log_device_err[] =   "%s: error writing to %s device\n";
+static const char log_ufo_string[] =   "???";
+static const char log_emerg_string[] = "EMERGENCY";
+static const char log_alert_string[] = "ALERT";
+static const char log_crit_string[] =  "CRITICAL";
+static const char log_err_string[] =   "Error";
+static const char log_warning_string[] =       "Warning";
+static const char log_notice_string[] =        "Notice";
+static const char log_info_string[] =  "info";
+static const char log_debug_string[] = "debug";
+\f
+/*
+ * Output logging.
+ *
+ * Output logging is now controlled by the configuration file.  We can specify
+ * the following syntaxes under the [logging]->entity specification.
+ *     FILE<opentype><pathname>
+ *     SYSLOG[=<severity>[:<facility>]]
+ *     STDERR
+ *     CONSOLE
+ *     DEVICE=<device-spec>
+ *
+ * Where:
+ *     <opentype> is ":" for open/append, "=" for open/create.
+ *     <pathname> is a valid path name.
+ *     <severity> is one of: (default = ERR)
+ *             EMERG
+ *             ALERT
+ *             CRIT
+ *             ERR
+ *             WARNING
+ *             NOTICE
+ *             INFO
+ *             DEBUG
+ *     <facility> is one of: (default = AUTH)
+ *             KERN
+ *             USER
+ *             MAIL
+ *             DAEMON
+ *             AUTH
+ *             LPR
+ *             NEWS
+ *             UUCP
+ *             CRON
+ *             LOCAL0..LOCAL7
+ *     <device-spec> is a valid device specification.
+ */
+struct log_entry {
+    enum log_type { K_LOG_FILE,
+                       K_LOG_SYSLOG,
+                       K_LOG_STDERR,
+                       K_LOG_CONSOLE,
+                       K_LOG_DEVICE,
+                       K_LOG_NONE } log_type;
+    krb5_pointer log_2free;
+    union log_union {
+       struct log_file {
+           FILE        *lf_filep;
+           char        *lf_fname;
+       } log_file;
+       struct log_syslog {
+           int         ls_facility;
+           int         ls_severity;
+       } log_syslog;
+       struct log_device {
+           FILE        *ld_filep;
+           char        *ld_devname;
+       } log_device;
+    } log_union;
+};
+#define        lfu_filep       log_union.log_file.lf_filep
+#define        lfu_fname       log_union.log_file.lf_fname
+#define        lsu_facility    log_union.log_syslog.ls_facility
+#define        lsu_severity    log_union.log_syslog.ls_severity
+#define        ldu_filep       log_union.log_device.ld_filep
+#define        ldu_devname     log_union.log_device.ld_devname
+
+struct log_control {
+    struct log_entry   *log_entries;
+    int                        log_nentries;
+    char               *log_whoami;
+    char               *log_hostname;
+    krb5_boolean       log_opened;
+};
+
+static struct log_control log_control = {
+    (struct log_entry *) NULL,
+    0,
+    (char *) NULL,
+    (char *) NULL,
+    0
+};
+static struct log_entry        def_log_entry;
+
+/*
+ * These macros define any special processing that needs to happen for
+ * devices.  For unix, of course, this is hardly anything.
+ */
+#define        DEVICE_OPEN(d, m)       fopen(d, m)
+#define        CONSOLE_OPEN(m)         fopen("/dev/console", m)
+#define        DEVICE_PRINT(f, m)      ((fprintf(f, m) >= 0) ?                 \
+                                (fprintf(f, "\r\n"), fflush(f), 0) :   \
+                                -1)
+#define        DEVICE_CLOSE(d)         fclose(d)
+
+\f
+/*
+ * klog_com_err_proc() - Handle com_err(3) messages as specified by the
+ *                       profile.
+ */
+static void
+klog_com_err_proc(whoami, code, format, ap)
+    const char *whoami;
+    long       code;
+    const char *format;
+    va_list    ap;
+{
+    char       outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE];
+    int                lindex;
+    char       *actual_format;
+#if    HAVE_SYSLOG
+    int                log_pri = -1;
+#endif /* HAVE_SYSLOG */
+    char       *cp;
+    char       *syslogp;
+
+    /* Make the header */
+    sprintf(outbuf, "%s: ", whoami);
+    /*
+     * Squirrel away address after header for syslog since syslog makes
+     * a header
+     */
+    syslogp = &outbuf[strlen(outbuf)];
+
+    /* If reporting an error message, separate it. */
+    if (code) {
+       strcat(outbuf, error_message(code));
+       strcat(outbuf, " - ");
+    }
+    cp = &outbuf[strlen(outbuf)];
+    
+    actual_format = (char *) format;
+#if    HAVE_SYSLOG
+    /*
+     * This is an unpleasant hack.  If the first character is less than
+     * 8, then we assume that it is a priority.
+     *
+     * Since it is not guaranteed that there is a direct mapping between
+     * syslog priorities (e.g. Ultrix and old BSD), we resort to this
+     * intermediate representation.
+     */
+    if ((((unsigned char) *format) > 0) && (((unsigned char) *format) <= 8)) {
+       actual_format = (char *) (format + 1);
+       switch ((unsigned char) *format) {
+#ifdef LOG_EMERG
+       case 1:
+           log_pri = LOG_EMERG;
+           break;
+#endif /* LOG_EMERG */
+#ifdef LOG_ALERT
+       case 2:
+           log_pri = LOG_ALERT;
+           break;
+#endif /* LOG_ALERT */
+#ifdef LOG_CRIT
+       case 3:
+           log_pri = LOG_CRIT;
+           break;
+#endif /* LOG_CRIT */
+       default:
+       case 4:
+           log_pri = LOG_ERR;
+           break;
+#ifdef LOG_WARNING
+       case 5:
+           log_pri = LOG_WARNING;
+           break;
+#endif /* LOG_WARNING */
+#ifdef LOG_NOTICE
+       case 6:
+           log_pri = LOG_NOTICE;
+           break;
+#endif /* LOG_NOTICE */
+#ifdef LOG_INFO
+       case 7:
+           log_pri = LOG_INFO;
+           break;
+#endif /* LOG_INFO */
+#ifdef LOG_DEBUG
+       case 8:
+           log_pri = LOG_DEBUG;
+           break;
+#endif /* LOG_DEBUG */
+       }
+    } 
+#endif /* HAVE_SYSLOG */
+
+    /* Now format the actual message */
+#if    HAVE_VSPRINTF
+    vsprintf(cp, actual_format, ap);
+#else  /* HAVE_VSPRINTF */
+    sprintf(cp, actual_format, ((int *) ap)[0], ((int *) ap)[1],
+           ((int *) ap)[2], ((int *) ap)[3],
+           ((int *) ap)[4], ((int *) ap)[5]);
+#endif /* HAVE_VSPRINTF */
+    
+    /*
+     * Now that we have the message formatted, perform the output to each
+     * logging specification.
+     */
+    for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
+       switch (log_control.log_entries[lindex].log_type) {
+       case K_LOG_FILE:
+       case K_LOG_STDERR:
+           /*
+            * Files/standard error.
+            */
+           if (fprintf(log_control.log_entries[lindex].lfu_filep,
+                       outbuf) < 0) {
+               /* Attempt to report error */
+               fprintf(stderr, log_file_err, whoami,
+                       log_control.log_entries[lindex].lfu_fname);
+           }
+           else {
+               fprintf(log_control.log_entries[lindex].lfu_filep, "\n");
+               fflush(log_control.log_entries[lindex].lfu_filep);
+           }
+           break;
+       case K_LOG_CONSOLE:
+       case K_LOG_DEVICE:
+           /*
+            * Devices (may need special handling)
+            */
+           if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep,
+                            outbuf) < 0) {
+               /* Attempt to report error */
+               fprintf(stderr, log_device_err, whoami,
+                       log_control.log_entries[lindex].ldu_devname);
+           }
+           break;
+#if    HAVE_SYSLOG
+       case K_LOG_SYSLOG:
+           /*
+            * System log.
+            */
+           /*
+            * If we have specified a priority through our hackery, then
+            * use it, otherwise use the default.
+            */
+           if (log_pri >= 0)
+               log_pri |= log_control.log_entries[lindex].lsu_facility;
+           else
+               log_pri = log_control.log_entries[lindex].lsu_facility |
+                   log_control.log_entries[lindex].lsu_severity;
+                                              
+           /* Log the message with our header trimmed off */
+           syslog(log_pri, syslogp);
+           break;
+#endif /* HAVE_SYSLOG */
+       default:
+           break;
+       }
+    }
+}
+\f
+/*
+ * krb5_klog_init()    - Initialize logging.
+ *
+ * This routine parses the syntax described above to specify destinations for
+ * com_err(3) or krb5_klog_syslog() messages generated by the caller.
+ *
+ * Parameters:
+ *     kcontext        - Kerberos context.
+ *     ename           - Entity name as it is to appear in the profile.
+ *     whoami          - Entity name as it is to appear in error output.
+ *     do_com_err      - Take over com_err(3) processing.
+ *
+ * Implicit inputs:
+ *     stderr          - This is where STDERR output goes.
+ *
+ * Implicit outputs:
+ *     log_nentries    - Number of log entries, both valid and invalid.
+ *     log_control     - List of entries (log_nentries long) which contains
+ *                       data for klog_com_err_proc() to use to determine
+ *                       where/how to send output.
+ */
+krb5_error_code
+krb5_klog_init(kcontext, ename, whoami, do_com_err)
+    krb5_context       kcontext;
+    char               *ename;
+    char               *whoami;
+    krb5_boolean       do_com_err;
+{
+    const char *logging_profent[3];
+    const char *logging_defent[3];
+    char       **logging_specs;
+    int                i, ngood;
+    char       *cp, *cp2;
+    char       savec;
+    int                error;
+    int                do_openlog, log_facility;
+    FILE       *f;
+
+    /* Initialize */
+    do_openlog = 0;
+    log_facility = 0;
+
+    /*
+     * Look up [logging]-><ename> in the profile.  If that doesn't
+     * succeed, then look for [logging]->default.
+     */
+    logging_profent[0] = "logging";
+    logging_profent[1] = ename;
+    logging_profent[2] = (char *) NULL;
+    logging_defent[0] = "logging";
+    logging_defent[1] = "default";
+    logging_defent[2] = (char *) NULL;
+    logging_specs = (char **) NULL;
+    ngood = 0;
+    log_control.log_nentries = 0;
+    if (!profile_get_values(kcontext->profile,
+                           logging_profent,
+                           &logging_specs) ||
+       !profile_get_values(kcontext->profile,
+                           logging_defent,
+                           &logging_specs)) {
+       /*
+        * We have a match, so we first count the number of elements
+        */
+       for (log_control.log_nentries = 0;
+            logging_specs[log_control.log_nentries];
+            log_control.log_nentries++);
+
+       /*
+        * Now allocate our structure.
+        */
+       log_control.log_entries = (struct log_entry *)
+           malloc(log_control.log_nentries * sizeof(struct log_entry));
+       if (log_control.log_entries) {
+           /*
+            * Scan through the list.
+            */
+           for (i=0; i<log_control.log_nentries; i++) {
+               log_control.log_entries[i].log_type = K_LOG_NONE;
+               log_control.log_entries[i].log_2free = logging_specs[i];
+               /*
+                * The format is:
+                *      <whitespace><data><whitespace>
+                * so, trim off the leading and trailing whitespace here.
+                */
+               for (cp = logging_specs[i]; isspace(*cp); cp++);
+               for (cp2 = &logging_specs[i][strlen(logging_specs[i])-1];
+                    isspace(*cp2); cp2--);
+               cp2++;
+               *cp2 = '\0';
+               /*
+                * Is this a file?
+                */
+               if (!strncasecmp(cp, "FILE", 4)) {
+                   /*
+                    * Check for append/overwrite, then open the file.
+                    */
+                   if (cp[4] == ':' || cp[4] == '=') {
+                       f = fopen(&cp[5], (cp[4] == ':') ? "a+" : "w");
+                       if (f) {
+                           log_control.log_entries[i].lfu_filep = f;
+                           log_control.log_entries[i].log_type = K_LOG_FILE;
+                           log_control.log_entries[i].lfu_fname = &cp[5];
+                       } else {
+                           fprintf(stderr,"Couldn't open log file %s: %s\n",
+                                   &cp[5], error_message(errno));
+                           continue;
+                       }
+                   }
+               }
+#if    HAVE_SYSLOG
+               /*
+                * Is this a syslog?
+                */
+               else if (!strncasecmp(cp, "SYSLOG", 6)) {
+                   error = 0;
+                   log_control.log_entries[i].lsu_facility = LOG_AUTH;
+                   log_control.log_entries[i].lsu_severity = LOG_ERR;
+                   /*
+                    * Is there a severify specified?
+                    */
+                   if (cp[6] == ':') {
+                       /*
+                        * Find the end of the severity.
+                        */
+                       if (cp2 = strchr(&cp[7], ':')) {
+                           savec = *cp2;
+                           *cp2 = '\0';
+                           cp2++;
+                       }
+
+                       /*
+                        * Match a severity.
+                        */
+                       if (!strcasecmp(&cp[7], "ERR")) {
+                           log_control.log_entries[i].lsu_severity = LOG_ERR;
+                       }
+#ifdef LOG_EMERG
+                       else if (!strcasecmp(&cp[7], "EMERG")) {
+                           log_control.log_entries[i].lsu_severity =
+                               LOG_EMERG;
+                       }
+#endif /* LOG_EMERG */
+#ifdef LOG_ALERT
+                       else if (!strcasecmp(&cp[7], "ALERT")) {
+                           log_control.log_entries[i].lsu_severity =
+                               LOG_ALERT;
+                       }
+#endif /* LOG_ALERT */
+#ifdef LOG_CRIT
+                       else if (!strcasecmp(&cp[7], "CRIT")) {
+                           log_control.log_entries[i].lsu_severity = LOG_CRIT;
+                       }
+#endif /* LOG_CRIT */
+#ifdef LOG_WARNING
+                       else if (!strcasecmp(&cp[7], "WARNING")) {
+                           log_control.log_entries[i].lsu_severity =
+                               LOG_WARNING;
+                       }
+#endif /* LOG_WARNING */
+#ifdef LOG_NOTICE
+                       else if (!strcasecmp(&cp[7], "NOTICE")) {
+                           log_control.log_entries[i].lsu_severity =
+                               LOG_NOTICE;
+                       }
+#endif /* LOG_NOTICE */
+#ifdef LOG_INFO
+                       else if (!strcasecmp(&cp[7], "INFO")) {
+                           log_control.log_entries[i].lsu_severity = LOG_INFO;
+                       }
+#endif /* LOG_INFO */
+#ifdef LOG_DEBUG
+                       else if (!strcasecmp(&cp[7], "DEBUG")) {
+                           log_control.log_entries[i].lsu_severity =
+                               LOG_DEBUG;
+                       }
+#endif /* LOG_DEBUG */
+                       else
+                           error = 1;
+
+                       /*
+                        * If there is a facility present, then parse that.
+                        */
+                       if (cp2) {
+                           if (!strcasecmp(cp2, "AUTH")) {
+                               log_control.log_entries[i].lsu_facility = LOG_AUTH;
+                           }
+#ifdef LOG_KERN
+                           else if (!strcasecmp(cp2, "KERN")) {
+                               log_control.log_entries[i].lsu_facility = LOG_KERN;
+                           }
+#endif /* LOG_KERN */
+#ifdef LOG_USER
+                           else if (!strcasecmp(cp2, "USER")) {
+                               log_control.log_entries[i].lsu_facility = LOG_USER;
+                           }
+#endif /* LOG_USER */
+#ifdef LOG_MAIL
+                           else if (!strcasecmp(cp2, "MAIL")) {
+                               log_control.log_entries[i].lsu_facility = LOG_MAIL;
+                           }
+#endif /* LOG_MAIL */
+#ifdef LOG_DAEMON
+                           else if (!strcasecmp(cp2, "DAEMON")) {
+                               log_control.log_entries[i].lsu_facility = LOG_DAEMON;
+                           }
+#endif /* LOG_DAEMON */
+#ifdef LOG_LPR
+                           else if (!strcasecmp(cp2, "LPR")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LPR;
+                           }
+#endif /* LOG_LPR */
+#ifdef LOG_NEWS
+                           else if (!strcasecmp(cp2, "NEWS")) {
+                               log_control.log_entries[i].lsu_facility = LOG_NEWS;
+                           }
+#endif /* LOG_NEWS */
+#ifdef LOG_UUCP
+                           else if (!strcasecmp(cp2, "UUCP")) {
+                               log_control.log_entries[i].lsu_facility = LOG_UUCP;
+                           }
+#endif /* LOG_UUCP */
+#ifdef LOG_CRON
+                           else if (!strcasecmp(cp2, "CRON")) {
+                               log_control.log_entries[i].lsu_facility = LOG_CRON;
+                           }
+#endif /* LOG_CRON */
+#ifdef LOG_LOCAL0
+                           else if (!strcasecmp(cp2, "LOCAL0")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL0;
+                           }
+#endif /* LOG_LOCAL0 */
+#ifdef LOG_LOCAL1
+                           else if (!strcasecmp(cp2, "LOCAL1")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL1;
+                           }
+#endif /* LOG_LOCAL1 */
+#ifdef LOG_LOCAL2
+                           else if (!strcasecmp(cp2, "LOCAL2")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL2;
+                           }
+#endif /* LOG_LOCAL2 */
+#ifdef LOG_LOCAL3
+                           else if (!strcasecmp(cp2, "LOCAL3")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL3;
+                           }
+#endif /* LOG_LOCAL3 */
+#ifdef LOG_LOCAL4
+                           else if (!strcasecmp(cp2, "LOCAL4")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL4;
+                           }
+#endif /* LOG_LOCAL4 */
+#ifdef LOG_LOCAL5
+                           else if (!strcasecmp(cp2, "LOCAL5")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL5;
+                           }
+#endif /* LOG_LOCAL5 */
+#ifdef LOG_LOCAL6
+                           else if (!strcasecmp(cp2, "LOCAL6")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL6;
+                           }
+#endif /* LOG_LOCAL6 */
+#ifdef LOG_LOCAL7
+                           else if (!strcasecmp(cp2, "LOCAL7")) {
+                               log_control.log_entries[i].lsu_facility = LOG_LOCAL7;
+                           }
+#endif /* LOG_LOCAL7 */
+                           cp2--;
+                           *cp2 = savec;
+                       }
+                   }
+                   if (!error) {
+                       log_control.log_entries[i].log_type = K_LOG_SYSLOG;
+                       do_openlog = 1;
+                       log_facility = log_control.log_entries[i].lsu_facility;
+                   }
+               }
+#endif /* HAVE_SYSLOG */
+               /*
+                * Is this a standard error specification?
+                */
+               else if (!strcasecmp(cp, "STDERR")) {
+                   if (log_control.log_entries[i].lfu_filep =
+                       fdopen(fileno(stderr), "a+")) {
+                       log_control.log_entries[i].log_type = K_LOG_STDERR;
+                       log_control.log_entries[i].lfu_fname =
+                           "standard error";
+                   }
+               }
+               /*
+                * Is this a specification of the console?
+                */
+               else if (!strcasecmp(cp, "CONSOLE")) {
+                   if (log_control.log_entries[i].ldu_filep =
+                       CONSOLE_OPEN("a+")) {
+                       log_control.log_entries[i].log_type = K_LOG_CONSOLE;
+                       log_control.log_entries[i].ldu_devname = "console";
+                   }
+               }
+               /*
+                * Is this a specification of a device?
+                */
+               else if (!strncasecmp(cp, "DEVICE", 6)) {
+                   /*
+                    * We handle devices very similarly to files.
+                    */
+                   if (cp[6] == '=') {
+                       if (log_control.log_entries[i].ldu_filep =
+                           DEVICE_OPEN(&cp[7], "w")) {
+                           log_control.log_entries[i].log_type = K_LOG_DEVICE;
+                           log_control.log_entries[i].ldu_devname = &cp[7];
+                       }
+                   }
+               }
+               /*
+                * See if we successfully parsed this specification.
+                */
+               if (log_control.log_entries[i].log_type == K_LOG_NONE) {
+                   fprintf(stderr, lspec_parse_err_1, whoami, cp);
+                   fprintf(stderr, lspec_parse_err_2, whoami);
+               }
+               else
+                   ngood++;
+           }
+       }
+       /*
+        * If we didn't find anything, then free our lists.
+        */
+       if (ngood == 0) {
+           for (i=0; i<log_control.log_nentries; i++)
+               free(logging_specs[i]);
+       }
+       free(logging_specs);
+    }
+    /*
+     * If we didn't find anything, go for the default which is to log to
+     * the system log.
+     */
+    if (ngood == 0) {
+       if (log_control.log_entries)
+           free(log_control.log_entries);
+       log_control.log_entries = &def_log_entry;
+       log_control.log_entries->log_type = K_LOG_SYSLOG;
+       log_control.log_entries->log_2free = (krb5_pointer) NULL;
+       log_control.log_entries->lsu_facility = LOG_AUTH;
+       log_control.log_entries->lsu_severity = LOG_ERR;
+       log_control.log_nentries = 1;
+    }
+    if (log_control.log_nentries) {
+       if (log_control.log_whoami = (char *) malloc(strlen(whoami)+1))
+           strcpy(log_control.log_whoami, whoami);
+       if (log_control.log_hostname = (char *) malloc(MAXHOSTNAMELEN))
+           gethostname(log_control.log_hostname, MAXHOSTNAMELEN);
+#if    HAVE_OPENLOG
+       if (do_openlog) {
+           openlog(whoami, LOG_NDELAY|LOG_PID, log_facility);
+           log_control.log_opened = 1;
+       }
+#endif /* HAVE_OPENLOG */
+       if (do_com_err)
+           (void) set_com_err_hook(klog_com_err_proc);
+    }
+    return((log_control.log_nentries) ? 0 : ENOENT);
+}
+
+/*
+ * krb5_klog_close()   - Close the logging context and free all data.
+ */
+void
+krb5_klog_close(kcontext)
+    krb5_context       kcontext;
+{
+    int lindex;
+    (void) reset_com_err_hook();
+    for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
+       switch (log_control.log_entries[lindex].log_type) {
+       case K_LOG_FILE:
+       case K_LOG_STDERR:
+           /*
+            * Files/standard error.
+            */
+           fclose(log_control.log_entries[lindex].lfu_filep);
+           break;
+       case K_LOG_CONSOLE:
+       case K_LOG_DEVICE:
+           /*
+            * Devices (may need special handling)
+            */
+           DEVICE_CLOSE(log_control.log_entries[lindex].ldu_filep);
+           break;
+#if    HAVE_SYSLOG
+       case K_LOG_SYSLOG:
+           /*
+            * System log.
+            */
+           break;
+#endif /* HAVE_SYSLOG */
+       default:
+           break;
+       }
+       if (log_control.log_entries[lindex].log_2free)
+           free(log_control.log_entries[lindex].log_2free);
+    }
+    if (log_control.log_entries != &def_log_entry)
+       free(log_control.log_entries);
+    log_control.log_entries = (struct log_entry *) NULL;
+    log_control.log_nentries = 0;
+    if (log_control.log_whoami)
+       free(log_control.log_whoami);
+    log_control.log_whoami = (char *) NULL;
+    if (log_control.log_hostname)
+       free(log_control.log_hostname);
+    log_control.log_hostname = (char *) NULL;
+#if    HAVE_CLOSELOG
+    if (log_control.log_opened)
+       closelog();
+#endif /* HAVE_CLOSELOG */
+}
+
+/*
+ * severity2string()   - Convert a severity to a string.
+ */
+static char *
+severity2string(severity)
+    int        severity;
+{
+    int s;
+    const char *ss;
+
+    s = severity & LOG_PRIMASK;
+    ss = log_ufo_string;
+    switch (s) {
+#ifdef LOG_EMERG
+    case LOG_EMERG:
+       ss = log_emerg_string;
+       break;
+#endif /* LOG_EMERG */
+#ifdef LOG_ALERT
+    case LOG_ALERT:
+       ss = log_alert_string;
+       break;
+#endif /* LOG_ALERT */
+#ifdef LOG_CRIT
+    case LOG_CRIT:
+       ss = log_crit_string;
+       break;
+#endif /* LOG_CRIT */
+    case LOG_ERR:
+       ss = log_err_string;
+       break;
+#ifdef LOG_WARNING
+    case LOG_WARNING:
+       ss = log_warning_string;
+       break;
+#endif /* LOG_WARNING */
+#ifdef LOG_NOTICE
+    case LOG_NOTICE:
+       ss = log_notice_string;
+       break;
+#endif /* LOG_NOTICE */
+#ifdef LOG_INFO
+    case LOG_INFO:
+       ss = log_info_string;
+       break;
+#endif /* LOG_INFO */
+#ifdef LOG_DEBUG
+    case LOG_DEBUG:
+       ss = log_debug_string;
+       break;
+#endif /* LOG_DEBUG */
+    }
+    return((char *) ss);
+}
+
+/*
+ * krb5_klog_syslog()  - Simulate the calling sequence of syslog(3), while
+ *                       also performing the logging redirection as specified
+ *                       by krb5_klog_init().
+ */
+static int
+klog_vsyslog(priority, format, arglist)
+    int                priority;
+    const char *format;
+    va_list    arglist;
+{
+    char       outbuf[KRB5_KLOG_MAX_ERRMSG_SIZE];
+    int                lindex;
+    char       *syslogp;
+    char       *cp;
+    time_t     now;
+#if    HAVE_STRFTIME
+    size_t     soff;
+#endif /* HAVE_STRFTIME */
+
+    /*
+     * Format a syslog-esque message of the format:
+     *
+     * (verbose form)
+     *                 <date> <hostname> <id>[<pid>](<priority>): <message>
+     *
+     * (short form)
+     *         <date> <message>
+     */
+    cp = outbuf;
+    (void) time(&now);
+#if    HAVE_STRFTIME
+    /*
+     * Format the date: mon dd hh:mm:ss
+     */
+    soff = strftime(outbuf, sizeof(outbuf), "%b %d %H:%M:%S", localtime(&now));
+    if (soff > 0)
+       cp += soff;
+    else
+       return(-1);
+#else  /* HAVE_STRFTIME */
+    /*
+     * Format the date:
+     * We ASSUME here that the output of ctime is of the format:
+     * dow mon dd hh:mm:ss tzs yyyy\n
+     *  012345678901234567890123456789
+     */
+    strncpy(outbuf, ctime(&now) + 4, 15);
+    cp += 15;
+#endif /* HAVE_STRFTIME */
+#ifdef VERBOSE_LOGS
+    sprintf(cp, " %s %s[%d](%s): ", 
+           log_control.log_hostname, log_control.log_whoami, getpid(),
+           severity2string(priority));
+#else
+    sprintf(cp, " ");
+#endif
+    syslogp = &outbuf[strlen(outbuf)];
+
+    /* Now format the actual message */
+#if    HAVE_VSPRINTF
+    vsprintf(syslogp, format, arglist);
+#else  /* HAVE_VSPRINTF */
+    sprintf(syslogp, format, ((int *) arglist)[0], ((int *) arglist)[1],
+           ((int *) arglist)[2], ((int *) arglist)[3],
+           ((int *) arglist)[4], ((int *) arglist)[5]);
+#endif /* HAVE_VSPRINTF */
+
+    /*
+     * Now that we have the message formatted, perform the output to each
+     * logging specification.
+     */
+    for (lindex = 0; lindex < log_control.log_nentries; lindex++) {
+       switch (log_control.log_entries[lindex].log_type) {
+       case K_LOG_FILE:
+       case K_LOG_STDERR:
+           /*
+            * Files/standard error.
+            */
+           if (fprintf(log_control.log_entries[lindex].lfu_filep, 
+                       outbuf) < 0) {
+               /* Attempt to report error */
+               fprintf(stderr, log_file_err,
+                       log_control.log_entries[lindex].lfu_fname);
+           }
+           else {
+               fprintf(log_control.log_entries[lindex].lfu_filep, "\n");
+               fflush(log_control.log_entries[lindex].lfu_filep);
+           }
+           break;
+       case K_LOG_CONSOLE:
+       case K_LOG_DEVICE:
+           /*
+            * Devices (may need special handling)
+            */
+           if (DEVICE_PRINT(log_control.log_entries[lindex].ldu_filep,
+                            outbuf) < 0) {
+               /* Attempt to report error */
+               fprintf(stderr, log_device_err,
+                       log_control.log_entries[lindex].ldu_devname);
+           }
+           break;
+#if    HAVE_SYSLOG
+       case K_LOG_SYSLOG:
+           /*
+            * System log.
+            */
+                                              
+           /* Log the message with our header trimmed off */
+           syslog(priority, syslogp);
+           break;
+#endif /* HAVE_SYSLOG */
+       default:
+           break;
+       }
+    }
+    return(0);
+}
+
+#if    HAVE_STDARG_H
+int
+krb5_klog_syslog(int priority, const char *format, ...)
+#else  /* HAVE_STDARG_H */
+int
+krb5_klog_syslog(priority, format, va_alist)
+    int                priority;
+    const char *format;
+    va_dcl
+#endif /* HAVE_STDARG_H */
+{
+    int                retval;
+    va_list    pvar;
+
+#if    HAVE_STDARG_H
+    va_start(pvar, format);
+#else  /* HAVE_STDARG_H */
+    va_start(pvar);
+#endif /* HAVE_STDARG_H */
+    retval = klog_vsyslog(priority, format, pvar);
+    va_end(pvar);
+    return(retval);
+}
+#endif /* !defined(_MSDOS) */
diff --git a/src/lib/kadm/str_conv.c b/src/lib/kadm/str_conv.c
new file mode 100644 (file)
index 0000000..ccd0da0
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * lib/kadm/str_conv.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * str_conv.c - Convert between strings and Kerberos internal data.
+ */
+
+/*
+ * Table of contents:
+ *
+ * String decoding:
+ * ----------------
+ * krb5_string_to_flags()      - Convert string to krb5_flags.
+ *
+ * String encoding:
+ * ----------------
+ * krb5_flags_to_string()      - Convert krb5_flags to string.
+ */
+
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+/*
+ * Local data structures.
+ */
+struct flags_lookup_entry {
+    krb5_flags         fl_flags;               /* Flag                 */
+    krb5_boolean       fl_sense;               /* Sense of the flag    */
+    const char *       fl_specifier;           /* How to recognize it  */
+    const char *       fl_output;              /* How to spit it out   */
+};
+
+/*
+ * Local strings
+ */
+
+/* Keytype strings */
+/* Flags strings */
+static const char flags_pdate_in[]     = "postdateable";
+static const char flags_fwd_in[]       = "forwardable";
+static const char flags_tgtbased_in[]  = "tgt-based";
+static const char flags_renew_in[]     = "renewable";
+static const char flags_proxy_in[]     = "proxiable";
+static const char flags_dup_skey_in[]  = "dup-skey";
+static const char flags_tickets_in[]   = "allow-tickets";
+static const char flags_preauth_in[]   = "preauth";
+static const char flags_hwauth_in[]    = "hwauth";
+static const char flags_pwchange_in[]  = "pwchange";
+static const char flags_service_in[]   = "service";
+static const char flags_pwsvc_in[]     = "pwservice";
+static const char flags_md5_in[]       = "md5";
+static const char flags_pdate_out[]    = "Not Postdateable";
+static const char flags_fwd_out[]      = "Not Forwardable";
+static const char flags_tgtbased_out[] = "No TGT-based requests";
+static const char flags_renew_out[]    = "Not renewable";
+static const char flags_proxy_out[]    = "Not proxiable";
+static const char flags_dup_skey_out[] = "No DUP_SKEY requests";
+static const char flags_tickets_out[]  = "All Tickets Disallowed";
+static const char flags_preauth_out[]  = "Preauthorization required";
+static const char flags_hwauth_out[]   = "HW Authorization required";
+static const char flags_pwchange_out[] = "Password Change required";
+static const char flags_service_out[]  = "Service Disabled";
+static const char flags_pwsvc_out[]    = "Password Changing Service";
+static const char flags_md5_out[]      = "RSA-MD5 supported";
+static const char flags_default_neg[]  = "-";
+static const char flags_default_sep[]  = " ";
+
+/*
+ * Lookup tables.
+ */
+
+static const struct flags_lookup_entry flags_table[] = {
+/* flag                                sense   input specifier    output string     */
+/*----------------------------- -------        ------------------ ------------------*/
+{ KRB5_KDB_DISALLOW_POSTDATED, 0,      flags_pdate_in,    flags_pdate_out   },
+{ KRB5_KDB_DISALLOW_FORWARDABLE,0,     flags_fwd_in,      flags_fwd_out     },
+{ KRB5_KDB_DISALLOW_TGT_BASED, 0,      flags_tgtbased_in, flags_tgtbased_out},
+{ KRB5_KDB_DISALLOW_RENEWABLE, 0,      flags_renew_in,    flags_renew_out   },
+{ KRB5_KDB_DISALLOW_PROXIABLE, 0,      flags_proxy_in,    flags_proxy_out   },
+{ KRB5_KDB_DISALLOW_DUP_SKEY,  0,      flags_dup_skey_in, flags_dup_skey_out},
+{ KRB5_KDB_DISALLOW_ALL_TIX,   0,      flags_tickets_in,  flags_tickets_out },
+{ KRB5_KDB_REQUIRES_PRE_AUTH,  1,      flags_preauth_in,  flags_preauth_out },
+{ KRB5_KDB_REQUIRES_HW_AUTH,   1,      flags_hwauth_in,   flags_hwauth_out  },
+{ KRB5_KDB_REQUIRES_PWCHANGE,  1,      flags_pwchange_in, flags_pwchange_out},
+{ KRB5_KDB_DISALLOW_SVR,       0,      flags_service_in,  flags_service_out },
+{ KRB5_KDB_PWCHANGE_SERVICE,   1,      flags_pwsvc_in,    flags_pwsvc_out   },
+{ KRB5_KDB_SUPPORT_DESMD5,     1,      flags_md5_in,      flags_md5_out     }
+};
+static const int flags_table_nents = sizeof(flags_table)/
+                                    sizeof(flags_table[0]);
+
+\f
+krb5_error_code
+krb5_string_to_flags(string, positive, negative, flagsp)
+    char       * string;
+    const char * positive;
+    const char * negative;
+    krb5_flags * flagsp;
+{
+    int        i;
+    int        found;
+    const char *neg;
+    size_t     nsize, psize;
+    int                cpos;
+    int                sense;
+
+    found = 0;
+    /* We need to have a way to negate it. */
+    neg = (negative) ? negative : flags_default_neg;
+    nsize = strlen(neg);
+    psize = (positive) ? strlen(positive) : 0;
+
+    cpos = 0;
+    sense = 1;
+    /* First check for positive or negative sense */
+    if (!strncasecmp(neg, string, nsize)) {
+       sense = 0;
+       cpos += (int) nsize;
+    }
+    else if (psize && !strncasecmp(positive, string, psize)) {
+       cpos += (int) psize;
+    }
+
+    for (i=0; i<flags_table_nents; i++) {
+       if (!strcasecmp(&string[cpos], flags_table[i].fl_specifier)) {
+           found = 1;
+           if (sense == (int) flags_table[i].fl_sense)
+               *flagsp |= flags_table[i].fl_flags;
+           else
+               *flagsp &= ~flags_table[i].fl_flags;
+
+           break;
+       }
+    }
+    return((found) ? 0 : EINVAL);
+}
+
+krb5_error_code
+krb5_flags_to_string(flags, sep, buffer, buflen)
+    krb5_flags flags;
+    const char * sep;
+    char       * buffer;
+    size_t     buflen;
+{
+    int                        i;
+    krb5_flags         pflags;
+    const char         *sepstring;
+    char               *op;
+    int                        initial;
+    krb5_error_code    retval;
+
+    retval = 0;
+    op = buffer;
+    pflags = 0;
+    initial = 1;
+    sepstring = (sep) ? sep : flags_default_sep;
+    /* Blast through the table matching all we can */
+    for (i=0; i<flags_table_nents; i++) {
+       if (flags & flags_table[i].fl_flags) {
+           /* Found a match, see if it'll fit into the output buffer */
+           if ((op+strlen(flags_table[i].fl_output)+strlen(sepstring)) <
+               (buffer + buflen)) {
+               if (!initial) {
+                   strcpy(op, sep);
+                   op += strlen(sep);
+               }
+               initial = 0;
+               strcpy(op, flags_table[i].fl_output);
+               op += strlen(flags_table[i].fl_output);
+           }
+           else {
+               retval = ENOMEM;
+               break;
+           }
+           /* Keep track of what we matched */
+           pflags |= flags_table[i].fl_flags;
+       }
+    }
+    if (!retval) {
+       /* See if there's any leftovers */
+       if (flags & ~pflags)
+           retval = EINVAL;
+       else if (initial)
+           *buffer = '\0';
+    }
+    return(retval);
+}
+
+krb5_error_code
+krb5_input_flag_to_string(flag, buffer, buflen)
+    int                flag;
+    char       * buffer;
+    size_t     buflen;
+{
+    if(flag < 0 || flag >= flags_table_nents) return ENOENT; /* End of list */
+    if(strlen(flags_table[flag].fl_specifier) > buflen) return ENOMEM;
+    strcpy(buffer, flags_table[flag].fl_specifier);
+    return  0;
+}
diff --git a/src/lib/kadm/t_dbentry.c b/src/lib/kadm/t_dbentry.c
new file mode 100644 (file)
index 0000000..6c71234
--- /dev/null
@@ -0,0 +1,965 @@
+/*
+ * lib/kadm/t_dbentry.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * t_dbentry.c - Test function of krb5_adm_{proto_to_dbent,dbent_to_proto}.
+ */
+
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+#if    HAVE_SRAND48
+#define        SRAND   srand48
+#define        RAND    lrand48
+#define        RAND_TYPE       long
+#endif /* HAVE_SRAND48 */
+
+#if    !defined(RAND_TYPE) && defined(HAVE_SRAND)
+#define        SRAND   srand
+#define        RAND    rand
+#define        RAND_TYPE       int
+#endif /* !defined(RAND_TYPE) && defined(HAVE_SRAND) */
+
+#if    !defined(RAND_TYPE) && defined(HAVE_SRANDOM)
+#define        SRAND   srandom
+#define        RAND    random
+#define        RAND_TYPE       long
+#endif /* !defined(RAND_TYPE) && defined(HAVE_SRANDOM) */
+
+#if    !defined(RAND_TYPE)
+There is no random number generator.
+#endif /* !defined(RAND_TYPE) */
+
+/*
+ * Generate a random event that has an a/b chance of succeeding
+ */
+#define        RANDOM_EVENT(a,b)       ((RAND() % b) < a)
+/* Define probabilities of generating each attribute type */
+#define        PASSWORD_EVENT          RANDOM_EVENT(3,5)
+#define        KVNO_EVENT              RANDOM_EVENT(2,5)
+#define        MAXLIFE_EVENT           RANDOM_EVENT(1,4)
+#define        MAXRENEWLIFE_EVENT      RANDOM_EVENT(1,4)
+#define        EXPIRATION_EVENT        RANDOM_EVENT(1,3)
+#define        PWEXPIRATION_EVENT      RANDOM_EVENT(1,3)
+#define        RANDOMKEY_EVENT         RANDOM_EVENT(1,8)
+#define        FLAGS_EVENT             RANDOM_EVENT(9,10)
+#define        SALT_EVENT              RANDOM_EVENT(7,16)
+#define        MKVNO_EVENT             RANDOM_EVENT(2,5)
+#define        LASTPWCHANGE_EVENT      RANDOM_EVENT(2,5)
+#define        LASTSUCCESS_EVENT       RANDOM_EVENT(2,5)
+#define        LASTFAILED_EVENT        RANDOM_EVENT(2,5)
+#define        FAILCOUNT_EVENT         RANDOM_EVENT(2,5)
+#define        MODNAME_EVENT           RANDOM_EVENT(2,5)
+#define        MODDATE_EVENT           RANDOM_EVENT(2,5)
+#define        EXTRA_EVENT             RANDOM_EVENT(1,5)
+#define        SET_EVENT               RANDOM_EVENT(1,4)
+
+/*
+ * Convert a time value to a string for output messages.
+ */
+static char *
+time2string(ts)
+    krb5_timestamp     ts;
+{
+    static char buf[1024];
+
+    strcpy(buf, ctime((time_t *) &ts));
+    /* Remove trailing \n */
+    buf[strlen(buf)-1] = '\0';
+    return(buf);
+}
+
+static krb5_boolean
+aux_data_inequal(in, out)
+    krb5_db_entry      *in, *out;
+{
+    krb5_tl_data       *intl, *outtl;
+    krb5_boolean       found;
+
+    if (in->n_tl_data != out->n_tl_data)
+       return(1);
+    found = 1;
+    for (intl = in->tl_data; intl; intl = intl->tl_data_next) {
+       found = 0;
+       for (outtl = out->tl_data; outtl; outtl = outtl->tl_data_next) {
+           if ((intl->tl_data_type == outtl->tl_data_type) &&
+               (intl->tl_data_length == outtl->tl_data_length) &&
+               !memcmp(intl->tl_data_contents,
+                       outtl->tl_data_contents,
+                       intl->tl_data_length)) {
+               outtl->tl_data_length = -outtl->tl_data_length;
+               found = 1;
+           }
+       }
+       if (!found)
+           break;
+    }
+    for (outtl = out->tl_data; outtl; outtl = outtl->tl_data_next) {
+       if (outtl->tl_data_length < 0)
+           outtl->tl_data_length = -outtl->tl_data_length;
+    }
+    return(!found);
+}
+
+static void
+print_auxdata(entp)
+    krb5_db_entry      *entp;
+{
+    krb5_tl_data       *tl;
+    int                        i;
+
+    for (tl = entp->tl_data; tl; tl = tl->tl_data_next) {
+       printf("tl_data(%d)[len=%d] ", tl->tl_data_type, tl->tl_data_length);
+       for (i=0; i<tl->tl_data_length; i++)
+           printf("%02x ", tl->tl_data_contents[i]);
+       printf("\n");
+    }
+}
+
+static krb5_boolean
+key_data_inequal(in, out)
+    krb5_db_entry      *in, *out;
+{
+    krb5_boolean       found;
+    int                i, j;
+
+    if (in->n_key_data != out->n_key_data)
+       return(1);
+    found = 1;
+    for (i=0; i<in->n_key_data; i++) {
+       found = 0;
+       for (j=0; j<out->n_key_data; j++) {
+           if ((in->key_data[i].key_data_kvno ==
+                out->key_data[j].key_data_kvno) &&
+               (in->key_data[i].key_data_type[0] ==
+                out->key_data[j].key_data_type[0]) &&
+               (in->key_data[i].key_data_type[1] ==
+                out->key_data[j].key_data_type[1]) &&
+               (in->key_data[i].key_data_length[0] ==
+                out->key_data[j].key_data_length[0]) &&
+               (in->key_data[i].key_data_length[1] ==
+                out->key_data[j].key_data_length[1]) &&
+               !memcmp(in->key_data[i].key_data_contents[0],
+                       out->key_data[j].key_data_contents[0],
+                       in->key_data[i].key_data_length[0]) &&
+               (!in->key_data[i].key_data_length[1] ||
+                !memcmp(in->key_data[i].key_data_contents[1],
+                       out->key_data[j].key_data_contents[1],
+                       in->key_data[i].key_data_length[1]))) {
+               out->key_data[j].key_data_length[0] = 
+                   -out->key_data[j].key_data_length[0];
+               found = 1;
+           }
+       }
+       if (!found)
+           break;
+    }
+    for (j=0; j<out->n_key_data; j++) {
+       if (out->key_data[j].key_data_length[0] < 0)
+           out->key_data[j].key_data_length[0] = 
+               -out->key_data[j].key_data_length[0];
+    }
+    return(!found);
+}
+
+static void
+print_keydata(entp)
+    krb5_db_entry      *entp;
+{
+    int                        i, j;
+
+    for (j=0; j<entp->n_key_data; j++) {
+       printf("key(vno=%d):key(type=%d)[contents= ",
+              entp->key_data[j].key_data_kvno,
+              entp->key_data[j].key_data_type[0]);
+       for (i=0; i<entp->key_data[j].key_data_length[0]; i++)
+           printf("%02x ", entp->key_data[j].key_data_contents[0][i]);
+       printf("] salt(type=%d)", entp->key_data[j].key_data_type[1]);
+       if (entp->key_data[j].key_data_length[1]) {
+           printf("[contents= ");
+           for (i=0; i<entp->key_data[j].key_data_length[1]; i++)
+               printf("%02x ", entp->key_data[j].key_data_contents[1][i]);
+           printf("]");
+       }
+       printf("\n");
+    }
+}
+
+static krb5_boolean
+extra_data_inequal(in, out)
+    krb5_db_entry      *in, *out;
+{
+    if (in->e_length != out->e_length)
+       return(1);
+    if (in->e_length && memcmp(in->e_data, out->e_data, (size_t) in->e_length))
+       return(1);
+    return(0);
+}
+
+static void
+print_extradata(entp)
+    krb5_db_entry      *entp;
+{
+    int i;
+
+    printf("extra:");
+    for (i=0; i<entp->e_length; i++)
+       printf("%02x ", entp->e_data[i]);
+    printf("\n");
+}
+
+/*
+ * Generate a database entry, either randomly, or using well known values.
+ */
+static void
+gen_dbent(kcontext, dbentp, isrand, validp, pwdp, expectp)
+    krb5_context       kcontext;
+    krb5_db_entry      *dbentp;
+    krb5_boolean       isrand;
+    krb5_ui_4          *validp;
+    char               **pwdp;
+    krb5_boolean       *expectp;
+{
+    time_t             now;
+    krb5_boolean       is_set;
+    size_t             pwlen;
+    int                        i;
+    static char                *defpass = "testpassword";
+    static char                *defprinc = "testprinc/instance@realm";
+
+    now = time((time_t *) NULL);
+    is_set = ((*validp & KRB5_ADM_M_SET) != 0);
+
+    /* Do password on set */
+    if (isrand) {
+       if (PASSWORD_EVENT) {
+           pwlen = 9 + (RAND() % 56);
+           *pwdp = (char *) malloc(pwlen);
+           for (i=0; i<pwlen-1; i++) {
+               (*pwdp)[i] = RAND() % 128;
+               while (!isalnum((int) (*pwdp)[i]))
+                   (*pwdp)[i] = RAND() % 128;
+           }
+           (*pwdp)[pwlen-1] = '\0';
+           *validp |= KRB5_ADM_M_PASSWORD;
+       }
+    }
+    else {
+       if (is_set) {
+           *pwdp = (char *) malloc(strlen(defpass)+1);
+           strcpy(*pwdp, defpass);
+           *validp |= KRB5_ADM_M_PASSWORD;
+       }
+    }
+
+    /* Do maxlife */
+    if (isrand) {
+       if (MAXLIFE_EVENT) {
+           dbentp->max_life = RAND();
+           *validp |= KRB5_ADM_M_MAXLIFE;
+       }
+    }
+    else {
+       dbentp->max_life = KRB5_KDB_MAX_LIFE;
+       *validp |= KRB5_ADM_M_MAXLIFE;
+    }
+
+    /* Do maxrenewlife */
+    if (isrand) {
+       if (MAXRENEWLIFE_EVENT) {
+           dbentp->max_renewable_life = RAND();
+           *validp |= KRB5_ADM_M_MAXRENEWLIFE;
+       }
+    }
+    else {
+       dbentp->max_renewable_life = KRB5_KDB_MAX_RLIFE;
+       *validp |= KRB5_ADM_M_MAXRENEWLIFE;
+    }
+
+    /* Do expiration */
+    if (isrand) {
+       if (EXPIRATION_EVENT) {
+           dbentp->expiration = RAND();
+           *validp |= KRB5_ADM_M_EXPIRATION;
+       }
+    }
+    else {
+       dbentp->expiration = KRB5_KDB_EXPIRATION;
+       *validp |= KRB5_ADM_M_EXPIRATION;
+    }
+
+    /* Do pw_expiration */
+    if (isrand) {
+       if (PWEXPIRATION_EVENT) {
+           dbentp->pw_expiration = RAND();
+           *validp |= KRB5_ADM_M_PWEXPIRATION;
+       }
+    }
+    else {
+       dbentp->pw_expiration = (krb5_timestamp) now + 3600;
+       *validp |= KRB5_ADM_M_PWEXPIRATION;
+    }
+
+    /* Do randomkey - 1/8 probability of doing randomkey */
+    if (isrand && (RANDOMKEY_EVENT)) {
+       *validp |= KRB5_ADM_M_RANDOMKEY;
+    }
+
+    /* Do flags */
+    if (isrand) {
+       if (FLAGS_EVENT) {
+           dbentp->attributes = RAND();
+           *validp |= KRB5_ADM_M_FLAGS;
+       }
+    }
+    else {
+       dbentp->attributes = KRB5_KDB_DEF_FLAGS;
+       *validp |= KRB5_ADM_M_FLAGS;
+    }
+
+    /* Do lastsuccess */
+    if (isrand) {
+       if (LASTSUCCESS_EVENT) {
+           dbentp->last_success = RAND();
+           *validp |= KRB5_ADM_M_LASTSUCCESS;
+       }
+    }
+    else {
+       if (!is_set) {
+           dbentp->last_success = (krb5_timestamp) now - 3600;
+           *validp |= KRB5_ADM_M_LASTSUCCESS;
+       }
+    }
+
+    /* Do lastfailed */
+    if (isrand) {
+       if (LASTFAILED_EVENT) {
+           dbentp->last_failed = RAND();
+           *validp |= KRB5_ADM_M_LASTFAILED;
+       }
+    }
+    else {
+       if (!is_set) {
+           dbentp->last_failed = (krb5_timestamp) now - 3600;
+           *validp |= KRB5_ADM_M_LASTFAILED;
+       }
+    }
+
+    /* Do failcount */
+    if (isrand) {
+       if (FAILCOUNT_EVENT) {
+           dbentp->fail_auth_count = RAND();
+           *validp |= KRB5_ADM_M_FAILCOUNT;
+       }
+    }
+    else {
+       if (!is_set) {
+           dbentp->fail_auth_count = 0;
+           *validp |= KRB5_ADM_M_FAILCOUNT;
+       }
+    }
+
+    /*
+     * Generate auxiliary data.
+     */
+    if (isrand) {
+       krb5_octet *lpw_change;
+       krb5_tl_data *tldata;
+       krb5_timestamp lpw;
+       krb5_tl_mod_princ mprinc;
+       int     didone;
+
+       didone = 0;
+       if (LASTPWCHANGE_EVENT) {
+           if ((tldata = (krb5_tl_data *) malloc(sizeof(krb5_tl_data))) &&
+               (lpw_change = (krb5_octet *) malloc(sizeof(krb5_timestamp)))) {
+               lpw = (krb5_timestamp) RAND();
+               lpw_change[0] = (unsigned char) ((lpw >> 24) & 0xff);
+               lpw_change[1] = (unsigned char) ((lpw >> 16) & 0xff);
+               lpw_change[2] = (unsigned char) ((lpw >> 8) & 0xff);
+               lpw_change[3] = (unsigned char) (lpw & 0xff);
+               tldata->tl_data_next = (krb5_tl_data *) NULL;
+               tldata->tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
+               tldata->tl_data_length = sizeof(krb5_timestamp);
+               tldata->tl_data_contents = lpw_change;
+               dbentp->n_tl_data = 1;
+               dbentp->tl_data = tldata;
+               didone++;
+           }
+       }
+       if (MODNAME_EVENT || MODDATE_EVENT) {
+           mprinc.mod_date = (krb5_timestamp) RAND();
+           if (!krb5_parse_name(kcontext, defprinc, &mprinc.mod_princ)) {
+               if (!krb5_dbe_encode_mod_princ_data(kcontext, &mprinc, dbentp))
+                   didone++;
+           }
+       }
+       if (didone)
+           *validp |= KRB5_ADM_M_AUXDATA;
+    }
+    else {
+       krb5_octet *lpw_change;
+       krb5_tl_data *tldata;
+       krb5_timestamp lpw;
+       krb5_tl_mod_princ mprinc;
+
+       if ((tldata = (krb5_tl_data *) malloc(sizeof(krb5_tl_data))) &&
+           (lpw_change = (krb5_octet *) malloc(sizeof(krb5_timestamp)))) {
+           lpw = (krb5_timestamp) now - 3600;
+           lpw_change[0] = (unsigned char) ((lpw >> 24) & 0xff);
+           lpw_change[1] = (unsigned char) ((lpw >> 16) & 0xff);
+           lpw_change[2] = (unsigned char) ((lpw >> 8) & 0xff);
+           lpw_change[3] = (unsigned char) (lpw & 0xff);
+           tldata->tl_data_next = (krb5_tl_data *) NULL;
+           tldata->tl_data_type = KRB5_TL_LAST_PWD_CHANGE;
+           tldata->tl_data_length = sizeof(krb5_timestamp);
+           tldata->tl_data_contents = lpw_change;
+           dbentp->n_tl_data = 1;
+           dbentp->tl_data = tldata;
+       }
+       mprinc.mod_date = (krb5_timestamp) now;
+       if (!krb5_parse_name(kcontext, defprinc, &mprinc.mod_princ))
+           krb5_dbe_encode_mod_princ_data(kcontext, &mprinc, dbentp);
+       *validp |= KRB5_ADM_M_AUXDATA;
+    }
+
+    /* Make key data */
+    if (isrand) {
+       int i, j, kl, sl;
+
+       if (!is_set) {
+           for (i=0; i<(1+(RAND()%8)); i++) {
+               if (!krb5_dbe_create_key_data(kcontext, dbentp)) {
+                   dbentp->key_data[i].key_data_kvno = RAND() % 32768;
+                   dbentp->key_data[i].key_data_type[0] = RAND() % 32768;
+                   dbentp->key_data[i].key_data_type[1] = RAND() % 32768;
+                   kl = dbentp->key_data[i].key_data_length[0] =
+                       8 + (RAND() % 128);
+                   sl = dbentp->key_data[i].key_data_length[1] =
+                       0 + (RAND() % 128);
+                   if (dbentp->key_data[i].key_data_contents[0] =
+                       (krb5_octet *) malloc(kl)) {
+                       for (j=0; j<kl; j++) {
+                           dbentp->key_data[i].key_data_contents[0][j] =
+                               RAND() % 256;
+                       }
+                   }
+                   if (dbentp->key_data[i].key_data_contents[1] =
+                       (krb5_octet *) malloc(sl)) {
+                       for (j=0; j<sl; j++) {
+                           dbentp->key_data[i].key_data_contents[1][j] =
+                               RAND() % 256;
+                       }
+                   }
+                   *validp |= KRB5_ADM_M_KEYDATA;
+               }
+           }
+       }
+    }
+    else {
+       if (!is_set) {
+           if (!krb5_dbe_create_key_data(kcontext, dbentp)) {
+               int i;
+
+               dbentp->key_data[0].key_data_kvno = 1;
+               dbentp->key_data[0].key_data_type[0] = 1;
+               dbentp->key_data[0].key_data_type[1] = 0;
+               dbentp->key_data[0].key_data_length[0] = 24;
+               dbentp->key_data[0].key_data_length[1] = 0;
+               if (dbentp->key_data[0].key_data_contents[0] =
+                   (krb5_octet *) malloc(24)) {
+                   for (i=0; i<24; i++)
+                       dbentp->key_data[0].key_data_contents[0][i] = RAND() % 256;
+               }
+               dbentp->key_data[0].key_data_contents[1] = (krb5_octet *) NULL;
+               *validp |= KRB5_ADM_M_KEYDATA;
+           }
+       }
+    }
+
+    /* Make extra data */
+    if (isrand && EXTRA_EVENT) {
+       dbentp->e_length = 8 + (RAND() % 504);
+       if (dbentp->e_data = (krb5_octet *)
+           malloc((size_t) dbentp->e_length)) {
+           int j;
+           for (j=0; j<dbentp->e_length; j++) {
+               dbentp->e_data[j] = RAND() % 256;
+           }
+           *validp |= KRB5_ADM_M_EXTRADATA;
+       }
+       else
+           dbentp->e_length = 0;
+    }
+
+    if (is_set) {
+       /* Only 25% may fail at most */
+       if (isrand && ((RAND() % 100) < 75)) {
+           *validp &= KRB5_ADM_M_SET_VALID;
+       }
+#ifdef notdef
+       if ((*validp & KRB5_ADM_M_PASSWORD) != 0)
+           *validp &= ~KRB5_ADM_M_RANDOMKEY;
+#endif /* notdef */
+       *expectp = ((*validp & ~KRB5_ADM_M_SET_VALID) != 0) ? 1 : 0;
+    }
+    else {
+       /* Only 25% may fail at most */
+       if (isrand && ((RAND() % 100) < 75))
+           *validp &= KRB5_ADM_M_GET_VALID;
+       *expectp = ((*validp & ~KRB5_ADM_M_GET_VALID) != 0) ? 1 : 0;
+    }
+}
+
+/*
+ * Compare two entries.
+ */
+static krb5_boolean
+compare_entries(kcontext, ivalid, ientp, ipwd, ovalid, oentp, opwd)
+    krb5_context       kcontext;
+    krb5_ui_4          ivalid;
+    krb5_db_entry      *ientp;
+    char               *ipwd;
+    krb5_ui_4          ovalid;
+    krb5_db_entry      *oentp;
+    char               *opwd;
+{
+    /* Handle/compare password */
+    if (((ivalid & KRB5_ADM_M_PASSWORD) != 0) &&
+       (((ovalid & KRB5_ADM_M_PASSWORD) == 0) ||
+        strcmp(ipwd, opwd)))
+           return(0);
+
+    /* Handle/compare maxlife */
+    if (((ivalid & KRB5_ADM_M_MAXLIFE) != 0) &&
+       (((ovalid & KRB5_ADM_M_MAXLIFE) == 0) ||
+        (ientp->max_life != oentp->max_life)))
+       return(0);
+
+    /* Handle/compare maxrenewlife */
+    if (((ivalid & KRB5_ADM_M_MAXRENEWLIFE) != 0) &&
+       (((ovalid & KRB5_ADM_M_MAXRENEWLIFE) == 0) ||
+        (ientp->max_renewable_life != oentp->max_renewable_life)))
+       return(0);
+
+    /* Handle/compare expiration */
+    if (((ivalid & KRB5_ADM_M_EXPIRATION) != 0) &&
+       (((ovalid & KRB5_ADM_M_EXPIRATION) == 0) ||
+        (ientp->expiration != oentp->expiration)))
+       return(0);
+
+    /* Handle/compare pwexpiration */
+    if (((ivalid & KRB5_ADM_M_PWEXPIRATION) != 0) &&
+       (((ovalid & KRB5_ADM_M_PWEXPIRATION) == 0) ||
+        (ientp->pw_expiration != oentp->pw_expiration)))
+       return(0);
+
+#ifdef notdef
+    /* Handle/compare random key */
+    if (((ivalid & KRB5_ADM_M_RANDOMKEY) != 0) &&
+       ((ovalid & KRB5_ADM_M_PASSWORD) != 0))
+       return(0);
+#endif /* notdef */
+
+    /* Handle/compare flags */
+    if (((ivalid & KRB5_ADM_M_FLAGS) != 0) &&
+       (((ovalid & KRB5_ADM_M_FLAGS) == 0) ||
+        (ientp->attributes != oentp->attributes)))
+       return(0);
+
+    /* Handle/compare lastsuccess */
+    if (((ivalid & KRB5_ADM_M_LASTSUCCESS) != 0) &&
+       (((ovalid & KRB5_ADM_M_LASTSUCCESS) == 0) ||
+        (ientp->last_success != oentp->last_success)))
+       return(0);
+
+    /* Handle/compare lastfailed */
+    if (((ivalid & KRB5_ADM_M_LASTFAILED) != 0) &&
+       (((ovalid & KRB5_ADM_M_LASTFAILED) == 0) ||
+        (ientp->last_failed != oentp->last_failed)))
+       return(0);
+
+    /* Handle/compare failcount */
+    if (((ivalid & KRB5_ADM_M_FAILCOUNT) != 0) &&
+       (((ovalid & KRB5_ADM_M_FAILCOUNT) == 0) ||
+        (ientp->fail_auth_count != oentp->fail_auth_count)))
+       return(0);
+
+    /* Handle/compare auxiliary data */
+    if (((ivalid & KRB5_ADM_M_AUXDATA) != 0) &&
+       (((ovalid & KRB5_ADM_M_AUXDATA) == 0) ||
+        aux_data_inequal(ientp, oentp)))
+       return(0);
+
+    /* Handle/compare key data */
+    if (((ivalid & KRB5_ADM_M_KEYDATA) != 0) &&
+       (((ovalid & KRB5_ADM_M_KEYDATA) == 0) ||
+        key_data_inequal(ientp, oentp)))
+       return(0);
+
+    /* Handle/compare extra data */
+    if (((ivalid & KRB5_ADM_M_EXTRADATA) != 0) &&
+       (((ovalid & KRB5_ADM_M_EXTRADATA) == 0) ||
+        extra_data_inequal(ientp, oentp)))
+       return(0);
+
+    return(1);
+}
+
+/*
+ * Print out an entry.
+ */
+static void
+print_dbent(kcontext, ivalid, ientp, ipwd)
+    krb5_context       kcontext;
+    krb5_ui_4          ivalid;
+    krb5_db_entry      *ientp;
+    char               *ipwd;
+{
+    printf("Valid mask:\t%08x\n", ivalid);
+
+    /* Print password */
+    if ((ivalid & KRB5_ADM_M_PASSWORD) != 0)
+       printf("Password:\t%s\n", ipwd);
+
+    /* Print maxlife */
+    if ((ivalid & KRB5_ADM_M_MAXLIFE) != 0)
+       printf("max_life:\t%8d\t%08x\n", ientp->max_life, ientp->max_life);
+
+    /* Print maxrenewlife */
+    if ((ivalid & KRB5_ADM_M_MAXRENEWLIFE) != 0)
+       printf("max_rlife:\t%8d\t%08x\n", ientp->max_renewable_life,
+              ientp->max_renewable_life);
+
+    /* Print expiration */
+    if ((ivalid & KRB5_ADM_M_EXPIRATION) != 0)
+       printf("expires:\t%8d\t%08x\t%s\n", ientp->expiration,
+              ientp->expiration, time2string(ientp->expiration));
+
+    /* Print pwexpiration */
+    if ((ivalid & KRB5_ADM_M_PWEXPIRATION) != 0)
+       printf("pw expires:\t%8d\t%08x\t%s\n", ientp->pw_expiration,
+              ientp->pw_expiration, time2string(ientp->pw_expiration));
+
+    /* Print random key */
+    if ((ivalid & KRB5_ADM_M_RANDOMKEY) != 0)
+       printf("random key\n");
+
+    /* Print flags */
+    if ((ivalid & KRB5_ADM_M_FLAGS) != 0)
+       printf("flags:\t\t%8d\t%08x\n", ientp->attributes, ientp->attributes);
+
+    /* Print lastsuccess */
+    if ((ivalid & KRB5_ADM_M_LASTSUCCESS) != 0)
+       printf("lastsucc:\t%8d\t%08x\t%s\n", ientp->last_success,
+              ientp->last_success, time2string(ientp->last_success));
+
+    /* Print lastfailed */
+    if ((ivalid & KRB5_ADM_M_LASTFAILED) != 0)
+       printf("lastfail:\t%8d\t%08x\t%s\n", ientp->last_failed,
+              ientp->last_failed, time2string(ientp->last_failed));
+
+    /* Print failcount */
+    if ((ivalid & KRB5_ADM_M_FAILCOUNT) != 0)
+       printf("failcount:\t%8d\t%08x\n", ientp->fail_auth_count,
+              ientp->fail_auth_count);
+
+    /* Print auxiliary data */
+    if ((ivalid & KRB5_ADM_M_AUXDATA) != 0)
+       print_auxdata(ientp);
+
+    /* Print key data */
+    if ((ivalid & KRB5_ADM_M_KEYDATA) != 0)
+       print_keydata(ientp);
+
+    /* Print extra data */
+    if ((ivalid & KRB5_ADM_M_EXTRADATA) != 0)
+       print_extradata(ientp);
+}
+
+/*
+ * Do a test case.
+ *
+ * Strategy: Generate the desired database entry type, then convert it using
+ *     krb5_adm_dbent_to_proto, then convert it back to a database entry
+ *     using krb5_adm_proto_to_dbent.  Then verify the match.
+ */
+static krb5_int32
+do_test(pname, verbose, isrand, is_a_set, title, passno)
+    char               *pname;
+    krb5_boolean       verbose;
+    krb5_boolean       isrand;
+    krb5_boolean       is_a_set;
+    char               *title;
+    krb5_int32         passno;
+{
+    krb5_context       kcontext;
+    krb5_db_entry      *in_dbent;
+    krb5_db_entry      *out_dbent;
+    krb5_error_code    kret;
+    krb5_int32         ncomps;
+    krb5_data          *complist;
+    krb5_ui_4          in_validmask;
+    krb5_ui_4          out_validmask;
+    char               *in_password;
+    char               *out_password;
+    krb5_boolean       should_fail;
+
+    if (verbose) {
+       printf("* Begin %s", title);
+       if (isrand)
+           printf(" pass %d", passno);
+       printf("\n");
+    }
+
+    kret = 0;
+    krb5_init_context(&kcontext);
+    krb5_init_ets(kcontext);
+    in_dbent = (krb5_db_entry *) malloc(sizeof(krb5_db_entry));
+    out_dbent = (krb5_db_entry *) malloc(sizeof(krb5_db_entry));
+    if (in_dbent && out_dbent) {
+       /* Initialize our data */
+       memset((char *) in_dbent, 0, sizeof(krb5_db_entry));
+       memset((char *) out_dbent, 0, sizeof(krb5_db_entry));
+       in_password = out_password = (char *) NULL;
+       out_validmask = 0;
+       ncomps = 0;
+       complist = (krb5_data *) NULL;
+       should_fail = 0;
+       if (!isrand) {
+           if (is_a_set)
+               in_validmask = KRB5_ADM_M_SET;
+           else
+               in_validmask = KRB5_ADM_M_GET;
+       }
+       else {
+           if (SET_EVENT)
+               in_validmask = KRB5_ADM_M_SET;
+           else
+               in_validmask = KRB5_ADM_M_GET;
+       }
+
+       /* Generate the database entry. */
+       gen_dbent(kcontext,
+                 in_dbent, isrand, &in_validmask, &in_password, &should_fail);
+
+       /* Convert it to the o-t-w protocol */
+       if (!(kret = krb5_adm_dbent_to_proto(kcontext,
+                                            in_validmask,
+                                            in_dbent,
+                                            in_password,
+                                            &ncomps,
+                                            &complist))) {
+           /* If this should fail, then we've got a problem here */
+           if (!should_fail) {
+
+               /* Otherwise, convert it back to a database entry */
+               if (!(kret = krb5_adm_proto_to_dbent(kcontext,
+                                                    ncomps,
+                                                    complist,
+                                                    &out_validmask,
+                                                    out_dbent,
+                                                    &out_password))) {
+                   /* Compare the entries */
+                   if (compare_entries(kcontext,
+                                       in_validmask,
+                                       in_dbent,
+                                       in_password,
+                                       out_validmask,
+                                       out_dbent,
+                                       out_password)) {
+                       /* Success */
+                       if (verbose) {
+                           printf("Successful translation");
+                           printf(" during %s", title);
+                           if (isrand)
+                               printf(" pass %d", passno);
+                           printf(" of:\n");
+                           print_dbent(kcontext,
+                                       in_validmask, in_dbent, in_password);
+                       }
+                   }
+                   else {
+                       /* Failed */
+                       fprintf(stderr, "%s: comparison mismatch", pname);
+                       fprintf(stderr, " during %s", title);
+                       if (isrand)
+                           fprintf(stderr, " pass %d", passno);
+                       fprintf(stderr, "\n");
+                       if (verbose) {
+                           printf("Input entry is as follows:\n");
+                           print_dbent(kcontext,
+                                       in_validmask, in_dbent, in_password);
+                           printf("Output entry is as follows:\n");
+                           print_dbent(kcontext,
+                                       out_validmask,
+                                       out_dbent,
+                                       out_password);
+                       }
+                       kret = KRB5KRB_ERR_GENERIC;
+                   }
+                   if (out_password)
+                       krb5_xfree(out_password);
+               }
+               else {
+                   /* Conversion to database entry failed */
+                   fprintf(stderr, "%s: protocol decode failed with %d",
+                       pname, kret);
+                   fprintf(stderr, " during %s", title);
+                   if (isrand)
+                       fprintf(stderr, " pass %d", passno);
+                   fprintf(stderr, "\n");
+               }
+           }
+           else {
+               /* Should have failed */
+               fprintf(stderr, "%s: protocol encode unexpectedly succeeded",
+                       pname);
+               kret = KRB5KRB_ERR_GENERIC;
+               fprintf(stderr, " during %s", title);
+               if (isrand)
+                   fprintf(stderr, " pass %d", passno);
+               fprintf(stderr, "\n");
+           }
+           krb5_free_adm_data(kcontext, ncomps, complist);
+       }
+       else {
+           /* Convert to protocol failed */
+           if (!should_fail) {
+               /* Unexpected failure */
+               fprintf(stderr, "%s: protocol encode failed with %d",
+                       pname, kret);
+               fprintf(stderr, " during %s", title);
+               if (isrand)
+                   fprintf(stderr, " pass %d", passno);
+               fprintf(stderr, "\n");
+           }
+           else {
+               /* Success */
+               if (verbose)
+                   printf("- Expected failure OK\n");
+               kret = 0;
+           }
+       }
+       /* Cleanup */
+       if (in_password)
+           free(in_password);
+       if (in_dbent->tl_data) {
+           krb5_tl_data *xxx, *xxx1;
+
+           for (xxx=in_dbent->tl_data; xxx; ) {
+               xxx1 = xxx;
+               xxx = xxx->tl_data_next;
+               free(xxx1);
+           }
+       }
+       free(in_dbent);
+       if (out_dbent->tl_data) {
+           krb5_tl_data *xxx, *xxx1;
+
+           for (xxx=out_dbent->tl_data; xxx; ) {
+               xxx1 = xxx;
+               xxx = xxx->tl_data_next;
+               free(xxx1);
+           }
+       }
+       free(out_dbent);
+    }
+    else {
+       fprintf(stderr, "%s: no memory\n", pname);
+       kret = ENOMEM;
+    }
+
+    krb5_free_context(kcontext);
+    if (verbose) {
+       printf("* End %s ", title);
+       if (isrand)
+           printf(" pass %d ", passno);
+       printf("%s", (kret) ? "FAILURE" : "SUCCESS");
+       if (kret)
+           printf("%d - %s", kret, error_message(kret));
+       printf("\n");
+    }
+    return((kret) ? 1 : 0);
+}
+
+/*
+ * usage is: t_dbentry [-r <nnn>] [-v]
+ */
+int
+main(argc, argv)
+    int                argc;
+    char       *argv[];
+{
+    krb5_boolean       verbose;
+    krb5_int32         randompasses;
+    krb5_int32         error;
+    int                option;
+    extern char                *optarg;
+    char               *programname;
+    int                        i;
+    time_t             now;
+
+    randompasses = 0;
+    verbose = 0;
+    error = 0;
+    programname = argv[0];
+
+    now = time((time_t *) NULL);
+    SRAND((RAND_TYPE) now);
+    while ((option = getopt(argc, argv, "r:v")) != EOF) {
+       switch (option) {
+       case 'r':
+           if (sscanf(optarg, "%d", &randompasses) != 1) {
+               fprintf(stderr, "%s: %s is not a number\n", argv[0], optarg);
+               error++;
+           }
+           break;
+       case 'v':
+           verbose = 1;
+           break;
+       default:
+           fprintf(stderr, "%s: usage is %s [-r number] [-v]\n",
+                   argv[0], argv[0]);
+           error++;
+           break;
+       }
+    }
+    if (error)
+       return(error);
+
+    error += do_test(programname, verbose, 0, 1, "Standard set test", 0);
+    error += do_test(programname, verbose, 0, 0, "Standard get test", 0);
+    for (i=0; i<randompasses; i++)
+       error += do_test(programname, verbose, 1, 0, "Random test", i+1);
+    if (verbose) {
+       if (error)
+           printf("%s: %d errors in %d tests (%5.2f%%)\n", argv[0], error,
+                  randompasses+2,
+                  (float) (error*100) / (float) (randompasses+2));
+    }
+    return(error);
+}
+
diff --git a/src/lib/kadm/t_ktentry.c b/src/lib/kadm/t_ktentry.c
new file mode 100644 (file)
index 0000000..0367947
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * lib/kadm/t_ktentry.c
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  M.I.T. makes no representations about the suitability of
+ * this software for any purpose.  It is provided "as is" without express
+ * or implied warranty.
+ *
+ */
+
+/*
+ * t_ktentry.c - Test function of krb5_adm_{proto_to_ktent,ktent_to_proto}.
+ */
+
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+#if    HAVE_SRAND48
+#define        SRAND   srand48
+#define        RAND    lrand48
+#define        RAND_TYPE       long
+#endif /* HAVE_SRAND48 */
+
+#if    !defined(RAND_TYPE) && defined(HAVE_SRAND)
+#define        SRAND   srand
+#define        RAND    rand
+#define        RAND_TYPE       int
+#endif /* !defined(RAND_TYPE) && defined(HAVE_SRAND) */
+
+#if    !defined(RAND_TYPE) && defined(HAVE_SRANDOM)
+#define        SRAND   srandom
+#define        RAND    random
+#define        RAND_TYPE       long
+#endif /* !defined(RAND_TYPE) && defined(HAVE_SRANDOM) */
+
+#if    !defined(RAND_TYPE)
+There is no random number generator.
+#endif /* !defined(RAND_TYPE) */
+
+/*
+ * Generate a principal name.
+ */
+static char *
+gen_princname(isrand)
+    krb5_boolean       isrand;
+{
+    static char        *defprinc = "testprinc/instance@realm";
+    char *pptr;
+
+    if (isrand) {
+       int i, j;
+       int ncomps;
+       size_t compsize[9];
+       char * complist[9];
+       size_t  totsize;
+
+       for (i=0; i<9; i++) {
+           compsize[i] = 0;
+           complist[i] = (char *) NULL;
+       }
+       ncomps = 2 + (RAND() % 7);
+       totsize = 0;
+       for (i=0; i<ncomps; i++) {
+           compsize[i] = 1 + (RAND() % 32);
+           complist[i] = (char *) malloc(compsize[i]+1);
+           if (complist[i]) {
+               for (j=0; j<compsize[i]; j++) {
+                   (complist[i])[j] = RAND() % 128;
+                   while (!isalnum((int) (complist[i])[j]))
+                       (complist[i])[j] = RAND() % 128;
+               }
+               (complist[i])[compsize[i]] = '\0';
+               totsize += (compsize[i] + 1);
+           }
+           else
+               break;
+       }
+       pptr = (char *) malloc(totsize+1);
+       if (pptr) {
+           pptr[0] = '\0';
+           for (i=1; i<ncomps; i++) {
+               if (complist[i]) {
+                   strcat(pptr, complist[i]);
+                   strcat(pptr, "/");
+                   free(complist[i]);
+               }
+               else
+                   break;
+           }
+           pptr[strlen(pptr)-1] = '\0';
+           strcat(pptr, "@");
+           strcat(pptr, complist[0]);
+           free(complist[0]);
+       }
+    }
+    else {
+       pptr = (char *) malloc(strlen(defprinc)+1);
+       if (pptr)
+           strcpy(pptr, defprinc);
+    }
+    return(pptr);
+}
+
+static void
+gen_key(ktentp, isrand)
+    krb5_keytab_entry  *ktentp;
+    krb5_boolean       isrand;
+{
+    static unsigned char defkey[8] = { 0x01, 0xfe, 0xab, 0xc3,
+                                          0x23, 0x16, 0x84, 0x23 };
+
+    if (isrand) {
+       size_t keylen;
+       int i;
+
+       keylen = 4 + (RAND() % 64);
+       ktentp->key.contents = (krb5_octet *) malloc(keylen);
+       if (ktentp->key.contents) {
+           ktentp->key.length = keylen;
+           for (i=0; i<keylen; i++)
+               ktentp->key.contents[i] = RAND() & 255;
+       }
+    }
+    else {
+       ktentp->key.contents = (krb5_octet *) malloc(sizeof(defkey));
+       if (ktentp->key.contents) {
+           ktentp->key.length = 8;
+           memcpy(ktentp->key.contents, defkey, sizeof(defkey));
+       }
+    }
+}
+
+/*
+ * Generate a keytab entry.
+ */
+static void
+gen_ktent(kcontext, ktentp, isrand)
+    krb5_context       kcontext;
+    krb5_keytab_entry  *ktentp;
+    krb5_boolean       isrand;
+{
+    char       *princname;
+
+    princname = gen_princname(isrand);
+    if (princname && !krb5_parse_name(kcontext,
+                                     princname,
+                                     &ktentp->principal)
+       ) {
+       ktentp->vno = (isrand) ? RAND() : 1;
+       gen_key(ktentp, isrand);
+       free(princname);
+    }
+}
+
+/*
+ * Compare two entries.
+ */
+static krb5_boolean
+compare_entries(kcontext, ientp, oentp)
+    krb5_context       kcontext;
+    krb5_keytab_entry  *ientp;
+    krb5_keytab_entry  *oentp;
+{
+    if (ientp->vno != oentp->vno)
+       return(0);
+
+    if ((ientp->key.length != oentp->key.length) ||
+       memcmp(ientp->key.contents, oentp->key.contents, ientp->key.length))
+       return(0);
+
+    if (!krb5_principal_compare(kcontext, ientp->principal, oentp->principal))
+       return(0);
+    return(1);
+}
+
+/*
+ * Print out an entry.
+ */
+static void
+print_ktent(kcontext, ientp)
+    krb5_context       kcontext;
+    krb5_keytab_entry  *ientp;
+{
+    char *princname;
+    int i;
+
+    if (!krb5_unparse_name(kcontext, ientp->principal, &princname)) {
+       printf("Principal: %s (version %d[%x])\n", princname, ientp->vno);
+       printf("Key:");
+       for (i=0; i<ientp->key.length; i++)
+           printf(" %02x", ientp->key.contents[i]);
+       printf("\n");
+       krb5_xfree(princname);
+    }
+}
+
+/*
+ * Do a test case.
+ *
+ * Strategy: Generate the desired keytab entry type, then convert it using
+ *     krb5_adm_ktent_to_proto, then convert it back to a keytab entry
+ *     using krb5_adm_proto_to_ktent.  Then verify the match.
+ */
+static krb5_int32
+do_test(pname, verbose, isrand, title, passno)
+    char               *pname;
+    krb5_boolean       verbose;
+    krb5_boolean       isrand;
+    char               *title;
+    krb5_int32         passno;
+{
+    krb5_context       kcontext;
+    krb5_keytab_entry  *in_ktent;
+    krb5_keytab_entry  *out_ktent;
+    krb5_error_code    kret;
+    krb5_int32         ncomps;
+    krb5_data          *complist;
+
+    if (verbose) {
+       printf("* Begin %s", title);
+       if (isrand)
+           printf(" pass %d", passno);
+       printf("\n");
+    }
+
+    kret = 0;
+    krb5_init_context(&kcontext);
+    krb5_init_ets(kcontext);
+    in_ktent = (krb5_keytab_entry *) malloc(sizeof(krb5_keytab_entry));
+    out_ktent = (krb5_keytab_entry *) malloc(sizeof(krb5_keytab_entry));
+    if (in_ktent && out_ktent) {
+       /* Initialize our data */
+       memset((char *) in_ktent, 0, sizeof(krb5_keytab_entry));
+       memset((char *) out_ktent, 0, sizeof(krb5_keytab_entry));
+       ncomps = 0;
+       complist = (krb5_data *) NULL;
+
+       /* Generate the keytab entry. */
+       gen_ktent(kcontext, in_ktent, isrand);
+
+       /* Convert it to the o-t-w protocol */
+       if (!(kret = krb5_adm_ktent_to_proto(kcontext,
+                                            in_ktent,
+                                            &ncomps,
+                                            &complist))) {
+               /* Otherwise, convert it back to a keytab entry */
+               if (!(kret = krb5_adm_proto_to_ktent(kcontext,
+                                                    ncomps,
+                                                    complist,
+                                                    out_ktent))) {
+                   /* Compare the entries */
+                   if (compare_entries(kcontext,
+                                       in_ktent,
+                                       out_ktent)) {
+                       /* Success */
+                       if (verbose) {
+                           printf("Successful translation");
+                           printf(" during %s", title);
+                           if (isrand)
+                               printf(" pass %d", passno);
+                           printf(" of:\n");
+                           print_ktent(kcontext, in_ktent);
+                       }
+                   }
+                   else {
+                       /* Failed */
+                       fprintf(stderr, "%s: comparison mismatch", pname);
+                       fprintf(stderr, " during %s", title);
+                       if (isrand)
+                           fprintf(stderr, " pass %d", passno);
+                       fprintf(stderr, "\n");
+                       if (verbose) {
+                           printf("Input entry is as follows:\n");
+                           print_ktent(kcontext, in_ktent);
+                           printf("Output entry is as follows:\n");
+                           print_ktent(kcontext, out_ktent);
+                       }
+                       kret = KRB5KRB_ERR_GENERIC;
+                   }
+               }
+               else {
+                   /* Conversion to keytab entry failed */
+                   fprintf(stderr, "%s: protocol decode failed with %d",
+                       pname, kret);
+                   fprintf(stderr, " during %s", title);
+                   if (isrand)
+                       fprintf(stderr, " pass %d", passno);
+                   fprintf(stderr, "\n");
+               }
+           krb5_free_adm_data(kcontext, ncomps, complist);
+       }
+       else {
+           /* Convert to protocol failed */
+               fprintf(stderr, "%s: protocol encode failed with %d",
+                       pname, kret);
+               fprintf(stderr, " during %s", title);
+               if (isrand)
+                   fprintf(stderr, " pass %d", passno);
+               fprintf(stderr, "\n");
+       }
+       /* Cleanup */
+       if (in_ktent->principal)
+           krb5_free_principal(kcontext, in_ktent->principal);
+       if (in_ktent->key.contents)
+           free(in_ktent->key.contents);
+       free(in_ktent);
+       if (out_ktent->principal)
+           krb5_free_principal(kcontext, out_ktent->principal);
+       if (out_ktent->key.contents)
+           free(out_ktent->key.contents);
+       free(out_ktent);
+    }
+    else {
+       fprintf(stderr, "%s: no memory\n", pname);
+       kret = ENOMEM;
+    }
+
+    krb5_free_context(kcontext);
+    if (verbose) {
+       printf("* End %s ", title);
+       if (isrand)
+           printf(" pass %d ", passno);
+       printf("%s", (kret) ? "FAILURE" : "SUCCESS");
+       if (kret)
+           printf("%d - %s", kret, error_message(kret));
+       printf("\n");
+    }
+    return((kret) ? 1 : 0);
+}
+
+/*
+ * usage is: t_ktentry [-r <nnn>] [-v]
+ */
+int
+main(argc, argv)
+    int                argc;
+    char       *argv[];
+{
+    krb5_boolean       verbose;
+    krb5_int32         randompasses;
+    krb5_int32         error;
+    int                option;
+    extern char                *optarg;
+    char               *programname;
+    int                        i;
+    time_t             now;
+
+    randompasses = 0;
+    verbose = 0;
+    error = 0;
+    programname = argv[0];
+
+    now = time((time_t *) NULL);
+    SRAND((RAND_TYPE) now);
+    while ((option = getopt(argc, argv, "r:v")) != EOF) {
+       switch (option) {
+       case 'r':
+           if (sscanf(optarg, "%d", &randompasses) != 1) {
+               fprintf(stderr, "%s: %s is not a number\n", argv[0], optarg);
+               error++;
+           }
+           break;
+       case 'v':
+           verbose = 1;
+           break;
+       default:
+           fprintf(stderr, "%s: usage is %s [-r number] [-v]\n",
+                   argv[0], argv[0]);
+           error++;
+           break;
+       }
+    }
+    if (error)
+       return(error);
+
+    error += do_test(programname, verbose, 0, "Standard test", 0);
+    for (i=0; i<randompasses; i++)
+       error += do_test(programname, verbose, 1, 0, "Random test", i+1);
+    if (verbose) {
+       if (error)
+           printf("%s: %d errors in %d tests (%5.2f%%)\n", argv[0], error,
+                  randompasses+2,
+                  (float) (error*100) / (float) (randompasses+2));
+    }
+    return(error);
+}
+