diff options
| author | Greg Hudson <ghudson@mit.edu> | 2011-02-07 18:40:00 +0000 |
|---|---|---|
| committer | Greg Hudson <ghudson@mit.edu> | 2011-02-07 18:40:00 +0000 |
| commit | 66587fcd6380eac2c53674df4f64a827d337aee5 (patch) | |
| tree | e3e98004479a87b3f1e1171056464f3a6be65d95 /src/lib/krb5 | |
| parent | 1b46b254240d95534b7a3ee1f45ac85f6c38db1b (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.in | 3 | ||||
| -rw-r--r-- | src/lib/krb5/krb/init_ctx.c | 7 | ||||
| -rw-r--r-- | src/lib/krb5/krb/rd_req_dec.c | 205 | ||||
| -rw-r--r-- | src/lib/krb5/krb/sname_match.c | 54 | ||||
| -rw-r--r-- | src/lib/krb5/libkrb5.exports | 1 |
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 |
