Loading lib/ssluse.c +87 −88 Original line number Diff line number Diff line Loading @@ -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; /* get a "list" of alternative names */ altnames = X509_get_ext_d2i(conn->ssl.server_cert, NID_subject_alt_name, NULL, NULL); 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; if(altnames) { int hostlen; int domainlen; char *domain; int numalts; int i; if(ntype == 3) { len1 = strlen(conn->hostname); if(GEN_DNS == target) { hostlen = strlen(conn->hostname); domain = strchr(conn->hostname, '.'); if(domain) { len2 = len1 - (domain-conn->hostname); } if(domain) domainlen = strlen(domain); } 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); /* 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)) else if((altptr[0] == '*') && (domainlen == altlen-1) && 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 */ /* 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, Loading Loading
lib/ssluse.c +87 −88 Original line number Diff line number Diff line Loading @@ -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; /* get a "list" of alternative names */ altnames = X509_get_ext_d2i(conn->ssl.server_cert, NID_subject_alt_name, NULL, NULL); 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; if(altnames) { int hostlen; int domainlen; char *domain; int numalts; int i; if(ntype == 3) { len1 = strlen(conn->hostname); if(GEN_DNS == target) { hostlen = strlen(conn->hostname); domain = strchr(conn->hostname, '.'); if(domain) { len2 = len1 - (domain-conn->hostname); } if(domain) domainlen = strlen(domain); } 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); /* 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)) else if((altptr[0] == '*') && (domainlen == altlen-1) && 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 */ /* 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, Loading