• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/credentials.h>
20 #include <grpc/grpc_security.h>
21 #include <grpc/grpc_security_constants.h>
22 #include <grpc/support/alloc.h>
23 #include <grpc/support/port_platform.h>
24 #include <grpc/support/string_util.h>
25 #include <string.h>
26 
27 #include <functional>
28 #include <memory>
29 #include <type_traits>  // IWYU pragma: keep
30 #include <utility>
31 
32 #include "absl/status/status.h"
33 #include "absl/status/statusor.h"
34 #include "src/core/lib/channel/channel_args.h"
35 #include "src/core/lib/channel/channel_fwd.h"
36 #include "src/core/lib/channel/channel_stack.h"
37 #include "src/core/lib/channel/promise_based_filter.h"
38 #include "src/core/lib/channel/status_util.h"
39 #include "src/core/lib/promise/arena_promise.h"
40 #include "src/core/lib/promise/context.h"
41 #include "src/core/lib/promise/promise.h"
42 #include "src/core/lib/promise/seq.h"
43 #include "src/core/lib/promise/try_seq.h"
44 #include "src/core/lib/resource_quota/arena.h"
45 #include "src/core/lib/security/context/security_context.h"
46 #include "src/core/lib/security/credentials/credentials.h"
47 #include "src/core/lib/security/security_connector/security_connector.h"
48 #include "src/core/lib/security/transport/auth_filters.h"
49 #include "src/core/lib/transport/metadata_batch.h"
50 #include "src/core/lib/transport/transport.h"
51 #include "src/core/util/debug_location.h"
52 #include "src/core/util/ref_counted_ptr.h"
53 
54 #define MAX_CREDENTIALS_METADATA_COUNT 4
55 
grpc_auth_metadata_context_copy(grpc_auth_metadata_context * from,grpc_auth_metadata_context * to)56 void grpc_auth_metadata_context_copy(grpc_auth_metadata_context* from,
57                                      grpc_auth_metadata_context* to) {
58   grpc_auth_metadata_context_reset(to);
59   to->channel_auth_context = from->channel_auth_context;
60   if (to->channel_auth_context != nullptr) {
61     const_cast<grpc_auth_context*>(to->channel_auth_context)
62         ->Ref(DEBUG_LOCATION, "grpc_auth_metadata_context_copy")
63         .release();
64   }
65   to->service_url = gpr_strdup(from->service_url);
66   to->method_name = gpr_strdup(from->method_name);
67 }
68 
grpc_auth_metadata_context_reset(grpc_auth_metadata_context * auth_md_context)69 void grpc_auth_metadata_context_reset(
70     grpc_auth_metadata_context* auth_md_context) {
71   if (auth_md_context->service_url != nullptr) {
72     gpr_free(const_cast<char*>(auth_md_context->service_url));
73     auth_md_context->service_url = nullptr;
74   }
75   if (auth_md_context->method_name != nullptr) {
76     gpr_free(const_cast<char*>(auth_md_context->method_name));
77     auth_md_context->method_name = nullptr;
78   }
79   if (auth_md_context->channel_auth_context != nullptr) {
80     const_cast<grpc_auth_context*>(auth_md_context->channel_auth_context)
81         ->Unref(DEBUG_LOCATION, "grpc_auth_metadata_context");
82     auth_md_context->channel_auth_context = nullptr;
83   }
84 }
85 
convert_security_level_string_to_enum(const char * security_level)86 static grpc_security_level convert_security_level_string_to_enum(
87     const char* security_level) {
88   if (strcmp(security_level, "TSI_INTEGRITY_ONLY") == 0) {
89     return GRPC_INTEGRITY_ONLY;
90   } else if (strcmp(security_level, "TSI_PRIVACY_AND_INTEGRITY") == 0) {
91     return GRPC_PRIVACY_AND_INTEGRITY;
92   }
93   return GRPC_SECURITY_NONE;
94 }
95 
grpc_check_security_level(grpc_security_level channel_level,grpc_security_level call_cred_level)96 bool grpc_check_security_level(grpc_security_level channel_level,
97                                grpc_security_level call_cred_level) {
98   return static_cast<int>(channel_level) >= static_cast<int>(call_cred_level);
99 }
100 
101 namespace grpc_core {
102 
ClientAuthFilter(RefCountedPtr<grpc_channel_security_connector> security_connector,RefCountedPtr<grpc_auth_context> auth_context)103 ClientAuthFilter::ClientAuthFilter(
104     RefCountedPtr<grpc_channel_security_connector> security_connector,
105     RefCountedPtr<grpc_auth_context> auth_context)
106     : args_{std::move(security_connector), std::move(auth_context)} {}
107 
GetCallCredsMetadata(CallArgs call_args)108 ArenaPromise<absl::StatusOr<CallArgs>> ClientAuthFilter::GetCallCredsMetadata(
109     CallArgs call_args) {
110   auto* ctx = GetContext<grpc_client_security_context>();
111   grpc_call_credentials* channel_call_creds =
112       args_.security_connector->mutable_request_metadata_creds();
113   const bool call_creds_has_md = (ctx != nullptr) && (ctx->creds != nullptr);
114 
115   if (channel_call_creds == nullptr && !call_creds_has_md) {
116     // Skip sending metadata altogether.
117     return Immediate(absl::StatusOr<CallArgs>(std::move(call_args)));
118   }
119 
120   RefCountedPtr<grpc_call_credentials> creds;
121   if (channel_call_creds != nullptr && call_creds_has_md) {
122     creds = RefCountedPtr<grpc_call_credentials>(
123         grpc_composite_call_credentials_create(channel_call_creds,
124                                                ctx->creds.get(), nullptr));
125     if (creds == nullptr) {
126       return Immediate(absl::UnauthenticatedError(
127           "Incompatible credentials set on channel and call."));
128     }
129   } else if (call_creds_has_md) {
130     creds = ctx->creds->Ref();
131   } else {
132     creds = channel_call_creds->Ref();
133   }
134 
135   // Check security level of call credential and channel, and do not send
136   // metadata if the check fails.
137   grpc_auth_property_iterator it = grpc_auth_context_find_properties_by_name(
138       args_.auth_context.get(), GRPC_TRANSPORT_SECURITY_LEVEL_PROPERTY_NAME);
139   const grpc_auth_property* prop = grpc_auth_property_iterator_next(&it);
140   if (prop == nullptr) {
141     return Immediate(
142         absl::UnauthenticatedError("Established channel does not have an auth "
143                                    "property representing a security level."));
144   }
145   const grpc_security_level call_cred_security_level =
146       creds->min_security_level();
147   const bool is_security_level_ok = grpc_check_security_level(
148       convert_security_level_string_to_enum(prop->value),
149       call_cred_security_level);
150   if (!is_security_level_ok) {
151     return Immediate(absl::UnauthenticatedError(
152         "Established channel does not have a sufficient security level to "
153         "transfer call credential."));
154   }
155 
156   auto client_initial_metadata = std::move(call_args.client_initial_metadata);
157   return TrySeq(
158       Seq(creds->GetRequestMetadata(std::move(client_initial_metadata), &args_),
159           [](absl::StatusOr<ClientMetadataHandle> new_metadata) mutable {
160             if (!new_metadata.ok()) {
161               return absl::StatusOr<ClientMetadataHandle>(
162                   MaybeRewriteIllegalStatusCode(new_metadata.status(),
163                                                 "call credentials"));
164             }
165             return new_metadata;
166           }),
167       [call_args =
168            std::move(call_args)](ClientMetadataHandle new_metadata) mutable {
169         call_args.client_initial_metadata = std::move(new_metadata);
170         return Immediate<absl::StatusOr<CallArgs>>(
171             absl::StatusOr<CallArgs>(std::move(call_args)));
172       });
173 }
174 
MakeCallPromise(CallArgs call_args,NextPromiseFactory next_promise_factory)175 ArenaPromise<ServerMetadataHandle> ClientAuthFilter::MakeCallPromise(
176     CallArgs call_args, NextPromiseFactory next_promise_factory) {
177   auto* sec_ctx = MaybeGetContext<grpc_client_security_context>();
178   if (sec_ctx == nullptr) {
179     sec_ctx = grpc_client_security_context_create(GetContext<Arena>(),
180                                                   /*creds=*/nullptr);
181     SetContext<SecurityContext>(sec_ctx);
182   }
183   sec_ctx->auth_context = args_.auth_context;
184 
185   auto* host =
186       call_args.client_initial_metadata->get_pointer(HttpAuthorityMetadata());
187   if (host == nullptr) {
188     return next_promise_factory(std::move(call_args));
189   }
190   return TrySeq(
191       args_.security_connector->CheckCallHost(host->as_string_view(),
192                                               args_.auth_context.get()),
193       [this, call_args = std::move(call_args)]() mutable {
194         return GetCallCredsMetadata(std::move(call_args));
195       },
196       next_promise_factory);
197 }
198 
Create(const ChannelArgs & args,ChannelFilter::Args)199 absl::StatusOr<std::unique_ptr<ClientAuthFilter>> ClientAuthFilter::Create(
200     const ChannelArgs& args, ChannelFilter::Args) {
201   auto* sc = args.GetObject<grpc_security_connector>();
202   if (sc == nullptr) {
203     return absl::InvalidArgumentError(
204         "Security connector missing from client auth filter args");
205   }
206   auto* auth_context = args.GetObject<grpc_auth_context>();
207   if (auth_context == nullptr) {
208     return absl::InvalidArgumentError(
209         "Auth context missing from client auth filter args");
210   }
211   return std::make_unique<ClientAuthFilter>(
212       sc->RefAsSubclass<grpc_channel_security_connector>(),
213       auth_context->Ref());
214 }
215 
216 const grpc_channel_filter ClientAuthFilter::kFilter =
217     MakePromiseBasedFilter<ClientAuthFilter, FilterEndpoint::kClient>();
218 
219 }  // namespace grpc_core
220