LCOV - code coverage report
Current view: top level - core/security - client_auth_filter.c (source / functions) Hit Total Coverage
Test: tmp.CaZ6RjdVn2 Lines: 142 154 92.2 %
Date: 2015-12-10 22:15:08 Functions: 12 12 100.0 %

          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 "src/core/security/auth_filters.h"
      35             : 
      36             : #include <string.h>
      37             : 
      38             : #include <grpc/support/alloc.h>
      39             : #include <grpc/support/log.h>
      40             : #include <grpc/support/string_util.h>
      41             : 
      42             : #include "src/core/channel/channel_stack.h"
      43             : #include "src/core/security/credentials.h"
      44             : #include "src/core/security/security_connector.h"
      45             : #include "src/core/security/security_context.h"
      46             : #include "src/core/support/string.h"
      47             : #include "src/core/surface/call.h"
      48             : #include "src/core/transport/static_metadata.h"
      49             : 
      50             : #define MAX_CREDENTIALS_METADATA_COUNT 4
      51             : 
      52             : /* We can have a per-call credentials. */
      53             : typedef struct {
      54             :   grpc_call_credentials *creds;
      55             :   grpc_mdstr *host;
      56             :   grpc_mdstr *method;
      57             :   /* pollset bound to this call; if we need to make external
      58             :      network requests, they should be done under this pollset
      59             :      so that work can progress when this call wants work to
      60             :      progress */
      61             :   grpc_pollset *pollset;
      62             :   grpc_transport_stream_op op;
      63             :   gpr_uint8 security_context_set;
      64             :   grpc_linked_mdelem md_links[MAX_CREDENTIALS_METADATA_COUNT];
      65             :   grpc_auth_metadata_context auth_md_context;
      66             : } call_data;
      67             : 
      68             : /* We can have a per-channel credentials. */
      69             : typedef struct {
      70             :   grpc_channel_security_connector *security_connector;
      71             : } channel_data;
      72             : 
      73      204629 : static void reset_auth_metadata_context(
      74             :     grpc_auth_metadata_context *auth_md_context) {
      75      204629 :   if (auth_md_context->service_url != NULL) {
      76       26175 :     gpr_free((char *)auth_md_context->service_url);
      77       26175 :     auth_md_context->service_url = NULL;
      78             :   }
      79      204629 :   if (auth_md_context->method_name != NULL) {
      80       26175 :     gpr_free((char *)auth_md_context->method_name);
      81       26175 :     auth_md_context->method_name = NULL;
      82             :   }
      83      204629 :   GRPC_AUTH_CONTEXT_UNREF(
      84             :       (grpc_auth_context *)auth_md_context->channel_auth_context,
      85             :       "grpc_auth_metadata_context");
      86      204629 :   auth_md_context->channel_auth_context = NULL;
      87      204629 : }
      88             : 
      89           2 : static void bubble_up_error(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
      90             :                             grpc_status_code status, const char *error_msg) {
      91           2 :   call_data *calld = elem->call_data;
      92           2 :   gpr_log(GPR_ERROR, "Client side authentication failure: %s", error_msg);
      93           2 :   grpc_transport_stream_op_add_cancellation(&calld->op, status);
      94           2 :   grpc_call_next_op(exec_ctx, elem, &calld->op);
      95           2 : }
      96             : 
      97       26175 : static void on_credentials_metadata(grpc_exec_ctx *exec_ctx, void *user_data,
      98             :                                     grpc_credentials_md *md_elems,
      99             :                                     size_t num_md,
     100             :                                     grpc_credentials_status status) {
     101       26166 :   grpc_call_element *elem = (grpc_call_element *)user_data;
     102       26175 :   call_data *calld = elem->call_data;
     103       26175 :   grpc_transport_stream_op *op = &calld->op;
     104             :   grpc_metadata_batch *mdb;
     105             :   size_t i;
     106       26175 :   reset_auth_metadata_context(&calld->auth_md_context);
     107       26175 :   if (status != GRPC_CREDENTIALS_OK) {
     108           2 :     bubble_up_error(exec_ctx, elem, GRPC_STATUS_UNAUTHENTICATED,
     109             :                     "Credentials failed to get metadata.");
     110       26177 :     return;
     111             :   }
     112       26173 :   GPR_ASSERT(num_md <= MAX_CREDENTIALS_METADATA_COUNT);
     113       26173 :   GPR_ASSERT(op->send_initial_metadata != NULL);
     114       26164 :   mdb = op->send_initial_metadata;
     115       52349 :   for (i = 0; i < num_md; i++) {
     116       52360 :     grpc_metadata_batch_add_tail(
     117             :         mdb, &calld->md_links[i],
     118       26175 :         grpc_mdelem_from_slices(gpr_slice_ref(md_elems[i].key),
     119       26185 :                                 gpr_slice_ref(md_elems[i].value)));
     120             :   }
     121       26173 :   grpc_call_next_op(exec_ctx, elem, op);
     122             : }
     123             : 
     124       26175 : void build_auth_metadata_context(grpc_security_connector *sc,
     125             :                                  call_data *calld) {
     126       26175 :   char *service = gpr_strdup(grpc_mdstr_as_c_string(calld->method));
     127       26175 :   char *last_slash = strrchr(service, '/');
     128       26166 :   char *method_name = NULL;
     129       26175 :   char *service_url = NULL;
     130       26175 :   reset_auth_metadata_context(&calld->auth_md_context);
     131       26175 :   if (last_slash == NULL) {
     132           0 :     gpr_log(GPR_ERROR, "No '/' found in fully qualified method name");
     133           0 :     service[0] = '\0';
     134       26175 :   } else if (last_slash == service) {
     135             :     /* No service part in fully qualified method name: will just be "/". */
     136       26157 :     service[1] = '\0';
     137             :   } else {
     138          18 :     *last_slash = '\0';
     139          18 :     method_name = gpr_strdup(last_slash + 1);
     140             :   }
     141       26175 :   if (method_name == NULL) method_name = gpr_strdup("");
     142       52350 :   gpr_asprintf(&service_url, "%s://%s%s",
     143       26175 :                sc->url_scheme == NULL ? "" : sc->url_scheme,
     144             :                grpc_mdstr_as_c_string(calld->host), service);
     145       26175 :   calld->auth_md_context.service_url = service_url;
     146       26175 :   calld->auth_md_context.method_name = method_name;
     147       26175 :   calld->auth_md_context.channel_auth_context =
     148       26175 :       GRPC_AUTH_CONTEXT_REF(sc->auth_context, "grpc_auth_metadata_context");
     149       26175 :   gpr_free(service);
     150       26175 : }
     151             : 
     152      152302 : static void send_security_metadata(grpc_exec_ctx *exec_ctx,
     153             :                                    grpc_call_element *elem,
     154             :                                    grpc_transport_stream_op *op) {
     155      152302 :   call_data *calld = elem->call_data;
     156      152302 :   channel_data *chand = elem->channel_data;
     157      152302 :   grpc_client_security_context *ctx =
     158      152302 :       (grpc_client_security_context *)op->context[GRPC_CONTEXT_SECURITY].value;
     159      152302 :   grpc_call_credentials *channel_call_creds =
     160      152302 :       chand->security_connector->request_metadata_creds;
     161      152302 :   int call_creds_has_md = (ctx != NULL) && (ctx->creds != NULL);
     162             : 
     163      152302 :   if (channel_call_creds == NULL && !call_creds_has_md) {
     164             :     /* Skip sending metadata altogether. */
     165      126127 :     grpc_call_next_op(exec_ctx, elem, op);
     166      126127 :     return;
     167             :   }
     168             : 
     169       26175 :   if (channel_call_creds != NULL && call_creds_has_md) {
     170           0 :     calld->creds = grpc_composite_call_credentials_create(channel_call_creds,
     171             :                                                           ctx->creds, NULL);
     172           0 :     if (calld->creds == NULL) {
     173           0 :       bubble_up_error(exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT,
     174             :                       "Incompatible credentials set on channel and call.");
     175           0 :       return;
     176             :     }
     177             :   } else {
     178       26175 :     calld->creds = grpc_call_credentials_ref(
     179             :         call_creds_has_md ? ctx->creds : channel_call_creds);
     180             :   }
     181             : 
     182       26175 :   build_auth_metadata_context(&chand->security_connector->base, calld);
     183       26175 :   calld->op = *op; /* Copy op (originates from the caller's stack). */
     184       26175 :   GPR_ASSERT(calld->pollset);
     185       26175 :   grpc_call_credentials_get_request_metadata(
     186             :       exec_ctx, calld->creds, calld->pollset, calld->auth_md_context,
     187             :       on_credentials_metadata, elem);
     188             : }
     189             : 
     190       26173 : static void on_host_checked(grpc_exec_ctx *exec_ctx, void *user_data,
     191             :                             grpc_security_status status) {
     192       26173 :   grpc_call_element *elem = (grpc_call_element *)user_data;
     193       26173 :   call_data *calld = elem->call_data;
     194             : 
     195       26173 :   if (status == GRPC_SECURITY_OK) {
     196       26173 :     send_security_metadata(exec_ctx, elem, &calld->op);
     197             :   } else {
     198             :     char *error_msg;
     199           0 :     gpr_asprintf(&error_msg, "Invalid host %s set in :authority metadata.",
     200             :                  grpc_mdstr_as_c_string(calld->host));
     201           0 :     bubble_up_error(exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT, error_msg);
     202           0 :     gpr_free(error_msg);
     203             :   }
     204       26173 : }
     205             : 
     206             : /* Called either:
     207             :      - in response to an API call (or similar) from above, to send something
     208             :      - a network event (or similar) from below, to receive something
     209             :    op contains type and call direction information, in addition to the data
     210             :    that is being sent or received. */
     211      153381 : static void auth_start_transport_op(grpc_exec_ctx *exec_ctx,
     212             :                                     grpc_call_element *elem,
     213             :                                     grpc_transport_stream_op *op) {
     214             :   /* grab pointers to our data from the call element */
     215      153381 :   call_data *calld = elem->call_data;
     216      153381 :   channel_data *chand = elem->channel_data;
     217             :   grpc_linked_mdelem *l;
     218      153314 :   grpc_client_security_context *sec_ctx = NULL;
     219             : 
     220      305683 :   if (calld->security_context_set == 0 &&
     221      152302 :       op->cancel_with_status == GRPC_STATUS_OK) {
     222      152302 :     calld->security_context_set = 1;
     223      152302 :     GPR_ASSERT(op->context);
     224      152302 :     if (op->context[GRPC_CONTEXT_SECURITY].value == NULL) {
     225      304536 :       op->context[GRPC_CONTEXT_SECURITY].value =
     226      152277 :           grpc_client_security_context_create();
     227      152277 :       op->context[GRPC_CONTEXT_SECURITY].destroy =
     228             :           grpc_client_security_context_destroy;
     229             :     }
     230      152302 :     sec_ctx = op->context[GRPC_CONTEXT_SECURITY].value;
     231      152302 :     GRPC_AUTH_CONTEXT_UNREF(sec_ctx->auth_context, "client auth filter");
     232      152302 :     sec_ctx->auth_context = GRPC_AUTH_CONTEXT_REF(
     233             :         chand->security_connector->base.auth_context, "client_auth_filter");
     234             :   }
     235             : 
     236      153381 :   if (op->send_initial_metadata != NULL) {
     237     1835839 :     for (l = op->send_initial_metadata->list.head; l != NULL; l = l->next) {
     238     1683537 :       grpc_mdelem *md = l->md;
     239             :       /* Pointer comparison is OK for md_elems created from the same context.
     240             :        */
     241     1683537 :       if (md->key == GRPC_MDSTR_AUTHORITY) {
     242      152302 :         if (calld->host != NULL) GRPC_MDSTR_UNREF(calld->host);
     243      152302 :         calld->host = GRPC_MDSTR_REF(md->value);
     244     1531235 :       } else if (md->key == GRPC_MDSTR_PATH) {
     245      152302 :         if (calld->method != NULL) GRPC_MDSTR_UNREF(calld->method);
     246      152302 :         calld->method = GRPC_MDSTR_REF(md->value);
     247             :       }
     248             :     }
     249      152302 :     if (calld->host != NULL) {
     250             :       grpc_security_status status;
     251      152302 :       const char *call_host = grpc_mdstr_as_c_string(calld->host);
     252      152302 :       calld->op = *op; /* Copy op (originates from the caller's stack). */
     253      152302 :       status = grpc_channel_security_connector_check_call_host(
     254             :           exec_ctx, chand->security_connector, call_host, on_host_checked,
     255             :           elem);
     256      152302 :       if (status != GRPC_SECURITY_OK) {
     257       26173 :         if (status == GRPC_SECURITY_ERROR) {
     258             :           char *error_msg;
     259           0 :           gpr_asprintf(&error_msg,
     260             :                        "Invalid host %s set in :authority metadata.",
     261             :                        call_host);
     262           0 :           bubble_up_error(exec_ctx, elem, GRPC_STATUS_INVALID_ARGUMENT,
     263             :                           error_msg);
     264           0 :           gpr_free(error_msg);
     265             :         }
     266       26173 :         return; /* early exit */
     267             :       }
     268             :     }
     269      126129 :     send_security_metadata(exec_ctx, elem, op);
     270      126129 :     return; /* early exit */
     271             :   }
     272             : 
     273             :   /* pass control down the stack */
     274        1079 :   grpc_call_next_op(exec_ctx, elem, op);
     275             : }
     276             : 
     277             : /* Constructor for call_data */
     278      152303 : static void init_call_elem(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     279             :                            grpc_call_element_args *args) {
     280      152303 :   call_data *calld = elem->call_data;
     281      152303 :   memset(calld, 0, sizeof(*calld));
     282      152303 : }
     283             : 
     284      152303 : static void set_pollset(grpc_exec_ctx *exec_ctx, grpc_call_element *elem,
     285             :                         grpc_pollset *pollset) {
     286      152303 :   call_data *calld = elem->call_data;
     287      152303 :   calld->pollset = pollset;
     288      152303 : }
     289             : 
     290             : /* Destructor for call_data */
     291      152279 : static void destroy_call_elem(grpc_exec_ctx *exec_ctx,
     292             :                               grpc_call_element *elem) {
     293      152279 :   call_data *calld = elem->call_data;
     294      152279 :   grpc_call_credentials_unref(calld->creds);
     295      152279 :   if (calld->host != NULL) {
     296      152278 :     GRPC_MDSTR_UNREF(calld->host);
     297             :   }
     298      152279 :   if (calld->method != NULL) {
     299      152278 :     GRPC_MDSTR_UNREF(calld->method);
     300             :   }
     301      152279 :   reset_auth_metadata_context(&calld->auth_md_context);
     302      152279 : }
     303             : 
     304             : /* Constructor for channel_data */
     305         585 : static void init_channel_elem(grpc_exec_ctx *exec_ctx,
     306             :                               grpc_channel_element *elem,
     307             :                               grpc_channel_element_args *args) {
     308         585 :   grpc_security_connector *sc =
     309         585 :       grpc_find_security_connector_in_args(args->channel_args);
     310             :   /* grab pointers to our data from the channel element */
     311         585 :   channel_data *chand = elem->channel_data;
     312             : 
     313             :   /* The first and the last filters tend to be implemented differently to
     314             :      handle the case that there's no 'next' filter to call on the up or down
     315             :      path */
     316         585 :   GPR_ASSERT(!args->is_last);
     317         585 :   GPR_ASSERT(sc != NULL);
     318             : 
     319             :   /* initialize members */
     320         585 :   GPR_ASSERT(sc->is_client_side);
     321         585 :   chand->security_connector =
     322         585 :       (grpc_channel_security_connector *)GRPC_SECURITY_CONNECTOR_REF(
     323             :           sc, "client_auth_filter");
     324         585 : }
     325             : 
     326             : /* Destructor for channel data */
     327         567 : static void destroy_channel_elem(grpc_exec_ctx *exec_ctx,
     328             :                                  grpc_channel_element *elem) {
     329             :   /* grab pointers to our data from the channel element */
     330         567 :   channel_data *chand = elem->channel_data;
     331         567 :   grpc_channel_security_connector *ctx = chand->security_connector;
     332         567 :   if (ctx != NULL) {
     333         567 :     GRPC_SECURITY_CONNECTOR_UNREF(&ctx->base, "client_auth_filter");
     334             :   }
     335         567 : }
     336             : 
     337             : const grpc_channel_filter grpc_client_auth_filter = {
     338             :     auth_start_transport_op, grpc_channel_next_op, sizeof(call_data),
     339             :     init_call_elem, set_pollset, destroy_call_elem, sizeof(channel_data),
     340             :     init_channel_elem, destroy_channel_elem, grpc_call_next_get_peer,
     341             :     "client-auth"};

Generated by: LCOV version 1.11