Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1 | /* |
| 2 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 3 | * Copyright 2015 gRPC authors. |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 4 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | * you may not use this file except in compliance with the License. |
| 7 | * You may obtain a copy of the License at |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 8 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 10 | * |
Jan Tattermusch | 7897ae9 | 2017-06-07 22:57:36 +0200 | [diff] [blame] | 11 | * Unless required by applicable law or agreed to in writing, software |
| 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | * See the License for the specific language governing permissions and |
| 15 | * limitations under the License. |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 16 | * |
| 17 | */ |
| 18 | |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 19 | #include <grpc/support/port_platform.h> |
| 20 | |
Alexander Polcyn | db3e898 | 2018-02-21 16:59:24 -0800 | [diff] [blame] | 21 | #include "src/core/tsi/ssl_transport_security.h" |
| 22 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 23 | #include <limits.h> |
Julien Boeuf | 330f4c8 | 2015-04-27 10:37:42 -0700 | [diff] [blame] | 24 | #include <string.h> |
Paul Querna | 27df689 | 2016-03-13 13:34:27 -0700 | [diff] [blame] | 25 | |
Nicolas "Pixel" Noble | e3df5a0 | 2016-10-27 00:41:13 +0200 | [diff] [blame] | 26 | /* TODO(jboeuf): refactor inet_ntop into a portability header. */ |
| 27 | /* Note: for whomever reads this and tries to refactor this, this |
| 28 | can't be in grpc, it has to be in gpr. */ |
| 29 | #ifdef GPR_WINDOWS |
| 30 | #include <ws2tcpip.h> |
| 31 | #else |
| 32 | #include <arpa/inet.h> |
Mehrdad Afshari | f583975 | 2017-04-18 10:26:40 -0700 | [diff] [blame] | 33 | #include <sys/socket.h> |
Nicolas "Pixel" Noble | e3df5a0 | 2016-10-27 00:41:13 +0200 | [diff] [blame] | 34 | #endif |
| 35 | |
Nicolas "Pixel" Noble | b29d8cf | 2016-04-08 01:38:29 +0200 | [diff] [blame] | 36 | #include <grpc/support/alloc.h> |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 37 | #include <grpc/support/log.h> |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 38 | #include <grpc/support/string_util.h> |
nnoble | 8a23a3d | 2014-12-16 10:10:29 -0800 | [diff] [blame] | 39 | #include <grpc/support/sync.h> |
Vijay Pai | c745690 | 2018-02-12 10:28:24 -0800 | [diff] [blame] | 40 | #include <grpc/support/thd_id.h> |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 41 | |
Yash Tibrewal | 15ce142 | 2017-09-25 17:46:32 -0700 | [diff] [blame] | 42 | extern "C" { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 43 | #include <openssl/bio.h> |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 44 | #include <openssl/crypto.h> /* For OPENSSL_free */ |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 45 | #include <openssl/err.h> |
| 46 | #include <openssl/ssl.h> |
| 47 | #include <openssl/x509.h> |
| 48 | #include <openssl/x509v3.h> |
Yash Tibrewal | 15ce142 | 2017-09-25 17:46:32 -0700 | [diff] [blame] | 49 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 50 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 51 | #include "src/core/lib/gpr/useful.h" |
| 52 | #include "src/core/tsi/ssl/session_cache/ssl_session_cache.h" |
Craig Tiller | b29f1fe | 2017-03-28 15:49:23 -0700 | [diff] [blame] | 53 | #include "src/core/tsi/ssl_types.h" |
| 54 | #include "src/core/tsi/transport_security.h" |
Craig Tiller | 0fe5ee7 | 2015-12-22 12:50:36 -0800 | [diff] [blame] | 55 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 56 | /* --- Constants. ---*/ |
| 57 | |
| 58 | #define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND 16384 |
| 59 | #define TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND 1024 |
| 60 | |
Julien Boeuf | 3e29de1 | 2015-06-18 23:29:13 +0200 | [diff] [blame] | 61 | /* Putting a macro like this and littering the source file with #if is really |
| 62 | bad practice. |
| 63 | TODO(jboeuf): refactor all the #if / #endif in a separate module. */ |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 64 | #ifndef TSI_OPENSSL_ALPN_SUPPORT |
| 65 | #define TSI_OPENSSL_ALPN_SUPPORT 1 |
| 66 | #endif |
| 67 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 68 | /* TODO(jboeuf): I have not found a way to get this number dynamically from the |
Julien Boeuf | 3e29de1 | 2015-06-18 23:29:13 +0200 | [diff] [blame] | 69 | SSL structure. This is what we would ultimately want though... */ |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 70 | #define TSI_SSL_MAX_PROTECTION_OVERHEAD 100 |
| 71 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 72 | /* --- Structure definitions. ---*/ |
| 73 | |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 74 | struct tsi_ssl_root_certs_store { |
| 75 | X509_STORE* store; |
| 76 | }; |
| 77 | |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 78 | struct tsi_ssl_handshaker_factory { |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 79 | const tsi_ssl_handshaker_factory_vtable* vtable; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 80 | gpr_refcount refcount; |
| 81 | }; |
| 82 | |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 83 | struct tsi_ssl_client_handshaker_factory { |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 84 | tsi_ssl_handshaker_factory base; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 85 | SSL_CTX* ssl_context; |
| 86 | unsigned char* alpn_protocol_list; |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 87 | size_t alpn_protocol_list_length; |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 88 | grpc_core::RefCountedPtr<tsi::SslSessionLRUCache> session_cache; |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 89 | }; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 90 | |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 91 | struct tsi_ssl_server_handshaker_factory { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 92 | /* Several contexts to support SNI. |
| 93 | The tsi_peer array contains the subject names of the server certificates |
| 94 | associated with the contexts at the same index. */ |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 95 | tsi_ssl_handshaker_factory base; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 96 | SSL_CTX** ssl_contexts; |
| 97 | tsi_peer* ssl_context_x509_subject_names; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 98 | size_t ssl_context_count; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 99 | unsigned char* alpn_protocol_list; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 100 | size_t alpn_protocol_list_length; |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 101 | }; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 102 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 103 | typedef struct { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 104 | tsi_handshaker base; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 105 | SSL* ssl; |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 106 | BIO* network_io; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 107 | tsi_result result; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 108 | tsi_ssl_handshaker_factory* factory_ref; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 109 | } tsi_ssl_handshaker; |
| 110 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 111 | typedef struct { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 112 | tsi_frame_protector base; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 113 | SSL* ssl; |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 114 | BIO* network_io; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 115 | unsigned char* buffer; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 116 | size_t buffer_size; |
| 117 | size_t buffer_offset; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 118 | } tsi_ssl_frame_protector; |
| 119 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 120 | /* --- Library Initialization. ---*/ |
| 121 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 122 | static gpr_once g_init_openssl_once = GPR_ONCE_INIT; |
| 123 | static gpr_mu* g_openssl_mutexes = nullptr; |
| 124 | static int g_ssl_ctx_ex_factory_index = -1; |
Yuchen Zeng | c6ae9b5 | 2018-01-04 16:29:06 -0800 | [diff] [blame] | 125 | static void openssl_locking_cb(int mode, int type, const char* file, |
| 126 | int line) GRPC_UNUSED; |
| 127 | static unsigned long openssl_thread_id_cb(void) GRPC_UNUSED; |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 128 | static const unsigned char kSslSessionIdContext[] = {'g', 'r', 'p', 'c'}; |
Julien Boeuf | 4a0a394 | 2015-02-03 15:04:45 -0800 | [diff] [blame] | 129 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 130 | static void openssl_locking_cb(int mode, int type, const char* file, int line) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 131 | if (mode & CRYPTO_LOCK) { |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 132 | gpr_mu_lock(&g_openssl_mutexes[type]); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 133 | } else { |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 134 | gpr_mu_unlock(&g_openssl_mutexes[type]); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 135 | } |
Julien Boeuf | 4a0a394 | 2015-02-03 15:04:45 -0800 | [diff] [blame] | 136 | } |
| 137 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 138 | static unsigned long openssl_thread_id_cb(void) { |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 139 | return static_cast<unsigned long>(gpr_thd_currentid()); |
Julien Boeuf | 4a0a394 | 2015-02-03 15:04:45 -0800 | [diff] [blame] | 140 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 141 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 142 | static void init_openssl(void) { |
Julien Boeuf | 4a0a394 | 2015-02-03 15:04:45 -0800 | [diff] [blame] | 143 | int i; |
Craig Tiller | 3121fd4 | 2015-09-10 09:56:20 -0700 | [diff] [blame] | 144 | int num_locks; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 145 | SSL_library_init(); |
| 146 | SSL_load_error_strings(); |
| 147 | OpenSSL_add_all_algorithms(); |
| 148 | num_locks = CRYPTO_num_locks(); |
| 149 | GPR_ASSERT(num_locks > 0); |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 150 | g_openssl_mutexes = static_cast<gpr_mu*>( |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 151 | gpr_malloc(static_cast<size_t>(num_locks) * sizeof(gpr_mu))); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 152 | for (i = 0; i < CRYPTO_num_locks(); i++) { |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 153 | gpr_mu_init(&g_openssl_mutexes[i]); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 154 | } |
| 155 | CRYPTO_set_locking_callback(openssl_locking_cb); |
| 156 | CRYPTO_set_id_callback(openssl_thread_id_cb); |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 157 | g_ssl_ctx_ex_factory_index = |
| 158 | SSL_CTX_get_ex_new_index(0, nullptr, nullptr, nullptr, nullptr); |
| 159 | GPR_ASSERT(g_ssl_ctx_ex_factory_index != -1); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 160 | } |
| 161 | |
| 162 | /* --- Ssl utils. ---*/ |
| 163 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 164 | static const char* ssl_error_string(int error) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 165 | switch (error) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 166 | case SSL_ERROR_NONE: |
| 167 | return "SSL_ERROR_NONE"; |
| 168 | case SSL_ERROR_ZERO_RETURN: |
| 169 | return "SSL_ERROR_ZERO_RETURN"; |
| 170 | case SSL_ERROR_WANT_READ: |
| 171 | return "SSL_ERROR_WANT_READ"; |
| 172 | case SSL_ERROR_WANT_WRITE: |
| 173 | return "SSL_ERROR_WANT_WRITE"; |
| 174 | case SSL_ERROR_WANT_CONNECT: |
| 175 | return "SSL_ERROR_WANT_CONNECT"; |
| 176 | case SSL_ERROR_WANT_ACCEPT: |
| 177 | return "SSL_ERROR_WANT_ACCEPT"; |
| 178 | case SSL_ERROR_WANT_X509_LOOKUP: |
| 179 | return "SSL_ERROR_WANT_X509_LOOKUP"; |
| 180 | case SSL_ERROR_SYSCALL: |
| 181 | return "SSL_ERROR_SYSCALL"; |
| 182 | case SSL_ERROR_SSL: |
| 183 | return "SSL_ERROR_SSL"; |
| 184 | default: |
| 185 | return "Unknown error"; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 186 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 187 | } |
| 188 | |
| 189 | /* TODO(jboeuf): Remove when we are past the debugging phase with this code. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 190 | static void ssl_log_where_info(const SSL* ssl, int where, int flag, |
| 191 | const char* msg) { |
Craig Tiller | 6014e8a | 2017-10-16 13:50:29 -0700 | [diff] [blame] | 192 | if ((where & flag) && tsi_tracing_enabled.enabled()) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 193 | gpr_log(GPR_INFO, "%20.20s - %30.30s - %5.10s", msg, |
| 194 | SSL_state_string_long(ssl), SSL_state_string(ssl)); |
| 195 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 196 | } |
| 197 | |
| 198 | /* Used for debugging. TODO(jboeuf): Remove when code is mature enough. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 199 | static void ssl_info_callback(const SSL* ssl, int where, int ret) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 200 | if (ret == 0) { |
| 201 | gpr_log(GPR_ERROR, "ssl_info_callback: error occured.\n"); |
| 202 | return; |
| 203 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 204 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 205 | ssl_log_where_info(ssl, where, SSL_CB_LOOP, "LOOP"); |
| 206 | ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_START, "HANDSHAKE START"); |
| 207 | ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE"); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 208 | } |
| 209 | |
Julien Boeuf | 0170a6c | 2015-02-24 18:08:01 -0800 | [diff] [blame] | 210 | /* Returns 1 if name looks like an IP address, 0 otherwise. |
Paul Querna | 4a9e7c4 | 2016-03-13 14:00:11 -0700 | [diff] [blame] | 211 | This is a very rough heuristic, and only handles IPv6 in hexadecimal form. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 212 | static int looks_like_ip_address(const char* name) { |
Julien Boeuf | 9fff77e | 2015-02-24 16:50:35 -0800 | [diff] [blame] | 213 | size_t i; |
| 214 | size_t dot_count = 0; |
| 215 | size_t num_size = 0; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 216 | for (i = 0; i < strlen(name); i++) { |
Paul Querna | 4a9e7c4 | 2016-03-13 14:00:11 -0700 | [diff] [blame] | 217 | if (name[i] == ':') { |
| 218 | /* IPv6 Address in hexadecimal form, : is not allowed in DNS names. */ |
| 219 | return 1; |
| 220 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 221 | if (name[i] >= '0' && name[i] <= '9') { |
| 222 | if (num_size > 3) return 0; |
| 223 | num_size++; |
| 224 | } else if (name[i] == '.') { |
| 225 | if (dot_count > 3 || num_size == 0) return 0; |
| 226 | dot_count++; |
| 227 | num_size = 0; |
| 228 | } else { |
| 229 | return 0; |
Julien Boeuf | 9fff77e | 2015-02-24 16:50:35 -0800 | [diff] [blame] | 230 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 231 | } |
| 232 | if (dot_count < 3 || num_size == 0) return 0; |
Julien Boeuf | 9fff77e | 2015-02-24 16:50:35 -0800 | [diff] [blame] | 233 | return 1; |
| 234 | } |
| 235 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 236 | /* Gets the subject CN from an X509 cert. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 237 | static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, |
| 238 | size_t* utf8_size) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 239 | int common_name_index = -1; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 240 | X509_NAME_ENTRY* common_name_entry = nullptr; |
| 241 | ASN1_STRING* common_name_asn1 = nullptr; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 242 | X509_NAME* subject_name = X509_get_subject_name(cert); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 243 | int utf8_returned_size = 0; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 244 | if (subject_name == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 245 | gpr_log(GPR_ERROR, "Could not get subject name from certificate."); |
| 246 | return TSI_NOT_FOUND; |
| 247 | } |
| 248 | common_name_index = |
| 249 | X509_NAME_get_index_by_NID(subject_name, NID_commonName, -1); |
| 250 | if (common_name_index == -1) { |
| 251 | gpr_log(GPR_ERROR, |
| 252 | "Could not get common name of subject from certificate."); |
| 253 | return TSI_NOT_FOUND; |
| 254 | } |
| 255 | common_name_entry = X509_NAME_get_entry(subject_name, common_name_index); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 256 | if (common_name_entry == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 257 | gpr_log(GPR_ERROR, "Could not get common name entry from certificate."); |
| 258 | return TSI_INTERNAL_ERROR; |
| 259 | } |
| 260 | common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 261 | if (common_name_asn1 == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 262 | gpr_log(GPR_ERROR, |
| 263 | "Could not get common name entry asn1 from certificate."); |
| 264 | return TSI_INTERNAL_ERROR; |
| 265 | } |
| 266 | utf8_returned_size = ASN1_STRING_to_UTF8(utf8, common_name_asn1); |
| 267 | if (utf8_returned_size < 0) { |
| 268 | gpr_log(GPR_ERROR, "Could not extract utf8 from asn1 string."); |
| 269 | return TSI_OUT_OF_RESOURCES; |
| 270 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 271 | *utf8_size = static_cast<size_t>(utf8_returned_size); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 272 | return TSI_OK; |
| 273 | } |
| 274 | |
| 275 | /* Gets the subject CN of an X509 cert as a tsi_peer_property. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 276 | static tsi_result peer_property_from_x509_common_name( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 277 | X509* cert, tsi_peer_property* property) { |
| 278 | unsigned char* common_name; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 279 | size_t common_name_size; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 280 | tsi_result result = |
| 281 | ssl_get_x509_common_name(cert, &common_name, &common_name_size); |
| 282 | if (result != TSI_OK) { |
| 283 | if (result == TSI_NOT_FOUND) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 284 | common_name = nullptr; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 285 | common_name_size = 0; |
| 286 | } else { |
| 287 | return result; |
Julien Boeuf | 9fff77e | 2015-02-24 16:50:35 -0800 | [diff] [blame] | 288 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 289 | } |
| 290 | result = tsi_construct_string_peer_property( |
| 291 | TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 292 | common_name == nullptr ? "" : reinterpret_cast<const char*>(common_name), |
| 293 | common_name_size, property); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 294 | OPENSSL_free(common_name); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 295 | return result; |
| 296 | } |
| 297 | |
Deepak Lukose | e61cbe3 | 2016-03-14 14:10:44 -0700 | [diff] [blame] | 298 | /* Gets the X509 cert in PEM format as a tsi_peer_property. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 299 | static tsi_result add_pem_certificate(X509* cert, tsi_peer_property* property) { |
| 300 | BIO* bio = BIO_new(BIO_s_mem()); |
Deepak Lukose | e61cbe3 | 2016-03-14 14:10:44 -0700 | [diff] [blame] | 301 | if (!PEM_write_bio_X509(bio, cert)) { |
| 302 | BIO_free(bio); |
| 303 | return TSI_INTERNAL_ERROR; |
| 304 | } |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 305 | char* contents; |
Deepak Lukose | e61cbe3 | 2016-03-14 14:10:44 -0700 | [diff] [blame] | 306 | long len = BIO_get_mem_data(bio, &contents); |
| 307 | if (len <= 0) { |
| 308 | BIO_free(bio); |
| 309 | return TSI_INTERNAL_ERROR; |
| 310 | } |
| 311 | tsi_result result = tsi_construct_string_peer_property( |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 312 | TSI_X509_PEM_CERT_PROPERTY, (const char*)contents, |
| 313 | static_cast<size_t>(len), property); |
Deepak Lukose | e61cbe3 | 2016-03-14 14:10:44 -0700 | [diff] [blame] | 314 | BIO_free(bio); |
| 315 | return result; |
| 316 | } |
| 317 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 318 | /* Gets the subject SANs from an X509 cert as a tsi_peer_property. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 319 | static tsi_result add_subject_alt_names_properties_to_peer( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 320 | tsi_peer* peer, GENERAL_NAMES* subject_alt_names, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 321 | size_t subject_alt_name_count) { |
Craig Tiller | 3121fd4 | 2015-09-10 09:56:20 -0700 | [diff] [blame] | 322 | size_t i; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 323 | tsi_result result = TSI_OK; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 324 | |
| 325 | /* Reset for DNS entries filtering. */ |
Julien Boeuf | 77e8c1c | 2015-05-13 13:50:59 -0700 | [diff] [blame] | 326 | peer->property_count -= subject_alt_name_count; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 327 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 328 | for (i = 0; i < subject_alt_name_count; i++) { |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 329 | GENERAL_NAME* subject_alt_name = |
Craig Tiller | 0fe5ee7 | 2015-12-22 12:50:36 -0800 | [diff] [blame] | 330 | sk_GENERAL_NAME_value(subject_alt_names, TSI_SIZE_AS_SIZE(i)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 331 | /* Filter out the non-dns entries names. */ |
| 332 | if (subject_alt_name->type == GEN_DNS) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 333 | unsigned char* name = nullptr; |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 334 | int name_size; |
| 335 | name_size = ASN1_STRING_to_UTF8(&name, subject_alt_name->d.dNSName); |
| 336 | if (name_size < 0) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 337 | gpr_log(GPR_ERROR, "Could not get utf8 from asn1 string."); |
| 338 | result = TSI_INTERNAL_ERROR; |
| 339 | break; |
| 340 | } |
| 341 | result = tsi_construct_string_peer_property( |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 342 | TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, |
| 343 | reinterpret_cast<const char*>(name), static_cast<size_t>(name_size), |
| 344 | &peer->properties[peer->property_count++]); |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 345 | OPENSSL_free(name); |
| 346 | } else if (subject_alt_name->type == GEN_IPADD) { |
| 347 | char ntop_buf[INET6_ADDRSTRLEN]; |
| 348 | int af; |
| 349 | |
| 350 | if (subject_alt_name->d.iPAddress->length == 4) { |
| 351 | af = AF_INET; |
| 352 | } else if (subject_alt_name->d.iPAddress->length == 16) { |
| 353 | af = AF_INET6; |
| 354 | } else { |
| 355 | gpr_log(GPR_ERROR, "SAN IP Address contained invalid IP"); |
| 356 | result = TSI_INTERNAL_ERROR; |
| 357 | break; |
| 358 | } |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 359 | const char* name = inet_ntop(af, subject_alt_name->d.iPAddress->data, |
Nicolas "Pixel" Noble | e3df5a0 | 2016-10-27 00:41:13 +0200 | [diff] [blame] | 360 | ntop_buf, INET6_ADDRSTRLEN); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 361 | if (name == nullptr) { |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 362 | gpr_log(GPR_ERROR, "Could not get IP string from asn1 octet."); |
| 363 | result = TSI_INTERNAL_ERROR; |
| 364 | break; |
| 365 | } |
| 366 | |
| 367 | result = tsi_construct_string_peer_property_from_cstring( |
| 368 | TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, name, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 369 | &peer->properties[peer->property_count++]); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 370 | } |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 371 | if (result != TSI_OK) break; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 372 | } |
Julien Boeuf | 77e8c1c | 2015-05-13 13:50:59 -0700 | [diff] [blame] | 373 | return result; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 374 | } |
| 375 | |
| 376 | /* Gets information about the peer's X509 cert as a tsi_peer object. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 377 | static tsi_result peer_from_x509(X509* cert, int include_certificate_type, |
| 378 | tsi_peer* peer) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 379 | /* TODO(jboeuf): Maybe add more properties. */ |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 380 | GENERAL_NAMES* subject_alt_names = static_cast<GENERAL_NAMES*>( |
| 381 | X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); |
| 382 | int subject_alt_name_count = |
| 383 | (subject_alt_names != nullptr) |
| 384 | ? static_cast<int>(sk_GENERAL_NAME_num(subject_alt_names)) |
| 385 | : 0; |
Craig Tiller | 3121fd4 | 2015-09-10 09:56:20 -0700 | [diff] [blame] | 386 | size_t property_count; |
| 387 | tsi_result result; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 388 | GPR_ASSERT(subject_alt_name_count >= 0); |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 389 | property_count = (include_certificate_type ? static_cast<size_t>(1) : 0) + |
Deepak Lukose | e61cbe3 | 2016-03-14 14:10:44 -0700 | [diff] [blame] | 390 | 2 /* common name, certificate */ + |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 391 | static_cast<size_t>(subject_alt_name_count); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 392 | result = tsi_construct_peer(property_count, peer); |
| 393 | if (result != TSI_OK) return result; |
| 394 | do { |
| 395 | if (include_certificate_type) { |
| 396 | result = tsi_construct_string_peer_property_from_cstring( |
| 397 | TSI_CERTIFICATE_TYPE_PEER_PROPERTY, TSI_X509_CERTIFICATE_TYPE, |
| 398 | &peer->properties[0]); |
| 399 | if (result != TSI_OK) break; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 400 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 401 | result = peer_property_from_x509_common_name( |
| 402 | cert, &peer->properties[include_certificate_type ? 1 : 0]); |
| 403 | if (result != TSI_OK) break; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 404 | |
Deepak Lukose | e61cbe3 | 2016-03-14 14:10:44 -0700 | [diff] [blame] | 405 | result = add_pem_certificate( |
| 406 | cert, &peer->properties[include_certificate_type ? 2 : 1]); |
| 407 | if (result != TSI_OK) break; |
| 408 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 409 | if (subject_alt_name_count != 0) { |
| 410 | result = add_subject_alt_names_properties_to_peer( |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 411 | peer, subject_alt_names, static_cast<size_t>(subject_alt_name_count)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 412 | if (result != TSI_OK) break; |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 413 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 414 | } while (0); |
| 415 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 416 | if (subject_alt_names != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 417 | sk_GENERAL_NAME_pop_free(subject_alt_names, GENERAL_NAME_free); |
| 418 | } |
| 419 | if (result != TSI_OK) tsi_peer_destruct(peer); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 420 | return result; |
| 421 | } |
| 422 | |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 423 | /* Logs the SSL error stack. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 424 | static void log_ssl_error_stack(void) { |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 425 | unsigned long err; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 426 | while ((err = ERR_get_error()) != 0) { |
| 427 | char details[256]; |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 428 | ERR_error_string_n(static_cast<uint32_t>(err), details, sizeof(details)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 429 | gpr_log(GPR_ERROR, "%s", details); |
| 430 | } |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 431 | } |
| 432 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 433 | /* Performs an SSL_read and handle errors. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 434 | static tsi_result do_ssl_read(SSL* ssl, unsigned char* unprotected_bytes, |
| 435 | size_t* unprotected_bytes_size) { |
Craig Tiller | f96dfc3 | 2015-09-10 14:43:18 -0700 | [diff] [blame] | 436 | int read_from_ssl; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 437 | GPR_ASSERT(*unprotected_bytes_size <= INT_MAX); |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 438 | read_from_ssl = SSL_read(ssl, unprotected_bytes, |
| 439 | static_cast<int>(*unprotected_bytes_size)); |
Julien Boeuf | ce91f4a | 2017-05-09 15:50:33 -0700 | [diff] [blame] | 440 | if (read_from_ssl <= 0) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 441 | read_from_ssl = SSL_get_error(ssl, read_from_ssl); |
| 442 | switch (read_from_ssl) { |
Julien Boeuf | ce91f4a | 2017-05-09 15:50:33 -0700 | [diff] [blame] | 443 | case SSL_ERROR_ZERO_RETURN: /* Received a close_notify alert. */ |
| 444 | case SSL_ERROR_WANT_READ: /* We need more data to finish the frame. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 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 Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 460 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 461 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 462 | *unprotected_bytes_size = static_cast<size_t>(read_from_ssl); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 463 | return TSI_OK; |
| 464 | } |
| 465 | |
| 466 | /* Performs an SSL_write and handle errors. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 467 | static tsi_result do_ssl_write(SSL* ssl, unsigned char* unprotected_bytes, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 468 | size_t unprotected_bytes_size) { |
Craig Tiller | f96dfc3 | 2015-09-10 14:43:18 -0700 | [diff] [blame] | 469 | int ssl_write_result; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 470 | GPR_ASSERT(unprotected_bytes_size <= INT_MAX); |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 471 | ssl_write_result = SSL_write(ssl, unprotected_bytes, |
| 472 | static_cast<int>(unprotected_bytes_size)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 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 Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 483 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 484 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 485 | return TSI_OK; |
| 486 | } |
| 487 | |
| 488 | /* Loads an in-memory PEM certificate chain into the SSL context. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 489 | static tsi_result ssl_ctx_use_certificate_chain(SSL_CTX* context, |
| 490 | const char* pem_cert_chain, |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 491 | size_t pem_cert_chain_size) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 492 | tsi_result result = TSI_OK; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 493 | X509* certificate = nullptr; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 494 | BIO* pem; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 495 | GPR_ASSERT(pem_cert_chain_size <= INT_MAX); |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 496 | pem = BIO_new_mem_buf((void*)pem_cert_chain, |
| 497 | static_cast<int>(pem_cert_chain_size)); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 498 | if (pem == nullptr) return TSI_OUT_OF_RESOURCES; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 499 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 500 | do { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 501 | certificate = PEM_read_bio_X509_AUX(pem, nullptr, nullptr, (void*)""); |
| 502 | if (certificate == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 503 | result = TSI_INVALID_ARGUMENT; |
| 504 | break; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 505 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 506 | if (!SSL_CTX_use_certificate(context, certificate)) { |
| 507 | result = TSI_INVALID_ARGUMENT; |
| 508 | break; |
| 509 | } |
| 510 | while (1) { |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 511 | X509* certificate_authority = |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 512 | PEM_read_bio_X509(pem, nullptr, nullptr, (void*)""); |
| 513 | if (certificate_authority == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 514 | ERR_clear_error(); |
| 515 | break; /* Done reading. */ |
| 516 | } |
| 517 | if (!SSL_CTX_add_extra_chain_cert(context, certificate_authority)) { |
| 518 | X509_free(certificate_authority); |
| 519 | result = TSI_INVALID_ARGUMENT; |
| 520 | break; |
| 521 | } |
| 522 | /* We don't need to free certificate_authority as its ownership has been |
| 523 | transfered to the context. That is not the case for certificate though. |
| 524 | */ |
| 525 | } |
| 526 | } while (0); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 527 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 528 | if (certificate != nullptr) X509_free(certificate); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 529 | BIO_free(pem); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 530 | return result; |
| 531 | } |
| 532 | |
| 533 | /* Loads an in-memory PEM private key into the SSL context. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 534 | static tsi_result ssl_ctx_use_private_key(SSL_CTX* context, const char* pem_key, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 535 | size_t pem_key_size) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 536 | tsi_result result = TSI_OK; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 537 | EVP_PKEY* private_key = nullptr; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 538 | BIO* pem; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 539 | GPR_ASSERT(pem_key_size <= INT_MAX); |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 540 | pem = BIO_new_mem_buf((void*)pem_key, static_cast<int>(pem_key_size)); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 541 | if (pem == nullptr) return TSI_OUT_OF_RESOURCES; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 542 | do { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 543 | private_key = PEM_read_bio_PrivateKey(pem, nullptr, nullptr, (void*)""); |
| 544 | if (private_key == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 545 | result = TSI_INVALID_ARGUMENT; |
| 546 | break; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 547 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 548 | if (!SSL_CTX_use_PrivateKey(context, private_key)) { |
| 549 | result = TSI_INVALID_ARGUMENT; |
| 550 | break; |
| 551 | } |
| 552 | } while (0); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 553 | if (private_key != nullptr) EVP_PKEY_free(private_key); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 554 | BIO_free(pem); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 555 | return result; |
| 556 | } |
| 557 | |
| 558 | /* Loads in-memory PEM verification certs into the SSL context and optionally |
| 559 | returns the verification cert names (root_names can be NULL). */ |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 560 | static tsi_result x509_store_load_certs(X509_STORE* cert_store, |
| 561 | const char* pem_roots, |
| 562 | size_t pem_roots_size, |
| 563 | STACK_OF(X509_NAME) * *root_names) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 564 | tsi_result result = TSI_OK; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 565 | size_t num_roots = 0; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 566 | X509* root = nullptr; |
| 567 | X509_NAME* root_name = nullptr; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 568 | BIO* pem; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 569 | GPR_ASSERT(pem_roots_size <= INT_MAX); |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 570 | pem = BIO_new_mem_buf((void*)pem_roots, static_cast<int>(pem_roots_size)); |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 571 | if (cert_store == nullptr) return TSI_INVALID_ARGUMENT; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 572 | if (pem == nullptr) return TSI_OUT_OF_RESOURCES; |
| 573 | if (root_names != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 574 | *root_names = sk_X509_NAME_new_null(); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 575 | if (*root_names == nullptr) return TSI_OUT_OF_RESOURCES; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 576 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 577 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 578 | while (1) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 579 | root = PEM_read_bio_X509_AUX(pem, nullptr, nullptr, (void*)""); |
| 580 | if (root == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 581 | ERR_clear_error(); |
| 582 | break; /* We're at the end of stream. */ |
nnoble | 0c475f0 | 2014-12-05 15:37:39 -0800 | [diff] [blame] | 583 | } |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 584 | if (root_names != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 585 | root_name = X509_get_subject_name(root); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 586 | if (root_name == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 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); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 592 | if (root_name == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 593 | result = TSI_OUT_OF_RESOURCES; |
| 594 | break; |
| 595 | } |
| 596 | sk_X509_NAME_push(*root_names, root_name); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 597 | root_name = nullptr; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 598 | } |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 599 | if (!X509_STORE_add_cert(cert_store, root)) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 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 Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 607 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 608 | if (num_roots == 0) { |
| 609 | gpr_log(GPR_ERROR, "Could not load any root certificate."); |
| 610 | result = TSI_INVALID_ARGUMENT; |
| 611 | } |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 612 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 613 | if (result != TSI_OK) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 614 | if (root != nullptr) X509_free(root); |
| 615 | if (root_names != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 616 | sk_X509_NAME_pop_free(*root_names, X509_NAME_free); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 617 | *root_names = nullptr; |
| 618 | if (root_name != nullptr) X509_NAME_free(root_name); |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 619 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 620 | } |
| 621 | BIO_free(pem); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 622 | return result; |
| 623 | } |
| 624 | |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 625 | static tsi_result ssl_ctx_load_verification_certs(SSL_CTX* context, |
| 626 | const char* pem_roots, |
| 627 | size_t pem_roots_size, |
| 628 | STACK_OF(X509_NAME) * |
| 629 | *root_name) { |
| 630 | X509_STORE* cert_store = SSL_CTX_get_cert_store(context); |
| 631 | return x509_store_load_certs(cert_store, pem_roots, pem_roots_size, |
| 632 | root_name); |
| 633 | } |
| 634 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 635 | /* Populates the SSL context with a private key and a cert chain, and sets the |
| 636 | cipher list and the ephemeral ECDH key. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 637 | static tsi_result populate_ssl_context( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 638 | SSL_CTX* context, const tsi_ssl_pem_key_cert_pair* key_cert_pair, |
| 639 | const char* cipher_list) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 640 | tsi_result result = TSI_OK; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 641 | if (key_cert_pair != nullptr) { |
| 642 | if (key_cert_pair->cert_chain != nullptr) { |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 643 | result = ssl_ctx_use_certificate_chain(context, key_cert_pair->cert_chain, |
| 644 | strlen(key_cert_pair->cert_chain)); |
| 645 | if (result != TSI_OK) { |
| 646 | gpr_log(GPR_ERROR, "Invalid cert chain file."); |
| 647 | return result; |
| 648 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 649 | } |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 650 | if (key_cert_pair->private_key != nullptr) { |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 651 | result = ssl_ctx_use_private_key(context, key_cert_pair->private_key, |
| 652 | strlen(key_cert_pair->private_key)); |
| 653 | if (result != TSI_OK || !SSL_CTX_check_private_key(context)) { |
| 654 | gpr_log(GPR_ERROR, "Invalid private key."); |
| 655 | return result != TSI_OK ? result : TSI_INVALID_ARGUMENT; |
| 656 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 657 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 658 | } |
Craig Tiller | be98d24 | 2017-11-10 15:26:57 -0800 | [diff] [blame] | 659 | if ((cipher_list != nullptr) && |
| 660 | !SSL_CTX_set_cipher_list(context, cipher_list)) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 661 | gpr_log(GPR_ERROR, "Invalid cipher list: %s.", cipher_list); |
| 662 | return TSI_INVALID_ARGUMENT; |
| 663 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 664 | { |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 665 | EC_KEY* ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 666 | if (!SSL_CTX_set_tmp_ecdh(context, ecdh)) { |
| 667 | gpr_log(GPR_ERROR, "Could not set ephemeral ECDH key."); |
| 668 | EC_KEY_free(ecdh); |
| 669 | return TSI_INTERNAL_ERROR; |
| 670 | } |
| 671 | SSL_CTX_set_options(context, SSL_OP_SINGLE_ECDH_USE); |
| 672 | EC_KEY_free(ecdh); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 673 | } |
| 674 | return TSI_OK; |
| 675 | } |
| 676 | |
| 677 | /* Extracts the CN and the SANs from an X509 cert as a peer object. */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 678 | static tsi_result extract_x509_subject_names_from_pem_cert(const char* pem_cert, |
| 679 | tsi_peer* peer) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 680 | tsi_result result = TSI_OK; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 681 | X509* cert = nullptr; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 682 | BIO* pem; |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 683 | pem = BIO_new_mem_buf((void*)pem_cert, static_cast<int>(strlen(pem_cert))); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 684 | if (pem == nullptr) return TSI_OUT_OF_RESOURCES; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 685 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 686 | cert = PEM_read_bio_X509(pem, nullptr, nullptr, (void*)""); |
| 687 | if (cert == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 688 | gpr_log(GPR_ERROR, "Invalid certificate"); |
| 689 | result = TSI_INVALID_ARGUMENT; |
| 690 | } else { |
| 691 | result = peer_from_x509(cert, 0, peer); |
| 692 | } |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 693 | if (cert != nullptr) X509_free(cert); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 694 | BIO_free(pem); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 695 | return result; |
| 696 | } |
| 697 | |
| 698 | /* Builds the alpn protocol name list according to rfc 7301. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 699 | static tsi_result build_alpn_protocol_name_list( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 700 | const char** alpn_protocols, uint16_t num_alpn_protocols, |
| 701 | unsigned char** protocol_name_list, size_t* protocol_name_list_length) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 702 | uint16_t i; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 703 | unsigned char* current; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 704 | *protocol_name_list = nullptr; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 705 | *protocol_name_list_length = 0; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 706 | if (num_alpn_protocols == 0) return TSI_INVALID_ARGUMENT; |
| 707 | for (i = 0; i < num_alpn_protocols; i++) { |
Craig Tiller | be98d24 | 2017-11-10 15:26:57 -0800 | [diff] [blame] | 708 | size_t length = |
| 709 | alpn_protocols[i] == nullptr ? 0 : strlen(alpn_protocols[i]); |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 710 | if (length == 0 || length > 255) { |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 711 | gpr_log(GPR_ERROR, "Invalid protocol name length: %d.", |
| 712 | static_cast<int>(length)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 713 | return TSI_INVALID_ARGUMENT; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 714 | } |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 715 | *protocol_name_list_length += length + 1; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 716 | } |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 717 | *protocol_name_list = |
| 718 | static_cast<unsigned char*>(gpr_malloc(*protocol_name_list_length)); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 719 | if (*protocol_name_list == nullptr) return TSI_OUT_OF_RESOURCES; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 720 | current = *protocol_name_list; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 721 | for (i = 0; i < num_alpn_protocols; i++) { |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 722 | size_t length = strlen(alpn_protocols[i]); |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 723 | *(current++) = static_cast<uint8_t>(length); /* max checked above. */ |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 724 | memcpy(current, alpn_protocols[i], length); |
| 725 | current += length; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 726 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 727 | /* Safety check. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 728 | if ((current < *protocol_name_list) || |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 729 | (static_cast<uintptr_t>(current - *protocol_name_list) != |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 730 | *protocol_name_list_length)) { |
| 731 | return TSI_INTERNAL_ERROR; |
| 732 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 733 | return TSI_OK; |
| 734 | } |
| 735 | |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 736 | // The verification callback is used for clients that don't really care about |
| 737 | // the server's certificate, but we need to pull it anyway, in case a higher |
| 738 | // layer wants to look at it. In this case the verification may fail, but |
| 739 | // we don't really care. |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 740 | static int NullVerifyCallback(int preverify_ok, X509_STORE_CTX* ctx) { |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 741 | return 1; |
| 742 | } |
| 743 | |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 744 | /* --- tsi_ssl_root_certs_store methods implementation. ---*/ |
| 745 | |
| 746 | tsi_ssl_root_certs_store* tsi_ssl_root_certs_store_create( |
| 747 | const char* pem_roots) { |
| 748 | if (pem_roots == nullptr) { |
| 749 | gpr_log(GPR_ERROR, "The root certificates are empty."); |
| 750 | return nullptr; |
| 751 | } |
| 752 | tsi_ssl_root_certs_store* root_store = static_cast<tsi_ssl_root_certs_store*>( |
| 753 | gpr_zalloc(sizeof(tsi_ssl_root_certs_store))); |
| 754 | if (root_store == nullptr) { |
| 755 | gpr_log(GPR_ERROR, "Could not allocate buffer for ssl_root_certs_store."); |
| 756 | return nullptr; |
| 757 | } |
| 758 | root_store->store = X509_STORE_new(); |
| 759 | if (root_store->store == nullptr) { |
| 760 | gpr_log(GPR_ERROR, "Could not allocate buffer for X509_STORE."); |
| 761 | gpr_free(root_store); |
| 762 | return nullptr; |
| 763 | } |
| 764 | tsi_result result = x509_store_load_certs(root_store->store, pem_roots, |
| 765 | strlen(pem_roots), nullptr); |
| 766 | if (result != TSI_OK) { |
| 767 | gpr_log(GPR_ERROR, "Could not load root certificates."); |
| 768 | X509_STORE_free(root_store->store); |
| 769 | gpr_free(root_store); |
| 770 | return nullptr; |
| 771 | } |
| 772 | return root_store; |
| 773 | } |
| 774 | |
| 775 | void tsi_ssl_root_certs_store_destroy(tsi_ssl_root_certs_store* self) { |
| 776 | if (self == nullptr) return; |
| 777 | X509_STORE_free(self->store); |
| 778 | gpr_free(self); |
| 779 | } |
| 780 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 781 | /* --- tsi_ssl_session_cache methods implementation. ---*/ |
| 782 | |
| 783 | tsi_ssl_session_cache* tsi_ssl_session_cache_create_lru(size_t capacity) { |
| 784 | /* Pointer will be dereferenced by unref call. */ |
| 785 | return reinterpret_cast<tsi_ssl_session_cache*>( |
| 786 | tsi::SslSessionLRUCache::Create(capacity).release()); |
| 787 | } |
| 788 | |
| 789 | void tsi_ssl_session_cache_ref(tsi_ssl_session_cache* cache) { |
| 790 | /* Pointer will be dereferenced by unref call. */ |
| 791 | reinterpret_cast<tsi::SslSessionLRUCache*>(cache)->Ref().release(); |
| 792 | } |
| 793 | |
| 794 | void tsi_ssl_session_cache_unref(tsi_ssl_session_cache* cache) { |
| 795 | reinterpret_cast<tsi::SslSessionLRUCache*>(cache)->Unref(); |
| 796 | } |
| 797 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 798 | /* --- tsi_frame_protector methods implementation. ---*/ |
| 799 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 800 | static tsi_result ssl_protector_protect(tsi_frame_protector* self, |
| 801 | const unsigned char* unprotected_bytes, |
| 802 | size_t* unprotected_bytes_size, |
| 803 | unsigned char* protected_output_frames, |
| 804 | size_t* protected_output_frames_size) { |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 805 | tsi_ssl_frame_protector* impl = |
| 806 | reinterpret_cast<tsi_ssl_frame_protector*>(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 807 | int read_from_ssl; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 808 | size_t available; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 809 | tsi_result result = TSI_OK; |
| 810 | |
| 811 | /* First see if we have some pending data in the SSL BIO. */ |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 812 | int pending_in_ssl = static_cast<int>(BIO_pending(impl->network_io)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 813 | if (pending_in_ssl > 0) { |
| 814 | *unprotected_bytes_size = 0; |
| 815 | GPR_ASSERT(*protected_output_frames_size <= INT_MAX); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 816 | read_from_ssl = BIO_read(impl->network_io, protected_output_frames, |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 817 | static_cast<int>(*protected_output_frames_size)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 818 | if (read_from_ssl < 0) { |
| 819 | gpr_log(GPR_ERROR, |
| 820 | "Could not read from BIO even though some data is pending"); |
| 821 | return TSI_INTERNAL_ERROR; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 822 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 823 | *protected_output_frames_size = static_cast<size_t>(read_from_ssl); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 824 | return TSI_OK; |
| 825 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 826 | |
| 827 | /* Now see if we can send a complete frame. */ |
| 828 | available = impl->buffer_size - impl->buffer_offset; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 829 | if (available > *unprotected_bytes_size) { |
| 830 | /* If we cannot, just copy the data in our internal buffer. */ |
| 831 | memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, |
| 832 | *unprotected_bytes_size); |
| 833 | impl->buffer_offset += *unprotected_bytes_size; |
| 834 | *protected_output_frames_size = 0; |
| 835 | return TSI_OK; |
| 836 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 837 | |
| 838 | /* If we can, prepare the buffer, send it to SSL_write and read. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 839 | memcpy(impl->buffer + impl->buffer_offset, unprotected_bytes, available); |
| 840 | result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_size); |
| 841 | if (result != TSI_OK) return result; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 842 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 843 | GPR_ASSERT(*protected_output_frames_size <= INT_MAX); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 844 | read_from_ssl = BIO_read(impl->network_io, protected_output_frames, |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 845 | static_cast<int>(*protected_output_frames_size)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 846 | if (read_from_ssl < 0) { |
| 847 | gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); |
| 848 | return TSI_INTERNAL_ERROR; |
| 849 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 850 | *protected_output_frames_size = static_cast<size_t>(read_from_ssl); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 851 | *unprotected_bytes_size = available; |
| 852 | impl->buffer_offset = 0; |
| 853 | return TSI_OK; |
| 854 | } |
| 855 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 856 | static tsi_result ssl_protector_protect_flush( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 857 | tsi_frame_protector* self, unsigned char* protected_output_frames, |
| 858 | size_t* protected_output_frames_size, size_t* still_pending_size) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 859 | tsi_result result = TSI_OK; |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 860 | tsi_ssl_frame_protector* impl = |
| 861 | reinterpret_cast<tsi_ssl_frame_protector*>(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 862 | int read_from_ssl = 0; |
Craig Tiller | 3121fd4 | 2015-09-10 09:56:20 -0700 | [diff] [blame] | 863 | int pending; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 864 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 865 | if (impl->buffer_offset != 0) { |
| 866 | result = do_ssl_write(impl->ssl, impl->buffer, impl->buffer_offset); |
| 867 | if (result != TSI_OK) return result; |
| 868 | impl->buffer_offset = 0; |
| 869 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 870 | |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 871 | pending = static_cast<int>(BIO_pending(impl->network_io)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 872 | GPR_ASSERT(pending >= 0); |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 873 | *still_pending_size = static_cast<size_t>(pending); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 874 | if (*still_pending_size == 0) return TSI_OK; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 875 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 876 | GPR_ASSERT(*protected_output_frames_size <= INT_MAX); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 877 | read_from_ssl = BIO_read(impl->network_io, protected_output_frames, |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 878 | static_cast<int>(*protected_output_frames_size)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 879 | if (read_from_ssl <= 0) { |
| 880 | gpr_log(GPR_ERROR, "Could not read from BIO after SSL_write."); |
| 881 | return TSI_INTERNAL_ERROR; |
| 882 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 883 | *protected_output_frames_size = static_cast<size_t>(read_from_ssl); |
| 884 | pending = static_cast<int>(BIO_pending(impl->network_io)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 885 | GPR_ASSERT(pending >= 0); |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 886 | *still_pending_size = static_cast<size_t>(pending); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 887 | return TSI_OK; |
| 888 | } |
| 889 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 890 | static tsi_result ssl_protector_unprotect( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 891 | tsi_frame_protector* self, const unsigned char* protected_frames_bytes, |
| 892 | size_t* protected_frames_bytes_size, unsigned char* unprotected_bytes, |
| 893 | size_t* unprotected_bytes_size) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 894 | tsi_result result = TSI_OK; |
| 895 | int written_into_ssl = 0; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 896 | size_t output_bytes_size = *unprotected_bytes_size; |
| 897 | size_t output_bytes_offset = 0; |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 898 | tsi_ssl_frame_protector* impl = |
| 899 | reinterpret_cast<tsi_ssl_frame_protector*>(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 900 | |
| 901 | /* First, try to read remaining data from ssl. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 902 | result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); |
| 903 | if (result != TSI_OK) return result; |
| 904 | if (*unprotected_bytes_size == output_bytes_size) { |
| 905 | /* We have read everything we could and cannot process any more input. */ |
| 906 | *protected_frames_bytes_size = 0; |
| 907 | return TSI_OK; |
| 908 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 909 | output_bytes_offset = *unprotected_bytes_size; |
| 910 | unprotected_bytes += output_bytes_offset; |
| 911 | *unprotected_bytes_size = output_bytes_size - output_bytes_offset; |
| 912 | |
| 913 | /* Then, try to write some data to ssl. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 914 | GPR_ASSERT(*protected_frames_bytes_size <= INT_MAX); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 915 | written_into_ssl = BIO_write(impl->network_io, protected_frames_bytes, |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 916 | static_cast<int>(*protected_frames_bytes_size)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 917 | if (written_into_ssl < 0) { |
| 918 | gpr_log(GPR_ERROR, "Sending protected frame to ssl failed with %d", |
| 919 | written_into_ssl); |
| 920 | return TSI_INTERNAL_ERROR; |
| 921 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 922 | *protected_frames_bytes_size = static_cast<size_t>(written_into_ssl); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 923 | |
| 924 | /* Now try to read some data again. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 925 | result = do_ssl_read(impl->ssl, unprotected_bytes, unprotected_bytes_size); |
| 926 | if (result == TSI_OK) { |
| 927 | /* Don't forget to output the total number of bytes read. */ |
| 928 | *unprotected_bytes_size += output_bytes_offset; |
| 929 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 930 | return result; |
| 931 | } |
| 932 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 933 | static void ssl_protector_destroy(tsi_frame_protector* self) { |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 934 | tsi_ssl_frame_protector* impl = |
| 935 | reinterpret_cast<tsi_ssl_frame_protector*>(self); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 936 | if (impl->buffer != nullptr) gpr_free(impl->buffer); |
| 937 | if (impl->ssl != nullptr) SSL_free(impl->ssl); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 938 | if (impl->network_io != nullptr) BIO_free(impl->network_io); |
Nicolas "Pixel" Noble | 7c9a154 | 2016-03-26 01:33:34 +0100 | [diff] [blame] | 939 | gpr_free(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 940 | } |
| 941 | |
| 942 | static const tsi_frame_protector_vtable frame_protector_vtable = { |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 943 | ssl_protector_protect, |
| 944 | ssl_protector_protect_flush, |
| 945 | ssl_protector_unprotect, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 946 | ssl_protector_destroy, |
Craig Tiller | d6c98df | 2015-08-18 09:33:44 -0700 | [diff] [blame] | 947 | }; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 948 | |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 949 | /* --- tsi_server_handshaker_factory methods implementation. --- */ |
| 950 | |
| 951 | static void tsi_ssl_handshaker_factory_destroy( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 952 | tsi_ssl_handshaker_factory* self) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 953 | if (self == nullptr) return; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 954 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 955 | if (self->vtable != nullptr && self->vtable->destroy != nullptr) { |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 956 | self->vtable->destroy(self); |
| 957 | } |
| 958 | /* Note, we don't free(self) here because this object is always directly |
| 959 | * embedded in another object. If tsi_ssl_handshaker_factory_init allocates |
| 960 | * any memory, it should be free'd here. */ |
| 961 | } |
| 962 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 963 | static tsi_ssl_handshaker_factory* tsi_ssl_handshaker_factory_ref( |
| 964 | tsi_ssl_handshaker_factory* self) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 965 | if (self == nullptr) return nullptr; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 966 | gpr_refn(&self->refcount, 1); |
| 967 | return self; |
| 968 | } |
| 969 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 970 | static void tsi_ssl_handshaker_factory_unref(tsi_ssl_handshaker_factory* self) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 971 | if (self == nullptr) return; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 972 | |
| 973 | if (gpr_unref(&self->refcount)) { |
| 974 | tsi_ssl_handshaker_factory_destroy(self); |
| 975 | } |
| 976 | } |
| 977 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 978 | static tsi_ssl_handshaker_factory_vtable handshaker_factory_vtable = {nullptr}; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 979 | |
| 980 | /* Initializes a tsi_ssl_handshaker_factory object. Caller is responsible for |
| 981 | * allocating memory for the factory. */ |
| 982 | static void tsi_ssl_handshaker_factory_init( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 983 | tsi_ssl_handshaker_factory* factory) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 984 | GPR_ASSERT(factory != nullptr); |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 985 | |
| 986 | factory->vtable = &handshaker_factory_vtable; |
| 987 | gpr_ref_init(&factory->refcount, 1); |
| 988 | } |
| 989 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 990 | /* --- tsi_handshaker methods implementation. ---*/ |
| 991 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 992 | static tsi_result ssl_handshaker_get_bytes_to_send_to_peer(tsi_handshaker* self, |
| 993 | unsigned char* bytes, |
| 994 | size_t* bytes_size) { |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 995 | tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 996 | int bytes_read_from_ssl = 0; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 997 | if (bytes == nullptr || bytes_size == nullptr || *bytes_size == 0 || |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 998 | *bytes_size > INT_MAX) { |
| 999 | return TSI_INVALID_ARGUMENT; |
| 1000 | } |
| 1001 | GPR_ASSERT(*bytes_size <= INT_MAX); |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 1002 | bytes_read_from_ssl = |
| 1003 | BIO_read(impl->network_io, bytes, static_cast<int>(*bytes_size)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1004 | if (bytes_read_from_ssl < 0) { |
| 1005 | *bytes_size = 0; |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1006 | if (!BIO_should_retry(impl->network_io)) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1007 | impl->result = TSI_INTERNAL_ERROR; |
| 1008 | return impl->result; |
| 1009 | } else { |
| 1010 | return TSI_OK; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1011 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1012 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1013 | *bytes_size = static_cast<size_t>(bytes_read_from_ssl); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1014 | return BIO_pending(impl->network_io) == 0 ? TSI_OK : TSI_INCOMPLETE_DATA; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1015 | } |
| 1016 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1017 | static tsi_result ssl_handshaker_get_result(tsi_handshaker* self) { |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1018 | tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1019 | if ((impl->result == TSI_HANDSHAKE_IN_PROGRESS) && |
| 1020 | SSL_is_init_finished(impl->ssl)) { |
| 1021 | impl->result = TSI_OK; |
| 1022 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1023 | return impl->result; |
| 1024 | } |
| 1025 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1026 | static tsi_result ssl_handshaker_process_bytes_from_peer( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1027 | tsi_handshaker* self, const unsigned char* bytes, size_t* bytes_size) { |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1028 | tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1029 | int bytes_written_into_ssl_size = 0; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1030 | if (bytes == nullptr || bytes_size == nullptr || *bytes_size > INT_MAX) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1031 | return TSI_INVALID_ARGUMENT; |
| 1032 | } |
| 1033 | GPR_ASSERT(*bytes_size <= INT_MAX); |
| 1034 | bytes_written_into_ssl_size = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1035 | BIO_write(impl->network_io, bytes, static_cast<int>(*bytes_size)); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1036 | if (bytes_written_into_ssl_size < 0) { |
| 1037 | gpr_log(GPR_ERROR, "Could not write to memory BIO."); |
| 1038 | impl->result = TSI_INTERNAL_ERROR; |
| 1039 | return impl->result; |
| 1040 | } |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1041 | *bytes_size = static_cast<size_t>(bytes_written_into_ssl_size); |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 1042 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1043 | if (!tsi_handshaker_is_in_progress(self)) { |
| 1044 | impl->result = TSI_OK; |
| 1045 | return impl->result; |
| 1046 | } else { |
| 1047 | /* Get ready to get some bytes from SSL. */ |
| 1048 | int ssl_result = SSL_do_handshake(impl->ssl); |
| 1049 | ssl_result = SSL_get_error(impl->ssl, ssl_result); |
| 1050 | switch (ssl_result) { |
| 1051 | case SSL_ERROR_WANT_READ: |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1052 | if (BIO_pending(impl->network_io) == 0) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1053 | /* We need more data. */ |
| 1054 | return TSI_INCOMPLETE_DATA; |
| 1055 | } else { |
| 1056 | return TSI_OK; |
| 1057 | } |
| 1058 | case SSL_ERROR_NONE: |
| 1059 | return TSI_OK; |
| 1060 | default: { |
| 1061 | char err_str[256]; |
| 1062 | ERR_error_string_n(ERR_get_error(), err_str, sizeof(err_str)); |
| 1063 | gpr_log(GPR_ERROR, "Handshake failed with fatal error %s: %s.", |
| 1064 | ssl_error_string(ssl_result), err_str); |
| 1065 | impl->result = TSI_PROTOCOL_FAILURE; |
| 1066 | return impl->result; |
| 1067 | } |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 1068 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1069 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1070 | } |
| 1071 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1072 | static tsi_result ssl_handshaker_extract_peer(tsi_handshaker* self, |
| 1073 | tsi_peer* peer) { |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1074 | tsi_result result = TSI_OK; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1075 | const unsigned char* alpn_selected = nullptr; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1076 | unsigned int alpn_selected_len; |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1077 | tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self); |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1078 | X509* peer_cert = SSL_get_peer_certificate(impl->ssl); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1079 | if (peer_cert != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1080 | result = peer_from_x509(peer_cert, 1, peer); |
| 1081 | X509_free(peer_cert); |
| 1082 | if (result != TSI_OK) return result; |
| 1083 | } |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1084 | #if TSI_OPENSSL_ALPN_SUPPORT |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1085 | SSL_get0_alpn_selected(impl->ssl, &alpn_selected, &alpn_selected_len); |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1086 | #endif /* TSI_OPENSSL_ALPN_SUPPORT */ |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1087 | if (alpn_selected == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1088 | /* Try npn. */ |
| 1089 | SSL_get0_next_proto_negotiated(impl->ssl, &alpn_selected, |
| 1090 | &alpn_selected_len); |
| 1091 | } |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1092 | |
| 1093 | // 1 is for session reused property. |
| 1094 | size_t new_property_count = peer->property_count + 1; |
| 1095 | if (alpn_selected != nullptr) new_property_count++; |
| 1096 | tsi_peer_property* new_properties = static_cast<tsi_peer_property*>( |
| 1097 | gpr_zalloc(sizeof(*new_properties) * new_property_count)); |
| 1098 | for (size_t i = 0; i < peer->property_count; i++) { |
| 1099 | new_properties[i] = peer->properties[i]; |
| 1100 | } |
| 1101 | if (peer->properties != nullptr) gpr_free(peer->properties); |
| 1102 | peer->properties = new_properties; |
| 1103 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1104 | if (alpn_selected != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1105 | result = tsi_construct_string_peer_property( |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 1106 | TSI_SSL_ALPN_SELECTED_PROTOCOL, |
| 1107 | reinterpret_cast<const char*>(alpn_selected), alpn_selected_len, |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1108 | &peer->properties[peer->property_count]); |
| 1109 | if (result != TSI_OK) return result; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1110 | peer->property_count++; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1111 | } |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1112 | |
| 1113 | const char* session_reused = SSL_session_reused(impl->ssl) ? "true" : "false"; |
| 1114 | result = tsi_construct_string_peer_property( |
| 1115 | TSI_SSL_SESSION_REUSED_PEER_PROPERTY, session_reused, |
| 1116 | strlen(session_reused) + 1, &peer->properties[peer->property_count]); |
| 1117 | if (result != TSI_OK) return result; |
| 1118 | peer->property_count++; |
| 1119 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1120 | return result; |
| 1121 | } |
| 1122 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1123 | static tsi_result ssl_handshaker_create_frame_protector( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1124 | tsi_handshaker* self, size_t* max_output_protected_frame_size, |
| 1125 | tsi_frame_protector** protector) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1126 | size_t actual_max_output_protected_frame_size = |
| 1127 | TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1128 | tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self); |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1129 | tsi_ssl_frame_protector* protector_impl = |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 1130 | static_cast<tsi_ssl_frame_protector*>( |
| 1131 | gpr_zalloc(sizeof(*protector_impl))); |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 1132 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1133 | if (max_output_protected_frame_size != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1134 | if (*max_output_protected_frame_size > |
| 1135 | TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND) { |
| 1136 | *max_output_protected_frame_size = |
| 1137 | TSI_SSL_MAX_PROTECTED_FRAME_SIZE_UPPER_BOUND; |
| 1138 | } else if (*max_output_protected_frame_size < |
| 1139 | TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND) { |
| 1140 | *max_output_protected_frame_size = |
| 1141 | TSI_SSL_MAX_PROTECTED_FRAME_SIZE_LOWER_BOUND; |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 1142 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1143 | actual_max_output_protected_frame_size = *max_output_protected_frame_size; |
| 1144 | } |
| 1145 | protector_impl->buffer_size = |
| 1146 | actual_max_output_protected_frame_size - TSI_SSL_MAX_PROTECTION_OVERHEAD; |
Yash Tibrewal | acd46e5 | 2017-09-20 11:28:25 -0700 | [diff] [blame] | 1147 | protector_impl->buffer = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1148 | static_cast<unsigned char*>(gpr_malloc(protector_impl->buffer_size)); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1149 | if (protector_impl->buffer == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1150 | gpr_log(GPR_ERROR, |
| 1151 | "Could not allocated buffer for tsi_ssl_frame_protector."); |
Nicolas "Pixel" Noble | 7c9a154 | 2016-03-26 01:33:34 +0100 | [diff] [blame] | 1152 | gpr_free(protector_impl); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1153 | return TSI_INTERNAL_ERROR; |
| 1154 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1155 | |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1156 | /* Transfer ownership of ssl and network_io to the frame protector. It is OK |
| 1157 | * as the caller cannot call anything else but destroy on the handshaker |
| 1158 | * after this call. */ |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1159 | protector_impl->ssl = impl->ssl; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1160 | impl->ssl = nullptr; |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1161 | protector_impl->network_io = impl->network_io; |
| 1162 | impl->network_io = nullptr; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1163 | |
| 1164 | protector_impl->base.vtable = &frame_protector_vtable; |
| 1165 | *protector = &protector_impl->base; |
| 1166 | return TSI_OK; |
| 1167 | } |
| 1168 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1169 | static void ssl_handshaker_destroy(tsi_handshaker* self) { |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1170 | tsi_ssl_handshaker* impl = reinterpret_cast<tsi_ssl_handshaker*>(self); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1171 | SSL_free(impl->ssl); |
| 1172 | BIO_free(impl->network_io); |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1173 | tsi_ssl_handshaker_factory_unref(impl->factory_ref); |
Nicolas "Pixel" Noble | 7c9a154 | 2016-03-26 01:33:34 +0100 | [diff] [blame] | 1174 | gpr_free(impl); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1175 | } |
| 1176 | |
| 1177 | static const tsi_handshaker_vtable handshaker_vtable = { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1178 | ssl_handshaker_get_bytes_to_send_to_peer, |
Craig Tiller | f40df23 | 2016-03-25 13:38:14 -0700 | [diff] [blame] | 1179 | ssl_handshaker_process_bytes_from_peer, |
| 1180 | ssl_handshaker_get_result, |
| 1181 | ssl_handshaker_extract_peer, |
| 1182 | ssl_handshaker_create_frame_protector, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1183 | ssl_handshaker_destroy, |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1184 | nullptr, |
Craig Tiller | d6c98df | 2015-08-18 09:33:44 -0700 | [diff] [blame] | 1185 | }; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1186 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1187 | /* --- tsi_ssl_handshaker_factory common methods. --- */ |
| 1188 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1189 | static void tsi_ssl_handshaker_resume_session( |
| 1190 | SSL* ssl, tsi::SslSessionLRUCache* session_cache) { |
| 1191 | const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); |
| 1192 | if (server_name == nullptr) { |
| 1193 | return; |
| 1194 | } |
| 1195 | tsi::SslSessionPtr session = session_cache->Get(server_name); |
| 1196 | if (session != nullptr) { |
| 1197 | // SSL_set_session internally increments reference counter. |
| 1198 | SSL_set_session(ssl, session.get()); |
| 1199 | } |
| 1200 | } |
| 1201 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1202 | static tsi_result create_tsi_ssl_handshaker(SSL_CTX* ctx, int is_client, |
| 1203 | const char* server_name_indication, |
| 1204 | tsi_ssl_handshaker_factory* factory, |
| 1205 | tsi_handshaker** handshaker) { |
| 1206 | SSL* ssl = SSL_new(ctx); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1207 | BIO* network_io = nullptr; |
| 1208 | BIO* ssl_io = nullptr; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1209 | tsi_ssl_handshaker* impl = nullptr; |
| 1210 | *handshaker = nullptr; |
| 1211 | if (ctx == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1212 | gpr_log(GPR_ERROR, "SSL Context is null. Should never happen."); |
| 1213 | return TSI_INTERNAL_ERROR; |
| 1214 | } |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1215 | if (ssl == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1216 | return TSI_OUT_OF_RESOURCES; |
| 1217 | } |
| 1218 | SSL_set_info_callback(ssl, ssl_info_callback); |
| 1219 | |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1220 | if (!BIO_new_bio_pair(&network_io, 0, &ssl_io, 0)) { |
| 1221 | gpr_log(GPR_ERROR, "BIO_new_bio_pair failed."); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1222 | SSL_free(ssl); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1223 | return TSI_OUT_OF_RESOURCES; |
| 1224 | } |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1225 | SSL_set_bio(ssl, ssl_io, ssl_io); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1226 | if (is_client) { |
| 1227 | int ssl_result; |
| 1228 | SSL_set_connect_state(ssl); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1229 | if (server_name_indication != nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1230 | if (!SSL_set_tlsext_host_name(ssl, server_name_indication)) { |
| 1231 | gpr_log(GPR_ERROR, "Invalid server name indication %s.", |
| 1232 | server_name_indication); |
| 1233 | SSL_free(ssl); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1234 | BIO_free(network_io); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1235 | return TSI_INTERNAL_ERROR; |
| 1236 | } |
| 1237 | } |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1238 | tsi_ssl_client_handshaker_factory* client_factory = |
| 1239 | reinterpret_cast<tsi_ssl_client_handshaker_factory*>(factory); |
| 1240 | if (client_factory->session_cache != nullptr) { |
| 1241 | tsi_ssl_handshaker_resume_session(ssl, |
| 1242 | client_factory->session_cache.get()); |
| 1243 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1244 | ssl_result = SSL_do_handshake(ssl); |
| 1245 | ssl_result = SSL_get_error(ssl, ssl_result); |
| 1246 | if (ssl_result != SSL_ERROR_WANT_READ) { |
| 1247 | gpr_log(GPR_ERROR, |
| 1248 | "Unexpected error received from first SSL_do_handshake call: %s", |
| 1249 | ssl_error_string(ssl_result)); |
| 1250 | SSL_free(ssl); |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1251 | BIO_free(network_io); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1252 | return TSI_INTERNAL_ERROR; |
| 1253 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1254 | } else { |
| 1255 | SSL_set_accept_state(ssl); |
| 1256 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1257 | |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1258 | impl = static_cast<tsi_ssl_handshaker*>(gpr_zalloc(sizeof(*impl))); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1259 | impl->ssl = ssl; |
Ruslan Nigmatullin | 1722640 | 2018-01-17 18:45:53 -0800 | [diff] [blame] | 1260 | impl->network_io = network_io; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1261 | impl->result = TSI_HANDSHAKE_IN_PROGRESS; |
| 1262 | impl->base.vtable = &handshaker_vtable; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1263 | impl->factory_ref = tsi_ssl_handshaker_factory_ref(factory); |
| 1264 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1265 | *handshaker = &impl->base; |
| 1266 | return TSI_OK; |
| 1267 | } |
| 1268 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1269 | static int select_protocol_list(const unsigned char** out, |
| 1270 | unsigned char* outlen, |
| 1271 | const unsigned char* client_list, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1272 | size_t client_list_len, |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1273 | const unsigned char* server_list, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1274 | size_t server_list_len) { |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1275 | const unsigned char* client_current = client_list; |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 1276 | while (static_cast<unsigned int>(client_current - client_list) < |
| 1277 | client_list_len) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1278 | unsigned char client_current_len = *(client_current++); |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1279 | const unsigned char* server_current = server_list; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1280 | while ((server_current >= server_list) && |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 1281 | static_cast<uintptr_t>(server_current - server_list) < |
| 1282 | server_list_len) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1283 | unsigned char server_current_len = *(server_current++); |
| 1284 | if ((client_current_len == server_current_len) && |
| 1285 | !memcmp(client_current, server_current, server_current_len)) { |
| 1286 | *out = server_current; |
| 1287 | *outlen = server_current_len; |
| 1288 | return SSL_TLSEXT_ERR_OK; |
| 1289 | } |
| 1290 | server_current += server_current_len; |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1291 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1292 | client_current += client_current_len; |
| 1293 | } |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1294 | return SSL_TLSEXT_ERR_NOACK; |
| 1295 | } |
| 1296 | |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1297 | /* --- tsi_ssl_client_handshaker_factory methods implementation. --- */ |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1298 | |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1299 | tsi_result tsi_ssl_client_handshaker_factory_create_handshaker( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1300 | tsi_ssl_client_handshaker_factory* self, const char* server_name_indication, |
| 1301 | tsi_handshaker** handshaker) { |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1302 | return create_tsi_ssl_handshaker(self->ssl_context, 1, server_name_indication, |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1303 | &self->base, handshaker); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1304 | } |
| 1305 | |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1306 | void tsi_ssl_client_handshaker_factory_unref( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1307 | tsi_ssl_client_handshaker_factory* self) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1308 | if (self == nullptr) return; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1309 | tsi_ssl_handshaker_factory_unref(&self->base); |
| 1310 | } |
| 1311 | |
| 1312 | static void tsi_ssl_client_handshaker_factory_destroy( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1313 | tsi_ssl_handshaker_factory* factory) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1314 | if (factory == nullptr) return; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1315 | tsi_ssl_client_handshaker_factory* self = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1316 | reinterpret_cast<tsi_ssl_client_handshaker_factory*>(factory); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1317 | if (self->ssl_context != nullptr) SSL_CTX_free(self->ssl_context); |
| 1318 | if (self->alpn_protocol_list != nullptr) gpr_free(self->alpn_protocol_list); |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1319 | self->session_cache.reset(); |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1320 | gpr_free(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1321 | } |
| 1322 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1323 | static int client_handshaker_factory_npn_callback(SSL* ssl, unsigned char** out, |
| 1324 | unsigned char* outlen, |
| 1325 | const unsigned char* in, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1326 | unsigned int inlen, |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1327 | void* arg) { |
| 1328 | tsi_ssl_client_handshaker_factory* factory = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1329 | static_cast<tsi_ssl_client_handshaker_factory*>(arg); |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1330 | return select_protocol_list((const unsigned char**)out, outlen, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1331 | factory->alpn_protocol_list, |
| 1332 | factory->alpn_protocol_list_length, in, inlen); |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1333 | } |
| 1334 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1335 | /* --- tsi_ssl_server_handshaker_factory methods implementation. --- */ |
| 1336 | |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1337 | tsi_result tsi_ssl_server_handshaker_factory_create_handshaker( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1338 | tsi_ssl_server_handshaker_factory* self, tsi_handshaker** handshaker) { |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1339 | if (self->ssl_context_count == 0) return TSI_INVALID_ARGUMENT; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1340 | /* Create the handshaker with the first context. We will switch if needed |
| 1341 | because of SNI in ssl_server_handshaker_factory_servername_callback. */ |
Craig Tiller | be98d24 | 2017-11-10 15:26:57 -0800 | [diff] [blame] | 1342 | return create_tsi_ssl_handshaker(self->ssl_contexts[0], 0, nullptr, |
| 1343 | &self->base, handshaker); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1344 | } |
| 1345 | |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1346 | void tsi_ssl_server_handshaker_factory_unref( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1347 | tsi_ssl_server_handshaker_factory* self) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1348 | if (self == nullptr) return; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1349 | tsi_ssl_handshaker_factory_unref(&self->base); |
| 1350 | } |
| 1351 | |
| 1352 | static void tsi_ssl_server_handshaker_factory_destroy( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1353 | tsi_ssl_handshaker_factory* factory) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1354 | if (factory == nullptr) return; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1355 | tsi_ssl_server_handshaker_factory* self = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1356 | reinterpret_cast<tsi_ssl_server_handshaker_factory*>(factory); |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 1357 | size_t i; |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1358 | for (i = 0; i < self->ssl_context_count; i++) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1359 | if (self->ssl_contexts[i] != nullptr) { |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1360 | SSL_CTX_free(self->ssl_contexts[i]); |
| 1361 | tsi_peer_destruct(&self->ssl_context_x509_subject_names[i]); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1362 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1363 | } |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1364 | if (self->ssl_contexts != nullptr) gpr_free(self->ssl_contexts); |
| 1365 | if (self->ssl_context_x509_subject_names != nullptr) { |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1366 | gpr_free(self->ssl_context_x509_subject_names); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1367 | } |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1368 | if (self->alpn_protocol_list != nullptr) gpr_free(self->alpn_protocol_list); |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1369 | gpr_free(self); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1370 | } |
| 1371 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1372 | static int does_entry_match_name(const char* entry, size_t entry_length, |
| 1373 | const char* name) { |
| 1374 | const char* dot; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1375 | const char* name_subdomain = nullptr; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1376 | size_t name_length = strlen(name); |
Julien Boeuf | 9fff77e | 2015-02-24 16:50:35 -0800 | [diff] [blame] | 1377 | size_t name_subdomain_length; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1378 | if (entry_length == 0) return 0; |
Julien Boeuf | 9fff77e | 2015-02-24 16:50:35 -0800 | [diff] [blame] | 1379 | |
| 1380 | /* Take care of '.' terminations. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1381 | if (name[name_length - 1] == '.') { |
| 1382 | name_length--; |
| 1383 | } |
| 1384 | if (entry[entry_length - 1] == '.') { |
| 1385 | entry_length--; |
| 1386 | if (entry_length == 0) return 0; |
| 1387 | } |
Julien Boeuf | 9fff77e | 2015-02-24 16:50:35 -0800 | [diff] [blame] | 1388 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1389 | if ((name_length == entry_length) && |
| 1390 | strncmp(name, entry, entry_length) == 0) { |
| 1391 | return 1; /* Perfect match. */ |
| 1392 | } |
| 1393 | if (entry[0] != '*') return 0; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1394 | |
| 1395 | /* Wildchar subdomain matching. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1396 | if (entry_length < 3 || entry[1] != '.') { /* At least *.x */ |
| 1397 | gpr_log(GPR_ERROR, "Invalid wildchar entry."); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1398 | return 0; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1399 | } |
| 1400 | name_subdomain = strchr(name, '.'); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1401 | if (name_subdomain == nullptr) return 0; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1402 | name_subdomain_length = strlen(name_subdomain); |
| 1403 | if (name_subdomain_length < 2) return 0; |
| 1404 | name_subdomain++; /* Starts after the dot. */ |
Julien Boeuf | 0170a6c | 2015-02-24 18:08:01 -0800 | [diff] [blame] | 1405 | name_subdomain_length--; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1406 | entry += 2; /* Remove *. */ |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1407 | entry_length -= 2; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1408 | dot = strchr(name_subdomain, '.'); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1409 | if ((dot == nullptr) || (dot == &name_subdomain[name_subdomain_length - 1])) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1410 | gpr_log(GPR_ERROR, "Invalid toplevel subdomain: %s", name_subdomain); |
| 1411 | return 0; |
| 1412 | } |
| 1413 | if (name_subdomain[name_subdomain_length - 1] == '.') { |
| 1414 | name_subdomain_length--; |
| 1415 | } |
| 1416 | return ((entry_length > 0) && (name_subdomain_length == entry_length) && |
| 1417 | strncmp(entry, name_subdomain, entry_length) == 0); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1418 | } |
| 1419 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1420 | static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap, |
| 1421 | void* arg) { |
| 1422 | tsi_ssl_server_handshaker_factory* impl = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1423 | static_cast<tsi_ssl_server_handshaker_factory*>(arg); |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 1424 | size_t i = 0; |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1425 | const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1426 | if (servername == nullptr || strlen(servername) == 0) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1427 | return SSL_TLSEXT_ERR_NOACK; |
| 1428 | } |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 1429 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1430 | for (i = 0; i < impl->ssl_context_count; i++) { |
| 1431 | if (tsi_ssl_peer_matches_name(&impl->ssl_context_x509_subject_names[i], |
| 1432 | servername)) { |
| 1433 | SSL_set_SSL_CTX(ssl, impl->ssl_contexts[i]); |
| 1434 | return SSL_TLSEXT_ERR_OK; |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 1435 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1436 | } |
| 1437 | gpr_log(GPR_ERROR, "No match found for server name: %s.", servername); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1438 | return SSL_TLSEXT_ERR_ALERT_WARNING; |
| 1439 | } |
| 1440 | |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1441 | #if TSI_OPENSSL_ALPN_SUPPORT |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1442 | static int server_handshaker_factory_alpn_callback( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1443 | SSL* ssl, const unsigned char** out, unsigned char* outlen, |
| 1444 | const unsigned char* in, unsigned int inlen, void* arg) { |
| 1445 | tsi_ssl_server_handshaker_factory* factory = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1446 | static_cast<tsi_ssl_server_handshaker_factory*>(arg); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1447 | return select_protocol_list(out, outlen, in, inlen, |
| 1448 | factory->alpn_protocol_list, |
| 1449 | factory->alpn_protocol_list_length); |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1450 | } |
| 1451 | #endif /* TSI_OPENSSL_ALPN_SUPPORT */ |
| 1452 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1453 | static int server_handshaker_factory_npn_advertised_callback( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1454 | SSL* ssl, const unsigned char** out, unsigned int* outlen, void* arg) { |
| 1455 | tsi_ssl_server_handshaker_factory* factory = |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1456 | static_cast<tsi_ssl_server_handshaker_factory*>(arg); |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1457 | *out = factory->alpn_protocol_list; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1458 | GPR_ASSERT(factory->alpn_protocol_list_length <= UINT_MAX); |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1459 | *outlen = static_cast<unsigned int>(factory->alpn_protocol_list_length); |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1460 | return SSL_TLSEXT_ERR_OK; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1461 | } |
| 1462 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1463 | /// This callback is called when new \a session is established and ready to |
| 1464 | /// be cached. This session can be reused for new connections to similar |
| 1465 | /// servers at later point of time. |
| 1466 | /// It's intended to be used with SSL_CTX_sess_set_new_cb function. |
| 1467 | /// |
| 1468 | /// It returns 1 if callback takes ownership over \a session and 0 otherwise. |
| 1469 | static int server_handshaker_factory_new_session_callback( |
| 1470 | SSL* ssl, SSL_SESSION* session) { |
| 1471 | SSL_CTX* ssl_context = SSL_get_SSL_CTX(ssl); |
| 1472 | if (ssl_context == nullptr) { |
| 1473 | return 0; |
| 1474 | } |
| 1475 | void* arg = SSL_CTX_get_ex_data(ssl_context, g_ssl_ctx_ex_factory_index); |
| 1476 | tsi_ssl_client_handshaker_factory* factory = |
| 1477 | static_cast<tsi_ssl_client_handshaker_factory*>(arg); |
| 1478 | const char* server_name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); |
| 1479 | if (server_name == nullptr) { |
| 1480 | return 0; |
| 1481 | } |
| 1482 | factory->session_cache->Put(server_name, tsi::SslSessionPtr(session)); |
| 1483 | // Return 1 to indicate transfered ownership over the given session. |
| 1484 | return 1; |
| 1485 | } |
| 1486 | |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1487 | /* --- tsi_ssl_handshaker_factory constructors. --- */ |
| 1488 | |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1489 | static tsi_ssl_handshaker_factory_vtable client_handshaker_factory_vtable = { |
| 1490 | tsi_ssl_client_handshaker_factory_destroy}; |
| 1491 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1492 | tsi_result tsi_create_ssl_client_handshaker_factory( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1493 | const tsi_ssl_pem_key_cert_pair* pem_key_cert_pair, |
| 1494 | const char* pem_root_certs, const char* cipher_suites, |
| 1495 | const char** alpn_protocols, uint16_t num_alpn_protocols, |
| 1496 | tsi_ssl_client_handshaker_factory** factory) { |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1497 | tsi_ssl_client_handshaker_options options; |
| 1498 | memset(&options, 0, sizeof(options)); |
| 1499 | options.pem_key_cert_pair = pem_key_cert_pair; |
| 1500 | options.pem_root_certs = pem_root_certs; |
| 1501 | options.cipher_suites = cipher_suites; |
| 1502 | options.alpn_protocols = alpn_protocols; |
| 1503 | options.num_alpn_protocols = num_alpn_protocols; |
| 1504 | return tsi_create_ssl_client_handshaker_factory_with_options(&options, |
| 1505 | factory); |
| 1506 | } |
| 1507 | |
| 1508 | tsi_result tsi_create_ssl_client_handshaker_factory_with_options( |
| 1509 | const tsi_ssl_client_handshaker_options* options, |
| 1510 | tsi_ssl_client_handshaker_factory** factory) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1511 | SSL_CTX* ssl_context = nullptr; |
| 1512 | tsi_ssl_client_handshaker_factory* impl = nullptr; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1513 | tsi_result result = TSI_OK; |
| 1514 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1515 | gpr_once_init(&g_init_openssl_once, init_openssl); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1516 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1517 | if (factory == nullptr) return TSI_INVALID_ARGUMENT; |
| 1518 | *factory = nullptr; |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 1519 | if (options->pem_root_certs == nullptr && options->root_store == nullptr) { |
| 1520 | return TSI_INVALID_ARGUMENT; |
| 1521 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1522 | |
| 1523 | ssl_context = SSL_CTX_new(TLSv1_2_method()); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1524 | if (ssl_context == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1525 | gpr_log(GPR_ERROR, "Could not create ssl context."); |
Craig Tiller | 45724b3 | 2015-09-22 10:42:19 -0700 | [diff] [blame] | 1526 | return TSI_INVALID_ARGUMENT; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1527 | } |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1528 | |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 1529 | impl = static_cast<tsi_ssl_client_handshaker_factory*>( |
| 1530 | gpr_zalloc(sizeof(*impl))); |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1531 | tsi_ssl_handshaker_factory_init(&impl->base); |
| 1532 | impl->base.vtable = &client_handshaker_factory_vtable; |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1533 | impl->ssl_context = ssl_context; |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1534 | if (options->session_cache != nullptr) { |
| 1535 | // Unref is called manually on factory destruction. |
| 1536 | impl->session_cache = |
| 1537 | reinterpret_cast<tsi::SslSessionLRUCache*>(options->session_cache) |
| 1538 | ->Ref(); |
| 1539 | SSL_CTX_set_ex_data(ssl_context, g_ssl_ctx_ex_factory_index, impl); |
| 1540 | SSL_CTX_sess_set_new_cb(ssl_context, |
| 1541 | server_handshaker_factory_new_session_callback); |
| 1542 | SSL_CTX_set_session_cache_mode(ssl_context, SSL_SESS_CACHE_CLIENT); |
| 1543 | } |
| 1544 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1545 | do { |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1546 | result = populate_ssl_context(ssl_context, options->pem_key_cert_pair, |
| 1547 | options->cipher_suites); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1548 | if (result != TSI_OK) break; |
jiangtaoli2016 | 144f555 | 2018-03-23 11:28:48 -0700 | [diff] [blame^] | 1549 | |
| 1550 | #if OPENSSL_VERSION_NUMBER >= 0x10100000 |
| 1551 | // X509_STORE_up_ref is only available since OpenSSL 1.1. |
| 1552 | if (options->root_store != nullptr) { |
| 1553 | X509_STORE_up_ref(options->root_store->store); |
| 1554 | SSL_CTX_set_cert_store(ssl_context, options->root_store->store); |
| 1555 | } |
| 1556 | #endif |
| 1557 | if (OPENSSL_VERSION_NUMBER < 0x10100000 || options->root_store == nullptr) { |
| 1558 | result = ssl_ctx_load_verification_certs( |
| 1559 | ssl_context, options->pem_root_certs, strlen(options->pem_root_certs), |
| 1560 | nullptr); |
| 1561 | if (result != TSI_OK) { |
| 1562 | gpr_log(GPR_ERROR, "Cannot load server root certificates."); |
| 1563 | break; |
| 1564 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1565 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1566 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1567 | if (options->num_alpn_protocols != 0) { |
| 1568 | result = build_alpn_protocol_name_list( |
| 1569 | options->alpn_protocols, options->num_alpn_protocols, |
| 1570 | &impl->alpn_protocol_list, &impl->alpn_protocol_list_length); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1571 | if (result != TSI_OK) { |
| 1572 | gpr_log(GPR_ERROR, "Building alpn list failed with error %s.", |
| 1573 | tsi_result_to_string(result)); |
| 1574 | break; |
| 1575 | } |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1576 | #if TSI_OPENSSL_ALPN_SUPPORT |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1577 | GPR_ASSERT(impl->alpn_protocol_list_length < UINT_MAX); |
| 1578 | if (SSL_CTX_set_alpn_protos( |
| 1579 | ssl_context, impl->alpn_protocol_list, |
Noah Eisen | be82e64 | 2018-02-09 09:16:55 -0800 | [diff] [blame] | 1580 | static_cast<unsigned int>(impl->alpn_protocol_list_length))) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1581 | gpr_log(GPR_ERROR, "Could not set alpn protocol list to context."); |
| 1582 | result = TSI_INVALID_ARGUMENT; |
| 1583 | break; |
| 1584 | } |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1585 | #endif /* TSI_OPENSSL_ALPN_SUPPORT */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1586 | SSL_CTX_set_next_proto_select_cb( |
| 1587 | ssl_context, client_handshaker_factory_npn_callback, impl); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1588 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1589 | } while (0); |
| 1590 | if (result != TSI_OK) { |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1591 | tsi_ssl_handshaker_factory_unref(&impl->base); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1592 | return result; |
| 1593 | } |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1594 | SSL_CTX_set_verify(ssl_context, SSL_VERIFY_PEER, nullptr); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1595 | /* TODO(jboeuf): Add revocation verification. */ |
| 1596 | |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1597 | *factory = impl; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1598 | return TSI_OK; |
| 1599 | } |
| 1600 | |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1601 | static tsi_ssl_handshaker_factory_vtable server_handshaker_factory_vtable = { |
| 1602 | tsi_ssl_server_handshaker_factory_destroy}; |
| 1603 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1604 | tsi_result tsi_create_ssl_server_handshaker_factory( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1605 | const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs, |
| 1606 | size_t num_key_cert_pairs, const char* pem_client_root_certs, |
| 1607 | int force_client_auth, const char* cipher_suites, |
| 1608 | const char** alpn_protocols, uint16_t num_alpn_protocols, |
| 1609 | tsi_ssl_server_handshaker_factory** factory) { |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1610 | return tsi_create_ssl_server_handshaker_factory_ex( |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 1611 | pem_key_cert_pairs, num_key_cert_pairs, pem_client_root_certs, |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1612 | force_client_auth ? TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY |
| 1613 | : TSI_DONT_REQUEST_CLIENT_CERTIFICATE, |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 1614 | cipher_suites, alpn_protocols, num_alpn_protocols, factory); |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1615 | } |
| 1616 | |
| 1617 | tsi_result tsi_create_ssl_server_handshaker_factory_ex( |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1618 | const tsi_ssl_pem_key_cert_pair* pem_key_cert_pairs, |
| 1619 | size_t num_key_cert_pairs, const char* pem_client_root_certs, |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1620 | tsi_client_certificate_request_type client_certificate_request, |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1621 | const char* cipher_suites, const char** alpn_protocols, |
| 1622 | uint16_t num_alpn_protocols, tsi_ssl_server_handshaker_factory** factory) { |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1623 | tsi_ssl_server_handshaker_options options; |
| 1624 | memset(&options, 0, sizeof(options)); |
| 1625 | options.pem_key_cert_pairs = pem_key_cert_pairs; |
| 1626 | options.num_key_cert_pairs = num_key_cert_pairs; |
| 1627 | options.pem_client_root_certs = pem_client_root_certs; |
| 1628 | options.client_certificate_request = client_certificate_request; |
| 1629 | options.cipher_suites = cipher_suites; |
| 1630 | options.alpn_protocols = alpn_protocols; |
| 1631 | options.num_alpn_protocols = num_alpn_protocols; |
| 1632 | return tsi_create_ssl_server_handshaker_factory_with_options(&options, |
| 1633 | factory); |
| 1634 | } |
| 1635 | |
| 1636 | tsi_result tsi_create_ssl_server_handshaker_factory_with_options( |
| 1637 | const tsi_ssl_server_handshaker_options* options, |
| 1638 | tsi_ssl_server_handshaker_factory** factory) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1639 | tsi_ssl_server_handshaker_factory* impl = nullptr; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1640 | tsi_result result = TSI_OK; |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 1641 | size_t i = 0; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1642 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1643 | gpr_once_init(&g_init_openssl_once, init_openssl); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1644 | |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1645 | if (factory == nullptr) return TSI_INVALID_ARGUMENT; |
| 1646 | *factory = nullptr; |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1647 | if (options->num_key_cert_pairs == 0 || |
| 1648 | options->pem_key_cert_pairs == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1649 | return TSI_INVALID_ARGUMENT; |
| 1650 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1651 | |
Noah Eisen | 4d20a66 | 2018-02-09 09:34:04 -0800 | [diff] [blame] | 1652 | impl = static_cast<tsi_ssl_server_handshaker_factory*>( |
| 1653 | gpr_zalloc(sizeof(*impl))); |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1654 | tsi_ssl_handshaker_factory_init(&impl->base); |
| 1655 | impl->base.vtable = &server_handshaker_factory_vtable; |
| 1656 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1657 | impl->ssl_contexts = static_cast<SSL_CTX**>( |
| 1658 | gpr_zalloc(options->num_key_cert_pairs * sizeof(SSL_CTX*))); |
| 1659 | impl->ssl_context_x509_subject_names = static_cast<tsi_peer*>( |
| 1660 | gpr_zalloc(options->num_key_cert_pairs * sizeof(tsi_peer))); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1661 | if (impl->ssl_contexts == nullptr || |
| 1662 | impl->ssl_context_x509_subject_names == nullptr) { |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1663 | tsi_ssl_handshaker_factory_unref(&impl->base); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1664 | return TSI_OUT_OF_RESOURCES; |
| 1665 | } |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1666 | impl->ssl_context_count = options->num_key_cert_pairs; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1667 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1668 | if (options->num_alpn_protocols > 0) { |
| 1669 | result = build_alpn_protocol_name_list( |
| 1670 | options->alpn_protocols, options->num_alpn_protocols, |
| 1671 | &impl->alpn_protocol_list, &impl->alpn_protocol_list_length); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1672 | if (result != TSI_OK) { |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1673 | tsi_ssl_handshaker_factory_unref(&impl->base); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1674 | return result; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1675 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1676 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1677 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1678 | for (i = 0; i < options->num_key_cert_pairs; i++) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1679 | do { |
| 1680 | impl->ssl_contexts[i] = SSL_CTX_new(TLSv1_2_method()); |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1681 | if (impl->ssl_contexts[i] == nullptr) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1682 | gpr_log(GPR_ERROR, "Could not create ssl context."); |
| 1683 | result = TSI_OUT_OF_RESOURCES; |
| 1684 | break; |
| 1685 | } |
Julien Boeuf | b71ef65 | 2017-04-12 21:44:49 -0700 | [diff] [blame] | 1686 | result = populate_ssl_context(impl->ssl_contexts[i], |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1687 | &options->pem_key_cert_pairs[i], |
| 1688 | options->cipher_suites); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1689 | if (result != TSI_OK) break; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1690 | |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1691 | // TODO(elessar): Provide ability to disable session ticket keys. |
| 1692 | |
| 1693 | // Allow client cache sessions (it's needed for OpenSSL only). |
| 1694 | int set_sid_ctx_result = SSL_CTX_set_session_id_context( |
| 1695 | impl->ssl_contexts[i], kSslSessionIdContext, |
| 1696 | GPR_ARRAY_SIZE(kSslSessionIdContext)); |
| 1697 | if (set_sid_ctx_result == 0) { |
| 1698 | gpr_log(GPR_ERROR, "Failed to set session id context."); |
| 1699 | result = TSI_INTERNAL_ERROR; |
| 1700 | break; |
| 1701 | } |
| 1702 | |
| 1703 | if (options->session_ticket_key != nullptr) { |
| 1704 | if (SSL_CTX_set_tlsext_ticket_keys( |
| 1705 | impl->ssl_contexts[i], |
| 1706 | const_cast<char*>(options->session_ticket_key), |
| 1707 | options->session_ticket_key_size) == 0) { |
| 1708 | gpr_log(GPR_ERROR, "Invalid STEK size."); |
| 1709 | result = TSI_INVALID_ARGUMENT; |
| 1710 | break; |
| 1711 | } |
| 1712 | } |
| 1713 | |
| 1714 | if (options->pem_client_root_certs != nullptr) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1715 | STACK_OF(X509_NAME)* root_names = nullptr; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1716 | result = ssl_ctx_load_verification_certs( |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1717 | impl->ssl_contexts[i], options->pem_client_root_certs, |
| 1718 | strlen(options->pem_client_root_certs), &root_names); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1719 | if (result != TSI_OK) { |
| 1720 | gpr_log(GPR_ERROR, "Invalid verification certs."); |
| 1721 | break; |
| 1722 | } |
| 1723 | SSL_CTX_set_client_CA_list(impl->ssl_contexts[i], root_names); |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1724 | switch (options->client_certificate_request) { |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1725 | case TSI_DONT_REQUEST_CLIENT_CERTIFICATE: |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1726 | SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_NONE, nullptr); |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1727 | break; |
| 1728 | case TSI_REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: |
| 1729 | SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, |
| 1730 | NullVerifyCallback); |
| 1731 | break; |
| 1732 | case TSI_REQUEST_CLIENT_CERTIFICATE_AND_VERIFY: |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1733 | SSL_CTX_set_verify(impl->ssl_contexts[i], SSL_VERIFY_PEER, nullptr); |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1734 | break; |
| 1735 | case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY: |
| 1736 | SSL_CTX_set_verify( |
| 1737 | impl->ssl_contexts[i], |
| 1738 | SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, |
| 1739 | NullVerifyCallback); |
| 1740 | break; |
| 1741 | case TSI_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY: |
| 1742 | SSL_CTX_set_verify( |
| 1743 | impl->ssl_contexts[i], |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1744 | SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); |
Deepak Lukose | dba4c5f | 2016-03-25 12:54:25 -0700 | [diff] [blame] | 1745 | break; |
| 1746 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1747 | /* TODO(jboeuf): Add revocation verification. */ |
| 1748 | } |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1749 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1750 | result = extract_x509_subject_names_from_pem_cert( |
Ruslan Nigmatullin | 7ae3733 | 2018-02-21 16:44:35 -0800 | [diff] [blame] | 1751 | options->pem_key_cert_pairs[i].cert_chain, |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1752 | &impl->ssl_context_x509_subject_names[i]); |
| 1753 | if (result != TSI_OK) break; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1754 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1755 | SSL_CTX_set_tlsext_servername_callback( |
| 1756 | impl->ssl_contexts[i], |
| 1757 | ssl_server_handshaker_factory_servername_callback); |
| 1758 | SSL_CTX_set_tlsext_servername_arg(impl->ssl_contexts[i], impl); |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1759 | #if TSI_OPENSSL_ALPN_SUPPORT |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1760 | SSL_CTX_set_alpn_select_cb(impl->ssl_contexts[i], |
| 1761 | server_handshaker_factory_alpn_callback, impl); |
Julien Boeuf | d153132 | 2015-06-18 11:05:39 +0200 | [diff] [blame] | 1762 | #endif /* TSI_OPENSSL_ALPN_SUPPORT */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1763 | SSL_CTX_set_next_protos_advertised_cb( |
| 1764 | impl->ssl_contexts[i], |
| 1765 | server_handshaker_factory_npn_advertised_callback, impl); |
| 1766 | } while (0); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1767 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1768 | if (result != TSI_OK) { |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1769 | tsi_ssl_handshaker_factory_unref(&impl->base); |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1770 | return result; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1771 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1772 | } |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1773 | |
Julien Boeuf | 935d02e | 2017-04-09 00:07:09 -0700 | [diff] [blame] | 1774 | *factory = impl; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1775 | return TSI_OK; |
| 1776 | } |
| 1777 | |
| 1778 | /* --- tsi_ssl utils. --- */ |
| 1779 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1780 | int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { |
Julien Boeuf | b222b4d | 2015-01-15 17:01:39 -0800 | [diff] [blame] | 1781 | size_t i = 0; |
Julien Boeuf | 597a4f2 | 2015-02-23 15:57:14 -0800 | [diff] [blame] | 1782 | size_t san_count = 0; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1783 | const tsi_peer_property* cn_property = nullptr; |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 1784 | int like_ip = looks_like_ip_address(name); |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1785 | |
Julien Boeuf | 597a4f2 | 2015-02-23 15:57:14 -0800 | [diff] [blame] | 1786 | /* Check the SAN first. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1787 | for (i = 0; i < peer->property_count; i++) { |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1788 | const tsi_peer_property* property = &peer->properties[i]; |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1789 | if (property->name == nullptr) continue; |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1790 | if (strcmp(property->name, |
| 1791 | TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) == 0) { |
| 1792 | san_count++; |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 1793 | |
| 1794 | if (!like_ip && does_entry_match_name(property->value.data, |
| 1795 | property->value.length, name)) { |
| 1796 | return 1; |
| 1797 | } else if (like_ip && |
| 1798 | strncmp(name, property->value.data, property->value.length) == |
| 1799 | 0 && |
| 1800 | strlen(name) == property->value.length) { |
| 1801 | /* IP Addresses are exact matches only. */ |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1802 | return 1; |
| 1803 | } |
| 1804 | } else if (strcmp(property->name, |
| 1805 | TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY) == 0) { |
| 1806 | cn_property = property; |
Nicolas Noble | b7ebd3b | 2014-11-26 16:33:03 -0800 | [diff] [blame] | 1807 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1808 | } |
Julien Boeuf | 597a4f2 | 2015-02-23 15:57:14 -0800 | [diff] [blame] | 1809 | |
Paul Querna | 47d841d | 2016-03-10 11:19:17 -0800 | [diff] [blame] | 1810 | /* If there's no SAN, try the CN, but only if its not like an IP Address */ |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1811 | if (san_count == 0 && cn_property != nullptr && !like_ip) { |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1812 | if (does_entry_match_name(cn_property->value.data, |
| 1813 | cn_property->value.length, name)) { |
| 1814 | return 1; |
Julien Boeuf | 597a4f2 | 2015-02-23 15:57:14 -0800 | [diff] [blame] | 1815 | } |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1816 | } |
Julien Boeuf | 597a4f2 | 2015-02-23 15:57:14 -0800 | [diff] [blame] | 1817 | |
Craig Tiller | a82950e | 2015-09-22 12:33:20 -0700 | [diff] [blame] | 1818 | return 0; /* Not found. */ |
Craig Tiller | 190d360 | 2015-02-18 09:23:38 -0800 | [diff] [blame] | 1819 | } |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1820 | |
| 1821 | /* --- Testing support. --- */ |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1822 | const tsi_ssl_handshaker_factory_vtable* tsi_ssl_handshaker_factory_swap_vtable( |
| 1823 | tsi_ssl_handshaker_factory* factory, |
| 1824 | tsi_ssl_handshaker_factory_vtable* new_vtable) { |
Craig Tiller | 4782d92 | 2017-11-10 09:53:21 -0800 | [diff] [blame] | 1825 | GPR_ASSERT(factory != nullptr); |
| 1826 | GPR_ASSERT(factory->vtable != nullptr); |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1827 | |
Craig Tiller | baa14a9 | 2017-11-03 09:09:36 -0700 | [diff] [blame] | 1828 | const tsi_ssl_handshaker_factory_vtable* orig_vtable = factory->vtable; |
Justin Burke | 4984135 | 2017-08-31 17:42:54 -0700 | [diff] [blame] | 1829 | factory->vtable = new_vtable; |
| 1830 | return orig_vtable; |
| 1831 | } |