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 | |