diff --git a/src/network/ssl/socket.c b/src/network/ssl/socket.c index c080233fb..83c1c0710 100644 --- a/src/network/ssl/socket.c +++ b/src/network/ssl/socket.c @@ -181,12 +181,28 @@ int ssl_connect(struct socket *socket) { int ret; + unsigned char *server_name; + struct connection *conn = socket->conn; - if (init_ssl_connection(socket) == S_SSL_ERROR) { + /* TODO: Recode server_name to UTF-8. */ + server_name = get_uri_string(conn->proxied_uri, URI_HOST); + if (!server_name) { + socket->ops->done(socket, connection_state(S_OUT_OF_MEM)); + return -1; + } + + /* RFC 3546 says literal IPv4 and IPv6 addresses are not allowed. */ + if (is_ip_address(server_name, strlen(server_name))) + mem_free_set(&server_name, NULL); + + if (init_ssl_connection(socket, server_name) == S_SSL_ERROR) { + mem_free_if(server_name); socket->ops->done(socket, connection_state(S_SSL_ERROR)); return -1; } + mem_free_if(server_name); + if (socket->no_tls) ssl_set_no_tls(socket); diff --git a/src/network/ssl/ssl.c b/src/network/ssl/ssl.c index 723b265e0..faf3c0844 100644 --- a/src/network/ssl/ssl.c +++ b/src/network/ssl/ssl.c @@ -249,13 +249,26 @@ struct module ssl_module = struct_module( ); int -init_ssl_connection(struct socket *socket) +init_ssl_connection(struct socket *socket, + const unsigned char *server_name) { #ifdef USE_OPENSSL socket->ssl = SSL_new(context); if (!socket->ssl) return S_SSL_ERROR; + + /* If the server name is known, pass it to OpenSSL. + * + * The return value of SSL_set_tlsext_host_name is not + * documented. The source shows that it returns 1 if + * successful; on error, it calls SSLerr and returns 0. */ + if (server_name + && !SSL_set_tlsext_host_name(socket->ssl, server_name)) { + SSL_free(socket->ssl); + socket->ssl = NULL; + return S_SSL_ERROR; + } + #elif defined(CONFIG_GNUTLS) - /* const unsigned char server_name[] = "localhost"; */ ssl_t *state = mem_alloc(sizeof(ssl_t)); if (!state) return S_SSL_ERROR; @@ -294,11 +307,17 @@ init_ssl_connection(struct socket *socket) /* gnutls_handshake_set_private_extensions(*state, 1); */ gnutls_cipher_set_priority(*state, cipher_priority); gnutls_kx_set_priority(*state, kx_priority); - /* gnutls_certificate_type_set_priority(*state, cert_type_priority); - gnutls_server_name_set(*state, GNUTLS_NAME_DNS, server_name, - sizeof(server_name) - 1); */ + /* gnutls_certificate_type_set_priority(*state, cert_type_priority); */ #endif + if (server_name + && gnutls_server_name_set(*state, GNUTLS_NAME_DNS, server_name, + strlen(server_name))) { + gnutls_deinit(*state); + mem_free(state); + return S_SSL_ERROR; + } + socket->ssl = state; #endif diff --git a/src/network/ssl/ssl.h b/src/network/ssl/ssl.h index 21ca1420b..7f89a511e 100644 --- a/src/network/ssl/ssl.h +++ b/src/network/ssl/ssl.h @@ -11,8 +11,18 @@ struct socket; extern struct module ssl_module; /* Initializes the SSL connection data. Returns S_OK on success and S_SSL_ERROR - * on failure. */ -int init_ssl_connection(struct socket *socket); + * on failure. + * + * server_name is the DNS name of the server (in UTF-8), or NULL if + * ELinks knows only the IP address. ELinks reports that name to the + * server so that the server can choose the correct certificate if it + * has multiple virtual hosts on the same IP address. See RFC 3546 + * section 3.1. + * + * server_name does not affect how ELinks verifies the certificate + * after the server has returned it. */ +int init_ssl_connection(struct socket *socket, + const unsigned char *server_name); /* Releases the SSL connection data */ void done_ssl_connection(struct socket *socket);