Skip to content
Snippets Groups Projects
Commit 597c1fe6 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

rewritten alternative name check

parent 7201a5a2
No related branches found
No related tags found
No related merge requests found
...@@ -744,118 +744,117 @@ cert_hostcheck(const char *certname, const char *hostname) ...@@ -744,118 +744,117 @@ cert_hostcheck(const char *certname, const char *hostname)
return 0; return 0;
} }
/* this subjectAltName patch is code originating from OpenLDAP, which uses /* Quote from RFC2818 section 3.1 "Server Identity"
a license as described here:
http://www.openldap.org/software/release/license.html If a subjectAltName extension of type dNSName is present, that MUST
be used as the identity. Otherwise, the (most specific) Common Name
field in the Subject field of the certificate MUST be used. Although
the use of the Common Name is existing practice, it is deprecated and
Certification Authorities are encouraged to use the dNSName instead.
Matching is performed using the matching rules specified by
[RFC2459]. If more than one identity of a given type is present in
the certificate (e.g., more than one dNSName name, a match in any one
of the set is considered acceptable.) Names may contain the wildcard
character * which is considered to match any single domain name
component or component fragment. E.g., *.a.com matches foo.a.com but
not bar.foo.a.com. f*.com matches foo.com but not bar.com.
In some cases, the URI is specified as an IP address rather than a
hostname. In this case, the iPAddress subjectAltName must be present
in the certificate and must exactly match the IP in the URI.
It is not GPL-compatible, so we cannot have this situation in a release-
version of libcurl.
This needs to be addressed!
*/ */
static CURLcode verifyhost(struct connectdata *conn) static CURLcode verifyhost(struct connectdata *conn)
{ {
char peer_CN[257]; char peer_CN[257];
int ntype = 3; /* 1 = IPv6, 2 = IPv4, 3=DNS */ bool matched = FALSE; /* no alternative match yet */
int i; int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */
int altmatch = 0; int addrlen;
struct SessionHandle *data = conn->data;
STACK_OF(GENERAL_NAME) *altnames;
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
struct in6_addr addr; struct in6_addr addr;
#else #else
struct in_addr addr; struct in_addr addr;
#endif #endif
char *ptr;
struct SessionHandle *data = conn->data;
#ifdef ENABLE_IPV6 #ifdef ENABLE_IPV6
if(conn->hostname[0] == '[' && strchr(conn->hostname, ']')) { if(conn->bits.ipv6_ip &&
char *n2 = strdup(conn->hostname+1); Curl_inet_pton(AF_INET6, conn->hostname, &addr)) {
*strchr(n2, ']') = '\0'; target = GEN_IPADD;
if(Curl_inet_pton(AF_INET6, n2, &addr)) addrlen = sizeof(struct in6_addr);
ntype = 1;
free(n2);
} }
else else
#endif #endif
{ if(Curl_inet_pton(AF_INET, conn->hostname, &addr)) {
if((ptr = strrchr(conn->hostname, '.')) && target = GEN_IPADD;
isdigit((unsigned char)ptr[1])) { addrlen = sizeof(struct in_addr);
if(Curl_inet_pton(AF_INET, conn->hostname, &addr))
ntype = 2;
} }
}
/* get a "list" of alternative names */
i = X509_get_ext_by_NID(conn->ssl.server_cert, NID_subject_alt_name, -1); altnames = X509_get_ext_d2i(conn->ssl.server_cert, NID_subject_alt_name,
if(i >= 0) { NULL, NULL);
X509_EXTENSION *ex;
STACK_OF(GENERAL_NAME) *alt; if(altnames) {
int hostlen;
ex = X509_get_ext(conn->ssl.server_cert, i); int domainlen;
alt = X509V3_EXT_d2i(ex); char *domain;
if(alt) { int numalts;
int n, len1 = 0, len2 = 0; int i;
char *domain = NULL;
GENERAL_NAME *gn;
if(ntype == 3) { if(GEN_DNS == target) {
len1 = strlen(conn->hostname); hostlen = strlen(conn->hostname);
domain = strchr(conn->hostname, '.'); domain = strchr(conn->hostname, '.');
if(domain) { if(domain)
len2 = len1 - (domain-conn->hostname); domainlen = strlen(domain);
} }
}
n = sk_GENERAL_NAME_num(alt); /* get amount of alternatives, RFC2459 claims there MUST be at least
for (i=0; i<n; i++) { one, but we don't depend on it... */
char *sn; numalts = sk_GENERAL_NAME_num(altnames);
int sl;
gn = sk_GENERAL_NAME_value(alt, i); /* loop through all alternatives while none has matched */
if(gn->type == GEN_DNS) { for (i=0; (i<numalts) && !matched; i++) {
if(ntype != 3) /* get a handle to alternative name number i */
continue; const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);
sn = (char *) ASN1_STRING_data(gn->d.ia5); /* only check alternatives of the same type the target is */
sl = ASN1_STRING_length(gn->d.ia5); if(check->type == target) {
/* get data and length */
const char *altptr = (char *)ASN1_STRING_data(check->d.ia5);
const int altlen = ASN1_STRING_length(check->d.ia5);
switch(target) {
case GEN_DNS: /* name comparison */
/* Is this an exact match? */ /* Is this an exact match? */
if((len1 == sl) && curl_strnequal(conn->hostname, sn, len1)) if((hostlen == altlen) &&
break; curl_strnequal(conn->hostname, altptr, hostlen))
matched = TRUE;
/* Is this a wildcard match? */ /* Is this a wildcard match? */
if((*sn == '*') && domain && (len2 == sl-1) && else if((altptr[0] == '*') &&
curl_strnequal(domain, sn+1, len2)) (domainlen == altlen-1) &&
break; curl_strnequal(domain, altptr+1, domainlen))
matched = TRUE;
} break;
else if(gn->type == GEN_IPADD) {
if(ntype == 3)
continue;
sn = (char *) ASN1_STRING_data(gn->d.ia5);
sl = ASN1_STRING_length(gn->d.ia5);
#ifdef ENABLE_IPv6
if(ntype == 1 && sl != sizeof(struct in6_addr))
continue;
else
#endif
if(ntype == 2 && sl != sizeof(struct in_addr))
continue;
if(!memcmp(sn, &addr, sl)) case GEN_IPADD: /* IP address comparison */
break; /* compare alternative IP address if the data chunk is the same size
our server IP address is */
if((altlen == addrlen) && !memcmp(altptr, &addr, altlen))
matched = TRUE;
break;
} }
} }
GENERAL_NAMES_free(alt);
if(i < n) { /* got a match in altnames */
altmatch = 1;
infof(data, "\t subjectAltName: %s matched\n", conn->hostname);
}
} }
GENERAL_NAMES_free(altnames);
} }
if(!altmatch) { if(matched)
/* an alternative name matched the server hostname */
infof(data, "\t subjectAltName: %s matched\n", conn->hostname);
else {
bool obtain=FALSE; bool obtain=FALSE;
if(X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert), if(X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert),
NID_commonName, NID_commonName,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment