• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright 2016 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/credentials/plugin/plugin_credentials.h"
22 
23 #include <string.h>
24 
25 #include <grpc/grpc.h>
26 #include <grpc/support/alloc.h>
27 #include <grpc/support/log.h>
28 #include <grpc/support/string_util.h>
29 #include <grpc/support/sync.h>
30 
31 #include "src/core/lib/slice/slice_internal.h"
32 #include "src/core/lib/slice/slice_string_helpers.h"
33 #include "src/core/lib/surface/api_trace.h"
34 #include "src/core/lib/surface/validate_metadata.h"
35 
36 grpc_core::TraceFlag grpc_plugin_credentials_trace(false, "plugin_credentials");
37 
plugin_destruct(grpc_call_credentials * creds)38 static void plugin_destruct(grpc_call_credentials* creds) {
39   grpc_plugin_credentials* c =
40       reinterpret_cast<grpc_plugin_credentials*>(creds);
41   gpr_mu_destroy(&c->mu);
42   if (c->plugin.state != nullptr && c->plugin.destroy != nullptr) {
43     c->plugin.destroy(c->plugin.state);
44   }
45 }
46 
pending_request_remove_locked(grpc_plugin_credentials * c,grpc_plugin_credentials_pending_request * pending_request)47 static void pending_request_remove_locked(
48     grpc_plugin_credentials* c,
49     grpc_plugin_credentials_pending_request* pending_request) {
50   if (pending_request->prev == nullptr) {
51     c->pending_requests = pending_request->next;
52   } else {
53     pending_request->prev->next = pending_request->next;
54   }
55   if (pending_request->next != nullptr) {
56     pending_request->next->prev = pending_request->prev;
57   }
58 }
59 
60 // Checks if the request has been cancelled.
61 // If not, removes it from the pending list, so that it cannot be
62 // cancelled out from under us.
63 // When this returns, r->cancelled indicates whether the request was
64 // cancelled before completion.
pending_request_complete(grpc_plugin_credentials_pending_request * r)65 static void pending_request_complete(
66     grpc_plugin_credentials_pending_request* r) {
67   gpr_mu_lock(&r->creds->mu);
68   if (!r->cancelled) pending_request_remove_locked(r->creds, r);
69   gpr_mu_unlock(&r->creds->mu);
70   // Ref to credentials not needed anymore.
71   grpc_call_credentials_unref(&r->creds->base);
72 }
73 
process_plugin_result(grpc_plugin_credentials_pending_request * r,const grpc_metadata * md,size_t num_md,grpc_status_code status,const char * error_details)74 static grpc_error* process_plugin_result(
75     grpc_plugin_credentials_pending_request* r, const grpc_metadata* md,
76     size_t num_md, grpc_status_code status, const char* error_details) {
77   grpc_error* error = GRPC_ERROR_NONE;
78   if (status != GRPC_STATUS_OK) {
79     char* msg;
80     gpr_asprintf(&msg, "Getting metadata from plugin failed with error: %s",
81                  error_details);
82     error = GRPC_ERROR_CREATE_FROM_COPIED_STRING(msg);
83     gpr_free(msg);
84   } else {
85     bool seen_illegal_header = false;
86     for (size_t i = 0; i < num_md; ++i) {
87       if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin",
88                              grpc_validate_header_key_is_legal(md[i].key))) {
89         seen_illegal_header = true;
90         break;
91       } else if (!grpc_is_binary_header(md[i].key) &&
92                  !GRPC_LOG_IF_ERROR(
93                      "validate_metadata_from_plugin",
94                      grpc_validate_header_nonbin_value_is_legal(md[i].value))) {
95         gpr_log(GPR_ERROR, "Plugin added invalid metadata value.");
96         seen_illegal_header = true;
97         break;
98       }
99     }
100     if (seen_illegal_header) {
101       error = GRPC_ERROR_CREATE_FROM_STATIC_STRING("Illegal metadata");
102     } else {
103       for (size_t i = 0; i < num_md; ++i) {
104         grpc_mdelem mdelem =
105             grpc_mdelem_from_slices(grpc_slice_ref_internal(md[i].key),
106                                     grpc_slice_ref_internal(md[i].value));
107         grpc_credentials_mdelem_array_add(r->md_array, mdelem);
108         GRPC_MDELEM_UNREF(mdelem);
109       }
110     }
111   }
112   return error;
113 }
114 
plugin_md_request_metadata_ready(void * request,const grpc_metadata * md,size_t num_md,grpc_status_code status,const char * error_details)115 static void plugin_md_request_metadata_ready(void* request,
116                                              const grpc_metadata* md,
117                                              size_t num_md,
118                                              grpc_status_code status,
119                                              const char* error_details) {
120   /* called from application code */
121   grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_FINISHED |
122                               GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP);
123   grpc_plugin_credentials_pending_request* r =
124       static_cast<grpc_plugin_credentials_pending_request*>(request);
125   if (grpc_plugin_credentials_trace.enabled()) {
126     gpr_log(GPR_INFO,
127             "plugin_credentials[%p]: request %p: plugin returned "
128             "asynchronously",
129             r->creds, r);
130   }
131   // Remove request from pending list if not previously cancelled.
132   pending_request_complete(r);
133   // If it has not been cancelled, process it.
134   if (!r->cancelled) {
135     grpc_error* error =
136         process_plugin_result(r, md, num_md, status, error_details);
137     GRPC_CLOSURE_SCHED(r->on_request_metadata, error);
138   } else if (grpc_plugin_credentials_trace.enabled()) {
139     gpr_log(GPR_INFO,
140             "plugin_credentials[%p]: request %p: plugin was previously "
141             "cancelled",
142             r->creds, r);
143   }
144   gpr_free(r);
145 }
146 
plugin_get_request_metadata(grpc_call_credentials * creds,grpc_polling_entity * pollent,grpc_auth_metadata_context context,grpc_credentials_mdelem_array * md_array,grpc_closure * on_request_metadata,grpc_error ** error)147 static bool plugin_get_request_metadata(grpc_call_credentials* creds,
148                                         grpc_polling_entity* pollent,
149                                         grpc_auth_metadata_context context,
150                                         grpc_credentials_mdelem_array* md_array,
151                                         grpc_closure* on_request_metadata,
152                                         grpc_error** error) {
153   grpc_plugin_credentials* c =
154       reinterpret_cast<grpc_plugin_credentials*>(creds);
155   bool retval = true;  // Synchronous return.
156   if (c->plugin.get_metadata != nullptr) {
157     // Create pending_request object.
158     grpc_plugin_credentials_pending_request* pending_request =
159         static_cast<grpc_plugin_credentials_pending_request*>(
160             gpr_zalloc(sizeof(*pending_request)));
161     pending_request->creds = c;
162     pending_request->md_array = md_array;
163     pending_request->on_request_metadata = on_request_metadata;
164     // Add it to the pending list.
165     gpr_mu_lock(&c->mu);
166     if (c->pending_requests != nullptr) {
167       c->pending_requests->prev = pending_request;
168     }
169     pending_request->next = c->pending_requests;
170     c->pending_requests = pending_request;
171     gpr_mu_unlock(&c->mu);
172     // Invoke the plugin.  The callback holds a ref to us.
173     if (grpc_plugin_credentials_trace.enabled()) {
174       gpr_log(GPR_INFO, "plugin_credentials[%p]: request %p: invoking plugin",
175               c, pending_request);
176     }
177     grpc_call_credentials_ref(creds);
178     grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX];
179     size_t num_creds_md = 0;
180     grpc_status_code status = GRPC_STATUS_OK;
181     const char* error_details = nullptr;
182     if (!c->plugin.get_metadata(c->plugin.state, context,
183                                 plugin_md_request_metadata_ready,
184                                 pending_request, creds_md, &num_creds_md,
185                                 &status, &error_details)) {
186       if (grpc_plugin_credentials_trace.enabled()) {
187         gpr_log(GPR_INFO,
188                 "plugin_credentials[%p]: request %p: plugin will return "
189                 "asynchronously",
190                 c, pending_request);
191       }
192       return false;  // Asynchronous return.
193     }
194     // Returned synchronously.
195     // Remove request from pending list if not previously cancelled.
196     pending_request_complete(pending_request);
197     // If the request was cancelled, the error will have been returned
198     // asynchronously by plugin_cancel_get_request_metadata(), so return
199     // false.  Otherwise, process the result.
200     if (pending_request->cancelled) {
201       if (grpc_plugin_credentials_trace.enabled()) {
202         gpr_log(GPR_INFO,
203                 "plugin_credentials[%p]: request %p was cancelled, error "
204                 "will be returned asynchronously",
205                 c, pending_request);
206       }
207       retval = false;
208     } else {
209       if (grpc_plugin_credentials_trace.enabled()) {
210         gpr_log(GPR_INFO,
211                 "plugin_credentials[%p]: request %p: plugin returned "
212                 "synchronously",
213                 c, pending_request);
214       }
215       *error = process_plugin_result(pending_request, creds_md, num_creds_md,
216                                      status, error_details);
217     }
218     // Clean up.
219     for (size_t i = 0; i < num_creds_md; ++i) {
220       grpc_slice_unref_internal(creds_md[i].key);
221       grpc_slice_unref_internal(creds_md[i].value);
222     }
223     gpr_free((void*)error_details);
224     gpr_free(pending_request);
225   }
226   return retval;
227 }
228 
plugin_cancel_get_request_metadata(grpc_call_credentials * creds,grpc_credentials_mdelem_array * md_array,grpc_error * error)229 static void plugin_cancel_get_request_metadata(
230     grpc_call_credentials* creds, grpc_credentials_mdelem_array* md_array,
231     grpc_error* error) {
232   grpc_plugin_credentials* c =
233       reinterpret_cast<grpc_plugin_credentials*>(creds);
234   gpr_mu_lock(&c->mu);
235   for (grpc_plugin_credentials_pending_request* pending_request =
236            c->pending_requests;
237        pending_request != nullptr; pending_request = pending_request->next) {
238     if (pending_request->md_array == md_array) {
239       if (grpc_plugin_credentials_trace.enabled()) {
240         gpr_log(GPR_INFO, "plugin_credentials[%p]: cancelling request %p", c,
241                 pending_request);
242       }
243       pending_request->cancelled = true;
244       GRPC_CLOSURE_SCHED(pending_request->on_request_metadata,
245                          GRPC_ERROR_REF(error));
246       pending_request_remove_locked(c, pending_request);
247       break;
248     }
249   }
250   gpr_mu_unlock(&c->mu);
251   GRPC_ERROR_UNREF(error);
252 }
253 
254 static grpc_call_credentials_vtable plugin_vtable = {
255     plugin_destruct, plugin_get_request_metadata,
256     plugin_cancel_get_request_metadata};
257 
grpc_metadata_credentials_create_from_plugin(grpc_metadata_credentials_plugin plugin,void * reserved)258 grpc_call_credentials* grpc_metadata_credentials_create_from_plugin(
259     grpc_metadata_credentials_plugin plugin, void* reserved) {
260   grpc_plugin_credentials* c =
261       static_cast<grpc_plugin_credentials*>(gpr_zalloc(sizeof(*c)));
262   GRPC_API_TRACE("grpc_metadata_credentials_create_from_plugin(reserved=%p)", 1,
263                  (reserved));
264   GPR_ASSERT(reserved == nullptr);
265   c->base.type = plugin.type;
266   c->base.vtable = &plugin_vtable;
267   gpr_ref_init(&c->base.refcount, 1);
268   c->plugin = plugin;
269   gpr_mu_init(&c->mu);
270   return &c->base;
271 }
272