Update opusfile from 0.8 to 0.9
This commit is contained in:
parent
8611eb421d
commit
58a315fe3f
7 changed files with 538 additions and 369 deletions
|
@ -214,6 +214,7 @@ static const char *op_parse_file_url(const char *_src){
|
|||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
# include <openssl/ssl.h>
|
||||
# include <openssl/asn1.h>
|
||||
# include "winerrno.h"
|
||||
|
||||
typedef SOCKET op_sock;
|
||||
|
@ -307,6 +308,12 @@ static int op_poll_win32(struct pollfd *_fds,nfds_t _nfds,int _timeout){
|
|||
operate on sockets, because we don't use non-socket I/O here, and this
|
||||
minimizes the changes needed to deal with Winsock.*/
|
||||
# define close(_fd) closesocket(_fd)
|
||||
/*This takes an int for the address length, even though the value is of type
|
||||
socklen_t (defined as an unsigned integer type with at least 32 bits).*/
|
||||
# define connect(_fd,_addr,_addrlen) \
|
||||
(OP_UNLIKELY((_addrlen)>(socklen_t)INT_MAX)? \
|
||||
WSASetLastError(WSA_NOT_ENOUGH_MEMORY),-1: \
|
||||
connect(_fd,_addr,(int)(_addrlen)))
|
||||
/*This relies on sizeof(u_long)==sizeof(int), which is always true on both
|
||||
Win32 and Win64.*/
|
||||
# define ioctl(_fd,_req,_arg) ioctlsocket(_fd,_req,(u_long *)(_arg))
|
||||
|
@ -338,6 +345,7 @@ int SSL_CTX_set_default_verify_paths_win32(SSL_CTX *_ssl_ctx);
|
|||
# include <poll.h>
|
||||
# include <unistd.h>
|
||||
# include <openssl/ssl.h>
|
||||
# include <openssl/asn1.h>
|
||||
|
||||
typedef int op_sock;
|
||||
|
||||
|
@ -471,7 +479,7 @@ static int op_parse_url_impl(OpusParsedURL *_dst,const char *_src){
|
|||
scheme_end=_src+strspn(_src,OP_URL_SCHEME);
|
||||
if(OP_UNLIKELY(*scheme_end!=':')
|
||||
||OP_UNLIKELY(scheme_end-_src<4)||OP_UNLIKELY(scheme_end-_src>5)
|
||||
||OP_UNLIKELY(op_strncasecmp(_src,"https",scheme_end-_src)!=0)){
|
||||
||OP_UNLIKELY(op_strncasecmp(_src,"https",(int)(scheme_end-_src))!=0)){
|
||||
/*Unsupported protocol.*/
|
||||
return OP_EIMPL;
|
||||
}
|
||||
|
@ -674,7 +682,10 @@ static int op_sb_append(OpusStringBuf *_sb,const char *_s,int _len){
|
|||
}
|
||||
|
||||
static int op_sb_append_string(OpusStringBuf *_sb,const char *_s){
|
||||
return op_sb_append(_sb,_s,strlen(_s));
|
||||
size_t len;
|
||||
len=strlen(_s);
|
||||
if(OP_UNLIKELY(len>(size_t)INT_MAX))return OP_EFAULT;
|
||||
return op_sb_append(_sb,_s,(int)len);
|
||||
}
|
||||
|
||||
static int op_sb_append_port(OpusStringBuf *_sb,unsigned _port){
|
||||
|
@ -962,7 +973,8 @@ static int op_http_conn_write_fully(OpusHTTPConn *_conn,
|
|||
ret=send(fd.fd,_buf,_buf_size,0);
|
||||
if(ret>0){
|
||||
_buf+=ret;
|
||||
_buf_size-=ret;
|
||||
OP_ASSERT(ret<=_buf_size);
|
||||
_buf_size-=(int)ret;
|
||||
continue;
|
||||
}
|
||||
err=op_errno();
|
||||
|
@ -1077,8 +1089,9 @@ static int op_http_conn_read(OpusHTTPConn *_conn,
|
|||
if(ret>0){
|
||||
/*Read some data.
|
||||
Keep going to see if there's more.*/
|
||||
nread+=ret;
|
||||
nread_unblocked+=ret;
|
||||
OP_ASSERT(ret<=_buf_size-nread);
|
||||
nread+=(int)ret;
|
||||
nread_unblocked+=(int)ret;
|
||||
continue;
|
||||
}
|
||||
/*If we already read some data or the connection was closed, return
|
||||
|
@ -1171,8 +1184,8 @@ static int op_http_conn_read_response(OpusHTTPConn *_conn,
|
|||
if(OP_UNLIKELY(ret<=0))return size<=0?OP_EREAD:OP_FALSE;
|
||||
/*We read some data.*/
|
||||
/*Make sure the starting characters are "HTTP".
|
||||
Otherwise we could wind up waiting forever for a response from
|
||||
something that is not an HTTP server.*/
|
||||
Otherwise we could wind up waiting for a response from something that is
|
||||
not an HTTP server until we time out.*/
|
||||
if(size<4&&op_strncasecmp(buf,"HTTP",OP_MIN(size+ret,4))!=0){
|
||||
return OP_FALSE;
|
||||
}
|
||||
|
@ -1245,10 +1258,10 @@ static char *op_http_parse_status_line(int *_v1_1_compat,
|
|||
char *status_code;
|
||||
int v1_1_compat;
|
||||
size_t d;
|
||||
/*RFC 2616 Section 6.1 does not say that the tokens in the Status-Line cannot
|
||||
be separated by optional LWS, but since it specifically calls out where
|
||||
/*RFC 2616 Section 6.1 does not say if the tokens in the Status-Line can be
|
||||
separated by optional LWS, but since it specifically calls out where
|
||||
spaces are to be placed and that CR and LF are not allowed except at the
|
||||
end, I am assuming this to be true.*/
|
||||
end, we are assuming extra LWS is not allowed.*/
|
||||
/*We already validated that this starts with "HTTP"*/
|
||||
OP_ASSERT(op_strncasecmp(_response,"HTTP",4)==0);
|
||||
next=_response+4;
|
||||
|
@ -1272,7 +1285,7 @@ static char *op_http_parse_status_line(int *_v1_1_compat,
|
|||
d--;
|
||||
}
|
||||
/*We don't need to parse the version number.
|
||||
Any non-zero digit means it's greater than 1.*/
|
||||
Any non-zero digit means it's at least 1.*/
|
||||
v1_1_compat=d>0;
|
||||
next+=d;
|
||||
if(OP_UNLIKELY(*next++!=' '))return NULL;
|
||||
|
@ -1520,6 +1533,7 @@ static long op_bio_retry_ctrl(BIO *_b,int _cmd,long _num,void *_ptr){
|
|||
# if OPENSSL_VERSION_NUMBER<0x10100000L
|
||||
# define BIO_set_data(_b,_ptr) ((_b)->ptr=(_ptr))
|
||||
# define BIO_set_init(_b,_init) ((_b)->init=(_init))
|
||||
# define ASN1_STRING_get0_data ASN1_STRING_data
|
||||
# endif
|
||||
|
||||
static int op_bio_retry_new(BIO *_b){
|
||||
|
@ -1603,11 +1617,25 @@ static int op_http_conn_establish_tunnel(OpusHTTPStream *_stream,
|
|||
next=op_http_parse_status_line(NULL,&status_code,_stream->response.buf);
|
||||
/*According to RFC 2817, "Any successful (2xx) response to a
|
||||
CONNECT request indicates that the proxy has established a
|
||||
connection to the requested host and port.*/
|
||||
connection to the requested host and port."*/
|
||||
if(OP_UNLIKELY(next==NULL)||OP_UNLIKELY(status_code[0]!='2'))return OP_FALSE;
|
||||
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.*/
|
||||
|
@ -1620,13 +1648,15 @@ static int op_http_hostname_match(const char *_host,size_t _host_len,
|
|||
size_t pattern_label_len;
|
||||
size_t pattern_prefix_len;
|
||||
size_t pattern_suffix_len;
|
||||
pattern=(const char *)ASN1_STRING_data(_pattern);
|
||||
if(OP_UNLIKELY(_host_len>(size_t)INT_MAX))return 0;
|
||||
pattern=(const char *)ASN1_STRING_get0_data(_pattern);
|
||||
pattern_len=strlen(pattern);
|
||||
/*Check the pattern for embedded NULs.*/
|
||||
if(OP_UNLIKELY(pattern_len!=(size_t)ASN1_STRING_length(_pattern)))return 0;
|
||||
pattern_label_len=strcspn(pattern,".");
|
||||
OP_ASSERT(pattern_label_len<=pattern_len);
|
||||
pattern_prefix_len=strcspn(pattern,"*");
|
||||
if(OP_UNLIKELY(pattern_prefix_len>(size_t)INT_MAX))return 0;
|
||||
if(pattern_prefix_len>=pattern_label_len){
|
||||
/*"The client SHOULD NOT attempt to match a presented identifier in which
|
||||
the wildcard character comprises a label other than the left-most label
|
||||
|
@ -1637,7 +1667,8 @@ static int op_http_hostname_match(const char *_host,size_t _host_len,
|
|||
Don't use the system strcasecmp here, as that uses the locale and
|
||||
RFC 4343 makes clear that DNS's case-insensitivity only applies to
|
||||
the ASCII range.*/
|
||||
return _host_len==pattern_len&&op_strncasecmp(_host,pattern,_host_len)==0;
|
||||
return _host_len==pattern_len
|
||||
&&op_strncasecmp(_host,pattern,(int)_host_len)==0;
|
||||
}
|
||||
/*"However, the client SHOULD NOT attempt to match a presented identifier
|
||||
where the wildcard character is embedded within an A-label or U-label of
|
||||
|
@ -1672,34 +1703,25 @@ static int op_http_hostname_match(const char *_host,size_t _host_len,
|
|||
pattern_suffix_len=pattern_len-pattern_prefix_len-1;
|
||||
host_suffix_len=_host_len-host_label_len
|
||||
+pattern_label_len-pattern_prefix_len-1;
|
||||
OP_ASSERT(host_suffix_len<=_host_len);
|
||||
return pattern_suffix_len==host_suffix_len
|
||||
&&op_strncasecmp(_host,pattern,pattern_prefix_len)==0
|
||||
&&op_strncasecmp(_host,pattern,(int)pattern_prefix_len)==0
|
||||
&&op_strncasecmp(_host+_host_len-host_suffix_len,
|
||||
pattern+pattern_prefix_len+1,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;
|
||||
pattern+pattern_prefix_len+1,(int)host_suffix_len)==0;
|
||||
}
|
||||
|
||||
/*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.*/
|
||||
static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){
|
||||
X509 *peer_cert;
|
||||
STACK_OF(GENERAL_NAME) *san_names;
|
||||
char *host;
|
||||
size_t host_len;
|
||||
int ret;
|
||||
X509 *peer_cert;
|
||||
struct addrinfo *addr;
|
||||
char *host;
|
||||
size_t host_len;
|
||||
unsigned char *ip;
|
||||
int ip_len;
|
||||
int check_cn;
|
||||
int ret;
|
||||
host=_stream->url.host;
|
||||
host_len=strlen(host);
|
||||
peer_cert=SSL_get_peer_certificate(_ssl_conn);
|
||||
|
@ -1707,142 +1729,154 @@ static int op_http_verify_hostname(OpusHTTPStream *_stream,SSL *_ssl_conn){
|
|||
if(OP_UNLIKELY(peer_cert==NULL))return 0;
|
||||
ret=0;
|
||||
OP_ASSERT(host_len<INT_MAX);
|
||||
/*RFC 2818 says (after correcting for Eratta 1077): "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 RFC 2459.
|
||||
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 condered 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."*/
|
||||
san_names=X509_get_ext_d2i(peer_cert,NID_subject_alt_name,NULL,NULL);
|
||||
if(san_names!=NULL){
|
||||
struct addrinfo *addr;
|
||||
unsigned char *ip;
|
||||
int ip_len;
|
||||
int nsan_names;
|
||||
int sni;
|
||||
/*Check to see if the host was specified as a simple IP address.*/
|
||||
addr=op_inet_pton(host);
|
||||
ip=NULL;
|
||||
ip_len=0;
|
||||
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);
|
||||
}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);
|
||||
}break;
|
||||
}
|
||||
/*By default, fall back to checking the Common Name if we don't check any
|
||||
subjectAltNames of type dNSName.*/
|
||||
check_cn=1;
|
||||
/*Check to see if the host was specified as a simple IP address.*/
|
||||
addr=op_inet_pton(host);
|
||||
ip=NULL;
|
||||
ip_len=0;
|
||||
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);
|
||||
/*RFC 6125 says, "In this case, the iPAddress subjectAltName must [sic]
|
||||
be present in the certificate and must [sic] exactly match the IP in
|
||||
the URI."
|
||||
So don't allow falling back to a Common Name.*/
|
||||
check_cn=0;
|
||||
}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);
|
||||
check_cn=0;
|
||||
}break;
|
||||
}
|
||||
/*We can only verify fully-qualified domain names.
|
||||
To quote RFC 6125: "The extracted data MUST include only information that
|
||||
can be securely parsed out of the inputs (e.g., parsing the fully
|
||||
qualified DNS domain name out of the "host" component (or its
|
||||
equivalent) of a URI or deriving the application service type from the
|
||||
scheme of a URI) ..."
|
||||
We don't have a way to check (without relying on DNS records, which might
|
||||
be subverted) if this address is fully-qualified.
|
||||
This is particularly problematic when using a CONNECT tunnel, as it is
|
||||
the server that does DNS lookup, not us.
|
||||
However, we are certain that if the hostname has no '.', it is definitely
|
||||
not a fully-qualified domain name (with the exception of crazy TLDs that
|
||||
actually resolve, like "uz", but I am willing to ignore those).
|
||||
RFC 1535 says "...in any event where a '.' exists in a specified name it
|
||||
should be assumed to be a fully qualified domain name (FQDN) and SHOULD
|
||||
be tried as a rooted name first."
|
||||
That doesn't give us any security guarantees, of course (a subverted DNS
|
||||
could fail the original query and our resolver might still retry with a
|
||||
local domain appended).
|
||||
If we don't have a FQDN, just set the number of names to 0, so we'll fail
|
||||
and clean up any resources we allocated.*/
|
||||
if(ip==NULL&&strchr(host,'.')==NULL)nsan_names=0;
|
||||
/*RFC 2459 says there MUST be at least one, but we don't depend on it.*/
|
||||
else nsan_names=sk_GENERAL_NAME_num(san_names);
|
||||
for(sni=0;sni<nsan_names;sni++){
|
||||
const GENERAL_NAME *name;
|
||||
name=sk_GENERAL_NAME_value(san_names,sni);
|
||||
if(ip==NULL){
|
||||
if(name->type==GEN_DNS
|
||||
&&op_http_hostname_match(host,host_len,name->d.dNSName)){
|
||||
ret=1;
|
||||
break;
|
||||
}
|
||||
/*We can only verify IP addresses and "fully-qualified" domain names.
|
||||
To quote RFC 6125: "The extracted data MUST include only information that
|
||||
can be securely parsed out of the inputs (e.g., parsing the fully
|
||||
qualified DNS domain name out of the "host" component (or its
|
||||
equivalent) of a URI or deriving the application service type from the
|
||||
scheme of a URI) ..."
|
||||
We don't have a way to check (without relying on DNS records, which might
|
||||
be subverted) if this address is fully-qualified.
|
||||
This is particularly problematic when using a CONNECT tunnel, as it is
|
||||
the server that does DNS lookup, not us.
|
||||
However, we are certain that if the hostname has no '.', it is definitely
|
||||
not a fully-qualified domain name (with the exception of crazy TLDs that
|
||||
actually resolve, like "uz", but I am willing to ignore those).
|
||||
RFC 1535 says "...in any event where a '.' exists in a specified name it
|
||||
should be assumed to be a fully qualified domain name (FQDN) and SHOULD
|
||||
be tried as a rooted name first."
|
||||
That doesn't give us any security guarantees, of course (a subverted DNS
|
||||
could fail the original query and our resolver might still retry with a
|
||||
local domain appended).*/
|
||||
if(ip!=NULL||strchr(host,'.')!=NULL){
|
||||
STACK_OF(GENERAL_NAME) *san_names;
|
||||
/*RFC 2818 says (after correcting for Eratta 1077): "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 RFC 2459.
|
||||
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 condered 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."*/
|
||||
san_names=X509_get_ext_d2i(peer_cert,NID_subject_alt_name,NULL,NULL);
|
||||
if(san_names!=NULL){
|
||||
int nsan_names;
|
||||
int sni;
|
||||
/*RFC 2459 says there MUST be at least one, but we don't depend on it.*/
|
||||
nsan_names=sk_GENERAL_NAME_num(san_names);
|
||||
for(sni=0;sni<nsan_names;sni++){
|
||||
const GENERAL_NAME *name;
|
||||
name=sk_GENERAL_NAME_value(san_names,sni);
|
||||
if(ip==NULL){
|
||||
if(name->type==GEN_DNS){
|
||||
/*We have a subjectAltName extension of type dNSName, so don't fall
|
||||
back to a Common Name.
|
||||
https://marc.info/?l=openssl-dev&m=139617145216047&w=2 says that
|
||||
subjectAltNames of other types do not trigger this restriction,
|
||||
(e.g., if they are all IP addresses, we will still check a
|
||||
non-IP hostname against a Common Name).*/
|
||||
check_cn=0;
|
||||
if(op_http_hostname_match(host,host_len,name->d.dNSName)){
|
||||
ret=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(name->type==GEN_IPADD){
|
||||
unsigned const char *cert_ip;
|
||||
/*If we do have an IP address, compare it directly.
|
||||
RFC 6125: "When the reference identity is an IP address, the
|
||||
identity MUST be converted to the 'network byte order' octet
|
||||
string representation.
|
||||
For IP Version 4, as specified in RFC 791, the octet string will
|
||||
contain exactly four octets.
|
||||
For IP Version 6, as specified in RFC 2460, the octet string will
|
||||
contain exactly sixteen octets.
|
||||
This octet string is then compared against subjectAltName values of
|
||||
type iPAddress.
|
||||
A match occurs if the reference identity octet string and the value
|
||||
octet strings are identical."*/
|
||||
cert_ip=ASN1_STRING_get0_data(name->d.iPAddress);
|
||||
if(ip_len==ASN1_STRING_length(name->d.iPAddress)
|
||||
&&memcmp(ip,cert_ip,ip_len)==0){
|
||||
ret=1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(name->type==GEN_IPADD){
|
||||
unsigned char *cert_ip;
|
||||
/*If we do have an IP address, compare it directly.
|
||||
RFC 6125: "When the reference identity is an IP address, the identity
|
||||
MUST be converted to the 'network byte order' octet string
|
||||
representation.
|
||||
For IP Version 4, as specified in RFC 791, the octet string will
|
||||
contain exactly four octets.
|
||||
For IP Version 6, as specified in RFC 2460, the octet string will
|
||||
contain exactly sixteen octets.
|
||||
This octet string is then compared against subjectAltName values of
|
||||
type iPAddress.
|
||||
A match occurs if the reference identity octet string and the value
|
||||
octet strings are identical."*/
|
||||
cert_ip=ASN1_STRING_data(name->d.iPAddress);
|
||||
if(ip_len==ASN1_STRING_length(name->d.iPAddress)
|
||||
&&memcmp(ip,cert_ip,ip_len)==0){
|
||||
ret=1;
|
||||
break;
|
||||
}
|
||||
sk_GENERAL_NAME_pop_free(san_names,GENERAL_NAME_free);
|
||||
}
|
||||
/*If we're supposed to fall back to a Common Name, match against it here.*/
|
||||
if(check_cn){
|
||||
int last_cn_loc;
|
||||
int cn_loc;
|
||||
/*RFC 6125 says that at least one significant CA is known to issue certs
|
||||
with multiple CNs, although it SHOULD NOT.
|
||||
It also says: "The server's identity may also be verified by comparing
|
||||
the reference identity to the Common Name (CN) value in the last
|
||||
Relative Distinguished Name (RDN) of the subject field of the server's
|
||||
certificate (where "last" refers to the DER-encoded order...)."
|
||||
So find the last one and check it.*/
|
||||
cn_loc=-1;
|
||||
do{
|
||||
last_cn_loc=cn_loc;
|
||||
cn_loc=X509_NAME_get_index_by_NID(X509_get_subject_name(peer_cert),
|
||||
NID_commonName,last_cn_loc);
|
||||
}
|
||||
while(cn_loc>=0);
|
||||
ret=last_cn_loc>=0
|
||||
&&op_http_hostname_match(host,host_len,
|
||||
X509_NAME_ENTRY_get_data(
|
||||
X509_NAME_get_entry(X509_get_subject_name(peer_cert),last_cn_loc)));
|
||||
}
|
||||
sk_GENERAL_NAME_pop_free(san_names,GENERAL_NAME_free);
|
||||
if(addr!=NULL)freeaddrinfo(addr);
|
||||
}
|
||||
/*Do the same FQDN check we did above.
|
||||
We don't do this once in advance for both cases, because in the
|
||||
subjectAltName case we might have an IPv6 address without a dot.*/
|
||||
else if(strchr(host,'.')!=NULL){
|
||||
int last_cn_loc;
|
||||
int cn_loc;
|
||||
/*If there is no subjectAltName, match against commonName.
|
||||
RFC 6125 says that at least one significant CA is known to issue certs
|
||||
with multiple CNs, although it SHOULD NOT.
|
||||
It also says: "The server's identity may also be verified by comparing
|
||||
the reference identity to the Common Name (CN) value in the last
|
||||
Relative Distinguished Name (RDN) of the subject field of the server's
|
||||
certificate (where "last" refers to the DER-encoded order...)."
|
||||
So find the last one and check it.*/
|
||||
cn_loc=-1;
|
||||
do{
|
||||
last_cn_loc=cn_loc;
|
||||
cn_loc=X509_NAME_get_index_by_NID(X509_get_subject_name(peer_cert),
|
||||
NID_commonName,last_cn_loc);
|
||||
}
|
||||
while(cn_loc>=0);
|
||||
ret=last_cn_loc>=0
|
||||
&&op_http_hostname_match(host,host_len,
|
||||
X509_NAME_ENTRY_get_data(
|
||||
X509_NAME_get_entry(X509_get_subject_name(peer_cert),last_cn_loc)));
|
||||
}
|
||||
if(addr!=NULL)freeaddrinfo(addr);
|
||||
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,
|
||||
|
@ -1851,11 +1885,56 @@ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
|||
BIO *ssl_bio;
|
||||
int skip_certificate_check;
|
||||
int ret;
|
||||
ssl_bio=BIO_new_socket(_fd,BIO_NOCLOSE);
|
||||
/*This always takes an int, even though with Winsock op_sock is a SOCKET.*/
|
||||
ssl_bio=BIO_new_socket((int)_fd,BIO_NOCLOSE);
|
||||
if(OP_LIKELY(ssl_bio==NULL))return OP_FALSE;
|
||||
# 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){
|
||||
|
@ -1876,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);
|
||||
|
@ -1911,11 +1995,10 @@ static int op_http_conn_start_tls(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
|||
left to try.
|
||||
*_addr will be set to NULL in this case.*/
|
||||
static int op_sock_connect_next(op_sock _fd,
|
||||
const struct addrinfo **_addr,int _ai_family){
|
||||
const struct addrinfo *addr;
|
||||
int err;
|
||||
addr=*_addr;
|
||||
for(;;){
|
||||
struct addrinfo **_addr,int _ai_family){
|
||||
struct addrinfo *addr;
|
||||
int err;
|
||||
for(addr=*_addr;;addr=addr->ai_next){
|
||||
/*Move to the next address of the requested type.*/
|
||||
for(;addr!=NULL&&addr->ai_family!=_ai_family;addr=addr->ai_next);
|
||||
*_addr=addr;
|
||||
|
@ -1925,7 +2008,6 @@ static int op_sock_connect_next(op_sock _fd,
|
|||
err=op_errno();
|
||||
/*Winsock will set WSAEWOULDBLOCK.*/
|
||||
if(OP_LIKELY(err==EINPROGRESS||err==EWOULDBLOCK))return 0;
|
||||
addr=addr->ai_next;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1933,15 +2015,15 @@ static int op_sock_connect_next(op_sock _fd,
|
|||
# define OP_NPROTOS (2)
|
||||
|
||||
static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
||||
const struct addrinfo *_addrs,struct timeb *_start_time){
|
||||
const struct addrinfo *addr;
|
||||
const struct addrinfo *addrs[OP_NPROTOS];
|
||||
struct pollfd fds[OP_NPROTOS];
|
||||
int ai_family;
|
||||
int nprotos;
|
||||
int ret;
|
||||
int pi;
|
||||
int pj;
|
||||
struct addrinfo *_addrs,struct timeb *_start_time){
|
||||
struct addrinfo *addr;
|
||||
struct addrinfo *addrs[OP_NPROTOS];
|
||||
struct pollfd fds[OP_NPROTOS];
|
||||
int ai_family;
|
||||
int nprotos;
|
||||
int ret;
|
||||
int pi;
|
||||
int pj;
|
||||
for(pi=0;pi<OP_NPROTOS;pi++)addrs[pi]=NULL;
|
||||
/*Try connecting via both IPv4 and IPv6 simultaneously, and keep the first
|
||||
one that succeeds.
|
||||
|
@ -1950,8 +2032,8 @@ static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
|||
families were returned in the DNS records in accordance with RFC 6555.*/
|
||||
for(addr=_addrs,nprotos=0;addr!=NULL&&nprotos<OP_NPROTOS;addr=addr->ai_next){
|
||||
if(addr->ai_family==AF_INET6||addr->ai_family==AF_INET){
|
||||
OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in6));
|
||||
OP_ASSERT(addr->ai_addrlen<=sizeof(struct sockaddr_in));
|
||||
OP_ASSERT(addr->ai_addrlen<=
|
||||
OP_MAX(sizeof(struct sockaddr_in6),sizeof(struct sockaddr_in)));
|
||||
/*If we've seen this address family before, skip this address for now.*/
|
||||
for(pi=0;pi<nprotos;pi++)if(addrs[pi]->ai_family==addr->ai_family)break;
|
||||
if(pi<nprotos)continue;
|
||||
|
@ -2065,7 +2147,7 @@ static int op_http_connect_impl(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
|||
}
|
||||
|
||||
static int op_http_connect(OpusHTTPStream *_stream,OpusHTTPConn *_conn,
|
||||
const struct addrinfo *_addrs,struct timeb *_start_time){
|
||||
struct addrinfo *_addrs,struct timeb *_start_time){
|
||||
struct timeb resolve_time;
|
||||
struct addrinfo *new_addrs;
|
||||
int ret;
|
||||
|
@ -2138,19 +2220,20 @@ static char *op_base64_encode(char *_dst,const char *_src,int _len){
|
|||
Scheme and append it to the given string buffer.*/
|
||||
static int op_sb_append_basic_auth_header(OpusStringBuf *_sb,
|
||||
const char *_header,const char *_user,const char *_pass){
|
||||
int user_len;
|
||||
int pass_len;
|
||||
int user_pass_len;
|
||||
int base64_len;
|
||||
int nbuf_total;
|
||||
int ret;
|
||||
size_t user_len;
|
||||
size_t pass_len;
|
||||
int user_pass_len;
|
||||
int base64_len;
|
||||
int nbuf_total;
|
||||
int ret;
|
||||
ret=op_sb_append_string(_sb,_header);
|
||||
ret|=op_sb_append(_sb,": Basic ",8);
|
||||
user_len=strlen(_user);
|
||||
pass_len=strlen(_pass);
|
||||
if(OP_UNLIKELY(user_len>(size_t)INT_MAX))return OP_EFAULT;
|
||||
if(OP_UNLIKELY(pass_len>INT_MAX-user_len))return OP_EFAULT;
|
||||
if(OP_UNLIKELY(user_len+pass_len>(INT_MAX>>2)*3-3))return OP_EFAULT;
|
||||
user_pass_len=user_len+1+pass_len;
|
||||
if(OP_UNLIKELY((int)(user_len+pass_len)>(INT_MAX>>2)*3-3))return OP_EFAULT;
|
||||
user_pass_len=(int)(user_len+pass_len)+1;
|
||||
base64_len=OP_BASE64_LENGTH(user_pass_len);
|
||||
/*Stick "user:pass" at the end of the buffer so we can Base64 encode it
|
||||
in-place.*/
|
||||
|
@ -2160,9 +2243,9 @@ static int op_sb_append_basic_auth_header(OpusStringBuf *_sb,
|
|||
ret|=op_sb_ensure_capacity(_sb,nbuf_total);
|
||||
if(OP_UNLIKELY(ret<0))return ret;
|
||||
_sb->nbuf=nbuf_total-user_pass_len;
|
||||
OP_ALWAYS_TRUE(!op_sb_append(_sb,_user,user_len));
|
||||
OP_ALWAYS_TRUE(!op_sb_append(_sb,_user,(int)user_len));
|
||||
OP_ALWAYS_TRUE(!op_sb_append(_sb,":",1));
|
||||
OP_ALWAYS_TRUE(!op_sb_append(_sb,_pass,pass_len));
|
||||
OP_ALWAYS_TRUE(!op_sb_append(_sb,_pass,(int)pass_len));
|
||||
op_base64_encode(_sb->buf+nbuf_total-base64_len,
|
||||
_sb->buf+nbuf_total-user_pass_len,user_pass_len);
|
||||
return op_sb_append(_sb,"\r\n",2);
|
||||
|
@ -2781,7 +2864,7 @@ static int op_http_conn_open_pos(OpusHTTPStream *_stream,
|
|||
ret=op_http_conn_handle_response(_stream,_conn);
|
||||
if(OP_UNLIKELY(ret!=0))return OP_FALSE;
|
||||
ftime(&end_time);
|
||||
_stream->cur_conni=_conn-_stream->conns;
|
||||
_stream->cur_conni=(int)(_conn-_stream->conns);
|
||||
OP_ASSERT(_stream->cur_conni>=0&&_stream->cur_conni<OP_NCONNS_MAX);
|
||||
/*The connection has been successfully opened.
|
||||
Update the connection time estimate.*/
|
||||
|
@ -2885,7 +2968,7 @@ static int op_http_conn_read_body(OpusHTTPStream *_stream,
|
|||
content_length=_stream->content_length;
|
||||
}
|
||||
OP_ASSERT(end_pos>pos);
|
||||
_buf_size=OP_MIN(_buf_size,end_pos-pos);
|
||||
_buf_size=(int)OP_MIN(_buf_size,end_pos-pos);
|
||||
}
|
||||
nread=op_http_conn_read(_conn,(char *)_buf,_buf_size,1);
|
||||
if(OP_UNLIKELY(nread<0))return nread;
|
||||
|
@ -2921,7 +3004,7 @@ static int op_http_conn_read_body(OpusHTTPStream *_stream,
|
|||
static int op_http_stream_read(void *_stream,
|
||||
unsigned char *_ptr,int _buf_size){
|
||||
OpusHTTPStream *stream;
|
||||
ptrdiff_t nread;
|
||||
int nread;
|
||||
opus_int64 size;
|
||||
opus_int64 pos;
|
||||
int ci;
|
||||
|
@ -3125,7 +3208,8 @@ static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){
|
|||
*pnext=conn->next;
|
||||
conn->next=stream->lru_head;
|
||||
stream->lru_head=conn;
|
||||
stream->cur_conni=conn-stream->conns;
|
||||
stream->cur_conni=(int)(conn-stream->conns);
|
||||
OP_ASSERT(stream->cur_conni>=0&&stream->cur_conni<OP_NCONNS_MAX);
|
||||
return 0;
|
||||
}
|
||||
pnext=&conn->next;
|
||||
|
@ -3177,7 +3261,8 @@ static int op_http_stream_seek(void *_stream,opus_int64 _offset,int _whence){
|
|||
*pnext=conn->next;
|
||||
conn->next=stream->lru_head;
|
||||
stream->lru_head=conn;
|
||||
stream->cur_conni=conn-stream->conns;
|
||||
stream->cur_conni=(int)(conn-stream->conns);
|
||||
OP_ASSERT(stream->cur_conni>=0&&stream->cur_conni<OP_NCONNS_MAX);
|
||||
return 0;
|
||||
}
|
||||
close_pnext=pnext;
|
||||
|
@ -3323,7 +3408,7 @@ static void *op_url_stream_create_impl(OpusFileCallbacks *_cb,const char *_url,
|
|||
*_pinfo will be NULL.
|
||||
Our caller is responsible for copying *_info to **_pinfo if it ultimately
|
||||
succeeds, or for clearing *_info if it ultimately fails.*/
|
||||
void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
|
||||
static void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
|
||||
const char *_url,OpusServerInfo *_info,OpusServerInfo **_pinfo,va_list _ap){
|
||||
int skip_certificate_check;
|
||||
const char *proxy_host;
|
||||
|
@ -3337,6 +3422,7 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
|
|||
proxy_user=NULL;
|
||||
proxy_pass=NULL;
|
||||
pinfo=NULL;
|
||||
*_pinfo=NULL;
|
||||
for(;;){
|
||||
ptrdiff_t request;
|
||||
request=va_arg(_ap,char *)-(char *)NULL;
|
||||
|
@ -3368,7 +3454,6 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
|
|||
}
|
||||
/*If the caller has requested server information, proxy it to a local copy to
|
||||
simplify error handling.*/
|
||||
*_pinfo=NULL;
|
||||
if(pinfo!=NULL){
|
||||
void *ret;
|
||||
opus_server_info_init(_info);
|
||||
|
@ -3385,7 +3470,7 @@ void *op_url_stream_vcreate_impl(OpusFileCallbacks *_cb,
|
|||
void *op_url_stream_vcreate(OpusFileCallbacks *_cb,
|
||||
const char *_url,va_list _ap){
|
||||
OpusServerInfo info;
|
||||
OpusServerInfo *pinfo=NULL;
|
||||
OpusServerInfo *pinfo;
|
||||
void *ret;
|
||||
ret=op_url_stream_vcreate_impl(_cb,_url,&info,&pinfo,_ap);
|
||||
if(pinfo!=NULL)*pinfo=*&info;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue