{"action":"created","issue":{"url":"https://api.github.com/repos/curl/curl/issues/959","repository_url":"https://api.github.com/repos/curl/curl","labels_url":"https://api.github.com/repos/curl/curl/issues/959/labels{/name}","comments_url":"https://api.github.com/repos/curl/curl/issues/959/comments","events_url":"https://api.github.com/repos/curl/curl/issues/959/events","html_url":"https://github.com/curl/curl/issues/959","id":170808038,"number":959,"title":"libcurl+openssl does not accept https certificate iPAddress if dNSName is present","user":{"login":"jay","id":965580,"avatar_url":"https://avatars.githubusercontent.com/u/965580?v=3","gravatar_id":"","url":"https://api.github.com/users/jay","html_url":"https://github.com/jay","followers_url":"https://api.github.com/users/jay/followers","following_url":"https://api.github.com/users/jay/following{/other_user}","gists_url":"https://api.github.com/users/jay/gists{/gist_id}","starred_url":"https://api.github.com/users/jay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jay/subscriptions","organizations_url":"https://api.github.com/users/jay/orgs","repos_url":"https://api.github.com/users/jay/repos","events_url":"https://api.github.com/users/jay/events{/privacy}","received_events_url":"https://api.github.com/users/jay/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/curl/curl/labels/SSL/TLS","name":"SSL/TLS","color":"bfdadc"}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":4,"created_at":"2016-08-12T06:36:50Z","updated_at":"2016-08-12T07:18:17Z","closed_at":null,"body":"@wmsch [reported](https://github.com/curl/curl/issues/875#issuecomment-239208584) in #875 that IP address verifications are failing when both IP addresses and hostnames are present in the SAN:\r\n\r\n> If an IP address is specified in the request instead of a name, and it matches an address in the iPAddress field, and a dNSName field exists, shouldn't this still work? It does not work for me in 7.50.1 if both iPAddress and dNSName are provided. If dNSName does not exist, it works.\r\n\r\n\r\n[RFC 2818](https://tools.ietf.org/html/rfc2818#page-4) says this:\r\n\r\n> 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.\r\n\r\n...but also:\r\n\r\n> 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.\r\n\r\nseems unclear. if you have an iPAddress and dNSName in there I would think the intention is either... thoughts?\r\n\r\ntested master 2c8ccda 2016-08-11, returns:\r\n`curl: (51) SSL: no alternative certificate subject name matches target host name '127.0.0.1'`\r\n\r\nTested Firefox and Chrome and they accept iPAddress even when dNSName is present.\r\n\r\nTo reproduce:\r\n\r\nCreate a file **server.cnf**:\r\n```\r\n[req]\r\ndistinguished_name = req_distinguished_name\r\nreq_extensions = v3_req\r\nprompt = no\r\n\r\n[req_distinguished_name]\r\nCN = FOOBAR\r\n\r\n[v3_req]\r\nbasicConstraints = CA:FALSE\r\nkeyUsage = digitalSignature, keyEncipherment\r\nsubjectAltName = @alt_names\r\n\r\n[alt_names]\r\nDNS.1 = DOESNOTEXIST\r\nIP.1 = 127.0.0.1\r\n```\r\n\r\nCreate a file **ca.cnf**:\r\n```\r\n[req]\r\ndistinguished_name = req_distinguished_name\r\nprompt = no\r\n\r\n[req_distinguished_name]\r\nCN = CA_FOOBAR\r\n```\r\n\r\nCreate the certificates:\r\n```\r\nopenssl req -config ca.cnf -x509 -nodes -days 1095 -newkey rsa:2048 -x509 -out ca.crt -keyout ca.key\r\ncat ca.key ca.crt > ca.pem\r\n\r\nopenssl req -config server.cnf -new -nodes -days 1095 -newkey rsa:2048 -out server.req -keyout server.key\r\nopenssl x509 -extfile server.cnf -sha256 -req -in server.req -out server.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 1095 -extensions v3_req\r\ncat server.key server.crt > server.pem\r\n```\r\n\r\nListen:\r\n```\r\nsocat openssl-listen:4433,reuseaddr,cert=server.pem,verify=0,fork -\r\n```\r\n\r\nConnect:\r\n```\r\ncurl -v --cacert ca.crt https://127.0.0.1:4433/\r\n```\r\n\r\nOutput:\r\n```\r\n* Server certificate:\r\n*  subject: CN=FOOBAR\r\n*  start date: Aug 12 06:08:21 2016 GMT\r\n*  expire date: Aug 12 06:08:21 2019 GMT\r\n*  subjectAltName: host \"127.0.0.1\" matched cert's IP address!\r\n*  subjectAltName does not match 127.0.0.1\r\n* SSL: no alternative certificate subject name matches target host name '127.0.0.1'\r\n* multi_done\r\n* Closing connection 0\r\n* The cache now contains 0 members\r\n* TLSv1.2 (OUT), TLS alert, Client hello (1):\r\ncurl: (51) SSL: no alternative certificate subject name matches target host name '127.0.0.1'\r\n```\r\n\r\nVersion:\r\n```\r\ncurl 7.50.2-DEV (i386-pc-win32) libcurl/7.50.2-DEV OpenSSL/1.0.2h nghttp2/1.11.1\r\nProtocols: dict file ftp ftps gopher http https imap imaps ldap pop3 pop3s rtsp\r\nsmb smbs smtp smtps telnet tftp\r\nFeatures: AsynchDNS Debug Largefile NTLM SSL HTTP2\r\n```\r\n\r\nPossible solution if we want to allow either:\r\n```diff\r\ndiff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c\r\nindex 3027ca3..18c878e 100644\r\n--- a/lib/vtls/openssl.c\r\n+++ b/lib/vtls/openssl.c\r\n@@ -1115,8 +1115,6 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)\r\n       /* get a handle to alternative name number i */\r\n       const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);\r\n \r\n-      /* If a subjectAltName extension of type dNSName is present, that MUST\r\n-         be used as the identity. / RFC2818 section 3.1 */\r\n       if(check->type == GEN_DNS)\r\n         dNSName = TRUE;\r\n \r\n@@ -1164,11 +1162,8 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)\r\n     }\r\n     GENERAL_NAMES_free(altnames);\r\n \r\n-    if(dnsmatched || (!dNSName && ipmatched)) {\r\n-      /* count as a match if the dnsname matched or if there was no dnsname\r\n-         fields at all AND there was an IP field match */\r\n+    if(dnsmatched || ipmatched)\r\n       matched = TRUE;\r\n-    }\r\n   }\r\n \r\n   if(matched)\r\n```\r\n"},"comment":{"url":"https://api.github.com/repos/curl/curl/issues/comments/239377987","html_url":"https://github.com/curl/curl/issues/959#issuecomment-239377987","issue_url":"https://api.github.com/repos/curl/curl/issues/959","id":239377987,"user":{"login":"jay","id":965580,"avatar_url":"https://avatars.githubusercontent.com/u/965580?v=3","gravatar_id":"","url":"https://api.github.com/users/jay","html_url":"https://github.com/jay","followers_url":"https://api.github.com/users/jay/followers","following_url":"https://api.github.com/users/jay/following{/other_user}","gists_url":"https://api.github.com/users/jay/gists{/gist_id}","starred_url":"https://api.github.com/users/jay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jay/subscriptions","organizations_url":"https://api.github.com/users/jay/orgs","repos_url":"https://api.github.com/users/jay/repos","events_url":"https://api.github.com/users/jay/events{/privacy}","received_events_url":"https://api.github.com/users/jay/received_events","type":"User","site_admin":false},"created_at":"2016-08-12T07:18:16Z","updated_at":"2016-08-12T07:18:16Z","body":"Firefox and Chrome don't default to CN when iPAddress is present but dNSName is not. To repro follow above but use this **server.cnf**:\r\n```\r\n[req]\r\ndistinguished_name = req_distinguished_name\r\nreq_extensions = v3_req\r\nprompt = no\r\n\r\n[req_distinguished_name]\r\nCN = 127.0.0.1\r\n\r\n[v3_req]\r\nbasicConstraints = CA:FALSE\r\nkeyUsage = digitalSignature, keyEncipherment\r\nsubjectAltName = @alt_names\r\n\r\n[alt_names]\r\nIP.1 = 8.8.8.8\r\n```\r\n\r\nChrome shows `This server could not prove that it is 127.0.0.1; its security certificate is from 127.0.0.1.  NET::ERR_CERT_COMMON_NAME_INVALID`\r\n\r\nFirefox shows `The certificate is only valid for 8.8.8.8 Error code: SSL_ERROR_BAD_CERT_DOMAIN`\r\n\r\nSo assuming that's correct the fix looks more like this:\r\n~~~diff\r\ndiff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c\r\nindex 3027ca3..eedba0d 100644\r\n--- a/lib/vtls/openssl.c\r\n+++ b/lib/vtls/openssl.c\r\n@@ -1083,6 +1083,7 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)\r\n #endif\r\n   CURLcode result = CURLE_OK;\r\n   bool dNSName = FALSE; /* if a dNSName field exists in the cert */\r\n+  bool iPAddress = FALSE; /* if a iPAddress field exists in the cert */\r\n \r\n #ifdef ENABLE_IPV6\r\n   if(conn->bits.ipv6_ip &&\r\n@@ -1115,10 +1116,10 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)\r\n       /* get a handle to alternative name number i */\r\n       const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i);\r\n \r\n-      /* If a subjectAltName extension of type dNSName is present, that MUST\r\n-         be used as the identity. / RFC2818 section 3.1 */\r\n       if(check->type == GEN_DNS)\r\n         dNSName = TRUE;\r\n+      else if(check->type == GEN_IPADD)\r\n+        iPAddress = TRUE;\r\n \r\n       /* only check alternatives of the same type the target is */\r\n       if(check->type == target) {\r\n@@ -1164,18 +1165,14 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)\r\n     }\r\n     GENERAL_NAMES_free(altnames);\r\n \r\n-    if(dnsmatched || (!dNSName && ipmatched)) {\r\n-      /* count as a match if the dnsname matched or if there was no dnsname\r\n-         fields at all AND there was an IP field match */\r\n+    if(dnsmatched || ipmatched)\r\n       matched = TRUE;\r\n-    }\r\n   }\r\n \r\n   if(matched)\r\n     /* an alternative name matched */\r\n     ;\r\n-  else if(dNSName) {\r\n-    /* an dNSName field existed, but didn't match and then we MUST fail */\r\n+  else if(dNSName || (iPAddress && target == GEN_IPADD)) {\r\n     infof(data, \" subjectAltName does not match %s\\n\", conn->host.dispname);\r\n     failf(data, \"SSL: no alternative certificate subject name matches \"\r\n           \"target host name '%s'\", conn->host.dispname);\r\n~~~"}}
{"actor":{"display_login":"jay"}}