Cannot figure out server filter logic for error in auth md processing.

- Positive tests pass even if we will have to change the interface to
  add the processor to the server credentials (will be done in a
  separate pull request).
- ASAN leaks for the error case.
- The client should get a GRPC_STATUS_UNAUTHENTICATED as opposed to
  GPRC_STATUS_INTERNAL.
pull/2492/head
Julien Boeuf 10 years ago
parent ea456fc2bf
commit a87d6c2af6
  1. 25
      include/grpc/grpc_security.h
  2. 13
      src/core/security/security_context.c
  3. 3
      src/core/security/security_context.h
  4. 55
      src/core/security/server_auth_filter.c
  5. 248
      test/core/end2end/tests/request_response_with_payload_and_call_creds.c

@ -291,16 +291,23 @@ typedef void (*grpc_process_auth_metadata_done_cb)(
void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md,
int success, grpc_auth_context *result);
/* Pluggable metadata processing function */
typedef void (*grpc_process_auth_metadata_func)(
grpc_auth_ticket *ticket, grpc_auth_context *channel_ctx,
const grpc_metadata *md, size_t md_count,
grpc_process_auth_metadata_done_cb cb, void *user_data);
/* Registration function for metadata processing.
/* Pluggable server-side metadata processor object */
typedef struct {
void (*process)(void *state, grpc_auth_ticket *ticket,
grpc_auth_context *channel_ctx, const grpc_metadata *md,
size_t md_count, grpc_process_auth_metadata_done_cb cb,
void *user_data);
void *state;
} grpc_auth_metadata_processor;
/* XXXX: this is a temporarty interface. Please do NOT use.
This function will be moved to the server_credentials in a subsequent
pull request. XXXX
Registration function for metadata processing.
Should be called before the server is started. */
void grpc_server_auth_context_register_process_metadata_func(
grpc_process_auth_metadata_func func);
void grpc_server_register_auth_metadata_processor(
grpc_auth_metadata_processor processor);
#ifdef __cplusplus
}

@ -44,16 +44,15 @@
/* --- grpc_process_auth_metadata_func --- */
static grpc_process_auth_metadata_func server_md_func = NULL;
static grpc_auth_metadata_processor server_processor = {NULL, NULL};
void grpc_server_auth_context_register_process_metadata_func(
grpc_process_auth_metadata_func func) {
server_md_func = func;
grpc_auth_metadata_processor grpc_server_get_auth_metadata_processor(void) {
return server_processor;
}
grpc_process_auth_metadata_func
grpc_server_auth_context_get_process_metadata_func(void) {
return server_md_func;
void grpc_server_register_auth_metadata_processor(
grpc_auth_metadata_processor processor) {
server_processor = processor;
}
/* --- grpc_call --- */

@ -106,8 +106,7 @@ void grpc_server_security_context_destroy(void *ctx);
/* --- Auth metadata processing. --- */
grpc_process_auth_metadata_func
grpc_server_auth_context_get_process_metadata_func(void);
grpc_auth_metadata_processor grpc_server_get_auth_metadata_processor(void);
#endif /* GRPC_INTERNAL_CORE_SECURITY_SECURITY_CONTEXT_H */

@ -42,16 +42,14 @@
typedef struct call_data {
gpr_uint8 got_client_metadata;
gpr_uint8 sent_status;
gpr_uint8 success;
grpc_linked_mdelem status;
grpc_stream_op_buffer *recv_ops;
/* Closure to call when finished with the hs_on_recv hook. */
/* Closure to call when finished with the auth_on_recv hook. */
grpc_iomgr_closure *on_done_recv;
/* Receive closures are chained: we inject this closure as the on_done_recv
up-call on transport_op, and remember to call our on_done_recv member after
handling it. */
grpc_iomgr_closure auth_on_recv;
grpc_transport_stream_op transport_op;
const grpc_metadata *consumed_md;
size_t num_consumed_md;
grpc_stream_op *md_op;
@ -61,7 +59,7 @@ typedef struct call_data {
typedef struct channel_data {
grpc_security_connector *security_connector;
grpc_mdelem *status_auth_failure;
grpc_mdctx *mdctx;
} channel_data;
static grpc_metadata_array metadata_batch_to_md_array(
@ -112,8 +110,8 @@ static void on_md_processing_done(void *user_data,
grpc_auth_context *result) {
grpc_call_element *elem = user_data;
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
calld->success = success;
if (success) {
calld->consumed_md = consumed_md;
calld->num_consumed_md = num_consumed_md;
@ -124,11 +122,14 @@ static void on_md_processing_done(void *user_data,
"releasing old context.");
*calld->call_auth_context =
GRPC_AUTH_CONTEXT_REF(result, "refing new context.");
calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success);
} else {
grpc_call_element_send_cancel(elem);
grpc_transport_stream_op_add_cancellation(
&calld->transport_op, GRPC_STATUS_UNAUTHENTICATED,
grpc_mdstr_from_string(chand->mdctx,
"Authentication metadata processing failed."));
grpc_call_next_op(elem, &calld->transport_op);
}
calld->on_done_recv->cb(calld->on_done_recv->cb_arg, success);
}
static void auth_on_recv(void *user_data, int success) {
@ -141,16 +142,18 @@ static void auth_on_recv(void *user_data, int success) {
grpc_stream_op *ops = calld->recv_ops->ops;
for (i = 0; i < nops; i++) {
grpc_metadata_array md_array;
grpc_process_auth_metadata_func processor =
grpc_server_auth_context_get_process_metadata_func();
grpc_auth_metadata_processor processor =
grpc_server_get_auth_metadata_processor();
grpc_stream_op *op = &ops[i];
if (op->type != GRPC_OP_METADATA) continue;
if (op->type != GRPC_OP_METADATA || calld->got_client_metadata) continue;
calld->got_client_metadata = 1;
if (processor == NULL) continue;
if (processor.process == NULL) continue;
calld->md_op = op;
md_array = metadata_batch_to_md_array(&op->data.metadata);
processor(&calld->ticket, chand->security_connector->auth_context,
md_array.metadata, md_array.count, on_md_processing_done, elem);
processor.process(processor.state, &calld->ticket,
chand->security_connector->auth_context,
md_array.metadata, md_array.count,
on_md_processing_done, elem);
grpc_metadata_array_destroy(&md_array);
return;
}
@ -161,28 +164,13 @@ static void auth_on_recv(void *user_data, int success) {
static void set_recv_ops_md_callbacks(grpc_call_element *elem,
grpc_transport_stream_op *op) {
call_data *calld = elem->call_data;
channel_data *chand = elem->channel_data;
if (op->send_ops && !calld->sent_status && !calld->success) {
size_t i;
size_t nops = op->send_ops->nops;
grpc_stream_op *ops = op->send_ops->ops;
for (i = 0; i < nops; i++) {
grpc_stream_op *op = &ops[i];
if (op->type != GRPC_OP_METADATA) continue;
calld->sent_status = 1;
grpc_metadata_batch_add_head(
&op->data.metadata, &calld->status,
GRPC_MDELEM_REF(chand->status_auth_failure));
break;
}
}
if (op->recv_ops && !calld->got_client_metadata) {
/* substitute our callback for the higher callback */
calld->recv_ops = op->recv_ops;
calld->on_done_recv = op->on_done_recv;
op->on_done_recv = &calld->auth_on_recv;
calld->transport_op = *op;
}
}
@ -209,7 +197,6 @@ static void init_call_elem(grpc_call_element *elem,
/* initialize members */
memset(calld, 0, sizeof(*calld));
grpc_iomgr_closure_init(&calld->auth_on_recv, auth_on_recv, elem);
calld->success = 1;
GPR_ASSERT(initial_op && initial_op->context != NULL &&
initial_op->context[GRPC_CONTEXT_SECURITY].value == NULL);
@ -260,8 +247,7 @@ static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
GPR_ASSERT(!sc->is_client_side);
chand->security_connector =
GRPC_SECURITY_CONNECTOR_REF(sc, "server_auth_filter");
chand->status_auth_failure =
grpc_mdelem_from_strings(mdctx, ":status", "401");
chand->mdctx = mdctx;
}
/* Destructor for channel data */
@ -270,7 +256,6 @@ static void destroy_channel_elem(grpc_channel_element *elem) {
channel_data *chand = elem->channel_data;
GRPC_SECURITY_CONNECTOR_UNREF(chand->security_connector,
"server_auth_filter");
GRPC_MDELEM_UNREF(chand->status_auth_failure);
}
const grpc_channel_filter grpc_server_auth_filter = {

@ -84,45 +84,51 @@ static void check_peer_identity(grpc_auth_context *ctx,
GPR_ASSERT(strcmp(expected_identity, prop->value) == 0);
GPR_ASSERT(grpc_auth_property_iterator_next(&it) == NULL);
}
static void process_auth_md_success(grpc_auth_ticket *t,
static void process_auth_md_success(void *state, grpc_auth_ticket *t,
grpc_auth_context *channel_ctx,
const grpc_metadata *md, size_t md_count,
grpc_process_auth_metadata_done_cb cb,
void *user_data) {
grpc_auth_context *new_auth_ctx = grpc_auth_context_create(channel_ctx);
const grpc_metadata *custom_creds_md =
find_metadata(md, md_count, custom_creds_md_name, custom_creds_md_value);
GPR_ASSERT(custom_creds_md != NULL);
grpc_auth_context_add_cstring_property(
new_auth_ctx, client_identity_property_name, client_identity);
GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
new_auth_ctx, client_identity_property_name) == 1);
cb(user_data, custom_creds_md, 1, 1, new_auth_ctx);
grpc_auth_context_release(new_auth_ctx);
override_mode *mode;
GPR_ASSERT(state != NULL);
mode = (override_mode *)state;
if (*mode != DESTROY) {
grpc_auth_context *new_auth_ctx = grpc_auth_context_create(channel_ctx);
const grpc_metadata *custom_creds_md = find_metadata(
md, md_count, custom_creds_md_name, custom_creds_md_value);
GPR_ASSERT(custom_creds_md != NULL);
grpc_auth_context_add_cstring_property(
new_auth_ctx, client_identity_property_name, client_identity);
GPR_ASSERT(grpc_auth_context_set_peer_identity_property_name(
new_auth_ctx, client_identity_property_name) == 1);
cb(user_data, custom_creds_md, 1, 1, new_auth_ctx);
grpc_auth_context_release(new_auth_ctx);
} else {
cb(user_data, NULL, 0, 1, channel_ctx);
}
}
#if 0
static void process_auth_md_failure(grpc_auth_ticket *t,
static void process_auth_md_failure(void *state, grpc_auth_ticket *t,
grpc_auth_context *channel_ctx,
const grpc_metadata *md, size_t md_count,
grpc_process_auth_metadata_done_cb cb,
void *user_data) {
const grpc_metadata *custom_creds_md =
find_metadata(md, md_count, custom_creds_md_name, custom_creds_md_value);
GPR_ASSERT(custom_creds_md != NULL);
override_mode *mode;
GPR_ASSERT(state != NULL);
mode = (override_mode *)state;
if (*mode != DESTROY) {
const grpc_metadata *custom_creds_md = find_metadata(
md, md_count, custom_creds_md_name, custom_creds_md_value);
GPR_ASSERT(custom_creds_md != NULL);
}
cb(user_data, NULL, 0, 0, NULL); /* Fail. */
}
#endif
static grpc_end2end_test_fixture begin_test(
grpc_end2end_test_config config, const char *test_name,
grpc_process_auth_metadata_func md_func, override_mode mode) {
grpc_auth_metadata_processor processor) {
grpc_end2end_test_fixture f;
if (mode != DESTROY) {
grpc_server_auth_context_register_process_metadata_func(md_func);
} else {
grpc_server_auth_context_register_process_metadata_func(NULL);
}
grpc_server_register_auth_metadata_processor(processor);
gpr_log(GPR_INFO, "%s/%s", test_name, config.name);
f = config.create_fixture(NULL, NULL);
config.init_client(&f, NULL);
@ -200,8 +206,9 @@ static grpc_credentials *iam_custom_composite_creds_create(
static void test_call_creds_failure(grpc_end2end_test_config config) {
grpc_call *c;
grpc_credentials *creds = NULL;
grpc_auth_metadata_processor p = {NULL, NULL};
grpc_end2end_test_fixture f =
begin_test(config, "test_call_creds_failure", NULL, NONE);
begin_test(config, "test_call_creds_failure", p);
gpr_timespec deadline = five_seconds_time();
c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr",
deadline);
@ -230,10 +237,9 @@ static void request_response_with_payload_and_call_creds(
grpc_byte_buffer *response_payload =
grpc_raw_byte_buffer_create(&response_payload_slice, 1);
gpr_timespec deadline = five_seconds_time();
grpc_end2end_test_fixture f =
begin_test(config, test_name, process_auth_md_success, mode);
cq_verifier *cqv = cq_verifier_create(f.cq);
grpc_auth_metadata_processor p;
grpc_end2end_test_fixture f;
cq_verifier *cqv;
grpc_op ops[6];
grpc_op *op;
grpc_metadata_array initial_metadata_recv;
@ -250,6 +256,11 @@ static void request_response_with_payload_and_call_creds(
grpc_auth_context *s_auth_context = NULL;
grpc_auth_context *c_auth_context = NULL;
p.process = process_auth_md_success;
p.state = &mode;
f = begin_test(config, test_name, p);
cqv = cq_verifier_create(f.cq);
c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr",
deadline);
GPR_ASSERT(c);
@ -446,54 +457,41 @@ static void test_request_response_with_payload_and_deleted_call_creds(
DESTROY);
}
static void test_request_with_bad_creds(void) {
#if 0
grpc_call *c;
grpc_call *s;
gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
grpc_byte_buffer *request_payload =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
gpr_timespec deadline = five_seconds_time();
grpc_end2end_test_fixture f =
begin_test(config, test_name, process_auth_md_failure, NONE);
cq_verifier *cqv = cq_verifier_create(f.cq);
static void test_request_with_server_rejecting_client_creds(
grpc_end2end_test_config config) {
grpc_op ops[6];
grpc_op *op;
grpc_call *c;
grpc_auth_metadata_processor p;
grpc_end2end_test_fixture f;
gpr_timespec deadline = five_seconds_time();
cq_verifier *cqv;
grpc_metadata_array initial_metadata_recv;
grpc_metadata_array trailing_metadata_recv;
grpc_metadata_array request_metadata_recv;
grpc_byte_buffer *request_payload_recv = NULL;
grpc_byte_buffer *response_payload_recv = NULL;
grpc_call_details call_details;
grpc_status_code status;
char *details = NULL;
size_t details_capacity = 0;
int was_cancelled = 2;
grpc_credentials *creds = NULL;
grpc_auth_context *s_auth_context = NULL;
grpc_auth_context *c_auth_context = NULL;
grpc_byte_buffer *response_payload_recv = NULL;
gpr_slice request_payload_slice = gpr_slice_from_copied_string("hello world");
grpc_byte_buffer *request_payload =
grpc_raw_byte_buffer_create(&request_payload_slice, 1);
override_mode mode = NONE;
grpc_credentials *creds;
p.process = process_auth_md_failure;
p.state = &mode;
f = begin_test(config, "test_request_with_server_rejecting_client_creds", p);
cqv = cq_verifier_create(f.cq);
c = grpc_channel_create_call(f.client, f.cq, "/foo", "foo.test.google.fr",
deadline);
GPR_ASSERT(c);
creds = iam_custom_composite_creds_create(iam_token, iam_selector);
GPR_ASSERT(creds != NULL);
GPR_ASSERT(grpc_call_set_credentials(c, creds) == GRPC_CALL_OK);
switch (mode) {
case NONE:
break;
case OVERRIDE:
grpc_credentials_release(creds);
creds = iam_custom_composite_creds_create(overridden_iam_token,
overridden_iam_selector);
GPR_ASSERT(creds != NULL);
GPR_ASSERT(grpc_call_set_credentials(c, creds) == GRPC_CALL_OK);
break;
case DESTROY:
GPR_ASSERT(grpc_call_set_credentials(c, NULL) == GRPC_CALL_OK);
break;
}
grpc_credentials_release(creds);
grpc_metadata_array_init(&initial_metadata_recv);
@ -502,6 +500,13 @@ static void test_request_with_bad_creds(void) {
grpc_call_details_init(&call_details);
op = ops;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
op->data.recv_status_on_client.status = &status;
op->data.recv_status_on_client.status_details = &details;
op->data.recv_status_on_client.status_details_capacity = &details_capacity;
op->flags = 0;
op++;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
@ -521,134 +526,31 @@ static void test_request_with_bad_creds(void) {
op->data.recv_message = &response_payload_recv;
op->flags = 0;
op++;
op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
op->data.recv_status_on_client.trailing_metadata = &trailing_metadata_recv;
op->data.recv_status_on_client.status = &status;
op->data.recv_status_on_client.status_details = &details;
op->data.recv_status_on_client.status_details_capacity = &details_capacity;
op->flags = 0;
op++;
GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(c, ops, op - ops, tag(1)));
GPR_ASSERT(GRPC_CALL_OK == grpc_server_request_call(
f.server, &s, &call_details,
&request_metadata_recv, f.cq, f.cq, tag(101)));
cq_expect_completion(cqv, tag(101), 1);
cq_verify(cqv);
s_auth_context = grpc_call_auth_context(s);
GPR_ASSERT(s_auth_context != NULL);
print_auth_context(0, s_auth_context);
grpc_auth_context_release(s_auth_context);
c_auth_context = grpc_call_auth_context(c);
GPR_ASSERT(c_auth_context != NULL);
print_auth_context(1, c_auth_context);
grpc_auth_context_release(c_auth_context);
/* Cannot set creds on the server call object. */
GPR_ASSERT(grpc_call_set_credentials(s, NULL) != GRPC_CALL_OK);
op = ops;
op->op = GRPC_OP_SEND_INITIAL_METADATA;
op->data.send_initial_metadata.count = 0;
op->flags = 0;
op++;
op->op = GRPC_OP_RECV_MESSAGE;
op->data.recv_message = &request_payload_recv;
op->flags = 0;
op++;
GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(102)));
cq_expect_completion(cqv, tag(102), 1);
cq_verify(cqv);
op = ops;
op->op = GRPC_OP_RECV_CLOSE_ON_SERVER;
op->data.recv_close_on_server.cancelled = &was_cancelled;
op->flags = 0;
op++;
op->op = GRPC_OP_SEND_MESSAGE;
op->data.send_message = response_payload;
op->flags = 0;
op++;
op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
op->data.send_status_from_server.trailing_metadata_count = 0;
op->data.send_status_from_server.status = GRPC_STATUS_OK;
op->data.send_status_from_server.status_details = "xyz";
op->flags = 0;
op++;
GPR_ASSERT(GRPC_CALL_OK == grpc_call_start_batch(s, ops, op - ops, tag(103)));
cq_expect_completion(cqv, tag(103), 1);
cq_expect_completion(cqv, tag(1), 1);
cq_verify(cqv);
GPR_ASSERT(status == GRPC_STATUS_OK);
GPR_ASSERT(0 == strcmp(details, "xyz"));
GPR_ASSERT(0 == strcmp(call_details.method, "/foo"));
GPR_ASSERT(0 == strcmp(call_details.host, "foo.test.google.fr"));
GPR_ASSERT(was_cancelled == 0);
GPR_ASSERT(byte_buffer_eq_string(request_payload_recv, "hello world"));
GPR_ASSERT(byte_buffer_eq_string(response_payload_recv, "hello you"));
/* Has been processed by the auth metadata processor. */
GPR_ASSERT(!contains_metadata(&request_metadata_recv, custom_creds_md_name,
custom_creds_md_value));
switch (mode) {
case NONE:
GPR_ASSERT(contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY,
iam_token));
GPR_ASSERT(contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY,
iam_selector));
check_peer_identity(s_auth_context, client_identity);
break;
case OVERRIDE:
GPR_ASSERT(contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY,
overridden_iam_token));
GPR_ASSERT(contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY,
overridden_iam_selector));
check_peer_identity(s_auth_context, client_identity);
break;
case DESTROY:
GPR_ASSERT(!contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY,
iam_token));
GPR_ASSERT(!contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY,
iam_selector));
GPR_ASSERT(!contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORIZATION_TOKEN_METADATA_KEY,
overridden_iam_token));
GPR_ASSERT(!contains_metadata(&request_metadata_recv,
GRPC_IAM_AUTHORITY_SELECTOR_METADATA_KEY,
overridden_iam_selector));
break;
}
/* XXX Should be GRPC_STATUS_UNAUTHENTICATED but it looks like there is a bug
(probably in the server_auth_context.c code) where this error on the server
does not get to the client. The current error code we are getting is
GRPC_STATUS_INTERNAL. */
GPR_ASSERT(status != GRPC_STATUS_OK);
gpr_free(details);
grpc_metadata_array_destroy(&initial_metadata_recv);
grpc_metadata_array_destroy(&trailing_metadata_recv);
grpc_metadata_array_destroy(&request_metadata_recv);
grpc_call_details_destroy(&call_details);
grpc_call_destroy(c);
grpc_call_destroy(s);
cq_verifier_destroy(cqv);
grpc_byte_buffer_destroy(request_payload);
grpc_byte_buffer_destroy(response_payload);
grpc_byte_buffer_destroy(request_payload_recv);
grpc_byte_buffer_destroy(response_payload_recv);
gpr_free(details);
grpc_call_destroy(c);
cq_verifier_destroy(cqv);
end_test(&f);
config.tear_down_data(&f);
#endif
}
void grpc_end2end_tests(grpc_end2end_test_config config) {
@ -657,6 +559,6 @@ void grpc_end2end_tests(grpc_end2end_test_config config) {
test_request_response_with_payload_and_call_creds(config);
test_request_response_with_payload_and_overridden_call_creds(config);
test_request_response_with_payload_and_deleted_call_creds(config);
test_request_with_bad_creds();
test_request_with_server_rejecting_client_creds(config);
}
}

Loading…
Cancel
Save