diff --git a/src/http.c b/src/http.c index 9e4ef5e208691df66a109f3b807ec89f42951859..988bfeaa7d65527fb74edece5f873e5de61099db 100644 --- a/src/http.c +++ b/src/http.c @@ -1622,6 +1622,20 @@ static int op_http_conn_establish_tunnel(OpusHTTPStream *_stream, 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 @@ static int op_http_hostname_match(const char *_host,size_t _host_len, 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 @@ static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){ 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, @@ -1889,6 +1891,50 @@ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, # if !defined(OPENSSL_NO_TLSEXT) /*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){ @@ -1909,17 +1955,22 @@ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn, 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);