blob: 9e49a807f12f2a637e9a0f8eee4452b56ffcffea [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
Julien Boeufc6f8d0a2015-05-11 22:40:02 -070034#include "src/core/security/auth_filters.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080035
36#include <string.h>
37
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080038#include <grpc/support/alloc.h>
39#include <grpc/support/log.h>
Masood Malekghassemi701af602015-06-03 15:01:17 -070040#include <grpc/support/string_util.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080041
Julien Boeuf54b21922015-02-04 16:39:35 -080042#include "src/core/support/string.h"
43#include "src/core/channel/channel_stack.h"
Julien Boeufd7f768b2015-05-08 16:37:16 -070044#include "src/core/security/security_context.h"
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -070045#include "src/core/security/security_connector.h"
Julien Boeuf54b21922015-02-04 16:39:35 -080046#include "src/core/security/credentials.h"
47#include "src/core/surface/call.h"
48
Craig Tiller9c9d4e02015-04-20 09:03:29 -070049#define MAX_CREDENTIALS_METADATA_COUNT 4
Craig Tiller6902ad22015-04-16 08:01:49 -070050
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080051/* We can have a per-call credentials. */
52typedef struct {
53 grpc_credentials *creds;
Julien Boeuf54b21922015-02-04 16:39:35 -080054 grpc_mdstr *host;
Julien Boeuff47a5cb2015-02-18 12:24:08 -080055 grpc_mdstr *method;
Craig Tiller6174b9a2015-06-18 08:13:05 -070056 /* pollset bound to this call; if we need to make external
57 network requests, they should be done under this pollset
58 so that work can progress when this call wants work to
59 progress */
Craig Tiller06bac342015-06-01 12:55:57 -070060 grpc_pollset *pollset;
Craig Tillerb7959a02015-06-25 08:50:54 -070061 grpc_transport_stream_op op;
Craig Tiller6e84aba2015-04-23 15:08:17 -070062 size_t op_md_idx;
63 int sent_initial_metadata;
yang-gd8c466e2015-06-30 09:50:53 -070064 gpr_uint8 security_context_set;
Craig Tiller9c9d4e02015-04-20 09:03:29 -070065 grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080066} call_data;
67
68/* We can have a per-channel credentials. */
69typedef struct {
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -070070 grpc_channel_security_connector *security_connector;
Julien Boeuf54b21922015-02-04 16:39:35 -080071 grpc_mdctx *md_ctx;
72 grpc_mdstr *authority_string;
Julien Boeuff47a5cb2015-02-18 12:24:08 -080073 grpc_mdstr *path_string;
Julien Boeuf54b21922015-02-04 16:39:35 -080074 grpc_mdstr *error_msg_key;
Craig Tillereb131bb2015-03-03 11:00:40 -080075 grpc_mdstr *status_key;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080076} channel_data;
77
Julien Boeuf9f218dd2015-04-23 10:24:02 -070078static void bubble_up_error(grpc_call_element *elem, const char *error_msg) {
79 call_data *calld = elem->call_data;
80 channel_data *chand = elem->channel_data;
Craig Tillerb7959a02015-06-25 08:50:54 -070081 grpc_transport_stream_op_add_cancellation(
Julien Boeuf9f218dd2015-04-23 10:24:02 -070082 &calld->op, GRPC_STATUS_UNAUTHENTICATED,
83 grpc_mdstr_from_string(chand->md_ctx, error_msg));
84 grpc_call_next_op(elem, &calld->op);
85}
86
Julien Boeuf75c9b6f2015-05-29 13:12:12 -070087static void on_credentials_metadata(void *user_data,
88 grpc_credentials_md *md_elems,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080089 size_t num_md,
90 grpc_credentials_status status) {
91 grpc_call_element *elem = (grpc_call_element *)user_data;
Craig Tiller6902ad22015-04-16 08:01:49 -070092 call_data *calld = elem->call_data;
Julien Boeuf75c9b6f2015-05-29 13:12:12 -070093 channel_data *chand = elem->channel_data;
Craig Tillerb7959a02015-06-25 08:50:54 -070094 grpc_transport_stream_op *op = &calld->op;
Craig Tiller6e84aba2015-04-23 15:08:17 -070095 grpc_metadata_batch *mdb;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080096 size_t i;
Julien Boeuf9f218dd2015-04-23 10:24:02 -070097 if (status != GRPC_CREDENTIALS_OK) {
98 bubble_up_error(elem, "Credentials failed to get metadata.");
99 return;
100 }
Craig Tiller9c9d4e02015-04-20 09:03:29 -0700101 GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
Craig Tiller1a727fd2015-04-24 13:21:22 -0700102 GPR_ASSERT(op->send_ops && op->send_ops->nops > calld->op_md_idx &&
103 op->send_ops->ops[calld->op_md_idx].type == GRPC_OP_METADATA);
Craig Tiller6e84aba2015-04-23 15:08:17 -0700104 mdb = &op->send_ops->ops[calld->op_md_idx].data.metadata;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800105 for (i = 0; i < num_md; i++) {
Julien Boeuf75c9b6f2015-05-29 13:12:12 -0700106 grpc_metadata_batch_add_tail(
107 mdb, &calld->md_links[i],
108 grpc_mdelem_from_slices(chand->md_ctx, gpr_slice_ref(md_elems[i].key),
109 gpr_slice_ref(md_elems[i].value)));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800110 }
Craig Tiller6e84aba2015-04-23 15:08:17 -0700111 grpc_call_next_op(elem, op);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800112}
113
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800114static char *build_service_url(const char *url_scheme, call_data *calld) {
115 char *service_url;
116 char *service = gpr_strdup(grpc_mdstr_as_c_string(calld->method));
117 char *last_slash = strrchr(service, '/');
118 if (last_slash == NULL) {
119 gpr_log(GPR_ERROR, "No '/' found in fully qualified method name");
120 service[0] = '\0';
121 } else if (last_slash == service) {
122 /* No service part in fully qualified method name: will just be "/". */
123 service[1] = '\0';
124 } else {
125 *last_slash = '\0';
126 }
127 if (url_scheme == NULL) url_scheme = "";
128 gpr_asprintf(&service_url, "%s://%s%s", url_scheme,
129 grpc_mdstr_as_c_string(calld->host), service);
130 gpr_free(service);
131 return service_url;
132}
133
Craig Tiller1a727fd2015-04-24 13:21:22 -0700134static void send_security_metadata(grpc_call_element *elem,
Craig Tillerb7959a02015-06-25 08:50:54 -0700135 grpc_transport_stream_op *op) {
Julien Boeuf54b21922015-02-04 16:39:35 -0800136 call_data *calld = elem->call_data;
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700137 channel_data *chand = elem->channel_data;
138 grpc_client_security_context *ctx =
Julien Boeuf83b02972015-05-20 22:50:34 -0700139 (grpc_client_security_context *)op->context[GRPC_CONTEXT_SECURITY].value;
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700140 char *service_url = NULL;
Julien Boeuf54b21922015-02-04 16:39:35 -0800141 grpc_credentials *channel_creds =
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700142 chand->security_connector->request_metadata_creds;
143 int channel_creds_has_md =
144 (channel_creds != NULL) &&
145 grpc_credentials_has_request_metadata(channel_creds);
146 int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL) &&
147 grpc_credentials_has_request_metadata(ctx->creds);
148
149 if (!channel_creds_has_md && !call_creds_has_md) {
150 /* Skip sending metadata altogether. */
Julien Boeuf54b21922015-02-04 16:39:35 -0800151 grpc_call_next_op(elem, op);
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700152 return;
Julien Boeuf54b21922015-02-04 16:39:35 -0800153 }
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700154
155 if (channel_creds_has_md && call_creds_has_md) {
156 calld->creds = grpc_composite_credentials_create(channel_creds, ctx->creds);
157 if (calld->creds == NULL) {
158 bubble_up_error(elem,
159 "Incompatible credentials set on channel and call.");
160 return;
161 }
162 } else {
163 calld->creds =
164 grpc_credentials_ref(call_creds_has_md ? ctx->creds : channel_creds);
165 }
166
167 service_url =
168 build_service_url(chand->security_connector->base.url_scheme, calld);
169 calld->op = *op; /* Copy op (originates from the caller's stack). */
Craig Tiller06bac342015-06-01 12:55:57 -0700170 GPR_ASSERT(calld->pollset);
171 grpc_credentials_get_request_metadata(
172 calld->creds, calld->pollset, service_url, on_credentials_metadata, elem);
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700173 gpr_free(service_url);
Julien Boeuf54b21922015-02-04 16:39:35 -0800174}
175
176static void on_host_checked(void *user_data, grpc_security_status status) {
177 grpc_call_element *elem = (grpc_call_element *)user_data;
178 call_data *calld = elem->call_data;
179
180 if (status == GRPC_SECURITY_OK) {
181 send_security_metadata(elem, &calld->op);
182 } else {
183 char *error_msg;
184 gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
185 grpc_mdstr_as_c_string(calld->host));
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700186 bubble_up_error(elem, error_msg);
Julien Boeuf54b21922015-02-04 16:39:35 -0800187 gpr_free(error_msg);
188 }
189}
190
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800191/* Called either:
192 - in response to an API call (or similar) from above, to send something
193 - a network event (or similar) from below, to receive something
194 op contains type and call direction information, in addition to the data
195 that is being sent or received. */
Craig Tiller1a727fd2015-04-24 13:21:22 -0700196static void auth_start_transport_op(grpc_call_element *elem,
Craig Tillerb7959a02015-06-25 08:50:54 -0700197 grpc_transport_stream_op *op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800198 /* grab pointers to our data from the call element */
199 call_data *calld = elem->call_data;
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700200 channel_data *chand = elem->channel_data;
Craig Tiller6902ad22015-04-16 08:01:49 -0700201 grpc_linked_mdelem *l;
Craig Tiller6e84aba2015-04-23 15:08:17 -0700202 size_t i;
yang-gd8c466e2015-06-30 09:50:53 -0700203 grpc_client_security_context* sec_ctx = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800204
yang-gd8c466e2015-06-30 09:50:53 -0700205 if (calld->security_context_set == 0) {
206 calld->security_context_set = 1;
207 GPR_ASSERT(op->context);
208 if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) {
209 op->context[GRPC_CONTEXT_SECURITY].value =
210 grpc_client_security_context_create();
211 op->context[GRPC_CONTEXT_SECURITY].destroy =
212 grpc_client_security_context_destroy;
213 }
214 sec_ctx = op->context[GRPC_CONTEXT_SECURITY].value;
yang-gf9e8e592015-07-09 12:32:15 -0700215 GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter");
216 sec_ctx->auth_context = GRPC_AUTH_CONTEXT_REF(
217 chand->security_connector->base.auth_context, "client_auth_filter");
yang-gd8c466e2015-06-30 09:50:53 -0700218 }
Julien Boeuf84d964a2015-04-29 11:31:06 -0700219
Craig Tiller06bac342015-06-01 12:55:57 -0700220 if (op->bind_pollset) {
221 calld->pollset = op->bind_pollset;
222 }
223
Craig Tiller6e84aba2015-04-23 15:08:17 -0700224 if (op->send_ops && !calld->sent_initial_metadata) {
225 size_t nops = op->send_ops->nops;
226 grpc_stream_op *ops = op->send_ops->ops;
227 for (i = 0; i < nops; i++) {
228 grpc_stream_op *sop = &ops[i];
229 if (sop->type != GRPC_OP_METADATA) continue;
Craig Tiller4e87e002015-04-24 08:49:10 -0700230 calld->op_md_idx = i;
Craig Tiller6e84aba2015-04-23 15:08:17 -0700231 calld->sent_initial_metadata = 1;
232 for (l = sop->data.metadata.list.head; l != NULL; l = l->next) {
Craig Tiller6902ad22015-04-16 08:01:49 -0700233 grpc_mdelem *md = l->md;
Craig Tiller87d5b192015-04-16 14:37:57 -0700234 /* Pointer comparison is OK for md_elems created from the same context.
235 */
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700236 if (md->key == chand->authority_string) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700237 if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host);
238 calld->host = GRPC_MDSTR_REF(md->value);
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700239 } else if (md->key == chand->path_string) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700240 if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method);
241 calld->method = GRPC_MDSTR_REF(md->value);
Craig Tiller6902ad22015-04-16 08:01:49 -0700242 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800243 }
Julien Boeuf54b21922015-02-04 16:39:35 -0800244 if (calld->host != NULL) {
245 grpc_security_status status;
246 const char *call_host = grpc_mdstr_as_c_string(calld->host);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800247 calld->op = *op; /* Copy op (originates from the caller's stack). */
Julien Boeuf7d1d9ca2015-04-17 14:38:48 -0700248 status = grpc_channel_security_connector_check_call_host(
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700249 chand->security_connector, call_host, on_host_checked, elem);
Julien Boeuf54b21922015-02-04 16:39:35 -0800250 if (status != GRPC_SECURITY_OK) {
251 if (status == GRPC_SECURITY_ERROR) {
252 char *error_msg;
253 gpr_asprintf(&error_msg,
254 "Invalid host %s set in :authority metadata.",
255 call_host);
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700256 bubble_up_error(elem, error_msg);
Julien Boeuf54b21922015-02-04 16:39:35 -0800257 gpr_free(error_msg);
258 }
Craig Tiller6e84aba2015-04-23 15:08:17 -0700259 return; /* early exit */
Julien Boeuf54b21922015-02-04 16:39:35 -0800260 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800261 }
Julien Boeuf54b21922015-02-04 16:39:35 -0800262 send_security_metadata(elem, op);
Craig Tiller6e84aba2015-04-23 15:08:17 -0700263 return; /* early exit */
264 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800265 }
Craig Tiller6e84aba2015-04-23 15:08:17 -0700266
267 /* pass control up or down the stack */
268 grpc_call_next_op(elem, op);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800269}
270
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800271/* Constructor for call_data */
272static void init_call_elem(grpc_call_element *elem,
Craig Tiller1a727fd2015-04-24 13:21:22 -0700273 const void *server_transport_data,
Craig Tillerb7959a02015-06-25 08:50:54 -0700274 grpc_transport_stream_op *initial_op) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800275 call_data *calld = elem->call_data;
276 calld->creds = NULL;
Julien Boeuf54b21922015-02-04 16:39:35 -0800277 calld->host = NULL;
Julien Boeuff47a5cb2015-02-18 12:24:08 -0800278 calld->method = NULL;
Craig Tiller06bac342015-06-01 12:55:57 -0700279 calld->pollset = NULL;
Craig Tiller4e87e002015-04-24 08:49:10 -0700280 calld->sent_initial_metadata = 0;
yang-gd8c466e2015-06-30 09:50:53 -0700281 calld->security_context_set = 0;
Craig Tiller6e84aba2015-04-23 15:08:17 -0700282
283 GPR_ASSERT(!initial_op || !initial_op->send_ops);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800284}
285
286/* Destructor for call_data */
287static void destroy_call_elem(grpc_call_element *elem) {
288 call_data *calld = elem->call_data;
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700289 grpc_credentials_unref(calld->creds);
Julien Boeuf54b21922015-02-04 16:39:35 -0800290 if (calld->host != NULL) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700291 GRPC_MDSTR_UNREF(calld->host);
Julien Boeuf54b21922015-02-04 16:39:35 -0800292 }
Craig Tiller7d0f9ea2015-02-23 13:37:07 -0800293 if (calld->method != NULL) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700294 GRPC_MDSTR_UNREF(calld->method);
Craig Tiller7d0f9ea2015-02-23 13:37:07 -0800295 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800296}
297
298/* Constructor for channel_data */
Craig Tiller079a11b2015-06-30 10:07:15 -0700299static void init_channel_elem(grpc_channel_element *elem, grpc_channel *master,
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800300 const grpc_channel_args *args,
301 grpc_mdctx *metadata_context, int is_first,
302 int is_last) {
Julien Boeufc6f8d0a2015-05-11 22:40:02 -0700303 grpc_security_connector *sc = grpc_find_security_connector_in_args(args);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800304 /* grab pointers to our data from the channel element */
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700305 channel_data *chand = elem->channel_data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800306
307 /* The first and the last filters tend to be implemented differently to
308 handle the case that there's no 'next' filter to call on the up or down
309 path */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800310 GPR_ASSERT(!is_last);
Julien Boeufc6f8d0a2015-05-11 22:40:02 -0700311 GPR_ASSERT(sc != NULL);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800312
313 /* initialize members */
Julien Boeufc6f8d0a2015-05-11 22:40:02 -0700314 GPR_ASSERT(sc->is_client_side);
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700315 chand->security_connector =
Craig Tillerb3671532015-07-01 10:37:40 -0700316 (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF(
317 sc, "client_auth_filter");
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700318 chand->md_ctx = metadata_context;
Craig Tiller8674cb12015-06-05 07:09:25 -0700319 chand->authority_string = grpc_mdstr_from_string(chand->md_ctx, ":authority");
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700320 chand->path_string = grpc_mdstr_from_string(chand->md_ctx, ":path");
Craig Tiller8674cb12015-06-05 07:09:25 -0700321 chand->error_msg_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-message");
322 chand->status_key = grpc_mdstr_from_string(chand->md_ctx, "grpc-status");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800323}
324
325/* Destructor for channel data */
326static void destroy_channel_elem(grpc_channel_element *elem) {
327 /* grab pointers to our data from the channel element */
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700328 channel_data *chand = elem->channel_data;
329 grpc_channel_security_connector *ctx = chand->security_connector;
Craig Tillerb3671532015-07-01 10:37:40 -0700330 if (ctx != NULL)
331 GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter");
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700332 if (chand->authority_string != NULL) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700333 GRPC_MDSTR_UNREF(chand->authority_string);
Julien Boeuf54b21922015-02-04 16:39:35 -0800334 }
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700335 if (chand->error_msg_key != NULL) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700336 GRPC_MDSTR_UNREF(chand->error_msg_key);
Julien Boeuf54b21922015-02-04 16:39:35 -0800337 }
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700338 if (chand->status_key != NULL) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700339 GRPC_MDSTR_UNREF(chand->status_key);
Craig Tillereb131bb2015-03-03 11:00:40 -0800340 }
Julien Boeuf9f218dd2015-04-23 10:24:02 -0700341 if (chand->path_string != NULL) {
Craig Tiller1a65a232015-07-06 10:22:32 -0700342 GRPC_MDSTR_UNREF(chand->path_string);
Craig Tiller7d0f9ea2015-02-23 13:37:07 -0800343 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800344}
345
346const grpc_channel_filter grpc_client_auth_filter = {
Craig Tiller079a11b2015-06-30 10:07:15 -0700347 auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
Craig Tiller8674cb12015-06-05 07:09:25 -0700348 init_call_elem, destroy_call_elem, sizeof(channel_data),
349 init_channel_elem, destroy_channel_elem, "client-auth"};