New administrative protocol server
authorPaul Park <pjpark@mit.edu>
Wed, 26 Apr 1995 21:05:11 +0000 (21:05 +0000)
committerPaul Park <pjpark@mit.edu>
Wed, 26 Apr 1995 21:05:11 +0000 (21:05 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@5510 dc483132-0cff-0310-8789-dd5450dbe970

src/kadmin/v5server/Makefile.in [new file with mode: 0644]
src/kadmin/v5server/configure.in [new file with mode: 0644]
src/kadmin/v5server/kadm5_defs.h [new file with mode: 0644]
src/kadmin/v5server/kadmind5.M [new file with mode: 0644]
src/kadmin/v5server/passwd.c [new file with mode: 0644]
src/kadmin/v5server/proto_serv.c [new file with mode: 0644]
src/kadmin/v5server/srv_acl.c [new file with mode: 0644]
src/kadmin/v5server/srv_key.c [new file with mode: 0644]
src/kadmin/v5server/srv_main.c [new file with mode: 0644]
src/kadmin/v5server/srv_net.c [new file with mode: 0644]
src/kadmin/v5server/srv_output.c [new file with mode: 0644]

diff --git a/src/kadmin/v5server/Makefile.in b/src/kadmin/v5server/Makefile.in
new file mode 100644 (file)
index 0000000..5dd6822
--- /dev/null
@@ -0,0 +1,35 @@
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+LDFLAGS = -g
+
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+SSLIB=$(BUILDTOP)/util/ss/libss.a
+DBMLIB=
+KDBLIB=$(TOPLIBD)/libkdb5.a 
+
+all::
+
+KLIB = $(KDBLIB) $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COMERRLIB) $(DBMLIB) 
+DEPKLIB = $(TOPLIBD)/libkrb5.a $(TOPLIBD)/libcrypto.a $(SSLIB) $(COMERRLIB) $(DBMLIB)
+
+SRCS = \
+       $(srcdir)/srv_main.c $(srcdir)/srv_key.c  \
+       $(srcdir)/srv_acl.c $(srcdir)/srv_output.c $(srcdir)/srv_net.c \
+       $(srcdir)/proto_serv.c $(srcdir)/passwd.c
+
+OBJS = \
+       srv_main.o srv_key.o srv_acl.o srv_output.o srv_net.o \
+       proto_serv.o passwd.o
+
+
+all::  kadmind5
+
+kadmind5: $(KDBDEPLIB) $(OBJS) $(DEPKLIB)
+       $(CC) $(CFLAGS) -o kadmind5 $(OBJS) $(KLIB) $(LIBS)
+
+install::
+       $(INSTALL_PROGRAM) kadmind5 ${DESTDIR}$(CLIENT_BINDIR)/kadmind5
+       $(INSTALL_DATA) $(srcdir)/kadmind5.M ${DESTDIR}$(CLIENT_MANDIR)/kadmind.8
+
+clean::
+       $(RM) kadmind5
+
diff --git a/src/kadmin/v5server/configure.in b/src/kadmin/v5server/configure.in
new file mode 100644 (file)
index 0000000..24dc436
--- /dev/null
@@ -0,0 +1,12 @@
+AC_INIT(srv_main.c)
+WITH_CCOPTS
+CONFIG_RULES
+AC_SET_BUILDTOP
+AC_PROG_INSTALL
+WITH_NETLIB
+AC_FUNC_CHECK(waitpid,AC_DEFINE(HAVE_WAITPID))
+CHECK_WAIT_TYPE
+ET_RULES
+KRB_INCLUDE
+WITH_KRB5ROOT
+V5_AC_OUTPUT_MAKEFILE
diff --git a/src/kadmin/v5server/kadm5_defs.h b/src/kadmin/v5server/kadm5_defs.h
new file mode 100644 (file)
index 0000000..8b2ea5f
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * kadmin/v5server/kadm5_defs.h
+ *
+ * Copyright 1995 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ *   require a specific license from the United States Government.
+ *   It is the responsibility of any person or organization contemplating
+ *   export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission.  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.
+ *
+ */
+
+/*
+ * kadmind5
+ * Version 5 administrative daemon.
+ */
+#ifndef        KADM5_DEFS_H__
+#define        KADM5_DEFS_H__
+
+/*
+ * Debug definitions.
+ */
+#define        DEBUG_SPROC     1
+#define        DEBUG_OPERATION 2
+#define        DEBUG_HOST      4
+#define        DEBUG_REALM     8
+#define        DEBUG_REQUESTS  16
+#define        DEBUG_ACL       32
+#define        DEBUG_PROTO     64
+#define        DEBUG_CALLS     128
+#ifdef DEBUG
+#define        DPRINT(l1, cl, al)      if ((cl & l1) != 0) printf al
+#else  /* DEBUG */
+#define        DPRINT(l1, cl, al)
+#endif /* DEBUG */
+#define        DLOG(l1, cl, msg)       if ((cl & l1) != 0)     \
+                                       com_err(programname, 0, msg)
+
+/*
+ * Access control bits.
+ */
+#define        ACL_ADD_PRINCIPAL       1
+#define        ACL_DELETE_PRINCIPAL    2
+#define        ACL_MODIFY_PRINCIPAL    4
+#define        ACL_CHANGEPW            8
+#define        ACL_CHANGE_OWN_PW       16
+#define        ACL_INQUIRE             32
+
+#define        ACL_PRINCIPAL_MASK      (ACL_ADD_PRINCIPAL|ACL_DELETE_PRINCIPAL|\
+                                ACL_MODIFY_PRINCIPAL)
+#define        ACL_PASSWD_MASK         (ACL_CHANGEPW|ACL_CHANGE_OWN_PW)
+#define        ACL_ALL_MASK            (ACL_ADD_PRINCIPAL      | \
+                                ACL_DELETE_PRINCIPAL   | \
+                                ACL_MODIFY_PRINCIPAL   | \
+                                ACL_CHANGEPW           | \
+                                ACL_CHANGE_OWN_PW      | \
+                                ACL_INQUIRE)
+/*
+ * Inter-module function prototypes
+ */
+
+/* srv_key.c */
+krb5_error_code key_init
+       PROTOTYPE((krb5_context,
+                  int,
+                  int,
+                  int,
+                  char *,
+                  int,
+                  char *,
+                  char *));
+void key_finish
+       PROTOTYPE((krb5_context,
+                  int));
+krb5_error_code key_string_to_keys
+       PROTOTYPE((krb5_context,
+                  krb5_principal,
+                  krb5_data *,
+                  krb5_int32,
+                  krb5_int32,
+                  krb5_keyblock *,
+                  krb5_keyblock *));
+krb5_error_code key_encrypt_keys
+       PROTOTYPE((krb5_context,
+                  krb5_principal,
+                  krb5_keyblock *,
+                  krb5_keyblock *,
+                  krb5_encrypted_keyblock *,
+                  krb5_encrypted_keyblock *));
+krb5_error_code key_decrypt_keys
+       PROTOTYPE((krb5_context,
+                  krb5_principal,
+                  krb5_encrypted_keyblock *,
+                  krb5_encrypted_keyblock *,
+                  krb5_keyblock *,
+                  krb5_keyblock *));
+krb5_boolean key_pwd_is_weak
+       PROTOTYPE((krb5_context,
+                  krb5_principal,
+                  krb5_data *,
+                  krb5_int32,
+                  krb5_int32));
+
+/* srv_acl.c */
+krb5_error_code acl_init
+       PROTOTYPE((krb5_context,
+                  int,
+                  char *));
+void acl_finish
+       PROTOTYPE((krb5_context,
+                  int));
+krb5_boolean acl_op_permitted
+       PROTOTYPE((krb5_context,
+                  krb5_principal,
+                  krb5_int32));
+
+/* srv_output.c */
+krb5_error_code output_init
+       PROTOTYPE((krb5_context,
+                  int,
+                  char *,
+                  krb5_boolean));
+void output_finish
+       PROTOTYPE((krb5_context,
+                  int));
+krb5_boolean output_lang_supported
+       PROTOTYPE((char *));
+char *output_krb5_errmsg
+       PROTOTYPE((char *,
+                  krb5_boolean,
+                  krb5_int32));
+char *output_adm_error
+       PROTOTYPE((char *,
+                  krb5_boolean,
+                  krb5_int32,
+                  krb5_int32,
+                  krb5_int32,
+                  krb5_data *));
+
+/* srv_net.c */
+krb5_error_code net_init
+       PROTOTYPE((krb5_context,
+                  int));
+void net_finish
+       PROTOTYPE((krb5_context,
+                  int));
+krb5_error_code net_dispatch
+       PROTOTYPE((krb5_context));
+krb5_principal net_server_princ();
+
+/* proto_serv.c */
+krb5_error_code proto_init
+       PROTOTYPE((krb5_context,
+                  int,
+                  int));
+void proto_finish
+       PROTOTYPE((krb5_context,
+                  int));
+krb5_error_code proto_serv
+       PROTOTYPE((krb5_context,
+                  krb5_int32,
+                  int,
+                  void *,
+                  void *));
+
+/* passwd.c */
+krb5_int32 passwd_check
+       PROTOTYPE((krb5_context,
+                  int,
+                  krb5_auth_context *,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_int32 *));
+krb5_int32 passwd_change
+       PROTOTYPE((krb5_context,
+                  int,
+                  krb5_auth_context *,
+                  krb5_ticket *,
+                  krb5_data *,
+                  krb5_data *,
+                  krb5_int32 *));
+
+#endif /* KADM5_DEFS_H__ */
diff --git a/src/kadmin/v5server/kadmind5.M b/src/kadmin/v5server/kadmind5.M
new file mode 100644 (file)
index 0000000..b5d7dd4
--- /dev/null
@@ -0,0 +1,207 @@
+.\" $Source$
+.\" $Author$
+.\" $Id$
+.\" 
+.\" 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 KADMIND5 1 "Kerberos Version 5.0" "MIT Project Athena"
+.SH NAME
+kadmind5 \- network daemon for Kerberos version 5 database information
+.SH SYNOPSIS
+.B kadmind5
+[
+.B \-i
+] [
+.B \-a
+aclfile
+] [
+.B \-d
+dbname
+] [
+.B \-e
+enctype
+] [
+.B \-k
+mkeytype
+] [
+.B \-l
+langlist
+] [
+.B \-m
+1|0
+] [
+.B \-t
+timeout
+] [
+.B \-D
+debugmask
+] [
+.B \-M
+mkeyname
+]
+.SH DESCRIPTION
+.I kadmind5
+is the network database server for the Kerberos version 5
+password-changing and administration tools.
+
+.SH FLAGS
+.PP
+.B Database, Key and Realm flags
+.IP \-r 
+.B realm
+specifies the realm that this server is to administer.  The default is
+the local realm.
+.IP \-d
+.B dbname
+specifies the location of the database.
+.IP \-M
+.B mkeyname
+specifies the name of the master key.
+.IP \-k
+.B mkeytype
+specifies the master key type.
+.IP \-i
+Indicates that the master key name is to be entered interactively.
+.IP \-e
+.B enctype
+specifies the encryption type which is to be used.
+.PP
+.B ACL flag
+.IP \-a
+.B aclfile
+specifies the location of the ACL file.  This file controls remote
+principals' abilities to perform administrative functions.  See the
+ACL FILE section below for the format of this file.
+.PP
+.B Connection flag
+.IP \-t
+Indicates that the server is to terminate a connection if it remains
+inactive for
+.B timeout
+seconds.
+.PP
+.B Debugging flag
+.IP \-D
+Enables certain debugging features and messages selected by
+.B debugmask.
+.PP
+.B Protocol Message flags
+.IP \-l
+.B langlist
+specifies the list of supported language names [not implemented].
+.IP \-m
+Enables (
+.B 1
+) or disables (
+.B 0
+) MIME encoding support [not implemented].
+
+.SH ACL FILE
+.PP
+The ACL file controls which principals can or cannot perform which
+administrative functions.  This file can contain comment lines, null
+lines or lines which contain ACL entries.  Comment lines start with
+the sharp sign (
+.B \#
+) and continue until the end of the line.  Lines containing ACL
+entries have the format of
+.B principal
+.I whitespace
+.B operation-mask.
+Ordering is important.  The first matching entry is the one which will
+control access for a particular principal.
+.PP
+.IP principal
+may specify a partially or fully qualified Kerberos version 5
+principal name.  Each component of the name may be wildcarded using
+the asterick (
+.B *
+) character.
+.IP operation-mask
+Specifies what operations may or may not be peformed by a principal
+matching a particular entry.  This is a string of one or more of the
+following list of characters or their upper-case counterparts.  If the
+character is upper-case, then the operation is disallowed.  If the
+character is lower-case, then the operation is permitted.
+.TP i
+.I a
+[Dis]allows the addition of principals from the database.
+.TP i
+.I d
+[Dis]allows the deletion of principals from the database.
+.TP i
+.I m
+[Dis]allows the modification of principals in the database.
+.TP i
+.I c
+[Dis]allows the changing of passwords for principals in the database.
+.TP i
+.I o
+[Dis]allows the changing of the principal's own password in the
+database.
+.TP i
+.I i
+[Dis]allows inquiries to the database.
+.TP i
+.I p
+Short for
+.I adm.
+.TP i
+.I w
+Short for
+.I pw.
+.TP i
+.I x or *
+Short for
+.I admcoi.
+.PP
+Some examples of valid entries here are:
+.TP 2i
+.I user/instance@realm po
+A standard fully qualified name.  The
+.B operation-mask
+only applies to this principal and specifies that [s]he may add,
+delete or modify principals and change his/her own password, but not
+anybody elses.
+.TP 2i
+.I user/*@realm aw
+A wildcarded name.  The
+.B operation-mask
+applies to all principals in realm "realm" whose first component is
+"user" and specifies that [s]he may add principals and change anybody
+else's password or change his/her own.
+.TP 2i
+.I * o
+A catchall entry.  The
+.B operation-mask
+applies to all principals and indicates that they may change their own
+passwords.
+
+.SH FILES
+.TP 2i
+/krb5/principal.*
+the default location of the database.
+.TP 2i
+/etc/krb5_adm.acl
+the default location of the ACL file.
+.SH SEE ALSO
+kpasswd(1), kadmin5(8)
+.SH BUGS
+
diff --git a/src/kadmin/v5server/passwd.c b/src/kadmin/v5server/passwd.c
new file mode 100644 (file)
index 0000000..0dea07d
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * kadmin/v5server/passwd.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.
+ *
+ */
+
+/*
+ * passwd.c - handle server password-related functions.
+ */
+
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+#include "adm.h"
+
+/*
+ * These defines turn on various checking in passwd_check_npass_ok.
+ */
+#define        KPWD_CHECK_LENGTH       1
+#define        KPWD_CHECK_WEAKNESS     1
+
+#define        KPWD_MIN_PWD_LENGTH     8
+
+extern char *programname;
+static const char *pwd_bad_old_pwd = "incorrect old password for %s";
+static const char *pwd_perm_denied = "ACL entry prevents password change for %s";
+static const char *pwd_changed_pwd = "changed password for %s";
+\f
+/*
+ * passwd_check_princ()        - Check if the principal specified in the ticket is ok
+ */
+static krb5_error_code
+passwd_check_princ(kcontext, debug_level, ticket,
+                  princp, namep, db_entp, db_numentp, db_morep)
+    krb5_context       kcontext;
+    int                        debug_level;
+    krb5_ticket                *ticket;
+    krb5_principal     *princp;
+    char               **namep;
+    krb5_db_entry      *db_entp;
+    int                        *db_numentp;
+    krb5_boolean       *db_morep;
+{
+    krb5_error_code    kret;
+
+    DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check_princ()\n"));
+    *princp = (krb5_principal) NULL;
+    *namep = (char *) NULL;
+
+    /* Copy principal out of ticket */
+    if (kret = krb5_copy_principal(kcontext,
+                                  ticket->enc_part2->client,
+                                  princp))
+       goto cleanup;
+
+    /* Flatten name */
+    if (kret = krb5_unparse_name(kcontext, *princp, namep))
+       goto cleanup;
+
+    /* Get database entry */
+    if (kret = krb5_db_get_principal(kcontext,
+                                    *princp,
+                                    db_entp,
+                                    db_numentp,
+                                    db_morep))
+       goto cleanup;
+
+    if (*db_numentp == 0)
+       kret = KRB5_KDB_NOENTRY;
+
+ cleanup:
+    if (kret) {
+       if (*namep) {
+           krb5_xfree(*namep);
+           *namep = (char *) NULL;
+       }
+       if (*princp) {
+           krb5_free_principal(kcontext, *princp);
+           *princp = (krb5_principal) NULL;
+       }
+    }
+
+    DPRINT(DEBUG_CALLS, debug_level, ("X passwd_check_princ() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * passwd_check_opass_ok()     - Check of specified old password is good.
+ */
+static krb5_boolean
+passwd_check_opass_ok(kcontext, debug_level, princ, dbentp, pwdata)
+    krb5_context       kcontext;
+    int                        debug_level;
+    krb5_principal     princ;
+    krb5_db_entry      *dbentp;
+    krb5_data          *pwdata;
+{
+    krb5_boolean       pwret;
+    krb5_keyblock      pkey, akey;
+    krb5_keyblock      pkey1, akey1;
+    krb5_error_code    kret;
+
+    DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check_opass_ok()\n"));
+    pwret = 1;
+
+    /* Initialize */
+    memset((char *) &pkey, 0, sizeof(pkey));
+    memset((char *) &akey, 0, sizeof(akey));
+    memset((char *) &pkey1, 0, sizeof(pkey));
+    memset((char *) &akey1, 0, sizeof(akey));
+
+    /* Make key(s) using alleged old password */
+    kret = key_string_to_keys(kcontext,
+                             princ,
+                             pwdata,
+                             dbentp->salt_type,
+                             dbentp->alt_salt_type,
+                             &pkey,
+                             &akey);
+
+    /* Now decrypt database entries */
+    if (!kret)
+       kret = key_decrypt_keys(kcontext, princ,
+                               &dbentp->key, &dbentp->alt_key,
+                               &pkey1, &akey1);
+    if (kret)
+       goto cleanup;
+
+    /*
+     * Compare decrypted keys.  If they differ, then we're wrong!
+     */
+    if ((pkey1.length && (pkey.length != pkey1.length)) ||
+       (akey1.length && (akey.length != akey1.length)) ||
+       (pkey1.length && memcmp(pkey1.contents,
+                               pkey.contents,
+                               pkey.length)) ||
+       (akey1.length && memcmp(akey1.contents,
+                               akey.contents,
+                               akey.length)))
+       pwret = 0;
+
+ cleanup:
+    if (kret)
+       pwret = 0;
+    if (akey1.contents) {
+       memset((char *) akey1.contents, 0, akey1.length);
+       krb5_xfree(akey1.contents);
+    }
+    if (pkey1.contents) {
+       memset((char *) pkey1.contents, 0, pkey1.length);
+       krb5_xfree(pkey1.contents);
+    }
+    if (akey.contents) {
+       memset((char *) akey.contents, 0, akey.length);
+       krb5_xfree(akey.contents);
+    }
+    if (pkey.contents) {
+       memset((char *) pkey.contents, 0, pkey.length);
+       krb5_xfree(pkey.contents);
+    }
+    DPRINT(DEBUG_CALLS, debug_level,
+          ("X passwd_check_opass_ok() = %d\n", pwret));
+    return(pwret);
+}
+\f
+/*
+ * passwd_check_npass_ok()     - Check if new password is ok.
+ */
+static krb5_boolean
+passwd_check_npass_ok(kcontext, debug_level, princ, dbentp, pwdata, supp)
+    krb5_context       kcontext;
+    int                        debug_level;
+    krb5_principal     princ;
+    krb5_db_entry      *dbentp;
+    krb5_data          *pwdata;
+    krb5_int32         *supp;
+{
+    krb5_boolean       pwret;
+
+    DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check_npass_ok()\n"));
+    pwret = 1;
+
+    /*
+     * Check whether a new password is good.
+     */
+#if    KPWD_CHECK_LENGTH
+    /* Check length */
+    if (pwdata->length < KPWD_MIN_PWD_LENGTH) {
+       pwret = 0;
+       *supp = KRB5_ADM_PWD_TOO_SHORT;
+       DPRINT(DEBUG_CALLS, debug_level,
+              ("* passwd_check_npass_ok() - TOO SHORT\n"));
+    }
+#endif /* KPWD_CHECK_LENGTH */
+
+#if    KPWD_CHECK_WEAKNESS
+    /* Check weakness of keys generated by password */
+    if (key_pwd_is_weak(kcontext,
+                       princ,
+                       pwdata,
+                       dbentp->salt_type,
+                       dbentp->alt_salt_type)) {
+       pwret = 0;
+       *supp = KRB5_ADM_PWD_WEAK;
+       DPRINT(DEBUG_CALLS, debug_level,
+              ("* passwd_check_npass_ok() - WEAK\n"));
+    }
+#endif /* KPWD_CHECK_WEAKNESS */
+
+    DPRINT(DEBUG_CALLS, debug_level,
+          ("X passwd_check_npass_ok() = %d\n", pwret));
+    return(pwret);
+}
+\f
+/*
+ * passwd_set_npass()  - Set new password
+ */
+static krb5_error_code
+passwd_set_npass(kcontext, debug_level, princ, dbentp, pwdata)
+    krb5_context       kcontext;
+    int                        debug_level;
+    krb5_principal     princ;
+    krb5_db_entry      *dbentp;
+    krb5_data          *pwdata;
+{
+    krb5_keyblock      pkey, akey;
+    krb5_error_code    kret;
+    krb5_db_entry      entry2write;
+    int                        nwrite;
+
+    DPRINT(DEBUG_CALLS, debug_level, ("* passwd_set_npass()\n"));
+
+    /* Initialize */
+    memset((char *) &pkey, 0, sizeof(pkey));
+    memset((char *) &akey, 0, sizeof(akey));
+    memset((char *) &entry2write, 0, sizeof(krb5_db_entry));
+
+    /* Make key(s) using the new password */
+    if (kret = key_string_to_keys(kcontext,
+                                 princ,
+                                 pwdata,
+                                 dbentp->salt_type,
+                                 dbentp->alt_salt_type,
+                                 &pkey,
+                                 &akey))
+       goto cleanup;
+
+    /* Now get a new database entry */
+    memcpy((char *) &entry2write, (char *) dbentp, sizeof(krb5_db_entry));
+    memset((char *) &entry2write.key, 0, sizeof(krb5_encrypted_keyblock));
+    memset((char *) &entry2write.alt_key, 0, sizeof(krb5_encrypted_keyblock));
+
+    /* Encrypt the new keys to the database entry. */
+    if (kret = key_encrypt_keys(kcontext,
+                               princ,
+                               &pkey,
+                               &akey,
+                               &entry2write.key,
+                               &entry2write.alt_key))
+       goto cleanup;
+
+    /* Set the time */
+    if (kret = krb5_timeofday(kcontext, &entry2write.mod_date))
+       goto cleanup;
+
+    /* Salt? */
+
+    /* Now write the entry */
+    nwrite = 1;
+    if (kret = krb5_db_put_principal(kcontext, &entry2write, &nwrite))
+       goto cleanup;
+
+    if (nwrite != 1)
+       kret = KRB_ERR_GENERIC;
+
+ cleanup:
+    if (entry2write.key.contents) {
+       memset((char *) &entry2write.key, 0, sizeof(krb5_encrypted_keyblock));
+       krb5_xfree(entry2write.key.contents);
+    }
+    if (entry2write.alt_key.contents) {
+       memset((char *) &entry2write.alt_key, 0,
+              sizeof(krb5_encrypted_keyblock));
+       krb5_xfree(entry2write.alt_key.contents);
+    }
+    if (akey.contents) {
+       memset((char *) akey.contents, 0, akey.length);
+       krb5_xfree(akey.contents);
+    }
+    if (pkey.contents) {
+       memset((char *) pkey.contents, 0, pkey.length);
+       krb5_xfree(pkey.contents);
+    }
+    DPRINT(DEBUG_CALLS, debug_level,
+          ("X passwd_set_npass() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * passwd_check()      - Check if a password is ok.
+ */
+krb5_int32
+passwd_check(kcontext, debug_level, auth_context, ticket, pwdata, supp)
+    krb5_context       kcontext;
+    int                        debug_level;
+    krb5_auth_context  *auth_context;
+    krb5_ticket                *ticket;
+    krb5_data          *pwdata;
+    krb5_int32         *supp;
+{
+    krb5_int32         pwret;
+    krb5_error_code    kret;
+    krb5_principal     client;
+    char               *canon_name;
+    krb5_db_entry      tmp_entry;
+    int                        tmp_nents;
+    int                        tmp_more;
+
+    DPRINT(DEBUG_CALLS, debug_level, ("* passwd_check()\n"));
+    pwret = KRB5_ADM_SUCCESS;
+    client = (krb5_principal) NULL;
+    canon_name = (char *) NULL;
+
+    /*
+     * Check out our principal
+     */
+    tmp_nents = 1;
+    if (kret = passwd_check_princ(kcontext,
+                                 debug_level,
+                                 ticket,
+                                 &client,
+                                 &canon_name,
+                                 &tmp_entry,
+                                 &tmp_nents,
+                                 &tmp_more)) {
+       *supp = KRB5_ADM_BAD_PRINC;
+       goto cleanup;
+    }
+
+    DPRINT(DEBUG_REQUESTS, debug_level,
+          ("> Checking password for client \"%s\"\n", canon_name));
+
+    /*
+     * And check out our password.
+     */
+    if (!passwd_check_npass_ok(kcontext,
+                              debug_level,
+                              client,
+                              &tmp_entry,
+                              pwdata,
+                              supp))
+       pwret = KRB5_ADM_PW_UNACCEPT;
+
+ cleanup:
+    if (kret) {
+       pwret = KRB5_ADM_PW_UNACCEPT;
+    }
+    if (tmp_nents > 0)
+       krb5_db_free_principal(kcontext, &tmp_entry, tmp_nents);
+    if (canon_name)
+       krb5_xfree(canon_name);
+    if (client)
+       krb5_free_principal(kcontext, client);
+
+ done:
+    DPRINT(DEBUG_CALLS, debug_level, ("X passwd_check() = %d\n", pwret));
+    return(pwret);
+}
+\f
+/*
+ * passwd_change()     - Change a password.
+ */
+krb5_int32
+passwd_change(kcontext, debug_level, auth_context, ticket,
+             olddata, newdata, supp)
+    krb5_context       kcontext;
+    int                        debug_level;
+    krb5_auth_context  *auth_context;
+    krb5_ticket                *ticket;
+    krb5_data          *olddata;
+    krb5_data          *newdata;
+    krb5_int32         *supp;
+{
+    krb5_int32         pwret;
+    krb5_error_code    kret;
+    krb5_principal     client;
+    char               *canon_name;
+    krb5_db_entry      tmp_entry;
+    int                        tmp_nents;
+    int                        tmp_more;
+
+    DPRINT(DEBUG_CALLS, debug_level, ("* passwd_change()\n"));
+    pwret = KRB5_ADM_SUCCESS;
+    client = (krb5_principal) NULL;
+    canon_name = (char *) NULL;
+
+    /* Make sure the ticket is initial, otherwise don't trust it */
+    if ((ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
+       pwret = KRB5_ADM_NOT_IN_TKT;
+       goto done;
+    }
+
+    /*
+     * Check out our principal
+     */
+    tmp_nents = 1;
+    if (kret = passwd_check_princ(kcontext,
+                                 debug_level,
+                                 ticket,
+                                 &client,
+                                 &canon_name,
+                                 &tmp_entry,
+                                 &tmp_nents,
+                                 &tmp_more)) {
+       *supp = KRB5_ADM_BAD_PRINC;
+       goto cleanup;
+    }
+
+    /*
+     * Check if we're restricted by an ACL from changing our own password.
+     */
+    if (!acl_op_permitted(kcontext, client, ACL_CHANGE_OWN_PW)) {
+       com_err(programname, 0, pwd_perm_denied, canon_name);
+       pwret = KRB5_ADM_CANT_CHANGE;
+       *supp = KRB5_ADM_NOT_ALLOWED;
+       goto cleanup;
+    }
+
+    DPRINT(DEBUG_REQUESTS, debug_level,
+          ("> Changing password for client \"%s\"\n", canon_name));
+
+    /*
+     * Check out our old password.
+     */
+    if (!passwd_check_opass_ok(kcontext,
+                              debug_level,
+                              client,
+                              &tmp_entry,
+                              olddata)) {
+       com_err(programname, 0, pwd_bad_old_pwd, canon_name);
+       pwret = KRB5_ADM_BAD_PW;
+       goto cleanup;
+    }
+
+    /*
+     * Check out the new password.
+     */
+    if (!passwd_check_npass_ok(kcontext,
+                              debug_level,
+                              client,
+                              &tmp_entry,
+                              newdata,
+                              supp)) {
+       pwret = KRB5_ADM_PW_UNACCEPT;
+       goto cleanup;
+    }
+
+    /* Now set the new entry */
+    kret = passwd_set_npass(kcontext,
+                           debug_level,
+                           client,
+                           &tmp_entry,
+                           newdata);
+    if (!kret) {
+       com_err(programname, 0, pwd_changed_pwd, canon_name);
+    }
+
+ cleanup:
+    if (kret) {
+       pwret = KRB5_ADM_CANT_CHANGE;
+    }
+    if (tmp_nents > 0)
+       krb5_db_free_principal(kcontext, &tmp_entry, tmp_nents);
+    if (canon_name)
+       krb5_xfree(canon_name);
+    if (client)
+       krb5_free_principal(kcontext, client);
+
+ done:
+    DPRINT(DEBUG_CALLS, debug_level, ("X passwd_change() = %d\n", pwret));
+    return(pwret);
+}
diff --git a/src/kadmin/v5server/proto_serv.c b/src/kadmin/v5server/proto_serv.c
new file mode 100644 (file)
index 0000000..a49e689
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ * kadmin/v5server/proto_serv.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.
+ *
+ */
+
+/*
+ * proto_serv.c - Engage in protocol.  This module reflects the connection
+ *     protocol as implemented in lib/krb5/os/adm_conn.c.  Any changes
+ *     in one module must be reflected in the other.
+ */
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <setjmp.h>
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+#include "adm.h"
+
+static const char *proto_addrs_msg = "%d: cannot get memory for addresses";
+static const char *proto_rcache_msg = "%d: cannot get replay cache";
+static const char *proto_ap_req_msg = "%d: error reading AP_REQ message";
+static const char *proto_auth_con_msg = "%d: cannot get authorization context";
+static const char *proto_rd_req_msg = "%d: cannot decode AP_REQ message";
+static const char *proto_mk_rep_msg = "%d: cannot generate AP_REP message";
+static const char *proto_wr_rep_msg = "%d: cannot write AP_REP message";
+static const char *proto_conn_abort_msg = "%d: connection destroyed by client";
+static const char *proto_seq_err_msg = "%d: protocol sequence violation";
+static const char *proto_rd_cmd_msg = "%d: cannot read administrative protocol command";
+static const char *proto_wr_reply_msg = "%d: cannot write administrative protocol reply";
+static const char *proto_fmt_reply_msg = "%d: cannot format administrative protocol reply";
+extern char *programname;
+
+static int     proto_proto_timeout = -1;
+static int     proto_debug_level = 0;
+static jmp_buf timeout_jmp;
+
+static krb5_sigtype
+proto_alarmclock()
+{
+    longjmp(timeout_jmp, 1);
+    /* NOTREACHED */
+}
+
+krb5_error_code
+proto_init(kcontext, debug_level, timeo)
+    krb5_context       kcontext;
+    int                        debug_level;
+    int                        timeo;
+{
+    krb5_error_code    kret;
+
+    proto_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, proto_debug_level,
+          ("* proto_init(timeo=%d)\n", timeo));
+    kret = 0;
+    proto_proto_timeout = timeo;
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_init() = %d\n", kret));
+    return(kret);
+}
+
+void
+proto_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("* proto_finish()\n"));
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_finish()\n"));
+}
+
+krb5_error_code
+proto_serv(kcontext, my_id, cl_sock, sv_p, cl_p)
+    krb5_context       kcontext;
+    krb5_int32         my_id;
+    int                        cl_sock;
+    void               *sv_p;
+    void               *cl_p;
+{
+    krb5_error_code    kret;
+    struct sockaddr_in *cl_addr;
+    struct sockaddr_in *sv_addr;
+
+    krb5_data          in_data;
+    krb5_data          out_data;
+    krb5_rcache                rcache;
+    krb5_auth_context  *auth_context;
+    krb5_flags         ap_options;
+    krb5_ticket                *ticket;
+    krb5_address       *local;
+    krb5_address       *remote;
+
+    char               *curr_lang = (char *) NULL;
+    krb5_boolean       mime_setting = 0;
+
+    krb5_int32         num_args;
+    krb5_data          *arglist;
+
+    cl_addr = (struct sockaddr_in *) cl_p;
+    sv_addr = (struct sockaddr_in *) sv_p;
+    DPRINT(DEBUG_CALLS, proto_debug_level,
+          ("* proto_serv(id=%d, sock=%d, local=%x, remote=%x)\n",
+           my_id, cl_sock,
+           ntohl(sv_addr->sin_addr.s_addr),
+           ntohl(cl_addr->sin_addr.s_addr)));
+
+    /* Initialize */
+    memset((char *) &in_data, 0, sizeof(in_data));
+    memset((char *) &out_data, 0, sizeof(out_data));
+    num_args = 0;
+    local = (krb5_address *) NULL;
+    remote = (krb5_address *) NULL;
+    ticket = (krb5_ticket *) NULL;
+
+    /* Get memory for addresses */
+    local = (krb5_address *) malloc(sizeof(krb5_address));
+    remote = (krb5_address *) malloc(sizeof(krb5_address));
+    if (!local || !remote) {
+       kret = ENOMEM;
+       com_err(programname, kret, proto_addrs_msg, my_id);
+       goto cleanup;
+    }
+
+    local->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    remote->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    if (!local->contents || !remote->contents) {
+       kret = ENOMEM;
+       com_err(programname, kret, proto_addrs_msg, my_id);
+       goto cleanup;
+    }
+
+    /*
+     * First setup the replay cache.
+     */
+    if (kret = krb5_get_server_rcache(kcontext,
+                                     krb5_princ_component(kcontext,
+                                                          net_server_princ(),
+                                                          0),
+                                     &rcache)) {
+       com_err(programname, kret, proto_rcache_msg, my_id);
+       goto cleanup;
+    }
+
+    /* Initialize the auth context */
+    if (kret = krb5_auth_con_init(kcontext, &auth_context)) {
+       com_err(programname, kret, proto_auth_con_msg, my_id);
+       goto cleanup;
+    }
+
+    krb5_auth_con_setrcache(kcontext, auth_context, rcache);
+
+    /*
+     * Set up addresses.
+     */
+    local->addrtype = remote->addrtype = ADDRTYPE_INET;
+    local->length = remote->length = sizeof(struct in_addr);
+    memcpy((char *) local->contents,
+          (char *) &sv_addr->sin_addr,
+          sizeof(struct in_addr));
+    memcpy((char *) remote->contents,
+          (char *) &cl_addr->sin_addr,
+          sizeof(struct in_addr));
+    krb5_auth_con_setflags(kcontext, auth_context,
+                          KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                          KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+    krb5_auth_con_setaddrs(kcontext, auth_context, local, remote);
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:read message(local=%x, remote=%x)\n",
+           my_id,
+           ntohl(sv_addr->sin_addr.s_addr),
+           ntohl(cl_addr->sin_addr.s_addr)));
+    /* Now, read in the AP_REQ message and decode it. */
+    if (kret = krb5_read_message(kcontext,
+                                (krb5_pointer) &cl_sock,
+                                &in_data)) {
+       com_err(programname, kret, proto_ap_req_msg, my_id);
+       goto cleanup;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:parse message(%d bytes)\n", my_id, in_data.length));
+    /* Parse the AP_REQ message */
+    if (kret = krb5_rd_req(kcontext,
+                          &auth_context,
+                          &in_data,
+                          net_server_princ(),
+                          (krb5_keytab) NULL,
+                          &ap_options,
+                          &ticket)) {
+       com_err(programname, kret, proto_rd_req_msg, my_id);
+       goto err_reply;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:check AP_REQ(options are %x)\n", my_id, ap_options));
+    /* Check our options */
+    if ((ap_options & AP_OPTS_MUTUAL_REQUIRED) == 0) {
+       kret = KRB5KRB_AP_ERR_MSG_TYPE;
+       goto err_reply;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:make AP_REP\n", my_id));
+    if (kret = krb5_mk_rep(kcontext, auth_context, &out_data)) {
+       com_err(programname, kret, proto_mk_rep_msg, my_id);
+       goto cleanup;
+    }
+
+    DPRINT(DEBUG_PROTO, proto_debug_level,
+          ("= %d:write AP_REP(%d bytes)\n", my_id, out_data.length));
+    if (kret = krb5_write_message(kcontext,
+                                 (krb5_pointer) &cl_sock,
+                                 &out_data)) {
+       com_err(programname, kret, proto_wr_rep_msg, my_id);
+       goto cleanup;
+    }
+
+    /*
+     * Initialization is now complete.
+     *
+     * If enabled, the protocol times out after proto_proto_timeout seconds.
+     */
+    if (setjmp(timeout_jmp) == 0) {
+       if (proto_proto_timeout > 0) {
+           signal(SIGALRM, proto_alarmclock);
+       }
+       /*
+        * Loop forever - or until somebody puts us out of our misery.
+        */
+       while (1) {
+           krb5_int32  cmd_error;
+           krb5_int32  err_aux;
+           krb5_int32  cmd_repl_ncomps;
+           krb5_data   *cmd_repl_complist;
+           int         do_quit;
+
+           /*
+            * Read a command and figure out what to do.
+            */
+           if (proto_proto_timeout > 0)
+               alarm(proto_proto_timeout);
+           num_args = 0;
+           DPRINT(DEBUG_PROTO, proto_debug_level,
+                  ("= %d:waiting for command\n", my_id));
+           kret = krb5_read_adm_cmd(kcontext,
+                                    (krb5_pointer) &cl_sock,
+                                    auth_context,
+                                    &num_args,
+                                    &arglist);
+           if (proto_proto_timeout > 0)
+               alarm(0);
+           if (kret) {
+               /*
+                * It's OK to have connections abort here.
+                */
+               if (kret == ECONNABORTED) {
+                   com_err(programname, kret, proto_conn_abort_msg, my_id);
+                   kret = 0;
+               }
+               else if (kret == KRB5KRB_AP_ERR_BADORDER) {
+                   com_err(programname, kret, proto_seq_err_msg, my_id);
+                   kret = 0;
+               }
+               else
+                   com_err(programname, kret, proto_rd_cmd_msg, my_id);
+               goto cleanup;
+           }
+
+           cmd_error = KRB5_ADM_SUCCESS;
+           do_quit = 0;
+
+           /*
+            * Now check our arguments.
+            */
+           DPRINT(DEBUG_PROTO, proto_debug_level,
+                  ("= %d:parse command\n", my_id));
+           cmd_repl_ncomps = 0;
+           cmd_repl_complist = (krb5_data *) NULL;
+           err_aux = 0;
+           if (num_args > 0) {
+               if (!strcmp(arglist[0].data, KRB5_ADM_QUIT_CMD)) {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:QUIT command\n", my_id));
+                   /* QUIT takes no arguments */
+                   if (num_args == 1) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:QUIT command syntax OK\n", my_id));
+                       do_quit = 1;
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:QUIT command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KRB5_ADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcmp(arglist[0].data, KRB5_ADM_CHECKPW_CMD)) {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:CHECKPW command\n", my_id));
+                   if (num_args == 2) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHECKPW command syntax OK\n", my_id));
+                       cmd_error = passwd_check(kcontext,
+                                                proto_debug_level,
+                                                auth_context,
+                                                ticket,
+                                                &arglist[1],
+                                                &err_aux);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHECKPW command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KRB5_ADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcmp(arglist[0].data, KRB5_ADM_CHANGEPW_CMD)) {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:CHANGEPW command\n", my_id));
+                   if (num_args == 3) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHANGEPW command syntax OK\n", my_id));
+                       cmd_error = passwd_change(kcontext,
+                                                 proto_debug_level,
+                                                 auth_context,
+                                                 ticket,
+                                                 &arglist[1],
+                                                 &arglist[2],
+                                                 &err_aux);
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:CHANGEPW command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN; 
+                       err_aux = KRB5_ADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcmp(arglist[0].data, KRB5_ADM_MOTD_CMD)) {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:MOTD command\n", my_id));
+                   if (num_args <= 2) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MOTD command syntax OK\n", my_id));
+                       printf("@@@ motd command ");
+                       if (num_args == 2)
+                           printf("context is %s", arglist[2].data);
+                       printf("\n");
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MOTD command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KRB5_ADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcmp(arglist[0].data, KRB5_ADM_MIME_CMD)) {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:MIME command\n", my_id));
+                   if (num_args == 1) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MIME command syntax OK\n", my_id));
+                       mime_setting = 1;
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:MIME command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KRB5_ADM_BAD_ARGS;
+                   }
+               }
+               else if (!strcmp(arglist[0].data, KRB5_ADM_LANGUAGE_CMD)) {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:LANGUAGE command\n", my_id));
+                   if (num_args == 2) {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:LANGUAGE command syntax OK\n", my_id));
+                       if (output_lang_supported(arglist[1].data)) {
+                           if (curr_lang)
+                               free(curr_lang);
+                           curr_lang = (char *)
+                               malloc(strlen(arglist[1].data));
+                           if (curr_lang)
+                               strcpy(curr_lang, arglist[1].data);
+                       }
+                       else
+                           cmd_error = KRB5_ADM_LANG_NOT_SUPPORTED;
+                   }
+                   else {
+                       DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                              ("> %d:LANGUAGE command syntax BAD\n", my_id));
+                       cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                       err_aux = KRB5_ADM_BAD_ARGS;
+                   }
+               }
+               else {
+                   DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                          ("> %d:UNKNOWN command %s\n", my_id,
+                         arglist[0].data));
+                   cmd_error = KRB5_ADM_CMD_UNKNOWN;
+                   err_aux = KRB5_ADM_BAD_CMD;
+               }
+           }
+           else {
+               DPRINT(DEBUG_REQUESTS, proto_debug_level,
+                      ("> %d:NO command!\n", my_id));
+               cmd_error = KRB5_ADM_CMD_UNKNOWN;
+               err_aux = KRB5_ADM_NO_CMD;
+           }
+
+           /*
+            * Now make the reply.
+            */
+           DPRINT(DEBUG_PROTO, proto_debug_level,
+                  ("= %d:sending reply(stat=%d)\n", my_id, cmd_error));
+           if (cmd_error == KRB5_ADM_SUCCESS) {
+               kret = krb5_send_adm_reply(kcontext,
+                                          (krb5_pointer) &cl_sock,
+                                          auth_context,
+                                          cmd_error,
+                                          cmd_repl_ncomps,
+                                          cmd_repl_complist);
+               if (kret) {
+                   com_err(programname, kret, proto_wr_reply_msg, my_id);
+                   goto cleanup;
+               }
+           }
+           else {
+               char            *adm_errmsg;
+               krb5_data       reply_comps;
+
+               adm_errmsg =  output_adm_error(curr_lang,
+                                              mime_setting,
+                                              cmd_error,
+                                              err_aux,
+                                              num_args,
+                                              arglist);
+               if (!adm_errmsg) {
+                   com_err(programname, kret, proto_fmt_reply_msg, my_id);
+                   goto cleanup;
+               }
+               reply_comps.data = adm_errmsg;
+               reply_comps.length = strlen(adm_errmsg);
+               kret = krb5_send_adm_reply(kcontext,
+                                          (krb5_pointer) &cl_sock,
+                                          auth_context,
+                                          cmd_error,
+                                          1,
+                                          &reply_comps);
+               free(adm_errmsg);
+               if (kret) {
+                   com_err(programname, kret, proto_wr_reply_msg, my_id);
+                   goto cleanup;
+               }
+           }
+           if (cmd_repl_ncomps > 0)
+               krb5_free_adm_data(kcontext,
+                                  cmd_repl_ncomps,
+                                  cmd_repl_complist);
+
+           if (do_quit)
+               break;
+           krb5_free_adm_data(kcontext, num_args, arglist);
+       }
+    }
+    else {
+       DLOG(DEBUG_REQUESTS, proto_debug_level, "connection timed out");
+    }
+
+    
+ err_reply:
+    if (kret) {
+       krb5_error_code er_kret;
+       krb5_error      errbuf;
+       char            *errmsg;
+       krb5_data       errout;
+
+       memset((char *) &errbuf, 0, sizeof(errbuf));
+       krb5_us_timeofday(kcontext, &errbuf.stime, &errbuf.susec);
+       errbuf.server = net_server_princ();
+       errbuf.error = kret - ERROR_TABLE_BASE_krb5;
+       if (errbuf.error > 127)
+           errbuf.error = KRB_ERR_GENERIC;
+       /* Format the error message in our language */
+       errmsg = output_krb5_errmsg(curr_lang, mime_setting, kret);
+       errbuf.text.length = strlen(errmsg);
+       errbuf.text.data = errmsg;
+       er_kret = krb5_mk_error(kcontext, &errbuf, &errout);
+       if (!er_kret)
+           krb5_write_message(kcontext, (krb5_pointer) &cl_sock, &errout);
+       free(errbuf.text.data);
+       krb5_xfree(errout.data);
+    }
+
+ cleanup:
+    /* If the alarm was set, make sure it's cancelled */
+    if (proto_proto_timeout > 0)
+       alarm(0);
+    if (ticket)
+       krb5_free_ticket(kcontext, ticket);
+    if (rcache)
+       krb5_rc_close(kcontext, rcache);
+    if (auth_context)
+       krb5_xfree(auth_context);
+    if (curr_lang)
+       free(curr_lang);
+    if (num_args)
+       krb5_free_adm_data(kcontext, num_args, arglist);
+    if (in_data.data)
+       krb5_xfree(in_data.data);
+    if (out_data.data)
+       krb5_xfree(out_data.data);
+    if (local && local->contents)
+       free(local->contents);
+    if (remote && remote->contents)
+       free(remote->contents);
+    if (local)
+       free(local);
+    if (remote)
+       free(remote);
+    close(cl_sock);
+
+ done:
+    DPRINT(DEBUG_CALLS, proto_debug_level, ("X proto_serv() = %d\n", kret));
+    return(kret);
+}
diff --git a/src/kadmin/v5server/srv_acl.c b/src/kadmin/v5server/srv_acl.c
new file mode 100644 (file)
index 0000000..5cca1b0
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * kadmin/v5server/srv_acl.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.
+ *
+ */
+
+/*
+ * srv_acl.c - Handle Kerberos ACL related functions.
+ */
+#include <stdio.h>
+#include <sys/param.h>
+#include <sys/signal.h>
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+
+typedef struct _acl_op_table {
+    char       ao_op;
+    krb5_int32 ao_mask;
+} aop_t;
+
+typedef struct _acl_entry {
+    struct _acl_entry  *ae_next;
+    char               *ae_name;
+    int                        ae_name_bad;
+    krb5_principal     ae_principal;
+    krb5_int32         ae_op_allowed;
+} aent_t;
+
+static const aop_t acl_op_table[] = {
+    { 'a',     ACL_ADD_PRINCIPAL },
+    { 'd',     ACL_DELETE_PRINCIPAL },
+    { 'm',     ACL_MODIFY_PRINCIPAL },
+    { 'c',     ACL_CHANGEPW },
+    { 'o',     ACL_CHANGE_OWN_PW },
+    { 'i',     ACL_INQUIRE },
+    { 'p',     ACL_PRINCIPAL_MASK },
+    { 'w',     ACL_PASSWD_MASK },
+    { 'x',     ACL_ALL_MASK },
+    { '*',     ACL_ALL_MASK },
+    { '\0',    0 }
+};
+
+static aent_t  *acl_list_head = (aent_t *) NULL;
+static aent_t  *acl_list_tail = (aent_t *) NULL;
+
+static const char *acl_default_file = "/etc/krb5_adm.acl";
+static char *acl_acl_file = (char *) NULL;
+static int acl_inited = 0;
+static int acl_debug_level = 0;
+
+static const char *acl_line2long_msg = "%s: line %d too long, truncated\n";
+static const char *acl_op_bad_msg = "Unrecognized ACL operation '%c' in %s\n";
+static const char *acl_syn_err_msg = "%s: syntax error at line %d <%10s...>\n";
+static const char *acl_cantopen_msg = "cannot open ACL file";
+\f
+/*
+ * acl_get_line()      - Get a line from the ACL file.
+ */
+static char *
+acl_get_line(fp, lnp)
+    FILE       *fp;
+    int                *lnp;
+{
+    int                i, domore;
+    static char acl_buf[BUFSIZ];
+
+    for (domore = 1; domore && !feof(fp); ) {
+       /* Copy in the line */
+       for (i=0;
+            ((i<BUFSIZ) &&
+             (!feof(fp)) &&
+             ((acl_buf[i] = fgetc(fp)) != '\n'));
+            i++);
+       acl_buf[i] = '\0';
+
+       /* Check if we exceeded our buffer size */
+       if ((i == BUFSIZ) && (!feof(fp)) && (acl_buf[i] != '\n')) {
+           fprintf(stderr, acl_line2long_msg, acl_acl_file, *lnp);
+           while (fgetc(fp) != '\n');
+       }
+       if (acl_buf[0] == EOF)  /* ptooey */
+           acl_buf[0] = '\0';
+       else
+           (*lnp)++;
+       if ((acl_buf[0] != '#') && (acl_buf[0] != '\0'))
+           domore = 0;
+    }
+    if (domore || (strlen(acl_buf) == 0))
+       return((char *) NULL);
+    else
+       return(acl_buf);
+}
+\f
+/*
+ * acl_parse_line()    - Parse the contents of an ACL line.
+ */
+static aent_t *
+acl_parse_line(lp)
+    char *lp;
+{
+    static char acle_principal[BUFSIZ];
+    static char acle_ops[BUFSIZ];
+    aent_t     *acle;
+    char       *op;
+    int                t, found, opok;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("* acl_parse_line(line=%20s)\n", lp));
+    /*
+     * Format is very simple:
+     * entry ::=       <whitespace> <principal> <whitespace> <opstring>
+     */
+    acle = (aent_t *) NULL;
+    if (sscanf(lp, "%s %s", acle_principal, acle_ops) == 2) {
+       acle = (aent_t *) malloc(sizeof(aent_t));
+       if (acle) {
+           acle->ae_next = (aent_t *) NULL;
+           acle->ae_op_allowed = (krb5_int32) 0;
+           opok = 1;
+           for (op=acle_ops; *op; op++) {
+               char rop;
+
+               rop = (isupper(*op)) ? tolower(*op) : *op;
+               found = 0;
+               for (t=0; acl_op_table[t].ao_op; t++) {
+                   if (rop == acl_op_table[t].ao_op) {
+                       found = 1;
+                       if (rop == *op)
+                           acle->ae_op_allowed |= acl_op_table[t].ao_mask;
+                       else
+                           acle->ae_op_allowed &= ~acl_op_table[t].ao_mask;
+                   }
+               }
+               if (!found) {
+                   fprintf(stderr, acl_op_bad_msg, *op, lp);
+                   opok = 0;
+               }
+           }
+           if (opok) {
+               acle->ae_name = (char *) malloc(strlen(acle_principal)+1);
+               if (acle->ae_name) {
+                   strcpy(acle->ae_name, acle_principal);
+                   acle->ae_principal = (krb5_principal) NULL;
+                   acle->ae_name_bad = 0;
+                   DPRINT(DEBUG_ACL, acl_debug_level,
+                          ("A ACL entry %s -> opmask %x\n",
+                           acle->ae_name, acle->ae_op_allowed));
+               }
+               else {
+                   free(acle);
+                   acle = (aent_t *) NULL;
+               }
+           }
+           else {
+               free(acle);
+               acle = (aent_t *) NULL;
+           }
+       }
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("X acl_parse_line() = %x\n", (long) acle));
+    return(acle);
+}
+\f
+/*
+ * acl_free_entries()  - Free all ACL entries.
+ */
+static void
+acl_free_entries()
+{
+    aent_t     *ap;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_free_entries()\n"));
+    for (ap=acl_list_head; ap; ap = ap->ae_next) {
+       if (ap->ae_name)
+           free(ap->ae_name);
+       if (ap->ae_principal)
+           krb5_free_principal((krb5_context) NULL, ap->ae_principal);
+       free(ap);
+    }
+    acl_list_head = acl_list_tail = (aent_t *) NULL;
+    acl_inited = 0;
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_free_entries()\n"));
+}
+\f
+/*
+ * acl_load_acl_file() - Open and parse the ACL file.
+ */
+static int
+acl_load_acl_file()
+{
+    FILE       *afp;
+    char       *alinep;
+    aent_t     **aentpp;
+    int                alineno;
+    int                retval = 1;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_load_acl_file()\n"));
+    /* Open the ACL file for read */
+    if (afp = fopen(acl_acl_file, "r")) {
+       alineno = 1;
+       aentpp = &acl_list_head;
+
+       /* Get a non-comment line */
+       while (alinep = acl_get_line(afp, &alineno)) {
+           /* Parse it */
+           *aentpp = acl_parse_line(alinep);
+           /* If syntax error, then fall out */
+           if (!*aentpp) {
+               fprintf(stderr, acl_syn_err_msg,
+                       acl_acl_file, alineno, alinep);
+               retval = 0;
+               break;
+           }
+           acl_list_tail = *aentpp;
+           aentpp = &(*aentpp)->ae_next;
+       }
+       fclose(afp);
+    }
+    else {
+       com_err(acl_acl_file, errno, acl_cantopen_msg);
+    }
+
+    if (!retval) {
+       acl_free_entries();
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("X acl_load_acl_file() = %d\n", retval));
+    return(retval);
+}
+\f
+/*
+ * acl_reload_acl_file()       - Reload the acl file.
+ */
+static krb5_sigtype
+acl_reload_acl_file()
+{
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_reload_acl_file()\n"));
+    acl_free_entries();
+    acl_inited = acl_load_acl_file();
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_reload_acl_file()\n"));
+}
+\f
+/*
+ * acl_match_data()    - See if two data entries match.
+ *
+ * Wildcarding is only supported for a whole component.
+ */
+static krb5_boolean
+acl_match_data(e1, e2)
+    krb5_data  *e1, *e2;
+{
+    krb5_boolean       retval;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, 
+          ("* acl_match_entry(%s, %s)\n", e1->data, e2->data));
+    retval = 0;
+    if (!strncmp(e1->data, "*", e1->length) ||
+       !strncmp(e2->data, "*", e2->length)) {
+       retval = 1;
+    }
+    else {
+       if ((e1->length == e2->length) &&
+           (!strncmp(e1->data, e2->data, e1->length)))
+           retval = 1;
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval));
+    return(retval);
+}
+\f
+/*
+ * acl_find_entry()    - Find a matching entry.
+ */
+static aent_t *
+acl_find_entry(kcontext, principal)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+{
+    aent_t             *entry;
+    krb5_error_code    kret;
+    int                        i;
+    int                        matchgood;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_find_entry()\n"));
+    for (entry=acl_list_head; entry; entry = entry->ae_next) {
+       if (!strcmp(entry->ae_name, "*")) {
+           DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
+           break;
+       }
+       if (!entry->ae_principal && !entry->ae_name_bad) {
+           kret = krb5_parse_name(kcontext,
+                                  entry->ae_name,
+                                  &entry->ae_principal);
+           if (kret)
+               entry->ae_name_bad = 1;
+       }
+       if (entry->ae_name_bad) {
+           DPRINT(DEBUG_ACL, acl_debug_level,
+                  ("A Bad ACL entry %s\n", entry->ae_name));
+           continue;
+       }
+       matchgood = 0;
+       if (acl_match_data(&entry->ae_principal->realm,
+                          &principal->realm) &&
+           (entry->ae_principal->length == principal->length)) {
+           matchgood = 1;
+           for (i=0; i<principal->length; i++) {
+               if (!acl_match_data(&entry->ae_principal->data[i],
+                                   &principal->data[i])) {
+                   matchgood = 0;
+                   break;
+               }
+           }
+       }
+       if (matchgood)
+           break;
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_find_entry()=%x\n",entry));
+    return(entry);
+}
+\f
+/*
+ * acl_init()  - Initialize ACL context.
+ */
+krb5_error_code
+acl_init(kcontext, debug_level, acl_file)
+    krb5_context       kcontext;
+    int                        debug_level;
+    char               *acl_file;
+{
+    krb5_error_code    kret;
+
+    kret = 0;
+    acl_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, acl_debug_level,
+          ("* acl_init(afile=%s)\n",
+           ((acl_file) ? acl_file : "(null)")));
+    acl_acl_file = (acl_file) ? acl_file : acl_default_file;
+    acl_inited = acl_load_acl_file();
+    signal(SIGHUP, acl_reload_acl_file);
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_init() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * acl_finish  - Terminate ACL context.
+ */
+void
+acl_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_finish()\n"));
+    acl_free_entries();
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_finish()\n"));
+}
+\f
+/*
+ * acl_op_permitted()  - Is this operation permitted for this principal?
+ */
+krb5_boolean
+acl_op_permitted(kcontext, principal, opmask)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_int32         opmask;
+{
+    krb5_boolean       retval;
+    aent_t             *aentry;
+
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n"));
+    retval = 1;
+    if (aentry = acl_find_entry(kcontext, principal)) {
+       if ((aentry->ae_op_allowed & opmask) != opmask)
+           retval = 0;
+    }
+    DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n",
+                                         retval));
+    return(retval);
+}
diff --git a/src/kadmin/v5server/srv_key.c b/src/kadmin/v5server/srv_key.c
new file mode 100644 (file)
index 0000000..8a3055a
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * kadmin/v5server/srv_key.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.
+ *
+ */
+
+/*
+ * srv_key.c - Handle Kerberos key related functions.
+ */
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+#include "mit-des.h"
+
+static const char *key_bad_etype_fmt = "%s: bad etype %d (%s).\n";
+static const char *key_def_realm_fmt = "%s: cannot find default realm (%s).\n";
+static const char *key_setup_mkey_fmt = "%s: cannot setup master key name (%s).\n";
+static const char *key_get_mkey_fmt = "%s: cannot retrieve master key (%s).\n";
+static const char *key_vfy_mkey_fmt = "%s: cannot verify master key (%s).\n";
+static const char *key_proc_mkey_fmt = "%s: cannot process master key (%s).\n";
+static const char *key_kgen_initf_fmt = "%s: disabling key type %d because initialization failed (%s).\n";
+static const char *key_bad_name_fmt = "%s: cannot set database name to %s (%s).\n";
+static const char *key_cant_init_fmt = "%s: cannot initialize database (%s).\n";
+static const char *key_vmast_key_fmt = "%s: cannot verify master key (%s).\n";
+static const char *key_key_pp_fmt = "%s: cannot preprocess key (%s).\n";
+static const char *key_getm_fmt = "%s: cannot get master entry (%s).\n";
+
+static int             mprinc_init = 0;
+static krb5_principal  master_principal;
+
+static int             mkeyb_init = 0;
+static krb5_keyblock   master_keyblock;
+
+static int             mencb_init = 0;
+static krb5_encrypt_block master_encblock;
+
+static int             mrand_init = 0;
+static krb5_pointer    master_random;
+
+static int             ment_init = 0;
+static krb5_db_entry   master_entry;
+
+static int key_debug_level = 0;
+
+extern char *programname;
+\f
+/*
+ * key_init()  - Initialize key context.
+ */
+krb5_error_code
+key_init(kcontext, debug_level, enc_type, key_type, master_key_name, manual,
+        db_file, db_realm)
+    krb5_context       kcontext;
+    int                        debug_level;
+    int                        enc_type;
+    int                        key_type;
+    char               *master_key_name;
+    int                        manual;
+    char               *db_file;
+    char               *db_realm;
+{
+    krb5_enctype       kdc_etype;
+    char               *mkey_name;
+    char               *the_realm;
+
+    krb5_error_code    kret;
+    krb5_enctype       etype;
+    int                        one_success;
+    int                number_of_entries;
+    krb5_boolean       more_entries;
+
+    key_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, key_debug_level,
+          ("* key_init(enc-type=%d, key-type=%d,\n\tmkeyname=%s, manual=%d,\n\tdb=%s, realm=%s)\n",
+           enc_type, key_type,
+           ((master_key_name) ? master_key_name : "(null)"),
+           manual,
+           ((db_file) ? db_file : "(default)"),
+           ((db_realm) ? db_realm : "(null)")));
+    /*
+     * Figure out arguments.
+     */
+    master_keyblock.keytype = ((key_type == -1) ? KEYTYPE_DES : key_type);
+    mkey_name = ((!master_key_name) ? KRB5_KDB_M_NAME : master_key_name);
+    kdc_etype = ((enc_type == -1) ? DEFAULT_KDC_ETYPE : enc_type);
+    if (!valid_etype(kdc_etype)) {
+       kret = KRB5_PROG_ETYPE_NOSUPP;
+       fprintf(stderr, key_bad_etype_fmt, programname, kdc_etype,
+               error_message(kret));
+       goto leave;
+    }
+    if (!db_realm) {
+       kret = krb5_get_default_realm(kcontext, &the_realm);
+       if (kret) {
+           fprintf(stderr, key_def_realm_fmt, programname,
+                   error_message(kret));
+           goto leave;
+       }
+    }
+    else
+       the_realm = db_realm;
+    DPRINT(DEBUG_REALM, key_debug_level,
+          ("- initializing for realm %s\n", the_realm));
+
+    /* Set database name if supplied */
+    if (db_file && (kret = krb5_db_set_name(kcontext, db_file))) {
+       fprintf(stderr, key_bad_name_fmt, programname, db_file,
+               error_message(kret));
+       goto leave;
+    }
+
+    /* Initialize database */
+    if (kret = krb5_db_init(kcontext)) {
+       fprintf(stderr, key_cant_init_fmt, programname,
+               error_message(kret));
+       goto leave;
+    }
+
+    /* Assemble and parse the master key name */
+    kret = krb5_db_setup_mkey_name(kcontext,
+                                  mkey_name,
+                                  the_realm,
+                                  (char **) NULL,
+                                  &master_principal);
+    if (kret) {
+       fprintf(stderr, key_setup_mkey_fmt, programname,
+               error_message(kret));
+       goto cleanup;
+    }
+    mprinc_init = 1;
+    DPRINT(DEBUG_HOST, key_debug_level,
+          ("- master key is %s@%s\n", mkey_name, the_realm));
+
+    /* Get the master database entry and save it. */
+    number_of_entries = 1;
+    kret = krb5_db_get_principal(kcontext,
+                                    master_principal,
+                                    &master_entry,
+                                    &number_of_entries,
+                                    &more_entries);
+    if (!kret) {
+       if (number_of_entries != 1) {
+           if (number_of_entries)
+               krb5_db_free_principal(kcontext,
+                                      &master_entry,
+                                      number_of_entries);
+           kret = KRB5_KDB_NOMASTERKEY;
+       }
+       else if (more_entries) {
+           krb5_db_free_principal(kcontext,
+                                  &master_entry,
+                                  number_of_entries);
+           kret = KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE;
+       }
+    }
+    if (kret) {
+       fprintf(stderr, key_getm_fmt, programname, error_message(kret));
+       goto leave;
+    }
+    ment_init = 1;
+
+    krb5_use_cstype(kcontext, &master_encblock, kdc_etype);
+    mencb_init = 1;
+
+    /* Go get the master key */
+    kret = krb5_db_fetch_mkey(kcontext,
+                             master_principal,
+                             &master_encblock,
+                             manual,
+                             FALSE,            /* Only read once if manual */
+                             0,                /* No salt */
+                             &master_keyblock);
+    if (kret) {
+       fprintf(stderr, key_get_mkey_fmt, programname,
+               error_message(kret));
+       goto cleanup;
+    }
+    mkeyb_init = 1;
+
+    /* Verify the master key */
+    if (kret = krb5_db_verify_master_key(kcontext,
+                                        master_principal,
+                                        &master_keyblock,
+                                        &master_encblock)) {
+       fprintf(stderr, key_vmast_key_fmt, programname,
+               error_message(kret));
+       goto leave;
+    }
+
+    /* Do any key pre-processing */
+    if (kret = krb5_process_key(kcontext,
+                               &master_encblock,
+                               &master_keyblock)) {
+       fprintf(stderr, key_key_pp_fmt, programname, error_message(kret));
+       goto leave;
+    }
+
+    /* Now initialize the random key */
+    kret = krb5_init_random_key(kcontext,
+                               &master_encblock,
+                               &master_keyblock,
+                               &master_random);
+    if (!kret)
+       mrand_init = 1;
+
+ cleanup:
+    if (kret) {
+       if (mrand_init) {
+           krb5_finish_random_key(kcontext, &master_encblock, &master_random);
+           mrand_init = 0;
+       }
+       if (mencb_init) {
+           krb5_finish_key(kcontext, &master_encblock);
+           mencb_init = 0;
+       }
+       if (mkeyb_init) {
+           memset(master_keyblock.contents, 0, master_keyblock.length);
+           krb5_xfree(master_keyblock.contents);
+           mkeyb_init = 0;
+       }
+       if (ment_init) {
+           krb5_db_free_principal(kcontext, &master_entry, 1);
+           ment_init = 0;
+       }
+    }
+    if (the_realm != db_realm)
+       krb5_xfree(the_realm);
+ leave:
+    DPRINT(DEBUG_CALLS, key_debug_level, ("X key_init() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * key_finish  - Terminate key context.
+ */
+void
+key_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, key_debug_level, ("* key_finish()\n"));
+    if (mrand_init) {
+       krb5_finish_random_key(kcontext, &master_encblock, &master_random);
+       mrand_init = 0;
+    }
+    if (mkeyb_init) {
+       krb5_finish_key(kcontext, &master_encblock);
+       memset((char *) &master_encblock, 0,
+              sizeof(master_encblock));
+       mkeyb_init = 0;
+    }
+    if (mprinc_init) {
+       krb5_free_principal(kcontext, master_principal);
+       mprinc_init = 0;
+    }
+    if (mencb_init) {
+       master_encblock.crypto_entry = 0;
+       mencb_init = 0;
+    }
+    if (ment_init) {
+       krb5_db_free_principal(kcontext, &master_entry, 1);
+       ment_init = 0;
+    }
+    krb5_db_fini(kcontext);
+    /* memset((char *) tgs_key.contents, 0, tgs_key.length); */
+    DPRINT(DEBUG_CALLS, key_debug_level, ("X key_finish()\n"));
+}
+\f
+/*
+ * key_string_to_keys() - convert string to keys.
+ */
+krb5_error_code
+key_string_to_keys(kcontext, principal, string, psalttype, asalttype,
+                  primary, alternate)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_data          *string;
+    krb5_int32         psalttype;
+    krb5_int32         asalttype;
+    krb5_keyblock      *primary;
+    krb5_keyblock      *alternate;
+{
+    krb5_error_code    kret;
+    krb5_data          psalt_data, asalt_data;
+
+    DPRINT(DEBUG_CALLS, key_debug_level, ("* key_string_to_keys()\n"));
+    kret = KRB_ERR_GENERIC;
+    psalt_data.length = asalt_data.length = 0;
+    psalt_data.data = asalt_data.data = (char *) NULL;
+
+    /*
+     * Determine the primary salt type.
+     */
+    switch (psalttype) {
+    case KRB5_KDB_SALTTYPE_NORMAL:
+       /* Normal salt */
+       if (kret = krb5_principal2salt(kcontext, principal, &psalt_data))
+           goto done;
+       asalt_data.data = (char *) NULL;
+       asalt_data.length = 0;
+       break;
+    case KRB5_KDB_SALTTYPE_V4:
+       /* V4 salt */
+       psalt_data.data = (char *) NULL;
+       psalt_data.length = 0;
+       if (kret = krb5_principal2salt(kcontext, principal, &asalt_data))
+           goto done;
+       break;
+    case KRB5_KDB_SALTTYPE_NOREALM:
+       if (kret = krb5_principal2salt_norealm(kcontext,
+                                              principal,
+                                              &psalt_data))
+           goto done;
+       asalt_data.data = (char *) NULL;
+       asalt_data.length = 0;
+       break;
+    case KRB5_KDB_SALTTYPE_ONLYREALM:
+    {
+       krb5_data       *tmp;
+
+       if (kret = krb5_copy_data(kcontext,
+                                 krb5_princ_realm(kcontext, principal),
+                                 &tmp))
+           goto done;
+       psalt_data = *tmp;
+       krb5_xfree(tmp);
+       asalt_data.data = (char *) NULL;
+       asalt_data.length = 0;
+       break;
+    }
+    default:
+       goto done;
+    }
+
+    /* Now convert the string to keys */
+    kret = krb5_string_to_key(kcontext,
+                             &master_encblock,
+                             master_keyblock.keytype,
+                             primary,
+                             string,
+                             &psalt_data);
+    if (!kret)
+       kret = krb5_string_to_key(kcontext,
+                                 &master_encblock,
+                                 master_keyblock.keytype,
+                                 alternate,
+                                 string,
+                                 &asalt_data);
+
+ done:
+    if (kret) {
+       if (primary->contents) {
+           memset((char *) primary->contents, 0, primary->length);
+           krb5_xfree(primary->contents);
+       }
+       if (alternate->contents) {
+           memset((char *) alternate->contents, 0, alternate->length);
+           krb5_xfree(alternate->contents);
+       }
+    }
+    if (psalt_data.data) {
+       memset(psalt_data.data, 0, psalt_data.length);
+       krb5_xfree(psalt_data.data);
+    }
+    if (asalt_data.data) {
+       memset(asalt_data.data, 0, asalt_data.length);
+       krb5_xfree(asalt_data.data);
+    }
+    DPRINT(DEBUG_CALLS, key_debug_level,
+          ("X key_string_to_keys() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * key_encrypt_keys() - encrypt keys.
+ */
+krb5_error_code
+key_encrypt_keys(kcontext, principal, primary, alternate, eprimary, ealternate)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_keyblock      *primary;
+    krb5_keyblock      *alternate;
+    krb5_encrypted_keyblock    *eprimary;
+    krb5_encrypted_keyblock    *ealternate;
+{
+    krb5_error_code    kret;
+
+    DPRINT(DEBUG_CALLS, key_debug_level, ("* key_encrypt_keys()\n"));
+
+    kret = krb5_kdb_encrypt_key(kcontext,
+                               &master_encblock,
+                               primary,
+                               eprimary);
+    if (kret)
+       kret = krb5_kdb_encrypt_key(kcontext,
+                                   &master_encblock,
+                                   alternate,
+                                   ealternate);
+ done:
+    if (kret) {
+       if (eprimary->contents) {
+           memset((char *) eprimary->contents, 0, eprimary->length);
+           krb5_xfree(eprimary->contents);
+       }
+       if (ealternate->contents) {
+           memset((char *) ealternate->contents, 0, ealternate->length);
+           krb5_xfree(ealternate->contents);
+       }
+    }
+    DPRINT(DEBUG_CALLS, key_debug_level,
+          ("X key_encrypt_keys() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * key_decrypt_keys() - decrypt keys.
+ */
+krb5_error_code
+key_decrypt_keys(kcontext, principal, eprimary, ealternate, primary, alternate)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_encrypted_keyblock    *eprimary;
+    krb5_encrypted_keyblock    *ealternate;
+    krb5_keyblock      *primary;
+    krb5_keyblock      *alternate;
+{
+    krb5_error_code    kret;
+
+    DPRINT(DEBUG_CALLS, key_debug_level, ("* key_decrypt_keys()\n"));
+
+    kret = krb5_kdb_decrypt_key(kcontext,
+                               &master_encblock,
+                               eprimary,
+                               primary);
+    if (kret)
+       kret = krb5_kdb_decrypt_key(kcontext,
+                                   &master_encblock,
+                                   ealternate,
+                                   alternate);
+ done:
+    if (kret) {
+       if (primary->contents) {
+           memset((char *) primary->contents, 0, primary->length);
+           krb5_xfree(primary->contents);
+       }
+       if (alternate->contents) {
+           memset((char *) alternate->contents, 0, alternate->length);
+           krb5_xfree(alternate->contents);
+       }
+    }
+    DPRINT(DEBUG_CALLS, key_debug_level,
+          ("X key_decrypt_keys() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * key_pwd_is_weak()   - Check for weakness of key from password
+ */
+krb5_boolean
+key_pwd_is_weak(kcontext, principal, string, psalttype, asalttype)
+    krb5_context       kcontext;
+    krb5_principal     principal;
+    krb5_data          *string;
+    krb5_int32         psalttype;
+    krb5_int32         asalttype;
+{
+    krb5_boolean       weakness;
+    krb5_error_code    kret;
+    krb5_keyblock      primary;
+    krb5_keyblock      alternate;
+
+    DPRINT(DEBUG_CALLS, key_debug_level, ("* key_pwd_is_weak()\n"));
+    weakness = 0;
+
+    if (master_encblock.key->etype != ETYPE_NULL) {
+       memset((char *) &primary, 0, sizeof(primary));
+       memset((char *) &alternate, 0, sizeof(alternate));
+
+       kret = key_string_to_keys(kcontext,
+                                 principal,
+                                 string,
+                                 psalttype,
+                                 asalttype,
+                                 &primary,
+                                 &alternate);
+       if (!kret) {
+           if (primary.length &&
+               (primary.length == sizeof(mit_des_cblock)) &&
+               mit_des_is_weak_key(primary.contents))
+               weakness = 1;
+           if (alternate.length &&
+               (alternate.length == sizeof(mit_des_cblock)) &&
+               mit_des_is_weak_key(alternate.contents))
+               weakness = 1;
+           if (primary.contents) {
+               memset((char *) primary.contents, 0, primary.length);
+               krb5_xfree(primary.contents);
+           }
+           if (alternate.contents) {
+               memset((char *) alternate.contents, 0, alternate.length);
+               krb5_xfree(alternate.contents);
+           }
+       }
+    }
+    DPRINT(DEBUG_CALLS, key_debug_level,
+          ("X key_pwd_is_weak() = %d\n", weakness));
+    return(weakness);
+}
+
diff --git a/src/kadmin/v5server/srv_main.c b/src/kadmin/v5server/srv_main.c
new file mode 100644 (file)
index 0000000..ff11545
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * kadmin/v5server/srv_main.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.
+ *
+ */
+
+/*
+ * srv_main.c
+ * Main function of administrative server which speaks new Kerberos V5
+ * administrative protocol.
+ */
+\f
+#include <stdio.h>
+#include <sys/signal.h>
+#include <syslog.h>
+#include <setjmp.h>
+#include "k5-int.h"
+#include "com_err.h"
+#if    HAVE_STDARG_H
+#include <stdarg.h>
+#else  /* HAVE_STDARG_H */
+#include <varargs.h>
+#endif /* HAVE_STDARG_H */
+
+#define        KADM_MAX_ERRMSG_SIZE    1024
+#ifndef        LOG_AUTH
+#define        LOG_AUTH        0
+#endif /* LOG_AUTH */
+
+static const char *usage_format =      "%s: usage is %s [-a aclfile] [-d database] [-e enctype] [-i]\n\t[-k mkeytype] [-l langlist] [-m bool] [-r realm] [-t timeout]\n\t[-D dbg] [-M mkeyname].\n";
+static const char *fval_not_number =   "%s: value (%s) specified for -%c is not numeric.\n";
+static const char *extra_params =      "%s extra paramters beginning with %s... \n";
+static const char *no_memory_fmt =     "%s: cannot allocate %d bytes for %s.\n";
+static const char *begin_op_msg =      "%s starting.";
+static const char *disp_err_fmt =      "dispatch error.";
+static const char *happy_exit_fmt =    "terminating normally.";
+static const char *init_error_fmt =    "%s: cannot initialize %s.\n";
+static const char *unh_signal_fmt =    "exiting on signal %d.";
+
+static const char *messages_msg =      "messages";
+static const char *proto_msg =         "protocol module";
+static const char *net_msg =           "network";
+static const char *output_msg =                "output";
+static const char *acl_msg =           "ACLs";
+static const char *key_msg =           "key and database";
+static const char *server_name_msg =   "Kerberos V5 administrative server";
+
+char *programname = (char *) NULL;
+static jmp_buf terminal_jmp;
+\f
+static void
+usage(prog)
+    char *prog;
+{
+    fprintf(stderr, usage_format, prog, prog);
+}
+
+/*
+ * An unhandled signal just proceeds from the setjmp() in main.
+ */
+static krb5_sigtype
+unhandled_signal(signo)
+    int signo;
+{
+    longjmp(terminal_jmp, signo);
+    /* NOTREACHED */
+}
+
+static void
+kadm_com_err_proc(whoami, code, format, ap)
+    const char *whoami;
+    long       code;
+    const char *format;
+    va_list    ap;
+{
+    char *outbuf;
+
+    outbuf = (char *) malloc(KADM_MAX_ERRMSG_SIZE);
+    if (outbuf) {
+       char *cp;
+       sprintf(outbuf, "%s: ", whoami);
+       if (code) {
+           strcat(outbuf, error_message(code));
+           strcat(outbuf, " - ");
+       }
+       cp = &outbuf[strlen(outbuf)];
+       vsprintf(cp, format, ap);
+#ifndef        DEBUG
+       syslog(LOG_AUTH|LOG_ERR, outbuf);
+#endif /* DEBUG */
+       strcat(outbuf, "\n");
+       fprintf(stderr, outbuf);
+       free(outbuf);
+    }
+    else {
+       fprintf(stderr, no_memory_fmt, programname,
+               KADM_MAX_ERRMSG_SIZE, messages_msg);
+    }
+}
+
+int
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    extern int         optind;
+    extern char                *optarg;
+    int                        option;
+    krb5_error_code    error;
+
+    int                        enc_type = -1;
+    int                        key_type = -1;
+    int                        manual_entry = 0;
+    krb5_boolean       mime_enabled = 0;
+    int                        debug_level = 0;
+    int                        timeout = -1;
+    char               *acl_file = (char *) NULL;
+    char               *db_file = (char *) NULL;
+    char               *language_list = (char *) NULL;
+    char               *db_realm = (char *) NULL;
+    char               *master_key_name = (char *) NULL;
+
+    /* Kerberatic contexts */
+    krb5_context       kcontext;
+
+    const char         *errmsg;
+    int                        signal_number;
+
+    /*
+     * usage is:
+     * kadmind5        [-a aclfile]
+     *                 [-d database]
+     *                 [-e enctype]
+     *                 [-i]
+     *                 [-k masterkeytype]
+     *                 [-l languagelist]
+     *                 [-m yesno]
+     *                 [-r realmname]
+     *                 [-t timeout]
+     *                 [-D debuglevel]
+     *                 [-M masterkeyname]
+     */
+    error = 0;
+    while ((option = getopt(argc, argv, "a:d:e:ik:l:m:r:t:D:M:")) != EOF) {
+       switch (option) {
+       case 'a':
+           acl_file = optarg;
+           break;
+       case 'd':
+           db_file = optarg;
+           break;
+       case 'e':
+           if (sscanf(optarg, "%d", &enc_type) != 1) {
+               fprintf(stderr, fval_not_number, argv[0], optarg, 'e');
+               error++;
+           }
+           break;
+       case 'i':
+           manual_entry++;
+           break;
+       case 'k':
+           if (sscanf(optarg, "%d", &key_type) != 1) {
+               fprintf(stderr, fval_not_number, argv[0], optarg, 'k');
+               error++;
+           }
+           break;
+       case 'l':
+           language_list = optarg;
+           break;
+       case 'm':
+           if (sscanf(optarg, "%d", &mime_enabled) != 1) {
+               fprintf(stderr, fval_not_number, argv[0], optarg, 'm');
+               error++;
+           }
+           break;
+       case 'r':
+           db_realm = optarg;
+           break;
+       case 't':
+           if (sscanf(optarg, "%d", &timeout) != 1) {
+               fprintf(stderr, fval_not_number, argv[0], optarg, 't');
+               error++;
+           }
+           break;
+       case 'D':
+           if (sscanf(optarg, "%d", &debug_level) != 1) {
+               fprintf(stderr, fval_not_number, argv[0], optarg, 'D');
+               error++;
+           }
+           break;
+       case 'M':
+           master_key_name = optarg;
+           break;
+       default:
+           error++;
+           break;
+       }
+    }
+    if (optind - argc > 0) {
+       fprintf(stderr, extra_params, argv[0], argv[optind+1]);
+       error++;
+    }
+    if (error) {
+       usage(argv[0]);
+       return(1);
+    }
+
+    /*
+     * We've come this far.  Our arguments are good.
+     */
+#ifndef        DEBUG
+    programname = (char *) strrchr(argv[0], '/');
+    if (programname)
+       programname++;
+    else
+       programname = argv[0];
+#else  /* DEBUG */
+    programname = argv[0];
+#endif /* DEBUG */
+    krb5_init_context(&kcontext);
+    krb5_init_ets(kcontext);
+    openlog(programname, LOG_AUTH|LOG_CONS|LOG_NDELAY|LOG_PID, LOG_LOCAL6);
+    (void) set_com_err_hook(kadm_com_err_proc);
+
+    if ((signal_number = setjmp(terminal_jmp)) == 0) {
+       /*
+        * Initialize signal handling.
+        */
+       signal(SIGINT, unhandled_signal);
+       signal(SIGTERM, unhandled_signal);
+       signal(SIGHUP, unhandled_signal);
+       signal(SIGQUIT, unhandled_signal);
+       signal(SIGPIPE, unhandled_signal);
+       signal(SIGALRM, unhandled_signal);
+       signal(SIGCHLD, unhandled_signal);
+
+       /*
+        * Initialize our modules.
+        */
+       error = key_init(kcontext, debug_level, enc_type, key_type,
+                        master_key_name, manual_entry, db_file, db_realm);
+       if (!error) {
+           error = acl_init(kcontext, debug_level, acl_file);
+           if (!error) {
+               error = output_init(kcontext, debug_level,
+                                   language_list, mime_enabled);
+               if (!error) {
+                   error = net_init(kcontext,
+                                    debug_level);
+                   if (!error) {
+                       error = proto_init(kcontext, debug_level, timeout);
+
+                       if (error)
+                           errmsg = proto_msg;
+                   }
+                   else
+                       errmsg = net_msg;
+               }
+               else
+                   errmsg = output_msg;
+           }
+           else
+               errmsg = acl_msg;
+       }
+       else
+           errmsg = key_msg;
+
+       if (!error) {
+           /*
+            * We've successfully initialized here.
+            */
+#ifndef        DEBUG
+           syslog(LOG_AUTH|LOG_INFO, begin_op_msg, server_name_msg);
+#endif /* DEBUG */
+
+           /*
+            * net_dispatch() only returns when we're done for some reason.
+            */
+           error = net_dispatch(kcontext);
+
+           com_err(programname, error,
+                   ((error) ? disp_err_fmt : happy_exit_fmt));
+       }
+       else {
+           /* Initialization error */
+           fprintf(stderr, init_error_fmt, programname, errmsg);
+       }
+    }
+    else {
+       /* Received an unhandled signal */
+       com_err(programname, 0, unh_signal_fmt, signal_number);
+    }
+
+    /* Now clean up after ourselves */
+    proto_finish(kcontext, debug_level);
+    net_finish(kcontext, debug_level);
+    output_finish(kcontext, debug_level);
+    acl_finish(kcontext, debug_level);
+    key_finish(kcontext, debug_level);
+    krb5_xfree(kcontext);
+    return(error);
+}
diff --git a/src/kadmin/v5server/srv_net.c b/src/kadmin/v5server/srv_net.c
new file mode 100644 (file)
index 0000000..bdef499
--- /dev/null
@@ -0,0 +1,664 @@
+/*
+ * kadmin/v5server/srv_net.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.
+ *
+ */
+
+/*
+ * srv_net.c - handle networking functions of the administrative server.
+ */
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <sys/wait.h>
+#ifdef USE_PTHREADS
+#include <pthread.h>
+#endif /* USE_PTHREADS */
+
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+#include "adm.h"
+
+#define        MAX_BIND_TRIES  5
+
+/*
+ * This module can use the pthreads library.  To do so, define USE_PTHREADS.
+ * You'll need to find out what else pthreads requires (e.g. -lmach -lc_r
+ * under OSF/1).
+ */
+#ifdef USE_PTHREADS
+#define        net_slave_type  pthread_t *
+#ifndef        MAX_SLAVES
+#define        MAX_SLAVES      SOMAXCONN
+#endif /* MAX_SLAVES */
+#else  /* USE_PTHREADS */
+#define        net_slave_type  pid_t
+#ifndef        MAX_SLAVES
+#define        MAX_SLAVES      SOMAXCONN
+#endif /* MAX_SLAVES */
+#endif /* USE_PTHREADS */
+#define        NET_SLAVE_FULL_SLEEP    15      /* seconds */
+
+/*
+ * Slave information storage.
+ */
+typedef struct _net_slave_info {
+    int                                sl_inuse;
+    net_slave_type             sl_id;
+    krb5_context               sl_context;
+    int                                sl_socket;
+    struct sockaddr_in         sl_local_addr;  /* local address */
+    struct sockaddr_in         sl_remote_addr; /* remote address */
+} net_slave_info;
+
+/*
+ * Error messages.
+ */
+static const char *net_waiterr_msg = "child wait failed - cannot reap children";
+static const char *net_def_realm_fmt = "%s: cannot get default realm (%s).\n";
+static const char *net_no_mem_fmt = "%s: cannot get memory.\n";
+static const char *net_parse_srv_fmt = "%s: cannot parse server name %s (%s).\n";
+static const char *net_no_hostname_fmt = "%s: cannot get our host name (%s).\n";
+static const char *net_no_hostent_fmt = "%s: cannot get our host entry (%s).\n";
+static const char *net_no_servent_fmt = "%s: cannot get service entry for %s (%s).\n";
+static const char *net_sockerr_fmt = "%s: cannot open network socket (%s).\n";
+static const char *net_binderr_fmt = "%s: cannot bind to network address (%s).\n";
+
+static const char *net_select_fmt = "select failed";
+static const char *net_cl_disp_fmt = "client dispatch failed";
+static const char *net_not_ready_fmt = "select error - no socket to read";
+static const char *net_dispatch_msg = "network dispatch";
+
+static int net_debug_level = 0;
+
+static char            *net_service_name = (char *) NULL;
+static int             net_service_princ_init = 0;
+static krb5_principal  net_service_principal = (krb5_principal) NULL;
+static int             net_server_addr_init = 0;
+static struct sockaddr_in      net_server_addr;
+static int             net_listen_socket = -1;
+static int             net_slaves_active = 0;
+static int             net_max_slaves = 0;
+static net_slave_info  *net_slave_table = (net_slave_info *) NULL;
+
+static jmp_buf shutdown_jmp;
+
+extern char *programname;
+\f
+/*
+ * net_find_free_entry()       - Find a free entry in the slave table.
+ */
+static net_slave_info *
+net_find_free_entry()
+{
+    int i, found;
+
+    /* Find a table entry */
+    while (1) {
+       found = 0;
+       for (i=0; i<net_max_slaves; i++) {
+           if (!net_slave_table[i].sl_inuse) {
+               net_slave_table[i].sl_inuse = 1;
+               found = 1;
+               break;
+           }
+       }
+       if (found)
+           break;
+       sleep(NET_SLAVE_FULL_SLEEP);
+    }
+    return(&net_slave_table[i]);
+}
+\f
+/*
+ * net_find_slave()    - Find a slave entry by its identity.
+ */
+static net_slave_info *
+net_find_slave(id)
+    net_slave_type     id;
+{
+    int i, found;
+
+    for (i=0; i<net_max_slaves; i++) {
+       if (net_slave_table[i].sl_inuse &&
+           (net_slave_table[i].sl_id == id)) {
+           found = 1;
+           break;
+       }
+    }
+    if (found)
+       return(&net_slave_table[i]);
+    else
+       return((net_slave_info *) NULL);
+}
+
+/*
+ * net_free_slave_entry()      - Mark an entry as free.
+ */
+static void
+net_free_slave_entry(entp)
+    net_slave_info *entp;
+{
+    entp->sl_inuse = 0;
+}
+\f
+/*
+ * net_shutdown()      - Destroy all slaves on signal reception
+ */
+static krb5_sigtype
+net_shutdown()
+{
+    int i;
+
+    /* Loop through all slaves */
+    for (i=0; i<net_max_slaves; i++) {
+       if (net_slave_table[i].sl_inuse){
+#ifdef DEBUG
+           /* If not us (see net_dispatch_client) */
+           if (net_slave_table[i].sl_id != (net_slave_type) getpid()) {
+#endif /* DEBUG */
+#if    USE_PTHREADS
+               pthread_cancel(*net_slave_table[i].sl_id);
+#else  /* USE_PTHREADS */
+               kill(net_slave_table[i].sl_id, SIGKILL);
+#endif /* USE_PTHREADS */
+#ifdef DEBUG
+           }
+#endif /* DEBUG */
+       }
+    }
+    sleep(5);          /* to allow children to die and be reaped */
+    longjmp(shutdown_jmp, 1);
+    /* NOTREACHED */
+}
+\f
+#if    !USE_PTHREADS
+/*
+ * net_reaper()        - Child process termination handler.
+ */
+static krb5_sigtype
+net_reaper()
+{
+#ifdef WAIT_USES_INT
+    int                child_exit;
+#else  /* WAIT_USES_INT */
+    union wait         child_exit;
+#endif /* WAIT_USES_INT */
+    pid_t              deadmeat;
+    net_slave_info     *slent;
+
+    /* Reap everybody we can */
+    while (
+          (
+#ifdef HAVE_WAITPID
+           deadmeat = waitpid((pid_t) -1, &child_exit, WNOHANG)
+#else  /* HAVE_WAITPID */
+           deadmeat = wait3(&child_exit, WNOHANG, (struct rusage *) NULL)
+#endif /* HAVE_WAITPID */
+           ) > 0) {
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| process %d finished with %d\n", deadmeat, child_exit));
+       slent = net_find_slave(deadmeat);
+       if (slent) {
+           net_free_slave_entry(slent);
+       }
+       else {
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| cannot find slave entry for %d\n", deadmeat));
+       }
+    }
+    if ((deadmeat == -1) && (errno != ECHILD))
+       com_err(programname, errno, net_waiterr_msg);
+}
+#endif /* USE_PTHREADS */
+\f
+#if    USE_PTHREADS
+/*
+ * net_slave_proto()   - pthread main routine.
+ */
+static krb5_error_code
+net_slave_proto(stent)
+    net_slave_info     *stent;
+{
+    krb5_error_code    kret;
+
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("* net_slave_proto()\n"));
+    DPRINT(DEBUG_SPROC, net_debug_level,
+          ("| thread %d starting\n", stent->sl_id));
+    kret = proto_serv(stent->sl_context,
+                     (krb5_int32) stent->sl_id,
+                     stent->sl_socket,
+                     &stent->sl_local_addr,
+                     &stent->sl_remote_addr);
+    DPRINT(DEBUG_SPROC, net_debug_level,
+          ("| thread %d finished with %d\n", stent->sl_id, kret));
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("* net_slave_proto() = %d\n", kret));
+    net_free_slave_entry(stent);
+    return(kret);
+}
+#endif /* USE_PTHREADS */
+\f
+/*
+ * net_dispatch_client()       - Handle client dispatch.
+ */
+static krb5_error_code
+net_dispatch_client(kcontext, listen_sock, conn_sock, client_addr)
+    krb5_context       kcontext;
+    int                        listen_sock;
+    int                        conn_sock;
+    struct sockaddr_in *client_addr;
+{
+    krb5_error_code    kret;
+    net_slave_info     *slent;
+
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("* net_dispatch_client(listen=%d)\n", listen_sock));
+    kret = 0;
+
+    /* Find a free entry */
+    slent = net_find_free_entry();
+
+    /* Initialize the slave entry */
+    slent->sl_context = kcontext;
+    slent->sl_socket = conn_sock;
+    memcpy((char *) &slent->sl_remote_addr,
+          (char *) client_addr,
+          sizeof(struct sockaddr_in));
+    memcpy((char *) &slent->sl_local_addr,
+          (char *) &net_server_addr,
+          sizeof(struct sockaddr_in));
+
+#ifdef DEBUG
+    if (net_debug_level < DEBUG_REQUESTS) {
+#endif /* DEBUG */
+       /* Do a real slave creation */
+#if    USE_PTHREADS
+       if (!slent->sl_id)
+           slent->sl_id = (pthread_t *) malloc(sizeof(pthread_t));
+       if (slent->sl_id == (pthread_t *) NULL) {
+           kret = ENOMEM;
+           goto done;
+       }
+       if (kret = pthread_create(slent->sl_id,
+                                 pthread_attr_default,
+                                 (pthread_startroutine_t) net_slave_proto,
+                                 (pthread_addr_t) slent)) {
+           kret = errno;
+           goto done;
+       }
+       if (pthread_detach(slent->sl_id)) {
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| (%d) child thread %d detach failed (%d)\n",
+                   getpid(), slent->sl_id, errno));
+       }
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| (%d) created child thread %d\n",
+               getpid(), slent->sl_id));
+#else  /* USE_PTHREADS */
+       slent->sl_id = fork();
+       if (slent->sl_id < 0) {
+           kret = errno;
+           slent->sl_inuse = 0;
+           goto done;
+       }
+
+       if (slent->sl_id > 0) {
+           /* parent */
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| (%d) created child process %d\n",
+                   getpid(), slent->sl_id));
+           kret = 0;
+           goto done;
+       }
+       else {
+           /* child */
+           signal(SIGINT, SIG_IGN);    /* Ignore SIGINT */
+           signal(SIGTERM, SIG_IGN);   /* Ignore SIGTERM */
+           signal(SIGHUP, SIG_IGN);    /* Ignore SIGHUP */
+           signal(SIGQUIT, SIG_IGN);   /* Ignore SIGQUIT */
+           signal(SIGPIPE, SIG_IGN);   /* Ignore SIGPIPE */
+           signal(SIGCHLD, SIG_DFL);   /* restore SIGCHLD handling */
+           close(listen_sock);
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| process %d starting\n", getpid()));
+           kret = proto_serv(slent->sl_context,
+                             (krb5_int32) slent->sl_id,
+                             slent->sl_socket,
+                             &slent->sl_local_addr,
+                             &slent->sl_remote_addr);
+           DPRINT(DEBUG_SPROC, net_debug_level,
+                  ("| process %d exiting with %d\n", getpid(), kret));
+           exit(kret);
+       }
+#endif /* USE_PTHREADS */
+#ifdef DEBUG
+    }
+    else {
+       net_slave_info *sl1;
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| (%d) not doing child creation\n", getpid()));
+       slent->sl_id = (net_slave_type) getpid();
+       kret = proto_serv(slent->sl_context,
+                         (krb5_int32) slent->sl_id,
+                         slent->sl_socket,
+                         &slent->sl_local_addr,
+                         &slent->sl_remote_addr);
+       sl1 = net_find_slave(slent->sl_id);
+       if (!sl1)
+           net_free_slave_entry(sl1);
+       DPRINT(DEBUG_SPROC, net_debug_level,
+              ("| (%d) returned with %d\n", getpid(), kret));
+    }
+#endif /* DEBUG */
+ done:
+    DPRINT(DEBUG_CALLS, net_debug_level,
+          ("X net_dispatch_client() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * net_init()  - Initialize network context.
+ */
+krb5_error_code
+net_init(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    krb5_error_code    kret;
+    char               *local_realm;
+    char               our_host_name[MAXHOSTNAMELEN];
+    struct hostent     *our_hostent;
+    struct servent     *our_servent;
+    int                        bind_tries;
+    int                        bind_sleep;
+
+    net_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, net_debug_level, ("* net_init()\n"));
+
+    /*
+     * Get our realm.
+     */
+    if (kret = krb5_get_default_realm(kcontext, &local_realm)) {
+       fprintf(stderr, net_def_realm_fmt, programname, error_message(kret));
+       goto done;
+    }
+
+    /* Allocate the slave table */
+    net_slave_table = (net_slave_info *) malloc(MAX_SLAVES *
+                                               sizeof(net_slave_info));
+    /* Make our service name */
+    net_service_name = (char *) malloc(strlen(local_realm) +
+                                      strlen(KRB5_ADM_SERVICE_NAME) + 2);
+    if ((net_service_name == (char *) NULL) ||
+       (net_slave_table == (net_slave_info *) NULL)) {
+       kret = ENOMEM;
+       krb5_xfree(local_realm);
+       fprintf(stderr, net_no_mem_fmt, programname);
+       goto done;
+    }
+    (void) sprintf(net_service_name, "%s%s%s",
+                  KRB5_ADM_SERVICE_NAME, "/", local_realm);
+    krb5_xfree(local_realm);
+    memset((char *) net_slave_table, 0, MAX_SLAVES * sizeof(net_slave_info));
+    net_max_slaves = MAX_SLAVES;
+    DPRINT(DEBUG_HOST, net_debug_level,
+          ("- name of service is %s\n", net_service_name));
+
+    /* Now formulate the principal name */
+    if (kret = krb5_parse_name(kcontext,
+                              net_service_name,
+                              &net_service_principal)) {
+       fprintf(stderr, net_parse_srv_fmt, programname, net_service_name,
+               error_message(kret));
+       goto done;
+    }
+    net_service_princ_init = 1;
+
+    /* Now get our host name/entry */
+    if (gethostname(our_host_name, sizeof(our_host_name))) {
+       kret = errno;
+       fprintf(stderr, net_no_hostname_fmt, programname, error_message(kret));
+       goto done;
+    }
+    if (!(our_hostent = gethostbyname(our_host_name))) {
+       kret = errno;
+       fprintf(stderr, net_no_hostent_fmt, programname, error_message(kret));
+       goto done;
+    }
+    DPRINT(DEBUG_HOST, net_debug_level,
+          ("- name of host is %s\n", our_hostent->h_name));
+
+    /* Now initialize our network address */
+    net_server_addr.sin_family = AF_INET;
+    memcpy((char *) &net_server_addr.sin_addr,
+          (char *) our_hostent->h_addr,
+          sizeof(net_server_addr.sin_addr));
+    DPRINT(DEBUG_HOST, net_debug_level,
+          ("- address of host is %x\n",
+           ntohl(net_server_addr.sin_addr.s_addr)));
+
+    /* Get the service entry */
+    if (!(our_servent = getservbyname(KRB5_ADM_SERVICE_NAME, "tcp"))) {
+       kret = errno;
+       fprintf(stderr, net_no_servent_fmt, programname,
+               KRB5_ADM_SERVICE_NAME, error_message(kret));
+       goto done;
+    }
+    net_server_addr.sin_port = our_servent->s_port;
+    net_server_addr_init = 1;
+    DPRINT(DEBUG_HOST, net_debug_level,
+          ("- service name (%s) is on port %d\n", our_servent->s_name,
+           our_servent->s_port));
+
+    /* Now open the listen socket */
+    net_listen_socket = socket(AF_INET, SOCK_STREAM, 0);
+    if (net_listen_socket < 0) {
+       kret = errno;
+       fprintf(stderr, net_sockerr_fmt, programname, error_message(kret));
+       goto done;
+    }
+
+    /* Bind socket */
+    bind_sleep = 1;
+    bind_tries = 0;
+    do {
+       if (bind(net_listen_socket,
+                &net_server_addr,
+                sizeof(net_server_addr)) < 0) {
+           kret = errno;
+           if (errno != EADDRINUSE) {
+               fprintf(stderr, net_binderr_fmt, programname,
+                       error_message(kret));
+               goto done;
+           }
+           else {
+               bind_tries++;
+               DPRINT(DEBUG_HOST, net_debug_level,
+                      ("- bind failed sleeping for %d seconds\n",
+                       bind_sleep));
+               sleep(bind_sleep);
+               bind_sleep = bind_sleep << 1;
+           }
+       }
+       else {
+           DPRINT(DEBUG_HOST, net_debug_level,
+                  ("- bound socket %d on port\n", net_listen_socket));
+           break;
+       }
+    } while (bind_tries < MAX_BIND_TRIES);
+
+ done:
+    DPRINT(DEBUG_CALLS, net_debug_level, ("X net_init() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * net_finish()        - Finish network context.
+ */
+void
+net_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, net_debug_level, ("* net_finish()\n"));
+    if (net_max_slaves)
+       free(net_slave_table);
+    if (net_listen_socket >= 0)
+       close(net_listen_socket);
+    if (net_service_princ_init)
+       krb5_free_principal(kcontext, net_service_principal);
+    if (net_service_name)
+       free(net_service_name);
+    DPRINT(DEBUG_CALLS, net_debug_level, ("X net_finish()\n"));
+}
+\f
+/*
+ * net_dispatch()      - Listen and dispatch request.
+ *
+ * Loop forever selecting on the listen socket.  When an incoming connection
+ * comes in, dispatch to net_client_connect().
+ */
+krb5_error_code
+net_dispatch(kcontext)
+    krb5_context       kcontext;
+{
+    krb5_error_code    kret;
+    fd_set             mask, readfds;
+    int                        nready;
+
+    DPRINT(DEBUG_CALLS, net_debug_level, ("* net_dispatch()\n"));
+
+    kret = 0;
+
+    /* Set up the fdset mask */
+    FD_ZERO(&mask);
+    FD_SET(net_listen_socket, &mask);
+
+    /*
+     * SIGTERM (or SIGINT, if debug) shuts us down.
+     */
+    signal(SIGTERM, net_shutdown);
+#ifdef DEBUG
+    signal(SIGINT, net_shutdown);
+#endif /* DEBUG */
+
+#if    !USE_PTHREADS
+    /*
+     * SIGCHILD indicates end of child process life.
+     */
+    signal(SIGCHLD, net_reaper);
+#endif /* !USE_PTHREADS */
+
+    /* Receive connections on the socket */
+    DLOG(DEBUG_OPERATION, net_debug_level, "listening on socket");
+    if (setjmp(shutdown_jmp) == 0) {
+       if (listen(net_listen_socket, SOMAXCONN) < 0)
+           kret = errno;
+    }
+    else
+       kret = EINTR;
+    DLOG(DEBUG_OPERATION, net_debug_level, "listen done");
+
+    while (kret == 0) {
+       /*
+        * Prepare to catch signals.
+        */
+       if (setjmp(shutdown_jmp) == 0) {
+           readfds = mask;
+           DLOG(DEBUG_OPERATION, net_debug_level, "doing select");
+           if ((nready = select(net_listen_socket+1,
+                                &readfds,
+                                (fd_set *) NULL,
+                                (fd_set *) NULL,
+                                (struct timeval *) NULL)) == 0) {
+               DLOG(DEBUG_OPERATION, net_debug_level, "nobody ready");
+               continue;       /* Nobody ready */
+           }
+
+           if ((nready < 0) && (errno != EINTR)) {
+               com_err(net_dispatch_msg, errno, net_select_fmt);
+               continue;
+           }
+
+           if (FD_ISSET(net_listen_socket, &readfds)) {
+               struct sockaddr_in      client_addr;
+               int                     addrlen;
+               int                     conn_sock;
+
+               addrlen = sizeof(client_addr);
+               DLOG(DEBUG_OPERATION, net_debug_level,
+                    "accept connection");
+               while (((conn_sock = accept(net_listen_socket,
+                                           (struct sockaddr *) &client_addr,
+                                           &addrlen)) < 0) &&
+                      (errno == EINTR));
+
+               if (conn_sock < 0)  {
+                   kret = errno;
+                   break;
+               }
+               DLOG(DEBUG_OPERATION, net_debug_level,
+                    "accepted connection");
+               kret = net_dispatch_client(kcontext,
+                                          net_listen_socket,
+                                          conn_sock,
+                                          &client_addr);
+               if (kret) {
+                   com_err(net_dispatch_msg, kret, net_cl_disp_fmt);
+                   continue;
+               }
+               DLOG(DEBUG_OPERATION, net_debug_level, "dispatch done");
+           }
+           else {
+               com_err(net_dispatch_msg, 0, net_not_ready_fmt);
+               kret = EIO;
+           }
+       }
+       else {
+           DLOG(DEBUG_OPERATION, net_debug_level,
+                "dispatch interrupted by SIGTERM");
+           kret = 0;
+           break;
+       }
+    }
+
+    DPRINT(DEBUG_CALLS, net_debug_level, ("X net_dispatch() = %d\n", kret));
+    return(kret);
+}
+\f
+/*
+ * Return our service principal.
+ */
+krb5_principal
+net_server_princ()
+{
+    if (net_service_princ_init)
+       return(net_service_principal);
+    else
+       return((krb5_principal) NULL);
+}
diff --git a/src/kadmin/v5server/srv_output.c b/src/kadmin/v5server/srv_output.c
new file mode 100644 (file)
index 0000000..b1b72a3
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * kadmin/v5server/srv_output.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.
+ *
+ */
+
+/*
+ * srv_output.c - Handle Kerberos output related functions.
+ */
+#include "k5-int.h"
+#include "com_err.h"
+#include "kadm5_defs.h"
+#include "adm.h"
+
+static const char *out_adm_success = "Operation successful.";
+static const char *out_adm_cmd_unknown = "Command %s unknown.";
+static const char *out_adm_pw_unaccept = "Password unacceptable.";
+static const char *out_adm_bad_princ = "Principal unknown.";
+static const char *out_adm_pwd_too_short = "Password is too short.";
+static const char *out_adm_pwd_weak = "Password generates weak key.";
+static const char *out_adm_not_allowed = "You are not allowed to change your password.";
+static const char *out_adm_bad_pw = "Password incorrect.";
+static const char *out_adm_not_in_tkt = "Not an initial ticket.";
+static const char *out_adm_cant_change = "Cannot change password.";
+static const char *out_adm_lang_unsupp = "Language %s unsupported.";
+static const char *out_adm_bad_args = "Bad argument list format for %s command.";
+static const char *out_adm_bad_cmd = "Command %s not supported.";
+static const char *out_adm_no_cmd = "No command in message.";
+static const char *out_adm_no_err = "Unknown error.";
+static int output_debug_level = 0;
+static int output_mime_enabled = 0;
+\f
+static char *
+lang_error_message(lang, kval)
+    char               *lang;
+    krb5_error_code    kval;
+{
+    char       *ret;
+    char       *ermsg;
+
+    ermsg = error_message(kval);
+    if (lang && output_lang_supported(lang)) {
+       /*
+        * Just for demonstration purposes.
+        */
+       ret = (char *) malloc(strlen(ermsg)+strlen(lang)+3+1);
+       if (ret)
+           sprintf(ret, "%s - %s", lang, ermsg);
+    }
+    else {
+       ret = (char *) malloc(strlen(ermsg)+1);
+       if (ret)
+           strcpy(ret, ermsg);
+    }
+    return(ret);
+}
+
+static char *
+lang_adm_message(lang, ecode, aux, nargs, alist)
+    char               *lang;
+    krb5_int32         ecode;
+    krb5_int32         aux;
+    krb5_int32         nargs;
+    krb5_data          *alist;
+{
+    char       *ret;
+    const char *ermsg;
+    char       *erarg;
+    int                alen;
+
+    erarg = (char *) NULL;
+    switch (ecode) {
+    case KRB5_ADM_SUCCESS:
+       ermsg = out_adm_success; break;
+    case KRB5_ADM_CMD_UNKNOWN:
+       switch (aux) {
+       case KRB5_ADM_BAD_ARGS:
+           ermsg = out_adm_bad_args;
+           erarg = ((nargs >= 1) ? alist[0].data : (char *) NULL);
+           break;
+       case KRB5_ADM_BAD_CMD:
+           ermsg = out_adm_bad_cmd;
+           erarg = ((nargs >= 1) ? alist[0].data : (char *) NULL);
+           break;
+       case KRB5_ADM_NO_CMD:
+           ermsg = out_adm_no_cmd;
+           break;
+       default:
+           ermsg = out_adm_bad_args;
+           erarg = ((nargs >= 1) ? alist[0].data : (char *) NULL);
+           break;
+       }
+       break;
+    case KRB5_ADM_PW_UNACCEPT:
+       switch (aux) {
+       case KRB5_ADM_BAD_PRINC:
+           ermsg = out_adm_bad_princ;
+           break;
+       case KRB5_ADM_PWD_TOO_SHORT:
+           ermsg = out_adm_pwd_too_short;
+           break;
+       case KRB5_ADM_PWD_WEAK:
+           ermsg = out_adm_pwd_weak;
+           break;
+       default:
+           ermsg = out_adm_pw_unaccept;
+           break;
+       }
+       break;
+    case KRB5_ADM_BAD_PW:
+       ermsg = out_adm_bad_pw; break;
+    case KRB5_ADM_NOT_IN_TKT:
+       ermsg = out_adm_not_in_tkt; break;
+    case KRB5_ADM_CANT_CHANGE:
+       switch (aux) {
+       case KRB5_ADM_BAD_PRINC:
+           ermsg = out_adm_bad_princ;
+           break;
+       case KRB5_ADM_PWD_TOO_SHORT:
+           ermsg = out_adm_pwd_too_short;
+           break;
+       case KRB5_ADM_PWD_WEAK:
+           ermsg = out_adm_pwd_weak;
+           break;
+       case KRB5_ADM_NOT_ALLOWED:
+           ermsg = out_adm_not_allowed;
+           break;
+       default:
+           ermsg = out_adm_cant_change;
+           break;
+       }
+       break;
+    case KRB5_ADM_LANG_NOT_SUPPORTED:
+       ermsg = out_adm_lang_unsupp;
+       erarg = ((nargs >= 2) ? alist[1].data : (char *) NULL);
+       break;
+    default:
+       ermsg = out_adm_no_err; break;
+    }
+
+    alen = strlen(ermsg)+1;
+    if (erarg)
+       alen += strlen(erarg);
+    if (lang && output_lang_supported(lang)) {
+       alen += strlen(lang)+3;
+    }
+    ret = (char *) malloc(alen);
+    if (lang && output_lang_supported(lang)) {
+       char *xxx;
+
+       /*
+        * Just for demonstration purposes.
+        */
+       if (ret) {
+           sprintf(ret, "%s - ", lang);
+           xxx = &ret[strlen(ret)];
+           sprintf(xxx, ermsg, erarg);
+       }
+    }
+    else {
+       if (ret)
+           sprintf(ret, ermsg, erarg);
+    }
+    return(ret);
+}
+
+static char *
+mimeify_text(msg)
+    char *msg;
+{
+    char *ret;
+    /*
+     * Just for demonstration purposes.
+     */
+
+    if (output_mime_enabled) {
+       ret = (char *) malloc(strlen(msg)+6+3);
+       if (ret)
+           sprintf(ret, "MIME: %s\r\n", msg);
+       if (!ret)
+           ret = msg;
+    }
+    else
+       ret = msg;
+    return(ret);
+}
+
+/*
+ * output_init()       - Initialize output context.
+ */
+krb5_error_code
+output_init(kcontext, debug_level, language_list, mime_enabled)
+    krb5_context       kcontext;
+    int                        debug_level;
+    char               *language_list;
+    krb5_boolean       mime_enabled;
+{
+    krb5_error_code    kret;
+
+    kret = 0;
+    output_debug_level = debug_level;
+    DPRINT(DEBUG_CALLS, output_debug_level,
+          ("* output_init(llist=%s, mime=%d)\n",
+           ((language_list) ? language_list : "(null)"),
+           mime_enabled));
+    output_mime_enabled = mime_enabled;
+    DPRINT(DEBUG_CALLS, output_debug_level, ("X output_init() = %d\n", kret));
+    return(kret);
+}
+
+/*
+ * output_finish       - Terminate output context.
+ */
+void
+output_finish(kcontext, debug_level)
+    krb5_context       kcontext;
+    int                        debug_level;
+{
+    DPRINT(DEBUG_CALLS, output_debug_level, ("* output_finish()\n"));
+    DPRINT(DEBUG_CALLS, output_debug_level, ("X output_finish()\n"));
+}
+
+/*
+ * output_lang_supported- Is a language supported?
+ */
+krb5_boolean
+output_lang_supported(lname)
+    char               *lname;
+{
+    krb5_boolean       ret;
+    DPRINT(DEBUG_CALLS, output_debug_level,
+          ("* output_lang_supported(lang=%s)\n",
+           ((lname) ? lname : "(default)")));
+    ret = 1;
+    DPRINT(DEBUG_CALLS, output_debug_level,
+          ("X output_lang_supported() = %d\n", ret));
+    return(ret);
+}
+
+/*
+ * output_errmsg       - Return an error message.
+ */
+char *
+output_krb5_errmsg(lang, mime, kval)
+    char *             lang;
+    krb5_boolean       mime;
+    krb5_error_code    kval;
+{
+    char *ret;
+    char *ermsg;
+    int alen;
+
+    DPRINT(DEBUG_CALLS, output_debug_level,
+          ("* output_krb5_errmsg(v=%d, lang=%s, mime=%d)\n",
+           kval, ((lang) ? lang : "(default)", mime)));
+    ermsg = lang_error_message(lang, kval);
+    if (mime) {
+       ret = mimeify_text(ermsg);
+       if (ret != ermsg)
+           free(ermsg);
+    }
+    else
+       ret = ermsg;
+    DPRINT(DEBUG_CALLS, output_debug_level, ("X output_krb5_errmsg()\n"));
+    return(ret);
+}
+
+/*
+ * output_adm_error    - Output an administrative error message string.
+ */
+char *
+output_adm_error(lang, mime, ecode, aux, nargs, alist)
+    char               *lang;
+    krb5_boolean       mime;
+    krb5_int32         ecode;
+    krb5_int32         aux;
+    krb5_int32         nargs;
+    krb5_data          *alist;
+{
+    char               *ermsg;
+    char               *ret;
+
+    DPRINT(DEBUG_CALLS, output_debug_level,
+          ("* output_adm_err(lang=%s, mime=%d, code=%d/%d)\n",
+           ((lang) ? lang : "(default)"), mime, ecode, aux));
+    ermsg = lang_adm_message(lang, ecode, aux, nargs, alist);
+    if (mime) {
+       ret = mimeify_text(ermsg);
+       if (ret != ermsg)
+           free(ermsg);
+    }
+    else
+       ret = ermsg;
+    DPRINT(DEBUG_CALLS, output_debug_level, ("X output_adm_err()\n"));
+    return(ret);
+}