From 87047d7e932246eef44c92598361dc60351a0bab Mon Sep 17 00:00:00 2001 From: Julien Boeuf Date: Fri, 21 Aug 2015 14:30:33 -0700 Subject: Security connector is now responsible for the handshake. --- src/core/httpcli/httpcli_security_connector.c | 28 ++- src/core/security/handshake.c | 285 +++++++++++++++++++++++++ src/core/security/handshake.h | 47 +++++ src/core/security/secure_transport_setup.c | 293 -------------------------- src/core/security/secure_transport_setup.h | 53 ----- src/core/security/security_connector.c | 93 +++++--- src/core/security/security_connector.h | 20 +- src/core/security/server_secure_chttp2.c | 12 +- src/core/surface/secure_channel_create.c | 14 +- 9 files changed, 439 insertions(+), 406 deletions(-) create mode 100644 src/core/security/handshake.c create mode 100644 src/core/security/handshake.h delete mode 100644 src/core/security/secure_transport_setup.c delete mode 100644 src/core/security/secure_transport_setup.h (limited to 'src') diff --git a/src/core/httpcli/httpcli_security_connector.c b/src/core/httpcli/httpcli_security_connector.c index 7887f9d530..a4cd00d9a0 100644 --- a/src/core/httpcli/httpcli_security_connector.c +++ b/src/core/httpcli/httpcli_security_connector.c @@ -35,7 +35,7 @@ #include -#include "src/core/security/secure_transport_setup.h" +#include "src/core/security/handshake.h" #include "src/core/support/string.h" #include #include @@ -55,23 +55,31 @@ static void httpcli_ssl_destroy(grpc_security_connector *sc) { tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); } if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name); + tsi_handshaker_destroy(sc->handshaker); gpr_free(sc); } -static grpc_security_status httpcli_ssl_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { +static void httpcli_ssl_do_handshake( + grpc_security_connector *sc, grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, void *user_data) { grpc_httpcli_ssl_channel_security_connector *c = (grpc_httpcli_ssl_channel_security_connector *)sc; tsi_result result = TSI_OK; - if (c->handshaker_factory == NULL) return GRPC_SECURITY_ERROR; + if (c->handshaker_factory == NULL) { + cb(user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + return; + } + tsi_handshaker_destroy(sc->handshaker); + sc->handshaker = NULL; result = tsi_ssl_handshaker_factory_create_handshaker( - c->handshaker_factory, c->secure_peer_name, handshaker); + c->handshaker_factory, c->secure_peer_name, &sc->handshaker); if (result != TSI_OK) { gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", tsi_result_to_string(result)); - return GRPC_SECURITY_ERROR; + cb(user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + } else { + grpc_do_security_handshake(sc, nonsecure_endpoint, cb, user_data); } - return GRPC_SECURITY_OK; } static grpc_security_status httpcli_ssl_check_peer(grpc_security_connector *sc, @@ -94,7 +102,7 @@ static grpc_security_status httpcli_ssl_check_peer(grpc_security_connector *sc, } static grpc_security_connector_vtable httpcli_ssl_vtable = { - httpcli_ssl_destroy, httpcli_ssl_create_handshaker, httpcli_ssl_check_peer}; + httpcli_ssl_destroy, httpcli_ssl_do_handshake, httpcli_ssl_check_peer}; static grpc_security_status httpcli_ssl_channel_security_connector_create( const unsigned char *pem_root_certs, size_t pem_root_certs_size, @@ -169,8 +177,8 @@ static void ssl_handshake(void *arg, grpc_endpoint *tcp, const char *host, GPR_ASSERT(httpcli_ssl_channel_security_connector_create( pem_root_certs, pem_root_certs_size, host, &sc) == GRPC_SECURITY_OK); - grpc_setup_secure_transport(&sc->base, tcp, on_secure_transport_setup_done, - c); + grpc_security_connector_do_handshake(&sc->base, tcp, + on_secure_transport_setup_done, c); GRPC_SECURITY_CONNECTOR_UNREF(&sc->base, "httpcli"); } diff --git a/src/core/security/handshake.c b/src/core/security/handshake.c new file mode 100644 index 0000000000..23830ed5a1 --- /dev/null +++ b/src/core/security/handshake.c @@ -0,0 +1,285 @@ +/* + * + * Copyright 2015, 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/security/handshake.h" + +#include + +#include "src/core/security/secure_endpoint.h" +#include +#include +#include + +#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 + +typedef struct { + grpc_security_connector *connector; + unsigned char *handshake_buffer; + size_t handshake_buffer_size; + grpc_endpoint *wrapped_endpoint; + grpc_endpoint *secure_endpoint; + gpr_slice_buffer left_overs; + grpc_security_handshake_done_cb cb; + void *user_data; +} grpc_security_handshake; + +static void on_handshake_data_received_from_peer(void *handshake, + gpr_slice *slices, + size_t nslices, + grpc_endpoint_cb_status error); + +static void on_handshake_data_sent_to_peer(void *handshake, + grpc_endpoint_cb_status error); + +static void security_handshake_done(grpc_security_handshake *h, + int is_success) { + if (is_success) { + h->cb(h->user_data, GRPC_SECURITY_OK, h->wrapped_endpoint, + h->secure_endpoint); + } else { + if (h->secure_endpoint != NULL) { + grpc_endpoint_shutdown(h->secure_endpoint); + grpc_endpoint_destroy(h->secure_endpoint); + } else { + grpc_endpoint_destroy(h->wrapped_endpoint); + } + h->cb(h->user_data, GRPC_SECURITY_ERROR, h->wrapped_endpoint, NULL); + } + if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer); + gpr_slice_buffer_destroy(&h->left_overs); + gpr_free(h); +} + +static void on_peer_checked(void *user_data, grpc_security_status status) { + grpc_security_handshake *h = user_data; + tsi_frame_protector *protector; + tsi_result result; + if (status != GRPC_SECURITY_OK) { + gpr_log(GPR_ERROR, "Error checking peer."); + security_handshake_done(h, 0); + return; + } + result = tsi_handshaker_create_frame_protector(h->connector->handshaker, NULL, + &protector); + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.", + tsi_result_to_string(result)); + security_handshake_done(h, 0); + return; + } + h->secure_endpoint = + grpc_secure_endpoint_create(protector, h->wrapped_endpoint, + h->left_overs.slices, h->left_overs.count); + security_handshake_done(h, 1); + return; +} + +static void check_peer(grpc_security_handshake *h) { + grpc_security_status peer_status; + tsi_peer peer; + tsi_result result = + tsi_handshaker_extract_peer(h->connector->handshaker, &peer); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Peer extraction failed with error %s", + tsi_result_to_string(result)); + security_handshake_done(h, 0); + return; + } + peer_status = grpc_security_connector_check_peer(h->connector, peer, + on_peer_checked, h); + if (peer_status == GRPC_SECURITY_ERROR) { + gpr_log(GPR_ERROR, "Peer check failed."); + security_handshake_done(h, 0); + return; + } else if (peer_status == GRPC_SECURITY_OK) { + on_peer_checked(h, peer_status); + } +} + +static void send_handshake_bytes_to_peer(grpc_security_handshake *h) { + size_t offset = 0; + tsi_result result = TSI_OK; + gpr_slice to_send; + grpc_endpoint_write_status write_status; + + do { + size_t to_send_size = h->handshake_buffer_size - offset; + result = tsi_handshaker_get_bytes_to_send_to_peer( + h->connector->handshaker, h->handshake_buffer + offset, &to_send_size); + offset += to_send_size; + if (result == TSI_INCOMPLETE_DATA) { + h->handshake_buffer_size *= 2; + h->handshake_buffer = + gpr_realloc(h->handshake_buffer, h->handshake_buffer_size); + } + } while (result == TSI_INCOMPLETE_DATA); + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshake failed with error %s", + tsi_result_to_string(result)); + security_handshake_done(h, 0); + return; + } + + to_send = + gpr_slice_from_copied_buffer((const char *)h->handshake_buffer, offset); + /* TODO(klempner,jboeuf): This should probably use the client setup + deadline */ + write_status = grpc_endpoint_write(h->wrapped_endpoint, &to_send, 1, + on_handshake_data_sent_to_peer, h); + if (write_status == GRPC_ENDPOINT_WRITE_ERROR) { + gpr_log(GPR_ERROR, "Could not send handshake data to peer."); + security_handshake_done(h, 0); + } else if (write_status == GRPC_ENDPOINT_WRITE_DONE) { + on_handshake_data_sent_to_peer(h, GRPC_ENDPOINT_CB_OK); + } +} + +static void cleanup_slices(gpr_slice *slices, size_t num_slices) { + size_t i; + for (i = 0; i < num_slices; i++) { + gpr_slice_unref(slices[i]); + } +} + +static void on_handshake_data_received_from_peer( + void *handshake, gpr_slice *slices, size_t nslices, + grpc_endpoint_cb_status error) { + grpc_security_handshake *h = handshake; + size_t consumed_slice_size = 0; + tsi_result result = TSI_OK; + size_t i; + size_t num_left_overs; + int has_left_overs_in_current_slice = 0; + + if (error != GRPC_ENDPOINT_CB_OK) { + gpr_log(GPR_ERROR, "Read failed."); + cleanup_slices(slices, nslices); + security_handshake_done(h, 0); + return; + } + + for (i = 0; i < nslices; i++) { + consumed_slice_size = GPR_SLICE_LENGTH(slices[i]); + result = tsi_handshaker_process_bytes_from_peer( + h->connector->handshaker, GPR_SLICE_START_PTR(slices[i]), + &consumed_slice_size); + if (!tsi_handshaker_is_in_progress(h->connector->handshaker)) break; + } + + if (tsi_handshaker_is_in_progress(h->connector->handshaker)) { + /* We may need more data. */ + if (result == TSI_INCOMPLETE_DATA) { + /* TODO(klempner,jboeuf): This should probably use the client setup + deadline */ + grpc_endpoint_notify_on_read( + h->wrapped_endpoint, on_handshake_data_received_from_peer, handshake); + cleanup_slices(slices, nslices); + return; + } else { + send_handshake_bytes_to_peer(h); + cleanup_slices(slices, nslices); + return; + } + } + + if (result != TSI_OK) { + gpr_log(GPR_ERROR, "Handshake failed with error %s", + tsi_result_to_string(result)); + cleanup_slices(slices, nslices); + security_handshake_done(h, 0); + return; + } + + /* Handshake is done and successful this point. */ + has_left_overs_in_current_slice = + (consumed_slice_size < GPR_SLICE_LENGTH(slices[i])); + num_left_overs = (has_left_overs_in_current_slice ? 1 : 0) + nslices - i - 1; + if (num_left_overs == 0) { + cleanup_slices(slices, nslices); + check_peer(h); + return; + } + cleanup_slices(slices, nslices - num_left_overs); + + /* Put the leftovers in our buffer (ownership transfered). */ + if (has_left_overs_in_current_slice) { + gpr_slice_buffer_add(&h->left_overs, + gpr_slice_split_tail(&slices[i], consumed_slice_size)); + gpr_slice_unref(slices[i]); /* split_tail above increments refcount. */ + } + gpr_slice_buffer_addn( + &h->left_overs, &slices[i + 1], + num_left_overs - (size_t)has_left_overs_in_current_slice); + check_peer(h); +} + +/* If handshake is NULL, the handshake is done. */ +static void on_handshake_data_sent_to_peer(void *handshake, + grpc_endpoint_cb_status error) { + grpc_security_handshake *h = handshake; + + /* Make sure that write is OK. */ + if (error != GRPC_ENDPOINT_CB_OK) { + gpr_log(GPR_ERROR, "Write failed with error %d.", error); + if (handshake != NULL) security_handshake_done(h, 0); + return; + } + + /* We may be done. */ + if (tsi_handshaker_is_in_progress(h->connector->handshaker)) { + /* TODO(klempner,jboeuf): This should probably use the client setup + deadline */ + grpc_endpoint_notify_on_read( + h->wrapped_endpoint, on_handshake_data_received_from_peer, handshake); + } else { + check_peer(h); + } +} + +void grpc_do_security_handshake(grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + grpc_security_handshake *h = gpr_malloc(sizeof(grpc_security_handshake)); + memset(h, 0, sizeof(grpc_security_handshake)); + h->connector = connector; + h->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; + h->handshake_buffer = gpr_malloc(h->handshake_buffer_size); + h->wrapped_endpoint = nonsecure_endpoint; + h->user_data = user_data; + h->cb = cb; + gpr_slice_buffer_init(&h->left_overs); + send_handshake_bytes_to_peer(h); +} diff --git a/src/core/security/handshake.h b/src/core/security/handshake.h new file mode 100644 index 0000000000..1739855d69 --- /dev/null +++ b/src/core/security/handshake.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2015, 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_INTERNAL_CORE_SECURITY_HANDSHAKE_H +#define GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H + +#include "src/core/iomgr/endpoint.h" +#include "src/core/security/security_connector.h" + + +/* Calls the callback upon completion. */ +void grpc_do_security_handshake(grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data); + +#endif /* GRPC_INTERNAL_CORE_SECURITY_HANDSHAKE_H */ diff --git a/src/core/security/secure_transport_setup.c b/src/core/security/secure_transport_setup.c deleted file mode 100644 index 0c3572b53c..0000000000 --- a/src/core/security/secure_transport_setup.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * - * Copyright 2015, 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/security/secure_transport_setup.h" - -#include - -#include "src/core/security/secure_endpoint.h" -#include -#include -#include - -#define GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE 256 - -typedef struct { - grpc_security_connector *connector; - tsi_handshaker *handshaker; - unsigned char *handshake_buffer; - size_t handshake_buffer_size; - grpc_endpoint *wrapped_endpoint; - grpc_endpoint *secure_endpoint; - gpr_slice_buffer left_overs; - grpc_secure_transport_setup_done_cb cb; - void *user_data; -} grpc_secure_transport_setup; - -static void on_handshake_data_received_from_peer(void *setup, gpr_slice *slices, - size_t nslices, - grpc_endpoint_cb_status error); - -static void on_handshake_data_sent_to_peer(void *setup, - grpc_endpoint_cb_status error); - -static void secure_transport_setup_done(grpc_secure_transport_setup *s, - int is_success) { - if (is_success) { - s->cb(s->user_data, GRPC_SECURITY_OK, s->wrapped_endpoint, - s->secure_endpoint); - } else { - if (s->secure_endpoint != NULL) { - grpc_endpoint_shutdown(s->secure_endpoint); - grpc_endpoint_destroy(s->secure_endpoint); - } else { - grpc_endpoint_destroy(s->wrapped_endpoint); - } - s->cb(s->user_data, GRPC_SECURITY_ERROR, s->wrapped_endpoint, NULL); - } - if (s->handshaker != NULL) tsi_handshaker_destroy(s->handshaker); - if (s->handshake_buffer != NULL) gpr_free(s->handshake_buffer); - gpr_slice_buffer_destroy(&s->left_overs); - GRPC_SECURITY_CONNECTOR_UNREF(s->connector, "secure_transport_setup"); - gpr_free(s); -} - -static void on_peer_checked(void *user_data, grpc_security_status status) { - grpc_secure_transport_setup *s = user_data; - tsi_frame_protector *protector; - tsi_result result; - if (status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, "Error checking peer."); - secure_transport_setup_done(s, 0); - return; - } - result = - tsi_handshaker_create_frame_protector(s->handshaker, NULL, &protector); - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Frame protector creation failed with error %s.", - tsi_result_to_string(result)); - secure_transport_setup_done(s, 0); - return; - } - s->secure_endpoint = - grpc_secure_endpoint_create(protector, s->wrapped_endpoint, - s->left_overs.slices, s->left_overs.count); - secure_transport_setup_done(s, 1); - return; -} - -static void check_peer(grpc_secure_transport_setup *s) { - grpc_security_status peer_status; - tsi_peer peer; - tsi_result result = tsi_handshaker_extract_peer(s->handshaker, &peer); - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Peer extraction failed with error %s", - tsi_result_to_string(result)); - secure_transport_setup_done(s, 0); - return; - } - peer_status = grpc_security_connector_check_peer(s->connector, peer, - on_peer_checked, s); - if (peer_status == GRPC_SECURITY_ERROR) { - gpr_log(GPR_ERROR, "Peer check failed."); - secure_transport_setup_done(s, 0); - return; - } else if (peer_status == GRPC_SECURITY_OK) { - on_peer_checked(s, peer_status); - } -} - -static void send_handshake_bytes_to_peer(grpc_secure_transport_setup *s) { - size_t offset = 0; - tsi_result result = TSI_OK; - gpr_slice to_send; - grpc_endpoint_write_status write_status; - - do { - size_t to_send_size = s->handshake_buffer_size - offset; - result = tsi_handshaker_get_bytes_to_send_to_peer( - s->handshaker, s->handshake_buffer + offset, &to_send_size); - offset += to_send_size; - if (result == TSI_INCOMPLETE_DATA) { - s->handshake_buffer_size *= 2; - s->handshake_buffer = - gpr_realloc(s->handshake_buffer, s->handshake_buffer_size); - } - } while (result == TSI_INCOMPLETE_DATA); - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshake failed with error %s", - tsi_result_to_string(result)); - secure_transport_setup_done(s, 0); - return; - } - - to_send = - gpr_slice_from_copied_buffer((const char *)s->handshake_buffer, offset); - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - write_status = grpc_endpoint_write(s->wrapped_endpoint, &to_send, 1, - on_handshake_data_sent_to_peer, s); - if (write_status == GRPC_ENDPOINT_WRITE_ERROR) { - gpr_log(GPR_ERROR, "Could not send handshake data to peer."); - secure_transport_setup_done(s, 0); - } else if (write_status == GRPC_ENDPOINT_WRITE_DONE) { - on_handshake_data_sent_to_peer(s, GRPC_ENDPOINT_CB_OK); - } -} - -static void cleanup_slices(gpr_slice *slices, size_t num_slices) { - size_t i; - for (i = 0; i < num_slices; i++) { - gpr_slice_unref(slices[i]); - } -} - -static void on_handshake_data_received_from_peer( - void *setup, gpr_slice *slices, size_t nslices, - grpc_endpoint_cb_status error) { - grpc_secure_transport_setup *s = setup; - size_t consumed_slice_size = 0; - tsi_result result = TSI_OK; - size_t i; - size_t num_left_overs; - int has_left_overs_in_current_slice = 0; - - if (error != GRPC_ENDPOINT_CB_OK) { - gpr_log(GPR_ERROR, "Read failed."); - cleanup_slices(slices, nslices); - secure_transport_setup_done(s, 0); - return; - } - - for (i = 0; i < nslices; i++) { - consumed_slice_size = GPR_SLICE_LENGTH(slices[i]); - result = tsi_handshaker_process_bytes_from_peer( - s->handshaker, GPR_SLICE_START_PTR(slices[i]), &consumed_slice_size); - if (!tsi_handshaker_is_in_progress(s->handshaker)) break; - } - - if (tsi_handshaker_is_in_progress(s->handshaker)) { - /* We may need more data. */ - if (result == TSI_INCOMPLETE_DATA) { - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - grpc_endpoint_notify_on_read(s->wrapped_endpoint, - on_handshake_data_received_from_peer, setup); - cleanup_slices(slices, nslices); - return; - } else { - send_handshake_bytes_to_peer(s); - cleanup_slices(slices, nslices); - return; - } - } - - if (result != TSI_OK) { - gpr_log(GPR_ERROR, "Handshake failed with error %s", - tsi_result_to_string(result)); - cleanup_slices(slices, nslices); - secure_transport_setup_done(s, 0); - return; - } - - /* Handshake is done and successful this point. */ - has_left_overs_in_current_slice = - (consumed_slice_size < GPR_SLICE_LENGTH(slices[i])); - num_left_overs = (has_left_overs_in_current_slice ? 1 : 0) + nslices - i - 1; - if (num_left_overs == 0) { - cleanup_slices(slices, nslices); - check_peer(s); - return; - } - cleanup_slices(slices, nslices - num_left_overs); - - /* Put the leftovers in our buffer (ownership transfered). */ - if (has_left_overs_in_current_slice) { - gpr_slice_buffer_add(&s->left_overs, - gpr_slice_split_tail(&slices[i], consumed_slice_size)); - gpr_slice_unref(slices[i]); /* split_tail above increments refcount. */ - } - gpr_slice_buffer_addn( - &s->left_overs, &slices[i + 1], - num_left_overs - (size_t)has_left_overs_in_current_slice); - check_peer(s); -} - -/* If setup is NULL, the setup is done. */ -static void on_handshake_data_sent_to_peer(void *setup, - grpc_endpoint_cb_status error) { - grpc_secure_transport_setup *s = setup; - - /* Make sure that write is OK. */ - if (error != GRPC_ENDPOINT_CB_OK) { - gpr_log(GPR_ERROR, "Write failed with error %d.", error); - if (setup != NULL) secure_transport_setup_done(s, 0); - return; - } - - /* We may be done. */ - if (tsi_handshaker_is_in_progress(s->handshaker)) { - /* TODO(klempner,jboeuf): This should probably use the client setup - deadline */ - grpc_endpoint_notify_on_read(s->wrapped_endpoint, - on_handshake_data_received_from_peer, setup); - } else { - check_peer(s); - } -} - -void grpc_setup_secure_transport(grpc_security_connector *connector, - grpc_endpoint *nonsecure_endpoint, - grpc_secure_transport_setup_done_cb cb, - void *user_data) { - grpc_security_status result = GRPC_SECURITY_OK; - grpc_secure_transport_setup *s = - gpr_malloc(sizeof(grpc_secure_transport_setup)); - memset(s, 0, sizeof(grpc_secure_transport_setup)); - result = grpc_security_connector_create_handshaker(connector, &s->handshaker); - if (result != GRPC_SECURITY_OK) { - secure_transport_setup_done(s, 0); - return; - } - s->connector = - GRPC_SECURITY_CONNECTOR_REF(connector, "secure_transport_setup"); - s->handshake_buffer_size = GRPC_INITIAL_HANDSHAKE_BUFFER_SIZE; - s->handshake_buffer = gpr_malloc(s->handshake_buffer_size); - s->wrapped_endpoint = nonsecure_endpoint; - s->user_data = user_data; - s->cb = cb; - gpr_slice_buffer_init(&s->left_overs); - send_handshake_bytes_to_peer(s); -} diff --git a/src/core/security/secure_transport_setup.h b/src/core/security/secure_transport_setup.h deleted file mode 100644 index d9b802556d..0000000000 --- a/src/core/security/secure_transport_setup.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * - * Copyright 2015, 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_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H -#define GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H - -#include "src/core/iomgr/endpoint.h" -#include "src/core/security/security_connector.h" - -/* --- Secure transport setup --- */ - -/* Ownership of the secure_endpoint is transfered. */ -typedef void (*grpc_secure_transport_setup_done_cb)( - void *user_data, grpc_security_status status, - grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint); - -/* Calls the callback upon completion. */ -void grpc_setup_secure_transport(grpc_security_connector *connector, - grpc_endpoint *nonsecure_endpoint, - grpc_secure_transport_setup_done_cb cb, - void *user_data); - -#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURE_TRANSPORT_SETUP_H */ diff --git a/src/core/security/security_connector.c b/src/core/security/security_connector.c index a354536dcd..922b92adea 100644 --- a/src/core/security/security_connector.c +++ b/src/core/security/security_connector.c @@ -36,6 +36,7 @@ #include #include "src/core/security/credentials.h" +#include "src/core/security/handshake.h" #include "src/core/security/secure_endpoint.h" #include "src/core/security/security_context.h" #include "src/core/support/env.h" @@ -101,10 +102,15 @@ const tsi_peer_property *tsi_peer_get_property_by_name(const tsi_peer *peer, return NULL; } -grpc_security_status grpc_security_connector_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { - if (sc == NULL || handshaker == NULL) return GRPC_SECURITY_ERROR; - return sc->vtable->create_handshaker(sc, handshaker); +void grpc_security_connector_do_handshake(grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + if (sc == NULL || nonsecure_endpoint == NULL) { + cb(user_data, GRPC_SECURITY_ERROR, nonsecure_endpoint, NULL); + } else { + sc->vtable->do_handshake(sc, nonsecure_endpoint, cb, user_data); + } } grpc_security_status grpc_security_connector_check_peer( @@ -216,27 +222,17 @@ typedef struct { static void fake_channel_destroy(grpc_security_connector *sc) { grpc_channel_security_connector *c = (grpc_channel_security_connector *)sc; grpc_credentials_unref(c->request_metadata_creds); + tsi_handshaker_destroy(sc->handshaker); GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); gpr_free(sc); } static void fake_server_destroy(grpc_security_connector *sc) { + tsi_handshaker_destroy(sc->handshaker); GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); gpr_free(sc); } -static grpc_security_status fake_channel_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { - *handshaker = tsi_create_fake_handshaker(1); - return GRPC_SECURITY_OK; -} - -static grpc_security_status fake_server_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { - *handshaker = tsi_create_fake_handshaker(0); - return GRPC_SECURITY_OK; -} - static grpc_security_status fake_check_peer(grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb, @@ -286,11 +282,29 @@ static grpc_security_status fake_channel_check_call_host( } } +static void fake_channel_do_handshake(grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + tsi_handshaker_destroy(sc->handshaker); + sc->handshaker = tsi_create_fake_handshaker(1); + grpc_do_security_handshake(sc, nonsecure_endpoint, cb, user_data); +} + +static void fake_server_do_handshake(grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { + tsi_handshaker_destroy(sc->handshaker); + sc->handshaker = tsi_create_fake_handshaker(0); + grpc_do_security_handshake(sc, nonsecure_endpoint, cb, user_data); +} + static grpc_security_connector_vtable fake_channel_vtable = { - fake_channel_destroy, fake_channel_create_handshaker, fake_check_peer}; + fake_channel_destroy, fake_channel_do_handshake, fake_check_peer}; static grpc_security_connector_vtable fake_server_vtable = { - fake_server_destroy, fake_server_create_handshaker, fake_check_peer}; + fake_server_destroy, fake_server_do_handshake, fake_check_peer}; grpc_channel_security_connector *grpc_fake_channel_security_connector_create( grpc_credentials *request_metadata_creds, int call_host_check_is_async) { @@ -344,6 +358,7 @@ static void ssl_channel_destroy(grpc_security_connector *sc) { if (c->overridden_target_name != NULL) gpr_free(c->overridden_target_name); tsi_peer_destruct(&c->peer); GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + tsi_handshaker_destroy(sc->handshaker); gpr_free(sc); } @@ -354,6 +369,7 @@ static void ssl_server_destroy(grpc_security_connector *sc) { tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); } GRPC_AUTH_CONTEXT_UNREF(sc->auth_context, "connector"); + tsi_handshaker_destroy(sc->handshaker); gpr_free(sc); } @@ -362,6 +378,8 @@ static grpc_security_status ssl_create_handshaker( const char *peer_name, tsi_handshaker **handshaker) { tsi_result result = TSI_OK; if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR; + tsi_handshaker_destroy(*handshaker); + *handshaker = NULL; result = tsi_ssl_handshaker_factory_create_handshaker( handshaker_factory, is_client ? peer_name : NULL, handshaker); if (result != TSI_OK) { @@ -372,22 +390,37 @@ static grpc_security_status ssl_create_handshaker( return GRPC_SECURITY_OK; } -static grpc_security_status ssl_channel_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { +static void ssl_channel_do_handshake(grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { grpc_ssl_channel_security_connector *c = (grpc_ssl_channel_security_connector *)sc; - return ssl_create_handshaker(c->handshaker_factory, 1, - c->overridden_target_name != NULL - ? c->overridden_target_name - : c->target_name, - handshaker); + grpc_security_status status = ssl_create_handshaker( + c->handshaker_factory, 1, + c->overridden_target_name != NULL ? c->overridden_target_name + : c->target_name, + &sc->handshaker); + if (status != GRPC_SECURITY_OK) { + cb(user_data, status, nonsecure_endpoint, NULL); + } else { + grpc_do_security_handshake(sc, nonsecure_endpoint, cb, user_data); + } } -static grpc_security_status ssl_server_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker) { +static void ssl_server_do_handshake(grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data) { grpc_ssl_server_security_connector *c = (grpc_ssl_server_security_connector *)sc; - return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker); + grpc_security_status status = + ssl_create_handshaker(c->handshaker_factory, 0, NULL, &sc->handshaker); + if (status != GRPC_SECURITY_OK) { + cb(user_data, status, nonsecure_endpoint, NULL); + } else { + grpc_do_security_handshake(sc, nonsecure_endpoint, cb, user_data); + } } static int ssl_host_matches_name(const tsi_peer *peer, const char *peer_name) { @@ -512,10 +545,10 @@ static grpc_security_status ssl_channel_check_call_host( } static grpc_security_connector_vtable ssl_channel_vtable = { - ssl_channel_destroy, ssl_channel_create_handshaker, ssl_channel_check_peer}; + ssl_channel_destroy, ssl_channel_do_handshake, ssl_channel_check_peer}; static grpc_security_connector_vtable ssl_server_vtable = { - ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer}; + ssl_server_destroy, ssl_server_do_handshake, ssl_server_check_peer}; static gpr_slice default_pem_root_certs; diff --git a/src/core/security/security_connector.h b/src/core/security/security_connector.h index 2c9aa1c5a4..76d860d277 100644 --- a/src/core/security/security_connector.h +++ b/src/core/security/security_connector.h @@ -63,10 +63,17 @@ typedef struct grpc_security_connector grpc_security_connector; typedef void (*grpc_security_check_cb)(void *user_data, grpc_security_status status); + +/* Ownership of the secure_endpoint is transfered. */ +typedef void (*grpc_security_handshake_done_cb)( + void *user_data, grpc_security_status status, + grpc_endpoint *wrapped_endpoint, grpc_endpoint *secure_endpoint); + typedef struct { void (*destroy)(grpc_security_connector *sc); - grpc_security_status (*create_handshaker)(grpc_security_connector *sc, - tsi_handshaker **handshaker); + void (*do_handshake)(grpc_security_connector *sc, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, void *user_data); grpc_security_status (*check_peer)(grpc_security_connector *sc, tsi_peer peer, grpc_security_check_cb cb, void *user_data); @@ -77,6 +84,7 @@ struct grpc_security_connector { gpr_refcount refcount; int is_client_side; const char *url_scheme; + tsi_handshaker *handshaker; grpc_auth_context *auth_context; /* Populated after the peer is checked. */ }; @@ -100,9 +108,11 @@ grpc_security_connector *grpc_security_connector_ref( void grpc_security_connector_unref(grpc_security_connector *policy); #endif -/* Handshake creation. */ -grpc_security_status grpc_security_connector_create_handshaker( - grpc_security_connector *sc, tsi_handshaker **handshaker); +/* Handshake. */ +void grpc_security_connector_do_handshake(grpc_security_connector *connector, + grpc_endpoint *nonsecure_endpoint, + grpc_security_handshake_done_cb cb, + void *user_data); /* Check the peer. Implementations can choose to check the peer either synchronously or diff --git a/src/core/security/server_secure_chttp2.c b/src/core/security/server_secure_chttp2.c index 8d9d036d80..a3976ac239 100644 --- a/src/core/security/server_secure_chttp2.c +++ b/src/core/security/server_secure_chttp2.c @@ -44,7 +44,6 @@ #include "src/core/security/credentials.h" #include "src/core/security/security_connector.h" #include "src/core/security/security_context.h" -#include "src/core/security/secure_transport_setup.h" #include "src/core/surface/server.h" #include "src/core/transport/chttp2_transport.h" #include @@ -121,10 +120,9 @@ static int remove_tcp_from_list_locked(grpc_server_secure_state *state, return -1; } -static void on_secure_transport_setup_done(void *statep, - grpc_security_status status, - grpc_endpoint *wrapped_endpoint, - grpc_endpoint *secure_endpoint) { +static void on_secure_handshake_done(void *statep, grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint) { grpc_server_secure_state *state = statep; grpc_transport *transport; grpc_mdctx *mdctx; @@ -163,8 +161,8 @@ static void on_accept(void *statep, grpc_endpoint *tcp) { node->next = state->handshaking_tcp_endpoints; state->handshaking_tcp_endpoints = node; gpr_mu_unlock(&state->mu); - grpc_setup_secure_transport(state->sc, tcp, on_secure_transport_setup_done, - state); + grpc_security_connector_do_handshake(state->sc, tcp, on_secure_handshake_done, + state); } /* Server callback: start listening on our ports */ diff --git a/src/core/surface/secure_channel_create.c b/src/core/surface/secure_channel_create.c index 5b03ba95a7..43b8c97e02 100644 --- a/src/core/surface/secure_channel_create.c +++ b/src/core/surface/secure_channel_create.c @@ -46,7 +46,6 @@ #include "src/core/iomgr/tcp_client.h" #include "src/core/security/auth_filters.h" #include "src/core/security/credentials.h" -#include "src/core/security/secure_transport_setup.h" #include "src/core/surface/channel.h" #include "src/core/transport/chttp2_transport.h" #include "src/core/tsi/transport_security_interface.h" @@ -74,14 +73,13 @@ static void connector_unref(grpc_connector *con) { } } -static void on_secure_transport_setup_done(void *arg, - grpc_security_status status, - grpc_endpoint *wrapped_endpoint, - grpc_endpoint *secure_endpoint) { +static void on_secure_handshake_done(void *arg, grpc_security_status status, + grpc_endpoint *wrapped_endpoint, + grpc_endpoint *secure_endpoint) { connector *c = arg; grpc_iomgr_closure *notify; if (status != GRPC_SECURITY_OK) { - gpr_log(GPR_ERROR, "Secure transport setup failed with error %d.", status); + gpr_log(GPR_ERROR, "Secure handshake failed with error %d.", status); memset(c->result, 0, sizeof(*c->result)); } else { c->result->transport = grpc_create_chttp2_transport( @@ -101,8 +99,8 @@ static void connected(void *arg, grpc_endpoint *tcp) { connector *c = arg; grpc_iomgr_closure *notify; if (tcp != NULL) { - grpc_setup_secure_transport(&c->security_connector->base, tcp, - on_secure_transport_setup_done, c); + grpc_security_connector_do_handshake(&c->security_connector->base, tcp, + on_secure_handshake_done, c); } else { memset(c->result, 0, sizeof(*c->result)); notify = c->notify; -- cgit v1.2.3