shithub: opusfile

Download patch

ref: cc1fff58eeaf776a79ca88375c64cbceb908df21
parent: 0a94cf8f3cafbe70389f226f6fad51a7cb75f811
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Sat Dec 31 01:33:40 EST 2016

Use OpenSSL's hostname validation if available.

As of version 1.0.2, OpenSSL can finally do automatic hostname
 validation for us.
Their implementation is likely to have received much better review
 than ours, and there are other good reasons to prefer it, so use it
 when we can.

--- a/src/http.c
+++ b/src/http.c
@@ -1622,6 +1622,20 @@
   return 0;
 }
 
+/*Convert a host to a numeric address, if possible.
+  Return: A struct addrinfo containing the address, if it was numeric, and NULL
+           otherwise.*/
+static struct addrinfo *op_inet_pton(const char *_host){
+  struct addrinfo *addrs;
+  struct addrinfo  hints;
+  memset(&hints,0,sizeof(hints));
+  hints.ai_socktype=SOCK_STREAM;
+  hints.ai_flags=AI_NUMERICHOST;
+  if(!getaddrinfo(_host,NULL,&hints,&addrs))return addrs;
+  return NULL;
+}
+
+# if OPENSSL_VERSION_NUMBER<0x10002000L
 /*Match a host name against a host with a possible wildcard pattern according
    to the rules of RFC 6125 Section 6.4.3.
   Return: 0 if the pattern doesn't match, and a non-zero value if it does.*/
@@ -1696,19 +1710,6 @@
    pattern+pattern_prefix_len+1,(int)host_suffix_len)==0;
 }
 
-/*Convert a host to a numeric address, if possible.
-  Return: A struct addrinfo containing the address, if it was numeric, and NULL
-           otherise.*/
-static struct addrinfo *op_inet_pton(const char *_host){
-  struct addrinfo *addrs;
-  struct addrinfo  hints;
-  memset(&hints,0,sizeof(hints));
-  hints.ai_socktype=SOCK_STREAM;
-  hints.ai_flags=AI_NUMERICHOST;
-  if(!getaddrinfo(_host,NULL,&hints,&addrs))return addrs;
-  return NULL;
-}
-
 /*Verify the server's hostname matches the certificate they presented using
    the procedure from Section 6 of RFC 6125.
   Return: 0 if the certificate doesn't match, and a non-zero value if it does.*/
@@ -1875,6 +1876,7 @@
   X509_free(peer_cert);
   return ret;
 }
+# endif
 
 /*Perform the TLS handshake on a new connection.*/
 static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
@@ -1890,6 +1892,50 @@
   /*Support for RFC 6066 Server Name Indication.*/
   SSL_set_tlsext_host_name(_ssl_conn,_stream->url.host);
 # endif
+  skip_certificate_check=_stream->skip_certificate_check;
+# if OPENSSL_VERSION_NUMBER>=0x10002000L
+  /*As of version 1.0.2, OpenSSL can finally do hostname checks automatically.
+    Of course, they make it much more complicated than it needs to be.*/
+  if(!skip_certificate_check){
+    X509_VERIFY_PARAM *param;
+    struct addrinfo   *addr;
+    char              *host;
+    unsigned char     *ip;
+    int                ip_len;
+    param=SSL_get0_param(_ssl_conn);
+    OP_ASSERT(param!=NULL);
+    host=_stream->url.host;
+    ip=NULL;
+    ip_len=0;
+    /*Check to see if the host was specified as a simple IP address.*/
+    addr=op_inet_pton(host);
+    if(addr!=NULL){
+      switch(addr->ai_family){
+        case AF_INET:{
+          struct sockaddr_in *s;
+          s=(struct sockaddr_in *)addr->ai_addr;
+          OP_ASSERT(addr->ai_addrlen>=sizeof(*s));
+          ip=(unsigned char *)&s->sin_addr;
+          ip_len=sizeof(s->sin_addr);
+          host=NULL;
+        }break;
+        case AF_INET6:{
+          struct sockaddr_in6 *s;
+          s=(struct sockaddr_in6 *)addr->ai_addr;
+          OP_ASSERT(addr->ai_addrlen>=sizeof(*s));
+          ip=(unsigned char *)&s->sin6_addr;
+          ip_len=sizeof(s->sin6_addr);
+          host=NULL;
+        }break;
+      }
+    }
+    /*Always set both host and ip to prevent matching against an old one.
+      One of the two will always be NULL, clearing that parameter.*/
+    X509_VERIFY_PARAM_set1_host(param,host,0);
+    X509_VERIFY_PARAM_set1_ip(param,ip,ip_len);
+    if(addr!=NULL)freeaddrinfo(addr);
+  }
+# endif
   /*Resume a previous session if available.*/
   if(_stream->ssl_session!=NULL){
     SSL_set_session(_ssl_conn,_stream->ssl_session);
@@ -1909,17 +1955,22 @@
   ret=op_do_ssl_step(_ssl_conn,_fd,SSL_connect);
   if(OP_UNLIKELY(ret<=0))return OP_FALSE;
   ssl_session=_stream->ssl_session;
-  skip_certificate_check=_stream->skip_certificate_check;
-  if(ssl_session==NULL||!skip_certificate_check){
+  if(ssl_session==NULL
+# if OPENSSL_VERSION_NUMBER<0x10002000L
+   ||!skip_certificate_check
+# endif
+   ){
     ret=op_do_ssl_step(_ssl_conn,_fd,SSL_do_handshake);
     if(OP_UNLIKELY(ret<=0))return OP_FALSE;
-    /*OpenSSL does not do hostname verification, despite the fact that we just
-       passed it the hostname above in the call to SSL_set_tlsext_host_name(),
-       because they are morons.
+# if OPENSSL_VERSION_NUMBER<0x10002000L
+    /*OpenSSL before version 1.0.2 does not do automatic hostname verification,
+       despite the fact that we just passed it the hostname above in the call
+       to SSL_set_tlsext_host_name().
       Do it for them.*/
     if(!skip_certificate_check&&!op_http_verify_hostname(_stream,_ssl_conn)){
       return OP_FALSE;
     }
+# endif
     if(ssl_session==NULL){
       /*Save the session for later resumption.*/
       _stream->ssl_session=SSL_get1_session(_ssl_conn);