3 * Copyright 2015 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <grpc/support/port_platform.h>
23 #include <grpc/support/alloc.h>
24 #include <grpc/support/log.h>
26 #include "src/core/lib/security/context/security_context.h"
27 #include "src/core/lib/security/credentials/credentials.h"
28 #include "src/core/lib/security/transport/auth_filters.h"
29 #include "src/core/lib/slice/slice_internal.h"
31 static void recv_initial_metadata_ready(void* arg, grpc_error* error);
32 static void recv_trailing_metadata_ready(void* user_data, grpc_error* error);
42 channel_data(grpc_auth_context* auth_context, grpc_server_credentials* creds)
43 : auth_context(auth_context->Ref()), creds(creds->Ref()) {}
44 ~channel_data() { auth_context.reset(DEBUG_LOCATION, "server_auth_filter"); }
46 grpc_core::RefCountedPtr<grpc_auth_context> auth_context;
47 grpc_core::RefCountedPtr<grpc_server_credentials> creds;
51 call_data(grpc_call_element* elem, const grpc_call_element_args& args)
52 : call_combiner(args.call_combiner), owning_call(args.call_stack) {
53 GRPC_CLOSURE_INIT(&recv_initial_metadata_ready,
54 ::recv_initial_metadata_ready, elem,
55 grpc_schedule_on_exec_ctx);
56 GRPC_CLOSURE_INIT(&recv_trailing_metadata_ready,
57 ::recv_trailing_metadata_ready, elem,
58 grpc_schedule_on_exec_ctx);
59 // Create server security context. Set its auth context from channel
60 // data and save it in the call context.
61 grpc_server_security_context* server_ctx =
62 grpc_server_security_context_create(args.arena);
63 channel_data* chand = static_cast<channel_data*>(elem->channel_data);
64 server_ctx->auth_context =
65 chand->auth_context->Ref(DEBUG_LOCATION, "server_auth_filter");
66 if (args.context[GRPC_CONTEXT_SECURITY].value != nullptr) {
67 args.context[GRPC_CONTEXT_SECURITY].destroy(
68 args.context[GRPC_CONTEXT_SECURITY].value);
70 args.context[GRPC_CONTEXT_SECURITY].value = server_ctx;
71 args.context[GRPC_CONTEXT_SECURITY].destroy =
72 grpc_server_security_context_destroy;
75 ~call_data() { GRPC_ERROR_UNREF(recv_initial_metadata_error); }
77 grpc_core::CallCombiner* call_combiner;
78 grpc_call_stack* owning_call;
79 grpc_transport_stream_op_batch* recv_initial_metadata_batch;
80 grpc_closure* original_recv_initial_metadata_ready;
81 grpc_closure recv_initial_metadata_ready;
82 grpc_error* recv_initial_metadata_error = GRPC_ERROR_NONE;
83 grpc_closure recv_trailing_metadata_ready;
84 grpc_closure* original_recv_trailing_metadata_ready;
85 grpc_error* recv_trailing_metadata_error;
86 bool seen_recv_trailing_metadata_ready = false;
87 grpc_metadata_array md;
88 const grpc_metadata* consumed_md;
89 size_t num_consumed_md;
90 grpc_closure cancel_closure;
91 gpr_atm state = STATE_INIT; // async_state
96 static grpc_metadata_array metadata_batch_to_md_array(
97 const grpc_metadata_batch* batch) {
98 grpc_linked_mdelem* l;
99 grpc_metadata_array result;
100 grpc_metadata_array_init(&result);
101 for (l = batch->list.head; l != nullptr; l = l->next) {
102 grpc_metadata* usr_md = nullptr;
103 grpc_mdelem md = l->md;
104 grpc_slice key = GRPC_MDKEY(md);
105 grpc_slice value = GRPC_MDVALUE(md);
106 if (result.count == result.capacity) {
107 result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2);
108 result.metadata = static_cast<grpc_metadata*>(gpr_realloc(
109 result.metadata, result.capacity * sizeof(grpc_metadata)));
111 usr_md = &result.metadata[result.count++];
112 usr_md->key = grpc_slice_ref_internal(key);
113 usr_md->value = grpc_slice_ref_internal(value);
118 static grpc_filtered_mdelem remove_consumed_md(void* user_data,
120 grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
121 call_data* calld = static_cast<call_data*>(elem->call_data);
123 for (i = 0; i < calld->num_consumed_md; i++) {
124 const grpc_metadata* consumed_md = &calld->consumed_md[i];
125 if (grpc_slice_eq(GRPC_MDKEY(md), consumed_md->key) &&
126 grpc_slice_eq(GRPC_MDVALUE(md), consumed_md->value))
127 return GRPC_FILTERED_REMOVE();
129 return GRPC_FILTERED_MDELEM(md);
132 static void on_md_processing_done_inner(grpc_call_element* elem,
133 const grpc_metadata* consumed_md,
134 size_t num_consumed_md,
135 const grpc_metadata* response_md,
136 size_t num_response_md,
138 call_data* calld = static_cast<call_data*>(elem->call_data);
139 grpc_transport_stream_op_batch* batch = calld->recv_initial_metadata_batch;
140 /* TODO(jboeuf): Implement support for response_md. */
141 if (response_md != nullptr && num_response_md > 0) {
143 "response_md in auth metadata processing not supported for now. "
146 if (error == GRPC_ERROR_NONE) {
147 calld->consumed_md = consumed_md;
148 calld->num_consumed_md = num_consumed_md;
149 error = grpc_metadata_batch_filter(
150 batch->payload->recv_initial_metadata.recv_initial_metadata,
151 remove_consumed_md, elem, "Response metadata filtering error");
153 calld->recv_initial_metadata_error = GRPC_ERROR_REF(error);
154 grpc_closure* closure = calld->original_recv_initial_metadata_ready;
155 calld->original_recv_initial_metadata_ready = nullptr;
156 if (calld->seen_recv_trailing_metadata_ready) {
157 GRPC_CALL_COMBINER_START(calld->call_combiner,
158 &calld->recv_trailing_metadata_ready,
159 calld->recv_trailing_metadata_error,
160 "continue recv_trailing_metadata_ready");
162 GRPC_CLOSURE_SCHED(closure, error);
165 // Called from application code.
166 static void on_md_processing_done(
167 void* user_data, const grpc_metadata* consumed_md, size_t num_consumed_md,
168 const grpc_metadata* response_md, size_t num_response_md,
169 grpc_status_code status, const char* error_details) {
170 grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
171 call_data* calld = static_cast<call_data*>(elem->call_data);
172 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
173 grpc_core::ExecCtx exec_ctx;
174 // If the call was not cancelled while we were in flight, process the result.
175 if (gpr_atm_full_cas(&calld->state, static_cast<gpr_atm>(STATE_INIT),
176 static_cast<gpr_atm>(STATE_DONE))) {
177 grpc_error* error = GRPC_ERROR_NONE;
178 if (status != GRPC_STATUS_OK) {
179 if (error_details == nullptr) {
180 error_details = "Authentication metadata processing failed.";
182 error = grpc_error_set_int(
183 GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_details),
184 GRPC_ERROR_INT_GRPC_STATUS, status);
186 on_md_processing_done_inner(elem, consumed_md, num_consumed_md, response_md,
187 num_response_md, error);
190 for (size_t i = 0; i < calld->md.count; i++) {
191 grpc_slice_unref_internal(calld->md.metadata[i].key);
192 grpc_slice_unref_internal(calld->md.metadata[i].value);
194 grpc_metadata_array_destroy(&calld->md);
195 GRPC_CALL_STACK_UNREF(calld->owning_call, "server_auth_metadata");
198 static void cancel_call(void* arg, grpc_error* error) {
199 grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
200 call_data* calld = static_cast<call_data*>(elem->call_data);
201 // If the result was not already processed, invoke the callback now.
202 if (error != GRPC_ERROR_NONE &&
203 gpr_atm_full_cas(&calld->state, static_cast<gpr_atm>(STATE_INIT),
204 static_cast<gpr_atm>(STATE_CANCELLED))) {
205 on_md_processing_done_inner(elem, nullptr, 0, nullptr, 0,
206 GRPC_ERROR_REF(error));
210 static void recv_initial_metadata_ready(void* arg, grpc_error* error) {
211 grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
212 channel_data* chand = static_cast<channel_data*>(elem->channel_data);
213 call_data* calld = static_cast<call_data*>(elem->call_data);
214 grpc_transport_stream_op_batch* batch = calld->recv_initial_metadata_batch;
215 if (error == GRPC_ERROR_NONE) {
216 if (chand->creds != nullptr &&
217 chand->creds->auth_metadata_processor().process != nullptr) {
218 // We're calling out to the application, so we need to make sure
219 // to drop the call combiner early if we get cancelled.
220 GRPC_CLOSURE_INIT(&calld->cancel_closure, cancel_call, elem,
221 grpc_schedule_on_exec_ctx);
222 calld->call_combiner->SetNotifyOnCancel(&calld->cancel_closure);
223 GRPC_CALL_STACK_REF(calld->owning_call, "server_auth_metadata");
224 calld->md = metadata_batch_to_md_array(
225 batch->payload->recv_initial_metadata.recv_initial_metadata);
226 chand->creds->auth_metadata_processor().process(
227 chand->creds->auth_metadata_processor().state,
228 chand->auth_context.get(), calld->md.metadata, calld->md.count,
229 on_md_processing_done, elem);
233 grpc_closure* closure = calld->original_recv_initial_metadata_ready;
234 calld->original_recv_initial_metadata_ready = nullptr;
235 if (calld->seen_recv_trailing_metadata_ready) {
236 GRPC_CALL_COMBINER_START(calld->call_combiner,
237 &calld->recv_trailing_metadata_ready,
238 calld->recv_trailing_metadata_error,
239 "continue recv_trailing_metadata_ready");
241 GRPC_CLOSURE_RUN(closure, GRPC_ERROR_REF(error));
244 static void recv_trailing_metadata_ready(void* user_data, grpc_error* err) {
245 grpc_call_element* elem = static_cast<grpc_call_element*>(user_data);
246 call_data* calld = static_cast<call_data*>(elem->call_data);
247 if (calld->original_recv_initial_metadata_ready != nullptr) {
248 calld->recv_trailing_metadata_error = GRPC_ERROR_REF(err);
249 calld->seen_recv_trailing_metadata_ready = true;
250 GRPC_CALL_COMBINER_STOP(calld->call_combiner,
251 "deferring recv_trailing_metadata_ready until "
252 "after recv_initial_metadata_ready");
255 err = grpc_error_add_child(
256 GRPC_ERROR_REF(err), GRPC_ERROR_REF(calld->recv_initial_metadata_error));
257 GRPC_CLOSURE_RUN(calld->original_recv_trailing_metadata_ready, err);
260 static void auth_start_transport_stream_op_batch(
261 grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
262 call_data* calld = static_cast<call_data*>(elem->call_data);
263 if (batch->recv_initial_metadata) {
264 // Inject our callback.
265 calld->recv_initial_metadata_batch = batch;
266 calld->original_recv_initial_metadata_ready =
267 batch->payload->recv_initial_metadata.recv_initial_metadata_ready;
268 batch->payload->recv_initial_metadata.recv_initial_metadata_ready =
269 &calld->recv_initial_metadata_ready;
271 if (batch->recv_trailing_metadata) {
272 calld->original_recv_trailing_metadata_ready =
273 batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready;
274 batch->payload->recv_trailing_metadata.recv_trailing_metadata_ready =
275 &calld->recv_trailing_metadata_ready;
277 grpc_call_next_op(elem, batch);
280 /* Constructor for call_data */
281 static grpc_error* init_call_elem(grpc_call_element* elem,
282 const grpc_call_element_args* args) {
283 new (elem->call_data) call_data(elem, *args);
284 return GRPC_ERROR_NONE;
287 /* Destructor for call_data */
288 static void destroy_call_elem(grpc_call_element* elem,
289 const grpc_call_final_info* final_info,
290 grpc_closure* ignored) {
291 call_data* calld = static_cast<call_data*>(elem->call_data);
295 /* Constructor for channel_data */
296 static grpc_error* init_channel_elem(grpc_channel_element* elem,
297 grpc_channel_element_args* args) {
298 GPR_ASSERT(!args->is_last);
299 grpc_auth_context* auth_context =
300 grpc_find_auth_context_in_args(args->channel_args);
301 GPR_ASSERT(auth_context != nullptr);
302 grpc_server_credentials* creds =
303 grpc_find_server_credentials_in_args(args->channel_args);
304 new (elem->channel_data) channel_data(auth_context, creds);
305 return GRPC_ERROR_NONE;
308 /* Destructor for channel data */
309 static void destroy_channel_elem(grpc_channel_element* elem) {
310 channel_data* chand = static_cast<channel_data*>(elem->channel_data);
311 chand->~channel_data();
314 const grpc_channel_filter grpc_server_auth_filter = {
315 auth_start_transport_stream_op_batch,
316 grpc_channel_next_op,
319 grpc_call_stack_ignore_set_pollset_or_pollset_set,
321 sizeof(channel_data),
323 destroy_channel_elem,
324 grpc_channel_next_get_info,