aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/tsi
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/tsi')
-rw-r--r--src/core/tsi/fake_transport_security.c1
-rw-r--r--src/core/tsi/ssl_transport_security.c151
-rw-r--r--src/core/tsi/ssl_transport_security.h100
-rw-r--r--src/core/tsi/transport_security.c83
-rw-r--r--src/core/tsi/transport_security.h26
-rw-r--r--src/core/tsi/transport_security_adapter.c236
-rw-r--r--src/core/tsi/transport_security_adapter.h62
-rw-r--r--src/core/tsi/transport_security_interface.h240
8 files changed, 691 insertions, 208 deletions
diff --git a/src/core/tsi/fake_transport_security.c b/src/core/tsi/fake_transport_security.c
index 822fad51cb..1836beefc4 100644
--- a/src/core/tsi/fake_transport_security.c
+++ b/src/core/tsi/fake_transport_security.c
@@ -499,6 +499,7 @@ static const tsi_handshaker_vtable handshaker_vtable = {
fake_handshaker_extract_peer,
fake_handshaker_create_frame_protector,
fake_handshaker_destroy,
+ NULL,
};
tsi_handshaker *tsi_create_fake_handshaker(int is_client) {
diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c
index 984f745b01..e1d634a1fa 100644
--- a/src/core/tsi/ssl_transport_security.c
+++ b/src/core/tsi/ssl_transport_security.c
@@ -45,6 +45,7 @@
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
+#include <sys/socket.h>
#endif
#include <grpc/support/alloc.h>
@@ -478,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;
@@ -521,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;
@@ -548,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;
@@ -617,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)) {
@@ -655,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, "");
@@ -678,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;
@@ -687,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) ||
@@ -1039,6 +1042,7 @@ static const tsi_handshaker_vtable handshaker_vtable = {
ssl_handshaker_extract_peer,
ssl_handshaker_create_frame_protector,
ssl_handshaker_destroy,
+ NULL,
};
/* --- tsi_ssl_handshaker_factory common methods. --- */
@@ -1279,11 +1283,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;
@@ -1306,20 +1308,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));
@@ -1351,34 +1352,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;
@@ -1387,33 +1378,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) {
@@ -1421,16 +1411,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;
@@ -1463,7 +1452,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;
diff --git a/src/core/tsi/ssl_transport_security.h b/src/core/tsi/ssl_transport_security.h
index 48dcaec121..3117571d9f 100644
--- a/src/core/tsi/ssl_transport_security.h
+++ b/src/core/tsi/ssl_transport_security.h
@@ -60,27 +60,32 @@ extern "C" {
typedef struct tsi_ssl_client_handshaker_factory
tsi_ssl_client_handshaker_factory;
+/* Object that holds a private key / certificate chain pair in PEM format. */
+typedef struct {
+ /* private_key is the NULL-terminated string containing the PEM encoding of
+ the client's private key. */
+ const char *private_key;
+
+ /* cert_chain is the NULL-terminated string containing the PEM encoding of
+ the client's certificate chain. */
+ const char *cert_chain;
+} tsi_ssl_pem_key_cert_pair;
+
/* Creates a client handshaker factory.
- - pem_private_key is the buffer containing the PEM encoding of the client's
- private key. This parameter can be NULL if the client does not have a
- private key.
- - pem_private_key_size is the size of the associated buffer.
- - pem_cert_chain is the buffer containing the PEM encoding of the client's
- certificate chain. This parameter can be NULL if the client does not have
- a certificate chain.
- - pem_cert_chain_size is the size of the associated buffer.
- - pem_roots_cert is the buffer containing the PEM encoding of the server
- root certificates. This parameter cannot be NULL.
- - pem_roots_cert_size is the size of the associated buffer.
+ - pem_key_cert_pair is a pointer to the object containing client's private
+ key and certificate chain. This parameter can be NULL if the client does
+ not have such a key/cert pair.
+ - pem_roots_cert is the NULL-terminated string containing the PEM encoding of
+ the client root certificates. This parameter may be NULL if the server does
+ not want the client to be authenticated with SSL.
- cipher_suites contains an optional list of the ciphers that the client
supports. The format of this string is described in:
https://www.openssl.org/docs/apps/ciphers.html.
This parameter can be set to NULL to use the default set of ciphers.
TODO(jboeuf): Revisit the format of this parameter.
- - alpn_protocols is an array containing the protocol names that the
- handshakers created with this factory support. This parameter can be NULL.
- - alpn_protocols_lengths is an array containing the lengths of the alpn
- protocols specified in alpn_protocols. This parameter can be NULL.
+ - alpn_protocols is an array containing the NULL terminated protocol names
+ that the handshakers created with this factory support. This parameter can
+ be NULL.
- num_alpn_protocols is the number of alpn protocols and associated lengths
specified. If this parameter is 0, the other alpn parameters must be NULL.
- factory is the address of the factory pointer to be created.
@@ -88,11 +93,9 @@ typedef struct tsi_ssl_client_handshaker_factory
- This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
where a parameter is invalid. */
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_suites, 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);
/* Creates a client handshaker.
@@ -122,37 +125,19 @@ typedef struct tsi_ssl_server_handshaker_factory
tsi_ssl_server_handshaker_factory;
/* Creates a server handshaker factory.
- - version indicates which version of the specification to use.
- - pem_private_keys is an array containing the PEM encoding of the server's
- private keys. This parameter cannot be NULL. The size of the array is
- given by the key_cert_pair_count parameter.
- - pem_private_keys_sizes is the array containing the sizes of the associated
- buffers.
- - pem_cert_chains is an array containing the PEM encoding of the server's
- cert chains. This parameter cannot be NULL. The size of the array is
- given by the key_cert_pair_count parameter.
- - pem_cert_chains_sizes is the array containing the sizes of the associated
- buffers.
- - key_cert_pair_count indicates the number of items in the private_key_files
- and cert_chain_files parameters.
- - pem_client_roots is the buffer containing the PEM encoding of the client
- root certificates. This parameter may be NULL in which case the server will
- not authenticate the client. If not NULL, the force_client_auth parameter
- specifies if the server will accept only authenticated clients or both
- authenticated and non-authenticated clients.
- - pem_client_root_certs_size is the size of the associated buffer.
- - force_client_auth, if set to non-zero will force the client to authenticate
- with an SSL cert. Note that this option is ignored if pem_client_root_certs
- is NULL or pem_client_roots_certs_size is 0
+ - pem_key_cert_pairs is an array private key / certificate chains of the
+ server.
+ - num_key_cert_pairs is the number of items in the pem_key_cert_pairs array.
+ - pem_root_certs is the NULL-terminated string containing the PEM encoding
+ of the server root certificates.
- cipher_suites contains an optional list of the ciphers that the server
supports. The format of this string is described in:
https://www.openssl.org/docs/apps/ciphers.html.
This parameter can be set to NULL to use the default set of ciphers.
TODO(jboeuf): Revisit the format of this parameter.
- - alpn_protocols is an array containing the protocol names that the
- handshakers created with this factory support. This parameter can be NULL.
- - alpn_protocols_lengths is an array containing the lengths of the alpn
- protocols specified in alpn_protocols. This parameter can be NULL.
+ - alpn_protocols is an array containing the NULL terminated protocol names
+ that the handshakers created with this factory support. This parameter can
+ be NULL.
- num_alpn_protocols is the number of alpn protocols and associated lengths
specified. If this parameter is 0, the other alpn parameters must be NULL.
- factory is the address of the factory pointer to be created.
@@ -160,13 +145,10 @@ typedef struct tsi_ssl_server_handshaker_factory
- This method returns TSI_OK on success or TSI_INVALID_PARAMETER in the case
where a parameter is invalid. */
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_suites, 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);
/* Same as tsi_create_ssl_server_handshaker_factory method except uses
@@ -176,15 +158,11 @@ tsi_result tsi_create_ssl_server_handshaker_factory(
authenticate with an SSL cert. Note that this option is ignored if
pem_client_root_certs is NULL or pem_client_roots_certs_size is 0 */
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_suites, 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);
/* Creates a server handshaker.
- self is the factory from which the handshaker will be created.
diff --git a/src/core/tsi/transport_security.c b/src/core/tsi/transport_security.c
index 67ebe1b1f3..b11c00c43c 100644
--- a/src/core/tsi/transport_security.c
+++ b/src/core/tsi/transport_security.c
@@ -73,6 +73,8 @@ const char *tsi_result_to_string(tsi_result result) {
return "TSI_HANDSHAKE_IN_PROGRESS";
case TSI_OUT_OF_RESOURCES:
return "TSI_OUT_OF_RESOURCES";
+ case TSI_ASYNC:
+ return "TSI_ASYNC";
default:
return "UNKNOWN";
}
@@ -92,6 +94,9 @@ tsi_result tsi_frame_protector_protect(tsi_frame_protector *self,
protected_output_frames_size == NULL) {
return TSI_INVALID_ARGUMENT;
}
+ if (self->vtable == NULL || self->vtable->protect == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
return self->vtable->protect(self, unprotected_bytes, unprotected_bytes_size,
protected_output_frames,
protected_output_frames_size);
@@ -104,6 +109,9 @@ tsi_result tsi_frame_protector_protect_flush(
protected_output_frames_size == NULL || still_pending_size == NULL) {
return TSI_INVALID_ARGUMENT;
}
+ if (self->vtable == NULL || self->vtable->protect_flush == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
return self->vtable->protect_flush(self, protected_output_frames,
protected_output_frames_size,
still_pending_size);
@@ -118,6 +126,9 @@ tsi_result tsi_frame_protector_unprotect(
unprotected_bytes_size == NULL) {
return TSI_INVALID_ARGUMENT;
}
+ if (self->vtable == NULL || self->vtable->unprotect == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
return self->vtable->unprotect(self, protected_frames_bytes,
protected_frames_bytes_size, unprotected_bytes,
unprotected_bytes_size);
@@ -139,6 +150,9 @@ tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self,
return TSI_INVALID_ARGUMENT;
}
if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+ if (self->vtable == NULL || self->vtable->get_bytes_to_send_to_peer == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
return self->vtable->get_bytes_to_send_to_peer(self, bytes, bytes_size);
}
@@ -149,12 +163,18 @@ tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self,
return TSI_INVALID_ARGUMENT;
}
if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+ if (self->vtable == NULL || self->vtable->process_bytes_from_peer == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
return self->vtable->process_bytes_from_peer(self, bytes, bytes_size);
}
tsi_result tsi_handshaker_get_result(tsi_handshaker *self) {
if (self == NULL) return TSI_INVALID_ARGUMENT;
if (self->frame_protector_created) return TSI_FAILED_PRECONDITION;
+ if (self->vtable == NULL || self->vtable->get_result == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
return self->vtable->get_result(self);
}
@@ -165,6 +185,9 @@ tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer) {
if (tsi_handshaker_get_result(self) != TSI_OK) {
return TSI_FAILED_PRECONDITION;
}
+ if (self->vtable == NULL || self->vtable->extract_peer == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
return self->vtable->extract_peer(self, peer);
}
@@ -177,19 +200,77 @@ tsi_result tsi_handshaker_create_frame_protector(
if (tsi_handshaker_get_result(self) != TSI_OK) {
return TSI_FAILED_PRECONDITION;
}
+ if (self->vtable == NULL || self->vtable->create_frame_protector == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
result = self->vtable->create_frame_protector(self, max_protected_frame_size,
protector);
if (result == TSI_OK) {
- self->frame_protector_created = 1;
+ self->frame_protector_created = true;
}
return result;
}
+tsi_result tsi_handshaker_next(
+ tsi_handshaker *self, const unsigned char *received_bytes,
+ size_t received_bytes_size, unsigned char **bytes_to_send,
+ size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result,
+ tsi_handshaker_on_next_done_cb cb, void *user_data) {
+ if (self == NULL) return TSI_INVALID_ARGUMENT;
+ if (self->handshaker_result_created) return TSI_FAILED_PRECONDITION;
+ if (self->vtable == NULL || self->vtable->next == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
+ return self->vtable->next(self, received_bytes, received_bytes_size,
+ bytes_to_send, bytes_to_send_size,
+ handshaker_result, cb, user_data);
+}
+
void tsi_handshaker_destroy(tsi_handshaker *self) {
if (self == NULL) return;
self->vtable->destroy(self);
}
+/* --- tsi_handshaker_result implementation. --- */
+
+tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result *self,
+ tsi_peer *peer) {
+ if (self == NULL || peer == NULL) return TSI_INVALID_ARGUMENT;
+ memset(peer, 0, sizeof(tsi_peer));
+ if (self->vtable == NULL || self->vtable->extract_peer == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
+ return self->vtable->extract_peer(self, peer);
+}
+
+tsi_result tsi_handshaker_result_create_frame_protector(
+ const tsi_handshaker_result *self, size_t *max_protected_frame_size,
+ tsi_frame_protector **protector) {
+ if (self == NULL || protector == NULL) return TSI_INVALID_ARGUMENT;
+ if (self->vtable == NULL || self->vtable->create_frame_protector == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
+ return self->vtable->create_frame_protector(self, max_protected_frame_size,
+ protector);
+}
+
+tsi_result tsi_handshaker_result_get_unused_bytes(
+ const tsi_handshaker_result *self, unsigned char **bytes,
+ size_t *bytes_size) {
+ if (self == NULL || bytes == NULL || bytes_size == NULL) {
+ return TSI_INVALID_ARGUMENT;
+ }
+ if (self->vtable == NULL || self->vtable->get_unused_bytes == NULL) {
+ return TSI_UNIMPLEMENTED;
+ }
+ return self->vtable->get_unused_bytes(self, bytes, bytes_size);
+}
+
+void tsi_handshaker_result_destroy(tsi_handshaker_result *self) {
+ if (self == NULL) return;
+ self->vtable->destroy(self);
+}
+
/* --- tsi_peer implementation. --- */
tsi_peer_property tsi_init_peer_property(void) {
diff --git a/src/core/tsi/transport_security.h b/src/core/tsi/transport_security.h
index 491fa1a8bd..a4c9cbc001 100644
--- a/src/core/tsi/transport_security.h
+++ b/src/core/tsi/transport_security.h
@@ -34,6 +34,8 @@
#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_H
#define GRPC_CORE_TSI_TRANSPORT_SECURITY_H
+#include <stdbool.h>
+
#include "src/core/tsi/transport_security_interface.h"
#ifdef __cplusplus
@@ -81,11 +83,33 @@ typedef struct {
size_t *max_protected_frame_size,
tsi_frame_protector **protector);
void (*destroy)(tsi_handshaker *self);
+ tsi_result (*next)(tsi_handshaker *self, const unsigned char *received_bytes,
+ size_t received_bytes_size, unsigned char **bytes_to_send,
+ size_t *bytes_to_send_size,
+ tsi_handshaker_result **handshaker_result,
+ tsi_handshaker_on_next_done_cb cb, void *user_data);
} tsi_handshaker_vtable;
struct tsi_handshaker {
const tsi_handshaker_vtable *vtable;
- int frame_protector_created;
+ bool frame_protector_created;
+ bool handshaker_result_created;
+};
+
+/* Base for tsi_handshaker_result implementations.
+ See transport_security_interface.h for documentation. */
+typedef struct {
+ tsi_result (*extract_peer)(const tsi_handshaker_result *self, tsi_peer *peer);
+ tsi_result (*create_frame_protector)(const tsi_handshaker_result *self,
+ size_t *max_output_protected_frame_size,
+ tsi_frame_protector **protector);
+ tsi_result (*get_unused_bytes)(const tsi_handshaker_result *self,
+ unsigned char **bytes, size_t *bytes_size);
+ void (*destroy)(tsi_handshaker_result *self);
+} tsi_handshaker_result_vtable;
+
+struct tsi_handshaker_result {
+ const tsi_handshaker_result_vtable *vtable;
};
/* Peer and property construction/destruction functions. */
diff --git a/src/core/tsi/transport_security_adapter.c b/src/core/tsi/transport_security_adapter.c
new file mode 100644
index 0000000000..9f2147b530
--- /dev/null
+++ b/src/core/tsi/transport_security_adapter.c
@@ -0,0 +1,236 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/tsi/transport_security_adapter.h"
+
+#include <string.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/tsi/transport_security.h"
+
+#define TSI_ADAPTER_INITIAL_BUFFER_SIZE 256
+
+/* --- tsi_adapter_handshaker_result implementation ---*/
+
+typedef struct {
+ tsi_handshaker_result base;
+ tsi_handshaker *wrapped;
+ unsigned char *unused_bytes;
+ size_t unused_bytes_size;
+} tsi_adapter_handshaker_result;
+
+static tsi_result adapter_result_extract_peer(const tsi_handshaker_result *self,
+ tsi_peer *peer) {
+ tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self;
+ return tsi_handshaker_extract_peer(impl->wrapped, peer);
+}
+
+static tsi_result adapter_result_create_frame_protector(
+ const tsi_handshaker_result *self, size_t *max_output_protected_frame_size,
+ tsi_frame_protector **protector) {
+ tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self;
+ return tsi_handshaker_create_frame_protector(
+ impl->wrapped, max_output_protected_frame_size, protector);
+}
+
+static tsi_result adapter_result_get_unused_bytes(
+ const tsi_handshaker_result *self, unsigned char **bytes,
+ size_t *byte_size) {
+ tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self;
+ *bytes = impl->unused_bytes;
+ *byte_size = impl->unused_bytes_size;
+ return TSI_OK;
+}
+
+static void adapter_result_destroy(tsi_handshaker_result *self) {
+ tsi_adapter_handshaker_result *impl = (tsi_adapter_handshaker_result *)self;
+ tsi_handshaker_destroy(impl->wrapped);
+ gpr_free(impl->unused_bytes);
+ gpr_free(self);
+}
+
+static const tsi_handshaker_result_vtable result_vtable = {
+ adapter_result_extract_peer, adapter_result_create_frame_protector,
+ adapter_result_get_unused_bytes, adapter_result_destroy,
+};
+
+/* Ownership of wrapped tsi_handshaker is transferred to the result object. */
+static tsi_result tsi_adapter_create_handshaker_result(
+ tsi_handshaker *wrapped, const unsigned char *unused_bytes,
+ size_t unused_bytes_size, tsi_handshaker_result **handshaker_result) {
+ if (wrapped == NULL || (unused_bytes_size > 0 && unused_bytes == NULL)) {
+ return TSI_INVALID_ARGUMENT;
+ }
+ tsi_adapter_handshaker_result *impl = gpr_zalloc(sizeof(*impl));
+ impl->base.vtable = &result_vtable;
+ impl->wrapped = wrapped;
+ impl->unused_bytes_size = unused_bytes_size;
+ if (unused_bytes_size > 0) {
+ impl->unused_bytes = gpr_malloc(unused_bytes_size);
+ memcpy(impl->unused_bytes, unused_bytes, unused_bytes_size);
+ } else {
+ impl->unused_bytes = NULL;
+ }
+ *handshaker_result = &impl->base;
+ return TSI_OK;
+}
+
+/* --- tsi_adapter_handshaker implementation ---*/
+
+typedef struct {
+ tsi_handshaker base;
+ tsi_handshaker *wrapped;
+ unsigned char *adapter_buffer;
+ size_t adapter_buffer_size;
+} tsi_adapter_handshaker;
+
+static tsi_result adapter_get_bytes_to_send_to_peer(tsi_handshaker *self,
+ unsigned char *bytes,
+ size_t *bytes_size) {
+ return tsi_handshaker_get_bytes_to_send_to_peer(
+ tsi_adapter_handshaker_get_wrapped(self), bytes, bytes_size);
+}
+
+static tsi_result adapter_process_bytes_from_peer(tsi_handshaker *self,
+ const unsigned char *bytes,
+ size_t *bytes_size) {
+ return tsi_handshaker_process_bytes_from_peer(
+ tsi_adapter_handshaker_get_wrapped(self), bytes, bytes_size);
+}
+
+static tsi_result adapter_get_result(tsi_handshaker *self) {
+ return tsi_handshaker_get_result(tsi_adapter_handshaker_get_wrapped(self));
+}
+
+static tsi_result adapter_extract_peer(tsi_handshaker *self, tsi_peer *peer) {
+ return tsi_handshaker_extract_peer(tsi_adapter_handshaker_get_wrapped(self),
+ peer);
+}
+
+static tsi_result adapter_create_frame_protector(
+ tsi_handshaker *self, size_t *max_protected_frame_size,
+ tsi_frame_protector **protector) {
+ return tsi_handshaker_create_frame_protector(
+ tsi_adapter_handshaker_get_wrapped(self), max_protected_frame_size,
+ protector);
+}
+
+static void adapter_destroy(tsi_handshaker *self) {
+ tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)self;
+ tsi_handshaker_destroy(impl->wrapped);
+ gpr_free(impl->adapter_buffer);
+ gpr_free(self);
+}
+
+static tsi_result adapter_next(
+ tsi_handshaker *self, const unsigned char *received_bytes,
+ size_t received_bytes_size, unsigned char **bytes_to_send,
+ size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result,
+ tsi_handshaker_on_next_done_cb cb, void *user_data) {
+ /* Input sanity check. */
+ if ((received_bytes_size > 0 && received_bytes == NULL) ||
+ bytes_to_send == NULL || bytes_to_send_size == NULL ||
+ handshaker_result == NULL) {
+ return TSI_INVALID_ARGUMENT;
+ }
+
+ /* If there are received bytes, process them first. */
+ tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)self;
+ tsi_result status = TSI_OK;
+ size_t bytes_consumed = received_bytes_size;
+ if (received_bytes_size > 0) {
+ status = tsi_handshaker_process_bytes_from_peer(
+ impl->wrapped, received_bytes, &bytes_consumed);
+ if (status != TSI_OK) return status;
+ }
+
+ /* Get bytes to send to the peer, if available. */
+ size_t offset = 0;
+ do {
+ size_t to_send_size = impl->adapter_buffer_size - offset;
+ status = tsi_handshaker_get_bytes_to_send_to_peer(
+ impl->wrapped, impl->adapter_buffer + offset, &to_send_size);
+ offset += to_send_size;
+ if (status == TSI_INCOMPLETE_DATA) {
+ impl->adapter_buffer_size *= 2;
+ impl->adapter_buffer =
+ gpr_realloc(impl->adapter_buffer, impl->adapter_buffer_size);
+ }
+ } while (status == TSI_INCOMPLETE_DATA);
+ if (status != TSI_OK) return status;
+ *bytes_to_send = impl->adapter_buffer;
+ *bytes_to_send_size = offset;
+
+ /* If handshake completes, create tsi_handshaker_result. */
+ if (tsi_handshaker_is_in_progress(impl->wrapped)) {
+ *handshaker_result = NULL;
+ } else {
+ size_t unused_bytes_size = received_bytes_size - bytes_consumed;
+ const unsigned char *unused_bytes =
+ unused_bytes_size == 0 ? NULL : received_bytes + bytes_consumed;
+ status = tsi_adapter_create_handshaker_result(
+ impl->wrapped, unused_bytes, unused_bytes_size, handshaker_result);
+ if (status == TSI_OK) {
+ impl->base.handshaker_result_created = true;
+ impl->wrapped = NULL;
+ }
+ }
+ return status;
+}
+
+static const tsi_handshaker_vtable handshaker_vtable = {
+ adapter_get_bytes_to_send_to_peer,
+ adapter_process_bytes_from_peer,
+ adapter_get_result,
+ adapter_extract_peer,
+ adapter_create_frame_protector,
+ adapter_destroy,
+ adapter_next,
+};
+
+tsi_handshaker *tsi_create_adapter_handshaker(tsi_handshaker *wrapped) {
+ GPR_ASSERT(wrapped != NULL);
+ tsi_adapter_handshaker *impl = gpr_zalloc(sizeof(*impl));
+ impl->base.vtable = &handshaker_vtable;
+ impl->wrapped = wrapped;
+ impl->adapter_buffer_size = TSI_ADAPTER_INITIAL_BUFFER_SIZE;
+ impl->adapter_buffer = gpr_malloc(impl->adapter_buffer_size);
+ return &impl->base;
+}
+
+tsi_handshaker *tsi_adapter_handshaker_get_wrapped(tsi_handshaker *adapter) {
+ if (adapter == NULL) return NULL;
+ tsi_adapter_handshaker *impl = (tsi_adapter_handshaker *)adapter;
+ return impl->wrapped;
+}
diff --git a/src/core/tsi/transport_security_adapter.h b/src/core/tsi/transport_security_adapter.h
new file mode 100644
index 0000000000..400df2f11b
--- /dev/null
+++ b/src/core/tsi/transport_security_adapter.h
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright 2017, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef GRPC_CORE_TSI_TRANSPORT_SECURITY_ADAPTER_H
+#define GRPC_CORE_TSI_TRANSPORT_SECURITY_ADAPTER_H
+
+#include "src/core/tsi/transport_security_interface.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Create a tsi handshaker that takes an implementation of old interface and
+ converts into an implementation of new interface. In the old interface,
+ there are get_bytes_to_send_to_peer, process_bytes_from_peer, get_result,
+ extract_peer, and create_frame_protector. In the new interface, only next
+ method is needed. See transport_security_interface.h for details. Note that
+ this tsi adapter handshaker is temporary. It will be removed once TSI has
+ been fully migrated to the new interface.
+ Ownership of input tsi_handshaker is transferred to this new adapter. */
+tsi_handshaker *tsi_create_adapter_handshaker(tsi_handshaker *wrapped);
+
+/* Given a tsi adapter handshaker, return the original wrapped handshaker. The
+ adapter still owns the wrapped handshaker which should not be destroyed by
+ the caller. */
+tsi_handshaker *tsi_adapter_handshaker_get_wrapped(tsi_handshaker *adapter);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* GRPC_CORE_TSI_TRANSPORT_SECURITY_ADAPTER_H */
diff --git a/src/core/tsi/transport_security_interface.h b/src/core/tsi/transport_security_interface.h
index caed43eac4..f2112b62b6 100644
--- a/src/core/tsi/transport_security_interface.h
+++ b/src/core/tsi/transport_security_interface.h
@@ -56,7 +56,8 @@ typedef enum {
TSI_NOT_FOUND = 9,
TSI_PROTOCOL_FAILURE = 10,
TSI_HANDSHAKE_IN_PROGRESS = 11,
- TSI_OUT_OF_RESOURCES = 12
+ TSI_OUT_OF_RESOURCES = 12,
+ TSI_ASYNC = 13
} tsi_result;
typedef enum {
@@ -208,76 +209,138 @@ typedef struct {
/* Destructs the tsi_peer object. */
void tsi_peer_destruct(tsi_peer *self);
+/* --- tsi_handshaker_result object ---
+
+ This object contains all necessary handshake results and data such as peer
+ info, negotiated keys, unused handshake bytes, when the handshake completes.
+ Implementations of this object must be thread compatible. */
+
+typedef struct tsi_handshaker_result tsi_handshaker_result;
+
+/* This method extracts tsi peer. It returns TSI_OK assuming there is no fatal
+ error.
+ The caller is responsible for destructing the peer. */
+tsi_result tsi_handshaker_result_extract_peer(const tsi_handshaker_result *self,
+ tsi_peer *peer);
+
+/* This method creates a tsi_frame_protector object. It returns TSI_OK assuming
+ there is no fatal error.
+ The caller is responsible for destroying the protector. */
+tsi_result tsi_handshaker_result_create_frame_protector(
+ const tsi_handshaker_result *self, size_t *max_output_protected_frame_size,
+ tsi_frame_protector **protector);
+
+/* This method returns the unused bytes from the handshake. It returns TSI_OK
+ assuming there is no fatal error.
+ Ownership of the bytes is retained by the handshaker result. As a
+ consequence, the caller must not free the bytes. */
+tsi_result tsi_handshaker_result_get_unused_bytes(
+ const tsi_handshaker_result *self, unsigned char **bytes,
+ size_t *byte_size);
+
+/* This method releases the tsi_handshaker_handshaker object. After this method
+ is called, no other method can be called on the object. */
+void tsi_handshaker_result_destroy(tsi_handshaker_result *self);
+
/* --- tsi_handshaker objects ----
Implementations of this object must be thread compatible.
- A typical usage of this object would be:
+ ------------------------------------------------------------------------
+
+ A typical usage supporting both synchronous and asynchronous TSI handshaker
+ implementations would be:
------------------------------------------------------------------------
- tsi_result result = TSI_OK;
- unsigned char buf[4096];
- size_t buf_offset;
- size_t buf_size;
- while (1) {
- // See if we need to send some bytes to the peer.
- do {
- size_t buf_size_to_send = sizeof(buf);
- result = tsi_handshaker_get_bytes_to_send_to_peer(handshaker, buf,
- &buf_size_to_send);
- if (buf_size_to_send > 0) send_bytes_to_peer(buf, buf_size_to_send);
- } while (result == TSI_INCOMPLETE_DATA);
- if (result != TSI_OK) return result;
- if (!tsi_handshaker_is_in_progress(handshaker)) break;
-
- do {
- // Read bytes from the peer.
- buf_size = sizeof(buf);
- buf_offset = 0;
- read_bytes_from_peer(buf, &buf_size);
- if (buf_size == 0) break;
-
- // Process the bytes from the peer. We have to be careful as these bytes
- // may contain non-handshake data (protected data). If this is the case,
- // we will exit from the loop with buf_size > 0.
- size_t consumed_by_handshaker = buf_size;
- result = tsi_handshaker_process_bytes_from_peer(
- handshaker, buf, &consumed_by_handshaker);
- buf_size -= consumed_by_handshaker;
- buf_offset += consumed_by_handshaker;
- } while (result == TSI_INCOMPLETE_DATA);
-
- if (result != TSI_OK) return result;
- if (!tsi_handshaker_is_in_progress(handshaker)) break;
+
+ typedef struct {
+ tsi_handshaker *handshaker;
+ tsi_handshaker_result *handshaker_result;
+ unsigned char *handshake_buffer;
+ size_t handshake_buffer_size;
+ ...
+ } security_handshaker;
+
+ void do_handshake(security_handshaker *h, ...) {
+ // Start the handshake by the calling do_handshake_next.
+ do_handshake_next(h, NULL, 0);
+ ...
}
- // Check the Peer.
- tsi_peer peer;
- do {
- result = tsi_handshaker_extract_peer(handshaker, &peer);
- if (result != TSI_OK) break;
- result = check_peer(&peer);
- } while (0);
- tsi_peer_destruct(&peer);
- if (result != TSI_OK) return result;
-
- // Create the protector.
- tsi_frame_protector* protector = NULL;
- result = tsi_handshaker_create_frame_protector(handshaker, NULL,
- &protector);
- if (result != TSI_OK) return result;
-
- // Do not forget to unprotect outstanding data if any.
- if (buf_size > 0) {
- result = tsi_frame_protector_unprotect(protector, buf + buf_offset,
- buf_size, ..., ...);
- ....
+ // This method is the callback function when data is received from the
+ // peer. This method will read bytes into the handshake buffer and call
+ // do_handshake_next.
+ void on_handshake_data_received_from_peer(void *user_data) {
+ security_handshaker *h = (security_handshaker *)user_data;
+ size_t bytes_received_size = h->handshake_buffer_size;
+ read_bytes_from_peer(h->handshake_buffer, &bytes_received_size);
+ do_handshake_next(h, h->handshake_buffer, bytes_received_size);
+ }
+
+ // This method processes a step of handshake, calling tsi_handshaker_next.
+ void do_handshake_next(security_handshaker *h,
+ const unsigned char* bytes_received,
+ size_t bytes_received_size) {
+ tsi_result status = TSI_OK;
+ unsigned char *bytes_to_send = NULL;
+ size_t bytes_to_send_size = 0;
+ tsi_handshaker_result *result = NULL;
+ status = tsi_handshaker_next(
+ handshaker, bytes_received, bytes_received_size, &bytes_to_send,
+ &bytes_to_send_size, &result, on_handshake_next_done, h);
+ // If TSI handshaker is asynchronous, on_handshake_next_done will be
+ // executed inside tsi_handshaker_next.
+ if (status == TSI_ASYNC) return;
+ // If TSI handshaker is synchronous, invoke callback directly in this
+ // thread.
+ on_handshake_next_done(status, (void *)h, bytes_to_send,
+ bytes_to_send_size, result);
+ }
+
+ // This is the callback function to execute after tsi_handshaker_next.
+ // It is passed to tsi_handshaker_next as a function parameter.
+ void on_handshake_next_done(
+ tsi_result status, void *user_data, const unsigned char *bytes_to_send,
+ size_t bytes_to_send_size, tsi_handshaker_result *result) {
+ security_handshaker *h = (security_handshaker *)user_data;
+ if (status == TSI_INCOMPLETE_DATA) {
+ // Schedule an asynchronous read from the peer. If handshake data are
+ // received, on_handshake_data_received_from_peer will be called.
+ async_read_from_peer(..., ..., on_handshake_data_received_from_peer);
+ return;
+ }
+ if (status != TSI_OK) return;
+
+ if (bytes_to_send_size > 0) {
+ send_bytes_to_peer(bytes_to_send, bytes_to_send_size);
+ }
+
+ if (result != NULL) {
+ // Handshake completed.
+ h->result = result;
+ // Check the Peer.
+ tsi_peer peer;
+ status = tsi_handshaker_result_extract_peer(result, &peer);
+ if (status != TSI_OK) return;
+ status = check_peer(&peer);
+ tsi_peer_destruct(&peer);
+ if (status != TSI_OK) return;
+
+ // Create the protector.
+ tsi_frame_protector* protector = NULL;
+ status = tsi_handshaker_result_create_frame_protector(result, NULL,
+ &protector);
+ if (status != TSI_OK) return;
+
+ // Do not forget to unprotect outstanding data if any.
+ ....
+ }
}
- ...
------------------------------------------------------------------------ */
typedef struct tsi_handshaker tsi_handshaker;
-/* Gets bytes that need to be sent to the peer.
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead.
+ Gets bytes that need to be sent to the peer.
- bytes is the buffer that will be written with the data to be sent to the
peer.
- bytes_size is an input/output parameter specifying the capacity of the
@@ -292,7 +355,8 @@ tsi_result tsi_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self,
unsigned char *bytes,
size_t *bytes_size);
-/* Processes bytes received from the peer.
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_next instead.
+ Processes bytes received from the peer.
- bytes is the buffer containing the data.
- bytes_size is an input/output parameter specifying the size of the data as
input and the number of bytes consumed as output.
@@ -305,24 +369,29 @@ tsi_result tsi_handshaker_process_bytes_from_peer(tsi_handshaker *self,
const unsigned char *bytes,
size_t *bytes_size);
-/* Gets the result of the handshaker.
+/* TO BE DEPRECATED SOON.
+ Gets the result of the handshaker.
Returns TSI_OK if the hanshake completed successfully and there has been no
errors. Returns TSI_HANDSHAKE_IN_PROGRESS if the handshaker is not done yet
but no error has been encountered so far. Otherwise the handshaker failed
with the returned error. */
tsi_result tsi_handshaker_get_result(tsi_handshaker *self);
-/* Returns 1 if the handshake is in progress, 0 otherwise. */
+/* TO BE DEPRECATED SOON.
+ Returns 1 if the handshake is in progress, 0 otherwise. */
#define tsi_handshaker_is_in_progress(h) \
(tsi_handshaker_get_result((h)) == TSI_HANDSHAKE_IN_PROGRESS)
-/* This method may return TSI_FAILED_PRECONDITION if
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_extract_peer instead.
+ This method may return TSI_FAILED_PRECONDITION if
tsi_handshaker_is_in_progress returns 1, it returns TSI_OK otherwise
assuming the handshaker is not in a fatal error state.
The caller is responsible for destructing the peer. */
tsi_result tsi_handshaker_extract_peer(tsi_handshaker *self, tsi_peer *peer);
-/* This method creates a tsi_frame_protector object after the handshake phase
+/* TO BE DEPRECATED SOON. Use tsi_handshaker_result_create_frame_protector
+ instead.
+ This method creates a tsi_frame_protector object after the handshake phase
is done. After this method has been called successfully, the only method
that can be called on this object is Destroy.
- max_output_protected_frame_size is an input/output parameter specifying the
@@ -342,10 +411,53 @@ tsi_result tsi_handshaker_create_frame_protector(
tsi_handshaker *self, size_t *max_output_protected_frame_size,
tsi_frame_protector **protector);
+/* Callback function definition for tsi_handshaker_next.
+ - status indicates the status of the next operation.
+ - user_data is the argument to callback function passed from the caller.
+ - bytes_to_send is the data buffer to be sent to the peer.
+ - bytes_to_send_size is the size of data buffer to be sent to the peer.
+ - handshaker_result is the result of handshake when the handshake completes,
+ is NULL otherwise. */
+typedef void (*tsi_handshaker_on_next_done_cb)(
+ tsi_result status, void *user_data, const unsigned char *bytes_to_send,
+ size_t bytes_to_send_size, tsi_handshaker_result *handshaker_result);
+
+/* Conduct a next step of the handshake.
+ - received_bytes is the buffer containing the data received from the peer.
+ - received_bytes_size is the size of the data received from the peer.
+ - bytes_to_send is the data buffer to be sent to the peer.
+ - bytes_to_send_size is the size of data buffer to be sent to the peer.
+ - handshaker_result is the result of handshake if the handshake completes.
+ - cb is the callback function defined above. It can be NULL for synchronous
+ TSI handshaker implementation.
+ - user_data is the argument to callback function passed from the caller.
+ This method returns TSI_ASYNC if the TSI handshaker implementation is
+ asynchronous, and in this case, the callback is guaranteed to run in another
+ thread owned by TSI. It returns TSI_OK if the handshake completes or if
+ there are data to send to the peer, otherwise returns TSI_INCOMPLETE_DATA
+ which indicates that this method needs to be called again with more data
+ from the peer. In case of a fatal error in the handshake, another specific
+ error code is returned.
+ The caller is responsible for destroying the handshaker_result. However,
+ the caller should not free bytes_to_send, as the buffer is owned by the
+ tsi_handshaker object. */
+tsi_result tsi_handshaker_next(
+ tsi_handshaker *self, const unsigned char *received_bytes,
+ size_t received_bytes_size, unsigned char **bytes_to_send,
+ size_t *bytes_to_send_size, tsi_handshaker_result **handshaker_result,
+ tsi_handshaker_on_next_done_cb cb, void *user_data);
+
/* This method releases the tsi_handshaker object. After this method is called,
no other method can be called on the object. */
void tsi_handshaker_destroy(tsi_handshaker *self);
+/* This method initializes the necessary shared objects used for tsi
+ implementation. */
+void tsi_init();
+
+/* This method destroys the shared objects created by tsi_init. */
+void tsi_destroy();
+
#ifdef __cplusplus
}
#endif