--- lib/ssluse.c | 98 +++++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 23 deletions(-) --- lib/ssluse.c.orig +++ lib/ssluse.c @@ -780,7 +780,6 @@ cert_hostcheck(const char *certname, con static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert) { - char peer_CN[257]; bool matched = FALSE; /* no alternative match yet */ int target = GEN_DNS; /* target type, GEN_DNS or GEN_IPADD */ int addrlen = 0; @@ -791,6 +790,7 @@ static CURLcode verifyhost(struct connec #else struct in_addr addr; #endif + CURLcode res = CURLE_OK; #ifdef ENABLE_IPV6 if(conn->bits.ipv6_ip && @@ -839,8 +839,12 @@ static CURLcode verifyhost(struct connec switch(target) { case GEN_DNS: /* name comparison */ + if(altlen == strlen(altptr)) + /* if this isn't true, there was an embedded zero in the name + string and we cannot match it. */ + ; /* Is this an exact match? */ - if((hostlen == altlen) && + else if((hostlen == altlen) && curl_strnequal(conn->hostname, altptr, hostlen)) matched = TRUE; @@ -867,11 +871,62 @@ static CURLcode verifyhost(struct connec /* 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(server_cert), - NID_commonName, - peer_CN, - sizeof(peer_CN)) < 0) { + /* we have to look to the last occurence of a commonName in the + distinguished one to get the most significant one. */ + int j,i=-1 ; + +/* The following is done because of a bug in 0.9.6b */ + + unsigned char *nulstr = (unsigned char *)""; + unsigned char *peer_CN = nulstr; + + X509_NAME *name = X509_get_subject_name(server_cert) ; + if (name) + while ((j=X509_NAME_get_index_by_NID(name,NID_commonName,i))>=0) + i=j; + + /* we have the name entry and we will now convert this to a string + that we can use for comparison. Doing this we support BMPstring, + UTF8 etc. */ + + if (i>=0) { + ASN1_STRING *tmp = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name,i)); + + /* In OpenSSL 0.9.7d and earlier, ASN1_STRING_to_UTF8 fails if the input + is already UTF-8 encoded. We check for this case and copy the raw + string manually to avoid the problem. This code can be made + conditional in the future when OpenSSL has been fixed. Work-around + brought by Alexis S. L. Carvalho. */ + if(tmp) { + if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) { + j = ASN1_STRING_length(tmp); + if(j >= 0) { + peer_CN = OPENSSL_malloc(j+1); + if(peer_CN) { + memcpy(peer_CN, ASN1_STRING_data(tmp), j); + peer_CN[j] = '\0'; + } + } + } + else /* not a UTF8 name */ + j = ASN1_STRING_to_UTF8(&peer_CN, tmp); + + if(peer_CN && ((int)strlen((char *)peer_CN) != j)) { + /* there was a terminating zero before the end of string, this + cannot match and we return failure! */ + failf(data, "SSL: illegal cert name field"); + res = CURLE_SSL_PEER_CERTIFICATE; + } + } + } + + if (peer_CN == nulstr) + peer_CN = NULL; + + if(res) + /* error already detected, pass through */ + ; + else if(!peer_CN) { if(data->set.ssl.verifyhost > 1) { failf(data, "SSL: unable to obtain common name from peer certificate"); @@ -883,26 +938,23 @@ static CURLcode verifyhost(struct connec infof(data, "\t common name: WARNING couldn't obtain\n"); } } - else - obtain = TRUE; - - if(obtain) { - if(!cert_hostcheck(peer_CN, conn->hostname)) { - if(data->set.ssl.verifyhost > 1) { - failf(data, "SSL: certificate subject name '%s' does not match " - "target host name '%s'", peer_CN, conn->hostname); - return CURLE_SSL_PEER_CERTIFICATE; - } - else - infof(data, "\t common name: %s (does not match '%s')\n", - peer_CN, conn->hostname); + else if(!cert_hostcheck((const char *)peer_CN, conn->hostname)) { + if(data->set.ssl.verifyhost > 1) { + failf(data, "SSL: certificate subject name '%s' does not match " + "target host name '%s'", peer_CN, conn->hostname); + res = CURLE_SSL_PEER_CERTIFICATE; } else - infof(data, "\t common name: %s (matched)\n", peer_CN); + infof(data, "\t common name: %s (does not match '%s')\n", + peer_CN, conn->hostname); } + else { + infof(data, "\t common name: %s (matched)\n", peer_CN); + } + if(peer_CN) + OPENSSL_free(peer_CN); } - - return CURLE_OK; + return res; } #endif