aboutsummaryrefslogtreecommitdiff
path: root/src/lib/gssapi
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/gssapi
parent1b46b254240d95534b7a3ee1f45ac85f6c38db1b (diff)
downloadkrb5-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.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
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);