aboutsummaryrefslogtreecommitdiff
path: root/kdc
diff options
context:
space:
mode:
Diffstat (limited to 'kdc')
-rw-r--r--kdc/524.c400
-rw-r--r--kdc/Makefile.am122
-rw-r--r--kdc/Makefile.in1151
-rw-r--r--kdc/config.c322
-rw-r--r--kdc/connect.c900
-rw-r--r--kdc/default_config.c285
-rw-r--r--kdc/digest.c1456
-rw-r--r--kdc/headers.h109
-rw-r--r--kdc/hprop.8190
-rw-r--r--kdc/hprop.c807
-rw-r--r--kdc/hprop.h75
-rw-r--r--kdc/hpropd.8107
-rw-r--r--kdc/hpropd.c271
-rw-r--r--kdc/kadb.h84
-rw-r--r--kdc/kaserver.c951
-rw-r--r--kdc/kdc-private.h286
-rw-r--r--kdc/kdc-protos.h92
-rw-r--r--kdc/kdc-replay.c197
-rw-r--r--kdc/kdc.8262
-rw-r--r--kdc/kdc.h96
-rw-r--r--kdc/kdc_locl.h72
-rw-r--r--kdc/kerberos4.c805
-rw-r--r--kdc/kerberos5.c1852
-rw-r--r--kdc/krb5tgs.c1914
-rw-r--r--kdc/kstash.8104
-rw-r--r--kdc/kstash.c165
-rw-r--r--kdc/kx509.c460
-rw-r--r--kdc/log.c93
-rw-r--r--kdc/main.c99
-rw-r--r--kdc/misc.c122
-rw-r--r--kdc/mit_dump.c373
-rw-r--r--kdc/pkinit.c1673
-rw-r--r--kdc/process.c219
-rw-r--r--kdc/rx.h79
-rw-r--r--kdc/set_dbinfo.c100
-rw-r--r--kdc/string2key.8110
-rw-r--r--kdc/string2key.c201
-rw-r--r--kdc/v4_dump.c143
-rw-r--r--kdc/version-script.map18
-rw-r--r--kdc/windc.c109
-rw-r--r--kdc/windc_plugin.h82
41 files changed, 16956 insertions, 0 deletions
diff --git a/kdc/524.c b/kdc/524.c
new file mode 100644
index 0000000000000..3e4ad292537b4
--- /dev/null
+++ b/kdc/524.c
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: 524.c 18270 2006-10-06 17:06:30Z lha $");
+
+#include <krb5-v4compat.h>
+
+/*
+ * fetch the server from `t', returning the name in malloced memory in
+ * `spn' and the entry itself in `server'
+ */
+
+static krb5_error_code
+fetch_server (krb5_context context,
+ krb5_kdc_configuration *config,
+ const Ticket *t,
+ char **spn,
+ hdb_entry_ex **server,
+ const char *from)
+{
+ krb5_error_code ret;
+ krb5_principal sprinc;
+
+ ret = _krb5_principalname2krb5_principal(context, &sprinc,
+ t->sname, t->realm);
+ if (ret) {
+ kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ ret = krb5_unparse_name(context, sprinc, spn);
+ if (ret) {
+ krb5_free_principal(context, sprinc);
+ kdc_log(context, config, 0, "krb5_unparse_name: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER,
+ NULL, server);
+ krb5_free_principal(context, sprinc);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Request to convert ticket from %s for unknown principal %s: %s",
+ from, *spn, krb5_get_err_text(context, ret));
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ return ret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+log_524 (krb5_context context,
+ krb5_kdc_configuration *config,
+ const EncTicketPart *et,
+ const char *from,
+ const char *spn)
+{
+ krb5_principal client;
+ char *cpn;
+ krb5_error_code ret;
+
+ ret = _krb5_principalname2krb5_principal(context, &client,
+ et->cname, et->crealm);
+ if (ret) {
+ kdc_log(context, config, 0, "_krb5_principalname2krb5_principal: %s",
+ krb5_get_err_text (context, ret));
+ return ret;
+ }
+ ret = krb5_unparse_name(context, client, &cpn);
+ if (ret) {
+ krb5_free_principal(context, client);
+ kdc_log(context, config, 0, "krb5_unparse_name: %s",
+ krb5_get_err_text (context, ret));
+ return ret;
+ }
+ kdc_log(context, config, 1, "524-REQ %s from %s for %s", cpn, from, spn);
+ free(cpn);
+ krb5_free_principal(context, client);
+ return 0;
+}
+
+static krb5_error_code
+verify_flags (krb5_context context,
+ krb5_kdc_configuration *config,
+ const EncTicketPart *et,
+ const char *spn)
+{
+ if(et->endtime < kdc_time){
+ kdc_log(context, config, 0, "Ticket expired (%s)", spn);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+ if(et->flags.invalid){
+ kdc_log(context, config, 0, "Ticket not valid (%s)", spn);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ return 0;
+}
+
+/*
+ * set the `et->caddr' to the most appropriate address to use, where
+ * `addr' is the address the request was received from.
+ */
+
+static krb5_error_code
+set_address (krb5_context context,
+ krb5_kdc_configuration *config,
+ EncTicketPart *et,
+ struct sockaddr *addr,
+ const char *from)
+{
+ krb5_error_code ret;
+ krb5_address *v4_addr;
+
+ v4_addr = malloc (sizeof(*v4_addr));
+ if (v4_addr == NULL)
+ return ENOMEM;
+
+ ret = krb5_sockaddr2address(context, addr, v4_addr);
+ if(ret) {
+ free (v4_addr);
+ kdc_log(context, config, 0, "Failed to convert address (%s)", from);
+ return ret;
+ }
+
+ if (et->caddr && !krb5_address_search (context, v4_addr, et->caddr)) {
+ kdc_log(context, config, 0, "Incorrect network address (%s)", from);
+ krb5_free_address(context, v4_addr);
+ free (v4_addr);
+ return KRB5KRB_AP_ERR_BADADDR;
+ }
+ if(v4_addr->addr_type == KRB5_ADDRESS_INET) {
+ /* we need to collapse the addresses in the ticket to a
+ single address; best guess is to use the address the
+ connection came from */
+
+ if (et->caddr != NULL) {
+ free_HostAddresses(et->caddr);
+ } else {
+ et->caddr = malloc (sizeof (*et->caddr));
+ if (et->caddr == NULL) {
+ krb5_free_address(context, v4_addr);
+ free(v4_addr);
+ return ENOMEM;
+ }
+ }
+ et->caddr->val = v4_addr;
+ et->caddr->len = 1;
+ } else {
+ krb5_free_address(context, v4_addr);
+ free(v4_addr);
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+encrypt_v4_ticket(krb5_context context,
+ krb5_kdc_configuration *config,
+ void *buf,
+ size_t len,
+ krb5_keyblock *skey,
+ EncryptedData *reply)
+{
+ krb5_crypto crypto;
+ krb5_error_code ret;
+ ret = krb5_crypto_init(context, skey, ETYPE_DES_PCBC_NONE, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TICKET,
+ buf,
+ len,
+ 0,
+ reply);
+ krb5_crypto_destroy(context, crypto);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encrypt data: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+encode_524_response(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *spn, const EncTicketPart et,
+ const Ticket *t, hdb_entry_ex *server,
+ EncryptedData *ticket, int *kvno)
+{
+ krb5_error_code ret;
+ int use_2b;
+ size_t len;
+
+ use_2b = krb5_config_get_bool(context, NULL, "kdc", "use_2b", spn, NULL);
+ if(use_2b) {
+ ASN1_MALLOC_ENCODE(EncryptedData,
+ ticket->cipher.data, ticket->cipher.length,
+ &t->enc_part, &len, ret);
+
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed to encode v4 (2b) ticket (%s)", spn);
+ return ret;
+ }
+
+ ticket->etype = 0;
+ ticket->kvno = NULL;
+ *kvno = 213; /* 2b's use this magic kvno */
+ } else {
+ unsigned char buf[MAX_KTXT_LEN + 4 * 4];
+ Key *skey;
+
+ if (!config->enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) {
+ kdc_log(context, config, 0, "524 cross-realm %s -> %s disabled", et.crealm,
+ t->realm);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ ret = _kdc_encode_v4_ticket(context, config,
+ buf + sizeof(buf) - 1, sizeof(buf),
+ &et, &t->sname, &len);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to encode v4 ticket (%s)", spn);
+ return ret;
+ }
+ ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "no suitable DES key for server (%s)", spn);
+ return ret;
+ }
+ ret = encrypt_v4_ticket(context, config, buf + sizeof(buf) - len, len,
+ &skey->key, ticket);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to encrypt v4 ticket (%s)", spn);
+ return ret;
+ }
+ *kvno = server->entry.kvno;
+ }
+
+ return 0;
+}
+
+/*
+ * process a 5->4 request, based on `t', and received `from, addr',
+ * returning the reply in `reply'
+ */
+
+krb5_error_code
+_kdc_do_524(krb5_context context,
+ krb5_kdc_configuration *config,
+ const Ticket *t, krb5_data *reply,
+ const char *from, struct sockaddr *addr)
+{
+ krb5_error_code ret = 0;
+ krb5_crypto crypto;
+ hdb_entry_ex *server = NULL;
+ Key *skey;
+ krb5_data et_data;
+ EncTicketPart et;
+ EncryptedData ticket;
+ krb5_storage *sp;
+ char *spn = NULL;
+ unsigned char buf[MAX_KTXT_LEN + 4 * 4];
+ size_t len;
+ int kvno = 0;
+
+ if(!config->enable_524) {
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Rejected ticket conversion request from %s", from);
+ goto out;
+ }
+
+ ret = fetch_server (context, config, t, &spn, &server, from);
+ if (ret) {
+ goto out;
+ }
+
+ ret = hdb_enctype2key(context, &server->entry, t->enc_part.etype, &skey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "No suitable key found for server (%s) from %s", spn, from);
+ goto out;
+ }
+ ret = krb5_crypto_init(context, &skey->key, 0, &crypto);
+ if (ret) {
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TICKET,
+ &t->enc_part,
+ &et_data);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to decrypt ticket from %s for %s", from, spn);
+ goto out;
+ }
+ ret = krb5_decode_EncTicketPart(context, et_data.data, et_data.length,
+ &et, &len);
+ krb5_data_free(&et_data);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to decode ticket from %s for %s", from, spn);
+ goto out;
+ }
+
+ ret = log_524 (context, config, &et, from, spn);
+ if (ret) {
+ free_EncTicketPart(&et);
+ goto out;
+ }
+
+ ret = verify_flags (context, config, &et, spn);
+ if (ret) {
+ free_EncTicketPart(&et);
+ goto out;
+ }
+
+ ret = set_address (context, config, &et, addr, from);
+ if (ret) {
+ free_EncTicketPart(&et);
+ goto out;
+ }
+
+ ret = encode_524_response(context, config, spn, et, t,
+ server, &ticket, &kvno);
+ free_EncTicketPart(&et);
+
+ out:
+ /* make reply */
+ memset(buf, 0, sizeof(buf));
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp) {
+ krb5_store_int32(sp, ret);
+ if(ret == 0){
+ krb5_store_int32(sp, kvno);
+ krb5_store_data(sp, ticket.cipher);
+ /* Aargh! This is coded as a KTEXT_ST. */
+ krb5_storage_seek(sp, MAX_KTXT_LEN - ticket.cipher.length, SEEK_CUR);
+ krb5_store_int32(sp, 0); /* mbz */
+ free_EncryptedData(&ticket);
+ }
+ ret = krb5_storage_to_data(sp, reply);
+ reply->length = krb5_storage_seek(sp, 0, SEEK_CUR);
+ krb5_storage_free(sp);
+ } else
+ krb5_data_zero(reply);
+ if(spn)
+ free(spn);
+ if(server)
+ _kdc_free_ent (context, server);
+ return ret;
+}
diff --git a/kdc/Makefile.am b/kdc/Makefile.am
new file mode 100644
index 0000000000000..ff20bde9c5726
--- /dev/null
+++ b/kdc/Makefile.am
@@ -0,0 +1,122 @@
+# $Id: Makefile.am 22489 2008-01-21 11:49:06Z lha $
+
+include $(top_srcdir)/Makefile.am.common
+
+AM_CPPFLAGS += $(INCLUDE_krb4) $(INCLUDE_hcrypto) -I$(srcdir)/../lib/krb5
+
+lib_LTLIBRARIES = libkdc.la
+
+bin_PROGRAMS = string2key
+
+sbin_PROGRAMS = kstash
+
+libexec_PROGRAMS = hprop hpropd kdc
+
+noinst_PROGRAMS = kdc-replay
+
+man_MANS = kdc.8 kstash.8 hprop.8 hpropd.8 string2key.8
+
+hprop_SOURCES = hprop.c mit_dump.c v4_dump.c hprop.h kadb.h
+hpropd_SOURCES = hpropd.c hprop.h
+
+kstash_SOURCES = kstash.c headers.h
+
+string2key_SOURCES = string2key.c headers.h
+
+kdc_SOURCES = connect.c \
+ config.c \
+ main.c
+
+libkdc_la_SOURCES = \
+ kdc-private.h \
+ kdc-protos.h \
+ default_config.c \
+ set_dbinfo.c \
+ digest.c \
+ kdc_locl.h \
+ kerberos5.c \
+ krb5tgs.c \
+ pkinit.c \
+ log.c \
+ misc.c \
+ 524.c \
+ kerberos4.c \
+ kaserver.c \
+ kx509.c \
+ process.c \
+ windc.c \
+ rx.h
+
+
+$(libkdc_la_OBJECTS): $(srcdir)/kdc-protos.h $(srcdir)/kdc-private.h
+
+libkdc_la_LDFLAGS = -version-info 2:0:0
+
+if versionscript
+libkdc_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+endif
+$(libkdc_la_OBJECTS): $(srcdir)/version-script.map
+
+$(srcdir)/kdc-protos.h:
+ cd $(srcdir) && perl ../cf/make-proto.pl -q -P comment -o kdc-protos.h $(libkdc_la_SOURCES) || rm -f kdc-protos.h
+
+$(srcdir)/kdc-private.h:
+ cd $(srcdir) && perl ../cf/make-proto.pl -q -P comment -p kdc-private.h $(libkdc_la_SOURCES) || rm -f kdc-private.h
+
+
+hprop_LDADD = \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_kdb) $(LIB_krb4) \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+hpropd_LDADD = \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_kdb) $(LIB_krb4) \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+if PKINIT
+LIB_pkinit = $(top_builddir)/lib/hx509/libhx509.la
+endif
+
+libkdc_la_LIBADD = \
+ $(LIB_pkinit) \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_kdb) $(LIB_krb4) \
+ $(top_builddir)/lib/ntlm/libheimntlm.la \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+LDADD = $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_krb4) \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+kdc_LDADD = libkdc.la $(LDADD) $(LIB_pidfile)
+kdc_replay_LDADD = $(kdc_LDADD)
+
+include_HEADERS = kdc.h kdc-protos.h
+
+krb5dir = $(includedir)/krb5
+krb5_HEADERS = windc_plugin.h
+
+build_HEADERZ = $(krb5_HEADERS) # XXX
+
+EXTRA_DIST = $(man_MANS) version-script.map
diff --git a/kdc/Makefile.in b/kdc/Makefile.in
new file mode 100644
index 0000000000000..d7e623afe7e38
--- /dev/null
+++ b/kdc/Makefile.in
@@ -0,0 +1,1151 @@
+# Makefile.in generated by automake 1.10 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $Id: Makefile.am 22489 2008-01-21 11:49:06Z lha $
+
+# $Id: Makefile.am.common 10998 2002-05-19 18:35:37Z joda $
+
+# $Id: Makefile.am.common 22488 2008-01-21 11:47:22Z lha $
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+DIST_COMMON = $(include_HEADERS) $(krb5_HEADERS) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common
+bin_PROGRAMS = string2key$(EXEEXT)
+sbin_PROGRAMS = kstash$(EXEEXT)
+libexec_PROGRAMS = hprop$(EXEEXT) hpropd$(EXEEXT) kdc$(EXEEXT)
+noinst_PROGRAMS = kdc-replay$(EXEEXT)
+@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+subdir = kdc
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
+ $(top_srcdir)/cf/auth-modules.m4 $(top_srcdir)/cf/autobuild.m4 \
+ $(top_srcdir)/cf/broken-getaddrinfo.m4 \
+ $(top_srcdir)/cf/broken-glob.m4 \
+ $(top_srcdir)/cf/broken-realloc.m4 \
+ $(top_srcdir)/cf/broken-snprintf.m4 $(top_srcdir)/cf/broken.m4 \
+ $(top_srcdir)/cf/broken2.m4 $(top_srcdir)/cf/c-attribute.m4 \
+ $(top_srcdir)/cf/capabilities.m4 \
+ $(top_srcdir)/cf/check-compile-et.m4 \
+ $(top_srcdir)/cf/check-getpwnam_r-posix.m4 \
+ $(top_srcdir)/cf/check-man.m4 \
+ $(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
+ $(top_srcdir)/cf/check-type-extra.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
+ $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
+ $(top_srcdir)/cf/dlopen.m4 \
+ $(top_srcdir)/cf/find-func-no-libs.m4 \
+ $(top_srcdir)/cf/find-func-no-libs2.m4 \
+ $(top_srcdir)/cf/find-func.m4 \
+ $(top_srcdir)/cf/find-if-not-broken.m4 \
+ $(top_srcdir)/cf/framework-security.m4 \
+ $(top_srcdir)/cf/have-struct-field.m4 \
+ $(top_srcdir)/cf/have-type.m4 $(top_srcdir)/cf/irix.m4 \
+ $(top_srcdir)/cf/krb-bigendian.m4 \
+ $(top_srcdir)/cf/krb-func-getlogin.m4 \
+ $(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-readline.m4 \
+ $(top_srcdir)/cf/krb-struct-spwd.m4 \
+ $(top_srcdir)/cf/krb-struct-winsize.m4 \
+ $(top_srcdir)/cf/largefile.m4 $(top_srcdir)/cf/mips-abi.m4 \
+ $(top_srcdir)/cf/misc.m4 $(top_srcdir)/cf/need-proto.m4 \
+ $(top_srcdir)/cf/osfc2.m4 $(top_srcdir)/cf/otp.m4 \
+ $(top_srcdir)/cf/proto-compat.m4 $(top_srcdir)/cf/pthreads.m4 \
+ $(top_srcdir)/cf/resolv.m4 $(top_srcdir)/cf/retsigtype.m4 \
+ $(top_srcdir)/cf/roken-frag.m4 \
+ $(top_srcdir)/cf/socket-wrapper.m4 $(top_srcdir)/cf/sunos.m4 \
+ $(top_srcdir)/cf/telnet.m4 $(top_srcdir)/cf/test-package.m4 \
+ $(top_srcdir)/cf/version-script.m4 $(top_srcdir)/cf/wflags.m4 \
+ $(top_srcdir)/cf/win32.m4 $(top_srcdir)/cf/with-all.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.in
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
+ "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \
+ "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(includedir)" \
+ "$(DESTDIR)$(krb5dir)"
+libLTLIBRARIES_INSTALL = $(INSTALL)
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+libkdc_la_DEPENDENCIES = $(LIB_pkinit) \
+ $(top_builddir)/lib/hdb/libhdb.la $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/krb5/libkrb5.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/ntlm/libheimntlm.la \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/asn1/libasn1.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libkdc_la_OBJECTS = default_config.lo set_dbinfo.lo digest.lo \
+ kerberos5.lo krb5tgs.lo pkinit.lo log.lo misc.lo 524.lo \
+ kerberos4.lo kaserver.lo kx509.lo process.lo windc.lo
+libkdc_la_OBJECTS = $(am_libkdc_la_OBJECTS)
+libkdc_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libkdc_la_LDFLAGS) $(LDFLAGS) -o $@
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+libexecPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+sbinPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) $(noinst_PROGRAMS) \
+ $(sbin_PROGRAMS)
+am_hprop_OBJECTS = hprop.$(OBJEXT) mit_dump.$(OBJEXT) \
+ v4_dump.$(OBJEXT)
+hprop_OBJECTS = $(am_hprop_OBJECTS)
+hprop_DEPENDENCIES = $(top_builddir)/lib/hdb/libhdb.la \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/krb5/libkrb5.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/asn1/libasn1.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_hpropd_OBJECTS = hpropd.$(OBJEXT)
+hpropd_OBJECTS = $(am_hpropd_OBJECTS)
+hpropd_DEPENDENCIES = $(top_builddir)/lib/hdb/libhdb.la \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/krb5/libkrb5.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/asn1/libasn1.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_kdc_OBJECTS = connect.$(OBJEXT) config.$(OBJEXT) main.$(OBJEXT)
+kdc_OBJECTS = $(am_kdc_OBJECTS)
+am__DEPENDENCIES_2 = $(top_builddir)/lib/hdb/libhdb.la \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/krb5/libkrb5.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+kdc_DEPENDENCIES = libkdc.la $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_1)
+kdc_replay_SOURCES = kdc-replay.c
+kdc_replay_OBJECTS = kdc-replay.$(OBJEXT)
+am__DEPENDENCIES_3 = libkdc.la $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_1)
+kdc_replay_DEPENDENCIES = $(am__DEPENDENCIES_3)
+am_kstash_OBJECTS = kstash.$(OBJEXT)
+kstash_OBJECTS = $(am_kstash_OBJECTS)
+kstash_LDADD = $(LDADD)
+kstash_DEPENDENCIES = $(top_builddir)/lib/hdb/libhdb.la \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/krb5/libkrb5.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+am_string2key_OBJECTS = string2key.$(OBJEXT)
+string2key_OBJECTS = $(am_string2key_OBJECTS)
+string2key_LDADD = $(LDADD)
+string2key_DEPENDENCIES = $(top_builddir)/lib/hdb/libhdb.la \
+ $(am__DEPENDENCIES_1) $(top_builddir)/lib/krb5/libkrb5.la \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES = -I. -I$(top_builddir)/include@am__isrc@
+depcomp =
+am__depfiles_maybe =
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libkdc_la_SOURCES) $(hprop_SOURCES) $(hpropd_SOURCES) \
+ $(kdc_SOURCES) kdc-replay.c $(kstash_SOURCES) \
+ $(string2key_SOURCES)
+DIST_SOURCES = $(libkdc_la_SOURCES) $(hprop_SOURCES) $(hpropd_SOURCES) \
+ $(kdc_SOURCES) kdc-replay.c $(kstash_SOURCES) \
+ $(string2key_SOURCES)
+man8dir = $(mandir)/man8
+MANS = $(man_MANS)
+includeHEADERS_INSTALL = $(INSTALL_HEADER)
+krb5HEADERS_INSTALL = $(INSTALL_HEADER)
+HEADERS = $(include_HEADERS) $(krb5_HEADERS)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CANONICAL_HOST = @CANONICAL_HOST@
+CATMAN = @CATMAN@
+CATMANEXT = @CATMANEXT@
+CC = @CC@
+CFLAGS = @CFLAGS@
+COMPILE_ET = @COMPILE_ET@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DBLIB = @DBLIB@
+DEFS = @DEFS@
+DIR_com_err = @DIR_com_err@
+DIR_hcrypto = @DIR_hcrypto@
+DIR_hdbdir = @DIR_hdbdir@
+DIR_roken = @DIR_roken@
+ECHO = @ECHO@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+F77 = @F77@
+FFLAGS = @FFLAGS@
+GREP = @GREP@
+GROFF = @GROFF@
+INCLUDES_roken = @INCLUDES_roken@
+INCLUDE_hcrypto = @INCLUDE_hcrypto@
+INCLUDE_hesiod = @INCLUDE_hesiod@
+INCLUDE_krb4 = @INCLUDE_krb4@
+INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_readline = @INCLUDE_readline@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_VERSION_SCRIPT = @LDFLAGS_VERSION_SCRIPT@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBADD_roken = @LIBADD_roken@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
+LIB_NDBM = @LIB_NDBM@
+LIB_XauFileName = @LIB_XauFileName@
+LIB_XauReadAuth = @LIB_XauReadAuth@
+LIB_XauWriteAuth = @LIB_XauWriteAuth@
+LIB_bswap16 = @LIB_bswap16@
+LIB_bswap32 = @LIB_bswap32@
+LIB_com_err = @LIB_com_err@
+LIB_com_err_a = @LIB_com_err_a@
+LIB_com_err_so = @LIB_com_err_so@
+LIB_crypt = @LIB_crypt@
+LIB_db_create = @LIB_db_create@
+LIB_dbm_firstkey = @LIB_dbm_firstkey@
+LIB_dbopen = @LIB_dbopen@
+LIB_dlopen = @LIB_dlopen@
+LIB_dn_expand = @LIB_dn_expand@
+LIB_door_create = @LIB_door_create@
+LIB_el_init = @LIB_el_init@
+LIB_freeaddrinfo = @LIB_freeaddrinfo@
+LIB_gai_strerror = @LIB_gai_strerror@
+LIB_getaddrinfo = @LIB_getaddrinfo@
+LIB_gethostbyname = @LIB_gethostbyname@
+LIB_gethostbyname2 = @LIB_gethostbyname2@
+LIB_getnameinfo = @LIB_getnameinfo@
+LIB_getpwnam_r = @LIB_getpwnam_r@
+LIB_getsockopt = @LIB_getsockopt@
+LIB_hcrypto = @LIB_hcrypto@
+LIB_hcrypto_a = @LIB_hcrypto_a@
+LIB_hcrypto_appl = @LIB_hcrypto_appl@
+LIB_hcrypto_so = @LIB_hcrypto_so@
+LIB_hesiod = @LIB_hesiod@
+LIB_hstrerror = @LIB_hstrerror@
+LIB_kdb = @LIB_kdb@
+LIB_krb4 = @LIB_krb4@
+LIB_loadquery = @LIB_loadquery@
+LIB_logout = @LIB_logout@
+LIB_logwtmp = @LIB_logwtmp@
+LIB_openldap = @LIB_openldap@
+LIB_openpty = @LIB_openpty@
+LIB_otp = @LIB_otp@
+LIB_pidfile = @LIB_pidfile@
+LIB_readline = @LIB_readline@
+LIB_res_ndestroy = @LIB_res_ndestroy@
+LIB_res_nsearch = @LIB_res_nsearch@
+LIB_res_search = @LIB_res_search@
+LIB_roken = @LIB_roken@
+LIB_security = @LIB_security@
+LIB_setsockopt = @LIB_setsockopt@
+LIB_socket = @LIB_socket@
+LIB_syslog = @LIB_syslog@
+LIB_tgetent = @LIB_tgetent@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+NROFF = @NROFF@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PTHREADS_CFLAGS = @PTHREADS_CFLAGS@
+PTHREADS_LIBS = @PTHREADS_LIBS@
+RANLIB = @RANLIB@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+VERSION = @VERSION@
+VERSIONING = @VERSIONING@
+VOID_RETSIGTYPE = @VOID_RETSIGTYPE@
+WFLAGS = @WFLAGS@
+WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
+WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
+XMKMF = @XMKMF@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_F77 = @ac_ct_F77@
+am__leading_dot = @am__leading_dot@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dpagaix_cflags = @dpagaix_cflags@
+dpagaix_ldadd = @dpagaix_ldadd@
+dpagaix_ldflags = @dpagaix_ldflags@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUFFIXES = .et .h .x .z .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+AM_CPPFLAGS = -I$(top_builddir)/include $(INCLUDES_roken) \
+ $(INCLUDE_krb4) $(INCLUDE_hcrypto) -I$(srcdir)/../lib/krb5
+@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
+AM_CFLAGS = $(WFLAGS)
+CP = cp
+buildinclude = $(top_builddir)/include
+LIB_getattr = @LIB_getattr@
+LIB_getpwent_r = @LIB_getpwent_r@
+LIB_odm_initialize = @LIB_odm_initialize@
+LIB_setpcred = @LIB_setpcred@
+HESIODLIB = @HESIODLIB@
+HESIODINCLUDE = @HESIODINCLUDE@
+NROFF_MAN = groff -mandoc -Tascii
+LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
+@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
+
+@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
+@KRB5_TRUE@LIB_tsasl = $(top_builddir)/lib/tsasl/libtsasl.la
+@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+lib_LTLIBRARIES = libkdc.la
+man_MANS = kdc.8 kstash.8 hprop.8 hpropd.8 string2key.8
+hprop_SOURCES = hprop.c mit_dump.c v4_dump.c hprop.h kadb.h
+hpropd_SOURCES = hpropd.c hprop.h
+kstash_SOURCES = kstash.c headers.h
+string2key_SOURCES = string2key.c headers.h
+kdc_SOURCES = connect.c \
+ config.c \
+ main.c
+
+libkdc_la_SOURCES = \
+ kdc-private.h \
+ kdc-protos.h \
+ default_config.c \
+ set_dbinfo.c \
+ digest.c \
+ kdc_locl.h \
+ kerberos5.c \
+ krb5tgs.c \
+ pkinit.c \
+ log.c \
+ misc.c \
+ 524.c \
+ kerberos4.c \
+ kaserver.c \
+ kx509.c \
+ process.c \
+ windc.c \
+ rx.h
+
+libkdc_la_LDFLAGS = -version-info 2:0:0 $(am__append_1)
+hprop_LDADD = \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_kdb) $(LIB_krb4) \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+hpropd_LDADD = \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_kdb) $(LIB_krb4) \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+@PKINIT_TRUE@LIB_pkinit = $(top_builddir)/lib/hx509/libhx509.la
+libkdc_la_LIBADD = \
+ $(LIB_pkinit) \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_kdb) $(LIB_krb4) \
+ $(top_builddir)/lib/ntlm/libheimntlm.la \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+LDADD = $(top_builddir)/lib/hdb/libhdb.la \
+ $(LIB_openldap) \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_krb4) \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DBLIB)
+
+kdc_LDADD = libkdc.la $(LDADD) $(LIB_pidfile)
+kdc_replay_LDADD = $(kdc_LDADD)
+include_HEADERS = kdc.h kdc-protos.h
+krb5dir = $(includedir)/krb5
+krb5_HEADERS = windc_plugin.h
+build_HEADERZ = $(krb5_HEADERS) # XXX
+EXTRA_DIST = $(man_MANS) version-script.map
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .et .h .x .z .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign --ignore-deps kdc/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --foreign --ignore-deps kdc/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ if test -f $$p; then \
+ f=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
+ else :; fi; \
+ done
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ p=$(am__strip_dir) \
+ echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
+ $(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libkdc.la: $(libkdc_la_OBJECTS) $(libkdc_la_DEPENDENCIES)
+ $(libkdc_la_LINK) -rpath $(libdir) $(libkdc_la_OBJECTS) $(libkdc_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+install-libexecPROGRAMS: $(libexec_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)"
+ @list='$(libexec_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(libexecPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(libexecdir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(libexecPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(libexecdir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-libexecPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(libexec_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(libexecdir)/$$f'"; \
+ rm -f "$(DESTDIR)$(libexecdir)/$$f"; \
+ done
+
+clean-libexecPROGRAMS:
+ @list='$(libexec_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ || test -f $$p1 \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(sbinPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) --mode=install $(sbinPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(sbindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(sbindir)/$$f"; \
+ done
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ f=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f $$p $$f"; \
+ rm -f $$p $$f ; \
+ done
+hprop$(EXEEXT): $(hprop_OBJECTS) $(hprop_DEPENDENCIES)
+ @rm -f hprop$(EXEEXT)
+ $(LINK) $(hprop_OBJECTS) $(hprop_LDADD) $(LIBS)
+hpropd$(EXEEXT): $(hpropd_OBJECTS) $(hpropd_DEPENDENCIES)
+ @rm -f hpropd$(EXEEXT)
+ $(LINK) $(hpropd_OBJECTS) $(hpropd_LDADD) $(LIBS)
+kdc$(EXEEXT): $(kdc_OBJECTS) $(kdc_DEPENDENCIES)
+ @rm -f kdc$(EXEEXT)
+ $(LINK) $(kdc_OBJECTS) $(kdc_LDADD) $(LIBS)
+kdc-replay$(EXEEXT): $(kdc_replay_OBJECTS) $(kdc_replay_DEPENDENCIES)
+ @rm -f kdc-replay$(EXEEXT)
+ $(LINK) $(kdc_replay_OBJECTS) $(kdc_replay_LDADD) $(LIBS)
+kstash$(EXEEXT): $(kstash_OBJECTS) $(kstash_DEPENDENCIES)
+ @rm -f kstash$(EXEEXT)
+ $(LINK) $(kstash_OBJECTS) $(kstash_LDADD) $(LIBS)
+string2key$(EXEEXT): $(string2key_OBJECTS) $(string2key_DEPENDENCIES)
+ @rm -f string2key$(EXEEXT)
+ $(LINK) $(string2key_OBJECTS) $(string2key_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+.c.o:
+ $(COMPILE) -c $<
+
+.c.obj:
+ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man8: $(man8_MANS) $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS)'; \
+ l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \
+ for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ case "$$ext" in \
+ 8*) ;; \
+ *) ext='8' ;; \
+ esac; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed -e 's/^.*\///'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f '$(DESTDIR)$(man8dir)/$$inst'"; \
+ rm -f "$(DESTDIR)$(man8dir)/$$inst"; \
+ done
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
+ $(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
+ rm -f "$(DESTDIR)$(includedir)/$$f"; \
+ done
+install-krb5HEADERS: $(krb5_HEADERS)
+ @$(NORMAL_INSTALL)
+ test -z "$(krb5dir)" || $(MKDIR_P) "$(DESTDIR)$(krb5dir)"
+ @list='$(krb5_HEADERS)'; for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ f=$(am__strip_dir) \
+ echo " $(krb5HEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(krb5dir)/$$f'"; \
+ $(krb5HEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(krb5dir)/$$f"; \
+ done
+
+uninstall-krb5HEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(krb5_HEADERS)'; for p in $$list; do \
+ f=$(am__strip_dir) \
+ echo " rm -f '$(DESTDIR)$(krb5dir)/$$f'"; \
+ rm -f "$(DESTDIR)$(krb5dir)/$$f"; \
+ done
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) check-local
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(MANS) $(HEADERS) \
+ all-local
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(includedir)" "$(DESTDIR)$(krb5dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libexecPROGRAMS clean-libtool clean-noinstPROGRAMS \
+ clean-sbinPROGRAMS mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS install-krb5HEADERS \
+ install-man
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-data-hook
+
+install-dvi: install-dvi-am
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES \
+ install-libexecPROGRAMS install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+
+install-html: install-html-am
+
+install-info: install-info-am
+
+install-man: install-man8
+
+install-pdf: install-pdf-am
+
+install-ps: install-ps-am
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \
+ uninstall-krb5HEADERS uninstall-libLTLIBRARIES \
+ uninstall-libexecPROGRAMS uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+
+uninstall-man: uninstall-man8
+
+.MAKE: install-am install-data-am install-exec-am install-strip \
+ uninstall-am
+
+.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
+ clean clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libexecPROGRAMS clean-libtool clean-noinstPROGRAMS \
+ clean-sbinPROGRAMS ctags dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am \
+ install-data-hook install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-hook install-html install-html-am \
+ install-includeHEADERS install-info install-info-am \
+ install-krb5HEADERS install-libLTLIBRARIES \
+ install-libexecPROGRAMS install-man install-man8 install-pdf \
+ install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
+ pdf pdf-am ps ps-am tags uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-hook uninstall-includeHEADERS \
+ uninstall-krb5HEADERS uninstall-libLTLIBRARIES \
+ uninstall-libexecPROGRAMS uninstall-man uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+
+install-suid-programs:
+ @foo='$(bin_SUIDS)'; \
+ for file in $$foo; do \
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; done
+
+install-exec-hook: install-suid-programs
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+ for f in $$foo; do \
+ f=`basename $$f`; \
+ if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
+ else file="$$f"; fi; \
+ if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
+ : ; else \
+ echo " $(CP) $$file $(buildinclude)/$$f"; \
+ $(CP) $$file $(buildinclude)/$$f; \
+ fi ; \
+ done ; \
+ foo='$(nobase_include_HEADERS)'; \
+ for f in $$foo; do \
+ if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
+ else file="$$f"; fi; \
+ $(mkdir_p) $(buildinclude)/`dirname $$f` ; \
+ if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
+ : ; else \
+ echo " $(CP) $$file $(buildinclude)/$$f"; \
+ $(CP) $$file $(buildinclude)/$$f; \
+ fi ; \
+ done
+
+all-local: install-build-headers
+
+check-local::
+ @if test '$(CHECK_LOCAL)' = "no-check-local"; then \
+ foo=''; elif test '$(CHECK_LOCAL)'; then \
+ foo='$(CHECK_LOCAL)'; else \
+ foo='$(PROGRAMS)'; fi; \
+ if test "$$foo"; then \
+ failed=0; all=0; \
+ for i in $$foo; do \
+ all=`expr $$all + 1`; \
+ if (./$$i --version && ./$$i --help) > /dev/null 2>&1; then \
+ echo "PASS: $$i"; \
+ else \
+ echo "FAIL: $$i"; \
+ failed=`expr $$failed + 1`; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="$$failed of $$all tests failed"; \
+ fi; \
+ dashes=`echo "$$banner" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0 || exit 1; \
+ fi
+
+.x.c:
+ @cmp -s $< $@ 2> /dev/null || cp $< $@
+#NROFF_MAN = nroff -man
+.1.cat1:
+ $(NROFF_MAN) $< > $@
+.3.cat3:
+ $(NROFF_MAN) $< > $@
+.5.cat5:
+ $(NROFF_MAN) $< > $@
+.8.cat8:
+ $(NROFF_MAN) $< > $@
+
+dist-cat1-mans:
+ @foo='$(man1_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.1) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat1/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat3-mans:
+ @foo='$(man3_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.3) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat3/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat5-mans:
+ @foo='$(man5_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.5) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat5/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat8-mans:
+ @foo='$(man8_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.8) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat8/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+
+install-cat-mans:
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+
+uninstall-cat-mans:
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+
+install-data-hook: install-cat-mans
+uninstall-hook: uninstall-cat-mans
+
+.et.h:
+ $(COMPILE_ET) $<
+.et.c:
+ $(COMPILE_ET) $<
+
+#
+# Useful target for debugging
+#
+
+check-valgrind:
+ tobjdir=`cd $(top_builddir) && pwd` ; \
+ tsrcdir=`cd $(top_srcdir) && pwd` ; \
+ env TESTS_ENVIRONMENT="$${tobjdir}/libtool --mode execute valgrind --leak-check=full --trace-children=yes --quiet -q --num-callers=30 --suppressions=$${tsrcdir}/cf/valgrind-suppressions" make check
+
+#
+# Target to please samba build farm, builds distfiles in-tree.
+# Will break when automake changes...
+#
+
+distdir-in-tree: $(DISTFILES) $(INFO_DEPS)
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" != .; then \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) distdir-in-tree) ; \
+ fi ; \
+ done
+
+$(libkdc_la_OBJECTS): $(srcdir)/kdc-protos.h $(srcdir)/kdc-private.h
+$(libkdc_la_OBJECTS): $(srcdir)/version-script.map
+
+$(srcdir)/kdc-protos.h:
+ cd $(srcdir) && perl ../cf/make-proto.pl -q -P comment -o kdc-protos.h $(libkdc_la_SOURCES) || rm -f kdc-protos.h
+
+$(srcdir)/kdc-private.h:
+ cd $(srcdir) && perl ../cf/make-proto.pl -q -P comment -p kdc-private.h $(libkdc_la_SOURCES) || rm -f kdc-private.h
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/kdc/config.c b/kdc/config.c
new file mode 100644
index 0000000000000..a4d40fce4fa92
--- /dev/null
+++ b/kdc/config.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <getarg.h>
+#include <parse_bytes.h>
+
+RCSID("$Id: config.c 22248 2007-12-08 23:52:12Z lha $");
+
+struct dbinfo {
+ char *realm;
+ char *dbname;
+ char *mkey_file;
+ struct dbinfo *next;
+};
+
+static char *config_file; /* location of kdc config file */
+
+static int require_preauth = -1; /* 1 == require preauth for all principals */
+static char *max_request_str; /* `max_request' as a string */
+
+static int disable_des = -1;
+static int enable_v4 = -1;
+static int enable_kaserver = -1;
+static int enable_524 = -1;
+static int enable_v4_cross_realm = -1;
+
+static int builtin_hdb_flag;
+static int help_flag;
+static int version_flag;
+
+static struct getarg_strings addresses_str; /* addresses to listen on */
+
+static char *v4_realm;
+
+static struct getargs args[] = {
+ {
+ "config-file", 'c', arg_string, &config_file,
+ "location of config file", "file"
+ },
+ {
+ "require-preauth", 'p', arg_negative_flag, &require_preauth,
+ "don't require pa-data in as-reqs"
+ },
+ {
+ "max-request", 0, arg_string, &max_request,
+ "max size for a kdc-request", "size"
+ },
+ { "enable-http", 'H', arg_flag, &enable_http, "turn on HTTP support" },
+ { "524", 0, arg_negative_flag, &enable_524,
+ "don't respond to 524 requests"
+ },
+ {
+ "kaserver", 'K', arg_flag, &enable_kaserver,
+ "enable kaserver support"
+ },
+ { "kerberos4", 0, arg_flag, &enable_v4,
+ "respond to kerberos 4 requests"
+ },
+ {
+ "v4-realm", 'r', arg_string, &v4_realm,
+ "realm to serve v4-requests for"
+ },
+ { "kerberos4-cross-realm", 0, arg_flag,
+ &enable_v4_cross_realm,
+ "respond to kerberos 4 requests from foreign realms"
+ },
+ { "ports", 'P', arg_string, &port_str,
+ "ports to listen to", "portspec"
+ },
+#if DETACH_IS_DEFAULT
+ {
+ "detach", 'D', arg_negative_flag, &detach_from_console,
+ "don't detach from console"
+ },
+#else
+ {
+ "detach", 0 , arg_flag, &detach_from_console,
+ "detach from console"
+ },
+#endif
+ { "addresses", 0, arg_strings, &addresses_str,
+ "addresses to listen on", "list of addresses" },
+ { "disable-des", 0, arg_flag, &disable_des,
+ "disable DES" },
+ { "builtin-hdb", 0, arg_flag, &builtin_hdb_flag,
+ "list builtin hdb backends"},
+ { "help", 'h', arg_flag, &help_flag },
+ { "version", 'v', arg_flag, &version_flag }
+};
+
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int ret)
+{
+ arg_printusage (args, num_args, NULL, "");
+ exit (ret);
+}
+
+static void
+add_one_address (krb5_context context, const char *str, int first)
+{
+ krb5_error_code ret;
+ krb5_addresses tmp;
+
+ ret = krb5_parse_address (context, str, &tmp);
+ if (ret)
+ krb5_err (context, 1, ret, "parse_address `%s'", str);
+ if (first)
+ krb5_copy_addresses(context, &tmp, &explicit_addresses);
+ else
+ krb5_append_addresses(context, &explicit_addresses, &tmp);
+ krb5_free_addresses (context, &tmp);
+}
+
+krb5_kdc_configuration *
+configure(krb5_context context, int argc, char **argv)
+{
+ krb5_kdc_configuration *config;
+ krb5_error_code ret;
+ int optidx = 0;
+ const char *p;
+
+ while(getarg(args, num_args, argc, argv, &optidx))
+ warnx("error at argument `%s'", argv[optidx]);
+
+ if(help_flag)
+ usage (0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ if (builtin_hdb_flag) {
+ char *list;
+ ret = hdb_list_builtin(context, &list);
+ if (ret)
+ krb5_err(context, 1, ret, "listing builtin hdb backends");
+ printf("builtin hdb backends: %s\n", list);
+ free(list);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 0)
+ usage(1);
+
+ {
+ char **files;
+
+ if (config_file == NULL) {
+ asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
+ if (config_file == NULL)
+ errx(1, "out of memory");
+ }
+
+ ret = krb5_prepend_config_files_default(config_file, &files);
+ if (ret)
+ krb5_err(context, 1, ret, "getting configuration files");
+
+ ret = krb5_set_config_files(context, files);
+ krb5_free_config_files(files);
+ if(ret)
+ krb5_err(context, 1, ret, "reading configuration files");
+ }
+
+ ret = krb5_kdc_get_config(context, &config);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kdc_default_config");
+
+ kdc_openlog(context, config);
+
+ ret = krb5_kdc_set_dbinfo(context, config);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kdc_set_dbinfo");
+
+ if(max_request_str)
+ max_request = parse_bytes(max_request_str, NULL);
+
+ if(max_request == 0){
+ p = krb5_config_get_string (context,
+ NULL,
+ "kdc",
+ "max-request",
+ NULL);
+ if(p)
+ max_request = parse_bytes(p, NULL);
+ }
+
+ if(require_preauth != -1)
+ config->require_preauth = require_preauth;
+
+ if(port_str == NULL){
+ p = krb5_config_get_string(context, NULL, "kdc", "ports", NULL);
+ if (p != NULL)
+ port_str = strdup(p);
+ }
+
+ explicit_addresses.len = 0;
+
+ if (addresses_str.num_strings) {
+ int i;
+
+ for (i = 0; i < addresses_str.num_strings; ++i)
+ add_one_address (context, addresses_str.strings[i], i == 0);
+ free_getarg_strings (&addresses_str);
+ } else {
+ char **foo = krb5_config_get_strings (context, NULL,
+ "kdc", "addresses", NULL);
+
+ if (foo != NULL) {
+ add_one_address (context, *foo++, TRUE);
+ while (*foo)
+ add_one_address (context, *foo++, FALSE);
+ }
+ }
+
+ if(enable_v4 != -1)
+ config->enable_v4 = enable_v4;
+
+ if(enable_v4_cross_realm != -1)
+ config->enable_v4_cross_realm = enable_v4_cross_realm;
+
+ if(enable_524 != -1)
+ config->enable_524 = enable_524;
+
+ if(enable_http == -1)
+ enable_http = krb5_config_get_bool(context, NULL, "kdc",
+ "enable-http", NULL);
+
+ if(request_log == NULL)
+ request_log = krb5_config_get_string(context, NULL,
+ "kdc",
+ "kdc-request-log",
+ NULL);
+
+ if (krb5_config_get_string(context, NULL, "kdc",
+ "enforce-transited-policy", NULL))
+ krb5_errx(context, 1, "enforce-transited-policy deprecated, "
+ "use [kdc]transited-policy instead");
+
+ if (enable_kaserver != -1)
+ config->enable_kaserver = enable_kaserver;
+
+ if(detach_from_console == -1)
+ detach_from_console = krb5_config_get_bool_default(context, NULL,
+ DETACH_IS_DEFAULT,
+ "kdc",
+ "detach", NULL);
+
+ if(max_request == 0)
+ max_request = 64 * 1024;
+
+ if (port_str == NULL)
+ port_str = "+";
+
+ if (v4_realm)
+ config->v4_realm = v4_realm;
+
+ if(config->v4_realm == NULL && (config->enable_kaserver || config->enable_v4))
+ krb5_errx(context, 1, "Kerberos 4 enabled but no realm configured");
+
+ if(disable_des == -1)
+ disable_des = krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "kdc",
+ "disable-des", NULL);
+ if(disable_des) {
+ krb5_enctype_disable(context, ETYPE_DES_CBC_CRC);
+ krb5_enctype_disable(context, ETYPE_DES_CBC_MD4);
+ krb5_enctype_disable(context, ETYPE_DES_CBC_MD5);
+ krb5_enctype_disable(context, ETYPE_DES_CBC_NONE);
+ krb5_enctype_disable(context, ETYPE_DES_CFB64_NONE);
+ krb5_enctype_disable(context, ETYPE_DES_PCBC_NONE);
+
+ kdc_log(context, config,
+ 0, "DES was disabled, turned off Kerberos V4, 524 "
+ "and kaserver");
+ config->enable_v4 = 0;
+ config->enable_524 = 0;
+ config->enable_kaserver = 0;
+ }
+
+ krb5_kdc_windc_init(context);
+
+ return config;
+}
diff --git a/kdc/connect.c b/kdc/connect.c
new file mode 100644
index 0000000000000..c2df088342f46
--- /dev/null
+++ b/kdc/connect.c
@@ -0,0 +1,900 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: connect.c 22434 2008-01-14 09:21:37Z lha $");
+
+/* Should we enable the HTTP hack? */
+int enable_http = -1;
+
+/* Log over requests to the KDC */
+const char *request_log;
+
+/* A string describing on what ports to listen */
+const char *port_str;
+
+krb5_addresses explicit_addresses;
+
+size_t max_request; /* maximal size of a request */
+
+/*
+ * a tuple describing on what to listen
+ */
+
+struct port_desc{
+ int family;
+ int type;
+ int port;
+};
+
+/* the current ones */
+
+static struct port_desc *ports;
+static int num_ports;
+
+/*
+ * add `family, port, protocol' to the list with duplicate suppresion.
+ */
+
+static void
+add_port(krb5_context context,
+ int family, int port, const char *protocol)
+{
+ int type;
+ int i;
+
+ if(strcmp(protocol, "udp") == 0)
+ type = SOCK_DGRAM;
+ else if(strcmp(protocol, "tcp") == 0)
+ type = SOCK_STREAM;
+ else
+ return;
+ for(i = 0; i < num_ports; i++){
+ if(ports[i].type == type
+ && ports[i].port == port
+ && ports[i].family == family)
+ return;
+ }
+ ports = realloc(ports, (num_ports + 1) * sizeof(*ports));
+ if (ports == NULL)
+ krb5_err (context, 1, errno, "realloc");
+ ports[num_ports].family = family;
+ ports[num_ports].type = type;
+ ports[num_ports].port = port;
+ num_ports++;
+}
+
+/*
+ * add a triple but with service -> port lookup
+ * (this prints warnings for stuff that does not exist)
+ */
+
+static void
+add_port_service(krb5_context context,
+ int family, const char *service, int port,
+ const char *protocol)
+{
+ port = krb5_getportbyname (context, service, protocol, port);
+ add_port (context, family, port, protocol);
+}
+
+/*
+ * add the port with service -> port lookup or string -> number
+ * (no warning is printed)
+ */
+
+static void
+add_port_string (krb5_context context,
+ int family, const char *str, const char *protocol)
+{
+ struct servent *sp;
+ int port;
+
+ sp = roken_getservbyname (str, protocol);
+ if (sp != NULL) {
+ port = sp->s_port;
+ } else {
+ char *end;
+
+ port = htons(strtol(str, &end, 0));
+ if (end == str)
+ return;
+ }
+ add_port (context, family, port, protocol);
+}
+
+/*
+ * add the standard collection of ports for `family'
+ */
+
+static void
+add_standard_ports (krb5_context context,
+ krb5_kdc_configuration *config,
+ int family)
+{
+ add_port_service(context, family, "kerberos", 88, "udp");
+ add_port_service(context, family, "kerberos", 88, "tcp");
+ add_port_service(context, family, "kerberos-sec", 88, "udp");
+ add_port_service(context, family, "kerberos-sec", 88, "tcp");
+ if(enable_http)
+ add_port_service(context, family, "http", 80, "tcp");
+ if(config->enable_524) {
+ add_port_service(context, family, "krb524", 4444, "udp");
+ add_port_service(context, family, "krb524", 4444, "tcp");
+ }
+ if(config->enable_v4) {
+ add_port_service(context, family, "kerberos-iv", 750, "udp");
+ add_port_service(context, family, "kerberos-iv", 750, "tcp");
+ }
+ if (config->enable_kaserver)
+ add_port_service(context, family, "afs3-kaserver", 7004, "udp");
+ if(config->enable_kx509) {
+ add_port_service(context, family, "kca_service", 9878, "udp");
+ add_port_service(context, family, "kca_service", 9878, "tcp");
+ }
+
+}
+
+/*
+ * parse the set of space-delimited ports in `str' and add them.
+ * "+" => all the standard ones
+ * otherwise it's port|service[/protocol]
+ */
+
+static void
+parse_ports(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *str)
+{
+ char *pos = NULL;
+ char *p;
+ char *str_copy = strdup (str);
+
+ p = strtok_r(str_copy, " \t", &pos);
+ while(p != NULL) {
+ if(strcmp(p, "+") == 0) {
+#ifdef HAVE_IPV6
+ add_standard_ports(context, config, AF_INET6);
+#endif
+ add_standard_ports(context, config, AF_INET);
+ } else {
+ char *q = strchr(p, '/');
+ if(q){
+ *q++ = 0;
+#ifdef HAVE_IPV6
+ add_port_string(context, AF_INET6, p, q);
+#endif
+ add_port_string(context, AF_INET, p, q);
+ }else {
+#ifdef HAVE_IPV6
+ add_port_string(context, AF_INET6, p, "udp");
+ add_port_string(context, AF_INET6, p, "tcp");
+#endif
+ add_port_string(context, AF_INET, p, "udp");
+ add_port_string(context, AF_INET, p, "tcp");
+ }
+ }
+
+ p = strtok_r(NULL, " \t", &pos);
+ }
+ free (str_copy);
+}
+
+/*
+ * every socket we listen on
+ */
+
+struct descr {
+ int s;
+ int type;
+ int port;
+ unsigned char *buf;
+ size_t size;
+ size_t len;
+ time_t timeout;
+ struct sockaddr_storage __ss;
+ struct sockaddr *sa;
+ socklen_t sock_len;
+ char addr_string[128];
+};
+
+static void
+init_descr(struct descr *d)
+{
+ memset(d, 0, sizeof(*d));
+ d->sa = (struct sockaddr *)&d->__ss;
+ d->s = -1;
+}
+
+/*
+ * re-initialize all `n' ->sa in `d'.
+ */
+
+static void
+reinit_descrs (struct descr *d, int n)
+{
+ int i;
+
+ for (i = 0; i < n; ++i)
+ d[i].sa = (struct sockaddr *)&d[i].__ss;
+}
+
+/*
+ * Create the socket (family, type, port) in `d'
+ */
+
+static void
+init_socket(krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr *d, krb5_address *a, int family, int type, int port)
+{
+ krb5_error_code ret;
+ struct sockaddr_storage __ss;
+ struct sockaddr *sa = (struct sockaddr *)&__ss;
+ krb5_socklen_t sa_size = sizeof(__ss);
+
+ init_descr (d);
+
+ ret = krb5_addr2sockaddr (context, a, sa, &sa_size, port);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_addr2sockaddr");
+ close(d->s);
+ d->s = -1;
+ return;
+ }
+
+ if (sa->sa_family != family)
+ return;
+
+ d->s = socket(family, type, 0);
+ if(d->s < 0){
+ krb5_warn(context, errno, "socket(%d, %d, 0)", family, type);
+ d->s = -1;
+ return;
+ }
+#if defined(HAVE_SETSOCKOPT) && defined(SOL_SOCKET) && defined(SO_REUSEADDR)
+ {
+ int one = 1;
+ setsockopt(d->s, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
+ }
+#endif
+ d->type = type;
+ d->port = port;
+
+ if(bind(d->s, sa, sa_size) < 0){
+ char a_str[256];
+ size_t len;
+
+ krb5_print_address (a, a_str, sizeof(a_str), &len);
+ krb5_warn(context, errno, "bind %s/%d", a_str, ntohs(port));
+ close(d->s);
+ d->s = -1;
+ return;
+ }
+ if(type == SOCK_STREAM && listen(d->s, SOMAXCONN) < 0){
+ char a_str[256];
+ size_t len;
+
+ krb5_print_address (a, a_str, sizeof(a_str), &len);
+ krb5_warn(context, errno, "listen %s/%d", a_str, ntohs(port));
+ close(d->s);
+ d->s = -1;
+ return;
+ }
+}
+
+/*
+ * Allocate descriptors for all the sockets that we should listen on
+ * and return the number of them.
+ */
+
+static int
+init_sockets(krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr **desc)
+{
+ krb5_error_code ret;
+ int i, j;
+ struct descr *d;
+ int num = 0;
+ krb5_addresses addresses;
+
+ if (explicit_addresses.len) {
+ addresses = explicit_addresses;
+ } else {
+ ret = krb5_get_all_server_addrs (context, &addresses);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
+ }
+ parse_ports(context, config, port_str);
+ d = malloc(addresses.len * num_ports * sizeof(*d));
+ if (d == NULL)
+ krb5_errx(context, 1, "malloc(%lu) failed",
+ (unsigned long)num_ports * sizeof(*d));
+
+ for (i = 0; i < num_ports; i++){
+ for (j = 0; j < addresses.len; ++j) {
+ init_socket(context, config, &d[num], &addresses.val[j],
+ ports[i].family, ports[i].type, ports[i].port);
+ if(d[num].s != -1){
+ char a_str[80];
+ size_t len;
+
+ krb5_print_address (&addresses.val[j], a_str,
+ sizeof(a_str), &len);
+
+ kdc_log(context, config, 5, "listening on %s port %u/%s",
+ a_str,
+ ntohs(ports[i].port),
+ (ports[i].type == SOCK_STREAM) ? "tcp" : "udp");
+ /* XXX */
+ num++;
+ }
+ }
+ }
+ krb5_free_addresses (context, &addresses);
+ d = realloc(d, num * sizeof(*d));
+ if (d == NULL && num != 0)
+ krb5_errx(context, 1, "realloc(%lu) failed",
+ (unsigned long)num * sizeof(*d));
+ reinit_descrs (d, num);
+ *desc = d;
+ return num;
+}
+
+/*
+ *
+ */
+
+static const char *
+descr_type(struct descr *d)
+{
+ if (d->type == SOCK_DGRAM)
+ return "udp";
+ else if (d->type == SOCK_STREAM)
+ return "tcp";
+ return "unknown";
+}
+
+static void
+addr_to_string(krb5_context context,
+ struct sockaddr *addr, size_t addr_len, char *str, size_t len)
+{
+ krb5_address a;
+ if(krb5_sockaddr2address(context, addr, &a) == 0) {
+ if(krb5_print_address(&a, str, len, &len) == 0) {
+ krb5_free_address(context, &a);
+ return;
+ }
+ krb5_free_address(context, &a);
+ }
+ snprintf(str, len, "<family=%d>", addr->sa_family);
+}
+
+/*
+ *
+ */
+
+static void
+send_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_boolean prependlength,
+ struct descr *d,
+ krb5_data *reply)
+{
+ kdc_log(context, config, 5,
+ "sending %lu bytes to %s", (unsigned long)reply->length,
+ d->addr_string);
+ if(prependlength){
+ unsigned char l[4];
+ l[0] = (reply->length >> 24) & 0xff;
+ l[1] = (reply->length >> 16) & 0xff;
+ l[2] = (reply->length >> 8) & 0xff;
+ l[3] = reply->length & 0xff;
+ if(sendto(d->s, l, sizeof(l), 0, d->sa, d->sock_len) < 0) {
+ kdc_log (context, config,
+ 0, "sendto(%s): %s", d->addr_string, strerror(errno));
+ return;
+ }
+ }
+ if(sendto(d->s, reply->data, reply->length, 0, d->sa, d->sock_len) < 0) {
+ kdc_log (context, config,
+ 0, "sendto(%s): %s", d->addr_string, strerror(errno));
+ return;
+ }
+}
+
+/*
+ * Handle the request in `buf, len' to socket `d'
+ */
+
+static void
+do_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ void *buf, size_t len, krb5_boolean prependlength,
+ struct descr *d)
+{
+ krb5_error_code ret;
+ krb5_data reply;
+ int datagram_reply = (d->type == SOCK_DGRAM);
+
+ krb5_kdc_update_time(NULL);
+
+ krb5_data_zero(&reply);
+ ret = krb5_kdc_process_request(context, config,
+ buf, len, &reply, &prependlength,
+ d->addr_string, d->sa,
+ datagram_reply);
+ if(request_log)
+ krb5_kdc_save_request(context, request_log, buf, len, &reply, d->sa);
+ if(reply.length){
+ send_reply(context, config, prependlength, d, &reply);
+ krb5_data_free(&reply);
+ }
+ if(ret)
+ kdc_log(context, config, 0,
+ "Failed processing %lu byte request from %s",
+ (unsigned long)len, d->addr_string);
+}
+
+/*
+ * Handle incoming data to the UDP socket in `d'
+ */
+
+static void
+handle_udp(krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr *d)
+{
+ unsigned char *buf;
+ int n;
+
+ buf = malloc(max_request);
+ if(buf == NULL){
+ kdc_log(context, config, 0, "Failed to allocate %lu bytes", (unsigned long)max_request);
+ return;
+ }
+
+ d->sock_len = sizeof(d->__ss);
+ n = recvfrom(d->s, buf, max_request, 0, d->sa, &d->sock_len);
+ if(n < 0)
+ krb5_warn(context, errno, "recvfrom");
+ else {
+ addr_to_string (context, d->sa, d->sock_len,
+ d->addr_string, sizeof(d->addr_string));
+ do_request(context, config, buf, n, FALSE, d);
+ }
+ free (buf);
+}
+
+static void
+clear_descr(struct descr *d)
+{
+ if(d->buf)
+ memset(d->buf, 0, d->size);
+ d->len = 0;
+ if(d->s != -1)
+ close(d->s);
+ d->s = -1;
+}
+
+
+/* remove HTTP %-quoting from buf */
+static int
+de_http(char *buf)
+{
+ unsigned char *p, *q;
+ for(p = q = (unsigned char *)buf; *p; p++, q++) {
+ if(*p == '%' && isxdigit(p[1]) && isxdigit(p[2])) {
+ unsigned int x;
+ if(sscanf((char *)p + 1, "%2x", &x) != 1)
+ return -1;
+ *q = x;
+ p += 2;
+ } else
+ *q = *p;
+ }
+ *q = '\0';
+ return 0;
+}
+
+#define TCP_TIMEOUT 4
+
+/*
+ * accept a new TCP connection on `d[parent]' and store it in `d[child]'
+ */
+
+static void
+add_new_tcp (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr *d, int parent, int child)
+{
+ int s;
+
+ if (child == -1)
+ return;
+
+ d[child].sock_len = sizeof(d[child].__ss);
+ s = accept(d[parent].s, d[child].sa, &d[child].sock_len);
+ if(s < 0) {
+ krb5_warn(context, errno, "accept");
+ return;
+ }
+
+ if (s >= FD_SETSIZE) {
+ krb5_warnx(context, "socket FD too large");
+ close (s);
+ return;
+ }
+
+ d[child].s = s;
+ d[child].timeout = time(NULL) + TCP_TIMEOUT;
+ d[child].type = SOCK_STREAM;
+ addr_to_string (context,
+ d[child].sa, d[child].sock_len,
+ d[child].addr_string, sizeof(d[child].addr_string));
+}
+
+/*
+ * Grow `d' to handle at least `n'.
+ * Return != 0 if fails
+ */
+
+static int
+grow_descr (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr *d, size_t n)
+{
+ if (d->size - d->len < n) {
+ unsigned char *tmp;
+ size_t grow;
+
+ grow = max(1024, d->len + n);
+ if (d->size + grow > max_request) {
+ kdc_log(context, config, 0, "Request exceeds max request size (%lu bytes).",
+ (unsigned long)d->size + grow);
+ clear_descr(d);
+ return -1;
+ }
+ tmp = realloc (d->buf, d->size + grow);
+ if (tmp == NULL) {
+ kdc_log(context, config, 0, "Failed to re-allocate %lu bytes.",
+ (unsigned long)d->size + grow);
+ clear_descr(d);
+ return -1;
+ }
+ d->size += grow;
+ d->buf = tmp;
+ }
+ return 0;
+}
+
+/*
+ * Try to handle the TCP data at `d->buf, d->len'.
+ * Return -1 if failed, 0 if succesful, and 1 if data is complete.
+ */
+
+static int
+handle_vanilla_tcp (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr *d)
+{
+ krb5_storage *sp;
+ uint32_t len;
+
+ sp = krb5_storage_from_mem(d->buf, d->len);
+ if (sp == NULL) {
+ kdc_log (context, config, 0, "krb5_storage_from_mem failed");
+ return -1;
+ }
+ krb5_ret_uint32(sp, &len);
+ krb5_storage_free(sp);
+ if(d->len - 4 >= len) {
+ memmove(d->buf, d->buf + 4, d->len - 4);
+ d->len -= 4;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Try to handle the TCP/HTTP data at `d->buf, d->len'.
+ * Return -1 if failed, 0 if succesful, and 1 if data is complete.
+ */
+
+static int
+handle_http_tcp (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr *d)
+{
+ char *s, *p, *t;
+ void *data;
+ char *proto;
+ int len;
+
+ s = (char *)d->buf;
+
+ p = strstr(s, "\r\n");
+ if (p == NULL) {
+ kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
+ return -1;
+ }
+ *p = 0;
+
+ p = NULL;
+ t = strtok_r(s, " \t", &p);
+ if (t == NULL) {
+ kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
+ return -1;
+ }
+ t = strtok_r(NULL, " \t", &p);
+ if(t == NULL) {
+ kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
+ return -1;
+ }
+ data = malloc(strlen(t));
+ if (data == NULL) {
+ kdc_log(context, config, 0, "Failed to allocate %lu bytes",
+ (unsigned long)strlen(t));
+ return -1;
+ }
+ if(*t == '/')
+ t++;
+ if(de_http(t) != 0) {
+ kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
+ kdc_log(context, config, 5, "HTTP request: %s", t);
+ free(data);
+ return -1;
+ }
+ proto = strtok_r(NULL, " \t", &p);
+ if (proto == NULL) {
+ kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string);
+ free(data);
+ return -1;
+ }
+ len = base64_decode(t, data);
+ if(len <= 0){
+ const char *msg =
+ " 404 Not found\r\n"
+ "Server: Heimdal/" VERSION "\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Pragma: no-cache\r\n"
+ "Content-type: text/html\r\n"
+ "Content-transfer-encoding: 8bit\r\n\r\n"
+ "<TITLE>404 Not found</TITLE>\r\n"
+ "<H1>404 Not found</H1>\r\n"
+ "That page doesn't exist, maybe you are looking for "
+ "<A HREF=\"http://www.h5l.org/\">Heimdal</A>?\r\n";
+ kdc_log(context, config, 0, "HTTP request from %s is non KDC request", d->addr_string);
+ kdc_log(context, config, 5, "HTTP request: %s", t);
+ free(data);
+ if (write(d->s, proto, strlen(proto)) < 0) {
+ kdc_log(context, config, 0, "HTTP write failed: %s: %s",
+ d->addr_string, strerror(errno));
+ return -1;
+ }
+ if (write(d->s, msg, strlen(msg)) < 0) {
+ kdc_log(context, config, 0, "HTTP write failed: %s: %s",
+ d->addr_string, strerror(errno));
+ return -1;
+ }
+ return -1;
+ }
+ {
+ const char *msg =
+ " 200 OK\r\n"
+ "Server: Heimdal/" VERSION "\r\n"
+ "Cache-Control: no-cache\r\n"
+ "Pragma: no-cache\r\n"
+ "Content-type: application/octet-stream\r\n"
+ "Content-transfer-encoding: binary\r\n\r\n";
+ if (write(d->s, proto, strlen(proto)) < 0) {
+ kdc_log(context, config, 0, "HTTP write failed: %s: %s",
+ d->addr_string, strerror(errno));
+ return -1;
+ }
+ if (write(d->s, msg, strlen(msg)) < 0) {
+ kdc_log(context, config, 0, "HTTP write failed: %s: %s",
+ d->addr_string, strerror(errno));
+ return -1;
+ }
+ }
+ memcpy(d->buf, data, len);
+ d->len = len;
+ free(data);
+ return 1;
+}
+
+/*
+ * Handle incoming data to the TCP socket in `d[index]'
+ */
+
+static void
+handle_tcp(krb5_context context,
+ krb5_kdc_configuration *config,
+ struct descr *d, int idx, int min_free)
+{
+ unsigned char buf[1024];
+ int n;
+ int ret = 0;
+
+ if (d[idx].timeout == 0) {
+ add_new_tcp (context, config, d, idx, min_free);
+ return;
+ }
+
+ n = recvfrom(d[idx].s, buf, sizeof(buf), 0, NULL, NULL);
+ if(n < 0){
+ krb5_warn(context, errno, "recvfrom failed from %s to %s/%d",
+ d[idx].addr_string, descr_type(d + idx),
+ ntohs(d[idx].port));
+ return;
+ } else if (n == 0) {
+ krb5_warnx(context, "connection closed before end of data after %lu "
+ "bytes from %s to %s/%d", (unsigned long)d[idx].len,
+ d[idx].addr_string, descr_type(d + idx),
+ ntohs(d[idx].port));
+ clear_descr (d + idx);
+ return;
+ }
+ if (grow_descr (context, config, &d[idx], n))
+ return;
+ memcpy(d[idx].buf + d[idx].len, buf, n);
+ d[idx].len += n;
+ if(d[idx].len > 4 && d[idx].buf[0] == 0) {
+ ret = handle_vanilla_tcp (context, config, &d[idx]);
+ } else if(enable_http &&
+ d[idx].len >= 4 &&
+ strncmp((char *)d[idx].buf, "GET ", 4) == 0 &&
+ strncmp((char *)d[idx].buf + d[idx].len - 4,
+ "\r\n\r\n", 4) == 0) {
+ ret = handle_http_tcp (context, config, &d[idx]);
+ if (ret < 0)
+ clear_descr (d + idx);
+ } else if (d[idx].len > 4) {
+ kdc_log (context, config,
+ 0, "TCP data of strange type from %s to %s/%d",
+ d[idx].addr_string, descr_type(d + idx),
+ ntohs(d[idx].port));
+ if (d[idx].buf[0] & 0x80) {
+ krb5_data reply;
+
+ kdc_log (context, config, 0, "TCP extension not supported");
+
+ ret = krb5_mk_error(context,
+ KRB5KRB_ERR_FIELD_TOOLONG,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &reply);
+ if (ret == 0) {
+ send_reply(context, config, TRUE, d + idx, &reply);
+ krb5_data_free(&reply);
+ }
+ }
+ clear_descr(d + idx);
+ return;
+ }
+ if (ret < 0)
+ return;
+ else if (ret == 1) {
+ do_request(context, config,
+ d[idx].buf, d[idx].len, TRUE, &d[idx]);
+ clear_descr(d + idx);
+ }
+}
+
+void
+loop(krb5_context context,
+ krb5_kdc_configuration *config)
+{
+ struct descr *d;
+ int ndescr;
+
+ ndescr = init_sockets(context, config, &d);
+ if(ndescr <= 0)
+ krb5_errx(context, 1, "No sockets!");
+ kdc_log(context, config, 0, "KDC started");
+ while(exit_flag == 0){
+ struct timeval tmout;
+ fd_set fds;
+ int min_free = -1;
+ int max_fd = 0;
+ int i;
+
+ FD_ZERO(&fds);
+ for(i = 0; i < ndescr; i++) {
+ if(d[i].s >= 0){
+ if(d[i].type == SOCK_STREAM &&
+ d[i].timeout && d[i].timeout < time(NULL)) {
+ kdc_log(context, config, 1,
+ "TCP-connection from %s expired after %lu bytes",
+ d[i].addr_string, (unsigned long)d[i].len);
+ clear_descr(&d[i]);
+ continue;
+ }
+ if(max_fd < d[i].s)
+ max_fd = d[i].s;
+ if (max_fd >= FD_SETSIZE)
+ krb5_errx(context, 1, "fd too large");
+ FD_SET(d[i].s, &fds);
+ } else if(min_free < 0 || i < min_free)
+ min_free = i;
+ }
+ if(min_free == -1){
+ struct descr *tmp;
+ tmp = realloc(d, (ndescr + 4) * sizeof(*d));
+ if(tmp == NULL)
+ krb5_warnx(context, "No memory");
+ else {
+ d = tmp;
+ reinit_descrs (d, ndescr);
+ memset(d + ndescr, 0, 4 * sizeof(*d));
+ for(i = ndescr; i < ndescr + 4; i++)
+ init_descr (&d[i]);
+ min_free = ndescr;
+ ndescr += 4;
+ }
+ }
+
+ tmout.tv_sec = TCP_TIMEOUT;
+ tmout.tv_usec = 0;
+ switch(select(max_fd + 1, &fds, 0, 0, &tmout)){
+ case 0:
+ break;
+ case -1:
+ if (errno != EINTR)
+ krb5_warn(context, errno, "select");
+ break;
+ default:
+ for(i = 0; i < ndescr; i++)
+ if(d[i].s >= 0 && FD_ISSET(d[i].s, &fds)) {
+ if(d[i].type == SOCK_DGRAM)
+ handle_udp(context, config, &d[i]);
+ else if(d[i].type == SOCK_STREAM)
+ handle_tcp(context, config, d, i, min_free);
+ }
+ }
+ }
+ if(exit_flag == SIGXCPU)
+ kdc_log(context, config, 0, "CPU time limit exceeded");
+ else if(exit_flag == SIGINT || exit_flag == SIGTERM)
+ kdc_log(context, config, 0, "Terminated");
+ else
+ kdc_log(context, config, 0, "Unexpected exit reason: %d", exit_flag);
+ free (d);
+}
diff --git a/kdc/default_config.c b/kdc/default_config.c
new file mode 100644
index 0000000000000..5f336e3275db2
--- /dev/null
+++ b/kdc/default_config.c
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <getarg.h>
+#include <parse_bytes.h>
+
+RCSID("$Id: default_config.c 21405 2007-07-04 10:35:45Z lha $");
+
+krb5_error_code
+krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
+{
+ krb5_kdc_configuration *c;
+
+ c = calloc(1, sizeof(*c));
+ if (c == NULL) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ c->require_preauth = TRUE;
+ c->kdc_warn_pwexpire = 0;
+ c->encode_as_rep_as_tgs_rep = FALSE;
+ c->check_ticket_addresses = TRUE;
+ c->allow_null_ticket_addresses = TRUE;
+ c->allow_anonymous = FALSE;
+ c->trpolicy = TRPOLICY_ALWAYS_CHECK;
+ c->enable_v4 = FALSE;
+ c->enable_kaserver = FALSE;
+ c->enable_524 = FALSE;
+ c->enable_v4_cross_realm = FALSE;
+ c->enable_pkinit = FALSE;
+ c->pkinit_princ_in_cert = TRUE;
+ c->pkinit_require_binding = TRUE;
+ c->db = NULL;
+ c->num_db = 0;
+ c->logf = NULL;
+
+ c->require_preauth =
+ krb5_config_get_bool_default(context, NULL,
+ c->require_preauth,
+ "kdc", "require-preauth", NULL);
+ c->enable_v4 =
+ krb5_config_get_bool_default(context, NULL,
+ c->enable_v4,
+ "kdc", "enable-kerberos4", NULL);
+ c->enable_v4_cross_realm =
+ krb5_config_get_bool_default(context, NULL,
+ c->enable_v4_cross_realm,
+ "kdc",
+ "enable-kerberos4-cross-realm", NULL);
+ c->enable_524 =
+ krb5_config_get_bool_default(context, NULL,
+ c->enable_v4,
+ "kdc", "enable-524", NULL);
+ c->enable_digest =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "kdc", "enable-digest", NULL);
+
+ {
+ const char *digests;
+
+ digests = krb5_config_get_string(context, NULL,
+ "kdc",
+ "digests_allowed", NULL);
+ if (digests == NULL)
+ digests = "ntlm-v2";
+ c->digests_allowed = parse_flags(digests,_kdc_digestunits, 0);
+ if (c->digests_allowed == -1) {
+ kdc_log(context, c, 0,
+ "unparsable digest units (%s), turning off digest",
+ digests);
+ c->enable_digest = 0;
+ } else if (c->digests_allowed == 0) {
+ kdc_log(context, c, 0,
+ "no digest enable, turning digest off",
+ digests);
+ c->enable_digest = 0;
+ }
+ }
+
+ c->enable_kx509 =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "kdc", "enable-kx509", NULL);
+
+ if (c->enable_kx509) {
+ c->kx509_template =
+ krb5_config_get_string(context, NULL,
+ "kdc", "kx509_template", NULL);
+ c->kx509_ca =
+ krb5_config_get_string(context, NULL,
+ "kdc", "kx509_ca", NULL);
+ if (c->kx509_ca == NULL || c->kx509_template == NULL) {
+ kdc_log(context, c, 0,
+ "missing kx509 configuration, turning off");
+ c->enable_kx509 = FALSE;
+ }
+ }
+
+ c->check_ticket_addresses =
+ krb5_config_get_bool_default(context, NULL,
+ c->check_ticket_addresses,
+ "kdc",
+ "check-ticket-addresses", NULL);
+ c->allow_null_ticket_addresses =
+ krb5_config_get_bool_default(context, NULL,
+ c->allow_null_ticket_addresses,
+ "kdc",
+ "allow-null-ticket-addresses", NULL);
+
+ c->allow_anonymous =
+ krb5_config_get_bool_default(context, NULL,
+ c->allow_anonymous,
+ "kdc",
+ "allow-anonymous", NULL);
+
+ c->max_datagram_reply_length =
+ krb5_config_get_int_default(context,
+ NULL,
+ 1400,
+ "kdc",
+ "max-kdc-datagram-reply-length",
+ NULL);
+
+ {
+ const char *trpolicy_str;
+
+ trpolicy_str =
+ krb5_config_get_string_default(context, NULL, "DEFAULT", "kdc",
+ "transited-policy", NULL);
+ if(strcasecmp(trpolicy_str, "always-check") == 0) {
+ c->trpolicy = TRPOLICY_ALWAYS_CHECK;
+ } else if(strcasecmp(trpolicy_str, "allow-per-principal") == 0) {
+ c->trpolicy = TRPOLICY_ALLOW_PER_PRINCIPAL;
+ } else if(strcasecmp(trpolicy_str, "always-honour-request") == 0) {
+ c->trpolicy = TRPOLICY_ALWAYS_HONOUR_REQUEST;
+ } else if(strcasecmp(trpolicy_str, "DEFAULT") == 0) {
+ /* default */
+ } else {
+ kdc_log(context, c, 0,
+ "unknown transited-policy: %s, "
+ "reverting to default (always-check)",
+ trpolicy_str);
+ }
+ }
+
+ {
+ const char *p;
+ p = krb5_config_get_string (context, NULL,
+ "kdc",
+ "v4-realm",
+ NULL);
+ if(p != NULL) {
+ c->v4_realm = strdup(p);
+ if (c->v4_realm == NULL)
+ krb5_errx(context, 1, "out of memory");
+ } else {
+ c->v4_realm = NULL;
+ }
+ }
+
+ c->enable_kaserver =
+ krb5_config_get_bool_default(context,
+ NULL,
+ c->enable_kaserver,
+ "kdc", "enable-kaserver", NULL);
+
+
+ c->encode_as_rep_as_tgs_rep =
+ krb5_config_get_bool_default(context, NULL,
+ c->encode_as_rep_as_tgs_rep,
+ "kdc",
+ "encode_as_rep_as_tgs_rep", NULL);
+
+ c->kdc_warn_pwexpire =
+ krb5_config_get_time_default (context, NULL,
+ c->kdc_warn_pwexpire,
+ "kdc", "kdc_warn_pwexpire", NULL);
+
+
+#ifdef PKINIT
+ c->enable_pkinit =
+ krb5_config_get_bool_default(context,
+ NULL,
+ c->enable_pkinit,
+ "kdc",
+ "enable-pkinit",
+ NULL);
+ if (c->enable_pkinit) {
+ const char *user_id, *anchors, *ocsp_file;
+ char **pool_list, **revoke_list;
+
+ user_id =
+ krb5_config_get_string(context, NULL,
+ "kdc", "pkinit_identity", NULL);
+ if (user_id == NULL)
+ krb5_errx(context, 1, "pkinit enabled but no identity");
+
+ anchors = krb5_config_get_string(context, NULL,
+ "kdc", "pkinit_anchors", NULL);
+ if (anchors == NULL)
+ krb5_errx(context, 1, "pkinit enabled but no X509 anchors");
+
+ pool_list =
+ krb5_config_get_strings(context, NULL,
+ "kdc", "pkinit_pool", NULL);
+
+ revoke_list =
+ krb5_config_get_strings(context, NULL,
+ "kdc", "pkinit_revoke", NULL);
+
+ ocsp_file =
+ krb5_config_get_string(context, NULL,
+ "kdc", "pkinit_kdc_ocsp", NULL);
+ if (ocsp_file) {
+ c->pkinit_kdc_ocsp_file = strdup(ocsp_file);
+ if (c->pkinit_kdc_ocsp_file == NULL)
+ krb5_errx(context, 1, "out of memory");
+ }
+
+ _kdc_pk_initialize(context, c, user_id, anchors,
+ pool_list, revoke_list);
+
+ krb5_config_free_strings(pool_list);
+ krb5_config_free_strings(revoke_list);
+
+ c->pkinit_princ_in_cert =
+ krb5_config_get_bool_default(context, NULL,
+ c->pkinit_princ_in_cert,
+ "kdc",
+ "pkinit_principal_in_certificate",
+ NULL);
+
+ c->pkinit_require_binding =
+ krb5_config_get_bool_default(context, NULL,
+ c->pkinit_require_binding,
+ "kdc",
+ "pkinit_win2k_require_binding",
+ NULL);
+ }
+
+ c->pkinit_dh_min_bits =
+ krb5_config_get_int_default(context, NULL,
+ 0,
+ "kdc", "pkinit_dh_min_bits", NULL);
+
+#endif
+
+ *config = c;
+
+ return 0;
+}
diff --git a/kdc/digest.c b/kdc/digest.c
new file mode 100644
index 0000000000000..b845b0f9a894e
--- /dev/null
+++ b/kdc/digest.c
@@ -0,0 +1,1456 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <hex.h>
+
+RCSID("$Id: digest.c 22374 2007-12-28 18:36:52Z lha $");
+
+#define MS_CHAP_V2 0x20
+#define CHAP_MD5 0x10
+#define DIGEST_MD5 0x08
+#define NTLM_V2 0x04
+#define NTLM_V1_SESSION 0x02
+#define NTLM_V1 0x01
+
+const struct units _kdc_digestunits[] = {
+ {"ms-chap-v2", 1U << 5},
+ {"chap-md5", 1U << 4},
+ {"digest-md5", 1U << 3},
+ {"ntlm-v2", 1U << 2},
+ {"ntlm-v1-session", 1U << 1},
+ {"ntlm-v1", 1U << 0},
+ {NULL, 0}
+};
+
+
+static krb5_error_code
+get_digest_key(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *server,
+ krb5_crypto *crypto)
+{
+ krb5_error_code ret;
+ krb5_enctype enctype;
+ Key *key;
+
+ ret = _kdc_get_preferred_key(context,
+ config,
+ server,
+ "digest-service",
+ &enctype,
+ &key);
+ if (ret)
+ return ret;
+ return krb5_crypto_init(context, &key->key, 0, crypto);
+}
+
+/*
+ *
+ */
+
+static char *
+get_ntlm_targetname(krb5_context context,
+ hdb_entry_ex *client)
+{
+ char *targetname, *p;
+
+ targetname = strdup(krb5_principal_get_realm(context,
+ client->entry.principal));
+ if (targetname == NULL)
+ return NULL;
+
+ p = strchr(targetname, '.');
+ if (p)
+ *p = '\0';
+
+ strupr(targetname);
+ return targetname;
+}
+
+static krb5_error_code
+fill_targetinfo(krb5_context context,
+ char *targetname,
+ hdb_entry_ex *client,
+ krb5_data *data)
+{
+ struct ntlm_targetinfo ti;
+ krb5_error_code ret;
+ struct ntlm_buf d;
+ krb5_principal p;
+ const char *str;
+
+ memset(&ti, 0, sizeof(ti));
+
+ ti.domainname = targetname;
+ p = client->entry.principal;
+ str = krb5_principal_get_comp_string(context, p, 0);
+ if (str != NULL &&
+ (strcmp("host", str) == 0 ||
+ strcmp("ftp", str) == 0 ||
+ strcmp("imap", str) == 0 ||
+ strcmp("pop", str) == 0 ||
+ strcmp("smtp", str)))
+ {
+ str = krb5_principal_get_comp_string(context, p, 1);
+ ti.dnsservername = rk_UNCONST(str);
+ }
+
+ ret = heim_ntlm_encode_targetinfo(&ti, 1, &d);
+ if (ret)
+ return ret;
+
+ data->data = d.data;
+ data->length = d.length;
+
+ return 0;
+}
+
+
+static const unsigned char ms_chap_v2_magic1[39] = {
+ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+};
+static const unsigned char ms_chap_v2_magic2[41] = {
+ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E
+};
+static const unsigned char ms_rfc3079_magic1[27] = {
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+};
+
+/*
+ *
+ */
+
+static krb5_error_code
+get_password_entry(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *username,
+ char **password)
+{
+ krb5_principal clientprincipal;
+ krb5_error_code ret;
+ hdb_entry_ex *user;
+ HDB *db;
+
+ /* get username */
+ ret = krb5_parse_name(context, username, &clientprincipal);
+ if (ret)
+ return ret;
+
+ ret = _kdc_db_fetch(context, config, clientprincipal,
+ HDB_F_GET_CLIENT, &db, &user);
+ krb5_free_principal(context, clientprincipal);
+ if (ret)
+ return ret;
+
+ ret = hdb_entry_get_password(context, db, &user->entry, password);
+ if (ret || password == NULL) {
+ if (ret == 0) {
+ ret = EINVAL;
+ krb5_set_error_string(context, "password missing");
+ }
+ memset(user, 0, sizeof(*user));
+ }
+ _kdc_free_ent (context, user);
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_do_digest(krb5_context context,
+ krb5_kdc_configuration *config,
+ const DigestREQ *req, krb5_data *reply,
+ const char *from, struct sockaddr *addr)
+{
+ krb5_error_code ret = 0;
+ krb5_ticket *ticket = NULL;
+ krb5_auth_context ac = NULL;
+ krb5_keytab id = NULL;
+ krb5_crypto crypto = NULL;
+ DigestReqInner ireq;
+ DigestRepInner r;
+ DigestREP rep;
+ krb5_flags ap_req_options;
+ krb5_data buf;
+ size_t size;
+ krb5_storage *sp = NULL;
+ Checksum res;
+ hdb_entry_ex *server = NULL, *user = NULL;
+ hdb_entry_ex *client = NULL;
+ char *client_name = NULL, *password = NULL;
+ krb5_data serverNonce;
+
+ if(!config->enable_digest) {
+ kdc_log(context, config, 0,
+ "Rejected digest request (disabled) from %s", from);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(reply);
+ krb5_data_zero(&serverNonce);
+ memset(&ireq, 0, sizeof(ireq));
+ memset(&r, 0, sizeof(r));
+ memset(&rep, 0, sizeof(rep));
+
+ kdc_log(context, config, 0, "Digest request from %s", from);
+
+ ret = krb5_kt_resolve(context, "HDB:", &id);
+ if (ret) {
+ kdc_log(context, config, 0, "Can't open database for digest");
+ goto out;
+ }
+
+ ret = krb5_rd_req(context,
+ &ac,
+ &req->apReq,
+ NULL,
+ id,
+ &ap_req_options,
+ &ticket);
+ if (ret)
+ goto out;
+
+ /* check the server principal in the ticket matches digest/R@R */
+ {
+ krb5_principal principal = NULL;
+ const char *p, *r;
+
+ ret = krb5_ticket_get_server(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = EINVAL;
+ krb5_set_error_string(context, "Wrong digest server principal used");
+ p = krb5_principal_get_comp_string(context, principal, 0);
+ if (p == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ if (strcmp(p, KRB5_DIGEST_NAME) != 0) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+
+ p = krb5_principal_get_comp_string(context, principal, 1);
+ if (p == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ r = krb5_principal_get_realm(context, principal);
+ if (r == NULL) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ if (strcmp(p, r) != 0) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+ krb5_clear_error_string(context);
+
+ ret = _kdc_db_fetch(context, config, principal,
+ HDB_F_GET_SERVER, NULL, &server);
+ if (ret)
+ goto out;
+
+ krb5_free_principal(context, principal);
+ }
+
+ /* check the client is allowed to do digest auth */
+ {
+ krb5_principal principal = NULL;
+
+ ret = krb5_ticket_get_client(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, principal, &client_name);
+ if (ret) {
+ krb5_free_principal(context, principal);
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, principal,
+ HDB_F_GET_CLIENT, NULL, &client);
+ krb5_free_principal(context, principal);
+ if (ret)
+ goto out;
+
+ if (client->entry.flags.allow_digest == 0) {
+ kdc_log(context, config, 0,
+ "Client %s tried to use digest "
+ "but is not allowed to",
+ client_name);
+ krb5_set_error_string(context,
+ "Client is not permitted to use digest");
+ ret = KRB5KDC_ERR_POLICY;
+ goto out;
+ }
+ }
+
+ /* unpack request */
+ {
+ krb5_keyblock *key;
+
+ ret = krb5_auth_con_getremotesubkey(context, ac, &key);
+ if (ret)
+ goto out;
+ if (key == NULL) {
+ krb5_set_error_string(context, "digest: remote subkey not found");
+ ret = EINVAL;
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
+ &req->innerReq, &buf);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ ret = decode_DigestReqInner(buf.data, buf.length, &ireq, NULL);
+ krb5_data_free(&buf);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to decode digest inner request");
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "Valid digest request from %s (%s)",
+ client_name, from);
+
+ /*
+ * Process the inner request
+ */
+
+ switch (ireq.element) {
+ case choice_DigestReqInner_init: {
+ unsigned char server_nonce[16], identifier;
+
+ RAND_pseudo_bytes(&identifier, sizeof(identifier));
+ RAND_pseudo_bytes(server_nonce, sizeof(server_nonce));
+
+ server_nonce[0] = kdc_time & 0xff;
+ server_nonce[1] = (kdc_time >> 8) & 0xff;
+ server_nonce[2] = (kdc_time >> 16) & 0xff;
+ server_nonce[3] = (kdc_time >> 24) & 0xff;
+
+ r.element = choice_DigestRepInner_initReply;
+
+ hex_encode(server_nonce, sizeof(server_nonce), &r.u.initReply.nonce);
+ if (r.u.initReply.nonce == NULL) {
+ krb5_set_error_string(context, "Failed to decode server nonce");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+ ret = krb5_store_stringz(sp, ireq.u.init.type);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ if (ireq.u.init.channel) {
+ char *s;
+
+ asprintf(&s, "%s-%s:%s", r.u.initReply.nonce,
+ ireq.u.init.channel->cb_type,
+ ireq.u.init.channel->cb_binding);
+ if (s == NULL) {
+ krb5_set_error_string(context, "Failed to allocate "
+ "channel binding");
+ ret = ENOMEM;
+ goto out;
+ }
+ free(r.u.initReply.nonce);
+ r.u.initReply.nonce = s;
+ }
+
+ ret = krb5_store_stringz(sp, r.u.initReply.nonce);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ if (strcasecmp(ireq.u.init.type, "CHAP") == 0) {
+ r.u.initReply.identifier =
+ malloc(sizeof(*r.u.initReply.identifier));
+ if (r.u.initReply.identifier == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ asprintf(r.u.initReply.identifier, "%02X", identifier & 0xff);
+ if (*r.u.initReply.identifier == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ } else
+ r.u.initReply.identifier = NULL;
+
+ if (ireq.u.init.hostname) {
+ ret = krb5_store_stringz(sp, *ireq.u.init.hostname);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_create_checksum(context,
+ crypto,
+ KRB5_KU_DIGEST_OPAQUE,
+ 0,
+ buf.data,
+ buf.length,
+ &res);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(Checksum, buf.data, buf.length, &res, &size, ret);
+ free_Checksum(&res);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to encode "
+ "checksum in digest request");
+ goto out;
+ }
+ if (size != buf.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ hex_encode(buf.data, buf.length, &r.u.initReply.opaque);
+ free(buf.data);
+ if (r.u.initReply.opaque == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "Digest %s init request successful from %s",
+ ireq.u.init.type, from);
+
+ break;
+ }
+ case choice_DigestReqInner_digestRequest: {
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+ ret = krb5_store_stringz(sp, ireq.u.digestRequest.type);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ krb5_store_stringz(sp, ireq.u.digestRequest.serverNonce);
+
+ if (ireq.u.digestRequest.hostname) {
+ ret = krb5_store_stringz(sp, *ireq.u.digestRequest.hostname);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ buf.length = strlen(ireq.u.digestRequest.opaque);
+ buf.data = malloc(buf.length);
+ if (buf.data == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = hex_decode(ireq.u.digestRequest.opaque, buf.data, buf.length);
+ if (ret <= 0) {
+ krb5_set_error_string(context, "Failed to decode opaque");
+ ret = ENOMEM;
+ goto out;
+ }
+ buf.length = ret;
+
+ ret = decode_Checksum(buf.data, buf.length, &res, NULL);
+ free(buf.data);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to decode digest Checksum");
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ serverNonce.length = strlen(ireq.u.digestRequest.serverNonce);
+ serverNonce.data = malloc(serverNonce.length);
+ if (serverNonce.data == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * CHAP does the checksum of the raw nonce, but do it for all
+ * types, since we need to check the timestamp.
+ */
+ {
+ ssize_t ssize;
+
+ ssize = hex_decode(ireq.u.digestRequest.serverNonce,
+ serverNonce.data, serverNonce.length);
+ if (ssize <= 0) {
+ krb5_set_error_string(context, "Failed to decode serverNonce");
+ ret = ENOMEM;
+ goto out;
+ }
+ serverNonce.length = ssize;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_DIGEST_OPAQUE,
+ buf.data, buf.length, &res);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ /* verify time */
+ {
+ unsigned char *p = serverNonce.data;
+ uint32_t t;
+
+ if (serverNonce.length < 4) {
+ krb5_set_error_string(context, "server nonce too short");
+ ret = EINVAL;
+ goto out;
+ }
+ t = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
+
+ if (abs((kdc_time & 0xffffffff) - t) > context->max_skew) {
+ krb5_set_error_string(context, "time screw in server nonce ");
+ ret = EINVAL;
+ goto out;
+ }
+ }
+
+ if (strcasecmp(ireq.u.digestRequest.type, "CHAP") == 0) {
+ MD5_CTX ctx;
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char *mdx;
+ char id;
+
+ if ((config->digests_allowed & CHAP_MD5) == 0) {
+ kdc_log(context, config, 0, "Digest CHAP MD5 not allowed");
+ goto out;
+ }
+
+ if (ireq.u.digestRequest.identifier == NULL) {
+ krb5_set_error_string(context, "Identifier missing "
+ "from CHAP request");
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (hex_decode(*ireq.u.digestRequest.identifier, &id, 1) != 1) {
+ krb5_set_error_string(context, "failed to decode identifier");
+ ret = EINVAL;
+ goto out;
+ }
+
+ ret = get_password_entry(context, config,
+ ireq.u.digestRequest.username,
+ &password);
+ if (ret)
+ goto out;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, &id, 1);
+ MD5_Update(&ctx, password, strlen(password));
+ MD5_Update(&ctx, serverNonce.data, serverNonce.length);
+ MD5_Final(md, &ctx);
+
+ hex_encode(md, sizeof(md), &mdx);
+ if (mdx == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ r.element = choice_DigestRepInner_response;
+
+ ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
+ free(mdx);
+ if (ret == 0) {
+ r.u.response.success = TRUE;
+ } else {
+ kdc_log(context, config, 0,
+ "CHAP reply mismatch for %s",
+ ireq.u.digestRequest.username);
+ r.u.response.success = FALSE;
+ }
+
+ } else if (strcasecmp(ireq.u.digestRequest.type, "SASL-DIGEST-MD5") == 0) {
+ MD5_CTX ctx;
+ unsigned char md[MD5_DIGEST_LENGTH];
+ char *mdx;
+ char *A1, *A2;
+
+ if ((config->digests_allowed & DIGEST_MD5) == 0) {
+ kdc_log(context, config, 0, "Digest SASL MD5 not allowed");
+ goto out;
+ }
+
+ if (ireq.u.digestRequest.nonceCount == NULL)
+ goto out;
+ if (ireq.u.digestRequest.clientNonce == NULL)
+ goto out;
+ if (ireq.u.digestRequest.qop == NULL)
+ goto out;
+ if (ireq.u.digestRequest.realm == NULL)
+ goto out;
+
+ ret = get_password_entry(context, config,
+ ireq.u.digestRequest.username,
+ &password);
+ if (ret)
+ goto failed;
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, ireq.u.digestRequest.username,
+ strlen(ireq.u.digestRequest.username));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.realm,
+ strlen(*ireq.u.digestRequest.realm));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, password, strlen(password));
+ MD5_Final(md, &ctx);
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, md, sizeof(md));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
+ strlen(ireq.u.digestRequest.serverNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
+ strlen(*ireq.u.digestRequest.nonceCount));
+ if (ireq.u.digestRequest.authid) {
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.authid,
+ strlen(*ireq.u.digestRequest.authid));
+ }
+ MD5_Final(md, &ctx);
+ hex_encode(md, sizeof(md), &A1);
+ if (A1 == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto failed;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, "AUTHENTICATE:", sizeof("AUTHENTICATE:") - 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.uri,
+ strlen(*ireq.u.digestRequest.uri));
+
+ /* conf|int */
+ if (strcmp(ireq.u.digestRequest.digest, "clear") != 0) {
+ static char conf_zeros[] = ":00000000000000000000000000000000";
+ MD5_Update(&ctx, conf_zeros, sizeof(conf_zeros) - 1);
+ }
+
+ MD5_Final(md, &ctx);
+ hex_encode(md, sizeof(md), &A2);
+ if (A2 == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ free(A1);
+ goto failed;
+ }
+
+ MD5_Init(&ctx);
+ MD5_Update(&ctx, A1, strlen(A2));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, ireq.u.digestRequest.serverNonce,
+ strlen(ireq.u.digestRequest.serverNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.nonceCount,
+ strlen(*ireq.u.digestRequest.nonceCount));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.clientNonce,
+ strlen(*ireq.u.digestRequest.clientNonce));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, *ireq.u.digestRequest.qop,
+ strlen(*ireq.u.digestRequest.qop));
+ MD5_Update(&ctx, ":", 1);
+ MD5_Update(&ctx, A2, strlen(A2));
+
+ MD5_Final(md, &ctx);
+
+ free(A1);
+ free(A2);
+
+ hex_encode(md, sizeof(md), &mdx);
+ if (mdx == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ r.element = choice_DigestRepInner_response;
+ ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
+ free(mdx);
+ if (ret == 0) {
+ r.u.response.success = TRUE;
+ } else {
+ kdc_log(context, config, 0,
+ "DIGEST-MD5 reply mismatch for %s",
+ ireq.u.digestRequest.username);
+ r.u.response.success = FALSE;
+ }
+
+ } else if (strcasecmp(ireq.u.digestRequest.type, "MS-CHAP-V2") == 0) {
+ unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH];
+ krb5_principal clientprincipal = NULL;
+ char *mdx;
+ const char *username;
+ struct ntlm_buf answer;
+ Key *key = NULL;
+ SHA_CTX ctx;
+
+ if ((config->digests_allowed & MS_CHAP_V2) == 0) {
+ kdc_log(context, config, 0, "MS-CHAP-V2 not allowed");
+ goto failed;
+ }
+
+ if (ireq.u.digestRequest.clientNonce == NULL) {
+ krb5_set_error_string(context,
+ "MS-CHAP-V2 clientNonce missing");
+ ret = EINVAL;
+ goto failed;
+ }
+ if (serverNonce.length != 16) {
+ krb5_set_error_string(context,
+ "MS-CHAP-V2 serverNonce wrong length");
+ ret = EINVAL;
+ goto failed;
+ }
+
+ /* strip of the domain component */
+ username = strchr(ireq.u.digestRequest.username, '\\');
+ if (username == NULL)
+ username = ireq.u.digestRequest.username;
+ else
+ username++;
+
+ /* ChallangeHash */
+ SHA1_Init(&ctx);
+ {
+ ssize_t ssize;
+ krb5_data clientNonce;
+
+ clientNonce.length = strlen(*ireq.u.digestRequest.clientNonce);
+ clientNonce.data = malloc(clientNonce.length);
+ if (clientNonce.data == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+
+ ssize = hex_decode(*ireq.u.digestRequest.clientNonce,
+ clientNonce.data, clientNonce.length);
+ if (ssize != 16) {
+ krb5_set_error_string(context,
+ "Failed to decode clientNonce");
+ ret = ENOMEM;
+ goto out;
+ }
+ SHA1_Update(&ctx, clientNonce.data, ssize);
+ free(clientNonce.data);
+ }
+ SHA1_Update(&ctx, serverNonce.data, serverNonce.length);
+ SHA1_Update(&ctx, username, strlen(username));
+ SHA1_Final(challange, &ctx);
+
+ /* NtPasswordHash */
+ ret = krb5_parse_name(context, username, &clientprincipal);
+ if (ret)
+ goto failed;
+
+ ret = _kdc_db_fetch(context, config, clientprincipal,
+ HDB_F_GET_CLIENT, NULL, &user);
+ krb5_free_principal(context, clientprincipal);
+ if (ret) {
+ krb5_set_error_string(context,
+ "MS-CHAP-V2 user %s not in database",
+ username);
+ goto failed;
+ }
+
+ ret = hdb_enctype2key(context, &user->entry,
+ ETYPE_ARCFOUR_HMAC_MD5, &key);
+ if (ret) {
+ krb5_set_error_string(context,
+ "MS-CHAP-V2 missing arcfour key %s",
+ username);
+ goto failed;
+ }
+
+ /* ChallengeResponse */
+ ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
+ key->key.keyvalue.length,
+ challange, &answer);
+ if (ret) {
+ krb5_set_error_string(context, "NTLM missing arcfour key");
+ goto failed;
+ }
+
+ hex_encode(answer.data, answer.length, &mdx);
+ if (mdx == NULL) {
+ free(answer.data);
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ r.element = choice_DigestRepInner_response;
+ ret = strcasecmp(mdx, ireq.u.digestRequest.responseData);
+ if (ret == 0) {
+ r.u.response.success = TRUE;
+ } else {
+ kdc_log(context, config, 0,
+ "MS-CHAP-V2 hash mismatch for %s",
+ ireq.u.digestRequest.username);
+ r.u.response.success = FALSE;
+ }
+ free(mdx);
+
+ if (r.u.response.success) {
+ unsigned char hashhash[MD4_DIGEST_LENGTH];
+
+ /* hashhash */
+ {
+ MD4_CTX hctx;
+
+ MD4_Init(&hctx);
+ MD4_Update(&hctx, key->key.keyvalue.data,
+ key->key.keyvalue.length);
+ MD4_Final(hashhash, &hctx);
+ }
+
+ /* GenerateAuthenticatorResponse */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, hashhash, sizeof(hashhash));
+ SHA1_Update(&ctx, answer.data, answer.length);
+ SHA1_Update(&ctx, ms_chap_v2_magic1,sizeof(ms_chap_v2_magic1));
+ SHA1_Final(md, &ctx);
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, md, sizeof(md));
+ SHA1_Update(&ctx, challange, 8);
+ SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2));
+ SHA1_Final(md, &ctx);
+
+ r.u.response.rsp = calloc(1, sizeof(*r.u.response.rsp));
+ if (r.u.response.rsp == NULL) {
+ free(answer.data);
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ hex_encode(md, sizeof(md), r.u.response.rsp);
+ if (r.u.response.rsp == NULL) {
+ free(answer.data);
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /* get_master, rfc 3079 3.4 */
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, hashhash, 16); /* md4(hash) */
+ SHA1_Update(&ctx, answer.data, answer.length);
+ SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1));
+ SHA1_Final(md, &ctx);
+
+ free(answer.data);
+
+ r.u.response.session_key =
+ calloc(1, sizeof(*r.u.response.session_key));
+ if (r.u.response.session_key == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = krb5_data_copy(r.u.response.session_key, md, 16);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ }
+
+ } else {
+ r.element = choice_DigestRepInner_error;
+ asprintf(&r.u.error.reason, "Unsupported digest type %s",
+ ireq.u.digestRequest.type);
+ if (r.u.error.reason == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ r.u.error.code = EINVAL;
+ }
+
+ kdc_log(context, config, 0, "Digest %s request successful %s",
+ ireq.u.digestRequest.type, ireq.u.digestRequest.username);
+
+ break;
+ }
+ case choice_DigestReqInner_ntlmInit:
+
+ if ((config->digests_allowed & (NTLM_V1|NTLM_V1_SESSION|NTLM_V2)) == 0) {
+ kdc_log(context, config, 0, "NTLM not allowed");
+ goto failed;
+ }
+
+ r.element = choice_DigestRepInner_ntlmInitReply;
+
+ r.u.ntlmInitReply.flags = NTLM_NEG_UNICODE;
+
+ if ((ireq.u.ntlmInit.flags & NTLM_NEG_UNICODE) == 0) {
+ kdc_log(context, config, 0, "NTLM client have no unicode");
+ goto failed;
+ }
+
+ if (ireq.u.ntlmInit.flags & NTLM_NEG_NTLM)
+ r.u.ntlmInitReply.flags |= NTLM_NEG_NTLM;
+ else {
+ kdc_log(context, config, 0, "NTLM client doesn't support NTLM");
+ goto failed;
+ }
+
+ r.u.ntlmInitReply.flags |=
+ NTLM_NEG_TARGET |
+ NTLM_TARGET_DOMAIN |
+ NTLM_ENC_128;
+
+#define ALL \
+ NTLM_NEG_SIGN| \
+ NTLM_NEG_SEAL| \
+ NTLM_NEG_ALWAYS_SIGN| \
+ NTLM_NEG_NTLM2_SESSION| \
+ NTLM_NEG_KEYEX
+
+ r.u.ntlmInitReply.flags |= (ireq.u.ntlmInit.flags & (ALL));
+
+#undef ALL
+
+ r.u.ntlmInitReply.targetname =
+ get_ntlm_targetname(context, client);
+ if (r.u.ntlmInitReply.targetname == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ r.u.ntlmInitReply.challange.data = malloc(8);
+ if (r.u.ntlmInitReply.challange.data == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ r.u.ntlmInitReply.challange.length = 8;
+ if (RAND_bytes(r.u.ntlmInitReply.challange.data,
+ r.u.ntlmInitReply.challange.length) != 1)
+ {
+ krb5_set_error_string(context, "out of random error");
+ ret = ENOMEM;
+ goto out;
+ }
+ /* XXX fix targetinfo */
+ ALLOC(r.u.ntlmInitReply.targetinfo);
+ if (r.u.ntlmInitReply.targetinfo == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = fill_targetinfo(context,
+ r.u.ntlmInitReply.targetname,
+ client,
+ r.u.ntlmInitReply.targetinfo);
+ if (ret) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ /*
+ * Save data encryted in opaque for the second part of the
+ * ntlm authentication
+ */
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+
+ ret = krb5_storage_write(sp, r.u.ntlmInitReply.challange.data, 8);
+ if (ret != 8) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "storage write challange");
+ goto out;
+ }
+ ret = krb5_store_uint32(sp, r.u.ntlmInitReply.flags);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(sp, &buf);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_encrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
+ buf.data, buf.length, &r.u.ntlmInitReply.opaque);
+ krb5_data_free(&buf);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret)
+ goto out;
+
+ kdc_log(context, config, 0, "NTLM init from %s", from);
+
+ break;
+
+ case choice_DigestReqInner_ntlmRequest: {
+ krb5_principal clientprincipal;
+ unsigned char sessionkey[16];
+ unsigned char challange[8];
+ uint32_t flags;
+ Key *key = NULL;
+ int version;
+
+ r.element = choice_DigestRepInner_ntlmResponse;
+ r.u.ntlmResponse.success = 0;
+ r.u.ntlmResponse.flags = 0;
+ r.u.ntlmResponse.sessionkey = NULL;
+ r.u.ntlmResponse.tickets = NULL;
+
+ /* get username */
+ ret = krb5_parse_name(context,
+ ireq.u.ntlmRequest.username,
+ &clientprincipal);
+ if (ret)
+ goto failed;
+
+ ret = _kdc_db_fetch(context, config, clientprincipal,
+ HDB_F_GET_CLIENT, NULL, &user);
+ krb5_free_principal(context, clientprincipal);
+ if (ret) {
+ krb5_set_error_string(context, "NTLM user %s not in database",
+ ireq.u.ntlmRequest.username);
+ goto failed;
+ }
+
+ ret = get_digest_key(context, config, server, &crypto);
+ if (ret)
+ goto failed;
+
+ ret = krb5_decrypt(context, crypto, KRB5_KU_DIGEST_OPAQUE,
+ ireq.u.ntlmRequest.opaque.data,
+ ireq.u.ntlmRequest.opaque.length, &buf);
+ krb5_crypto_destroy(context, crypto);
+ crypto = NULL;
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed to decrypt nonce from %s", from);
+ goto failed;
+ }
+
+ sp = krb5_storage_from_data(&buf);
+ if (sp == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+
+ ret = krb5_storage_read(sp, challange, sizeof(challange));
+ if (ret != sizeof(challange)) {
+ krb5_set_error_string(context, "NTLM storage read challange");
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = krb5_ret_uint32(sp, &flags);
+ if (ret) {
+ krb5_set_error_string(context, "NTLM storage read flags");
+ goto out;
+ }
+ krb5_data_free(&buf);
+
+ if ((flags & NTLM_NEG_NTLM) == 0) {
+ ret = EINVAL;
+ krb5_set_error_string(context, "NTLM not negotiated");
+ goto out;
+ }
+
+ ret = hdb_enctype2key(context, &user->entry,
+ ETYPE_ARCFOUR_HMAC_MD5, &key);
+ if (ret) {
+ krb5_set_error_string(context, "NTLM missing arcfour key");
+ goto out;
+ }
+
+ /* check if this is NTLMv2 */
+ if (ireq.u.ntlmRequest.ntlm.length != 24) {
+ struct ntlm_buf infotarget, answer;
+ char *targetname;
+
+ if ((config->digests_allowed & NTLM_V2) == 0) {
+ kdc_log(context, config, 0, "NTLM v2 not allowed");
+ goto out;
+ }
+
+ version = 2;
+
+ targetname = get_ntlm_targetname(context, client);
+ if (targetname == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+
+ answer.length = ireq.u.ntlmRequest.ntlm.length;
+ answer.data = ireq.u.ntlmRequest.ntlm.data;
+
+ ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data,
+ key->key.keyvalue.length,
+ ireq.u.ntlmRequest.username,
+ targetname,
+ 0,
+ challange,
+ &answer,
+ &infotarget,
+ sessionkey);
+ free(targetname);
+ if (ret) {
+ krb5_set_error_string(context, "NTLM v2 verify failed");
+ goto failed;
+ }
+
+ /* XXX verify infotarget matches client (checksum ?) */
+
+ free(infotarget.data);
+ /* */
+
+ } else {
+ struct ntlm_buf answer;
+
+ version = 1;
+
+ if (flags & NTLM_NEG_NTLM2_SESSION) {
+ unsigned char sessionhash[MD5_DIGEST_LENGTH];
+ MD5_CTX md5ctx;
+
+ if ((config->digests_allowed & NTLM_V1_SESSION) == 0) {
+ kdc_log(context, config, 0, "NTLM v1-session not allowed");
+ ret = EINVAL;
+ goto failed;
+ }
+
+ if (ireq.u.ntlmRequest.lm.length != 24) {
+ krb5_set_error_string(context, "LM hash have wrong length "
+ "for NTLM session key");
+ ret = EINVAL;
+ goto failed;
+ }
+
+ MD5_Init(&md5ctx);
+ MD5_Update(&md5ctx, challange, sizeof(challange));
+ MD5_Update(&md5ctx, ireq.u.ntlmRequest.lm.data, 8);
+ MD5_Final(sessionhash, &md5ctx);
+ memcpy(challange, sessionhash, sizeof(challange));
+ } else {
+ if ((config->digests_allowed & NTLM_V1) == 0) {
+ kdc_log(context, config, 0, "NTLM v1 not allowed");
+ goto failed;
+ }
+ }
+
+ ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data,
+ key->key.keyvalue.length,
+ challange, &answer);
+ if (ret) {
+ krb5_set_error_string(context, "NTLM missing arcfour key");
+ goto failed;
+ }
+
+ if (ireq.u.ntlmRequest.ntlm.length != answer.length ||
+ memcmp(ireq.u.ntlmRequest.ntlm.data, answer.data, answer.length) != 0)
+ {
+ free(answer.data);
+ ret = EINVAL;
+ krb5_set_error_string(context, "NTLM hash mismatch");
+ goto failed;
+ }
+ free(answer.data);
+
+ {
+ MD4_CTX ctx;
+
+ MD4_Init(&ctx);
+ MD4_Update(&ctx,
+ key->key.keyvalue.data, key->key.keyvalue.length);
+ MD4_Final(sessionkey, &ctx);
+ }
+ }
+
+ if (ireq.u.ntlmRequest.sessionkey) {
+ unsigned char masterkey[MD4_DIGEST_LENGTH];
+ RC4_KEY rc4;
+ size_t len;
+
+ if ((flags & NTLM_NEG_KEYEX) == 0) {
+ krb5_set_error_string(context,
+ "NTLM client failed to neg key "
+ "exchange but still sent key");
+ ret = EINVAL;
+ goto failed;
+ }
+
+ len = ireq.u.ntlmRequest.sessionkey->length;
+ if (len != sizeof(masterkey)){
+ krb5_set_error_string(context,
+ "NTLM master key wrong length: %lu",
+ (unsigned long)len);
+ goto failed;
+ }
+
+ RC4_set_key(&rc4, sizeof(sessionkey), sessionkey);
+
+ RC4(&rc4, sizeof(masterkey),
+ ireq.u.ntlmRequest.sessionkey->data,
+ masterkey);
+ memset(&rc4, 0, sizeof(rc4));
+
+ r.u.ntlmResponse.sessionkey =
+ malloc(sizeof(*r.u.ntlmResponse.sessionkey));
+ if (r.u.ntlmResponse.sessionkey == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+
+ ret = krb5_data_copy(r.u.ntlmResponse.sessionkey,
+ masterkey, sizeof(masterkey));
+ if (ret) {
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+ }
+
+ r.u.ntlmResponse.success = 1;
+ kdc_log(context, config, 0, "NTLM version %d successful for %s",
+ version, ireq.u.ntlmRequest.username);
+ break;
+ }
+ case choice_DigestReqInner_supportedMechs:
+
+ kdc_log(context, config, 0, "digest supportedMechs from %s", from);
+
+ r.element = choice_DigestRepInner_supportedMechs;
+ memset(&r.u.supportedMechs, 0, sizeof(r.u.supportedMechs));
+
+ if (config->digests_allowed & NTLM_V1)
+ r.u.supportedMechs.ntlm_v1 = 1;
+ if (config->digests_allowed & NTLM_V1_SESSION)
+ r.u.supportedMechs.ntlm_v1_session = 1;
+ if (config->digests_allowed & NTLM_V2)
+ r.u.supportedMechs.ntlm_v2 = 1;
+ if (config->digests_allowed & DIGEST_MD5)
+ r.u.supportedMechs.digest_md5 = 1;
+ if (config->digests_allowed & CHAP_MD5)
+ r.u.supportedMechs.chap_md5 = 1;
+ if (config->digests_allowed & MS_CHAP_V2)
+ r.u.supportedMechs.ms_chap_v2 = 1;
+ break;
+
+ default: {
+ char *s;
+ krb5_set_error_string(context, "unknown operation to digest");
+ ret = EINVAL;
+
+ failed:
+
+ s = krb5_get_error_message(context, ret);
+ if (s == NULL) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "Digest failed with: %s", s);
+
+ r.element = choice_DigestRepInner_error;
+ r.u.error.reason = strdup("unknown error");
+ krb5_free_error_string(context, s);
+ if (r.u.error.reason == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ r.u.error.code = EINVAL;
+ break;
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(DigestRepInner, buf.data, buf.length, &r, &size, ret);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to encode inner digest reply");
+ goto out;
+ }
+ if (size != buf.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ krb5_auth_con_addflags(context, ac, KRB5_AUTH_CONTEXT_USE_SUBKEY, NULL);
+
+ ret = krb5_mk_rep (context, ac, &rep.apRep);
+ if (ret)
+ goto out;
+
+ {
+ krb5_keyblock *key;
+
+ ret = krb5_auth_con_getlocalsubkey(context, ac, &key);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ krb5_free_keyblock (context, key);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_encrypt_EncryptedData(context, crypto, KRB5_KU_DIGEST_ENCRYPT,
+ buf.data, buf.length, 0,
+ &rep.innerRep);
+
+ ASN1_MALLOC_ENCODE(DigestREP, reply->data, reply->length, &rep, &size, ret);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to encode digest reply");
+ goto out;
+ }
+ if (size != reply->length)
+ krb5_abortx(context, "ASN1 internal error");
+
+
+out:
+ if (ac)
+ krb5_auth_con_free(context, ac);
+ if (ret)
+ krb5_warn(context, ret, "Digest request from %s failed", from);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if (id)
+ krb5_kt_close(context, id);
+ if (crypto)
+ krb5_crypto_destroy(context, crypto);
+ if (sp)
+ krb5_storage_free(sp);
+ if (user)
+ _kdc_free_ent (context, user);
+ if (server)
+ _kdc_free_ent (context, server);
+ if (client)
+ _kdc_free_ent (context, client);
+ if (password) {
+ memset(password, 0, strlen(password));
+ free (password);
+ }
+ if (client_name)
+ free (client_name);
+ krb5_data_free(&buf);
+ krb5_data_free(&serverNonce);
+ free_DigestREP(&rep);
+ free_DigestRepInner(&r);
+ free_DigestReqInner(&ireq);
+
+ return ret;
+}
diff --git a/kdc/headers.h b/kdc/headers.h
new file mode 100644
index 0000000000000..64f6b6e438cb6
--- /dev/null
+++ b/kdc/headers.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: headers.h 19658 2007-01-04 00:15:34Z lha $
+ */
+
+#ifndef __HEADERS_H__
+#define __HEADERS_H__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#include <err.h>
+#include <roken.h>
+#include <getarg.h>
+#include <base64.h>
+#include <parse_units.h>
+#include <krb5.h>
+#include <krb5_locl.h>
+#include <digest_asn1.h>
+#include <kx509_asn1.h>
+#include <hdb.h>
+#include <hdb_err.h>
+#include <der.h>
+
+#include <heimntlm.h>
+#include <windc_plugin.h>
+
+#undef ALLOC
+#define ALLOC(X) ((X) = malloc(sizeof(*(X))))
+#undef ALLOC_SEQ
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); \
+(X)->val = calloc((X)->len, sizeof(*(X)->val)); } while(0)
+
+#endif /* __HEADERS_H__ */
diff --git a/kdc/hprop.8 b/kdc/hprop.8
new file mode 100644
index 0000000000000..99fc9784bd911
--- /dev/null
+++ b/kdc/hprop.8
@@ -0,0 +1,190 @@
+.\" Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: hprop.8 20456 2007-04-19 20:29:42Z lha $
+.\"
+.Dd December 8, 2004
+.Dt HPROP 8
+.Os HEIMDAL
+.Sh NAME
+.Nm hprop
+.Nd propagate the KDC database
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl m Ar file \*(Ba Xo
+.Fl -master-key= Ns Pa file
+.Xc
+.Oc
+.Oo Fl d Ar file \*(Ba Xo
+.Fl -database= Ns Pa file
+.Xc
+.Oc
+.Op Fl -source= Ns Ar heimdal|mit-dump|krb4-dump|kaserver
+.Oo Fl r Ar string \*(Ba Xo
+.Fl -v4-realm= Ns Ar string
+.Xc
+.Oc
+.Oo Fl c Ar cell \*(Ba Xo
+.Fl -cell= Ns Ar cell
+.Xc
+.Oc
+.Op Fl S | Fl -kaspecials
+.Oo Fl k Ar keytab \*(Ba Xo
+.Fl -keytab= Ns Ar keytab
+.Xc
+.Oc
+.Oo Fl R Ar string \*(Ba Xo
+.Fl -v5-realm= Ns Ar string
+.Xc
+.Oc
+.Op Fl D | Fl -decrypt
+.Op Fl E | Fl -encrypt
+.Op Fl n | Fl -stdout
+.Op Fl v | Fl -verbose
+.Op Fl -version
+.Op Fl h | Fl -help
+.Op Ar host Ns Op : Ns Ar port
+.Ar ...
+.Ek
+.Sh DESCRIPTION
+.Nm
+takes a principal database in a specified format and converts it into
+a stream of Heimdal database records. This stream can either be
+written to standard out, or (more commonly) be propagated to a
+.Xr hpropd 8
+server running on a different machine.
+.Pp
+If propagating, it connects to all
+.Ar hosts
+specified on the command by opening a TCP connection to port 754
+(service hprop) and sends the database in encrypted form.
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Xo
+.Fl m Ar file ,
+.Fl -master-key= Ns Pa file
+.Xc
+Where to find the master key to encrypt or decrypt keys with.
+.It Xo
+.Fl d Ar file ,
+.Fl -database= Ns Pa file
+.Xc
+The database to be propagated.
+.It Xo
+.Fl -source= Ns Ar heimdal|mit-dump|krb4-dump|kaserver
+.Xc
+Specifies the type of the source database. Alternatives include:
+.Pp
+.Bl -tag -width krb4-dump -compact -offset indent
+.It heimdal
+a Heimdal database
+.It mit-dump
+a MIT Kerberos 5 dump file
+.It krb4-dump
+a Kerberos 4 dump file
+.It kaserver
+an AFS kaserver database
+.El
+.It Xo
+.Fl k Ar keytab ,
+.Fl -keytab= Ns Ar keytab
+.Xc
+The keytab to use for fetching the key to be used for authenticating
+to the propagation daemon(s). The key
+.Pa kadmin/hprop
+is used from this keytab. The default is to fetch the key from the
+KDC database.
+.It Xo
+.Fl R Ar string ,
+.Fl -v5-realm= Ns Ar string
+.Xc
+Local realm override.
+.It Xo
+.Fl D ,
+.Fl -decrypt
+.Xc
+The encryption keys in the database can either be in clear, or
+encrypted with a master key. This option transmits the database with
+unencrypted keys.
+.It Xo
+.Fl E ,
+.Fl -encrypt
+.Xc
+This option transmits the database with encrypted keys.
+.It Xo
+.Fl n ,
+.Fl -stdout
+.Xc
+Dump the database on stdout, in a format that can be fed to hpropd.
+.El
+.Pp
+The following options are only valid if
+.Nm hprop
+is compiled with support for Kerberos 4 (kaserver).
+.Bl -tag -width Ds
+.It Xo
+.Fl r Ar string ,
+.Fl -v4-realm= Ns Ar string
+.Xc
+v4 realm to use.
+.It Xo
+.Fl c Ar cell ,
+.Fl -cell= Ns Ar cell
+.Xc
+The AFS cell name, used if reading a kaserver database.
+.It Xo
+.Fl S ,
+.Fl -kaspecials
+.Xc
+Also dump the principals marked as special in the kaserver database.
+.It Xo
+.Fl K ,
+.Fl -ka-db
+.Xc
+Deprecated, identical to
+.Sq --source=kaserver .
+.El
+.Sh EXAMPLES
+The following will propagate a database to another machine (which
+should run
+.Xr hpropd 8 ):
+.Bd -literal -offset indent
+$ hprop slave-1 slave-2
+.Ed
+.Pp
+Convert a Kerberos 4 dump-file for use with a Heimdal KDC:
+.Bd -literal -offset indent
+$ hprop -n --source=krb4-dump -d /var/kerberos/principal.dump --master-key=/.k | hpropd -n
+.Ed
+.Sh SEE ALSO
+.Xr hpropd 8
diff --git a/kdc/hprop.c b/kdc/hprop.c
new file mode 100644
index 0000000000000..e5b7fd11fb40d
--- /dev/null
+++ b/kdc/hprop.c
@@ -0,0 +1,807 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hprop.h"
+
+RCSID("$Id: hprop.c 21745 2007-07-31 16:11:25Z lha $");
+
+static int version_flag;
+static int help_flag;
+static const char *ktname = HPROP_KEYTAB;
+static const char *database;
+static char *mkeyfile;
+static int to_stdout;
+static int verbose_flag;
+static int encrypt_flag;
+static int decrypt_flag;
+static hdb_master_key mkey5;
+
+static char *source_type;
+
+static char *afs_cell;
+static char *v4_realm;
+
+static int kaspecials_flag;
+static int ka_use_null_salt;
+
+static char *local_realm=NULL;
+
+static int
+open_socket(krb5_context context, const char *hostname, const char *port)
+{
+ struct addrinfo *ai, *a;
+ struct addrinfo hints;
+ int error;
+
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ error = getaddrinfo (hostname, port, &hints, &ai);
+ if (error) {
+ warnx ("%s: %s", hostname, gai_strerror(error));
+ return -1;
+ }
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ int s;
+
+ s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
+ if (s < 0)
+ continue;
+ if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
+ warn ("connect(%s)", hostname);
+ close (s);
+ continue;
+ }
+ freeaddrinfo (ai);
+ return s;
+ }
+ warnx ("failed to contact %s", hostname);
+ freeaddrinfo (ai);
+ return -1;
+}
+
+krb5_error_code
+v5_prop(krb5_context context, HDB *db, hdb_entry_ex *entry, void *appdata)
+{
+ krb5_error_code ret;
+ struct prop_data *pd = appdata;
+ krb5_data data;
+
+ if(encrypt_flag) {
+ ret = hdb_seal_keys_mkey(context, &entry->entry, mkey5);
+ if (ret) {
+ krb5_warn(context, ret, "hdb_seal_keys_mkey");
+ return ret;
+ }
+ }
+ if(decrypt_flag) {
+ ret = hdb_unseal_keys_mkey(context, &entry->entry, mkey5);
+ if (ret) {
+ krb5_warn(context, ret, "hdb_unseal_keys_mkey");
+ return ret;
+ }
+ }
+
+ ret = hdb_entry2value(context, &entry->entry, &data);
+ if(ret) {
+ krb5_warn(context, ret, "hdb_entry2value");
+ return ret;
+ }
+
+ if(to_stdout)
+ ret = krb5_write_message(context, &pd->sock, &data);
+ else
+ ret = krb5_write_priv_message(context, pd->auth_context,
+ &pd->sock, &data);
+ krb5_data_free(&data);
+ return ret;
+}
+
+int
+v4_prop(void *arg, struct v4_principal *p)
+{
+ struct prop_data *pd = arg;
+ hdb_entry_ex ent;
+ krb5_error_code ret;
+
+ memset(&ent, 0, sizeof(ent));
+
+ ret = krb5_425_conv_principal(pd->context, p->name, p->instance, v4_realm,
+ &ent.entry.principal);
+ if(ret) {
+ krb5_warn(pd->context, ret,
+ "krb5_425_conv_principal %s.%s@%s",
+ p->name, p->instance, v4_realm);
+ return 0;
+ }
+
+ if(verbose_flag) {
+ char *s;
+ krb5_unparse_name_short(pd->context, ent.entry.principal, &s);
+ krb5_warnx(pd->context, "%s.%s -> %s", p->name, p->instance, s);
+ free(s);
+ }
+
+ ent.entry.kvno = p->kvno;
+ ent.entry.keys.len = 3;
+ ent.entry.keys.val = malloc(ent.entry.keys.len * sizeof(*ent.entry.keys.val));
+ if (ent.entry.keys.val == NULL)
+ krb5_errx(pd->context, ENOMEM, "malloc");
+ if(p->mkvno != -1) {
+ ent.entry.keys.val[0].mkvno = malloc (sizeof(*ent.entry.keys.val[0].mkvno));
+ if (ent.entry.keys.val[0].mkvno == NULL)
+ krb5_errx(pd->context, ENOMEM, "malloc");
+ *(ent.entry.keys.val[0].mkvno) = p->mkvno;
+ } else
+ ent.entry.keys.val[0].mkvno = NULL;
+ ent.entry.keys.val[0].salt = calloc(1, sizeof(*ent.entry.keys.val[0].salt));
+ if (ent.entry.keys.val[0].salt == NULL)
+ krb5_errx(pd->context, ENOMEM, "calloc");
+ ent.entry.keys.val[0].salt->type = KRB5_PADATA_PW_SALT;
+ ent.entry.keys.val[0].key.keytype = ETYPE_DES_CBC_MD5;
+ krb5_data_alloc(&ent.entry.keys.val[0].key.keyvalue, DES_KEY_SZ);
+ memcpy(ent.entry.keys.val[0].key.keyvalue.data, p->key, 8);
+
+ copy_Key(&ent.entry.keys.val[0], &ent.entry.keys.val[1]);
+ ent.entry.keys.val[1].key.keytype = ETYPE_DES_CBC_MD4;
+ copy_Key(&ent.entry.keys.val[0], &ent.entry.keys.val[2]);
+ ent.entry.keys.val[2].key.keytype = ETYPE_DES_CBC_CRC;
+
+ {
+ int life = _krb5_krb_life_to_time(0, p->max_life);
+ if(life == NEVERDATE){
+ ent.entry.max_life = NULL;
+ } else {
+ /* clean up lifetime a bit */
+ if(life > 86400)
+ life = (life + 86399) / 86400 * 86400;
+ else if(life > 3600)
+ life = (life + 3599) / 3600 * 3600;
+ ALLOC(ent.entry.max_life);
+ *ent.entry.max_life = life;
+ }
+ }
+
+ ALLOC(ent.entry.valid_end);
+ *ent.entry.valid_end = p->exp_date;
+
+ ret = krb5_make_principal(pd->context, &ent.entry.created_by.principal,
+ v4_realm,
+ "kadmin",
+ "hprop",
+ NULL);
+ if(ret){
+ krb5_warn(pd->context, ret, "krb5_make_principal");
+ ret = 0;
+ goto out;
+ }
+ ent.entry.created_by.time = time(NULL);
+ ALLOC(ent.entry.modified_by);
+ ret = krb5_425_conv_principal(pd->context, p->mod_name, p->mod_instance,
+ v4_realm, &ent.entry.modified_by->principal);
+ if(ret){
+ krb5_warn(pd->context, ret, "%s.%s@%s", p->name, p->instance, v4_realm);
+ ent.entry.modified_by->principal = NULL;
+ ret = 0;
+ goto out;
+ }
+ ent.entry.modified_by->time = p->mod_date;
+
+ ent.entry.flags.forwardable = 1;
+ ent.entry.flags.renewable = 1;
+ ent.entry.flags.proxiable = 1;
+ ent.entry.flags.postdate = 1;
+ ent.entry.flags.client = 1;
+ ent.entry.flags.server = 1;
+
+ /* special case password changing service */
+ if(strcmp(p->name, "changepw") == 0 &&
+ strcmp(p->instance, "kerberos") == 0) {
+ ent.entry.flags.forwardable = 0;
+ ent.entry.flags.renewable = 0;
+ ent.entry.flags.proxiable = 0;
+ ent.entry.flags.postdate = 0;
+ ent.entry.flags.initial = 1;
+ ent.entry.flags.change_pw = 1;
+ }
+
+ ret = v5_prop(pd->context, NULL, &ent, pd);
+
+ if (strcmp (p->name, "krbtgt") == 0
+ && strcmp (v4_realm, p->instance) != 0) {
+ krb5_free_principal (pd->context, ent.entry.principal);
+ ret = krb5_425_conv_principal (pd->context, p->name,
+ v4_realm, p->instance,
+ &ent.entry.principal);
+ if (ret == 0)
+ ret = v5_prop (pd->context, NULL, &ent, pd);
+ }
+
+ out:
+ hdb_free_entry(pd->context, &ent);
+ return ret;
+}
+
+#include "kadb.h"
+
+/* read a `ka_entry' from `fd' at offset `pos' */
+static void
+read_block(krb5_context context, int fd, int32_t pos, void *buf, size_t len)
+{
+ krb5_error_code ret;
+#ifdef HAVE_PREAD
+ if((ret = pread(fd, buf, len, 64 + pos)) < 0)
+ krb5_err(context, 1, errno, "pread(%u)", 64 + pos);
+#else
+ if(lseek(fd, 64 + pos, SEEK_SET) == (off_t)-1)
+ krb5_err(context, 1, errno, "lseek(%u)", 64 + pos);
+ ret = read(fd, buf, len);
+ if(ret < 0)
+ krb5_err(context, 1, errno, "read(%lu)", (unsigned long)len);
+#endif
+ if(ret != len)
+ krb5_errx(context, 1, "read(%lu) = %u", (unsigned long)len, ret);
+}
+
+static int
+ka_convert(struct prop_data *pd, int fd, struct ka_entry *ent)
+{
+ int32_t flags = ntohl(ent->flags);
+ krb5_error_code ret;
+ hdb_entry_ex hdb;
+
+ if(!kaspecials_flag
+ && (flags & KAFNORMAL) == 0) /* remove special entries */
+ return 0;
+ memset(&hdb, 0, sizeof(hdb));
+ ret = krb5_425_conv_principal(pd->context, ent->name, ent->instance,
+ v4_realm, &hdb.entry.principal);
+ if(ret) {
+ krb5_warn(pd->context, ret,
+ "krb5_425_conv_principal (%s.%s@%s)",
+ ent->name, ent->instance, v4_realm);
+ return 0;
+ }
+ hdb.entry.kvno = ntohl(ent->kvno);
+ hdb.entry.keys.len = 3;
+ hdb.entry.keys.val =
+ malloc(hdb.entry.keys.len * sizeof(*hdb.entry.keys.val));
+ if (hdb.entry.keys.val == NULL)
+ krb5_errx(pd->context, ENOMEM, "malloc");
+ hdb.entry.keys.val[0].mkvno = NULL;
+ hdb.entry.keys.val[0].salt = calloc(1, sizeof(*hdb.entry.keys.val[0].salt));
+ if (hdb.entry.keys.val[0].salt == NULL)
+ krb5_errx(pd->context, ENOMEM, "calloc");
+ if (ka_use_null_salt) {
+ hdb.entry.keys.val[0].salt->type = hdb_pw_salt;
+ hdb.entry.keys.val[0].salt->salt.data = NULL;
+ hdb.entry.keys.val[0].salt->salt.length = 0;
+ } else {
+ hdb.entry.keys.val[0].salt->type = hdb_afs3_salt;
+ hdb.entry.keys.val[0].salt->salt.data = strdup(afs_cell);
+ if (hdb.entry.keys.val[0].salt->salt.data == NULL)
+ krb5_errx(pd->context, ENOMEM, "strdup");
+ hdb.entry.keys.val[0].salt->salt.length = strlen(afs_cell);
+ }
+
+ hdb.entry.keys.val[0].key.keytype = ETYPE_DES_CBC_MD5;
+ krb5_data_copy(&hdb.entry.keys.val[0].key.keyvalue,
+ ent->key,
+ sizeof(ent->key));
+ copy_Key(&hdb.entry.keys.val[0], &hdb.entry.keys.val[1]);
+ hdb.entry.keys.val[1].key.keytype = ETYPE_DES_CBC_MD4;
+ copy_Key(&hdb.entry.keys.val[0], &hdb.entry.keys.val[2]);
+ hdb.entry.keys.val[2].key.keytype = ETYPE_DES_CBC_CRC;
+
+ ALLOC(hdb.entry.max_life);
+ *hdb.entry.max_life = ntohl(ent->max_life);
+
+ if(ntohl(ent->valid_end) != NEVERDATE && ntohl(ent->valid_end) != 0xffffffff) {
+ ALLOC(hdb.entry.valid_end);
+ *hdb.entry.valid_end = ntohl(ent->valid_end);
+ }
+
+ if (ntohl(ent->pw_change) != NEVERDATE &&
+ ent->pw_expire != 255 &&
+ ent->pw_expire != 0) {
+ ALLOC(hdb.entry.pw_end);
+ *hdb.entry.pw_end = ntohl(ent->pw_change)
+ + 24 * 60 * 60 * ent->pw_expire;
+ }
+
+ ret = krb5_make_principal(pd->context, &hdb.entry.created_by.principal,
+ v4_realm,
+ "kadmin",
+ "hprop",
+ NULL);
+ hdb.entry.created_by.time = time(NULL);
+
+ if(ent->mod_ptr){
+ struct ka_entry mod;
+ ALLOC(hdb.entry.modified_by);
+ read_block(pd->context, fd, ntohl(ent->mod_ptr), &mod, sizeof(mod));
+
+ krb5_425_conv_principal(pd->context, mod.name, mod.instance, v4_realm,
+ &hdb.entry.modified_by->principal);
+ hdb.entry.modified_by->time = ntohl(ent->mod_time);
+ memset(&mod, 0, sizeof(mod));
+ }
+
+ hdb.entry.flags.forwardable = 1;
+ hdb.entry.flags.renewable = 1;
+ hdb.entry.flags.proxiable = 1;
+ hdb.entry.flags.postdate = 1;
+ /* XXX - AFS 3.4a creates krbtgt.REALMOFCELL as NOTGS+NOSEAL */
+ if (strcmp(ent->name, "krbtgt") == 0 &&
+ (flags & (KAFNOTGS|KAFNOSEAL)) == (KAFNOTGS|KAFNOSEAL))
+ flags &= ~(KAFNOTGS|KAFNOSEAL);
+
+ hdb.entry.flags.client = (flags & KAFNOTGS) == 0;
+ hdb.entry.flags.server = (flags & KAFNOSEAL) == 0;
+
+ ret = v5_prop(pd->context, NULL, &hdb, pd);
+ hdb_free_entry(pd->context, &hdb);
+ return ret;
+}
+
+static int
+ka_dump(struct prop_data *pd, const char *file)
+{
+ struct ka_header header;
+ int i;
+ int fd = open(file, O_RDONLY);
+
+ if(fd < 0)
+ krb5_err(pd->context, 1, errno, "open(%s)", file);
+ read_block(pd->context, fd, 0, &header, sizeof(header));
+ if(header.version1 != header.version2)
+ krb5_errx(pd->context, 1, "Version mismatch in header: %ld/%ld",
+ (long)ntohl(header.version1), (long)ntohl(header.version2));
+ if(ntohl(header.version1) != 5)
+ krb5_errx(pd->context, 1, "Unknown database version %ld (expected 5)",
+ (long)ntohl(header.version1));
+ for(i = 0; i < ntohl(header.hashsize); i++){
+ int32_t pos = ntohl(header.hash[i]);
+ while(pos){
+ struct ka_entry ent;
+ read_block(pd->context, fd, pos, &ent, sizeof(ent));
+ ka_convert(pd, fd, &ent);
+ pos = ntohl(ent.next);
+ }
+ }
+ return 0;
+}
+
+
+
+struct getargs args[] = {
+ { "master-key", 'm', arg_string, &mkeyfile, "v5 master key file", "file" },
+ { "database", 'd', arg_string, &database, "database", "file" },
+ { "source", 0, arg_string, &source_type, "type of database to read",
+ "heimdal"
+ "|mit-dump"
+ "|krb4-dump"
+ "|kaserver"
+ },
+
+ { "v4-realm", 'r', arg_string, &v4_realm, "v4 realm to use" },
+ { "cell", 'c', arg_string, &afs_cell, "name of AFS cell" },
+ { "kaspecials", 'S', arg_flag, &kaspecials_flag, "dump KASPECIAL keys"},
+ { "keytab", 'k', arg_string, &ktname, "keytab to use for authentication", "keytab" },
+ { "v5-realm", 'R', arg_string, &local_realm, "v5 realm to use" },
+ { "decrypt", 'D', arg_flag, &decrypt_flag, "decrypt keys" },
+ { "encrypt", 'E', arg_flag, &encrypt_flag, "encrypt keys" },
+ { "stdout", 'n', arg_flag, &to_stdout, "dump to stdout" },
+ { "verbose", 'v', arg_flag, &verbose_flag },
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 'h', arg_flag, &help_flag }
+};
+
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int ret)
+{
+ arg_printusage (args, num_args, NULL, "[host[:port]] ...");
+ exit (ret);
+}
+
+static void
+get_creds(krb5_context context, krb5_ccache *cache)
+{
+ krb5_keytab keytab;
+ krb5_principal client;
+ krb5_error_code ret;
+ krb5_get_init_creds_opt *init_opts;
+ krb5_preauthtype preauth = KRB5_PADATA_ENC_TIMESTAMP;
+ krb5_creds creds;
+
+ ret = krb5_kt_register(context, &hdb_kt_ops);
+ if(ret) krb5_err(context, 1, ret, "krb5_kt_register");
+
+ ret = krb5_kt_resolve(context, ktname, &keytab);
+ if(ret) krb5_err(context, 1, ret, "krb5_kt_resolve");
+
+ ret = krb5_make_principal(context, &client, NULL,
+ "kadmin", HPROP_NAME, NULL);
+ if(ret) krb5_err(context, 1, ret, "krb5_make_principal");
+
+ ret = krb5_get_init_creds_opt_alloc(context, &init_opts);
+ if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
+ krb5_get_init_creds_opt_set_preauth_list(init_opts, &preauth, 1);
+
+ ret = krb5_get_init_creds_keytab(context, &creds, client, keytab, 0, NULL, init_opts);
+ if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds");
+
+ krb5_get_init_creds_opt_free(context, init_opts);
+
+ ret = krb5_kt_close(context, keytab);
+ if(ret) krb5_err(context, 1, ret, "krb5_kt_close");
+
+ ret = krb5_cc_gen_new(context, &krb5_mcc_ops, cache);
+ if(ret) krb5_err(context, 1, ret, "krb5_cc_gen_new");
+
+ ret = krb5_cc_initialize(context, *cache, client);
+ if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ krb5_free_principal(context, client);
+
+ ret = krb5_cc_store_cred(context, *cache, &creds);
+ if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred");
+
+ krb5_free_cred_contents(context, &creds);
+}
+
+enum hprop_source {
+ HPROP_HEIMDAL = 1,
+ HPROP_KRB4_DUMP,
+ HPROP_KASERVER,
+ HPROP_MIT_DUMP
+};
+
+#define IS_TYPE_V4(X) ((X) == HPROP_KRB4_DUMP || (X) == HPROP_KASERVER)
+
+struct {
+ int type;
+ const char *name;
+} types[] = {
+ { HPROP_HEIMDAL, "heimdal" },
+ { HPROP_KRB4_DUMP, "krb4-dump" },
+ { HPROP_KASERVER, "kaserver" },
+ { HPROP_MIT_DUMP, "mit-dump" }
+};
+
+static int
+parse_source_type(const char *s)
+{
+ int i;
+ for(i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
+ if(strstr(types[i].name, s) == types[i].name)
+ return types[i].type;
+ }
+ return 0;
+}
+
+static int
+iterate (krb5_context context,
+ const char *database_name,
+ HDB *db,
+ int type,
+ struct prop_data *pd)
+{
+ int ret;
+
+ switch(type) {
+ case HPROP_KRB4_DUMP:
+ ret = v4_prop_dump(pd, database_name);
+ if(ret)
+ krb5_warnx(context, "v4_prop_dump: %s",
+ krb5_get_err_text(context, ret));
+ break;
+ case HPROP_KASERVER:
+ ret = ka_dump(pd, database_name);
+ if(ret)
+ krb5_warn(context, ret, "ka_dump");
+ break;
+ case HPROP_MIT_DUMP:
+ ret = mit_prop_dump(pd, database_name);
+ if (ret)
+ krb5_warnx(context, "mit_prop_dump: %s",
+ krb5_get_err_text(context, ret));
+ break;
+ case HPROP_HEIMDAL:
+ ret = hdb_foreach(context, db, HDB_F_DECRYPT, v5_prop, pd);
+ if(ret)
+ krb5_warn(context, ret, "hdb_foreach");
+ break;
+ default:
+ krb5_errx(context, 1, "unknown prop type: %d", type);
+ }
+ return ret;
+}
+
+static int
+dump_database (krb5_context context, int type,
+ const char *database_name, HDB *db)
+{
+ krb5_error_code ret;
+ struct prop_data pd;
+ krb5_data data;
+
+ pd.context = context;
+ pd.auth_context = NULL;
+ pd.sock = STDOUT_FILENO;
+
+ ret = iterate (context, database_name, db, type, &pd);
+ if (ret)
+ krb5_errx(context, 1, "iterate failure");
+ krb5_data_zero (&data);
+ ret = krb5_write_message (context, &pd.sock, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_write_message");
+
+ return 0;
+}
+
+static int
+propagate_database (krb5_context context, int type,
+ const char *database_name,
+ HDB *db, krb5_ccache ccache,
+ int optidx, int argc, char **argv)
+{
+ krb5_principal server;
+ krb5_error_code ret;
+ int i, failed = 0;
+
+ for(i = optidx; i < argc; i++){
+ krb5_auth_context auth_context;
+ int fd;
+ struct prop_data pd;
+ krb5_data data;
+
+ char *port, portstr[NI_MAXSERV];
+ char *host = argv[i];
+
+ port = strchr(host, ':');
+ if(port == NULL) {
+ snprintf(portstr, sizeof(portstr), "%u",
+ ntohs(krb5_getportbyname (context, "hprop", "tcp",
+ HPROP_PORT)));
+ port = portstr;
+ } else
+ *port++ = '\0';
+
+ fd = open_socket(context, host, port);
+ if(fd < 0) {
+ failed++;
+ krb5_warn (context, errno, "connect %s", host);
+ continue;
+ }
+
+ ret = krb5_sname_to_principal(context, argv[i],
+ HPROP_NAME, KRB5_NT_SRV_HST, &server);
+ if(ret) {
+ failed++;
+ krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host);
+ close(fd);
+ continue;
+ }
+
+ if (local_realm) {
+ krb5_realm my_realm;
+ krb5_get_default_realm(context,&my_realm);
+
+ free (*krb5_princ_realm(context, server));
+ krb5_princ_set_realm(context,server,&my_realm);
+ }
+
+ auth_context = NULL;
+ ret = krb5_sendauth(context,
+ &auth_context,
+ &fd,
+ HPROP_VERSION,
+ NULL,
+ server,
+ AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
+ NULL, /* in_data */
+ NULL, /* in_creds */
+ ccache,
+ NULL,
+ NULL,
+ NULL);
+
+ krb5_free_principal(context, server);
+
+ if(ret) {
+ failed++;
+ krb5_warn(context, ret, "krb5_sendauth (%s)", host);
+ close(fd);
+ goto next_host;
+ }
+
+ pd.context = context;
+ pd.auth_context = auth_context;
+ pd.sock = fd;
+
+ ret = iterate (context, database_name, db, type, &pd);
+ if (ret) {
+ krb5_warnx(context, "iterate to host %s failed", host);
+ failed++;
+ goto next_host;
+ }
+
+ krb5_data_zero (&data);
+ ret = krb5_write_priv_message(context, auth_context, &fd, &data);
+ if(ret) {
+ krb5_warn(context, ret, "krb5_write_priv_message");
+ failed++;
+ goto next_host;
+ }
+
+ ret = krb5_read_priv_message(context, auth_context, &fd, &data);
+ if(ret) {
+ krb5_warn(context, ret, "krb5_read_priv_message: %s", host);
+ failed++;
+ goto next_host;
+ } else
+ krb5_data_free (&data);
+
+ next_host:
+ krb5_auth_con_free(context, auth_context);
+ close(fd);
+ }
+ if (failed)
+ return 1;
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache ccache = NULL;
+ HDB *db = NULL;
+ int optidx = 0;
+
+ int type, exit_code;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+
+ if(help_flag)
+ usage(0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ ret = krb5_init_context(&context);
+ if(ret)
+ exit(1);
+
+ if(local_realm)
+ krb5_set_default_realm(context, local_realm);
+
+ if(v4_realm == NULL) {
+ ret = krb5_get_default_realm(context, &v4_realm);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_get_default_realm");
+ }
+
+ if(afs_cell == NULL) {
+ afs_cell = strdup(v4_realm);
+ if(afs_cell == NULL)
+ krb5_errx(context, 1, "out of memory");
+ strlwr(afs_cell);
+ }
+
+
+ if(encrypt_flag && decrypt_flag)
+ krb5_errx(context, 1,
+ "only one of `--encrypt' and `--decrypt' is meaningful");
+
+ if(source_type != NULL) {
+ type = parse_source_type(source_type);
+ if(type == 0)
+ krb5_errx(context, 1, "unknown source type `%s'", source_type);
+ } else
+ type = HPROP_HEIMDAL;
+
+ if(!to_stdout)
+ get_creds(context, &ccache);
+
+ if(decrypt_flag || encrypt_flag) {
+ ret = hdb_read_master_key(context, mkeyfile, &mkey5);
+ if(ret && ret != ENOENT)
+ krb5_err(context, 1, ret, "hdb_read_master_key");
+ if(ret)
+ krb5_errx(context, 1, "No master key file found");
+ }
+
+ if (IS_TYPE_V4(type) && v4_realm == NULL)
+ krb5_errx(context, 1, "Its a Kerberos 4 database "
+ "but no realm configured");
+
+ switch(type) {
+ case HPROP_KASERVER:
+ if (database == NULL)
+ database = DEFAULT_DATABASE;
+ ka_use_null_salt = krb5_config_get_bool_default(context, NULL, FALSE,
+ "hprop",
+ "afs_uses_null_salt",
+ NULL);
+
+ break;
+ case HPROP_KRB4_DUMP:
+ if (database == NULL)
+ krb5_errx(context, 1, "no dump file specified");
+
+ break;
+ case HPROP_MIT_DUMP:
+ if (database == NULL)
+ krb5_errx(context, 1, "no dump file specified");
+ break;
+ case HPROP_HEIMDAL:
+ ret = hdb_create (context, &db, database);
+ if(ret)
+ krb5_err(context, 1, ret, "hdb_create: %s", database);
+ ret = db->hdb_open(context, db, O_RDONLY, 0);
+ if(ret)
+ krb5_err(context, 1, ret, "db->hdb_open");
+ break;
+ default:
+ krb5_errx(context, 1, "unknown dump type `%d'", type);
+ break;
+ }
+
+ if (to_stdout)
+ exit_code = dump_database (context, type, database, db);
+ else
+ exit_code = propagate_database (context, type, database,
+ db, ccache, optidx, argc, argv);
+
+ if(ccache != NULL)
+ krb5_cc_destroy(context, ccache);
+
+ if(db != NULL)
+ (*db->hdb_destroy)(context, db);
+
+ krb5_free_context(context);
+ return exit_code;
+}
diff --git a/kdc/hprop.h b/kdc/hprop.h
new file mode 100644
index 0000000000000..d43d04c215393
--- /dev/null
+++ b/kdc/hprop.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id: hprop.h 16378 2005-12-12 12:40:12Z lha $ */
+
+#ifndef __HPROP_H__
+#define __HPROP_H__
+
+#include "headers.h"
+
+struct prop_data{
+ krb5_context context;
+ krb5_auth_context auth_context;
+ int sock;
+};
+
+#define HPROP_VERSION "hprop-0.0"
+#define HPROP_NAME "hprop"
+#define HPROP_KEYTAB "HDB:"
+#define HPROP_PORT 754
+
+#ifndef NEVERDATE
+#define NEVERDATE ((1U << 31) - 1)
+#endif
+
+krb5_error_code v5_prop(krb5_context, HDB*, hdb_entry_ex*, void*);
+int mit_prop_dump(void*, const char*);
+
+struct v4_principal {
+ char name[64];
+ char instance[64];
+ DES_cblock key;
+ int kvno;
+ int mkvno;
+ time_t exp_date;
+ time_t mod_date;
+ char mod_name[64];
+ char mod_instance[64];
+ int max_life;
+};
+
+int v4_prop(void*, struct v4_principal*);
+int v4_prop_dump(void *arg, const char*);
+
+#endif /* __HPROP_H__ */
diff --git a/kdc/hpropd.8 b/kdc/hpropd.8
new file mode 100644
index 0000000000000..74a3dad816d74
--- /dev/null
+++ b/kdc/hpropd.8
@@ -0,0 +1,107 @@
+.\" Copyright (c) 1997, 2000 - 2003 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: hpropd.8 14381 2004-12-10 09:44:05Z lha $
+.\"
+.Dd August 27, 1997
+.Dt HPROPD 8
+.Os HEIMDAL
+.Sh NAME
+.Nm hpropd
+.Nd receive a propagated database
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl d Ar file \*(Ba Xo
+.Fl -database= Ns Ar file
+.Xc
+.Oc
+.Op Fl n | Fl -stdin
+.Op Fl -print
+.Op Fl i | Fl -no-inetd
+.Oo Fl k Ar keytab \*(Ba Xo
+.Fl -keytab= Ns Ar keytab
+.Xc
+.Oc
+.Op Fl 4 | Fl -v4dump
+.Ek
+.Sh DESCRIPTION
+.Nm
+receives a database sent by
+.Nm hprop .
+and writes it as a local database.
+.Pp
+By default,
+.Nm
+expects to be started from
+.Nm inetd
+if stdin is a socket and expects to receive the dumped database over
+stdin otherwise.
+If the database is sent over the network, it is authenticated and
+encrypted.
+Only connections authenticated with the principal
+.Nm kadmin Ns / Ns Nm hprop
+are accepted.
+.Pp
+Options supported:
+.Bl -tag -width Ds
+.It Xo
+.Fl d Ar file ,
+.Fl -database= Ns Ar file
+.Xc
+database
+.It Xo
+.Fl n ,
+.Fl -stdin
+.Xc
+read from stdin
+.It Xo
+.Fl -print
+.Xc
+print dump to stdout
+.It Xo
+.Fl i ,
+.Fl -no-inetd
+.Xc
+not started from inetd
+.It Xo
+.Fl k Ar keytab ,
+.Fl -keytab= Ns Ar keytab
+.Xc
+keytab to use for authentication
+.It Xo
+.Fl 4 ,
+.Fl -v4dump
+.Xc
+create v4 type DB
+.El
+.Sh SEE ALSO
+.Xr hprop 8
diff --git a/kdc/hpropd.c b/kdc/hpropd.c
new file mode 100644
index 0000000000000..12a9766572104
--- /dev/null
+++ b/kdc/hpropd.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hprop.h"
+
+RCSID("$Id: hpropd.c 22245 2007-12-08 23:48:52Z lha $");
+
+static int inetd_flag = -1;
+static int help_flag;
+static int version_flag;
+static int print_dump;
+static const char *database;
+static int from_stdin;
+static char *local_realm;
+static char *ktname = NULL;
+
+struct getargs args[] = {
+ { "database", 'd', arg_string, &database, "database", "file" },
+ { "stdin", 'n', arg_flag, &from_stdin, "read from stdin" },
+ { "print", 0, arg_flag, &print_dump, "print dump to stdout" },
+ { "inetd", 'i', arg_negative_flag, &inetd_flag,
+ "Not started from inetd" },
+ { "keytab", 'k', arg_string, &ktname, "keytab to use for authentication", "keytab" },
+ { "realm", 'r', arg_string, &local_realm, "realm to use" },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL}
+};
+
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int ret)
+{
+ arg_printusage (args, num_args, NULL, "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_auth_context ac = NULL;
+ krb5_principal c1, c2;
+ krb5_authenticator authent;
+ krb5_keytab keytab;
+ int fd;
+ HDB *db;
+ int optidx = 0;
+ char *tmp_db;
+ krb5_log_facility *fac;
+ int nprincs;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if(ret)
+ exit(1);
+
+ ret = krb5_openlog(context, "hpropd", &fac);
+ if(ret)
+ ;
+ krb5_set_warn_dest(context, fac);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+
+ if(local_realm != NULL)
+ krb5_set_default_realm(context, local_realm);
+
+ if(help_flag)
+ usage(0);
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 0)
+ usage(1);
+
+ if (database == NULL)
+ database = hdb_default_db(context);
+
+ if(from_stdin)
+ fd = STDIN_FILENO;
+ else {
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ socklen_t sin_len = sizeof(ss);
+ char addr_name[256];
+ krb5_ticket *ticket;
+ char *server;
+
+ fd = STDIN_FILENO;
+ if (inetd_flag == -1) {
+ if (getpeername (fd, sa, &sin_len) < 0)
+ inetd_flag = 0;
+ else
+ inetd_flag = 1;
+ }
+ if (!inetd_flag) {
+ mini_inetd (krb5_getportbyname (context, "hprop", "tcp",
+ HPROP_PORT));
+ }
+ sin_len = sizeof(ss);
+ if(getpeername(fd, sa, &sin_len) < 0)
+ krb5_err(context, 1, errno, "getpeername");
+
+ if (inet_ntop(sa->sa_family,
+ socket_get_address (sa),
+ addr_name,
+ sizeof(addr_name)) == NULL)
+ strlcpy (addr_name, "unknown address",
+ sizeof(addr_name));
+
+ krb5_log(context, fac, 0, "Connection from %s", addr_name);
+
+ ret = krb5_kt_register(context, &hdb_kt_ops);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_kt_register");
+
+ if (ktname != NULL) {
+ ret = krb5_kt_resolve(context, ktname, &keytab);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_kt_resolve %s", ktname);
+ } else {
+ ret = krb5_kt_default (context, &keytab);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_kt_default");
+ }
+
+ ret = krb5_recvauth(context, &ac, &fd, HPROP_VERSION, NULL,
+ 0, keytab, &ticket);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_recvauth");
+
+ ret = krb5_unparse_name(context, ticket->server, &server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+ if (strncmp(server, "hprop/", 5) != 0)
+ krb5_errx(context, 1, "ticket not for hprop (%s)", server);
+
+ free(server);
+ krb5_free_ticket (context, ticket);
+
+ ret = krb5_auth_con_getauthenticator(context, ac, &authent);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_auth_con_getauthenticator");
+
+ ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_make_principal");
+ _krb5_principalname2krb5_principal(context, &c2,
+ authent->cname, authent->crealm);
+ if(!krb5_principal_compare(context, c1, c2)) {
+ char *s;
+ ret = krb5_unparse_name(context, c2, &s);
+ if (ret)
+ s = "unparseable name";
+ krb5_errx(context, 1, "Unauthorized connection from %s", s);
+ }
+ krb5_free_principal(context, c1);
+ krb5_free_principal(context, c2);
+
+ ret = krb5_kt_close(context, keytab);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_kt_close");
+ }
+
+ if(!print_dump) {
+ asprintf(&tmp_db, "%s~", database);
+
+ ret = hdb_create(context, &db, tmp_db);
+ if(ret)
+ krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db);
+ ret = db->hdb_open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if(ret)
+ krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db);
+ }
+
+ nprincs = 0;
+ while(1){
+ krb5_data data;
+ hdb_entry_ex entry;
+
+ if(from_stdin) {
+ ret = krb5_read_message(context, &fd, &data);
+ if(ret != 0 && ret != HEIM_ERR_EOF)
+ krb5_err(context, 1, ret, "krb5_read_message");
+ } else {
+ ret = krb5_read_priv_message(context, ac, &fd, &data);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_read_priv_message");
+ }
+
+ if(ret == HEIM_ERR_EOF || data.length == 0) {
+ if(!from_stdin) {
+ data.data = NULL;
+ data.length = 0;
+ krb5_write_priv_message(context, ac, &fd, &data);
+ }
+ if(!print_dump) {
+ ret = db->hdb_rename(context, db, database);
+ if(ret)
+ krb5_err(context, 1, ret, "db_rename");
+ ret = db->hdb_close(context, db);
+ if(ret)
+ krb5_err(context, 1, ret, "db_close");
+ }
+ break;
+ }
+ memset(&entry, 0, sizeof(entry));
+ ret = hdb_value2entry(context, &data, &entry.entry);
+ krb5_data_free(&data);
+ if(ret)
+ krb5_err(context, 1, ret, "hdb_value2entry");
+ if(print_dump)
+ hdb_print_entry(context, db, &entry, stdout);
+ else {
+ ret = db->hdb_store(context, db, 0, &entry);
+ if(ret == HDB_ERR_EXISTS) {
+ char *s;
+ ret = krb5_unparse_name(context, entry.entry.principal, &s);
+ if (ret)
+ s = strdup("unparseable name");
+ krb5_warnx(context, "Entry exists: %s", s);
+ free(s);
+ } else if(ret)
+ krb5_err(context, 1, ret, "db_store");
+ else
+ nprincs++;
+ }
+ hdb_free_entry(context, &entry);
+ }
+ if (!print_dump)
+ krb5_log(context, fac, 0, "Received %d principals", nprincs);
+ exit(0);
+}
diff --git a/kdc/kadb.h b/kdc/kadb.h
new file mode 100644
index 0000000000000..4b59abe1cf970
--- /dev/null
+++ b/kdc/kadb.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id: kadb.h 7997 2000-03-03 12:36:26Z assar $ */
+
+#ifndef __kadb_h__
+#define __kadb_h__
+
+#define HASHSIZE 8191
+
+struct ka_header {
+ int32_t version1; /* file format version, should
+ match version2 */
+ int32_t size;
+ int32_t free_ptr;
+ int32_t eof_ptr;
+ int32_t kvno_ptr;
+ int32_t stats[8];
+ int32_t admin_accounts;
+ int32_t special_keys_version;
+ int32_t hashsize; /* allocated size of hash */
+ int32_t hash[HASHSIZE];
+ int32_t version2;
+};
+
+struct ka_entry {
+ int32_t flags; /* see below */
+ int32_t next; /* next in hash list */
+ int32_t valid_end; /* expiration date */
+ int32_t mod_time; /* time last modified */
+ int32_t mod_ptr; /* pointer to modifier */
+ int32_t pw_change; /* last pw change */
+ int32_t max_life; /* max ticket life */
+ int32_t kvno;
+ int32_t foo2[2]; /* huh? */
+ char name[64];
+ char instance[64];
+ char key[8];
+ u_char pw_expire; /* # days before password expires */
+ u_char spare;
+ u_char attempts;
+ u_char locktime;
+};
+
+#define KAFNORMAL (1<<0)
+#define KAFADMIN (1<<2) /* an administrator */
+#define KAFNOTGS (1<<3) /* ! allow principal to get or use TGT */
+#define KAFNOSEAL (1<<5) /* ! allow principal as server in GetTicket */
+#define KAFNOCPW (1<<6) /* ! allow principal to change its own key */
+#define KAFSPECIAL (1<<8) /* set if special AuthServer principal */
+
+#define DEFAULT_DATABASE "/usr/afs/db/kaserver.DB0"
+
+#endif /* __kadb_h__ */
diff --git a/kdc/kaserver.c b/kdc/kaserver.c
new file mode 100644
index 0000000000000..27f497ea6643c
--- /dev/null
+++ b/kdc/kaserver.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: kaserver.c 21654 2007-07-21 17:30:18Z lha $");
+
+#include <krb5-v4compat.h>
+#include <rx.h>
+
+#define KA_AUTHENTICATION_SERVICE 731
+#define KA_TICKET_GRANTING_SERVICE 732
+#define KA_MAINTENANCE_SERVICE 733
+
+#define AUTHENTICATE_OLD 1
+#define CHANGEPASSWORD 2
+#define GETTICKET_OLD 3
+#define SETPASSWORD 4
+#define SETFIELDS 5
+#define CREATEUSER 6
+#define DELETEUSER 7
+#define GETENTRY 8
+#define LISTENTRY 9
+#define GETSTATS 10
+#define DEBUG 11
+#define GETPASSWORD 12
+#define GETRANDOMKEY 13
+#define AUTHENTICATE 21
+#define AUTHENTICATE_V2 22
+#define GETTICKET 23
+
+/* XXX - Where do we get these? */
+
+#define RXGEN_OPCODE (-455)
+
+#define KADATABASEINCONSISTENT (180480L)
+#define KAEXIST (180481L)
+#define KAIO (180482L)
+#define KACREATEFAIL (180483L)
+#define KANOENT (180484L)
+#define KAEMPTY (180485L)
+#define KABADNAME (180486L)
+#define KABADINDEX (180487L)
+#define KANOAUTH (180488L)
+#define KAANSWERTOOLONG (180489L)
+#define KABADREQUEST (180490L)
+#define KAOLDINTERFACE (180491L)
+#define KABADARGUMENT (180492L)
+#define KABADCMD (180493L)
+#define KANOKEYS (180494L)
+#define KAREADPW (180495L)
+#define KABADKEY (180496L)
+#define KAUBIKINIT (180497L)
+#define KAUBIKCALL (180498L)
+#define KABADPROTOCOL (180499L)
+#define KANOCELLS (180500L)
+#define KANOCELL (180501L)
+#define KATOOMANYUBIKS (180502L)
+#define KATOOMANYKEYS (180503L)
+#define KABADTICKET (180504L)
+#define KAUNKNOWNKEY (180505L)
+#define KAKEYCACHEINVALID (180506L)
+#define KABADSERVER (180507L)
+#define KABADUSER (180508L)
+#define KABADCPW (180509L)
+#define KABADCREATE (180510L)
+#define KANOTICKET (180511L)
+#define KAASSOCUSER (180512L)
+#define KANOTSPECIAL (180513L)
+#define KACLOCKSKEW (180514L)
+#define KANORECURSE (180515L)
+#define KARXFAIL (180516L)
+#define KANULLPASSWORD (180517L)
+#define KAINTERNALERROR (180518L)
+#define KAPWEXPIRED (180519L)
+#define KAREUSED (180520L)
+#define KATOOSOON (180521L)
+#define KALOCKED (180522L)
+
+
+static krb5_error_code
+decode_rx_header (krb5_storage *sp,
+ struct rx_header *h)
+{
+ krb5_error_code ret;
+
+ ret = krb5_ret_uint32(sp, &h->epoch);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->connid);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->callid);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->seqno);
+ if (ret) return ret;
+ ret = krb5_ret_uint32(sp, &h->serialno);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->type);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->flags);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->status);
+ if (ret) return ret;
+ ret = krb5_ret_uint8(sp, &h->secindex);
+ if (ret) return ret;
+ ret = krb5_ret_uint16(sp, &h->reserved);
+ if (ret) return ret;
+ ret = krb5_ret_uint16(sp, &h->serviceid);
+ if (ret) return ret;
+
+ return 0;
+}
+
+static krb5_error_code
+encode_rx_header (struct rx_header *h,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+
+ ret = krb5_store_uint32(sp, h->epoch);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->connid);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->callid);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->seqno);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, h->serialno);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->type);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->flags);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->status);
+ if (ret) return ret;
+ ret = krb5_store_uint8(sp, h->secindex);
+ if (ret) return ret;
+ ret = krb5_store_uint16(sp, h->reserved);
+ if (ret) return ret;
+ ret = krb5_store_uint16(sp, h->serviceid);
+ if (ret) return ret;
+
+ return 0;
+}
+
+static void
+init_reply_header (struct rx_header *hdr,
+ struct rx_header *reply_hdr,
+ u_char type,
+ u_char flags)
+{
+ reply_hdr->epoch = hdr->epoch;
+ reply_hdr->connid = hdr->connid;
+ reply_hdr->callid = hdr->callid;
+ reply_hdr->seqno = 1;
+ reply_hdr->serialno = 1;
+ reply_hdr->type = type;
+ reply_hdr->flags = flags;
+ reply_hdr->status = 0;
+ reply_hdr->secindex = 0;
+ reply_hdr->reserved = 0;
+ reply_hdr->serviceid = hdr->serviceid;
+}
+
+/*
+ * Create an error `reply´ using for the packet `hdr' with the error
+ * `error´ code.
+ */
+static void
+make_error_reply (struct rx_header *hdr,
+ uint32_t error,
+ krb5_data *reply)
+
+{
+ struct rx_header reply_hdr;
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ init_reply_header (hdr, &reply_hdr, HT_ABORT, HF_LAST);
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return;
+ ret = encode_rx_header (&reply_hdr, sp);
+ if (ret)
+ return;
+ krb5_store_int32(sp, error);
+ krb5_storage_to_data (sp, reply);
+ krb5_storage_free (sp);
+}
+
+static krb5_error_code
+krb5_ret_xdr_data(krb5_storage *sp,
+ krb5_data *data)
+{
+ int ret;
+ int size;
+ ret = krb5_ret_int32(sp, &size);
+ if(ret)
+ return ret;
+ if(size < 0)
+ return ERANGE;
+ data->length = size;
+ if (size) {
+ u_char foo[4];
+ size_t pad = (4 - size % 4) % 4;
+
+ data->data = malloc(size);
+ if (data->data == NULL)
+ return ENOMEM;
+ ret = krb5_storage_read(sp, data->data, size);
+ if(ret != size)
+ return (ret < 0)? errno : KRB5_CC_END;
+ if (pad) {
+ ret = krb5_storage_read(sp, foo, pad);
+ if (ret != pad)
+ return (ret < 0)? errno : KRB5_CC_END;
+ }
+ } else
+ data->data = NULL;
+ return 0;
+}
+
+static krb5_error_code
+krb5_store_xdr_data(krb5_storage *sp,
+ krb5_data data)
+{
+ u_char zero[4] = {0, 0, 0, 0};
+ int ret;
+ size_t pad;
+
+ ret = krb5_store_int32(sp, data.length);
+ if(ret < 0)
+ return ret;
+ ret = krb5_storage_write(sp, data.data, data.length);
+ if(ret != data.length){
+ if(ret < 0)
+ return errno;
+ return KRB5_CC_END;
+ }
+ pad = (4 - data.length % 4) % 4;
+ if (pad) {
+ ret = krb5_storage_write(sp, zero, pad);
+ if (ret != pad) {
+ if (ret < 0)
+ return errno;
+ return KRB5_CC_END;
+ }
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+create_reply_ticket (krb5_context context,
+ struct rx_header *hdr,
+ Key *skey,
+ char *name, char *instance, char *realm,
+ struct sockaddr_in *addr,
+ int life,
+ int kvno,
+ int32_t max_seq_len,
+ const char *sname, const char *sinstance,
+ uint32_t challenge,
+ const char *label,
+ krb5_keyblock *key,
+ krb5_data *reply)
+{
+ krb5_error_code ret;
+ krb5_data ticket;
+ krb5_keyblock session;
+ krb5_storage *sp;
+ krb5_data enc_data;
+ struct rx_header reply_hdr;
+ char zero[8];
+ size_t pad;
+ unsigned fyrtiosjuelva;
+
+ /* create the ticket */
+
+ krb5_generate_random_keyblock(context, ETYPE_DES_PCBC_NONE, &session);
+
+ _krb5_krb_create_ticket(context,
+ 0,
+ name,
+ instance,
+ realm,
+ addr->sin_addr.s_addr,
+ &session,
+ life,
+ kdc_time,
+ sname,
+ sinstance,
+ &skey->key,
+ &ticket);
+
+ /* create the encrypted part of the reply */
+ sp = krb5_storage_emem ();
+ krb5_generate_random_block(&fyrtiosjuelva, sizeof(fyrtiosjuelva));
+ fyrtiosjuelva &= 0xffffffff;
+ krb5_store_int32 (sp, fyrtiosjuelva);
+ krb5_store_int32 (sp, challenge);
+ krb5_storage_write (sp, session.keyvalue.data, 8);
+ krb5_free_keyblock_contents(context, &session);
+ krb5_store_int32 (sp, kdc_time);
+ krb5_store_int32 (sp, kdc_time + _krb5_krb_life_to_time (0, life));
+ krb5_store_int32 (sp, kvno);
+ krb5_store_int32 (sp, ticket.length);
+ krb5_store_stringz (sp, name);
+ krb5_store_stringz (sp, instance);
+#if 1 /* XXX - Why shouldn't the realm go here? */
+ krb5_store_stringz (sp, "");
+#else
+ krb5_store_stringz (sp, realm);
+#endif
+ krb5_store_stringz (sp, sname);
+ krb5_store_stringz (sp, sinstance);
+ krb5_storage_write (sp, ticket.data, ticket.length);
+ krb5_storage_write (sp, label, strlen(label));
+
+ /* pad to DES block */
+ memset (zero, 0, sizeof(zero));
+ pad = (8 - krb5_storage_seek (sp, 0, SEEK_CUR) % 8) % 8;
+ krb5_storage_write (sp, zero, pad);
+
+ krb5_storage_to_data (sp, &enc_data);
+ krb5_storage_free (sp);
+
+ if (enc_data.length > max_seq_len) {
+ krb5_data_free (&enc_data);
+ make_error_reply (hdr, KAANSWERTOOLONG, reply);
+ return 0;
+ }
+
+ /* encrypt it */
+ {
+ DES_key_schedule schedule;
+ DES_cblock deskey;
+
+ memcpy (&deskey, key->keyvalue.data, sizeof(deskey));
+ DES_set_key (&deskey, &schedule);
+ DES_pcbc_encrypt (enc_data.data,
+ enc_data.data,
+ enc_data.length,
+ &schedule,
+ &deskey,
+ DES_ENCRYPT);
+ memset (&schedule, 0, sizeof(schedule));
+ memset (&deskey, 0, sizeof(deskey));
+ }
+
+ /* create the reply packet */
+ init_reply_header (hdr, &reply_hdr, HT_DATA, HF_LAST);
+ sp = krb5_storage_emem ();
+ ret = encode_rx_header (&reply_hdr, sp);
+ krb5_store_int32 (sp, max_seq_len);
+ krb5_store_xdr_data (sp, enc_data);
+ krb5_data_free (&enc_data);
+ krb5_storage_to_data (sp, reply);
+ krb5_storage_free (sp);
+ return 0;
+}
+
+static krb5_error_code
+unparse_auth_args (krb5_storage *sp,
+ char **name,
+ char **instance,
+ time_t *start_time,
+ time_t *end_time,
+ krb5_data *request,
+ int32_t *max_seq_len)
+{
+ krb5_data data;
+ int32_t tmp;
+
+ krb5_ret_xdr_data (sp, &data);
+ *name = malloc(data.length + 1);
+ if (*name == NULL)
+ return ENOMEM;
+ memcpy (*name, data.data, data.length);
+ (*name)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, &data);
+ *instance = malloc(data.length + 1);
+ if (*instance == NULL) {
+ free (*name);
+ return ENOMEM;
+ }
+ memcpy (*instance, data.data, data.length);
+ (*instance)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_int32 (sp, &tmp);
+ *start_time = tmp;
+ krb5_ret_int32 (sp, &tmp);
+ *end_time = tmp;
+ krb5_ret_xdr_data (sp, request);
+ krb5_ret_int32 (sp, max_seq_len);
+ /* ignore the rest */
+ return 0;
+}
+
+static void
+do_authenticate (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct rx_header *hdr,
+ krb5_storage *sp,
+ struct sockaddr_in *addr,
+ const char *from,
+ krb5_data *reply)
+{
+ krb5_error_code ret;
+ char *name = NULL;
+ char *instance = NULL;
+ time_t start_time;
+ time_t end_time;
+ krb5_data request;
+ int32_t max_seq_len;
+ hdb_entry_ex *client_entry = NULL;
+ hdb_entry_ex *server_entry = NULL;
+ Key *ckey = NULL;
+ Key *skey = NULL;
+ krb5_storage *reply_sp;
+ time_t max_life;
+ uint8_t life;
+ int32_t chal;
+ char client_name[256];
+ char server_name[256];
+
+ krb5_data_zero (&request);
+
+ ret = unparse_auth_args (sp, &name, &instance, &start_time, &end_time,
+ &request, &max_seq_len);
+ if (ret != 0 || request.length < 8) {
+ make_error_reply (hdr, KABADREQUEST, reply);
+ goto out;
+ }
+
+ snprintf (client_name, sizeof(client_name), "%s.%s@%s",
+ name, instance, config->v4_realm);
+ snprintf (server_name, sizeof(server_name), "%s.%s@%s",
+ "krbtgt", config->v4_realm, config->v4_realm);
+
+ kdc_log(context, config, 0, "AS-REQ (kaserver) %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch4 (context, config, name, instance,
+ config->v4_realm, HDB_F_GET_CLIENT,
+ &client_entry);
+ if (ret) {
+ kdc_log(context, config, 0, "Client not found in database: %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_db_fetch4 (context, config, "krbtgt",
+ config->v4_realm, config->v4_realm,
+ HDB_F_GET_KRBTGT, &server_entry);
+ if (ret) {
+ kdc_log(context, config, 0, "Server not found in database: %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client_entry, client_name,
+ server_entry, server_name,
+ TRUE);
+ if (ret) {
+ make_error_reply (hdr, KAPWEXPIRED, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for client");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for server");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ {
+ DES_cblock key;
+ DES_key_schedule schedule;
+
+ /* try to decode the `request' */
+ memcpy (&key, ckey->key.keyvalue.data, sizeof(key));
+ DES_set_key (&key, &schedule);
+ DES_pcbc_encrypt (request.data,
+ request.data,
+ request.length,
+ &schedule,
+ &key,
+ DES_DECRYPT);
+ memset (&schedule, 0, sizeof(schedule));
+ memset (&key, 0, sizeof(key));
+ }
+
+ /* check for the magic label */
+ if (memcmp ((char *)request.data + 4, "gTGS", 4) != 0) {
+ kdc_log(context, config, 0, "preauth failed for %s", client_name);
+ make_error_reply (hdr, KABADREQUEST, reply);
+ goto out;
+ }
+
+ reply_sp = krb5_storage_from_mem (request.data, 4);
+ krb5_ret_int32 (reply_sp, &chal);
+ krb5_storage_free (reply_sp);
+
+ if (abs(chal - kdc_time) > context->max_skew) {
+ make_error_reply (hdr, KACLOCKSKEW, reply);
+ goto out;
+ }
+
+ /* life */
+ max_life = end_time - kdc_time;
+ /* end_time - kdc_time can sometimes be non-positive due to slight
+ time skew between client and server. Let's make sure it is postive */
+ if(max_life < 1)
+ max_life = 1;
+ if (client_entry->entry.max_life)
+ max_life = min(max_life, *client_entry->entry.max_life);
+ if (server_entry->entry.max_life)
+ max_life = min(max_life, *server_entry->entry.max_life);
+
+ life = krb_time_to_life(kdc_time, kdc_time + max_life);
+
+ create_reply_ticket (context,
+ hdr, skey,
+ name, instance, config->v4_realm,
+ addr, life, server_entry->entry.kvno,
+ max_seq_len,
+ "krbtgt", config->v4_realm,
+ chal + 1, "tgsT",
+ &ckey->key, reply);
+
+ out:
+ if (request.length) {
+ memset (request.data, 0, request.length);
+ krb5_data_free (&request);
+ }
+ if (name)
+ free (name);
+ if (instance)
+ free (instance);
+ if (client_entry)
+ _kdc_free_ent (context, client_entry);
+ if (server_entry)
+ _kdc_free_ent (context, server_entry);
+}
+
+static krb5_error_code
+unparse_getticket_args (krb5_storage *sp,
+ int *kvno,
+ char **auth_domain,
+ krb5_data *ticket,
+ char **name,
+ char **instance,
+ krb5_data *times,
+ int32_t *max_seq_len)
+{
+ krb5_data data;
+ int32_t tmp;
+
+ krb5_ret_int32 (sp, &tmp);
+ *kvno = tmp;
+
+ krb5_ret_xdr_data (sp, &data);
+ *auth_domain = malloc(data.length + 1);
+ if (*auth_domain == NULL)
+ return ENOMEM;
+ memcpy (*auth_domain, data.data, data.length);
+ (*auth_domain)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, ticket);
+
+ krb5_ret_xdr_data (sp, &data);
+ *name = malloc(data.length + 1);
+ if (*name == NULL) {
+ free (*auth_domain);
+ return ENOMEM;
+ }
+ memcpy (*name, data.data, data.length);
+ (*name)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, &data);
+ *instance = malloc(data.length + 1);
+ if (*instance == NULL) {
+ free (*auth_domain);
+ free (*name);
+ return ENOMEM;
+ }
+ memcpy (*instance, data.data, data.length);
+ (*instance)[data.length] = '\0';
+ krb5_data_free (&data);
+
+ krb5_ret_xdr_data (sp, times);
+
+ krb5_ret_int32 (sp, max_seq_len);
+ /* ignore the rest */
+ return 0;
+}
+
+static void
+do_getticket (krb5_context context,
+ krb5_kdc_configuration *config,
+ struct rx_header *hdr,
+ krb5_storage *sp,
+ struct sockaddr_in *addr,
+ const char *from,
+ krb5_data *reply)
+{
+ krb5_error_code ret;
+ int kvno;
+ char *auth_domain = NULL;
+ krb5_data aticket;
+ char *name = NULL;
+ char *instance = NULL;
+ krb5_data times;
+ int32_t max_seq_len;
+ hdb_entry_ex *server_entry = NULL;
+ hdb_entry_ex *client_entry = NULL;
+ hdb_entry_ex *krbtgt_entry = NULL;
+ Key *kkey = NULL;
+ Key *skey = NULL;
+ DES_cblock key;
+ DES_key_schedule schedule;
+ DES_cblock session;
+ time_t max_life;
+ int8_t life;
+ time_t start_time, end_time;
+ char server_name[256];
+ char client_name[256];
+ struct _krb5_krb_auth_data ad;
+
+ krb5_data_zero (&aticket);
+ krb5_data_zero (&times);
+
+ memset(&ad, 0, sizeof(ad));
+
+ unparse_getticket_args (sp, &kvno, &auth_domain, &aticket,
+ &name, &instance, &times, &max_seq_len);
+ if (times.length < 8) {
+ make_error_reply (hdr, KABADREQUEST, reply);
+ goto out;
+
+ }
+
+ snprintf (server_name, sizeof(server_name),
+ "%s.%s@%s", name, instance, config->v4_realm);
+
+ ret = _kdc_db_fetch4 (context, config, name, instance,
+ config->v4_realm, HDB_F_GET_SERVER, &server_entry);
+ if (ret) {
+ kdc_log(context, config, 0, "Server not found in database: %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_db_fetch4 (context, config, "krbtgt",
+ config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &krbtgt_entry);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Server not found in database: %s.%s@%s: %s",
+ "krbtgt", config->v4_realm, config->v4_realm,
+ krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for krbtgt");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ /* find a DES key */
+ ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for server");
+ make_error_reply (hdr, KANOKEYS, reply);
+ goto out;
+ }
+
+ /* decrypt the incoming ticket */
+ memcpy (&key, kkey->key.keyvalue.data, sizeof(key));
+
+ /* unpack the ticket */
+ {
+ char *sname = NULL;
+ char *sinstance = NULL;
+
+ ret = _krb5_krb_decomp_ticket(context, &aticket, &kkey->key,
+ config->v4_realm, &sname,
+ &sinstance, &ad);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "kaserver: decomp failed for %s.%s with %d",
+ sname, sinstance, ret);
+ make_error_reply (hdr, KABADTICKET, reply);
+ goto out;
+ }
+
+ if (strcmp (sname, "krbtgt") != 0
+ || strcmp (sinstance, config->v4_realm) != 0) {
+ kdc_log(context, config, 0, "no TGT: %s.%s for %s.%s@%s",
+ sname, sinstance,
+ ad.pname, ad.pinst, ad.prealm);
+ make_error_reply (hdr, KABADTICKET, reply);
+ free(sname);
+ free(sinstance);
+ goto out;
+ }
+ free(sname);
+ free(sinstance);
+
+ if (kdc_time > _krb5_krb_life_to_time(ad.time_sec, ad.life)) {
+ kdc_log(context, config, 0, "TGT expired: %s.%s@%s",
+ ad.pname, ad.pinst, ad.prealm);
+ make_error_reply (hdr, KABADTICKET, reply);
+ goto out;
+ }
+ }
+
+ snprintf (client_name, sizeof(client_name),
+ "%s.%s@%s", ad.pname, ad.pinst, ad.prealm);
+
+ kdc_log(context, config, 0, "TGS-REQ (kaserver) %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch4 (context, config,
+ ad.pname, ad.pinst, ad.prealm, HDB_F_GET_CLIENT,
+ &client_entry);
+ if(ret && ret != HDB_ERR_NOENTRY) {
+ kdc_log(context, config, 0,
+ "Client not found in database: (krb4) %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+ if (client_entry == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
+ kdc_log(context, config, 0,
+ "Local client not found in database: (krb4) "
+ "%s", client_name);
+ make_error_reply (hdr, KANOENT, reply);
+ goto out;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client_entry, client_name,
+ server_entry, server_name,
+ FALSE);
+ if (ret) {
+ make_error_reply (hdr, KAPWEXPIRED, reply);
+ goto out;
+ }
+
+ /* decrypt the times */
+ memcpy(&session, ad.session.keyvalue.data, sizeof(session));
+ DES_set_key (&session, &schedule);
+ DES_ecb_encrypt (times.data,
+ times.data,
+ &schedule,
+ DES_DECRYPT);
+ memset (&schedule, 0, sizeof(schedule));
+ memset (&session, 0, sizeof(session));
+
+ /* and extract them */
+ {
+ krb5_storage *tsp;
+ int32_t tmp;
+
+ tsp = krb5_storage_from_mem (times.data, times.length);
+ krb5_ret_int32 (tsp, &tmp);
+ start_time = tmp;
+ krb5_ret_int32 (tsp, &tmp);
+ end_time = tmp;
+ krb5_storage_free (tsp);
+ }
+
+ /* life */
+ max_life = end_time - kdc_time;
+ /* end_time - kdc_time can sometimes be non-positive due to slight
+ time skew between client and server. Let's make sure it is postive */
+ if(max_life < 1)
+ max_life = 1;
+ if (krbtgt_entry->entry.max_life)
+ max_life = min(max_life, *krbtgt_entry->entry.max_life);
+ if (server_entry->entry.max_life)
+ max_life = min(max_life, *server_entry->entry.max_life);
+ /* if this is a cross realm request, the client_entry will likely
+ be NULL */
+ if (client_entry && client_entry->entry.max_life)
+ max_life = min(max_life, *client_entry->entry.max_life);
+
+ life = _krb5_krb_time_to_life(kdc_time, kdc_time + max_life);
+
+ create_reply_ticket (context,
+ hdr, skey,
+ ad.pname, ad.pinst, ad.prealm,
+ addr, life, server_entry->entry.kvno,
+ max_seq_len,
+ name, instance,
+ 0, "gtkt",
+ &ad.session, reply);
+
+ out:
+ _krb5_krb_free_auth_data(context, &ad);
+ if (aticket.length) {
+ memset (aticket.data, 0, aticket.length);
+ krb5_data_free (&aticket);
+ }
+ if (times.length) {
+ memset (times.data, 0, times.length);
+ krb5_data_free (&times);
+ }
+ if (auth_domain)
+ free (auth_domain);
+ if (name)
+ free (name);
+ if (instance)
+ free (instance);
+ if (krbtgt_entry)
+ _kdc_free_ent (context, krbtgt_entry);
+ if (server_entry)
+ _kdc_free_ent (context, server_entry);
+}
+
+krb5_error_code
+_kdc_do_kaserver(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr_in *addr)
+{
+ krb5_error_code ret = 0;
+ struct rx_header hdr;
+ uint32_t op;
+ krb5_storage *sp;
+
+ if (len < RX_HEADER_SIZE)
+ return -1;
+ sp = krb5_storage_from_mem (buf, len);
+
+ ret = decode_rx_header (sp, &hdr);
+ if (ret)
+ goto out;
+ buf += RX_HEADER_SIZE;
+ len -= RX_HEADER_SIZE;
+
+ switch (hdr.type) {
+ case HT_DATA :
+ break;
+ case HT_ACK :
+ case HT_BUSY :
+ case HT_ABORT :
+ case HT_ACKALL :
+ case HT_CHAL :
+ case HT_RESP :
+ case HT_DEBUG :
+ default:
+ /* drop */
+ goto out;
+ }
+
+
+ if (hdr.serviceid != KA_AUTHENTICATION_SERVICE
+ && hdr.serviceid != KA_TICKET_GRANTING_SERVICE) {
+ ret = -1;
+ goto out;
+ }
+
+ ret = krb5_ret_uint32(sp, &op);
+ if (ret)
+ goto out;
+ switch (op) {
+ case AUTHENTICATE :
+ case AUTHENTICATE_V2 :
+ do_authenticate (context, config, &hdr, sp, addr, from, reply);
+ break;
+ case GETTICKET :
+ do_getticket (context, config, &hdr, sp, addr, from, reply);
+ break;
+ case AUTHENTICATE_OLD :
+ case CHANGEPASSWORD :
+ case GETTICKET_OLD :
+ case SETPASSWORD :
+ case SETFIELDS :
+ case CREATEUSER :
+ case DELETEUSER :
+ case GETENTRY :
+ case LISTENTRY :
+ case GETSTATS :
+ case DEBUG :
+ case GETPASSWORD :
+ case GETRANDOMKEY :
+ default :
+ make_error_reply (&hdr, RXGEN_OPCODE, reply);
+ break;
+ }
+
+out:
+ krb5_storage_free (sp);
+ return ret;
+}
diff --git a/kdc/kdc-private.h b/kdc/kdc-private.h
new file mode 100644
index 0000000000000..030be9ae58bae
--- /dev/null
+++ b/kdc/kdc-private.h
@@ -0,0 +1,286 @@
+/* This is a generated file */
+#ifndef __kdc_private_h__
+#define __kdc_private_h__
+
+#include <stdarg.h>
+
+krb5_error_code
+_kdc_add_KRB5SignedPath (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ hdb_entry_ex */*krbtgt*/,
+ krb5_enctype /*enctype*/,
+ krb5_const_principal /*server*/,
+ KRB5SignedPathPrincipals */*principals*/,
+ EncTicketPart */*tkt*/);
+
+krb5_error_code
+_kdc_add_inital_verified_cas (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ pk_client_params */*params*/,
+ EncTicketPart */*tkt*/);
+
+krb5_error_code
+_kdc_as_rep (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ KDC_REQ */*req*/,
+ const krb5_data */*req_buffer*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr */*from_addr*/,
+ int /*datagram_reply*/);
+
+krb5_boolean
+_kdc_check_addresses (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ HostAddresses */*addresses*/,
+ const struct sockaddr */*from*/);
+
+krb5_error_code
+_kdc_check_flags (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ hdb_entry_ex */*client_ex*/,
+ const char */*client_name*/,
+ hdb_entry_ex */*server_ex*/,
+ const char */*server_name*/,
+ krb5_boolean /*is_as_req*/);
+
+krb5_error_code
+_kdc_db_fetch (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ krb5_const_principal /*principal*/,
+ unsigned /*flags*/,
+ HDB **/*db*/,
+ hdb_entry_ex **/*h*/);
+
+krb5_error_code
+_kdc_db_fetch4 (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const char */*name*/,
+ const char */*instance*/,
+ const char */*realm*/,
+ unsigned /*flags*/,
+ hdb_entry_ex **/*ent*/);
+
+krb5_error_code
+_kdc_do_524 (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const Ticket */*t*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr */*addr*/);
+
+krb5_error_code
+_kdc_do_digest (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const DigestREQ */*req*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr */*addr*/);
+
+krb5_error_code
+_kdc_do_kaserver (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ unsigned char */*buf*/,
+ size_t /*len*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr_in */*addr*/);
+
+krb5_error_code
+_kdc_do_kx509 (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const Kx509Request */*req*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr */*addr*/);
+
+krb5_error_code
+_kdc_do_version4 (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ unsigned char */*buf*/,
+ size_t /*len*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr_in */*addr*/);
+
+krb5_error_code
+_kdc_encode_reply (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ KDC_REP */*rep*/,
+ const EncTicketPart */*et*/,
+ EncKDCRepPart */*ek*/,
+ krb5_enctype /*etype*/,
+ int /*skvno*/,
+ const EncryptionKey */*skey*/,
+ int /*ckvno*/,
+ const EncryptionKey */*ckey*/,
+ const char **/*e_text*/,
+ krb5_data */*reply*/);
+
+krb5_error_code
+_kdc_encode_v4_ticket (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ void */*buf*/,
+ size_t /*len*/,
+ const EncTicketPart */*et*/,
+ const PrincipalName */*service*/,
+ size_t */*size*/);
+
+krb5_error_code
+_kdc_find_etype (
+ krb5_context /*context*/,
+ const hdb_entry_ex */*princ*/,
+ krb5_enctype */*etypes*/,
+ unsigned /*len*/,
+ Key **/*ret_key*/,
+ krb5_enctype */*ret_etype*/);
+
+const PA_DATA*
+_kdc_find_padata (
+ const KDC_REQ */*req*/,
+ int */*start*/,
+ int /*type*/);
+
+void
+_kdc_fix_time (time_t **/*t*/);
+
+void
+_kdc_free_ent (
+ krb5_context /*context*/,
+ hdb_entry_ex */*ent*/);
+
+krb5_error_code
+_kdc_get_des_key (
+ krb5_context /*context*/,
+ hdb_entry_ex */*principal*/,
+ krb5_boolean /*is_server*/,
+ krb5_boolean /*prefer_afs_key*/,
+ Key **/*ret_key*/);
+
+krb5_error_code
+_kdc_get_preferred_key (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ hdb_entry_ex */*h*/,
+ const char */*name*/,
+ krb5_enctype */*enctype*/,
+ Key **/*key*/);
+
+void
+_kdc_log_timestamp (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const char */*type*/,
+ KerberosTime /*authtime*/,
+ KerberosTime */*starttime*/,
+ KerberosTime /*endtime*/,
+ KerberosTime */*renew_till*/);
+
+krb5_error_code
+_kdc_make_anonymous_principalname (PrincipalName */*pn*/);
+
+int
+_kdc_maybe_version4 (
+ unsigned char */*buf*/,
+ int /*len*/);
+
+krb5_error_code
+_kdc_pac_generate (
+ krb5_context /*context*/,
+ hdb_entry_ex */*client*/,
+ krb5_pac */*pac*/);
+
+krb5_error_code
+_kdc_pac_verify (
+ krb5_context /*context*/,
+ const krb5_principal /*client_principal*/,
+ hdb_entry_ex */*client*/,
+ hdb_entry_ex */*server*/,
+ krb5_pac */*pac*/);
+
+krb5_error_code
+_kdc_pk_check_client (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const hdb_entry_ex */*client*/,
+ pk_client_params */*client_params*/,
+ char **/*subject_name*/);
+
+void
+_kdc_pk_free_client_param (
+ krb5_context /*context*/,
+ pk_client_params */*client_params*/);
+
+krb5_error_code
+_kdc_pk_initialize (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const char */*user_id*/,
+ const char */*anchors*/,
+ char **/*pool*/,
+ char **/*revoke_list*/);
+
+krb5_error_code
+_kdc_pk_mk_pa_reply (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ pk_client_params */*client_params*/,
+ const hdb_entry_ex */*client*/,
+ const KDC_REQ */*req*/,
+ const krb5_data */*req_buffer*/,
+ krb5_keyblock **/*reply_key*/,
+ METHOD_DATA */*md*/);
+
+krb5_error_code
+_kdc_pk_rd_padata (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ const KDC_REQ */*req*/,
+ const PA_DATA */*pa*/,
+ pk_client_params **/*ret_params*/);
+
+krb5_error_code
+_kdc_tgs_rep (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ KDC_REQ */*req*/,
+ krb5_data */*data*/,
+ const char */*from*/,
+ struct sockaddr */*from_addr*/,
+ int /*datagram_reply*/);
+
+krb5_error_code
+_kdc_tkt_add_if_relevant_ad (
+ krb5_context /*context*/,
+ EncTicketPart */*tkt*/,
+ int /*type*/,
+ const krb5_data */*data*/);
+
+krb5_error_code
+_kdc_try_kx509_request (
+ void */*ptr*/,
+ size_t /*len*/,
+ Kx509Request */*req*/,
+ size_t */*size*/);
+
+krb5_error_code
+_kdc_windc_client_access (
+ krb5_context /*context*/,
+ struct hdb_entry_ex */*client*/,
+ KDC_REQ */*req*/);
+
+#endif /* __kdc_private_h__ */
diff --git a/kdc/kdc-protos.h b/kdc/kdc-protos.h
new file mode 100644
index 0000000000000..15e8c29f4cb13
--- /dev/null
+++ b/kdc/kdc-protos.h
@@ -0,0 +1,92 @@
+/* This is a generated file */
+#ifndef __kdc_protos_h__
+#define __kdc_protos_h__
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void
+kdc_log (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ int /*level*/,
+ const char */*fmt*/,
+ ...);
+
+char*
+kdc_log_msg (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ int /*level*/,
+ const char */*fmt*/,
+ ...);
+
+char*
+kdc_log_msg_va (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ int /*level*/,
+ const char */*fmt*/,
+ va_list /*ap*/);
+
+void
+kdc_openlog (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/);
+
+krb5_error_code
+krb5_kdc_get_config (
+ krb5_context /*context*/,
+ krb5_kdc_configuration **/*config*/);
+
+int
+krb5_kdc_process_krb5_request (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ unsigned char */*buf*/,
+ size_t /*len*/,
+ krb5_data */*reply*/,
+ const char */*from*/,
+ struct sockaddr */*addr*/,
+ int /*datagram_reply*/);
+
+int
+krb5_kdc_process_request (
+ krb5_context /*context*/,
+ krb5_kdc_configuration */*config*/,
+ unsigned char */*buf*/,
+ size_t /*len*/,
+ krb5_data */*reply*/,
+ krb5_boolean */*prependlength*/,
+ const char */*from*/,
+ struct sockaddr */*addr*/,
+ int /*datagram_reply*/);
+
+int
+krb5_kdc_save_request (
+ krb5_context /*context*/,
+ const char */*fn*/,
+ const unsigned char */*buf*/,
+ size_t /*len*/,
+ const krb5_data */*reply*/,
+ const struct sockaddr */*sa*/);
+
+krb5_error_code
+krb5_kdc_set_dbinfo (
+ krb5_context /*context*/,
+ struct krb5_kdc_configuration */*c*/);
+
+void
+krb5_kdc_update_time (struct timeval */*tv*/);
+
+krb5_error_code
+krb5_kdc_windc_init (krb5_context /*context*/);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __kdc_protos_h__ */
diff --git a/kdc/kdc-replay.c b/kdc/kdc-replay.c
new file mode 100644
index 0000000000000..966831dca3f0d
--- /dev/null
+++ b/kdc/kdc-replay.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: kdc-replay.c 21945 2007-10-03 21:52:24Z lha $");
+
+static int version_flag;
+static int help_flag;
+
+struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag },
+ { "help", 'h', arg_flag, &help_flag }
+};
+
+const static int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int ret)
+{
+ arg_printusage (args, num_args, NULL, "kdc-request-log-file");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_kdc_configuration *config;
+ krb5_storage *sp;
+ int fd, optidx = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+
+ if(help_flag)
+ usage(0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed to parse configuration file");
+
+ ret = krb5_kdc_get_config(context, &config);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kdc_default_config");
+
+ kdc_openlog(context, config);
+
+ ret = krb5_kdc_set_dbinfo(context, config);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kdc_set_dbinfo");
+
+ if (argc != 2)
+ errx(1, "argc != 2");
+
+ printf("kdc replay\n");
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0)
+ err(1, "open: %s", argv[1]);
+
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ krb5_errx(context, 1, "krb5_storage_from_fd");
+
+ while(1) {
+ struct sockaddr_storage sa;
+ krb5_socklen_t salen = sizeof(sa);
+ struct timeval tv;
+ krb5_address a;
+ krb5_data d, r;
+ uint32_t t, clty, tag;
+ char astr[80];
+
+ ret = krb5_ret_uint32(sp, &t);
+ if (ret == HEIM_ERR_EOF)
+ break;
+ else if (ret)
+ krb5_errx(context, 1, "krb5_ret_uint32(version)");
+ if (t != 1)
+ krb5_errx(context, 1, "version not 1");
+ ret = krb5_ret_uint32(sp, &t);
+ if (ret)
+ krb5_errx(context, 1, "krb5_ret_uint32(time)");
+ ret = krb5_ret_address(sp, &a);
+ if (ret)
+ krb5_errx(context, 1, "krb5_ret_address");
+ ret = krb5_ret_data(sp, &d);
+ if (ret)
+ krb5_errx(context, 1, "krb5_ret_data");
+ ret = krb5_ret_uint32(sp, &clty);
+ if (ret)
+ krb5_errx(context, 1, "krb5_ret_uint32(class|type)");
+ ret = krb5_ret_uint32(sp, &tag);
+ if (ret)
+ krb5_errx(context, 1, "krb5_ret_uint32(tag)");
+
+
+ ret = krb5_addr2sockaddr (context, &a, (struct sockaddr *)&sa,
+ &salen, 88);
+ if (ret == KRB5_PROG_ATYPE_NOSUPP)
+ goto out;
+ else if (ret)
+ krb5_err(context, 1, ret, "krb5_addr2sockaddr");
+
+ ret = krb5_print_address(&a, astr, sizeof(astr), NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_print_address");
+
+ printf("processing request from %s, %lu bytes\n",
+ astr, (unsigned long)d.length);
+
+ r.length = 0;
+ r.data = NULL;
+
+ tv.tv_sec = t;
+ tv.tv_usec = 0;
+
+ krb5_kdc_update_time(&tv);
+ krb5_set_real_time(context, tv.tv_sec, 0);
+
+ ret = krb5_kdc_process_request(context, config, d.data, d.length,
+ &r, NULL, astr,
+ (struct sockaddr *)&sa, 0);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kdc_process_request");
+
+ if (r.length) {
+ Der_class cl;
+ Der_type ty;
+ unsigned int tag2;
+ ret = der_get_tag (r.data, r.length,
+ &cl, &ty, &tag2, NULL);
+ if (MAKE_TAG(cl, ty, 0) != clty)
+ krb5_errx(context, 1, "class|type mismatch: %d != %d",
+ (int)MAKE_TAG(cl, ty, 0), (int)clty);
+ if (tag != tag2)
+ krb5_errx(context, 1, "tag mismatch");
+
+ krb5_data_free(&r);
+ } else {
+ if (clty != 0xffffffff)
+ krb5_errx(context, 1, "clty not invalid");
+ if (tag != 0xffffffff)
+ krb5_errx(context, 1, "tag not invalid");
+ }
+
+ out:
+ krb5_data_free(&d);
+ krb5_free_address(context, &a);
+ }
+
+ krb5_storage_free(sp);
+ krb5_free_context(context);
+
+ printf("done\n");
+
+ return 0;
+}
diff --git a/kdc/kdc.8 b/kdc/kdc.8
new file mode 100644
index 0000000000000..331682f1cd6bf
--- /dev/null
+++ b/kdc/kdc.8
@@ -0,0 +1,262 @@
+.\" Copyright (c) 2003 - 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: kdc.8 18419 2006-10-12 10:05:57Z lha $
+.\"
+.Dd August 24, 2006
+.Dt KDC 8
+.Os HEIMDAL
+.Sh NAME
+.Nm kdc
+.Nd Kerberos 5 server
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl c Ar file \*(Ba Xo
+.Fl -config-file= Ns Ar file
+.Xc
+.Oc
+.Op Fl p | Fl -no-require-preauth
+.Op Fl -max-request= Ns Ar size
+.Op Fl H | Fl -enable-http
+.Op Fl -no-524
+.Op Fl -kerberos4
+.Op Fl -kerberos4-cross-realm
+.Oo Fl r Ar string \*(Ba Xo
+.Fl -v4-realm= Ns Ar string
+.Xc
+.Oc
+.Op Fl K | Fl -kaserver
+.Oo Fl P Ar portspec \*(Ba Xo
+.Fl -ports= Ns Ar portspec
+.Xc
+.Oc
+.Op Fl -detach
+.Op Fl -disable-DES
+.Op Fl -addresses= Ns Ar list of addresses
+.Ek
+.Sh DESCRIPTION
+.Nm
+serves requests for tickets.
+When it starts, it first checks the flags passed, any options that are
+not specified with a command line flag are taken from a config file,
+or from a default compiled-in value.
+.Pp
+Options supported:
+.Bl -tag -width Ds
+.It Xo
+.Fl c Ar file ,
+.Fl -config-file= Ns Ar file
+.Xc
+Specifies the location of the config file, the default is
+.Pa /var/heimdal/kdc.conf .
+This is the only value that can't be specified in the config file.
+.It Xo
+.Fl p ,
+.Fl -no-require-preauth
+.Xc
+Turn off the requirement for pre-autentication in the initial AS-REQ
+for all principals.
+The use of pre-authentication makes it more difficult to do offline
+password attacks.
+You might want to turn it off if you have clients
+that don't support pre-authentication.
+Since the version 4 protocol doesn't support any pre-authentication,
+serving version 4 clients is just about the same as not requiring
+pre-athentication.
+The default is to require pre-authentication.
+Adding the require-preauth per principal is a more flexible way of
+handling this.
+.It Xo
+.Fl -max-request= Ns Ar size
+.Xc
+Gives an upper limit on the size of the requests that the kdc is
+willing to handle.
+.It Xo
+.Fl H ,
+.Fl -enable-http
+.Xc
+Makes the kdc listen on port 80 and handle requests encapsulated in HTTP.
+.It Xo
+.Fl -no-524
+.Xc
+don't respond to 524 requests
+.It Xo
+.Fl -kerberos4
+.Xc
+respond to Kerberos 4 requests
+.It Xo
+.Fl -kerberos4-cross-realm
+.Xc
+respond to Kerberos 4 requests from foreign realms.
+This is a known security hole and should not be enabled unless you
+understand the consequences and are willing to live with them.
+.It Xo
+.Fl r Ar string ,
+.Fl -v4-realm= Ns Ar string
+.Xc
+What realm this server should act as when dealing with version 4
+requests.
+The database can contain any number of realms, but since the version 4
+protocol doesn't contain a realm for the server, it must be explicitly
+specified.
+The default is whatever is returned by
+.Fn krb_get_lrealm .
+This option is only availabe if the KDC has been compiled with version
+4 support.
+.It Xo
+.Fl K ,
+.Fl -kaserver
+.Xc
+Enable kaserver emulation (in case it's compiled in).
+.It Xo
+.Fl P Ar portspec ,
+.Fl -ports= Ns Ar portspec
+.Xc
+Specifies the set of ports the KDC should listen on.
+It is given as a
+white-space separated list of services or port numbers.
+.It Fl -addresses= Ns Ar list of addresses
+The list of addresses to listen for requests on.
+By default, the kdc will listen on all the locally configured
+addresses.
+If only a subset is desired, or the automatic detection fails, this
+option might be used.
+.It Fl -detach
+detach from pty and run as a daemon.
+.It Fl -disable-DES
+disable add des encryption types, makes the kdc not use them.
+.El
+.Pp
+All activities are logged to one or more destinations, see
+.Xr krb5.conf 5 ,
+and
+.Xr krb5_openlog 3 .
+The entity used for logging is
+.Nm kdc .
+.Sh CONFIGURATION FILE
+The configuration file has the same syntax as
+.Xr krb5.conf 5 ,
+but will be read before
+.Pa /etc/krb5.conf ,
+so it may override settings found there.
+Options specific to the KDC only are found in the
+.Dq [kdc]
+section.
+All the command-line options can preferably be added in the
+configuration file.
+The only difference is the pre-authentication flag, which has to be
+specified as:
+.Pp
+.Dl require-preauth = no
+.Pp
+(in fact you can specify the option as
+.Fl -require-preauth=no ) .
+.Pp
+And there are some configuration options which do not have
+command-line equivalents:
+.Bl -tag -width "xxx" -offset indent
+.It Li enable-digest = Va boolean
+turn on support for digest processing in the KDC.
+The default is FALSE.
+.It Li check-ticket-addresses = Va boolean
+Check the addresses in the ticket when processing TGS requests.
+The default is TRUE.
+.It Li allow-null-ticket-addresses = Va boolean
+Permit tickets with no addresses.
+This option is only relevant when check-ticket-addresses is TRUE.
+.It Li allow-anonymous = Va boolean
+Permit anonymous tickets with no addresses.
+.It Li max-kdc-datagram-reply-length = Va number
+Maximum packet size the UDP rely that the KDC will transmit, instead
+the KDC sends back a reply telling the client to use TCP instead.
+.It Li transited-policy = Xo
+.Li always-check \*(Ba
+.Li allow-per-principal |
+.Li always-honour-request
+.Xc
+This controls how KDC requests with the
+.Li disable-transited-check
+flag are handled. It can be one of:
+.Bl -tag -width "xxx" -offset indent
+.It Li always-check
+Always check transited encoding, this is the default.
+.It Li allow-per-principal
+Currently this is identical to
+.Li always-check .
+In a future release, it will be possible to mark a principal as able
+to handle unchecked requests.
+.It Li always-honour-request
+Always do what the client asked.
+In a future release, it will be possible to force a check per
+principal.
+.El
+.It encode_as_rep_as_tgs_rep = Va boolean
+Encode AS-Rep as TGS-Rep to be bug-compatible with old DCE code.
+The Heimdal clients allow both.
+.It kdc_warn_pwexpire = Va time
+How long before password/principal expiration the KDC should start
+sending out warning messages.
+.El
+.Pp
+The configuration file is only read when the
+.Nm
+is started.
+If changes made to the configuration file are to take effect, the
+.Nm
+needs to be restarted.
+.Pp
+An example of a config file:
+.Bd -literal -offset indent
+[kdc]
+ require-preauth = no
+ v4-realm = FOO.SE
+.Ed
+.Sh BUGS
+If the machine running the KDC has new addresses added to it, the KDC
+will have to be restarted to listen to them.
+The reason it doesn't just listen to wildcarded (like INADDR_ANY)
+addresses, is that the replies has to come from the same address they
+were sent to, and most OS:es doesn't pass this information to the
+application.
+If your normal mode of operation require that you add and remove
+addresses, the best option is probably to listen to a wildcarded TCP
+socket, and make sure your clients use TCP to connect.
+For instance, this will listen to IPv4 TCP port 88 only:
+.Bd -literal -offset indent
+kdc --addresses=0.0.0.0 --ports="88/tcp"
+.Ed
+.Pp
+There should be a way to specify protocol, port, and address triplets,
+not just addresses and protocol, port tuples.
+.Sh SEE ALSO
+.Xr kinit 1 ,
+.Xr krb5.conf 5
diff --git a/kdc/kdc.h b/kdc/kdc.h
new file mode 100644
index 0000000000000..6c129f38f520b
--- /dev/null
+++ b/kdc/kdc.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * Copyright (c) 2005 Andrew Bartlett <abartlet@samba.org>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: kdc.h 21287 2007-06-25 14:09:03Z lha $
+ */
+
+#ifndef __KDC_H__
+#define __KDC_H__
+
+#include <krb5.h>
+
+enum krb5_kdc_trpolicy {
+ TRPOLICY_ALWAYS_CHECK,
+ TRPOLICY_ALLOW_PER_PRINCIPAL,
+ TRPOLICY_ALWAYS_HONOUR_REQUEST
+};
+
+typedef struct krb5_kdc_configuration {
+ krb5_boolean require_preauth; /* require preauth for all principals */
+ time_t kdc_warn_pwexpire; /* time before expiration to print a warning */
+
+ struct HDB **db;
+ int num_db;
+
+ krb5_boolean encode_as_rep_as_tgs_rep; /* bug compatibility */
+
+ krb5_boolean check_ticket_addresses;
+ krb5_boolean allow_null_ticket_addresses;
+ krb5_boolean allow_anonymous;
+ enum krb5_kdc_trpolicy trpolicy;
+
+ char *v4_realm;
+ krb5_boolean enable_v4;
+ krb5_boolean enable_v4_cross_realm;
+ krb5_boolean enable_v4_per_principal;
+
+ krb5_boolean enable_kaserver;
+
+ krb5_boolean enable_524;
+
+ krb5_boolean enable_pkinit;
+ krb5_boolean pkinit_princ_in_cert;
+ char *pkinit_kdc_ocsp_file;
+ int pkinit_dh_min_bits;
+ int pkinit_require_binding;
+
+ krb5_log_facility *logf;
+
+ int enable_digest;
+ int digests_allowed;
+
+ size_t max_datagram_reply_length;
+
+ int enable_kx509;
+ const char *kx509_template;
+ const char *kx509_ca;
+
+} krb5_kdc_configuration;
+
+#include <kdc-protos.h>
+
+#endif
diff --git a/kdc/kdc_locl.h b/kdc/kdc_locl.h
new file mode 100644
index 0000000000000..fe0523665a4db
--- /dev/null
+++ b/kdc/kdc_locl.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id: kdc_locl.h 22247 2007-12-08 23:49:41Z lha $
+ */
+
+#ifndef __KDC_LOCL_H__
+#define __KDC_LOCL_H__
+
+#include "headers.h"
+#include "kdc.h"
+
+typedef struct pk_client_params pk_client_params;
+#include <kdc-private.h>
+
+extern sig_atomic_t exit_flag;
+extern size_t max_request;
+extern const char *request_log;
+extern const char *port_str;
+extern krb5_addresses explicit_addresses;
+
+extern int enable_http;
+
+#define DETACH_IS_DEFAULT FALSE
+
+extern int detach_from_console;
+
+extern const struct units _kdc_digestunits[];
+
+#define KDC_LOG_FILE "kdc.log"
+
+extern struct timeval _kdc_now;
+#define kdc_time (_kdc_now.tv_sec)
+
+void
+loop(krb5_context context, krb5_kdc_configuration *config);
+
+krb5_kdc_configuration *
+configure(krb5_context context, int argc, char **argv);
+
+#endif /* __KDC_LOCL_H__ */
diff --git a/kdc/kerberos4.c b/kdc/kerberos4.c
new file mode 100644
index 0000000000000..cbba64945b3e4
--- /dev/null
+++ b/kdc/kerberos4.c
@@ -0,0 +1,805 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+#include <krb5-v4compat.h>
+
+RCSID("$Id: kerberos4.c 21577 2007-07-16 08:14:06Z lha $");
+
+#ifndef swap32
+static uint32_t
+swap32(uint32_t x)
+{
+ return ((x << 24) & 0xff000000) |
+ ((x << 8) & 0xff0000) |
+ ((x >> 8) & 0xff00) |
+ ((x >> 24) & 0xff);
+}
+#endif /* swap32 */
+
+int
+_kdc_maybe_version4(unsigned char *buf, int len)
+{
+ return len > 0 && *buf == 4;
+}
+
+static void
+make_err_reply(krb5_context context, krb5_data *reply,
+ int code, const char *msg)
+{
+ _krb5_krb_cr_err_reply(context, "", "", "",
+ kdc_time, code, msg, reply);
+}
+
+struct valid_princ_ctx {
+ krb5_kdc_configuration *config;
+ unsigned flags;
+};
+
+static krb5_boolean
+valid_princ(krb5_context context,
+ void *funcctx,
+ krb5_principal princ)
+{
+ struct valid_princ_ctx *ctx = funcctx;
+ krb5_error_code ret;
+ char *s;
+ hdb_entry_ex *ent;
+
+ ret = krb5_unparse_name(context, princ, &s);
+ if (ret)
+ return FALSE;
+ ret = _kdc_db_fetch(context, ctx->config, princ, ctx->flags, NULL, &ent);
+ if (ret) {
+ kdc_log(context, ctx->config, 7, "Lookup %s failed: %s", s,
+ krb5_get_err_text (context, ret));
+ free(s);
+ return FALSE;
+ }
+ kdc_log(context, ctx->config, 7, "Lookup %s succeeded", s);
+ free(s);
+ _kdc_free_ent(context, ent);
+ return TRUE;
+}
+
+krb5_error_code
+_kdc_db_fetch4(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *name, const char *instance, const char *realm,
+ unsigned flags,
+ hdb_entry_ex **ent)
+{
+ krb5_principal p;
+ krb5_error_code ret;
+ struct valid_princ_ctx ctx;
+
+ ctx.config = config;
+ ctx.flags = flags;
+
+ ret = krb5_425_conv_principal_ext2(context, name, instance, realm,
+ valid_princ, &ctx, 0, &p);
+ if(ret)
+ return ret;
+ ret = _kdc_db_fetch(context, config, p, flags, NULL, ent);
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+#define RCHECK(X, L) if(X){make_err_reply(context, reply, KFAILURE, "Packet too short"); goto L;}
+
+/*
+ * Process the v4 request in `buf, len' (received from `addr'
+ * (with string `from').
+ * Return an error code and a reply in `reply'.
+ */
+
+krb5_error_code
+_kdc_do_version4(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr_in *addr)
+{
+ krb5_storage *sp;
+ krb5_error_code ret;
+ hdb_entry_ex *client = NULL, *server = NULL;
+ Key *ckey, *skey;
+ int8_t pvno;
+ int8_t msg_type;
+ int lsb;
+ char *name = NULL, *inst = NULL, *realm = NULL;
+ char *sname = NULL, *sinst = NULL;
+ int32_t req_time;
+ time_t max_life;
+ uint8_t life;
+ char client_name[256];
+ char server_name[256];
+
+ if(!config->enable_v4) {
+ kdc_log(context, config, 0,
+ "Rejected version 4 request from %s", from);
+ make_err_reply(context, reply, KRB4ET_KDC_GEN_ERR,
+ "Function not enabled");
+ return 0;
+ }
+
+ sp = krb5_storage_from_mem(buf, len);
+ RCHECK(krb5_ret_int8(sp, &pvno), out);
+ if(pvno != 4){
+ kdc_log(context, config, 0,
+ "Protocol version mismatch (krb4) (%d)", pvno);
+ make_err_reply(context, reply, KRB4ET_KDC_PKT_VER, "protocol mismatch");
+ goto out;
+ }
+ RCHECK(krb5_ret_int8(sp, &msg_type), out);
+ lsb = msg_type & 1;
+ msg_type &= ~1;
+ switch(msg_type){
+ case AUTH_MSG_KDC_REQUEST: {
+ krb5_data ticket, cipher;
+ krb5_keyblock session;
+
+ krb5_data_zero(&ticket);
+ krb5_data_zero(&cipher);
+
+ RCHECK(krb5_ret_stringz(sp, &name), out1);
+ RCHECK(krb5_ret_stringz(sp, &inst), out1);
+ RCHECK(krb5_ret_stringz(sp, &realm), out1);
+ RCHECK(krb5_ret_int32(sp, &req_time), out1);
+ if(lsb)
+ req_time = swap32(req_time);
+ RCHECK(krb5_ret_uint8(sp, &life), out1);
+ RCHECK(krb5_ret_stringz(sp, &sname), out1);
+ RCHECK(krb5_ret_stringz(sp, &sinst), out1);
+ snprintf (client_name, sizeof(client_name),
+ "%s.%s@%s", name, inst, realm);
+ snprintf (server_name, sizeof(server_name),
+ "%s.%s@%s", sname, sinst, config->v4_realm);
+
+ kdc_log(context, config, 0, "AS-REQ (krb4) %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch4(context, config, name, inst, realm,
+ HDB_F_GET_CLIENT, &client);
+ if(ret) {
+ kdc_log(context, config, 0, "Client not found in database: %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "principal unknown");
+ goto out1;
+ }
+ ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm,
+ HDB_F_GET_SERVER, &server);
+ if(ret){
+ kdc_log(context, config, 0, "Server not found in database: %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "principal unknown");
+ goto out1;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client, client_name,
+ server, server_name,
+ TRUE);
+ if (ret) {
+ /* good error code? */
+ make_err_reply(context, reply, KRB4ET_KDC_NAME_EXP,
+ "operation not allowed");
+ goto out1;
+ }
+
+ if (config->enable_v4_per_principal &&
+ client->entry.flags.allow_kerberos4 == 0)
+ {
+ kdc_log(context, config, 0,
+ "Per principal Kerberos 4 flag not turned on for %s",
+ client_name);
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "allow kerberos4 flag required");
+ goto out1;
+ }
+
+ /*
+ * There's no way to do pre-authentication in v4 and thus no
+ * good error code to return if preauthentication is required.
+ */
+
+ if (config->require_preauth
+ || client->entry.flags.require_preauth
+ || server->entry.flags.require_preauth) {
+ kdc_log(context, config, 0,
+ "Pre-authentication required for v4-request: "
+ "%s for %s",
+ client_name, server_name);
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "preauth required");
+ goto out1;
+ }
+
+ ret = _kdc_get_des_key(context, client, FALSE, FALSE, &ckey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for client");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for client");
+ goto out1;
+ }
+
+#if 0
+ /* this is not necessary with the new code in libkrb */
+ /* find a properly salted key */
+ while(ckey->salt == NULL || ckey->salt->salt.length != 0)
+ ret = hdb_next_keytype2key(context, &client->entry, KEYTYPE_DES, &ckey);
+ if(ret){
+ kdc_log(context, config, 0, "No version-4 salted key in database -- %s.%s@%s",
+ name, inst, realm);
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "No version-4 salted key in database");
+ goto out1;
+ }
+#endif
+
+ ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
+ if(ret){
+ kdc_log(context, config, 0, "no suitable DES key for server");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for server");
+ goto out1;
+ }
+
+ max_life = _krb5_krb_life_to_time(0, life);
+ if(client->entry.max_life)
+ max_life = min(max_life, *client->entry.max_life);
+ if(server->entry.max_life)
+ max_life = min(max_life, *server->entry.max_life);
+
+ life = krb_time_to_life(kdc_time, kdc_time + max_life);
+
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_DES_PCBC_NONE,
+ &session);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "Not enough random i KDC");
+ goto out1;
+ }
+
+ ret = _krb5_krb_create_ticket(context,
+ 0,
+ name,
+ inst,
+ config->v4_realm,
+ addr->sin_addr.s_addr,
+ &session,
+ life,
+ kdc_time,
+ sname,
+ sinst,
+ &skey->key,
+ &ticket);
+ if (ret) {
+ krb5_free_keyblock_contents(context, &session);
+ make_err_reply(context, reply, KFAILURE,
+ "failed to create v4 ticket");
+ goto out1;
+ }
+
+ ret = _krb5_krb_create_ciph(context,
+ &session,
+ sname,
+ sinst,
+ config->v4_realm,
+ life,
+ server->entry.kvno % 255,
+ &ticket,
+ kdc_time,
+ &ckey->key,
+ &cipher);
+ krb5_free_keyblock_contents(context, &session);
+ krb5_data_free(&ticket);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "Failed to create v4 cipher");
+ goto out1;
+ }
+
+ ret = _krb5_krb_create_auth_reply(context,
+ name,
+ inst,
+ realm,
+ req_time,
+ 0,
+ client->entry.pw_end ? *client->entry.pw_end : 0,
+ client->entry.kvno % 256,
+ &cipher,
+ reply);
+ krb5_data_free(&cipher);
+
+ out1:
+ break;
+ }
+ case AUTH_MSG_APPL_REQUEST: {
+ struct _krb5_krb_auth_data ad;
+ int8_t kvno;
+ int8_t ticket_len;
+ int8_t req_len;
+ krb5_data auth;
+ int32_t address;
+ size_t pos;
+ krb5_principal tgt_princ = NULL;
+ hdb_entry_ex *tgt = NULL;
+ Key *tkey;
+ time_t max_end, actual_end, issue_time;
+
+ memset(&ad, 0, sizeof(ad));
+ krb5_data_zero(&auth);
+
+ RCHECK(krb5_ret_int8(sp, &kvno), out2);
+ RCHECK(krb5_ret_stringz(sp, &realm), out2);
+
+ ret = krb5_425_conv_principal(context, "krbtgt", realm,
+ config->v4_realm,
+ &tgt_princ);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Converting krbtgt principal (krb4): %s",
+ krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KFAILURE,
+ "Failed to convert v4 principal (krbtgt)");
+ goto out2;
+ }
+
+ ret = _kdc_db_fetch(context, config, tgt_princ,
+ HDB_F_GET_KRBTGT, NULL, &tgt);
+ if(ret){
+ char *s;
+ s = kdc_log_msg(context, config, 0, "Ticket-granting ticket not "
+ "found in database (krb4): krbtgt.%s@%s: %s",
+ realm, config->v4_realm,
+ krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KFAILURE, s);
+ free(s);
+ goto out2;
+ }
+
+ if(tgt->entry.kvno % 256 != kvno){
+ kdc_log(context, config, 0,
+ "tgs-req (krb4) with old kvno %d (current %d) for "
+ "krbtgt.%s@%s", kvno, tgt->entry.kvno % 256,
+ realm, config->v4_realm);
+ make_err_reply(context, reply, KRB4ET_KDC_AUTH_EXP,
+ "old krbtgt kvno used");
+ goto out2;
+ }
+
+ ret = _kdc_get_des_key(context, tgt, TRUE, FALSE, &tkey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "no suitable DES key for krbtgt (krb4)");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for krbtgt");
+ goto out2;
+ }
+
+ RCHECK(krb5_ret_int8(sp, &ticket_len), out2);
+ RCHECK(krb5_ret_int8(sp, &req_len), out2);
+
+ pos = krb5_storage_seek(sp, ticket_len + req_len, SEEK_CUR);
+
+ auth.data = buf;
+ auth.length = pos;
+
+ if (config->check_ticket_addresses)
+ address = addr->sin_addr.s_addr;
+ else
+ address = 0;
+
+ ret = _krb5_krb_rd_req(context, &auth, "krbtgt", realm,
+ config->v4_realm,
+ address, &tkey->key, &ad);
+ if(ret){
+ kdc_log(context, config, 0, "krb_rd_req: %d", ret);
+ make_err_reply(context, reply, ret, "failed to parse request");
+ goto out2;
+ }
+
+ RCHECK(krb5_ret_int32(sp, &req_time), out2);
+ if(lsb)
+ req_time = swap32(req_time);
+ RCHECK(krb5_ret_uint8(sp, &life), out2);
+ RCHECK(krb5_ret_stringz(sp, &sname), out2);
+ RCHECK(krb5_ret_stringz(sp, &sinst), out2);
+ snprintf (server_name, sizeof(server_name),
+ "%s.%s@%s",
+ sname, sinst, config->v4_realm);
+ snprintf (client_name, sizeof(client_name),
+ "%s.%s@%s",
+ ad.pname, ad.pinst, ad.prealm);
+
+ kdc_log(context, config, 0, "TGS-REQ (krb4) %s from %s for %s",
+ client_name, from, server_name);
+
+ if(strcmp(ad.prealm, realm)){
+ kdc_log(context, config, 0,
+ "Can't hop realms (krb4) %s -> %s", realm, ad.prealm);
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "Can't hop realms");
+ goto out2;
+ }
+
+ if (!config->enable_v4_cross_realm && strcmp(realm, config->v4_realm) != 0) {
+ kdc_log(context, config, 0,
+ "krb4 Cross-realm %s -> %s disabled",
+ realm, config->v4_realm);
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "Can't hop realms");
+ goto out2;
+ }
+
+ if(strcmp(sname, "changepw") == 0){
+ kdc_log(context, config, 0,
+ "Bad request for changepw ticket (krb4)");
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN,
+ "Can't authorize password change based on TGT");
+ goto out2;
+ }
+
+ ret = _kdc_db_fetch4(context, config, ad.pname, ad.pinst, ad.prealm,
+ HDB_F_GET_CLIENT, &client);
+ if(ret && ret != HDB_ERR_NOENTRY) {
+ char *s;
+ s = kdc_log_msg(context, config, 0,
+ "Client not found in database: (krb4) %s: %s",
+ client_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s);
+ free(s);
+ goto out2;
+ }
+ if (client == NULL && strcmp(ad.prealm, config->v4_realm) == 0) {
+ char *s;
+ s = kdc_log_msg(context, config, 0,
+ "Local client not found in database: (krb4) "
+ "%s", client_name);
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s);
+ free(s);
+ goto out2;
+ }
+
+ ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm,
+ HDB_F_GET_SERVER, &server);
+ if(ret){
+ char *s;
+ s = kdc_log_msg(context, config, 0,
+ "Server not found in database (krb4): %s: %s",
+ server_name, krb5_get_err_text(context, ret));
+ make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s);
+ free(s);
+ goto out2;
+ }
+
+ ret = _kdc_check_flags (context, config,
+ client, client_name,
+ server, server_name,
+ FALSE);
+ if (ret) {
+ make_err_reply(context, reply, KRB4ET_KDC_NAME_EXP,
+ "operation not allowed");
+ goto out2;
+ }
+
+ ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey);
+ if(ret){
+ kdc_log(context, config, 0,
+ "no suitable DES key for server (krb4)");
+ make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY,
+ "no suitable DES key for server");
+ goto out2;
+ }
+
+ max_end = _krb5_krb_life_to_time(ad.time_sec, ad.life);
+ max_end = min(max_end, _krb5_krb_life_to_time(kdc_time, life));
+ if(server->entry.max_life)
+ max_end = min(max_end, kdc_time + *server->entry.max_life);
+ if(client && client->entry.max_life)
+ max_end = min(max_end, kdc_time + *client->entry.max_life);
+ life = min(life, krb_time_to_life(kdc_time, max_end));
+
+ issue_time = kdc_time;
+ actual_end = _krb5_krb_life_to_time(issue_time, life);
+ while (actual_end > max_end && life > 1) {
+ /* move them into the next earlier lifetime bracket */
+ life--;
+ actual_end = _krb5_krb_life_to_time(issue_time, life);
+ }
+ if (actual_end > max_end) {
+ /* if life <= 1 and it's still too long, backdate the ticket */
+ issue_time -= actual_end - max_end;
+ }
+
+ {
+ krb5_data ticket, cipher;
+ krb5_keyblock session;
+
+ krb5_data_zero(&ticket);
+ krb5_data_zero(&cipher);
+
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_DES_PCBC_NONE,
+ &session);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "Not enough random i KDC");
+ goto out2;
+ }
+
+ ret = _krb5_krb_create_ticket(context,
+ 0,
+ ad.pname,
+ ad.pinst,
+ ad.prealm,
+ addr->sin_addr.s_addr,
+ &session,
+ life,
+ issue_time,
+ sname,
+ sinst,
+ &skey->key,
+ &ticket);
+ if (ret) {
+ krb5_free_keyblock_contents(context, &session);
+ make_err_reply(context, reply, KFAILURE,
+ "failed to create v4 ticket");
+ goto out2;
+ }
+
+ ret = _krb5_krb_create_ciph(context,
+ &session,
+ sname,
+ sinst,
+ config->v4_realm,
+ life,
+ server->entry.kvno % 255,
+ &ticket,
+ issue_time,
+ &ad.session,
+ &cipher);
+ krb5_free_keyblock_contents(context, &session);
+ if (ret) {
+ make_err_reply(context, reply, KFAILURE,
+ "failed to create v4 cipher");
+ goto out2;
+ }
+
+ ret = _krb5_krb_create_auth_reply(context,
+ ad.pname,
+ ad.pinst,
+ ad.prealm,
+ req_time,
+ 0,
+ 0,
+ 0,
+ &cipher,
+ reply);
+ krb5_data_free(&cipher);
+ }
+ out2:
+ _krb5_krb_free_auth_data(context, &ad);
+ if(tgt_princ)
+ krb5_free_principal(context, tgt_princ);
+ if(tgt)
+ _kdc_free_ent(context, tgt);
+ break;
+ }
+ case AUTH_MSG_ERR_REPLY:
+ break;
+ default:
+ kdc_log(context, config, 0, "Unknown message type (krb4): %d from %s",
+ msg_type, from);
+
+ make_err_reply(context, reply, KFAILURE, "Unknown message type");
+ }
+ out:
+ if(name)
+ free(name);
+ if(inst)
+ free(inst);
+ if(realm)
+ free(realm);
+ if(sname)
+ free(sname);
+ if(sinst)
+ free(sinst);
+ if(client)
+ _kdc_free_ent(context, client);
+ if(server)
+ _kdc_free_ent(context, server);
+ krb5_storage_free(sp);
+ return 0;
+}
+
+krb5_error_code
+_kdc_encode_v4_ticket(krb5_context context,
+ krb5_kdc_configuration *config,
+ void *buf, size_t len, const EncTicketPart *et,
+ const PrincipalName *service, size_t *size)
+{
+ krb5_storage *sp;
+ krb5_error_code ret;
+ char name[40], inst[40], realm[40];
+ char sname[40], sinst[40];
+
+ {
+ krb5_principal princ;
+ _krb5_principalname2krb5_principal(context,
+ &princ,
+ *service,
+ et->crealm);
+ ret = krb5_524_conv_principal(context,
+ princ,
+ sname,
+ sinst,
+ realm);
+ krb5_free_principal(context, princ);
+ if(ret)
+ return ret;
+
+ _krb5_principalname2krb5_principal(context,
+ &princ,
+ et->cname,
+ et->crealm);
+
+ ret = krb5_524_conv_principal(context,
+ princ,
+ name,
+ inst,
+ realm);
+ krb5_free_principal(context, princ);
+ }
+ if(ret)
+ return ret;
+
+ sp = krb5_storage_emem();
+
+ krb5_store_int8(sp, 0); /* flags */
+ krb5_store_stringz(sp, name);
+ krb5_store_stringz(sp, inst);
+ krb5_store_stringz(sp, realm);
+ {
+ unsigned char tmp[4] = { 0, 0, 0, 0 };
+ int i;
+ if(et->caddr){
+ for(i = 0; i < et->caddr->len; i++)
+ if(et->caddr->val[i].addr_type == AF_INET &&
+ et->caddr->val[i].address.length == 4){
+ memcpy(tmp, et->caddr->val[i].address.data, 4);
+ break;
+ }
+ }
+ krb5_storage_write(sp, tmp, sizeof(tmp));
+ }
+
+ if((et->key.keytype != ETYPE_DES_CBC_MD5 &&
+ et->key.keytype != ETYPE_DES_CBC_MD4 &&
+ et->key.keytype != ETYPE_DES_CBC_CRC) ||
+ et->key.keyvalue.length != 8)
+ return -1;
+ krb5_storage_write(sp, et->key.keyvalue.data, 8);
+
+ {
+ time_t start = et->starttime ? *et->starttime : et->authtime;
+ krb5_store_int8(sp, krb_time_to_life(start, et->endtime));
+ krb5_store_int32(sp, start);
+ }
+
+ krb5_store_stringz(sp, sname);
+ krb5_store_stringz(sp, sinst);
+
+ {
+ krb5_data data;
+ krb5_storage_to_data(sp, &data);
+ krb5_storage_free(sp);
+ *size = (data.length + 7) & ~7; /* pad to 8 bytes */
+ if(*size > len)
+ return -1;
+ memset((unsigned char*)buf - *size + 1, 0, *size);
+ memcpy((unsigned char*)buf - *size + 1, data.data, data.length);
+ krb5_data_free(&data);
+ }
+ return 0;
+}
+
+krb5_error_code
+_kdc_get_des_key(krb5_context context,
+ hdb_entry_ex *principal, krb5_boolean is_server,
+ krb5_boolean prefer_afs_key, Key **ret_key)
+{
+ Key *v5_key = NULL, *v4_key = NULL, *afs_key = NULL, *server_key = NULL;
+ int i;
+ krb5_enctype etypes[] = { ETYPE_DES_CBC_MD5,
+ ETYPE_DES_CBC_MD4,
+ ETYPE_DES_CBC_CRC };
+
+ for(i = 0;
+ i < sizeof(etypes)/sizeof(etypes[0])
+ && (v5_key == NULL || v4_key == NULL ||
+ afs_key == NULL || server_key == NULL);
+ ++i) {
+ Key *key = NULL;
+ while(hdb_next_enctype2key(context, &principal->entry, etypes[i], &key) == 0) {
+ if(key->salt == NULL) {
+ if(v5_key == NULL)
+ v5_key = key;
+ } else if(key->salt->type == hdb_pw_salt &&
+ key->salt->salt.length == 0) {
+ if(v4_key == NULL)
+ v4_key = key;
+ } else if(key->salt->type == hdb_afs3_salt) {
+ if(afs_key == NULL)
+ afs_key = key;
+ } else if(server_key == NULL)
+ server_key = key;
+ }
+ }
+
+ if(prefer_afs_key) {
+ if(afs_key)
+ *ret_key = afs_key;
+ else if(v4_key)
+ *ret_key = v4_key;
+ else if(v5_key)
+ *ret_key = v5_key;
+ else if(is_server && server_key)
+ *ret_key = server_key;
+ else
+ return KRB4ET_KDC_NULL_KEY;
+ } else {
+ if(v4_key)
+ *ret_key = v4_key;
+ else if(afs_key)
+ *ret_key = afs_key;
+ else if(v5_key)
+ *ret_key = v5_key;
+ else if(is_server && server_key)
+ *ret_key = server_key;
+ else
+ return KRB4ET_KDC_NULL_KEY;
+ }
+
+ if((*ret_key)->key.keyvalue.length == 0)
+ return KRB4ET_KDC_NULL_KEY;
+ return 0;
+}
+
diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c
new file mode 100644
index 0000000000000..9582cd85ec384
--- /dev/null
+++ b/kdc/kerberos5.c
@@ -0,0 +1,1852 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: kerberos5.c 22071 2007-11-14 20:04:50Z lha $");
+
+#define MAX_TIME ((time_t)((1U << 31) - 1))
+
+void
+_kdc_fix_time(time_t **t)
+{
+ if(*t == NULL){
+ ALLOC(*t);
+ **t = MAX_TIME;
+ }
+ if(**t == 0) **t = MAX_TIME; /* fix for old clients */
+}
+
+static int
+realloc_method_data(METHOD_DATA *md)
+{
+ PA_DATA *pa;
+ pa = realloc(md->val, (md->len + 1) * sizeof(*md->val));
+ if(pa == NULL)
+ return ENOMEM;
+ md->val = pa;
+ md->len++;
+ return 0;
+}
+
+static void
+set_salt_padata (METHOD_DATA *md, Salt *salt)
+{
+ if (salt) {
+ realloc_method_data(md);
+ md->val[md->len - 1].padata_type = salt->type;
+ der_copy_octet_string(&salt->salt,
+ &md->val[md->len - 1].padata_value);
+ }
+}
+
+const PA_DATA*
+_kdc_find_padata(const KDC_REQ *req, int *start, int type)
+{
+ if (req->padata == NULL)
+ return NULL;
+
+ while(*start < req->padata->len){
+ (*start)++;
+ if(req->padata->val[*start - 1].padata_type == type)
+ return &req->padata->val[*start - 1];
+ }
+ return NULL;
+}
+
+/*
+ * Detect if `key' is the using the the precomputed `default_salt'.
+ */
+
+static krb5_boolean
+is_default_salt_p(const krb5_salt *default_salt, const Key *key)
+{
+ if (key->salt == NULL)
+ return TRUE;
+ if (default_salt->salttype != key->salt->type)
+ return FALSE;
+ if (krb5_data_cmp(&default_salt->saltvalue, &key->salt->salt))
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ * return the first appropriate key of `princ' in `ret_key'. Look for
+ * all the etypes in (`etypes', `len'), stopping as soon as we find
+ * one, but preferring one that has default salt
+ */
+
+krb5_error_code
+_kdc_find_etype(krb5_context context, const hdb_entry_ex *princ,
+ krb5_enctype *etypes, unsigned len,
+ Key **ret_key, krb5_enctype *ret_etype)
+{
+ int i;
+ krb5_error_code ret = KRB5KDC_ERR_ETYPE_NOSUPP;
+ krb5_salt def_salt;
+
+ krb5_get_pw_salt (context, princ->entry.principal, &def_salt);
+
+ for(i = 0; ret != 0 && i < len ; i++) {
+ Key *key = NULL;
+
+ if (krb5_enctype_valid(context, etypes[i]) != 0)
+ continue;
+
+ while (hdb_next_enctype2key(context, &princ->entry, etypes[i], &key) == 0) {
+ if (key->key.keyvalue.length == 0) {
+ ret = KRB5KDC_ERR_NULL_KEY;
+ continue;
+ }
+ *ret_key = key;
+ *ret_etype = etypes[i];
+ ret = 0;
+ if (is_default_salt_p(&def_salt, key)) {
+ krb5_free_salt (context, def_salt);
+ return ret;
+ }
+ }
+ }
+ krb5_free_salt (context, def_salt);
+ return ret;
+}
+
+krb5_error_code
+_kdc_make_anonymous_principalname (PrincipalName *pn)
+{
+ pn->name_type = KRB5_NT_PRINCIPAL;
+ pn->name_string.len = 1;
+ pn->name_string.val = malloc(sizeof(*pn->name_string.val));
+ if (pn->name_string.val == NULL)
+ return ENOMEM;
+ pn->name_string.val[0] = strdup("anonymous");
+ if (pn->name_string.val[0] == NULL) {
+ free(pn->name_string.val);
+ pn->name_string.val = NULL;
+ return ENOMEM;
+ }
+ return 0;
+}
+
+void
+_kdc_log_timestamp(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *type,
+ KerberosTime authtime, KerberosTime *starttime,
+ KerberosTime endtime, KerberosTime *renew_till)
+{
+ char authtime_str[100], starttime_str[100],
+ endtime_str[100], renewtime_str[100];
+
+ krb5_format_time(context, authtime,
+ authtime_str, sizeof(authtime_str), TRUE);
+ if (starttime)
+ krb5_format_time(context, *starttime,
+ starttime_str, sizeof(starttime_str), TRUE);
+ else
+ strlcpy(starttime_str, "unset", sizeof(starttime_str));
+ krb5_format_time(context, endtime,
+ endtime_str, sizeof(endtime_str), TRUE);
+ if (renew_till)
+ krb5_format_time(context, *renew_till,
+ renewtime_str, sizeof(renewtime_str), TRUE);
+ else
+ strlcpy(renewtime_str, "unset", sizeof(renewtime_str));
+
+ kdc_log(context, config, 5,
+ "%s authtime: %s starttime: %s endtime: %s renew till: %s",
+ type, authtime_str, starttime_str, endtime_str, renewtime_str);
+}
+
+static void
+log_patypes(krb5_context context,
+ krb5_kdc_configuration *config,
+ METHOD_DATA *padata)
+{
+ struct rk_strpool *p = NULL;
+ char *str;
+ int i;
+
+ for (i = 0; i < padata->len; i++) {
+ switch(padata->val[i].padata_type) {
+ case KRB5_PADATA_PK_AS_REQ:
+ p = rk_strpoolprintf(p, "PK-INIT(ietf)");
+ break;
+ case KRB5_PADATA_PK_AS_REQ_WIN:
+ p = rk_strpoolprintf(p, "PK-INIT(win2k)");
+ break;
+ case KRB5_PADATA_PA_PK_OCSP_RESPONSE:
+ p = rk_strpoolprintf(p, "OCSP");
+ break;
+ case KRB5_PADATA_ENC_TIMESTAMP:
+ p = rk_strpoolprintf(p, "encrypted-timestamp");
+ break;
+ default:
+ p = rk_strpoolprintf(p, "%d", padata->val[i].padata_type);
+ break;
+ }
+ if (p && i + 1 < padata->len)
+ p = rk_strpoolprintf(p, ", ");
+ if (p == NULL) {
+ kdc_log(context, config, 0, "out of memory");
+ return;
+ }
+ }
+ if (p == NULL)
+ p = rk_strpoolprintf(p, "none");
+
+ str = rk_strpoolcollect(p);
+ kdc_log(context, config, 0, "Client sent patypes: %s", str);
+ free(str);
+}
+
+/*
+ *
+ */
+
+
+krb5_error_code
+_kdc_encode_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek,
+ krb5_enctype etype,
+ int skvno, const EncryptionKey *skey,
+ int ckvno, const EncryptionKey *ckey,
+ const char **e_text,
+ krb5_data *reply)
+{
+ unsigned char *buf;
+ size_t buf_size;
+ size_t len;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, et, &len, ret);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encode ticket: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ return KRB5KRB_ERR_GENERIC;
+ }
+
+ ret = krb5_crypto_init(context, skey, etype, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TICKET,
+ buf,
+ len,
+ skvno,
+ &rep->ticket.enc_part);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encrypt data: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+
+ if(rep->msg_type == krb_as_rep && !config->encode_as_rep_as_tgs_rep)
+ ASN1_MALLOC_ENCODE(EncASRepPart, buf, buf_size, ek, &len, ret);
+ else
+ ASN1_MALLOC_ENCODE(EncTGSRepPart, buf, buf_size, ek, &len, ret);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encode KDC-REP: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ return KRB5KRB_ERR_GENERIC;
+ }
+ ret = krb5_crypto_init(context, ckey, 0, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(rep->msg_type == krb_as_rep) {
+ krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_AS_REP_ENC_PART,
+ buf,
+ len,
+ ckvno,
+ &rep->enc_part);
+ free(buf);
+ ASN1_MALLOC_ENCODE(AS_REP, buf, buf_size, rep, &len, ret);
+ } else {
+ krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TGS_REP_ENC_PART_SESSION,
+ buf,
+ len,
+ ckvno,
+ &rep->enc_part);
+ free(buf);
+ ASN1_MALLOC_ENCODE(TGS_REP, buf, buf_size, rep, &len, ret);
+ }
+ krb5_crypto_destroy(context, crypto);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to encode KDC-REP: %s",
+ krb5_get_err_text(context, ret));
+ return ret;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ return KRB5KRB_ERR_GENERIC;
+ }
+ reply->data = buf;
+ reply->length = buf_size;
+ return 0;
+}
+
+/*
+ * Return 1 if the client have only older enctypes, this is for
+ * determining if the server should send ETYPE_INFO2 or not.
+ */
+
+static int
+older_enctype(krb5_enctype enctype)
+{
+ switch (enctype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ case ETYPE_DES3_CBC_SHA1:
+ case ETYPE_ARCFOUR_HMAC_MD5:
+ case ETYPE_ARCFOUR_HMAC_MD5_56:
+ /*
+ * The following three is "old" windows enctypes and is needed for
+ * windows 2000 hosts.
+ */
+ case ETYPE_ARCFOUR_MD4:
+ case ETYPE_ARCFOUR_HMAC_OLD:
+ case ETYPE_ARCFOUR_HMAC_OLD_EXP:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+only_older_enctype_p(const KDC_REQ *req)
+{
+ int i;
+
+ for(i = 0; i < req->req_body.etype.len; i++) {
+ if (!older_enctype(req->req_body.etype.val[i]))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key)
+{
+ ent->etype = key->key.keytype;
+ if(key->salt){
+#if 0
+ ALLOC(ent->salttype);
+
+ if(key->salt->type == hdb_pw_salt)
+ *ent->salttype = 0; /* or 1? or NULL? */
+ else if(key->salt->type == hdb_afs3_salt)
+ *ent->salttype = 2;
+ else {
+ kdc_log(context, config, 0, "unknown salt-type: %d",
+ key->salt->type);
+ return KRB5KRB_ERR_GENERIC;
+ }
+ /* according to `the specs', we can't send a salt if
+ we have AFS3 salted key, but that requires that you
+ *know* what cell you are using (e.g by assuming
+ that the cell is the same as the realm in lower
+ case) */
+#elif 0
+ ALLOC(ent->salttype);
+ *ent->salttype = key->salt->type;
+#else
+ /*
+ * We shouldn't sent salttype since it is incompatible with the
+ * specification and it breaks windows clients. The afs
+ * salting problem is solved by using KRB5-PADATA-AFS3-SALT
+ * implemented in Heimdal 0.7 and later.
+ */
+ ent->salttype = NULL;
+#endif
+ krb5_copy_data(context, &key->salt->salt,
+ &ent->salt);
+ } else {
+ /* we return no salt type at all, as that should indicate
+ * the default salt type and make everybody happy. some
+ * systems (like w2k) dislike being told the salt type
+ * here. */
+
+ ent->salttype = NULL;
+ ent->salt = NULL;
+ }
+ return 0;
+}
+
+static krb5_error_code
+get_pa_etype_info(krb5_context context,
+ krb5_kdc_configuration *config,
+ METHOD_DATA *md, hdb_entry *client,
+ ENCTYPE *etypes, unsigned int etypes_len)
+{
+ krb5_error_code ret = 0;
+ int i, j;
+ unsigned int n = 0;
+ ETYPE_INFO pa;
+ unsigned char *buf;
+ size_t len;
+
+
+ pa.len = client->keys.len;
+ if(pa.len > UINT_MAX/sizeof(*pa.val))
+ return ERANGE;
+ pa.val = malloc(pa.len * sizeof(*pa.val));
+ if(pa.val == NULL)
+ return ENOMEM;
+ memset(pa.val, 0, pa.len * sizeof(*pa.val));
+
+ for(i = 0; i < client->keys.len; i++) {
+ for (j = 0; j < n; j++)
+ if (pa.val[j].etype == client->keys.val[i].key.keytype)
+ goto skip1;
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j]) {
+ if (krb5_enctype_valid(context, etypes[j]) != 0)
+ continue;
+ if (!older_enctype(etypes[j]))
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info_entry(context,
+ &pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO(&pa);
+ return ret;
+ }
+ break;
+ }
+ }
+ skip1:;
+ }
+ for(i = 0; i < client->keys.len; i++) {
+ /* already added? */
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j])
+ goto skip2;
+ }
+ if (krb5_enctype_valid(context, client->keys.val[i].key.keytype) != 0)
+ continue;
+ if (!older_enctype(etypes[j]))
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info_entry(context,
+ &pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO(&pa);
+ return ret;
+ }
+ skip2:;
+ }
+
+ if(n < pa.len) {
+ /* stripped out dups, newer enctypes, and not valid enctypes */
+ pa.len = n;
+ }
+
+ ASN1_MALLOC_ENCODE(ETYPE_INFO, buf, len, &pa, &len, ret);
+ free_ETYPE_INFO(&pa);
+ if(ret)
+ return ret;
+ ret = realloc_method_data(md);
+ if(ret) {
+ free(buf);
+ return ret;
+ }
+ md->val[md->len - 1].padata_type = KRB5_PADATA_ETYPE_INFO;
+ md->val[md->len - 1].padata_value.length = len;
+ md->val[md->len - 1].padata_value.data = buf;
+ return 0;
+}
+
+/*
+ *
+ */
+
+extern int _krb5_AES_string_to_default_iterator;
+
+static krb5_error_code
+make_etype_info2_entry(ETYPE_INFO2_ENTRY *ent, Key *key)
+{
+ ent->etype = key->key.keytype;
+ if(key->salt) {
+ ALLOC(ent->salt);
+ if (ent->salt == NULL)
+ return ENOMEM;
+ *ent->salt = malloc(key->salt->salt.length + 1);
+ if (*ent->salt == NULL) {
+ free(ent->salt);
+ ent->salt = NULL;
+ return ENOMEM;
+ }
+ memcpy(*ent->salt, key->salt->salt.data, key->salt->salt.length);
+ (*ent->salt)[key->salt->salt.length] = '\0';
+ } else
+ ent->salt = NULL;
+
+ ent->s2kparams = NULL;
+
+ switch (key->key.keytype) {
+ case ETYPE_AES128_CTS_HMAC_SHA1_96:
+ case ETYPE_AES256_CTS_HMAC_SHA1_96:
+ ALLOC(ent->s2kparams);
+ if (ent->s2kparams == NULL)
+ return ENOMEM;
+ ent->s2kparams->length = 4;
+ ent->s2kparams->data = malloc(ent->s2kparams->length);
+ if (ent->s2kparams->data == NULL) {
+ free(ent->s2kparams);
+ ent->s2kparams = NULL;
+ return ENOMEM;
+ }
+ _krb5_put_int(ent->s2kparams->data,
+ _krb5_AES_string_to_default_iterator,
+ ent->s2kparams->length);
+ break;
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ /* Check if this was a AFS3 salted key */
+ if(key->salt && key->salt->type == hdb_afs3_salt){
+ ALLOC(ent->s2kparams);
+ if (ent->s2kparams == NULL)
+ return ENOMEM;
+ ent->s2kparams->length = 1;
+ ent->s2kparams->data = malloc(ent->s2kparams->length);
+ if (ent->s2kparams->data == NULL) {
+ free(ent->s2kparams);
+ ent->s2kparams = NULL;
+ return ENOMEM;
+ }
+ _krb5_put_int(ent->s2kparams->data,
+ 1,
+ ent->s2kparams->length);
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Return an ETYPE-INFO2. Enctypes are storted the same way as in the
+ * database (client supported enctypes first, then the unsupported
+ * enctypes).
+ */
+
+static krb5_error_code
+get_pa_etype_info2(krb5_context context,
+ krb5_kdc_configuration *config,
+ METHOD_DATA *md, hdb_entry *client,
+ ENCTYPE *etypes, unsigned int etypes_len)
+{
+ krb5_error_code ret = 0;
+ int i, j;
+ unsigned int n = 0;
+ ETYPE_INFO2 pa;
+ unsigned char *buf;
+ size_t len;
+
+ pa.len = client->keys.len;
+ if(pa.len > UINT_MAX/sizeof(*pa.val))
+ return ERANGE;
+ pa.val = malloc(pa.len * sizeof(*pa.val));
+ if(pa.val == NULL)
+ return ENOMEM;
+ memset(pa.val, 0, pa.len * sizeof(*pa.val));
+
+ for(i = 0; i < client->keys.len; i++) {
+ for (j = 0; j < n; j++)
+ if (pa.val[j].etype == client->keys.val[i].key.keytype)
+ goto skip1;
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j]) {
+ if (krb5_enctype_valid(context, etypes[j]) != 0)
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info2_entry(&pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO2(&pa);
+ return ret;
+ }
+ break;
+ }
+ }
+ skip1:;
+ }
+ /* send enctypes that the client doesn't know about too */
+ for(i = 0; i < client->keys.len; i++) {
+ /* already added? */
+ for(j = 0; j < etypes_len; j++) {
+ if(client->keys.val[i].key.keytype == etypes[j])
+ goto skip2;
+ }
+ if (krb5_enctype_valid(context, client->keys.val[i].key.keytype) != 0)
+ continue;
+ if (n >= pa.len)
+ krb5_abortx(context, "internal error: n >= p.len");
+ if((ret = make_etype_info2_entry(&pa.val[n++],
+ &client->keys.val[i])) != 0) {
+ free_ETYPE_INFO2(&pa);
+ return ret;
+ }
+ skip2:;
+ }
+
+ if(n < pa.len) {
+ /* stripped out dups, and not valid enctypes */
+ pa.len = n;
+ }
+
+ ASN1_MALLOC_ENCODE(ETYPE_INFO2, buf, len, &pa, &len, ret);
+ free_ETYPE_INFO2(&pa);
+ if(ret)
+ return ret;
+ ret = realloc_method_data(md);
+ if(ret) {
+ free(buf);
+ return ret;
+ }
+ md->val[md->len - 1].padata_type = KRB5_PADATA_ETYPE_INFO2;
+ md->val[md->len - 1].padata_value.length = len;
+ md->val[md->len - 1].padata_value.data = buf;
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+log_as_req(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_enctype cetype,
+ krb5_enctype setype,
+ const KDC_REQ_BODY *b)
+{
+ krb5_error_code ret;
+ struct rk_strpool *p = NULL;
+ char *str;
+ int i;
+
+ for (i = 0; i < b->etype.len; i++) {
+ ret = krb5_enctype_to_string(context, b->etype.val[i], &str);
+ if (ret == 0) {
+ p = rk_strpoolprintf(p, "%s", str);
+ free(str);
+ } else
+ p = rk_strpoolprintf(p, "%d", b->etype.val[i]);
+ if (p && i + 1 < b->etype.len)
+ p = rk_strpoolprintf(p, ", ");
+ if (p == NULL) {
+ kdc_log(context, config, 0, "out of memory");
+ return;
+ }
+ }
+ if (p == NULL)
+ p = rk_strpoolprintf(p, "no encryption types");
+
+ str = rk_strpoolcollect(p);
+ kdc_log(context, config, 0, "Client supported enctypes: %s", str);
+ free(str);
+
+ {
+ char *cet;
+ char *set;
+
+ ret = krb5_enctype_to_string(context, cetype, &cet);
+ if(ret == 0) {
+ ret = krb5_enctype_to_string(context, setype, &set);
+ if (ret == 0) {
+ kdc_log(context, config, 5, "Using %s/%s", cet, set);
+ free(set);
+ }
+ free(cet);
+ }
+ if (ret != 0)
+ kdc_log(context, config, 5, "Using e-types %d/%d", cetype, setype);
+ }
+
+ {
+ char fixedstr[128];
+ unparse_flags(KDCOptions2int(b->kdc_options), asn1_KDCOptions_units(),
+ fixedstr, sizeof(fixedstr));
+ if(*fixedstr)
+ kdc_log(context, config, 2, "Requested flags: %s", fixedstr);
+ }
+}
+
+/*
+ * verify the flags on `client' and `server', returning 0
+ * if they are OK and generating an error messages and returning
+ * and error code otherwise.
+ */
+
+krb5_error_code
+_kdc_check_flags(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client_ex, const char *client_name,
+ hdb_entry_ex *server_ex, const char *server_name,
+ krb5_boolean is_as_req)
+{
+ if(client_ex != NULL) {
+ hdb_entry *client = &client_ex->entry;
+
+ /* check client */
+ if (client->flags.invalid) {
+ kdc_log(context, config, 0,
+ "Client (%s) has invalid bit set", client_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if(!client->flags.client){
+ kdc_log(context, config, 0,
+ "Principal may not act as client -- %s", client_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if (client->valid_start && *client->valid_start > kdc_time) {
+ char starttime_str[100];
+ krb5_format_time(context, *client->valid_start,
+ starttime_str, sizeof(starttime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Client not yet valid until %s -- %s",
+ starttime_str, client_name);
+ return KRB5KDC_ERR_CLIENT_NOTYET;
+ }
+
+ if (client->valid_end && *client->valid_end < kdc_time) {
+ char endtime_str[100];
+ krb5_format_time(context, *client->valid_end,
+ endtime_str, sizeof(endtime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Client expired at %s -- %s",
+ endtime_str, client_name);
+ return KRB5KDC_ERR_NAME_EXP;
+ }
+
+ if (client->pw_end && *client->pw_end < kdc_time
+ && (server_ex == NULL || !server_ex->entry.flags.change_pw)) {
+ char pwend_str[100];
+ krb5_format_time(context, *client->pw_end,
+ pwend_str, sizeof(pwend_str), TRUE);
+ kdc_log(context, config, 0,
+ "Client's key has expired at %s -- %s",
+ pwend_str, client_name);
+ return KRB5KDC_ERR_KEY_EXPIRED;
+ }
+ }
+
+ /* check server */
+
+ if (server_ex != NULL) {
+ hdb_entry *server = &server_ex->entry;
+
+ if (server->flags.invalid) {
+ kdc_log(context, config, 0,
+ "Server has invalid flag set -- %s", server_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if(!server->flags.server){
+ kdc_log(context, config, 0,
+ "Principal may not act as server -- %s", server_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if(!is_as_req && server->flags.initial) {
+ kdc_log(context, config, 0,
+ "AS-REQ is required for server -- %s", server_name);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ if (server->valid_start && *server->valid_start > kdc_time) {
+ char starttime_str[100];
+ krb5_format_time(context, *server->valid_start,
+ starttime_str, sizeof(starttime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Server not yet valid until %s -- %s",
+ starttime_str, server_name);
+ return KRB5KDC_ERR_SERVICE_NOTYET;
+ }
+
+ if (server->valid_end && *server->valid_end < kdc_time) {
+ char endtime_str[100];
+ krb5_format_time(context, *server->valid_end,
+ endtime_str, sizeof(endtime_str), TRUE);
+ kdc_log(context, config, 0,
+ "Server expired at %s -- %s",
+ endtime_str, server_name);
+ return KRB5KDC_ERR_SERVICE_EXP;
+ }
+
+ if (server->pw_end && *server->pw_end < kdc_time) {
+ char pwend_str[100];
+ krb5_format_time(context, *server->pw_end,
+ pwend_str, sizeof(pwend_str), TRUE);
+ kdc_log(context, config, 0,
+ "Server's key has expired at -- %s",
+ pwend_str, server_name);
+ return KRB5KDC_ERR_KEY_EXPIRED;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return TRUE if `from' is part of `addresses' taking into consideration
+ * the configuration variables that tells us how strict we should be about
+ * these checks
+ */
+
+krb5_boolean
+_kdc_check_addresses(krb5_context context,
+ krb5_kdc_configuration *config,
+ HostAddresses *addresses, const struct sockaddr *from)
+{
+ krb5_error_code ret;
+ krb5_address addr;
+ krb5_boolean result;
+ krb5_boolean only_netbios = TRUE;
+ int i;
+
+ if(config->check_ticket_addresses == 0)
+ return TRUE;
+
+ if(addresses == NULL)
+ return config->allow_null_ticket_addresses;
+
+ for (i = 0; i < addresses->len; ++i) {
+ if (addresses->val[i].addr_type != KRB5_ADDRESS_NETBIOS) {
+ only_netbios = FALSE;
+ }
+ }
+
+ /* Windows sends it's netbios name, which I can only assume is
+ * used for the 'allowed workstations' check. This is painful,
+ * but we still want to check IP addresses if they happen to be
+ * present.
+ */
+
+ if(only_netbios)
+ return config->allow_null_ticket_addresses;
+
+ ret = krb5_sockaddr2address (context, from, &addr);
+ if(ret)
+ return FALSE;
+
+ result = krb5_address_search(context, &addr, addresses);
+ krb5_free_address (context, &addr);
+ return result;
+}
+
+/*
+ *
+ */
+
+static krb5_boolean
+send_pac_p(krb5_context context, KDC_REQ *req)
+{
+ krb5_error_code ret;
+ PA_PAC_REQUEST pacreq;
+ const PA_DATA *pa;
+ int i = 0;
+
+ pa = _kdc_find_padata(req, &i, KRB5_PADATA_PA_PAC_REQUEST);
+ if (pa == NULL)
+ return TRUE;
+
+ ret = decode_PA_PAC_REQUEST(pa->padata_value.data,
+ pa->padata_value.length,
+ &pacreq,
+ NULL);
+ if (ret)
+ return TRUE;
+ i = pacreq.include_pac;
+ free_PA_PAC_REQUEST(&pacreq);
+ if (i == 0)
+ return FALSE;
+ return TRUE;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_as_rep(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ const krb5_data *req_buffer,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr *from_addr,
+ int datagram_reply)
+{
+ KDC_REQ_BODY *b = &req->req_body;
+ AS_REP rep;
+ KDCOptions f = b->kdc_options;
+ hdb_entry_ex *client = NULL, *server = NULL;
+ krb5_enctype cetype, setype, sessionetype;
+ krb5_data e_data;
+ EncTicketPart et;
+ EncKDCRepPart ek;
+ krb5_principal client_princ = NULL, server_princ = NULL;
+ char *client_name = NULL, *server_name = NULL;
+ krb5_error_code ret = 0;
+ const char *e_text = NULL;
+ krb5_crypto crypto;
+ Key *ckey, *skey;
+ EncryptionKey *reply_key;
+ int flags = 0;
+#ifdef PKINIT
+ pk_client_params *pkp = NULL;
+#endif
+
+ memset(&rep, 0, sizeof(rep));
+ krb5_data_zero(&e_data);
+
+ if (f.canonicalize)
+ flags |= HDB_F_CANON;
+
+ if(b->sname == NULL){
+ ret = KRB5KRB_ERR_GENERIC;
+ e_text = "No server in request";
+ } else{
+ ret = _krb5_principalname2krb5_principal (context,
+ &server_princ,
+ *(b->sname),
+ b->realm);
+ if (ret == 0)
+ ret = krb5_unparse_name(context, server_princ, &server_name);
+ }
+ if (ret) {
+ kdc_log(context, config, 0,
+ "AS-REQ malformed server name from %s", from);
+ goto out;
+ }
+
+ if(b->cname == NULL){
+ ret = KRB5KRB_ERR_GENERIC;
+ e_text = "No client in request";
+ } else {
+
+ if (b->cname->name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ if (b->cname->name_string.len != 1) {
+ kdc_log(context, config, 0,
+ "AS-REQ malformed canon request from %s, "
+ "enterprise name with %d name components",
+ from, b->cname->name_string.len);
+ ret = KRB5_PARSE_MALFORMED;
+ goto out;
+ }
+ ret = krb5_parse_name(context, b->cname->name_string.val[0],
+ &client_princ);
+ if (ret)
+ goto out;
+ } else {
+ ret = _krb5_principalname2krb5_principal (context,
+ &client_princ,
+ *(b->cname),
+ b->realm);
+ if (ret)
+ goto out;
+ }
+ ret = krb5_unparse_name(context, client_princ, &client_name);
+ }
+ if (ret) {
+ kdc_log(context, config, 0,
+ "AS-REQ malformed client name from %s", from);
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "AS-REQ %s from %s for %s",
+ client_name, from, server_name);
+
+ ret = _kdc_db_fetch(context, config, client_princ,
+ HDB_F_GET_CLIENT | flags, NULL, &client);
+ if(ret){
+ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name,
+ krb5_get_err_text(context, ret));
+ ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, server_princ,
+ HDB_F_GET_SERVER|HDB_F_GET_KRBTGT,
+ NULL, &server);
+ if(ret){
+ kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name,
+ krb5_get_err_text(context, ret));
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+
+ ret = _kdc_windc_client_access(context, client, req);
+ if(ret)
+ goto out;
+
+ ret = _kdc_check_flags(context, config,
+ client, client_name,
+ server, server_name,
+ TRUE);
+ if(ret)
+ goto out;
+
+ memset(&et, 0, sizeof(et));
+ memset(&ek, 0, sizeof(ek));
+
+ if(req->padata){
+ int i;
+ const PA_DATA *pa;
+ int found_pa = 0;
+
+ log_patypes(context, config, req->padata);
+
+#ifdef PKINIT
+ kdc_log(context, config, 5,
+ "Looking for PKINIT pa-data -- %s", client_name);
+
+ e_text = "No PKINIT PA found";
+
+ i = 0;
+ if ((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ)))
+ ;
+ if (pa == NULL) {
+ i = 0;
+ if((pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN)))
+ ;
+ }
+ if (pa) {
+ char *client_cert = NULL;
+
+ ret = _kdc_pk_rd_padata(context, config, req, pa, &pkp);
+ if (ret) {
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ kdc_log(context, config, 5,
+ "Failed to decode PKINIT PA-DATA -- %s",
+ client_name);
+ goto ts_enc;
+ }
+ if (ret == 0 && pkp == NULL)
+ goto ts_enc;
+
+ ret = _kdc_pk_check_client(context,
+ config,
+ client,
+ pkp,
+ &client_cert);
+ if (ret) {
+ e_text = "PKINIT certificate not allowed to "
+ "impersonate principal";
+ _kdc_pk_free_client_param(context, pkp);
+
+ kdc_log(context, config, 0, "%s", e_text);
+ pkp = NULL;
+ goto out;
+ }
+ found_pa = 1;
+ et.flags.pre_authent = 1;
+ kdc_log(context, config, 0,
+ "PKINIT pre-authentication succeeded -- %s using %s",
+ client_name, client_cert);
+ free(client_cert);
+ if (pkp)
+ goto preauth_done;
+ }
+ ts_enc:
+#endif
+ kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s",
+ client_name);
+
+ i = 0;
+ e_text = "No ENC-TS found";
+ while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
+ krb5_data ts_data;
+ PA_ENC_TS_ENC p;
+ size_t len;
+ EncryptedData enc_data;
+ Key *pa_key;
+ char *str;
+
+ found_pa = 1;
+
+ ret = decode_EncryptedData(pa->padata_value.data,
+ pa->padata_value.length,
+ &enc_data,
+ &len);
+ if (ret) {
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s",
+ client_name);
+ goto out;
+ }
+
+ ret = hdb_enctype2key(context, &client->entry,
+ enc_data.etype, &pa_key);
+ if(ret){
+ char *estr;
+ e_text = "No key matches pa-data";
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP;
+ if(krb5_enctype_to_string(context, enc_data.etype, &estr))
+ estr = NULL;
+ if(estr == NULL)
+ kdc_log(context, config, 5,
+ "No client key matching pa-data (%d) -- %s",
+ enc_data.etype, client_name);
+ else
+ kdc_log(context, config, 5,
+ "No client key matching pa-data (%s) -- %s",
+ estr, client_name);
+ free(estr);
+
+ free_EncryptedData(&enc_data);
+ continue;
+ }
+
+ try_next_key:
+ ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto);
+ if (ret) {
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ free_EncryptedData(&enc_data);
+ continue;
+ }
+
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_PA_ENC_TIMESTAMP,
+ &enc_data,
+ &ts_data);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ krb5_error_code ret2;
+ ret2 = krb5_enctype_to_string(context,
+ pa_key->key.keytype, &str);
+ if (ret2)
+ str = NULL;
+ kdc_log(context, config, 5,
+ "Failed to decrypt PA-DATA -- %s "
+ "(enctype %s) error %s",
+ client_name,
+ str ? str : "unknown enctype",
+ krb5_get_err_text(context, ret));
+ free(str);
+
+ if(hdb_next_enctype2key(context, &client->entry,
+ enc_data.etype, &pa_key) == 0)
+ goto try_next_key;
+ e_text = "Failed to decrypt PA-DATA";
+
+ free_EncryptedData(&enc_data);
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ continue;
+ }
+ free_EncryptedData(&enc_data);
+ ret = decode_PA_ENC_TS_ENC(ts_data.data,
+ ts_data.length,
+ &p,
+ &len);
+ krb5_data_free(&ts_data);
+ if(ret){
+ e_text = "Failed to decode PA-ENC-TS-ENC";
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ kdc_log(context, config,
+ 5, "Failed to decode PA-ENC-TS_ENC -- %s",
+ client_name);
+ continue;
+ }
+ free_PA_ENC_TS_ENC(&p);
+ if (abs(kdc_time - p.patimestamp) > context->max_skew) {
+ char client_time[100];
+
+ krb5_format_time(context, p.patimestamp,
+ client_time, sizeof(client_time), TRUE);
+
+ ret = KRB5KRB_AP_ERR_SKEW;
+ kdc_log(context, config, 0,
+ "Too large time skew, "
+ "client time %s is out by %u > %u seconds -- %s",
+ client_time,
+ (unsigned)abs(kdc_time - p.patimestamp),
+ context->max_skew,
+ client_name);
+#if 0
+ /* This code is from samba, needs testing */
+ /*
+ * the following is needed to make windows clients
+ * to retry using the timestamp in the error message
+ *
+ * this is maybe a bug in windows to not trying when e_text
+ * is present...
+ */
+ e_text = NULL;
+#else
+ e_text = "Too large time skew";
+#endif
+ goto out;
+ }
+ et.flags.pre_authent = 1;
+
+ ret = krb5_enctype_to_string(context,pa_key->key.keytype, &str);
+ if (ret)
+ str = NULL;
+
+ kdc_log(context, config, 2,
+ "ENC-TS Pre-authentication succeeded -- %s using %s",
+ client_name, str ? str : "unknown enctype");
+ free(str);
+ break;
+ }
+#ifdef PKINIT
+ preauth_done:
+#endif
+ if(found_pa == 0 && config->require_preauth)
+ goto use_pa;
+ /* We come here if we found a pa-enc-timestamp, but if there
+ was some problem with it, other than too large skew */
+ if(found_pa && et.flags.pre_authent == 0){
+ kdc_log(context, config, 0, "%s -- %s", e_text, client_name);
+ e_text = NULL;
+ goto out;
+ }
+ }else if (config->require_preauth
+ || client->entry.flags.require_preauth
+ || server->entry.flags.require_preauth) {
+ METHOD_DATA method_data;
+ PA_DATA *pa;
+ unsigned char *buf;
+ size_t len;
+
+ use_pa:
+ method_data.len = 0;
+ method_data.val = NULL;
+
+ ret = realloc_method_data(&method_data);
+ pa = &method_data.val[method_data.len-1];
+ pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP;
+ pa->padata_value.length = 0;
+ pa->padata_value.data = NULL;
+
+#ifdef PKINIT
+ ret = realloc_method_data(&method_data);
+ pa = &method_data.val[method_data.len-1];
+ pa->padata_type = KRB5_PADATA_PK_AS_REQ;
+ pa->padata_value.length = 0;
+ pa->padata_value.data = NULL;
+
+ ret = realloc_method_data(&method_data);
+ pa = &method_data.val[method_data.len-1];
+ pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN;
+ pa->padata_value.length = 0;
+ pa->padata_value.data = NULL;
+#endif
+
+ /*
+ * RFC4120 requires:
+ * - If the client only knows about old enctypes, then send
+ * both info replies (we send 'info' first in the list).
+ * - If the client is 'modern', because it knows about 'new'
+ * enctype types, then only send the 'info2' reply.
+ */
+
+ /* XXX check ret */
+ if (only_older_enctype_p(req))
+ ret = get_pa_etype_info(context, config,
+ &method_data, &client->entry,
+ b->etype.val, b->etype.len);
+ /* XXX check ret */
+ ret = get_pa_etype_info2(context, config, &method_data,
+ &client->entry, b->etype.val, b->etype.len);
+
+
+ ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret);
+ free_METHOD_DATA(&method_data);
+
+ e_data.data = buf;
+ e_data.length = len;
+ e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ",
+
+ ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
+
+ kdc_log(context, config, 0,
+ "No preauth found, returning PREAUTH-REQUIRED -- %s",
+ client_name);
+ goto out;
+ }
+
+ /*
+ * Find the client key (for preauth ENC-TS verification and reply
+ * encryption). Then the best encryption type for the KDC and
+ * last the best session key that shared between the client and
+ * KDC runtime enctypes.
+ */
+
+ ret = _kdc_find_etype(context, client, b->etype.val, b->etype.len,
+ &ckey, &cetype);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Client (%s) has no support for etypes", client_name);
+ goto out;
+ }
+
+ ret = _kdc_get_preferred_key(context, config,
+ server, server_name,
+ &setype, &skey);
+ if(ret)
+ goto out;
+
+ /*
+ * Select a session enctype from the list of the crypto systems
+ * supported enctype, is supported by the client and is one of the
+ * enctype of the enctype of the krbtgt.
+ *
+ * The later is used as a hint what enctype all KDC are supporting
+ * to make sure a newer version of KDC wont generate a session
+ * enctype that and older version of a KDC in the same realm can't
+ * decrypt.
+ *
+ * But if the KDC admin is paranoid and doesn't want to have "no
+ * the best" enctypes on the krbtgt, lets save the best pick from
+ * the client list and hope that that will work for any other
+ * KDCs.
+ */
+ {
+ const krb5_enctype *p;
+ krb5_enctype clientbest = ETYPE_NULL;
+ int i, j;
+
+ p = krb5_kerberos_enctypes(context);
+
+ sessionetype = ETYPE_NULL;
+
+ for (i = 0; p[i] != ETYPE_NULL && sessionetype == ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+
+ for (j = 0; j < b->etype.len && sessionetype == ETYPE_NULL; j++) {
+ Key *dummy;
+ /* check with client */
+ if (p[i] != b->etype.val[j])
+ continue;
+ /* save best of union of { client, crypto system } */
+ if (clientbest == ETYPE_NULL)
+ clientbest = p[i];
+ /* check with krbtgt */
+ ret = hdb_enctype2key(context, &server->entry, p[i], &dummy);
+ if (ret)
+ continue;
+ sessionetype = p[i];
+ }
+ }
+ /* if krbtgt had no shared keys with client, pick clients best */
+ if (clientbest != ETYPE_NULL && sessionetype == ETYPE_NULL) {
+ sessionetype = clientbest;
+ } else if (sessionetype == ETYPE_NULL) {
+ kdc_log(context, config, 0,
+ "Client (%s) from %s has no common enctypes with KDC"
+ "to use for the session key",
+ client_name, from);
+ goto out;
+ }
+ }
+
+ log_as_req(context, config, cetype, setype, b);
+
+ if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey
+ || (f.request_anonymous && !config->allow_anonymous)) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ kdc_log(context, config, 0, "Bad KDC options -- %s", client_name);
+ goto out;
+ }
+
+ rep.pvno = 5;
+ rep.msg_type = krb_as_rep;
+ copy_Realm(&client->entry.principal->realm, &rep.crealm);
+ if (f.request_anonymous)
+ _kdc_make_anonymous_principalname (&rep.cname);
+ else
+ _krb5_principal2principalname(&rep.cname,
+ client->entry.principal);
+ rep.ticket.tkt_vno = 5;
+ copy_Realm(&server->entry.principal->realm, &rep.ticket.realm);
+ _krb5_principal2principalname(&rep.ticket.sname,
+ server->entry.principal);
+ /* java 1.6 expects the name to be the same type, lets allow that
+ * uncomplicated name-types. */
+#define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t)
+ if (CNT(b, UNKNOWN) || CNT(b, PRINCIPAL) || CNT(b, SRV_INST) || CNT(b, SRV_HST) || CNT(b, SRV_XHST))
+ rep.ticket.sname.name_type = b->sname->name_type;
+#undef CNT
+
+ et.flags.initial = 1;
+ if(client->entry.flags.forwardable && server->entry.flags.forwardable)
+ et.flags.forwardable = f.forwardable;
+ else if (f.forwardable) {
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Ticket may not be forwardable -- %s", client_name);
+ goto out;
+ }
+ if(client->entry.flags.proxiable && server->entry.flags.proxiable)
+ et.flags.proxiable = f.proxiable;
+ else if (f.proxiable) {
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Ticket may not be proxiable -- %s", client_name);
+ goto out;
+ }
+ if(client->entry.flags.postdate && server->entry.flags.postdate)
+ et.flags.may_postdate = f.allow_postdate;
+ else if (f.allow_postdate){
+ ret = KRB5KDC_ERR_POLICY;
+ kdc_log(context, config, 0,
+ "Ticket may not be postdatable -- %s", client_name);
+ goto out;
+ }
+
+ /* check for valid set of addresses */
+ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ kdc_log(context, config, 0,
+ "Bad address list requested -- %s", client_name);
+ goto out;
+ }
+
+ ret = krb5_generate_random_keyblock(context, sessionetype, &et.key);
+ if (ret)
+ goto out;
+ copy_PrincipalName(&rep.cname, &et.cname);
+ copy_Realm(&rep.crealm, &et.crealm);
+
+ {
+ time_t start;
+ time_t t;
+
+ start = et.authtime = kdc_time;
+
+ if(f.postdated && req->req_body.from){
+ ALLOC(et.starttime);
+ start = *et.starttime = *req->req_body.from;
+ et.flags.invalid = 1;
+ et.flags.postdated = 1; /* XXX ??? */
+ }
+ _kdc_fix_time(&b->till);
+ t = *b->till;
+
+ /* be careful not overflowing */
+
+ if(client->entry.max_life)
+ t = start + min(t - start, *client->entry.max_life);
+ if(server->entry.max_life)
+ t = start + min(t - start, *server->entry.max_life);
+#if 0
+ t = min(t, start + realm->max_life);
+#endif
+ et.endtime = t;
+ if(f.renewable_ok && et.endtime < *b->till){
+ f.renewable = 1;
+ if(b->rtime == NULL){
+ ALLOC(b->rtime);
+ *b->rtime = 0;
+ }
+ if(*b->rtime < *b->till)
+ *b->rtime = *b->till;
+ }
+ if(f.renewable && b->rtime){
+ t = *b->rtime;
+ if(t == 0)
+ t = MAX_TIME;
+ if(client->entry.max_renew)
+ t = start + min(t - start, *client->entry.max_renew);
+ if(server->entry.max_renew)
+ t = start + min(t - start, *server->entry.max_renew);
+#if 0
+ t = min(t, start + realm->max_renew);
+#endif
+ ALLOC(et.renew_till);
+ *et.renew_till = t;
+ et.flags.renewable = 1;
+ }
+ }
+
+ if (f.request_anonymous)
+ et.flags.anonymous = 1;
+
+ if(b->addresses){
+ ALLOC(et.caddr);
+ copy_HostAddresses(b->addresses, et.caddr);
+ }
+
+ et.transited.tr_type = DOMAIN_X500_COMPRESS;
+ krb5_data_zero(&et.transited.contents);
+
+ copy_EncryptionKey(&et.key, &ek.key);
+
+ /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded
+ * as 0 and as 0x80 (meaning indefinite length) apart, and is thus
+ * incapable of correctly decoding SEQUENCE OF's of zero length.
+ *
+ * To fix this, always send at least one no-op last_req
+ *
+ * If there's a pw_end or valid_end we will use that,
+ * otherwise just a dummy lr.
+ */
+ ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val));
+ if (ek.last_req.val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ek.last_req.len = 0;
+ if (client->entry.pw_end
+ && (config->kdc_warn_pwexpire == 0
+ || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) {
+ ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME;
+ ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end;
+ ++ek.last_req.len;
+ }
+ if (client->entry.valid_end) {
+ ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME;
+ ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end;
+ ++ek.last_req.len;
+ }
+ if (ek.last_req.len == 0) {
+ ek.last_req.val[ek.last_req.len].lr_type = LR_NONE;
+ ek.last_req.val[ek.last_req.len].lr_value = 0;
+ ++ek.last_req.len;
+ }
+ ek.nonce = b->nonce;
+ if (client->entry.valid_end || client->entry.pw_end) {
+ ALLOC(ek.key_expiration);
+ if (client->entry.valid_end) {
+ if (client->entry.pw_end)
+ *ek.key_expiration = min(*client->entry.valid_end,
+ *client->entry.pw_end);
+ else
+ *ek.key_expiration = *client->entry.valid_end;
+ } else
+ *ek.key_expiration = *client->entry.pw_end;
+ } else
+ ek.key_expiration = NULL;
+ ek.flags = et.flags;
+ ek.authtime = et.authtime;
+ if (et.starttime) {
+ ALLOC(ek.starttime);
+ *ek.starttime = *et.starttime;
+ }
+ ek.endtime = et.endtime;
+ if (et.renew_till) {
+ ALLOC(ek.renew_till);
+ *ek.renew_till = *et.renew_till;
+ }
+ copy_Realm(&rep.ticket.realm, &ek.srealm);
+ copy_PrincipalName(&rep.ticket.sname, &ek.sname);
+ if(et.caddr){
+ ALLOC(ek.caddr);
+ copy_HostAddresses(et.caddr, ek.caddr);
+ }
+
+ ALLOC(rep.padata);
+ rep.padata->len = 0;
+ rep.padata->val = NULL;
+
+ reply_key = &ckey->key;
+#if PKINIT
+ if (pkp) {
+ ret = _kdc_pk_mk_pa_reply(context, config, pkp, client,
+ req, req_buffer,
+ &reply_key, rep.padata);
+ if (ret)
+ goto out;
+ ret = _kdc_add_inital_verified_cas(context,
+ config,
+ pkp,
+ &et);
+ if (ret)
+ goto out;
+ }
+#endif
+
+ set_salt_padata (rep.padata, ckey->salt);
+
+ /* Add signing of alias referral */
+ if (f.canonicalize) {
+ PA_ClientCanonicalized canon;
+ krb5_data data;
+ PA_DATA pa;
+ krb5_crypto crypto;
+ size_t len;
+
+ memset(&canon, 0, sizeof(canon));
+
+ canon.names.requested_name = *b->cname;
+ canon.names.real_name = client->entry.principal->name;
+
+ ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
+ &canon.names, &len, ret);
+ if (ret)
+ goto out;
+ if (data.length != len)
+ krb5_abortx(context, "internal asn.1 error");
+
+ /* sign using "returned session key" */
+ ret = krb5_crypto_init(context, &et.key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ goto out;
+ }
+
+ ret = krb5_create_checksum(context, crypto,
+ KRB5_KU_CANONICALIZED_NAMES, 0,
+ data.data, data.length,
+ &canon.canon_checksum);
+ free(data.data);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(PA_ClientCanonicalized, data.data, data.length,
+ &canon, &len, ret);
+ free_Checksum(&canon.canon_checksum);
+ if (ret)
+ goto out;
+ if (data.length != len)
+ krb5_abortx(context, "internal asn.1 error");
+
+ pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED;
+ pa.padata_value = data;
+ ret = add_METHOD_DATA(rep.padata, &pa);
+ free(data.data);
+ if (ret)
+ goto out;
+ }
+
+ if (rep.padata->len == 0) {
+ free(rep.padata);
+ rep.padata = NULL;
+ }
+
+ /* Add the PAC */
+ if (send_pac_p(context, req)) {
+ krb5_pac p = NULL;
+ krb5_data data;
+
+ ret = _kdc_pac_generate(context, client, &p);
+ if (ret) {
+ kdc_log(context, config, 0, "PAC generation failed for -- %s",
+ client_name);
+ goto out;
+ }
+ if (p != NULL) {
+ ret = _krb5_pac_sign(context, p, et.authtime,
+ client->entry.principal,
+ &skey->key, /* Server key */
+ &skey->key, /* FIXME: should be krbtgt key */
+ &data);
+ krb5_pac_free(context, p);
+ if (ret) {
+ kdc_log(context, config, 0, "PAC signing failed for -- %s",
+ client_name);
+ goto out;
+ }
+
+ ret = _kdc_tkt_add_if_relevant_ad(context, &et,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &data);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+ }
+ }
+
+ _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime,
+ et.endtime, et.renew_till);
+
+ /* do this as the last thing since this signs the EncTicketPart */
+ ret = _kdc_add_KRB5SignedPath(context,
+ config,
+ server,
+ setype,
+ NULL,
+ NULL,
+ &et);
+ if (ret)
+ goto out;
+
+ ret = _kdc_encode_reply(context, config,
+ &rep, &et, &ek, setype, server->entry.kvno,
+ &skey->key, client->entry.kvno,
+ reply_key, &e_text, reply);
+ free_EncTicketPart(&et);
+ free_EncKDCRepPart(&ek);
+ if (ret)
+ goto out;
+
+ /* */
+ if (datagram_reply && reply->length > config->max_datagram_reply_length) {
+ krb5_data_free(reply);
+ ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
+ e_text = "Reply packet too large";
+ }
+
+out:
+ free_AS_REP(&rep);
+ if(ret){
+ krb5_mk_error(context,
+ ret,
+ e_text,
+ (e_data.data ? &e_data : NULL),
+ client_princ,
+ server_princ,
+ NULL,
+ NULL,
+ reply);
+ ret = 0;
+ }
+#ifdef PKINIT
+ if (pkp)
+ _kdc_pk_free_client_param(context, pkp);
+#endif
+ if (e_data.data)
+ free(e_data.data);
+ if (client_princ)
+ krb5_free_principal(context, client_princ);
+ free(client_name);
+ if (server_princ)
+ krb5_free_principal(context, server_princ);
+ free(server_name);
+ if(client)
+ _kdc_free_ent(context, client);
+ if(server)
+ _kdc_free_ent(context, server);
+ return ret;
+}
+
+/*
+ * Add the AuthorizationData `data´ of `type´ to the last element in
+ * the sequence of authorization_data in `tkt´ wrapped in an IF_RELEVANT
+ */
+
+krb5_error_code
+_kdc_tkt_add_if_relevant_ad(krb5_context context,
+ EncTicketPart *tkt,
+ int type,
+ const krb5_data *data)
+{
+ krb5_error_code ret;
+ size_t size;
+
+ if (tkt->authorization_data == NULL) {
+ tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data));
+ if (tkt->authorization_data == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ return ENOMEM;
+ }
+ }
+
+ /* add the entry to the last element */
+ {
+ AuthorizationData ad = { 0, NULL };
+ AuthorizationDataElement ade;
+
+ ade.ad_type = type;
+ ade.ad_data = *data;
+
+ ret = add_AuthorizationData(&ad, &ade);
+ if (ret) {
+ krb5_set_error_string(context, "add AuthorizationData failed");
+ return ret;
+ }
+
+ ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData,
+ ade.ad_data.data, ade.ad_data.length,
+ &ad, &size, ret);
+ free_AuthorizationData(&ad);
+ if (ret) {
+ krb5_set_error_string(context, "ASN.1 encode of "
+ "AuthorizationData failed");
+ return ret;
+ }
+ if (ade.ad_data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ ret = add_AuthorizationData(tkt->authorization_data, &ade);
+ der_free_octet_string(&ade.ad_data);
+ if (ret) {
+ krb5_set_error_string(context, "add AuthorizationData failed");
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c
new file mode 100644
index 0000000000000..32bdee9799ca8
--- /dev/null
+++ b/kdc/krb5tgs.c
@@ -0,0 +1,1914 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: krb5tgs.c 22071 2007-11-14 20:04:50Z lha $");
+
+/*
+ * return the realm of a krbtgt-ticket or NULL
+ */
+
+static Realm
+get_krbtgt_realm(const PrincipalName *p)
+{
+ if(p->name_string.len == 2
+ && strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0)
+ return p->name_string.val[1];
+ else
+ return NULL;
+}
+
+/*
+ * The KDC might add a signed path to the ticket authorization data
+ * field. This is to avoid server impersonating clients and the
+ * request constrained delegation.
+ *
+ * This is done by storing a KRB5_AUTHDATA_IF_RELEVANT with a single
+ * entry of type KRB5SignedPath.
+ */
+
+static krb5_error_code
+find_KRB5SignedPath(krb5_context context,
+ const AuthorizationData *ad,
+ krb5_data *data)
+{
+ AuthorizationData child;
+ krb5_error_code ret;
+ int pos;
+
+ if (ad == NULL || ad->len == 0)
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ pos = ad->len - 1;
+
+ if (ad->val[pos].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ ret = decode_AuthorizationData(ad->val[pos].ad_data.data,
+ ad->val[pos].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to decode "
+ "IF_RELEVANT with %d", ret);
+ return ret;
+ }
+
+ if (child.len != 1) {
+ free_AuthorizationData(&child);
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ }
+
+ if (child.val[0].ad_type != KRB5_AUTHDATA_SIGNTICKET) {
+ free_AuthorizationData(&child);
+ return KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ }
+
+ if (data)
+ ret = der_copy_octet_string(&child.val[0].ad_data, data);
+ free_AuthorizationData(&child);
+ return ret;
+}
+
+krb5_error_code
+_kdc_add_KRB5SignedPath(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype enctype,
+ krb5_const_principal server,
+ KRB5SignedPathPrincipals *principals,
+ EncTicketPart *tkt)
+{
+ krb5_error_code ret;
+ KRB5SignedPath sp;
+ krb5_data data;
+ krb5_crypto crypto = NULL;
+ size_t size;
+
+ if (server && principals) {
+ ret = add_KRB5SignedPathPrincipals(principals, server);
+ if (ret)
+ return ret;
+ }
+
+ {
+ KRB5SignedPathData spd;
+
+ spd.encticket = *tkt;
+ spd.delegated = principals;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
+ &spd, &size, ret);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+ }
+
+ {
+ Key *key;
+ ret = hdb_enctype2key(context, &krbtgt->entry, enctype, &key);
+ if (ret == 0)
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ return ret;
+ }
+ }
+
+ /*
+ * Fill in KRB5SignedPath
+ */
+
+ sp.etype = enctype;
+ sp.delegated = principals;
+
+ ret = krb5_create_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH, 0,
+ data.data, data.length, &sp.cksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPath, data.data, data.length, &sp, &size, ret);
+ free_Checksum(&sp.cksum);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+
+ /*
+ * Add IF-RELEVANT(KRB5SignedPath) to the last slot in
+ * authorization data field.
+ */
+
+ ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
+ KRB5_AUTHDATA_SIGNTICKET, &data);
+ krb5_data_free(&data);
+
+ return ret;
+}
+
+static krb5_error_code
+check_KRB5SignedPath(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *krbtgt,
+ EncTicketPart *tkt,
+ KRB5SignedPathPrincipals **delegated,
+ int require_signedpath)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ krb5_crypto crypto = NULL;
+
+ *delegated = NULL;
+
+ ret = find_KRB5SignedPath(context, tkt->authorization_data, &data);
+ if (ret == 0) {
+ KRB5SignedPathData spd;
+ KRB5SignedPath sp;
+ AuthorizationData *ad;
+ size_t size;
+
+ ret = decode_KRB5SignedPath(data.data, data.length, &sp, NULL);
+ krb5_data_free(&data);
+ if (ret)
+ return ret;
+
+ spd.encticket = *tkt;
+ /* the KRB5SignedPath is the last entry */
+ ad = spd.encticket.authorization_data;
+ if (--ad->len == 0)
+ spd.encticket.authorization_data = NULL;
+ spd.delegated = sp.delegated;
+
+ ASN1_MALLOC_ENCODE(KRB5SignedPathData, data.data, data.length,
+ &spd, &size, ret);
+ ad->len++;
+ spd.encticket.authorization_data = ad;
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ {
+ Key *key;
+ ret = hdb_enctype2key(context, &krbtgt->entry, sp.etype, &key);
+ if (ret == 0)
+ ret = krb5_crypto_init(context, &key->key, 0, &crypto);
+ if (ret) {
+ free(data.data);
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+ }
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_KRB5SIGNEDPATH,
+ data.data, data.length,
+ &sp.cksum);
+ krb5_crypto_destroy(context, crypto);
+ free(data.data);
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ return ret;
+ }
+
+ if (sp.delegated) {
+
+ *delegated = malloc(sizeof(*sp.delegated));
+ if (*delegated == NULL) {
+ free_KRB5SignedPath(&sp);
+ return ENOMEM;
+ }
+
+ ret = copy_KRB5SignedPathPrincipals(*delegated, sp.delegated);
+ if (ret) {
+ free_KRB5SignedPath(&sp);
+ free(*delegated);
+ *delegated = NULL;
+ return ret;
+ }
+ }
+ free_KRB5SignedPath(&sp);
+
+ } else {
+ if (require_signedpath)
+ return KRB5KDC_ERR_BADOPTION;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_PAC(krb5_context context,
+ krb5_kdc_configuration *config,
+ const krb5_principal client_principal,
+ hdb_entry_ex *client,
+ hdb_entry_ex *server,
+ const EncryptionKey *server_key,
+ const EncryptionKey *krbtgt_key,
+ EncTicketPart *tkt,
+ krb5_data *rspac,
+ int *require_signedpath)
+{
+ AuthorizationData *ad = tkt->authorization_data;
+ unsigned i, j;
+ krb5_error_code ret;
+
+ if (ad == NULL || ad->len == 0)
+ return 0;
+
+ for (i = 0; i < ad->len; i++) {
+ AuthorizationData child;
+
+ if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ continue;
+
+ ret = decode_AuthorizationData(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to decode "
+ "IF_RELEVANT with %d", ret);
+ return ret;
+ }
+ for (j = 0; j < child.len; j++) {
+
+ if (child.val[j].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
+ krb5_pac pac;
+
+ /* Found PAC */
+ ret = krb5_pac_parse(context,
+ child.val[j].ad_data.data,
+ child.val[j].ad_data.length,
+ &pac);
+ free_AuthorizationData(&child);
+ if (ret)
+ return ret;
+
+ ret = krb5_pac_verify(context, pac, tkt->authtime,
+ client_principal,
+ krbtgt_key, NULL);
+ if (ret) {
+ krb5_pac_free(context, pac);
+ return ret;
+ }
+
+ ret = _kdc_pac_verify(context, client_principal,
+ client, server, &pac);
+ if (ret) {
+ krb5_pac_free(context, pac);
+ return ret;
+ }
+ *require_signedpath = 0;
+
+ ret = _krb5_pac_sign(context, pac, tkt->authtime,
+ client_principal,
+ server_key, krbtgt_key, rspac);
+
+ krb5_pac_free(context, pac);
+
+ return ret;
+ }
+ }
+ free_AuthorizationData(&child);
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_tgs_flags(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
+{
+ KDCOptions f = b->kdc_options;
+
+ if(f.validate){
+ if(!tgt->flags.invalid || tgt->starttime == NULL){
+ kdc_log(context, config, 0,
+ "Bad request to validate ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ if(*tgt->starttime > kdc_time){
+ kdc_log(context, config, 0,
+ "Early request to validate ticket");
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ /* XXX tkt = tgt */
+ et->flags.invalid = 0;
+ }else if(tgt->flags.invalid){
+ kdc_log(context, config, 0,
+ "Ticket-granting ticket has INVALID flag set");
+ return KRB5KRB_AP_ERR_TKT_INVALID;
+ }
+
+ if(f.forwardable){
+ if(!tgt->flags.forwardable){
+ kdc_log(context, config, 0,
+ "Bad request for forwardable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.forwardable = 1;
+ }
+ if(f.forwarded){
+ if(!tgt->flags.forwardable){
+ kdc_log(context, config, 0,
+ "Request to forward non-forwardable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.forwarded = 1;
+ et->caddr = b->addresses;
+ }
+ if(tgt->flags.forwarded)
+ et->flags.forwarded = 1;
+
+ if(f.proxiable){
+ if(!tgt->flags.proxiable){
+ kdc_log(context, config, 0,
+ "Bad request for proxiable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.proxiable = 1;
+ }
+ if(f.proxy){
+ if(!tgt->flags.proxiable){
+ kdc_log(context, config, 0,
+ "Request to proxy non-proxiable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.proxy = 1;
+ et->caddr = b->addresses;
+ }
+ if(tgt->flags.proxy)
+ et->flags.proxy = 1;
+
+ if(f.allow_postdate){
+ if(!tgt->flags.may_postdate){
+ kdc_log(context, config, 0,
+ "Bad request for post-datable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.may_postdate = 1;
+ }
+ if(f.postdated){
+ if(!tgt->flags.may_postdate){
+ kdc_log(context, config, 0,
+ "Bad request for postdated ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ if(b->from)
+ *et->starttime = *b->from;
+ et->flags.postdated = 1;
+ et->flags.invalid = 1;
+ }else if(b->from && *b->from > kdc_time + context->max_skew){
+ kdc_log(context, config, 0, "Ticket cannot be postdated");
+ return KRB5KDC_ERR_CANNOT_POSTDATE;
+ }
+
+ if(f.renewable){
+ if(!tgt->flags.renewable){
+ kdc_log(context, config, 0,
+ "Bad request for renewable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ et->flags.renewable = 1;
+ ALLOC(et->renew_till);
+ _kdc_fix_time(&b->rtime);
+ *et->renew_till = *b->rtime;
+ }
+ if(f.renew){
+ time_t old_life;
+ if(!tgt->flags.renewable || tgt->renew_till == NULL){
+ kdc_log(context, config, 0,
+ "Request to renew non-renewable ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+ old_life = tgt->endtime;
+ if(tgt->starttime)
+ old_life -= *tgt->starttime;
+ else
+ old_life -= tgt->authtime;
+ et->endtime = *et->starttime + old_life;
+ if (et->renew_till != NULL)
+ et->endtime = min(*et->renew_till, et->endtime);
+ }
+
+#if 0
+ /* checks for excess flags */
+ if(f.request_anonymous && !config->allow_anonymous){
+ kdc_log(context, config, 0,
+ "Request for anonymous ticket");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+#endif
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+check_constrained_delegation(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *client,
+ krb5_const_principal server)
+{
+ const HDB_Ext_Constrained_delegation_acl *acl;
+ krb5_error_code ret;
+ int i;
+
+ ret = hdb_entry_get_ConstrainedDelegACL(&client->entry, &acl);
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ if (acl) {
+ for (i = 0; i < acl->len; i++) {
+ if (krb5_principal_compare(context, server, &acl->val[i]) == TRUE)
+ return 0;
+ }
+ }
+ kdc_log(context, config, 0,
+ "Bad request for constrained delegation");
+ return KRB5KDC_ERR_BADOPTION;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+verify_flags (krb5_context context,
+ krb5_kdc_configuration *config,
+ const EncTicketPart *et,
+ const char *pstr)
+{
+ if(et->endtime < kdc_time){
+ kdc_log(context, config, 0, "Ticket expired (%s)", pstr);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+ if(et->flags.invalid){
+ kdc_log(context, config, 0, "Ticket not valid (%s)", pstr);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+fix_transited_encoding(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_boolean check_policy,
+ const TransitedEncoding *tr,
+ EncTicketPart *et,
+ const char *client_realm,
+ const char *server_realm,
+ const char *tgt_realm)
+{
+ krb5_error_code ret = 0;
+ char **realms, **tmp;
+ int num_realms;
+ int i;
+
+ switch (tr->tr_type) {
+ case DOMAIN_X500_COMPRESS:
+ break;
+ case 0:
+ /*
+ * Allow empty content of type 0 because that is was Microsoft
+ * generates in their TGT.
+ */
+ if (tr->contents.length == 0)
+ break;
+ kdc_log(context, config, 0,
+ "Transited type 0 with non empty content");
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+ default:
+ kdc_log(context, config, 0,
+ "Unknown transited type: %u", tr->tr_type);
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+ }
+
+ ret = krb5_domain_x500_decode(context,
+ tr->contents,
+ &realms,
+ &num_realms,
+ client_realm,
+ server_realm);
+ if(ret){
+ krb5_warn(context, ret,
+ "Decoding transited encoding");
+ return ret;
+ }
+ if(strcmp(client_realm, tgt_realm) && strcmp(server_realm, tgt_realm)) {
+ /* not us, so add the previous realm to transited set */
+ if (num_realms < 0 || num_realms + 1 > UINT_MAX/sizeof(*realms)) {
+ ret = ERANGE;
+ goto free_realms;
+ }
+ tmp = realloc(realms, (num_realms + 1) * sizeof(*realms));
+ if(tmp == NULL){
+ ret = ENOMEM;
+ goto free_realms;
+ }
+ realms = tmp;
+ realms[num_realms] = strdup(tgt_realm);
+ if(realms[num_realms] == NULL){
+ ret = ENOMEM;
+ goto free_realms;
+ }
+ num_realms++;
+ }
+ if(num_realms == 0) {
+ if(strcmp(client_realm, server_realm))
+ kdc_log(context, config, 0,
+ "cross-realm %s -> %s", client_realm, server_realm);
+ } else {
+ size_t l = 0;
+ char *rs;
+ for(i = 0; i < num_realms; i++)
+ l += strlen(realms[i]) + 2;
+ rs = malloc(l);
+ if(rs != NULL) {
+ *rs = '\0';
+ for(i = 0; i < num_realms; i++) {
+ if(i > 0)
+ strlcat(rs, ", ", l);
+ strlcat(rs, realms[i], l);
+ }
+ kdc_log(context, config, 0,
+ "cross-realm %s -> %s via [%s]",
+ client_realm, server_realm, rs);
+ free(rs);
+ }
+ }
+ if(check_policy) {
+ ret = krb5_check_transited(context, client_realm,
+ server_realm,
+ realms, num_realms, NULL);
+ if(ret) {
+ krb5_warn(context, ret, "cross-realm %s -> %s",
+ client_realm, server_realm);
+ goto free_realms;
+ }
+ et->flags.transited_policy_checked = 1;
+ }
+ et->transited.tr_type = DOMAIN_X500_COMPRESS;
+ ret = krb5_domain_x500_encode(realms, num_realms, &et->transited.contents);
+ if(ret)
+ krb5_warn(context, ret, "Encoding transited encoding");
+ free_realms:
+ for(i = 0; i < num_realms; i++)
+ free(realms[i]);
+ free(realms);
+ return ret;
+}
+
+
+static krb5_error_code
+tgs_make_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b,
+ krb5_const_principal tgt_name,
+ const EncTicketPart *tgt,
+ const EncryptionKey *serverkey,
+ const krb5_keyblock *sessionkey,
+ krb5_kvno kvno,
+ AuthorizationData *auth_data,
+ hdb_entry_ex *server,
+ const char *server_name,
+ hdb_entry_ex *client,
+ krb5_principal client_principal,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype krbtgt_etype,
+ KRB5SignedPathPrincipals *spp,
+ const krb5_data *rspac,
+ const char **e_text,
+ krb5_data *reply)
+{
+ KDC_REP rep;
+ EncKDCRepPart ek;
+ EncTicketPart et;
+ KDCOptions f = b->kdc_options;
+ krb5_error_code ret;
+
+ memset(&rep, 0, sizeof(rep));
+ memset(&et, 0, sizeof(et));
+ memset(&ek, 0, sizeof(ek));
+
+ rep.pvno = 5;
+ rep.msg_type = krb_tgs_rep;
+
+ et.authtime = tgt->authtime;
+ _kdc_fix_time(&b->till);
+ et.endtime = min(tgt->endtime, *b->till);
+ ALLOC(et.starttime);
+ *et.starttime = kdc_time;
+
+ ret = check_tgs_flags(context, config, b, tgt, &et);
+ if(ret)
+ goto out;
+
+ /* We should check the transited encoding if:
+ 1) the request doesn't ask not to be checked
+ 2) globally enforcing a check
+ 3) principal requires checking
+ 4) we allow non-check per-principal, but principal isn't marked as allowing this
+ 5) we don't globally allow this
+ */
+
+#define GLOBAL_FORCE_TRANSITED_CHECK \
+ (config->trpolicy == TRPOLICY_ALWAYS_CHECK)
+#define GLOBAL_ALLOW_PER_PRINCIPAL \
+ (config->trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL)
+#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK \
+ (config->trpolicy == TRPOLICY_ALWAYS_HONOUR_REQUEST)
+
+/* these will consult the database in future release */
+#define PRINCIPAL_FORCE_TRANSITED_CHECK(P) 0
+#define PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(P) 0
+
+ ret = fix_transited_encoding(context, config,
+ !f.disable_transited_check ||
+ GLOBAL_FORCE_TRANSITED_CHECK ||
+ PRINCIPAL_FORCE_TRANSITED_CHECK(server) ||
+ !((GLOBAL_ALLOW_PER_PRINCIPAL &&
+ PRINCIPAL_ALLOW_DISABLE_TRANSITED_CHECK(server)) ||
+ GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK),
+ &tgt->transited, &et,
+ *krb5_princ_realm(context, client_principal),
+ *krb5_princ_realm(context, server->entry.principal),
+ *krb5_princ_realm(context, krbtgt->entry.principal));
+ if(ret)
+ goto out;
+
+ copy_Realm(krb5_princ_realm(context, server->entry.principal),
+ &rep.ticket.realm);
+ _krb5_principal2principalname(&rep.ticket.sname, server->entry.principal);
+ copy_Realm(&tgt_name->realm, &rep.crealm);
+/*
+ if (f.request_anonymous)
+ _kdc_make_anonymous_principalname (&rep.cname);
+ else */
+
+ copy_PrincipalName(&tgt_name->name, &rep.cname);
+ rep.ticket.tkt_vno = 5;
+
+ ek.caddr = et.caddr;
+ if(et.caddr == NULL)
+ et.caddr = tgt->caddr;
+
+ {
+ time_t life;
+ life = et.endtime - *et.starttime;
+ if(client && client->entry.max_life)
+ life = min(life, *client->entry.max_life);
+ if(server->entry.max_life)
+ life = min(life, *server->entry.max_life);
+ et.endtime = *et.starttime + life;
+ }
+ if(f.renewable_ok && tgt->flags.renewable &&
+ et.renew_till == NULL && et.endtime < *b->till){
+ et.flags.renewable = 1;
+ ALLOC(et.renew_till);
+ *et.renew_till = *b->till;
+ }
+ if(et.renew_till){
+ time_t renew;
+ renew = *et.renew_till - et.authtime;
+ if(client && client->entry.max_renew)
+ renew = min(renew, *client->entry.max_renew);
+ if(server->entry.max_renew)
+ renew = min(renew, *server->entry.max_renew);
+ *et.renew_till = et.authtime + renew;
+ }
+
+ if(et.renew_till){
+ *et.renew_till = min(*et.renew_till, *tgt->renew_till);
+ *et.starttime = min(*et.starttime, *et.renew_till);
+ et.endtime = min(et.endtime, *et.renew_till);
+ }
+
+ *et.starttime = min(*et.starttime, et.endtime);
+
+ if(*et.starttime == et.endtime){
+ ret = KRB5KDC_ERR_NEVER_VALID;
+ goto out;
+ }
+ if(et.renew_till && et.endtime == *et.renew_till){
+ free(et.renew_till);
+ et.renew_till = NULL;
+ et.flags.renewable = 0;
+ }
+
+ et.flags.pre_authent = tgt->flags.pre_authent;
+ et.flags.hw_authent = tgt->flags.hw_authent;
+ et.flags.anonymous = tgt->flags.anonymous;
+ et.flags.ok_as_delegate = server->entry.flags.ok_as_delegate;
+
+ if (auth_data) {
+ /* XXX Check enc-authorization-data */
+ et.authorization_data = calloc(1, sizeof(*et.authorization_data));
+ if (et.authorization_data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = copy_AuthorizationData(auth_data, et.authorization_data);
+ if (ret)
+ goto out;
+
+ /* Filter out type KRB5SignedPath */
+ ret = find_KRB5SignedPath(context, et.authorization_data, NULL);
+ if (ret == 0) {
+ if (et.authorization_data->len == 1) {
+ free_AuthorizationData(et.authorization_data);
+ free(et.authorization_data);
+ et.authorization_data = NULL;
+ } else {
+ AuthorizationData *ad = et.authorization_data;
+ free_AuthorizationDataElement(&ad->val[ad->len - 1]);
+ ad->len--;
+ }
+ }
+ }
+
+ if(rspac->length) {
+ /*
+ * No not need to filter out the any PAC from the
+ * auth_data since it's signed by the KDC.
+ */
+ ret = _kdc_tkt_add_if_relevant_ad(context, &et,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ rspac);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_copy_keyblock_contents(context, sessionkey, &et.key);
+ if (ret)
+ goto out;
+ et.crealm = tgt->crealm;
+ et.cname = tgt_name->name;
+
+ ek.key = et.key;
+ /* MIT must have at least one last_req */
+ ek.last_req.len = 1;
+ ek.last_req.val = calloc(1, sizeof(*ek.last_req.val));
+ if (ek.last_req.val == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ek.nonce = b->nonce;
+ ek.flags = et.flags;
+ ek.authtime = et.authtime;
+ ek.starttime = et.starttime;
+ ek.endtime = et.endtime;
+ ek.renew_till = et.renew_till;
+ ek.srealm = rep.ticket.realm;
+ ek.sname = rep.ticket.sname;
+
+ _kdc_log_timestamp(context, config, "TGS-REQ", et.authtime, et.starttime,
+ et.endtime, et.renew_till);
+
+ /* Don't sign cross realm tickets, they can't be checked anyway */
+ {
+ char *r = get_krbtgt_realm(&ek.sname);
+
+ if (r == NULL || strcmp(r, ek.srealm) == 0) {
+ ret = _kdc_add_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ krbtgt_etype,
+ NULL,
+ spp,
+ &et);
+ if (ret)
+ goto out;
+ }
+ }
+
+ /* It is somewhat unclear where the etype in the following
+ encryption should come from. What we have is a session
+ key in the passed tgt, and a list of preferred etypes
+ *for the new ticket*. Should we pick the best possible
+ etype, given the keytype in the tgt, or should we look
+ at the etype list here as well? What if the tgt
+ session key is DES3 and we want a ticket with a (say)
+ CAST session key. Should the DES3 etype be added to the
+ etype list, even if we don't want a session key with
+ DES3? */
+ ret = _kdc_encode_reply(context, config,
+ &rep, &et, &ek, et.key.keytype,
+ kvno,
+ serverkey, 0, &tgt->key, e_text, reply);
+out:
+ free_TGS_REP(&rep);
+ free_TransitedEncoding(&et.transited);
+ if(et.starttime)
+ free(et.starttime);
+ if(et.renew_till)
+ free(et.renew_till);
+ if(et.authorization_data) {
+ free_AuthorizationData(et.authorization_data);
+ free(et.authorization_data);
+ }
+ free_LastReq(&ek.last_req);
+ memset(et.key.keyvalue.data, 0, et.key.keyvalue.length);
+ free_EncryptionKey(&et.key);
+ return ret;
+}
+
+static krb5_error_code
+tgs_check_authenticator(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_auth_context ac,
+ KDC_REQ_BODY *b,
+ const char **e_text,
+ krb5_keyblock *key)
+{
+ krb5_authenticator auth;
+ size_t len;
+ unsigned char *buf;
+ size_t buf_size;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ krb5_auth_con_getauthenticator(context, ac, &auth);
+ if(auth->cksum == NULL){
+ kdc_log(context, config, 0, "No authenticator in request");
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ goto out;
+ }
+ /*
+ * according to RFC1510 it doesn't need to be keyed,
+ * but according to the latest draft it needs to.
+ */
+ if (
+#if 0
+!krb5_checksum_is_keyed(context, auth->cksum->cksumtype)
+ ||
+#endif
+ !krb5_checksum_is_collision_proof(context, auth->cksum->cksumtype)) {
+ kdc_log(context, config, 0, "Bad checksum type in authenticator: %d",
+ auth->cksum->cksumtype);
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ goto out;
+ }
+
+ /* XXX should not re-encode this */
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, b, &len, ret);
+ if(ret){
+ kdc_log(context, config, 0, "Failed to encode KDC-REQ-BODY: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ if(buf_size != len) {
+ free(buf);
+ kdc_log(context, config, 0, "Internal error in ASN.1 encoder");
+ *e_text = "KDC internal error";
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free(buf);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ ret = krb5_verify_checksum(context,
+ crypto,
+ KRB5_KU_TGS_REQ_AUTH_CKSUM,
+ buf,
+ len,
+ auth->cksum);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ kdc_log(context, config, 0,
+ "Failed to verify authenticator checksum: %s",
+ krb5_get_err_text(context, ret));
+ }
+out:
+ free_Authenticator(auth);
+ free(auth);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static const char *
+find_rpath(krb5_context context, Realm crealm, Realm srealm)
+{
+ const char *new_realm = krb5_config_get_string(context,
+ NULL,
+ "capaths",
+ crealm,
+ srealm,
+ NULL);
+ return new_realm;
+}
+
+
+static krb5_boolean
+need_referral(krb5_context context, krb5_principal server, krb5_realm **realms)
+{
+ if(server->name.name_type != KRB5_NT_SRV_INST ||
+ server->name.name_string.len != 2)
+ return FALSE;
+
+ return _krb5_get_host_realm_int(context, server->name.name_string.val[1],
+ FALSE, realms) == 0;
+}
+
+static krb5_error_code
+tgs_parse_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ_BODY *b,
+ const PA_DATA *tgs_req,
+ hdb_entry_ex **krbtgt,
+ krb5_enctype *krbtgt_etype,
+ krb5_ticket **ticket,
+ const char **e_text,
+ const char *from,
+ const struct sockaddr *from_addr,
+ time_t **csec,
+ int **cusec,
+ AuthorizationData **auth_data)
+{
+ krb5_ap_req ap_req;
+ krb5_error_code ret;
+ krb5_principal princ;
+ krb5_auth_context ac = NULL;
+ krb5_flags ap_req_options;
+ krb5_flags verify_ap_req_flags;
+ krb5_crypto crypto;
+ Key *tkey;
+
+ *auth_data = NULL;
+ *csec = NULL;
+ *cusec = NULL;
+
+ memset(&ap_req, 0, sizeof(ap_req));
+ ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req);
+ if(ret){
+ kdc_log(context, config, 0, "Failed to decode AP-REQ: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ if(!get_krbtgt_realm(&ap_req.ticket.sname)){
+ /* XXX check for ticket.sname == req.sname */
+ kdc_log(context, config, 0, "PA-DATA is not a ticket-granting ticket");
+ ret = KRB5KDC_ERR_POLICY; /* ? */
+ goto out;
+ }
+
+ _krb5_principalname2krb5_principal(context,
+ &princ,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+
+ ret = _kdc_db_fetch(context, config, princ, HDB_F_GET_KRBTGT, NULL, krbtgt);
+
+ if(ret) {
+ char *p;
+ ret = krb5_unparse_name(context, princ, &p);
+ if (ret != 0)
+ p = "<unparse_name failed>";
+ krb5_free_principal(context, princ);
+ kdc_log(context, config, 0,
+ "Ticket-granting ticket not found in database: %s: %s",
+ p, krb5_get_err_text(context, ret));
+ if (ret == 0)
+ free(p);
+ ret = KRB5KRB_AP_ERR_NOT_US;
+ goto out;
+ }
+
+ if(ap_req.ticket.enc_part.kvno &&
+ *ap_req.ticket.enc_part.kvno != (*krbtgt)->entry.kvno){
+ char *p;
+
+ ret = krb5_unparse_name (context, princ, &p);
+ krb5_free_principal(context, princ);
+ if (ret != 0)
+ p = "<unparse_name failed>";
+ kdc_log(context, config, 0,
+ "Ticket kvno = %d, DB kvno = %d (%s)",
+ *ap_req.ticket.enc_part.kvno,
+ (*krbtgt)->entry.kvno,
+ p);
+ if (ret == 0)
+ free (p);
+ ret = KRB5KRB_AP_ERR_BADKEYVER;
+ goto out;
+ }
+
+ *krbtgt_etype = ap_req.ticket.enc_part.etype;
+
+ ret = hdb_enctype2key(context, &(*krbtgt)->entry,
+ ap_req.ticket.enc_part.etype, &tkey);
+ if(ret){
+ char *str = NULL, *p = NULL;
+
+ krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str);
+ krb5_unparse_name(context, princ, &p);
+ kdc_log(context, config, 0,
+ "No server key with enctype %s found for %s",
+ str ? str : "<unknown enctype>",
+ p ? p : "<unparse_name failed>");
+ free(str);
+ free(p);
+ ret = KRB5KRB_AP_ERR_BADKEYVER;
+ goto out;
+ }
+
+ if (b->kdc_options.validate)
+ verify_ap_req_flags = KRB5_VERIFY_AP_REQ_IGNORE_INVALID;
+ else
+ verify_ap_req_flags = 0;
+
+ ret = krb5_verify_ap_req2(context,
+ &ac,
+ &ap_req,
+ princ,
+ &tkey->key,
+ verify_ap_req_flags,
+ &ap_req_options,
+ ticket,
+ KRB5_KU_TGS_REQ_AUTH);
+
+ krb5_free_principal(context, princ);
+ if(ret) {
+ kdc_log(context, config, 0, "Failed to verify AP-REQ: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ {
+ krb5_authenticator auth;
+
+ ret = krb5_auth_con_getauthenticator(context, ac, &auth);
+ if (ret == 0) {
+ *csec = malloc(sizeof(**csec));
+ if (*csec == NULL) {
+ krb5_free_authenticator(context, &auth);
+ kdc_log(context, config, 0, "malloc failed");
+ goto out;
+ }
+ **csec = auth->ctime;
+ *cusec = malloc(sizeof(**cusec));
+ if (*cusec == NULL) {
+ krb5_free_authenticator(context, &auth);
+ kdc_log(context, config, 0, "malloc failed");
+ goto out;
+ }
+ **cusec = auth->cusec;
+ krb5_free_authenticator(context, &auth);
+ }
+ }
+
+ ret = tgs_check_authenticator(context, config,
+ ac, b, e_text, &(*ticket)->ticket.key);
+ if (ret) {
+ krb5_auth_con_free(context, ac);
+ goto out;
+ }
+
+ if (b->enc_authorization_data) {
+ unsigned usage = KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY;
+ krb5_keyblock *subkey;
+ krb5_data ad;
+
+ ret = krb5_auth_con_getremotesubkey(context,
+ ac,
+ &subkey);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "Failed to get remote subkey: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ if(subkey == NULL){
+ usage = KRB5_KU_TGS_REQ_AUTH_DAT_SESSION;
+ ret = krb5_auth_con_getkey(context, ac, &subkey);
+ if(ret) {
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "Failed to get session key: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ }
+ if(subkey == NULL){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0,
+ "Failed to get key for enc-authorization-data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ ret = krb5_crypto_init(context, subkey, 0, &crypto);
+ if (ret) {
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage,
+ b->enc_authorization_data,
+ &ad);
+ krb5_crypto_destroy(context, crypto);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ kdc_log(context, config, 0,
+ "Failed to decrypt enc-authorization-data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ krb5_free_keyblock(context, subkey);
+ ALLOC(*auth_data);
+ if (*auth_data == NULL) {
+ krb5_auth_con_free(context, ac);
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ ret = decode_AuthorizationData(ad.data, ad.length, *auth_data, NULL);
+ if(ret){
+ krb5_auth_con_free(context, ac);
+ free(*auth_data);
+ *auth_data = NULL;
+ kdc_log(context, config, 0, "Failed to decode authorization data");
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */
+ goto out;
+ }
+ }
+
+ krb5_auth_con_free(context, ac);
+
+out:
+ free_AP_REQ(&ap_req);
+
+ return ret;
+}
+
+static krb5_error_code
+tgs_build_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ KDC_REQ_BODY *b,
+ hdb_entry_ex *krbtgt,
+ krb5_enctype krbtgt_etype,
+ krb5_ticket *ticket,
+ krb5_data *reply,
+ const char *from,
+ const char **e_text,
+ AuthorizationData *auth_data,
+ const struct sockaddr *from_addr,
+ int datagram_reply)
+{
+ krb5_error_code ret;
+ krb5_principal cp = NULL, sp = NULL;
+ krb5_principal client_principal = NULL;
+ char *spn = NULL, *cpn = NULL;
+ hdb_entry_ex *server = NULL, *client = NULL;
+ EncTicketPart *tgt = &ticket->ticket;
+ KRB5SignedPathPrincipals *spp = NULL;
+ const EncryptionKey *ekey;
+ krb5_keyblock sessionkey;
+ krb5_kvno kvno;
+ krb5_data rspac;
+ int cross_realm = 0;
+
+ PrincipalName *s;
+ Realm r;
+ int nloop = 0;
+ EncTicketPart adtkt;
+ char opt_str[128];
+ int require_signedpath = 0;
+
+ memset(&sessionkey, 0, sizeof(sessionkey));
+ memset(&adtkt, 0, sizeof(adtkt));
+ krb5_data_zero(&rspac);
+
+ s = b->sname;
+ r = b->realm;
+
+ if(b->kdc_options.enc_tkt_in_skey){
+ Ticket *t;
+ hdb_entry_ex *uu;
+ krb5_principal p;
+ Key *uukey;
+
+ if(b->additional_tickets == NULL ||
+ b->additional_tickets->len == 0){
+ ret = KRB5KDC_ERR_BADOPTION; /* ? */
+ kdc_log(context, config, 0,
+ "No second ticket present in request");
+ goto out;
+ }
+ t = &b->additional_tickets->val[0];
+ if(!get_krbtgt_realm(&t->sname)){
+ kdc_log(context, config, 0,
+ "Additional ticket is not a ticket-granting ticket");
+ ret = KRB5KDC_ERR_POLICY;
+ goto out;
+ }
+ _krb5_principalname2krb5_principal(context, &p, t->sname, t->realm);
+ ret = _kdc_db_fetch(context, config, p,
+ HDB_F_GET_CLIENT|HDB_F_GET_SERVER,
+ NULL, &uu);
+ krb5_free_principal(context, p);
+ if(ret){
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+ ret = hdb_enctype2key(context, &uu->entry,
+ t->enc_part.etype, &uukey);
+ if(ret){
+ _kdc_free_ent(context, uu);
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+ ret = krb5_decrypt_ticket(context, t, &uukey->key, &adtkt, 0);
+ _kdc_free_ent(context, uu);
+ if(ret)
+ goto out;
+
+ ret = verify_flags(context, config, &adtkt, spn);
+ if (ret)
+ goto out;
+
+ s = &adtkt.cname;
+ r = adtkt.crealm;
+ }
+
+ _krb5_principalname2krb5_principal(context, &sp, *s, r);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+ _krb5_principalname2krb5_principal(context, &cp, tgt->cname, tgt->crealm);
+ ret = krb5_unparse_name(context, cp, &cpn);
+ if (ret)
+ goto out;
+ unparse_flags (KDCOptions2int(b->kdc_options),
+ asn1_KDCOptions_units(),
+ opt_str, sizeof(opt_str));
+ if(*opt_str)
+ kdc_log(context, config, 0,
+ "TGS-REQ %s from %s for %s [%s]",
+ cpn, from, spn, opt_str);
+ else
+ kdc_log(context, config, 0,
+ "TGS-REQ %s from %s for %s", cpn, from, spn);
+
+ /*
+ * Fetch server
+ */
+
+server_lookup:
+ ret = _kdc_db_fetch(context, config, sp, HDB_F_GET_SERVER, NULL, &server);
+
+ if(ret){
+ const char *new_rlm;
+ Realm req_rlm;
+ krb5_realm *realms;
+
+ if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) {
+ if(nloop++ < 2) {
+ new_rlm = find_rpath(context, tgt->crealm, req_rlm);
+ if(new_rlm) {
+ kdc_log(context, config, 5, "krbtgt for realm %s "
+ "not found, trying %s",
+ req_rlm, new_rlm);
+ krb5_free_principal(context, sp);
+ free(spn);
+ krb5_make_principal(context, &sp, r,
+ KRB5_TGS_NAME, new_rlm, NULL);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+ auth_data = NULL; /* ms don't handle AD in referals */
+ goto server_lookup;
+ }
+ }
+ } else if(need_referral(context, sp, &realms)) {
+ if (strcmp(realms[0], sp->realm) != 0) {
+ kdc_log(context, config, 5,
+ "Returning a referral to realm %s for "
+ "server %s that was not found",
+ realms[0], spn);
+ krb5_free_principal(context, sp);
+ free(spn);
+ krb5_make_principal(context, &sp, r, KRB5_TGS_NAME,
+ realms[0], NULL);
+ ret = krb5_unparse_name(context, sp, &spn);
+ if (ret)
+ goto out;
+ krb5_free_host_realm(context, realms);
+ auth_data = NULL; /* ms don't handle AD in referals */
+ goto server_lookup;
+ }
+ krb5_free_host_realm(context, realms);
+ }
+ kdc_log(context, config, 0,
+ "Server not found in database: %s: %s", spn,
+ krb5_get_err_text(context, ret));
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+
+ ret = _kdc_db_fetch(context, config, cp, HDB_F_GET_CLIENT, NULL, &client);
+ if(ret) {
+ const char *krbtgt_realm;
+
+ /*
+ * If the client belongs to the same realm as our krbtgt, it
+ * should exist in the local database.
+ *
+ */
+
+ krbtgt_realm =
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal, 1);
+
+ if(strcmp(krb5_principal_get_realm(context, cp), krbtgt_realm) == 0) {
+ if (ret == HDB_ERR_NOENTRY)
+ ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
+ kdc_log(context, config, 1, "Client no longer in database: %s",
+ cpn);
+ goto out;
+ }
+
+ kdc_log(context, config, 1, "Client not found in database: %s: %s",
+ cpn, krb5_get_err_text(context, ret));
+
+ cross_realm = 1;
+ }
+
+ /*
+ * Check that service is in the same realm as the krbtgt. If it's
+ * not the same, it's someone that is using a uni-directional trust
+ * backward.
+ */
+
+ if (strcmp(krb5_principal_get_realm(context, sp),
+ krb5_principal_get_comp_string(context,
+ krbtgt->entry.principal,
+ 1)) != 0) {
+ char *tpn;
+ ret = krb5_unparse_name(context, krbtgt->entry.principal, &tpn);
+ kdc_log(context, config, 0,
+ "Request with wrong krbtgt: %s",
+ (ret == 0) ? tpn : "<unknown>");
+ if(ret == 0)
+ free(tpn);
+ ret = KRB5KRB_AP_ERR_NOT_US;
+ goto out;
+ }
+
+ /*
+ *
+ */
+
+ client_principal = cp;
+
+ if (client) {
+ const PA_DATA *sdata;
+ int i = 0;
+
+ sdata = _kdc_find_padata(req, &i, KRB5_PADATA_S4U2SELF);
+ if (sdata) {
+ krb5_crypto crypto;
+ krb5_data datack;
+ PA_S4U2Self self;
+ char *selfcpn = NULL;
+ const char *str;
+
+ ret = decode_PA_S4U2Self(sdata->padata_value.data,
+ sdata->padata_value.length,
+ &self, NULL);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to decode PA-S4U2Self");
+ goto out;
+ }
+
+ ret = _krb5_s4u2self_to_checksumdata(context, &self, &datack);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, &tgt->key, 0, &crypto);
+ if (ret) {
+ free_PA_S4U2Self(&self);
+ krb5_data_free(&datack);
+ kdc_log(context, config, 0, "krb5_crypto_init failed: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ ret = krb5_verify_checksum(context,
+ crypto,
+ KRB5_KU_OTHER_CKSUM,
+ datack.data,
+ datack.length,
+ &self.cksum);
+ krb5_data_free(&datack);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_PA_S4U2Self(&self);
+ kdc_log(context, config, 0,
+ "krb5_verify_checksum failed for S4U2Self: %s",
+ krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &client_principal,
+ self.name,
+ self.realm);
+ free_PA_S4U2Self(&self);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, client_principal, &selfcpn);
+ if (ret)
+ goto out;
+
+ /*
+ * Check that service doing the impersonating is
+ * requesting a ticket to it-self.
+ */
+ if (krb5_principal_compare(context, cp, sp) != TRUE) {
+ kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
+ "to impersonate some other user "
+ "(tried for user %s to service %s)",
+ cpn, selfcpn, spn);
+ free(selfcpn);
+ ret = KRB5KDC_ERR_BADOPTION; /* ? */
+ goto out;
+ }
+
+ /*
+ * If the service isn't trusted for authentication to
+ * delegation, remove the forward flag.
+ */
+
+ if (client->entry.flags.trusted_for_delegation) {
+ str = "[forwardable]";
+ } else {
+ b->kdc_options.forwardable = 0;
+ str = "";
+ }
+ kdc_log(context, config, 0, "s4u2self %s impersonating %s to "
+ "service %s %s", cpn, selfcpn, spn, str);
+ free(selfcpn);
+ }
+ }
+
+ /*
+ * Constrained delegation
+ */
+
+ if (client != NULL
+ && b->additional_tickets != NULL
+ && b->additional_tickets->len != 0
+ && b->kdc_options.enc_tkt_in_skey == 0)
+ {
+ Key *clientkey;
+ Ticket *t;
+ char *str;
+
+ t = &b->additional_tickets->val[0];
+
+ ret = hdb_enctype2key(context, &client->entry,
+ t->enc_part.etype, &clientkey);
+ if(ret){
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+
+ ret = krb5_decrypt_ticket(context, t, &clientkey->key, &adtkt, 0);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "failed to decrypt ticket for "
+ "constrained delegation from %s to %s ", spn, cpn);
+ goto out;
+ }
+
+ /* check that ticket is valid */
+
+ if (adtkt.flags.forwardable == 0) {
+ kdc_log(context, config, 0,
+ "Missing forwardable flag on ticket for "
+ "constrained delegation from %s to %s ", spn, cpn);
+ ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */
+ goto out;
+ }
+
+ ret = check_constrained_delegation(context, config, client, sp);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "constrained delegation from %s to %s not allowed",
+ spn, cpn);
+ goto out;
+ }
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &client_principal,
+ adtkt.cname,
+ adtkt.crealm);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, client_principal, &str);
+ if (ret)
+ goto out;
+
+ ret = verify_flags(context, config, &adtkt, str);
+ if (ret) {
+ free(str);
+ goto out;
+ }
+
+ /*
+ * Check KRB5SignedPath in authorization data and add new entry to
+ * make sure servers can't fake a ticket to us.
+ */
+
+ ret = check_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ &adtkt,
+ &spp,
+ 1);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "KRB5SignedPath check from service %s failed "
+ "for delegation to %s for client %s "
+ "from %s failed with %s",
+ spn, str, cpn, from, krb5_get_err_text(context, ret));
+ free(str);
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "constrained delegation for %s "
+ "from %s to %s", str, cpn, spn);
+ free(str);
+
+ /*
+ * Also require that the KDC have issue the service's krbtgt
+ * used to do the request.
+ */
+ require_signedpath = 1;
+ }
+
+ /*
+ * Check flags
+ */
+
+ ret = _kdc_check_flags(context, config,
+ client, cpn,
+ server, spn,
+ FALSE);
+ if(ret)
+ goto out;
+
+ if((b->kdc_options.validate || b->kdc_options.renew) &&
+ !krb5_principal_compare(context,
+ krbtgt->entry.principal,
+ server->entry.principal)){
+ kdc_log(context, config, 0, "Inconsistent request.");
+ ret = KRB5KDC_ERR_SERVER_NOMATCH;
+ goto out;
+ }
+
+ /* check for valid set of addresses */
+ if(!_kdc_check_addresses(context, config, tgt->caddr, from_addr)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ kdc_log(context, config, 0, "Request from wrong address");
+ goto out;
+ }
+
+ /*
+ * Select enctype, return key and kvno.
+ */
+
+ {
+ krb5_enctype etype;
+
+ if(b->kdc_options.enc_tkt_in_skey) {
+ int i;
+ ekey = &adtkt.key;
+ for(i = 0; i < b->etype.len; i++)
+ if (b->etype.val[i] == adtkt.key.keytype)
+ break;
+ if(i == b->etype.len) {
+ krb5_clear_error_string(context);
+ return KRB5KDC_ERR_ETYPE_NOSUPP;
+ }
+ etype = b->etype.val[i];
+ kvno = 0;
+ } else {
+ Key *skey;
+
+ ret = _kdc_find_etype(context, server, b->etype.val, b->etype.len,
+ &skey, &etype);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Server (%s) has no support for etypes", spp);
+ return ret;
+ }
+ ekey = &skey->key;
+ kvno = server->entry.kvno;
+ }
+
+ ret = krb5_generate_random_keyblock(context, etype, &sessionkey);
+ if (ret)
+ goto out;
+ }
+
+ /* check PAC if not cross realm and if there is one */
+ if (!cross_realm) {
+ Key *tkey;
+
+ ret = hdb_enctype2key(context, &krbtgt->entry,
+ krbtgt_etype, &tkey);
+ if(ret) {
+ kdc_log(context, config, 0,
+ "Failed to find key for krbtgt PAC check");
+ goto out;
+ }
+
+ ret = check_PAC(context, config, client_principal,
+ client, server, ekey, &tkey->key,
+ tgt, &rspac, &require_signedpath);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Verify PAC failed for %s (%s) from %s with %s",
+ spn, cpn, from, krb5_get_err_text(context, ret));
+ goto out;
+ }
+ }
+
+ /* also check the krbtgt for signature */
+ ret = check_KRB5SignedPath(context,
+ config,
+ krbtgt,
+ tgt,
+ &spp,
+ require_signedpath);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "KRB5SignedPath check failed for %s (%s) from %s with %s",
+ spn, cpn, from, krb5_get_err_text(context, ret));
+ goto out;
+ }
+
+ /*
+ *
+ */
+
+ ret = tgs_make_reply(context,
+ config,
+ b,
+ client_principal,
+ tgt,
+ ekey,
+ &sessionkey,
+ kvno,
+ auth_data,
+ server,
+ spn,
+ client,
+ cp,
+ krbtgt,
+ krbtgt_etype,
+ spp,
+ &rspac,
+ e_text,
+ reply);
+
+out:
+ free(spn);
+ free(cpn);
+
+ krb5_data_free(&rspac);
+ krb5_free_keyblock_contents(context, &sessionkey);
+ if(server)
+ _kdc_free_ent(context, server);
+ if(client)
+ _kdc_free_ent(context, client);
+
+ if (client_principal && client_principal != cp)
+ krb5_free_principal(context, client_principal);
+ if (cp)
+ krb5_free_principal(context, cp);
+ if (sp)
+ krb5_free_principal(context, sp);
+
+ free_EncTicketPart(&adtkt);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_tgs_rep(krb5_context context,
+ krb5_kdc_configuration *config,
+ KDC_REQ *req,
+ krb5_data *data,
+ const char *from,
+ struct sockaddr *from_addr,
+ int datagram_reply)
+{
+ AuthorizationData *auth_data = NULL;
+ krb5_error_code ret;
+ int i = 0;
+ const PA_DATA *tgs_req;
+
+ hdb_entry_ex *krbtgt = NULL;
+ krb5_ticket *ticket = NULL;
+ const char *e_text = NULL;
+ krb5_enctype krbtgt_etype = ETYPE_NULL;
+
+ time_t *csec = NULL;
+ int *cusec = NULL;
+
+ if(req->padata == NULL){
+ ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */
+ kdc_log(context, config, 0,
+ "TGS-REQ from %s without PA-DATA", from);
+ goto out;
+ }
+
+ tgs_req = _kdc_find_padata(req, &i, KRB5_PADATA_TGS_REQ);
+
+ if(tgs_req == NULL){
+ ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+
+ kdc_log(context, config, 0,
+ "TGS-REQ from %s without PA-TGS-REQ", from);
+ goto out;
+ }
+ ret = tgs_parse_request(context, config,
+ &req->req_body, tgs_req,
+ &krbtgt,
+ &krbtgt_etype,
+ &ticket,
+ &e_text,
+ from, from_addr,
+ &csec, &cusec,
+ &auth_data);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed parsing TGS-REQ from %s", from);
+ goto out;
+ }
+
+ ret = tgs_build_reply(context,
+ config,
+ req,
+ &req->req_body,
+ krbtgt,
+ krbtgt_etype,
+ ticket,
+ data,
+ from,
+ &e_text,
+ auth_data,
+ from_addr,
+ datagram_reply);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Failed building TGS-REP to %s", from);
+ goto out;
+ }
+
+ /* */
+ if (datagram_reply && data->length > config->max_datagram_reply_length) {
+ krb5_data_free(data);
+ ret = KRB5KRB_ERR_RESPONSE_TOO_BIG;
+ e_text = "Reply packet too large";
+ }
+
+out:
+ if(ret && data->data == NULL){
+ krb5_mk_error(context,
+ ret,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ csec,
+ cusec,
+ data);
+ }
+ free(csec);
+ free(cusec);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if(krbtgt)
+ _kdc_free_ent(context, krbtgt);
+
+ if (auth_data) {
+ free_AuthorizationData(auth_data);
+ free(auth_data);
+ }
+
+ return 0;
+}
diff --git a/kdc/kstash.8 b/kdc/kstash.8
new file mode 100644
index 0000000000000..f30eac693d0a6
--- /dev/null
+++ b/kdc/kstash.8
@@ -0,0 +1,104 @@
+.\" Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: kstash.8 20316 2007-04-11 11:53:20Z lha $
+.\"
+.Dd April 10, 2007
+.Dt KSTASH 8
+.Os HEIMDAL
+.Sh NAME
+.Nm kstash
+.Nd "store the KDC master password in a file"
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl e Ar string \*(Ba Xo
+.Fl -enctype= Ns Ar string
+.Xc
+.Oc
+.Oo Fl k Ar file \*(Ba Xo
+.Fl -key-file= Ns Ar file
+.Xc
+.Oc
+.Op Fl -convert-file
+.Op Fl -random-key
+.Op Fl -master-key-fd= Ns Ar fd
+.Op Fl -random-key
+.Op Fl h | Fl -help
+.Op Fl -version
+.Ek
+.Sh DESCRIPTION
+.Nm
+reads the Kerberos master key and stores it in a file that will be
+used by the KDC.
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Xo
+.Fl e Ar string ,
+.Fl -enctype= Ns Ar string
+.Xc
+the encryption type to use, defaults to DES3-CBC-SHA1.
+.It Xo
+.Fl k Ar file ,
+.Fl -key-file= Ns Ar file
+.Xc
+the name of the master key file.
+.It Xo
+.Fl -convert-file
+.Xc
+don't ask for a new master key, just read an old master key file, and
+write it back in the new keyfile format.
+.It Xo
+.Fl -random-key
+.Xc
+generate a random master key.
+.It Xo
+.Fl -master-key-fd= Ns Ar fd
+.Xc
+filedescriptor to read passphrase from, if not specified the
+passphrase will be read from the terminal.
+.El
+.\".Sh ENVIRONMENT
+.Sh FILES
+.Pa /var/heimdal/m-key
+is the default keyfile if no other keyfile is specified.
+The format of a Heimdal master key is the same as a keytab, so
+.Nm ktutil
+list can be used to list the content of the file.
+.\".Sh EXAMPLES
+.\".Sh DIAGNOSTICS
+.Sh SEE ALSO
+.Xr kdc 8
+.\".Sh STANDARDS
+.\".Sh HISTORY
+.\".Sh AUTHORS
+.\".Sh BUGS
diff --git a/kdc/kstash.c b/kdc/kstash.c
new file mode 100644
index 0000000000000..9e499a1093a2d
--- /dev/null
+++ b/kdc/kstash.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "headers.h"
+
+RCSID("$Id: kstash.c 22244 2007-12-08 23:47:42Z lha $");
+
+krb5_context context;
+
+static char *keyfile;
+static int convert_flag;
+static int help_flag;
+static int version_flag;
+
+static int master_key_fd = -1;
+static int random_key_flag;
+
+static const char *enctype_str = "des3-cbc-sha1";
+
+static struct getargs args[] = {
+ { "enctype", 'e', arg_string, &enctype_str, "encryption type" },
+ { "key-file", 'k', arg_string, &keyfile, "master key file", "file" },
+ { "convert-file", 0, arg_flag, &convert_flag,
+ "just convert keyfile to new format" },
+ { "master-key-fd", 0, arg_integer, &master_key_fd,
+ "filedescriptor to read passphrase from", "fd" },
+ { "random-key", 0, arg_flag, &random_key_flag, "generate a random master key" },
+ { "help", 'h', arg_flag, &help_flag },
+ { "version", 0, arg_flag, &version_flag }
+};
+
+int num_args = sizeof(args) / sizeof(args[0]);
+
+int
+main(int argc, char **argv)
+{
+ char buf[1024];
+ krb5_error_code ret;
+
+ krb5_enctype enctype;
+
+ hdb_master_key mkey;
+
+ krb5_program_setup(&context, argc, argv, args, num_args, NULL);
+
+ if(help_flag)
+ krb5_std_usage(0, args, num_args);
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ if (master_key_fd != -1 && random_key_flag)
+ krb5_errx(context, 1, "random-key and master-key-fd "
+ "is mutual exclusive");
+
+ if (keyfile == NULL)
+ asprintf(&keyfile, "%s/m-key", hdb_db_dir(context));
+
+ ret = krb5_string_to_enctype(context, enctype_str, &enctype);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_string_to_enctype");
+
+ ret = hdb_read_master_key(context, keyfile, &mkey);
+ if(ret && ret != ENOENT)
+ krb5_err(context, 1, ret, "reading master key from %s", keyfile);
+
+ if (convert_flag) {
+ if (ret)
+ krb5_err(context, 1, ret, "reading master key from %s", keyfile);
+ } else {
+ krb5_keyblock key;
+ krb5_salt salt;
+ salt.salttype = KRB5_PW_SALT;
+ /* XXX better value? */
+ salt.saltvalue.data = NULL;
+ salt.saltvalue.length = 0;
+ if (random_key_flag) {
+ ret = krb5_generate_random_keyblock(context, enctype, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ } else {
+ if(master_key_fd != -1) {
+ ssize_t n;
+ n = read(master_key_fd, buf, sizeof(buf));
+ if(n <= 0)
+ krb5_err(context, 1, errno, "failed to read passphrase");
+ buf[n] = '\0';
+ buf[strcspn(buf, "\r\n")] = '\0';
+
+ } else {
+ if(UI_UTIL_read_pw_string(buf, sizeof(buf), "Master key: ", 1))
+ exit(1);
+ }
+ krb5_string_to_key_salt(context, enctype, buf, salt, &key);
+ }
+ ret = hdb_add_master_key(context, &key, &mkey);
+
+ krb5_free_keyblock_contents(context, &key);
+
+ }
+
+ {
+ char *new, *old;
+ asprintf(&old, "%s.old", keyfile);
+ asprintf(&new, "%s.new", keyfile);
+ if(unlink(new) < 0 && errno != ENOENT) {
+ ret = errno;
+ goto out;
+ }
+ krb5_warnx(context, "writing key to `%s'", keyfile);
+ ret = hdb_write_master_key(context, new, mkey);
+ if(ret)
+ unlink(new);
+ else {
+ unlink(old);
+ if(link(keyfile, old) < 0 && errno != ENOENT) {
+ ret = errno;
+ unlink(new);
+ } else if(rename(new, keyfile) < 0) {
+ ret = errno;
+ }
+ }
+ out:
+ free(old);
+ free(new);
+ if(ret)
+ krb5_warn(context, errno, "writing master key file");
+ }
+
+ hdb_free_master_key(context, mkey);
+
+ exit(ret != 0);
+}
diff --git a/kdc/kx509.c b/kdc/kx509.c
new file mode 100644
index 0000000000000..b1b861efef88e
--- /dev/null
+++ b/kdc/kx509.c
@@ -0,0 +1,460 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#include <hex.h>
+#include <rfc2459_asn1.h>
+#include <hx509.h>
+
+RCSID("$Id: kx509.c 21607 2007-07-17 07:04:52Z lha $");
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_try_kx509_request(void *ptr, size_t len, Kx509Request *req, size_t *size)
+{
+ if (len < 4)
+ return -1;
+ if (memcmp("\x00\x00\x02\x00", ptr, 4) != 0)
+ return -1;
+ return decode_Kx509Request(((unsigned char *)ptr) + 4, len - 4, req, size);
+}
+
+/*
+ *
+ */
+
+static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
+
+static krb5_error_code
+verify_req_hash(krb5_context context,
+ const Kx509Request *req,
+ krb5_keyblock *key)
+{
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ HMAC_CTX ctx;
+
+ if (req->pk_hash.length != sizeof(digest)) {
+ krb5_set_error_string(context, "pk-hash have wrong length: %lu",
+ (unsigned long)req->pk_hash.length);
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ HMAC_CTX_init(&ctx);
+ HMAC_Init_ex(&ctx,
+ key->keyvalue.data, key->keyvalue.length,
+ EVP_sha1(), NULL);
+ if (sizeof(digest) != HMAC_size(&ctx))
+ krb5_abortx(context, "runtime error, hmac buffer wrong size in kx509");
+ HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
+ HMAC_Update(&ctx, req->pk_key.data, req->pk_key.length);
+ HMAC_Final(&ctx, digest, 0);
+ HMAC_CTX_cleanup(&ctx);
+
+ if (memcmp(req->pk_hash.data, digest, sizeof(digest)) != 0) {
+ krb5_set_error_string(context, "pk-hash is not correct");
+ return KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+ return 0;
+}
+
+static krb5_error_code
+calculate_reply_hash(krb5_context context,
+ krb5_keyblock *key,
+ Kx509Response *rep)
+{
+ HMAC_CTX ctx;
+
+ HMAC_CTX_init(&ctx);
+
+ HMAC_Init_ex(&ctx,
+ key->keyvalue.data, key->keyvalue.length,
+ EVP_sha1(), NULL);
+ rep->hash->length = HMAC_size(&ctx);
+ rep->hash->data = malloc(rep->hash->length);
+ if (rep->hash->data == NULL) {
+ HMAC_CTX_cleanup(&ctx);
+ krb5_set_error_string(context, "out of memory");
+ return ENOMEM;
+ }
+
+ HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
+ if (rep->error_code) {
+ int32_t t = *rep->error_code;
+ do {
+ unsigned char p = (t & 0xff);
+ HMAC_Update(&ctx, &p, 1);
+ t >>= 8;
+ } while (t);
+ }
+ if (rep->certificate)
+ HMAC_Update(&ctx, rep->certificate->data, rep->certificate->length);
+ if (rep->e_text)
+ HMAC_Update(&ctx, (unsigned char *)*rep->e_text, strlen(*rep->e_text));
+
+ HMAC_Final(&ctx, rep->hash->data, 0);
+ HMAC_CTX_cleanup(&ctx);
+
+ return 0;
+}
+
+/*
+ * Build a certifate for `principal´ that will expire at `endtime´.
+ */
+
+static krb5_error_code
+build_certificate(krb5_context context,
+ krb5_kdc_configuration *config,
+ const krb5_data *key,
+ time_t endtime,
+ krb5_principal principal,
+ krb5_data *certificate)
+{
+ hx509_context hxctx = NULL;
+ hx509_ca_tbs tbs = NULL;
+ hx509_env env = NULL;
+ hx509_cert cert = NULL;
+ hx509_cert signer = NULL;
+ int ret;
+
+ if (krb5_principal_get_comp_string(context, principal, 1) != NULL) {
+ kdc_log(context, config, 0, "Principal is not a user");
+ return EINVAL;
+ }
+
+ ret = hx509_context_init(&hxctx);
+ if (ret)
+ goto out;
+
+ ret = hx509_env_init(hxctx, &env);
+ if (ret)
+ goto out;
+
+ ret = hx509_env_add(hxctx, env, "principal-name",
+ krb5_principal_get_comp_string(context, principal, 0));
+ if (ret)
+ goto out;
+
+ {
+ hx509_certs certs;
+ hx509_query *q;
+
+ ret = hx509_certs_init(hxctx, config->kx509_ca, 0,
+ NULL, &certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to load CA %s",
+ config->kx509_ca);
+ goto out;
+ }
+ ret = hx509_query_alloc(hxctx, &q);
+ if (ret) {
+ hx509_certs_free(&certs);
+ goto out;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_KEYCERTSIGN);
+
+ ret = hx509_certs_find(hxctx, certs, q, &signer);
+ hx509_query_free(hxctx, q);
+ hx509_certs_free(&certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to find a CA in %s",
+ config->kx509_ca);
+ goto out;
+ }
+ }
+
+ ret = hx509_ca_tbs_init(hxctx, &tbs);
+ if (ret)
+ goto out;
+
+ {
+ SubjectPublicKeyInfo spki;
+ heim_any any;
+
+ memset(&spki, 0, sizeof(spki));
+
+ spki.subjectPublicKey.data = key->data;
+ spki.subjectPublicKey.length = key->length * 8;
+
+ ret = der_copy_oid(oid_id_pkcs1_rsaEncryption(),
+ &spki.algorithm.algorithm);
+
+ any.data = "\x05\x00";
+ any.length = 2;
+ spki.algorithm.parameters = &any;
+
+ ret = hx509_ca_tbs_set_spki(hxctx, tbs, &spki);
+ der_free_oid(&spki.algorithm.algorithm);
+ if (ret)
+ goto out;
+ }
+
+ {
+ hx509_certs certs;
+ hx509_cert template;
+
+ ret = hx509_certs_init(hxctx, config->kx509_template, 0,
+ NULL, &certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to load template %s",
+ config->kx509_template);
+ goto out;
+ }
+ ret = hx509_get_one_cert(hxctx, certs, &template);
+ hx509_certs_free(&certs);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to find template in %s",
+ config->kx509_template);
+ goto out;
+ }
+ ret = hx509_ca_tbs_set_template(hxctx, tbs,
+ HX509_CA_TEMPLATE_SUBJECT|
+ HX509_CA_TEMPLATE_KU|
+ HX509_CA_TEMPLATE_EKU,
+ template);
+ hx509_cert_free(template);
+ if (ret)
+ goto out;
+ }
+
+ hx509_ca_tbs_set_notAfter(hxctx, tbs, endtime);
+
+ hx509_ca_tbs_subject_expand(hxctx, tbs, env);
+ hx509_env_free(&env);
+
+ ret = hx509_ca_sign(hxctx, tbs, signer, &cert);
+ hx509_cert_free(signer);
+ if (ret)
+ goto out;
+
+ hx509_ca_tbs_free(&tbs);
+
+ ret = hx509_cert_binary(hxctx, cert, certificate);
+ hx509_cert_free(cert);
+ if (ret)
+ goto out;
+
+ hx509_context_free(&hxctx);
+
+ return 0;
+out:
+ if (env)
+ hx509_env_free(&env);
+ if (tbs)
+ hx509_ca_tbs_free(&tbs);
+ if (signer)
+ hx509_cert_free(signer);
+ if (hxctx)
+ hx509_context_free(&hxctx);
+ krb5_set_error_string(context, "cert creation failed");
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_do_kx509(krb5_context context,
+ krb5_kdc_configuration *config,
+ const Kx509Request *req, krb5_data *reply,
+ const char *from, struct sockaddr *addr)
+{
+ krb5_error_code ret;
+ krb5_ticket *ticket = NULL;
+ krb5_flags ap_req_options;
+ krb5_auth_context ac = NULL;
+ krb5_keytab id = NULL;
+ krb5_principal sprincipal = NULL, cprincipal = NULL;
+ char *cname = NULL;
+ Kx509Response rep;
+ size_t size;
+ krb5_keyblock *key = NULL;
+
+ krb5_data_zero(reply);
+ memset(&rep, 0, sizeof(rep));
+
+ if(!config->enable_kx509) {
+ kdc_log(context, config, 0,
+ "Rejected kx509 request (disabled) from %s", from);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ kdc_log(context, config, 0, "Kx509 request from %s", from);
+
+ ret = krb5_kt_resolve(context, "HDB:", &id);
+ if (ret) {
+ kdc_log(context, config, 0, "Can't open database for digest");
+ goto out;
+ }
+
+ ret = krb5_rd_req(context,
+ &ac,
+ &req->authenticator,
+ NULL,
+ id,
+ &ap_req_options,
+ &ticket);
+ if (ret)
+ goto out;
+
+ ret = krb5_ticket_get_client(context, ticket, &cprincipal);
+ if (ret)
+ goto out;
+
+ ret = krb5_unparse_name(context, cprincipal, &cname);
+ if (ret)
+ goto out;
+
+ /* verify server principal */
+
+ ret = krb5_sname_to_principal(context, NULL, "kca_service",
+ KRB5_NT_UNKNOWN, &sprincipal);
+ if (ret)
+ goto out;
+
+ {
+ krb5_principal principal = NULL;
+
+ ret = krb5_ticket_get_server(context, ticket, &principal);
+ if (ret)
+ goto out;
+
+ ret = krb5_principal_compare(context, sprincipal, principal);
+ krb5_free_principal(context, principal);
+ if (ret != TRUE) {
+ ret = KRB5KDC_ERR_SERVER_NOMATCH;
+ krb5_set_error_string(context,
+ "User %s used wrong Kx509 service principal",
+ cname);
+ goto out;
+ }
+ }
+
+ ret = krb5_auth_con_getkey(context, ac, &key);
+ if (ret || key == NULL) {
+ krb5_set_error_string(context, "Kx509 can't get session key");
+ goto out;
+ }
+
+ ret = verify_req_hash(context, req, key);
+ if (ret)
+ goto out;
+
+ /* Verify that the key is encoded RSA key */
+ {
+ RSAPublicKey key;
+ size_t size;
+
+ ret = decode_RSAPublicKey(req->pk_key.data, req->pk_key.length,
+ &key, &size);
+ if (ret)
+ goto out;
+ free_RSAPublicKey(&key);
+ if (size != req->pk_key.length)
+ ;
+ }
+
+ ALLOC(rep.certificate);
+ if (rep.certificate == NULL)
+ goto out;
+ krb5_data_zero(rep.certificate);
+ ALLOC(rep.hash);
+ if (rep.hash == NULL)
+ goto out;
+ krb5_data_zero(rep.hash);
+
+ ret = build_certificate(context, config, &req->pk_key,
+ krb5_ticket_get_endtime(context, ticket),
+ cprincipal, rep.certificate);
+ if (ret)
+ goto out;
+
+ ret = calculate_reply_hash(context, key, &rep);
+ if (ret)
+ goto out;
+
+ /*
+ * Encode reply, [ version | Kx509Response ]
+ */
+
+ {
+ krb5_data data;
+
+ ASN1_MALLOC_ENCODE(Kx509Response, data.data, data.length, &rep,
+ &size, ret);
+ if (ret) {
+ krb5_set_error_string(context, "Failed to encode kx509 reply");
+ goto out;
+ }
+ if (size != data.length)
+ krb5_abortx(context, "ASN1 internal error");
+
+ ret = krb5_data_alloc(reply, data.length + sizeof(version_2_0));
+ if (ret) {
+ free(data.data);
+ goto out;
+ }
+ memcpy(reply->data, version_2_0, sizeof(version_2_0));
+ memcpy(((unsigned char *)reply->data) + sizeof(version_2_0),
+ data.data, data.length);
+ free(data.data);
+ }
+
+ kdc_log(context, config, 0, "Successful Kx509 request for %s", cname);
+
+out:
+ if (ac)
+ krb5_auth_con_free(context, ac);
+ if (ret)
+ krb5_warn(context, ret, "Kx509 request from %s failed", from);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+ if (id)
+ krb5_kt_close(context, id);
+ if (sprincipal)
+ krb5_free_principal(context, sprincipal);
+ if (cprincipal)
+ krb5_free_principal(context, cprincipal);
+ if (key)
+ krb5_free_keyblock (context, key);
+ if (cname)
+ free(cname);
+ free_Kx509Response(&rep);
+
+ return 0;
+}
diff --git a/kdc/log.c b/kdc/log.c
new file mode 100644
index 0000000000000..8cf967fbfb8b1
--- /dev/null
+++ b/kdc/log.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1997, 1998, 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+RCSID("$Id: log.c 22254 2007-12-09 06:01:05Z lha $");
+
+void
+kdc_openlog(krb5_context context,
+ krb5_kdc_configuration *config)
+{
+ char **s = NULL, **p;
+ krb5_initlog(context, "kdc", &config->logf);
+ s = krb5_config_get_strings(context, NULL, "kdc", "logging", NULL);
+ if(s == NULL)
+ s = krb5_config_get_strings(context, NULL, "logging", "kdc", NULL);
+ if(s){
+ for(p = s; *p; p++)
+ krb5_addlog_dest(context, config->logf, *p);
+ krb5_config_free_strings(s);
+ }else {
+ char *s;
+ asprintf(&s, "0-1/FILE:%s/%s", hdb_db_dir(context), KDC_LOG_FILE);
+ krb5_addlog_dest(context, config->logf, s);
+ free(s);
+ }
+ krb5_set_warn_dest(context, config->logf);
+}
+
+char*
+kdc_log_msg_va(krb5_context context,
+ krb5_kdc_configuration *config,
+ int level, const char *fmt, va_list ap)
+{
+ char *msg;
+ krb5_vlog_msg(context, config->logf, &msg, level, fmt, ap);
+ return msg;
+}
+
+char*
+kdc_log_msg(krb5_context context,
+ krb5_kdc_configuration *config,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+ va_start(ap, fmt);
+ s = kdc_log_msg_va(context, config, level, fmt, ap);
+ va_end(ap);
+ return s;
+}
+
+void
+kdc_log(krb5_context context,
+ krb5_kdc_configuration *config,
+ int level, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+ va_start(ap, fmt);
+ s = kdc_log_msg_va(context, config, level, fmt, ap);
+ if(s) free(s);
+ va_end(ap);
+}
diff --git a/kdc/main.c b/kdc/main.c
new file mode 100644
index 0000000000000..9195b0488952f
--- /dev/null
+++ b/kdc/main.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+RCSID("$Id: main.c 20454 2007-04-19 20:21:51Z lha $");
+
+sig_atomic_t exit_flag = 0;
+
+int detach_from_console = -1;
+
+static RETSIGTYPE
+sigterm(int sig)
+{
+ exit_flag = sig;
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_kdc_configuration *config;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret == KRB5_CONFIG_BADFORMAT)
+ errx (1, "krb5_init_context failed to parse configuration file");
+ else if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = krb5_kt_register(context, &hdb_kt_ops);
+ if (ret)
+ errx (1, "krb5_kt_register(HDB) failed: %d", ret);
+
+ config = configure(context, argc, argv);
+
+#ifdef HAVE_SIGACTION
+ {
+ struct sigaction sa;
+
+ sa.sa_flags = 0;
+ sa.sa_handler = sigterm;
+ sigemptyset(&sa.sa_mask);
+
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGXCPU, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+ }
+#else
+ signal(SIGINT, sigterm);
+ signal(SIGTERM, sigterm);
+ signal(SIGXCPU, sigterm);
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ if (detach_from_console)
+ daemon(0, 0);
+ pidfile(NULL);
+ loop(context, config);
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/kdc/misc.c b/kdc/misc.c
new file mode 100644
index 0000000000000..072df44042979
--- /dev/null
+++ b/kdc/misc.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: misc.c 21106 2007-06-18 10:18:11Z lha $");
+
+struct timeval _kdc_now;
+
+krb5_error_code
+_kdc_db_fetch(krb5_context context,
+ krb5_kdc_configuration *config,
+ krb5_const_principal principal,
+ unsigned flags,
+ HDB **db,
+ hdb_entry_ex **h)
+{
+ hdb_entry_ex *ent;
+ krb5_error_code ret;
+ int i;
+
+ ent = calloc (1, sizeof (*ent));
+ if (ent == NULL) {
+ krb5_set_error_string(context, "out of memory");
+ return ENOMEM;
+ }
+
+ for(i = 0; i < config->num_db; i++) {
+ ret = config->db[i]->hdb_open(context, config->db[i], O_RDONLY, 0);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to open database: %s",
+ krb5_get_err_text(context, ret));
+ continue;
+ }
+ ret = config->db[i]->hdb_fetch(context,
+ config->db[i],
+ principal,
+ flags | HDB_F_DECRYPT,
+ ent);
+ config->db[i]->hdb_close(context, config->db[i]);
+ if(ret == 0) {
+ if (db)
+ *db = config->db[i];
+ *h = ent;
+ return 0;
+ }
+ }
+ free(ent);
+ krb5_set_error_string(context, "no such entry found in hdb");
+ return HDB_ERR_NOENTRY;
+}
+
+void
+_kdc_free_ent(krb5_context context, hdb_entry_ex *ent)
+{
+ hdb_free_entry (context, ent);
+ free (ent);
+}
+
+/*
+ * Use the order list of preferred encryption types and sort the
+ * available keys and return the most preferred key.
+ */
+
+krb5_error_code
+_kdc_get_preferred_key(krb5_context context,
+ krb5_kdc_configuration *config,
+ hdb_entry_ex *h,
+ const char *name,
+ krb5_enctype *enctype,
+ Key **key)
+{
+ const krb5_enctype *p;
+ krb5_error_code ret;
+ int i;
+
+ p = krb5_kerberos_enctypes(context);
+
+ for (i = 0; p[i] != ETYPE_NULL; i++) {
+ if (krb5_enctype_valid(context, p[i]) != 0)
+ continue;
+ ret = hdb_enctype2key(context, &h->entry, p[i], key);
+ if (ret == 0) {
+ *enctype = p[i];
+ return 0;
+ }
+ }
+
+ krb5_set_error_string(context, "No valid kerberos key found for %s", name);
+ return EINVAL;
+}
+
diff --git a/kdc/mit_dump.c b/kdc/mit_dump.c
new file mode 100644
index 0000000000000..dd2f5d78c8e94
--- /dev/null
+++ b/kdc/mit_dump.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hprop.h"
+
+RCSID("$Id: mit_dump.c 21745 2007-07-31 16:11:25Z lha $");
+
+/*
+can have any number of princ stanzas.
+format is as follows (only \n indicates newlines)
+princ\t%d\t (%d is KRB5_KDB_V1_BASE_LENGTH, always 38)
+%d\t (strlen of principal e.g. shadow/foo@ANDREW.CMU.EDU)
+%d\t (number of tl_data)
+%d\t (number of key data, e.g. how many keys for this user)
+%d\t (extra data length)
+%s\t (principal name)
+%d\t (attributes)
+%d\t (max lifetime, seconds)
+%d\t (max renewable life, seconds)
+%d\t (expiration, seconds since epoch or 2145830400 for never)
+%d\t (password expiration, seconds, 0 for never)
+%d\t (last successful auth, seconds since epoch)
+%d\t (last failed auth, per above)
+%d\t (failed auth count)
+foreach tl_data 0 to number of tl_data - 1 as above
+ %d\t%d\t (data type, data length)
+ foreach tl_data 0 to length-1
+ %02x (tl data contents[element n])
+ except if tl_data length is 0
+ %d (always -1)
+ \t
+foreach key 0 to number of keys - 1 as above
+ %d\t%d\t (key data version, kvno)
+ foreach version 0 to key data version - 1 (a key or a salt)
+ %d\t%d\t(data type for this key, data length for this key)
+ foreach key data length 0 to length-1
+ %02x (key data contents[element n])
+ except if key_data length is 0
+ %d (always -1)
+ \t
+foreach extra data length 0 to length - 1
+ %02x (extra data part)
+unless no extra data
+ %d (always -1)
+;\n
+
+*/
+
+static int
+hex_to_octet_string(const char *ptr, krb5_data *data)
+{
+ int i;
+ unsigned int v;
+ for(i = 0; i < data->length; i++) {
+ if(sscanf(ptr + 2 * i, "%02x", &v) != 1)
+ return -1;
+ ((unsigned char*)data->data)[i] = v;
+ }
+ return 2 * i;
+}
+
+static char *
+nexttoken(char **p)
+{
+ char *q;
+ do {
+ q = strsep(p, " \t");
+ } while(q && *q == '\0');
+ return q;
+}
+
+static size_t
+getdata(char **p, unsigned char *buf, size_t len)
+{
+ size_t i;
+ int v;
+ char *q = nexttoken(p);
+ i = 0;
+ while(*q && i < len) {
+ if(sscanf(q, "%02x", &v) != 1)
+ break;
+ buf[i++] = v;
+ q += 2;
+ }
+ return i;
+}
+
+static int
+getint(char **p)
+{
+ int val;
+ char *q = nexttoken(p);
+ sscanf(q, "%d", &val);
+ return val;
+}
+
+#include <kadm5/admin.h>
+
+static void
+attr_to_flags(unsigned attr, HDBFlags *flags)
+{
+ flags->postdate = !(attr & KRB5_KDB_DISALLOW_POSTDATED);
+ flags->forwardable = !(attr & KRB5_KDB_DISALLOW_FORWARDABLE);
+ flags->initial = !!(attr & KRB5_KDB_DISALLOW_TGT_BASED);
+ flags->renewable = !(attr & KRB5_KDB_DISALLOW_RENEWABLE);
+ flags->proxiable = !(attr & KRB5_KDB_DISALLOW_PROXIABLE);
+ /* DUP_SKEY */
+ flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX);
+ flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH);
+ /* HW_AUTH */
+ flags->server = !(attr & KRB5_KDB_DISALLOW_SVR);
+ flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE);
+ flags->client = 1; /* XXX */
+}
+
+#define KRB5_KDB_SALTTYPE_NORMAL 0
+#define KRB5_KDB_SALTTYPE_V4 1
+#define KRB5_KDB_SALTTYPE_NOREALM 2
+#define KRB5_KDB_SALTTYPE_ONLYREALM 3
+#define KRB5_KDB_SALTTYPE_SPECIAL 4
+#define KRB5_KDB_SALTTYPE_AFS3 5
+
+static krb5_error_code
+fix_salt(krb5_context context, hdb_entry *ent, int key_num)
+{
+ krb5_error_code ret;
+ Salt *salt = ent->keys.val[key_num].salt;
+ /* fix salt type */
+ switch((int)salt->type) {
+ case KRB5_KDB_SALTTYPE_NORMAL:
+ salt->type = KRB5_PADATA_PW_SALT;
+ break;
+ case KRB5_KDB_SALTTYPE_V4:
+ krb5_data_free(&salt->salt);
+ salt->type = KRB5_PADATA_PW_SALT;
+ break;
+ case KRB5_KDB_SALTTYPE_NOREALM:
+ {
+ size_t len;
+ int i;
+ char *p;
+
+ len = 0;
+ for (i = 0; i < ent->principal->name.name_string.len; ++i)
+ len += strlen(ent->principal->name.name_string.val[i]);
+ ret = krb5_data_alloc (&salt->salt, len);
+ if (ret)
+ return ret;
+ p = salt->salt.data;
+ for (i = 0; i < ent->principal->name.name_string.len; ++i) {
+ memcpy (p,
+ ent->principal->name.name_string.val[i],
+ strlen(ent->principal->name.name_string.val[i]));
+ p += strlen(ent->principal->name.name_string.val[i]);
+ }
+
+ salt->type = KRB5_PADATA_PW_SALT;
+ break;
+ }
+ case KRB5_KDB_SALTTYPE_ONLYREALM:
+ krb5_data_free(&salt->salt);
+ ret = krb5_data_copy(&salt->salt,
+ ent->principal->realm,
+ strlen(ent->principal->realm));
+ if(ret)
+ return ret;
+ salt->type = KRB5_PADATA_PW_SALT;
+ break;
+ case KRB5_KDB_SALTTYPE_SPECIAL:
+ salt->type = KRB5_PADATA_PW_SALT;
+ break;
+ case KRB5_KDB_SALTTYPE_AFS3:
+ krb5_data_free(&salt->salt);
+ ret = krb5_data_copy(&salt->salt,
+ ent->principal->realm,
+ strlen(ent->principal->realm));
+ if(ret)
+ return ret;
+ salt->type = KRB5_PADATA_AFS3_SALT;
+ break;
+ default:
+ abort();
+ }
+ return 0;
+}
+
+int
+mit_prop_dump(void *arg, const char *file)
+{
+ krb5_error_code ret;
+ char line [2048];
+ FILE *f;
+ int lineno = 0;
+ struct hdb_entry_ex ent;
+
+ struct prop_data *pd = arg;
+
+ f = fopen(file, "r");
+ if(f == NULL)
+ return errno;
+
+ while(fgets(line, sizeof(line), f)) {
+ char *p = line, *q;
+
+ int i;
+
+ int num_tl_data;
+ int num_key_data;
+ int extra_data_length;
+ int attributes;
+
+ int tmp;
+
+ lineno++;
+
+ memset(&ent, 0, sizeof(ent));
+
+ q = nexttoken(&p);
+ if(strcmp(q, "kdb5_util") == 0) {
+ int major;
+ q = nexttoken(&p); /* load_dump */
+ if(strcmp(q, "load_dump"))
+ errx(1, "line %d: unknown version", lineno);
+ q = nexttoken(&p); /* load_dump */
+ if(strcmp(q, "version"))
+ errx(1, "line %d: unknown version", lineno);
+ q = nexttoken(&p); /* x.0 */
+ if(sscanf(q, "%d", &major) != 1)
+ errx(1, "line %d: unknown version", lineno);
+ if(major != 4)
+ errx(1, "unknown dump file format, got %d, expected 4", major);
+ continue;
+ } else if(strcmp(q, "princ") != 0) {
+ warnx("line %d: not a principal", lineno);
+ continue;
+ }
+ tmp = getint(&p);
+ if(tmp != 38) {
+ warnx("line %d: bad base length %d != 38", lineno, tmp);
+ continue;
+ }
+ q = nexttoken(&p); /* length of principal */
+ num_tl_data = getint(&p); /* number of tl-data */
+ num_key_data = getint(&p); /* number of key-data */
+ extra_data_length = getint(&p); /* length of extra data */
+ q = nexttoken(&p); /* principal name */
+ krb5_parse_name(pd->context, q, &ent.entry.principal);
+ attributes = getint(&p); /* attributes */
+ attr_to_flags(attributes, &ent.entry.flags);
+ tmp = getint(&p); /* max life */
+ if(tmp != 0) {
+ ALLOC(ent.entry.max_life);
+ *ent.entry.max_life = tmp;
+ }
+ tmp = getint(&p); /* max renewable life */
+ if(tmp != 0) {
+ ALLOC(ent.entry.max_renew);
+ *ent.entry.max_renew = tmp;
+ }
+ tmp = getint(&p); /* expiration */
+ if(tmp != 0 && tmp != 2145830400) {
+ ALLOC(ent.entry.valid_end);
+ *ent.entry.valid_end = tmp;
+ }
+ tmp = getint(&p); /* pw expiration */
+ if(tmp != 0) {
+ ALLOC(ent.entry.pw_end);
+ *ent.entry.pw_end = tmp;
+ }
+ q = nexttoken(&p); /* last auth */
+ q = nexttoken(&p); /* last failed auth */
+ q = nexttoken(&p); /* fail auth count */
+ for(i = 0; i < num_tl_data; i++) {
+ unsigned long val;
+ int tl_type, tl_length;
+ unsigned char *buf;
+ krb5_principal princ;
+
+ tl_type = getint(&p); /* data type */
+ tl_length = getint(&p); /* data length */
+
+#define mit_KRB5_TL_LAST_PWD_CHANGE 1
+#define mit_KRB5_TL_MOD_PRINC 2
+ switch(tl_type) {
+ case mit_KRB5_TL_MOD_PRINC:
+ buf = malloc(tl_length);
+ if (buf == NULL)
+ errx(ENOMEM, "malloc");
+ getdata(&p, buf, tl_length); /* data itself */
+ val = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ ret = krb5_parse_name(pd->context, (char *)buf + 4, &princ);
+ free(buf);
+ ALLOC(ent.entry.modified_by);
+ ent.entry.modified_by->time = val;
+ ent.entry.modified_by->principal = princ;
+ break;
+ default:
+ nexttoken(&p);
+ break;
+ }
+ }
+ ALLOC_SEQ(&ent.entry.keys, num_key_data);
+ for(i = 0; i < num_key_data; i++) {
+ int key_versions;
+ key_versions = getint(&p); /* key data version */
+ ent.entry.kvno = getint(&p); /* XXX kvno */
+
+ ALLOC(ent.entry.keys.val[i].mkvno);
+ *ent.entry.keys.val[i].mkvno = 0;
+
+ /* key version 0 -- actual key */
+ ent.entry.keys.val[i].key.keytype = getint(&p); /* key type */
+ tmp = getint(&p); /* key length */
+ /* the first two bytes of the key is the key length --
+ skip it */
+ krb5_data_alloc(&ent.entry.keys.val[i].key.keyvalue, tmp - 2);
+ q = nexttoken(&p); /* key itself */
+ hex_to_octet_string(q + 4, &ent.entry.keys.val[i].key.keyvalue);
+
+ if(key_versions > 1) {
+ /* key version 1 -- optional salt */
+ ALLOC(ent.entry.keys.val[i].salt);
+ ent.entry.keys.val[i].salt->type = getint(&p); /* salt type */
+ tmp = getint(&p); /* salt length */
+ if(tmp > 0) {
+ krb5_data_alloc(&ent.entry.keys.val[i].salt->salt, tmp - 2);
+ q = nexttoken(&p); /* salt itself */
+ hex_to_octet_string(q + 4,
+ &ent.entry.keys.val[i].salt->salt);
+ } else {
+ ent.entry.keys.val[i].salt->salt.length = 0;
+ ent.entry.keys.val[i].salt->salt.data = NULL;
+ tmp = getint(&p); /* -1, if no data. */
+ }
+ fix_salt(pd->context, &ent.entry, i);
+ }
+ }
+ q = nexttoken(&p); /* extra data */
+ v5_prop(pd->context, NULL, &ent, arg);
+ }
+ fclose(f);
+ return 0;
+}
diff --git a/kdc/pkinit.c b/kdc/pkinit.c
new file mode 100644
index 0000000000000..bf248af588fcb
--- /dev/null
+++ b/kdc/pkinit.c
@@ -0,0 +1,1673 @@
+/*
+ * Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: pkinit.c 22243 2007-12-08 23:39:30Z lha $");
+
+#ifdef PKINIT
+
+#include <heim_asn1.h>
+#include <rfc2459_asn1.h>
+#include <cms_asn1.h>
+#include <pkinit_asn1.h>
+
+#include <hx509.h>
+#include "crypto-headers.h"
+
+/* XXX copied from lib/krb5/pkinit.c */
+struct krb5_pk_identity {
+ hx509_context hx509ctx;
+ hx509_verify_ctx verify_ctx;
+ hx509_certs certs;
+ hx509_certs anchors;
+ hx509_certs certpool;
+ hx509_revoke_ctx revoke;
+};
+
+enum pkinit_type {
+ PKINIT_COMPAT_WIN2K = 1,
+ PKINIT_COMPAT_27 = 3
+};
+
+struct pk_client_params {
+ enum pkinit_type type;
+ BIGNUM *dh_public_key;
+ hx509_cert cert;
+ unsigned nonce;
+ DH *dh;
+ EncryptionKey reply_key;
+ char *dh_group_name;
+ hx509_peer_info peer;
+ hx509_certs client_anchors;
+};
+
+struct pk_principal_mapping {
+ unsigned int len;
+ struct pk_allowed_princ {
+ krb5_principal principal;
+ char *subject;
+ } *val;
+};
+
+static struct krb5_pk_identity *kdc_identity;
+static struct pk_principal_mapping principal_mappings;
+static struct krb5_dh_moduli **moduli;
+
+static struct {
+ krb5_data data;
+ time_t expire;
+ time_t next_update;
+} ocsp;
+
+/*
+ *
+ */
+
+static krb5_error_code
+pk_check_pkauthenticator_win2k(krb5_context context,
+ PKAuthenticator_Win2k *a,
+ const KDC_REQ *req)
+{
+ krb5_timestamp now;
+
+ krb5_timeofday (context, &now);
+
+ /* XXX cusec */
+ if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
+ krb5_clear_error_string(context);
+ return KRB5KRB_AP_ERR_SKEW;
+ }
+ return 0;
+}
+
+static krb5_error_code
+pk_check_pkauthenticator(krb5_context context,
+ PKAuthenticator *a,
+ const KDC_REQ *req)
+{
+ u_char *buf = NULL;
+ size_t buf_size;
+ krb5_error_code ret;
+ size_t len;
+ krb5_timestamp now;
+ Checksum checksum;
+
+ krb5_timeofday (context, &now);
+
+ /* XXX cusec */
+ if (a->ctime == 0 || abs(a->ctime - now) > context->max_skew) {
+ krb5_clear_error_string(context);
+ return KRB5KRB_AP_ERR_SKEW;
+ }
+
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, &req->req_body, &len, ret);
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+ if (buf_size != len)
+ krb5_abortx(context, "Internal error in ASN.1 encoder");
+
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_SHA1,
+ buf,
+ len,
+ &checksum);
+ free(buf);
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ if (a->paChecksum == NULL) {
+ krb5_clear_error_string(context);
+ ret = KRB5_KDC_ERR_PA_CHECKSUM_MUST_BE_INCLUDED;
+ goto out;
+ }
+
+ if (der_heim_octet_string_cmp(a->paChecksum, &checksum.checksum) != 0) {
+ krb5_clear_error_string(context);
+ ret = KRB5KRB_ERR_GENERIC;
+ }
+
+out:
+ free_Checksum(&checksum);
+
+ return ret;
+}
+
+void
+_kdc_pk_free_client_param(krb5_context context,
+ pk_client_params *client_params)
+{
+ if (client_params->cert)
+ hx509_cert_free(client_params->cert);
+ if (client_params->dh)
+ DH_free(client_params->dh);
+ if (client_params->dh_public_key)
+ BN_free(client_params->dh_public_key);
+ krb5_free_keyblock_contents(context, &client_params->reply_key);
+ if (client_params->dh_group_name)
+ free(client_params->dh_group_name);
+ if (client_params->peer)
+ hx509_peer_info_free(client_params->peer);
+ if (client_params->client_anchors)
+ hx509_certs_free(&client_params->client_anchors);
+ memset(client_params, 0, sizeof(*client_params));
+ free(client_params);
+}
+
+static krb5_error_code
+generate_dh_keyblock(krb5_context context, pk_client_params *client_params,
+ krb5_enctype enctype, krb5_keyblock *reply_key)
+{
+ unsigned char *dh_gen_key = NULL;
+ krb5_keyblock key;
+ krb5_error_code ret;
+ size_t dh_gen_keylen, size;
+
+ memset(&key, 0, sizeof(key));
+
+ if (!DH_generate_key(client_params->dh)) {
+ krb5_set_error_string(context, "Can't generate Diffie-Hellman keys");
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+ if (client_params->dh_public_key == NULL) {
+ krb5_set_error_string(context, "dh_public_key");
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+
+ dh_gen_keylen = DH_size(client_params->dh);
+ size = BN_num_bytes(client_params->dh->p);
+ if (size < dh_gen_keylen)
+ size = dh_gen_keylen;
+
+ dh_gen_key = malloc(size);
+ if (dh_gen_key == NULL) {
+ krb5_set_error_string(context, "malloc: out of memory");
+ ret = ENOMEM;
+ goto out;
+ }
+ memset(dh_gen_key, 0, size - dh_gen_keylen);
+
+ dh_gen_keylen = DH_compute_key(dh_gen_key + (size - dh_gen_keylen),
+ client_params->dh_public_key,
+ client_params->dh);
+ if (dh_gen_keylen == -1) {
+ krb5_set_error_string(context, "Can't compute Diffie-Hellman key");
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+
+ ret = _krb5_pk_octetstring2key(context,
+ enctype,
+ dh_gen_key, dh_gen_keylen,
+ NULL, NULL,
+ reply_key);
+
+ out:
+ if (dh_gen_key)
+ free(dh_gen_key);
+ if (key.keyvalue.data)
+ krb5_free_keyblock_contents(context, &key);
+
+ return ret;
+}
+
+static BIGNUM *
+integer_to_BN(krb5_context context, const char *field, heim_integer *f)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
+ if (bn == NULL) {
+ krb5_set_error_string(context, "PKINIT: parsing BN failed %s", field);
+ return NULL;
+ }
+ BN_set_negative(bn, f->negative);
+ return bn;
+}
+
+static krb5_error_code
+get_dh_param(krb5_context context,
+ krb5_kdc_configuration *config,
+ SubjectPublicKeyInfo *dh_key_info,
+ pk_client_params *client_params)
+{
+ DomainParameters dhparam;
+ DH *dh = NULL;
+ krb5_error_code ret;
+
+ memset(&dhparam, 0, sizeof(dhparam));
+
+ if (der_heim_oid_cmp(&dh_key_info->algorithm.algorithm, oid_id_dhpublicnumber())) {
+ krb5_set_error_string(context,
+ "PKINIT invalid oid in clientPublicValue");
+ return KRB5_BADMSGTYPE;
+ }
+
+ if (dh_key_info->algorithm.parameters == NULL) {
+ krb5_set_error_string(context, "PKINIT missing algorithm parameter "
+ "in clientPublicValue");
+ return KRB5_BADMSGTYPE;
+ }
+
+ ret = decode_DomainParameters(dh_key_info->algorithm.parameters->data,
+ dh_key_info->algorithm.parameters->length,
+ &dhparam,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "Can't decode algorithm "
+ "parameters in clientPublicValue");
+ goto out;
+ }
+
+ if ((dh_key_info->subjectPublicKey.length % 8) != 0) {
+ ret = KRB5_BADMSGTYPE;
+ krb5_set_error_string(context, "PKINIT: subjectPublicKey not aligned "
+ "to 8 bit boundary");
+ goto out;
+ }
+
+
+ ret = _krb5_dh_group_ok(context, config->pkinit_dh_min_bits,
+ &dhparam.p, &dhparam.g, &dhparam.q, moduli,
+ &client_params->dh_group_name);
+ if (ret) {
+ /* XXX send back proposal of better group */
+ goto out;
+ }
+
+ dh = DH_new();
+ if (dh == NULL) {
+ krb5_set_error_string(context, "Cannot create DH structure");
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = KRB5_BADMSGTYPE;
+ dh->p = integer_to_BN(context, "DH prime", &dhparam.p);
+ if (dh->p == NULL)
+ goto out;
+ dh->g = integer_to_BN(context, "DH base", &dhparam.g);
+ if (dh->g == NULL)
+ goto out;
+ dh->q = integer_to_BN(context, "DH p-1 factor", &dhparam.q);
+ if (dh->g == NULL)
+ goto out;
+
+ {
+ heim_integer glue;
+ size_t size;
+
+ ret = decode_DHPublicKey(dh_key_info->subjectPublicKey.data,
+ dh_key_info->subjectPublicKey.length / 8,
+ &glue,
+ &size);
+ if (ret) {
+ krb5_clear_error_string(context);
+ return ret;
+ }
+
+ client_params->dh_public_key = integer_to_BN(context,
+ "subjectPublicKey",
+ &glue);
+ der_free_heim_integer(&glue);
+ if (client_params->dh_public_key == NULL)
+ goto out;
+ }
+
+ client_params->dh = dh;
+ dh = NULL;
+ ret = 0;
+
+ out:
+ if (dh)
+ DH_free(dh);
+ free_DomainParameters(&dhparam);
+ return ret;
+}
+
+krb5_error_code
+_kdc_pk_rd_padata(krb5_context context,
+ krb5_kdc_configuration *config,
+ const KDC_REQ *req,
+ const PA_DATA *pa,
+ pk_client_params **ret_params)
+{
+ pk_client_params *client_params;
+ krb5_error_code ret;
+ heim_oid eContentType = { 0, NULL }, contentInfoOid = { 0, NULL };
+ krb5_data eContent = { 0, NULL };
+ krb5_data signed_content = { 0, NULL };
+ const char *type = "unknown type";
+ int have_data = 0;
+
+ *ret_params = NULL;
+
+ if (!config->enable_pkinit) {
+ kdc_log(context, config, 0, "PK-INIT request but PK-INIT not enabled");
+ krb5_clear_error_string(context);
+ return 0;
+ }
+
+ hx509_verify_set_time(kdc_identity->verify_ctx, _kdc_now.tv_sec);
+
+ client_params = calloc(1, sizeof(*client_params));
+ if (client_params == NULL) {
+ krb5_clear_error_string(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
+ PA_PK_AS_REQ_Win2k r;
+
+ type = "PK-INIT-Win2k";
+
+ ret = decode_PA_PK_AS_REQ_Win2k(pa->padata_value.data,
+ pa->padata_value.length,
+ &r,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "Can't decode "
+ "PK-AS-REQ-Win2k: %d", ret);
+ goto out;
+ }
+
+ ret = hx509_cms_unwrap_ContentInfo(&r.signed_auth_pack,
+ &contentInfoOid,
+ &signed_content,
+ &have_data);
+ free_PA_PK_AS_REQ_Win2k(&r);
+ if (ret) {
+ krb5_set_error_string(context, "Can't decode PK-AS-REQ: %d", ret);
+ goto out;
+ }
+
+ } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
+ PA_PK_AS_REQ r;
+
+ type = "PK-INIT-IETF";
+
+ ret = decode_PA_PK_AS_REQ(pa->padata_value.data,
+ pa->padata_value.length,
+ &r,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "Can't decode PK-AS-REQ: %d", ret);
+ goto out;
+ }
+
+ /* XXX look at r.kdcPkId */
+ if (r.trustedCertifiers) {
+ ExternalPrincipalIdentifiers *edi = r.trustedCertifiers;
+ unsigned int i;
+
+ ret = hx509_certs_init(kdc_identity->hx509ctx,
+ "MEMORY:client-anchors",
+ 0, NULL,
+ &client_params->client_anchors);
+ if (ret) {
+ krb5_set_error_string(context, "Can't allocate client anchors: %d", ret);
+ goto out;
+
+ }
+ for (i = 0; i < edi->len; i++) {
+ IssuerAndSerialNumber iasn;
+ hx509_query *q;
+ hx509_cert cert;
+ size_t size;
+
+ if (edi->val[i].issuerAndSerialNumber == NULL)
+ continue;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret) {
+ krb5_set_error_string(context,
+ "Failed to allocate hx509_query");
+ goto out;
+ }
+
+ ret = decode_IssuerAndSerialNumber(edi->val[i].issuerAndSerialNumber->data,
+ edi->val[i].issuerAndSerialNumber->length,
+ &iasn,
+ &size);
+ if (ret) {
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ continue;
+ }
+ ret = hx509_query_match_issuer_serial(q, &iasn.issuer, &iasn.serialNumber);
+ free_IssuerAndSerialNumber(&iasn);
+ if (ret)
+ continue;
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret)
+ continue;
+ hx509_certs_add(kdc_identity->hx509ctx,
+ client_params->client_anchors, cert);
+ hx509_cert_free(cert);
+ }
+ }
+
+ ret = hx509_cms_unwrap_ContentInfo(&r.signedAuthPack,
+ &contentInfoOid,
+ &signed_content,
+ &have_data);
+ free_PA_PK_AS_REQ(&r);
+ if (ret) {
+ krb5_set_error_string(context, "Can't unwrap ContentInfo: %d", ret);
+ goto out;
+ }
+
+ } else {
+ krb5_clear_error_string(context);
+ ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP;
+ goto out;
+ }
+
+ ret = der_heim_oid_cmp(&contentInfoOid, oid_id_pkcs7_signedData());
+ if (ret != 0) {
+ krb5_set_error_string(context, "PK-AS-REQ-Win2k invalid content "
+ "type oid");
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+
+ if (!have_data) {
+ krb5_set_error_string(context,
+ "PK-AS-REQ-Win2k no signed auth pack");
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+
+ {
+ hx509_certs signer_certs;
+
+ ret = hx509_cms_verify_signed(kdc_identity->hx509ctx,
+ kdc_identity->verify_ctx,
+ signed_content.data,
+ signed_content.length,
+ NULL,
+ kdc_identity->certpool,
+ &eContentType,
+ &eContent,
+ &signer_certs);
+ if (ret) {
+ char *s = hx509_get_error_string(kdc_identity->hx509ctx, ret);
+ krb5_warnx(context, "PKINIT: failed to verify signature: %s: %d",
+ s, ret);
+ free(s);
+ goto out;
+ }
+
+ ret = hx509_get_one_cert(kdc_identity->hx509ctx, signer_certs,
+ &client_params->cert);
+ hx509_certs_free(&signer_certs);
+ if (ret)
+ goto out;
+ }
+
+ /* Signature is correct, now verify the signed message */
+ if (der_heim_oid_cmp(&eContentType, oid_id_pkcs7_data()) != 0 &&
+ der_heim_oid_cmp(&eContentType, oid_id_pkauthdata()) != 0)
+ {
+ krb5_set_error_string(context, "got wrong oid for pkauthdata");
+ ret = KRB5_BADMSGTYPE;
+ goto out;
+ }
+
+ if (pa->padata_type == KRB5_PADATA_PK_AS_REQ_WIN) {
+ AuthPack_Win2k ap;
+
+ ret = decode_AuthPack_Win2k(eContent.data,
+ eContent.length,
+ &ap,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "can't decode AuthPack: %d", ret);
+ goto out;
+ }
+
+ ret = pk_check_pkauthenticator_win2k(context,
+ &ap.pkAuthenticator,
+ req);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ goto out;
+ }
+
+ client_params->type = PKINIT_COMPAT_WIN2K;
+ client_params->nonce = ap.pkAuthenticator.nonce;
+
+ if (ap.clientPublicValue) {
+ krb5_set_error_string(context, "DH not supported for windows");
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+ free_AuthPack_Win2k(&ap);
+
+ } else if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
+ AuthPack ap;
+
+ ret = decode_AuthPack(eContent.data,
+ eContent.length,
+ &ap,
+ NULL);
+ if (ret) {
+ krb5_set_error_string(context, "can't decode AuthPack: %d", ret);
+ free_AuthPack(&ap);
+ goto out;
+ }
+
+ ret = pk_check_pkauthenticator(context,
+ &ap.pkAuthenticator,
+ req);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+
+ client_params->type = PKINIT_COMPAT_27;
+ client_params->nonce = ap.pkAuthenticator.nonce;
+
+ if (ap.clientPublicValue) {
+ ret = get_dh_param(context, config,
+ ap.clientPublicValue, client_params);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+ }
+
+ if (ap.supportedCMSTypes) {
+ ret = hx509_peer_info_alloc(kdc_identity->hx509ctx,
+ &client_params->peer);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+ ret = hx509_peer_info_set_cms_algs(kdc_identity->hx509ctx,
+ client_params->peer,
+ ap.supportedCMSTypes->val,
+ ap.supportedCMSTypes->len);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+ }
+ free_AuthPack(&ap);
+ } else
+ krb5_abortx(context, "internal pkinit error");
+
+ kdc_log(context, config, 0, "PK-INIT request of type %s", type);
+
+out:
+ if (ret)
+ krb5_warn(context, ret, "PKINIT");
+
+ if (signed_content.data)
+ free(signed_content.data);
+ krb5_data_free(&eContent);
+ der_free_oid(&eContentType);
+ der_free_oid(&contentInfoOid);
+ if (ret)
+ _kdc_pk_free_client_param(context, client_params);
+ else
+ *ret_params = client_params;
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
+{
+ integer->length = BN_num_bytes(bn);
+ integer->data = malloc(integer->length);
+ if (integer->data == NULL) {
+ krb5_clear_error_string(context);
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, integer->data);
+ integer->negative = BN_is_negative(bn);
+ return 0;
+}
+
+static krb5_error_code
+pk_mk_pa_reply_enckey(krb5_context context,
+ krb5_kdc_configuration *config,
+ pk_client_params *client_params,
+ const KDC_REQ *req,
+ const krb5_data *req_buffer,
+ krb5_keyblock *reply_key,
+ ContentInfo *content_info)
+{
+ const heim_oid *envelopedAlg = NULL, *sdAlg = NULL;
+ krb5_error_code ret;
+ krb5_data buf, signed_data;
+ size_t size;
+ int do_win2k = 0;
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(&signed_data);
+
+ /*
+ * If the message client is a win2k-type but it send pa data
+ * 09-binding it expects a IETF (checksum) reply so there can be
+ * no replay attacks.
+ */
+
+ switch (client_params->type) {
+ case PKINIT_COMPAT_WIN2K: {
+ int i = 0;
+ if (_kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_09_BINDING) == NULL
+ && config->pkinit_require_binding == 0)
+ {
+ do_win2k = 1;
+ }
+ break;
+ }
+ case PKINIT_COMPAT_27:
+ break;
+ default:
+ krb5_abortx(context, "internal pkinit error");
+ }
+
+ if (do_win2k) {
+ ReplyKeyPack_Win2k kp;
+ memset(&kp, 0, sizeof(kp));
+
+ envelopedAlg = oid_id_rsadsi_des_ede3_cbc();
+ sdAlg = oid_id_pkcs7_data();
+
+ ret = copy_EncryptionKey(reply_key, &kp.replyKey);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ kp.nonce = client_params->nonce;
+
+ ASN1_MALLOC_ENCODE(ReplyKeyPack_Win2k,
+ buf.data, buf.length,
+ &kp, &size,ret);
+ free_ReplyKeyPack_Win2k(&kp);
+ } else {
+ krb5_crypto ascrypto;
+ ReplyKeyPack kp;
+ memset(&kp, 0, sizeof(kp));
+
+ sdAlg = oid_id_pkrkeydata();
+
+ ret = copy_EncryptionKey(reply_key, &kp.replyKey);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, reply_key, 0, &ascrypto);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ ret = krb5_create_checksum(context, ascrypto, 6, 0,
+ req_buffer->data, req_buffer->length,
+ &kp.asChecksum);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+
+ ret = krb5_crypto_destroy(context, ascrypto);
+ if (ret) {
+ krb5_clear_error_string(context);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(ReplyKeyPack, buf.data, buf.length, &kp, &size,ret);
+ free_ReplyKeyPack(&kp);
+ }
+ if (ret) {
+ krb5_set_error_string(context, "ASN.1 encoding of ReplyKeyPack "
+ "failed (%d)", ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ {
+ hx509_query *q;
+ hx509_cert cert;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret)
+ goto out;
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
+ 0,
+ sdAlg,
+ buf.data,
+ buf.length,
+ NULL,
+ cert,
+ client_params->peer,
+ client_params->client_anchors,
+ kdc_identity->certpool,
+ &signed_data);
+ hx509_cert_free(cert);
+ }
+
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ if (client_params->type == PKINIT_COMPAT_WIN2K) {
+ ret = hx509_cms_wrap_ContentInfo(oid_id_pkcs7_signedData(),
+ &signed_data,
+ &buf);
+ if (ret)
+ goto out;
+ krb5_data_free(&signed_data);
+ signed_data = buf;
+ }
+
+ ret = hx509_cms_envelope_1(kdc_identity->hx509ctx,
+ 0,
+ client_params->cert,
+ signed_data.data, signed_data.length,
+ envelopedAlg,
+ oid_id_pkcs7_signedData(), &buf);
+ if (ret)
+ goto out;
+
+ ret = _krb5_pk_mk_ContentInfo(context,
+ &buf,
+ oid_id_pkcs7_envelopedData(),
+ content_info);
+out:
+ krb5_data_free(&buf);
+ krb5_data_free(&signed_data);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+pk_mk_pa_reply_dh(krb5_context context,
+ DH *kdc_dh,
+ pk_client_params *client_params,
+ krb5_keyblock *reply_key,
+ ContentInfo *content_info,
+ hx509_cert *kdc_cert)
+{
+ KDCDHKeyInfo dh_info;
+ krb5_data signed_data, buf;
+ ContentInfo contentinfo;
+ krb5_error_code ret;
+ size_t size;
+ heim_integer i;
+
+ memset(&contentinfo, 0, sizeof(contentinfo));
+ memset(&dh_info, 0, sizeof(dh_info));
+ krb5_data_zero(&buf);
+ krb5_data_zero(&signed_data);
+
+ *kdc_cert = NULL;
+
+ ret = BN_to_integer(context, kdc_dh->pub_key, &i);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(DHPublicKey, buf.data, buf.length, &i, &size, ret);
+ if (ret) {
+ krb5_set_error_string(context, "ASN.1 encoding of "
+ "DHPublicKey failed (%d)", ret);
+ krb5_clear_error_string(context);
+ return ret;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ dh_info.subjectPublicKey.length = buf.length * 8;
+ dh_info.subjectPublicKey.data = buf.data;
+
+ dh_info.nonce = client_params->nonce;
+
+ ASN1_MALLOC_ENCODE(KDCDHKeyInfo, buf.data, buf.length, &dh_info, &size,
+ ret);
+ if (ret) {
+ krb5_set_error_string(context, "ASN.1 encoding of "
+ "KdcDHKeyInfo failed (%d)", ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ /*
+ * Create the SignedData structure and sign the KdcDHKeyInfo
+ * filled in above
+ */
+
+ {
+ hx509_query *q;
+ hx509_cert cert;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret)
+ goto out;
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_create_signed_1(kdc_identity->hx509ctx,
+ 0,
+ oid_id_pkdhkeydata(),
+ buf.data,
+ buf.length,
+ NULL,
+ cert,
+ client_params->peer,
+ client_params->client_anchors,
+ kdc_identity->certpool,
+ &signed_data);
+ *kdc_cert = cert;
+ }
+ if (ret)
+ goto out;
+
+ ret = _krb5_pk_mk_ContentInfo(context,
+ &signed_data,
+ oid_id_pkcs7_signedData(),
+ content_info);
+ if (ret)
+ goto out;
+
+ out:
+ if (ret && *kdc_cert) {
+ hx509_cert_free(*kdc_cert);
+ *kdc_cert = NULL;
+ }
+
+ krb5_data_free(&buf);
+ krb5_data_free(&signed_data);
+ free_KDCDHKeyInfo(&dh_info);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_pk_mk_pa_reply(krb5_context context,
+ krb5_kdc_configuration *config,
+ pk_client_params *client_params,
+ const hdb_entry_ex *client,
+ const KDC_REQ *req,
+ const krb5_data *req_buffer,
+ krb5_keyblock **reply_key,
+ METHOD_DATA *md)
+{
+ krb5_error_code ret;
+ void *buf;
+ size_t len, size;
+ krb5_enctype enctype;
+ int pa_type;
+ hx509_cert kdc_cert = NULL;
+ int i;
+
+ if (!config->enable_pkinit) {
+ krb5_clear_error_string(context);
+ return 0;
+ }
+
+ if (req->req_body.etype.len > 0) {
+ for (i = 0; i < req->req_body.etype.len; i++)
+ if (krb5_enctype_valid(context, req->req_body.etype.val[i]) == 0)
+ break;
+ if (req->req_body.etype.len <= i) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_string(context,
+ "No valid enctype available from client");
+ goto out;
+ }
+ enctype = req->req_body.etype.val[i];
+ } else
+ enctype = ETYPE_DES3_CBC_SHA1;
+
+ if (client_params->type == PKINIT_COMPAT_27) {
+ PA_PK_AS_REP rep;
+ const char *type, *other = "";
+
+ memset(&rep, 0, sizeof(rep));
+
+ pa_type = KRB5_PADATA_PK_AS_REP;
+
+ if (client_params->dh == NULL) {
+ ContentInfo info;
+
+ type = "enckey";
+
+ rep.element = choice_PA_PK_AS_REP_encKeyPack;
+
+ ret = krb5_generate_random_keyblock(context, enctype,
+ &client_params->reply_key);
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ ret = pk_mk_pa_reply_enckey(context,
+ config,
+ client_params,
+ req,
+ req_buffer,
+ &client_params->reply_key,
+ &info);
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
+ rep.u.encKeyPack.length, &info, &size,
+ ret);
+ free_ContentInfo(&info);
+ if (ret) {
+ krb5_set_error_string(context, "encoding of Key ContentInfo "
+ "failed %d", ret);
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ if (rep.u.encKeyPack.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ } else {
+ ContentInfo info;
+
+ type = "dh";
+ if (client_params->dh_group_name)
+ other = client_params->dh_group_name;
+
+ rep.element = choice_PA_PK_AS_REP_dhInfo;
+
+ ret = generate_dh_keyblock(context, client_params, enctype,
+ &client_params->reply_key);
+ if (ret)
+ return ret;
+
+ ret = pk_mk_pa_reply_dh(context, client_params->dh,
+ client_params,
+ &client_params->reply_key,
+ &info,
+ &kdc_cert);
+
+ ASN1_MALLOC_ENCODE(ContentInfo, rep.u.dhInfo.dhSignedData.data,
+ rep.u.dhInfo.dhSignedData.length, &info, &size,
+ ret);
+ free_ContentInfo(&info);
+ if (ret) {
+ krb5_set_error_string(context, "encoding of Key ContentInfo "
+ "failed %d", ret);
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+ if (rep.u.encKeyPack.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ }
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REP, buf, len, &rep, &size, ret);
+ free_PA_PK_AS_REP(&rep);
+ if (ret) {
+ krb5_set_error_string(context, "encode PA-PK-AS-REP failed %d",
+ ret);
+ goto out;
+ }
+ if (len != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ kdc_log(context, config, 0, "PK-INIT using %s %s", type, other);
+
+ } else if (client_params->type == PKINIT_COMPAT_WIN2K) {
+ PA_PK_AS_REP_Win2k rep;
+ ContentInfo info;
+
+ if (client_params->dh) {
+ krb5_set_error_string(context, "Windows PK-INIT doesn't support DH");
+ ret = KRB5KRB_ERR_GENERIC;
+ goto out;
+ }
+
+ memset(&rep, 0, sizeof(rep));
+
+ pa_type = KRB5_PADATA_PK_AS_REP_19;
+ rep.element = choice_PA_PK_AS_REP_encKeyPack;
+
+ ret = krb5_generate_random_keyblock(context, enctype,
+ &client_params->reply_key);
+ if (ret) {
+ free_PA_PK_AS_REP_Win2k(&rep);
+ goto out;
+ }
+ ret = pk_mk_pa_reply_enckey(context,
+ config,
+ client_params,
+ req,
+ req_buffer,
+ &client_params->reply_key,
+ &info);
+ if (ret) {
+ free_PA_PK_AS_REP_Win2k(&rep);
+ goto out;
+ }
+ ASN1_MALLOC_ENCODE(ContentInfo, rep.u.encKeyPack.data,
+ rep.u.encKeyPack.length, &info, &size,
+ ret);
+ free_ContentInfo(&info);
+ if (ret) {
+ krb5_set_error_string(context, "encoding of Key ContentInfo "
+ "failed %d", ret);
+ free_PA_PK_AS_REP_Win2k(&rep);
+ goto out;
+ }
+ if (rep.u.encKeyPack.length != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REP_Win2k, buf, len, &rep, &size, ret);
+ free_PA_PK_AS_REP_Win2k(&rep);
+ if (ret) {
+ krb5_set_error_string(context,
+ "encode PA-PK-AS-REP-Win2k failed %d", ret);
+ goto out;
+ }
+ if (len != size)
+ krb5_abortx(context, "Internal ASN.1 encoder error");
+
+ } else
+ krb5_abortx(context, "PK-INIT internal error");
+
+
+ ret = krb5_padata_add(context, md, pa_type, buf, len);
+ if (ret) {
+ krb5_set_error_string(context, "failed adding PA-PK-AS-REP %d", ret);
+ free(buf);
+ goto out;
+ }
+
+ if (config->pkinit_kdc_ocsp_file) {
+
+ if (ocsp.expire == 0 && ocsp.next_update > kdc_time) {
+ struct stat sb;
+ int fd;
+
+ krb5_data_free(&ocsp.data);
+
+ ocsp.expire = 0;
+ ocsp.next_update = kdc_time + 60 * 5;
+
+ fd = open(config->pkinit_kdc_ocsp_file, O_RDONLY);
+ if (fd < 0) {
+ kdc_log(context, config, 0,
+ "PK-INIT failed to open ocsp data file %d", errno);
+ goto out_ocsp;
+ }
+ ret = fstat(fd, &sb);
+ if (ret) {
+ ret = errno;
+ close(fd);
+ kdc_log(context, config, 0,
+ "PK-INIT failed to stat ocsp data %d", ret);
+ goto out_ocsp;
+ }
+
+ ret = krb5_data_alloc(&ocsp.data, sb.st_size);
+ if (ret) {
+ close(fd);
+ kdc_log(context, config, 0,
+ "PK-INIT failed to stat ocsp data %d", ret);
+ goto out_ocsp;
+ }
+ ocsp.data.length = sb.st_size;
+ ret = read(fd, ocsp.data.data, sb.st_size);
+ close(fd);
+ if (ret != sb.st_size) {
+ kdc_log(context, config, 0,
+ "PK-INIT failed to read ocsp data %d", errno);
+ goto out_ocsp;
+ }
+
+ ret = hx509_ocsp_verify(kdc_identity->hx509ctx,
+ kdc_time,
+ kdc_cert,
+ 0,
+ ocsp.data.data, ocsp.data.length,
+ &ocsp.expire);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "PK-INIT failed to verify ocsp data %d", ret);
+ krb5_data_free(&ocsp.data);
+ ocsp.expire = 0;
+ } else if (ocsp.expire > 180) {
+ ocsp.expire -= 180; /* refetch the ocsp before it expire */
+ ocsp.next_update = ocsp.expire;
+ } else {
+ ocsp.next_update = kdc_time;
+ }
+ out_ocsp:
+ ret = 0;
+ }
+
+ if (ocsp.expire != 0 && ocsp.expire > kdc_time) {
+
+ ret = krb5_padata_add(context, md,
+ KRB5_PADATA_PA_PK_OCSP_RESPONSE,
+ ocsp.data.data, ocsp.data.length);
+ if (ret) {
+ krb5_set_error_string(context,
+ "Failed adding OCSP response %d", ret);
+ goto out;
+ }
+ }
+ }
+
+out:
+ if (kdc_cert)
+ hx509_cert_free(kdc_cert);
+
+ if (ret == 0)
+ *reply_key = &client_params->reply_key;
+ return ret;
+}
+
+static int
+match_rfc_san(krb5_context context,
+ krb5_kdc_configuration *config,
+ hx509_context hx509ctx,
+ hx509_cert client_cert,
+ krb5_const_principal match)
+{
+ hx509_octet_string_list list;
+ int ret, i, found = 0;
+
+ memset(&list, 0 , sizeof(list));
+
+ ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
+ client_cert,
+ oid_id_pkinit_san(),
+ &list);
+ if (ret)
+ goto out;
+
+ for (i = 0; !found && i < list.len; i++) {
+ krb5_principal_data principal;
+ KRB5PrincipalName kn;
+ size_t size;
+
+ ret = decode_KRB5PrincipalName(list.val[i].data,
+ list.val[i].length,
+ &kn, &size);
+ if (ret) {
+ kdc_log(context, config, 0,
+ "Decoding kerberos name in certificate failed: %s",
+ krb5_get_err_text(context, ret));
+ break;
+ }
+ if (size != list.val[i].length) {
+ kdc_log(context, config, 0,
+ "Decoding kerberos name have extra bits on the end");
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+ }
+
+ principal.name = kn.principalName;
+ principal.realm = kn.realm;
+
+ if (krb5_principal_compare(context, &principal, match) == TRUE)
+ found = 1;
+ free_KRB5PrincipalName(&kn);
+ }
+
+out:
+ hx509_free_octet_string_list(&list);
+ if (ret)
+ return ret;
+
+ if (!found)
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+
+ return 0;
+}
+
+static int
+match_ms_upn_san(krb5_context context,
+ krb5_kdc_configuration *config,
+ hx509_context hx509ctx,
+ hx509_cert client_cert,
+ krb5_const_principal match)
+{
+ hx509_octet_string_list list;
+ krb5_principal principal = NULL;
+ int ret, found = 0;
+ MS_UPN_SAN upn;
+ size_t size;
+
+ memset(&list, 0 , sizeof(list));
+
+ ret = hx509_cert_find_subjectAltName_otherName(hx509ctx,
+ client_cert,
+ oid_id_pkinit_ms_san(),
+ &list);
+ if (ret)
+ goto out;
+
+ if (list.len != 1) {
+ kdc_log(context, config, 0,
+ "More then one PK-INIT MS UPN SAN");
+ goto out;
+ }
+
+ ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length, &upn, &size);
+ if (ret) {
+ kdc_log(context, config, 0, "Decode of MS-UPN-SAN failed");
+ goto out;
+ }
+
+ kdc_log(context, config, 0, "found MS UPN SAN: %s", upn);
+
+ ret = krb5_parse_name(context, upn, &principal);
+ free_MS_UPN_SAN(&upn);
+ if (ret) {
+ kdc_log(context, config, 0, "Failed to parse principal in MS UPN SAN");
+ goto out;
+ }
+
+ /*
+ * This is very wrong, but will do for now, should really and a
+ * plugin to the windc layer to very this ACL.
+ */
+ strupr(principal->realm);
+
+ if (krb5_principal_compare(context, principal, match) == TRUE)
+ found = 1;
+
+out:
+ if (principal)
+ krb5_free_principal(context, principal);
+ hx509_free_octet_string_list(&list);
+ if (ret)
+ return ret;
+
+ if (!found)
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+
+ return 0;
+}
+
+krb5_error_code
+_kdc_pk_check_client(krb5_context context,
+ krb5_kdc_configuration *config,
+ const hdb_entry_ex *client,
+ pk_client_params *client_params,
+ char **subject_name)
+{
+ const HDB_Ext_PKINIT_acl *acl;
+ krb5_error_code ret;
+ hx509_name name;
+ int i;
+
+ ret = hx509_cert_get_base_subject(kdc_identity->hx509ctx,
+ client_params->cert,
+ &name);
+ if (ret)
+ return ret;
+
+ ret = hx509_name_to_string(name, subject_name);
+ hx509_name_free(&name);
+ if (ret)
+ return ret;
+
+ kdc_log(context, config, 0,
+ "Trying to authorize PK-INIT subject DN %s",
+ *subject_name);
+
+ if (config->pkinit_princ_in_cert) {
+ ret = match_rfc_san(context, config,
+ kdc_identity->hx509ctx,
+ client_params->cert,
+ client->entry.principal);
+ if (ret == 0) {
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT SAN in certificate");
+ return 0;
+ }
+ ret = match_ms_upn_san(context, config,
+ kdc_identity->hx509ctx,
+ client_params->cert,
+ client->entry.principal);
+ if (ret == 0) {
+ kdc_log(context, config, 5,
+ "Found matching MS UPN SAN in certificate");
+ return 0;
+ }
+ }
+
+ ret = hdb_entry_get_pkinit_acl(&client->entry, &acl);
+ if (ret == 0 && acl != NULL) {
+ /*
+ * Cheat here and compare the generated name with the string
+ * and not the reverse.
+ */
+ for (i = 0; i < acl->len; i++) {
+ if (strcmp(*subject_name, acl->val[0].subject) != 0)
+ continue;
+
+ /* Don't support isser and anchor checking right now */
+ if (acl->val[0].issuer)
+ continue;
+ if (acl->val[0].anchor)
+ continue;
+
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT database ACL");
+ return 0;
+ }
+ }
+
+ for (i = 0; i < principal_mappings.len; i++) {
+ krb5_boolean b;
+
+ b = krb5_principal_compare(context,
+ client->entry.principal,
+ principal_mappings.val[i].principal);
+ if (b == FALSE)
+ continue;
+ if (strcmp(principal_mappings.val[i].subject, *subject_name) != 0)
+ continue;
+ kdc_log(context, config, 5,
+ "Found matching PK-INIT FILE ACL");
+ return 0;
+ }
+
+ krb5_set_error_string(context,
+ "PKINIT no matching principals for %s",
+ *subject_name);
+
+ kdc_log(context, config, 5,
+ "PKINIT no matching principals for %s",
+ *subject_name);
+
+ free(*subject_name);
+ *subject_name = NULL;
+
+ return KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+}
+
+static krb5_error_code
+add_principal_mapping(krb5_context context,
+ const char *principal_name,
+ const char * subject)
+{
+ struct pk_allowed_princ *tmp;
+ krb5_principal principal;
+ krb5_error_code ret;
+
+ tmp = realloc(principal_mappings.val,
+ (principal_mappings.len + 1) * sizeof(*tmp));
+ if (tmp == NULL)
+ return ENOMEM;
+ principal_mappings.val = tmp;
+
+ ret = krb5_parse_name(context, principal_name, &principal);
+ if (ret)
+ return ret;
+
+ principal_mappings.val[principal_mappings.len].principal = principal;
+
+ principal_mappings.val[principal_mappings.len].subject = strdup(subject);
+ if (principal_mappings.val[principal_mappings.len].subject == NULL) {
+ krb5_free_principal(context, principal);
+ return ENOMEM;
+ }
+ principal_mappings.len++;
+
+ return 0;
+}
+
+krb5_error_code
+_kdc_add_inital_verified_cas(krb5_context context,
+ krb5_kdc_configuration *config,
+ pk_client_params *params,
+ EncTicketPart *tkt)
+{
+ AD_INITIAL_VERIFIED_CAS cas;
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+
+ memset(&cas, 0, sizeof(cas));
+
+ /* XXX add CAs to cas here */
+
+ ASN1_MALLOC_ENCODE(AD_INITIAL_VERIFIED_CAS, data.data, data.length,
+ &cas, &size, ret);
+ if (ret)
+ return ret;
+ if (data.length != size)
+ krb5_abortx(context, "internal asn.1 encoder error");
+
+ ret = _kdc_tkt_add_if_relevant_ad(context, tkt,
+ KRB5_AUTHDATA_INITIAL_VERIFIED_CAS,
+ &data);
+ krb5_data_free(&data);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static void
+load_mappings(krb5_context context, const char *fn)
+{
+ krb5_error_code ret;
+ char buf[1024];
+ unsigned long lineno = 0;
+ FILE *f;
+
+ f = fopen(fn, "r");
+ if (f == NULL)
+ return;
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ char *subject_name, *p;
+
+ buf[strcspn(buf, "\n")] = '\0';
+ lineno++;
+
+ p = buf + strspn(buf, " \t");
+
+ if (*p == '#' || *p == '\0')
+ continue;
+
+ subject_name = strchr(p, ':');
+ if (subject_name == NULL) {
+ krb5_warnx(context, "pkinit mapping file line %lu "
+ "missing \":\" :%s",
+ lineno, buf);
+ continue;
+ }
+ *subject_name++ = '\0';
+
+ ret = add_principal_mapping(context, p, subject_name);
+ if (ret) {
+ krb5_warn(context, ret, "failed to add line %lu \":\" :%s\n",
+ lineno, buf);
+ continue;
+ }
+ }
+
+ fclose(f);
+}
+
+/*
+ *
+ */
+
+krb5_error_code
+_kdc_pk_initialize(krb5_context context,
+ krb5_kdc_configuration *config,
+ const char *user_id,
+ const char *anchors,
+ char **pool,
+ char **revoke_list)
+{
+ const char *file;
+ char *fn = NULL;
+ krb5_error_code ret;
+
+ file = krb5_config_get_string(context, NULL,
+ "libdefaults", "moduli", NULL);
+
+ ret = _krb5_parse_moduli(context, file, &moduli);
+ if (ret)
+ krb5_err(context, 1, ret, "PKINIT: failed to load modidi file");
+
+ principal_mappings.len = 0;
+ principal_mappings.val = NULL;
+
+ ret = _krb5_pk_load_id(context,
+ &kdc_identity,
+ user_id,
+ anchors,
+ pool,
+ revoke_list,
+ NULL,
+ NULL,
+ NULL);
+ if (ret) {
+ krb5_warn(context, ret, "PKINIT: ");
+ config->enable_pkinit = 0;
+ return ret;
+ }
+
+ {
+ hx509_query *q;
+ hx509_cert cert;
+
+ ret = hx509_query_alloc(kdc_identity->hx509ctx, &q);
+ if (ret) {
+ krb5_warnx(context, "PKINIT: out of memory");
+ return ENOMEM;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ ret = hx509_certs_find(kdc_identity->hx509ctx,
+ kdc_identity->certs,
+ q,
+ &cert);
+ hx509_query_free(kdc_identity->hx509ctx, q);
+ if (ret == 0) {
+ if (hx509_cert_check_eku(kdc_identity->hx509ctx, cert,
+ oid_id_pkkdcekuoid(), 0))
+ krb5_warnx(context, "WARNING Found KDC certificate "
+ "is missing the PK-INIT KDC EKU, this is bad for "
+ "interoperability.");
+ hx509_cert_free(cert);
+ } else
+ krb5_warnx(context, "PKINIT: failed to find a signing "
+ "certifiate with a public key");
+ }
+
+ ret = krb5_config_get_bool_default(context,
+ NULL,
+ FALSE,
+ "kdc",
+ "pkinit_allow_proxy_certificate",
+ NULL);
+ _krb5_pk_allow_proxy_certificate(kdc_identity, ret);
+
+ file = krb5_config_get_string(context,
+ NULL,
+ "kdc",
+ "pkinit_mappings_file",
+ NULL);
+ if (file == NULL) {
+ asprintf(&fn, "%s/pki-mapping", hdb_db_dir(context));
+ file = fn;
+ }
+
+ load_mappings(context, file);
+ if (fn)
+ free(fn);
+
+ return 0;
+}
+
+#endif /* PKINIT */
diff --git a/kdc/process.c b/kdc/process.c
new file mode 100644
index 0000000000000..1d0a01a215d10
--- /dev/null
+++ b/kdc/process.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: process.c 20959 2007-06-07 04:46:06Z lha $");
+
+/*
+ *
+ */
+
+void
+krb5_kdc_update_time(struct timeval *tv)
+{
+ if (tv == NULL)
+ gettimeofday(&_kdc_now, NULL);
+ else
+ _kdc_now = *tv;
+}
+
+/*
+ * handle the request in `buf, len', from `addr' (or `from' as a string),
+ * sending a reply in `reply'.
+ */
+
+int
+krb5_kdc_process_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ krb5_boolean *prependlength,
+ const char *from,
+ struct sockaddr *addr,
+ int datagram_reply)
+{
+ KDC_REQ req;
+ Ticket ticket;
+ DigestREQ digestreq;
+ Kx509Request kx509req;
+ krb5_error_code ret;
+ size_t i;
+
+ if(decode_AS_REQ(buf, len, &req, &i) == 0){
+ krb5_data req_buffer;
+
+ req_buffer.data = buf;
+ req_buffer.length = len;
+
+ ret = _kdc_as_rep(context, config, &req, &req_buffer,
+ reply, from, addr, datagram_reply);
+ free_AS_REQ(&req);
+ return ret;
+ }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){
+ ret = _kdc_tgs_rep(context, config, &req, reply, from, addr, datagram_reply);
+ free_TGS_REQ(&req);
+ return ret;
+ }else if(decode_Ticket(buf, len, &ticket, &i) == 0){
+ ret = _kdc_do_524(context, config, &ticket, reply, from, addr);
+ free_Ticket(&ticket);
+ return ret;
+ }else if(decode_DigestREQ(buf, len, &digestreq, &i) == 0){
+ ret = _kdc_do_digest(context, config, &digestreq, reply, from, addr);
+ free_DigestREQ(&digestreq);
+ return ret;
+ } else if (_kdc_try_kx509_request(buf, len, &kx509req, &i) == 0) {
+ ret = _kdc_do_kx509(context, config, &kx509req, reply, from, addr);
+ free_Kx509Request(&kx509req);
+ return ret;
+ } else if(_kdc_maybe_version4(buf, len)){
+ *prependlength = FALSE; /* elbitapmoc sdrawkcab XXX */
+ _kdc_do_version4(context, config, buf, len, reply, from,
+ (struct sockaddr_in*)addr);
+ return 0;
+ } else if (config->enable_kaserver) {
+ ret = _kdc_do_kaserver(context, config, buf, len, reply, from,
+ (struct sockaddr_in*)addr);
+ return ret;
+ }
+
+ return -1;
+}
+
+/*
+ * handle the request in `buf, len', from `addr' (or `from' as a string),
+ * sending a reply in `reply'.
+ *
+ * This only processes krb5 requests
+ */
+
+int
+krb5_kdc_process_krb5_request(krb5_context context,
+ krb5_kdc_configuration *config,
+ unsigned char *buf,
+ size_t len,
+ krb5_data *reply,
+ const char *from,
+ struct sockaddr *addr,
+ int datagram_reply)
+{
+ KDC_REQ req;
+ krb5_error_code ret;
+ size_t i;
+
+ if(decode_AS_REQ(buf, len, &req, &i) == 0){
+ krb5_data req_buffer;
+
+ req_buffer.data = buf;
+ req_buffer.length = len;
+
+ ret = _kdc_as_rep(context, config, &req, &req_buffer,
+ reply, from, addr, datagram_reply);
+ free_AS_REQ(&req);
+ return ret;
+ }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){
+ ret = _kdc_tgs_rep(context, config, &req, reply, from, addr, datagram_reply);
+ free_TGS_REQ(&req);
+ return ret;
+ }
+ return -1;
+}
+
+/*
+ *
+ */
+
+int
+krb5_kdc_save_request(krb5_context context,
+ const char *fn,
+ const unsigned char *buf,
+ size_t len,
+ const krb5_data *reply,
+ const struct sockaddr *sa)
+{
+ krb5_storage *sp;
+ krb5_address a;
+ int fd, ret;
+ uint32_t t;
+ krb5_data d;
+
+ memset(&a, 0, sizeof(a));
+
+ d.data = rk_UNCONST(buf);
+ d.length = len;
+ t = _kdc_now.tv_sec;
+
+ fd = open(fn, O_WRONLY|O_CREAT|O_APPEND, 0600);
+ if (fd < 0) {
+ krb5_set_error_string(context, "Failed to open: %s", fn);
+ return errno;
+ }
+
+ sp = krb5_storage_from_fd(fd);
+ close(fd);
+ if (sp == NULL) {
+ krb5_set_error_string(context, "Storage failed to open fd");
+ return ENOMEM;
+ }
+
+ ret = krb5_sockaddr2address(context, sa, &a);
+ if (ret)
+ goto out;
+
+ krb5_store_uint32(sp, 1);
+ krb5_store_uint32(sp, t);
+ krb5_store_address(sp, a);
+ krb5_store_data(sp, d);
+ {
+ Der_class cl;
+ Der_type ty;
+ unsigned int tag;
+ ret = der_get_tag (reply->data, reply->length,
+ &cl, &ty, &tag, NULL);
+ if (ret) {
+ krb5_store_uint32(sp, 0xffffffff);
+ krb5_store_uint32(sp, 0xffffffff);
+ } else {
+ krb5_store_uint32(sp, MAKE_TAG(cl, ty, 0));
+ krb5_store_uint32(sp, tag);
+ }
+ }
+
+ krb5_free_address(context, &a);
+out:
+ krb5_storage_free(sp);
+
+ return 0;
+}
diff --git a/kdc/rx.h b/kdc/rx.h
new file mode 100644
index 0000000000000..18806d79dae69
--- /dev/null
+++ b/kdc/rx.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id: rx.h 17447 2006-05-05 10:52:01Z lha $ */
+
+#ifndef __RX_H__
+#define __RX_H__
+
+/* header of a RPC packet */
+
+enum rx_header_type {
+ HT_DATA = 1,
+ HT_ACK = 2,
+ HT_BUSY = 3,
+ HT_ABORT = 4,
+ HT_ACKALL = 5,
+ HT_CHAL = 6,
+ HT_RESP = 7,
+ HT_DEBUG = 8
+};
+
+/* For flags in header */
+
+enum rx_header_flag {
+ HF_CLIENT_INITIATED = 1,
+ HF_REQ_ACK = 2,
+ HF_LAST = 4,
+ HF_MORE = 8
+};
+
+struct rx_header {
+ uint32_t epoch;
+ uint32_t connid; /* And channel ID */
+ uint32_t callid;
+ uint32_t seqno;
+ uint32_t serialno;
+ u_char type;
+ u_char flags;
+ u_char status;
+ u_char secindex;
+ uint16_t reserved; /* ??? verifier? */
+ uint16_t serviceid;
+/* This should be the other way around according to everything but */
+/* tcpdump */
+};
+
+#define RX_HEADER_SIZE 28
+
+#endif /* __RX_H__ */
diff --git a/kdc/set_dbinfo.c b/kdc/set_dbinfo.c
new file mode 100644
index 0000000000000..651f4c4a4b21a
--- /dev/null
+++ b/kdc/set_dbinfo.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: default_config.c 21296 2007-06-25 14:49:11Z lha $");
+
+krb5_error_code
+krb5_kdc_set_dbinfo(krb5_context context, struct krb5_kdc_configuration *c)
+{
+ struct hdb_dbinfo *info, *d;
+ krb5_error_code ret;
+ int i;
+
+ /* fetch the databases */
+ ret = hdb_get_dbinfo(context, &info);
+ if (ret)
+ return ret;
+
+ d = NULL;
+ while ((d = hdb_dbinfo_get_next(info, d)) != NULL) {
+ void *ptr;
+
+ ptr = realloc(c->db, (c->num_db + 1) * sizeof(*c->db));
+ if (ptr == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_string(context, "out of memory");
+ goto out;
+ }
+ c->db = ptr;
+
+ ret = hdb_create(context, &c->db[c->num_db],
+ hdb_dbinfo_get_dbname(context, d));
+ if(ret)
+ goto out;
+
+ ret = hdb_set_master_keyfile(context, c->db[c->num_db],
+ hdb_dbinfo_get_mkey_file(context, d));
+ if (ret)
+ goto out;
+
+ c->num_db++;
+
+ kdc_log(context, c, 0, "label: %s",
+ hdb_dbinfo_get_label(context, d));
+ kdc_log(context, c, 0, "\tdbname: %s",
+ hdb_dbinfo_get_dbname(context, d));
+ kdc_log(context, c, 0, "\tmkey_file: %s",
+ hdb_dbinfo_get_mkey_file(context, d));
+ kdc_log(context, c, 0, "\tacl_file: %s",
+ hdb_dbinfo_get_acl_file(context, d));
+ }
+ hdb_free_dbinfo(context, &info);
+
+ return 0;
+out:
+ for (i = 0; i < c->num_db; i++)
+ if (c->db[i] && c->db[i]->hdb_destroy)
+ (*c->db[i]->hdb_destroy)(context, c->db[i]);
+ c->num_db = 0;
+ free(c->db);
+ c->db = NULL;
+
+ hdb_free_dbinfo(context, &info);
+
+ return ret;
+}
+
+
diff --git a/kdc/string2key.8 b/kdc/string2key.8
new file mode 100644
index 0000000000000..8f2d562cc8096
--- /dev/null
+++ b/kdc/string2key.8
@@ -0,0 +1,110 @@
+.\" Copyright (c) 2000 - 2002 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: string2key.8 11648 2003-02-16 21:10:32Z lha $
+.\"
+.Dd March 4, 2000
+.Dt STRING2KEY 8
+.Os HEIMDAL
+.Sh NAME
+.Nm string2key
+.Nd map a password into a key
+.Sh SYNOPSIS
+.Nm
+.Op Fl 5 | Fl -version5
+.Op Fl 4 | Fl -version4
+.Op Fl a | Fl -afs
+.Oo Fl c Ar cell \*(Ba Xo
+.Fl -cell= Ns Ar cell
+.Xc
+.Oc
+.Oo Fl w Ar password \*(Ba Xo
+.Fl -password= Ns Ar password
+.Xc
+.Oc
+.Oo Fl p Ar principal \*(Ba Xo
+.Fl -principal= Ns Ar principal
+.Xc
+.Oc
+.Oo Fl k Ar string \*(Ba Xo
+.Fl -keytype= Ns Ar string
+.Xc
+.Oc
+.Ar password
+.Sh DESCRIPTION
+.Nm
+performs the string-to-key function.
+This is useful when you want to handle the raw key instead of the password.
+Supported options:
+.Bl -tag -width Ds
+.It Xo
+.Fl 5 ,
+.Fl -version5
+.Xc
+Output Kerberos v5 string-to-key
+.It Xo
+.Fl 4 ,
+.Fl -version4
+.Xc
+Output Kerberos v4 string-to-key
+.It Xo
+.Fl a ,
+.Fl -afs
+.Xc
+Output AFS string-to-key
+.It Xo
+.Fl c Ar cell ,
+.Fl -cell= Ns Ar cell
+.Xc
+AFS cell to use
+.It Xo
+.Fl w Ar password ,
+.Fl -password= Ns Ar password
+.Xc
+Password to use
+.It Xo
+.Fl p Ar principal ,
+.Fl -principal= Ns Ar principal
+.Xc
+Kerberos v5 principal to use
+.It Xo
+.Fl k Ar string ,
+.Fl -keytype= Ns Ar string
+.Xc
+Keytype
+.It Xo
+.Fl -version
+.Xc
+print version
+.It Xo
+.Fl -help
+.Xc
+.El
diff --git a/kdc/string2key.c b/kdc/string2key.c
new file mode 100644
index 0000000000000..4211bf7a93f5d
--- /dev/null
+++ b/kdc/string2key.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "headers.h"
+#include <getarg.h>
+
+RCSID("$Id: string2key.c 19213 2006-12-04 23:36:36Z lha $");
+
+int version5;
+int version4;
+int afs;
+char *principal;
+char *cell;
+char *password;
+const char *keytype_str = "des3-cbc-sha1";
+int version;
+int help;
+
+struct getargs args[] = {
+ { "version5", '5', arg_flag, &version5, "Output Kerberos v5 string-to-key" },
+ { "version4", '4', arg_flag, &version4, "Output Kerberos v4 string-to-key" },
+ { "afs", 'a', arg_flag, &afs, "Output AFS string-to-key" },
+ { "cell", 'c', arg_string, &cell, "AFS cell to use", "cell" },
+ { "password", 'w', arg_string, &password, "Password to use", "password" },
+ { "principal",'p', arg_string, &principal, "Kerberos v5 principal to use", "principal" },
+ { "keytype", 'k', arg_string, &keytype_str, "Keytype" },
+ { "version", 0, arg_flag, &version, "print version" },
+ { "help", 0, arg_flag, &help, NULL }
+};
+
+int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int status)
+{
+ arg_printusage (args, num_args, NULL, "password");
+ exit(status);
+}
+
+static void
+tokey(krb5_context context,
+ krb5_enctype enctype,
+ const char *pw,
+ krb5_salt salt,
+ const char *label)
+{
+ krb5_error_code ret;
+ int i;
+ krb5_keyblock key;
+ char *e;
+
+ ret = krb5_string_to_key_salt(context, enctype, pw, salt, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_string_to_key_salt");
+ ret = krb5_enctype_to_string(context, enctype, &e);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_enctype_to_string");
+ printf(label, e);
+ printf(": ");
+ for(i = 0; i < key.keyvalue.length; i++)
+ printf("%02x", ((unsigned char*)key.keyvalue.data)[i]);
+ printf("\n");
+ krb5_free_keyblock_contents(context, &key);
+ free(e);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_principal princ;
+ krb5_salt salt;
+ int optidx;
+ char buf[1024];
+ krb5_enctype etype;
+ krb5_error_code ret;
+
+ optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL);
+
+ if(help)
+ usage(0);
+
+ if(version){
+ print_version (NULL);
+ return 0;
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc > 1)
+ usage(1);
+
+ if(!version5 && !version4 && !afs)
+ version5 = 1;
+
+ ret = krb5_string_to_enctype(context, keytype_str, &etype);
+ if(ret) {
+ krb5_keytype keytype;
+ int *etypes;
+ unsigned num;
+ char *str;
+ ret = krb5_string_to_keytype(context, keytype_str, &keytype);
+ if(ret)
+ krb5_err(context, 1, ret, "%s", keytype_str);
+ ret = krb5_keytype_to_enctypes(context, keytype, &num, &etypes);
+ if(ret)
+ krb5_err(context, 1, ret, "%s", keytype_str);
+ if(num == 0)
+ krb5_errx(context, 1, "there are no encryption types for that keytype");
+ etype = etypes[0];
+ krb5_enctype_to_string(context, etype, &str);
+ keytype_str = str;
+ if(num > 1 && version5)
+ krb5_warnx(context, "ambiguous keytype, using %s", keytype_str);
+ }
+
+ if((etype != ETYPE_DES_CBC_CRC &&
+ etype != ETYPE_DES_CBC_MD4 &&
+ etype != ETYPE_DES_CBC_MD5) &&
+ (afs || version4)) {
+ if(!version5) {
+ etype = ETYPE_DES_CBC_CRC;
+ } else {
+ krb5_errx(context, 1,
+ "DES is the only valid keytype for AFS and Kerberos 4");
+ }
+ }
+
+ if(version5 && principal == NULL){
+ printf("Kerberos v5 principal: ");
+ if(fgets(buf, sizeof(buf), stdin) == NULL)
+ return 1;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ principal = estrdup(buf);
+ }
+ if(afs && cell == NULL){
+ printf("AFS cell: ");
+ if(fgets(buf, sizeof(buf), stdin) == NULL)
+ return 1;
+ buf[strcspn(buf, "\r\n")] = '\0';
+ cell = estrdup(buf);
+ }
+ if(argv[0])
+ password = argv[0];
+ if(password == NULL){
+ if(UI_UTIL_read_pw_string(buf, sizeof(buf), "Password: ", 0))
+ return 1;
+ password = buf;
+ }
+
+ if(version5){
+ krb5_parse_name(context, principal, &princ);
+ krb5_get_pw_salt(context, princ, &salt);
+ tokey(context, etype, password, salt, "Kerberos 5 (%s)");
+ krb5_free_salt(context, salt);
+ }
+ if(version4){
+ salt.salttype = KRB5_PW_SALT;
+ salt.saltvalue.length = 0;
+ salt.saltvalue.data = NULL;
+ tokey(context, ETYPE_DES_CBC_MD5, password, salt, "Kerberos 4");
+ }
+ if(afs){
+ salt.salttype = KRB5_AFS3_SALT;
+ salt.saltvalue.length = strlen(cell);
+ salt.saltvalue.data = cell;
+ tokey(context, ETYPE_DES_CBC_MD5, password, salt, "AFS");
+ }
+ return 0;
+}
diff --git a/kdc/v4_dump.c b/kdc/v4_dump.c
new file mode 100644
index 0000000000000..93c56f87f27fc
--- /dev/null
+++ b/kdc/v4_dump.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2000 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hprop.h"
+
+RCSID("$Id: v4_dump.c 17023 2006-04-09 17:41:47Z lha $");
+
+static time_t
+time_parse(const char *cp)
+{
+ char wbuf[5];
+ struct tm tp;
+ int local;
+
+ memset(&tp, 0, sizeof(tp)); /* clear out the struct */
+
+ /* new format is YYYYMMDDHHMM UTC,
+ old format is YYMMDDHHMM local time */
+ if (strlen(cp) > 10) { /* new format */
+ strlcpy(wbuf, cp, sizeof(wbuf));
+ tp.tm_year = atoi(wbuf) - 1900;
+ cp += 4;
+ local = 0;
+ } else {
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ wbuf[2] = '\0';
+ tp.tm_year = atoi(wbuf);
+ if(tp.tm_year < 38)
+ tp.tm_year += 100;
+ local = 1;
+ }
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ wbuf[2] = 0;
+ tp.tm_mon = atoi(wbuf) - 1;
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ tp.tm_mday = atoi(wbuf);
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ tp.tm_hour = atoi(wbuf);
+
+ wbuf[0] = *cp++;
+ wbuf[1] = *cp++;
+ tp.tm_min = atoi(wbuf);
+
+ return(tm2time(tp, local));
+}
+
+/* convert a version 4 dump file */
+int
+v4_prop_dump(void *arg, const char *file)
+{
+ char buf [1024];
+ FILE *f;
+ int lineno = 0;
+
+ f = fopen(file, "r");
+ if(f == NULL)
+ return errno;
+
+ while(fgets(buf, sizeof(buf), f)) {
+ int ret;
+ unsigned long key[2]; /* yes, long */
+ char exp_date[64], mod_date[64];
+ struct v4_principal pr;
+ int attributes;
+
+ memset(&pr, 0, sizeof(pr));
+ errno = 0;
+ lineno++;
+ ret = sscanf(buf, "%63s %63s %d %d %d %d %lx %lx %63s %63s %63s %63s",
+ pr.name, pr.instance,
+ &pr.max_life, &pr.mkvno, &pr.kvno,
+ &attributes,
+ &key[0], &key[1],
+ exp_date, mod_date,
+ pr.mod_name, pr.mod_instance);
+ if(ret != 12){
+ warnx("Line %d malformed (ignored)", lineno);
+ continue;
+ }
+ if(attributes != 0) {
+ warnx("Line %d (%s.%s) has non-zero attributes - skipping",
+ lineno, pr.name, pr.instance);
+ continue;
+ }
+ pr.key[0] = (key[0] >> 24) & 0xff;
+ pr.key[1] = (key[0] >> 16) & 0xff;
+ pr.key[2] = (key[0] >> 8) & 0xff;
+ pr.key[3] = (key[0] >> 0) & 0xff;
+ pr.key[4] = (key[1] >> 24) & 0xff;
+ pr.key[5] = (key[1] >> 16) & 0xff;
+ pr.key[6] = (key[1] >> 8) & 0xff;
+ pr.key[7] = (key[1] >> 0) & 0xff;
+ pr.exp_date = time_parse(exp_date);
+ pr.mod_date = time_parse(mod_date);
+ if (pr.instance[0] == '*')
+ pr.instance[0] = '\0';
+ if (pr.mod_name[0] == '*')
+ pr.mod_name[0] = '\0';
+ if (pr.mod_instance[0] == '*')
+ pr.mod_instance[0] = '\0';
+ v4_prop(arg, &pr);
+ memset(&pr, 0, sizeof(pr));
+ }
+ fclose(f);
+ return 0;
+}
diff --git a/kdc/version-script.map b/kdc/version-script.map
new file mode 100644
index 0000000000000..2612b8ed261ee
--- /dev/null
+++ b/kdc/version-script.map
@@ -0,0 +1,18 @@
+# $Id: version-script.map 21110 2007-06-18 10:52:20Z lha $
+
+HEIMDAL_KDC_1.0 {
+ global:
+ kdc_log;
+ kdc_log_msg;
+ kdc_log_msg_va;
+ kdc_openlog;
+ krb5_kdc_windc_init;
+ krb5_kdc_get_config;
+ krb5_kdc_set_dbinfo;
+ krb5_kdc_process_krb5_request;
+ krb5_kdc_process_request;
+ krb5_kdc_save_request;
+ krb5_kdc_update_time;
+ local:
+ *;
+};
diff --git a/kdc/windc.c b/kdc/windc.c
new file mode 100644
index 0000000000000..395ab73432846
--- /dev/null
+++ b/kdc/windc.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kdc_locl.h"
+
+RCSID("$Id: windc.c 20559 2007-04-24 16:00:07Z lha $");
+
+static krb5plugin_windc_ftable *windcft;
+static void *windcctx;
+
+/*
+ * Pick the first WINDC module that we find.
+ */
+
+krb5_error_code
+krb5_kdc_windc_init(krb5_context context)
+{
+ struct krb5_plugin *list = NULL, *e;
+ krb5_error_code ret;
+
+ ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "windc", &list);
+ if(ret != 0 || list == NULL)
+ return 0;
+
+ for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
+
+ windcft = _krb5_plugin_get_symbol(e);
+ if (windcft->minor_version < KRB5_WINDC_PLUGING_MINOR)
+ continue;
+
+ (*windcft->init)(context, &windcctx);
+ break;
+ }
+ if (e == NULL) {
+ _krb5_plugin_free(list);
+ krb5_set_error_string(context, "Did not find any WINDC plugin");
+ windcft = NULL;
+ return ENOENT;
+ }
+
+ return 0;
+}
+
+
+krb5_error_code
+_kdc_pac_generate(krb5_context context,
+ hdb_entry_ex *client,
+ krb5_pac *pac)
+{
+ *pac = NULL;
+ if (windcft == NULL)
+ return 0;
+ return (windcft->pac_generate)(windcctx, context, client, pac);
+}
+
+krb5_error_code
+_kdc_pac_verify(krb5_context context,
+ const krb5_principal client_principal,
+ hdb_entry_ex *client,
+ hdb_entry_ex *server,
+ krb5_pac *pac)
+{
+ if (windcft == NULL) {
+ krb5_set_error_string(context, "Can't verify PAC, no function");
+ return EINVAL;
+ }
+ return (windcft->pac_verify)(windcctx, context,
+ client_principal, client, server, pac);
+}
+
+krb5_error_code
+_kdc_windc_client_access(krb5_context context,
+ struct hdb_entry_ex *client,
+ KDC_REQ *req)
+{
+ if (windcft == NULL)
+ return 0;
+ return (windcft->client_access)(windcctx, context, client, req);
+}
diff --git a/kdc/windc_plugin.h b/kdc/windc_plugin.h
new file mode 100644
index 0000000000000..ec480cf950c66
--- /dev/null
+++ b/kdc/windc_plugin.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id: windc_plugin.h 19798 2007-01-10 15:24:51Z lha $ */
+
+#ifndef HEIMDAL_KRB5_PAC_PLUGIN_H
+#define HEIMDAL_KRB5_PAC_PLUGIN_H 1
+
+#include <krb5.h>
+
+/*
+ * The PAC generate function should allocate a krb5_pac using
+ * krb5_pac_init and fill in the PAC structure for the principal using
+ * krb5_pac_add_buffer.
+ *
+ * The PAC verify function should verify all components in the PAC
+ * using krb5_pac_get_types and krb5_pac_get_buffer for all types.
+ *
+ * Check client access function check if the client is authorized.
+ */
+
+struct hdb_entry_ex;
+
+typedef krb5_error_code
+(*krb5plugin_windc_pac_generate)(void *, krb5_context,
+ struct hdb_entry_ex *, krb5_pac *);
+
+typedef krb5_error_code
+(*krb5plugin_windc_pac_verify)(void *, krb5_context,
+ const krb5_principal,
+ struct hdb_entry_ex *,
+ struct hdb_entry_ex *,
+ krb5_pac *);
+
+typedef krb5_error_code
+(*krb5plugin_windc_client_access)(
+ void *, krb5_context, struct hdb_entry_ex *, KDC_REQ *);
+
+
+#define KRB5_WINDC_PLUGING_MINOR 2
+
+typedef struct krb5plugin_windc_ftable {
+ int minor_version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
+ krb5plugin_windc_pac_generate pac_generate;
+ krb5plugin_windc_pac_verify pac_verify;
+ krb5plugin_windc_client_access client_access;
+} krb5plugin_windc_ftable;
+
+#endif /* HEIMDAL_KRB5_PAC_PLUGIN_H */
+