summaryrefslogtreecommitdiffstats
path: root/src/lib
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
parent1b46b254240d95534b7a3ee1f45ac85f6c38db1b (diff)
downloadkrb5-66587fcd6380eac2c53674df4f64a827d337aee5.tar.gz
krb5-66587fcd6380eac2c53674df4f64a827d337aee5.tar.xz
krb5-66587fcd6380eac2c53674df4f64a827d337aee5.zip
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')
-rw-r--r--src/lib/gssapi/krb5/accept_sec_context.c24
-rw-r--r--src/lib/gssapi/krb5/acquire_cred.c83
-rw-r--r--src/lib/gssapi/krb5/gssapiP_krb5.h18
-rw-r--r--src/lib/gssapi/krb5/import_name.c67
-rw-r--r--src/lib/gssapi/krb5/naming_exts.c66
-rw-r--r--src/lib/gssapi/krb5/s4u_gss_glue.c3
-rw-r--r--src/lib/gssapi/krb5/ser_sctx.c4
-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
-rw-r--r--src/lib/krb5_32.def3
13 files changed, 388 insertions, 150 deletions
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index c3cb2f19e..0344ebd10 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -240,7 +240,8 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred)
/* copy the client principle into it... */
if ((retval =
- kg_init_name(context, creds[0]->client, NULL, 0, &cred->name))) {
+ kg_init_name(context, creds[0]->client, NULL, NULL, NULL, 0,
+ &cred->name))) {
k5_mutex_destroy(&cred->lock);
retval = ENOMEM; /* out of memory? */
xfree(cred); /* clean up memory on failure */
@@ -472,6 +473,7 @@ kg_accept_krb5(minor_status, context_handle,
krb5_flags ap_req_options = 0;
krb5_enctype negotiated_etype;
krb5_authdata_context ad_context = NULL;
+ krb5_principal accprinc = NULL;
code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
if (code) {
@@ -632,11 +634,15 @@ kg_accept_krb5(minor_status, context_handle,
}
}
- if ((code = krb5_rd_req(context, &auth_context, &ap_req,
- cred->default_identity ? NULL : cred->name->princ,
- cred->keytab,
- &ap_req_options,
- &ticket))) {
+ if (!cred->default_identity) {
+ if ((code = kg_acceptor_princ(context, cred->name, &accprinc))) {
+ major_status = GSS_S_FAILURE;
+ goto fail;
+ }
+ }
+
+ if ((code = krb5_rd_req(context, &auth_context, &ap_req, accprinc,
+ cred->keytab, &ap_req_options, &ticket))) {
major_status = GSS_S_FAILURE;
goto fail;
}
@@ -918,7 +924,8 @@ kg_accept_krb5(minor_status, context_handle,
major_status = GSS_S_FAILURE;
goto fail;
}
- if ((code = kg_init_name(context, ticket->server, NULL, 0, &ctx->here))) {
+ if ((code = kg_init_name(context, ticket->server, NULL, NULL, NULL, 0,
+ &ctx->here))) {
major_status = GSS_S_FAILURE;
goto fail;
}
@@ -927,7 +934,7 @@ kg_accept_krb5(minor_status, context_handle,
major_status = GSS_S_FAILURE;
goto fail;
}
- if ((code = kg_init_name(context, authdat->client,
+ if ((code = kg_init_name(context, authdat->client, NULL, NULL,
ad_context, KG_INIT_NAME_NO_COPY, &ctx->there))) {
major_status = GSS_S_FAILURE;
goto fail;
@@ -1269,7 +1276,6 @@ fail:
krb_error_data.error = code;
(void) krb5_us_timeofday(context, &krb_error_data.stime,
&krb_error_data.susec);
- krb_error_data.server = cred->name ? cred->name->princ : NULL;
code = krb5_mk_error(context, &krb_error_data, &scratch);
if (code)
diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
index 4fe9ebcea..ae34f95cc 100644
--- a/src/lib/gssapi/krb5/acquire_cred.c
+++ b/src/lib/gssapi/krb5/acquire_cred.c
@@ -128,6 +128,59 @@ gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status,
return GSS_S_COMPLETE;
}
+/* Try to verify that keytab contains at least one entry for name. Return 0 if
+ * it does, KRB5_KT_NOTFOUND if it doesn't, or another error as appropriate. */
+static krb5_error_code
+check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name)
+{
+ krb5_error_code code;
+ krb5_keytab_entry ent;
+ krb5_kt_cursor cursor;
+ krb5_principal accprinc = NULL;
+ krb5_boolean match;
+ char *princname;
+
+ if (name->service == NULL) {
+ code = krb5_kt_get_entry(context, kt, name->princ, 0, 0, &ent);
+ if (code == 0)
+ krb5_kt_free_entry(context, &ent);
+ return code;
+ }
+
+ /* If we can't iterate through the keytab, skip this check. */
+ if (kt->ops->start_seq_get == NULL)
+ return 0;
+
+ /* Get the partial principal for the acceptor name. */
+ code = kg_acceptor_princ(context, name, &accprinc);
+ if (code)
+ return code;
+
+ /* Scan the keytab for host-based entries matching accprinc. */
+ code = krb5_kt_start_seq_get(context, kt, &cursor);
+ if (code)
+ goto cleanup;
+ while ((code = krb5_kt_next_entry(context, kt, &ent, &cursor)) == 0) {
+ match = krb5_sname_match(context, accprinc, ent.principal);
+ (void)krb5_free_keytab_entry_contents(context, &ent);
+ if (match)
+ break;
+ }
+ (void)krb5_kt_end_seq_get(context, kt, &cursor);
+ if (code == KRB5_KT_END) {
+ code = KRB5_KT_NOTFOUND;
+ if (krb5_unparse_name(context, accprinc, &princname) == 0) {
+ krb5_set_error_message(context, code, "No key table entry "
+ "found matching %s", princname);
+ free(princname);
+ }
+ }
+
+cleanup:
+ krb5_free_principal(context, accprinc);
+ return code;
+}
+
/* get credentials corresponding to a key in the krb5 keytab.
If successful, set the keytab-specific fields in cred
*/
@@ -135,13 +188,12 @@ gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status,
static OM_uint32
acquire_accept_cred(krb5_context context,
OM_uint32 *minor_status,
- krb5_principal desired_princ,
+ krb5_gss_name_t desired_name,
krb5_keytab req_keytab,
krb5_gss_cred_id_rec *cred)
{
krb5_error_code code;
krb5_keytab kt;
- krb5_keytab_entry entry;
assert(cred->keytab == NULL);
@@ -174,8 +226,8 @@ acquire_accept_cred(krb5_context context,
return GSS_S_CRED_UNAVAIL;
}
- if (desired_princ != NULL) {
- code = krb5_kt_get_entry(context, kt, desired_princ, 0, 0, &entry);
+ if (desired_name != NULL) {
+ code = check_keytab(context, kt, desired_name);
if (code) {
krb5_kt_close(context, kt);
if (code == KRB5_KT_NOTFOUND) {
@@ -187,18 +239,16 @@ acquire_accept_cred(krb5_context context,
*minor_status = code;
return GSS_S_CRED_UNAVAIL;
}
- krb5_kt_free_entry(context, &entry);
assert(cred->name == NULL);
- code = kg_init_name(context, desired_princ, NULL, 0, &cred->name);
+ code = kg_duplicate_name(context, desired_name, 0, &cred->name);
if (code) {
*minor_status = code;
return GSS_S_FAILURE;
}
/* Open the replay cache for this principal. */
- code = krb5_get_server_rcache(context,
- krb5_princ_component(context, desired_princ, 0),
+ code = krb5_get_server_rcache(context, &desired_name->princ->data[0],
&cred->rcache);
if (code) {
*minor_status = code;
@@ -376,7 +426,7 @@ acquire_init_cred(krb5_context context,
* cred->name to the credentials cache principal name.
*/
if (cred->name == NULL) {
- if ((code = kg_init_name(context, ccache_princ, NULL,
+ if ((code = kg_init_name(context, ccache_princ, NULL, NULL, NULL,
KG_INIT_NAME_NO_COPY, &cred->name))) {
krb5_free_principal(context, ccache_princ);
krb5_cc_close(context, ccache);
@@ -511,9 +561,9 @@ acquire_cred(OM_uint32 *minor_status,
{
krb5_context context = NULL;
krb5_gss_cred_id_t cred = NULL;
+ krb5_gss_name_t name = (krb5_gss_name_t)args->desired_name;
OM_uint32 ret;
krb5_error_code code = 0;
- krb5_principal desired_princ = NULL;
/* make sure all outputs are valid */
*output_cred_handle = GSS_C_NO_CREDENTIAL;
@@ -536,7 +586,7 @@ acquire_cred(OM_uint32 *minor_status,
cred->usage = args->cred_usage;
cred->name = NULL;
cred->iakerb_mech = args->iakerb;
- cred->default_identity = (args->desired_name == GSS_C_NO_NAME);
+ cred->default_identity = (name == NULL);
#ifndef LEAN_CLIENT
cred->keytab = NULL;
#endif /* LEAN_CLIENT */
@@ -558,18 +608,14 @@ acquire_cred(OM_uint32 *minor_status,
goto error_out;
}
- if (args->desired_name != GSS_C_NO_NAME)
- desired_princ = ((krb5_gss_name_t)args->desired_name)->princ;
-
#ifndef LEAN_CLIENT
/*
* If requested, acquire credentials for accepting. This will fill
* in cred->name if desired_princ is specified.
*/
if (args->cred_usage == GSS_C_ACCEPT || args->cred_usage == GSS_C_BOTH) {
- ret = acquire_accept_cred(context, minor_status,
- desired_princ,
- args->keytab, cred);
+ ret = acquire_accept_cred(context, minor_status, name, args->keytab,
+ cred);
if (ret != GSS_S_COMPLETE)
goto error_out;
}
@@ -581,7 +627,8 @@ acquire_cred(OM_uint32 *minor_status,
*/
if (args->cred_usage == GSS_C_INITIATE || args->cred_usage == GSS_C_BOTH) {
ret = acquire_init_cred(context, minor_status, args->ccache,
- desired_princ, args->password, cred);
+ name ? name->princ : NULL, args->password,
+ cred);
if (ret != GSS_S_COMPLETE)
goto error_out;
}
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index a0e60be5e..6649331e7 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -158,8 +158,10 @@ enum qop {
/** internal types **/
typedef struct _krb5_gss_name_rec {
- krb5_principal princ; /* immutable */
- k5_mutex_t lock; /* protects ad_context only for now */
+ krb5_principal princ; /* immutable */
+ char *service; /* immutable */
+ char *host; /* immutable */
+ k5_mutex_t lock; /* protects ad_context only for now */
krb5_authdata_context ad_context;
} krb5_gss_name_rec, *krb5_gss_name_t;
@@ -893,11 +895,9 @@ int gss_krb5int_rotate_left (void *ptr, size_t bufsiz, size_t rc);
#define KG_INIT_NAME_NO_COPY 0x2
krb5_error_code
-kg_init_name(krb5_context context,
- krb5_principal principal,
- krb5_authdata_context ad_context,
- krb5_flags flags,
- krb5_gss_name_t *name);
+kg_init_name(krb5_context context, krb5_principal principal,
+ char *service, char *host, krb5_authdata_context ad_context,
+ krb5_flags flags, krb5_gss_name_t *name);
krb5_error_code
kg_release_name(krb5_context context,
@@ -915,6 +915,10 @@ kg_compare_name(krb5_context context,
krb5_gss_name_t name1,
krb5_gss_name_t name2);
+krb5_boolean
+kg_acceptor_princ(krb5_context context, krb5_gss_name_t name,
+ krb5_principal *princ_out);
+
OM_uint32
krb5_gss_display_name_ext(OM_uint32 *minor_status,
gss_name_t name,
diff --git a/src/lib/gssapi/krb5/import_name.c b/src/lib/gssapi/krb5/import_name.c
index 0f36721c1..2ba178a04 100644
--- a/src/lib/gssapi/krb5/import_name.c
+++ b/src/lib/gssapi/krb5/import_name.c
@@ -78,6 +78,44 @@ import_name_composite(krb5_context context,
return 0;
}
+/* Split a host-based name "service[@host]" into allocated strings
+ * placed in *service_out and *host_out (possibly NULL). */
+static krb5_error_code
+parse_hostbased(const char *str, size_t len,
+ char **service_out, char **host_out)
+{
+ const char *at;
+ size_t servicelen, hostlen;
+ char *service, *host = NULL;
+
+ *service_out = *host_out = NULL;
+
+ /* Find the bound of the service name and copy it. */
+ at = memchr(str, '@', len);
+ servicelen = (at == NULL) ? len : (size_t)(at - str);
+ service = xmalloc(servicelen + 1);
+ if (service == NULL)
+ return ENOMEM;
+ memcpy(service, str, servicelen);
+ service[servicelen] = '\0';
+
+ /* If present, copy the hostname. */
+ if (at != NULL) {
+ hostlen = len - servicelen - 1;
+ host = malloc(hostlen + 1);
+ if (host == NULL) {
+ free(service);
+ return ENOMEM;
+ }
+ memcpy(host, at + 1, hostlen);
+ host[hostlen] = '\0';
+ }
+
+ *service_out = service;
+ *host_out = host;
+ return 0;
+}
+
OM_uint32
krb5_gss_import_name(minor_status, input_name_buffer,
input_name_type, output_name)
@@ -90,7 +128,7 @@ krb5_gss_import_name(minor_status, input_name_buffer,
krb5_principal princ = NULL;
krb5_error_code code;
unsigned char *cp, *end;
- char *tmp = NULL, *tmp2 = NULL, *stringrep;
+ char *tmp = NULL, *tmp2 = NULL, *service = NULL, *host = NULL, *stringrep;
ssize_t length;
#ifndef NO_PASSWORD
struct passwd *pw;
@@ -110,21 +148,17 @@ krb5_gss_import_name(minor_status, input_name_buffer,
if ((input_name_type != GSS_C_NULL_OID) &&
(g_OID_equal(input_name_type, gss_nt_service_name) ||
g_OID_equal(input_name_type, gss_nt_service_name_v2))) {
- char *service, *host;
-
- tmp = k5alloc(input_name_buffer->length + 1, &code);
- if (tmp == NULL)
+ /* Split the name into service and host (or NULL). */
+ code = parse_hostbased(input_name_buffer->value,
+ input_name_buffer->length, &service, &host);
+ if (code)
goto cleanup;
- memcpy(tmp, input_name_buffer->value, input_name_buffer->length);
- tmp[input_name_buffer->length] = '\0';
-
- service = tmp;
- if ((host = strchr(tmp, '@'))) {
- *host = '\0';
- host++;
- }
-
+ /*
+ * Compute the initiator target name. In some cases this is a waste of
+ * getaddrinfo/getnameinfo queries, but computing the name when we need
+ * it would require a lot of code changes.
+ */
code = krb5_sname_to_principal(context, host, service, KRB5_NT_SRV_HST,
&princ);
if (code)
@@ -271,12 +305,13 @@ krb5_gss_import_name(minor_status, input_name_buffer,
}
/* Create a name and save it in the validation database. */
- code = kg_init_name(context, princ, ad_context,
+ code = kg_init_name(context, princ, service, host, ad_context,
KG_INIT_NAME_INTERN | KG_INIT_NAME_NO_COPY, &name);
if (code)
goto cleanup;
princ = NULL;
ad_context = NULL;
+ service = host = NULL;
*output_name = (gss_name_t)name;
status = GSS_S_COMPLETE;
@@ -289,5 +324,7 @@ cleanup:
krb5_free_context(context);
free(tmp);
free(tmp2);
+ free(service);
+ free(host);
return status;
}
diff --git a/src/lib/gssapi/krb5/naming_exts.c b/src/lib/gssapi/krb5/naming_exts.c
index 8cb21bf60..d1940123f 100644
--- a/src/lib/gssapi/krb5/naming_exts.c
+++ b/src/lib/gssapi/krb5/naming_exts.c
@@ -33,11 +33,9 @@
#include <stdarg.h>
krb5_error_code
-kg_init_name(krb5_context context,
- krb5_principal principal,
- krb5_authdata_context ad_context,
- krb5_flags flags,
- krb5_gss_name_t *ret_name)
+kg_init_name(krb5_context context, krb5_principal principal,
+ char *service, char *host, krb5_authdata_context ad_context,
+ krb5_flags flags, krb5_gss_name_t *ret_name)
{
krb5_error_code code;
krb5_gss_name_t name;
@@ -71,8 +69,23 @@ kg_init_name(krb5_context context,
if (code != 0)
goto cleanup;
}
+
+ code = ENOMEM;
+ if (service != NULL) {
+ name->service = strdup(service);
+ if (name->service == NULL)
+ goto cleanup;
+ }
+ if (host != NULL) {
+ name->host = strdup(host);
+ if (name->host == NULL)
+ goto cleanup;
+ }
+ code = 0;
} else {
name->princ = principal;
+ name->service = service;
+ name->host = host;
name->ad_context = ad_context;
}
@@ -100,6 +113,8 @@ kg_release_name(krb5_context context,
if (flags & KG_INIT_NAME_INTERN)
kg_delete_name((gss_name_t)*name);
krb5_free_principal(context, (*name)->princ);
+ free((*name)->service);
+ free((*name)->host);
krb5_authdata_context_free(context, (*name)->ad_context);
k5_mutex_destroy(&(*name)->lock);
free(*name);
@@ -121,7 +136,7 @@ kg_duplicate_name(krb5_context context,
if (code != 0)
return code;
- code = kg_init_name(context, src->princ,
+ code = kg_init_name(context, src->princ, src->service, src->host,
src->ad_context, flags, dst);
k5_mutex_unlock(&src->lock);
@@ -138,6 +153,45 @@ kg_compare_name(krb5_context context,
return krb5_principal_compare(context, name1->princ, name2->princ);
}
+/* Determine the principal to use for an acceptor name, which is different from
+ * name->princ for host-based names. */
+krb5_boolean
+kg_acceptor_princ(krb5_context context, krb5_gss_name_t name,
+ krb5_principal *princ_out)
+{
+ krb5_error_code code;
+ const char *host;
+ char *tmp = NULL;
+
+ *princ_out = NULL;
+ if (name == NULL)
+ return 0;
+
+ /* If it's not a host-based name, just copy name->princ. */
+ if (name->service == NULL)
+ return krb5_copy_principal(context, name->princ, princ_out);
+
+ if (name->host != NULL && name->princ->length == 2) {
+ /* If a host was given, we have to use the canonicalized form of it (as
+ * given by krb5_sname_to_principal) for backward compatibility. */
+ const krb5_data *d = &name->princ->data[1];
+ tmp = k5alloc(d->length + 1, &code);
+ if (tmp == NULL)
+ return ENOMEM;
+ memcpy(tmp, d->data, d->length);
+ tmp[d->length] = '\0';
+ host = tmp;
+ } else /* No host was given; use an empty string. */
+ host = "";
+
+ code = krb5_build_principal(context, princ_out, 0, "", name->service, host,
+ (char *)NULL);
+ if (code == 0)
+ (*princ_out)->type = KRB5_NT_SRV_HST;
+ free(tmp);
+ return code;
+}
+
static OM_uint32
kg_map_name_error(OM_uint32 *minor_status, krb5_error_code code)
{
diff --git a/src/lib/gssapi/krb5/s4u_gss_glue.c b/src/lib/gssapi/krb5/s4u_gss_glue.c
index ac07dad5d..4cebf7fa6 100644
--- a/src/lib/gssapi/krb5/s4u_gss_glue.c
+++ b/src/lib/gssapi/krb5/s4u_gss_glue.c
@@ -221,7 +221,8 @@ kg_compose_deleg_cred(OM_uint32 *minor_status,
cred->tgt_expire = impersonator_cred->tgt_expire;
- code = kg_init_name(context, subject_creds->client, NULL, 0, &cred->name);
+ code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0,
+ &cred->name);
if (code != 0)
goto cleanup;
diff --git a/src/lib/gssapi/krb5/ser_sctx.c b/src/lib/gssapi/krb5/ser_sctx.c
index ae6350cce..dc679c9d8 100644
--- a/src/lib/gssapi/krb5/ser_sctx.c
+++ b/src/lib/gssapi/krb5/ser_sctx.c
@@ -669,7 +669,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
(krb5_pointer *) &princ,
&bp, &remain);
if (kret == 0) {
- kret = kg_init_name(kcontext, princ, NULL,
+ kret = kg_init_name(kcontext, princ, NULL, NULL, NULL,
KG_INIT_NAME_NO_COPY, &ctx->here);
if (kret)
krb5_free_principal(kcontext, princ);
@@ -681,7 +681,7 @@ kg_ctx_internalize(kcontext, argp, buffer, lenremain)
(krb5_pointer *) &princ,
&bp, &remain);
if (kret == 0) {
- kret = kg_init_name(kcontext, princ, NULL,
+ kret = kg_init_name(kcontext, princ, NULL, NULL, NULL,
KG_INIT_NAME_NO_COPY, &ctx->there);
if (kret)
krb5_free_principal(kcontext, princ);
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
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index e74943700..8c8a8f503 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -405,3 +405,6 @@ EXPORTS
krb5_sendto_kdc @381 ; PRIVATE GSSAPI
krb5int_copy_data_contents_add0 @382 ; PRIVATE GSSAPI
krb5int_free_data_list @383 ; PRIVATE GSSAPI
+
+; new in 1.10
+ krb5_sname_match @384