in src/core/tsi/ssl_transport_security.cc [1917:2060]
tsi_result tsi_create_ssl_server_handshaker_factory_with_options(
const tsi_ssl_server_handshaker_options* options,
tsi_ssl_server_handshaker_factory** factory) {
tsi_ssl_server_handshaker_factory* impl = nullptr;
tsi_result result = TSI_OK;
size_t i = 0;
gpr_once_init(&g_init_openssl_once, init_openssl);
if (factory == nullptr) return TSI_INVALID_ARGUMENT;
*factory = nullptr;
if (options->num_key_cert_pairs == 0 ||
options->pem_key_cert_pairs == nullptr) {
return TSI_INVALID_ARGUMENT;
}
impl = static_cast<tsi_ssl_server_handshaker_factory*>(
gpr_zalloc(sizeof(*impl)));
tsi_ssl_handshaker_factory_init(&impl->base);
impl->base.vtable = &server_handshaker_factory_vtable;
impl->ssl_contexts = static_cast<SSL_CTX**>(
gpr_zalloc(options->num_key_cert_pairs * sizeof(SSL_CTX*)));
impl->ssl_context_x509_subject_names = static_cast<tsi_peer*>(
gpr_zalloc(options->num_key_cert_pairs * sizeof(tsi_peer)));
if (impl->ssl_contexts == nullptr ||
impl->ssl_context_x509_subject_names == nullptr) {
tsi_ssl_handshaker_factory_unref(&impl->base);
return TSI_OUT_OF_RESOURCES;
}
impl->ssl_context_count = options->num_key_cert_pairs;
if (options->num_alpn_protocols > 0) {
result = build_alpn_protocol_name_list(
options->alpn_protocols, options->num_alpn_protocols,
&impl->alpn_protocol_list, &impl->alpn_protocol_list_length);
if (result != TSI_OK) {
tsi_ssl_handshaker_factory_unref(&impl->base);
return result;
}
}
for (i = 0; i < options->num_key_cert_pairs; i++) {
do {
#if defined(OPENSSL_NO_TLS1_2_METHOD) || OPENSSL_API_COMPAT >= 0x10100000L
impl->ssl_contexts[i] = SSL_CTX_new(TLS_method());
#else
impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method());
#endif
if (impl->ssl_contexts[i] == nullptr) {
gpr_log(GPR_ERROR, "Could not create ssl context.");
result = TSI_OUT_OF_RESOURCES;
break;
}
result = populate_ssl_context(impl->ssl_contexts[i],
&options->pem_key_cert_pairs[i],
options->cipher_suites);
if (result != TSI_OK) break;
// TODO(elessar): Provide ability to disable session ticket keys.
// Allow client cache sessions (it's needed for OpenSSL only).
int set_sid_ctx_result = SSL_CTX_set_session_id_context(
impl->ssl_contexts[i], kSslSessionIdContext,
GPR_ARRAY_SIZE(kSslSessionIdContext));
if (set_sid_ctx_result == 0) {
gpr_log(GPR_ERROR, "Failed to set session id context.");
result = TSI_INTERNAL_ERROR;
break;
}
if (options->session_ticket_key != nullptr) {
if (SSL_CTX_set_tlsext_ticket_keys(
impl->ssl_contexts[i],
const_cast<char*>(options->session_ticket_key),
options->session_ticket_key_size) == 0) {
gpr_log(GPR_ERROR, "Invalid STEK size.");
result = TSI_INVALID_ARGUMENT;
break;
}
}
if (options->pem_client_root_certs != nullptr) {
STACK_OF(X509_NAME)* root_names = nullptr;
result = ssl_ctx_load_verification_certs(
impl->ssl_contexts[i], options->pem_client_root_certs,
strlen(options->pem_client_root_certs), &root_names);
if (result != TSI_OK) {
gpr_log(GPR_ERROR, "Invalid verification certs.");
break;
}
SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names);
}
switch (options->client_certificate_request) {
case TSI_DONT_REQUEST_CLIENT_CERTIFICATE:
SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, nullptr);
break;
case TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER,
NullVerifyCallback);
break;
case TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY:
SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, nullptr);
break;
case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY:
SSL_CTX_set_verify(impl->ssl_contexts[i],
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
NullVerifyCallback);
break;
case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY:
SSL_CTX_set_verify(impl->ssl_contexts[i],
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
break;
}
/* TODO(jboeuf): Add revocation verification. */
result = tsi_ssl_extract_x509_subject_names_from_pem_cert(
options->pem_key_cert_pairs[i].cert_chain,
&impl->ssl_context_x509_subject_names[i]);
if (result != TSI_OK) break;
SSL_CTX_set_tlsext_servername_callback(
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) {
tsi_ssl_handshaker_factory_unref(&impl->base);
return result;
}
}
*factory = impl;
return TSI_OK;
}