diff options
author | Craig Tiller <ctiller@google.com> | 2017-08-29 14:16:14 -0700 |
---|---|---|
committer | Craig Tiller <ctiller@google.com> | 2017-08-29 14:16:14 -0700 |
commit | e944c5d4907732b2bc0a9c6e355ff1e6a65b37e2 (patch) | |
tree | 7be516e29013f54061921af50862ad736b8fb020 /src/core/lib/security | |
parent | 33aeabad66e8083d47f47ddc4bafa4483f1585f8 (diff) | |
parent | 33b51aaa755b2e5f10aade60b67f216bff86cb36 (diff) |
Merge branch 'stats' into stats_histo
Diffstat (limited to 'src/core/lib/security')
20 files changed, 743 insertions, 461 deletions
diff --git a/src/core/lib/security/credentials/composite/composite_credentials.c b/src/core/lib/security/credentials/composite/composite_credentials.c index 77d7b04627..09fd60a12c 100644 --- a/src/core/lib/security/credentials/composite/composite_credentials.c +++ b/src/core/lib/security/credentials/composite/composite_credentials.c @@ -32,88 +32,98 @@ typedef struct { grpc_composite_call_credentials *composite_creds; size_t creds_index; - grpc_credentials_md_store *md_elems; - grpc_auth_metadata_context auth_md_context; - void *user_data; grpc_polling_entity *pollent; - grpc_credentials_metadata_cb cb; + grpc_auth_metadata_context auth_md_context; + grpc_credentials_mdelem_array *md_array; + grpc_closure *on_request_metadata; + grpc_closure internal_on_request_metadata; } grpc_composite_call_credentials_metadata_context; static void composite_call_destruct(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds) { grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; - size_t i; - for (i = 0; i < c->inner.num_creds; i++) { + for (size_t i = 0; i < c->inner.num_creds; i++) { grpc_call_credentials_unref(exec_ctx, c->inner.creds_array[i]); } gpr_free(c->inner.creds_array); } -static void composite_call_md_context_destroy( - grpc_exec_ctx *exec_ctx, - grpc_composite_call_credentials_metadata_context *ctx) { - grpc_credentials_md_store_unref(exec_ctx, ctx->md_elems); - gpr_free(ctx); -} - -static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_credentials_md *md_elems, - size_t num_md, - grpc_credentials_status status, - const char *error_details) { +static void composite_call_metadata_cb(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { grpc_composite_call_credentials_metadata_context *ctx = - (grpc_composite_call_credentials_metadata_context *)user_data; - if (status != GRPC_CREDENTIALS_OK) { - ctx->cb(exec_ctx, ctx->user_data, NULL, 0, status, error_details); - return; - } - - /* Copy the metadata in the context. */ - if (num_md > 0) { - size_t i; - for (i = 0; i < num_md; i++) { - grpc_credentials_md_store_add(ctx->md_elems, md_elems[i].key, - md_elems[i].value); + (grpc_composite_call_credentials_metadata_context *)arg; + if (error == GRPC_ERROR_NONE) { + /* See if we need to get some more metadata. */ + if (ctx->creds_index < ctx->composite_creds->inner.num_creds) { + grpc_call_credentials *inner_creds = + ctx->composite_creds->inner.creds_array[ctx->creds_index++]; + if (grpc_call_credentials_get_request_metadata( + exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context, + ctx->md_array, &ctx->internal_on_request_metadata, &error)) { + // Synchronous response, so call ourselves recursively. + composite_call_metadata_cb(exec_ctx, arg, error); + GRPC_ERROR_UNREF(error); + } + return; } + // We're done! } - - /* See if we need to get some more metadata. */ - if (ctx->creds_index < ctx->composite_creds->inner.num_creds) { - grpc_call_credentials *inner_creds = - ctx->composite_creds->inner.creds_array[ctx->creds_index++]; - grpc_call_credentials_get_request_metadata( - exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context, - composite_call_metadata_cb, ctx); - return; - } - - /* We're done!. */ - ctx->cb(exec_ctx, ctx->user_data, ctx->md_elems->entries, - ctx->md_elems->num_entries, GRPC_CREDENTIALS_OK, NULL); - composite_call_md_context_destroy(exec_ctx, ctx); + GRPC_CLOSURE_SCHED(exec_ctx, ctx->on_request_metadata, GRPC_ERROR_REF(error)); + gpr_free(ctx); } -static void composite_call_get_request_metadata( +static bool composite_call_get_request_metadata( grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context auth_md_context, - grpc_credentials_metadata_cb cb, void *user_data) { + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; grpc_composite_call_credentials_metadata_context *ctx; - ctx = gpr_zalloc(sizeof(grpc_composite_call_credentials_metadata_context)); - ctx->auth_md_context = auth_md_context; - ctx->user_data = user_data; - ctx->cb = cb; ctx->composite_creds = c; ctx->pollent = pollent; - ctx->md_elems = grpc_credentials_md_store_create(c->inner.num_creds); - grpc_call_credentials_get_request_metadata( - exec_ctx, c->inner.creds_array[ctx->creds_index++], ctx->pollent, - auth_md_context, composite_call_metadata_cb, ctx); + ctx->auth_md_context = auth_md_context; + ctx->md_array = md_array; + ctx->on_request_metadata = on_request_metadata; + GRPC_CLOSURE_INIT(&ctx->internal_on_request_metadata, + composite_call_metadata_cb, ctx, grpc_schedule_on_exec_ctx); + while (ctx->creds_index < ctx->composite_creds->inner.num_creds) { + grpc_call_credentials *inner_creds = + ctx->composite_creds->inner.creds_array[ctx->creds_index++]; + if (grpc_call_credentials_get_request_metadata( + exec_ctx, inner_creds, ctx->pollent, ctx->auth_md_context, + ctx->md_array, &ctx->internal_on_request_metadata, error)) { + if (*error != GRPC_ERROR_NONE) break; + } else { + break; + } + } + // If we got through all creds synchronously or we got a synchronous + // error on one of them, return synchronously. + if (ctx->creds_index == ctx->composite_creds->inner.num_creds || + *error != GRPC_ERROR_NONE) { + gpr_free(ctx); + return true; + } + // At least one inner cred is returning asynchronously, so we'll + // return asynchronously as well. + return false; +} + +static void composite_call_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + grpc_composite_call_credentials *c = (grpc_composite_call_credentials *)creds; + for (size_t i = 0; i < c->inner.num_creds; ++i) { + grpc_call_credentials_cancel_get_request_metadata( + exec_ctx, c->inner.creds_array[i], md_array, GRPC_ERROR_REF(error)); + } + GRPC_ERROR_UNREF(error); } static grpc_call_credentials_vtable composite_call_credentials_vtable = { - composite_call_destruct, composite_call_get_request_metadata}; + composite_call_destruct, composite_call_get_request_metadata, + composite_call_cancel_get_request_metadata}; static grpc_call_credentials_array get_creds_array( grpc_call_credentials **creds_addr) { diff --git a/src/core/lib/security/credentials/credentials.c b/src/core/lib/security/credentials/credentials.c index b1f1e82076..8a67c9865b 100644 --- a/src/core/lib/security/credentials/credentials.c +++ b/src/core/lib/security/credentials/credentials.c @@ -38,13 +38,10 @@ /* -- Common. -- */ grpc_credentials_metadata_request *grpc_credentials_metadata_request_create( - grpc_call_credentials *creds, grpc_credentials_metadata_cb cb, - void *user_data) { + grpc_call_credentials *creds) { grpc_credentials_metadata_request *r = gpr_zalloc(sizeof(grpc_credentials_metadata_request)); r->creds = grpc_call_credentials_ref(creds); - r->cb = cb; - r->user_data = user_data; return r; } @@ -104,18 +101,25 @@ void grpc_call_credentials_release(grpc_call_credentials *creds) { grpc_exec_ctx_finish(&exec_ctx); } -void grpc_call_credentials_get_request_metadata( +bool grpc_call_credentials_get_request_metadata( grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data) { + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { if (creds == NULL || creds->vtable->get_request_metadata == NULL) { - if (cb != NULL) { - cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK, NULL); - } + return true; + } + return creds->vtable->get_request_metadata( + exec_ctx, creds, pollent, context, md_array, on_request_metadata, error); +} + +void grpc_call_credentials_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + if (creds == NULL || creds->vtable->cancel_get_request_metadata == NULL) { return; } - creds->vtable->get_request_metadata(exec_ctx, creds, pollent, context, cb, - user_data); + creds->vtable->cancel_get_request_metadata(exec_ctx, creds, md_array, error); } grpc_security_status grpc_channel_credentials_create_security_connector( diff --git a/src/core/lib/security/credentials/credentials.h b/src/core/lib/security/credentials/credentials.h index c45c2e957c..04a54b0ca8 100644 --- a/src/core/lib/security/credentials/credentials.h +++ b/src/core/lib/security/credentials/credentials.h @@ -138,48 +138,39 @@ grpc_channel_credentials *grpc_channel_credentials_from_arg( grpc_channel_credentials *grpc_channel_credentials_find_in_args( const grpc_channel_args *args); -/* --- grpc_credentials_md. --- */ +/* --- grpc_credentials_mdelem_array. --- */ typedef struct { - grpc_slice key; - grpc_slice value; -} grpc_credentials_md; + grpc_mdelem *md; + size_t size; +} grpc_credentials_mdelem_array; -typedef struct { - grpc_credentials_md *entries; - size_t num_entries; - size_t allocated; - gpr_refcount refcount; -} grpc_credentials_md_store; +/// Takes a new ref to \a md. +void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array *list, + grpc_mdelem md); -grpc_credentials_md_store *grpc_credentials_md_store_create( - size_t initial_capacity); +/// Appends all elements from \a src to \a dst, taking a new ref to each one. +void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array *dst, + grpc_credentials_mdelem_array *src); -/* Will ref key and value. */ -void grpc_credentials_md_store_add(grpc_credentials_md_store *store, - grpc_slice key, grpc_slice value); -void grpc_credentials_md_store_add_cstrings(grpc_credentials_md_store *store, - const char *key, const char *value); -grpc_credentials_md_store *grpc_credentials_md_store_ref( - grpc_credentials_md_store *store); -void grpc_credentials_md_store_unref(grpc_exec_ctx *exec_ctx, - grpc_credentials_md_store *store); +void grpc_credentials_mdelem_array_destroy(grpc_exec_ctx *exec_ctx, + grpc_credentials_mdelem_array *list); /* --- grpc_call_credentials. --- */ -/* error_details must be NULL if status is GRPC_CREDENTIALS_OK. */ -typedef void (*grpc_credentials_metadata_cb)( - grpc_exec_ctx *exec_ctx, void *user_data, grpc_credentials_md *md_elems, - size_t num_md, grpc_credentials_status status, const char *error_details); - typedef struct { void (*destruct)(grpc_exec_ctx *exec_ctx, grpc_call_credentials *c); - void (*get_request_metadata)(grpc_exec_ctx *exec_ctx, + bool (*get_request_metadata)(grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, - void *user_data); + grpc_credentials_mdelem_array *md_array, + grpc_closure *on_request_metadata, + grpc_error **error); + void (*cancel_get_request_metadata)(grpc_exec_ctx *exec_ctx, + grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, + grpc_error *error); } grpc_call_credentials_vtable; struct grpc_call_credentials { @@ -191,15 +182,29 @@ struct grpc_call_credentials { grpc_call_credentials *grpc_call_credentials_ref(grpc_call_credentials *creds); void grpc_call_credentials_unref(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds); -void grpc_call_credentials_get_request_metadata( + +/// Returns true if completed synchronously, in which case \a error will +/// be set to indicate the result. Otherwise, \a on_request_metadata will +/// be invoked asynchronously when complete. \a md_array will be populated +/// with the resulting metadata once complete. +bool grpc_call_credentials_get_request_metadata( grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data); + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error); + +/// Cancels a pending asynchronous operation started by +/// grpc_call_credentials_get_request_metadata() with the corresponding +/// value of \a md_array. +void grpc_call_credentials_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error); /* Metadata-only credentials with the specified key and value where asynchronicity can be simulated for testing. */ grpc_call_credentials *grpc_md_only_test_credentials_create( - const char *md_key, const char *md_value, int is_async); + grpc_exec_ctx *exec_ctx, const char *md_key, const char *md_value, + bool is_async); /* --- grpc_server_credentials. --- */ @@ -238,14 +243,11 @@ grpc_server_credentials *grpc_find_server_credentials_in_args( typedef struct { grpc_call_credentials *creds; - grpc_credentials_metadata_cb cb; grpc_http_response response; - void *user_data; } grpc_credentials_metadata_request; grpc_credentials_metadata_request *grpc_credentials_metadata_request_create( - grpc_call_credentials *creds, grpc_credentials_metadata_cb cb, - void *user_data); + grpc_call_credentials *creds); void grpc_credentials_metadata_request_destroy( grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *r); diff --git a/src/core/lib/security/credentials/credentials_metadata.c b/src/core/lib/security/credentials/credentials_metadata.c index fcfdd52d20..ccd39e610f 100644 --- a/src/core/lib/security/credentials/credentials_metadata.c +++ b/src/core/lib/security/credentials/credentials_metadata.c @@ -24,65 +24,36 @@ #include "src/core/lib/slice/slice_internal.h" -static void store_ensure_capacity(grpc_credentials_md_store *store) { - if (store->num_entries == store->allocated) { - store->allocated = (store->allocated == 0) ? 1 : store->allocated * 2; - store->entries = gpr_realloc( - store->entries, store->allocated * sizeof(grpc_credentials_md)); +static void mdelem_list_ensure_capacity(grpc_credentials_mdelem_array *list, + size_t additional_space_needed) { + size_t target_size = list->size + additional_space_needed; + // Find the next power of two greater than the target size (i.e., + // whenever we add more space, we double what we already have). + size_t new_size = 2; + while (new_size < target_size) { + new_size *= 2; } + list->md = gpr_realloc(list->md, sizeof(grpc_mdelem) * new_size); } -grpc_credentials_md_store *grpc_credentials_md_store_create( - size_t initial_capacity) { - grpc_credentials_md_store *store = - gpr_zalloc(sizeof(grpc_credentials_md_store)); - if (initial_capacity > 0) { - store->entries = gpr_malloc(initial_capacity * sizeof(grpc_credentials_md)); - store->allocated = initial_capacity; - } - gpr_ref_init(&store->refcount, 1); - return store; -} - -void grpc_credentials_md_store_add(grpc_credentials_md_store *store, - grpc_slice key, grpc_slice value) { - if (store == NULL) return; - store_ensure_capacity(store); - store->entries[store->num_entries].key = grpc_slice_ref_internal(key); - store->entries[store->num_entries].value = grpc_slice_ref_internal(value); - store->num_entries++; +void grpc_credentials_mdelem_array_add(grpc_credentials_mdelem_array *list, + grpc_mdelem md) { + mdelem_list_ensure_capacity(list, 1); + list->md[list->size++] = GRPC_MDELEM_REF(md); } -void grpc_credentials_md_store_add_cstrings(grpc_credentials_md_store *store, - const char *key, - const char *value) { - if (store == NULL) return; - store_ensure_capacity(store); - store->entries[store->num_entries].key = grpc_slice_from_copied_string(key); - store->entries[store->num_entries].value = - grpc_slice_from_copied_string(value); - store->num_entries++; -} - -grpc_credentials_md_store *grpc_credentials_md_store_ref( - grpc_credentials_md_store *store) { - if (store == NULL) return NULL; - gpr_ref(&store->refcount); - return store; +void grpc_credentials_mdelem_array_append(grpc_credentials_mdelem_array *dst, + grpc_credentials_mdelem_array *src) { + mdelem_list_ensure_capacity(dst, src->size); + for (size_t i = 0; i < src->size; ++i) { + dst->md[dst->size++] = GRPC_MDELEM_REF(src->md[i]); + } } -void grpc_credentials_md_store_unref(grpc_exec_ctx *exec_ctx, - grpc_credentials_md_store *store) { - if (store == NULL) return; - if (gpr_unref(&store->refcount)) { - if (store->entries != NULL) { - size_t i; - for (i = 0; i < store->num_entries; i++) { - grpc_slice_unref_internal(exec_ctx, store->entries[i].key); - grpc_slice_unref_internal(exec_ctx, store->entries[i].value); - } - gpr_free(store->entries); - } - gpr_free(store); +void grpc_credentials_mdelem_array_destroy( + grpc_exec_ctx *exec_ctx, grpc_credentials_mdelem_array *list) { + for (size_t i = 0; i < list->size; ++i) { + GRPC_MDELEM_UNREF(exec_ctx, list->md[i]); } + gpr_free(list->md); } diff --git a/src/core/lib/security/credentials/fake/fake_credentials.c b/src/core/lib/security/credentials/fake/fake_credentials.c index 67e74f7b92..ac9017850f 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.c +++ b/src/core/lib/security/credentials/fake/fake_credentials.c @@ -98,49 +98,44 @@ const char *grpc_fake_transport_get_expected_targets( static void md_only_test_destruct(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds) { grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; - grpc_credentials_md_store_unref(exec_ctx, c->md_store); + GRPC_MDELEM_UNREF(exec_ctx, c->md); } -static void on_simulated_token_fetch_done(grpc_exec_ctx *exec_ctx, - void *user_data, grpc_error *error) { - grpc_credentials_metadata_request *r = - (grpc_credentials_metadata_request *)user_data; - grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)r->creds; - r->cb(exec_ctx, r->user_data, c->md_store->entries, c->md_store->num_entries, - GRPC_CREDENTIALS_OK, NULL); - grpc_credentials_metadata_request_destroy(exec_ctx, r); -} - -static void md_only_test_get_request_metadata( +static bool md_only_test_get_request_metadata( grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data) { + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { grpc_md_only_test_credentials *c = (grpc_md_only_test_credentials *)creds; - + grpc_credentials_mdelem_array_add(md_array, c->md); if (c->is_async) { - grpc_credentials_metadata_request *cb_arg = - grpc_credentials_metadata_request_create(creds, cb, user_data); - GRPC_CLOSURE_SCHED(exec_ctx, - GRPC_CLOSURE_CREATE(on_simulated_token_fetch_done, - cb_arg, grpc_executor_scheduler), - GRPC_ERROR_NONE); - } else { - cb(exec_ctx, user_data, c->md_store->entries, 1, GRPC_CREDENTIALS_OK, NULL); + GRPC_CLOSURE_SCHED(exec_ctx, on_request_metadata, GRPC_ERROR_NONE); + return false; } + return true; +} + +static void md_only_test_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); } static grpc_call_credentials_vtable md_only_test_vtable = { - md_only_test_destruct, md_only_test_get_request_metadata}; + md_only_test_destruct, md_only_test_get_request_metadata, + md_only_test_cancel_get_request_metadata}; grpc_call_credentials *grpc_md_only_test_credentials_create( - const char *md_key, const char *md_value, int is_async) { + grpc_exec_ctx *exec_ctx, const char *md_key, const char *md_value, + bool is_async) { grpc_md_only_test_credentials *c = gpr_zalloc(sizeof(grpc_md_only_test_credentials)); c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; c->base.vtable = &md_only_test_vtable; gpr_ref_init(&c->base.refcount, 1); - c->md_store = grpc_credentials_md_store_create(1); - grpc_credentials_md_store_add_cstrings(c->md_store, md_key, md_value); + c->md = + grpc_mdelem_from_slices(exec_ctx, grpc_slice_from_copied_string(md_key), + grpc_slice_from_copied_string(md_value)); c->is_async = is_async; return &c->base; } diff --git a/src/core/lib/security/credentials/fake/fake_credentials.h b/src/core/lib/security/credentials/fake/fake_credentials.h index fae7a6a9c4..aa0f3b6e20 100644 --- a/src/core/lib/security/credentials/fake/fake_credentials.h +++ b/src/core/lib/security/credentials/fake/fake_credentials.h @@ -52,8 +52,8 @@ const char *grpc_fake_transport_get_expected_targets( typedef struct { grpc_call_credentials base; - grpc_credentials_md_store *md_store; - int is_async; + grpc_mdelem md; + bool is_async; } grpc_md_only_test_credentials; #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_FAKE_FAKE_CREDENTIALS_H */ diff --git a/src/core/lib/security/credentials/iam/iam_credentials.c b/src/core/lib/security/credentials/iam/iam_credentials.c index 4b32c5a047..3de8319d98 100644 --- a/src/core/lib/security/credentials/iam/iam_credentials.c +++ b/src/core/lib/security/credentials/iam/iam_credentials.c @@ -30,26 +30,33 @@ static void iam_destruct(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds) { grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; - grpc_credentials_md_store_unref(exec_ctx, c->iam_md); + grpc_credentials_mdelem_array_destroy(exec_ctx, &c->md_array); } -static void iam_get_request_metadata(grpc_exec_ctx *exec_ctx, +static bool iam_get_request_metadata(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, - void *user_data) { + grpc_credentials_mdelem_array *md_array, + grpc_closure *on_request_metadata, + grpc_error **error) { grpc_google_iam_credentials *c = (grpc_google_iam_credentials *)creds; - cb(exec_ctx, user_data, c->iam_md->entries, c->iam_md->num_entries, - GRPC_CREDENTIALS_OK, NULL); + grpc_credentials_mdelem_array_append(md_array, &c->md_array); + return true; } -static grpc_call_credentials_vtable iam_vtable = {iam_destruct, - iam_get_request_metadata}; +static void iam_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); +} + +static grpc_call_credentials_vtable iam_vtable = { + iam_destruct, iam_get_request_metadata, iam_cancel_get_request_metadata}; grpc_call_credentials *grpc_google_iam_credentials_create( const char *token, const char *authority_selector, void *reserved) { - grpc_google_iam_credentials *c; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; GRPC_API_TRACE( "grpc_iam_credentials_create(token=%s, authority_selector=%s, " "reserved=%p)", @@ -57,14 +64,22 @@ grpc_call_credentials *grpc_google_iam_credentials_create( GPR_ASSERT(reserved == NULL); GPR_ASSERT(token != NULL); GPR_ASSERT(authority_selector != NULL); - c = gpr_zalloc(sizeof(grpc_google_iam_credentials)); + grpc_google_iam_credentials *c = gpr_zalloc(sizeof(*c)); c->base.type = GRPC_CALL_CREDENTIALS_TYPE_IAM; c->base.vtable = &iam_vtable; gpr_ref_init(&c->base.refcount, 1); - c->iam_md = grpc_credentials_md_store_create(2); - grpc_credentials_md_store_add_cstrings( - c->iam_md, GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY, token); - grpc_credentials_md_store_add_cstrings( - c->iam_md, GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY, authority_selector); + grpc_mdelem md = grpc_mdelem_from_slices( + &exec_ctx, + grpc_slice_from_static_string(GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY), + grpc_slice_from_copied_string(token)); + grpc_credentials_mdelem_array_add(&c->md_array, md); + GRPC_MDELEM_UNREF(&exec_ctx, md); + md = grpc_mdelem_from_slices( + &exec_ctx, + grpc_slice_from_static_string(GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY), + grpc_slice_from_copied_string(authority_selector)); + grpc_credentials_mdelem_array_add(&c->md_array, md); + GRPC_MDELEM_UNREF(&exec_ctx, md); + grpc_exec_ctx_finish(&exec_ctx); return &c->base; } diff --git a/src/core/lib/security/credentials/iam/iam_credentials.h b/src/core/lib/security/credentials/iam/iam_credentials.h index fff3c6d2ca..5e3cf65bad 100644 --- a/src/core/lib/security/credentials/iam/iam_credentials.h +++ b/src/core/lib/security/credentials/iam/iam_credentials.h @@ -23,7 +23,7 @@ typedef struct { grpc_call_credentials base; - grpc_credentials_md_store *iam_md; + grpc_credentials_mdelem_array md_array; } grpc_google_iam_credentials; #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_IAM_IAM_CREDENTIALS_H */ diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.c b/src/core/lib/security/credentials/jwt/jwt_credentials.c index 4357657def..02c82e99ba 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.c +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.c @@ -29,10 +29,8 @@ static void jwt_reset_cache(grpc_exec_ctx *exec_ctx, grpc_service_account_jwt_access_credentials *c) { - if (c->cached.jwt_md != NULL) { - grpc_credentials_md_store_unref(exec_ctx, c->cached.jwt_md); - c->cached.jwt_md = NULL; - } + GRPC_MDELEM_UNREF(exec_ctx, c->cached.jwt_md); + c->cached.jwt_md = GRPC_MDNULL; if (c->cached.service_url != NULL) { gpr_free(c->cached.service_url); c->cached.service_url = NULL; @@ -49,33 +47,34 @@ static void jwt_destruct(grpc_exec_ctx *exec_ctx, gpr_mu_destroy(&c->cache_mu); } -static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx, +static bool jwt_get_request_metadata(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, - void *user_data) { + grpc_credentials_mdelem_array *md_array, + grpc_closure *on_request_metadata, + grpc_error **error) { grpc_service_account_jwt_access_credentials *c = (grpc_service_account_jwt_access_credentials *)creds; gpr_timespec refresh_threshold = gpr_time_from_seconds( GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); /* See if we can return a cached jwt. */ - grpc_credentials_md_store *jwt_md = NULL; + grpc_mdelem jwt_md = GRPC_MDNULL; { gpr_mu_lock(&c->cache_mu); if (c->cached.service_url != NULL && strcmp(c->cached.service_url, context.service_url) == 0 && - c->cached.jwt_md != NULL && + !GRPC_MDISNULL(c->cached.jwt_md) && (gpr_time_cmp(gpr_time_sub(c->cached.jwt_expiration, gpr_now(GPR_CLOCK_REALTIME)), refresh_threshold) > 0)) { - jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md); + jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md); } gpr_mu_unlock(&c->cache_mu); } - if (jwt_md == NULL) { + if (GRPC_MDISNULL(jwt_md)) { char *jwt = NULL; /* Generate a new jwt. */ gpr_mu_lock(&c->cache_mu); @@ -89,27 +88,33 @@ static void jwt_get_request_metadata(grpc_exec_ctx *exec_ctx, c->cached.jwt_expiration = gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), c->jwt_lifetime); c->cached.service_url = gpr_strdup(context.service_url); - c->cached.jwt_md = grpc_credentials_md_store_create(1); - grpc_credentials_md_store_add_cstrings( - c->cached.jwt_md, GRPC_AUTHORIZATION_METADATA_KEY, md_value); + c->cached.jwt_md = grpc_mdelem_from_slices( + exec_ctx, + grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(md_value)); gpr_free(md_value); - jwt_md = grpc_credentials_md_store_ref(c->cached.jwt_md); + jwt_md = GRPC_MDELEM_REF(c->cached.jwt_md); } gpr_mu_unlock(&c->cache_mu); } - if (jwt_md != NULL) { - cb(exec_ctx, user_data, jwt_md->entries, jwt_md->num_entries, - GRPC_CREDENTIALS_OK, NULL); - grpc_credentials_md_store_unref(exec_ctx, jwt_md); + if (!GRPC_MDISNULL(jwt_md)) { + grpc_credentials_mdelem_array_add(md_array, jwt_md); + GRPC_MDELEM_UNREF(exec_ctx, jwt_md); } else { - cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_ERROR, - "Could not generate JWT."); + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Could not generate JWT."); } + return true; +} + +static void jwt_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); } -static grpc_call_credentials_vtable jwt_vtable = {jwt_destruct, - jwt_get_request_metadata}; +static grpc_call_credentials_vtable jwt_vtable = { + jwt_destruct, jwt_get_request_metadata, jwt_cancel_get_request_metadata}; grpc_call_credentials * grpc_service_account_jwt_access_credentials_create_from_auth_json_key( diff --git a/src/core/lib/security/credentials/jwt/jwt_credentials.h b/src/core/lib/security/credentials/jwt/jwt_credentials.h index 6e461f1715..07f4022669 100644 --- a/src/core/lib/security/credentials/jwt/jwt_credentials.h +++ b/src/core/lib/security/credentials/jwt/jwt_credentials.h @@ -29,7 +29,7 @@ typedef struct { // the service_url for a more sophisticated one. gpr_mu cache_mu; struct { - grpc_credentials_md_store *jwt_md; + grpc_mdelem jwt_md; char *service_url; gpr_timespec jwt_expiration; } cached; diff --git a/src/core/lib/security/credentials/jwt/jwt_verifier.c b/src/core/lib/security/credentials/jwt/jwt_verifier.c index 6cd558d123..a27284bc50 100644 --- a/src/core/lib/security/credentials/jwt/jwt_verifier.c +++ b/src/core/lib/security/credentials/jwt/jwt_verifier.c @@ -442,7 +442,7 @@ static EVP_PKEY *extract_pkey_from_x509(const char *x509_str) { end: BIO_free(bio); - if (x509 != NULL) X509_free(x509); + X509_free(x509); return result; } @@ -496,6 +496,8 @@ static EVP_PKEY *pkey_from_jwk(grpc_exec_ctx *exec_ctx, const grpc_json *json, const grpc_json *key_prop; RSA *rsa = NULL; EVP_PKEY *result = NULL; + BIGNUM *tmp_n = NULL; + BIGNUM *tmp_e = NULL; GPR_ASSERT(kty != NULL && json != NULL); if (strcmp(kty, "RSA") != 0) { @@ -507,8 +509,6 @@ static EVP_PKEY *pkey_from_jwk(grpc_exec_ctx *exec_ctx, const grpc_json *json, gpr_log(GPR_ERROR, "Could not create rsa key."); goto end; } - BIGNUM *tmp_n = NULL; - BIGNUM *tmp_e = NULL; for (key_prop = json->child; key_prop != NULL; key_prop = key_prop->next) { if (strcmp(key_prop->key, "n") == 0) { tmp_n = @@ -528,11 +528,16 @@ static EVP_PKEY *pkey_from_jwk(grpc_exec_ctx *exec_ctx, const grpc_json *json, gpr_log(GPR_ERROR, "Cannot set RSA key from inputs."); goto end; } + /* RSA_set0_key takes ownership on success. */ + tmp_n = NULL; + tmp_e = NULL; result = EVP_PKEY_new(); EVP_PKEY_set1_RSA(result, rsa); /* uprefs rsa. */ end: - if (rsa != NULL) RSA_free(rsa); + RSA_free(rsa); + BN_free(tmp_n); + BN_free(tmp_e); return result; } @@ -618,7 +623,7 @@ static int verify_jwt_signature(EVP_PKEY *key, const char *alg, result = 1; end: - if (md_ctx != NULL) EVP_MD_CTX_destroy(md_ctx); + EVP_MD_CTX_destroy(md_ctx); return result; } @@ -658,7 +663,7 @@ static void on_keys_retrieved(grpc_exec_ctx *exec_ctx, void *user_data, end: if (json != NULL) grpc_json_destroy(json); - if (verification_key != NULL) EVP_PKEY_free(verification_key); + EVP_PKEY_free(verification_key); ctx->user_cb(exec_ctx, ctx->user_data, status, claims); verifier_cb_ctx_destroy(exec_ctx, ctx); } diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c index 9de561b310..10b270c49c 100644 --- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.c +++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.c @@ -107,15 +107,17 @@ static void oauth2_token_fetcher_destruct(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds) { grpc_oauth2_token_fetcher_credentials *c = (grpc_oauth2_token_fetcher_credentials *)creds; - grpc_credentials_md_store_unref(exec_ctx, c->access_token_md); + GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); gpr_mu_destroy(&c->mu); + grpc_pollset_set_destroy(exec_ctx, + grpc_polling_entity_pollset_set(&c->pollent)); grpc_httpcli_context_destroy(exec_ctx, &c->httpcli_context); } grpc_credentials_status grpc_oauth2_token_fetcher_credentials_parse_server_response( grpc_exec_ctx *exec_ctx, const grpc_http_response *response, - grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime) { + grpc_mdelem *token_md, gpr_timespec *token_lifetime) { char *null_terminated_body = NULL; char *new_access_token = NULL; grpc_credentials_status status = GRPC_CREDENTIALS_OK; @@ -184,17 +186,18 @@ grpc_oauth2_token_fetcher_credentials_parse_server_response( token_lifetime->tv_sec = strtol(expires_in->value, NULL, 10); token_lifetime->tv_nsec = 0; token_lifetime->clock_type = GPR_TIMESPAN; - if (*token_md != NULL) grpc_credentials_md_store_unref(exec_ctx, *token_md); - *token_md = grpc_credentials_md_store_create(1); - grpc_credentials_md_store_add_cstrings( - *token_md, GRPC_AUTHORIZATION_METADATA_KEY, new_access_token); + if (!GRPC_MDISNULL(*token_md)) GRPC_MDELEM_UNREF(exec_ctx, *token_md); + *token_md = grpc_mdelem_from_slices( + exec_ctx, + grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(new_access_token)); status = GRPC_CREDENTIALS_OK; } end: - if (status != GRPC_CREDENTIALS_OK && (*token_md != NULL)) { - grpc_credentials_md_store_unref(exec_ctx, *token_md); - *token_md = NULL; + if (status != GRPC_CREDENTIALS_OK && !GRPC_MDISNULL(*token_md)) { + GRPC_MDELEM_UNREF(exec_ctx, *token_md); + *token_md = GRPC_MDNULL; } if (null_terminated_body != NULL) gpr_free(null_terminated_body); if (new_access_token != NULL) gpr_free(new_access_token); @@ -205,63 +208,130 @@ end: static void on_oauth2_token_fetcher_http_response(grpc_exec_ctx *exec_ctx, void *user_data, grpc_error *error) { + GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error)); grpc_credentials_metadata_request *r = (grpc_credentials_metadata_request *)user_data; grpc_oauth2_token_fetcher_credentials *c = (grpc_oauth2_token_fetcher_credentials *)r->creds; + grpc_mdelem access_token_md = GRPC_MDNULL; gpr_timespec token_lifetime; - grpc_credentials_status status; - - GRPC_LOG_IF_ERROR("oauth_fetch", GRPC_ERROR_REF(error)); - + grpc_credentials_status status = + grpc_oauth2_token_fetcher_credentials_parse_server_response( + exec_ctx, &r->response, &access_token_md, &token_lifetime); + // Update cache and grab list of pending requests. gpr_mu_lock(&c->mu); - status = grpc_oauth2_token_fetcher_credentials_parse_server_response( - exec_ctx, &r->response, &c->access_token_md, &token_lifetime); - if (status == GRPC_CREDENTIALS_OK) { - c->token_expiration = - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime); - r->cb(exec_ctx, r->user_data, c->access_token_md->entries, - c->access_token_md->num_entries, GRPC_CREDENTIALS_OK, NULL); - } else { - c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); - r->cb(exec_ctx, r->user_data, NULL, 0, status, - "Error occured when fetching oauth2 token."); - } + c->token_fetch_pending = false; + c->access_token_md = GRPC_MDELEM_REF(access_token_md); + c->token_expiration = + status == GRPC_CREDENTIALS_OK + ? gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), token_lifetime) + : gpr_inf_past(GPR_CLOCK_REALTIME); + grpc_oauth2_pending_get_request_metadata *pending_request = + c->pending_requests; + c->pending_requests = NULL; gpr_mu_unlock(&c->mu); + // Invoke callbacks for all pending requests. + while (pending_request != NULL) { + if (status == GRPC_CREDENTIALS_OK) { + grpc_credentials_mdelem_array_add(pending_request->md_array, + access_token_md); + } else { + error = GRPC_ERROR_CREATE_REFERENCING_FROM_STATIC_STRING( + "Error occured when fetching oauth2 token.", &error, 1); + } + GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, error); + grpc_polling_entity_del_from_pollset_set( + exec_ctx, pending_request->pollent, + grpc_polling_entity_pollset_set(&c->pollent)); + grpc_oauth2_pending_get_request_metadata *prev = pending_request; + pending_request = pending_request->next; + gpr_free(prev); + } + GRPC_MDELEM_UNREF(exec_ctx, access_token_md); + grpc_call_credentials_unref(exec_ctx, r->creds); grpc_credentials_metadata_request_destroy(exec_ctx, r); } -static void oauth2_token_fetcher_get_request_metadata( +static bool oauth2_token_fetcher_get_request_metadata( grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data) { + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { grpc_oauth2_token_fetcher_credentials *c = (grpc_oauth2_token_fetcher_credentials *)creds; + // Check if we can use the cached token. gpr_timespec refresh_threshold = gpr_time_from_seconds( GRPC_SECURE_TOKEN_REFRESH_THRESHOLD_SECS, GPR_TIMESPAN); - grpc_credentials_md_store *cached_access_token_md = NULL; - { - gpr_mu_lock(&c->mu); - if (c->access_token_md != NULL && - (gpr_time_cmp( - gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)), - refresh_threshold) > 0)) { - cached_access_token_md = - grpc_credentials_md_store_ref(c->access_token_md); - } + grpc_mdelem cached_access_token_md = GRPC_MDNULL; + gpr_mu_lock(&c->mu); + if (!GRPC_MDISNULL(c->access_token_md) && + (gpr_time_cmp( + gpr_time_sub(c->token_expiration, gpr_now(GPR_CLOCK_REALTIME)), + refresh_threshold) > 0)) { + cached_access_token_md = GRPC_MDELEM_REF(c->access_token_md); + } + if (!GRPC_MDISNULL(cached_access_token_md)) { gpr_mu_unlock(&c->mu); + grpc_credentials_mdelem_array_add(md_array, cached_access_token_md); + GRPC_MDELEM_UNREF(exec_ctx, cached_access_token_md); + return true; } - if (cached_access_token_md != NULL) { - cb(exec_ctx, user_data, cached_access_token_md->entries, - cached_access_token_md->num_entries, GRPC_CREDENTIALS_OK, NULL); - grpc_credentials_md_store_unref(exec_ctx, cached_access_token_md); - } else { + // Couldn't get the token from the cache. + // Add request to c->pending_requests and start a new fetch if needed. + grpc_oauth2_pending_get_request_metadata *pending_request = + (grpc_oauth2_pending_get_request_metadata *)gpr_malloc( + sizeof(*pending_request)); + pending_request->md_array = md_array; + pending_request->on_request_metadata = on_request_metadata; + pending_request->pollent = pollent; + grpc_polling_entity_add_to_pollset_set( + exec_ctx, pollent, grpc_polling_entity_pollset_set(&c->pollent)); + pending_request->next = c->pending_requests; + c->pending_requests = pending_request; + bool start_fetch = false; + if (!c->token_fetch_pending) { + c->token_fetch_pending = true; + start_fetch = true; + } + gpr_mu_unlock(&c->mu); + if (start_fetch) { + grpc_call_credentials_ref(creds); c->fetch_func( - exec_ctx, - grpc_credentials_metadata_request_create(creds, cb, user_data), - &c->httpcli_context, pollent, on_oauth2_token_fetcher_http_response, - gpr_time_add(gpr_now(GPR_CLOCK_REALTIME), refresh_threshold)); + exec_ctx, grpc_credentials_metadata_request_create(creds), + &c->httpcli_context, &c->pollent, on_oauth2_token_fetcher_http_response, + gpr_time_add(gpr_now(GPR_CLOCK_MONOTONIC), refresh_threshold)); } + return false; +} + +static void oauth2_token_fetcher_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + grpc_oauth2_token_fetcher_credentials *c = + (grpc_oauth2_token_fetcher_credentials *)creds; + gpr_mu_lock(&c->mu); + grpc_oauth2_pending_get_request_metadata *prev = NULL; + grpc_oauth2_pending_get_request_metadata *pending_request = + c->pending_requests; + while (pending_request != NULL) { + if (pending_request->md_array == md_array) { + // Remove matching pending request from the list. + if (prev != NULL) { + prev->next = pending_request->next; + } else { + c->pending_requests = pending_request->next; + } + // Invoke the callback immediately with an error. + GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, + GRPC_ERROR_REF(error)); + gpr_free(pending_request); + break; + } + prev = pending_request; + pending_request = pending_request->next; + } + gpr_mu_unlock(&c->mu); + GRPC_ERROR_UNREF(error); } static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c, @@ -272,6 +342,8 @@ static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c, gpr_mu_init(&c->mu); c->token_expiration = gpr_inf_past(GPR_CLOCK_REALTIME); c->fetch_func = fetch_func; + c->pollent = + grpc_polling_entity_create_from_pollset_set(grpc_pollset_set_create()); grpc_httpcli_context_init(&c->httpcli_context); } @@ -280,7 +352,8 @@ static void init_oauth2_token_fetcher(grpc_oauth2_token_fetcher_credentials *c, // static grpc_call_credentials_vtable compute_engine_vtable = { - oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata}; + oauth2_token_fetcher_destruct, oauth2_token_fetcher_get_request_metadata, + oauth2_token_fetcher_cancel_get_request_metadata}; static void compute_engine_fetch_oauth2( grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, @@ -301,7 +374,6 @@ static void compute_engine_fetch_oauth2( grpc_httpcli_get( exec_ctx, httpcli_context, pollent, resource_quota, &request, deadline, GRPC_CLOSURE_CREATE(response_cb, metadata_req, grpc_schedule_on_exec_ctx), - &metadata_req->response); grpc_resource_quota_unref_internal(exec_ctx, resource_quota); } @@ -331,7 +403,8 @@ static void refresh_token_destruct(grpc_exec_ctx *exec_ctx, } static grpc_call_credentials_vtable refresh_token_vtable = { - refresh_token_destruct, oauth2_token_fetcher_get_request_metadata}; + refresh_token_destruct, oauth2_token_fetcher_get_request_metadata, + oauth2_token_fetcher_cancel_get_request_metadata}; static void refresh_token_fetch_oauth2( grpc_exec_ctx *exec_ctx, grpc_credentials_metadata_request *metadata_req, @@ -416,26 +489,33 @@ grpc_call_credentials *grpc_google_refresh_token_credentials_create( static void access_token_destruct(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds) { grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; - grpc_credentials_md_store_unref(exec_ctx, c->access_token_md); + GRPC_MDELEM_UNREF(exec_ctx, c->access_token_md); } -static void access_token_get_request_metadata( +static bool access_token_get_request_metadata( grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, void *user_data) { + grpc_credentials_mdelem_array *md_array, grpc_closure *on_request_metadata, + grpc_error **error) { grpc_access_token_credentials *c = (grpc_access_token_credentials *)creds; - cb(exec_ctx, user_data, c->access_token_md->entries, 1, GRPC_CREDENTIALS_OK, - NULL); + grpc_credentials_mdelem_array_add(md_array, c->access_token_md); + return true; +} + +static void access_token_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *c, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + GRPC_ERROR_UNREF(error); } static grpc_call_credentials_vtable access_token_vtable = { - access_token_destruct, access_token_get_request_metadata}; + access_token_destruct, access_token_get_request_metadata, + access_token_cancel_get_request_metadata}; grpc_call_credentials *grpc_access_token_credentials_create( const char *access_token, void *reserved) { grpc_access_token_credentials *c = gpr_zalloc(sizeof(grpc_access_token_credentials)); - char *token_md_value; GRPC_API_TRACE( "grpc_access_token_credentials_create(access_token=<redacted>, " "reserved=%p)", @@ -444,10 +524,13 @@ grpc_call_credentials *grpc_access_token_credentials_create( c->base.type = GRPC_CALL_CREDENTIALS_TYPE_OAUTH2; c->base.vtable = &access_token_vtable; gpr_ref_init(&c->base.refcount, 1); - c->access_token_md = grpc_credentials_md_store_create(1); + char *token_md_value; gpr_asprintf(&token_md_value, "Bearer %s", access_token); - grpc_credentials_md_store_add_cstrings( - c->access_token_md, GRPC_AUTHORIZATION_METADATA_KEY, token_md_value); + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + c->access_token_md = grpc_mdelem_from_slices( + &exec_ctx, grpc_slice_from_static_string(GRPC_AUTHORIZATION_METADATA_KEY), + grpc_slice_from_copied_string(token_md_value)); + grpc_exec_ctx_finish(&exec_ctx); gpr_free(token_md_value); return &c->base; } diff --git a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h index 72093afe9e..d9ad6691b8 100644 --- a/src/core/lib/security/credentials/oauth2/oauth2_credentials.h +++ b/src/core/lib/security/credentials/oauth2/oauth2_credentials.h @@ -58,13 +58,24 @@ typedef void (*grpc_fetch_oauth2_func)(grpc_exec_ctx *exec_ctx, grpc_polling_entity *pollent, grpc_iomgr_cb_func cb, gpr_timespec deadline); + +typedef struct grpc_oauth2_pending_get_request_metadata { + grpc_credentials_mdelem_array *md_array; + grpc_closure *on_request_metadata; + grpc_polling_entity *pollent; + struct grpc_oauth2_pending_get_request_metadata *next; +} grpc_oauth2_pending_get_request_metadata; + typedef struct { grpc_call_credentials base; gpr_mu mu; - grpc_credentials_md_store *access_token_md; + grpc_mdelem access_token_md; gpr_timespec token_expiration; + bool token_fetch_pending; + grpc_oauth2_pending_get_request_metadata *pending_requests; grpc_httpcli_context httpcli_context; grpc_fetch_oauth2_func fetch_func; + grpc_polling_entity pollent; } grpc_oauth2_token_fetcher_credentials; // Google refresh token credentials. @@ -76,7 +87,7 @@ typedef struct { // Access token credentials. typedef struct { grpc_call_credentials base; - grpc_credentials_md_store *access_token_md; + grpc_mdelem access_token_md; } grpc_access_token_credentials; // Private constructor for refresh token credentials from an already parsed @@ -89,6 +100,6 @@ grpc_refresh_token_credentials_create_from_auth_refresh_token( grpc_credentials_status grpc_oauth2_token_fetcher_credentials_parse_server_response( grpc_exec_ctx *exec_ctx, const struct grpc_http_response *response, - grpc_credentials_md_store **token_md, gpr_timespec *token_lifetime); + grpc_mdelem *token_md, gpr_timespec *token_lifetime); #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_OAUTH2_OAUTH2_CREDENTIALS_H */ diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.c b/src/core/lib/security/credentials/plugin/plugin_credentials.c index 96ebfb4d0d..73e0c23e0f 100644 --- a/src/core/lib/security/credentials/plugin/plugin_credentials.c +++ b/src/core/lib/security/credentials/plugin/plugin_credentials.c @@ -31,19 +31,28 @@ #include "src/core/lib/surface/api_trace.h" #include "src/core/lib/surface/validate_metadata.h" -typedef struct { - void *user_data; - grpc_credentials_metadata_cb cb; -} grpc_metadata_plugin_request; - static void plugin_destruct(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds) { grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; + gpr_mu_destroy(&c->mu); if (c->plugin.state != NULL && c->plugin.destroy != NULL) { c->plugin.destroy(c->plugin.state); } } +static void pending_request_remove_locked( + grpc_plugin_credentials *c, + grpc_plugin_credentials_pending_request *pending_request) { + if (pending_request->prev == NULL) { + c->pending_requests = pending_request->next; + } else { + pending_request->prev->next = pending_request->next; + } + if (pending_request->next != NULL) { + pending_request->next->prev = pending_request->prev; + } +} + static void plugin_md_request_metadata_ready(void *request, const grpc_metadata *md, size_t num_md, @@ -53,76 +62,117 @@ static void plugin_md_request_metadata_ready(void *request, grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INITIALIZER( GRPC_EXEC_CTX_FLAG_IS_FINISHED | GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP, NULL, NULL); - grpc_metadata_plugin_request *r = (grpc_metadata_plugin_request *)request; - if (status != GRPC_STATUS_OK) { - if (error_details != NULL) { - gpr_log(GPR_ERROR, "Getting metadata from plugin failed with error: %s", - error_details); - } - r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR, - error_details); - } else { - size_t i; - bool seen_illegal_header = false; - grpc_credentials_md *md_array = NULL; - for (i = 0; i < num_md; i++) { - if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin", - grpc_validate_header_key_is_legal(md[i].key))) { - seen_illegal_header = true; - break; - } else if (!grpc_is_binary_header(md[i].key) && - !GRPC_LOG_IF_ERROR( - "validate_metadata_from_plugin", - grpc_validate_header_nonbin_value_is_legal(md[i].value))) { - gpr_log(GPR_ERROR, "Plugin added invalid metadata value."); - seen_illegal_header = true; - break; + grpc_plugin_credentials_pending_request *r = + (grpc_plugin_credentials_pending_request *)request; + // Check if the request has been cancelled. + // If not, remove it from the pending list, so that it cannot be + // cancelled out from under us. + gpr_mu_lock(&r->creds->mu); + if (!r->cancelled) pending_request_remove_locked(r->creds, r); + gpr_mu_unlock(&r->creds->mu); + grpc_call_credentials_unref(&exec_ctx, &r->creds->base); + // If it has not been cancelled, process it. + if (!r->cancelled) { + if (status != GRPC_STATUS_OK) { + char *msg; + gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s", + error_details); + GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, + GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg)); + gpr_free(msg); + } else { + bool seen_illegal_header = false; + for (size_t i = 0; i < num_md; ++i) { + if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin", + grpc_validate_header_key_is_legal(md[i].key))) { + seen_illegal_header = true; + break; + } else if (!grpc_is_binary_header(md[i].key) && + !GRPC_LOG_IF_ERROR( + "validate_metadata_from_plugin", + grpc_validate_header_nonbin_value_is_legal( + md[i].value))) { + gpr_log(GPR_ERROR, "Plugin added invalid metadata value."); + seen_illegal_header = true; + break; + } } - } - if (seen_illegal_header) { - r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_ERROR, - "Illegal metadata"); - } else if (num_md > 0) { - md_array = gpr_malloc(num_md * sizeof(grpc_credentials_md)); - for (i = 0; i < num_md; i++) { - md_array[i].key = grpc_slice_ref_internal(md[i].key); - md_array[i].value = grpc_slice_ref_internal(md[i].value); + if (seen_illegal_header) { + GRPC_CLOSURE_SCHED( + &exec_ctx, r->on_request_metadata, + GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata")); + } else { + for (size_t i = 0; i < num_md; ++i) { + grpc_mdelem mdelem = grpc_mdelem_from_slices( + &exec_ctx, grpc_slice_ref_internal(md[i].key), + grpc_slice_ref_internal(md[i].value)); + grpc_credentials_mdelem_array_add(r->md_array, mdelem); + GRPC_MDELEM_UNREF(&exec_ctx, mdelem); + } + GRPC_CLOSURE_SCHED(&exec_ctx, r->on_request_metadata, GRPC_ERROR_NONE); } - r->cb(&exec_ctx, r->user_data, md_array, num_md, GRPC_CREDENTIALS_OK, - NULL); - for (i = 0; i < num_md; i++) { - grpc_slice_unref_internal(&exec_ctx, md_array[i].key); - grpc_slice_unref_internal(&exec_ctx, md_array[i].value); - } - gpr_free(md_array); - } else if (num_md == 0) { - r->cb(&exec_ctx, r->user_data, NULL, 0, GRPC_CREDENTIALS_OK, NULL); } } gpr_free(r); grpc_exec_ctx_finish(&exec_ctx); } -static void plugin_get_request_metadata(grpc_exec_ctx *exec_ctx, +static bool plugin_get_request_metadata(grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, grpc_polling_entity *pollent, grpc_auth_metadata_context context, - grpc_credentials_metadata_cb cb, - void *user_data) { + grpc_credentials_mdelem_array *md_array, + grpc_closure *on_request_metadata, + grpc_error **error) { grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; if (c->plugin.get_metadata != NULL) { - grpc_metadata_plugin_request *request = gpr_zalloc(sizeof(*request)); - request->user_data = user_data; - request->cb = cb; + // Create pending_request object. + grpc_plugin_credentials_pending_request *pending_request = + (grpc_plugin_credentials_pending_request *)gpr_zalloc( + sizeof(*pending_request)); + pending_request->creds = c; + pending_request->md_array = md_array; + pending_request->on_request_metadata = on_request_metadata; + // Add it to the pending list. + gpr_mu_lock(&c->mu); + if (c->pending_requests != NULL) { + c->pending_requests->prev = pending_request; + } + pending_request->next = c->pending_requests; + c->pending_requests = pending_request; + gpr_mu_unlock(&c->mu); + // Invoke the plugin. The callback holds a ref to us. + grpc_call_credentials_ref(creds); c->plugin.get_metadata(c->plugin.state, context, - plugin_md_request_metadata_ready, request); - } else { - cb(exec_ctx, user_data, NULL, 0, GRPC_CREDENTIALS_OK, NULL); + plugin_md_request_metadata_ready, pending_request); + return false; + } + return true; +} + +static void plugin_cancel_get_request_metadata( + grpc_exec_ctx *exec_ctx, grpc_call_credentials *creds, + grpc_credentials_mdelem_array *md_array, grpc_error *error) { + grpc_plugin_credentials *c = (grpc_plugin_credentials *)creds; + gpr_mu_lock(&c->mu); + for (grpc_plugin_credentials_pending_request *pending_request = + c->pending_requests; + pending_request != NULL; pending_request = pending_request->next) { + if (pending_request->md_array == md_array) { + pending_request->cancelled = true; + GRPC_CLOSURE_SCHED(exec_ctx, pending_request->on_request_metadata, + GRPC_ERROR_REF(error)); + pending_request_remove_locked(c, pending_request); + break; + } } + gpr_mu_unlock(&c->mu); + GRPC_ERROR_UNREF(error); } static grpc_call_credentials_vtable plugin_vtable = { - plugin_destruct, plugin_get_request_metadata}; + plugin_destruct, plugin_get_request_metadata, + plugin_cancel_get_request_metadata}; grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( grpc_metadata_credentials_plugin plugin, void *reserved) { @@ -134,5 +184,6 @@ grpc_call_credentials *grpc_metadata_credentials_create_from_plugin( c->base.vtable = &plugin_vtable; gpr_ref_init(&c->base.refcount, 1); c->plugin = plugin; + gpr_mu_init(&c->mu); return &c->base; } diff --git a/src/core/lib/security/credentials/plugin/plugin_credentials.h b/src/core/lib/security/credentials/plugin/plugin_credentials.h index ba3dd76859..57266d589a 100644 --- a/src/core/lib/security/credentials/plugin/plugin_credentials.h +++ b/src/core/lib/security/credentials/plugin/plugin_credentials.h @@ -21,10 +21,22 @@ #include "src/core/lib/security/credentials/credentials.h" -typedef struct { +struct grpc_plugin_credentials; + +typedef struct grpc_plugin_credentials_pending_request { + bool cancelled; + struct grpc_plugin_credentials *creds; + grpc_credentials_mdelem_array *md_array; + grpc_closure *on_request_metadata; + struct grpc_plugin_credentials_pending_request *prev; + struct grpc_plugin_credentials_pending_request *next; +} grpc_plugin_credentials_pending_request; + +typedef struct grpc_plugin_credentials { grpc_call_credentials base; grpc_metadata_credentials_plugin plugin; - grpc_credentials_md_store *plugin_md; + gpr_mu mu; + grpc_plugin_credentials_pending_request *pending_requests; } grpc_plugin_credentials; #endif /* GRPC_CORE_LIB_SECURITY_CREDENTIALS_PLUGIN_PLUGIN_CREDENTIALS_H */ diff --git a/src/core/lib/security/transport/client_auth_filter.c b/src/core/lib/security/transport/client_auth_filter.c index 50a51b31cd..e3f0163a6c 100644 --- a/src/core/lib/security/transport/client_auth_filter.c +++ b/src/core/lib/security/transport/client_auth_filter.c @@ -39,6 +39,7 @@ /* We can have a per-call credentials. */ typedef struct { + grpc_call_combiner *call_combiner; grpc_call_credentials *creds; bool have_host; bool have_method; @@ -49,10 +50,11 @@ typedef struct { pollset_set so that work can progress when this call wants work to progress */ grpc_polling_entity *pollent; - gpr_atm security_context_set; - gpr_mu security_context_mu; + grpc_credentials_mdelem_array md_array; grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT]; grpc_auth_metadata_context auth_md_context; + grpc_closure async_cancel_closure; + grpc_closure async_result_closure; } call_data; /* We can have a per-channel credentials. */ @@ -86,42 +88,32 @@ static void add_error(grpc_error **combined, grpc_error *error) { *combined = grpc_error_add_child(*combined, error); } -static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_credentials_md *md_elems, - size_t num_md, - grpc_credentials_status status, - const char *error_details) { - grpc_transport_stream_op_batch *batch = - (grpc_transport_stream_op_batch *)user_data; +static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *input_error) { + grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; grpc_call_element *elem = batch->handler_private.extra_arg; call_data *calld = elem->call_data; + grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, NULL); reset_auth_metadata_context(&calld->auth_md_context); - grpc_error *error = GRPC_ERROR_NONE; - if (status != GRPC_CREDENTIALS_OK) { - error = grpc_error_set_int( - GRPC_ERROR_CREATE_FROM_COPIED_STRING( - error_details != NULL && strlen(error_details) > 0 - ? error_details - : "Credentials failed to get metadata."), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED); - } else { - GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT); + grpc_error *error = GRPC_ERROR_REF(input_error); + if (error == GRPC_ERROR_NONE) { + GPR_ASSERT(calld->md_array.size <= MAX_CREDENTIALS_METADATA_COUNT); GPR_ASSERT(batch->send_initial_metadata); grpc_metadata_batch *mdb = batch->payload->send_initial_metadata.send_initial_metadata; - for (size_t i = 0; i < num_md; i++) { - add_error(&error, - grpc_metadata_batch_add_tail( - exec_ctx, mdb, &calld->md_links[i], - grpc_mdelem_from_slices( - exec_ctx, grpc_slice_ref_internal(md_elems[i].key), - grpc_slice_ref_internal(md_elems[i].value)))); + for (size_t i = 0; i < calld->md_array.size; ++i) { + add_error(&error, grpc_metadata_batch_add_tail( + exec_ctx, mdb, &calld->md_links[i], + GRPC_MDELEM_REF(calld->md_array.md[i]))); } } if (error == GRPC_ERROR_NONE) { grpc_call_next_op(exec_ctx, elem, batch); } else { - grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, batch, error); + error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS, + GRPC_STATUS_UNAUTHENTICATED); + grpc_transport_stream_op_batch_finish_with_failure(exec_ctx, batch, error, + calld->call_combiner); } } @@ -155,6 +147,14 @@ void build_auth_metadata_context(grpc_security_connector *sc, gpr_free(host); } +static void cancel_get_request_metadata(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + grpc_call_credentials_cancel_get_request_metadata( + exec_ctx, calld->creds, &calld->md_array, GRPC_ERROR_REF(error)); +} + static void send_security_metadata(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_transport_stream_op_batch *batch) { @@ -183,7 +183,8 @@ static void send_security_metadata(grpc_exec_ctx *exec_ctx, grpc_error_set_int( GRPC_ERROR_CREATE_FROM_STATIC_STRING( "Incompatible credentials set on channel and call."), - GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED)); + GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); return; } } else { @@ -193,20 +194,34 @@ static void send_security_metadata(grpc_exec_ctx *exec_ctx, build_auth_metadata_context(&chand->security_connector->base, chand->auth_context, calld); + GPR_ASSERT(calld->pollent != NULL); - grpc_call_credentials_get_request_metadata( - exec_ctx, calld->creds, calld->pollent, calld->auth_md_context, - on_credentials_metadata, batch); + + GRPC_CLOSURE_INIT(&calld->async_result_closure, on_credentials_metadata, + batch, grpc_schedule_on_exec_ctx); + grpc_error *error = GRPC_ERROR_NONE; + if (grpc_call_credentials_get_request_metadata( + exec_ctx, calld->creds, calld->pollent, calld->auth_md_context, + &calld->md_array, &calld->async_result_closure, &error)) { + // Synchronous return; invoke on_credentials_metadata() directly. + on_credentials_metadata(exec_ctx, batch, error); + GRPC_ERROR_UNREF(error); + } else { + // Async return; register cancellation closure with call combiner. + GRPC_CLOSURE_INIT(&calld->async_cancel_closure, cancel_get_request_metadata, + elem, grpc_schedule_on_exec_ctx); + grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, + &calld->async_cancel_closure); + } } -static void on_host_checked(grpc_exec_ctx *exec_ctx, void *user_data, - grpc_security_status status) { - grpc_transport_stream_op_batch *batch = - (grpc_transport_stream_op_batch *)user_data; +static void on_host_checked(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_transport_stream_op_batch *batch = (grpc_transport_stream_op_batch *)arg; grpc_call_element *elem = batch->handler_private.extra_arg; call_data *calld = elem->call_data; - - if (status == GRPC_SECURITY_OK) { + grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, NULL); + if (error == GRPC_ERROR_NONE) { send_security_metadata(exec_ctx, elem, batch); } else { char *error_msg; @@ -218,11 +233,22 @@ static void on_host_checked(grpc_exec_ctx *exec_ctx, void *user_data, exec_ctx, batch, grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg), GRPC_ERROR_INT_GRPC_STATUS, - GRPC_STATUS_UNAUTHENTICATED)); + GRPC_STATUS_UNAUTHENTICATED), + calld->call_combiner); gpr_free(error_msg); } } +static void cancel_check_call_host(grpc_exec_ctx *exec_ctx, void *arg, + grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = (call_data *)elem->call_data; + channel_data *chand = (channel_data *)elem->channel_data; + grpc_channel_security_connector_cancel_check_call_host( + exec_ctx, chand->security_connector, &calld->async_result_closure, + GRPC_ERROR_REF(error)); +} + static void auth_start_transport_stream_op_batch( grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_transport_stream_op_batch *batch) { @@ -233,26 +259,18 @@ static void auth_start_transport_stream_op_batch( channel_data *chand = elem->channel_data; if (!batch->cancel_stream) { - /* double checked lock over security context to ensure it's set once */ - if (gpr_atm_acq_load(&calld->security_context_set) == 0) { - gpr_mu_lock(&calld->security_context_mu); - if (gpr_atm_acq_load(&calld->security_context_set) == 0) { - GPR_ASSERT(batch->payload->context != NULL); - if (batch->payload->context[GRPC_CONTEXT_SECURITY].value == NULL) { - batch->payload->context[GRPC_CONTEXT_SECURITY].value = - grpc_client_security_context_create(); - batch->payload->context[GRPC_CONTEXT_SECURITY].destroy = - grpc_client_security_context_destroy; - } - grpc_client_security_context *sec_ctx = - batch->payload->context[GRPC_CONTEXT_SECURITY].value; - GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter"); - sec_ctx->auth_context = - GRPC_AUTH_CONTEXT_REF(chand->auth_context, "client_auth_filter"); - gpr_atm_rel_store(&calld->security_context_set, 1); - } - gpr_mu_unlock(&calld->security_context_mu); + GPR_ASSERT(batch->payload->context != NULL); + if (batch->payload->context[GRPC_CONTEXT_SECURITY].value == NULL) { + batch->payload->context[GRPC_CONTEXT_SECURITY].value = + grpc_client_security_context_create(); + batch->payload->context[GRPC_CONTEXT_SECURITY].destroy = + grpc_client_security_context_destroy; } + grpc_client_security_context *sec_ctx = + batch->payload->context[GRPC_CONTEXT_SECURITY].value; + GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter"); + sec_ctx->auth_context = + GRPC_AUTH_CONTEXT_REF(chand->auth_context, "client_auth_filter"); } if (batch->send_initial_metadata) { @@ -277,11 +295,24 @@ static void auth_start_transport_stream_op_batch( } } if (calld->have_host) { - char *call_host = grpc_slice_to_c_string(calld->host); batch->handler_private.extra_arg = elem; - grpc_channel_security_connector_check_call_host( - exec_ctx, chand->security_connector, call_host, chand->auth_context, - on_host_checked, batch); + GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch, + grpc_schedule_on_exec_ctx); + char *call_host = grpc_slice_to_c_string(calld->host); + grpc_error *error = GRPC_ERROR_NONE; + if (grpc_channel_security_connector_check_call_host( + exec_ctx, chand->security_connector, call_host, + chand->auth_context, &calld->async_result_closure, &error)) { + // Synchronous return; invoke on_host_checked() directly. + on_host_checked(exec_ctx, batch, error); + GRPC_ERROR_UNREF(error); + } else { + // Async return; register cancellation closure with call combiner. + GRPC_CLOSURE_INIT(&calld->async_cancel_closure, cancel_check_call_host, + elem, grpc_schedule_on_exec_ctx); + grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, + &calld->async_cancel_closure); + } gpr_free(call_host); GPR_TIMER_END("auth_start_transport_stream_op_batch", 0); return; /* early exit */ @@ -298,8 +329,7 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const grpc_call_element_args *args) { call_data *calld = elem->call_data; - memset(calld, 0, sizeof(*calld)); - gpr_mu_init(&calld->security_context_mu); + calld->call_combiner = args->call_combiner; return GRPC_ERROR_NONE; } @@ -315,6 +345,7 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, const grpc_call_final_info *final_info, grpc_closure *ignored) { call_data *calld = elem->call_data; + grpc_credentials_mdelem_array_destroy(exec_ctx, &calld->md_array); grpc_call_credentials_unref(exec_ctx, calld->creds); if (calld->have_host) { grpc_slice_unref_internal(exec_ctx, calld->host); @@ -323,7 +354,6 @@ static void destroy_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem, grpc_slice_unref_internal(exec_ctx, calld->method); } reset_auth_metadata_context(&calld->auth_md_context); - gpr_mu_destroy(&calld->security_context_mu); } /* Constructor for channel_data */ @@ -382,6 +412,5 @@ const grpc_channel_filter grpc_client_auth_filter = { sizeof(channel_data), init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, grpc_channel_next_get_info, "client-auth"}; diff --git a/src/core/lib/security/transport/security_connector.c b/src/core/lib/security/transport/security_connector.c index 6788126769..a7568b995f 100644 --- a/src/core/lib/security/transport/security_connector.c +++ b/src/core/lib/security/transport/security_connector.c @@ -136,15 +136,27 @@ void grpc_security_connector_check_peer(grpc_exec_ctx *exec_ctx, } } -void grpc_channel_security_connector_check_call_host( +bool grpc_channel_security_connector_check_call_host( grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, const char *host, grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, void *user_data) { + grpc_closure *on_call_host_checked, grpc_error **error) { if (sc == NULL || sc->check_call_host == NULL) { - cb(exec_ctx, user_data, GRPC_SECURITY_ERROR); - } else { - sc->check_call_host(exec_ctx, sc, host, auth_context, cb, user_data); + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "cannot check call host -- no security connector"); + return true; } + return sc->check_call_host(exec_ctx, sc, host, auth_context, + on_call_host_checked, error); +} + +void grpc_channel_security_connector_cancel_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, grpc_error *error) { + if (sc == NULL || sc->cancel_check_call_host == NULL) { + GRPC_ERROR_UNREF(error); + return; + } + sc->cancel_check_call_host(exec_ctx, sc, on_call_host_checked, error); } #ifndef NDEBUG @@ -368,13 +380,19 @@ static void fake_server_check_peer(grpc_exec_ctx *exec_ctx, fake_check_peer(exec_ctx, sc, peer, auth_context, on_peer_checked); } -static void fake_channel_check_call_host(grpc_exec_ctx *exec_ctx, +static bool fake_channel_check_call_host(grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, const char *host, grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, - void *user_data) { - cb(exec_ctx, user_data, GRPC_SECURITY_OK); + grpc_closure *on_call_host_checked, + grpc_error **error) { + return true; +} + +static void fake_channel_cancel_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, grpc_error *error) { + GRPC_ERROR_UNREF(error); } static void fake_channel_add_handshakers( @@ -413,6 +431,7 @@ grpc_channel_security_connector *grpc_fake_channel_security_connector_create( c->base.request_metadata_creds = grpc_call_credentials_ref(request_metadata_creds); c->base.check_call_host = fake_channel_check_call_host; + c->base.cancel_check_call_host = fake_channel_cancel_check_call_host; c->base.add_handshakers = fake_channel_add_handshakers; c->target = gpr_strdup(target); const char *expected_targets = grpc_fake_transport_get_expected_targets(args); @@ -663,26 +682,35 @@ void tsi_shallow_peer_destruct(tsi_peer *peer) { if (peer->properties != NULL) gpr_free(peer->properties); } -static void ssl_channel_check_call_host(grpc_exec_ctx *exec_ctx, +static bool ssl_channel_check_call_host(grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, const char *host, grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, - void *user_data) { + grpc_closure *on_call_host_checked, + grpc_error **error) { grpc_ssl_channel_security_connector *c = (grpc_ssl_channel_security_connector *)sc; grpc_security_status status = GRPC_SECURITY_ERROR; tsi_peer peer = tsi_shallow_peer_from_ssl_auth_context(auth_context); if (ssl_host_matches_name(&peer, host)) status = GRPC_SECURITY_OK; - /* If the target name was overridden, then the original target_name was 'checked' transitively during the previous peer check at the end of the handshake. */ if (c->overridden_target_name != NULL && strcmp(host, c->target_name) == 0) { status = GRPC_SECURITY_OK; } - cb(exec_ctx, user_data, status); + if (status != GRPC_SECURITY_OK) { + *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "call host does not match SSL server name"); + } tsi_shallow_peer_destruct(&peer); + return true; +} + +static void ssl_channel_cancel_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, grpc_error *error) { + GRPC_ERROR_UNREF(error); } static grpc_security_connector_vtable ssl_channel_vtable = { @@ -811,6 +839,7 @@ grpc_security_status grpc_ssl_channel_security_connector_create( c->base.request_metadata_creds = grpc_call_credentials_ref(request_metadata_creds); c->base.check_call_host = ssl_channel_check_call_host; + c->base.cancel_check_call_host = ssl_channel_cancel_check_call_host; c->base.add_handshakers = ssl_channel_add_handshakers; gpr_split_host_port(target_name, &c->target_name, &port); gpr_free(port); diff --git a/src/core/lib/security/transport/security_connector.h b/src/core/lib/security/transport/security_connector.h index 1c0fe40045..4f9b63ad20 100644 --- a/src/core/lib/security/transport/security_connector.h +++ b/src/core/lib/security/transport/security_connector.h @@ -117,27 +117,38 @@ grpc_security_connector *grpc_security_connector_find_in_args( typedef struct grpc_channel_security_connector grpc_channel_security_connector; -typedef void (*grpc_security_call_host_check_cb)(grpc_exec_ctx *exec_ctx, - void *user_data, - grpc_security_status status); - struct grpc_channel_security_connector { grpc_security_connector base; grpc_call_credentials *request_metadata_creds; - void (*check_call_host)(grpc_exec_ctx *exec_ctx, + bool (*check_call_host)(grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, const char *host, grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, void *user_data); + grpc_closure *on_call_host_checked, + grpc_error **error); + void (*cancel_check_call_host)(grpc_exec_ctx *exec_ctx, + grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, + grpc_error *error); void (*add_handshakers)(grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, grpc_handshake_manager *handshake_mgr); }; -/* Checks that the host that will be set for a call is acceptable. */ -void grpc_channel_security_connector_check_call_host( +/// Checks that the host that will be set for a call is acceptable. +/// Returns true if completed synchronously, in which case \a error will +/// be set to indicate the result. Otherwise, \a on_call_host_checked +/// will be invoked when complete. +bool grpc_channel_security_connector_check_call_host( grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, const char *host, grpc_auth_context *auth_context, - grpc_security_call_host_check_cb cb, void *user_data); + grpc_closure *on_call_host_checked, grpc_error **error); + +/// Cancels a pending asychronous call to +/// grpc_channel_security_connector_check_call_host() with +/// \a on_call_host_checked as its callback. +void grpc_channel_security_connector_cancel_check_call_host( + grpc_exec_ctx *exec_ctx, grpc_channel_security_connector *sc, + grpc_closure *on_call_host_checked, grpc_error *error); /* Registers handshakers with \a handshake_mgr. */ void grpc_channel_security_connector_add_handshakers( diff --git a/src/core/lib/security/transport/security_handshaker.c b/src/core/lib/security/transport/security_handshaker.c index 239a211c0b..fc9c9f980f 100644 --- a/src/core/lib/security/transport/security_handshaker.c +++ b/src/core/lib/security/transport/security_handshaker.c @@ -147,7 +147,7 @@ static void on_peer_checked(grpc_exec_ctx *exec_ctx, void *arg, goto done; } // Get unused bytes. - unsigned char *unused_bytes = NULL; + const unsigned char *unused_bytes = NULL; size_t unused_bytes_size = 0; result = tsi_handshaker_result_get_unused_bytes( h->handshaker_result, &unused_bytes, &unused_bytes_size); @@ -261,7 +261,7 @@ static grpc_error *do_handshaker_next_locked( grpc_exec_ctx *exec_ctx, security_handshaker *h, const unsigned char *bytes_received, size_t bytes_received_size) { // Invoke TSI handshaker. - unsigned char *bytes_to_send = NULL; + const unsigned char *bytes_to_send = NULL; size_t bytes_to_send_size = 0; tsi_handshaker_result *handshaker_result = NULL; tsi_result result = tsi_handshaker_next( diff --git a/src/core/lib/security/transport/server_auth_filter.c b/src/core/lib/security/transport/server_auth_filter.c index 9bf3f0ca0f..b721ce4a22 100644 --- a/src/core/lib/security/transport/server_auth_filter.c +++ b/src/core/lib/security/transport/server_auth_filter.c @@ -26,7 +26,15 @@ #include "src/core/lib/security/transport/auth_filters.h" #include "src/core/lib/slice/slice_internal.h" +typedef enum { + STATE_INIT = 0, + STATE_DONE, + STATE_CANCELLED, +} async_state; + typedef struct call_data { + grpc_call_combiner *call_combiner; + grpc_call_stack *owning_call; grpc_transport_stream_op_batch *recv_initial_metadata_batch; grpc_closure *original_recv_initial_metadata_ready; grpc_closure recv_initial_metadata_ready; @@ -34,6 +42,8 @@ typedef struct call_data { const grpc_metadata *consumed_md; size_t num_consumed_md; grpc_auth_context *auth_context; + grpc_closure cancel_closure; + gpr_atm state; // async_state } call_data; typedef struct channel_data { @@ -78,54 +88,92 @@ static grpc_filtered_mdelem remove_consumed_md(grpc_exec_ctx *exec_ctx, return GRPC_FILTERED_MDELEM(md); } -/* called from application code */ -static void on_md_processing_done( - void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, - const grpc_metadata *response_md, size_t num_response_md, - grpc_status_code status, const char *error_details) { - grpc_call_element *elem = user_data; +static void on_md_processing_done_inner(grpc_exec_ctx *exec_ctx, + grpc_call_element *elem, + const grpc_metadata *consumed_md, + size_t num_consumed_md, + const grpc_metadata *response_md, + size_t num_response_md, + grpc_error *error) { call_data *calld = elem->call_data; grpc_transport_stream_op_batch *batch = calld->recv_initial_metadata_batch; - grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, NULL); /* TODO(jboeuf): Implement support for response_md. */ if (response_md != NULL && num_response_md > 0) { gpr_log(GPR_INFO, "response_md in auth metadata processing not supported for now. " "Ignoring..."); } - grpc_error *error = GRPC_ERROR_NONE; - if (status == GRPC_STATUS_OK) { + if (error == GRPC_ERROR_NONE) { calld->consumed_md = consumed_md; calld->num_consumed_md = num_consumed_md; error = grpc_metadata_batch_filter( - &exec_ctx, batch->payload->recv_initial_metadata.recv_initial_metadata, + exec_ctx, batch->payload->recv_initial_metadata.recv_initial_metadata, remove_consumed_md, elem, "Response metadata filtering error"); - } else { - if (error_details == NULL) { - error_details = "Authentication metadata processing failed."; + } + GRPC_CLOSURE_SCHED(exec_ctx, calld->original_recv_initial_metadata_ready, + error); +} + +// Called from application code. +static void on_md_processing_done( + void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md, + const grpc_metadata *response_md, size_t num_response_md, + grpc_status_code status, const char *error_details) { + grpc_call_element *elem = user_data; + call_data *calld = elem->call_data; + grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT; + // If the call was not cancelled while we were in flight, process the result. + if (gpr_atm_full_cas(&calld->state, (gpr_atm)STATE_INIT, + (gpr_atm)STATE_DONE)) { + grpc_error *error = GRPC_ERROR_NONE; + if (status != GRPC_STATUS_OK) { + if (error_details == NULL) { + error_details = "Authentication metadata processing failed."; + } + error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details), + GRPC_ERROR_INT_GRPC_STATUS, status); } - error = - grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details), - GRPC_ERROR_INT_GRPC_STATUS, status); + on_md_processing_done_inner(&exec_ctx, elem, consumed_md, num_consumed_md, + response_md, num_response_md, error); } + // Clean up. for (size_t i = 0; i < calld->md.count; i++) { grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].key); grpc_slice_unref_internal(&exec_ctx, calld->md.metadata[i].value); } grpc_metadata_array_destroy(&calld->md); - GRPC_CLOSURE_SCHED(&exec_ctx, calld->original_recv_initial_metadata_ready, - error); + GRPC_CALL_STACK_UNREF(&exec_ctx, calld->owning_call, "server_auth_metadata"); grpc_exec_ctx_finish(&exec_ctx); } +static void cancel_call(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { + grpc_call_element *elem = (grpc_call_element *)arg; + call_data *calld = elem->call_data; + // If the result was not already processed, invoke the callback now. + if (gpr_atm_full_cas(&calld->state, (gpr_atm)STATE_INIT, + (gpr_atm)STATE_CANCELLED)) { + on_md_processing_done_inner(exec_ctx, elem, NULL, 0, NULL, 0, + GRPC_ERROR_REF(error)); + } +} + static void recv_initial_metadata_ready(grpc_exec_ctx *exec_ctx, void *arg, grpc_error *error) { - grpc_call_element *elem = arg; + grpc_call_element *elem = (grpc_call_element *)arg; channel_data *chand = elem->channel_data; call_data *calld = elem->call_data; grpc_transport_stream_op_batch *batch = calld->recv_initial_metadata_batch; if (error == GRPC_ERROR_NONE) { if (chand->creds != NULL && chand->creds->processor.process != NULL) { + // We're calling out to the application, so we need to make sure + // to drop the call combiner early if we get cancelled. + GRPC_CLOSURE_INIT(&calld->cancel_closure, cancel_call, elem, + grpc_schedule_on_exec_ctx); + grpc_call_combiner_set_notify_on_cancel(exec_ctx, calld->call_combiner, + &calld->cancel_closure); + GRPC_CALL_STACK_REF(calld->owning_call, "server_auth_metadata"); calld->md = metadata_batch_to_md_array( batch->payload->recv_initial_metadata.recv_initial_metadata); chand->creds->processor.process( @@ -159,6 +207,8 @@ static grpc_error *init_call_elem(grpc_exec_ctx *exec_ctx, const grpc_call_element_args *args) { call_data *calld = elem->call_data; channel_data *chand = elem->channel_data; + calld->call_combiner = args->call_combiner; + calld->owning_call = args->call_stack; GRPC_CLOSURE_INIT(&calld->recv_initial_metadata_ready, recv_initial_metadata_ready, elem, grpc_schedule_on_exec_ctx); @@ -218,6 +268,5 @@ const grpc_channel_filter grpc_server_auth_filter = { sizeof(channel_data), init_channel_elem, destroy_channel_elem, - grpc_call_next_get_peer, grpc_channel_next_get_info, "server-auth"}; |