blob: 2322c12aa545d3457e6a648bc399c9106b5cdc16 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller06059952015-02-18 08:34:56 -08003 * Copyright 2015, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/security/auth.h"
35
36#include <string.h>
37
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080038#include <grpc/support/alloc.h>
39#include <grpc/support/log.h>
40
Julien Boeuf54b21922015-02-04 16:39:35 -080041#include "src/core/support/string.h"
42#include "src/core/channel/channel_stack.h"
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -070043#include "src/core/security/security_connector.h"
Julien Boeuf54b21922015-02-04 16:39:35 -080044#include "src/core/security/credentials.h"
45#include "src/core/surface/call.h"
46
Craig Tiller9c9d4e02015-04-20 09:03:29 -070047#define MAX_CREDENTIALS_METADATA_COUNT 4
Craig Tiller6902ad22015-04-16 08:01:49 -070048
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080049/* We can have a per-call credentials. */
50typedef struct {
51 grpc_credentials *creds;
Julien Boeuf54b21922015-02-04 16:39:35 -080052 grpc_mdstr *host;
Julien Boeuff47a5cb2015-02-18 12:24:08 -080053 grpc_mdstr *method;
Craig Tiller6e84aba2015-04-23 15:08:17 -070054 grpc_transport_op op;
55 size_t op_md_idx;
56 int sent_initial_metadata;
Craig Tiller9c9d4e02015-04-20 09:03:29 -070057 grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080058} call_data;
59
60/* We can have a per-channel credentials. */
61typedef struct {
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -070062 grpc_channel_security_connector *security_connector;
Julien Boeuf54b21922015-02-04 16:39:35 -080063 grpc_mdctx *md_ctx;
64 grpc_mdstr *authority_string;
Julien Boeuff47a5cb2015-02-18 12:24:08 -080065 grpc_mdstr *path_string;
Julien Boeuf54b21922015-02-04 16:39:35 -080066 grpc_mdstr *error_msg_key;
Craig Tillereb131bb2015-03-03 11:00:40 -080067 grpc_mdstr *status_key;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080068} channel_data;
69
70static void on_credentials_metadata(void *user_data, grpc_mdelem **md_elems,
71 size_t num_md,
72 grpc_credentials_status status) {
73 grpc_call_element *elem = (grpc_call_element *)user_data;
Craig Tiller6902ad22015-04-16 08:01:49 -070074 call_data *calld = elem->call_data;
Craig Tiller6e84aba2015-04-23 15:08:17 -070075 grpc_transport_op *op = &calld->op;
76 grpc_metadata_batch *mdb;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080077 size_t i;
Craig Tiller9c9d4e02015-04-20 09:03:29 -070078 GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
Craig Tiller1a727fd2015-04-24 13:21:22 -070079 GPR_ASSERT(op->send_ops && op->send_ops->nops > calld->op_md_idx &&
80 op->send_ops->ops[calld->op_md_idx].type == GRPC_OP_METADATA);
Craig Tiller6e84aba2015-04-23 15:08:17 -070081 mdb = &op->send_ops->ops[calld->op_md_idx].data.metadata;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080082 for (i = 0; i < num_md; i++) {
Craig Tiller6e84aba2015-04-23 15:08:17 -070083 grpc_metadata_batch_add_tail(mdb, &calld->md_links[i],
Craig Tiller76f5d462015-04-17 14:58:12 -070084 grpc_mdelem_ref(md_elems[i]));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080085 }
Craig Tiller6e84aba2015-04-23 15:08:17 -070086 grpc_call_next_op(elem, op);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080087}
88
Julien Boeuff47a5cb2015-02-18 12:24:08 -080089static char *build_service_url(const char *url_scheme, call_data *calld) {
90 char *service_url;
91 char *service = gpr_strdup(grpc_mdstr_as_c_string(calld->method));
92 char *last_slash = strrchr(service, '/');
93 if (last_slash == NULL) {
94 gpr_log(GPR_ERROR, "No '/' found in fully qualified method name");
95 service[0] = '\0';
96 } else if (last_slash == service) {
97 /* No service part in fully qualified method name: will just be "/". */
98 service[1] = '\0';
99 } else {
100 *last_slash = '\0';
101 }
102 if (url_scheme == NULL) url_scheme = "";
103 gpr_asprintf(&service_url, "%s://%s%s", url_scheme,
104 grpc_mdstr_as_c_string(calld->host), service);
105 gpr_free(service);
106 return service_url;
107}
108
Craig Tiller1a727fd2015-04-24 13:21:22 -0700109static void send_security_metadata(grpc_call_element *elem,
110 grpc_transport_op *op) {
Julien Boeuf54b21922015-02-04 16:39:35 -0800111 /* grab pointers to our data from the call element */
112 call_data *calld = elem->call_data;
113 channel_data *channeld = elem->channel_data;
114
115 grpc_credentials *channel_creds =
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700116 channeld->security_connector->request_metadata_creds;
Julien Boeuf54b21922015-02-04 16:39:35 -0800117 /* TODO(jboeuf):
118 Decide on the policy in this case:
119 - populate both channel and call?
120 - the call takes precedence over the channel?
121 - leave this decision up to the channel credentials? */
122 if (calld->creds != NULL) {
123 gpr_log(GPR_ERROR, "Ignoring per call credentials for now.");
124 }
125 if (channel_creds != NULL &&
126 grpc_credentials_has_request_metadata(channel_creds)) {
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800127 char *service_url =
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700128 build_service_url(channeld->security_connector->base.url_scheme, calld);
Julien Boeuf54b21922015-02-04 16:39:35 -0800129 calld->op = *op; /* Copy op (originates from the caller's stack). */
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800130 grpc_credentials_get_request_metadata(channel_creds, service_url,
Julien Boeuf54b21922015-02-04 16:39:35 -0800131 on_credentials_metadata, elem);
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800132 gpr_free(service_url);
Julien Boeuf54b21922015-02-04 16:39:35 -0800133 } else {
134 grpc_call_next_op(elem, op);
135 }
136}
137
138static void on_host_checked(void *user_data, grpc_security_status status) {
139 grpc_call_element *elem = (grpc_call_element *)user_data;
140 call_data *calld = elem->call_data;
Craig Tiller2ea37fd2015-04-24 13:03:49 -0700141 channel_data *chand = elem->channel_data;
Julien Boeuf54b21922015-02-04 16:39:35 -0800142
143 if (status == GRPC_SECURITY_OK) {
144 send_security_metadata(elem, &calld->op);
145 } else {
146 char *error_msg;
147 gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
148 grpc_mdstr_as_c_string(calld->host));
Craig Tiller1a727fd2015-04-24 13:21:22 -0700149 grpc_transport_op_add_cancellation(
150 &calld->op, GRPC_STATUS_UNAUTHENTICATED,
151 grpc_mdstr_from_string(chand->md_ctx, error_msg));
Julien Boeuf54b21922015-02-04 16:39:35 -0800152 gpr_free(error_msg);
Craig Tiller2ea37fd2015-04-24 13:03:49 -0700153 grpc_call_next_op(elem, &calld->op);
Julien Boeuf54b21922015-02-04 16:39:35 -0800154 }
155}
156
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800157/* Called either:
158 - in response to an API call (or similar) from above, to send something
159 - a network event (or similar) from below, to receive something
160 op contains type and call direction information, in addition to the data
161 that is being sent or received. */
Craig Tiller1a727fd2015-04-24 13:21:22 -0700162static void auth_start_transport_op(grpc_call_element *elem,
163 grpc_transport_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800164 /* grab pointers to our data from the call element */
165 call_data *calld = elem->call_data;
166 channel_data *channeld = elem->channel_data;
Craig Tiller6902ad22015-04-16 08:01:49 -0700167 grpc_linked_mdelem *l;
Craig Tiller6e84aba2015-04-23 15:08:17 -0700168 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800169
Craig Tiller6e84aba2015-04-23 15:08:17 -0700170 if (op->send_ops && !calld->sent_initial_metadata) {
171 size_t nops = op->send_ops->nops;
172 grpc_stream_op *ops = op->send_ops->ops;
173 for (i = 0; i < nops; i++) {
174 grpc_stream_op *sop = &ops[i];
175 if (sop->type != GRPC_OP_METADATA) continue;
Craig Tiller4e87e002015-04-24 08:49:10 -0700176 calld->op_md_idx = i;
Craig Tiller6e84aba2015-04-23 15:08:17 -0700177 calld->sent_initial_metadata = 1;
178 for (l = sop->data.metadata.list.head; l != NULL; l = l->next) {
Craig Tiller6902ad22015-04-16 08:01:49 -0700179 grpc_mdelem *md = l->md;
Craig Tiller87d5b192015-04-16 14:37:57 -0700180 /* Pointer comparison is OK for md_elems created from the same context.
181 */
Craig Tiller6902ad22015-04-16 08:01:49 -0700182 if (md->key == channeld->authority_string) {
183 if (calld->host != NULL) grpc_mdstr_unref(calld->host);
184 calld->host = grpc_mdstr_ref(md->value);
185 } else if (md->key == channeld->path_string) {
186 if (calld->method != NULL) grpc_mdstr_unref(calld->method);
187 calld->method = grpc_mdstr_ref(md->value);
188 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800189 }
Julien Boeuf54b21922015-02-04 16:39:35 -0800190 if (calld->host != NULL) {
191 grpc_security_status status;
192 const char *call_host = grpc_mdstr_as_c_string(calld->host);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800193 calld->op = *op; /* Copy op (originates from the caller's stack). */
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700194 status = grpc_channel_security_connector_check_call_host(
195 channeld->security_connector, call_host, on_host_checked, elem);
Julien Boeuf54b21922015-02-04 16:39:35 -0800196 if (status != GRPC_SECURITY_OK) {
197 if (status == GRPC_SECURITY_ERROR) {
198 char *error_msg;
199 gpr_asprintf(&error_msg,
200 "Invalid host %s set in :authority metadata.",
201 call_host);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700202 grpc_transport_op_add_cancellation(
203 &calld->op, GRPC_STATUS_UNAUTHENTICATED,
204 grpc_mdstr_from_string(channeld->md_ctx, error_msg));
Julien Boeuf54b21922015-02-04 16:39:35 -0800205 gpr_free(error_msg);
Craig Tiller2ea37fd2015-04-24 13:03:49 -0700206 grpc_call_next_op(elem, &calld->op);
Julien Boeuf54b21922015-02-04 16:39:35 -0800207 }
Craig Tiller6e84aba2015-04-23 15:08:17 -0700208 return; /* early exit */
Julien Boeuf54b21922015-02-04 16:39:35 -0800209 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800210 }
Julien Boeuf54b21922015-02-04 16:39:35 -0800211 send_security_metadata(elem, op);
Craig Tiller6e84aba2015-04-23 15:08:17 -0700212 return; /* early exit */
213 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800214 }
Craig Tiller6e84aba2015-04-23 15:08:17 -0700215
216 /* pass control up or down the stack */
217 grpc_call_next_op(elem, op);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800218}
219
220/* Called on special channel events, such as disconnection or new incoming
221 calls on the server */
ctillerf962f522014-12-10 15:28:27 -0800222static void channel_op(grpc_channel_element *elem,
223 grpc_channel_element *from_elem, grpc_channel_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800224 grpc_channel_next_op(elem, op);
225}
226
227/* Constructor for call_data */
228static void init_call_elem(grpc_call_element *elem,
Craig Tiller1a727fd2015-04-24 13:21:22 -0700229 const void *server_transport_data,
230 grpc_transport_op *initial_op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800231 /* TODO(jboeuf):
232 Find a way to pass-in the credentials from the caller here. */
233 call_data *calld = elem->call_data;
234 calld->creds = NULL;
Julien Boeuf54b21922015-02-04 16:39:35 -0800235 calld->host = NULL;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800236 calld->method = NULL;
Craig Tiller4e87e002015-04-24 08:49:10 -0700237 calld->sent_initial_metadata = 0;
Craig Tiller6e84aba2015-04-23 15:08:17 -0700238
239 GPR_ASSERT(!initial_op || !initial_op->send_ops);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800240}
241
242/* Destructor for call_data */
243static void destroy_call_elem(grpc_call_element *elem) {
244 call_data *calld = elem->call_data;
245 if (calld->creds != NULL) {
246 grpc_credentials_unref(calld->creds);
247 }
Julien Boeuf54b21922015-02-04 16:39:35 -0800248 if (calld->host != NULL) {
249 grpc_mdstr_unref(calld->host);
250 }
Craig Tiller7d0f9ea2015-02-23 13:37:07 -0800251 if (calld->method != NULL) {
252 grpc_mdstr_unref(calld->method);
253 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800254}
255
256/* Constructor for channel_data */
257static void init_channel_elem(grpc_channel_element *elem,
258 const grpc_channel_args *args,
259 grpc_mdctx *metadata_context, int is_first,
260 int is_last) {
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700261 grpc_security_connector *ctx = grpc_find_security_connector_in_args(args);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800262 /* grab pointers to our data from the channel element */
263 channel_data *channeld = elem->channel_data;
264
265 /* The first and the last filters tend to be implemented differently to
266 handle the case that there's no 'next' filter to call on the up or down
267 path */
268 GPR_ASSERT(!is_first);
269 GPR_ASSERT(!is_last);
270 GPR_ASSERT(ctx != NULL);
271
272 /* initialize members */
273 GPR_ASSERT(ctx->is_client_side);
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700274 channeld->security_connector =
275 (grpc_channel_security_connector *)grpc_security_connector_ref(ctx);
Julien Boeuf54b21922015-02-04 16:39:35 -0800276 channeld->md_ctx = metadata_context;
277 channeld->authority_string =
278 grpc_mdstr_from_string(channeld->md_ctx, ":authority");
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800279 channeld->path_string = grpc_mdstr_from_string(channeld->md_ctx, ":path");
Julien Boeuf54b21922015-02-04 16:39:35 -0800280 channeld->error_msg_key =
281 grpc_mdstr_from_string(channeld->md_ctx, "grpc-message");
Craig Tiller87d5b192015-04-16 14:37:57 -0700282 channeld->status_key =
283 grpc_mdstr_from_string(channeld->md_ctx, "grpc-status");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800284}
285
286/* Destructor for channel data */
287static void destroy_channel_elem(grpc_channel_element *elem) {
288 /* grab pointers to our data from the channel element */
289 channel_data *channeld = elem->channel_data;
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700290 grpc_channel_security_connector *ctx = channeld->security_connector;
291 if (ctx != NULL) grpc_security_connector_unref(&ctx->base);
Julien Boeuf54b21922015-02-04 16:39:35 -0800292 if (channeld->authority_string != NULL) {
293 grpc_mdstr_unref(channeld->authority_string);
294 }
295 if (channeld->error_msg_key != NULL) {
296 grpc_mdstr_unref(channeld->error_msg_key);
297 }
Craig Tillereb131bb2015-03-03 11:00:40 -0800298 if (channeld->status_key != NULL) {
299 grpc_mdstr_unref(channeld->status_key);
300 }
Craig Tiller7d0f9ea2015-02-23 13:37:07 -0800301 if (channeld->path_string != NULL) {
302 grpc_mdstr_unref(channeld->path_string);
303 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800304}
305
306const grpc_channel_filter grpc_client_auth_filter = {
Craig Tiller1a727fd2015-04-24 13:21:22 -0700307 auth_start_transport_op, channel_op, sizeof(call_data), init_call_elem,
308 destroy_call_elem, sizeof(channel_data), init_channel_elem,
309 destroy_channel_elem, "auth"};