Built motion from commit 6a09e18b.|2.6.11
[motion2.git] / legacy-libs / grpc / deps / grpc / src / core / lib / security / transport / client_auth_filter.cc
1 /*
2  *
3  * Copyright 2015 gRPC authors.
4  *
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
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
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.
16  *
17  */
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/security/transport/auth_filters.h"
22
23 #include <string.h>
24
25 #include <grpc/support/alloc.h>
26 #include <grpc/support/log.h>
27 #include <grpc/support/string_util.h>
28
29 #include "src/core/lib/channel/channel_stack.h"
30 #include "src/core/lib/gpr/string.h"
31 #include "src/core/lib/profiling/timers.h"
32 #include "src/core/lib/security/context/security_context.h"
33 #include "src/core/lib/security/credentials/credentials.h"
34 #include "src/core/lib/security/security_connector/security_connector.h"
35 #include "src/core/lib/security/security_connector/ssl_utils.h"
36 #include "src/core/lib/slice/slice_internal.h"
37 #include "src/core/lib/slice/slice_string_helpers.h"
38 #include "src/core/lib/surface/call.h"
39 #include "src/core/lib/transport/static_metadata.h"
40
41 #define MAX_CREDENTIALS_METADATA_COUNT 4
42
43 namespace {
44
45 /* We can have a per-channel credentials. */
46 struct channel_data {
47   channel_data(grpc_channel_security_connector* security_connector,
48                grpc_auth_context* auth_context)
49       : security_connector(
50             security_connector->Ref(DEBUG_LOCATION, "client_auth_filter")),
51         auth_context(auth_context->Ref(DEBUG_LOCATION, "client_auth_filter")) {}
52   ~channel_data() {
53     security_connector.reset(DEBUG_LOCATION, "client_auth_filter");
54     auth_context.reset(DEBUG_LOCATION, "client_auth_filter");
55   }
56
57   grpc_core::RefCountedPtr<grpc_channel_security_connector> security_connector;
58   grpc_core::RefCountedPtr<grpc_auth_context> auth_context;
59 };
60
61 /* We can have a per-call credentials. */
62 struct call_data {
63   call_data(grpc_call_element* elem, const grpc_call_element_args& args)
64       : owning_call(args.call_stack), call_combiner(args.call_combiner) {
65     channel_data* chand = static_cast<channel_data*>(elem->channel_data);
66     GPR_ASSERT(args.context != nullptr);
67     if (args.context[GRPC_CONTEXT_SECURITY].value == nullptr) {
68       args.context[GRPC_CONTEXT_SECURITY].value =
69           grpc_client_security_context_create(args.arena, /*creds=*/nullptr);
70       args.context[GRPC_CONTEXT_SECURITY].destroy =
71           grpc_client_security_context_destroy;
72     }
73     grpc_client_security_context* sec_ctx =
74         static_cast<grpc_client_security_context*>(
75             args.context[GRPC_CONTEXT_SECURITY].value);
76     sec_ctx->auth_context.reset(DEBUG_LOCATION, "client_auth_filter");
77     sec_ctx->auth_context =
78         chand->auth_context->Ref(DEBUG_LOCATION, "client_auth_filter");
79   }
80
81   // This method is technically the dtor of this class. However, since
82   // `get_request_metadata_cancel_closure` can run in parallel to
83   // `destroy_call_elem`, we cannot call the dtor in them. Otherwise,
84   // fields will be accessed after calling dtor, and msan correctly complains
85   // that the memory is not initialized.
86   void destroy() {
87     grpc_credentials_mdelem_array_destroy(&md_array);
88     creds.reset();
89     grpc_slice_unref_internal(host);
90     grpc_slice_unref_internal(method);
91     grpc_auth_metadata_context_reset(&auth_md_context);
92   }
93
94   grpc_call_stack* owning_call;
95   grpc_core::CallCombiner* call_combiner;
96   grpc_core::RefCountedPtr<grpc_call_credentials> creds;
97   grpc_slice host = grpc_empty_slice();
98   grpc_slice method = grpc_empty_slice();
99   /* pollset{_set} bound to this call; if we need to make external
100      network requests, they should be done under a pollset added to this
101      pollset_set so that work can progress when this call wants work to progress
102   */
103   grpc_polling_entity* pollent = nullptr;
104   grpc_credentials_mdelem_array md_array;
105   grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT] = {};
106   grpc_auth_metadata_context auth_md_context =
107       grpc_auth_metadata_context();  // Zero-initialize the C struct.
108   grpc_closure async_result_closure;
109   grpc_closure check_call_host_cancel_closure;
110   grpc_closure get_request_metadata_cancel_closure;
111 };
112
113 }  // namespace
114
115 void grpc_auth_metadata_context_copy(grpc_auth_metadata_context* from,
116                                      grpc_auth_metadata_context* to) {
117   grpc_auth_metadata_context_reset(to);
118   to->channel_auth_context = from->channel_auth_context;
119   if (to->channel_auth_context != nullptr) {
120     const_cast<grpc_auth_context*>(to->channel_auth_context)
121         ->Ref(DEBUG_LOCATION, "grpc_auth_metadata_context_copy")
122         .release();
123   }
124   to->service_url = gpr_strdup(from->service_url);
125   to->method_name = gpr_strdup(from->method_name);
126 }
127
128 void grpc_auth_metadata_context_reset(
129     grpc_auth_metadata_context* auth_md_context) {
130   if (auth_md_context->service_url != nullptr) {
131     gpr_free(const_cast<char*>(auth_md_context->service_url));
132     auth_md_context->service_url = nullptr;
133   }
134   if (auth_md_context->method_name != nullptr) {
135     gpr_free(const_cast<char*>(auth_md_context->method_name));
136     auth_md_context->method_name = nullptr;
137   }
138   if (auth_md_context->channel_auth_context != nullptr) {
139     const_cast<grpc_auth_context*>(auth_md_context->channel_auth_context)
140         ->Unref(DEBUG_LOCATION, "grpc_auth_metadata_context");
141     auth_md_context->channel_auth_context = nullptr;
142   }
143 }
144
145 static void add_error(grpc_error** combined, grpc_error* error) {
146   if (error == GRPC_ERROR_NONE) return;
147   if (*combined == GRPC_ERROR_NONE) {
148     *combined = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
149         "Client auth metadata plugin error");
150   }
151   *combined = grpc_error_add_child(*combined, error);
152 }
153
154 static void on_credentials_metadata(void* arg, grpc_error* input_error) {
155   grpc_transport_stream_op_batch* batch =
156       static_cast<grpc_transport_stream_op_batch*>(arg);
157   grpc_call_element* elem =
158       static_cast<grpc_call_element*>(batch->handler_private.extra_arg);
159   call_data* calld = static_cast<call_data*>(elem->call_data);
160   grpc_auth_metadata_context_reset(&calld->auth_md_context);
161   grpc_error* error = GRPC_ERROR_REF(input_error);
162   if (error == GRPC_ERROR_NONE) {
163     GPR_ASSERT(calld->md_array.size <= MAX_CREDENTIALS_METADATA_COUNT);
164     GPR_ASSERT(batch->send_initial_metadata);
165     grpc_metadata_batch* mdb =
166         batch->payload->send_initial_metadata.send_initial_metadata;
167     for (size_t i = 0; i < calld->md_array.size; ++i) {
168       add_error(&error, grpc_metadata_batch_add_tail(
169                             mdb, &calld->md_links[i],
170                             GRPC_MDELEM_REF(calld->md_array.md[i])));
171     }
172   }
173   if (error == GRPC_ERROR_NONE) {
174     grpc_call_next_op(elem, batch);
175   } else {
176     error = grpc_error_set_int(error, GRPC_ERROR_INT_GRPC_STATUS,
177                                GRPC_STATUS_UNAVAILABLE);
178     grpc_transport_stream_op_batch_finish_with_failure(batch, error,
179                                                        calld->call_combiner);
180   }
181   GRPC_CALL_STACK_UNREF(calld->owning_call, "get_request_metadata");
182 }
183
184 void grpc_auth_metadata_context_build(
185     const char* url_scheme, const grpc_slice& call_host,
186     const grpc_slice& call_method, grpc_auth_context* auth_context,
187     grpc_auth_metadata_context* auth_md_context) {
188   char* service = grpc_slice_to_c_string(call_method);
189   char* last_slash = strrchr(service, '/');
190   char* method_name = nullptr;
191   char* service_url = nullptr;
192   grpc_auth_metadata_context_reset(auth_md_context);
193   if (last_slash == nullptr) {
194     gpr_log(GPR_ERROR, "No '/' found in fully qualified method name");
195     service[0] = '\0';
196     method_name = gpr_strdup("");
197   } else if (last_slash == service) {
198     method_name = gpr_strdup("");
199   } else {
200     *last_slash = '\0';
201     method_name = gpr_strdup(last_slash + 1);
202   }
203   char* host_and_port = grpc_slice_to_c_string(call_host);
204   if (url_scheme != nullptr && strcmp(url_scheme, GRPC_SSL_URL_SCHEME) == 0) {
205     /* Remove the port if it is 443. */
206     char* port_delimiter = strrchr(host_and_port, ':');
207     if (port_delimiter != nullptr && strcmp(port_delimiter + 1, "443") == 0) {
208       *port_delimiter = '\0';
209     }
210   }
211   gpr_asprintf(&service_url, "%s://%s%s",
212                url_scheme == nullptr ? "" : url_scheme, host_and_port, service);
213   auth_md_context->service_url = service_url;
214   auth_md_context->method_name = method_name;
215   auth_md_context->channel_auth_context =
216       auth_context == nullptr
217           ? nullptr
218           : auth_context->Ref(DEBUG_LOCATION, "grpc_auth_metadata_context")
219                 .release();
220   gpr_free(service);
221   gpr_free(host_and_port);
222 }
223
224 static void cancel_get_request_metadata(void* arg, grpc_error* error) {
225   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
226   call_data* calld = static_cast<call_data*>(elem->call_data);
227   if (error != GRPC_ERROR_NONE) {
228     calld->creds->cancel_get_request_metadata(&calld->md_array,
229                                               GRPC_ERROR_REF(error));
230   }
231 }
232
233 static void send_security_metadata(grpc_call_element* elem,
234                                    grpc_transport_stream_op_batch* batch) {
235   call_data* calld = static_cast<call_data*>(elem->call_data);
236   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
237   grpc_client_security_context* ctx =
238       static_cast<grpc_client_security_context*>(
239           batch->payload->context[GRPC_CONTEXT_SECURITY].value);
240   grpc_call_credentials* channel_call_creds =
241       chand->security_connector->mutable_request_metadata_creds();
242   int call_creds_has_md = (ctx != nullptr) && (ctx->creds != nullptr);
243
244   if (channel_call_creds == nullptr && !call_creds_has_md) {
245     /* Skip sending metadata altogether. */
246     grpc_call_next_op(elem, batch);
247     return;
248   }
249
250   if (channel_call_creds != nullptr && call_creds_has_md) {
251     calld->creds = grpc_core::RefCountedPtr<grpc_call_credentials>(
252         grpc_composite_call_credentials_create(channel_call_creds,
253                                                ctx->creds.get(), nullptr));
254     if (calld->creds == nullptr) {
255       grpc_transport_stream_op_batch_finish_with_failure(
256           batch,
257           grpc_error_set_int(
258               GRPC_ERROR_CREATE_FROM_STATIC_STRING(
259                   "Incompatible credentials set on channel and call."),
260               GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAUTHENTICATED),
261           calld->call_combiner);
262       return;
263     }
264   } else {
265     calld->creds =
266         call_creds_has_md ? ctx->creds->Ref() : channel_call_creds->Ref();
267   }
268
269   grpc_auth_metadata_context_build(
270       chand->security_connector->url_scheme(), calld->host, calld->method,
271       chand->auth_context.get(), &calld->auth_md_context);
272
273   GPR_ASSERT(calld->pollent != nullptr);
274   GRPC_CALL_STACK_REF(calld->owning_call, "get_request_metadata");
275   GRPC_CLOSURE_INIT(&calld->async_result_closure, on_credentials_metadata,
276                     batch, grpc_schedule_on_exec_ctx);
277   grpc_error* error = GRPC_ERROR_NONE;
278   if (calld->creds->get_request_metadata(
279           calld->pollent, calld->auth_md_context, &calld->md_array,
280           &calld->async_result_closure, &error)) {
281     // Synchronous return; invoke on_credentials_metadata() directly.
282     on_credentials_metadata(batch, error);
283     GRPC_ERROR_UNREF(error);
284   } else {
285     // Async return; register cancellation closure with call combiner.
286     calld->call_combiner->SetNotifyOnCancel(GRPC_CLOSURE_INIT(
287         &calld->get_request_metadata_cancel_closure,
288         cancel_get_request_metadata, elem, grpc_schedule_on_exec_ctx));
289   }
290 }
291
292 static void on_host_checked(void* arg, grpc_error* error) {
293   grpc_transport_stream_op_batch* batch =
294       static_cast<grpc_transport_stream_op_batch*>(arg);
295   grpc_call_element* elem =
296       static_cast<grpc_call_element*>(batch->handler_private.extra_arg);
297   call_data* calld = static_cast<call_data*>(elem->call_data);
298   if (error == GRPC_ERROR_NONE) {
299     send_security_metadata(elem, batch);
300   } else {
301     char* error_msg;
302     char* host = grpc_slice_to_c_string(calld->host);
303     gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
304                  host);
305     gpr_free(host);
306     grpc_transport_stream_op_batch_finish_with_failure(
307         batch,
308         grpc_error_set_int(GRPC_ERROR_CREATE_FROM_COPIED_STRING(error_msg),
309                            GRPC_ERROR_INT_GRPC_STATUS,
310                            GRPC_STATUS_UNAUTHENTICATED),
311         calld->call_combiner);
312     gpr_free(error_msg);
313   }
314   GRPC_CALL_STACK_UNREF(calld->owning_call, "check_call_host");
315 }
316
317 static void cancel_check_call_host(void* arg, grpc_error* error) {
318   grpc_call_element* elem = static_cast<grpc_call_element*>(arg);
319   call_data* calld = static_cast<call_data*>(elem->call_data);
320   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
321   if (error != GRPC_ERROR_NONE) {
322     chand->security_connector->cancel_check_call_host(
323         &calld->async_result_closure, GRPC_ERROR_REF(error));
324   }
325 }
326
327 static void auth_start_transport_stream_op_batch(
328     grpc_call_element* elem, grpc_transport_stream_op_batch* batch) {
329   GPR_TIMER_SCOPE("auth_start_transport_stream_op_batch", 0);
330
331   /* grab pointers to our data from the call element */
332   call_data* calld = static_cast<call_data*>(elem->call_data);
333   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
334
335   if (batch->send_initial_metadata) {
336     grpc_metadata_batch* metadata =
337         batch->payload->send_initial_metadata.send_initial_metadata;
338     if (metadata->idx.named.path != nullptr) {
339       calld->method =
340           grpc_slice_ref_internal(GRPC_MDVALUE(metadata->idx.named.path->md));
341     }
342     if (metadata->idx.named.authority != nullptr) {
343       calld->host = grpc_slice_ref_internal(
344           GRPC_MDVALUE(metadata->idx.named.authority->md));
345       batch->handler_private.extra_arg = elem;
346       GRPC_CALL_STACK_REF(calld->owning_call, "check_call_host");
347       GRPC_CLOSURE_INIT(&calld->async_result_closure, on_host_checked, batch,
348                         grpc_schedule_on_exec_ctx);
349       grpc_core::StringView call_host(calld->host);
350       grpc_error* error = GRPC_ERROR_NONE;
351       if (chand->security_connector->check_call_host(
352               call_host, chand->auth_context.get(),
353               &calld->async_result_closure, &error)) {
354         // Synchronous return; invoke on_host_checked() directly.
355         on_host_checked(batch, error);
356         GRPC_ERROR_UNREF(error);
357       } else {
358         // Async return; register cancellation closure with call combiner.
359         calld->call_combiner->SetNotifyOnCancel(GRPC_CLOSURE_INIT(
360             &calld->check_call_host_cancel_closure, cancel_check_call_host,
361             elem, grpc_schedule_on_exec_ctx));
362       }
363       return; /* early exit */
364     }
365   }
366
367   /* pass control down the stack */
368   grpc_call_next_op(elem, batch);
369 }
370
371 /* Constructor for call_data */
372 static grpc_error* init_call_elem(grpc_call_element* elem,
373                                   const grpc_call_element_args* args) {
374   new (elem->call_data) call_data(elem, *args);
375   return GRPC_ERROR_NONE;
376 }
377
378 static void set_pollset_or_pollset_set(grpc_call_element* elem,
379                                        grpc_polling_entity* pollent) {
380   call_data* calld = static_cast<call_data*>(elem->call_data);
381   calld->pollent = pollent;
382 }
383
384 /* Destructor for call_data */
385 static void destroy_call_elem(grpc_call_element* elem,
386                               const grpc_call_final_info* final_info,
387                               grpc_closure* ignored) {
388   call_data* calld = static_cast<call_data*>(elem->call_data);
389   calld->destroy();
390 }
391
392 /* Constructor for channel_data */
393 static grpc_error* init_channel_elem(grpc_channel_element* elem,
394                                      grpc_channel_element_args* args) {
395   /* The first and the last filters tend to be implemented differently to
396      handle the case that there's no 'next' filter to call on the up or down
397      path */
398   GPR_ASSERT(!args->is_last);
399   grpc_security_connector* sc =
400       grpc_security_connector_find_in_args(args->channel_args);
401   if (sc == nullptr) {
402     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
403         "Security connector missing from client auth filter args");
404   }
405   grpc_auth_context* auth_context =
406       grpc_find_auth_context_in_args(args->channel_args);
407   if (auth_context == nullptr) {
408     return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
409         "Auth context missing from client auth filter args");
410   }
411   new (elem->channel_data) channel_data(
412       static_cast<grpc_channel_security_connector*>(sc), auth_context);
413   return GRPC_ERROR_NONE;
414 }
415
416 /* Destructor for channel data */
417 static void destroy_channel_elem(grpc_channel_element* elem) {
418   channel_data* chand = static_cast<channel_data*>(elem->channel_data);
419   chand->~channel_data();
420 }
421
422 const grpc_channel_filter grpc_client_auth_filter = {
423     auth_start_transport_stream_op_batch,
424     grpc_channel_next_op,
425     sizeof(call_data),
426     init_call_elem,
427     set_pollset_or_pollset_set,
428     destroy_call_elem,
429     sizeof(channel_data),
430     init_channel_elem,
431     destroy_channel_elem,
432     grpc_channel_next_get_info,
433     "client-auth"};