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