|
|
|
@ -479,9 +479,9 @@ static tsi_result do_ssl_write(SSL *ssl, unsigned char *unprotected_bytes, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Loads an in-memory PEM certificate chain into the SSL context. */ |
|
|
|
|
static tsi_result ssl_ctx_use_certificate_chain( |
|
|
|
|
SSL_CTX *context, const unsigned char *pem_cert_chain, |
|
|
|
|
size_t pem_cert_chain_size) { |
|
|
|
|
static tsi_result ssl_ctx_use_certificate_chain(SSL_CTX *context, |
|
|
|
|
const char *pem_cert_chain, |
|
|
|
|
size_t pem_cert_chain_size) { |
|
|
|
|
tsi_result result = TSI_OK; |
|
|
|
|
X509 *certificate = NULL; |
|
|
|
|
BIO *pem; |
|
|
|
@ -522,8 +522,7 @@ static tsi_result ssl_ctx_use_certificate_chain( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Loads an in-memory PEM private key into the SSL context. */ |
|
|
|
|
static tsi_result ssl_ctx_use_private_key(SSL_CTX *context, |
|
|
|
|
const unsigned char *pem_key, |
|
|
|
|
static tsi_result ssl_ctx_use_private_key(SSL_CTX *context, const char *pem_key, |
|
|
|
|
size_t pem_key_size) { |
|
|
|
|
tsi_result result = TSI_OK; |
|
|
|
|
EVP_PKEY *private_key = NULL; |
|
|
|
@ -549,9 +548,11 @@ static tsi_result ssl_ctx_use_private_key(SSL_CTX *context, |
|
|
|
|
|
|
|
|
|
/* Loads in-memory PEM verification certs into the SSL context and optionally
|
|
|
|
|
returns the verification cert names (root_names can be NULL). */ |
|
|
|
|
static tsi_result ssl_ctx_load_verification_certs( |
|
|
|
|
SSL_CTX *context, const unsigned char *pem_roots, size_t pem_roots_size, |
|
|
|
|
STACK_OF(X509_NAME) * *root_names) { |
|
|
|
|
static tsi_result ssl_ctx_load_verification_certs(SSL_CTX *context, |
|
|
|
|
const char *pem_roots, |
|
|
|
|
size_t pem_roots_size, |
|
|
|
|
STACK_OF(X509_NAME) * |
|
|
|
|
*root_names) { |
|
|
|
|
tsi_result result = TSI_OK; |
|
|
|
|
size_t num_roots = 0; |
|
|
|
|
X509 *root = NULL; |
|
|
|
@ -618,24 +619,25 @@ static tsi_result ssl_ctx_load_verification_certs( |
|
|
|
|
/* Populates the SSL context with a private key and a cert chain, and sets the
|
|
|
|
|
cipher list and the ephemeral ECDH key. */ |
|
|
|
|
static tsi_result populate_ssl_context( |
|
|
|
|
SSL_CTX *context, const unsigned char *pem_private_key, |
|
|
|
|
size_t pem_private_key_size, const unsigned char *pem_certificate_chain, |
|
|
|
|
size_t pem_certificate_chain_size, const char *cipher_list) { |
|
|
|
|
SSL_CTX *context, const tsi_ssl_pem_key_cert_pair *key_cert_pair, |
|
|
|
|
const char *cipher_list) { |
|
|
|
|
tsi_result result = TSI_OK; |
|
|
|
|
if (pem_certificate_chain != NULL) { |
|
|
|
|
result = ssl_ctx_use_certificate_chain(context, pem_certificate_chain, |
|
|
|
|
pem_certificate_chain_size); |
|
|
|
|
if (result != TSI_OK) { |
|
|
|
|
gpr_log(GPR_ERROR, "Invalid cert chain file."); |
|
|
|
|
return result; |
|
|
|
|
if (key_cert_pair != NULL) { |
|
|
|
|
if (key_cert_pair->cert_chain != NULL) { |
|
|
|
|
result = ssl_ctx_use_certificate_chain(context, key_cert_pair->cert_chain, |
|
|
|
|
strlen(key_cert_pair->cert_chain)); |
|
|
|
|
if (result != TSI_OK) { |
|
|
|
|
gpr_log(GPR_ERROR, "Invalid cert chain file."); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (pem_private_key != NULL) { |
|
|
|
|
result = |
|
|
|
|
ssl_ctx_use_private_key(context, pem_private_key, pem_private_key_size); |
|
|
|
|
if (result != TSI_OK || !SSL_CTX_check_private_key(context)) { |
|
|
|
|
gpr_log(GPR_ERROR, "Invalid private key."); |
|
|
|
|
return result != TSI_OK ? result : TSI_INVALID_ARGUMENT; |
|
|
|
|
if (key_cert_pair->private_key != NULL) { |
|
|
|
|
result = ssl_ctx_use_private_key(context, key_cert_pair->private_key, |
|
|
|
|
strlen(key_cert_pair->private_key)); |
|
|
|
|
if (result != TSI_OK || !SSL_CTX_check_private_key(context)) { |
|
|
|
|
gpr_log(GPR_ERROR, "Invalid private key."); |
|
|
|
|
return result != TSI_OK ? result : TSI_INVALID_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if ((cipher_list != NULL) && !SSL_CTX_set_cipher_list(context, cipher_list)) { |
|
|
|
@ -656,13 +658,12 @@ static tsi_result populate_ssl_context( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Extracts the CN and the SANs from an X509 cert as a peer object. */ |
|
|
|
|
static tsi_result extract_x509_subject_names_from_pem_cert( |
|
|
|
|
const unsigned char *pem_cert, size_t pem_cert_size, tsi_peer *peer) { |
|
|
|
|
static tsi_result extract_x509_subject_names_from_pem_cert(const char *pem_cert, |
|
|
|
|
tsi_peer *peer) { |
|
|
|
|
tsi_result result = TSI_OK; |
|
|
|
|
X509 *cert = NULL; |
|
|
|
|
BIO *pem; |
|
|
|
|
GPR_ASSERT(pem_cert_size <= INT_MAX); |
|
|
|
|
pem = BIO_new_mem_buf((void *)pem_cert, (int)pem_cert_size); |
|
|
|
|
pem = BIO_new_mem_buf((void *)pem_cert, (int)strlen(pem_cert)); |
|
|
|
|
if (pem == NULL) return TSI_OUT_OF_RESOURCES; |
|
|
|
|
|
|
|
|
|
cert = PEM_read_bio_X509(pem, NULL, NULL, ""); |
|
|
|
@ -679,8 +680,7 @@ static tsi_result extract_x509_subject_names_from_pem_cert( |
|
|
|
|
|
|
|
|
|
/* Builds the alpn protocol name list according to rfc 7301. */ |
|
|
|
|
static tsi_result build_alpn_protocol_name_list( |
|
|
|
|
const unsigned char **alpn_protocols, |
|
|
|
|
const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, |
|
|
|
|
const char **alpn_protocols, uint16_t num_alpn_protocols, |
|
|
|
|
unsigned char **protocol_name_list, size_t *protocol_name_list_length) { |
|
|
|
|
uint16_t i; |
|
|
|
|
unsigned char *current; |
|
|
|
@ -688,19 +688,21 @@ static tsi_result build_alpn_protocol_name_list( |
|
|
|
|
*protocol_name_list_length = 0; |
|
|
|
|
if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT; |
|
|
|
|
for (i = 0; i < num_alpn_protocols; i++) { |
|
|
|
|
if (alpn_protocols_lengths[i] == 0) { |
|
|
|
|
gpr_log(GPR_ERROR, "Invalid 0-length protocol name."); |
|
|
|
|
size_t length = alpn_protocols[i] == NULL ? 0 : strlen(alpn_protocols[i]); |
|
|
|
|
if (length == 0 || length > 255) { |
|
|
|
|
gpr_log(GPR_ERROR, "Invalid protocol name length: %d.", (int)length); |
|
|
|
|
return TSI_INVALID_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
*protocol_name_list_length += (size_t)alpn_protocols_lengths[i] + 1; |
|
|
|
|
*protocol_name_list_length += length + 1; |
|
|
|
|
} |
|
|
|
|
*protocol_name_list = gpr_malloc(*protocol_name_list_length); |
|
|
|
|
if (*protocol_name_list == NULL) return TSI_OUT_OF_RESOURCES; |
|
|
|
|
current = *protocol_name_list; |
|
|
|
|
for (i = 0; i < num_alpn_protocols; i++) { |
|
|
|
|
*(current++) = alpn_protocols_lengths[i]; |
|
|
|
|
memcpy(current, alpn_protocols[i], alpn_protocols_lengths[i]); |
|
|
|
|
current += alpn_protocols_lengths[i]; |
|
|
|
|
size_t length = strlen(alpn_protocols[i]); |
|
|
|
|
*(current++) = (uint8_t)length; /* max checked above. */ |
|
|
|
|
memcpy(current, alpn_protocols[i], length); |
|
|
|
|
current += length; |
|
|
|
|
} |
|
|
|
|
/* Safety check. */ |
|
|
|
|
if ((current < *protocol_name_list) || |
|
|
|
@ -1280,11 +1282,9 @@ static int server_handshaker_factory_npn_advertised_callback( |
|
|
|
|
/* --- tsi_ssl_handshaker_factory constructors. --- */ |
|
|
|
|
|
|
|
|
|
tsi_result tsi_create_ssl_client_handshaker_factory( |
|
|
|
|
const unsigned char *pem_private_key, size_t pem_private_key_size, |
|
|
|
|
const unsigned char *pem_cert_chain, size_t pem_cert_chain_size, |
|
|
|
|
const unsigned char *pem_root_certs, size_t pem_root_certs_size, |
|
|
|
|
const char *cipher_list, const unsigned char **alpn_protocols, |
|
|
|
|
const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, |
|
|
|
|
const tsi_ssl_pem_key_cert_pair *pem_key_cert_pair, |
|
|
|
|
const char *pem_root_certs, const char *cipher_suites, |
|
|
|
|
const char **alpn_protocols, uint16_t num_alpn_protocols, |
|
|
|
|
tsi_ssl_client_handshaker_factory **factory) { |
|
|
|
|
SSL_CTX *ssl_context = NULL; |
|
|
|
|
tsi_ssl_client_handshaker_factory *impl = NULL; |
|
|
|
@ -1307,20 +1307,19 @@ tsi_result tsi_create_ssl_client_handshaker_factory( |
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
result = |
|
|
|
|
populate_ssl_context(ssl_context, pem_private_key, pem_private_key_size, |
|
|
|
|
pem_cert_chain, pem_cert_chain_size, cipher_list); |
|
|
|
|
populate_ssl_context(ssl_context, pem_key_cert_pair, cipher_suites); |
|
|
|
|
if (result != TSI_OK) break; |
|
|
|
|
result = ssl_ctx_load_verification_certs(ssl_context, pem_root_certs, |
|
|
|
|
pem_root_certs_size, NULL); |
|
|
|
|
strlen(pem_root_certs), NULL); |
|
|
|
|
if (result != TSI_OK) { |
|
|
|
|
gpr_log(GPR_ERROR, "Cannot load server root certificates."); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (num_alpn_protocols != 0) { |
|
|
|
|
result = build_alpn_protocol_name_list( |
|
|
|
|
alpn_protocols, alpn_protocols_lengths, num_alpn_protocols, |
|
|
|
|
&impl->alpn_protocol_list, &impl->alpn_protocol_list_length); |
|
|
|
|
result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, |
|
|
|
|
&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)); |
|
|
|
@ -1352,34 +1351,24 @@ tsi_result tsi_create_ssl_client_handshaker_factory( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tsi_result tsi_create_ssl_server_handshaker_factory( |
|
|
|
|
const unsigned char **pem_private_keys, |
|
|
|
|
const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains, |
|
|
|
|
const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count, |
|
|
|
|
const unsigned char *pem_client_root_certs, |
|
|
|
|
size_t pem_client_root_certs_size, int force_client_auth, |
|
|
|
|
const char *cipher_list, const unsigned char **alpn_protocols, |
|
|
|
|
const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, |
|
|
|
|
const tsi_ssl_pem_key_cert_pair *pem_key_cert_pairs, |
|
|
|
|
size_t num_key_cert_pairs, const char *pem_client_root_certs, |
|
|
|
|
int force_client_auth, const char *cipher_suites, |
|
|
|
|
const char **alpn_protocols, uint16_t num_alpn_protocols, |
|
|
|
|
tsi_ssl_server_handshaker_factory **factory) { |
|
|
|
|
return tsi_create_ssl_server_handshaker_factory_ex( |
|
|
|
|
pem_private_keys, pem_private_keys_sizes, pem_cert_chains, |
|
|
|
|
pem_cert_chains_sizes, key_cert_pair_count, pem_client_root_certs, |
|
|
|
|
pem_client_root_certs_size, |
|
|
|
|
pem_key_cert_pairs, num_key_cert_pairs, pem_client_root_certs, |
|
|
|
|
force_client_auth ? TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY |
|
|
|
|
: TSI_DONT_REQUEST_CLIENT_CERTIFICATE, |
|
|
|
|
cipher_list, alpn_protocols, alpn_protocols_lengths, num_alpn_protocols, |
|
|
|
|
factory); |
|
|
|
|
cipher_suites, alpn_protocols, num_alpn_protocols, factory); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
tsi_result tsi_create_ssl_server_handshaker_factory_ex( |
|
|
|
|
const unsigned char **pem_private_keys, |
|
|
|
|
const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains, |
|
|
|
|
const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count, |
|
|
|
|
const unsigned char *pem_client_root_certs, |
|
|
|
|
size_t pem_client_root_certs_size, |
|
|
|
|
const tsi_ssl_pem_key_cert_pair *pem_key_cert_pairs, |
|
|
|
|
size_t num_key_cert_pairs, const char *pem_client_root_certs, |
|
|
|
|
tsi_client_certificate_request_type client_certificate_request, |
|
|
|
|
const char *cipher_list, const unsigned char **alpn_protocols, |
|
|
|
|
const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols, |
|
|
|
|
tsi_ssl_server_handshaker_factory **factory) { |
|
|
|
|
const char *cipher_suites, const char **alpn_protocols, |
|
|
|
|
uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory **factory) { |
|
|
|
|
tsi_ssl_server_handshaker_factory *impl = NULL; |
|
|
|
|
tsi_result result = TSI_OK; |
|
|
|
|
size_t i = 0; |
|
|
|
@ -1388,33 +1377,32 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( |
|
|
|
|
|
|
|
|
|
if (factory == NULL) return TSI_INVALID_ARGUMENT; |
|
|
|
|
*factory = NULL; |
|
|
|
|
if (key_cert_pair_count == 0 || pem_private_keys == NULL || |
|
|
|
|
pem_cert_chains == NULL) { |
|
|
|
|
if (num_key_cert_pairs == 0 || pem_key_cert_pairs == NULL) { |
|
|
|
|
return TSI_INVALID_ARGUMENT; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl = gpr_zalloc(sizeof(*impl)); |
|
|
|
|
impl->ssl_contexts = gpr_zalloc(key_cert_pair_count * sizeof(SSL_CTX *)); |
|
|
|
|
impl->ssl_contexts = gpr_zalloc(num_key_cert_pairs * sizeof(SSL_CTX *)); |
|
|
|
|
impl->ssl_context_x509_subject_names = |
|
|
|
|
gpr_zalloc(key_cert_pair_count * sizeof(tsi_peer)); |
|
|
|
|
gpr_zalloc(num_key_cert_pairs * sizeof(tsi_peer)); |
|
|
|
|
if (impl->ssl_contexts == NULL || |
|
|
|
|
impl->ssl_context_x509_subject_names == NULL) { |
|
|
|
|
tsi_ssl_server_handshaker_factory_destroy(impl); |
|
|
|
|
return TSI_OUT_OF_RESOURCES; |
|
|
|
|
} |
|
|
|
|
impl->ssl_context_count = key_cert_pair_count; |
|
|
|
|
impl->ssl_context_count = num_key_cert_pairs; |
|
|
|
|
|
|
|
|
|
if (num_alpn_protocols > 0) { |
|
|
|
|
result = build_alpn_protocol_name_list( |
|
|
|
|
alpn_protocols, alpn_protocols_lengths, num_alpn_protocols, |
|
|
|
|
&impl->alpn_protocol_list, &impl->alpn_protocol_list_length); |
|
|
|
|
result = build_alpn_protocol_name_list(alpn_protocols, num_alpn_protocols, |
|
|
|
|
&impl->alpn_protocol_list, |
|
|
|
|
&impl->alpn_protocol_list_length); |
|
|
|
|
if (result != TSI_OK) { |
|
|
|
|
tsi_ssl_server_handshaker_factory_destroy(impl); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (i = 0; i < key_cert_pair_count; i++) { |
|
|
|
|
for (i = 0; i < num_key_cert_pairs; i++) { |
|
|
|
|
do { |
|
|
|
|
impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); |
|
|
|
|
if (impl->ssl_contexts[i] == NULL) { |
|
|
|
@ -1422,16 +1410,15 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( |
|
|
|
|
result = TSI_OUT_OF_RESOURCES; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
result = populate_ssl_context( |
|
|
|
|
impl->ssl_contexts[i], pem_private_keys[i], pem_private_keys_sizes[i], |
|
|
|
|
pem_cert_chains[i], pem_cert_chains_sizes[i], cipher_list); |
|
|
|
|
result = populate_ssl_context(impl->ssl_contexts[i], |
|
|
|
|
&pem_key_cert_pairs[i], cipher_suites); |
|
|
|
|
if (result != TSI_OK) break; |
|
|
|
|
|
|
|
|
|
if (pem_client_root_certs != NULL) { |
|
|
|
|
STACK_OF(X509_NAME) *root_names = NULL; |
|
|
|
|
result = ssl_ctx_load_verification_certs( |
|
|
|
|
impl->ssl_contexts[i], pem_client_root_certs, |
|
|
|
|
pem_client_root_certs_size, &root_names); |
|
|
|
|
strlen(pem_client_root_certs), &root_names); |
|
|
|
|
if (result != TSI_OK) { |
|
|
|
|
gpr_log(GPR_ERROR, "Invalid verification certs."); |
|
|
|
|
break; |
|
|
|
@ -1464,7 +1451,7 @@ tsi_result tsi_create_ssl_server_handshaker_factory_ex( |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
result = extract_x509_subject_names_from_pem_cert( |
|
|
|
|
pem_cert_chains[i], pem_cert_chains_sizes[i], |
|
|
|
|
pem_key_cert_pairs[i].cert_chain, |
|
|
|
|
&impl->ssl_context_x509_subject_names[i]); |
|
|
|
|
if (result != TSI_OK) break; |
|
|
|
|
|
|
|
|
|