|
|
|
@ -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); |
|
|
|
|