|  | @@ -54,8 +54,16 @@
 | 
	
		
			
				|  |  |  #define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384
 | 
	
		
			
				|  |  |  #define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/* Putting a macro like this and littering the source file with #if is really
 | 
	
		
			
				|  |  | +   bad practice.
 | 
	
		
			
				|  |  | +   TODO(jboeuf): refactor all the #if / #endif in a separate module. */
 | 
	
		
			
				|  |  | +#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
 | 
	
		
			
				|  |  | - * 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
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* --- Structure definitions. ---*/
 | 
	
	
		
			
				|  | @@ -70,6 +78,8 @@ struct tsi_ssl_handshaker_factory {
 | 
	
		
			
				|  |  |  typedef struct {
 | 
	
		
			
				|  |  |    tsi_ssl_handshaker_factory base;
 | 
	
		
			
				|  |  |    SSL_CTX* ssl_context;
 | 
	
		
			
				|  |  | +  unsigned char* alpn_protocol_list;
 | 
	
		
			
				|  |  | +  size_t alpn_protocol_list_length;
 | 
	
		
			
				|  |  |  } tsi_ssl_client_handshaker_factory;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  typedef struct {
 | 
	
	
		
			
				|  | @@ -841,7 +851,7 @@ static tsi_result ssl_handshaker_process_bytes_from_peer(
 | 
	
		
			
				|  |  |  static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self,
 | 
	
		
			
				|  |  |                                                tsi_peer* peer) {
 | 
	
		
			
				|  |  |    tsi_result result = TSI_OK;
 | 
	
		
			
				|  |  | -  const unsigned char* alpn_selected;
 | 
	
		
			
				|  |  | +  const unsigned char* alpn_selected = NULL;
 | 
	
		
			
				|  |  |    unsigned int alpn_selected_len;
 | 
	
		
			
				|  |  |    tsi_ssl_handshaker* impl = (tsi_ssl_handshaker*)self;
 | 
	
		
			
				|  |  |    X509* peer_cert = SSL_get_peer_certificate(impl->ssl);
 | 
	
	
		
			
				|  | @@ -850,7 +860,14 @@ static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self,
 | 
	
		
			
				|  |  |      X509_free(peer_cert);
 | 
	
		
			
				|  |  |      if (result != TSI_OK) return result;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +#if TSI_OPENSSL_ALPN_SUPPORT
 | 
	
		
			
				|  |  |    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) {
 | 
	
		
			
				|  |  |      size_t i;
 | 
	
		
			
				|  |  |      tsi_peer_property* new_properties =
 | 
	
	
		
			
				|  | @@ -1012,6 +1029,32 @@ static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client,
 | 
	
		
			
				|  |  |    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. --- */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static tsi_result ssl_client_handshaker_factory_create_handshaker(
 | 
	
	
		
			
				|  | @@ -1027,10 +1070,21 @@ static void ssl_client_handshaker_factory_destroy(
 | 
	
		
			
				|  |  |      tsi_ssl_handshaker_factory* self) {
 | 
	
		
			
				|  |  |    tsi_ssl_client_handshaker_factory* impl =
 | 
	
		
			
				|  |  |        (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);
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +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. --- */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  static tsi_result ssl_server_handshaker_factory_create_handshaker(
 | 
	
	
		
			
				|  | @@ -1134,30 +1188,25 @@ static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap,
 | 
	
		
			
				|  |  |    return SSL_TLSEXT_ERR_ALERT_WARNING;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +#if TSI_OPENSSL_ALPN_SUPPORT
 | 
	
		
			
				|  |  |  static int server_handshaker_factory_alpn_callback(
 | 
	
		
			
				|  |  |      SSL* ssl, const unsigned char** out, unsigned char* outlen,
 | 
	
		
			
				|  |  |      const unsigned char* in, unsigned int inlen, void* arg) {
 | 
	
		
			
				|  |  |    tsi_ssl_server_handshaker_factory* factory =
 | 
	
		
			
				|  |  |        (tsi_ssl_server_handshaker_factory*)arg;
 | 
	
		
			
				|  |  | -  const unsigned char* client_current = in;
 | 
	
		
			
				|  |  | -  while ((unsigned int)(client_current - in) < inlen) {
 | 
	
		
			
				|  |  | -    unsigned char client_current_len = *(client_current++);
 | 
	
		
			
				|  |  | -    const unsigned char* server_current = factory->alpn_protocol_list;
 | 
	
		
			
				|  |  | -    while ((server_current >= factory->alpn_protocol_list) &&
 | 
	
		
			
				|  |  | -           (gpr_uintptr)(server_current - factory->alpn_protocol_list) <
 | 
	
		
			
				|  |  | -           factory->alpn_protocol_list_length) {
 | 
	
		
			
				|  |  | -      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;
 | 
	
		
			
				|  |  | +  return select_protocol_list(out, outlen, in, inlen,
 | 
	
		
			
				|  |  | +                              factory->alpn_protocol_list,
 | 
	
		
			
				|  |  | +                              factory->alpn_protocol_list_length);
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +#endif /* TSI_OPENSSL_ALPN_SUPPORT */
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +static int server_handshaker_factory_npn_advertised_callback(
 | 
	
		
			
				|  |  | +    SSL* ssl, const unsigned char** out, unsigned int* outlen, void* arg) {
 | 
	
		
			
				|  |  | +  tsi_ssl_server_handshaker_factory* factory =
 | 
	
		
			
				|  |  | +      (tsi_ssl_server_handshaker_factory*)arg;
 | 
	
		
			
				|  |  | +  *out = factory->alpn_protocol_list;
 | 
	
		
			
				|  |  | +  *outlen = factory->alpn_protocol_list_length;
 | 
	
		
			
				|  |  | +  return SSL_TLSEXT_ERR_OK;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /* --- tsi_ssl_handshaker_factory constructors. --- */
 | 
	
	
		
			
				|  | @@ -1184,6 +1233,14 @@ tsi_result tsi_create_ssl_client_handshaker_factory(
 | 
	
		
			
				|  |  |      gpr_log(GPR_ERROR, "Could not create ssl context.");
 | 
	
		
			
				|  |  |      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 {
 | 
	
		
			
				|  |  |      result =
 | 
	
		
			
				|  |  |          populate_ssl_context(ssl_context, pem_private_key, pem_private_key_size,
 | 
	
	
		
			
				|  | @@ -1197,41 +1254,33 @@ tsi_result tsi_create_ssl_client_handshaker_factory(
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      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(
 | 
	
		
			
				|  |  |            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) {
 | 
	
		
			
				|  |  |          gpr_log(GPR_ERROR, "Building alpn list failed with error %s.",
 | 
	
		
			
				|  |  |                  tsi_result_to_string(result));
 | 
	
		
			
				|  |  | -        free(alpn_protocol_list);
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | -      ssl_failed = SSL_CTX_set_alpn_protos(ssl_context, alpn_protocol_list,
 | 
	
		
			
				|  |  | -                                           alpn_protocol_list_length);
 | 
	
		
			
				|  |  | -      free(alpn_protocol_list);
 | 
	
		
			
				|  |  | -      if (ssl_failed) {
 | 
	
		
			
				|  |  | +#if TSI_OPENSSL_ALPN_SUPPORT
 | 
	
		
			
				|  |  | +      if (SSL_CTX_set_alpn_protos(ssl_context, impl->alpn_protocol_list,
 | 
	
		
			
				|  |  | +                                  impl->alpn_protocol_list_length)) {
 | 
	
		
			
				|  |  |          gpr_log(GPR_ERROR, "Could not set alpn protocol list to context.");
 | 
	
		
			
				|  |  |          result = TSI_INVALID_ARGUMENT;
 | 
	
		
			
				|  |  |          break;
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  | +#endif /* TSI_OPENSSL_ALPN_SUPPORT */
 | 
	
		
			
				|  |  | +      SSL_CTX_set_next_proto_select_cb(
 | 
	
		
			
				|  |  | +          ssl_context, client_handshaker_factory_npn_callback, impl);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    } while (0);
 | 
	
		
			
				|  |  |    if (result != TSI_OK) {
 | 
	
		
			
				|  |  | -    SSL_CTX_free(ssl_context);
 | 
	
		
			
				|  |  | +    ssl_client_handshaker_factory_destroy(&impl->base);
 | 
	
		
			
				|  |  |      return result;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |    SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL);
 | 
	
		
			
				|  |  |    /* 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 =
 | 
	
		
			
				|  |  |        ssl_client_handshaker_factory_create_handshaker;
 | 
	
		
			
				|  |  |    impl->base.destroy = ssl_client_handshaker_factory_destroy;
 | 
	
	
		
			
				|  | @@ -1322,8 +1371,13 @@ tsi_result tsi_create_ssl_server_handshaker_factory(
 | 
	
		
			
				|  |  |            impl->ssl_contexts[i],
 | 
	
		
			
				|  |  |            ssl_server_handshaker_factory_servername_callback);
 | 
	
		
			
				|  |  |        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],
 | 
	
		
			
				|  |  |                                   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);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (result != TSI_OK) {
 |