diff options
author | Daniel Stenberg <daniel@haxx.se> | 2003-10-16 13:44:34 +0000 |
---|---|---|
committer | Daniel Stenberg <daniel@haxx.se> | 2003-10-16 13:44:34 +0000 |
commit | 597c1fe6bc06b367ed3fa1686291e13f3177e5fe (patch) | |
tree | 215fa961f66c67d7a286a0dbd0187d44c38d6518 | |
parent | 7201a5a290551893cb20fba41ba818c8cfaccb87 (diff) |
rewritten alternative name check
-rw-r--r-- | lib/ssluse.c | 175 |
1 files changed, 87 insertions, 88 deletions
diff --git a/lib/ssluse.c b/lib/ssluse.c index 049538ae1..0caa4ddfd 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -744,118 +744,117 @@ cert_hostcheck(const char *certname, const char *hostname) return 0; } -/* this subjectAltName patch is code originating from OpenLDAP, which uses - a license as described here: - http://www.openldap.org/software/release/license.html +/* Quote from RFC2818 section 3.1 "Server Identity" + + 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) { char peer_CN[257]; - int ntype = 3; /* 1 = IPv6, 2 = IPv4, 3=DNS */ - int i; - int altmatch = 0; + bool matched = FALSE; /* no alternative match yet */ + int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ + int addrlen; + struct SessionHandle *data = conn->data; + STACK_OF(GENERAL_NAME) *altnames; #ifdef ENABLE_IPV6 struct in6_addr addr; #else struct in_addr addr; #endif - char *ptr; - struct SessionHandle *data = conn->data; - + #ifdef ENABLE_IPV6 - if(conn->hostname[0] == '[' && strchr(conn->hostname, ']')) { - char *n2 = strdup(conn->hostname+1); - *strchr(n2, ']') = '\0'; - if(Curl_inet_pton(AF_INET6, n2, &addr)) - ntype = 1; - free(n2); + if(conn->bits.ipv6_ip && + Curl_inet_pton(AF_INET6, conn->hostname, &addr)) { + target = GEN_IPADD; + addrlen = sizeof(struct in6_addr); } else #endif - { - if((ptr = strrchr(conn->hostname, '.')) && - isdigit((unsigned char)ptr[1])) { - if(Curl_inet_pton(AF_INET, conn->hostname, &addr)) - ntype = 2; + if(Curl_inet_pton(AF_INET, conn->hostname, &addr)) { + target = GEN_IPADD; + addrlen = sizeof(struct in_addr); } - } - - i = X509_get_ext_by_NID(conn->ssl.server_cert, NID_subject_alt_name, -1); - if(i >= 0) { - X509_EXTENSION *ex; - STACK_OF(GENERAL_NAME) *alt; - - ex = X509_get_ext(conn->ssl.server_cert, i); - alt = X509V3_EXT_d2i(ex); - if(alt) { - int n, len1 = 0, len2 = 0; - char *domain = NULL; - GENERAL_NAME *gn; + + /* get a "list" of alternative names */ + altnames = X509_get_ext_d2i(conn->ssl.server_cert, NID_subject_alt_name, + NULL, NULL); + + if(altnames) { + int hostlen; + int domainlen; + char *domain; + int numalts; + int i; - if(ntype == 3) { - len1 = strlen(conn->hostname); - domain = strchr(conn->hostname, '.'); - if(domain) { - len2 = len1 - (domain-conn->hostname); - } - } - n = sk_GENERAL_NAME_num(alt); - for (i=0; i<n; i++) { - char *sn; - int sl; - gn = sk_GENERAL_NAME_value(alt, i); - if(gn->type == GEN_DNS) { - if(ntype != 3) - continue; - - sn = (char *) ASN1_STRING_data(gn->d.ia5); - sl = ASN1_STRING_length(gn->d.ia5); - + if(GEN_DNS == target) { + hostlen = strlen(conn->hostname); + domain = strchr(conn->hostname, '.'); + if(domain) + domainlen = strlen(domain); + } + + /* get amount of alternatives, RFC2459 claims there MUST be at least + one, but we don't depend on it... */ + numalts = sk_GENERAL_NAME_num(altnames); + + /* loop through all alternatives while none has matched */ + for (i=0; (i<numalts) && !matched; i++) { + /* get a handle to alternative name number i */ + const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); + + /* only check alternatives of the same type the target is */ + 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? */ - if((len1 == sl) && curl_strnequal(conn->hostname, sn, len1)) - break; - + if((hostlen == altlen) && + curl_strnequal(conn->hostname, altptr, hostlen)) + matched = TRUE; + /* Is this a wildcard match? */ - if((*sn == '*') && domain && (len2 == sl-1) && - curl_strnequal(domain, sn+1, len2)) - 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; + else if((altptr[0] == '*') && + (domainlen == altlen-1) && + curl_strnequal(domain, altptr+1, domainlen)) + matched = TRUE; + break; - if(!memcmp(sn, &addr, sl)) - break; + case GEN_IPADD: /* IP address comparison */ + /* 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; if(X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert), NID_commonName, |