Support host-based GSS initiator names
This commit is contained in:
parent
042ca4af99
commit
0dd40e4ff0
84
Add-hostname-canonicalization-helper-to-k5test.py.patch
Normal file
84
Add-hostname-canonicalization-helper-to-k5test.py.patch
Normal file
@ -0,0 +1,84 @@
|
||||
From 3204462c480484845513f2d7f323e367efde62cd Mon Sep 17 00:00:00 2001
|
||||
From: Greg Hudson <ghudson@mit.edu>
|
||||
Date: Fri, 15 Jan 2021 14:43:34 -0500
|
||||
Subject: [PATCH] Add hostname canonicalization helper to k5test.py
|
||||
|
||||
To facilitate fallback tests, add a canonicalize_hostname() function
|
||||
to k5test.py which works similarly to krb5_expand_hostname(). Use it
|
||||
in t_gssapi.py for the recently-added acceptor name fallback test.
|
||||
|
||||
(cherry picked from commit 225fffe4e912772acea3a01d45bafb60bfb80948)
|
||||
---
|
||||
src/tests/gssapi/t_gssapi.py | 11 +++--------
|
||||
src/util/k5test.py | 22 ++++++++++++++++++++++
|
||||
2 files changed, 25 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/src/tests/gssapi/t_gssapi.py b/src/tests/gssapi/t_gssapi.py
|
||||
index 1af6f31c2..e22cec427 100755
|
||||
--- a/src/tests/gssapi/t_gssapi.py
|
||||
+++ b/src/tests/gssapi/t_gssapi.py
|
||||
@@ -8,7 +8,7 @@ for realm in multipass_realms():
|
||||
realm.run(['./t_iov', '-s', 'p:' + realm.host_princ])
|
||||
realm.run(['./t_pcontok', 'p:' + realm.host_princ])
|
||||
|
||||
-realm = K5Realm(krb5_conf={'libdefaults': {'rdns': 'false'}})
|
||||
+realm = K5Realm()
|
||||
|
||||
# Test gss_add_cred().
|
||||
realm.run(['./t_add_cred'])
|
||||
@@ -62,13 +62,8 @@ realm.run(['./t_accname', 'p:host/-nomatch-',
|
||||
expected_msg=' not found in keytab')
|
||||
|
||||
# If possible, test with an acceptor name requiring fallback to match
|
||||
-# against a keytab entry. Forward-canonicalize the hostname, relying
|
||||
-# on the rdns=false realm setting.
|
||||
-try:
|
||||
- ai = socket.getaddrinfo(hostname, None, 0, 0, 0, socket.AI_CANONNAME)
|
||||
- (family, socktype, proto, canonname, sockaddr) = ai[0]
|
||||
-except socket.gaierror:
|
||||
- canonname = hostname
|
||||
+# against a keytab entry.
|
||||
+canonname = canonicalize_hostname(hostname)
|
||||
if canonname != hostname:
|
||||
os.rename(realm.keytab, realm.keytab + '.save')
|
||||
canonprinc = 'host/' + canonname
|
||||
diff --git a/src/util/k5test.py b/src/util/k5test.py
|
||||
index 789b0f4b9..251d11a9d 100644
|
||||
--- a/src/util/k5test.py
|
||||
+++ b/src/util/k5test.py
|
||||
@@ -155,6 +155,10 @@ Scripts may use the following functions and variables:
|
||||
* password(name): Return a weakly random password based on name. The
|
||||
password will be consistent across calls with the same name.
|
||||
|
||||
+* canonicalize_hostname(name, rdns=True): Return the DNS
|
||||
+ canonicalization of name, optionally using reverse DNS. On error,
|
||||
+ return name converted to lowercase.
|
||||
+
|
||||
* stop_daemon(proc): Stop a daemon process started with
|
||||
realm.start_server() or realm.start_in_inetd(). Only necessary if
|
||||
the port needs to be reused; daemon processes will be stopped
|
||||
@@ -458,6 +462,24 @@ def password(name):
|
||||
return name + str(os.getpid())
|
||||
|
||||
|
||||
+def canonicalize_hostname(name, rdns=True):
|
||||
+ """Canonicalize name using DNS, optionally with reverse DNS."""
|
||||
+ try:
|
||||
+ ai = socket.getaddrinfo(name, None, 0, 0, 0, socket.AI_CANONNAME)
|
||||
+ except socket.gaierror as e:
|
||||
+ return name.lower()
|
||||
+ (family, socktype, proto, canonname, sockaddr) = ai[0]
|
||||
+
|
||||
+ if not rdns:
|
||||
+ return canonname.lower()
|
||||
+
|
||||
+ try:
|
||||
+ rname = socket.getnameinfo(sockaddr, socket.NI_NAMEREQD)
|
||||
+ except socket.gaierror:
|
||||
+ return canonname.lower()
|
||||
+ return rname[0].lower()
|
||||
+
|
||||
+
|
||||
# Exit handler which ensures processes are cleaned up and, on failure,
|
||||
# prints messages to help developers debug the problem.
|
||||
def _onexit():
|
578
Support-host-based-GSS-initiator-names.patch
Normal file
578
Support-host-based-GSS-initiator-names.patch
Normal file
@ -0,0 +1,578 @@
|
||||
From 067e3a509442d81d1a31dd4bcbcc190f55369cc9 Mon Sep 17 00:00:00 2001
|
||||
From: Greg Hudson <ghudson@mit.edu>
|
||||
Date: Fri, 15 Jan 2021 13:51:34 -0500
|
||||
Subject: [PATCH] Support host-based GSS initiator names
|
||||
|
||||
When checking if we can get initial credentials in the GSS krb5 mech,
|
||||
use krb5_kt_have_match() to support fallback iteration. When scanning
|
||||
the ccache or getting initial credentials, rewrite cred->name->princ
|
||||
to the canonical client name. When a name check is necessary (such as
|
||||
when the caller specifies both a name and ccache), use a new internal
|
||||
API k5_sname_compare() to support fallback iteration. Add fallback
|
||||
iteration to krb5_cc_cache_match() to allow host-based names to be
|
||||
canonicalized against the cache collection.
|
||||
|
||||
Create and store the matching principal for acceptor names in
|
||||
acquire_accept_cred() so that it isn't affected by changes in
|
||||
cred->name->princ during acquire_init_cred().
|
||||
|
||||
ticket: 8978 (new)
|
||||
(cherry picked from commit c374ab40dd059a5938ffc0440d87457ac5da3a46)
|
||||
---
|
||||
src/include/k5-int.h | 9 +++
|
||||
src/include/k5-trace.h | 3 +
|
||||
src/lib/gssapi/krb5/accept_sec_context.c | 15 +---
|
||||
src/lib/gssapi/krb5/acquire_cred.c | 89 ++++++++++++++----------
|
||||
src/lib/gssapi/krb5/gssapiP_krb5.h | 1 +
|
||||
src/lib/gssapi/krb5/rel_cred.c | 1 +
|
||||
src/lib/krb5/ccache/cccursor.c | 57 +++++++++++----
|
||||
src/lib/krb5/libkrb5.exports | 1 +
|
||||
src/lib/krb5/os/sn2princ.c | 23 +++++-
|
||||
src/lib/krb5_32.def | 1 +
|
||||
src/tests/gssapi/t_client_keytab.py | 44 ++++++++++++
|
||||
src/tests/gssapi/t_credstore.py | 32 +++++++++
|
||||
12 files changed, 214 insertions(+), 62 deletions(-)
|
||||
|
||||
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
|
||||
index efb523689..46f2ce2d3 100644
|
||||
--- a/src/include/k5-int.h
|
||||
+++ b/src/include/k5-int.h
|
||||
@@ -2411,4 +2411,13 @@ void k5_change_error_message_code(krb5_context ctx, krb5_error_code oldcode,
|
||||
#define k5_prependmsg krb5_prepend_error_message
|
||||
#define k5_wrapmsg krb5_wrap_error_message
|
||||
|
||||
+/*
|
||||
+ * Like krb5_principal_compare(), but with canonicalization of sname if
|
||||
+ * fallback is enabled. This function should be avoided if multiple matches
|
||||
+ * are required, since repeated canonicalization is inefficient.
|
||||
+ */
|
||||
+krb5_boolean
|
||||
+k5_sname_compare(krb5_context context, krb5_const_principal sname,
|
||||
+ krb5_const_principal princ);
|
||||
+
|
||||
#endif /* _KRB5_INT_H */
|
||||
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
|
||||
index b3e039dc8..79b5a7a85 100644
|
||||
--- a/src/include/k5-trace.h
|
||||
+++ b/src/include/k5-trace.h
|
||||
@@ -105,6 +105,9 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
|
||||
|
||||
#endif /* DISABLE_TRACING */
|
||||
|
||||
+#define TRACE_CC_CACHE_MATCH(c, princ, ret) \
|
||||
+ TRACE(c, "Matching {princ} in collection with result: {kerr}", \
|
||||
+ princ, ret)
|
||||
#define TRACE_CC_DESTROY(c, cache) \
|
||||
TRACE(c, "Destroying ccache {ccache}", cache)
|
||||
#define TRACE_CC_GEN_NEW(c, cache) \
|
||||
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
|
||||
index fcf2c2152..a1d7e0d96 100644
|
||||
--- a/src/lib/gssapi/krb5/accept_sec_context.c
|
||||
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
|
||||
@@ -683,7 +683,6 @@ 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;
|
||||
krb5_ap_req *request = NULL;
|
||||
|
||||
code = krb5int_accessor (&kaccess, KRB5INT_ACCESS_VERSION);
|
||||
@@ -849,17 +848,9 @@ kg_accept_krb5(minor_status, context_handle,
|
||||
}
|
||||
}
|
||||
|
||||
- if (!cred->default_identity) {
|
||||
- if ((code = kg_acceptor_princ(context, cred->name, &accprinc))) {
|
||||
- major_status = GSS_S_FAILURE;
|
||||
- goto fail;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- code = krb5_rd_req_decoded(context, &auth_context, request, accprinc,
|
||||
- cred->keytab, &ap_req_options, NULL);
|
||||
-
|
||||
- krb5_free_principal(context, accprinc);
|
||||
+ code = krb5_rd_req_decoded(context, &auth_context, request,
|
||||
+ cred->acceptor_mprinc, cred->keytab,
|
||||
+ &ap_req_options, NULL);
|
||||
if (code) {
|
||||
major_status = GSS_S_FAILURE;
|
||||
goto fail;
|
||||
diff --git a/src/lib/gssapi/krb5/acquire_cred.c b/src/lib/gssapi/krb5/acquire_cred.c
|
||||
index 632ee7def..e226a0269 100644
|
||||
--- a/src/lib/gssapi/krb5/acquire_cred.c
|
||||
+++ b/src/lib/gssapi/krb5/acquire_cred.c
|
||||
@@ -123,11 +123,11 @@ gss_krb5int_register_acceptor_identity(OM_uint32 *minor_status,
|
||||
/* 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)
|
||||
+check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name,
|
||||
+ krb5_principal mprinc)
|
||||
{
|
||||
krb5_error_code code;
|
||||
krb5_keytab_entry ent;
|
||||
- krb5_principal accprinc = NULL;
|
||||
char *princname;
|
||||
|
||||
if (name->service == NULL) {
|
||||
@@ -141,21 +141,15 @@ check_keytab(krb5_context context, krb5_keytab kt, krb5_gss_name_t name)
|
||||
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 = k5_kt_have_match(context, kt, accprinc);
|
||||
+ /* Scan the keytab for host-based entries matching mprinc. */
|
||||
+ code = k5_kt_have_match(context, kt, mprinc);
|
||||
if (code == KRB5_KT_NOTFOUND) {
|
||||
- if (krb5_unparse_name(context, accprinc, &princname) == 0) {
|
||||
+ if (krb5_unparse_name(context, mprinc, &princname) == 0) {
|
||||
k5_setmsg(context, code, _("No key table entry found matching %s"),
|
||||
princname);
|
||||
free(princname);
|
||||
}
|
||||
}
|
||||
- krb5_free_principal(context, accprinc);
|
||||
return code;
|
||||
}
|
||||
|
||||
@@ -202,8 +196,14 @@ acquire_accept_cred(krb5_context context, OM_uint32 *minor_status,
|
||||
}
|
||||
|
||||
if (cred->name != NULL) {
|
||||
+ code = kg_acceptor_princ(context, cred->name, &cred->acceptor_mprinc);
|
||||
+ if (code) {
|
||||
+ major = GSS_S_FAILURE;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+
|
||||
/* Make sure we have keys matching the desired name in the keytab. */
|
||||
- code = check_keytab(context, kt, cred->name);
|
||||
+ code = check_keytab(context, kt, cred->name, cred->acceptor_mprinc);
|
||||
if (code) {
|
||||
if (code == KRB5_KT_NOTFOUND) {
|
||||
k5_change_error_message_code(context, code, KG_KEYTAB_NOMATCH);
|
||||
@@ -324,7 +324,6 @@ static krb5_boolean
|
||||
can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred)
|
||||
{
|
||||
krb5_error_code code;
|
||||
- krb5_keytab_entry entry;
|
||||
|
||||
if (cred->password != NULL)
|
||||
return TRUE;
|
||||
@@ -336,20 +335,21 @@ can_get_initial_creds(krb5_context context, krb5_gss_cred_id_rec *cred)
|
||||
if (cred->name == NULL)
|
||||
return !krb5_kt_have_content(context, cred->client_keytab);
|
||||
|
||||
- /* Check if we have a keytab key for the client principal. */
|
||||
- code = krb5_kt_get_entry(context, cred->client_keytab, cred->name->princ,
|
||||
- 0, 0, &entry);
|
||||
- if (code) {
|
||||
- krb5_clear_error_message(context);
|
||||
- return FALSE;
|
||||
- }
|
||||
- krb5_free_keytab_entry_contents(context, &entry);
|
||||
- return TRUE;
|
||||
+ /*
|
||||
+ * Check if we have a keytab key for the client principal. This is a bit
|
||||
+ * more permissive than we really want because krb5_kt_have_match()
|
||||
+ * supports wildcarding and obeys ignore_acceptor_hostname, but that should
|
||||
+ * generally be harmless.
|
||||
+ */
|
||||
+ code = k5_kt_have_match(context, cred->client_keytab, cred->name->princ);
|
||||
+ return code == 0;
|
||||
}
|
||||
|
||||
-/* Scan cred->ccache for name, expiry time, impersonator, refresh time. */
|
||||
+/* Scan cred->ccache for name, expiry time, impersonator, refresh time. If
|
||||
+ * check_name is true, verify the cache name against the credential name. */
|
||||
static krb5_error_code
|
||||
-scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred)
|
||||
+scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred,
|
||||
+ krb5_boolean check_name)
|
||||
{
|
||||
krb5_error_code code;
|
||||
krb5_ccache ccache = cred->ccache;
|
||||
@@ -365,23 +365,31 @@ scan_ccache(krb5_context context, krb5_gss_cred_id_rec *cred)
|
||||
if (code)
|
||||
return code;
|
||||
|
||||
- /* Credentials cache principal must match the initiator name. */
|
||||
code = krb5_cc_get_principal(context, ccache, &ccache_princ);
|
||||
if (code != 0)
|
||||
goto cleanup;
|
||||
- if (cred->name != NULL &&
|
||||
- !krb5_principal_compare(context, ccache_princ, cred->name->princ)) {
|
||||
- code = KG_CCACHE_NOMATCH;
|
||||
- goto cleanup;
|
||||
- }
|
||||
|
||||
- /* Save the ccache principal as the credential name if not already set. */
|
||||
- if (!cred->name) {
|
||||
+ if (cred->name == NULL) {
|
||||
+ /* Save the ccache principal as the credential name. */
|
||||
code = kg_init_name(context, ccache_princ, NULL, NULL, NULL,
|
||||
KG_INIT_NAME_NO_COPY, &cred->name);
|
||||
if (code)
|
||||
goto cleanup;
|
||||
ccache_princ = NULL;
|
||||
+ } else {
|
||||
+ /* Check against the desired name if needed. */
|
||||
+ if (check_name) {
|
||||
+ if (!k5_sname_compare(context, cred->name->princ, ccache_princ)) {
|
||||
+ code = KG_CCACHE_NOMATCH;
|
||||
+ goto cleanup;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Replace the credential name principal with the canonical client
|
||||
+ * principal, retaining acceptor_mprinc if set. */
|
||||
+ krb5_free_principal(context, cred->name->princ);
|
||||
+ cred->name->princ = ccache_princ;
|
||||
+ ccache_princ = NULL;
|
||||
}
|
||||
|
||||
assert(cred->name->princ != NULL);
|
||||
@@ -447,7 +455,7 @@ get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
|
||||
assert(cred->name != NULL && cred->ccache == NULL);
|
||||
#ifdef USE_LEASH
|
||||
code = get_ccache_leash(context, cred->name->princ, &cred->ccache);
|
||||
- return code ? code : scan_ccache(context, cred);
|
||||
+ return code ? code : scan_ccache(context, cred, TRUE);
|
||||
#else
|
||||
/* Check first whether we can acquire tickets, to avoid overwriting the
|
||||
* extended error message from krb5_cc_cache_match. */
|
||||
@@ -456,7 +464,7 @@ get_cache_for_name(krb5_context context, krb5_gss_cred_id_rec *cred)
|
||||
/* Look for an existing cache for the client principal. */
|
||||
code = krb5_cc_cache_match(context, cred->name->princ, &cred->ccache);
|
||||
if (code == 0)
|
||||
- return scan_ccache(context, cred);
|
||||
+ return scan_ccache(context, cred, FALSE);
|
||||
if (code != KRB5_CC_NOTFOUND || !can_get)
|
||||
return code;
|
||||
krb5_clear_error_message(context);
|
||||
@@ -633,6 +641,13 @@ get_initial_cred(krb5_context context, const struct verify_params *verify,
|
||||
kg_cred_set_initial_refresh(context, cred, &creds.times);
|
||||
cred->have_tgt = TRUE;
|
||||
cred->expire = creds.times.endtime;
|
||||
+
|
||||
+ /* Steal the canonical client principal name from creds and save it in the
|
||||
+ * credential name, retaining acceptor_mprinc if set. */
|
||||
+ krb5_free_principal(context, cred->name->princ);
|
||||
+ cred->name->princ = creds.client;
|
||||
+ creds.client = NULL;
|
||||
+
|
||||
krb5_free_cred_contents(context, &creds);
|
||||
cleanup:
|
||||
krb5_get_init_creds_opt_free(context, opt);
|
||||
@@ -721,7 +736,7 @@ acquire_init_cred(krb5_context context, OM_uint32 *minor_status,
|
||||
|
||||
if (cred->ccache != NULL) {
|
||||
/* The caller specified a ccache; check what's in it. */
|
||||
- code = scan_ccache(context, cred);
|
||||
+ code = scan_ccache(context, cred, TRUE);
|
||||
if (code == KRB5_FCC_NOFILE) {
|
||||
/* See if we can get initial creds. If the caller didn't specify
|
||||
* a name, pick one from the client keytab. */
|
||||
@@ -984,7 +999,7 @@ kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
|
||||
}
|
||||
}
|
||||
if (cred->ccache != NULL) {
|
||||
- code = scan_ccache(context, cred);
|
||||
+ code = scan_ccache(context, cred, FALSE);
|
||||
if (code)
|
||||
goto kerr;
|
||||
}
|
||||
@@ -996,7 +1011,7 @@ kg_cred_resolve(OM_uint32 *minor_status, krb5_context context,
|
||||
code = krb5int_cc_default(context, &cred->ccache);
|
||||
if (code)
|
||||
goto kerr;
|
||||
- code = scan_ccache(context, cred);
|
||||
+ code = scan_ccache(context, cred, FALSE);
|
||||
if (code == KRB5_FCC_NOFILE) {
|
||||
/* Default ccache doesn't exist; fall through to client keytab. */
|
||||
krb5_cc_close(context, cred->ccache);
|
||||
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
|
||||
index 3bacdcd35..fd7abbd77 100644
|
||||
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
|
||||
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
|
||||
@@ -175,6 +175,7 @@ typedef struct _krb5_gss_cred_id_rec {
|
||||
/* name/type of credential */
|
||||
gss_cred_usage_t usage;
|
||||
krb5_gss_name_t name;
|
||||
+ krb5_principal acceptor_mprinc;
|
||||
krb5_principal impersonator;
|
||||
unsigned int default_identity : 1;
|
||||
unsigned int iakerb_mech : 1;
|
||||
diff --git a/src/lib/gssapi/krb5/rel_cred.c b/src/lib/gssapi/krb5/rel_cred.c
|
||||
index a9515daf7..0da6c1b95 100644
|
||||
--- a/src/lib/gssapi/krb5/rel_cred.c
|
||||
+++ b/src/lib/gssapi/krb5/rel_cred.c
|
||||
@@ -72,6 +72,7 @@ krb5_gss_release_cred(minor_status, cred_handle)
|
||||
if (cred->name)
|
||||
kg_release_name(context, &cred->name);
|
||||
|
||||
+ krb5_free_principal(context, cred->acceptor_mprinc);
|
||||
krb5_free_principal(context, cred->impersonator);
|
||||
|
||||
if (cred->req_enctypes)
|
||||
diff --git a/src/lib/krb5/ccache/cccursor.c b/src/lib/krb5/ccache/cccursor.c
|
||||
index 8f5872116..760216d05 100644
|
||||
--- a/src/lib/krb5/ccache/cccursor.c
|
||||
+++ b/src/lib/krb5/ccache/cccursor.c
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "cc-int.h"
|
||||
#include "../krb/int-proto.h"
|
||||
+#include "../os/os-proto.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@@ -141,18 +142,18 @@ krb5_cccol_cursor_free(krb5_context context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-krb5_error_code KRB5_CALLCONV
|
||||
-krb5_cc_cache_match(krb5_context context, krb5_principal client,
|
||||
- krb5_ccache *cache_out)
|
||||
+static krb5_error_code
|
||||
+match_caches(krb5_context context, krb5_const_principal client,
|
||||
+ krb5_ccache *cache_out)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_cccol_cursor cursor;
|
||||
krb5_ccache cache = NULL;
|
||||
krb5_principal princ;
|
||||
- char *name;
|
||||
krb5_boolean eq;
|
||||
|
||||
*cache_out = NULL;
|
||||
+
|
||||
ret = krb5_cccol_cursor_new(context, &cursor);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -169,20 +170,52 @@ krb5_cc_cache_match(krb5_context context, krb5_principal client,
|
||||
krb5_cc_close(context, cache);
|
||||
}
|
||||
krb5_cccol_cursor_free(context, &cursor);
|
||||
+
|
||||
if (ret)
|
||||
return ret;
|
||||
- if (cache == NULL) {
|
||||
- ret = krb5_unparse_name(context, client, &name);
|
||||
- if (ret == 0) {
|
||||
- k5_setmsg(context, KRB5_CC_NOTFOUND,
|
||||
+ if (cache == NULL)
|
||||
+ return KRB5_CC_NOTFOUND;
|
||||
+
|
||||
+ *cache_out = cache;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+krb5_error_code KRB5_CALLCONV
|
||||
+krb5_cc_cache_match(krb5_context context, krb5_principal client,
|
||||
+ krb5_ccache *cache_out)
|
||||
+{
|
||||
+ krb5_error_code ret;
|
||||
+ struct canonprinc iter = { client, .subst_defrealm = TRUE };
|
||||
+ krb5_const_principal canonprinc = NULL;
|
||||
+ krb5_ccache cache = NULL;
|
||||
+ char *name;
|
||||
+
|
||||
+ *cache_out = NULL;
|
||||
+
|
||||
+ while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
|
||||
+ canonprinc != NULL) {
|
||||
+ ret = match_caches(context, canonprinc, &cache);
|
||||
+ if (ret != KRB5_CC_NOTFOUND)
|
||||
+ break;
|
||||
+ }
|
||||
+ free_canonprinc(&iter);
|
||||
+
|
||||
+ if (ret == 0 && canonprinc == NULL) {
|
||||
+ ret = KRB5_CC_NOTFOUND;
|
||||
+ if (krb5_unparse_name(context, client, &name) == 0) {
|
||||
+ k5_setmsg(context, ret,
|
||||
_("Can't find client principal %s in cache collection"),
|
||||
name);
|
||||
krb5_free_unparsed_name(context, name);
|
||||
}
|
||||
- ret = KRB5_CC_NOTFOUND;
|
||||
- } else
|
||||
- *cache_out = cache;
|
||||
- return ret;
|
||||
+ }
|
||||
+
|
||||
+ TRACE_CC_CACHE_MATCH(context, client, ret);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ *cache_out = cache;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
/* Store the error state for code from context into errsave, but only if code
|
||||
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
|
||||
index 9de0fcdb3..25141dfc5 100644
|
||||
--- a/src/lib/krb5/libkrb5.exports
|
||||
+++ b/src/lib/krb5/libkrb5.exports
|
||||
@@ -181,6 +181,7 @@ k5_size_authdata_context
|
||||
k5_size_context
|
||||
k5_size_keyblock
|
||||
k5_size_principal
|
||||
+k5_sname_compare
|
||||
k5_unmarshal_cred
|
||||
k5_unmarshal_princ
|
||||
k5_unwrap_cammac_svc
|
||||
diff --git a/src/lib/krb5/os/sn2princ.c b/src/lib/krb5/os/sn2princ.c
|
||||
index 8b7214189..c99b7da17 100644
|
||||
--- a/src/lib/krb5/os/sn2princ.c
|
||||
+++ b/src/lib/krb5/os/sn2princ.c
|
||||
@@ -277,7 +277,8 @@ k5_canonprinc(krb5_context context, struct canonprinc *iter,
|
||||
|
||||
/* If we're not doing fallback, the input principal is canonical. */
|
||||
if (context->dns_canonicalize_hostname != CANONHOST_FALLBACK ||
|
||||
- iter->princ->type != KRB5_NT_SRV_HST || iter->princ->length != 2) {
|
||||
+ iter->princ->type != KRB5_NT_SRV_HST || iter->princ->length != 2 ||
|
||||
+ iter->princ->data[1].length == 0) {
|
||||
*princ_out = (step == 1) ? iter->princ : NULL;
|
||||
return 0;
|
||||
}
|
||||
@@ -288,6 +289,26 @@ k5_canonprinc(krb5_context context, struct canonprinc *iter,
|
||||
return canonicalize_princ(context, iter, step == 2, princ_out);
|
||||
}
|
||||
|
||||
+krb5_boolean
|
||||
+k5_sname_compare(krb5_context context, krb5_const_principal sname,
|
||||
+ krb5_const_principal princ)
|
||||
+{
|
||||
+ krb5_error_code ret;
|
||||
+ struct canonprinc iter = { sname, .subst_defrealm = TRUE };
|
||||
+ krb5_const_principal canonprinc = NULL;
|
||||
+ krb5_boolean match = FALSE;
|
||||
+
|
||||
+ while ((ret = k5_canonprinc(context, &iter, &canonprinc)) == 0 &&
|
||||
+ canonprinc != NULL) {
|
||||
+ if (krb5_principal_compare(context, canonprinc, princ)) {
|
||||
+ match = TRUE;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ free_canonprinc(&iter);
|
||||
+ return match;
|
||||
+}
|
||||
+
|
||||
krb5_error_code KRB5_CALLCONV
|
||||
krb5_sname_to_principal(krb5_context context, const char *hostname,
|
||||
const char *sname, krb5_int32 type,
|
||||
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
|
||||
index 60b8dd311..cf690dbe4 100644
|
||||
--- a/src/lib/krb5_32.def
|
||||
+++ b/src/lib/krb5_32.def
|
||||
@@ -507,3 +507,4 @@ EXPORTS
|
||||
; new in 1.20
|
||||
krb5_marshal_credentials @472
|
||||
krb5_unmarshal_credentials @473
|
||||
+ k5_sname_compare @474 ; PRIVATE GSSAPI
|
||||
diff --git a/src/tests/gssapi/t_client_keytab.py b/src/tests/gssapi/t_client_keytab.py
|
||||
index 7847b3ecd..9a61d53b8 100755
|
||||
--- a/src/tests/gssapi/t_client_keytab.py
|
||||
+++ b/src/tests/gssapi/t_client_keytab.py
|
||||
@@ -141,5 +141,49 @@ msgs = ('Getting initial credentials for user/admin@KRBTEST.COM',
|
||||
'/Matching credential not found')
|
||||
realm.run(['./t_ccselect', phost], expected_code=1,
|
||||
expected_msg='Ticket expired', expected_trace=msgs)
|
||||
+realm.run([kdestroy, '-A'])
|
||||
+
|
||||
+# Test 19: host-based initiator name
|
||||
+mark('host-based initiator name')
|
||||
+hsvc = 'h:svc@' + hostname
|
||||
+svcprinc = 'svc/%s@%s' % (hostname, realm.realm)
|
||||
+realm.addprinc(svcprinc)
|
||||
+realm.extract_keytab(svcprinc, realm.client_keytab)
|
||||
+# On the first run we match against the keytab while getting tickets,
|
||||
+# substituting the default realm.
|
||||
+msgs = ('/Can\'t find client principal svc/%s@ in' % hostname,
|
||||
+ 'Getting initial credentials for svc/%s@' % hostname,
|
||||
+ 'Found entries for %s in keytab' % svcprinc,
|
||||
+ 'Retrieving %s from FILE:%s' % (svcprinc, realm.client_keytab),
|
||||
+ 'Storing %s -> %s in' % (svcprinc, realm.krbtgt_princ),
|
||||
+ 'Retrieving %s -> %s from' % (svcprinc, realm.krbtgt_princ),
|
||||
+ 'authenticator for %s -> %s' % (svcprinc, realm.host_princ))
|
||||
+realm.run(['./t_ccselect', phost, hsvc], expected_trace=msgs)
|
||||
+# On the second run we match against the collection.
|
||||
+msgs = ('Matching svc/%s@ in collection with result: 0' % hostname,
|
||||
+ 'Getting credentials %s -> %s' % (svcprinc, realm.host_princ),
|
||||
+ 'authenticator for %s -> %s' % (svcprinc, realm.host_princ))
|
||||
+realm.run(['./t_ccselect', phost, hsvc], expected_trace=msgs)
|
||||
+realm.run([kdestroy, '-A'])
|
||||
+
|
||||
+# Test 20: host-based initiator name with fallback
|
||||
+mark('host-based fallback initiator name')
|
||||
+canonname = canonicalize_hostname(hostname)
|
||||
+if canonname != hostname:
|
||||
+ hfsvc = 'h:fsvc@' + hostname
|
||||
+ canonprinc = 'fsvc/%s@%s' % (canonname, realm.realm)
|
||||
+ realm.addprinc(canonprinc)
|
||||
+ realm.extract_keytab(canonprinc, realm.client_keytab)
|
||||
+ msgs = ('/Can\'t find client principal fsvc/%s@ in' % hostname,
|
||||
+ 'Found entries for %s in keytab' % canonprinc,
|
||||
+ 'authenticator for %s -> %s' % (canonprinc, realm.host_princ))
|
||||
+ realm.run(['./t_ccselect', phost, hfsvc], expected_trace=msgs)
|
||||
+ msgs = ('Matching fsvc/%s@ in collection with result: 0' % hostname,
|
||||
+ 'Getting credentials %s -> %s' % (canonprinc, realm.host_princ))
|
||||
+ realm.run(['./t_ccselect', phost, hfsvc], expected_trace=msgs)
|
||||
+ realm.run([kdestroy, '-A'])
|
||||
+else:
|
||||
+ skipped('GSS initiator name fallback test',
|
||||
+ '%s does not canonicalize to a different name' % hostname)
|
||||
|
||||
success('Client keytab tests')
|
||||
diff --git a/src/tests/gssapi/t_credstore.py b/src/tests/gssapi/t_credstore.py
|
||||
index c11975bf5..9be57bb82 100644
|
||||
--- a/src/tests/gssapi/t_credstore.py
|
||||
+++ b/src/tests/gssapi/t_credstore.py
|
||||
@@ -15,6 +15,38 @@ msgs = ('Storing %s -> %s in %s' % (service_cs, realm.krbtgt_princ,
|
||||
realm.run(['./t_credstore', '-s', 'p:' + service_cs, 'ccache', storagecache,
|
||||
'keytab', servicekeytab], expected_trace=msgs)
|
||||
|
||||
+mark('matching')
|
||||
+scc = 'FILE:' + os.path.join(realm.testdir, 'service_cache')
|
||||
+realm.kinit(realm.host_princ, flags=['-k', '-c', scc])
|
||||
+realm.run(['./t_credstore', '-i', 'p:' + realm.host_princ, 'ccache', scc])
|
||||
+realm.run(['./t_credstore', '-i', 'h:host', 'ccache', scc])
|
||||
+realm.run(['./t_credstore', '-i', 'h:host@' + hostname, 'ccache', scc])
|
||||
+realm.run(['./t_credstore', '-i', 'p:wrong', 'ccache', scc],
|
||||
+ expected_code=1, expected_msg='does not match desired name')
|
||||
+realm.run(['./t_credstore', '-i', 'h:host@-nomatch-', 'ccache', scc],
|
||||
+ expected_code=1, expected_msg='does not match desired name')
|
||||
+realm.run(['./t_credstore', '-i', 'h:svc', 'ccache', scc],
|
||||
+ expected_code=1, expected_msg='does not match desired name')
|
||||
+
|
||||
+mark('matching (fallback)')
|
||||
+canonname = canonicalize_hostname(hostname)
|
||||
+if canonname != hostname:
|
||||
+ canonprinc = 'host/%s@%s' % (canonname, realm.realm)
|
||||
+ realm.addprinc(canonprinc)
|
||||
+ realm.extract_keytab(canonprinc, realm.keytab)
|
||||
+ realm.kinit(canonprinc, flags=['-k', '-c', scc])
|
||||
+ realm.run(['./t_credstore', '-i', 'h:host', 'ccache', scc])
|
||||
+ realm.run(['./t_credstore', '-i', 'h:host@' + hostname, 'ccache', scc])
|
||||
+ realm.run(['./t_credstore', '-i', 'h:host@' + canonname, 'ccache', scc])
|
||||
+ realm.run(['./t_credstore', '-i', 'p:' + canonprinc, 'ccache', scc])
|
||||
+ realm.run(['./t_credstore', '-i', 'p:' + realm.host_princ, 'ccache', scc],
|
||||
+ expected_code=1, expected_msg='does not match desired name')
|
||||
+ realm.run(['./t_credstore', '-i', 'h:host@-nomatch-', 'ccache', scc],
|
||||
+ expected_code=1, expected_msg='does not match desired name')
|
||||
+else:
|
||||
+ skipped('fallback matching test',
|
||||
+ '%s does not canonicalize to a different name' % hostname)
|
||||
+
|
||||
mark('rcache')
|
||||
# t_credstore -r should produce a replay error normally, but not with
|
||||
# rcache set to "none:".
|
@ -19,7 +19,7 @@
|
||||
Summary: The Kerberos network authentication system
|
||||
Name: krb5
|
||||
Version: 1.19
|
||||
Release: %{?zdpd}4%{?dist}
|
||||
Release: %{?zdpd}5%{?dist}
|
||||
|
||||
# rharwood has trust path to signing key and verifies on check-in
|
||||
Source0: https://web.mit.edu/kerberos/dist/krb5/%{version}/krb5-%{version}%{?dashpre}.tar.gz
|
||||
@ -48,6 +48,8 @@ Patch5: downstream-Remove-3des-support.patch
|
||||
Patch6: downstream-Use-backported-version-of-OpenSSL-3-KDF-i.patch
|
||||
Patch7: downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch
|
||||
Patch8: Add-APIs-for-marshalling-credentials.patch
|
||||
Patch9: Add-hostname-canonicalization-helper-to-k5test.py.patch
|
||||
Patch10: Support-host-based-GSS-initiator-names.patch
|
||||
|
||||
License: MIT
|
||||
URL: https://web.mit.edu/kerberos/www/
|
||||
@ -611,6 +613,9 @@ exit 0
|
||||
%{_libdir}/libkadm5srv_mit.so.*
|
||||
|
||||
%changelog
|
||||
* Thu Jan 28 2021 Robbie Harwood <rharwood@redhat.com> - 1.19-5
|
||||
- Support host-based GSS initiator names
|
||||
|
||||
* Thu Jan 28 2021 Robbie Harwood <rharwood@redhat.com> - 1.19-0.beta2.4
|
||||
- Require krb5-pkinit from krb5-{server,workstation}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user