Adding NPN support if ALPN support is not available.

- ALPN may not be available:
  - because OpenSSL does not support it.
  - because one of the peers does not support it.
- Tested manually end to end by forcing TSI_OPENSSL_ALPN_SUPPORT to 0.
  Also tested if only one of the peers supports ALPN.
pull/2122/head
Julien Boeuf 10 years ago
parent c1c697a897
commit d1531323e7
  1. 122
      src/core/tsi/ssl_transport_security.c

@ -54,6 +54,10 @@
#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384 #define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384
#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024 #define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024
#ifndef TSI_OPENSSL_ALPN_SUPPORT
#define TSI_OPENSSL_ALPN_SUPPORT 1
#endif
/* TODO(jboeuf): I have not found a way to get this number dynamically from the /* TODO(jboeuf): I have not found a way to get this number dynamically from the
* SSL structure. This is what we would ultimately want though... */ * SSL structure. This is what we would ultimately want though... */
#define TSI_SSL_MAX_PROTECTION_OVERHEAD 100 #define TSI_SSL_MAX_PROTECTION_OVERHEAD 100
@ -70,6 +74,8 @@ struct tsi_ssl_handshaker_factory {
typedef struct { typedef struct {
tsi_ssl_handshaker_factory base; tsi_ssl_handshaker_factory base;
SSL_CTX* ssl_context; SSL_CTX* ssl_context;
unsigned char* alpn_protocol_list;
size_t alpn_protocol_list_length;
} tsi_ssl_client_handshaker_factory; } tsi_ssl_client_handshaker_factory;
typedef struct { typedef struct {
@ -841,7 +847,7 @@ static tsi_result ssl_handshaker_process_bytes_from_peer(
static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self, static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self,
tsi_peer* peer) { tsi_peer* peer) {
tsi_result result = TSI_OK; tsi_result result = TSI_OK;
const unsigned char* alpn_selected; const unsigned char* alpn_selected = NULL;
unsigned int alpn_selected_len; unsigned int alpn_selected_len;
tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self; tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self;
X509* peer_cert = SSL_get_peer_certificate(impl->ssl); X509* peer_cert = SSL_get_peer_certificate(impl->ssl);
@ -850,7 +856,14 @@ static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self,
X509_free(peer_cert); X509_free(peer_cert);
if (result != TSI_OK) return result; if (result != TSI_OK) return result;
} }
#if TSI_OPENSSL_ALPN_SUPPORT
SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len); SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len);
#endif /* TSI_OPENSSL_ALPN_SUPPORT */
if (alpn_selected == NULL) {
/* Try npn. */
SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected,
&alpn_selected_len);
}
if (alpn_selected != NULL) { if (alpn_selected != NULL) {
size_t i; size_t i;
tsi_peer_property* new_properties = tsi_peer_property* new_properties =
@ -1012,6 +1025,32 @@ static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client,
return TSI_OK; return TSI_OK;
} }
static int select_protocol_list(const unsigned char** out,
unsigned char* outlen,
const unsigned char* client_list,
unsigned int client_list_len,
const unsigned char* server_list,
unsigned int server_list_len) {
const unsigned char* client_current = client_list;
while ((unsigned int)(client_current - client_list) < client_list_len) {
unsigned char client_current_len = *(client_current++);
const unsigned char* server_current = server_list;
while ((server_current >= server_list) &&
(gpr_uintptr)(server_current - server_list) < server_list_len) {
unsigned char server_current_len = *(server_current++);
if ((client_current_len == server_current_len) &&
!memcmp(client_current, server_current, server_current_len)) {
*out = server_current;
*outlen = server_current_len;
return SSL_TLSEXT_ERR_OK;
}
server_current += server_current_len;
}
client_current += client_current_len;
}
return SSL_TLSEXT_ERR_NOACK;
}
/* --- tsi_ssl__client_handshaker_factory methods implementation. --- */ /* --- tsi_ssl__client_handshaker_factory methods implementation. --- */
static tsi_result ssl_client_handshaker_factory_create_handshaker( static tsi_result ssl_client_handshaker_factory_create_handshaker(
@ -1027,10 +1066,21 @@ static void ssl_client_handshaker_factory_destroy(
tsi_ssl_handshaker_factory* self) { tsi_ssl_handshaker_factory* self) {
tsi_ssl_client_handshaker_factory* impl = tsi_ssl_client_handshaker_factory* impl =
(tsi_ssl_client_handshaker_factory*)self; (tsi_ssl_client_handshaker_factory*)self;
SSL_CTX_free(impl->ssl_context); if (impl->ssl_context != NULL) SSL_CTX_free(impl->ssl_context);
if (impl->alpn_protocol_list != NULL) free(impl->alpn_protocol_list);
free(impl); free(impl);
} }
static int client_handshaker_factory_npn_callback(
SSL* ssl, unsigned char** out, unsigned char* outlen,
const unsigned char* in, unsigned int inlen, void* arg) {
tsi_ssl_client_handshaker_factory* factory =
(tsi_ssl_client_handshaker_factory*)arg;
return select_protocol_list((const unsigned char**)out, outlen,
factory->alpn_protocol_list,
factory->alpn_protocol_list_length, in, inlen);
}
/* --- tsi_ssl_server_handshaker_factory methods implementation. --- */ /* --- tsi_ssl_server_handshaker_factory methods implementation. --- */
static tsi_result ssl_server_handshaker_factory_create_handshaker( static tsi_result ssl_server_handshaker_factory_create_handshaker(
@ -1134,30 +1184,25 @@ static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap,
return SSL_TLSEXT_ERR_ALERT_WARNING; return SSL_TLSEXT_ERR_ALERT_WARNING;
} }
#if TSI_OPENSSL_ALPN_SUPPORT
static int server_handshaker_factory_alpn_callback( static int server_handshaker_factory_alpn_callback(
SSL* ssl, const unsigned char** out, unsigned char* outlen, SSL* ssl, const unsigned char** out, unsigned char* outlen,
const unsigned char* in, unsigned int inlen, void* arg) { const unsigned char* in, unsigned int inlen, void* arg) {
tsi_ssl_server_handshaker_factory* factory = tsi_ssl_server_handshaker_factory* factory =
(tsi_ssl_server_handshaker_factory*)arg; (tsi_ssl_server_handshaker_factory*)arg;
const unsigned char* client_current = in; return select_protocol_list(out, outlen, in, inlen,
while ((unsigned int)(client_current - in) < inlen) { factory->alpn_protocol_list,
unsigned char client_current_len = *(client_current++); factory->alpn_protocol_list_length);
const unsigned char* server_current = factory->alpn_protocol_list; }
while ((server_current >= factory->alpn_protocol_list) && #endif /* TSI_OPENSSL_ALPN_SUPPORT */
(gpr_uintptr)(server_current - factory->alpn_protocol_list) <
factory->alpn_protocol_list_length) { static int server_handshaker_factory_npn_advertised_callback(
unsigned char server_current_len = *(server_current++); SSL* ssl, const unsigned char** out, unsigned int* outlen, void* arg) {
if ((client_current_len == server_current_len) && tsi_ssl_server_handshaker_factory* factory =
!memcmp(client_current, server_current, server_current_len)) { (tsi_ssl_server_handshaker_factory*)arg;
*out = server_current; *out = factory->alpn_protocol_list;
*outlen = server_current_len; *outlen = factory->alpn_protocol_list_length;
return SSL_TLSEXT_ERR_OK; return SSL_TLSEXT_ERR_OK;
}
server_current += server_current_len;
}
client_current += client_current_len;
}
return SSL_TLSEXT_ERR_NOACK;
} }
/* --- tsi_ssl_handshaker_factory constructors. --- */ /* --- tsi_ssl_handshaker_factory constructors. --- */
@ -1184,6 +1229,14 @@ tsi_result tsi_create_ssl_client_handshaker_factory(
gpr_log(GPR_ERROR, "Could not create ssl context."); gpr_log(GPR_ERROR, "Could not create ssl context.");
return TSI_INVALID_ARGUMENT; return TSI_INVALID_ARGUMENT;
} }
impl = calloc(1, sizeof(tsi_ssl_client_handshaker_factory));
if (impl == NULL) {
SSL_CTX_free(ssl_context);
return TSI_OUT_OF_RESOURCES;
}
impl->ssl_context = ssl_context;
do { do {
result = result =
populate_ssl_context(ssl_context, pem_private_key, pem_private_key_size, populate_ssl_context(ssl_context, pem_private_key, pem_private_key_size,
@ -1197,41 +1250,33 @@ tsi_result tsi_create_ssl_client_handshaker_factory(
} }
if (num_alpn_protocols != 0) { if (num_alpn_protocols != 0) {
unsigned char* alpn_protocol_list = NULL;
size_t alpn_protocol_list_length = 0;
int ssl_failed;
result = build_alpn_protocol_name_list( result = build_alpn_protocol_name_list(
alpn_protocols, alpn_protocols_lengths, num_alpn_protocols, alpn_protocols, alpn_protocols_lengths, num_alpn_protocols,
&alpn_protocol_list, &alpn_protocol_list_length); &impl->alpn_protocol_list, &impl->alpn_protocol_list_length);
if (result != TSI_OK) { if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Building alpn list failed with error %s.", gpr_log(GPR_ERROR, "Building alpn list failed with error %s.",
tsi_result_to_string(result)); tsi_result_to_string(result));
free(alpn_protocol_list);
break; break;
} }
ssl_failed = SSL_CTX_set_alpn_protos(ssl_context, alpn_protocol_list, #if TSI_OPENSSL_ALPN_SUPPORT
alpn_protocol_list_length); if (SSL_CTX_set_alpn_protos(ssl_context, impl->alpn_protocol_list,
free(alpn_protocol_list); impl->alpn_protocol_list_length)) {
if (ssl_failed) {
gpr_log(GPR_ERROR, "Could not set alpn protocol list to context."); gpr_log(GPR_ERROR, "Could not set alpn protocol list to context.");
result = TSI_INVALID_ARGUMENT; result = TSI_INVALID_ARGUMENT;
break; break;
} }
#endif /* TSI_OPENSSL_ALPN_SUPPORT */
SSL_CTX_set_next_proto_select_cb(
ssl_context, client_handshaker_factory_npn_callback, impl);
} }
} while (0); } while (0);
if (result != TSI_OK) { if (result != TSI_OK) {
SSL_CTX_free(ssl_context); ssl_client_handshaker_factory_destroy(&impl->base);
return result; return result;
} }
SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL); SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL);
/* TODO(jboeuf): Add revocation verification. */ /* TODO(jboeuf): Add revocation verification. */
impl = calloc(1, sizeof(tsi_ssl_client_handshaker_factory));
if (impl == NULL) {
SSL_CTX_free(ssl_context);
return TSI_OUT_OF_RESOURCES;
}
impl->ssl_context = ssl_context;
impl->base.create_handshaker = impl->base.create_handshaker =
ssl_client_handshaker_factory_create_handshaker; ssl_client_handshaker_factory_create_handshaker;
impl->base.destroy = ssl_client_handshaker_factory_destroy; impl->base.destroy = ssl_client_handshaker_factory_destroy;
@ -1322,8 +1367,13 @@ tsi_result tsi_create_ssl_server_handshaker_factory(
impl->ssl_contexts[i], impl->ssl_contexts[i],
ssl_server_handshaker_factory_servername_callback); ssl_server_handshaker_factory_servername_callback);
SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl); SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl);
#if TSI_OPENSSL_ALPN_SUPPORT
SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i], SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i],
server_handshaker_factory_alpn_callback, impl); server_handshaker_factory_alpn_callback, impl);
#endif /* TSI_OPENSSL_ALPN_SUPPORT */
SSL_CTX_set_next_protos_advertised_cb(
impl->ssl_contexts[i],
server_handshaker_factory_npn_advertised_callback, impl);
} while (0); } while (0);
if (result != TSI_OK) { if (result != TSI_OK) {

Loading…
Cancel
Save