Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * 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 <string.h>
35 :
36 : #include "src/core/security/auth_filters.h"
37 : #include "src/core/security/credentials.h"
38 : #include "src/core/security/security_context.h"
39 :
40 : #include <grpc/support/alloc.h>
41 : #include <grpc/support/log.h>
42 :
43 : typedef struct call_data {
44 : gpr_uint8 got_client_metadata;
45 : grpc_stream_op_buffer *recv_ops;
46 : /* Closure to call when finished with the auth_on_recv hook. */
47 : grpc_closure *on_done_recv;
48 : /* Receive closures are chained: we inject this closure as the on_done_recv
49 : up-call on transport_op, and remember to call our on_done_recv member after
50 : handling it. */
51 : grpc_closure auth_on_recv;
52 : grpc_transport_stream_op transport_op;
53 : grpc_metadata_array md;
54 : const grpc_metadata *consumed_md;
55 : size_t num_consumed_md;
56 : grpc_stream_op *md_op;
57 : grpc_auth_context *auth_context;
58 : } call_data;
59 :
60 : typedef struct channel_data {
61 : grpc_auth_context *auth_context;
62 : grpc_server_credentials *creds;
63 : grpc_mdctx *mdctx;
64 : } channel_data;
65 :
66 350 : static grpc_metadata_array metadata_batch_to_md_array(
67 : const grpc_metadata_batch *batch) {
68 : grpc_linked_mdelem *l;
69 : grpc_metadata_array result;
70 350 : grpc_metadata_array_init(&result);
71 2238 : for (l = batch->list.head; l != NULL; l = l->next) {
72 1888 : grpc_metadata *usr_md = NULL;
73 1888 : grpc_mdelem *md = l->md;
74 1888 : grpc_mdstr *key = md->key;
75 1888 : grpc_mdstr *value = md->value;
76 1888 : if (result.count == result.capacity) {
77 350 : result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2);
78 350 : result.metadata =
79 350 : gpr_realloc(result.metadata, result.capacity * sizeof(grpc_metadata));
80 : }
81 1888 : usr_md = &result.metadata[result.count++];
82 1888 : usr_md->key = grpc_mdstr_as_c_string(key);
83 1888 : usr_md->value = grpc_mdstr_as_c_string(value);
84 1888 : usr_md->value_length = GPR_SLICE_LENGTH(value->slice);
85 : }
86 350 : return result;
87 : }
88 :
89 1840 : static grpc_mdelem *remove_consumed_md(void *user_data, grpc_mdelem *md) {
90 1840 : grpc_call_element *elem = user_data;
91 1840 : call_data *calld = elem->call_data;
92 : size_t i;
93 2396 : for (i = 0; i < calld->num_consumed_md; i++) {
94 665 : const grpc_metadata *consumed_md = &calld->consumed_md[i];
95 : /* Maybe we could do a pointer comparison but we do not have any guarantee
96 : that the metadata processor used the same pointers for consumed_md in the
97 : callback. */
98 881 : if (GPR_SLICE_LENGTH(md->key->slice) != strlen(consumed_md->key) ||
99 216 : GPR_SLICE_LENGTH(md->value->slice) != consumed_md->value_length) {
100 556 : continue;
101 : }
102 218 : if (memcmp(GPR_SLICE_START_PTR(md->key->slice), consumed_md->key,
103 327 : GPR_SLICE_LENGTH(md->key->slice)) == 0 &&
104 218 : memcmp(GPR_SLICE_START_PTR(md->value->slice), consumed_md->value,
105 218 : GPR_SLICE_LENGTH(md->value->slice)) == 0) {
106 109 : return NULL; /* Delete. */
107 : }
108 : }
109 1731 : return md;
110 : }
111 :
112 : /* called from application code */
113 350 : static void on_md_processing_done(
114 : void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md,
115 : const grpc_metadata *response_md, size_t num_response_md,
116 : grpc_status_code status, const char *error_details) {
117 350 : grpc_call_element *elem = user_data;
118 350 : call_data *calld = elem->call_data;
119 350 : grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
120 :
121 : /* TODO(jboeuf): Implement support for response_md. */
122 350 : if (response_md != NULL && num_response_md > 0) {
123 0 : gpr_log(GPR_INFO,
124 : "response_md in auth metadata processing not supported for now. "
125 : "Ignoring...");
126 : }
127 :
128 350 : if (status == GRPC_STATUS_OK) {
129 343 : calld->consumed_md = consumed_md;
130 343 : calld->num_consumed_md = num_consumed_md;
131 343 : grpc_metadata_batch_filter(&calld->md_op->data.metadata, remove_consumed_md,
132 : elem);
133 343 : grpc_metadata_array_destroy(&calld->md);
134 343 : calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 1);
135 : } else {
136 : gpr_slice message;
137 7 : grpc_metadata_array_destroy(&calld->md);
138 7 : error_details = error_details != NULL
139 : ? error_details
140 7 : : "Authentication metadata processing failed.";
141 7 : message = gpr_slice_from_copied_string(error_details);
142 7 : grpc_sopb_reset(calld->recv_ops);
143 7 : grpc_transport_stream_op_add_close(&calld->transport_op, status, &message);
144 7 : grpc_call_next_op(&exec_ctx, elem, &calld->transport_op);
145 : }
146 :
147 350 : grpc_exec_ctx_finish(&exec_ctx);
148 350 : }
149 :
150 876 : static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
151 : int success) {
152 876 : grpc_call_element *elem = user_data;
153 876 : call_data *calld = elem->call_data;
154 876 : channel_data *chand = elem->channel_data;
155 876 : if (success) {
156 : size_t i;
157 876 : size_t nops = calld->recv_ops->nops;
158 876 : grpc_stream_op *ops = calld->recv_ops->ops;
159 1701 : for (i = 0; i < nops; i++) {
160 1175 : grpc_stream_op *op = &ops[i];
161 1175 : if (op->type != GRPC_OP_METADATA || calld->got_client_metadata) continue;
162 869 : calld->got_client_metadata = 1;
163 869 : if (chand->creds->processor.process == NULL) continue;
164 350 : calld->md_op = op;
165 350 : calld->md = metadata_batch_to_md_array(&op->data.metadata);
166 1050 : chand->creds->processor.process(
167 350 : chand->creds->processor.state, calld->auth_context,
168 350 : calld->md.metadata, calld->md.count, on_md_processing_done, elem);
169 1226 : return;
170 : }
171 : }
172 526 : calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
173 : }
174 :
175 4533 : static void set_recv_ops_md_callbacks(grpc_call_element *elem,
176 : grpc_transport_stream_op *op) {
177 4533 : call_data *calld = elem->call_data;
178 :
179 4533 : if (op->recv_ops && !calld->got_client_metadata) {
180 : /* substitute our callback for the higher callback */
181 869 : calld->recv_ops = op->recv_ops;
182 869 : calld->on_done_recv = op->on_done_recv;
183 869 : op->on_done_recv = &calld->auth_on_recv;
184 869 : calld->transport_op = *op;
185 : }
186 4533 : }
187 :
188 : /* Called either:
189 : - in response to an API call (or similar) from above, to send something
190 : - a network event (or similar) from below, to receive something
191 : op contains type and call direction information, in addition to the data
192 : that is being sent or received. */
193 3664 : static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
194 : grpc_call_element *elem,
195 : grpc_transport_stream_op *op) {
196 3664 : set_recv_ops_md_callbacks(elem, op);
197 3664 : grpc_call_next_op(exec_ctx, elem, op);
198 3664 : }
199 :
200 : /* Constructor for call_data */
201 869 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
202 : const void *server_transport_data,
203 : grpc_transport_stream_op *initial_op) {
204 : /* grab pointers to our data from the call element */
205 869 : call_data *calld = elem->call_data;
206 869 : channel_data *chand = elem->channel_data;
207 869 : grpc_server_security_context *server_ctx = NULL;
208 :
209 : /* initialize members */
210 869 : memset(calld, 0, sizeof(*calld));
211 869 : grpc_closure_init(&calld->auth_on_recv, auth_on_recv, elem);
212 :
213 869 : GPR_ASSERT(initial_op && initial_op->context != NULL &&
214 : initial_op->context[GRPC_CONTEXT_SECURITY].value == NULL);
215 :
216 : /* Create a security context for the call and reference the auth context from
217 : the channel. */
218 869 : if (initial_op->context[GRPC_CONTEXT_SECURITY].value != NULL) {
219 0 : initial_op->context[GRPC_CONTEXT_SECURITY].destroy(
220 0 : initial_op->context[GRPC_CONTEXT_SECURITY].value);
221 : }
222 869 : server_ctx = grpc_server_security_context_create();
223 869 : server_ctx->auth_context =
224 869 : grpc_auth_context_create(chand->auth_context);
225 869 : server_ctx->auth_context->pollset = initial_op->bind_pollset;
226 869 : initial_op->context[GRPC_CONTEXT_SECURITY].value = server_ctx;
227 869 : initial_op->context[GRPC_CONTEXT_SECURITY].destroy =
228 : grpc_server_security_context_destroy;
229 869 : calld->auth_context = server_ctx->auth_context;
230 :
231 : /* Set the metadata callbacks. */
232 869 : set_recv_ops_md_callbacks(elem, initial_op);
233 869 : }
234 :
235 : /* Destructor for call_data */
236 869 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
237 869 : grpc_call_element *elem) {}
238 :
239 : /* Constructor for channel_data */
240 440 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
241 : grpc_channel_element *elem, grpc_channel *master,
242 : const grpc_channel_args *args, grpc_mdctx *mdctx,
243 : int is_first, int is_last) {
244 440 : grpc_auth_context *auth_context = grpc_find_auth_context_in_args(args);
245 440 : grpc_server_credentials *creds = grpc_find_server_credentials_in_args(args);
246 : /* grab pointers to our data from the channel element */
247 440 : channel_data *chand = elem->channel_data;
248 :
249 : /* The first and the last filters tend to be implemented differently to
250 : handle the case that there's no 'next' filter to call on the up or down
251 : path */
252 440 : GPR_ASSERT(!is_first);
253 440 : GPR_ASSERT(!is_last);
254 440 : GPR_ASSERT(auth_context != NULL);
255 440 : GPR_ASSERT(creds != NULL);
256 :
257 : /* initialize members */
258 440 : chand->auth_context =
259 440 : GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter");
260 440 : chand->creds = grpc_server_credentials_ref(creds);
261 440 : chand->mdctx = mdctx;
262 440 : }
263 :
264 : /* Destructor for channel data */
265 440 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
266 : grpc_channel_element *elem) {
267 : /* grab pointers to our data from the channel element */
268 440 : channel_data *chand = elem->channel_data;
269 440 : GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "server_auth_filter");
270 440 : grpc_server_credentials_unref(chand->creds);
271 440 : }
272 :
273 : const grpc_channel_filter grpc_server_auth_filter = {
274 : auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
275 : init_call_elem, destroy_call_elem, sizeof(channel_data),
276 : init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer,
277 : "server-auth"};
|