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 "src/core/lib/security/credentials/plugin/plugin_credentials.h"
20
21 #include <grpc/support/alloc.h>
22 #include <grpc/support/port_platform.h>
23
24 #include <atomic>
25 #include <memory>
26
27 #include "absl/log/check.h"
28 #include "absl/log/log.h"
29 #include "absl/status/status.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/string_view.h"
32 #include "src/core/lib/iomgr/error.h"
33 #include "src/core/lib/iomgr/exec_ctx.h"
34 #include "src/core/lib/promise/promise.h"
35 #include "src/core/lib/slice/slice.h"
36 #include "src/core/lib/slice/slice_internal.h"
37 #include "src/core/lib/surface/validate_metadata.h"
38 #include "src/core/lib/transport/metadata_batch.h"
39
~grpc_plugin_credentials()40 grpc_plugin_credentials::~grpc_plugin_credentials() {
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
type() const59 grpc_core::UniqueTypeName grpc_plugin_credentials::type() const {
60 static grpc_core::UniqueTypeName::Factory kFactory("Plugin");
61 return kFactory.Create();
62 }
63
64 absl::StatusOr<grpc_core::ClientMetadataHandle>
ProcessPluginResult(const grpc_metadata * md,size_t num_md,grpc_status_code status,const char * error_details)65 grpc_plugin_credentials::PendingRequest::ProcessPluginResult(
66 const grpc_metadata* md, size_t num_md, grpc_status_code status,
67 const char* error_details) {
68 if (status != GRPC_STATUS_OK) {
69 return absl::UnavailableError(absl::StrCat(
70 "Getting metadata from plugin failed with error: ", error_details));
71 } else {
72 bool seen_illegal_header = false;
73 for (size_t i = 0; i < num_md; ++i) {
74 if (!GRPC_LOG_IF_ERROR("validate_metadata_from_plugin",
75 grpc_validate_header_key_is_legal(md[i].key))) {
76 seen_illegal_header = true;
77 break;
78 } else if (!grpc_is_binary_header_internal(md[i].key) &&
79 !GRPC_LOG_IF_ERROR(
80 "validate_metadata_from_plugin",
81 grpc_validate_header_nonbin_value_is_legal(md[i].value))) {
82 LOG(ERROR) << "Plugin added invalid metadata value.";
83 seen_illegal_header = true;
84 break;
85 }
86 }
87 if (seen_illegal_header) {
88 return absl::UnavailableError("Illegal metadata");
89 } else {
90 absl::Status error;
91 for (size_t i = 0; i < num_md; ++i) {
92 md_->Append(
93 grpc_core::StringViewFromSlice(md[i].key),
94 grpc_core::Slice(grpc_core::CSliceRef(md[i].value)),
95 [&error](absl::string_view message, const grpc_core::Slice&) {
96 error = absl::UnavailableError(message);
97 });
98 }
99 if (!error.ok()) return std::move(error);
100 return grpc_core::ClientMetadataHandle(std::move(md_));
101 }
102 }
103 }
104
105 grpc_core::Poll<absl::StatusOr<grpc_core::ClientMetadataHandle>>
PollAsyncResult()106 grpc_plugin_credentials::PendingRequest::PollAsyncResult() {
107 if (!ready_.load(std::memory_order_acquire)) {
108 return grpc_core::Pending{};
109 }
110 return ProcessPluginResult(metadata_.data(), metadata_.size(), status_,
111 error_details_.c_str());
112 }
113
RequestMetadataReady(void * request,const grpc_metadata * md,size_t num_md,grpc_status_code status,const char * error_details)114 void grpc_plugin_credentials::PendingRequest::RequestMetadataReady(
115 void* request, const grpc_metadata* md, size_t num_md,
116 grpc_status_code status, const char* error_details) {
117 // called from application code
118 grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
119 grpc_core::ExecCtx exec_ctx(GRPC_EXEC_CTX_FLAG_IS_FINISHED |
120 GRPC_EXEC_CTX_FLAG_THREAD_RESOURCE_LOOP);
121 grpc_core::RefCountedPtr<grpc_plugin_credentials::PendingRequest> r(
122 static_cast<grpc_plugin_credentials::PendingRequest*>(request));
123 GRPC_TRACE_LOG(plugin_credentials, INFO)
124 << "plugin_credentials[" << r->creds() << "]: request " << r.get()
125 << ": plugin returned asynchronously";
126 for (size_t i = 0; i < num_md; ++i) {
127 grpc_metadata p;
128 p.key = grpc_core::CSliceRef(md[i].key);
129 p.value = grpc_core::CSliceRef(md[i].value);
130 r->metadata_.push_back(p);
131 }
132 r->error_details_ = error_details == nullptr ? "" : error_details;
133 r->status_ = status;
134 r->ready_.store(true, std::memory_order_release);
135 r->waker_.Wakeup();
136 }
137
138 grpc_core::ArenaPromise<absl::StatusOr<grpc_core::ClientMetadataHandle>>
GetRequestMetadata(grpc_core::ClientMetadataHandle initial_metadata,const grpc_call_credentials::GetRequestMetadataArgs * args)139 grpc_plugin_credentials::GetRequestMetadata(
140 grpc_core::ClientMetadataHandle initial_metadata,
141 const grpc_call_credentials::GetRequestMetadataArgs* args) {
142 if (plugin_.get_metadata == nullptr) {
143 return grpc_core::Immediate(std::move(initial_metadata));
144 }
145
146 // Create pending_request object.
147 auto request = grpc_core::MakeRefCounted<PendingRequest>(
148 RefAsSubclass<grpc_plugin_credentials>(), std::move(initial_metadata),
149 args);
150 // Invoke the plugin. The callback holds a ref to us.
151 GRPC_TRACE_LOG(plugin_credentials, INFO)
152 << "plugin_credentials[" << this << "]: request " << request.get()
153 << ": invoking plugin";
154 grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX];
155 size_t num_creds_md = 0;
156 grpc_status_code status = GRPC_STATUS_OK;
157 const char* error_details = nullptr;
158 // Add an extra ref to the request object for the async callback.
159 // If the request completes synchronously, we'll drop this later.
160 // If the request completes asynchronously, it will own a ref to the request
161 // object (which we release from our ownership below).
162 auto child_request = request->Ref();
163 if (!plugin_.get_metadata(plugin_.state, request->context(),
164 PendingRequest::RequestMetadataReady,
165 child_request.get(), creds_md, &num_creds_md,
166 &status, &error_details)) {
167 child_request.release();
168 GRPC_TRACE_LOG(plugin_credentials, INFO)
169 << "plugin_credentials[" << this << "]: request " << request.get()
170 << ": plugin will return asynchronously";
171 return [request] { return request->PollAsyncResult(); };
172 }
173 // Synchronous return.
174 GRPC_TRACE_LOG(plugin_credentials, INFO)
175 << "plugin_credentials[" << this << "]: request " << request.get()
176 << ": plugin returned synchronously";
177 auto result = request->ProcessPluginResult(creds_md, num_creds_md, status,
178 error_details);
179 // Clean up.
180 for (size_t i = 0; i < num_creds_md; ++i) {
181 grpc_core::CSliceUnref(creds_md[i].key);
182 grpc_core::CSliceUnref(creds_md[i].value);
183 }
184 gpr_free(const_cast<char*>(error_details));
185
186 return grpc_core::Immediate(std::move(result));
187 }
188
grpc_plugin_credentials(grpc_metadata_credentials_plugin plugin,grpc_security_level min_security_level)189 grpc_plugin_credentials::grpc_plugin_credentials(
190 grpc_metadata_credentials_plugin plugin,
191 grpc_security_level min_security_level)
192 : grpc_call_credentials(min_security_level), plugin_(plugin) {}
193
grpc_metadata_credentials_create_from_plugin(grpc_metadata_credentials_plugin plugin,grpc_security_level min_security_level,void * reserved)194 grpc_call_credentials* grpc_metadata_credentials_create_from_plugin(
195 grpc_metadata_credentials_plugin plugin,
196 grpc_security_level min_security_level, void* reserved) {
197 GRPC_TRACE_LOG(api, INFO)
198 << "grpc_metadata_credentials_create_from_plugin(reserved=" << reserved
199 << ")";
200 CHECK_EQ(reserved, nullptr);
201 return new grpc_plugin_credentials(plugin, min_security_level);
202 }
203