| 1 | /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. |
| 2 | Copyright (c) 2011, 2016, MariaDB |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or modify |
| 5 | it under the terms of the GNU General Public License as published by |
| 6 | the Free Software Foundation; version 2 of the License. |
| 7 | |
| 8 | This program is distributed in the hope that it will be useful, |
| 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | GNU General Public License for more details. |
| 12 | |
| 13 | You should have received a copy of the GNU General Public License |
| 14 | along with this program; if not, write to the Free Software |
| 15 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
| 16 | |
| 17 | #include "vio_priv.h" |
| 18 | #include <ssl_compat.h> |
| 19 | |
| 20 | #ifdef HAVE_OPENSSL |
| 21 | #ifndef HAVE_YASSL |
| 22 | #include <openssl/dh.h> |
| 23 | #include <openssl/bn.h> |
| 24 | #endif |
| 25 | |
| 26 | static my_bool ssl_algorithms_added = FALSE; |
| 27 | static my_bool ssl_error_strings_loaded= FALSE; |
| 28 | |
| 29 | /* the function below was generated with "openssl dhparam -2 -C 2048" */ |
| 30 | |
| 31 | static |
| 32 | DH *get_dh2048() |
| 33 | { |
| 34 | static unsigned char dhp_2048[] = { |
| 35 | 0xA1,0xBB,0x7C,0x20,0xC5,0x5B,0xC0,0x7B,0x21,0x8B,0xD6,0xA8, |
| 36 | 0x15,0xFC,0x3B,0xBA,0xAB,0x9F,0xDF,0x68,0xC4,0x79,0x78,0x0D, |
| 37 | 0xC1,0x12,0x64,0xE4,0x15,0xC9,0x66,0xDB,0xF6,0xCB,0xB3,0x39, |
| 38 | 0x02,0x5B,0x78,0x62,0xFB,0x09,0xAE,0x09,0x6B,0xDD,0xD4,0x5D, |
| 39 | 0x97,0xBC,0xDC,0x7F,0xE6,0xD6,0xF1,0xCB,0xF5,0xEB,0xDA,0xA7, |
| 40 | 0x2E,0x5A,0x43,0x2B,0xE9,0x40,0xE2,0x85,0x00,0x1C,0xC0,0x0A, |
| 41 | 0x98,0x77,0xA9,0x31,0xDE,0x0B,0x75,0x4D,0x1E,0x1F,0x16,0x83, |
| 42 | 0xCA,0xDE,0xBD,0x21,0xFC,0xC1,0x82,0x37,0x36,0x33,0x0B,0x66, |
| 43 | 0x06,0x3C,0xF3,0xAF,0x21,0x57,0x57,0x80,0xF6,0x94,0x1B,0xA9, |
| 44 | 0xD4,0xF6,0x8F,0x18,0x62,0x0E,0xC4,0x22,0xF9,0x5B,0x62,0xCC, |
| 45 | 0x3F,0x19,0x95,0xCF,0x4B,0x00,0xA6,0x6C,0x0B,0xAF,0x9F,0xD5, |
| 46 | 0xFA,0x3D,0x6D,0xDA,0x30,0x83,0x07,0x91,0xAC,0x15,0xFF,0x8F, |
| 47 | 0x59,0x54,0xEA,0x25,0xBC,0x4E,0xEB,0x6A,0x54,0xDF,0x75,0x09, |
| 48 | 0x72,0x0F,0xEF,0x23,0x70,0xE0,0xA8,0x04,0xEA,0xFF,0x90,0x54, |
| 49 | 0xCD,0x84,0x18,0xC0,0x75,0x91,0x99,0x0F,0xA1,0x78,0x0C,0x07, |
| 50 | 0xB7,0xC5,0xDE,0x55,0x06,0x7B,0x95,0x68,0x2C,0x33,0x39,0xBC, |
| 51 | 0x2C,0xD0,0x6D,0xDD,0xFA,0xDC,0xB5,0x8F,0x82,0x39,0xF8,0x67, |
| 52 | 0x44,0xF1,0xD8,0xF7,0x78,0x11,0x9A,0x77,0x9B,0x53,0x47,0xD6, |
| 53 | 0x2B,0x5D,0x67,0xB8,0xB7,0xBC,0xC1,0xD7,0x79,0x62,0x15,0xC2, |
| 54 | 0xC5,0x83,0x97,0xA7,0xF8,0xB4,0x9C,0xF6,0x8F,0x9A,0xC7,0xDA, |
| 55 | 0x1B,0xBB,0x87,0x07,0xA7,0x71,0xAD,0xB2,0x8A,0x50,0xF8,0x26, |
| 56 | 0x12,0xB7,0x3E,0x0B, |
| 57 | }; |
| 58 | static unsigned char dhg_2048[] = { |
| 59 | 0x02 |
| 60 | }; |
| 61 | DH *dh = DH_new(); |
| 62 | BIGNUM *dhp_bn, *dhg_bn; |
| 63 | |
| 64 | if (dh == NULL) |
| 65 | return NULL; |
| 66 | dhp_bn = BN_bin2bn(dhp_2048, sizeof (dhp_2048), NULL); |
| 67 | dhg_bn = BN_bin2bn(dhg_2048, sizeof (dhg_2048), NULL); |
| 68 | if (dhp_bn == NULL || dhg_bn == NULL |
| 69 | || !DH_set0_pqg(dh, dhp_bn, NULL, dhg_bn)) { |
| 70 | DH_free(dh); |
| 71 | BN_free(dhp_bn); |
| 72 | BN_free(dhg_bn); |
| 73 | return NULL; |
| 74 | } |
| 75 | return dh; |
| 76 | } |
| 77 | |
| 78 | static const char* |
| 79 | ssl_error_string[] = |
| 80 | { |
| 81 | "No error" , |
| 82 | "Unable to get certificate" , |
| 83 | "Unable to get private key" , |
| 84 | "Private key does not match the certificate public key" , |
| 85 | "SSL_CTX_set_default_verify_paths failed" , |
| 86 | "Failed to set ciphers to use" , |
| 87 | "SSL_CTX_new failed" , |
| 88 | "SSL_CTX_set_tmp_dh failed" |
| 89 | }; |
| 90 | |
| 91 | const char* |
| 92 | sslGetErrString(enum enum_ssl_init_error e) |
| 93 | { |
| 94 | DBUG_ASSERT(SSL_INITERR_NOERROR < e && e < SSL_INITERR_LASTERR); |
| 95 | return ssl_error_string[e]; |
| 96 | } |
| 97 | |
| 98 | static int |
| 99 | vio_set_cert_stuff(SSL_CTX *ctx, const char *cert_file, const char *key_file, |
| 100 | enum enum_ssl_init_error* error) |
| 101 | { |
| 102 | DBUG_ENTER("vio_set_cert_stuff" ); |
| 103 | DBUG_PRINT("enter" , ("ctx: %p cert_file: %s key_file: %s" , |
| 104 | ctx, cert_file, key_file)); |
| 105 | |
| 106 | if (!cert_file && key_file) |
| 107 | cert_file= key_file; |
| 108 | |
| 109 | if (!key_file && cert_file) |
| 110 | key_file= cert_file; |
| 111 | |
| 112 | if (cert_file && |
| 113 | SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0) |
| 114 | { |
| 115 | *error= SSL_INITERR_CERT; |
| 116 | DBUG_PRINT("error" ,("%s from file '%s'" , sslGetErrString(*error), cert_file)); |
| 117 | DBUG_EXECUTE("error" , ERR_print_errors_fp(DBUG_FILE);); |
| 118 | fprintf(stderr, "SSL error: %s from '%s'\n" , sslGetErrString(*error), |
| 119 | cert_file); |
| 120 | fflush(stderr); |
| 121 | DBUG_RETURN(1); |
| 122 | } |
| 123 | |
| 124 | if (key_file && |
| 125 | SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) |
| 126 | { |
| 127 | *error= SSL_INITERR_KEY; |
| 128 | DBUG_PRINT("error" , ("%s from file '%s'" , sslGetErrString(*error), key_file)); |
| 129 | DBUG_EXECUTE("error" , ERR_print_errors_fp(DBUG_FILE);); |
| 130 | fprintf(stderr, "SSL error: %s from '%s'\n" , sslGetErrString(*error), |
| 131 | key_file); |
| 132 | fflush(stderr); |
| 133 | DBUG_RETURN(1); |
| 134 | } |
| 135 | |
| 136 | /* |
| 137 | If we are using DSA, we can copy the parameters from the private key |
| 138 | Now we know that a key and cert have been set against the SSL context |
| 139 | */ |
| 140 | if (cert_file && !SSL_CTX_check_private_key(ctx)) |
| 141 | { |
| 142 | *error= SSL_INITERR_NOMATCH; |
| 143 | DBUG_PRINT("error" , ("%s" ,sslGetErrString(*error))); |
| 144 | DBUG_EXECUTE("error" , ERR_print_errors_fp(DBUG_FILE);); |
| 145 | fprintf(stderr, "SSL error: %s\n" , sslGetErrString(*error)); |
| 146 | fflush(stderr); |
| 147 | DBUG_RETURN(1); |
| 148 | } |
| 149 | |
| 150 | DBUG_RETURN(0); |
| 151 | } |
| 152 | |
| 153 | |
| 154 | static void check_ssl_init() |
| 155 | { |
| 156 | if (!ssl_algorithms_added) |
| 157 | { |
| 158 | ssl_algorithms_added= TRUE; |
| 159 | OPENSSL_init_ssl(0, NULL); |
| 160 | } |
| 161 | |
| 162 | if (!ssl_error_strings_loaded) |
| 163 | { |
| 164 | ssl_error_strings_loaded= TRUE; |
| 165 | SSL_load_error_strings(); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | /************************ VioSSLFd **********************************/ |
| 170 | static struct st_VioSSLFd * |
| 171 | new_VioSSLFd(const char *key_file, const char *cert_file, |
| 172 | const char *ca_file, const char *ca_path, |
| 173 | const char *cipher, my_bool is_client_method, |
| 174 | enum enum_ssl_init_error *error, |
| 175 | const char *crl_file, const char *crl_path) |
| 176 | { |
| 177 | DH *dh; |
| 178 | struct st_VioSSLFd *ssl_fd; |
| 179 | long ssl_ctx_options= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; |
| 180 | DBUG_ENTER("new_VioSSLFd" ); |
| 181 | DBUG_PRINT("enter" , |
| 182 | ("key_file: '%s' cert_file: '%s' ca_file: '%s' ca_path: '%s' " |
| 183 | "cipher: '%s' crl_file: '%s' crl_path: '%s' " , |
| 184 | key_file ? key_file : "NULL" , |
| 185 | cert_file ? cert_file : "NULL" , |
| 186 | ca_file ? ca_file : "NULL" , |
| 187 | ca_path ? ca_path : "NULL" , |
| 188 | cipher ? cipher : "NULL" , |
| 189 | crl_file ? crl_file : "NULL" , |
| 190 | crl_path ? crl_path : "NULL" )); |
| 191 | |
| 192 | check_ssl_init(); |
| 193 | |
| 194 | if (!(ssl_fd= ((struct st_VioSSLFd*) |
| 195 | my_malloc(sizeof(struct st_VioSSLFd),MYF(0))))) |
| 196 | goto err0; |
| 197 | if (!(ssl_fd->ssl_context= SSL_CTX_new(is_client_method ? |
| 198 | SSLv23_client_method() : |
| 199 | SSLv23_server_method()))) |
| 200 | { |
| 201 | *error= SSL_INITERR_MEMFAIL; |
| 202 | DBUG_PRINT("error" , ("%s" , sslGetErrString(*error))); |
| 203 | goto err1; |
| 204 | } |
| 205 | |
| 206 | SSL_CTX_set_options(ssl_fd->ssl_context, ssl_ctx_options); |
| 207 | |
| 208 | /* |
| 209 | Set the ciphers that can be used |
| 210 | NOTE: SSL_CTX_set_cipher_list will return 0 if |
| 211 | none of the provided ciphers could be selected |
| 212 | */ |
| 213 | if (cipher && |
| 214 | SSL_CTX_set_cipher_list(ssl_fd->ssl_context, cipher) == 0) |
| 215 | { |
| 216 | *error= SSL_INITERR_CIPHERS; |
| 217 | DBUG_PRINT("error" , ("%s" , sslGetErrString(*error))); |
| 218 | goto err2; |
| 219 | } |
| 220 | |
| 221 | /* Load certs from the trusted ca */ |
| 222 | if (SSL_CTX_load_verify_locations(ssl_fd->ssl_context, ca_file, ca_path) <= 0) |
| 223 | { |
| 224 | DBUG_PRINT("warning" , ("SSL_CTX_load_verify_locations failed" )); |
| 225 | if (ca_file || ca_path) |
| 226 | { |
| 227 | /* fail only if ca file or ca path were supplied and looking into |
| 228 | them fails. */ |
| 229 | *error= SSL_INITERR_BAD_PATHS; |
| 230 | DBUG_PRINT("error" , ("SSL_CTX_load_verify_locations failed : %s" , |
| 231 | sslGetErrString(*error))); |
| 232 | goto err2; |
| 233 | } |
| 234 | |
| 235 | /* otherwise go use the defaults */ |
| 236 | if (SSL_CTX_set_default_verify_paths(ssl_fd->ssl_context) == 0) |
| 237 | { |
| 238 | *error= SSL_INITERR_BAD_PATHS; |
| 239 | DBUG_PRINT("error" , ("%s" , sslGetErrString(*error))); |
| 240 | goto err2; |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | if (crl_file || crl_path) |
| 245 | { |
| 246 | #ifdef HAVE_YASSL |
| 247 | DBUG_PRINT("warning" , ("yaSSL doesn't support CRL" )); |
| 248 | DBUG_ASSERT(0); |
| 249 | #else |
| 250 | X509_STORE *store= SSL_CTX_get_cert_store(ssl_fd->ssl_context); |
| 251 | /* Load crls from the trusted ca */ |
| 252 | if (X509_STORE_load_locations(store, crl_file, crl_path) == 0 || |
| 253 | X509_STORE_set_flags(store, |
| 254 | X509_V_FLAG_CRL_CHECK | |
| 255 | X509_V_FLAG_CRL_CHECK_ALL) == 0) |
| 256 | { |
| 257 | DBUG_PRINT("warning" , ("X509_STORE_load_locations for CRL failed" )); |
| 258 | *error= SSL_INITERR_BAD_PATHS; |
| 259 | DBUG_PRINT("error" , ("%s" , sslGetErrString(*error))); |
| 260 | goto err2; |
| 261 | } |
| 262 | #endif |
| 263 | } |
| 264 | |
| 265 | if (vio_set_cert_stuff(ssl_fd->ssl_context, cert_file, key_file, error)) |
| 266 | { |
| 267 | DBUG_PRINT("error" , ("vio_set_cert_stuff failed" )); |
| 268 | goto err2; |
| 269 | } |
| 270 | |
| 271 | /* DH stuff */ |
| 272 | if (!is_client_method) |
| 273 | { |
| 274 | dh=get_dh2048(); |
| 275 | if (!SSL_CTX_set_tmp_dh(ssl_fd->ssl_context, dh)) |
| 276 | { |
| 277 | *error= SSL_INITERR_DH; |
| 278 | goto err3; |
| 279 | } |
| 280 | |
| 281 | DH_free(dh); |
| 282 | } |
| 283 | |
| 284 | DBUG_PRINT("exit" , ("OK 1" )); |
| 285 | |
| 286 | DBUG_RETURN(ssl_fd); |
| 287 | |
| 288 | err3: |
| 289 | DH_free(dh); |
| 290 | err2: |
| 291 | SSL_CTX_free(ssl_fd->ssl_context); |
| 292 | err1: |
| 293 | my_free(ssl_fd); |
| 294 | err0: |
| 295 | DBUG_EXECUTE("error" , ERR_print_errors_fp(DBUG_FILE);); |
| 296 | DBUG_RETURN(0); |
| 297 | } |
| 298 | |
| 299 | |
| 300 | /************************ VioSSLConnectorFd **********************************/ |
| 301 | struct st_VioSSLFd * |
| 302 | new_VioSSLConnectorFd(const char *key_file, const char *cert_file, |
| 303 | const char *ca_file, const char *ca_path, |
| 304 | const char *cipher, enum enum_ssl_init_error* error, |
| 305 | const char *crl_file, const char *crl_path) |
| 306 | { |
| 307 | struct st_VioSSLFd *ssl_fd; |
| 308 | int verify= SSL_VERIFY_PEER; |
| 309 | |
| 310 | /* |
| 311 | Turn off verification of servers certificate if both |
| 312 | ca_file and ca_path is set to NULL |
| 313 | */ |
| 314 | if (ca_file == 0 && ca_path == 0) |
| 315 | verify= SSL_VERIFY_NONE; |
| 316 | |
| 317 | if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, |
| 318 | ca_path, cipher, TRUE, error, |
| 319 | crl_file, crl_path))) |
| 320 | { |
| 321 | return 0; |
| 322 | } |
| 323 | |
| 324 | /* Init the VioSSLFd as a "connector" ie. the client side */ |
| 325 | |
| 326 | SSL_CTX_set_verify(ssl_fd->ssl_context, verify, NULL); |
| 327 | |
| 328 | return ssl_fd; |
| 329 | } |
| 330 | |
| 331 | |
| 332 | /************************ VioSSLAcceptorFd **********************************/ |
| 333 | struct st_VioSSLFd * |
| 334 | new_VioSSLAcceptorFd(const char *key_file, const char *cert_file, |
| 335 | const char *ca_file, const char *ca_path, |
| 336 | const char *cipher, enum enum_ssl_init_error* error, |
| 337 | const char *crl_file, const char *crl_path) |
| 338 | { |
| 339 | struct st_VioSSLFd *ssl_fd; |
| 340 | int verify= SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE; |
| 341 | if (!(ssl_fd= new_VioSSLFd(key_file, cert_file, ca_file, |
| 342 | ca_path, cipher, FALSE, error, |
| 343 | crl_file, crl_path))) |
| 344 | { |
| 345 | return 0; |
| 346 | } |
| 347 | /* Init the the VioSSLFd as a "acceptor" ie. the server side */ |
| 348 | |
| 349 | /* Set max number of cached sessions, returns the previous size */ |
| 350 | SSL_CTX_sess_set_cache_size(ssl_fd->ssl_context, 128); |
| 351 | |
| 352 | SSL_CTX_set_verify(ssl_fd->ssl_context, verify, NULL); |
| 353 | |
| 354 | /* |
| 355 | Set session_id - an identifier for this server session |
| 356 | Use the ssl_fd pointer |
| 357 | */ |
| 358 | SSL_CTX_set_session_id_context(ssl_fd->ssl_context, |
| 359 | (const unsigned char *)ssl_fd, |
| 360 | sizeof(ssl_fd)); |
| 361 | |
| 362 | return ssl_fd; |
| 363 | } |
| 364 | |
| 365 | void free_vio_ssl_acceptor_fd(struct st_VioSSLFd *fd) |
| 366 | { |
| 367 | SSL_CTX_free(fd->ssl_context); |
| 368 | my_free(fd); |
| 369 | } |
| 370 | #endif /* HAVE_OPENSSL */ |
| 371 | |