Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1 | /* |
| 2 | * |
| 3 | * Copyright 2014, 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/security_context.h" |
| 35 | |
| 36 | #include <string.h> |
| 37 | |
| 38 | #include "src/core/endpoint/secure_endpoint.h" |
| 39 | #include "src/core/security/credentials.h" |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 40 | #include "src/core/surface/lame_client.h" |
| 41 | #include "src/core/transport/chttp2/alpn.h" |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 42 | #include <grpc/support/alloc.h> |
| 43 | #include <grpc/support/log.h> |
| 44 | #include <grpc/support/slice_buffer.h> |
| 45 | #include <grpc/support/string.h> |
| 46 | |
| 47 | #include "src/core/tsi/fake_transport_security.h" |
| 48 | #include "src/core/tsi/ssl_transport_security.h" |
| 49 | |
| 50 | /* -- Constants. -- */ |
| 51 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 52 | /* Defines the cipher suites that we accept. All these cipher suites are |
| 53 | compliant with TLS 1.2 and use an RSA public key. We prefer GCM over CBC |
| 54 | and ECDHE-RSA over just RSA. */ |
| 55 | #define GRPC_SSL_CIPHER_SUITES \ |
| 56 | "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:AES128-GCM-SHA256:" \ |
| 57 | "AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:AES128-" \ |
| 58 | "SHA256:AES256-SHA256" |
| 59 | |
| 60 | /* -- Common methods. -- */ |
| 61 | |
| 62 | grpc_security_status grpc_security_context_create_handshaker( |
| 63 | grpc_security_context *ctx, tsi_handshaker **handshaker) { |
| 64 | if (ctx == NULL || handshaker == NULL) return GRPC_SECURITY_ERROR; |
| 65 | return ctx->vtable->create_handshaker(ctx, handshaker); |
| 66 | } |
| 67 | |
| 68 | grpc_security_status grpc_security_context_check_peer( |
| 69 | grpc_security_context *ctx, const tsi_peer *peer, |
| 70 | grpc_security_check_peer_cb cb, void *user_data) { |
| 71 | if (ctx == NULL) return GRPC_SECURITY_ERROR; |
| 72 | return ctx->vtable->check_peer(ctx, peer, cb, user_data); |
| 73 | } |
| 74 | |
| 75 | void grpc_security_context_unref(grpc_security_context *ctx) { |
| 76 | if (ctx == NULL) return; |
| 77 | if (gpr_unref(&ctx->refcount)) ctx->vtable->destroy(ctx); |
| 78 | } |
| 79 | |
| 80 | grpc_security_context *grpc_security_context_ref(grpc_security_context *ctx) { |
| 81 | if (ctx == NULL) return NULL; |
| 82 | gpr_ref(&ctx->refcount); |
| 83 | return ctx; |
| 84 | } |
| 85 | |
| 86 | static void context_pointer_arg_destroy(void *p) { |
| 87 | grpc_security_context_unref(p); |
| 88 | } |
| 89 | |
| 90 | static void *context_pointer_arg_copy(void *p) { |
| 91 | return grpc_security_context_ref(p); |
| 92 | } |
| 93 | |
| 94 | grpc_arg grpc_security_context_to_arg(grpc_security_context *ctx) { |
| 95 | grpc_arg result; |
| 96 | result.type = GRPC_ARG_POINTER; |
| 97 | result.key = GRPC_SECURITY_CONTEXT_ARG; |
| 98 | result.value.pointer.destroy = context_pointer_arg_destroy; |
| 99 | result.value.pointer.copy = context_pointer_arg_copy; |
| 100 | result.value.pointer.p = ctx; |
| 101 | return result; |
| 102 | } |
| 103 | |
| 104 | grpc_security_context *grpc_security_context_from_arg( |
| 105 | const grpc_arg *arg) { |
| 106 | if (strcmp(arg->key, GRPC_SECURITY_CONTEXT_ARG)) return NULL; |
| 107 | if (arg->type != GRPC_ARG_POINTER) { |
| 108 | gpr_log(GPR_ERROR, "Invalid type %d for arg %s", arg->type, |
| 109 | GRPC_SECURITY_CONTEXT_ARG); |
| 110 | return NULL; |
| 111 | } |
| 112 | return arg->value.pointer.p; |
| 113 | } |
| 114 | |
| 115 | grpc_security_context *grpc_find_security_context_in_args( |
| 116 | const grpc_channel_args *args) { |
| 117 | size_t i; |
| 118 | if (args == NULL) return NULL; |
| 119 | for (i = 0; i < args->num_args; i++) { |
| 120 | grpc_security_context *ctx = grpc_security_context_from_arg(&args->args[i]); |
| 121 | if (ctx != NULL) return ctx; |
| 122 | } |
| 123 | return NULL; |
| 124 | } |
| 125 | |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 126 | static int check_request_metadata_creds(grpc_credentials *creds) { |
| 127 | if (creds != NULL && !grpc_credentials_has_request_metadata(creds)) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 128 | gpr_log(GPR_ERROR, |
| 129 | "Incompatible credentials for channel security context: needs to " |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 130 | "set request metadata."); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 131 | return 0; |
| 132 | } |
| 133 | return 1; |
| 134 | } |
| 135 | |
| 136 | /* -- Fake implementation. -- */ |
| 137 | |
| 138 | static void fake_channel_destroy(grpc_security_context *ctx) { |
| 139 | grpc_channel_security_context *c = (grpc_channel_security_context *)ctx; |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 140 | grpc_credentials_unref(c->request_metadata_creds); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 141 | gpr_free(ctx); |
| 142 | } |
| 143 | |
| 144 | static void fake_server_destroy(grpc_security_context *ctx) { |
| 145 | gpr_free(ctx); |
| 146 | } |
| 147 | |
| 148 | static grpc_security_status fake_channel_create_handshaker( |
| 149 | grpc_security_context *ctx, tsi_handshaker **handshaker) { |
| 150 | *handshaker = tsi_create_fake_handshaker(1); |
| 151 | return GRPC_SECURITY_OK; |
| 152 | } |
| 153 | |
| 154 | static grpc_security_status fake_server_create_handshaker( |
| 155 | grpc_security_context *ctx, tsi_handshaker **handshaker) { |
| 156 | *handshaker = tsi_create_fake_handshaker(0); |
| 157 | return GRPC_SECURITY_OK; |
| 158 | } |
| 159 | |
| 160 | static grpc_security_status fake_check_peer(grpc_security_context *ctx, |
| 161 | const tsi_peer *peer, |
| 162 | grpc_security_check_peer_cb cb, |
| 163 | void *user_data) { |
| 164 | const char *prop_name; |
| 165 | if (peer->property_count != 1) { |
| 166 | gpr_log(GPR_ERROR, "Fake peers should only have 1 property."); |
| 167 | return GRPC_SECURITY_ERROR; |
| 168 | } |
| 169 | prop_name = peer->properties[0].name; |
| 170 | if (prop_name == NULL || |
| 171 | strcmp(prop_name, TSI_CERTIFICATE_TYPE_PEER_PROPERTY)) { |
| 172 | gpr_log(GPR_ERROR, "Unexpected property in fake peer: %s.", |
| 173 | prop_name == NULL ? "<EMPTY>" : prop_name); |
| 174 | return GRPC_SECURITY_ERROR; |
| 175 | } |
| 176 | if (peer->properties[0].type != TSI_PEER_PROPERTY_TYPE_STRING) { |
| 177 | gpr_log(GPR_ERROR, "Invalid type of cert type property."); |
| 178 | return GRPC_SECURITY_ERROR; |
| 179 | } |
| 180 | if (strncmp(peer->properties[0].value.string.data, TSI_FAKE_CERTIFICATE_TYPE, |
| 181 | peer->properties[0].value.string.length)) { |
| 182 | gpr_log(GPR_ERROR, "Invalid value for cert type property."); |
| 183 | return GRPC_SECURITY_ERROR; |
| 184 | } |
| 185 | return GRPC_SECURITY_OK; |
| 186 | } |
| 187 | |
| 188 | static grpc_security_context_vtable fake_channel_vtable = { |
| 189 | fake_channel_destroy, fake_channel_create_handshaker, fake_check_peer}; |
| 190 | |
| 191 | static grpc_security_context_vtable fake_server_vtable = { |
| 192 | fake_server_destroy, fake_server_create_handshaker, fake_check_peer}; |
| 193 | |
| 194 | grpc_channel_security_context *grpc_fake_channel_security_context_create( |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 195 | grpc_credentials *request_metadata_creds) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 196 | grpc_channel_security_context *c = |
| 197 | gpr_malloc(sizeof(grpc_channel_security_context)); |
| 198 | gpr_ref_init(&c->base.refcount, 1); |
| 199 | c->base.is_client_side = 1; |
| 200 | c->base.vtable = &fake_channel_vtable; |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 201 | GPR_ASSERT(check_request_metadata_creds(request_metadata_creds)); |
| 202 | c->request_metadata_creds = grpc_credentials_ref(request_metadata_creds); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 203 | return c; |
| 204 | } |
| 205 | |
| 206 | grpc_security_context *grpc_fake_server_security_context_create(void) { |
| 207 | grpc_security_context *c = gpr_malloc(sizeof(grpc_security_context)); |
| 208 | gpr_ref_init(&c->refcount, 1); |
| 209 | c->vtable = &fake_server_vtable; |
| 210 | return c; |
| 211 | } |
| 212 | |
| 213 | /* --- Ssl implementation. --- */ |
| 214 | |
| 215 | typedef struct { |
| 216 | grpc_channel_security_context base; |
| 217 | tsi_ssl_handshaker_factory *handshaker_factory; |
| 218 | char *secure_peer_name; |
| 219 | } grpc_ssl_channel_security_context; |
| 220 | |
| 221 | typedef struct { |
| 222 | grpc_security_context base; |
| 223 | tsi_ssl_handshaker_factory *handshaker_factory; |
| 224 | } grpc_ssl_server_security_context; |
| 225 | |
| 226 | static void ssl_channel_destroy(grpc_security_context *ctx) { |
| 227 | grpc_ssl_channel_security_context *c = |
| 228 | (grpc_ssl_channel_security_context *)ctx; |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 229 | grpc_credentials_unref(c->base.request_metadata_creds); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 230 | if (c->handshaker_factory != NULL) { |
| 231 | tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); |
| 232 | } |
| 233 | if (c->secure_peer_name != NULL) gpr_free(c->secure_peer_name); |
| 234 | gpr_free(ctx); |
| 235 | } |
| 236 | |
| 237 | static void ssl_server_destroy(grpc_security_context *ctx) { |
| 238 | grpc_ssl_server_security_context *c = |
| 239 | (grpc_ssl_server_security_context *)ctx; |
| 240 | if (c->handshaker_factory != NULL) { |
| 241 | tsi_ssl_handshaker_factory_destroy(c->handshaker_factory); |
| 242 | } |
| 243 | gpr_free(ctx); |
| 244 | } |
| 245 | |
| 246 | static grpc_security_status ssl_create_handshaker( |
| 247 | tsi_ssl_handshaker_factory *handshaker_factory, int is_client, |
| 248 | const char *secure_peer_name, tsi_handshaker **handshaker) { |
| 249 | tsi_result result = TSI_OK; |
| 250 | if (handshaker_factory == NULL) return GRPC_SECURITY_ERROR; |
| 251 | result = tsi_ssl_handshaker_factory_create_handshaker( |
| 252 | handshaker_factory, is_client ? secure_peer_name : NULL, handshaker); |
| 253 | if (result != TSI_OK) { |
| 254 | gpr_log(GPR_ERROR, "Handshaker creation failed with error %s.", |
| 255 | tsi_result_to_string(result)); |
| 256 | return GRPC_SECURITY_ERROR; |
| 257 | } |
| 258 | return GRPC_SECURITY_OK; |
| 259 | } |
| 260 | |
| 261 | static grpc_security_status ssl_channel_create_handshaker( |
| 262 | grpc_security_context *ctx, tsi_handshaker **handshaker) { |
| 263 | grpc_ssl_channel_security_context *c = |
| 264 | (grpc_ssl_channel_security_context *)ctx; |
| 265 | return ssl_create_handshaker(c->handshaker_factory, 1, c->secure_peer_name, |
| 266 | handshaker); |
| 267 | } |
| 268 | |
| 269 | static grpc_security_status ssl_server_create_handshaker( |
| 270 | grpc_security_context *ctx, tsi_handshaker **handshaker) { |
| 271 | grpc_ssl_server_security_context *c = |
| 272 | (grpc_ssl_server_security_context *)ctx; |
| 273 | return ssl_create_handshaker(c->handshaker_factory, 0, NULL, handshaker); |
| 274 | } |
| 275 | |
| 276 | static grpc_security_status ssl_check_peer(const char *secure_peer_name, |
| 277 | const tsi_peer *peer) { |
| 278 | /* Check the ALPN. */ |
| 279 | const tsi_peer_property *p = |
| 280 | tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL); |
| 281 | if (p == NULL || p->type != TSI_PEER_PROPERTY_TYPE_STRING) { |
| 282 | gpr_log(GPR_ERROR, "Invalid or missing selected ALPN property."); |
| 283 | return GRPC_SECURITY_ERROR; |
| 284 | } |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 285 | if (!grpc_chttp2_is_alpn_version_supported(p->value.string.data, |
| 286 | p->value.string.length)) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 287 | gpr_log(GPR_ERROR, "Invalid ALPN value."); |
| 288 | return GRPC_SECURITY_ERROR; |
| 289 | } |
| 290 | |
| 291 | /* Check the peer name if specified. */ |
| 292 | if (secure_peer_name != NULL && |
| 293 | !tsi_ssl_peer_matches_name(peer, secure_peer_name)) { |
| 294 | gpr_log(GPR_ERROR, "Peer name %s is not in peer certificate", |
| 295 | secure_peer_name); |
| 296 | return GRPC_SECURITY_ERROR; |
| 297 | } |
| 298 | return GRPC_SECURITY_OK; |
| 299 | } |
| 300 | |
| 301 | static grpc_security_status ssl_channel_check_peer( |
| 302 | grpc_security_context *ctx, const tsi_peer *peer, |
| 303 | grpc_security_check_peer_cb cb, void *user_data) { |
| 304 | grpc_ssl_channel_security_context *c = |
| 305 | (grpc_ssl_channel_security_context *)ctx; |
| 306 | return ssl_check_peer(c->secure_peer_name, peer); |
| 307 | } |
| 308 | |
| 309 | static grpc_security_status ssl_server_check_peer( |
| 310 | grpc_security_context *ctx, const tsi_peer *peer, |
| 311 | grpc_security_check_peer_cb cb, void *user_data) { |
| 312 | /* TODO(jboeuf): Find a way to expose the peer to the authorization layer. */ |
| 313 | return ssl_check_peer(NULL, peer); |
| 314 | } |
| 315 | |
| 316 | static grpc_security_context_vtable ssl_channel_vtable = { |
| 317 | ssl_channel_destroy, ssl_channel_create_handshaker, ssl_channel_check_peer}; |
| 318 | |
| 319 | static grpc_security_context_vtable ssl_server_vtable = { |
| 320 | ssl_server_destroy, ssl_server_create_handshaker, ssl_server_check_peer}; |
| 321 | |
| 322 | grpc_security_status grpc_ssl_channel_security_context_create( |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 323 | grpc_credentials *request_metadata_creds, const grpc_ssl_config *config, |
| 324 | const char *secure_peer_name, grpc_channel_security_context **ctx) { |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 325 | size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); |
| 326 | const unsigned char **alpn_protocol_strings = |
| 327 | gpr_malloc(sizeof(const char *) * num_alpn_protocols); |
| 328 | unsigned char *alpn_protocol_string_lengths = |
| 329 | gpr_malloc(sizeof(unsigned char) * num_alpn_protocols); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 330 | tsi_result result = TSI_OK; |
| 331 | grpc_ssl_channel_security_context *c; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 332 | size_t i; |
| 333 | |
| 334 | for (i = 0; i < num_alpn_protocols; i++) { |
| 335 | alpn_protocol_strings[i] = |
| 336 | (const unsigned char *)grpc_chttp2_get_alpn_version_index(i); |
| 337 | alpn_protocol_string_lengths[i] = |
| 338 | strlen(grpc_chttp2_get_alpn_version_index(i)); |
| 339 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 340 | |
| 341 | if (config == NULL || secure_peer_name == NULL || |
| 342 | config->pem_root_certs == NULL) { |
| 343 | gpr_log(GPR_ERROR, "An ssl channel needs a secure name and root certs."); |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 344 | goto error; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 345 | } |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 346 | if (!check_request_metadata_creds(request_metadata_creds)) { |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 347 | goto error; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 348 | } |
| 349 | |
| 350 | c = gpr_malloc(sizeof(grpc_ssl_channel_security_context)); |
| 351 | memset(c, 0, sizeof(grpc_ssl_channel_security_context)); |
| 352 | |
| 353 | gpr_ref_init(&c->base.base.refcount, 1); |
| 354 | c->base.base.vtable = &ssl_channel_vtable; |
| 355 | c->base.base.is_client_side = 1; |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 356 | c->base.request_metadata_creds = grpc_credentials_ref(request_metadata_creds); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 357 | if (secure_peer_name != NULL) { |
| 358 | c->secure_peer_name = gpr_strdup(secure_peer_name); |
| 359 | } |
| 360 | result = tsi_create_ssl_client_handshaker_factory( |
| 361 | config->pem_private_key, config->pem_private_key_size, |
| 362 | config->pem_cert_chain, config->pem_cert_chain_size, |
| 363 | config->pem_root_certs, config->pem_root_certs_size, |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 364 | GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings, |
| 365 | alpn_protocol_string_lengths, 1, &c->handshaker_factory); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 366 | if (result != TSI_OK) { |
| 367 | gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", |
| 368 | tsi_result_to_string(result)); |
| 369 | ssl_channel_destroy(&c->base.base); |
| 370 | *ctx = NULL; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 371 | goto error; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 372 | } |
| 373 | *ctx = &c->base; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 374 | gpr_free(alpn_protocol_strings); |
| 375 | gpr_free(alpn_protocol_string_lengths); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 376 | return GRPC_SECURITY_OK; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 377 | |
| 378 | error: |
| 379 | gpr_free(alpn_protocol_strings); |
| 380 | gpr_free(alpn_protocol_string_lengths); |
| 381 | return GRPC_SECURITY_ERROR; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 382 | } |
| 383 | |
| 384 | grpc_security_status grpc_ssl_server_security_context_create( |
| 385 | const grpc_ssl_config *config, grpc_security_context **ctx) { |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 386 | size_t num_alpn_protocols = grpc_chttp2_num_alpn_versions(); |
| 387 | const unsigned char **alpn_protocol_strings = |
| 388 | gpr_malloc(sizeof(const char *) * num_alpn_protocols); |
| 389 | unsigned char *alpn_protocol_string_lengths = |
| 390 | gpr_malloc(sizeof(unsigned char) * num_alpn_protocols); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 391 | tsi_result result = TSI_OK; |
| 392 | grpc_ssl_server_security_context *c; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 393 | size_t i; |
| 394 | |
| 395 | for (i = 0; i < num_alpn_protocols; i++) { |
| 396 | alpn_protocol_strings[i] = |
| 397 | (const unsigned char *)grpc_chttp2_get_alpn_version_index(i); |
| 398 | alpn_protocol_string_lengths[i] = |
| 399 | strlen(grpc_chttp2_get_alpn_version_index(i)); |
| 400 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 401 | |
| 402 | if (config == NULL || config->pem_private_key == NULL || |
| 403 | config->pem_cert_chain == NULL) { |
| 404 | gpr_log(GPR_ERROR, "An SSL server needs a key and a cert."); |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 405 | goto error; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 406 | } |
| 407 | c = gpr_malloc(sizeof(grpc_ssl_server_security_context)); |
| 408 | memset(c, 0, sizeof(grpc_ssl_server_security_context)); |
| 409 | |
| 410 | gpr_ref_init(&c->base.refcount, 1); |
| 411 | c->base.vtable = &ssl_server_vtable; |
| 412 | result = tsi_create_ssl_server_handshaker_factory( |
| 413 | (const unsigned char **)&config->pem_private_key, |
| 414 | (const gpr_uint32 *)&config->pem_private_key_size, |
| 415 | (const unsigned char **)&config->pem_cert_chain, |
| 416 | (const gpr_uint32 *)&config->pem_cert_chain_size, 1, |
| 417 | config->pem_root_certs, config->pem_root_certs_size, |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 418 | GRPC_SSL_CIPHER_SUITES, alpn_protocol_strings, |
| 419 | alpn_protocol_string_lengths, 1, &c->handshaker_factory); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 420 | if (result != TSI_OK) { |
| 421 | gpr_log(GPR_ERROR, "Handshaker factory creation failed with %s.", |
| 422 | tsi_result_to_string(result)); |
| 423 | ssl_server_destroy(&c->base); |
| 424 | *ctx = NULL; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 425 | goto error; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 426 | } |
| 427 | *ctx = &c->base; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 428 | gpr_free(alpn_protocol_strings); |
| 429 | gpr_free(alpn_protocol_string_lengths); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 430 | return GRPC_SECURITY_OK; |
ctiller | 84518b4 | 2014-12-09 15:05:40 -0800 | [diff] [blame^] | 431 | |
| 432 | error: |
| 433 | gpr_free(alpn_protocol_strings); |
| 434 | gpr_free(alpn_protocol_string_lengths); |
| 435 | return GRPC_SECURITY_ERROR; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 436 | } |
| 437 | |
| 438 | |
| 439 | |
| 440 | /* -- High level objects. -- */ |
| 441 | |
| 442 | static grpc_channel *grpc_ssl_channel_create(grpc_credentials *creds, |
| 443 | const grpc_ssl_config *config, |
| 444 | const char *target, |
| 445 | const grpc_channel_args *args) { |
| 446 | grpc_channel_security_context *ctx = NULL; |
| 447 | grpc_channel *channel = NULL; |
| 448 | grpc_security_status status = GRPC_SECURITY_OK; |
| 449 | size_t i = 0; |
| 450 | const char *secure_peer_name = target; |
| 451 | for (i = 0; i < args->num_args; i++) { |
| 452 | grpc_arg *arg = &args->args[i]; |
| 453 | if (!strcmp(arg->key, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG) && |
| 454 | arg->type == GRPC_ARG_STRING) { |
| 455 | secure_peer_name = arg->value.string; |
| 456 | break; |
| 457 | } |
| 458 | } |
| 459 | status = grpc_ssl_channel_security_context_create(creds, config, |
| 460 | secure_peer_name, &ctx); |
| 461 | if (status != GRPC_SECURITY_OK) { |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 462 | return grpc_lame_client_channel_create(); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 463 | } |
| 464 | channel = grpc_secure_channel_create_internal(target, args, ctx); |
| 465 | grpc_security_context_unref(&ctx->base); |
| 466 | return channel; |
| 467 | } |
| 468 | |
| 469 | |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 470 | static grpc_credentials *get_creds_from_composite( |
| 471 | grpc_credentials *composite_creds, const char *type) { |
| 472 | size_t i; |
| 473 | const grpc_credentials_array *inner_creds_array = |
| 474 | grpc_composite_credentials_get_credentials(composite_creds); |
| 475 | for (i = 0; i < inner_creds_array->num_creds; i++) { |
| 476 | if (!strcmp(type, inner_creds_array->creds_array[i]->type)) { |
| 477 | return inner_creds_array->creds_array[i]; |
| 478 | } |
| 479 | } |
| 480 | return NULL; |
| 481 | } |
| 482 | |
| 483 | static grpc_channel *grpc_channel_create_from_composite_creds( |
| 484 | grpc_credentials *composite_creds, const char *target, |
| 485 | const grpc_channel_args *args) { |
| 486 | grpc_credentials *creds = |
| 487 | get_creds_from_composite(composite_creds, GRPC_CREDENTIALS_TYPE_SSL); |
| 488 | if (creds != NULL) { |
| 489 | return grpc_ssl_channel_create( |
| 490 | composite_creds, grpc_ssl_credentials_get_config(creds), target, args); |
| 491 | } |
| 492 | return NULL; /* TODO(ctiller): return lame channel. */ |
| 493 | } |
| 494 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 495 | grpc_channel *grpc_secure_channel_create(grpc_credentials *creds, |
| 496 | const char *target, |
| 497 | const grpc_channel_args *args) { |
| 498 | if (grpc_credentials_has_request_metadata_only(creds)) { |
| 499 | gpr_log(GPR_ERROR, |
| 500 | "Credentials is insufficient to create a secure channel."); |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 501 | return grpc_lame_client_channel_create(); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 502 | } |
| 503 | if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) { |
| 504 | return grpc_ssl_channel_create(NULL, grpc_ssl_credentials_get_config(creds), |
| 505 | target, args); |
| 506 | } else if (!strcmp(creds->type, |
| 507 | GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY)) { |
| 508 | grpc_channel_security_context *ctx = |
| 509 | grpc_fake_channel_security_context_create(NULL); |
| 510 | grpc_channel *channel = |
| 511 | grpc_secure_channel_create_internal(target, args, ctx); |
| 512 | grpc_security_context_unref(&ctx->base); |
| 513 | return channel; |
| 514 | } else if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_COMPOSITE)) { |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 515 | return grpc_channel_create_from_composite_creds(creds, target, args); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 516 | } else { |
| 517 | gpr_log(GPR_ERROR, |
| 518 | "Unknown credentials type %s for creating a secure channel."); |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 519 | return grpc_lame_client_channel_create(); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 520 | } |
| 521 | } |
| 522 | |
| 523 | grpc_channel *grpc_default_secure_channel_create( |
| 524 | const char *target, const grpc_channel_args *args) { |
| 525 | return grpc_secure_channel_create(grpc_default_credentials_create(), target, |
| 526 | args); |
| 527 | } |
| 528 | |
| 529 | grpc_server *grpc_secure_server_create(grpc_server_credentials *creds, |
| 530 | grpc_completion_queue *cq, |
| 531 | const grpc_channel_args *args) { |
| 532 | grpc_security_status status = GRPC_SECURITY_ERROR; |
| 533 | grpc_security_context *ctx = NULL; |
| 534 | grpc_server *server = NULL; |
| 535 | if (creds == NULL) return NULL; /* TODO(ctiller): Return lame server. */ |
| 536 | if (!strcmp(creds->type, GRPC_CREDENTIALS_TYPE_SSL)) { |
| 537 | status = grpc_ssl_server_security_context_create( |
| 538 | grpc_ssl_server_credentials_get_config(creds), &ctx); |
| 539 | } else if (!strcmp(creds->type, |
| 540 | GRPC_CREDENTIALS_TYPE_FAKE_TRANSPORT_SECURITY)) { |
| 541 | ctx = grpc_fake_server_security_context_create(); |
| 542 | status = GRPC_SECURITY_OK; |
| 543 | } else { |
| 544 | gpr_log(GPR_ERROR, |
| 545 | "Unable to create secure server with credentials of type %s.", |
| 546 | creds->type); |
| 547 | } |
| 548 | if (status != GRPC_SECURITY_OK) { |
| 549 | return NULL; /* TODO(ctiller): Return lame server. */ |
| 550 | } |
| 551 | server = grpc_secure_server_create_internal(cq, args, ctx); |
| 552 | grpc_security_context_unref(ctx); |
| 553 | return server; |
| 554 | } |