[31508] in CVS-changelog-for-Kerberos-V5
krb5 commit: Use X509_check_host() to verify KKDCP server cert
daemon@ATHENA.MIT.EDU (ghudson@mit.edu)
Wed Mar 4 02:09:39 2026
From: ghudson@mit.edu
To: cvs-krb5@mit.edu
Message-Id: <20260304070933.282FF1046F6@krbdev.mit.edu>
Date: Wed, 4 Mar 2026 02:09:33 -0500 (EST)
MIME-Version: 1.0
Reply-To: krbdev@mit.edu
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
Errors-To: cvs-krb5-bounces@mit.edu
https://github.com/krb5/krb5/commit/f5bbfa4821cf590a4748f96d0e016bc0485e95c4
commit f5bbfa4821cf590a4748f96d0e016bc0485e95c4
Author: Greg Hudson <ghudson@mit.edu>
Date: Wed Feb 25 19:05:40 2026 -0500
Use X509_check_host() to verify KKDCP server cert
In the k5tls module, rely on X509_check_host() and
X509_check_ip_asc(), which were added in OpenSSL 1.0.2, instead of
doing our own verification.
There is one notable difference in behavior: X509_check_host() admits
wildcards with a prefix or suffix (but not both) within the label,
like "kdc*.mydomain.com". The old code only allows a wildcard to
match a complete label.
ticket: 9198 (new)
src/plugins/tls/k5tls/openssl.c | 211 ++--------------------------------------
1 file changed, 6 insertions(+), 205 deletions(-)
diff --git a/src/plugins/tls/k5tls/openssl.c b/src/plugins/tls/k5tls/openssl.c
index aab67c01c..42d72dc9e 100644
--- a/src/plugins/tls/k5tls/openssl.c
+++ b/src/plugins/tls/k5tls/openssl.c
@@ -71,218 +71,19 @@ flush_errors(krb5_context context)
}
}
-/* Return the passed-in character, lower-cased if it's an ASCII character. */
-static inline char
-ascii_tolower(char p)
-{
- if (KRB5_UPPER(p))
- return p + ('a' - 'A');
- return p;
-}
-
-/*
- * Check a single label. If allow_wildcard is true, and the presented name
- * includes a wildcard, return true and note that we matched a wildcard.
- * Otherwise, for both the presented and expected values, do a case-insensitive
- * comparison of ASCII characters, and a case-sensitive comparison of
- * everything else.
- */
-static krb5_boolean
-label_match(const char *presented, size_t plen, const char *expected,
- size_t elen, krb5_boolean allow_wildcard, krb5_boolean *wildcard)
-{
- unsigned int i;
-
- if (allow_wildcard && plen == 1 && presented[0] == '*') {
- *wildcard = TRUE;
- return TRUE;
- }
-
- if (plen != elen)
- return FALSE;
-
- for (i = 0; i < elen; i++) {
- if (ascii_tolower(presented[i]) != ascii_tolower(expected[i]))
- return FALSE;
- }
- return TRUE;
-}
-
-/* Break up the two names and check them, label by label. */
-static krb5_boolean
-domain_match(const char *presented, size_t plen, const char *expected)
-{
- const char *p, *q, *r, *s;
- int n_label;
- krb5_boolean used_wildcard = FALSE;
-
- n_label = 0;
- p = presented;
- r = expected;
- while (p < presented + plen && *r != '\0') {
- q = memchr(p, '.', plen - (p - presented));
- if (q == NULL)
- q = presented + plen;
- s = r + strcspn(r, ".");
- if (!label_match(p, q - p, r, s - r, n_label == 0, &used_wildcard))
- return FALSE;
- p = q < presented + plen ? q + 1 : q;
- r = *s ? s + 1 : s;
- n_label++;
- }
- if (used_wildcard && n_label <= 2)
- return FALSE;
- if (p == presented + plen && *r == '\0')
- return TRUE;
- return FALSE;
-}
-
-/* Fetch the list of subjectAltNames from a certificate. */
-static GENERAL_NAMES *
-get_cert_sans(X509 *x)
-{
- int ext;
- X509_EXTENSION *san_ext;
-
- ext = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
- if (ext < 0)
- return NULL;
- san_ext = X509_get_ext(x, ext);
- if (san_ext == NULL)
- return NULL;
- return X509V3_EXT_d2i(san_ext);
-}
-
-/* Fetch a CN value from the subjct name field, returning its length, or -1 if
- * there is no subject name or it contains no CN value. */
-static int
-get_cert_cn(X509 *x, char *buf, size_t bufsize)
-{
- X509_NAME *name;
-
- name = X509_get_subject_name(x);
- if (name == NULL)
- return -1;
- return X509_NAME_get_text_by_NID(name, NID_commonName, buf, bufsize);
-}
-
-/* Return true if text matches any of the addresses we can recover from x. */
-static krb5_boolean
-check_cert_address(X509 *x, const char *text)
-{
- char buf[1024];
- GENERAL_NAMES *sans;
- GENERAL_NAME *san = NULL;
- ASN1_OCTET_STRING *ip;
- krb5_boolean found_ip_san = FALSE, matched = FALSE;
- int n_sans, i;
- int name_length;
- struct in_addr sin;
- struct in6_addr sin6;
-
- /* Parse the IP address into an octet string. */
- ip = ASN1_OCTET_STRING_new();
- if (ip == NULL)
- return FALSE;
- if (inet_pton(AF_INET, text, &sin)) {
- ASN1_OCTET_STRING_set(ip, (unsigned char *)&sin, sizeof(sin));
- } else if (inet_pton(AF_INET6, text, &sin6)) {
- ASN1_OCTET_STRING_set(ip, (unsigned char *)&sin6, sizeof(sin6));
- } else {
- ASN1_OCTET_STRING_free(ip);
- return FALSE;
- }
-
- /* Check for matches in ipaddress subjectAltName values. */
- sans = get_cert_sans(x);
- if (sans != NULL) {
- n_sans = sk_GENERAL_NAME_num(sans);
- for (i = 0; i < n_sans; i++) {
- san = sk_GENERAL_NAME_value(sans, i);
- if (san->type != GEN_IPADD)
- continue;
- found_ip_san = TRUE;
- matched = (ASN1_OCTET_STRING_cmp(ip, san->d.iPAddress) == 0);
- if (matched)
- break;
- }
- sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free);
- }
- ASN1_OCTET_STRING_free(ip);
-
- if (found_ip_san)
- return matched;
-
- /* Check for a match against the CN value in the peer's subject name. */
- name_length = get_cert_cn(x, buf, sizeof(buf));
- if (name_length >= 0) {
- /* Do a string compare to check if it's an acceptable value. */
- return strlen(text) == (size_t)name_length &&
- strncmp(text, buf, name_length) == 0;
- }
-
- /* We didn't find a match. */
- return FALSE;
-}
-
-/* Return true if expected matches any of the names we can recover from x. */
-static krb5_boolean
-check_cert_servername(X509 *x, const char *expected)
-{
- char buf[1024];
- GENERAL_NAMES *sans;
- GENERAL_NAME *san = NULL;
- unsigned char *dnsname;
- krb5_boolean found_dns_san = FALSE, matched = FALSE;
- int name_length, n_sans, i;
-
- /* Check for matches in dnsname subjectAltName values. */
- sans = get_cert_sans(x);
- if (sans != NULL) {
- n_sans = sk_GENERAL_NAME_num(sans);
- for (i = 0; i < n_sans; i++) {
- san = sk_GENERAL_NAME_value(sans, i);
- if (san->type != GEN_DNS)
- continue;
- found_dns_san = TRUE;
- dnsname = NULL;
- name_length = ASN1_STRING_to_UTF8(&dnsname, san->d.dNSName);
- if (dnsname == NULL)
- continue;
- matched = domain_match((char *)dnsname, name_length, expected);
- OPENSSL_free(dnsname);
- if (matched)
- break;
- }
- sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free);
- }
-
- if (matched)
- return TRUE;
- if (found_dns_san)
- return matched;
-
- /* Check for a match against the CN value in the peer's subject name. */
- name_length = get_cert_cn(x, buf, sizeof(buf));
- if (name_length >= 0)
- return domain_match(buf, name_length, expected);
-
- /* We didn't find a match. */
- return FALSE;
-}
-
static krb5_boolean
check_cert_name_or_ip(X509 *x, const char *expected_name)
{
struct in_addr in;
struct in6_addr in6;
+ int r;
if (inet_pton(AF_INET, expected_name, &in) != 0 ||
- inet_pton(AF_INET6, expected_name, &in6) != 0) {
- return check_cert_address(x, expected_name);
- } else {
- return check_cert_servername(x, expected_name);
- }
+ inet_pton(AF_INET6, expected_name, &in6) != 0)
+ r = X509_check_ip_asc(x, expected_name, 0);
+ else
+ r = X509_check_host(x, expected_name, 0, 0, NULL);
+ return r == 1;
}
static int
_______________________________________________
cvs-krb5 mailing list
cvs-krb5@mit.edu
https://mailman.mit.edu/mailman/listinfo/cvs-krb5