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/gssapi | |
parent | 1b46b254240d95534b7a3ee1f45ac85f6c38db1b (diff) | |
download | krb5-66587fcd6380eac2c53674df4f64a827d337aee5.zip krb5-66587fcd6380eac2c53674df4f64a827d337aee5.tar.gz krb5-66587fcd6380eac2c53674df4f64a827d337aee5.tar.bz2 |
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/gssapi')
-rw-r--r-- | src/lib/gssapi/krb5/accept_sec_context.c | 24 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/acquire_cred.c | 83 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/gssapiP_krb5.h | 18 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/import_name.c | 67 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/naming_exts.c | 66 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/s4u_gss_glue.c | 3 | ||||
-rw-r--r-- | src/lib/gssapi/krb5/ser_sctx.c | 4 |
7 files changed, 207 insertions, 58 deletions
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c index c3cb2f1..0344ebd 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 4fe9ebc..ae34f95 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 a0e60be..6649331 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 0f36721..2ba178a 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 8cb21bf..d194012 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 ac07dad..4cebf7f 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 ae6350c..dc679c9 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); |