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 : grpc_metadata_batch *recv_initial_metadata;
45 : /* Closure to call when finished with the auth_on_recv hook. */
46 : grpc_closure *on_done_recv;
47 : /* Receive closures are chained: we inject this closure as the on_done_recv
48 : up-call on transport_op, and remember to call our on_done_recv member after
49 : handling it. */
50 : grpc_closure auth_on_recv;
51 : grpc_transport_stream_op transport_op;
52 : grpc_metadata_array md;
53 : const grpc_metadata *consumed_md;
54 : size_t num_consumed_md;
55 : grpc_auth_context *auth_context;
56 : } call_data;
57 :
58 : typedef struct channel_data {
59 : grpc_auth_context *auth_context;
60 : grpc_server_credentials *creds;
61 : } channel_data;
62 :
63 26389 : static grpc_metadata_array metadata_batch_to_md_array(
64 : const grpc_metadata_batch *batch) {
65 : grpc_linked_mdelem *l;
66 : grpc_metadata_array result;
67 26389 : grpc_metadata_array_init(&result);
68 262696 : for (l = batch->list.head; l != NULL; l = l->next) {
69 236307 : grpc_metadata *usr_md = NULL;
70 236307 : grpc_mdelem *md = l->md;
71 236307 : grpc_mdstr *key = md->key;
72 236307 : grpc_mdstr *value = md->value;
73 236307 : if (result.count == result.capacity) {
74 52453 : result.capacity = GPR_MAX(result.capacity + 8, result.capacity * 2);
75 52453 : result.metadata =
76 52453 : gpr_realloc(result.metadata, result.capacity * sizeof(grpc_metadata));
77 : }
78 236307 : usr_md = &result.metadata[result.count++];
79 236307 : usr_md->key = grpc_mdstr_as_c_string(key);
80 236307 : usr_md->value = grpc_mdstr_as_c_string(value);
81 236307 : usr_md->value_length = GPR_SLICE_LENGTH(value->slice);
82 : }
83 26389 : return result;
84 : }
85 :
86 236274 : static grpc_mdelem *remove_consumed_md(void *user_data, grpc_mdelem *md) {
87 236274 : grpc_call_element *elem = user_data;
88 236274 : call_data *calld = elem->call_data;
89 : size_t i;
90 445223 : for (i = 0; i < calld->num_consumed_md; i++) {
91 235099 : const grpc_metadata *consumed_md = &calld->consumed_md[i];
92 : /* Maybe we could do a pointer comparison but we do not have any guarantee
93 : that the metadata processor used the same pointers for consumed_md in the
94 : callback. */
95 287397 : if (GPR_SLICE_LENGTH(md->key->slice) != strlen(consumed_md->key) ||
96 52298 : GPR_SLICE_LENGTH(md->value->slice) != consumed_md->value_length) {
97 208949 : continue;
98 : }
99 52300 : if (memcmp(GPR_SLICE_START_PTR(md->key->slice), consumed_md->key,
100 78450 : GPR_SLICE_LENGTH(md->key->slice)) == 0 &&
101 52300 : memcmp(GPR_SLICE_START_PTR(md->value->slice), consumed_md->value,
102 52300 : GPR_SLICE_LENGTH(md->value->slice)) == 0) {
103 26150 : return NULL; /* Delete. */
104 : }
105 : }
106 210124 : return md;
107 : }
108 :
109 : /* called from application code */
110 26389 : static void on_md_processing_done(
111 : void *user_data, const grpc_metadata *consumed_md, size_t num_consumed_md,
112 : const grpc_metadata *response_md, size_t num_response_md,
113 : grpc_status_code status, const char *error_details) {
114 26389 : grpc_call_element *elem = user_data;
115 26389 : call_data *calld = elem->call_data;
116 26389 : grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
117 :
118 : /* TODO(jboeuf): Implement support for response_md. */
119 26389 : if (response_md != NULL && num_response_md > 0) {
120 0 : gpr_log(GPR_INFO,
121 : "response_md in auth metadata processing not supported for now. "
122 : "Ignoring...");
123 : }
124 :
125 26389 : if (status == GRPC_STATUS_OK) {
126 26384 : calld->consumed_md = consumed_md;
127 26384 : calld->num_consumed_md = num_consumed_md;
128 26384 : grpc_metadata_batch_filter(calld->recv_initial_metadata, remove_consumed_md,
129 : elem);
130 26384 : grpc_metadata_array_destroy(&calld->md);
131 26384 : calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 1);
132 : } else {
133 : gpr_slice message;
134 : grpc_transport_stream_op close_op;
135 5 : memset(&close_op, 0, sizeof(close_op));
136 5 : grpc_metadata_array_destroy(&calld->md);
137 5 : error_details = error_details != NULL
138 : ? error_details
139 5 : : "Authentication metadata processing failed.";
140 5 : message = gpr_slice_from_copied_string(error_details);
141 5 : calld->transport_op.send_initial_metadata = NULL;
142 5 : if (calld->transport_op.send_message != NULL) {
143 0 : grpc_byte_stream_destroy(calld->transport_op.send_message);
144 0 : calld->transport_op.send_message = NULL;
145 : }
146 5 : calld->transport_op.send_trailing_metadata = NULL;
147 5 : grpc_transport_stream_op_add_close(&close_op, status, &message);
148 5 : grpc_call_next_op(&exec_ctx, elem, &close_op);
149 5 : calld->on_done_recv->cb(&exec_ctx, calld->on_done_recv->cb_arg, 0);
150 : }
151 :
152 26389 : grpc_exec_ctx_finish(&exec_ctx);
153 26389 : }
154 :
155 152299 : static void auth_on_recv(grpc_exec_ctx *exec_ctx, void *user_data,
156 : int success) {
157 152276 : grpc_call_element *elem = user_data;
158 152299 : call_data *calld = elem->call_data;
159 152299 : channel_data *chand = elem->channel_data;
160 152299 : if (success) {
161 152299 : if (chand->creds->processor.process != NULL) {
162 26389 : calld->md = metadata_batch_to_md_array(calld->recv_initial_metadata);
163 79167 : chand->creds->processor.process(
164 26389 : chand->creds->processor.state, calld->auth_context,
165 26389 : calld->md.metadata, calld->md.count, on_md_processing_done, elem);
166 178688 : return;
167 : }
168 : }
169 125910 : calld->on_done_recv->cb(exec_ctx, calld->on_done_recv->cb_arg, success);
170 : }
171 :
172 400616 : static void set_recv_ops_md_callbacks(grpc_call_element *elem,
173 : grpc_transport_stream_op *op) {
174 400616 : call_data *calld = elem->call_data;
175 :
176 400616 : if (op->recv_initial_metadata != NULL) {
177 : /* substitute our callback for the higher callback */
178 152299 : calld->recv_initial_metadata = op->recv_initial_metadata;
179 152299 : calld->on_done_recv = op->on_complete;
180 152299 : op->on_complete = &calld->auth_on_recv;
181 152299 : calld->transport_op = *op;
182 : }
183 400616 : }
184 :
185 : /* Called either:
186 : - in response to an API call (or similar) from above, to send something
187 : - a network event (or similar) from below, to receive something
188 : op contains type and call direction information, in addition to the data
189 : that is being sent or received. */
190 400616 : static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
191 : grpc_call_element *elem,
192 : grpc_transport_stream_op *op) {
193 400616 : set_recv_ops_md_callbacks(elem, op);
194 400616 : grpc_call_next_op(exec_ctx, elem, op);
195 400616 : }
196 :
197 : /* Constructor for call_data */
198 152299 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
199 : grpc_call_element_args *args) {
200 : /* grab pointers to our data from the call element */
201 152299 : call_data *calld = elem->call_data;
202 152299 : channel_data *chand = elem->channel_data;
203 152276 : grpc_server_security_context *server_ctx = NULL;
204 :
205 : /* initialize members */
206 152299 : memset(calld, 0, sizeof(*calld));
207 152299 : grpc_closure_init(&calld->auth_on_recv, auth_on_recv, elem);
208 :
209 152299 : if (args->context[GRPC_CONTEXT_SECURITY].value != NULL) {
210 0 : args->context[GRPC_CONTEXT_SECURITY].destroy(
211 0 : args->context[GRPC_CONTEXT_SECURITY].value);
212 : }
213 :
214 152299 : server_ctx = grpc_server_security_context_create();
215 152299 : server_ctx->auth_context = grpc_auth_context_create(chand->auth_context);
216 152299 : calld->auth_context = server_ctx->auth_context;
217 :
218 152299 : args->context[GRPC_CONTEXT_SECURITY].value = server_ctx;
219 152299 : args->context[GRPC_CONTEXT_SECURITY].destroy =
220 : grpc_server_security_context_destroy;
221 152299 : }
222 :
223 152273 : static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
224 152273 : grpc_pollset *pollset) {}
225 :
226 : /* Destructor for call_data */
227 152284 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
228 152284 : grpc_call_element *elem) {}
229 :
230 : /* Constructor for channel_data */
231 590 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
232 : grpc_channel_element *elem,
233 : grpc_channel_element_args *args) {
234 590 : grpc_auth_context *auth_context =
235 590 : grpc_find_auth_context_in_args(args->channel_args);
236 590 : grpc_server_credentials *creds =
237 590 : grpc_find_server_credentials_in_args(args->channel_args);
238 : /* grab pointers to our data from the channel element */
239 590 : channel_data *chand = elem->channel_data;
240 :
241 590 : GPR_ASSERT(!args->is_last);
242 590 : GPR_ASSERT(auth_context != NULL);
243 590 : GPR_ASSERT(creds != NULL);
244 :
245 : /* initialize members */
246 590 : chand->auth_context =
247 590 : GRPC_AUTH_CONTEXT_REF(auth_context, "server_auth_filter");
248 590 : chand->creds = grpc_server_credentials_ref(creds);
249 590 : }
250 :
251 : /* Destructor for channel data */
252 578 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
253 : grpc_channel_element *elem) {
254 : /* grab pointers to our data from the channel element */
255 578 : channel_data *chand = elem->channel_data;
256 578 : GRPC_AUTH_CONTEXT_UNREF(chand->auth_context, "server_auth_filter");
257 578 : grpc_server_credentials_unref(chand->creds);
258 578 : }
259 :
260 : const grpc_channel_filter grpc_server_auth_filter = {
261 : auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
262 : init_call_elem, set_pollset, destroy_call_elem, sizeof(channel_data),
263 : init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer,
264 : "server-auth"};
|