aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2003-10-16 13:44:34 +0000
committerDaniel Stenberg <daniel@haxx.se>2003-10-16 13:44:34 +0000
commit597c1fe6bc06b367ed3fa1686291e13f3177e5fe (patch)
tree215fa961f66c67d7a286a0dbd0187d44c38d6518
parent7201a5a290551893cb20fba41ba818c8cfaccb87 (diff)
rewritten alternative name check
-rw-r--r--lib/ssluse.c175
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,