From 51b6601db456e699ea5d4843cbc7239ee92d9c13 Mon Sep 17 00:00:00 2001 From: dfr Date: Wed, 7 May 2008 13:39:42 +0000 Subject: Vendor import of Heimdal 1.1 --- crypto/heimdal/kdc/524.c | 167 ++- crypto/heimdal/kdc/Makefile.am | 93 +- crypto/heimdal/kdc/Makefile.in | 556 +++++--- crypto/heimdal/kdc/config.c | 357 ++--- crypto/heimdal/kdc/connect.c | 422 +++--- crypto/heimdal/kdc/default_config.c | 285 ++++ crypto/heimdal/kdc/digest.c | 1456 ++++++++++++++++++++ crypto/heimdal/kdc/headers.h | 19 +- crypto/heimdal/kdc/hprop.8 | 27 +- crypto/heimdal/kdc/hprop.c | 419 +++--- crypto/heimdal/kdc/hprop.h | 6 +- crypto/heimdal/kdc/hpropd.8 | 6 +- crypto/heimdal/kdc/hpropd.c | 254 +--- crypto/heimdal/kdc/kadb.h | 2 +- crypto/heimdal/kdc/kaserver.c | 496 ++++--- crypto/heimdal/kdc/kdc-private.h | 286 ++++ crypto/heimdal/kdc/kdc-protos.h | 92 ++ crypto/heimdal/kdc/kdc-replay.c | 197 +++ crypto/heimdal/kdc/kdc.8 | 47 +- crypto/heimdal/kdc/kdc.h | 96 ++ crypto/heimdal/kdc/kdc_locl.h | 85 +- crypto/heimdal/kdc/kerberos4.c | 563 +++++--- crypto/heimdal/kdc/kerberos5.c | 2351 ++++++++++++++++----------------- crypto/heimdal/kdc/krb5tgs.c | 1914 +++++++++++++++++++++++++++ crypto/heimdal/kdc/kstash.8 | 58 +- crypto/heimdal/kdc/kstash.c | 55 +- crypto/heimdal/kdc/kx509.c | 460 +++++++ crypto/heimdal/kdc/log.c | 39 +- crypto/heimdal/kdc/main.c | 57 +- crypto/heimdal/kdc/misc.c | 76 +- crypto/heimdal/kdc/mit_dump.c | 77 +- crypto/heimdal/kdc/pkinit.c | 1673 +++++++++++++++++++++++ crypto/heimdal/kdc/process.c | 219 +++ crypto/heimdal/kdc/rx.h | 16 +- crypto/heimdal/kdc/set_dbinfo.c | 100 ++ crypto/heimdal/kdc/string2key.8 | 2 +- crypto/heimdal/kdc/string2key.c | 35 +- crypto/heimdal/kdc/v4_dump.c | 3 +- crypto/heimdal/kdc/version-script.map | 18 + crypto/heimdal/kdc/windc.c | 109 ++ crypto/heimdal/kdc/windc_plugin.h | 82 ++ 41 files changed, 10272 insertions(+), 3003 deletions(-) create mode 100644 crypto/heimdal/kdc/default_config.c create mode 100644 crypto/heimdal/kdc/digest.c create mode 100644 crypto/heimdal/kdc/kdc-private.h create mode 100644 crypto/heimdal/kdc/kdc-protos.h create mode 100644 crypto/heimdal/kdc/kdc-replay.c create mode 100644 crypto/heimdal/kdc/kdc.h create mode 100644 crypto/heimdal/kdc/krb5tgs.c create mode 100644 crypto/heimdal/kdc/kx509.c create mode 100644 crypto/heimdal/kdc/pkinit.c create mode 100644 crypto/heimdal/kdc/process.c create mode 100644 crypto/heimdal/kdc/set_dbinfo.c create mode 100644 crypto/heimdal/kdc/version-script.map create mode 100644 crypto/heimdal/kdc/windc.c create mode 100644 crypto/heimdal/kdc/windc_plugin.h (limited to 'crypto/heimdal/kdc') diff --git a/crypto/heimdal/kdc/524.c b/crypto/heimdal/kdc/524.c index 225594e..3e4ad29 100644 --- a/crypto/heimdal/kdc/524.c +++ b/crypto/heimdal/kdc/524.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,11 +33,9 @@ #include "kdc_locl.h" -RCSID("$Id: 524.c,v 1.29 2003/03/17 05:35:47 assar Exp $"); +RCSID("$Id: 524.c 18270 2006-10-06 17:06:30Z lha $"); -#ifndef KRB4 #include -#endif /* * fetch the server from `t', returning the name in malloced memory in @@ -45,30 +43,35 @@ RCSID("$Id: 524.c,v 1.29 2003/03/17 05:35:47 assar Exp $"); */ static krb5_error_code -fetch_server (const Ticket *t, +fetch_server (krb5_context context, + krb5_kdc_configuration *config, + const Ticket *t, char **spn, - hdb_entry **server, + hdb_entry_ex **server, const char *from) { krb5_error_code ret; krb5_principal sprinc; - ret = principalname2krb5_principal(&sprinc, t->sname, t->realm); + ret = _krb5_principalname2krb5_principal(context, &sprinc, + t->sname, t->realm); if (ret) { - kdc_log(0, "principalname2krb5_principal: %s", + 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(0, "krb5_unparse_name: %s", krb5_get_err_text(context, ret)); + kdc_log(context, config, 0, "krb5_unparse_name: %s", + krb5_get_err_text(context, ret)); return ret; } - ret = db_fetch(sprinc, server); + ret = _kdc_db_fetch(context, config, sprinc, HDB_F_GET_SERVER, + NULL, server); krb5_free_principal(context, sprinc); if (ret) { - kdc_log(0, + 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) @@ -79,7 +82,9 @@ fetch_server (const Ticket *t, } static krb5_error_code -log_524 (const EncTicketPart *et, +log_524 (krb5_context context, + krb5_kdc_configuration *config, + const EncTicketPart *et, const char *from, const char *spn) { @@ -87,35 +92,38 @@ log_524 (const EncTicketPart *et, char *cpn; krb5_error_code ret; - ret = principalname2krb5_principal(&client, et->cname, et->crealm); + ret = _krb5_principalname2krb5_principal(context, &client, + et->cname, et->crealm); if (ret) { - kdc_log(0, "principalname2krb5_principal: %s", + 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(0, "krb5_unparse_name: %s", + kdc_log(context, config, 0, "krb5_unparse_name: %s", krb5_get_err_text (context, ret)); return ret; } - kdc_log(1, "524-REQ %s from %s for %s", cpn, from, spn); + 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 (const EncTicketPart *et, +verify_flags (krb5_context context, + krb5_kdc_configuration *config, + const EncTicketPart *et, const char *spn) { if(et->endtime < kdc_time){ - kdc_log(0, "Ticket expired (%s)", spn); + kdc_log(context, config, 0, "Ticket expired (%s)", spn); return KRB5KRB_AP_ERR_TKT_EXPIRED; } if(et->flags.invalid){ - kdc_log(0, "Ticket not valid (%s)", spn); + kdc_log(context, config, 0, "Ticket not valid (%s)", spn); return KRB5KRB_AP_ERR_TKT_NYV; } return 0; @@ -127,7 +135,9 @@ verify_flags (const EncTicketPart *et, */ static krb5_error_code -set_address (EncTicketPart *et, +set_address (krb5_context context, + krb5_kdc_configuration *config, + EncTicketPart *et, struct sockaddr *addr, const char *from) { @@ -141,12 +151,12 @@ set_address (EncTicketPart *et, ret = krb5_sockaddr2address(context, addr, v4_addr); if(ret) { free (v4_addr); - kdc_log(0, "Failed to convert address (%s)", from); + 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(0, "Incorrect network address (%s)", from); + kdc_log(context, config, 0, "Incorrect network address (%s)", from); krb5_free_address(context, v4_addr); free (v4_addr); return KRB5KRB_AP_ERR_BADADDR; @@ -177,7 +187,9 @@ set_address (EncTicketPart *et, static krb5_error_code -encrypt_v4_ticket(void *buf, +encrypt_v4_ticket(krb5_context context, + krb5_kdc_configuration *config, + void *buf, size_t len, krb5_keyblock *skey, EncryptedData *reply) @@ -187,7 +199,7 @@ encrypt_v4_ticket(void *buf, ret = krb5_crypto_init(context, skey, ETYPE_DES_PCBC_NONE, &crypto); if (ret) { free(buf); - kdc_log(0, "krb5_crypto_init failed: %s", + kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); return ret; } @@ -201,7 +213,7 @@ encrypt_v4_ticket(void *buf, reply); krb5_crypto_destroy(context, crypto); if(ret) { - kdc_log(0, "Failed to encrypt data: %s", + kdc_log(context, config, 0, "Failed to encrypt data: %s", krb5_get_err_text(context, ret)); return ret; } @@ -209,8 +221,11 @@ encrypt_v4_ticket(void *buf, } static krb5_error_code -encode_524_response(const char *spn, const EncTicketPart et, const Ticket *t, - hdb_entry *server, EncryptedData *ticket, int *kvno) +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; @@ -223,7 +238,8 @@ encode_524_response(const char *spn, const EncTicketPart et, const Ticket *t, &t->enc_part, &len, ret); if (ret) { - kdc_log(0, "Failed to encode v4 (2b) ticket (%s)", spn); + kdc_log(context, config, 0, + "Failed to encode v4 (2b) ticket (%s)", spn); return ret; } @@ -234,30 +250,34 @@ encode_524_response(const char *spn, const EncTicketPart et, const Ticket *t, unsigned char buf[MAX_KTXT_LEN + 4 * 4]; Key *skey; - if (!enable_v4_cross_realm && strcmp (et.crealm, t->realm) != 0) { - kdc_log(0, "524 cross-realm %s -> %s disabled", et.crealm, + 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 = encode_v4_ticket(buf + sizeof(buf) - 1, sizeof(buf), - &et, &t->sname, &len); + ret = _kdc_encode_v4_ticket(context, config, + buf + sizeof(buf) - 1, sizeof(buf), + &et, &t->sname, &len); if(ret){ - kdc_log(0, "Failed to encode v4 ticket (%s)", spn); + kdc_log(context, config, 0, + "Failed to encode v4 ticket (%s)", spn); return ret; } - ret = get_des_key(server, TRUE, FALSE, &skey); + ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); if(ret){ - kdc_log(0, "no suitable DES key for server (%s)", spn); + kdc_log(context, config, 0, + "no suitable DES key for server (%s)", spn); return ret; } - ret = encrypt_v4_ticket(buf + sizeof(buf) - len, len, + ret = encrypt_v4_ticket(context, config, buf + sizeof(buf) - len, len, &skey->key, ticket); if(ret){ - kdc_log(0, "Failed to encrypt v4 ticket (%s)", spn); + kdc_log(context, config, 0, + "Failed to encrypt v4 ticket (%s)", spn); return ret; } - *kvno = server->kvno; + *kvno = server->entry.kvno; } return 0; @@ -269,12 +289,14 @@ encode_524_response(const char *spn, const EncTicketPart et, const Ticket *t, */ krb5_error_code -do_524(const Ticket *t, krb5_data *reply, - const char *from, struct sockaddr *addr) +_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 *server = NULL; + hdb_entry_ex *server = NULL; Key *skey; krb5_data et_data; EncTicketPart et; @@ -283,27 +305,29 @@ do_524(const Ticket *t, krb5_data *reply, char *spn = NULL; unsigned char buf[MAX_KTXT_LEN + 4 * 4]; size_t len; - int kvno; + int kvno = 0; - if(!enable_524) { + if(!config->enable_524) { ret = KRB5KDC_ERR_POLICY; - kdc_log(0, "Rejected ticket conversion request from %s", from); + kdc_log(context, config, 0, + "Rejected ticket conversion request from %s", from); goto out; } - ret = fetch_server (t, &spn, &server, from); + ret = fetch_server (context, config, t, &spn, &server, from); if (ret) { goto out; } - ret = hdb_enctype2key(context, server, t->enc_part.etype, &skey); + ret = hdb_enctype2key(context, &server->entry, t->enc_part.etype, &skey); if(ret){ - kdc_log(0, "No suitable key found for server (%s) from %s", spn, from); + 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(0, "krb5_crypto_init failed: %s", + kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); goto out; } @@ -314,58 +338,63 @@ do_524(const Ticket *t, krb5_data *reply, &et_data); krb5_crypto_destroy(context, crypto); if(ret){ - kdc_log(0, "Failed to decrypt ticket from %s for %s", from, spn); + 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(0, "Failed to decode ticket from %s for %s", from, spn); + kdc_log(context, config, 0, + "Failed to decode ticket from %s for %s", from, spn); goto out; } - ret = log_524 (&et, from, spn); + ret = log_524 (context, config, &et, from, spn); if (ret) { free_EncTicketPart(&et); goto out; } - ret = verify_flags (&et, spn); + ret = verify_flags (context, config, &et, spn); if (ret) { free_EncTicketPart(&et); goto out; } - ret = set_address (&et, addr, from); + ret = set_address (context, config, &et, addr, from); if (ret) { free_EncTicketPart(&et); goto out; } - ret = encode_524_response(spn, et, t, server, &ticket, &kvno); + ret = encode_524_response(context, config, spn, et, t, + server, &ticket, &kvno); free_EncTicketPart(&et); -out: + out: /* make reply */ memset(buf, 0, sizeof(buf)); sp = krb5_storage_from_mem(buf, sizeof(buf)); - 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); - + 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) - free_ent (server); + _kdc_free_ent (context, server); return ret; } diff --git a/crypto/heimdal/kdc/Makefile.am b/crypto/heimdal/kdc/Makefile.am index f41f46e..ff20bde 100644 --- a/crypto/heimdal/kdc/Makefile.am +++ b/crypto/heimdal/kdc/Makefile.am @@ -1,8 +1,10 @@ -# $Id: Makefile.am,v 1.44 2003/01/14 05:47:06 lha Exp $ +# $Id: Makefile.am 22489 2008-01-21 11:49:06Z lha $ include $(top_srcdir)/Makefile.am.common -INCLUDES += $(INCLUDE_krb4) $(INCLUDE_des) -I$(srcdir)/../lib/krb5 +AM_CPPFLAGS += $(INCLUDE_krb4) $(INCLUDE_hcrypto) -I$(srcdir)/../lib/krb5 + +lib_LTLIBRARIES = libkdc.la bin_PROGRAMS = string2key @@ -10,6 +12,8 @@ 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 @@ -19,23 +23,45 @@ kstash_SOURCES = kstash.c headers.h string2key_SOURCES = string2key.c headers.h -if KRB4 -krb4_sources = kaserver.c rx.h -else -krb4_sources = +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 -kdc_SOURCES = \ - config.c \ - connect.c \ - kdc_locl.h \ - kerberos5.c \ - log.c \ - main.c \ - misc.c \ - 524.c \ - kerberos4.c \ - $(krb4_sources) +$(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 = \ @@ -43,7 +69,7 @@ hprop_LDADD = \ $(LIB_openldap) \ $(top_builddir)/lib/krb5/libkrb5.la \ $(LIB_kdb) $(LIB_krb4) \ - $(LIB_des) \ + $(LIB_hcrypto) \ $(top_builddir)/lib/asn1/libasn1.la \ $(LIB_roken) \ $(DBLIB) @@ -53,7 +79,23 @@ hpropd_LDADD = \ $(LIB_openldap) \ $(top_builddir)/lib/krb5/libkrb5.la \ $(LIB_kdb) $(LIB_krb4) \ - $(LIB_des) \ + $(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) @@ -62,10 +104,19 @@ LDADD = $(top_builddir)/lib/hdb/libhdb.la \ $(LIB_openldap) \ $(top_builddir)/lib/krb5/libkrb5.la \ $(LIB_krb4) \ - $(LIB_des) \ + $(LIB_hcrypto) \ $(top_builddir)/lib/asn1/libasn1.la \ $(LIB_roken) \ $(DBLIB) -kdc_LDADD = $(LDADD) $(LIB_pidfile) +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/crypto/heimdal/kdc/Makefile.in b/crypto/heimdal/kdc/Makefile.in index 6e5f5ca..d7e623a 100644 --- a/crypto/heimdal/kdc/Makefile.in +++ b/crypto/heimdal/kdc/Makefile.in @@ -1,8 +1,8 @@ -# Makefile.in generated by automake 1.8.3 from Makefile.am. +# 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 Free Software Foundation, Inc. +# 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. @@ -14,23 +14,19 @@ @SET_MAKE@ -# $Id: Makefile.am,v 1.44 2003/01/14 05:47:06 lha Exp $ +# $Id: Makefile.am 22489 2008-01-21 11:49:06Z lha $ -# $Id: Makefile.am.common,v 1.5 2002/05/19 18:35:37 joda Exp $ +# $Id: Makefile.am.common 10998 2002-05-19 18:35:37Z joda $ + +# $Id: Makefile.am.common 22488 2008-01-21 11:47:22Z lha $ -# $Id: Makefile.am.common,v 1.37.2.2 2003/10/13 13:15:39 joda Exp $ -SOURCES = $(hprop_SOURCES) $(hpropd_SOURCES) $(kdc_SOURCES) $(kstash_SOURCES) $(string2key_SOURCES) -srcdir = @srcdir@ -top_srcdir = @top_srcdir@ VPATH = @srcdir@ pkgdatadir = $(datadir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ -top_builddir = .. am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd -INSTALL = @INSTALL@ install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c @@ -42,26 +38,27 @@ POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : +build_triplet = @build@ host_triplet = @host@ -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(top_srcdir)/Makefile.am.common \ +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/auth-modules.m4 $(top_srcdir)/cf/autobuild.m4 \ $(top_srcdir)/cf/broken-getaddrinfo.m4 \ - $(top_srcdir)/cf/broken-getnameinfo.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-declaration.m4 \ $(top_srcdir)/cf/check-getpwnam_r-posix.m4 \ $(top_srcdir)/cf/check-man.m4 \ $(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \ @@ -74,6 +71,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.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 \ @@ -82,27 +80,56 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \ $(top_srcdir)/cf/krb-readline.m4 \ $(top_srcdir)/cf/krb-struct-spwd.m4 \ $(top_srcdir)/cf/krb-struct-winsize.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/retsigtype.m4 $(top_srcdir)/cf/roken-frag.m4 \ - $(top_srcdir)/cf/sunos.m4 $(top_srcdir)/cf/telnet.m4 \ - $(top_srcdir)/cf/test-package.m4 $(top_srcdir)/cf/wflags.m4 \ - $(top_srcdir)/cf/with-all.m4 $(top_srcdir)/configure.in + $(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 = $(SHELL) $(top_srcdir)/mkinstalldirs +mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = -am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)" +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) $(sbin_PROGRAMS) +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) -am__DEPENDENCIES_1 = hprop_DEPENDENCIES = $(top_builddir)/lib/hdb/libhdb.la \ $(am__DEPENDENCIES_1) $(top_builddir)/lib/krb5/libkrb5.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -115,20 +142,20 @@ hpropd_DEPENDENCIES = $(top_builddir)/lib/hdb/libhdb.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(top_builddir)/lib/asn1/libasn1.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -am__kdc_SOURCES_DIST = config.c connect.c kdc_locl.h kerberos5.c log.c \ - main.c misc.c 524.c kerberos4.c kaserver.c rx.h -@KRB4_TRUE@am__objects_1 = kaserver.$(OBJEXT) -am_kdc_OBJECTS = config.$(OBJEXT) connect.$(OBJEXT) \ - kerberos5.$(OBJEXT) log.$(OBJEXT) main.$(OBJEXT) \ - misc.$(OBJEXT) 524.$(OBJEXT) kerberos4.$(OBJEXT) \ - $(am__objects_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 = $(am__DEPENDENCIES_2) $(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) @@ -145,35 +172,34 @@ string2key_DEPENDENCIES = $(top_builddir)/lib/hdb/libhdb.la \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) -DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include +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) --mode=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) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ - $(AM_LDFLAGS) $(LDFLAGS) -o $@ -SOURCES = $(hprop_SOURCES) $(hpropd_SOURCES) $(kdc_SOURCES) \ - $(kstash_SOURCES) $(string2key_SOURCES) -DIST_SOURCES = $(hprop_SOURCES) $(hpropd_SOURCES) \ - $(am__kdc_SOURCES_DIST) $(kstash_SOURCES) \ +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@ -AIX4_FALSE = @AIX4_FALSE@ -AIX4_TRUE = @AIX4_TRUE@ -AIX_DYNAMIC_AFS_FALSE = @AIX_DYNAMIC_AFS_FALSE@ -AIX_DYNAMIC_AFS_TRUE = @AIX_DYNAMIC_AFS_TRUE@ AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@ -AIX_FALSE = @AIX_FALSE@ -AIX_TRUE = @AIX_TRUE@ AMTAR = @AMTAR@ AR = @AR@ AUTOCONF = @AUTOCONF@ @@ -183,8 +209,6 @@ AWK = @AWK@ CANONICAL_HOST = @CANONICAL_HOST@ CATMAN = @CATMAN@ CATMANEXT = @CATMANEXT@ -CATMAN_FALSE = @CATMAN_FALSE@ -CATMAN_TRUE = @CATMAN_TRUE@ CC = @CC@ CFLAGS = @CFLAGS@ COMPILE_ET = @COMPILE_ET@ @@ -195,11 +219,10 @@ CXXCPP = @CXXCPP@ CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLIB = @DBLIB@ -DCE_FALSE = @DCE_FALSE@ -DCE_TRUE = @DCE_TRUE@ DEFS = @DEFS@ DIR_com_err = @DIR_com_err@ -DIR_des = @DIR_des@ +DIR_hcrypto = @DIR_hcrypto@ +DIR_hdbdir = @DIR_hdbdir@ DIR_roken = @DIR_roken@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ @@ -207,42 +230,27 @@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ -EXTRA_LIB45 = @EXTRA_LIB45@ F77 = @F77@ FFLAGS = @FFLAGS@ +GREP = @GREP@ GROFF = @GROFF@ -HAVE_DB1_FALSE = @HAVE_DB1_FALSE@ -HAVE_DB1_TRUE = @HAVE_DB1_TRUE@ -HAVE_DB3_FALSE = @HAVE_DB3_FALSE@ -HAVE_DB3_TRUE = @HAVE_DB3_TRUE@ -HAVE_DLOPEN_FALSE = @HAVE_DLOPEN_FALSE@ -HAVE_DLOPEN_TRUE = @HAVE_DLOPEN_TRUE@ -HAVE_NDBM_FALSE = @HAVE_NDBM_FALSE@ -HAVE_NDBM_TRUE = @HAVE_NDBM_TRUE@ -HAVE_OPENSSL_FALSE = @HAVE_OPENSSL_FALSE@ -HAVE_OPENSSL_TRUE = @HAVE_OPENSSL_TRUE@ -HAVE_X_FALSE = @HAVE_X_FALSE@ -HAVE_X_TRUE = @HAVE_X_TRUE@ INCLUDES_roken = @INCLUDES_roken@ -INCLUDE_des = @INCLUDE_des@ +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@ -IRIX_FALSE = @IRIX_FALSE@ -IRIX_TRUE = @IRIX_TRUE@ -KRB4_FALSE = @KRB4_FALSE@ -KRB4_TRUE = @KRB4_TRUE@ -KRB5_FALSE = @KRB5_FALSE@ -KRB5_TRUE = @KRB5_TRUE@ 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@ @@ -260,12 +268,9 @@ LIB_crypt = @LIB_crypt@ LIB_db_create = @LIB_db_create@ LIB_dbm_firstkey = @LIB_dbm_firstkey@ LIB_dbopen = @LIB_dbopen@ -LIB_des = @LIB_des@ -LIB_des_a = @LIB_des_a@ -LIB_des_appl = @LIB_des_appl@ -LIB_des_so = @LIB_des_so@ 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@ @@ -275,15 +280,14 @@ 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_krb_disable_debug = @LIB_krb_disable_debug@ -LIB_krb_enable_debug = @LIB_krb_enable_debug@ -LIB_krb_get_kdc_time_diff = @LIB_krb_get_kdc_time_diff@ -LIB_krb_get_our_ip_for_realm = @LIB_krb_get_our_ip_for_realm@ -LIB_krb_kdctimeofday = @LIB_krb_kdctimeofday@ LIB_loadquery = @LIB_loadquery@ LIB_logout = @LIB_logout@ LIB_logwtmp = @LIB_logwtmp@ @@ -292,6 +296,7 @@ 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@ @@ -303,15 +308,10 @@ LIB_tgetent = @LIB_tgetent@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ MAINT = @MAINT@ -MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@ -MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@ MAKEINFO = @MAKEINFO@ -NEED_WRITEAUTH_FALSE = @NEED_WRITEAUTH_FALSE@ -NEED_WRITEAUTH_TRUE = @NEED_WRITEAUTH_TRUE@ +MKDIR_P = @MKDIR_P@ NROFF = @NROFF@ OBJEXT = @OBJEXT@ -OTP_FALSE = @OTP_FALSE@ -OTP_TRUE = @OTP_TRUE@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ @@ -319,74 +319,80 @@ 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@ -ac_ct_AR = @ac_ct_AR@ +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@ -ac_ct_RANLIB = @ac_ct_RANLIB@ -ac_ct_STRIP = @ac_ct_STRIP@ 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@ -do_roken_rename_FALSE = @do_roken_rename_FALSE@ -do_roken_rename_TRUE = @do_roken_rename_TRUE@ +datarootdir = @datarootdir@ +docdir = @docdir@ dpagaix_cflags = @dpagaix_cflags@ dpagaix_ldadd = @dpagaix_ldadd@ dpagaix_ldflags = @dpagaix_ldflags@ -el_compat_FALSE = @el_compat_FALSE@ -el_compat_TRUE = @el_compat_TRUE@ +dvidir = @dvidir@ exec_prefix = @exec_prefix@ -have_err_h_FALSE = @have_err_h_FALSE@ -have_err_h_TRUE = @have_err_h_TRUE@ -have_fnmatch_h_FALSE = @have_fnmatch_h_FALSE@ -have_fnmatch_h_TRUE = @have_fnmatch_h_TRUE@ -have_glob_h_FALSE = @have_glob_h_FALSE@ -have_glob_h_TRUE = @have_glob_h_TRUE@ -have_ifaddrs_h_FALSE = @have_ifaddrs_h_FALSE@ -have_ifaddrs_h_TRUE = @have_ifaddrs_h_TRUE@ -have_vis_h_FALSE = @have_vis_h_FALSE@ -have_vis_h_TRUE = @have_vis_h_TRUE@ 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@ -SUFFIXES = .et .h .x .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 -INCLUDES = -I$(top_builddir)/include $(INCLUDES_roken) $(INCLUDE_krb4) $(INCLUDE_des) -I$(srcdir)/../lib/krb5 +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 @@ -403,32 +409,45 @@ LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS) @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 -@KRB4_FALSE@krb4_sources = -@KRB4_TRUE@krb4_sources = kaserver.c rx.h -kdc_SOURCES = \ +kdc_SOURCES = connect.c \ config.c \ - connect.c \ - kdc_locl.h \ - kerberos5.c \ - log.c \ - main.c \ - misc.c \ - 524.c \ - kerberos4.c \ - $(krb4_sources) - + 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_des) \ + $(LIB_hcrypto) \ $(top_builddir)/lib/asn1/libasn1.la \ $(LIB_roken) \ $(DBLIB) @@ -438,7 +457,20 @@ hpropd_LDADD = \ $(LIB_openldap) \ $(top_builddir)/lib/krb5/libkrb5.la \ $(LIB_kdb) $(LIB_krb4) \ - $(LIB_des) \ + $(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) @@ -447,16 +479,22 @@ LDADD = $(top_builddir)/lib/hdb/libhdb.la \ $(LIB_openldap) \ $(top_builddir)/lib/krb5/libkrb5.la \ $(LIB_krb4) \ - $(LIB_des) \ + $(LIB_hcrypto) \ $(top_builddir)/lib/asn1/libasn1.la \ $(LIB_roken) \ $(DBLIB) -kdc_LDADD = $(LDADD) $(LIB_pidfile) +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 .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj +.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 \ @@ -486,9 +524,38 @@ $(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)" + 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 \ @@ -516,7 +583,7 @@ clean-binPROGRAMS: done install-libexecPROGRAMS: $(libexec_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(libexecdir)" || $(mkdir_p) "$(DESTDIR)$(libexecdir)" + 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 \ @@ -542,9 +609,16 @@ clean-libexecPROGRAMS: 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)" + 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 \ @@ -572,19 +646,22 @@ clean-sbinPROGRAMS: done hprop$(EXEEXT): $(hprop_OBJECTS) $(hprop_DEPENDENCIES) @rm -f hprop$(EXEEXT) - $(LINK) $(hprop_LDFLAGS) $(hprop_OBJECTS) $(hprop_LDADD) $(LIBS) + $(LINK) $(hprop_OBJECTS) $(hprop_LDADD) $(LIBS) hpropd$(EXEEXT): $(hpropd_OBJECTS) $(hpropd_DEPENDENCIES) @rm -f hpropd$(EXEEXT) - $(LINK) $(hpropd_LDFLAGS) $(hpropd_OBJECTS) $(hpropd_LDADD) $(LIBS) + $(LINK) $(hpropd_OBJECTS) $(hpropd_LDADD) $(LIBS) kdc$(EXEEXT): $(kdc_OBJECTS) $(kdc_DEPENDENCIES) @rm -f kdc$(EXEEXT) - $(LINK) $(kdc_LDFLAGS) $(kdc_OBJECTS) $(kdc_LDADD) $(LIBS) + $(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_LDFLAGS) $(kstash_OBJECTS) $(kstash_LDADD) $(LIBS) + $(LINK) $(kstash_OBJECTS) $(kstash_LDADD) $(LIBS) string2key$(EXEEXT): $(string2key_OBJECTS) $(string2key_DEPENDENCIES) @rm -f string2key$(EXEEXT) - $(LINK) $(string2key_LDFLAGS) $(string2key_OBJECTS) $(string2key_LDADD) $(LIBS) + $(LINK) $(string2key_OBJECTS) $(string2key_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -606,13 +683,9 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs - -distclean-libtool: - -rm -f libtool -uninstall-info-am: install-man8: $(man8_MANS) $(man_MANS) @$(NORMAL_INSTALL) - test -z "$(man8dir)" || $(mkdir_p) "$(DESTDIR)$(man8dir)" + 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 \ @@ -655,6 +728,40 @@ uninstall-man8: 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)'; \ @@ -676,9 +783,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ done | \ $(AWK) ' { files[$$0] = 1; } \ END { for (i in files) print i; }'`; \ - test -z "$(ETAGS_ARGS)$$tags$$unique" \ - || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ - $$tags $$unique + 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) @@ -703,23 +812,21 @@ distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(DISTFILES) - $(mkdir_p) $(distdir)/.. $(distdir)/../cf - @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ - topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ - list='$(DISTFILES)'; for file in $$list; do \ - case $$file in \ - $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ - $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ - esac; \ + @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; \ - dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ - if test "$$dir" != "$$file" && test "$$dir" != "."; then \ - dir="/$$dir"; \ - $(mkdir_p) "$(distdir)$$dir"; \ - else \ - dir=''; \ - 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; \ @@ -736,10 +843,13 @@ distdir: $(DISTFILES) check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: check-am -all-am: Makefile $(PROGRAMS) $(MANS) all-local +all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(MANS) $(HEADERS) \ + all-local +install-binPROGRAMS: install-libLTLIBRARIES + installdirs: - for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man8dir)"; do \ - test -z "$$dir" || $(mkdir_p) "$$dir"; \ + 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 @@ -760,20 +870,21 @@ mostlyclean-generic: clean-generic: distclean-generic: - -rm -f $(CONFIG_CLEAN_FILES) + -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-libexecPROGRAMS \ - clean-libtool clean-sbinPROGRAMS mostlyclean-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-libtool distclean-tags + distclean-tags dvi: dvi-am @@ -785,19 +896,28 @@ info: info-am info-am: -install-data-am: install-man +install-data-am: install-includeHEADERS install-krb5HEADERS \ + install-man @$(NORMAL_INSTALL) $(MAKE) $(AM_MAKEFLAGS) install-data-hook -install-exec-am: install-binPROGRAMS install-libexecPROGRAMS \ - install-sbinPROGRAMS +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 @@ -817,26 +937,38 @@ ps: ps-am ps-am: -uninstall-am: uninstall-binPROGRAMS uninstall-info-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-libexecPROGRAMS \ - clean-libtool clean-sbinPROGRAMS ctags 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-exec install-exec-am install-info \ - install-info-am install-libexecPROGRAMS install-man \ - install-man8 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-info-am uninstall-libexecPROGRAMS uninstall-man \ - uninstall-man8 uninstall-sbinPROGRAMS + 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: @@ -851,8 +983,8 @@ install-suid-programs: install-exec-hook: install-suid-programs -install-build-headers:: $(include_HEADERS) $(build_HEADERZ) - @foo='$(include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \ +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"; \ @@ -862,19 +994,31 @@ install-build-headers:: $(include_HEADERS) $(build_HEADERZ) 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)'; then \ + @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 > /dev/null 2>&1; then \ + if (./$$i --version && ./$$i --help) > /dev/null 2>&1; then \ echo "PASS: $$i"; \ else \ echo "FAIL: $$i"; \ @@ -890,7 +1034,7 @@ check-local:: echo "$$dashes"; \ echo "$$banner"; \ echo "$$dashes"; \ - test "$$failed" -eq 0; \ + test "$$failed" -eq 0 || exit 1; \ fi .x.c: @@ -960,14 +1104,48 @@ dist-cat8-mans: 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_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_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/crypto/heimdal/kdc/config.c b/crypto/heimdal/kdc/config.c index 8ab826a..a4d40fc 100644 --- a/crypto/heimdal/kdc/config.c +++ b/crypto/heimdal/kdc/config.c @@ -1,6 +1,7 @@ /* - * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan + * 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 @@ -35,52 +36,33 @@ #include #include -RCSID("$Id: config.c,v 1.46.2.2 2003/10/27 11:06:52 joda Exp $"); - -static const char *config_file; /* location of kdc config file */ +RCSID("$Id: config.c 22248 2007-12-08 23:52:12Z lha $"); -int require_preauth = -1; /* 1 == require preauth for all principals */ +struct dbinfo { + char *realm; + char *dbname; + char *mkey_file; + struct dbinfo *next; +}; -size_t max_request; /* maximal size of a request */ +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 */ -time_t kdc_warn_pwexpire; /* time before expiration to print a warning */ - -struct dbinfo *databases; -HDB **db; -int num_db; - -const char *port_str; +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; -#ifdef HAVE_DAEMON -int detach_from_console = -1; -#define DETACH_IS_DEFAULT FALSE -#endif - -int enable_http = -1; -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; -int trpolicy; -static const char *trpolicy_str; +static int builtin_hdb_flag; +static int help_flag; +static int version_flag; static struct getarg_strings addresses_str; /* addresses to listen on */ -krb5_addresses explicit_addresses; - -#ifdef KRB4 -char *v4_realm; -int enable_v4 = -1; -int enable_kaserver = -1; -#endif -int enable_524 = -1; -int enable_v4_cross_realm = -1; - -static int help_flag; -static int version_flag; +static char *v4_realm; static struct getargs args[] = { { @@ -95,17 +77,10 @@ static struct getargs args[] = { "max-request", 0, arg_string, &max_request, "max size for a kdc-request", "size" }, -#if 0 - { - "database", 'd', arg_string, &databases, - "location of database", "database" - }, -#endif { "enable-http", 'H', arg_flag, &enable_http, "turn on HTTP support" }, { "524", 0, arg_negative_flag, &enable_524, "don't respond to 524 requests" }, -#ifdef KRB4 { "kaserver", 'K', arg_flag, &enable_kaserver, "enable kaserver support" @@ -117,7 +92,6 @@ static struct getargs args[] = { "v4-realm", 'r', arg_string, &v4_realm, "realm to serve v4-requests for" }, -#endif { "kerberos4-cross-realm", 0, arg_flag, &enable_v4_cross_realm, "respond to kerberos 4 requests from foreign realms" @@ -125,7 +99,6 @@ static struct getargs args[] = { { "ports", 'P', arg_string, &port_str, "ports to listen to", "portspec" }, -#ifdef HAVE_DAEMON #if DETACH_IS_DEFAULT { "detach", 'D', arg_negative_flag, &detach_from_console, @@ -137,9 +110,12 @@ static struct getargs args[] = { "detach from console" }, #endif -#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 } }; @@ -154,86 +130,7 @@ usage(int ret) } static void -get_dbinfo(void) -{ - const krb5_config_binding *top_binding = NULL; - const krb5_config_binding *db_binding; - const krb5_config_binding *default_binding = NULL; - struct dbinfo *di, **dt; - const char *default_dbname = HDB_DEFAULT_DB; - const char *default_mkey = HDB_DB_DIR "/m-key"; - const char *p; - - databases = NULL; - dt = &databases; - while((db_binding = (const krb5_config_binding *) - krb5_config_get_next(context, NULL, &top_binding, - krb5_config_list, - "kdc", - "database", - NULL))) { - p = krb5_config_get_string(context, db_binding, "realm", NULL); - if(p == NULL) { - if(default_binding) { - krb5_warnx(context, "WARNING: more than one realm-less " - "database specification"); - krb5_warnx(context, "WARNING: using the first encountered"); - } else - default_binding = db_binding; - continue; - } - di = calloc(1, sizeof(*di)); - di->realm = strdup(p); - p = krb5_config_get_string(context, db_binding, "dbname", NULL); - if(p) - di->dbname = strdup(p); - p = krb5_config_get_string(context, db_binding, "mkey_file", NULL); - if(p) - di->mkey_file = strdup(p); - *dt = di; - dt = &di->next; - } - if(default_binding) { - di = calloc(1, sizeof(*di)); - p = krb5_config_get_string(context, default_binding, "dbname", NULL); - if(p) { - di->dbname = strdup(p); - default_dbname = p; - } - p = krb5_config_get_string(context, default_binding, "mkey_file", NULL); - if(p) { - di->mkey_file = strdup(p); - default_mkey = p; - } - *dt = di; - dt = &di->next; - } else if(databases == NULL) { - /* if there are none specified, use some default */ - di = calloc(1, sizeof(*di)); - di->dbname = strdup(default_dbname); - di->mkey_file = strdup(default_mkey); - *dt = di; - dt = &di->next; - } - for(di = databases; di; di = di->next) { - if(di->dbname == NULL) - di->dbname = strdup(default_dbname); - if(di->mkey_file == NULL) { - p = strrchr(di->dbname, '.'); - if(p == NULL || strchr(p, '/') != NULL) - /* final pathname component does not contain a . */ - asprintf(&di->mkey_file, "%s.mkey", di->dbname); - else - /* the filename is something.else, replace .else with - .mkey */ - asprintf(&di->mkey_file, "%.*s.mkey", - (int)(p - di->dbname), di->dbname); - } - } -} - -static void -add_one_address (const char *str, int first) +add_one_address (krb5_context context, const char *str, int first) { krb5_error_code ret; krb5_addresses tmp; @@ -248,15 +145,16 @@ add_one_address (const char *str, int first) krb5_free_addresses (context, &tmp); } -void -configure(int argc, char **argv) +krb5_kdc_configuration * +configure(krb5_context context, int argc, char **argv) { - int optind = 0; - int e; + krb5_kdc_configuration *config; + krb5_error_code ret; + int optidx = 0; const char *p; - while((e = getarg(args, num_args, argc, argv, &optind))) - warnx("error at argument `%s'", argv[optind]); + while(getarg(args, num_args, argc, argv, &optidx)) + warnx("error at argument `%s'", argv[optidx]); if(help_flag) usage (0); @@ -266,35 +164,51 @@ configure(int argc, char **argv) exit(0); } - argc -= optind; - argv += optind; + 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); { - krb5_error_code ret; char **files; - char *tmp; - if(config_file == NULL) - config_file = _PATH_KDC_CONF; - asprintf(&tmp, "%s:%s", config_file, krb5_config_file); - if(tmp == NULL) - krb5_errx(context, 1, "out of memory"); - - krb5_config_file = tmp; - ret = krb5_get_default_config_files(&files); - if(ret) - krb5_err(context, 1, ret, "reading configuration 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"); } - get_dbinfo(); - + 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); @@ -308,9 +222,8 @@ configure(int argc, char **argv) max_request = parse_bytes(p, NULL); } - if(require_preauth == -1) - require_preauth = krb5_config_get_bool(context, NULL, "kdc", - "require-preauth", NULL); + if(require_preauth != -1) + config->require_preauth = require_preauth; if(port_str == NULL){ p = krb5_config_get_string(context, NULL, "kdc", "ports", NULL); @@ -324,114 +237,86 @@ configure(int argc, char **argv) int i; for (i = 0; i < addresses_str.num_strings; ++i) - add_one_address (addresses_str.strings[i], i == 0); + 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 (*foo++, TRUE); + add_one_address (context, *foo++, TRUE); while (*foo) - add_one_address (*foo++, FALSE); + add_one_address (context, *foo++, FALSE); } } -#ifdef KRB4 - if(enable_v4 == -1) - enable_v4 = krb5_config_get_bool_default(context, NULL, FALSE, "kdc", - "enable-kerberos4", NULL); -#else -#define enable_v4 0 -#endif - if(enable_v4_cross_realm == -1) - enable_v4_cross_realm = - krb5_config_get_bool_default(context, NULL, - FALSE, "kdc", - "enable-kerberos4-cross-realm", - NULL); - if(enable_524 == -1) - enable_524 = krb5_config_get_bool_default(context, NULL, enable_v4, - "kdc", "enable-524", NULL); + 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); - check_ticket_addresses = - krb5_config_get_bool_default(context, NULL, TRUE, "kdc", - "check-ticket-addresses", NULL); - allow_null_ticket_addresses = - krb5_config_get_bool_default(context, NULL, TRUE, "kdc", - "allow-null-ticket-addresses", NULL); - - allow_anonymous = - krb5_config_get_bool(context, NULL, "kdc", - "allow-anonymous", NULL); - trpolicy_str = - krb5_config_get_string_default(context, NULL, "always-check", "kdc", - "transited-policy", NULL); - if(strcasecmp(trpolicy_str, "always-check") == 0) - trpolicy = TRPOLICY_ALWAYS_CHECK; - else if(strcasecmp(trpolicy_str, "allow-per-principal") == 0) - trpolicy = TRPOLICY_ALLOW_PER_PRINCIPAL; - else if(strcasecmp(trpolicy_str, "always-honour-request") == 0) - trpolicy = TRPOLICY_ALWAYS_HONOUR_REQUEST; - else { - kdc_log(0, "unknown transited-policy: %s, reverting to always-check", - trpolicy_str); - trpolicy = TRPOLICY_ALWAYS_CHECK; - } - - krb5_config_get_bool_default(context, NULL, TRUE, "kdc", - "enforce-transited-policy", NULL); -#ifdef KRB4 - if(v4_realm == NULL){ - p = krb5_config_get_string (context, NULL, - "kdc", - "v4-realm", - NULL); - if(p != NULL) { - v4_realm = strdup(p); - if (v4_realm == NULL) - krb5_errx(context, 1, "out of memory"); - } - } - if (enable_kaserver == -1) - enable_kaserver = krb5_config_get_bool_default(context, NULL, FALSE, - "kdc", - "enable-kaserver", - NULL); -#endif - encode_as_rep_as_tgs_rep = krb5_config_get_bool(context, NULL, "kdc", - "encode_as_rep_as_tgs_rep", - NULL); + if(request_log == NULL) + request_log = krb5_config_get_string(context, NULL, + "kdc", + "kdc-request-log", + NULL); - kdc_warn_pwexpire = krb5_config_get_time (context, NULL, - "kdc", - "kdc_warn_pwexpire", - 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; -#ifdef HAVE_DAEMON if(detach_from_console == -1) detach_from_console = krb5_config_get_bool_default(context, NULL, DETACH_IS_DEFAULT, "kdc", "detach", NULL); -#endif - kdc_openlog(); + if(max_request == 0) max_request = 64 * 1024; - if(require_preauth == -1) - require_preauth = 1; + if (port_str == NULL) port_str = "+"; -#ifdef KRB4 - if(v4_realm == NULL){ - v4_realm = malloc(40); /* REALM_SZ */ - if (v4_realm == NULL) - krb5_errx(context, 1, "out of memory"); - krb_get_lrealm(v4_realm, 1); + + 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; } -#endif + + krb5_kdc_windc_init(context); + + return config; } diff --git a/crypto/heimdal/kdc/connect.c b/crypto/heimdal/kdc/connect.c index 9e9e481..c2df088 100644 --- a/crypto/heimdal/kdc/connect.c +++ b/crypto/heimdal/kdc/connect.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,7 +33,20 @@ #include "kdc_locl.h" -RCSID("$Id: connect.c,v 1.90.2.2 2004/04/02 20:50:53 lha Exp $"); +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 @@ -55,7 +68,8 @@ static int num_ports; */ static void -add_port(int family, int port, const char *protocol) +add_port(krb5_context context, + int family, int port, const char *protocol) { int type; int i; @@ -87,11 +101,12 @@ add_port(int family, int port, const char *protocol) */ static void -add_port_service(int family, const char *service, int port, +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 (family, port, protocol); + add_port (context, family, port, protocol); } /* @@ -100,22 +115,23 @@ add_port_service(int family, const char *service, int port, */ static void -add_port_string (int family, const char *port_str, const char *protocol) +add_port_string (krb5_context context, + int family, const char *str, const char *protocol) { struct servent *sp; int port; - sp = roken_getservbyname (port_str, protocol); + sp = roken_getservbyname (str, protocol); if (sp != NULL) { port = sp->s_port; } else { char *end; - port = htons(strtol(port_str, &end, 0)); - if (end == port_str) + port = htons(strtol(str, &end, 0)); + if (end == str) return; } - add_port (family, port, protocol); + add_port (context, family, port, protocol); } /* @@ -123,26 +139,31 @@ add_port_string (int family, const char *port_str, const char *protocol) */ static void -add_standard_ports (int family) +add_standard_ports (krb5_context context, + krb5_kdc_configuration *config, + int family) { - add_port_service(family, "kerberos", 88, "udp"); - add_port_service(family, "kerberos", 88, "tcp"); - add_port_service(family, "kerberos-sec", 88, "udp"); - add_port_service(family, "kerberos-sec", 88, "tcp"); + 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(family, "http", 80, "tcp"); - if(enable_524) { - add_port_service(family, "krb524", 4444, "udp"); - add_port_service(family, "krb524", 4444, "tcp"); - } -#ifdef KRB4 - if(enable_v4) { - add_port_service(family, "kerberos-iv", 750, "udp"); - add_port_service(family, "kerberos-iv", 750, "tcp"); - } - if (enable_kaserver) - add_port_service(family, "afs3-kaserver", 7004, "udp"); -#endif + 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"); + } + } /* @@ -152,7 +173,9 @@ add_standard_ports (int family) */ static void -parse_ports(const char *str) +parse_ports(krb5_context context, + krb5_kdc_configuration *config, + const char *str) { char *pos = NULL; char *p; @@ -162,24 +185,24 @@ parse_ports(const char *str) while(p != NULL) { if(strcmp(p, "+") == 0) { #ifdef HAVE_IPV6 - add_standard_ports(AF_INET6); + add_standard_ports(context, config, AF_INET6); #endif - add_standard_ports(AF_INET); + add_standard_ports(context, config, AF_INET); } else { char *q = strchr(p, '/'); if(q){ *q++ = 0; #ifdef HAVE_IPV6 - add_port_string(AF_INET6, p, q); + add_port_string(context, AF_INET6, p, q); #endif - add_port_string(AF_INET, p, q); + add_port_string(context, AF_INET, p, q); }else { #ifdef HAVE_IPV6 - add_port_string(AF_INET6, p, "udp"); - add_port_string(AF_INET6, p, "tcp"); + add_port_string(context, AF_INET6, p, "udp"); + add_port_string(context, AF_INET6, p, "tcp"); #endif - add_port_string(AF_INET, p, "udp"); - add_port_string(AF_INET, p, "tcp"); + add_port_string(context, AF_INET, p, "udp"); + add_port_string(context, AF_INET, p, "tcp"); } } @@ -195,6 +218,7 @@ parse_ports(const char *str) struct descr { int s; int type; + int port; unsigned char *buf; size_t size; size_t len; @@ -231,12 +255,14 @@ reinit_descrs (struct descr *d, int n) */ static void -init_socket(struct descr *d, krb5_address *a, int family, int type, int port) +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; - int sa_size = sizeof(__ss); + krb5_socklen_t sa_size = sizeof(__ss); init_descr (d); @@ -264,6 +290,7 @@ init_socket(struct descr *d, krb5_address *a, int family, int type, int port) } #endif d->type = type; + d->port = port; if(bind(d->s, sa, sa_size) < 0){ char a_str[256]; @@ -293,7 +320,9 @@ init_socket(struct descr *d, krb5_address *a, int family, int type, int port) */ static int -init_sockets(struct descr **desc) +init_sockets(krb5_context context, + krb5_kdc_configuration *config, + struct descr **desc) { krb5_error_code ret; int i, j; @@ -308,7 +337,7 @@ init_sockets(struct descr **desc) if (ret) krb5_err (context, 1, ret, "krb5_get_all_server_addrs"); } - parse_ports(port_str); + parse_ports(context, config, port_str); d = malloc(addresses.len * num_ports * sizeof(*d)); if (d == NULL) krb5_errx(context, 1, "malloc(%lu) failed", @@ -316,7 +345,7 @@ init_sockets(struct descr **desc) for (i = 0; i < num_ports; i++){ for (j = 0; j < addresses.len; ++j) { - init_socket(&d[num], &addresses.val[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]; @@ -325,7 +354,7 @@ init_sockets(struct descr **desc) krb5_print_address (&addresses.val[j], a_str, sizeof(a_str), &len); - kdc_log(5, "listening on %s port %u/%s", + kdc_log(context, config, 5, "listening on %s port %u/%s", a_str, ntohs(ports[i].port), (ports[i].type == SOCK_STREAM) ? "tcp" : "udp"); @@ -345,52 +374,22 @@ init_sockets(struct descr **desc) } /* - * handle the request in `buf, len', from `addr' (or `from' as a string), - * sending a reply in `reply'. + * */ -static int -process_request(unsigned char *buf, - size_t len, - krb5_data *reply, - int *sendlength, - const char *from, - struct sockaddr *addr) +static const char * +descr_type(struct descr *d) { - KDC_REQ req; - Ticket ticket; - krb5_error_code ret; - size_t i; - - gettimeofday(&now, NULL); - if(decode_AS_REQ(buf, len, &req, &i) == 0){ - ret = as_rep(&req, reply, from, addr); - free_AS_REQ(&req); - return ret; - }else if(decode_TGS_REQ(buf, len, &req, &i) == 0){ - ret = tgs_rep(&req, reply, from, addr); - free_TGS_REQ(&req); - return ret; - }else if(decode_Ticket(buf, len, &ticket, &i) == 0){ - ret = do_524(&ticket, reply, from, addr); - free_Ticket(&ticket); - return ret; -#ifdef KRB4 - } else if(maybe_version4(buf, len)){ - *sendlength = 0; /* elbitapmoc sdrawkcab XXX */ - do_version4(buf, len, reply, from, (struct sockaddr_in*)addr); - return 0; - } else if (enable_kaserver) { - ret = do_kaserver (buf, len, reply, from, (struct sockaddr_in*)addr); - return ret; -#endif - } - - return -1; + if (d->type == SOCK_DGRAM) + return "udp"; + else if (d->type == SOCK_STREAM) + return "tcp"; + return "unknown"; } static void -addr_to_string(struct sockaddr *addr, size_t addr_len, char *str, size_t len) +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) { @@ -404,43 +403,68 @@ addr_to_string(struct sockaddr *addr, size_t addr_len, char *str, size_t len) } /* + * + */ + +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(void *buf, size_t len, int sendlength, +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; - - reply.length = 0; - ret = process_request(buf, len, &reply, &sendlength, - d->addr_string, d->sa); + 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){ - kdc_log(5, "sending %lu bytes to %s", (unsigned long)reply.length, - d->addr_string); - if(sendlength){ - unsigned char len[4]; - len[0] = (reply.length >> 24) & 0xff; - len[1] = (reply.length >> 16) & 0xff; - len[2] = (reply.length >> 8) & 0xff; - len[3] = reply.length & 0xff; - if(sendto(d->s, len, sizeof(len), 0, d->sa, d->sock_len) < 0) { - kdc_log (0, "sendto(%s): %s", d->addr_string, strerror(errno)); - krb5_data_free(&reply); - return; - } - } - if(sendto(d->s, reply.data, reply.length, 0, d->sa, d->sock_len) < 0) { - kdc_log (0, "sendto(%s): %s", d->addr_string, strerror(errno)); - krb5_data_free(&reply); - return; - } + send_reply(context, config, prependlength, d, &reply); krb5_data_free(&reply); } if(ret) - kdc_log(0, "Failed processing %lu byte request from %s", + kdc_log(context, config, 0, + "Failed processing %lu byte request from %s", (unsigned long)len, d->addr_string); } @@ -449,14 +473,16 @@ do_request(void *buf, size_t len, int sendlength, */ static void -handle_udp(struct descr *d) +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(0, "Failed to allocate %lu bytes", (unsigned long)max_request); + kdc_log(context, config, 0, "Failed to allocate %lu bytes", (unsigned long)max_request); return; } @@ -465,9 +491,9 @@ handle_udp(struct descr *d) if(n < 0) krb5_warn(context, errno, "recvfrom"); else { - addr_to_string (d->sa, d->sock_len, + addr_to_string (context, d->sa, d->sock_len, d->addr_string, sizeof(d->addr_string)); - do_request(buf, n, 0, d); + do_request(context, config, buf, n, FALSE, d); } free (buf); } @@ -488,11 +514,11 @@ clear_descr(struct descr *d) static int de_http(char *buf) { - char *p, *q; - for(p = q = buf; *p; p++, q++) { + 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(p + 1, "%2x", &x) != 1) + if(sscanf((char *)p + 1, "%2x", &x) != 1) return -1; *q = x; p += 2; @@ -510,7 +536,9 @@ de_http(char *buf) */ static void -add_new_tcp (struct descr *d, int parent, int child) +add_new_tcp (krb5_context context, + krb5_kdc_configuration *config, + struct descr *d, int parent, int child) { int s; @@ -533,7 +561,8 @@ add_new_tcp (struct descr *d, int parent, int child) d[child].s = s; d[child].timeout = time(NULL) + TCP_TIMEOUT; d[child].type = SOCK_STREAM; - addr_to_string (d[child].sa, d[child].sock_len, + addr_to_string (context, + d[child].sa, d[child].sock_len, d[child].addr_string, sizeof(d[child].addr_string)); } @@ -543,7 +572,9 @@ add_new_tcp (struct descr *d, int parent, int child) */ static int -grow_descr (struct descr *d, size_t n) +grow_descr (krb5_context context, + krb5_kdc_configuration *config, + struct descr *d, size_t n) { if (d->size - d->len < n) { unsigned char *tmp; @@ -551,14 +582,14 @@ grow_descr (struct descr *d, size_t n) grow = max(1024, d->len + n); if (d->size + grow > max_request) { - kdc_log(0, "Request exceeds max request size (%lu bytes).", + 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(0, "Failed to re-allocate %lu bytes.", + kdc_log(context, config, 0, "Failed to re-allocate %lu bytes.", (unsigned long)d->size + grow); clear_descr(d); return -1; @@ -575,20 +606,23 @@ grow_descr (struct descr *d, size_t n) */ static int -handle_vanilla_tcp (struct descr *d) +handle_vanilla_tcp (krb5_context context, + krb5_kdc_configuration *config, + struct descr *d) { krb5_storage *sp; - int32_t len; + uint32_t len; sp = krb5_storage_from_mem(d->buf, d->len); if (sp == NULL) { - kdc_log (0, "krb5_storage_from_mem failed"); + kdc_log (context, config, 0, "krb5_storage_from_mem failed"); return -1; } - krb5_ret_int32(sp, &len); + 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; @@ -600,7 +634,9 @@ handle_vanilla_tcp (struct descr *d) */ static int -handle_http_tcp (struct descr *d) +handle_http_tcp (krb5_context context, + krb5_kdc_configuration *config, + struct descr *d) { char *s, *p, *t; void *data; @@ -611,7 +647,7 @@ handle_http_tcp (struct descr *d) p = strstr(s, "\r\n"); if (p == NULL) { - kdc_log(0, "Malformed HTTP request from %s", d->addr_string); + kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); return -1; } *p = 0; @@ -619,31 +655,31 @@ handle_http_tcp (struct descr *d) p = NULL; t = strtok_r(s, " \t", &p); if (t == NULL) { - kdc_log(0, "Malformed HTTP request from %s", d->addr_string); + 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(0, "Malformed HTTP request from %s", d->addr_string); + kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); return -1; } data = malloc(strlen(t)); if (data == NULL) { - kdc_log(0, "Failed to allocate %lu bytes", + 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(0, "Malformed HTTP request from %s", d->addr_string); - kdc_log(5, "Request: %s", t); + 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(0, "Malformed HTTP request from %s", d->addr_string); + kdc_log(context, config, 0, "Malformed HTTP request from %s", d->addr_string); free(data); return -1; } @@ -659,12 +695,20 @@ handle_http_tcp (struct descr *d) "404 Not found\r\n" "

404 Not found

\r\n" "That page doesn't exist, maybe you are looking for " - "Heimdal?\r\n"; - write(d->s, proto, strlen(proto)); - write(d->s, msg, strlen(msg)); - kdc_log(0, "HTTP request from %s is non KDC request", d->addr_string); - kdc_log(5, "Request: %s", t); + "Heimdal?\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; } { @@ -675,8 +719,16 @@ handle_http_tcp (struct descr *d) "Pragma: no-cache\r\n" "Content-type: application/octet-stream\r\n" "Content-transfer-encoding: binary\r\n\r\n"; - write(d->s, proto, strlen(proto)); - write(d->s, msg, strlen(msg)); + 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; @@ -689,63 +741,94 @@ handle_http_tcp (struct descr *d) */ static void -handle_tcp(struct descr *d, int index, int min_free) +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[index].timeout == 0) { - add_new_tcp (d, index, min_free); + if (d[idx].timeout == 0) { + add_new_tcp (context, config, d, idx, min_free); return; } - n = recvfrom(d[index].s, buf, sizeof(buf), 0, NULL, NULL); + n = recvfrom(d[idx].s, buf, sizeof(buf), 0, NULL, NULL); if(n < 0){ - krb5_warn(context, errno, "recvfrom"); + 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", - (unsigned long)d[index].len, d[index].addr_string); - clear_descr (d + index); + "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 (&d[index], n)) + if (grow_descr (context, config, &d[idx], n)) return; - memcpy(d[index].buf + d[index].len, buf, n); - d[index].len += n; - if(d[index].len > 4 && d[index].buf[0] == 0) { - ret = handle_vanilla_tcp (&d[index]); + 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[index].len >= 4 && - strncmp((char *)d[index].buf, "GET ", 4) == 0 && - strncmp((char *)d[index].buf + d[index].len - 4, + 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 (&d[index]); + ret = handle_http_tcp (context, config, &d[idx]); if (ret < 0) - clear_descr (d + index); - } else if (d[index].len > 4) { - kdc_log (0, "TCP data of strange type from %s", d[index].addr_string); + 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(d[index].buf, d[index].len, 1, &d[index]); - clear_descr(d + index); + do_request(context, config, + d[idx].buf, d[idx].len, TRUE, &d[idx]); + clear_descr(d + idx); } } void -loop(void) +loop(krb5_context context, + krb5_kdc_configuration *config) { struct descr *d; int ndescr; - ndescr = init_sockets(&d); + 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; @@ -758,7 +841,8 @@ loop(void) if(d[i].s >= 0){ if(d[i].type == SOCK_STREAM && d[i].timeout && d[i].timeout < time(NULL)) { - kdc_log(1, "TCP-connection from %s expired after %lu bytes", + 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; @@ -800,11 +884,17 @@ loop(void) 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(&d[i]); + handle_udp(context, config, &d[i]); else if(d[i].type == SOCK_STREAM) - handle_tcp(d, i, min_free); + 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/crypto/heimdal/kdc/default_config.c b/crypto/heimdal/kdc/default_config.c new file mode 100644 index 0000000..5f336e3 --- /dev/null +++ b/crypto/heimdal/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 +#include + +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/crypto/heimdal/kdc/digest.c b/crypto/heimdal/kdc/digest.c new file mode 100644 index 0000000..b845b0f --- /dev/null +++ b/crypto/heimdal/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 + +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/crypto/heimdal/kdc/headers.h b/crypto/heimdal/kdc/headers.h index 91e4d50..64f6b6e 100644 --- a/crypto/heimdal/kdc/headers.h +++ b/crypto/heimdal/kdc/headers.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -32,7 +32,7 @@ */ /* - * $Id: headers.h,v 1.15 2002/09/10 20:04:46 joda Exp $ + * $Id: headers.h 19658 2007-01-04 00:15:34Z lha $ */ #ifndef __HEADERS_H__ @@ -72,6 +72,9 @@ #ifdef HAVE_ARPA_INET_H #include #endif +#ifdef HAVE_SYS_WAIT_H +#include +#endif #ifdef HAVE_NETDB_H #include #endif @@ -88,16 +91,14 @@ #include #include #include +#include +#include #include #include -#include /* copy_octet_string */ +#include -#ifdef KRB4 -#include -#include -#define Principal Principal4 -#include -#endif +#include +#include #undef ALLOC #define ALLOC(X) ((X) = malloc(sizeof(*(X)))) diff --git a/crypto/heimdal/kdc/hprop.8 b/crypto/heimdal/kdc/hprop.8 index f5e3879..99fc978 100644 --- a/crypto/heimdal/kdc/hprop.8 +++ b/crypto/heimdal/kdc/hprop.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2000 - 2003 Kungliga Tekniska Högskolan +.\" Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan .\" (Royal Institute of Technology, Stockholm, Sweden). .\" All rights reserved. .\" @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $Id: hprop.8,v 1.18 2003/02/16 21:10:19 lha Exp $ +.\" $Id: hprop.8 20456 2007-04-19 20:29:42Z lha $ .\" -.Dd June 19, 2000 +.Dd December 8, 2004 .Dt HPROP 8 .Os HEIMDAL .Sh NAME @@ -39,6 +39,7 @@ .Nd propagate the KDC database .Sh SYNOPSIS .Nm +.Bk -words .Oo Fl m Ar file \*(Ba Xo .Fl -master-key= Ns Pa file .Xc @@ -47,7 +48,7 @@ .Fl -database= Ns Pa file .Xc .Oc -.Op Fl -source= Ns Ar heimdal|mit-dump|krb4-dump|krb4-db|kaserver +.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 @@ -73,6 +74,7 @@ .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 @@ -99,7 +101,7 @@ Where to find the master key to encrypt or decrypt keys with. .Xc The database to be propagated. .It Xo -.Fl -source= Ns Ar heimdal|mit-dump|krb4-dump|krb4-db|kaserver +.Fl -source= Ns Ar heimdal|mit-dump|krb4-dump|kaserver .Xc Specifies the type of the source database. Alternatives include: .Pp @@ -108,8 +110,6 @@ Specifies the type of the source database. Alternatives include: a Heimdal database .It mit-dump a MIT Kerberos 5 dump file -.It krb4-db -a Kerberos 4 database .It krb4-dump a Kerberos 4 dump file .It kaserver @@ -168,12 +168,6 @@ The AFS cell name, used if reading a kaserver database. .Xc Also dump the principals marked as special in the kaserver database. .It Xo -.Fl 4 , -.Fl -v4-db -.Xc -Deprecated, identical to -.Sq --source=krb4-db . -.It Xo .Fl K , .Fl -ka-db .Xc @@ -183,16 +177,11 @@ Deprecated, identical to .Sh EXAMPLES The following will propagate a database to another machine (which should run -.Xr hpropd 8): +.Xr hpropd 8 ): .Bd -literal -offset indent $ hprop slave-1 slave-2 .Ed .Pp -Copy a Kerberos 4 database to a Kerberos 5 slave: -.Bd -literal -offset indent -$ hprop --source=krb4-db -E krb5-slave -.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 diff --git a/crypto/heimdal/kdc/hprop.c b/crypto/heimdal/kdc/hprop.c index 3bc066f..e5b7fd1 100644 --- a/crypto/heimdal/kdc/hprop.c +++ b/crypto/heimdal/kdc/hprop.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,7 +33,7 @@ #include "hprop.h" -RCSID("$Id: hprop.c,v 1.70 2002/09/04 18:19:41 joda Exp $"); +RCSID("$Id: hprop.c 21745 2007-07-31 16:11:25Z lha $"); static int version_flag; static int help_flag; @@ -93,28 +93,28 @@ open_socket(krb5_context context, const char *hostname, const char *port) } krb5_error_code -v5_prop(krb5_context context, HDB *db, hdb_entry *entry, void *appdata) +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, mkey5); + 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, mkey5); + 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, &data); + ret = hdb_entry2value(context, &entry->entry, &data); if(ret) { krb5_warn(context, ret, "hdb_entry2value"); return ret; @@ -129,88 +129,17 @@ v5_prop(krb5_context context, HDB *db, hdb_entry *entry, void *appdata) return ret; } -#ifdef KRB4 - -static char realm_buf[REALM_SZ]; - -static int -kdb_prop(void *arg, Principal *p) -{ - int ret; - struct v4_principal pr; - - memset(&pr, 0, sizeof(pr)); - - if(p->attributes != 0) { - warnx("%s.%s has non-zero attributes - skipping", - p->name, p->instance); - return 0; - } - strlcpy(pr.name, p->name, sizeof(pr.name)); - strlcpy(pr.instance, p->instance, sizeof(pr.instance)); - - copy_to_key(&p->key_low, &p->key_high, pr.key); - pr.exp_date = p->exp_date; - pr.mod_date = p->mod_date; - strlcpy(pr.mod_name, p->mod_name, sizeof(pr.mod_name)); - strlcpy(pr.mod_instance, p->mod_instance, sizeof(pr.mod_instance)); - pr.max_life = p->max_life; - pr.mkvno = p->kdc_key_ver; - pr.kvno = p->key_version; - - ret = v4_prop(arg, &pr); - memset(&pr, 0, sizeof(pr)); - return ret; -} - -#endif /* KRB4 */ - -#ifndef KRB4 -static time_t -krb_life_to_time(time_t start, int life) -{ - static int lifetimes[] = { - 38400, 41055, 43894, 46929, 50174, 53643, 57352, 61318, - 65558, 70091, 74937, 80119, 85658, 91581, 97914, 104684, - 111922, 119661, 127935, 136781, 146239, 156350, 167161, 178720, - 191077, 204289, 218415, 233517, 249664, 266926, 285383, 305116, - 326213, 348769, 372885, 398668, 426234, 455705, 487215, 520904, - 556921, 595430, 636601, 680618, 727680, 777995, 831789, 889303, - 950794, 1016537, 1086825, 1161973, 1242318, 1328218, 1420057, 1518247, - 1623226, 1735464, 1855462, 1983758, 2120925, 2267576, 2424367, 2592000 - }; - -#if 0 - int i; - double q = exp((log(2592000.0) - log(38400.0)) / 63); - double x = 38400; - for(i = 0; i < 64; i++) { - lifetimes[i] = (int)x; - x *= q; - } -#endif - - if(life == 0xff) - return NEVERDATE; - if(life < 0x80) - return start + life * 5 * 60; - if(life > 0xbf) - life = 0xbf; - return start + lifetimes[life - 0x80]; -} -#endif /* !KRB4 */ - int v4_prop(void *arg, struct v4_principal *p) { struct prop_data *pd = arg; - hdb_entry ent; + 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.principal); + &ent.entry.principal); if(ret) { krb5_warn(pd->context, ret, "krb5_425_conv_principal %s.%s@%s", @@ -220,49 +149,55 @@ v4_prop(void *arg, struct v4_principal *p) if(verbose_flag) { char *s; - krb5_unparse_name_short(pd->context, ent.principal, &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.kvno = p->kvno; - ent.keys.len = 3; - ent.keys.val = malloc(ent.keys.len * sizeof(*ent.keys.val)); + 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.keys.val[0].mkvno = malloc (sizeof(*ent.keys.val[0].mkvno)); - *(ent.keys.val[0].mkvno) = p->mkvno; + 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.keys.val[0].mkvno = NULL; - ent.keys.val[0].salt = calloc(1, sizeof(*ent.keys.val[0].salt)); - ent.keys.val[0].salt->type = KRB5_PADATA_PW_SALT; - ent.keys.val[0].key.keytype = ETYPE_DES_CBC_MD5; - krb5_data_alloc(&ent.keys.val[0].key.keyvalue, sizeof(des_cblock)); - memcpy(ent.keys.val[0].key.keyvalue.data, p->key, 8); - - copy_Key(&ent.keys.val[0], &ent.keys.val[1]); - ent.keys.val[1].key.keytype = ETYPE_DES_CBC_MD4; - copy_Key(&ent.keys.val[0], &ent.keys.val[2]); - ent.keys.val[2].key.keytype = ETYPE_DES_CBC_CRC; + 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 = krb_life_to_time(0, p->max_life); + int life = _krb5_krb_life_to_time(0, p->max_life); if(life == NEVERDATE){ - ent.max_life = NULL; + 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.max_life); - *ent.max_life = life; + ALLOC(ent.entry.max_life); + *ent.entry.max_life = life; } } - ALLOC(ent.valid_end); - *ent.valid_end = p->exp_date; + ALLOC(ent.entry.valid_end); + *ent.entry.valid_end = p->exp_date; - ret = krb5_make_principal(pd->context, &ent.created_by.principal, + ret = krb5_make_principal(pd->context, &ent.entry.created_by.principal, v4_realm, "kadmin", "hprop", @@ -272,44 +207,44 @@ v4_prop(void *arg, struct v4_principal *p) ret = 0; goto out; } - ent.created_by.time = time(NULL); - ALLOC(ent.modified_by); + 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.modified_by->principal); + v4_realm, &ent.entry.modified_by->principal); if(ret){ krb5_warn(pd->context, ret, "%s.%s@%s", p->name, p->instance, v4_realm); - ent.modified_by->principal = NULL; + ent.entry.modified_by->principal = NULL; ret = 0; goto out; } - ent.modified_by->time = p->mod_date; - - ent.flags.forwardable = 1; - ent.flags.renewable = 1; - ent.flags.proxiable = 1; - ent.flags.postdate = 1; - ent.flags.client = 1; - ent.flags.server = 1; + 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.flags.forwardable = 0; - ent.flags.renewable = 0; - ent.flags.proxiable = 0; - ent.flags.postdate = 0; - ent.flags.initial = 1; - ent.flags.change_pw = 1; + 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.principal); + krb5_free_principal (pd->context, ent.entry.principal); ret = krb5_425_conv_principal (pd->context, p->name, v4_realm, p->instance, - &ent.principal); + &ent.entry.principal); if (ret == 0) ret = v5_prop (pd->context, NULL, &ent, pd); } @@ -345,87 +280,96 @@ ka_convert(struct prop_data *pd, int fd, struct ka_entry *ent) { int32_t flags = ntohl(ent->flags); krb5_error_code ret; - hdb_entry hdb; + 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.principal); + 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.kvno = ntohl(ent->kvno); - hdb.keys.len = 3; - hdb.keys.val = malloc(hdb.keys.len * sizeof(*hdb.keys.val)); - hdb.keys.val[0].mkvno = NULL; - hdb.keys.val[0].salt = calloc(1, sizeof(*hdb.keys.val[0].salt)); + 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.keys.val[0].salt->type = hdb_pw_salt; - hdb.keys.val[0].salt->salt.data = NULL; - hdb.keys.val[0].salt->salt.length = 0; + 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.keys.val[0].salt->type = hdb_afs3_salt; - hdb.keys.val[0].salt->salt.data = strdup(afs_cell); - hdb.keys.val[0].salt->salt.length = strlen(afs_cell); + 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.keys.val[0].key.keytype = ETYPE_DES_CBC_MD5; - krb5_data_copy(&hdb.keys.val[0].key.keyvalue, ent->key, sizeof(ent->key)); - copy_Key(&hdb.keys.val[0], &hdb.keys.val[1]); - hdb.keys.val[1].key.keytype = ETYPE_DES_CBC_MD4; - copy_Key(&hdb.keys.val[0], &hdb.keys.val[2]); - hdb.keys.val[2].key.keytype = ETYPE_DES_CBC_CRC; - - ALLOC(hdb.max_life); - *hdb.max_life = ntohl(ent->max_life); - - if(ntohl(ent->valid_end) != NEVERDATE && ntohl(ent->valid_end) != -1){ - ALLOC(hdb.valid_end); - *hdb.valid_end = ntohl(ent->valid_end); + 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.pw_end); - *hdb.pw_end = ntohl(ent->pw_change) + 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.created_by.principal, + ret = krb5_make_principal(pd->context, &hdb.entry.created_by.principal, v4_realm, "kadmin", "hprop", NULL); - hdb.created_by.time = time(NULL); + hdb.entry.created_by.time = time(NULL); if(ent->mod_ptr){ struct ka_entry mod; - ALLOC(hdb.modified_by); + 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.modified_by->principal); - hdb.modified_by->time = ntohl(ent->mod_time); + &hdb.entry.modified_by->principal); + hdb.entry.modified_by->time = ntohl(ent->mod_time); memset(&mod, 0, sizeof(mod)); } - hdb.flags.forwardable = 1; - hdb.flags.renewable = 1; - hdb.flags.proxiable = 1; - hdb.flags.postdate = 1; + 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.flags.client = (flags & KAFNOTGS) == 0; - hdb.flags.server = (flags & KAFNOSEAL) == 0; + 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); @@ -469,9 +413,6 @@ struct getargs args[] = { "heimdal" "|mit-dump" "|krb4-dump" -#ifdef KRB4 - "|krb4-db" -#endif "|kaserver" }, @@ -503,7 +444,7 @@ 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_get_init_creds_opt *init_opts; krb5_preauthtype preauth = KRB5_PADATA_ENC_TIMESTAMP; krb5_creds creds; @@ -517,11 +458,14 @@ get_creds(krb5_context context, krb5_ccache *cache) "kadmin", HPROP_NAME, NULL); if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); - krb5_get_init_creds_opt_init(&init_opts); - krb5_get_init_creds_opt_set_preauth_list(&init_opts, &preauth, 1); + 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); + 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"); @@ -537,18 +481,17 @@ get_creds(krb5_context context, krb5_ccache *cache) ret = krb5_cc_store_cred(context, *cache, &creds); if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred"); - krb5_free_creds_contents(context, &creds); + krb5_free_cred_contents(context, &creds); } enum hprop_source { HPROP_HEIMDAL = 1, - HPROP_KRB4_DB, HPROP_KRB4_DUMP, HPROP_KASERVER, HPROP_MIT_DUMP }; -#define IS_TYPE_V4(X) ((X) == HPROP_KRB4_DB || (X) == HPROP_KRB4_DUMP || (X) == HPROP_KASERVER) +#define IS_TYPE_V4(X) ((X) == HPROP_KRB4_DUMP || (X) == HPROP_KASERVER) struct { int type; @@ -556,9 +499,6 @@ struct { } types[] = { { HPROP_HEIMDAL, "heimdal" }, { HPROP_KRB4_DUMP, "krb4-dump" }, -#ifdef KRB4 - { HPROP_KRB4_DB, "krb4-db" }, -#endif { HPROP_KASERVER, "kaserver" }, { HPROP_MIT_DUMP, "mit-dump" } }; @@ -574,9 +514,9 @@ parse_source_type(const char *s) return 0; } -static void +static int iterate (krb5_context context, - const char *database, + const char *database_name, HDB *db, int type, struct prop_data *pd) @@ -585,38 +525,36 @@ iterate (krb5_context context, switch(type) { case HPROP_KRB4_DUMP: - ret = v4_prop_dump(pd, database); - break; -#ifdef KRB4 - case HPROP_KRB4_DB: - ret = kerb_db_iterate ((k_iter_proc_t)kdb_prop, pd); + ret = v4_prop_dump(pd, database_name); if(ret) - krb5_errx(context, 1, "kerb_db_iterate: %s", - krb_get_err_text(ret)); + krb5_warnx(context, "v4_prop_dump: %s", + krb5_get_err_text(context, ret)); break; -#endif /* KRB4 */ case HPROP_KASERVER: - ret = ka_dump(pd, database); + ret = ka_dump(pd, database_name); if(ret) - krb5_err(context, 1, ret, "ka_dump"); + krb5_warn(context, ret, "ka_dump"); break; case HPROP_MIT_DUMP: - ret = mit_prop_dump(pd, database); + ret = mit_prop_dump(pd, database_name); if (ret) - krb5_errx(context, 1, "mit_prop_dump: %s", + 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_err(context, 1, ret, "hdb_foreach"); + 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, HDB *db) + const char *database_name, HDB *db) { krb5_error_code ret; struct prop_data pd; @@ -626,7 +564,9 @@ dump_database (krb5_context context, int type, pd.auth_context = NULL; pd.sock = STDOUT_FILENO; - iterate (context, database, db, type, &pd); + 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) @@ -637,23 +577,24 @@ dump_database (krb5_context context, int type, static int propagate_database (krb5_context context, int type, - const char *database, + const char *database_name, HDB *db, krb5_ccache ccache, - int optind, int argc, char **argv) + int optidx, int argc, char **argv) { krb5_principal server; krb5_error_code ret; - int i; + int i, failed = 0; - for(i = optind; i < argc; i++){ + 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]; - - port = strchr(argv[i], ':'); + char *host = argv[i]; + + port = strchr(host, ':'); if(port == NULL) { snprintf(portstr, sizeof(portstr), "%u", ntohs(krb5_getportbyname (context, "hprop", "tcp", @@ -662,16 +603,18 @@ propagate_database (krb5_context context, int type, } else *port++ = '\0'; - fd = open_socket(context, argv[i], port); + fd = open_socket(context, host, port); if(fd < 0) { - krb5_warn (context, errno, "connect %s", argv[i]); + 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) { - krb5_warn(context, ret, "krb5_sname_to_principal(%s)", argv[i]); + failed++; + krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host); close(fd); continue; } @@ -702,31 +645,45 @@ propagate_database (krb5_context context, int type, krb5_free_principal(context, server); if(ret) { - krb5_warn(context, ret, "krb5_sendauth"); + failed++; + krb5_warn(context, ret, "krb5_sendauth (%s)", host); close(fd); - continue; + goto next_host; } pd.context = context; pd.auth_context = auth_context; pd.sock = fd; - iterate (context, database, db, type, &pd); + 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) + 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"); - else + 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; } @@ -737,13 +694,13 @@ main(int argc, char **argv) krb5_context context; krb5_ccache ccache = NULL; HDB *db = NULL; - int optind = 0; + int optidx = 0; - int type = 0; + int type, exit_code; setprogname(argv[0]); - if(getarg(args, num_args, argc, argv, &optind)) + if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(help_flag) @@ -780,12 +737,10 @@ main(int argc, char **argv) "only one of `--encrypt' and `--decrypt' is meaningful"); if(source_type != NULL) { - if(type != 0) - krb5_errx(context, 1, "more than one database type specified"); type = parse_source_type(source_type); if(type == 0) krb5_errx(context, 1, "unknown source type `%s'", source_type); - } else if(type == 0) + } else type = HPROP_HEIMDAL; if(!to_stdout) @@ -799,27 +754,11 @@ main(int argc, char **argv) krb5_errx(context, 1, "No master key file found"); } -#ifdef KRB4 - if (IS_TYPE_V4(type)) { - int e; - - if (v4_realm == NULL) { - e = krb_get_lrealm(realm_buf, 1); - if(e) - krb5_errx(context, 1, "krb_get_lrealm: %s", - krb_get_err_text(e)); - v4_realm = realm_buf; - } - } -#endif + if (IS_TYPE_V4(type) && v4_realm == NULL) + krb5_errx(context, 1, "Its a Kerberos 4 database " + "but no realm configured"); switch(type) { -#ifdef KRB4 - case HPROP_KRB4_DB: - if (database == NULL) - krb5_errx(context, 1, "no database specified"); - break; -#endif case HPROP_KASERVER: if (database == NULL) database = DEFAULT_DATABASE; @@ -842,9 +781,9 @@ main(int argc, char **argv) ret = hdb_create (context, &db, database); if(ret) krb5_err(context, 1, ret, "hdb_create: %s", database); - ret = db->open(context, db, O_RDONLY, 0); + ret = db->hdb_open(context, db, O_RDONLY, 0); if(ret) - krb5_err(context, 1, ret, "db->open"); + krb5_err(context, 1, ret, "db->hdb_open"); break; default: krb5_errx(context, 1, "unknown dump type `%d'", type); @@ -852,17 +791,17 @@ main(int argc, char **argv) } if (to_stdout) - dump_database (context, type, database, db); + exit_code = dump_database (context, type, database, db); else - propagate_database (context, type, database, - db, ccache, optind, argc, argv); + exit_code = propagate_database (context, type, database, + db, ccache, optidx, argc, argv); if(ccache != NULL) krb5_cc_destroy(context, ccache); if(db != NULL) - (*db->destroy)(context, db); + (*db->hdb_destroy)(context, db); krb5_free_context(context); - return 0; + return exit_code; } diff --git a/crypto/heimdal/kdc/hprop.h b/crypto/heimdal/kdc/hprop.h index 0bcab88..d43d04c 100644 --- a/crypto/heimdal/kdc/hprop.h +++ b/crypto/heimdal/kdc/hprop.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: hprop.h,v 1.13 2001/01/26 15:54:19 joda Exp $ */ +/* $Id: hprop.h 16378 2005-12-12 12:40:12Z lha $ */ #ifndef __HPROP_H__ #define __HPROP_H__ @@ -53,13 +53,13 @@ struct prop_data{ #define NEVERDATE ((1U << 31) - 1) #endif -krb5_error_code v5_prop(krb5_context, HDB*, hdb_entry*, void*); +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; + DES_cblock key; int kvno; int mkvno; time_t exp_date; diff --git a/crypto/heimdal/kdc/hpropd.8 b/crypto/heimdal/kdc/hpropd.8 index 7bb2deb..74a3dad 100644 --- a/crypto/heimdal/kdc/hpropd.8 +++ b/crypto/heimdal/kdc/hpropd.8 @@ -29,7 +29,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $Id: hpropd.8,v 1.11 2003/02/16 21:10:20 lha Exp $ +.\" $Id: hpropd.8 14381 2004-12-10 09:44:05Z lha $ .\" .Dd August 27, 1997 .Dt HPROPD 8 @@ -39,6 +39,7 @@ .Nd receive a propagated database .Sh SYNOPSIS .Nm +.Bk -words .Oo Fl d Ar file \*(Ba Xo .Fl -database= Ns Ar file .Xc @@ -51,6 +52,7 @@ .Xc .Oc .Op Fl 4 | Fl -v4dump +.Ek .Sh DESCRIPTION .Nm receives a database sent by @@ -65,7 +67,7 @@ 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 from +Only connections authenticated with the principal .Nm kadmin Ns / Ns Nm hprop are accepted. .Pp diff --git a/crypto/heimdal/kdc/hpropd.c b/crypto/heimdal/kdc/hpropd.c index d27ff25..12a9766 100644 --- a/crypto/heimdal/kdc/hpropd.c +++ b/crypto/heimdal/kdc/hpropd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,142 +33,15 @@ #include "hprop.h" -RCSID("$Id: hpropd.c,v 1.36 2003/04/16 15:46:32 lha Exp $"); - -#ifdef KRB4 -static des_cblock mkey4; -static des_key_schedule msched4; - -static char * -time2str(time_t t) -{ - static char buf[128]; - strftime(buf, sizeof(buf), "%Y%m%d%H%M", gmtime(&t)); - return buf; -} - -static int -dump_krb4(krb5_context context, hdb_entry *ent, int fd) -{ - char name[ANAME_SZ]; - char instance[INST_SZ]; - char realm[REALM_SZ]; - char buf[1024]; - char *p; - int i; - int ret; - char *princ_name; - Event *modifier; - krb5_realm *realms; - int cmp; - - ret = krb5_524_conv_principal(context, ent->principal, - name, instance, realm); - if (ret) { - krb5_unparse_name(context, ent->principal, &princ_name); - krb5_warn(context, ret, "%s", princ_name); - free(princ_name); - return -1; - } - - ret = krb5_get_default_realms (context, &realms); - if (ret) { - krb5_warn(context, ret, "krb5_get_default_realms"); - return -1; - } - - cmp = strcmp (realms[0], ent->principal->realm); - krb5_free_host_realm (context, realms); - if (cmp != 0) - return -1; - - snprintf (buf, sizeof(buf), "%s %s ", name, - (strlen(instance) != 0) ? instance : "*"); - - if (ent->max_life) { - asprintf(&p, "%d", krb_time_to_life(0, *ent->max_life)); - strlcat(buf, p, sizeof(buf)); - free(p); - } else - strlcat(buf, "255", sizeof(buf)); - strlcat(buf, " ", sizeof(buf)); - - i = 0; - while (i < ent->keys.len && - ent->keys.val[i].key.keytype != KEYTYPE_DES) - ++i; - - if (i == ent->keys.len) { - krb5_warnx(context, "No DES key for %s.%s", name, instance); - return -1; - } - - if (ent->keys.val[i].mkvno) - asprintf(&p, "%d ", *ent->keys.val[i].mkvno); - else - asprintf(&p, "%d ", 1); - strlcat(buf, p, sizeof(buf)); - free(p); - - asprintf(&p, "%d ", ent->kvno); - strlcat(buf, p, sizeof(buf)); - free(p); - - asprintf(&p, "%d ", 0); /* Attributes are always 0*/ - strlcat(buf, p, sizeof(buf)); - free(p); - - { - u_int32_t *key = ent->keys.val[i].key.keyvalue.data; - kdb_encrypt_key((des_cblock*)key, (des_cblock*)key, - &mkey4, msched4, DES_ENCRYPT); - asprintf(&p, "%x %x ", (int)htonl(*key), (int)htonl(*(key+1))); - strlcat(buf, p, sizeof(buf)); - free(p); - } - - if (ent->valid_end == NULL) - strlcat(buf, time2str(60*60*24*365*50), sizeof(buf)); /*no expiration*/ - else - strlcat(buf, time2str(*ent->valid_end), sizeof(buf)); - strlcat(buf, " ", sizeof(buf)); - - if (ent->modified_by == NULL) - modifier = &ent->created_by; - else - modifier = ent->modified_by; - - ret = krb5_524_conv_principal(context, modifier->principal, - name, instance, realm); - if (ret) { - krb5_unparse_name(context, modifier->principal, &princ_name); - krb5_warn(context, ret, "%s", princ_name); - free(princ_name); - return -1; - } - asprintf(&p, "%s %s %s\n", time2str(modifier->time), - (strlen(name) != 0) ? name : "*", - (strlen(instance) != 0) ? instance : "*"); - strlcat(buf, p, sizeof(buf)); - free(p); - - ret = write(fd, buf, strlen(buf)); - if (ret == -1) - krb5_warnx(context, "write"); - return 0; -} -#endif /* KRB4 */ +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 = HDB_DEFAULT_DB; +static const char *database; static int from_stdin; static char *local_realm; -#ifdef KRB4 -static int v4dump; -#endif static char *ktname = NULL; struct getargs args[] = { @@ -179,9 +52,6 @@ struct getargs args[] = { "Not started from inetd" }, { "keytab", 'k', arg_string, &ktname, "keytab to use for authentication", "keytab" }, { "realm", 'r', arg_string, &local_realm, "realm to use" }, -#ifdef KRB4 - { "v4dump", '4', arg_flag, &v4dump, "create v4 type DB" }, -#endif { "version", 0, arg_flag, &version_flag, NULL, NULL }, { "help", 'h', arg_flag, &help_flag, NULL, NULL} }; @@ -206,14 +76,10 @@ main(int argc, char **argv) krb5_keytab keytab; int fd; HDB *db; - int optind = 0; + int optidx = 0; char *tmp_db; krb5_log_facility *fac; int nprincs; -#ifdef KRB4 - int e; - int fd_out = -1; -#endif setprogname(argv[0]); @@ -226,14 +92,9 @@ main(int argc, char **argv) ; krb5_set_warn_dest(context, fac); - if(getarg(args, num_args, argc, argv, &optind)) + if(getarg(args, num_args, argc, argv, &optidx)) usage(1); -#ifdef KRB4 - if (v4dump && database == HDB_DEFAULT_DB) - database = "/var/kerberos/524_dump"; -#endif /* KRB4 */ - if(local_realm != NULL) krb5_set_default_realm(context, local_realm); @@ -244,12 +105,15 @@ main(int argc, char **argv) exit(0); } - argc -= optind; - argv += optind; + argc -= optidx; + argv += optidx; if (argc != 0) usage(1); + if (database == NULL) + database = hdb_default_db(context); + if(from_stdin) fd = STDIN_FILENO; else { @@ -280,7 +144,7 @@ main(int argc, char **argv) addr_name, sizeof(addr_name)) == NULL) strlcpy (addr_name, "unknown address", - sizeof(addr_name)); + sizeof(addr_name)); krb5_log(context, fac, 0, "Connection from %s", addr_name); @@ -319,10 +183,13 @@ main(int argc, char **argv) ret = krb5_make_principal(context, &c1, NULL, "kadmin", "hprop", NULL); if(ret) krb5_err(context, 1, ret, "krb5_make_principal"); - principalname2krb5_principal(&c2, authent->cname, authent->crealm); + _krb5_principalname2krb5_principal(context, &c2, + authent->cname, authent->crealm); if(!krb5_principal_compare(context, c1, c2)) { char *s; - krb5_unparse_name(context, c2, &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); @@ -335,37 +202,19 @@ main(int argc, char **argv) if(!print_dump) { asprintf(&tmp_db, "%s~", database); -#ifdef KRB4 - if (v4dump) { - fd_out = open(tmp_db, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (fd_out == -1) - krb5_errx(context, 1, "%s", strerror(errno)); - } - else -#endif /* KRB4 */ - { - ret = hdb_create(context, &db, tmp_db); - if(ret) - krb5_err(context, 1, ret, "hdb_create(%s)", tmp_db); - ret = db->open(context, db, O_RDWR | O_CREAT | O_TRUNC, 0600); - if(ret) - krb5_err(context, 1, ret, "hdb_open(%s)", tmp_db); - } - } -#ifdef KRB4 - if (v4dump) { - e = kdb_get_master_key(0, &mkey4, msched4); - if(e) - krb5_errx(context, 1, "kdb_get_master_key: %s", - krb_get_err_text(e)); + 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); } -#endif /* KRB4 */ nprincs = 0; while(1){ krb5_data data; - hdb_entry entry; + hdb_entry_ex entry; if(from_stdin) { ret = krb5_read_message(context, &fd, &data); @@ -384,52 +233,35 @@ main(int argc, char **argv) krb5_write_priv_message(context, ac, &fd, &data); } if(!print_dump) { -#ifdef KRB4 - if (v4dump) { - ret = rename(tmp_db, database); - if (ret) - krb5_errx(context, 1, "rename"); - ret = close(fd_out); - if (ret) - krb5_errx(context, 1, "close"); - } else -#endif /* KRB4 */ - { - ret = db->rename(context, db, database); - if(ret) - krb5_err(context, 1, ret, "db_rename"); - ret = db->close(context, db); - if(ret) - krb5_err(context, 1, ret, "db_close"); - } + 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; } - ret = hdb_value2entry(context, &data, &entry); + 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 { -#ifdef KRB4 - if (v4dump) { - ret = dump_krb4(context, &entry, fd_out); - if(!ret) nprincs++; - } + 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 -#endif /* KRB4 */ - { - ret = db->store(context, db, 0, &entry); - if(ret == HDB_ERR_EXISTS) { - char *s; - krb5_unparse_name(context, entry.principal, &s); - krb5_warnx(context, "Entry exists: %s", s); - free(s); - } else if(ret) - krb5_err(context, 1, ret, "db_store"); - else - nprincs++; - } + nprincs++; } hdb_free_entry(context, &entry); } diff --git a/crypto/heimdal/kdc/kadb.h b/crypto/heimdal/kdc/kadb.h index 5c98ccc..4b59abe 100644 --- a/crypto/heimdal/kdc/kadb.h +++ b/crypto/heimdal/kdc/kadb.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: kadb.h,v 1.3 2000/03/03 12:36:26 assar Exp $ */ +/* $Id: kadb.h 7997 2000-03-03 12:36:26Z assar $ */ #ifndef __kadb_h__ #define __kadb_h__ diff --git a/crypto/heimdal/kdc/kaserver.c b/crypto/heimdal/kdc/kaserver.c index 8694471..27f497e 100644 --- a/crypto/heimdal/kdc/kaserver.c +++ b/crypto/heimdal/kdc/kaserver.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,9 +33,9 @@ #include "kdc_locl.h" -RCSID("$Id: kaserver.c,v 1.21.2.1 2003/10/06 21:02:35 lha Exp $"); - +RCSID("$Id: kaserver.c 21654 2007-07-21 17:30:18Z lha $"); +#include #include #define KA_AUTHENTICATION_SERVICE 731 @@ -107,38 +107,69 @@ RCSID("$Id: kaserver.c,v 1.21.2.1 2003/10/06 21:02:35 lha Exp $"); #define KATOOSOON (180521L) #define KALOCKED (180522L) -static void + +static krb5_error_code decode_rx_header (krb5_storage *sp, struct rx_header *h) { - krb5_ret_int32(sp, &h->epoch); - krb5_ret_int32(sp, &h->connid); - krb5_ret_int32(sp, &h->callid); - krb5_ret_int32(sp, &h->seqno); - krb5_ret_int32(sp, &h->serialno); - krb5_ret_int8(sp, &h->type); - krb5_ret_int8(sp, &h->flags); - krb5_ret_int8(sp, &h->status); - krb5_ret_int8(sp, &h->secindex); - krb5_ret_int16(sp, &h->reserved); - krb5_ret_int16(sp, &h->serviceid); + 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 void +static krb5_error_code encode_rx_header (struct rx_header *h, krb5_storage *sp) { - krb5_store_int32(sp, h->epoch); - krb5_store_int32(sp, h->connid); - krb5_store_int32(sp, h->callid); - krb5_store_int32(sp, h->seqno); - krb5_store_int32(sp, h->serialno); - krb5_store_int8(sp, h->type); - krb5_store_int8(sp, h->flags); - krb5_store_int8(sp, h->status); - krb5_store_int8(sp, h->secindex); - krb5_store_int16(sp, h->reserved); - krb5_store_int16(sp, h->serviceid); + 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 @@ -160,19 +191,28 @@ init_reply_header (struct rx_header *hdr, 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, - u_int32_t ret, + uint32_t error, krb5_data *reply) { - krb5_storage *sp; 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(); - encode_rx_header (&reply_hdr, sp); - krb5_store_int32(sp, ret); + 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); } @@ -240,7 +280,8 @@ krb5_store_xdr_data(krb5_storage *sp, static krb5_error_code -create_reply_ticket (struct rx_header *hdr, +create_reply_ticket (krb5_context context, + struct rx_header *hdr, Key *skey, char *name, char *instance, char *realm, struct sockaddr_in *addr, @@ -248,29 +289,38 @@ create_reply_ticket (struct rx_header *hdr, int kvno, int32_t max_seq_len, const char *sname, const char *sinstance, - u_int32_t challenge, + uint32_t challenge, const char *label, - des_cblock *key, + krb5_keyblock *key, krb5_data *reply) { - KTEXT_ST ticket; - des_cblock session; + krb5_error_code ret; + krb5_data ticket; + krb5_keyblock session; krb5_storage *sp; krb5_data enc_data; - des_key_schedule schedule; struct rx_header reply_hdr; - des_cblock zero; + char zero[8]; size_t pad; unsigned fyrtiosjuelva; /* create the ticket */ - des_new_random_key(&session); - - krb_create_ticket (&ticket, 0, name, instance, realm, - addr->sin_addr.s_addr, - &session, life, kdc_time, - sname, sinstance, skey->key.keyvalue.data); + 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 (); @@ -278,10 +328,10 @@ create_reply_ticket (struct rx_header *hdr, fyrtiosjuelva &= 0xffffffff; krb5_store_int32 (sp, fyrtiosjuelva); krb5_store_int32 (sp, challenge); - krb5_storage_write (sp, session, 8); - memset (&session, 0, sizeof(session)); + 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 + krb_life_to_time (0, life)); + 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); @@ -293,7 +343,7 @@ create_reply_ticket (struct rx_header *hdr, #endif krb5_store_stringz (sp, sname); krb5_store_stringz (sp, sinstance); - krb5_storage_write (sp, ticket.dat, ticket.length); + krb5_storage_write (sp, ticket.data, ticket.length); krb5_storage_write (sp, label, strlen(label)); /* pad to DES block */ @@ -311,19 +361,26 @@ create_reply_ticket (struct rx_header *hdr, } /* encrypt it */ - des_set_key (key, schedule); - des_pcbc_encrypt (enc_data.data, - enc_data.data, - enc_data.length, - schedule, - key, - DES_ENCRYPT); - memset (&schedule, 0, sizeof(schedule)); + { + 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 (); - encode_rx_header (&reply_hdr, sp); + 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); @@ -373,9 +430,12 @@ unparse_auth_args (krb5_storage *sp, } static void -do_authenticate (struct rx_header *hdr, +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; @@ -385,87 +445,99 @@ do_authenticate (struct rx_header *hdr, time_t end_time; krb5_data request; int32_t max_seq_len; - hdb_entry *client_entry = NULL; - hdb_entry *server_entry = NULL; + hdb_entry_ex *client_entry = NULL; + hdb_entry_ex *server_entry = NULL; Key *ckey = NULL; Key *skey = NULL; - des_cblock key; - des_key_schedule schedule; krb5_storage *reply_sp; time_t max_life; - u_int8_t life; + uint8_t life; int32_t chal; char client_name[256]; char server_name[256]; krb5_data_zero (&request); - unparse_auth_args (sp, &name, &instance, &start_time, &end_time, - &request, &max_seq_len); - if (request.length < 8) { + 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, v4_realm); + name, instance, config->v4_realm); + snprintf (server_name, sizeof(server_name), "%s.%s@%s", + "krbtgt", config->v4_realm, config->v4_realm); - ret = db_fetch4 (name, instance, v4_realm, &client_entry); + 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(0, "Client not found in database: %s: %s", + 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; } - snprintf (server_name, sizeof(server_name), "%s.%s@%s", - "krbtgt", v4_realm, v4_realm); - - ret = db_fetch4 ("krbtgt", v4_realm, v4_realm, &server_entry); + ret = _kdc_db_fetch4 (context, config, "krbtgt", + config->v4_realm, config->v4_realm, + HDB_F_GET_KRBTGT, &server_entry); if (ret) { - kdc_log(0, "Server not found in database: %s: %s", + 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 = check_flags (client_entry, client_name, - server_entry, server_name, - TRUE); + 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 = get_des_key(client_entry, FALSE, TRUE, &ckey); + ret = _kdc_get_des_key(context, client_entry, FALSE, TRUE, &ckey); if(ret){ - kdc_log(0, "no suitable DES key for client"); + kdc_log(context, config, 0, "no suitable DES key for client"); make_error_reply (hdr, KANOKEYS, reply); goto out; } /* find a DES key */ - ret = get_des_key(server_entry, TRUE, TRUE, &skey); + ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey); if(ret){ - kdc_log(0, "no suitable DES key for server"); + kdc_log(context, config, 0, "no suitable DES key for server"); make_error_reply (hdr, KANOKEYS, reply); goto out; } - /* 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)); + { + 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; } @@ -485,23 +557,23 @@ do_authenticate (struct rx_header *hdr, time skew between client and server. Let's make sure it is postive */ if(max_life < 1) max_life = 1; - 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); + 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 (hdr, skey, - name, instance, v4_realm, - addr, life, server_entry->kvno, + create_reply_ticket (context, + hdr, skey, + name, instance, config->v4_realm, + addr, life, server_entry->entry.kvno, max_seq_len, - "krbtgt", v4_realm, + "krbtgt", config->v4_realm, chal + 1, "tgsT", - &key, reply); - memset (&key, 0, sizeof(key)); + &ckey->key, reply); -out: + out: if (request.length) { memset (request.data, 0, request.length); krb5_data_free (&request); @@ -511,9 +583,9 @@ out: if (instance) free (instance); if (client_entry) - free_ent (client_entry); + _kdc_free_ent (context, client_entry); if (server_entry) - free_ent (server_entry); + _kdc_free_ent (context, server_entry); } static krb5_error_code @@ -571,9 +643,12 @@ unparse_getticket_args (krb5_storage *sp, } static void -do_getticket (struct rx_header *hdr, +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; @@ -584,24 +659,26 @@ do_getticket (struct rx_header *hdr, char *instance = NULL; krb5_data times; int32_t max_seq_len; - hdb_entry *server_entry = NULL; - hdb_entry *krbtgt_entry = NULL; + 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; + DES_cblock key; + DES_key_schedule schedule; + DES_cblock session; time_t max_life; int8_t life; time_t start_time, end_time; - char pname[ANAME_SZ]; - char pinst[INST_SZ]; - char prealm[REALM_SZ]; char server_name[256]; + char client_name[256]; + struct _krb5_krb_auth_data ad; krb5_data_zero (&aticket); krb5_data_zero (×); + memset(&ad, 0, sizeof(ad)); + unparse_getticket_args (sp, &kvno, &auth_domain, &aticket, &name, &instance, ×, &max_seq_len); if (times.length < 8) { @@ -611,44 +688,40 @@ do_getticket (struct rx_header *hdr, } snprintf (server_name, sizeof(server_name), - "%s.%s@%s", name, instance, v4_realm); + "%s.%s@%s", name, instance, config->v4_realm); - ret = db_fetch4 (name, instance, v4_realm, &server_entry); + ret = _kdc_db_fetch4 (context, config, name, instance, + config->v4_realm, HDB_F_GET_SERVER, &server_entry); if (ret) { - kdc_log(0, "Server not found in database: %s: %s", + 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 = check_flags (NULL, NULL, - server_entry, server_name, - FALSE); + ret = _kdc_db_fetch4 (context, config, "krbtgt", + config->v4_realm, config->v4_realm, HDB_F_GET_KRBTGT, &krbtgt_entry); if (ret) { - make_error_reply (hdr, KAPWEXPIRED, reply); - goto out; - } - - ret = db_fetch4 ("krbtgt", v4_realm, v4_realm, &krbtgt_entry); - if (ret) { - kdc_log(0, "Server not found in database: %s.%s@%s: %s", - "krbtgt", v4_realm, v4_realm, krb5_get_err_text(context, 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 = get_des_key(krbtgt_entry, TRUE, TRUE, &kkey); + ret = _kdc_get_des_key(context, krbtgt_entry, TRUE, TRUE, &kkey); if(ret){ - kdc_log(0, "no suitable DES key for krbtgt"); + kdc_log(context, config, 0, "no suitable DES key for krbtgt"); make_error_reply (hdr, KANOKEYS, reply); goto out; } /* find a DES key */ - ret = get_des_key(server_entry, TRUE, TRUE, &skey); + ret = _kdc_get_des_key(context, server_entry, TRUE, TRUE, &skey); if(ret){ - kdc_log(0, "no suitable DES key for server"); + kdc_log(context, config, 0, "no suitable DES key for server"); make_error_reply (hdr, KANOKEYS, reply); goto out; } @@ -658,67 +731,95 @@ do_getticket (struct rx_header *hdr, /* unpack the ticket */ { - KTEXT_ST ticket; - u_char flags; - int life; - u_int32_t time_sec; - char sname[ANAME_SZ]; - char sinstance[SNAME_SZ]; - u_int32_t paddress; - - if (aticket.length > sizeof(ticket.dat)) { - kdc_log(0, "ticket too long (%u > %u)", - (unsigned)aticket.length, - (unsigned)sizeof(ticket.dat)); + 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; } - ticket.length = aticket.length; - memcpy (ticket.dat, aticket.data, ticket.length); - - des_set_key (&key, schedule); - decomp_ticket (&ticket, &flags, pname, pinst, prealm, - &paddress, session, &life, &time_sec, - sname, sinstance, - &key, schedule); - if (strcmp (sname, "krbtgt") != 0 - || strcmp (sinstance, v4_realm) != 0) { - kdc_log(0, "no TGT: %s.%s for %s.%s@%s", + || strcmp (sinstance, config->v4_realm) != 0) { + kdc_log(context, config, 0, "no TGT: %s.%s for %s.%s@%s", sname, sinstance, - pname, pinst, prealm); + 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 > krb_life_to_time(time_sec, life)) { - kdc_log(0, "TGT expired: %s.%s@%s", - pname, pinst, prealm); + 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 */ - des_set_key (&session, schedule); - des_ecb_encrypt (times.data, + memcpy(&session, ad.session.keyvalue.data, sizeof(session)); + DES_set_key (&session, &schedule); + DES_ecb_encrypt (times.data, times.data, - schedule, + &schedule, DES_DECRYPT); memset (&schedule, 0, sizeof(schedule)); + memset (&session, 0, sizeof(session)); /* and extract them */ { - krb5_storage *sp; + krb5_storage *tsp; int32_t tmp; - sp = krb5_storage_from_mem (times.data, times.length); - krb5_ret_int32 (sp, &tmp); + tsp = krb5_storage_from_mem (times.data, times.length); + krb5_ret_int32 (tsp, &tmp); start_time = tmp; - krb5_ret_int32 (sp, &tmp); + krb5_ret_int32 (tsp, &tmp); end_time = tmp; - krb5_storage_free (sp); + krb5_storage_free (tsp); } /* life */ @@ -727,23 +828,28 @@ do_getticket (struct rx_header *hdr, time skew between client and server. Let's make sure it is postive */ if(max_life < 1) max_life = 1; - if (krbtgt_entry->max_life) - max_life = min(max_life, *krbtgt_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); - - create_reply_ticket (hdr, skey, - pname, pinst, prealm, - addr, life, server_entry->kvno, + 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", - &session, reply); - memset (&session, 0, sizeof(session)); + &ad.session, reply); -out: + out: + _krb5_krb_free_auth_data(context, &ad); if (aticket.length) { memset (aticket.data, 0, aticket.length); krb5_data_free (&aticket); @@ -759,28 +865,32 @@ out: if (instance) free (instance); if (krbtgt_entry) - free_ent (krbtgt_entry); + _kdc_free_ent (context, krbtgt_entry); if (server_entry) - free_ent (server_entry); + _kdc_free_ent (context, server_entry); } krb5_error_code -do_kaserver(unsigned char *buf, - size_t len, - krb5_data *reply, - const char *from, - struct sockaddr_in *addr) +_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; - u_int32_t op; + uint32_t op; krb5_storage *sp; if (len < RX_HEADER_SIZE) return -1; sp = krb5_storage_from_mem (buf, len); - decode_rx_header (sp, &hdr); + ret = decode_rx_header (sp, &hdr); + if (ret) + goto out; buf += RX_HEADER_SIZE; len -= RX_HEADER_SIZE; @@ -806,13 +916,16 @@ do_kaserver(unsigned char *buf, goto out; } - krb5_ret_int32(sp, &op); + ret = krb5_ret_uint32(sp, &op); + if (ret) + goto out; switch (op) { case AUTHENTICATE : - do_authenticate (&hdr, sp, addr, reply); + case AUTHENTICATE_V2 : + do_authenticate (context, config, &hdr, sp, addr, from, reply); break; case GETTICKET : - do_getticket (&hdr, sp, addr, reply); + do_getticket (context, config, &hdr, sp, addr, from, reply); break; case AUTHENTICATE_OLD : case CHANGEPASSWORD : @@ -827,7 +940,6 @@ do_kaserver(unsigned char *buf, case DEBUG : case GETPASSWORD : case GETRANDOMKEY : - case AUTHENTICATE_V2 : default : make_error_reply (&hdr, RXGEN_OPCODE, reply); break; diff --git a/crypto/heimdal/kdc/kdc-private.h b/crypto/heimdal/kdc/kdc-private.h new file mode 100644 index 0000000..030be9a --- /dev/null +++ b/crypto/heimdal/kdc/kdc-private.h @@ -0,0 +1,286 @@ +/* This is a generated file */ +#ifndef __kdc_private_h__ +#define __kdc_private_h__ + +#include + +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/crypto/heimdal/kdc/kdc-protos.h b/crypto/heimdal/kdc/kdc-protos.h new file mode 100644 index 0000000..15e8c29 --- /dev/null +++ b/crypto/heimdal/kdc/kdc-protos.h @@ -0,0 +1,92 @@ +/* This is a generated file */ +#ifndef __kdc_protos_h__ +#define __kdc_protos_h__ + +#include + +#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/crypto/heimdal/kdc/kdc-replay.c b/crypto/heimdal/kdc/kdc-replay.c new file mode 100644 index 0000000..966831d --- /dev/null +++ b/crypto/heimdal/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/crypto/heimdal/kdc/kdc.8 b/crypto/heimdal/kdc/kdc.8 index 29cca73..331682f 100644 --- a/crypto/heimdal/kdc/kdc.8 +++ b/crypto/heimdal/kdc/kdc.8 @@ -1,4 +1,4 @@ -.\" Copyright (c) 2003 Kungliga Tekniska Högskolan +.\" Copyright (c) 2003 - 2004 Kungliga Tekniska Högskolan .\" (Royal Institute of Technology, Stockholm, Sweden). .\" All rights reserved. .\" @@ -29,9 +29,9 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $Id: kdc.8,v 1.23.2.1 2003/10/21 20:06:01 lha Exp $ +.\" $Id: kdc.8 18419 2006-10-12 10:05:57Z lha $ .\" -.Dd October 21, 2003 +.Dd August 24, 2006 .Dt KDC 8 .Os HEIMDAL .Sh NAME @@ -39,6 +39,7 @@ .Nd Kerberos 5 server .Sh SYNOPSIS .Nm +.Bk -words .Oo Fl c Ar file \*(Ba Xo .Fl -config-file= Ns Ar file .Xc @@ -59,7 +60,9 @@ .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. @@ -147,6 +150,10 @@ 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 @@ -177,18 +184,41 @@ specified as: 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 FALSE. +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 enforce-transited-policy = Va boolean -Always verify the transited policy, ignoring the -.Va disable-transited-check -flag if set in the KDC client request. +.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. @@ -209,7 +239,6 @@ An example of a config file: [kdc] require-preauth = no v4-realm = FOO.SE - key-file = /key-file .Ed .Sh BUGS If the machine running the KDC has new addresses added to it, the KDC diff --git a/crypto/heimdal/kdc/kdc.h b/crypto/heimdal/kdc/kdc.h new file mode 100644 index 0000000..6c129f3 --- /dev/null +++ b/crypto/heimdal/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 + * + * 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 + +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 + +#endif diff --git a/crypto/heimdal/kdc/kdc_locl.h b/crypto/heimdal/kdc/kdc_locl.h index ed69f54..fe05236 100644 --- a/crypto/heimdal/kdc/kdc_locl.h +++ b/crypto/heimdal/kdc/kdc_locl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -32,94 +32,41 @@ */ /* - * $Id: kdc_locl.h,v 1.58.2.2 2003/10/27 11:07:16 joda Exp $ + * $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" -extern krb5_context context; +typedef struct pk_client_params pk_client_params; +#include -extern int require_preauth; extern sig_atomic_t exit_flag; extern size_t max_request; -extern time_t kdc_warn_pwexpire; -extern struct dbinfo { - char *realm; - char *dbname; - char *mkey_file; - struct dbinfo *next; -} *databases; -extern HDB **db; -extern int num_db; +extern const char *request_log; extern const char *port_str; extern krb5_addresses explicit_addresses; extern int enable_http; -extern krb5_boolean encode_as_rep_as_tgs_rep; -extern krb5_boolean check_ticket_addresses; -extern krb5_boolean allow_null_ticket_addresses; -extern krb5_boolean allow_anonymous; -enum { TRPOLICY_ALWAYS_CHECK, - TRPOLICY_ALLOW_PER_PRINCIPAL, - TRPOLICY_ALWAYS_HONOUR_REQUEST }; -extern int trpolicy; -extern int enable_524; -extern int enable_v4_cross_realm; -#ifdef KRB4 -extern char *v4_realm; -extern int enable_v4; -extern krb5_boolean enable_kaserver; -#endif +#define DETACH_IS_DEFAULT FALSE -#define _PATH_KDC_CONF HDB_DB_DIR "/kdc.conf" -#define DEFAULT_LOG_DEST "0-1/FILE:" HDB_DB_DIR "/kdc.log" +extern int detach_from_console; -extern struct timeval now; -#define kdc_time (now.tv_sec) +extern const struct units _kdc_digestunits[]; -krb5_error_code as_rep (KDC_REQ*, krb5_data*, const char*, struct sockaddr*); -void configure (int, char**); -krb5_error_code db_fetch (krb5_principal, hdb_entry**); -void free_ent(hdb_entry *); -void kdc_log (int, const char*, ...) - __attribute__ ((format (printf, 2,3))); +#define KDC_LOG_FILE "kdc.log" -char* kdc_log_msg (int, const char*, ...) - __attribute__ ((format (printf, 2,3))); -char* kdc_log_msg_va (int, const char*, va_list) - __attribute__ ((format (printf, 2,0))); -void kdc_openlog (void); -void loop (void); -void set_master_key (EncryptionKey); -krb5_error_code tgs_rep (KDC_REQ*, krb5_data*, const char*, struct sockaddr *); -Key* unseal_key (Key*); -krb5_error_code check_flags(hdb_entry *client, const char *client_name, - hdb_entry *server, const char *server_name, - krb5_boolean is_as_req); +extern struct timeval _kdc_now; +#define kdc_time (_kdc_now.tv_sec) -krb5_error_code get_des_key(hdb_entry*, krb5_boolean, krb5_boolean, Key**); -krb5_error_code encode_v4_ticket (void*, size_t, const EncTicketPart*, - const PrincipalName*, size_t*); -krb5_error_code do_524 (const Ticket*, krb5_data*, const char*, struct sockaddr*); +void +loop(krb5_context context, krb5_kdc_configuration *config); -#ifdef KRB4 -krb5_error_code db_fetch4 (const char*, const char*, const char*, hdb_entry**); -krb5_error_code do_version4 (unsigned char*, size_t, krb5_data*, const char*, - struct sockaddr_in*); -int maybe_version4 (unsigned char*, int); -#endif - -#ifdef KRB4 -krb5_error_code do_kaserver (unsigned char*, size_t, krb5_data*, const char*, - struct sockaddr_in*); -#endif - -#ifdef HAVE_OPENSSL -#define des_new_random_key des_random_key -#endif +krb5_kdc_configuration * +configure(krb5_context context, int argc, char **argv); #endif /* __KDC_LOCL_H__ */ diff --git a/crypto/heimdal/kdc/kerberos4.c b/crypto/heimdal/kdc/kerberos4.c index 050db5d..cbba649 100644 --- a/crypto/heimdal/kdc/kerberos4.c +++ b/crypto/heimdal/kdc/kerberos4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan + * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,13 +33,13 @@ #include "kdc_locl.h" -RCSID("$Id: kerberos4.c,v 1.45.2.1 2004/03/30 10:29:27 lha Exp $"); +#include -#ifdef KRB4 +RCSID("$Id: kerberos4.c 21577 2007-07-16 08:14:06Z lha $"); #ifndef swap32 -static u_int32_t -swap32(u_int32_t x) +static uint32_t +swap32(uint32_t x) { return ((x << 24) & 0xff000000) | ((x << 8) & 0xff0000) | @@ -49,66 +49,74 @@ swap32(u_int32_t x) #endif /* swap32 */ int -maybe_version4(unsigned char *buf, int len) +_kdc_maybe_version4(unsigned char *buf, int len) { return len > 0 && *buf == 4; } static void -make_err_reply(krb5_data *reply, int code, const char *msg) +make_err_reply(krb5_context context, krb5_data *reply, + int code, const char *msg) { - KTEXT_ST er; - - /* name, instance and realm are not checked in most (all?) - implementations; msg is also never used, but we send it anyway - (for debugging purposes) */ - - if(msg == NULL) - msg = krb_get_err_text(code); - cr_err_reply(&er, "", "", "", kdc_time, code, (char*)msg); - krb5_data_copy(reply, er.dat, er.length); + _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, krb5_principal princ) +valid_princ(krb5_context context, + void *funcctx, + krb5_principal princ) { + struct valid_princ_ctx *ctx = funcctx; krb5_error_code ret; char *s; - hdb_entry *ent; + hdb_entry_ex *ent; ret = krb5_unparse_name(context, princ, &s); if (ret) return FALSE; - ret = db_fetch(princ, &ent); + ret = _kdc_db_fetch(context, ctx->config, princ, ctx->flags, NULL, &ent); if (ret) { - kdc_log(7, "Lookup %s failed: %s", s, + kdc_log(context, ctx->config, 7, "Lookup %s failed: %s", s, krb5_get_err_text (context, ret)); free(s); return FALSE; } - kdc_log(7, "Lookup %s succeeded", s); + kdc_log(context, ctx->config, 7, "Lookup %s succeeded", s); free(s); - free_ent(ent); + _kdc_free_ent(context, ent); return TRUE; } krb5_error_code -db_fetch4(const char *name, const char *instance, const char *realm, - hdb_entry **ent) +_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_ext(context, name, instance, realm, - valid_princ, 0, &p); + ret = krb5_425_conv_principal_ext2(context, name, instance, realm, + valid_princ, &ctx, 0, &p); if(ret) return ret; - ret = db_fetch(p, ent); + 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(reply, KFAILURE, "Packet too short"); goto L;} +#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' @@ -117,15 +125,17 @@ db_fetch4(const char *name, const char *instance, const char *realm, */ krb5_error_code -do_version4(unsigned char *buf, - size_t len, - krb5_data *reply, - const char *from, - struct sockaddr_in *addr) +_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 *client = NULL, *server = NULL; + hdb_entry_ex *client = NULL, *server = NULL; Key *ckey, *skey; int8_t pvno; int8_t msg_type; @@ -133,67 +143,93 @@ do_version4(unsigned char *buf, char *name = NULL, *inst = NULL, *realm = NULL; char *sname = NULL, *sinst = NULL; int32_t req_time; - time_t max_life, max_end, actual_end, issue_time; - u_int8_t life; + time_t max_life; + uint8_t life; char client_name[256]; char server_name[256]; - if(!enable_v4) { - kdc_log(0, "Rejected version 4 request from %s", from); - make_err_reply(reply, KDC_GEN_ERR, "function not enabled"); + 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(0, "Protocol version mismatch (krb4) (%d)", pvno); - make_err_reply(reply, KDC_PKT_VER, NULL); + 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: + 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_int8(sp, &life), out1); + 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, v4_realm); + "%s.%s@%s", sname, sinst, config->v4_realm); - kdc_log(0, "AS-REQ (krb4) %s from %s for %s", + kdc_log(context, config, 0, "AS-REQ (krb4) %s from %s for %s", client_name, from, server_name); - ret = db_fetch4(name, inst, realm, &client); + ret = _kdc_db_fetch4(context, config, name, inst, realm, + HDB_F_GET_CLIENT, &client); if(ret) { - kdc_log(0, "Client not found in database: %s: %s", + kdc_log(context, config, 0, "Client not found in database: %s: %s", client_name, krb5_get_err_text(context, ret)); - make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, NULL); + make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, + "principal unknown"); goto out1; } - ret = db_fetch4(sname, sinst, v4_realm, &server); + ret = _kdc_db_fetch4(context, config, sname, sinst, config->v4_realm, + HDB_F_GET_SERVER, &server); if(ret){ - kdc_log(0, "Server not found in database: %s: %s", + kdc_log(context, config, 0, "Server not found in database: %s: %s", server_name, krb5_get_err_text(context, ret)); - make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, NULL); + make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, + "principal unknown"); goto out1; } - ret = check_flags (client, client_name, - server, server_name, - TRUE); + ret = _kdc_check_flags (context, config, + client, client_name, + server, server_name, + TRUE); if (ret) { /* good error code? */ - make_err_reply(reply, KERB_ERR_NAME_EXP, NULL); + 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; } @@ -202,21 +238,22 @@ do_version4(unsigned char *buf, * good error code to return if preauthentication is required. */ - if (require_preauth - || client->flags.require_preauth - || server->flags.require_preauth) { - kdc_log(0, + 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(reply, KERB_ERR_NULL_KEY, NULL); + make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, + "preauth required"); goto out1; } - ret = get_des_key(client, FALSE, FALSE, &ckey); + ret = _kdc_get_des_key(context, client, FALSE, FALSE, &ckey); if(ret){ - kdc_log(0, "no suitable DES key for client"); - make_err_reply(reply, KDC_NULL_KEY, + 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; } @@ -225,106 +262,154 @@ do_version4(unsigned char *buf, /* 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, KEYTYPE_DES, &ckey); + ret = hdb_next_keytype2key(context, &client->entry, KEYTYPE_DES, &ckey); if(ret){ - kdc_log(0, "No version-4 salted key in database -- %s.%s@%s", + kdc_log(context, config, 0, "No version-4 salted key in database -- %s.%s@%s", name, inst, realm); - make_err_reply(reply, KDC_NULL_KEY, + make_err_reply(context, reply, KRB4ET_KDC_NULL_KEY, "No version-4 salted key in database"); goto out1; } #endif - ret = get_des_key(server, TRUE, FALSE, &skey); + ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); if(ret){ - kdc_log(0, "no suitable DES key for server"); - /* XXX */ - make_err_reply(reply, KDC_NULL_KEY, + 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 = krb_life_to_time(0, life); - if(client->max_life) - max_life = min(max_life, *client->max_life); - if(server->max_life) - max_life = min(max_life, *server->max_life); + 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); - { - KTEXT_ST cipher, ticket; - KTEXT r; - des_cblock session; - - des_new_random_key(&session); - - krb_create_ticket(&ticket, 0, name, inst, v4_realm, - addr->sin_addr.s_addr, session, life, kdc_time, - sname, sinst, skey->key.keyvalue.data); + 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; + } - create_ciph(&cipher, session, sname, sinst, v4_realm, - life, server->kvno % 256, &ticket, kdc_time, - ckey->key.keyvalue.data); - memset(&session, 0, sizeof(session)); - r = create_auth_reply(name, inst, realm, req_time, 0, - client->pw_end ? *client->pw_end : 0, - client->kvno % 256, &cipher); - krb5_data_copy(reply, r->dat, r->length); - memset(&cipher, 0, sizeof(cipher)); - memset(&ticket, 0, sizeof(ticket)); + 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; - KTEXT_ST auth; - AUTH_DAT ad; + krb5_data auth; + int32_t address; size_t pos; krb5_principal tgt_princ = NULL; - hdb_entry *tgt = 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, v4_realm, + ret = krb5_425_conv_principal(context, "krbtgt", realm, + config->v4_realm, &tgt_princ); if(ret){ - kdc_log(0, "Converting krbtgt principal (krb4): %s", + kdc_log(context, config, 0, + "Converting krbtgt principal (krb4): %s", krb5_get_err_text(context, ret)); - make_err_reply(reply, KFAILURE, + make_err_reply(context, reply, KFAILURE, "Failed to convert v4 principal (krbtgt)"); goto out2; } - ret = db_fetch(tgt_princ, &tgt); + ret = _kdc_db_fetch(context, config, tgt_princ, + HDB_F_GET_KRBTGT, NULL, &tgt); if(ret){ char *s; - s = kdc_log_msg(0, "Ticket-granting ticket not " + s = kdc_log_msg(context, config, 0, "Ticket-granting ticket not " "found in database (krb4): krbtgt.%s@%s: %s", - realm, v4_realm, + realm, config->v4_realm, krb5_get_err_text(context, ret)); - make_err_reply(reply, KFAILURE, s); + make_err_reply(context, reply, KFAILURE, s); free(s); goto out2; } - if(tgt->kvno % 256 != kvno){ - kdc_log(0, "tgs-req (krb4) with old kvno %d (current %d) for " - "krbtgt.%s@%s", kvno, tgt->kvno % 256, realm, v4_realm); - make_err_reply(reply, KDC_AUTH_EXP, + 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 = get_des_key(tgt, TRUE, FALSE, &tkey); + ret = _kdc_get_des_key(context, tgt, TRUE, FALSE, &tkey); if(ret){ - kdc_log(0, "no suitable DES key for krbtgt (krb4)"); - /* XXX */ - make_err_reply(reply, KDC_NULL_KEY, + 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; } @@ -334,107 +419,130 @@ do_version4(unsigned char *buf, pos = krb5_storage_seek(sp, ticket_len + req_len, SEEK_CUR); - memset(&auth, 0, sizeof(auth)); - memcpy(&auth.dat, buf, pos); + auth.data = buf; auth.length = pos; - krb_set_key(tkey->key.keyvalue.data, 0); - krb_ignore_ip_address = !check_ticket_addresses; + if (config->check_ticket_addresses) + address = addr->sin_addr.s_addr; + else + address = 0; - ret = krb_rd_req(&auth, "krbtgt", realm, - addr->sin_addr.s_addr, &ad, 0); + ret = _krb5_krb_rd_req(context, &auth, "krbtgt", realm, + config->v4_realm, + address, &tkey->key, &ad); if(ret){ - kdc_log(0, "krb_rd_req: %s", krb_get_err_text(ret)); - make_err_reply(reply, ret, NULL); + 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_int8(sp, &life), out2); + 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, v4_realm); + sname, sinst, config->v4_realm); + snprintf (client_name, sizeof(client_name), + "%s.%s@%s", + ad.pname, ad.pinst, ad.prealm); - kdc_log(0, "TGS-REQ (krb4) %s.%s@%s from %s for %s", - ad.pname, ad.pinst, ad.prealm, from, server_name); + 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(0, "Can't hop realms (krb4) %s -> %s", realm, ad.prealm); - make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, + 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 (!enable_v4_cross_realm && strcmp(realm, v4_realm) != 0) { - kdc_log(0, "krb4 Cross-realm %s -> %s disabled", realm, v4_realm); - make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, + 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(0, "Bad request for changepw ticket (krb4)"); - make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, + 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; } -#if 0 - ret = db_fetch4(ad.pname, ad.pinst, ad.prealm, &client); - if(ret){ + 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(0, "Client not found in database: (krb4) " - "%s.%s@%s: %s", - ad.pname, ad.pinst, ad.prealm, - krb5_get_err_text(context, ret)); - make_err_reply(reply, KERB_ERR_PRINCIPAL_UNKNOWN, 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; } -#endif - - ret = db_fetch4(sname, sinst, v4_realm, &server); + 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(0, "Server not found in database (krb4): %s: %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(reply, KERB_ERR_PRINCIPAL_UNKNOWN, s); + make_err_reply(context, reply, KRB4ET_KDC_PR_UNKNOWN, s); free(s); goto out2; } - ret = check_flags (NULL, NULL, - server, server_name, - FALSE); + ret = _kdc_check_flags (context, config, + client, client_name, + server, server_name, + FALSE); if (ret) { - /* good error code? */ - make_err_reply(reply, KERB_ERR_NAME_EXP, NULL); + make_err_reply(context, reply, KRB4ET_KDC_NAME_EXP, + "operation not allowed"); goto out2; } - ret = get_des_key(server, TRUE, FALSE, &skey); + ret = _kdc_get_des_key(context, server, TRUE, FALSE, &skey); if(ret){ - kdc_log(0, "no suitable DES key for server (krb4)"); - /* XXX */ - make_err_reply(reply, KDC_NULL_KEY, + 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 = krb_life_to_time(ad.time_sec, ad.life); - max_end = min(max_end, krb_life_to_time(kdc_time, life)); + 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 = krb_life_to_time(issue_time, life); + 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 = krb_life_to_time(issue_time, 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 */ @@ -442,46 +550,88 @@ do_version4(unsigned char *buf, } { - KTEXT_ST cipher, ticket; - KTEXT r; - des_cblock session; - des_new_random_key(&session); - - krb_create_ticket(&ticket, 0, ad.pname, ad.pinst, ad.prealm, - addr->sin_addr.s_addr, &session, life, - issue_time, - sname, sinst, skey->key.keyvalue.data); - - create_ciph(&cipher, session, sname, sinst, v4_realm, - life, server->kvno % 256, &ticket, - issue_time, &ad.session); + 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; + } - memset(&session, 0, sizeof(session)); - memset(ad.session, 0, sizeof(ad.session)); - - r = create_auth_reply(ad.pname, ad.pinst, ad.prealm, - req_time, 0, 0, 0, &cipher); - krb5_data_copy(reply, r->dat, r->length); - memset(&cipher, 0, sizeof(cipher)); - memset(&ticket, 0, sizeof(ticket)); + 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) - free_ent(tgt); + _kdc_free_ent(context, tgt); break; } - case AUTH_MSG_ERR_REPLY: break; default: - kdc_log(0, "Unknown message type (krb4): %d from %s", + kdc_log(context, config, 0, "Unknown message type (krb4): %d from %s", msg_type, from); - make_err_reply(reply, KFAILURE, "Unknown message type"); + make_err_reply(context, reply, KFAILURE, "Unknown message type"); } -out: + out: if(name) free(name); if(inst) @@ -493,22 +643,18 @@ out: if(sinst) free(sinst); if(client) - free_ent(client); + _kdc_free_ent(context, client); if(server) - free_ent(server); + _kdc_free_ent(context, server); krb5_storage_free(sp); return 0; } -#else /* KRB4 */ - -#include - -#endif /* KRB4 */ - krb5_error_code -encode_v4_ticket(void *buf, size_t len, const EncTicketPart *et, - const PrincipalName *service, size_t *size) +_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; @@ -517,9 +663,10 @@ encode_v4_ticket(void *buf, size_t len, const EncTicketPart *et, { krb5_principal princ; - principalname2krb5_principal(&princ, - *service, - et->crealm); + _krb5_principalname2krb5_principal(context, + &princ, + *service, + et->crealm); ret = krb5_524_conv_principal(context, princ, sname, @@ -529,9 +676,10 @@ encode_v4_ticket(void *buf, size_t len, const EncTicketPart *et, if(ret) return ret; - principalname2krb5_principal(&princ, - et->cname, - et->crealm); + _krb5_principalname2krb5_principal(context, + &princ, + et->cname, + et->crealm); ret = krb5_524_conv_principal(context, princ, @@ -594,8 +742,9 @@ encode_v4_ticket(void *buf, size_t len, const EncTicketPart *et, } krb5_error_code -get_des_key(hdb_entry *principal, krb5_boolean is_server, - krb5_boolean prefer_afs_key, Key **ret_key) +_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; @@ -609,7 +758,7 @@ get_des_key(hdb_entry *principal, krb5_boolean is_server, afs_key == NULL || server_key == NULL); ++i) { Key *key = NULL; - while(hdb_next_enctype2key(context, principal, etypes[i], &key) == 0) { + while(hdb_next_enctype2key(context, &principal->entry, etypes[i], &key) == 0) { if(key->salt == NULL) { if(v5_key == NULL) v5_key = key; @@ -635,7 +784,7 @@ get_des_key(hdb_entry *principal, krb5_boolean is_server, else if(is_server && server_key) *ret_key = server_key; else - return KERB_ERR_NULL_KEY; + return KRB4ET_KDC_NULL_KEY; } else { if(v4_key) *ret_key = v4_key; @@ -646,11 +795,11 @@ get_des_key(hdb_entry *principal, krb5_boolean is_server, else if(is_server && server_key) *ret_key = server_key; else - return KERB_ERR_NULL_KEY; + return KRB4ET_KDC_NULL_KEY; } if((*ret_key)->key.keyvalue.length == 0) - return KERB_ERR_NULL_KEY; + return KRB4ET_KDC_NULL_KEY; return 0; } diff --git a/crypto/heimdal/kdc/kerberos5.c b/crypto/heimdal/kdc/kerberos5.c index f2736fd..9582cd8 100644 --- a/crypto/heimdal/kdc/kerberos5.c +++ b/crypto/heimdal/kdc/kerberos5.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2003 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,12 +33,12 @@ #include "kdc_locl.h" -RCSID("$Id: kerberos5.c,v 1.145.2.4 2004/08/13 19:28:26 lha Exp $"); +RCSID("$Id: kerberos5.c 22071 2007-11-14 20:04:50Z lha $"); #define MAX_TIME ((time_t)((1U << 31) - 1)) -static void -fix_time(time_t **t) +void +_kdc_fix_time(time_t **t) { if(*t == NULL){ ALLOC(*t); @@ -47,22 +47,35 @@ fix_time(time_t **t) 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 **m, Salt *salt) +set_salt_padata (METHOD_DATA *md, Salt *salt) { if (salt) { - ALLOC(*m); - (*m)->len = 1; - ALLOC((*m)->val); - (*m)->val->padata_type = salt->type; - copy_octet_string(&salt->salt, - &(*m)->val->padata_value); + 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); } } -static PA_DATA* -find_padata(KDC_REQ *req, int *start, int type) +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) @@ -72,22 +85,45 @@ find_padata(KDC_REQ *req, int *start, int type) } /* + * 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 */ -static krb5_error_code -find_etype(hdb_entry *princ, krb5_enctype *etypes, unsigned len, - Key **ret_key, krb5_enctype *ret_etype) +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; - while (hdb_next_enctype2key(context, princ, etypes[i], &key) == 0) { + 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; @@ -95,47 +131,18 @@ find_etype(hdb_entry *princ, krb5_enctype *etypes, unsigned len, *ret_key = key; *ret_etype = etypes[i]; ret = 0; - if (key->salt == NULL) + if (is_default_salt_p(&def_salt, key)) { + krb5_free_salt (context, def_salt); return ret; + } } } + krb5_free_salt (context, def_salt); return ret; } -static krb5_error_code -find_keys(hdb_entry *client, - hdb_entry *server, - Key **ckey, - krb5_enctype *cetype, - Key **skey, - krb5_enctype *setype, - krb5_enctype *etypes, - unsigned num_etypes) -{ - krb5_error_code ret; - - if(client){ - /* find client key */ - ret = find_etype(client, etypes, num_etypes, ckey, cetype); - if (ret) { - kdc_log(0, "Client has no support for etypes"); - return ret; - } - } - - if(server){ - /* find server key */ - ret = find_etype(server, etypes, num_etypes, skey, setype); - if (ret) { - kdc_log(0, "Server has no support for etypes"); - return ret; - } - } - return 0; -} - -static krb5_error_code -make_anonymous_principalname (PrincipalName *pn) +krb5_error_code +_kdc_make_anonymous_principalname (PrincipalName *pn) { pn->name_type = KRB5_NT_PRINCIPAL; pn->name_string.len = 1; @@ -151,13 +158,92 @@ make_anonymous_principalname (PrincipalName *pn) return 0; } -static krb5_error_code -encode_reply(KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek, - krb5_enctype etype, - int skvno, EncryptionKey *skey, - int ckvno, EncryptionKey *ckey, - const char **e_text, - krb5_data *reply) +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; @@ -167,13 +253,13 @@ encode_reply(KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek, ASN1_MALLOC_ENCODE(EncTicketPart, buf, buf_size, et, &len, ret); if(ret) { - kdc_log(0, "Failed to encode ticket: %s", + 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(0, "Internal error in ASN.1 encoder"); + kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } @@ -181,7 +267,7 @@ encode_reply(KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek, ret = krb5_crypto_init(context, skey, etype, &crypto); if (ret) { free(buf); - kdc_log(0, "krb5_crypto_init failed: %s", + kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); return ret; } @@ -196,30 +282,30 @@ encode_reply(KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek, free(buf); krb5_crypto_destroy(context, crypto); if(ret) { - kdc_log(0, "Failed to encrypt data: %s", + 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 && !encode_as_rep_as_tgs_rep) + 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(0, "Failed to encode KDC-REP: %s", + 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(0, "Internal error in ASN.1 encoder"); + 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(0, "krb5_crypto_init failed: %s", + kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); return ret; } @@ -246,13 +332,13 @@ encode_reply(KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek, } krb5_crypto_destroy(context, crypto); if(ret) { - kdc_log(0, "Failed to encode KDC-REP: %s", + 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(0, "Internal error in ASN.1 encoder"); + kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); *e_text = "KDC internal error"; return KRB5KRB_ERR_GENERIC; } @@ -261,31 +347,64 @@ encode_reply(KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek, 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 -realloc_method_data(METHOD_DATA *md) +only_older_enctype_p(const KDC_REQ *req) { - 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; + 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(ETYPE_INFO_ENTRY *ent, Key *key) +make_etype_info_entry(krb5_context context, ETYPE_INFO_ENTRY *ent, Key *key) { ent->etype = key->key.keytype; if(key->salt){ - ALLOC(ent->salttype); #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(0, "unknown salt-type: %d", + kdc_log(context, config, 0, "unknown salt-type: %d", key->salt->type); return KRB5KRB_ERR_GENERIC; } @@ -294,8 +413,17 @@ make_etype_info_entry(ETYPE_INFO_ENTRY *ent, Key *key) *know* what cell you are using (e.g by assuming that the cell is the same as the realm in lower case) */ -#else +#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); @@ -312,7 +440,9 @@ make_etype_info_entry(ETYPE_INFO_ENTRY *ent, Key *key) } static krb5_error_code -get_pa_etype_info(METHOD_DATA *md, hdb_entry *client, +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; @@ -329,41 +459,55 @@ get_pa_etype_info(METHOD_DATA *md, hdb_entry *client, pa.val = malloc(pa.len * sizeof(*pa.val)); if(pa.val == NULL) return ENOMEM; + memset(pa.val, 0, pa.len * sizeof(*pa.val)); - for(j = 0; j < etypes_len; j++) { - for (i = 0; i < n; i++) - if (pa.val[i].etype == etypes[j]) + 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(i = 0; i < client->keys.len; i++) { - if(client->keys.val[i].key.keytype == etypes[j]) - if((ret = make_etype_info_entry(&pa.val[n++], + 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((ret = make_etype_info_entry(&pa.val[n++], + 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:; + skip2:; } - if(n != pa.len) { - char *name; - krb5_unparse_name(context, client->principal, &name); - kdc_log(0, "internal error in get_pa_etype_info(%s): %d != %d", - name, n, pa.len); - free(name); - pa.len = n; + 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); @@ -382,77 +526,335 @@ get_pa_etype_info(METHOD_DATA *md, hdb_entry *client, } /* + * + */ + +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 -check_flags(hdb_entry *client, const char *client_name, - hdb_entry *server, const char *server_name, - krb5_boolean is_as_req) +_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 != NULL) { + if(client_ex != NULL) { + hdb_entry *client = &client_ex->entry; + /* check client */ if (client->flags.invalid) { - kdc_log(0, "Client (%s) has invalid bit set", client_name); + kdc_log(context, config, 0, + "Client (%s) has invalid bit set", client_name); return KRB5KDC_ERR_POLICY; } if(!client->flags.client){ - kdc_log(0, "Principal may not act as client -- %s", - client_name); + 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) { - kdc_log(0, "Client not yet valid -- %s", client_name); + 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) { - kdc_log(0, "Client expired -- %s", client_name); + 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->flags.change_pw) { - kdc_log(0, "Client's key has expired -- %s", client_name); + 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 != NULL) { + if (server_ex != NULL) { + hdb_entry *server = &server_ex->entry; + if (server->flags.invalid) { - kdc_log(0, "Server has invalid flag set -- %s", server_name); + kdc_log(context, config, 0, + "Server has invalid flag set -- %s", server_name); return KRB5KDC_ERR_POLICY; } if(!server->flags.server){ - kdc_log(0, "Principal may not act as server -- %s", - server_name); + 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(0, "AS-REQ is required for server -- %s", server_name); + 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) { - kdc_log(0, "Server not yet valid -- %s", server_name); + 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) { - kdc_log(0, "Server expired -- %s", server_name); + 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) { - kdc_log(0, "Server's key has expired -- %s", server_name); + 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; } } @@ -465,19 +867,38 @@ check_flags(hdb_entry *client, const char *client_name, * these checks */ -static krb5_boolean -check_addresses(HostAddresses *addresses, const struct sockaddr *from) +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(check_ticket_addresses == 0) + if(config->check_ticket_addresses == 0) return TRUE; if(addresses == NULL) - return allow_null_ticket_addresses; + 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; @@ -487,17 +908,55 @@ check_addresses(HostAddresses *addresses, const struct sockaddr *from) 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 -as_rep(KDC_REQ *req, - krb5_data *reply, - const char *from, - struct sockaddr *from_addr) +_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 *client = NULL, *server = NULL; - krb5_enctype cetype, setype; + 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; @@ -506,18 +965,32 @@ as_rep(KDC_REQ *req, 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{ - principalname2krb5_principal (&server_princ, *(b->sname), b->realm); - krb5_unparse_name(context, server_princ, &server_name); + 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(0, "AS-REQ malformed server name from %s", from); + kdc_log(context, config, 0, + "AS-REQ malformed server name from %s", from); goto out; } @@ -525,33 +998,66 @@ as_rep(KDC_REQ *req, ret = KRB5KRB_ERR_GENERIC; e_text = "No client in request"; } else { - principalname2krb5_principal (&client_princ, *(b->cname), b->realm); - krb5_unparse_name(context, client_princ, &client_name); - } - if (ret) { - kdc_log(0, "AS-REQ malformed client name from %s", from); - goto out; - } - - kdc_log(0, "AS-REQ %s from %s for %s", client_name, from, server_name); - ret = db_fetch(client_princ, &client); + 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(0, "UNKNOWN -- %s: %s", client_name, + 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 = db_fetch(server_princ, &server); + ret = _kdc_db_fetch(context, config, server_princ, + HDB_F_GET_SERVER|HDB_F_GET_KRBTGT, + NULL, &server); if(ret){ - kdc_log(0, "UNKNOWN -- %s: %s", server_name, + 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 = check_flags(client, client_name, server, server_name, TRUE); + 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; @@ -559,17 +1065,77 @@ as_rep(KDC_REQ *req, memset(&ek, 0, sizeof(ek)); if(req->padata){ - int i = 0; - PA_DATA *pa; + int i; + const PA_DATA *pa; int found_pa = 0; - kdc_log(5, "Looking for pa-data -- %s", client_name); - while((pa = find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){ + + 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; - time_t patime; size_t len; EncryptedData enc_data; Key *pa_key; + char *str; found_pa = 1; @@ -579,23 +1145,26 @@ as_rep(KDC_REQ *req, &len); if (ret) { ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; - kdc_log(5, "Failed to decode PA-DATA -- %s", + kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", client_name); goto out; } - ret = hdb_enctype2key(context, client, enc_data.etype, &pa_key); + 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_PREAUTH_FAILED; + ret = KRB5KDC_ERR_ETYPE_NOSUPP; if(krb5_enctype_to_string(context, enc_data.etype, &estr)) estr = NULL; if(estr == NULL) - kdc_log(5, "No client key matching pa-data (%d) -- %s", + kdc_log(context, config, 5, + "No client key matching pa-data (%d) -- %s", enc_data.etype, client_name); else - kdc_log(5, "No client key matching pa-data (%s) -- %s", + kdc_log(context, config, 5, + "No client key matching pa-data (%s) -- %s", estr, client_name); free(estr); @@ -603,10 +1172,10 @@ as_rep(KDC_REQ *req, continue; } - try_next_key: + try_next_key: ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto); if (ret) { - kdc_log(0, "krb5_crypto_init failed: %s", + kdc_log(context, config, 0, "krb5_crypto_init failed: %s", krb5_get_err_text(context, ret)); free_EncryptedData(&enc_data); continue; @@ -619,14 +1188,26 @@ as_rep(KDC_REQ *req, &ts_data); krb5_crypto_destroy(context, crypto); if(ret){ - if(hdb_next_enctype2key(context, client, + 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; - free_EncryptedData(&enc_data); e_text = "Failed to decrypt PA-DATA"; - kdc_log (5, "Failed to decrypt PA-DATA -- %s", - client_name); - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + + free_EncryptedData(&enc_data); + ret = KRB5KDC_ERR_PREAUTH_FAILED; continue; } free_EncryptedData(&enc_data); @@ -637,42 +1218,75 @@ as_rep(KDC_REQ *req, krb5_data_free(&ts_data); if(ret){ e_text = "Failed to decode PA-ENC-TS-ENC"; - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; - kdc_log (5, "Failed to decode PA-ENC-TS_ENC -- %s", - client_name); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + kdc_log(context, config, + 5, "Failed to decode PA-ENC-TS_ENC -- %s", + client_name); continue; } - patime = p.patimestamp; free_PA_ENC_TS_ENC(&p); if (abs(kdc_time - p.patimestamp) > context->max_skew) { - ret = KRB5KDC_ERR_PREAUTH_FAILED; + 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"; - kdc_log(0, "Too large time skew -- %s", client_name); +#endif goto out; } et.flags.pre_authent = 1; - kdc_log(2, "Pre-authentication succeded -- %s", client_name); + + 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; } - if(found_pa == 0 && require_preauth) +#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(0, "%s -- %s", e_text, client_name); + kdc_log(context, config, 0, "%s -- %s", e_text, client_name); e_text = NULL; goto out; } - }else if (require_preauth - || client->flags.require_preauth - || server->flags.require_preauth) { + }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; - krb5_data foo_data; - use_pa: + use_pa: method_data.len = 0; method_data.val = NULL; @@ -682,113 +1296,196 @@ as_rep(KDC_REQ *req, pa->padata_value.length = 0; pa->padata_value.data = NULL; - ret = get_pa_etype_info(&method_data, client, - b->etype.val, b->etype.len); /* XXX check ret */ +#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); - foo_data.data = buf; - foo_data.length = len; - + + 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; - krb5_mk_error(context, - ret, - "Need to use PA-ENC-TIMESTAMP", - &foo_data, - client_princ, - server_princ, - NULL, - NULL, - reply); - free(buf); - kdc_log(0, "No PA-ENC-TIMESTAMP -- %s", client_name); - ret = 0; - goto out2; + + kdc_log(context, config, 0, + "No preauth found, returning PREAUTH-REQUIRED -- %s", + client_name); + goto out; } - ret = find_keys(client, server, &ckey, &cetype, &skey, &setype, - b->etype.val, b->etype.len); - if(ret) { - kdc_log(0, "Server/client has no support for etypes"); + /* + * 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. + */ { - char *cet; - char *set; + const krb5_enctype *p; + krb5_enctype clientbest = ETYPE_NULL; + int i, j; - ret = krb5_enctype_to_string(context, cetype, &cet); - if(ret == 0) { - ret = krb5_enctype_to_string(context, setype, &set); - if (ret == 0) { - kdc_log(5, "Using %s/%s", cet, set); - free(set); + 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]; } - free(cet); } - if (ret != 0) - kdc_log(5, "Using e-types %d/%d", cetype, setype); - } - - { - char str[128]; - unparse_flags(KDCOptions2int(f), KDCOptions_units, str, sizeof(str)); - if(*str) - kdc_log(2, "Requested flags: %s", str); + /* 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 && !allow_anonymous)) { + || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; - kdc_log(0, "Bad KDC options -- %s", client_name); + kdc_log(context, config, 0, "Bad KDC options -- %s", client_name); goto out; } rep.pvno = 5; rep.msg_type = krb_as_rep; - copy_Realm(&b->realm, &rep.crealm); + copy_Realm(&client->entry.principal->realm, &rep.crealm); if (f.request_anonymous) - make_anonymous_principalname (&rep.cname); + _kdc_make_anonymous_principalname (&rep.cname); else - copy_PrincipalName(b->cname, &rep.cname); + _krb5_principal2principalname(&rep.cname, + client->entry.principal); rep.ticket.tkt_vno = 5; - copy_Realm(&b->realm, &rep.ticket.realm); - copy_PrincipalName(b->sname, &rep.ticket.sname); + 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->flags.forwardable && server->flags.forwardable) + if(client->entry.flags.forwardable && server->entry.flags.forwardable) et.flags.forwardable = f.forwardable; else if (f.forwardable) { ret = KRB5KDC_ERR_POLICY; - kdc_log(0, "Ticket may not be forwardable -- %s", client_name); + kdc_log(context, config, 0, + "Ticket may not be forwardable -- %s", client_name); goto out; } - if(client->flags.proxiable && server->flags.proxiable) + if(client->entry.flags.proxiable && server->entry.flags.proxiable) et.flags.proxiable = f.proxiable; else if (f.proxiable) { ret = KRB5KDC_ERR_POLICY; - kdc_log(0, "Ticket may not be proxiable -- %s", client_name); + kdc_log(context, config, 0, + "Ticket may not be proxiable -- %s", client_name); goto out; } - if(client->flags.postdate && server->flags.postdate) + 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(0, "Ticket may not be postdatable -- %s", client_name); + kdc_log(context, config, 0, + "Ticket may not be postdatable -- %s", client_name); goto out; } /* check for valid set of addresses */ - if(!check_addresses(b->addresses, from_addr)) { + if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) { ret = KRB5KRB_AP_ERR_BADADDR; - kdc_log(0, "Bad address list requested -- %s", client_name); + kdc_log(context, config, 0, + "Bad address list requested -- %s", client_name); goto out; } - krb5_generate_random_keyblock(context, setype, &et.key); + ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); + if (ret) + goto out; copy_PrincipalName(&rep.cname, &et.cname); - copy_Realm(&b->realm, &et.crealm); + copy_Realm(&rep.crealm, &et.crealm); { time_t start; @@ -802,15 +1499,15 @@ as_rep(KDC_REQ *req, et.flags.invalid = 1; et.flags.postdated = 1; /* XXX ??? */ } - fix_time(&b->till); + _kdc_fix_time(&b->till); t = *b->till; /* be careful not overflowing */ - if(client->max_life) - t = start + min(t - start, *client->max_life); - if(server->max_life) - t = start + min(t - start, *server->max_life); + 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 @@ -828,10 +1525,10 @@ as_rep(KDC_REQ *req, t = *b->rtime; if(t == 0) t = MAX_TIME; - if(client->max_renew) - t = start + min(t - start, *client->max_renew); - if(server->max_renew) - t = start + min(t - start, *server->max_renew); + 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 @@ -864,17 +1561,21 @@ as_rep(KDC_REQ *req, * 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->pw_end - && (kdc_warn_pwexpire == 0 - || kdc_time + kdc_warn_pwexpire <= *client->pw_end)) { + 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->pw_end; + ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end; ++ek.last_req.len; } - if (client->valid_end) { + 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->valid_end; + ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end; ++ek.last_req.len; } if (ek.last_req.len == 0) { @@ -883,15 +1584,16 @@ as_rep(KDC_REQ *req, ++ek.last_req.len; } ek.nonce = b->nonce; - if (client->valid_end || client->pw_end) { + if (client->entry.valid_end || client->entry.pw_end) { ALLOC(ek.key_expiration); - if (client->valid_end) { - if (client->pw_end) - *ek.key_expiration = min(*client->valid_end, *client->pw_end); + 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->valid_end; + *ek.key_expiration = *client->entry.valid_end; } else - *ek.key_expiration = *client->pw_end; + *ek.key_expiration = *client->entry.pw_end; } else ek.key_expiration = NULL; ek.flags = et.flags; @@ -912,1004 +1614,239 @@ as_rep(KDC_REQ *req, copy_HostAddresses(et.caddr, ek.caddr); } - set_salt_padata (&rep.padata, ckey->salt); - ret = encode_reply(&rep, &et, &ek, setype, server->kvno, &skey->key, - client->kvno, &ckey->key, &e_text, reply); - free_EncTicketPart(&et); - free_EncKDCRepPart(&ek); - out: - free_AS_REP(&rep); - if(ret){ - krb5_mk_error(context, - ret, - e_text, - NULL, - client_princ, - server_princ, - NULL, - NULL, - reply); - ret = 0; - } - out2: - 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) - free_ent(client); - if(server) - free_ent(server); - return ret; -} - - -static krb5_error_code -check_tgs_flags(KDC_REQ_BODY *b, EncTicketPart *tgt, EncTicketPart *et) -{ - KDCOptions f = b->kdc_options; - - if(f.validate){ - if(!tgt->flags.invalid || tgt->starttime == NULL){ - kdc_log(0, "Bad request to validate ticket"); - return KRB5KDC_ERR_BADOPTION; - } - if(*tgt->starttime > kdc_time){ - kdc_log(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(0, "Ticket-granting ticket has INVALID flag set"); - return KRB5KRB_AP_ERR_TKT_INVALID; - } - - if(f.forwardable){ - if(!tgt->flags.forwardable){ - kdc_log(0, "Bad request for forwardable ticket"); - return KRB5KDC_ERR_BADOPTION; - } - et->flags.forwardable = 1; - } - if(f.forwarded){ - if(!tgt->flags.forwardable){ - kdc_log(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(0, "Bad request for proxiable ticket"); - return KRB5KDC_ERR_BADOPTION; - } - et->flags.proxiable = 1; - } - if(f.proxy){ - if(!tgt->flags.proxiable){ - kdc_log(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(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(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(0, "Ticket cannot be postdated"); - return KRB5KDC_ERR_CANNOT_POSTDATE; - } - - if(f.renewable){ - if(!tgt->flags.renewable){ - kdc_log(0, "Bad request for renewable ticket"); - return KRB5KDC_ERR_BADOPTION; - } - et->flags.renewable = 1; - ALLOC(et->renew_till); - 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(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); - } - - /* checks for excess flags */ - if(f.request_anonymous && !allow_anonymous){ - kdc_log(0, "Request for anonymous ticket"); - return KRB5KDC_ERR_BADOPTION; - } - return 0; -} - -static krb5_error_code -fix_transited_encoding(krb5_boolean check_policy, - 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; - - if(tr->tr_type != DOMAIN_X500_COMPRESS) { - kdc_log(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(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(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(KDC_REQ_BODY *b, - EncTicketPart *tgt, - EncTicketPart *adtkt, - AuthorizationData *auth_data, - hdb_entry *server, - hdb_entry *client, - krb5_principal client_principal, - hdb_entry *krbtgt, - krb5_enctype cetype, - const char **e_text, - krb5_data *reply) -{ - KDC_REP rep; - EncKDCRepPart ek; - EncTicketPart et; - KDCOptions f = b->kdc_options; - krb5_error_code ret; - krb5_enctype etype; - Key *skey; - EncryptionKey *ekey; - - if(adtkt) { - int i; - krb5_keytype kt; - ekey = &adtkt->key; - for(i = 0; i < b->etype.len; i++){ - ret = krb5_enctype_to_keytype(context, b->etype.val[i], &kt); - if(ret) - continue; - if(adtkt->key.keytype == kt) - break; - } - if(i == b->etype.len) - return KRB5KDC_ERR_ETYPE_NOSUPP; - etype = b->etype.val[i]; - }else{ - ret = find_keys(NULL, server, NULL, NULL, &skey, &etype, - b->etype.val, b->etype.len); - if(ret) { - kdc_log(0, "Server has no support for etypes"); - return ret; - } - ekey = &skey->key; - } - - 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; - fix_time(&b->till); - et.endtime = min(tgt->endtime, *b->till); - ALLOC(et.starttime); - *et.starttime = kdc_time; - - ret = check_tgs_flags(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 (trpolicy == TRPOLICY_ALWAYS_CHECK) -#define GLOBAL_ALLOW_PER_PRINCIPAL (trpolicy == TRPOLICY_ALLOW_PER_PRINCIPAL) -#define GLOBAL_ALLOW_DISABLE_TRANSITED_CHECK (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(!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->principal), - *krb5_princ_realm(context, krbtgt->principal)); - if(ret) - goto out; - - copy_Realm(krb5_princ_realm(context, server->principal), - &rep.ticket.realm); - krb5_principal2principalname(&rep.ticket.sname, server->principal); - copy_Realm(&tgt->crealm, &rep.crealm); - if (f.request_anonymous) - make_anonymous_principalname (&tgt->cname); - else - copy_PrincipalName(&tgt->cname, &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->max_life) - life = min(life, *client->max_life); - if(server->max_life) - life = min(life, *server->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->max_renew) - renew = min(renew, *client->max_renew); - if(server->max_renew) - renew = min(renew, *server->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; - - /* XXX Check enc-authorization-data */ - et.authorization_data = auth_data; - - krb5_generate_random_keyblock(context, etype, &et.key); - et.crealm = tgt->crealm; - et.cname = tgt->cname; - - 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)); - 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; - - /* 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 = encode_reply(&rep, &et, &ek, etype, adtkt ? 0 : server->kvno, ekey, - 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); - free_LastReq(&ek.last_req); - memset(et.key.keyvalue.data, 0, et.key.keyvalue.length); - free_EncryptionKey(&et.key); - return ret; -} + ALLOC(rep.padata); + rep.padata->len = 0; + rep.padata->val = NULL; -static krb5_error_code -tgs_check_authenticator(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(0, "No authenticator in request"); - ret = KRB5KRB_AP_ERR_INAPP_CKSUM; - goto out; + 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; } - /* - * 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(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(0, "Failed to encode KDC-REQ-BODY: %s", - krb5_get_err_text(context, ret)); - goto out; - } - if(buf_size != len) { - free(buf); - kdc_log(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(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(0, "Failed to verify checksum: %s", - krb5_get_err_text(context, ret)); - } -out: - free_Authenticator(auth); - free(auth); - return ret; -} - -/* - * 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; -} -static Realm -find_rpath(Realm crealm, Realm srealm) -{ - const char *new_realm = krb5_config_get_string(context, - NULL, - "capaths", - crealm, - srealm, - NULL); - return (Realm)new_realm; -} - + set_salt_padata (rep.padata, ckey->salt); -static krb5_boolean -need_referral(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; -} + /* Add signing of alias referral */ + if (f.canonicalize) { + PA_ClientCanonicalized canon; + krb5_data data; + PA_DATA pa; + krb5_crypto crypto; + size_t len; -static krb5_error_code -tgs_rep2(KDC_REQ_BODY *b, - PA_DATA *tgs_req, - krb5_data *reply, - const char *from, - const struct sockaddr *from_addr, - time_t **csec, - int **cusec) -{ - krb5_ap_req ap_req; - krb5_error_code ret; - krb5_principal princ; - krb5_auth_context ac = NULL; - krb5_ticket *ticket = NULL; - krb5_flags ap_req_options; - krb5_flags verify_ap_req_flags; - const char *e_text = NULL; - krb5_crypto crypto; + memset(&canon, 0, sizeof(canon)); - hdb_entry *krbtgt = NULL; - EncTicketPart *tgt; - Key *tkey; - krb5_enctype cetype; - krb5_principal cp = NULL; - krb5_principal sp = NULL; - AuthorizationData *auth_data = NULL; + canon.names.requested_name = *b->cname; + canon.names.real_name = client->entry.principal->name; - *csec = NULL; - *cusec = NULL; + 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"); - memset(&ap_req, 0, sizeof(ap_req)); - ret = krb5_decode_ap_req(context, &tgs_req->padata_value, &ap_req); - if(ret){ - kdc_log(0, "Failed to decode AP-REQ: %s", - krb5_get_err_text(context, ret)); - goto out2; - } - - if(!get_krbtgt_realm(&ap_req.ticket.sname)){ - /* XXX check for ticket.sname == req.sname */ - kdc_log(0, "PA-DATA is not a ticket-granting ticket"); - ret = KRB5KDC_ERR_POLICY; /* ? */ - goto out2; - } - - principalname2krb5_principal(&princ, - ap_req.ticket.sname, - ap_req.ticket.realm); - - ret = db_fetch(princ, &krbtgt); + /* sign using "returned session key" */ + ret = krb5_crypto_init(context, &et.key, 0, &crypto); + if (ret) { + free(data.data); + goto out; + } - if(ret) { - char *p; - krb5_unparse_name(context, princ, &p); - krb5_free_principal(context, princ); - kdc_log(0, "Ticket-granting ticket not found in database: %s: %s", - p, krb5_get_err_text(context, ret)); - free(p); - ret = KRB5KRB_AP_ERR_NOT_US; - goto out2; - } - - if(ap_req.ticket.enc_part.kvno && - *ap_req.ticket.enc_part.kvno != krbtgt->kvno){ - char *p; - - krb5_unparse_name (context, princ, &p); - krb5_free_principal(context, princ); - kdc_log(0, "Ticket kvno = %d, DB kvno = %d (%s)", - *ap_req.ticket.enc_part.kvno, - krbtgt->kvno, - p); - free (p); - ret = KRB5KRB_AP_ERR_BADKEYVER; - goto out2; - } - - ret = hdb_enctype2key(context, krbtgt, ap_req.ticket.enc_part.etype, &tkey); - if(ret){ - char *str; - krb5_enctype_to_string(context, ap_req.ticket.enc_part.etype, &str); - kdc_log(0, "No server key found for %s", str); - free(str); - ret = KRB5KRB_AP_ERR_BADKEYVER; - goto out2; - } - - 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(0, "Failed to verify AP-REQ: %s", - krb5_get_err_text(context, ret)); - goto out2; + 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; } - { - 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(0, "malloc failed"); - goto out2; - } - **csec = auth->ctime; - *cusec = malloc(sizeof(**cusec)); - if (*cusec == NULL) { - krb5_free_authenticator(context, &auth); - kdc_log(0, "malloc failed"); - goto out2; - } - **csec = auth->cusec; - krb5_free_authenticator(context, &auth); - } + if (rep.padata->len == 0) { + free(rep.padata); + rep.padata = NULL; } - cetype = ap_req.authenticator.etype; - - tgt = &ticket->ticket; + /* Add the PAC */ + if (send_pac_p(context, req)) { + krb5_pac p = NULL; + krb5_data data; - ret = tgs_check_authenticator(ac, b, &e_text, &tgt->key); - - if (b->enc_authorization_data) { - krb5_keyblock *subkey; - krb5_data ad; - ret = krb5_auth_con_getremotesubkey(context, - ac, - &subkey); - if(ret){ - krb5_auth_con_free(context, ac); - kdc_log(0, "Failed to get remote subkey: %s", - krb5_get_err_text(context, ret)); - goto out2; - } - if(subkey == NULL){ - ret = krb5_auth_con_getkey(context, ac, &subkey); - if(ret) { - krb5_auth_con_free(context, ac); - kdc_log(0, "Failed to get session key: %s", - krb5_get_err_text(context, ret)); - goto out2; - } - } - if(subkey == NULL){ - krb5_auth_con_free(context, ac); - kdc_log(0, "Failed to get key for enc-authorization-data"); - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ - goto out2; - } - ret = krb5_crypto_init(context, subkey, 0, &crypto); + ret = _kdc_pac_generate(context, client, &p); if (ret) { - krb5_auth_con_free(context, ac); - kdc_log(0, "krb5_crypto_init failed: %s", - krb5_get_err_text(context, ret)); - goto out2; - } - ret = krb5_decrypt_EncryptedData (context, - crypto, - KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, - b->enc_authorization_data, - &ad); - krb5_crypto_destroy(context, crypto); - if(ret){ - krb5_auth_con_free(context, ac); - kdc_log(0, "Failed to decrypt enc-authorization-data"); - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ - goto out2; - } - krb5_free_keyblock(context, subkey); - ALLOC(auth_data); - 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(0, "Failed to decode authorization data"); - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; /* ? */ - goto out2; + kdc_log(context, config, 0, "PAC generation failed for -- %s", + client_name); + goto out; } - } - - krb5_auth_con_free(context, ac); - - if(ret){ - kdc_log(0, "Failed to verify authenticator: %s", - krb5_get_err_text(context, ret)); - goto out2; - } - - { - PrincipalName *s; - Realm r; - char *spn = NULL, *cpn = NULL; - hdb_entry *server = NULL, *client = NULL; - int loop = 0; - EncTicketPart adtkt; - char opt_str[128]; - - s = b->sname; - r = b->realm; - if(b->kdc_options.enc_tkt_in_skey){ - Ticket *t; - hdb_entry *uu; - krb5_principal p; - Key *tkey; - - if(b->additional_tickets == NULL || - b->additional_tickets->len == 0){ - ret = KRB5KDC_ERR_BADOPTION; /* ? */ - kdc_log(0, "No second ticket present in request"); - goto out; - } - t = &b->additional_tickets->val[0]; - if(!get_krbtgt_realm(&t->sname)){ - kdc_log(0, "Additional ticket is not a ticket-granting ticket"); - ret = KRB5KDC_ERR_POLICY; - goto out2; - } - principalname2krb5_principal(&p, t->sname, t->realm); - ret = db_fetch(p, &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, t->enc_part.etype, &tkey); - if(ret){ - ret = KRB5KDC_ERR_ETYPE_NOSUPP; /* XXX */ + 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 = krb5_decrypt_ticket(context, t, &tkey->key, &adtkt, 0); - if(ret) + ret = _kdc_tkt_add_if_relevant_ad(context, &et, + KRB5_AUTHDATA_WIN2K_PAC, + &data); + krb5_data_free(&data); + if (ret) goto out; - s = &adtkt.cname; - r = adtkt.crealm; - } - - principalname2krb5_principal(&sp, *s, r); - krb5_unparse_name(context, sp, &spn); - principalname2krb5_principal(&cp, tgt->cname, tgt->crealm); - krb5_unparse_name(context, cp, &cpn); - unparse_flags (KDCOptions2int(b->kdc_options), KDCOptions_units, - opt_str, sizeof(opt_str)); - if(*opt_str) - kdc_log(0, "TGS-REQ %s from %s for %s [%s]", - cpn, from, spn, opt_str); - else - kdc_log(0, "TGS-REQ %s from %s for %s", cpn, from, spn); - server_lookup: - ret = db_fetch(sp, &server); - - if(ret){ - Realm req_rlm, new_rlm; - krb5_realm *realms; - - if ((req_rlm = get_krbtgt_realm(&sp->name)) != NULL) { - if(loop++ < 2) { - new_rlm = find_rpath(tgt->crealm, req_rlm); - if(new_rlm) { - kdc_log(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); - krb5_unparse_name(context, sp, &spn); - goto server_lookup; - } - } - } else if(need_referral(sp, &realms)) { - if (strcmp(realms[0], sp->realm) != 0) { - kdc_log(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); - krb5_unparse_name(context, sp, &spn); - krb5_free_host_realm(context, realms); - goto server_lookup; - } - krb5_free_host_realm(context, realms); - } - kdc_log(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 = db_fetch(cp, &client); - if(ret) - kdc_log(1, "Client not found in database: %s: %s", - cpn, krb5_get_err_text(context, ret)); -#if 0 - /* XXX check client only if same realm as krbtgt-instance */ - if(ret){ - kdc_log(0, "Client not found in database: %s: %s", - cpn, krb5_get_err_text(context, ret)); - if (ret == HDB_ERR_NOENTRY) - ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; - goto out; } -#endif + } - if(strcmp(krb5_principal_get_realm(context, sp), - krb5_principal_get_comp_string(context, krbtgt->principal, 1)) != 0) { - char *tpn; - ret = krb5_unparse_name(context, krbtgt->principal, &tpn); - kdc_log(0, "Request with wrong krbtgt: %s", (ret == 0) ? tpn : ""); - if(ret == 0) - free(tpn); - ret = KRB5KRB_AP_ERR_NOT_US; - goto out; - - } + _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, + et.endtime, et.renew_till); - ret = check_flags(client, cpn, server, spn, FALSE); - if(ret) - goto out; + /* 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; - if((b->kdc_options.validate || b->kdc_options.renew) && - !krb5_principal_compare(context, - krbtgt->principal, - server->principal)){ - kdc_log(0, "Inconsistent request."); - ret = KRB5KDC_ERR_SERVER_NOMATCH; - 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; - /* check for valid set of addresses */ - if(!check_addresses(tgt->caddr, from_addr)) { - ret = KRB5KRB_AP_ERR_BADADDR; - kdc_log(0, "Request from wrong address"); - goto out; - } - - ret = tgs_make_reply(b, - tgt, - b->kdc_options.enc_tkt_in_skey ? &adtkt : NULL, - auth_data, - server, - client, - cp, - krbtgt, - cetype, - &e_text, - reply); - - out: - free(spn); - free(cpn); - - if(server) - free_ent(server); - if(client) - free_ent(client); + /* */ + 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"; } -out2: - if(ret) { + +out: + free_AS_REP(&rep); + if(ret){ krb5_mk_error(context, ret, e_text, - NULL, - cp, - sp, + (e_data.data ? &e_data : NULL), + client_princ, + server_princ, NULL, NULL, reply); - free(*csec); - free(*cusec); - *csec = NULL; - *cusec = NULL; - } - krb5_free_principal(context, cp); - krb5_free_principal(context, sp); - if (ticket) { - krb5_free_ticket(context, ticket); - free(ticket); - } - free_AP_REQ(&ap_req); - if(auth_data){ - free_AuthorizationData(auth_data); - free(auth_data); + ret = 0; } - - if(krbtgt) - free_ent(krbtgt); - +#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 -tgs_rep(KDC_REQ *req, - krb5_data *data, - const char *from, - struct sockaddr *from_addr) +_kdc_tkt_add_if_relevant_ad(krb5_context context, + EncTicketPart *tkt, + int type, + const krb5_data *data) { krb5_error_code ret; - int i = 0; - PA_DATA *tgs_req = NULL; - time_t *csec = NULL; - int *cusec = NULL; + size_t size; - if(req->padata == NULL){ - ret = KRB5KDC_ERR_PREAUTH_REQUIRED; /* XXX ??? */ - kdc_log(0, "TGS-REQ from %s without PA-DATA", from); - goto out; + 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; + } } - - tgs_req = find_padata(req, &i, KRB5_PADATA_TGS_REQ); + + /* add the entry to the last element */ + { + AuthorizationData ad = { 0, NULL }; + AuthorizationDataElement ade; + + ade.ad_type = type; + ade.ad_data = *data; - if(tgs_req == NULL){ - ret = KRB5KDC_ERR_PADATA_TYPE_NOSUPP; + 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"); - kdc_log(0, "TGS-REQ from %s without PA-TGS-REQ", from); - goto out; - } - ret = tgs_rep2(&req->req_body, tgs_req, data, from, from_addr, - &csec, &cusec); -out: - if(ret && data->data == NULL){ - krb5_mk_error(context, - ret, - NULL, - NULL, - NULL, - NULL, - csec, - cusec, - data); + 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; + } } - free(csec); - free(cusec); + return 0; } diff --git a/crypto/heimdal/kdc/krb5tgs.c b/crypto/heimdal/kdc/krb5tgs.c new file mode 100644 index 0000000..32bdee9 --- /dev/null +++ b/crypto/heimdal/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 = ""; + 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 = ""; + 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 : "", + p ? p : ""); + 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 : ""); + 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/crypto/heimdal/kdc/kstash.8 b/crypto/heimdal/kdc/kstash.8 index 3bd46c63..f30eac6 100644 --- a/crypto/heimdal/kdc/kstash.8 +++ b/crypto/heimdal/kdc/kstash.8 @@ -1,6 +1,37 @@ -.\" $Id: kstash.8,v 1.7 2002/08/20 16:37:14 joda Exp $ +.\" Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan +.\" (Royal Institute of Technology, Stockholm, Sweden). +.\" All rights reserved. .\" -.Dd September 1, 2000 +.\" 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 @@ -8,6 +39,7 @@ .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 @@ -17,9 +49,12 @@ .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 @@ -31,25 +66,34 @@ Supported options: .Fl e Ar string , .Fl -enctype= Ns Ar string .Xc -the encryption type to use, defaults to DES3-CBC-SHA1 +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 +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 +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 +passphrase will be read from the terminal. .El .\".Sh ENVIRONMENT -.\".Sh FILES +.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 diff --git a/crypto/heimdal/kdc/kstash.c b/crypto/heimdal/kdc/kstash.c index dc0621a..9e499a1 100644 --- a/crypto/heimdal/kdc/kstash.c +++ b/crypto/heimdal/kdc/kstash.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2004 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -33,26 +33,28 @@ #include "headers.h" -RCSID("$Id: kstash.c,v 1.15 2002/04/18 09:47:25 joda Exp $"); +RCSID("$Id: kstash.c 22244 2007-12-08 23:47:42Z lha $"); krb5_context context; -const char *keyfile = HDB_DB_DIR "/m-key"; -int convert_flag; -int help_flag; -int version_flag; +static char *keyfile; +static int convert_flag; +static int help_flag; +static int version_flag; -int master_key_fd = -1; +static int master_key_fd = -1; +static int random_key_flag; -const char *enctype_str = "des3-cbc-sha1"; +static const char *enctype_str = "des3-cbc-sha1"; -struct getargs args[] = { +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 } }; @@ -78,6 +80,13 @@ main(int argc, char **argv) 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"); @@ -96,18 +105,26 @@ main(int argc, char **argv) /* XXX better value? */ salt.saltvalue.data = NULL; salt.saltvalue.length = 0; - 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'; + 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(des_read_pw_string(buf, sizeof(buf), "Master key: ", 1)) - exit(1); + 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); } - krb5_string_to_key_salt(context, enctype, buf, salt, &key); ret = hdb_add_master_key(context, &key, &mkey); krb5_free_keyblock_contents(context, &key); diff --git a/crypto/heimdal/kdc/kx509.c b/crypto/heimdal/kdc/kx509.c new file mode 100644 index 0000000..b1b861e --- /dev/null +++ b/crypto/heimdal/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 +#include +#include + +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/crypto/heimdal/kdc/log.c b/crypto/heimdal/kdc/log.c index aa430aa..8cf967f 100644 --- a/crypto/heimdal/kdc/log.c +++ b/crypto/heimdal/kdc/log.c @@ -32,53 +32,62 @@ */ #include "kdc_locl.h" -RCSID("$Id: log.c,v 1.14 2002/08/19 12:17:49 joda Exp $"); - -static krb5_log_facility *logf; +RCSID("$Id: log.c 22254 2007-12-09 06:01:05Z lha $"); void -kdc_openlog(void) +kdc_openlog(krb5_context context, + krb5_kdc_configuration *config) { char **s = NULL, **p; - krb5_initlog(context, "kdc", &logf); + 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, logf, *p); + krb5_addlog_dest(context, config->logf, *p); krb5_config_free_strings(s); - }else - krb5_addlog_dest(context, logf, DEFAULT_LOG_DEST); - krb5_set_warn_dest(context, logf); + }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(int level, const char *fmt, va_list ap) +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, logf, &msg, level, fmt, ap); + krb5_vlog_msg(context, config->logf, &msg, level, fmt, ap); return msg; } char* -kdc_log_msg(int level, const char *fmt, ...) +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(level, fmt, ap); + s = kdc_log_msg_va(context, config, level, fmt, ap); va_end(ap); return s; } void -kdc_log(int level, const char *fmt, ...) +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(level, fmt, ap); + s = kdc_log_msg_va(context, config, level, fmt, ap); if(s) free(s); va_end(ap); } diff --git a/crypto/heimdal/kdc/main.c b/crypto/heimdal/kdc/main.c index 32ae20f..9195b04 100644 --- a/crypto/heimdal/kdc/main.c +++ b/crypto/heimdal/kdc/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2002 Kungliga Tekniska Högskolan + * Copyright (c) 1997-2005 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * All rights reserved. * @@ -36,57 +36,38 @@ #include #endif -RCSID("$Id: main.c,v 1.27 2002/08/28 21:27:16 joda Exp $"); +RCSID("$Id: main.c 20454 2007-04-19 20:21:51Z lha $"); sig_atomic_t exit_flag = 0; -krb5_context context; -#ifdef HAVE_DAEMON -extern int detach_from_console; -#endif +int detach_from_console = -1; static RETSIGTYPE sigterm(int sig) { - exit_flag = 1; + 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) + 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); - configure(argc, argv); + ret = krb5_kt_register(context, &hdb_kt_ops); + if (ret) + errx (1, "krb5_kt_register(HDB) failed: %d", ret); - if(databases == NULL) { - db = malloc(sizeof(*db)); - num_db = 1; - ret = hdb_create(context, &db[0], NULL); - if(ret) - krb5_err(context, 1, ret, "hdb_create %s", HDB_DEFAULT_DB); - ret = hdb_set_master_keyfile(context, db[0], NULL); - if (ret) - krb5_err(context, 1, ret, "hdb_set_master_keyfile"); - } else { - struct dbinfo *d; - int i; - /* count databases */ - for(d = databases, i = 0; d; d = d->next, i++); - db = malloc(i * sizeof(*db)); - for(d = databases, num_db = 0; d; d = d->next, num_db++) { - ret = hdb_create(context, &db[num_db], d->dbname); - if(ret) - krb5_err(context, 1, ret, "hdb_create %s", d->dbname); - ret = hdb_set_master_keyfile(context, db[num_db], d->mkey_file); - if (ret) - krb5_err(context, 1, ret, "hdb_set_master_keyfile"); - } - } + config = configure(context, argc, argv); #ifdef HAVE_SIGACTION { @@ -98,17 +79,21 @@ main(int argc, char **argv) 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 -#ifdef HAVE_DAEMON if (detach_from_console) daemon(0, 0); -#endif pidfile(NULL); - loop(); + loop(context, config); krb5_free_context(context); return 0; } diff --git a/crypto/heimdal/kdc/misc.c b/crypto/heimdal/kdc/misc.c index aebdc68..072df44 100644 --- a/crypto/heimdal/kdc/misc.c +++ b/crypto/heimdal/kdc/misc.c @@ -33,44 +33,90 @@ #include "kdc_locl.h" -RCSID("$Id: misc.c,v 1.22 2001/01/30 03:54:21 assar Exp $"); +RCSID("$Id: misc.c 21106 2007-06-18 10:18:11Z lha $"); -struct timeval now; +struct timeval _kdc_now; krb5_error_code -db_fetch(krb5_principal principal, hdb_entry **h) +_kdc_db_fetch(krb5_context context, + krb5_kdc_configuration *config, + krb5_const_principal principal, + unsigned flags, + HDB **db, + hdb_entry_ex **h) { - hdb_entry *ent; - krb5_error_code ret = HDB_ERR_NOENTRY; + hdb_entry_ex *ent; + krb5_error_code ret; int i; - ent = malloc (sizeof (*ent)); - if (ent == NULL) + ent = calloc (1, sizeof (*ent)); + if (ent == NULL) { + krb5_set_error_string(context, "out of memory"); return ENOMEM; - ent->principal = principal; + } - for(i = 0; i < num_db; i++) { - ret = db[i]->open(context, db[i], O_RDONLY, 0); + 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(0, "Failed to open database: %s", + kdc_log(context, config, 0, "Failed to open database: %s", krb5_get_err_text(context, ret)); continue; } - ret = db[i]->fetch(context, db[i], HDB_F_DECRYPT, ent); - db[i]->close(context, db[i]); + 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); - return ret; + krb5_set_error_string(context, "no such entry found in hdb"); + return HDB_ERR_NOENTRY; } void -free_ent(hdb_entry *ent) +_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/crypto/heimdal/kdc/mit_dump.c b/crypto/heimdal/kdc/mit_dump.c index 336d265..dd2f5d7 100644 --- a/crypto/heimdal/kdc/mit_dump.c +++ b/crypto/heimdal/kdc/mit_dump.c @@ -33,7 +33,7 @@ #include "hprop.h" -RCSID("$Id: mit_dump.c,v 1.3 2000/08/09 09:57:37 joda Exp $"); +RCSID("$Id: mit_dump.c 21745 2007-07-31 16:11:25Z lha $"); /* can have any number of princ stanzas. @@ -168,7 +168,6 @@ fix_salt(krb5_context context, hdb_entry *ent, int key_num) { size_t len; int i; - krb5_error_code ret; char *p; len = 0; @@ -219,10 +218,10 @@ int mit_prop_dump(void *arg, const char *file) { krb5_error_code ret; - char buf [1024]; + char line [2048]; FILE *f; int lineno = 0; - struct hdb_entry ent; + struct hdb_entry_ex ent; struct prop_data *pd = arg; @@ -230,8 +229,8 @@ mit_prop_dump(void *arg, const char *file) if(f == NULL) return errno; - while(fgets(buf, sizeof(buf), f)) { - char *p = buf, *q; + while(fgets(line, sizeof(line), f)) { + char *p = line, *q; int i; @@ -275,28 +274,28 @@ mit_prop_dump(void *arg, const char *file) 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.principal); + krb5_parse_name(pd->context, q, &ent.entry.principal); attributes = getint(&p); /* attributes */ - attr_to_flags(attributes, &ent.flags); + attr_to_flags(attributes, &ent.entry.flags); tmp = getint(&p); /* max life */ if(tmp != 0) { - ALLOC(ent.max_life); - *ent.max_life = tmp; + ALLOC(ent.entry.max_life); + *ent.entry.max_life = tmp; } tmp = getint(&p); /* max renewable life */ if(tmp != 0) { - ALLOC(ent.max_renew); - *ent.max_renew = tmp; + ALLOC(ent.entry.max_renew); + *ent.entry.max_renew = tmp; } tmp = getint(&p); /* expiration */ if(tmp != 0 && tmp != 2145830400) { - ALLOC(ent.valid_end); - *ent.valid_end = tmp; + ALLOC(ent.entry.valid_end); + *ent.entry.valid_end = tmp; } tmp = getint(&p); /* pw expiration */ if(tmp != 0) { - ALLOC(ent.pw_end); - *ent.pw_end = tmp; + ALLOC(ent.entry.pw_end); + *ent.entry.pw_end = tmp; } q = nexttoken(&p); /* last auth */ q = nexttoken(&p); /* last failed auth */ @@ -310,61 +309,65 @@ mit_prop_dump(void *arg, const char *file) tl_type = getint(&p); /* data type */ tl_length = getint(&p); /* data length */ -#define KRB5_TL_LAST_PWD_CHANGE 1 -#define KRB5_TL_MOD_PRINC 2 +#define mit_KRB5_TL_LAST_PWD_CHANGE 1 +#define mit_KRB5_TL_MOD_PRINC 2 switch(tl_type) { - case KRB5_TL_MOD_PRINC: + 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, buf + 4, &princ); + ret = krb5_parse_name(pd->context, (char *)buf + 4, &princ); free(buf); - ALLOC(ent.modified_by); - ent.modified_by->time = val; - ent.modified_by->principal = princ; + 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.keys, num_key_data); + 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.kvno = getint(&p); /* XXX kvno */ + ent.entry.kvno = getint(&p); /* XXX kvno */ - ALLOC(ent.keys.val[i].mkvno); - *ent.keys.val[i].mkvno = 0; + ALLOC(ent.entry.keys.val[i].mkvno); + *ent.entry.keys.val[i].mkvno = 0; /* key version 0 -- actual key */ - ent.keys.val[i].key.keytype = getint(&p); /* key type */ + 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.keys.val[i].key.keyvalue, tmp - 2); + krb5_data_alloc(&ent.entry.keys.val[i].key.keyvalue, tmp - 2); q = nexttoken(&p); /* key itself */ - hex_to_octet_string(q + 4, &ent.keys.val[i].key.keyvalue); + hex_to_octet_string(q + 4, &ent.entry.keys.val[i].key.keyvalue); if(key_versions > 1) { /* key version 1 -- optional salt */ - ALLOC(ent.keys.val[i].salt); - ent.keys.val[i].salt->type = getint(&p); /* salt type */ + 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.keys.val[i].salt->salt, tmp - 2); + krb5_data_alloc(&ent.entry.keys.val[i].salt->salt, tmp - 2); q = nexttoken(&p); /* salt itself */ - hex_to_octet_string(q + 4, &ent.keys.val[i].salt->salt); + hex_to_octet_string(q + 4, + &ent.entry.keys.val[i].salt->salt); } else { - ent.keys.val[i].salt->salt.length = 0; - ent.keys.val[i].salt->salt.data = NULL; + 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, i); + 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/crypto/heimdal/kdc/pkinit.c b/crypto/heimdal/kdc/pkinit.c new file mode 100644 index 0000000..bf248af --- /dev/null +++ b/crypto/heimdal/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 +#include +#include +#include + +#include +#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/crypto/heimdal/kdc/process.c b/crypto/heimdal/kdc/process.c new file mode 100644 index 0000000..1d0a01a --- /dev/null +++ b/crypto/heimdal/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/crypto/heimdal/kdc/rx.h b/crypto/heimdal/kdc/rx.h index ab8ec805..18806d7 100644 --- a/crypto/heimdal/kdc/rx.h +++ b/crypto/heimdal/kdc/rx.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. */ -/* $Id: rx.h,v 1.4 1999/12/02 17:05:00 joda Exp $ */ +/* $Id: rx.h 17447 2006-05-05 10:52:01Z lha $ */ #ifndef __RX_H__ #define __RX_H__ @@ -59,17 +59,17 @@ enum rx_header_flag { }; struct rx_header { - u_int32_t epoch; - u_int32_t connid; /* And channel ID */ - u_int32_t callid; - u_int32_t seqno; - u_int32_t serialno; + 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; - u_int16_t reserved; /* ??? verifier? */ - u_int16_t serviceid; + uint16_t reserved; /* ??? verifier? */ + uint16_t serviceid; /* This should be the other way around according to everything but */ /* tcpdump */ }; diff --git a/crypto/heimdal/kdc/set_dbinfo.c b/crypto/heimdal/kdc/set_dbinfo.c new file mode 100644 index 0000000..651f4c4 --- /dev/null +++ b/crypto/heimdal/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/crypto/heimdal/kdc/string2key.8 b/crypto/heimdal/kdc/string2key.8 index dc9d63b..8f2d562 100644 --- a/crypto/heimdal/kdc/string2key.8 +++ b/crypto/heimdal/kdc/string2key.8 @@ -29,7 +29,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $Id: string2key.8,v 1.6 2003/02/16 21:10:21 lha Exp $ +.\" $Id: string2key.8 11648 2003-02-16 21:10:32Z lha $ .\" .Dd March 4, 2000 .Dt STRING2KEY 8 diff --git a/crypto/heimdal/kdc/string2key.c b/crypto/heimdal/kdc/string2key.c index 8a38442..4211bf7 100644 --- a/crypto/heimdal/kdc/string2key.c +++ b/crypto/heimdal/kdc/string2key.c @@ -34,7 +34,7 @@ #include "headers.h" #include -RCSID("$Id: string2key.c,v 1.20 2003/03/25 12:28:52 joda Exp $"); +RCSID("$Id: string2key.c 19213 2006-12-04 23:36:36Z lha $"); int version5; int version4; @@ -70,21 +70,28 @@ usage(int status) static void tokey(krb5_context context, krb5_enctype enctype, - const char *password, + const char *pw, krb5_salt salt, const char *label) { + krb5_error_code ret; int i; krb5_keyblock key; char *e; - krb5_string_to_key_salt(context, enctype, password, salt, &key); - krb5_enctype_to_string(context, enctype, &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 @@ -93,12 +100,12 @@ main(int argc, char **argv) krb5_context context; krb5_principal princ; krb5_salt salt; - int optind; + int optidx; char buf[1024]; krb5_enctype etype; krb5_error_code ret; - optind = krb5_program_setup(&context, argc, argv, args, num_args, NULL); + optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL); if(help) usage(0); @@ -108,8 +115,8 @@ main(int argc, char **argv) return 0; } - argc -= optind; - argv += optind; + argc -= optidx; + argv += optidx; if (argc > 1) usage(1); @@ -122,6 +129,7 @@ main(int argc, char **argv) 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); @@ -131,7 +139,8 @@ main(int argc, char **argv) if(num == 0) krb5_errx(context, 1, "there are no encryption types for that keytype"); etype = etypes[0]; - krb5_enctype_to_string(context, etype, &keytype_str); + krb5_enctype_to_string(context, etype, &str); + keytype_str = str; if(num > 1 && version5) krb5_warnx(context, "ambiguous keytype, using %s", keytype_str); } @@ -152,22 +161,20 @@ main(int argc, char **argv) printf("Kerberos v5 principal: "); if(fgets(buf, sizeof(buf), stdin) == NULL) return 1; - if(buf[strlen(buf) - 1] == '\n') - buf[strlen(buf) - 1] = '\0'; + 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; - if(buf[strlen(buf) - 1] == '\n') - buf[strlen(buf) - 1] = '\0'; + buf[strcspn(buf, "\r\n")] = '\0'; cell = estrdup(buf); } if(argv[0]) password = argv[0]; if(password == NULL){ - if(des_read_pw_string(buf, sizeof(buf), "Password: ", 0)) + if(UI_UTIL_read_pw_string(buf, sizeof(buf), "Password: ", 0)) return 1; password = buf; } diff --git a/crypto/heimdal/kdc/v4_dump.c b/crypto/heimdal/kdc/v4_dump.c index ddf8222..93c56f8 100644 --- a/crypto/heimdal/kdc/v4_dump.c +++ b/crypto/heimdal/kdc/v4_dump.c @@ -33,7 +33,7 @@ #include "hprop.h" -RCSID("$Id: v4_dump.c,v 1.4.8.1 2003/04/28 12:24:54 lha Exp $"); +RCSID("$Id: v4_dump.c 17023 2006-04-09 17:41:47Z lha $"); static time_t time_parse(const char *cp) @@ -138,5 +138,6 @@ v4_prop_dump(void *arg, const char *file) v4_prop(arg, &pr); memset(&pr, 0, sizeof(pr)); } + fclose(f); return 0; } diff --git a/crypto/heimdal/kdc/version-script.map b/crypto/heimdal/kdc/version-script.map new file mode 100644 index 0000000..2612b8e --- /dev/null +++ b/crypto/heimdal/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/crypto/heimdal/kdc/windc.c b/crypto/heimdal/kdc/windc.c new file mode 100644 index 0000000..395ab73 --- /dev/null +++ b/crypto/heimdal/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/crypto/heimdal/kdc/windc_plugin.h b/crypto/heimdal/kdc/windc_plugin.h new file mode 100644 index 0000000..ec480cf --- /dev/null +++ b/crypto/heimdal/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 + +/* + * 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 */ + -- cgit v1.1