summaryrefslogtreecommitdiffstats
path: root/src/lib/krb5
diff options
context:
space:
mode:
authorGreg Hudson <ghudson@mit.edu>2011-02-07 18:40:00 +0000
committerGreg Hudson <ghudson@mit.edu>2011-02-07 18:40:00 +0000
commit66587fcd6380eac2c53674df4f64a827d337aee5 (patch)
treee3e98004479a87b3f1e1171056464f3a6be65d95 /src/lib/krb5
parent1b46b254240d95534b7a3ee1f45ac85f6c38db1b (diff)
Improve acceptor name flexibility
Be more flexible about the principal names we will accept for a given GSS acceptor name. Also add support for a new libdefaults profile variable ignore_acceptor_hostname, which causes the hostnames of host-based service principals to be ignored when passed by server applications as acceptor names. Note that we still always invoke krb5_sname_to_principal() when importing a gss-krb5 mechanism name, even though we won't always use the result. This is an unfortunate waste of getaddrinfo/getnameinfo queries in some situations, but the code surgery necessary to defer it appears too risky at this time. The project proposal for this change is at: http://k5wiki.kerberos.org/wiki/Projects/Acceptor_Names ticket: 6855 git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@24616 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'src/lib/krb5')
-rw-r--r--src/lib/krb5/krb/Makefile.in3
-rw-r--r--src/lib/krb5/krb/init_ctx.c7
-rw-r--r--src/lib/krb5/krb/rd_req_dec.c205
-rw-r--r--src/lib/krb5/krb/sname_match.c54
-rw-r--r--src/lib/krb5/libkrb5.exports1
5 files changed, 178 insertions, 92 deletions
diff --git a/src/lib/krb5/krb/Makefile.in b/src/lib/krb5/krb/Makefile.in
index b96229986..b1af2e747 100644
--- a/src/lib/krb5/krb/Makefile.in
+++ b/src/lib/krb5/krb/Makefile.in
@@ -101,6 +101,7 @@ STLIBOBJS= \
ser_princ.o \
serialize.o \
set_realm.o \
+ sname_match.o \
srv_dec_tkt.o \
srv_rcache.o \
str_conv.o \
@@ -202,6 +203,7 @@ OBJS= $(OUTPRE)addr_comp.$(OBJEXT) \
$(OUTPRE)ser_princ.$(OBJEXT) \
$(OUTPRE)serialize.$(OBJEXT) \
$(OUTPRE)set_realm.$(OBJEXT) \
+ $(OUTPRE)sname_match.$(OBJEXT) \
$(OUTPRE)srv_dec_tkt.$(OBJEXT) \
$(OUTPRE)srv_rcache.$(OBJEXT) \
$(OUTPRE)str_conv.$(OBJEXT) \
@@ -303,6 +305,7 @@ SRCS= $(srcdir)/addr_comp.c \
$(srcdir)/ser_princ.c \
$(srcdir)/serialize.c \
$(srcdir)/set_realm.c \
+ $(srcdir)/sname_match.c \
$(srcdir)/srv_dec_tkt.c \
$(srcdir)/srv_rcache.c \
$(srcdir)/str_conv.c \
diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c
index 4f0075031..4eae4d743 100644
--- a/src/lib/krb5/krb/init_ctx.c
+++ b/src/lib/krb5/krb/init_ctx.c
@@ -180,6 +180,13 @@ init_common (krb5_context *context, krb5_boolean secure, krb5_boolean kdc)
goto cleanup;
ctx->allow_weak_crypto = tmp;
+ retval = profile_get_boolean(ctx->profile, KRB5_CONF_LIBDEFAULTS,
+ KRB5_CONF_IGNORE_ACCEPTOR_HOSTNAME, NULL, 0,
+ &tmp);
+ if (retval)
+ goto cleanup;
+ ctx->ignore_acceptor_hostname = tmp;
+
/* initialize the prng (not well, but passable) */
if ((retval = krb5_c_random_os_entropy( ctx, 0, NULL)) !=0)
goto cleanup;
diff --git a/src/lib/krb5/krb/rd_req_dec.c b/src/lib/krb5/krb/rd_req_dec.c
index 9bc7c42f9..e065e2157 100644
--- a/src/lib/krb5/krb/rd_req_dec.c
+++ b/src/lib/krb5/krb/rd_req_dec.c
@@ -86,102 +86,138 @@ negotiate_etype(krb5_context context,
int permitted_etypes_len,
krb5_enctype *negotiated_etype);
+/* Return true if princ might match multiple principals. */
+static inline krb5_boolean
+is_matching(krb5_context context, krb5_const_principal princ)
+{
+ if (princ == NULL)
+ return TRUE;
+ return (princ->type == KRB5_NT_SRV_HST && princ->length == 2
+ && (princ->realm.length == 0 || princ->data[1].length == 0 ||
+ context->ignore_acceptor_hostname));
+}
+
+/* Decrypt the ticket in req using the key in ent. */
static krb5_error_code
-rd_req_decrypt_tkt_part(krb5_context context, const krb5_ap_req *req,
- krb5_const_principal server, krb5_keytab keytab,
- krb5_keyblock *key)
+try_one_entry(krb5_context context, const krb5_ap_req *req,
+ krb5_keytab_entry *ent, krb5_keyblock *keyblock_out)
{
- krb5_error_code retval;
- krb5_keytab_entry ktent;
+ krb5_error_code ret;
+ krb5_principal tmp = NULL;
+
+ /* Try decrypting the ticket with this entry's key. */
+ ret = krb5_decrypt_tkt_part(context, &ent->key, req->ticket);
+ if (ret)
+ return ret;
+
+ /* Make a copy of the principal for the ticket server field. */
+ ret = krb5_copy_principal(context, ent->principal, &tmp);
+ if (ret)
+ return ret;
+
+ /* Make a copy of the decrypting key if requested by the caller. */
+ if (keyblock_out != NULL) {
+ ret = krb5_copy_keyblock_contents(context, &ent->key, keyblock_out);
+ if (ret) {
+ krb5_free_principal(context, tmp);
+ return ret;
+ }
+ }
- retval = KRB5_KT_NOTFOUND;
+ /* Make req->ticket->server indicate the actual server principal. */
+ krb5_free_principal(context, req->ticket->server);
+ req->ticket->server = tmp;
-#ifndef LEAN_CLIENT
- if (server != NULL || keytab->ops->start_seq_get == NULL) {
- retval = krb5_kt_get_entry(context, keytab,
- server != NULL ? server : req->ticket->server,
- req->ticket->enc_part.kvno,
- req->ticket->enc_part.enctype, &ktent);
- if (retval == 0) {
- retval = krb5_decrypt_tkt_part(context, &ktent.key, req->ticket);
- if (retval == 0) {
- TRACE_RD_REQ_DECRYPT_SPECIFIC(context, ktent.principal,
- &ktent.key);
- }
- if (retval == 0 && key != NULL)
- retval = krb5_copy_keyblock_contents(context, &ktent.key, key);
+ return 0;
+}
- (void) krb5_free_keytab_entry_contents(context, &ktent);
- }
- } else {
- krb5_error_code code;
- krb5_kt_cursor cursor;
+/* Decrypt the ticket in req using a principal looked up from keytab. */
+static krb5_error_code
+try_one_princ(krb5_context context, const krb5_ap_req *req,
+ krb5_const_principal princ, krb5_keytab keytab,
+ krb5_keyblock *keyblock_out)
+{
+ krb5_error_code ret;
+ krb5_keytab_entry ent;
+
+ ret = krb5_kt_get_entry(context, keytab, princ,
+ req->ticket->enc_part.kvno,
+ req->ticket->enc_part.enctype, &ent);
+ if (ret)
+ return ret;
+ ret = try_one_entry(context, req, &ent, keyblock_out);
+ (void)krb5_free_keytab_entry_contents(context, &ent);
+ if (ret)
+ return ret;
+
+ TRACE_RD_REQ_DECRYPT_SPECIFIC(context, ent.principal, &ent.key);
+ return 0;
+}
- code = krb5_kt_start_seq_get(context, keytab, &cursor);
- if (code != 0) {
- retval = code;
- goto map_error;
- }
+/*
+ * Decrypt the ticket in req using an entry in keytab matching server (if
+ * given). Set req->ticket->server to the principal of the keytab entry used.
+ * Store the decrypting key in *keyblock_out if it is not NULL.
+ */
+static krb5_error_code
+decrypt_ticket(krb5_context context, const krb5_ap_req *req,
+ krb5_const_principal server, krb5_keytab keytab,
+ krb5_keyblock *keyblock_out)
+{
+ krb5_error_code ret;
+ krb5_keytab_entry ent;
+ krb5_kt_cursor cursor;
- while ((code = krb5_kt_next_entry(context, keytab,
- &ktent, &cursor)) == 0) {
- if (ktent.key.enctype != req->ticket->enc_part.enctype) {
- (void) krb5_free_keytab_entry_contents(context, &ktent);
- continue;
- }
+#ifdef LEAN_CLIENT
+ return KRB5KRB_AP_WRONG_PRINC;
+#else
+ /* If we have an explicit server principal, try just that one. */
+ if (!is_matching(context, server))
+ return try_one_princ(context, req, server, keytab, keyblock_out);
+
+ if (keytab->ops->start_seq_get == NULL) {
+ /* We can't iterate over the keytab. Try the principal asserted by the
+ * client if it's allowed by the server parameter. */
+ if (!krb5_sname_match(context, server, req->ticket->server))
+ return KRB5KRB_AP_WRONG_PRINC;
+ return try_one_princ(context, req, req->ticket->server, keytab,
+ keyblock_out);
+ }
- retval = krb5_decrypt_tkt_part(context, &ktent.key,
- req->ticket);
-
- if (retval == 0) {
- krb5_principal tmp = NULL;
-
- TRACE_RD_REQ_DECRYPT_ANY(context, ktent.principal, &ktent.key);
- /*
- * We overwrite ticket->server to be the principal
- * that we match in the keytab. The reason for doing
- * this is that GSS-API and other consumers look at
- * that principal to make authorization decisions
- * about whether the appropriate server is contacted.
- * It might be cleaner to create a new API and store
- * the server in the auth_context, but doing so would
- * probably miss existing uses of the server. Instead,
- * perhaps an API should be created to retrieve the
- * server as it appeared in the ticket.
- */
- retval = krb5_copy_principal(context, ktent.principal, &tmp);
- if (retval == 0 && key != NULL)
- retval = krb5_copy_keyblock_contents(context, &ktent.key, key);
- if (retval == 0) {
- krb5_free_principal(context, req->ticket->server);
- req->ticket->server = tmp;
- } else {
- krb5_free_principal(context, tmp);
- }
- (void) krb5_free_keytab_entry_contents(context, &ktent);
+ ret = krb5_kt_start_seq_get(context, keytab, &cursor);
+ if (ret)
+ goto cleanup;
+
+ while ((ret = krb5_kt_next_entry(context, keytab, &ent, &cursor)) == 0) {
+ if (ent.key.enctype == req->ticket->enc_part.enctype &&
+ krb5_sname_match(context, server, ent.principal)) {
+ ret = try_one_entry(context, req, &ent, keyblock_out);
+ if (ret == 0) {
+ TRACE_RD_REQ_DECRYPT_ANY(context, ent.principal, &ent.key);
+ (void)krb5_free_keytab_entry_contents(context, &ent);
break;
}
- (void) krb5_free_keytab_entry_contents(context, &ktent);
}
- code = krb5_kt_end_seq_get(context, keytab, &cursor);
- if (code != 0)
- retval = code;
+ (void)krb5_free_keytab_entry_contents(context, &ent);
}
-#endif /* LEAN_CLIENT */
-map_error:
- switch (retval) {
+ (void)krb5_kt_end_seq_get(context, keytab, &cursor);
+
+cleanup:
+ switch (ret) {
case KRB5_KT_KVNONOTFOUND:
case KRB5_KT_NOTFOUND:
+ case KRB5_KT_END:
case KRB5KRB_AP_ERR_BAD_INTEGRITY:
- retval = KRB5KRB_AP_WRONG_PRINC;
+ ret = KRB5KRB_AP_WRONG_PRINC;
break;
default:
break;
}
- return retval;
+ return ret;
+#endif /* LEAN_CLIENT */
}
#if 0
@@ -215,7 +251,6 @@ rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
krb5_ticket **ticket, int check_valid_flag)
{
krb5_error_code retval = 0;
- krb5_principal_data princ_data;
krb5_enctype *desired_etypes = NULL;
int desired_etypes_len = 0;
int rfc4537_etypes_len = 0;
@@ -225,19 +260,7 @@ rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
decrypt_key.enctype = ENCTYPE_NULL;
decrypt_key.contents = NULL;
-
req->ticket->enc_part2 = NULL;
- if (server && krb5_is_referral_realm(&server->realm)) {
- char *realm;
- princ_data = *server;
- server = &princ_data;
- retval = krb5_get_default_realm(context, &realm);
- if (retval)
- return retval;
- princ_data.realm.data = realm;
- princ_data.realm.length = strlen(realm);
- }
-
/* if (req->ap_options & AP_OPTS_USE_SESSION_KEY)
do we need special processing here ? */
@@ -255,9 +278,9 @@ rd_req_decoded_opt(krb5_context context, krb5_auth_context *auth_context,
krb5_k_free_key(context, (*auth_context)->key);
(*auth_context)->key = NULL;
} else {
- if ((retval = rd_req_decrypt_tkt_part(context, req,
- server, keytab,
- check_valid_flag ? &decrypt_key : NULL)))
+ retval = decrypt_ticket(context, req, server, keytab,
+ check_valid_flag ? &decrypt_key : NULL);
+ if (retval)
goto cleanup;
}
TRACE_RD_REQ_TICKET(context, req->ticket->enc_part2->client,
@@ -545,8 +568,6 @@ cleanup:
if (permitted_etypes != NULL &&
permitted_etypes != (*auth_context)->permitted_etypes)
free(permitted_etypes);
- if (server == &princ_data)
- krb5_free_default_realm(context, princ_data.realm.data);
if (retval) {
/* only free if we're erroring out...otherwise some
applications will need the output. */
diff --git a/src/lib/krb5/krb/sname_match.c b/src/lib/krb5/krb/sname_match.c
new file mode 100644
index 000000000..0c7bd39e8
--- /dev/null
+++ b/src/lib/krb5/krb/sname_match.c
@@ -0,0 +1,54 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/krb/sname_match.c - krb5_sname_match API function */
+/*
+ * Copyright (C) 2011 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+
+krb5_boolean KRB5_CALLCONV
+krb5_sname_match(krb5_context context, krb5_const_principal matching,
+ krb5_const_principal princ)
+{
+ if (matching == NULL)
+ return TRUE;
+
+ if (matching->type != KRB5_NT_SRV_HST || matching->length != 2)
+ return krb5_principal_compare(context, matching, princ);
+
+ /* Check the realm if present in matching. */
+ if (matching->realm.length != 0 && !data_eq(matching->realm, princ->realm))
+ return FALSE;
+
+ /* Check the service name (must be present in matching). */
+ if (!data_eq(matching->data[0], princ->data[0]))
+ return FALSE;
+
+ /* Check the hostname if present in matching and not ignored. */
+ if (matching->data[1].length != 0 && !context->ignore_acceptor_hostname &&
+ !data_eq(matching->data[1], princ->data[1]))
+ return FALSE;
+
+ /* All elements match. */
+ return TRUE;
+}
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index c8918dccd..686681397 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -556,6 +556,7 @@ krb5_set_time_offsets
krb5_size_opaque
krb5_skdc_timeout_1
krb5_skdc_timeout_shift
+krb5_sname_match
krb5_sname_to_principal
krb5_string_to_deltat
krb5_string_to_salttype