blob: 8df582609b9c6b00a050ca5d765967b853c22ca6 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Paul Querna47d841d2016-03-10 11:19:17 -08003 * Copyright 2015-2016, 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
34#include "src/core/tsi/ssl_transport_security.h"
35
Paul Querna47d841d2016-03-10 11:19:17 -080036#include <grpc/support/port_platform.h>
37
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080038#include <limits.h>
Julien Boeuf330f4c82015-04-27 10:37:42 -070039#include <string.h>
Paul Querna27df6892016-03-13 13:34:27 -070040
41/* TODO(jboeuf): refactor inet_ntop into a portability header. */
Paul Querna47d841d2016-03-10 11:19:17 -080042#ifdef GPR_WINSOCK_SOCKET
43#include <ws2tcpip.h>
44#else
45#include <arpa/inet.h>
46#endif
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080047
48#include <grpc/support/log.h>
nnoble8a23a3d2014-12-16 10:10:29 -080049#include <grpc/support/sync.h>
Julien Boeuf4a0a3942015-02-03 15:04:45 -080050#include <grpc/support/thd.h>
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +010051#include <grpc/support/useful.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080052
53#include <openssl/bio.h>
Craig Tillera82950e2015-09-22 12:33:20 -070054#include <openssl/crypto.h> /* For OPENSSL_free */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080055#include <openssl/err.h>
56#include <openssl/ssl.h>
57#include <openssl/x509.h>
58#include <openssl/x509v3.h>
59
Craig Tiller0fe5ee72015-12-22 12:50:36 -080060#include "src/core/tsi/ssl_types.h"
61#include "src/core/tsi/transport_security.h"
62
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080063/* --- Constants. ---*/
64
65#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384
66#define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024
67
Julien Boeuf3e29de12015-06-18 23:29:13 +020068/* Putting a macro like this and littering the source file with #if is really
69 bad practice.
70 TODO(jboeuf): refactor all the #if / #endif in a separate module. */
Julien Boeufd1531322015-06-18 11:05:39 +020071#ifndef TSI_OPENSSL_ALPN_SUPPORT
72#define TSI_OPENSSL_ALPN_SUPPORT 1
73#endif
74
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080075/* TODO(jboeuf): I have not found a way to get this number dynamically from the
Julien Boeuf3e29de12015-06-18 23:29:13 +020076 SSL structure. This is what we would ultimately want though... */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080077#define TSI_SSL_MAX_PROTECTION_OVERHEAD 100
78
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080079/* --- Structure definitions. ---*/
80
Craig Tillera82950e2015-09-22 12:33:20 -070081struct tsi_ssl_handshaker_factory {
82 tsi_result (*create_handshaker)(tsi_ssl_handshaker_factory *self,
83 const char *server_name_indication,
84 tsi_handshaker **handshaker);
85 void (*destroy)(tsi_ssl_handshaker_factory *self);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080086};
87
Craig Tillera82950e2015-09-22 12:33:20 -070088typedef struct {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080089 tsi_ssl_handshaker_factory base;
Craig Tiller45724b32015-09-22 10:42:19 -070090 SSL_CTX *ssl_context;
91 unsigned char *alpn_protocol_list;
Julien Boeufd1531322015-06-18 11:05:39 +020092 size_t alpn_protocol_list_length;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080093} tsi_ssl_client_handshaker_factory;
94
Craig Tillera82950e2015-09-22 12:33:20 -070095typedef struct {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080096 tsi_ssl_handshaker_factory base;
97
98 /* Several contexts to support SNI.
99 The tsi_peer array contains the subject names of the server certificates
100 associated with the contexts at the same index. */
Craig Tiller45724b32015-09-22 10:42:19 -0700101 SSL_CTX **ssl_contexts;
102 tsi_peer *ssl_context_x509_subject_names;
Julien Boeufb222b4d2015-01-15 17:01:39 -0800103 size_t ssl_context_count;
Craig Tiller45724b32015-09-22 10:42:19 -0700104 unsigned char *alpn_protocol_list;
Julien Boeufb222b4d2015-01-15 17:01:39 -0800105 size_t alpn_protocol_list_length;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800106} tsi_ssl_server_handshaker_factory;
107
Craig Tillera82950e2015-09-22 12:33:20 -0700108typedef struct {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800109 tsi_handshaker base;
Craig Tiller45724b32015-09-22 10:42:19 -0700110 SSL *ssl;
111 BIO *into_ssl;
112 BIO *from_ssl;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800113 tsi_result result;
114} tsi_ssl_handshaker;
115
Craig Tillera82950e2015-09-22 12:33:20 -0700116typedef struct {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800117 tsi_frame_protector base;
Craig Tiller45724b32015-09-22 10:42:19 -0700118 SSL *ssl;
119 BIO *into_ssl;
120 BIO *from_ssl;
121 unsigned char *buffer;
Julien Boeufb222b4d2015-01-15 17:01:39 -0800122 size_t buffer_size;
123 size_t buffer_offset;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800124} tsi_ssl_frame_protector;
125
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800126/* --- Library Initialization. ---*/
127
nnoble8a23a3d2014-12-16 10:10:29 -0800128static gpr_once init_openssl_once = GPR_ONCE_INIT;
Craig Tiller45724b32015-09-22 10:42:19 -0700129static gpr_mu *openssl_mutexes = NULL;
Julien Boeuf4a0a3942015-02-03 15:04:45 -0800130
Craig Tillera82950e2015-09-22 12:33:20 -0700131static void openssl_locking_cb(int mode, int type, const char *file, int line) {
132 if (mode & CRYPTO_LOCK) {
133 gpr_mu_lock(&openssl_mutexes[type]);
134 } else {
135 gpr_mu_unlock(&openssl_mutexes[type]);
136 }
Julien Boeuf4a0a3942015-02-03 15:04:45 -0800137}
138
Craig Tillera82950e2015-09-22 12:33:20 -0700139static unsigned long openssl_thread_id_cb(void) {
140 return (unsigned long)gpr_thd_currentid();
Julien Boeuf4a0a3942015-02-03 15:04:45 -0800141}
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800142
Craig Tillera82950e2015-09-22 12:33:20 -0700143static void init_openssl(void) {
Julien Boeuf4a0a3942015-02-03 15:04:45 -0800144 int i;
Craig Tiller3121fd42015-09-10 09:56:20 -0700145 int num_locks;
Craig Tillera82950e2015-09-22 12:33:20 -0700146 SSL_library_init();
147 SSL_load_error_strings();
148 OpenSSL_add_all_algorithms();
149 num_locks = CRYPTO_num_locks();
150 GPR_ASSERT(num_locks > 0);
151 openssl_mutexes = malloc((size_t)num_locks * sizeof(gpr_mu));
152 GPR_ASSERT(openssl_mutexes != NULL);
153 for (i = 0; i < CRYPTO_num_locks(); i++) {
154 gpr_mu_init(&openssl_mutexes[i]);
155 }
156 CRYPTO_set_locking_callback(openssl_locking_cb);
157 CRYPTO_set_id_callback(openssl_thread_id_cb);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800158}
159
160/* --- Ssl utils. ---*/
161
Craig Tillera82950e2015-09-22 12:33:20 -0700162static const char *ssl_error_string(int error) {
163 switch (error) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800164 case SSL_ERROR_NONE:
165 return "SSL_ERROR_NONE";
166 case SSL_ERROR_ZERO_RETURN:
167 return "SSL_ERROR_ZERO_RETURN";
168 case SSL_ERROR_WANT_READ:
169 return "SSL_ERROR_WANT_READ";
170 case SSL_ERROR_WANT_WRITE:
171 return "SSL_ERROR_WANT_WRITE";
172 case SSL_ERROR_WANT_CONNECT:
173 return "SSL_ERROR_WANT_CONNECT";
174 case SSL_ERROR_WANT_ACCEPT:
175 return "SSL_ERROR_WANT_ACCEPT";
176 case SSL_ERROR_WANT_X509_LOOKUP:
177 return "SSL_ERROR_WANT_X509_LOOKUP";
178 case SSL_ERROR_SYSCALL:
179 return "SSL_ERROR_SYSCALL";
180 case SSL_ERROR_SSL:
181 return "SSL_ERROR_SSL";
182 default:
183 return "Unknown error";
Craig Tillera82950e2015-09-22 12:33:20 -0700184 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800185}
186
187/* TODO(jboeuf): Remove when we are past the debugging phase with this code. */
Craig Tillera82950e2015-09-22 12:33:20 -0700188static void ssl_log_where_info(const SSL *ssl, int where, int flag,
189 const char *msg) {
190 if ((where & flag) && tsi_tracing_enabled) {
191 gpr_log(GPR_INFO, "%20.20s - %30.30s - %5.10s", msg,
192 SSL_state_string_long(ssl), SSL_state_string(ssl));
193 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800194}
195
196/* Used for debugging. TODO(jboeuf): Remove when code is mature enough. */
Craig Tillera82950e2015-09-22 12:33:20 -0700197static void ssl_info_callback(const SSL *ssl, int where, int ret) {
198 if (ret == 0) {
199 gpr_log(GPR_ERROR, "ssl_info_callback: error occured.\n");
200 return;
201 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800202
Craig Tillera82950e2015-09-22 12:33:20 -0700203 ssl_log_where_info(ssl, where, SSL_CB_LOOP, "LOOP");
204 ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START");
205 ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800206}
207
Julien Boeuf0170a6c2015-02-24 18:08:01 -0800208/* Returns 1 if name looks like an IP address, 0 otherwise.
Paul Querna4a9e7c42016-03-13 14:00:11 -0700209 This is a very rough heuristic, and only handles IPv6 in hexadecimal form. */
Craig Tillera82950e2015-09-22 12:33:20 -0700210static int looks_like_ip_address(const char *name) {
Julien Boeuf9fff77e2015-02-24 16:50:35 -0800211 size_t i;
212 size_t dot_count = 0;
213 size_t num_size = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700214 for (i = 0; i < strlen(name); i++) {
Paul Querna4a9e7c42016-03-13 14:00:11 -0700215 if (name[i] == ':') {
216 /* IPv6 Address in hexadecimal form, : is not allowed in DNS names. */
217 return 1;
218 }
Craig Tillera82950e2015-09-22 12:33:20 -0700219 if (name[i] >= '0' && name[i] <= '9') {
220 if (num_size > 3) return 0;
221 num_size++;
222 } else if (name[i] == '.') {
223 if (dot_count > 3 || num_size == 0) return 0;
224 dot_count++;
225 num_size = 0;
226 } else {
227 return 0;
Julien Boeuf9fff77e2015-02-24 16:50:35 -0800228 }
Craig Tillera82950e2015-09-22 12:33:20 -0700229 }
230 if (dot_count < 3 || num_size == 0) return 0;
Julien Boeuf9fff77e2015-02-24 16:50:35 -0800231 return 1;
232}
233
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800234/* Gets the subject CN from an X509 cert. */
Craig Tillera82950e2015-09-22 12:33:20 -0700235static tsi_result ssl_get_x509_common_name(X509 *cert, unsigned char **utf8,
236 size_t *utf8_size) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800237 int common_name_index = -1;
Craig Tiller45724b32015-09-22 10:42:19 -0700238 X509_NAME_ENTRY *common_name_entry = NULL;
239 ASN1_STRING *common_name_asn1 = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700240 X509_NAME *subject_name = X509_get_subject_name(cert);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800241 int utf8_returned_size = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700242 if (subject_name == NULL) {
243 gpr_log(GPR_ERROR, "Could not get subject name from certificate.");
244 return TSI_NOT_FOUND;
245 }
246 common_name_index =
247 X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1);
248 if (common_name_index == -1) {
249 gpr_log(GPR_ERROR,
250 "Could not get common name of subject from certificate.");
251 return TSI_NOT_FOUND;
252 }
253 common_name_entry = X509_NAME_get_entry(subject_name, common_name_index);
254 if (common_name_entry == NULL) {
255 gpr_log(GPR_ERROR, "Could not get common name entry from certificate.");
256 return TSI_INTERNAL_ERROR;
257 }
258 common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry);
259 if (common_name_asn1 == NULL) {
260 gpr_log(GPR_ERROR,
261 "Could not get common name entry asn1 from certificate.");
262 return TSI_INTERNAL_ERROR;
263 }
264 utf8_returned_size = ASN1_STRING_to_UTF8(utf8, common_name_asn1);
265 if (utf8_returned_size < 0) {
266 gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string.");
267 return TSI_OUT_OF_RESOURCES;
268 }
269 *utf8_size = (size_t)utf8_returned_size;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800270 return TSI_OK;
271}
272
273/* Gets the subject CN of an X509 cert as a tsi_peer_property. */
Craig Tillera82950e2015-09-22 12:33:20 -0700274static tsi_result peer_property_from_x509_common_name(
275 X509 *cert, tsi_peer_property *property) {
Craig Tiller45724b32015-09-22 10:42:19 -0700276 unsigned char *common_name;
Julien Boeufb222b4d2015-01-15 17:01:39 -0800277 size_t common_name_size;
Craig Tillera82950e2015-09-22 12:33:20 -0700278 tsi_result result =
279 ssl_get_x509_common_name(cert, &common_name, &common_name_size);
280 if (result != TSI_OK) {
281 if (result == TSI_NOT_FOUND) {
282 common_name = NULL;
283 common_name_size = 0;
284 } else {
285 return result;
Julien Boeuf9fff77e2015-02-24 16:50:35 -0800286 }
Craig Tillera82950e2015-09-22 12:33:20 -0700287 }
288 result = tsi_construct_string_peer_property(
289 TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY,
290 common_name == NULL ? "" : (const char *)common_name, common_name_size,
291 property);
292 OPENSSL_free(common_name);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800293 return result;
294}
295
Deepak Lukosee61cbe32016-03-14 14:10:44 -0700296/* Gets the X509 cert in PEM format as a tsi_peer_property. */
297static tsi_result add_pem_certificate(X509 *cert, tsi_peer_property *property) {
298 BIO *bio = BIO_new(BIO_s_mem());
299 if (!PEM_write_bio_X509(bio, cert)) {
300 BIO_free(bio);
301 return TSI_INTERNAL_ERROR;
302 }
303 char *contents;
304 long len = BIO_get_mem_data(bio, &contents);
305 if (len <= 0) {
306 BIO_free(bio);
307 return TSI_INTERNAL_ERROR;
308 }
309 tsi_result result = tsi_construct_string_peer_property(
310 TSI_X509_PEM_CERT_PROPERTY, (const char *)contents, (size_t)len,
311 property);
312 BIO_free(bio);
313 return result;
314}
315
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800316/* Gets the subject SANs from an X509 cert as a tsi_peer_property. */
Craig Tillera82950e2015-09-22 12:33:20 -0700317static tsi_result add_subject_alt_names_properties_to_peer(
318 tsi_peer *peer, GENERAL_NAMES *subject_alt_names,
319 size_t subject_alt_name_count) {
Craig Tiller3121fd42015-09-10 09:56:20 -0700320 size_t i;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800321 tsi_result result = TSI_OK;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800322
323 /* Reset for DNS entries filtering. */
Julien Boeuf77e8c1c2015-05-13 13:50:59 -0700324 peer->property_count -= subject_alt_name_count;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800325
Craig Tillera82950e2015-09-22 12:33:20 -0700326 for (i = 0; i < subject_alt_name_count; i++) {
327 GENERAL_NAME *subject_alt_name =
Craig Tiller0fe5ee72015-12-22 12:50:36 -0800328 sk_GENERAL_NAME_value(subject_alt_names, TSI_SIZE_AS_SIZE(i));
Craig Tillera82950e2015-09-22 12:33:20 -0700329 /* Filter out the non-dns entries names. */
330 if (subject_alt_name->type == GEN_DNS) {
Paul Querna47d841d2016-03-10 11:19:17 -0800331 unsigned char *name = NULL;
332 int name_size;
333 name_size = ASN1_STRING_to_UTF8(&name, subject_alt_name->d.dNSName);
334 if (name_size < 0) {
Craig Tillera82950e2015-09-22 12:33:20 -0700335 gpr_log(GPR_ERROR, "Could not get utf8 from asn1 string.");
336 result = TSI_INTERNAL_ERROR;
337 break;
338 }
339 result = tsi_construct_string_peer_property(
Paul Querna47d841d2016-03-10 11:19:17 -0800340 TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, (const char *)name,
341 (size_t)name_size, &peer->properties[peer->property_count++]);
342 OPENSSL_free(name);
343 } else if (subject_alt_name->type == GEN_IPADD) {
344 char ntop_buf[INET6_ADDRSTRLEN];
345 int af;
346
347 if (subject_alt_name->d.iPAddress->length == 4) {
348 af = AF_INET;
349 } else if (subject_alt_name->d.iPAddress->length == 16) {
350 af = AF_INET6;
351 } else {
352 gpr_log(GPR_ERROR, "SAN IP Address contained invalid IP");
353 result = TSI_INTERNAL_ERROR;
354 break;
355 }
356 const char *name = inet_ntop(af, subject_alt_name->d.iPAddress->data,
357 ntop_buf, INET6_ADDRSTRLEN);
358 if (name == NULL) {
359 gpr_log(GPR_ERROR, "Could not get IP string from asn1 octet.");
360 result = TSI_INTERNAL_ERROR;
361 break;
362 }
363
364 result = tsi_construct_string_peer_property_from_cstring(
365 TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, name,
Craig Tillera82950e2015-09-22 12:33:20 -0700366 &peer->properties[peer->property_count++]);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800367 }
Paul Querna47d841d2016-03-10 11:19:17 -0800368 if (result != TSI_OK) break;
Craig Tillera82950e2015-09-22 12:33:20 -0700369 }
Julien Boeuf77e8c1c2015-05-13 13:50:59 -0700370 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800371}
372
373/* Gets information about the peer's X509 cert as a tsi_peer object. */
Craig Tillera82950e2015-09-22 12:33:20 -0700374static tsi_result peer_from_x509(X509 *cert, int include_certificate_type,
375 tsi_peer *peer) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800376 /* TODO(jboeuf): Maybe add more properties. */
Craig Tillera82950e2015-09-22 12:33:20 -0700377 GENERAL_NAMES *subject_alt_names =
378 X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0);
Julien Boeuf01ac4f02015-10-09 16:08:24 -0700379 int subject_alt_name_count = (subject_alt_names != NULL)
380 ? (int)sk_GENERAL_NAME_num(subject_alt_names)
381 : 0;
Craig Tiller3121fd42015-09-10 09:56:20 -0700382 size_t property_count;
383 tsi_result result;
Craig Tillera82950e2015-09-22 12:33:20 -0700384 GPR_ASSERT(subject_alt_name_count >= 0);
385 property_count = (include_certificate_type ? (size_t)1 : 0) +
Deepak Lukosee61cbe32016-03-14 14:10:44 -0700386 2 /* common name, certificate */ +
387 (size_t)subject_alt_name_count;
Craig Tillera82950e2015-09-22 12:33:20 -0700388 result = tsi_construct_peer(property_count, peer);
389 if (result != TSI_OK) return result;
390 do {
391 if (include_certificate_type) {
392 result = tsi_construct_string_peer_property_from_cstring(
393 TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE,
394 &peer->properties[0]);
395 if (result != TSI_OK) break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800396 }
Craig Tillera82950e2015-09-22 12:33:20 -0700397 result = peer_property_from_x509_common_name(
398 cert, &peer->properties[include_certificate_type ? 1 : 0]);
399 if (result != TSI_OK) break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800400
Deepak Lukosee61cbe32016-03-14 14:10:44 -0700401 result = add_pem_certificate(
402 cert, &peer->properties[include_certificate_type ? 2 : 1]);
403 if (result != TSI_OK) break;
404
Craig Tillera82950e2015-09-22 12:33:20 -0700405 if (subject_alt_name_count != 0) {
406 result = add_subject_alt_names_properties_to_peer(
407 peer, subject_alt_names, (size_t)subject_alt_name_count);
408 if (result != TSI_OK) break;
Craig Tiller45724b32015-09-22 10:42:19 -0700409 }
Craig Tillera82950e2015-09-22 12:33:20 -0700410 } while (0);
411
412 if (subject_alt_names != NULL) {
413 sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free);
414 }
415 if (result != TSI_OK) tsi_peer_destruct(peer);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800416 return result;
417}
418
nnoble0c475f02014-12-05 15:37:39 -0800419/* Logs the SSL error stack. */
Craig Tillera82950e2015-09-22 12:33:20 -0700420static void log_ssl_error_stack(void) {
nnoble0c475f02014-12-05 15:37:39 -0800421 unsigned long err;
Craig Tillera82950e2015-09-22 12:33:20 -0700422 while ((err = ERR_get_error()) != 0) {
423 char details[256];
Julien Boeuf01ac4f02015-10-09 16:08:24 -0700424 ERR_error_string_n((uint32_t)err, details, sizeof(details));
Craig Tillera82950e2015-09-22 12:33:20 -0700425 gpr_log(GPR_ERROR, "%s", details);
426 }
nnoble0c475f02014-12-05 15:37:39 -0800427}
428
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800429/* Performs an SSL_read and handle errors. */
Craig Tillera82950e2015-09-22 12:33:20 -0700430static tsi_result do_ssl_read(SSL *ssl, unsigned char *unprotected_bytes,
431 size_t *unprotected_bytes_size) {
Craig Tillerf96dfc32015-09-10 14:43:18 -0700432 int read_from_ssl;
Craig Tillera82950e2015-09-22 12:33:20 -0700433 GPR_ASSERT(*unprotected_bytes_size <= INT_MAX);
434 read_from_ssl =
435 SSL_read(ssl, unprotected_bytes, (int)*unprotected_bytes_size);
436 if (read_from_ssl == 0) {
437 gpr_log(GPR_ERROR, "SSL_read returned 0 unexpectedly.");
438 return TSI_INTERNAL_ERROR;
439 }
440 if (read_from_ssl < 0) {
441 read_from_ssl = SSL_get_error(ssl, read_from_ssl);
442 switch (read_from_ssl) {
443 case SSL_ERROR_WANT_READ:
444 /* We need more data to finish the frame. */
445 *unprotected_bytes_size = 0;
446 return TSI_OK;
447 case SSL_ERROR_WANT_WRITE:
448 gpr_log(
449 GPR_ERROR,
450 "Peer tried to renegotiate SSL connection. This is unsupported.");
451 return TSI_UNIMPLEMENTED;
452 case SSL_ERROR_SSL:
453 gpr_log(GPR_ERROR, "Corruption detected.");
454 log_ssl_error_stack();
455 return TSI_DATA_CORRUPTED;
456 default:
457 gpr_log(GPR_ERROR, "SSL_read failed with error %s.",
458 ssl_error_string(read_from_ssl));
459 return TSI_PROTOCOL_FAILURE;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800460 }
Craig Tillera82950e2015-09-22 12:33:20 -0700461 }
462 *unprotected_bytes_size = (size_t)read_from_ssl;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800463 return TSI_OK;
464}
465
466/* Performs an SSL_write and handle errors. */
Craig Tillera82950e2015-09-22 12:33:20 -0700467static tsi_result do_ssl_write(SSL *ssl, unsigned char *unprotected_bytes,
468 size_t unprotected_bytes_size) {
Craig Tillerf96dfc32015-09-10 14:43:18 -0700469 int ssl_write_result;
Craig Tillera82950e2015-09-22 12:33:20 -0700470 GPR_ASSERT(unprotected_bytes_size <= INT_MAX);
471 ssl_write_result =
472 SSL_write(ssl, unprotected_bytes, (int)unprotected_bytes_size);
473 if (ssl_write_result < 0) {
474 ssl_write_result = SSL_get_error(ssl, ssl_write_result);
475 if (ssl_write_result == SSL_ERROR_WANT_READ) {
476 gpr_log(GPR_ERROR,
477 "Peer tried to renegotiate SSL connection. This is unsupported.");
478 return TSI_UNIMPLEMENTED;
479 } else {
480 gpr_log(GPR_ERROR, "SSL_write failed with error %s.",
481 ssl_error_string(ssl_write_result));
482 return TSI_INTERNAL_ERROR;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800483 }
Craig Tillera82950e2015-09-22 12:33:20 -0700484 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800485 return TSI_OK;
486}
487
488/* Loads an in-memory PEM certificate chain into the SSL context. */
Craig Tillera82950e2015-09-22 12:33:20 -0700489static tsi_result ssl_ctx_use_certificate_chain(
490 SSL_CTX *context, const unsigned char *pem_cert_chain,
491 size_t pem_cert_chain_size) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800492 tsi_result result = TSI_OK;
Craig Tiller45724b32015-09-22 10:42:19 -0700493 X509 *certificate = NULL;
494 BIO *pem;
Craig Tillera82950e2015-09-22 12:33:20 -0700495 GPR_ASSERT(pem_cert_chain_size <= INT_MAX);
496 pem = BIO_new_mem_buf((void *)pem_cert_chain, (int)pem_cert_chain_size);
497 if (pem == NULL) return TSI_OUT_OF_RESOURCES;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800498
Craig Tillera82950e2015-09-22 12:33:20 -0700499 do {
500 certificate = PEM_read_bio_X509_AUX(pem, NULL, NULL, "");
501 if (certificate == NULL) {
502 result = TSI_INVALID_ARGUMENT;
503 break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800504 }
Craig Tillera82950e2015-09-22 12:33:20 -0700505 if (!SSL_CTX_use_certificate(context, certificate)) {
506 result = TSI_INVALID_ARGUMENT;
507 break;
508 }
509 while (1) {
510 X509 *certificate_authority = PEM_read_bio_X509(pem, NULL, NULL, "");
511 if (certificate_authority == NULL) {
512 ERR_clear_error();
513 break; /* Done reading. */
514 }
515 if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) {
516 X509_free(certificate_authority);
517 result = TSI_INVALID_ARGUMENT;
518 break;
519 }
520 /* We don't need to free certificate_authority as its ownership has been
521 transfered to the context. That is not the case for certificate though.
522 */
523 }
524 } while (0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800525
Craig Tillera82950e2015-09-22 12:33:20 -0700526 if (certificate != NULL) X509_free(certificate);
527 BIO_free(pem);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800528 return result;
529}
530
531/* Loads an in-memory PEM private key into the SSL context. */
Craig Tillera82950e2015-09-22 12:33:20 -0700532static tsi_result ssl_ctx_use_private_key(SSL_CTX *context,
533 const unsigned char *pem_key,
534 size_t pem_key_size) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800535 tsi_result result = TSI_OK;
Craig Tiller45724b32015-09-22 10:42:19 -0700536 EVP_PKEY *private_key = NULL;
537 BIO *pem;
Craig Tillera82950e2015-09-22 12:33:20 -0700538 GPR_ASSERT(pem_key_size <= INT_MAX);
539 pem = BIO_new_mem_buf((void *)pem_key, (int)pem_key_size);
540 if (pem == NULL) return TSI_OUT_OF_RESOURCES;
541 do {
542 private_key = PEM_read_bio_PrivateKey(pem, NULL, NULL, "");
543 if (private_key == NULL) {
544 result = TSI_INVALID_ARGUMENT;
545 break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800546 }
Craig Tillera82950e2015-09-22 12:33:20 -0700547 if (!SSL_CTX_use_PrivateKey(context, private_key)) {
548 result = TSI_INVALID_ARGUMENT;
549 break;
550 }
551 } while (0);
552 if (private_key != NULL) EVP_PKEY_free(private_key);
553 BIO_free(pem);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800554 return result;
555}
556
557/* Loads in-memory PEM verification certs into the SSL context and optionally
558 returns the verification cert names (root_names can be NULL). */
Craig Tillera82950e2015-09-22 12:33:20 -0700559static tsi_result ssl_ctx_load_verification_certs(
560 SSL_CTX *context, const unsigned char *pem_roots, size_t pem_roots_size,
561 STACK_OF(X509_NAME) * *root_names) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800562 tsi_result result = TSI_OK;
Julien Boeufb222b4d2015-01-15 17:01:39 -0800563 size_t num_roots = 0;
Craig Tiller45724b32015-09-22 10:42:19 -0700564 X509 *root = NULL;
565 X509_NAME *root_name = NULL;
566 BIO *pem;
567 X509_STORE *root_store;
Craig Tillera82950e2015-09-22 12:33:20 -0700568 GPR_ASSERT(pem_roots_size <= INT_MAX);
569 pem = BIO_new_mem_buf((void *)pem_roots, (int)pem_roots_size);
570 root_store = SSL_CTX_get_cert_store(context);
571 if (root_store == NULL) return TSI_INVALID_ARGUMENT;
572 if (pem == NULL) return TSI_OUT_OF_RESOURCES;
573 if (root_names != NULL) {
574 *root_names = sk_X509_NAME_new_null();
575 if (*root_names == NULL) return TSI_OUT_OF_RESOURCES;
576 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800577
Craig Tillera82950e2015-09-22 12:33:20 -0700578 while (1) {
579 root = PEM_read_bio_X509_AUX(pem, NULL, NULL, "");
580 if (root == NULL) {
581 ERR_clear_error();
582 break; /* We're at the end of stream. */
nnoble0c475f02014-12-05 15:37:39 -0800583 }
Craig Tillera82950e2015-09-22 12:33:20 -0700584 if (root_names != NULL) {
585 root_name = X509_get_subject_name(root);
586 if (root_name == NULL) {
587 gpr_log(GPR_ERROR, "Could not get name from root certificate.");
588 result = TSI_INVALID_ARGUMENT;
589 break;
590 }
591 root_name = X509_NAME_dup(root_name);
592 if (root_name == NULL) {
593 result = TSI_OUT_OF_RESOURCES;
594 break;
595 }
596 sk_X509_NAME_push(*root_names, root_name);
597 root_name = NULL;
598 }
599 if (!X509_STORE_add_cert(root_store, root)) {
600 gpr_log(GPR_ERROR, "Could not add root certificate to ssl context.");
601 result = TSI_INTERNAL_ERROR;
602 break;
603 }
604 X509_free(root);
605 num_roots++;
606 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800607
Craig Tillera82950e2015-09-22 12:33:20 -0700608 if (num_roots == 0) {
609 gpr_log(GPR_ERROR, "Could not load any root certificate.");
610 result = TSI_INVALID_ARGUMENT;
611 }
Craig Tiller45724b32015-09-22 10:42:19 -0700612
Craig Tillera82950e2015-09-22 12:33:20 -0700613 if (result != TSI_OK) {
614 if (root != NULL) X509_free(root);
615 if (root_names != NULL) {
616 sk_X509_NAME_pop_free(*root_names, X509_NAME_free);
617 *root_names = NULL;
618 if (root_name != NULL) X509_NAME_free(root_name);
Craig Tiller45724b32015-09-22 10:42:19 -0700619 }
Craig Tillera82950e2015-09-22 12:33:20 -0700620 }
621 BIO_free(pem);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800622 return result;
623}
624
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800625/* Populates the SSL context with a private key and a cert chain, and sets the
626 cipher list and the ephemeral ECDH key. */
Craig Tillera82950e2015-09-22 12:33:20 -0700627static tsi_result populate_ssl_context(
628 SSL_CTX *context, const unsigned char *pem_private_key,
629 size_t pem_private_key_size, const unsigned char *pem_certificate_chain,
630 size_t pem_certificate_chain_size, const char *cipher_list) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800631 tsi_result result = TSI_OK;
Craig Tillera82950e2015-09-22 12:33:20 -0700632 if (pem_certificate_chain != NULL) {
633 result = ssl_ctx_use_certificate_chain(context, pem_certificate_chain,
634 pem_certificate_chain_size);
635 if (result != TSI_OK) {
636 gpr_log(GPR_ERROR, "Invalid cert chain file.");
637 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800638 }
Craig Tillera82950e2015-09-22 12:33:20 -0700639 }
640 if (pem_private_key != NULL) {
641 result =
642 ssl_ctx_use_private_key(context, pem_private_key, pem_private_key_size);
643 if (result != TSI_OK || !SSL_CTX_check_private_key(context)) {
644 gpr_log(GPR_ERROR, "Invalid private key.");
645 return result != TSI_OK ? result : TSI_INVALID_ARGUMENT;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800646 }
Craig Tillera82950e2015-09-22 12:33:20 -0700647 }
648 if ((cipher_list != NULL) && !SSL_CTX_set_cipher_list(context, cipher_list)) {
649 gpr_log(GPR_ERROR, "Invalid cipher list: %s.", cipher_list);
650 return TSI_INVALID_ARGUMENT;
651 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800652 {
Craig Tillera82950e2015-09-22 12:33:20 -0700653 EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
654 if (!SSL_CTX_set_tmp_ecdh(context, ecdh)) {
655 gpr_log(GPR_ERROR, "Could not set ephemeral ECDH key.");
656 EC_KEY_free(ecdh);
657 return TSI_INTERNAL_ERROR;
658 }
659 SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE);
660 EC_KEY_free(ecdh);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800661 }
662 return TSI_OK;
663}
664
665/* Extracts the CN and the SANs from an X509 cert as a peer object. */
Craig Tillera82950e2015-09-22 12:33:20 -0700666static tsi_result extract_x509_subject_names_from_pem_cert(
667 const unsigned char *pem_cert, size_t pem_cert_size, tsi_peer *peer) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800668 tsi_result result = TSI_OK;
Craig Tiller45724b32015-09-22 10:42:19 -0700669 X509 *cert = NULL;
670 BIO *pem;
Craig Tillera82950e2015-09-22 12:33:20 -0700671 GPR_ASSERT(pem_cert_size <= INT_MAX);
672 pem = BIO_new_mem_buf((void *)pem_cert, (int)pem_cert_size);
673 if (pem == NULL) return TSI_OUT_OF_RESOURCES;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800674
Craig Tillera82950e2015-09-22 12:33:20 -0700675 cert = PEM_read_bio_X509(pem, NULL, NULL, "");
676 if (cert == NULL) {
677 gpr_log(GPR_ERROR, "Invalid certificate");
678 result = TSI_INVALID_ARGUMENT;
679 } else {
680 result = peer_from_x509(cert, 0, peer);
681 }
682 if (cert != NULL) X509_free(cert);
683 BIO_free(pem);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800684 return result;
685}
686
687/* Builds the alpn protocol name list according to rfc 7301. */
Craig Tillera82950e2015-09-22 12:33:20 -0700688static tsi_result build_alpn_protocol_name_list(
689 const unsigned char **alpn_protocols,
690 const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
691 unsigned char **protocol_name_list, size_t *protocol_name_list_length) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800692 uint16_t i;
Craig Tiller45724b32015-09-22 10:42:19 -0700693 unsigned char *current;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800694 *protocol_name_list = NULL;
695 *protocol_name_list_length = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700696 if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT;
697 for (i = 0; i < num_alpn_protocols; i++) {
698 if (alpn_protocols_lengths[i] == 0) {
699 gpr_log(GPR_ERROR, "Invalid 0-length protocol name.");
700 return TSI_INVALID_ARGUMENT;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800701 }
Craig Tillera82950e2015-09-22 12:33:20 -0700702 *protocol_name_list_length += (size_t)alpn_protocols_lengths[i] + 1;
703 }
704 *protocol_name_list = malloc(*protocol_name_list_length);
705 if (*protocol_name_list == NULL) return TSI_OUT_OF_RESOURCES;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800706 current = *protocol_name_list;
Craig Tillera82950e2015-09-22 12:33:20 -0700707 for (i = 0; i < num_alpn_protocols; i++) {
708 *(current++) = alpn_protocols_lengths[i];
709 memcpy(current, alpn_protocols[i], alpn_protocols_lengths[i]);
710 current += alpn_protocols_lengths[i];
711 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800712 /* Safety check. */
Craig Tillera82950e2015-09-22 12:33:20 -0700713 if ((current < *protocol_name_list) ||
Craig Tiller7536af02015-12-22 13:49:30 -0800714 ((uintptr_t)(current - *protocol_name_list) !=
Craig Tillera82950e2015-09-22 12:33:20 -0700715 *protocol_name_list_length)) {
716 return TSI_INTERNAL_ERROR;
717 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800718 return TSI_OK;
719}
720
721/* --- tsi_frame_protector methods implementation. ---*/
722
Craig Tillera82950e2015-09-22 12:33:20 -0700723static tsi_result ssl_protector_protect(tsi_frame_protector *self,
724 const unsigned char *unprotected_bytes,
725 size_t *unprotected_bytes_size,
726 unsigned char *protected_output_frames,
727 size_t *protected_output_frames_size) {
728 tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800729 int read_from_ssl;
Julien Boeufb222b4d2015-01-15 17:01:39 -0800730 size_t available;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800731 tsi_result result = TSI_OK;
732
733 /* First see if we have some pending data in the SSL BIO. */
Julien Boeuf01ac4f02015-10-09 16:08:24 -0700734 int pending_in_ssl = (int)BIO_pending(impl->from_ssl);
Craig Tillera82950e2015-09-22 12:33:20 -0700735 if (pending_in_ssl > 0) {
736 *unprotected_bytes_size = 0;
737 GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
738 read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames,
739 (int)*protected_output_frames_size);
740 if (read_from_ssl < 0) {
741 gpr_log(GPR_ERROR,
742 "Could not read from BIO even though some data is pending");
743 return TSI_INTERNAL_ERROR;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800744 }
Craig Tillera82950e2015-09-22 12:33:20 -0700745 *protected_output_frames_size = (size_t)read_from_ssl;
746 return TSI_OK;
747 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800748
749 /* Now see if we can send a complete frame. */
750 available = impl->buffer_size - impl->buffer_offset;
Craig Tillera82950e2015-09-22 12:33:20 -0700751 if (available > *unprotected_bytes_size) {
752 /* If we cannot, just copy the data in our internal buffer. */
753 memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes,
754 *unprotected_bytes_size);
755 impl->buffer_offset += *unprotected_bytes_size;
756 *protected_output_frames_size = 0;
757 return TSI_OK;
758 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800759
760 /* If we can, prepare the buffer, send it to SSL_write and read. */
Craig Tillera82950e2015-09-22 12:33:20 -0700761 memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, available);
762 result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_size);
763 if (result != TSI_OK) return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800764
Craig Tillera82950e2015-09-22 12:33:20 -0700765 GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
766 read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames,
767 (int)*protected_output_frames_size);
768 if (read_from_ssl < 0) {
769 gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write.");
770 return TSI_INTERNAL_ERROR;
771 }
772 *protected_output_frames_size = (size_t)read_from_ssl;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800773 *unprotected_bytes_size = available;
774 impl->buffer_offset = 0;
775 return TSI_OK;
776}
777
Craig Tillera82950e2015-09-22 12:33:20 -0700778static tsi_result ssl_protector_protect_flush(
779 tsi_frame_protector *self, unsigned char *protected_output_frames,
780 size_t *protected_output_frames_size, size_t *still_pending_size) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800781 tsi_result result = TSI_OK;
Craig Tillera82950e2015-09-22 12:33:20 -0700782 tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800783 int read_from_ssl = 0;
Craig Tiller3121fd42015-09-10 09:56:20 -0700784 int pending;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800785
Craig Tillera82950e2015-09-22 12:33:20 -0700786 if (impl->buffer_offset != 0) {
787 result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_offset);
788 if (result != TSI_OK) return result;
789 impl->buffer_offset = 0;
790 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800791
Julien Boeuf01ac4f02015-10-09 16:08:24 -0700792 pending = (int)BIO_pending(impl->from_ssl);
Craig Tillera82950e2015-09-22 12:33:20 -0700793 GPR_ASSERT(pending >= 0);
794 *still_pending_size = (size_t)pending;
795 if (*still_pending_size == 0) return TSI_OK;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800796
Craig Tillera82950e2015-09-22 12:33:20 -0700797 GPR_ASSERT(*protected_output_frames_size <= INT_MAX);
798 read_from_ssl = BIO_read(impl->from_ssl, protected_output_frames,
799 (int)*protected_output_frames_size);
800 if (read_from_ssl <= 0) {
801 gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write.");
802 return TSI_INTERNAL_ERROR;
803 }
804 *protected_output_frames_size = (size_t)read_from_ssl;
Julien Boeuf01ac4f02015-10-09 16:08:24 -0700805 pending = (int)BIO_pending(impl->from_ssl);
Craig Tillera82950e2015-09-22 12:33:20 -0700806 GPR_ASSERT(pending >= 0);
807 *still_pending_size = (size_t)pending;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800808 return TSI_OK;
809}
810
Craig Tillera82950e2015-09-22 12:33:20 -0700811static tsi_result ssl_protector_unprotect(
812 tsi_frame_protector *self, const unsigned char *protected_frames_bytes,
813 size_t *protected_frames_bytes_size, unsigned char *unprotected_bytes,
814 size_t *unprotected_bytes_size) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800815 tsi_result result = TSI_OK;
816 int written_into_ssl = 0;
Julien Boeufb222b4d2015-01-15 17:01:39 -0800817 size_t output_bytes_size = *unprotected_bytes_size;
818 size_t output_bytes_offset = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700819 tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800820
821 /* First, try to read remaining data from ssl. */
Craig Tillera82950e2015-09-22 12:33:20 -0700822 result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size);
823 if (result != TSI_OK) return result;
824 if (*unprotected_bytes_size == output_bytes_size) {
825 /* We have read everything we could and cannot process any more input. */
826 *protected_frames_bytes_size = 0;
827 return TSI_OK;
828 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800829 output_bytes_offset = *unprotected_bytes_size;
830 unprotected_bytes += output_bytes_offset;
831 *unprotected_bytes_size = output_bytes_size - output_bytes_offset;
832
833 /* Then, try to write some data to ssl. */
Craig Tillera82950e2015-09-22 12:33:20 -0700834 GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX);
835 written_into_ssl = BIO_write(impl->into_ssl, protected_frames_bytes,
836 (int)*protected_frames_bytes_size);
837 if (written_into_ssl < 0) {
838 gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d",
839 written_into_ssl);
840 return TSI_INTERNAL_ERROR;
841 }
842 *protected_frames_bytes_size = (size_t)written_into_ssl;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800843
844 /* Now try to read some data again. */
Craig Tillera82950e2015-09-22 12:33:20 -0700845 result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size);
846 if (result == TSI_OK) {
847 /* Don't forget to output the total number of bytes read. */
848 *unprotected_bytes_size += output_bytes_offset;
849 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800850 return result;
851}
852
Craig Tillera82950e2015-09-22 12:33:20 -0700853static void ssl_protector_destroy(tsi_frame_protector *self) {
854 tsi_ssl_frame_protector *impl = (tsi_ssl_frame_protector *)self;
855 if (impl->buffer != NULL) free(impl->buffer);
856 if (impl->ssl != NULL) SSL_free(impl->ssl);
857 free(self);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800858}
859
860static const tsi_frame_protector_vtable frame_protector_vtable = {
Craig Tillera82950e2015-09-22 12:33:20 -0700861 ssl_protector_protect, ssl_protector_protect_flush, ssl_protector_unprotect,
862 ssl_protector_destroy,
Craig Tillerd6c98df2015-08-18 09:33:44 -0700863};
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800864
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800865/* --- tsi_handshaker methods implementation. ---*/
866
Craig Tillera82950e2015-09-22 12:33:20 -0700867static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker *self,
868 unsigned char *bytes,
869 size_t *bytes_size) {
870 tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800871 int bytes_read_from_ssl = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700872 if (bytes == NULL || bytes_size == NULL || *bytes_size == 0 ||
873 *bytes_size > INT_MAX) {
874 return TSI_INVALID_ARGUMENT;
875 }
876 GPR_ASSERT(*bytes_size <= INT_MAX);
877 bytes_read_from_ssl = BIO_read(impl->from_ssl, bytes, (int)*bytes_size);
878 if (bytes_read_from_ssl < 0) {
879 *bytes_size = 0;
880 if (!BIO_should_retry(impl->from_ssl)) {
881 impl->result = TSI_INTERNAL_ERROR;
882 return impl->result;
883 } else {
884 return TSI_OK;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800885 }
Craig Tillera82950e2015-09-22 12:33:20 -0700886 }
887 *bytes_size = (size_t)bytes_read_from_ssl;
888 return BIO_pending(impl->from_ssl) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800889}
890
Craig Tillera82950e2015-09-22 12:33:20 -0700891static tsi_result ssl_handshaker_get_result(tsi_handshaker *self) {
892 tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self;
893 if ((impl->result == TSI_HANDSHAKE_IN_PROGRESS) &&
894 SSL_is_init_finished(impl->ssl)) {
895 impl->result = TSI_OK;
896 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800897 return impl->result;
898}
899
Craig Tillera82950e2015-09-22 12:33:20 -0700900static tsi_result ssl_handshaker_process_bytes_from_peer(
901 tsi_handshaker *self, const unsigned char *bytes, size_t *bytes_size) {
902 tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800903 int bytes_written_into_ssl_size = 0;
Craig Tillera82950e2015-09-22 12:33:20 -0700904 if (bytes == NULL || bytes_size == 0 || *bytes_size > INT_MAX) {
905 return TSI_INVALID_ARGUMENT;
906 }
907 GPR_ASSERT(*bytes_size <= INT_MAX);
908 bytes_written_into_ssl_size =
909 BIO_write(impl->into_ssl, bytes, (int)*bytes_size);
910 if (bytes_written_into_ssl_size < 0) {
911 gpr_log(GPR_ERROR, "Could not write to memory BIO.");
912 impl->result = TSI_INTERNAL_ERROR;
913 return impl->result;
914 }
915 *bytes_size = (size_t)bytes_written_into_ssl_size;
Craig Tiller45724b32015-09-22 10:42:19 -0700916
Craig Tillera82950e2015-09-22 12:33:20 -0700917 if (!tsi_handshaker_is_in_progress(self)) {
918 impl->result = TSI_OK;
919 return impl->result;
920 } else {
921 /* Get ready to get some bytes from SSL. */
922 int ssl_result = SSL_do_handshake(impl->ssl);
923 ssl_result = SSL_get_error(impl->ssl, ssl_result);
924 switch (ssl_result) {
925 case SSL_ERROR_WANT_READ:
926 if (BIO_pending(impl->from_ssl) == 0) {
927 /* We need more data. */
928 return TSI_INCOMPLETE_DATA;
929 } else {
930 return TSI_OK;
931 }
932 case SSL_ERROR_NONE:
933 return TSI_OK;
934 default: {
935 char err_str[256];
936 ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str));
937 gpr_log(GPR_ERROR, "Handshake failed with fatal error %s: %s.",
938 ssl_error_string(ssl_result), err_str);
939 impl->result = TSI_PROTOCOL_FAILURE;
940 return impl->result;
941 }
Craig Tiller45724b32015-09-22 10:42:19 -0700942 }
Craig Tillera82950e2015-09-22 12:33:20 -0700943 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800944}
945
Craig Tillera82950e2015-09-22 12:33:20 -0700946static tsi_result ssl_handshaker_extract_peer(tsi_handshaker *self,
947 tsi_peer *peer) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800948 tsi_result result = TSI_OK;
Craig Tiller45724b32015-09-22 10:42:19 -0700949 const unsigned char *alpn_selected = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800950 unsigned int alpn_selected_len;
Craig Tillera82950e2015-09-22 12:33:20 -0700951 tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self;
952 X509 *peer_cert = SSL_get_peer_certificate(impl->ssl);
953 if (peer_cert != NULL) {
954 result = peer_from_x509(peer_cert, 1, peer);
955 X509_free(peer_cert);
956 if (result != TSI_OK) return result;
957 }
Julien Boeufd1531322015-06-18 11:05:39 +0200958#if TSI_OPENSSL_ALPN_SUPPORT
Craig Tillera82950e2015-09-22 12:33:20 -0700959 SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len);
Julien Boeufd1531322015-06-18 11:05:39 +0200960#endif /* TSI_OPENSSL_ALPN_SUPPORT */
Craig Tillera82950e2015-09-22 12:33:20 -0700961 if (alpn_selected == NULL) {
962 /* Try npn. */
963 SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected,
964 &alpn_selected_len);
965 }
966 if (alpn_selected != NULL) {
967 size_t i;
968 tsi_peer_property *new_properties =
969 calloc(1, sizeof(tsi_peer_property) * (peer->property_count + 1));
970 if (new_properties == NULL) return TSI_OUT_OF_RESOURCES;
971 for (i = 0; i < peer->property_count; i++) {
972 new_properties[i] = peer->properties[i];
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800973 }
Craig Tillera82950e2015-09-22 12:33:20 -0700974 result = tsi_construct_string_peer_property(
975 TSI_SSL_ALPN_SELECTED_PROTOCOL, (const char *)alpn_selected,
976 alpn_selected_len, &new_properties[peer->property_count]);
977 if (result != TSI_OK) {
978 free(new_properties);
979 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800980 }
Craig Tillera82950e2015-09-22 12:33:20 -0700981 if (peer->properties != NULL) free(peer->properties);
982 peer->property_count++;
983 peer->properties = new_properties;
984 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800985 return result;
986}
987
Craig Tillera82950e2015-09-22 12:33:20 -0700988static tsi_result ssl_handshaker_create_frame_protector(
989 tsi_handshaker *self, size_t *max_output_protected_frame_size,
990 tsi_frame_protector **protector) {
991 size_t actual_max_output_protected_frame_size =
992 TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND;
993 tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self;
994 tsi_ssl_frame_protector *protector_impl =
995 calloc(1, sizeof(tsi_ssl_frame_protector));
996 if (protector_impl == NULL) {
997 return TSI_OUT_OF_RESOURCES;
998 }
Craig Tiller45724b32015-09-22 10:42:19 -0700999
Craig Tillera82950e2015-09-22 12:33:20 -07001000 if (max_output_protected_frame_size != NULL) {
1001 if (*max_output_protected_frame_size >
1002 TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND) {
1003 *max_output_protected_frame_size =
1004 TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND;
1005 } else if (*max_output_protected_frame_size <
1006 TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND) {
1007 *max_output_protected_frame_size =
1008 TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND;
Craig Tiller45724b32015-09-22 10:42:19 -07001009 }
Craig Tillera82950e2015-09-22 12:33:20 -07001010 actual_max_output_protected_frame_size = *max_output_protected_frame_size;
1011 }
1012 protector_impl->buffer_size =
1013 actual_max_output_protected_frame_size - TSI_SSL_MAX_PROTECTION_OVERHEAD;
1014 protector_impl->buffer = malloc(protector_impl->buffer_size);
1015 if (protector_impl->buffer == NULL) {
1016 gpr_log(GPR_ERROR,
1017 "Could not allocated buffer for tsi_ssl_frame_protector.");
1018 free(protector_impl);
1019 return TSI_INTERNAL_ERROR;
1020 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001021
1022 /* Transfer ownership of ssl to the frame protector. It is OK as the caller
1023 * cannot call anything else but destroy on the handshaker after this call. */
1024 protector_impl->ssl = impl->ssl;
1025 impl->ssl = NULL;
1026 protector_impl->into_ssl = impl->into_ssl;
1027 protector_impl->from_ssl = impl->from_ssl;
1028
1029 protector_impl->base.vtable = &frame_protector_vtable;
1030 *protector = &protector_impl->base;
1031 return TSI_OK;
1032}
1033
Craig Tillera82950e2015-09-22 12:33:20 -07001034static void ssl_handshaker_destroy(tsi_handshaker *self) {
1035 tsi_ssl_handshaker *impl = (tsi_ssl_handshaker *)self;
1036 SSL_free(impl->ssl); /* The BIO objects are owned by ssl */
1037 free(impl);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001038}
1039
1040static const tsi_handshaker_vtable handshaker_vtable = {
Craig Tillera82950e2015-09-22 12:33:20 -07001041 ssl_handshaker_get_bytes_to_send_to_peer,
Craig Tillerf40df232016-03-25 13:38:14 -07001042 ssl_handshaker_process_bytes_from_peer,
1043 ssl_handshaker_get_result,
1044 ssl_handshaker_extract_peer,
1045 ssl_handshaker_create_frame_protector,
Craig Tillera82950e2015-09-22 12:33:20 -07001046 ssl_handshaker_destroy,
Craig Tillerd6c98df2015-08-18 09:33:44 -07001047};
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001048
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001049/* --- tsi_ssl_handshaker_factory common methods. --- */
1050
Craig Tillera82950e2015-09-22 12:33:20 -07001051tsi_result tsi_ssl_handshaker_factory_create_handshaker(
1052 tsi_ssl_handshaker_factory *self, const char *server_name_indication,
1053 tsi_handshaker **handshaker) {
1054 if (self == NULL || handshaker == NULL) return TSI_INVALID_ARGUMENT;
1055 return self->create_handshaker(self, server_name_indication, handshaker);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001056}
1057
Craig Tillera82950e2015-09-22 12:33:20 -07001058void tsi_ssl_handshaker_factory_destroy(tsi_ssl_handshaker_factory *self) {
1059 if (self == NULL) return;
1060 self->destroy(self);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001061}
1062
Craig Tillera82950e2015-09-22 12:33:20 -07001063static tsi_result create_tsi_ssl_handshaker(SSL_CTX *ctx, int is_client,
1064 const char *server_name_indication,
1065 tsi_handshaker **handshaker) {
1066 SSL *ssl = SSL_new(ctx);
Craig Tiller45724b32015-09-22 10:42:19 -07001067 BIO *into_ssl = NULL;
1068 BIO *from_ssl = NULL;
1069 tsi_ssl_handshaker *impl = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001070 *handshaker = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -07001071 if (ctx == NULL) {
1072 gpr_log(GPR_ERROR, "SSL Context is null. Should never happen.");
1073 return TSI_INTERNAL_ERROR;
1074 }
1075 if (ssl == NULL) {
1076 return TSI_OUT_OF_RESOURCES;
1077 }
1078 SSL_set_info_callback(ssl, ssl_info_callback);
1079
1080 into_ssl = BIO_new(BIO_s_mem());
1081 from_ssl = BIO_new(BIO_s_mem());
1082 if (into_ssl == NULL || from_ssl == NULL) {
1083 gpr_log(GPR_ERROR, "BIO_new failed.");
1084 SSL_free(ssl);
1085 if (into_ssl != NULL) BIO_free(into_ssl);
1086 if (from_ssl != NULL) BIO_free(into_ssl);
1087 return TSI_OUT_OF_RESOURCES;
1088 }
1089 SSL_set_bio(ssl, into_ssl, from_ssl);
1090 if (is_client) {
1091 int ssl_result;
1092 SSL_set_connect_state(ssl);
1093 if (server_name_indication != NULL) {
1094 if (!SSL_set_tlsext_host_name(ssl, server_name_indication)) {
1095 gpr_log(GPR_ERROR, "Invalid server name indication %s.",
1096 server_name_indication);
1097 SSL_free(ssl);
1098 return TSI_INTERNAL_ERROR;
1099 }
1100 }
1101 ssl_result = SSL_do_handshake(ssl);
1102 ssl_result = SSL_get_error(ssl, ssl_result);
1103 if (ssl_result != SSL_ERROR_WANT_READ) {
1104 gpr_log(GPR_ERROR,
1105 "Unexpected error received from first SSL_do_handshake call: %s",
1106 ssl_error_string(ssl_result));
1107 SSL_free(ssl);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001108 return TSI_INTERNAL_ERROR;
1109 }
Craig Tillera82950e2015-09-22 12:33:20 -07001110 } else {
1111 SSL_set_accept_state(ssl);
1112 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001113
Craig Tillera82950e2015-09-22 12:33:20 -07001114 impl = calloc(1, sizeof(tsi_ssl_handshaker));
1115 if (impl == NULL) {
1116 SSL_free(ssl);
1117 return TSI_OUT_OF_RESOURCES;
1118 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001119 impl->ssl = ssl;
1120 impl->into_ssl = into_ssl;
1121 impl->from_ssl = from_ssl;
1122 impl->result = TSI_HANDSHAKE_IN_PROGRESS;
1123 impl->base.vtable = &handshaker_vtable;
1124 *handshaker = &impl->base;
1125 return TSI_OK;
1126}
1127
Craig Tillera82950e2015-09-22 12:33:20 -07001128static int select_protocol_list(const unsigned char **out,
1129 unsigned char *outlen,
1130 const unsigned char *client_list,
1131 size_t client_list_len,
1132 const unsigned char *server_list,
1133 size_t server_list_len) {
Craig Tiller45724b32015-09-22 10:42:19 -07001134 const unsigned char *client_current = client_list;
Craig Tillera82950e2015-09-22 12:33:20 -07001135 while ((unsigned int)(client_current - client_list) < client_list_len) {
1136 unsigned char client_current_len = *(client_current++);
1137 const unsigned char *server_current = server_list;
1138 while ((server_current >= server_list) &&
Craig Tiller7536af02015-12-22 13:49:30 -08001139 (uintptr_t)(server_current - server_list) < server_list_len) {
Craig Tillera82950e2015-09-22 12:33:20 -07001140 unsigned char server_current_len = *(server_current++);
1141 if ((client_current_len == server_current_len) &&
1142 !memcmp(client_current, server_current, server_current_len)) {
1143 *out = server_current;
1144 *outlen = server_current_len;
1145 return SSL_TLSEXT_ERR_OK;
1146 }
1147 server_current += server_current_len;
Julien Boeufd1531322015-06-18 11:05:39 +02001148 }
Craig Tillera82950e2015-09-22 12:33:20 -07001149 client_current += client_current_len;
1150 }
Julien Boeufd1531322015-06-18 11:05:39 +02001151 return SSL_TLSEXT_ERR_NOACK;
1152}
1153
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001154/* --- tsi_ssl__client_handshaker_factory methods implementation. --- */
1155
Craig Tillera82950e2015-09-22 12:33:20 -07001156static tsi_result ssl_client_handshaker_factory_create_handshaker(
1157 tsi_ssl_handshaker_factory *self, const char *server_name_indication,
1158 tsi_handshaker **handshaker) {
1159 tsi_ssl_client_handshaker_factory *impl =
1160 (tsi_ssl_client_handshaker_factory *)self;
1161 return create_tsi_ssl_handshaker(impl->ssl_context, 1, server_name_indication,
1162 handshaker);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001163}
1164
Craig Tillera82950e2015-09-22 12:33:20 -07001165static void ssl_client_handshaker_factory_destroy(
1166 tsi_ssl_handshaker_factory *self) {
1167 tsi_ssl_client_handshaker_factory *impl =
1168 (tsi_ssl_client_handshaker_factory *)self;
1169 if (impl->ssl_context != NULL) SSL_CTX_free(impl->ssl_context);
1170 if (impl->alpn_protocol_list != NULL) free(impl->alpn_protocol_list);
1171 free(impl);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001172}
1173
Craig Tillera82950e2015-09-22 12:33:20 -07001174static int client_handshaker_factory_npn_callback(SSL *ssl, unsigned char **out,
1175 unsigned char *outlen,
1176 const unsigned char *in,
1177 unsigned int inlen,
1178 void *arg) {
1179 tsi_ssl_client_handshaker_factory *factory =
1180 (tsi_ssl_client_handshaker_factory *)arg;
1181 return select_protocol_list((const unsigned char **)out, outlen,
1182 factory->alpn_protocol_list,
1183 factory->alpn_protocol_list_length, in, inlen);
Julien Boeufd1531322015-06-18 11:05:39 +02001184}
1185
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001186/* --- tsi_ssl_server_handshaker_factory methods implementation. --- */
1187
Craig Tillera82950e2015-09-22 12:33:20 -07001188static tsi_result ssl_server_handshaker_factory_create_handshaker(
1189 tsi_ssl_handshaker_factory *self, const char *server_name_indication,
1190 tsi_handshaker **handshaker) {
1191 tsi_ssl_server_handshaker_factory *impl =
1192 (tsi_ssl_server_handshaker_factory *)self;
1193 if (impl->ssl_context_count == 0 || server_name_indication != NULL) {
1194 return TSI_INVALID_ARGUMENT;
1195 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001196 /* Create the handshaker with the first context. We will switch if needed
1197 because of SNI in ssl_server_handshaker_factory_servername_callback. */
Craig Tillera82950e2015-09-22 12:33:20 -07001198 return create_tsi_ssl_handshaker(impl->ssl_contexts[0], 0, NULL, handshaker);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001199}
1200
Craig Tillera82950e2015-09-22 12:33:20 -07001201static void ssl_server_handshaker_factory_destroy(
1202 tsi_ssl_handshaker_factory *self) {
1203 tsi_ssl_server_handshaker_factory *impl =
1204 (tsi_ssl_server_handshaker_factory *)self;
Julien Boeufb222b4d2015-01-15 17:01:39 -08001205 size_t i;
Craig Tillera82950e2015-09-22 12:33:20 -07001206 for (i = 0; i < impl->ssl_context_count; i++) {
1207 if (impl->ssl_contexts[i] != NULL) {
1208 SSL_CTX_free(impl->ssl_contexts[i]);
1209 tsi_peer_destruct(&impl->ssl_context_x509_subject_names[i]);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001210 }
Craig Tillera82950e2015-09-22 12:33:20 -07001211 }
1212 if (impl->ssl_contexts != NULL) free(impl->ssl_contexts);
1213 if (impl->ssl_context_x509_subject_names != NULL) {
1214 free(impl->ssl_context_x509_subject_names);
1215 }
1216 if (impl->alpn_protocol_list != NULL) free(impl->alpn_protocol_list);
1217 free(impl);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001218}
1219
Craig Tillera82950e2015-09-22 12:33:20 -07001220static int does_entry_match_name(const char *entry, size_t entry_length,
1221 const char *name) {
Craig Tiller45724b32015-09-22 10:42:19 -07001222 const char *dot;
1223 const char *name_subdomain = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -07001224 size_t name_length = strlen(name);
Julien Boeuf9fff77e2015-02-24 16:50:35 -08001225 size_t name_subdomain_length;
Craig Tillera82950e2015-09-22 12:33:20 -07001226 if (entry_length == 0) return 0;
Julien Boeuf9fff77e2015-02-24 16:50:35 -08001227
1228 /* Take care of '.' terminations. */
Craig Tillera82950e2015-09-22 12:33:20 -07001229 if (name[name_length - 1] == '.') {
1230 name_length--;
1231 }
1232 if (entry[entry_length - 1] == '.') {
1233 entry_length--;
1234 if (entry_length == 0) return 0;
1235 }
Julien Boeuf9fff77e2015-02-24 16:50:35 -08001236
Craig Tillera82950e2015-09-22 12:33:20 -07001237 if ((name_length == entry_length) &&
1238 strncmp(name, entry, entry_length) == 0) {
1239 return 1; /* Perfect match. */
1240 }
1241 if (entry[0] != '*') return 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001242
1243 /* Wildchar subdomain matching. */
Craig Tillera82950e2015-09-22 12:33:20 -07001244 if (entry_length < 3 || entry[1] != '.') { /* At least *.x */
1245 gpr_log(GPR_ERROR, "Invalid wildchar entry.");
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001246 return 0;
Craig Tillera82950e2015-09-22 12:33:20 -07001247 }
1248 name_subdomain = strchr(name, '.');
1249 if (name_subdomain == NULL) return 0;
1250 name_subdomain_length = strlen(name_subdomain);
1251 if (name_subdomain_length < 2) return 0;
1252 name_subdomain++; /* Starts after the dot. */
Julien Boeuf0170a6c2015-02-24 18:08:01 -08001253 name_subdomain_length--;
Craig Tillera82950e2015-09-22 12:33:20 -07001254 entry += 2; /* Remove *. */
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001255 entry_length -= 2;
Craig Tillera82950e2015-09-22 12:33:20 -07001256 dot = strchr(name_subdomain, '.');
1257 if ((dot == NULL) || (dot == &name_subdomain[name_subdomain_length - 1])) {
1258 gpr_log(GPR_ERROR, "Invalid toplevel subdomain: %s", name_subdomain);
1259 return 0;
1260 }
1261 if (name_subdomain[name_subdomain_length - 1] == '.') {
1262 name_subdomain_length--;
1263 }
1264 return ((entry_length > 0) && (name_subdomain_length == entry_length) &&
1265 strncmp(entry, name_subdomain, entry_length) == 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001266}
1267
Craig Tillera82950e2015-09-22 12:33:20 -07001268static int ssl_server_handshaker_factory_servername_callback(SSL *ssl, int *ap,
1269 void *arg) {
1270 tsi_ssl_server_handshaker_factory *impl =
1271 (tsi_ssl_server_handshaker_factory *)arg;
Julien Boeufb222b4d2015-01-15 17:01:39 -08001272 size_t i = 0;
Craig Tillera82950e2015-09-22 12:33:20 -07001273 const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
1274 if (servername == NULL || strlen(servername) == 0) {
1275 return SSL_TLSEXT_ERR_NOACK;
1276 }
Craig Tiller45724b32015-09-22 10:42:19 -07001277
Craig Tillera82950e2015-09-22 12:33:20 -07001278 for (i = 0; i < impl->ssl_context_count; i++) {
1279 if (tsi_ssl_peer_matches_name(&impl->ssl_context_x509_subject_names[i],
1280 servername)) {
1281 SSL_set_SSL_CTX(ssl, impl->ssl_contexts[i]);
1282 return SSL_TLSEXT_ERR_OK;
Craig Tiller45724b32015-09-22 10:42:19 -07001283 }
Craig Tillera82950e2015-09-22 12:33:20 -07001284 }
1285 gpr_log(GPR_ERROR, "No match found for server name: %s.", servername);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001286 return SSL_TLSEXT_ERR_ALERT_WARNING;
1287}
1288
Julien Boeufd1531322015-06-18 11:05:39 +02001289#if TSI_OPENSSL_ALPN_SUPPORT
Craig Tillera82950e2015-09-22 12:33:20 -07001290static int server_handshaker_factory_alpn_callback(
1291 SSL *ssl, const unsigned char **out, unsigned char *outlen,
1292 const unsigned char *in, unsigned int inlen, void *arg) {
1293 tsi_ssl_server_handshaker_factory *factory =
1294 (tsi_ssl_server_handshaker_factory *)arg;
1295 return select_protocol_list(out, outlen, in, inlen,
1296 factory->alpn_protocol_list,
1297 factory->alpn_protocol_list_length);
Julien Boeufd1531322015-06-18 11:05:39 +02001298}
1299#endif /* TSI_OPENSSL_ALPN_SUPPORT */
1300
Craig Tillera82950e2015-09-22 12:33:20 -07001301static int server_handshaker_factory_npn_advertised_callback(
1302 SSL *ssl, const unsigned char **out, unsigned int *outlen, void *arg) {
1303 tsi_ssl_server_handshaker_factory *factory =
1304 (tsi_ssl_server_handshaker_factory *)arg;
Julien Boeufd1531322015-06-18 11:05:39 +02001305 *out = factory->alpn_protocol_list;
Craig Tillera82950e2015-09-22 12:33:20 -07001306 GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX);
1307 *outlen = (unsigned int)factory->alpn_protocol_list_length;
Julien Boeufd1531322015-06-18 11:05:39 +02001308 return SSL_TLSEXT_ERR_OK;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001309}
1310
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001311/* --- tsi_ssl_handshaker_factory constructors. --- */
1312
Craig Tillera82950e2015-09-22 12:33:20 -07001313tsi_result tsi_create_ssl_client_handshaker_factory(
1314 const unsigned char *pem_private_key, size_t pem_private_key_size,
1315 const unsigned char *pem_cert_chain, size_t pem_cert_chain_size,
1316 const unsigned char *pem_root_certs, size_t pem_root_certs_size,
1317 const char *cipher_list, const unsigned char **alpn_protocols,
1318 const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
1319 tsi_ssl_handshaker_factory **factory) {
Craig Tiller45724b32015-09-22 10:42:19 -07001320 SSL_CTX *ssl_context = NULL;
1321 tsi_ssl_client_handshaker_factory *impl = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001322 tsi_result result = TSI_OK;
1323
Craig Tillera82950e2015-09-22 12:33:20 -07001324 gpr_once_init(&init_openssl_once, init_openssl);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001325
Craig Tillera82950e2015-09-22 12:33:20 -07001326 if (factory == NULL) return TSI_INVALID_ARGUMENT;
Craig Tiller45724b32015-09-22 10:42:19 -07001327 *factory = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -07001328 if (pem_root_certs == NULL) return TSI_INVALID_ARGUMENT;
1329
1330 ssl_context = SSL_CTX_new(TLSv1_2_method());
1331 if (ssl_context == NULL) {
1332 gpr_log(GPR_ERROR, "Could not create ssl context.");
Craig Tiller45724b32015-09-22 10:42:19 -07001333 return TSI_INVALID_ARGUMENT;
Craig Tillera82950e2015-09-22 12:33:20 -07001334 }
Julien Boeufd1531322015-06-18 11:05:39 +02001335
Craig Tillera82950e2015-09-22 12:33:20 -07001336 impl = calloc(1, sizeof(tsi_ssl_client_handshaker_factory));
1337 if (impl == NULL) {
1338 SSL_CTX_free(ssl_context);
1339 return TSI_OUT_OF_RESOURCES;
1340 }
Julien Boeufd1531322015-06-18 11:05:39 +02001341 impl->ssl_context = ssl_context;
1342
Craig Tillera82950e2015-09-22 12:33:20 -07001343 do {
1344 result =
1345 populate_ssl_context(ssl_context, pem_private_key, pem_private_key_size,
1346 pem_cert_chain, pem_cert_chain_size, cipher_list);
1347 if (result != TSI_OK) break;
1348 result = ssl_ctx_load_verification_certs(ssl_context, pem_root_certs,
1349 pem_root_certs_size, NULL);
1350 if (result != TSI_OK) {
1351 gpr_log(GPR_ERROR, "Cannot load server root certificates.");
1352 break;
1353 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001354
Craig Tillera82950e2015-09-22 12:33:20 -07001355 if (num_alpn_protocols != 0) {
1356 result = build_alpn_protocol_name_list(
1357 alpn_protocols, alpn_protocols_lengths, num_alpn_protocols,
1358 &impl->alpn_protocol_list, &impl->alpn_protocol_list_length);
1359 if (result != TSI_OK) {
1360 gpr_log(GPR_ERROR, "Building alpn list failed with error %s.",
1361 tsi_result_to_string(result));
1362 break;
1363 }
Julien Boeufd1531322015-06-18 11:05:39 +02001364#if TSI_OPENSSL_ALPN_SUPPORT
Craig Tillera82950e2015-09-22 12:33:20 -07001365 GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX);
1366 if (SSL_CTX_set_alpn_protos(
1367 ssl_context, impl->alpn_protocol_list,
1368 (unsigned int)impl->alpn_protocol_list_length)) {
1369 gpr_log(GPR_ERROR, "Could not set alpn protocol list to context.");
1370 result = TSI_INVALID_ARGUMENT;
1371 break;
1372 }
Julien Boeufd1531322015-06-18 11:05:39 +02001373#endif /* TSI_OPENSSL_ALPN_SUPPORT */
Craig Tillera82950e2015-09-22 12:33:20 -07001374 SSL_CTX_set_next_proto_select_cb(
1375 ssl_context, client_handshaker_factory_npn_callback, impl);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001376 }
Craig Tillera82950e2015-09-22 12:33:20 -07001377 } while (0);
1378 if (result != TSI_OK) {
1379 ssl_client_handshaker_factory_destroy(&impl->base);
1380 return result;
1381 }
1382 SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, NULL);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001383 /* TODO(jboeuf): Add revocation verification. */
1384
Craig Tillera82950e2015-09-22 12:33:20 -07001385 impl->base.create_handshaker =
1386 ssl_client_handshaker_factory_create_handshaker;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001387 impl->base.destroy = ssl_client_handshaker_factory_destroy;
1388 *factory = &impl->base;
1389 return TSI_OK;
1390}
1391
Craig Tillera82950e2015-09-22 12:33:20 -07001392tsi_result tsi_create_ssl_server_handshaker_factory(
1393 const unsigned char **pem_private_keys,
1394 const size_t *pem_private_keys_sizes, const unsigned char **pem_cert_chains,
1395 const size_t *pem_cert_chains_sizes, size_t key_cert_pair_count,
1396 const unsigned char *pem_client_root_certs,
1397 size_t pem_client_root_certs_size, int force_client_auth,
1398 const char *cipher_list, const unsigned char **alpn_protocols,
1399 const unsigned char *alpn_protocols_lengths, uint16_t num_alpn_protocols,
1400 tsi_ssl_handshaker_factory **factory) {
Craig Tiller45724b32015-09-22 10:42:19 -07001401 tsi_ssl_server_handshaker_factory *impl = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001402 tsi_result result = TSI_OK;
Julien Boeufb222b4d2015-01-15 17:01:39 -08001403 size_t i = 0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001404
Craig Tillera82950e2015-09-22 12:33:20 -07001405 gpr_once_init(&init_openssl_once, init_openssl);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001406
Craig Tillera82950e2015-09-22 12:33:20 -07001407 if (factory == NULL) return TSI_INVALID_ARGUMENT;
Craig Tiller45724b32015-09-22 10:42:19 -07001408 *factory = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -07001409 if (key_cert_pair_count == 0 || pem_private_keys == NULL ||
1410 pem_cert_chains == NULL) {
1411 return TSI_INVALID_ARGUMENT;
1412 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001413
Craig Tillera82950e2015-09-22 12:33:20 -07001414 impl = calloc(1, sizeof(tsi_ssl_server_handshaker_factory));
1415 if (impl == NULL) return TSI_OUT_OF_RESOURCES;
1416 impl->base.create_handshaker =
1417 ssl_server_handshaker_factory_create_handshaker;
Craig Tiller45724b32015-09-22 10:42:19 -07001418 impl->base.destroy = ssl_server_handshaker_factory_destroy;
Craig Tillera82950e2015-09-22 12:33:20 -07001419 impl->ssl_contexts = calloc(key_cert_pair_count, sizeof(SSL_CTX *));
1420 impl->ssl_context_x509_subject_names =
1421 calloc(key_cert_pair_count, sizeof(tsi_peer));
1422 if (impl->ssl_contexts == NULL ||
1423 impl->ssl_context_x509_subject_names == NULL) {
1424 tsi_ssl_handshaker_factory_destroy(&impl->base);
1425 return TSI_OUT_OF_RESOURCES;
1426 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001427 impl->ssl_context_count = key_cert_pair_count;
1428
Craig Tillera82950e2015-09-22 12:33:20 -07001429 if (num_alpn_protocols > 0) {
1430 result = build_alpn_protocol_name_list(
1431 alpn_protocols, alpn_protocols_lengths, num_alpn_protocols,
1432 &impl->alpn_protocol_list, &impl->alpn_protocol_list_length);
1433 if (result != TSI_OK) {
1434 tsi_ssl_handshaker_factory_destroy(&impl->base);
1435 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001436 }
Craig Tillera82950e2015-09-22 12:33:20 -07001437 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001438
Craig Tillera82950e2015-09-22 12:33:20 -07001439 for (i = 0; i < key_cert_pair_count; i++) {
1440 do {
1441 impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method());
1442 if (impl->ssl_contexts[i] == NULL) {
1443 gpr_log(GPR_ERROR, "Could not create ssl context.");
1444 result = TSI_OUT_OF_RESOURCES;
1445 break;
1446 }
1447 result = populate_ssl_context(
1448 impl->ssl_contexts[i], pem_private_keys[i], pem_private_keys_sizes[i],
1449 pem_cert_chains[i], pem_cert_chains_sizes[i], cipher_list);
1450 if (result != TSI_OK) break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001451
Craig Tillera82950e2015-09-22 12:33:20 -07001452 if (pem_client_root_certs != NULL) {
1453 int flags = SSL_VERIFY_PEER;
1454 STACK_OF(X509_NAME) *root_names = NULL;
1455 result = ssl_ctx_load_verification_certs(
1456 impl->ssl_contexts[i], pem_client_root_certs,
1457 pem_client_root_certs_size, &root_names);
1458 if (result != TSI_OK) {
1459 gpr_log(GPR_ERROR, "Invalid verification certs.");
1460 break;
1461 }
1462 SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names);
1463 if (force_client_auth) flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
1464 SSL_CTX_set_verify(impl->ssl_contexts[i], flags, NULL);
1465 /* TODO(jboeuf): Add revocation verification. */
1466 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001467
Craig Tillera82950e2015-09-22 12:33:20 -07001468 result = extract_x509_subject_names_from_pem_cert(
1469 pem_cert_chains[i], pem_cert_chains_sizes[i],
1470 &impl->ssl_context_x509_subject_names[i]);
1471 if (result != TSI_OK) break;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001472
Craig Tillera82950e2015-09-22 12:33:20 -07001473 SSL_CTX_set_tlsext_servername_callback(
1474 impl->ssl_contexts[i],
1475 ssl_server_handshaker_factory_servername_callback);
1476 SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl);
Julien Boeufd1531322015-06-18 11:05:39 +02001477#if TSI_OPENSSL_ALPN_SUPPORT
Craig Tillera82950e2015-09-22 12:33:20 -07001478 SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i],
1479 server_handshaker_factory_alpn_callback, impl);
Julien Boeufd1531322015-06-18 11:05:39 +02001480#endif /* TSI_OPENSSL_ALPN_SUPPORT */
Craig Tillera82950e2015-09-22 12:33:20 -07001481 SSL_CTX_set_next_protos_advertised_cb(
1482 impl->ssl_contexts[i],
1483 server_handshaker_factory_npn_advertised_callback, impl);
1484 } while (0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001485
Craig Tillera82950e2015-09-22 12:33:20 -07001486 if (result != TSI_OK) {
1487 tsi_ssl_handshaker_factory_destroy(&impl->base);
1488 return result;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001489 }
Craig Tillera82950e2015-09-22 12:33:20 -07001490 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001491 *factory = &impl->base;
1492 return TSI_OK;
1493}
1494
1495/* --- tsi_ssl utils. --- */
1496
Craig Tillera82950e2015-09-22 12:33:20 -07001497int tsi_ssl_peer_matches_name(const tsi_peer *peer, const char *name) {
Julien Boeufb222b4d2015-01-15 17:01:39 -08001498 size_t i = 0;
Julien Boeuf597a4f22015-02-23 15:57:14 -08001499 size_t san_count = 0;
Craig Tiller45724b32015-09-22 10:42:19 -07001500 const tsi_peer_property *cn_property = NULL;
Paul Querna47d841d2016-03-10 11:19:17 -08001501 int like_ip = looks_like_ip_address(name);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001502
Julien Boeuf597a4f22015-02-23 15:57:14 -08001503 /* Check the SAN first. */
Craig Tillera82950e2015-09-22 12:33:20 -07001504 for (i = 0; i < peer->property_count; i++) {
1505 const tsi_peer_property *property = &peer->properties[i];
1506 if (property->name == NULL) continue;
1507 if (strcmp(property->name,
1508 TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) {
1509 san_count++;
Paul Querna47d841d2016-03-10 11:19:17 -08001510
1511 if (!like_ip && does_entry_match_name(property->value.data,
1512 property->value.length, name)) {
1513 return 1;
1514 } else if (like_ip &&
1515 strncmp(name, property->value.data, property->value.length) ==
1516 0 &&
1517 strlen(name) == property->value.length) {
1518 /* IP Addresses are exact matches only. */
Craig Tillera82950e2015-09-22 12:33:20 -07001519 return 1;
1520 }
1521 } else if (strcmp(property->name,
1522 TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) {
1523 cn_property = property;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001524 }
Craig Tillera82950e2015-09-22 12:33:20 -07001525 }
Julien Boeuf597a4f22015-02-23 15:57:14 -08001526
Paul Querna47d841d2016-03-10 11:19:17 -08001527 /* If there's no SAN, try the CN, but only if its not like an IP Address */
1528 if (san_count == 0 && cn_property != NULL && !like_ip) {
Craig Tillera82950e2015-09-22 12:33:20 -07001529 if (does_entry_match_name(cn_property->value.data,
1530 cn_property->value.length, name)) {
1531 return 1;
Julien Boeuf597a4f22015-02-23 15:57:14 -08001532 }
Craig Tillera82950e2015-09-22 12:33:20 -07001533 }
Julien Boeuf597a4f22015-02-23 15:57:14 -08001534
Craig Tillera82950e2015-09-22 12:33:20 -07001535 return 0; /* Not found. */
Craig Tiller190d3602015-02-18 09:23:38 -08001536}