aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Mark D. Roth <roth@google.com>2016-11-30 11:40:57 -0800
committerGravatar Mark D. Roth <roth@google.com>2016-11-30 11:40:57 -0800
commit75d0c420f298384ce23ab025b2ab67860512b51d (patch)
tree8a61d0c47ca2b65a9dfb3b14b0696608e6e7c833 /src
parentc6e1e6927f8ab47ef3b9654b221a94af3b2f80e5 (diff)
Fix shutdown semantics for security handshaker.
Diffstat (limited to 'src')
-rw-r--r--src/core/lib/security/transport/security_handshaker.c174
1 files changed, 103 insertions, 71 deletions
diff --git a/src/core/lib/security/transport/security_handshaker.c b/src/core/lib/security/transport/security_handshaker.c
index 681826a287..964a692ae7 100644
--- a/src/core/lib/security/transport/security_handshaker.c
+++ b/src/core/lib/security/transport/security_handshaker.c
@@ -50,14 +50,23 @@
typedef struct {
grpc_handshaker base;
- // args will be NULL when either there is no handshake in progress or
- // when the handshaker is shutting down.
- grpc_handshaker_args* args;
- grpc_closure* on_handshake_done;
- grpc_security_connector *connector;
+
+ // State set at creation time.
tsi_handshaker *handshaker;
+ grpc_security_connector *connector;
+
gpr_mu mu;
gpr_refcount refs;
+
+ bool shutdown;
+ // Endpoint and read buffer to destroy after a shutdown.
+ grpc_endpoint *endpoint_to_destroy;
+ grpc_slice_buffer *read_buffer_to_destroy;
+
+ // State saved while performing the handshake.
+ grpc_handshaker_args* args;
+ grpc_closure* on_handshake_done;
+
unsigned char *handshake_buffer;
size_t handshake_buffer_size;
grpc_slice_buffer left_overs;
@@ -68,18 +77,19 @@ typedef struct {
grpc_auth_context *auth_context;
} security_handshaker;
-static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
- void *setup,
- grpc_error *error);
-
-static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx, void *setup,
- grpc_error *error);
-
-static void unref_handshake(security_handshaker *h) {
+static void security_handshaker_unref(grpc_exec_ctx *exec_ctx,
+ security_handshaker *h) {
if (gpr_unref(&h->refs)) {
- if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker);
gpr_mu_destroy(&h->mu);
+ if (h->handshaker != NULL) tsi_handshaker_destroy(h->handshaker);
if (h->handshake_buffer != NULL) gpr_free(h->handshake_buffer);
+ if (h->endpoint_to_destroy != NULL) {
+ grpc_endpoint_destroy(exec_ctx, h->endpoint_to_destroy);
+ }
+ if (h->read_buffer_to_destroy != NULL) {
+ grpc_slice_buffer_destroy(h->read_buffer_to_destroy);
+ gpr_free(h->read_buffer_to_destroy);
+ }
grpc_slice_buffer_destroy(&h->left_overs);
grpc_slice_buffer_destroy(&h->outgoing);
GRPC_AUTH_CONTEXT_UNREF(h->auth_context, "handshake");
@@ -88,27 +98,41 @@ static void unref_handshake(security_handshaker *h) {
}
}
-static void security_handshake_done_locked(grpc_exec_ctx *exec_ctx,
- security_handshaker *h,
- grpc_error *error) {
+// Set args fields to NULL, saving the endpoint and read buffer for
+// later destruction.
+static void cleanup_args_for_failure_locked(security_handshaker *h) {
+ h->endpoint_to_destroy = h->args->endpoint;
+ h->args->endpoint = NULL;
+ h->read_buffer_to_destroy = h->args->read_buffer;
+ h->args->read_buffer = NULL;
+ grpc_channel_args_destroy(h->args->args);
+ h->args->args = NULL;
+}
+
+// If the handshake failed or we're shutting down, clean up and invoke the
+// callback with the error.
+static void security_handshake_failed_locked(grpc_exec_ctx *exec_ctx,
+ security_handshaker *h,
+ grpc_error *error) {
if (error == GRPC_ERROR_NONE) {
- grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context);
- grpc_channel_args* tmp_args = h->args->args;
- h->args->args =
- grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1);
- grpc_channel_args_destroy(tmp_args);
- } else {
- const char *msg = grpc_error_string(error);
- gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg);
- grpc_error_free_string(msg);
+ // If we were shut down after the handshake succeeded but before an
+ // endpoint callback was invoked, we need to generate our own error.
+ error = GRPC_ERROR_CREATE("Handshaker shutdown");
+ }
+ const char *msg = grpc_error_string(error);
+ gpr_log(GPR_DEBUG, "Security handshake failed: %s", msg);
+ grpc_error_free_string(msg);
+ if (!h->shutdown) {
+ // TODO(ctiller): It is currently necessary to shutdown endpoints
+ // before destroying them, even if we know that there are no
+ // pending read/write callbacks. This should be fixed, at which
+ // point this can be removed.
grpc_endpoint_shutdown(exec_ctx, h->args->endpoint);
-// FIXME: clarify who should destroy...
- //grpc_endpoint_destroy(exec_ctx, h->args->endpoint);
+ // Not shutting down, so the write failed. Clean up before
+ // invoking the callback.
+ cleanup_args_for_failure_locked(h);
}
- // Clear out the read buffer before it gets passed to the transport,
- // since any excess bytes were already copied to h->left_overs.
- grpc_slice_buffer_reset_and_unref(h->args->read_buffer);
- h->args = NULL;
+ // Invoke callback.
grpc_exec_ctx_sched(exec_ctx, h->on_handshake_done, error, NULL);
}
@@ -116,9 +140,8 @@ static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg,
grpc_error *error) {
security_handshaker *h = arg;
gpr_mu_lock(&h->mu);
- if (error != GRPC_ERROR_NONE) {
- // Take a new ref to pass to security_handshake_done_locked().
- GRPC_ERROR_REF(error);
+ if (error != GRPC_ERROR_NONE || h->shutdown) {
+ security_handshake_failed_locked(exec_ctx, h, GRPC_ERROR_REF(error));
goto done;
}
// Get frame protector.
@@ -128,17 +151,30 @@ static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg,
if (result != TSI_OK) {
error = grpc_set_tsi_error_result(
GRPC_ERROR_CREATE("Frame protector creation failed"), result);
+ security_handshake_failed_locked(exec_ctx, h, error);
goto done;
}
+ // Success.
+ // Create secure endpoint.
h->args->endpoint =
grpc_secure_endpoint_create(protector, h->args->endpoint,
h->left_overs.slices, h->left_overs.count);
h->left_overs.count = 0;
h->left_overs.length = 0;
+ // Clear out the read buffer before it gets passed to the transport,
+ // since any excess bytes were already copied to h->left_overs.
+ grpc_slice_buffer_reset_and_unref(h->args->read_buffer);
+ // Add auth context to channel args.
+ grpc_arg auth_context_arg = grpc_auth_context_to_arg(h->auth_context);
+ grpc_channel_args* tmp_args = h->args->args;
+ h->args->args =
+ grpc_channel_args_copy_and_add(tmp_args, &auth_context_arg, 1);
+ grpc_channel_args_destroy(tmp_args);
+ // Invoke callback.
+ grpc_exec_ctx_sched(exec_ctx, h->on_handshake_done, GRPC_ERROR_NONE, NULL);
done:
- security_handshake_done_locked(exec_ctx, h, error);
gpr_mu_unlock(&h->mu);
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
}
static grpc_error* check_peer_locked(grpc_exec_ctx *exec_ctx,
@@ -185,16 +221,15 @@ static grpc_error* send_handshake_bytes_to_peer_locked(grpc_exec_ctx *exec_ctx,
}
static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
- void *handshake,
- grpc_error *error) {
- security_handshaker *h = handshake;
+ void *arg, grpc_error *error) {
+ security_handshaker *h = arg;
gpr_mu_lock(&h->mu);
- if (error != GRPC_ERROR_NONE) {
- security_handshake_done_locked(
+ if (error != GRPC_ERROR_NONE || h->shutdown) {
+ security_handshake_failed_locked(
exec_ctx, h,
GRPC_ERROR_CREATE_REFERENCING("Handshake read failed", &error, 1));
gpr_mu_unlock(&h->mu);
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
return;
}
// Process received data.
@@ -217,21 +252,21 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
} else {
error = send_handshake_bytes_to_peer_locked(exec_ctx, h);
if (error != GRPC_ERROR_NONE) {
- security_handshake_done_locked(exec_ctx, h, error);
+ security_handshake_failed_locked(exec_ctx, h, error);
gpr_mu_unlock(&h->mu);
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
return;
}
goto done;
}
}
if (result != TSI_OK) {
- security_handshake_done_locked(
+ security_handshake_failed_locked(
exec_ctx, h,
grpc_set_tsi_error_result(GRPC_ERROR_CREATE("Handshake failed"),
result));
gpr_mu_unlock(&h->mu);
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
return;
}
/* Handshake is done and successful this point. */
@@ -258,42 +293,37 @@ static void on_handshake_data_received_from_peer(grpc_exec_ctx *exec_ctx,
// Check peer.
error = check_peer_locked(exec_ctx, h);
if (error != GRPC_ERROR_NONE) {
- security_handshake_done_locked(exec_ctx, h, error);
+ security_handshake_failed_locked(exec_ctx, h, error);
gpr_mu_unlock(&h->mu);
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
return;
}
done:
gpr_mu_unlock(&h->mu);
}
-/* If handshake is NULL, the handshake is done. */
static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx,
- void *handshake, grpc_error *error) {
- security_handshaker *h = handshake;
- /* Make sure that write is OK. */
- if (error != GRPC_ERROR_NONE) {
- if (handshake != NULL) {
- gpr_mu_lock(&h->mu);
- security_handshake_done_locked(
- exec_ctx, h,
- GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1));
- gpr_mu_unlock(&h->mu);
- unref_handshake(h);
- }
+ void *arg, grpc_error *error) {
+ security_handshaker *h = arg;
+ gpr_mu_lock(&h->mu);
+ if (error != GRPC_ERROR_NONE || h->shutdown) {
+ security_handshake_failed_locked(
+ exec_ctx, h,
+ GRPC_ERROR_CREATE_REFERENCING("Handshake write failed", &error, 1));
+ gpr_mu_unlock(&h->mu);
+ security_handshaker_unref(exec_ctx, h);
return;
}
/* We may be done. */
- gpr_mu_lock(&h->mu);
if (tsi_handshaker_is_in_progress(h->handshaker)) {
grpc_endpoint_read(exec_ctx, h->args->endpoint, h->args->read_buffer,
&h->on_handshake_data_received_from_peer);
} else {
error = check_peer_locked(exec_ctx, h);
if (error != GRPC_ERROR_NONE) {
- security_handshake_done_locked(exec_ctx, h, error);
+ security_handshake_failed_locked(exec_ctx, h, error);
gpr_mu_unlock(&h->mu);
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
return;
}
}
@@ -305,17 +335,19 @@ static void on_handshake_data_sent_to_peer(grpc_exec_ctx *exec_ctx,
//
static void security_handshaker_destroy(grpc_exec_ctx* exec_ctx,
- grpc_handshaker* handshaker) {
+ grpc_handshaker* handshaker) {
security_handshaker* h = (security_handshaker*)handshaker;
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
}
static void security_handshaker_shutdown(grpc_exec_ctx* exec_ctx,
- grpc_handshaker* handshaker) {
+ grpc_handshaker* handshaker) {
security_handshaker *h = (security_handshaker*)handshaker;
gpr_mu_lock(&h->mu);
- if (h->args != NULL) {
+ if (!h->shutdown) {
+ h->shutdown = true;
grpc_endpoint_shutdown(exec_ctx, h->args->endpoint);
+ cleanup_args_for_failure_locked(h);
}
gpr_mu_unlock(&h->mu);
}
@@ -331,9 +363,9 @@ static void security_handshaker_do_handshake(
gpr_ref(&h->refs);
grpc_error* error = send_handshake_bytes_to_peer_locked(exec_ctx, h);
if (error != GRPC_ERROR_NONE) {
- security_handshake_done_locked(exec_ctx, h, error);
+ security_handshake_failed_locked(exec_ctx, h, error);
gpr_mu_unlock(&h->mu);
- unref_handshake(h);
+ security_handshaker_unref(exec_ctx, h);
return;
}
gpr_mu_unlock(&h->mu);