]>
Commit | Line | Data |
---|---|---|
f484cdf5 | 1 | |
2 | /* | |
673cd7db | 3 | * $Id: ssl_support.cc,v 1.11 2003/02/08 14:55:07 hno Exp $ |
f484cdf5 | 4 | * |
5 | * AUTHOR: Benno Rice | |
27d8545c | 6 | * DEBUG: section 83 SSL accelerator support |
f484cdf5 | 7 | * |
8 | * SQUID Internet Object Cache http://squid.nlanr.net/Squid/ | |
9 | * ---------------------------------------------------------- | |
10 | * | |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
13 | * National Laboratory for Applied Network Research and funded by the | |
14 | * National Science Foundation. Squid is Copyrighted (C) 1998 by | |
15 | * Duane Wessels and the University of California San Diego. Please | |
16 | * see the COPYRIGHT file for full details. Squid incorporates | |
17 | * software developed and/or copyrighted by other sources. Please see | |
18 | * the CREDITS file for full details. | |
19 | * | |
20 | * This program is free software; you can redistribute it and/or modify | |
21 | * it under the terms of the GNU General Public License as published by | |
22 | * the Free Software Foundation; either version 2 of the License, or | |
23 | * (at your option) any later version. | |
24 | * | |
25 | * This program is distributed in the hope that it will be useful, | |
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
28 | * GNU General Public License for more details. | |
29 | * | |
30 | * You should have received a copy of the GNU General Public License | |
31 | * along with this program; if not, write to the Free Software | |
32 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
33 | * | |
34 | */ | |
35 | ||
36 | #include "squid.h" | |
528b2c61 | 37 | #include "fde.h" |
f484cdf5 | 38 | |
39 | extern int commUnsetNonBlocking(int fd); | |
40 | extern int commSetNonBlocking(int fd); | |
41 | ||
42 | void clientNegotiateSSL(int fd, void *data); | |
43 | void clientReadSSLRequest(int fd, void *data); | |
f484cdf5 | 44 | |
45 | static RSA * | |
e6ccf245 | 46 | ssl_temp_rsa_cb(SSL * ssl, int anInt, int keylen) |
f484cdf5 | 47 | { |
48 | static RSA *rsa = NULL; | |
49 | ||
50 | if (rsa == NULL) | |
51 | rsa = RSA_generate_key(512, RSA_F4, NULL, NULL); | |
52 | return rsa; | |
53 | } | |
54 | ||
55 | static int | |
56 | ssl_verify_cb(int ok, X509_STORE_CTX * ctx) | |
57 | { | |
58 | char buffer[256]; | |
a7ad6e4e | 59 | SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); |
60 | SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl); | |
61 | const char *server = (const char *)SSL_get_ex_data(ssl, ssl_ex_index_server); | |
62 | void *dont_verify_domain = SSL_CTX_get_ex_data(sslctx, ssl_ctx_ex_index_dont_verify_domain); | |
63 | X509 *peer_cert = ctx->cert; | |
f484cdf5 | 64 | |
a7ad6e4e | 65 | X509_NAME_oneline(X509_get_subject_name(peer_cert), buffer, |
f484cdf5 | 66 | sizeof(buffer)); |
a7ad6e4e | 67 | |
68 | if (ok) { | |
69 | debug(83, 5) ("SSL Certificate signature OK: %s\n", buffer); | |
70 | if (server) { | |
71 | int i; | |
72 | int found = 0; | |
73 | char cn[1024]; | |
74 | X509_NAME *name = X509_get_subject_name(peer_cert); | |
75 | debug(83, 3) ("Verifying server domain %s to certificate dn %s\n", | |
76 | server, buffer); | |
77 | for (i = X509_NAME_get_index_by_NID(name, NID_commonName, -1); i >= 0; i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) { | |
78 | ASN1_STRING *data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); | |
79 | if (data->length > (int)sizeof(cn) - 1) | |
80 | continue; | |
81 | memcpy(cn, data->data, data->length); | |
82 | cn[data->length] = '\0'; | |
83 | debug(83, 4) ("Verifying server domain %s to certificate cn %s\n", | |
84 | server, cn); | |
85 | if (matchDomainName(server, cn[0] == '*' ? cn + 1 : cn) == 0) { | |
86 | found = 1; | |
87 | break; | |
88 | } | |
89 | } | |
90 | if (!found) { | |
91 | debug(83, 2) ("ERROR: Certificate %s does not match domainname %s\n", buffer, server); | |
92 | ok = 0; | |
93 | } | |
94 | } | |
95 | } else { | |
f484cdf5 | 96 | switch (ctx->error) { |
97 | case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: | |
27d8545c | 98 | debug(83, 5) ("SSL Certficate error: CA not known: %s\n", buffer); |
f484cdf5 | 99 | break; |
100 | case X509_V_ERR_CERT_NOT_YET_VALID: | |
27d8545c | 101 | debug(83, 5) ("SSL Certficate not yet valid: %s\n", buffer); |
f484cdf5 | 102 | break; |
103 | case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: | |
27d8545c | 104 | debug(83, 5) ("SSL Certificate has illegal \'not before\' field: %s\n", buffer); |
f484cdf5 | 105 | break; |
106 | case X509_V_ERR_CERT_HAS_EXPIRED: | |
27d8545c | 107 | debug(83, 5) ("SSL Certificate expired: %s\n", buffer); |
f484cdf5 | 108 | break; |
109 | case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: | |
27d8545c | 110 | debug(83, 5) ("SSL Certificate has invalid \'not after\' field: %s\n", buffer); |
f484cdf5 | 111 | break; |
112 | default: | |
a7ad6e4e | 113 | debug(83, 1) ("SSL unknown certificate error %d in %s\n", |
f484cdf5 | 114 | ctx->error, buffer); |
115 | break; | |
116 | } | |
117 | } | |
a7ad6e4e | 118 | if (!dont_verify_domain && server) { |
119 | } | |
f484cdf5 | 120 | return ok; |
121 | } | |
122 | ||
79d4ccdf | 123 | static struct ssl_option { |
124 | const char *name; | |
125 | long value; | |
126 | } ssl_options[] = { | |
127 | ||
673cd7db | 128 | #ifdef SSL_OP_MICROSOFT_SESS_ID_BUG |
79d4ccdf | 129 | { |
130 | "MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG | |
131 | }, | |
673cd7db | 132 | #endif |
133 | #ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG | |
79d4ccdf | 134 | { |
135 | "NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG | |
136 | }, | |
673cd7db | 137 | #endif |
138 | #ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG | |
79d4ccdf | 139 | { |
140 | "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG | |
141 | }, | |
673cd7db | 142 | #endif |
143 | #ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG | |
79d4ccdf | 144 | { |
145 | "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG | |
146 | }, | |
673cd7db | 147 | #endif |
148 | #ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER | |
79d4ccdf | 149 | { |
150 | "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER | |
151 | }, | |
673cd7db | 152 | #endif |
153 | #ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING | |
79d4ccdf | 154 | { |
155 | "MSIE_SSLV2_RSA_PADDING", SSL_OP_MSIE_SSLV2_RSA_PADDING | |
156 | }, | |
673cd7db | 157 | #endif |
158 | #ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG | |
79d4ccdf | 159 | { |
160 | "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG | |
161 | }, | |
673cd7db | 162 | #endif |
163 | #ifdef SSL_OP_TLS_D5_BUG | |
79d4ccdf | 164 | { |
165 | "TLS_D5_BUG", SSL_OP_TLS_D5_BUG | |
166 | }, | |
673cd7db | 167 | #endif |
168 | #ifdef SSL_OP_TLS_BLOCK_PADDING_BUG | |
79d4ccdf | 169 | { |
170 | "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG | |
171 | }, | |
673cd7db | 172 | #endif |
173 | #ifdef SSL_OP_TLS_ROLLBACK_BUG | |
79d4ccdf | 174 | { |
175 | "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG | |
176 | }, | |
673cd7db | 177 | #endif |
178 | #ifdef SSL_OP_ALL | |
179 | { | |
180 | "ALL", SSL_OP_ALL | |
181 | }, | |
182 | #endif | |
183 | #ifdef SSL_OP_SINGLE_DH_USE | |
79d4ccdf | 184 | { |
185 | "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE | |
186 | }, | |
673cd7db | 187 | #endif |
188 | #ifdef SSL_OP_EPHEMERAL_RSA | |
79d4ccdf | 189 | { |
190 | "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA | |
191 | }, | |
673cd7db | 192 | #endif |
193 | #ifdef SSL_OP_PKCS1_CHECK_1 | |
79d4ccdf | 194 | { |
195 | "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1 | |
196 | }, | |
673cd7db | 197 | #endif |
198 | #ifdef SSL_OP_PKCS1_CHECK_2 | |
79d4ccdf | 199 | { |
200 | "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2 | |
201 | }, | |
673cd7db | 202 | #endif |
203 | #ifdef SSL_OP_NETSCAPE_CA_DN_BUG | |
79d4ccdf | 204 | { |
205 | "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG | |
206 | }, | |
673cd7db | 207 | #endif |
208 | #ifdef SSL_OP_NON_EXPORT_FIRST | |
79d4ccdf | 209 | { |
210 | "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST | |
211 | }, | |
673cd7db | 212 | #endif |
213 | #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE | |
79d4ccdf | 214 | { |
673cd7db | 215 | "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE |
79d4ccdf | 216 | }, |
673cd7db | 217 | #endif |
218 | #ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG | |
79d4ccdf | 219 | { |
673cd7db | 220 | "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG |
79d4ccdf | 221 | }, |
673cd7db | 222 | #endif |
223 | #ifdef SSL_OP_NO_SSLv2 | |
79d4ccdf | 224 | { |
225 | "NO_SSLv2", SSL_OP_NO_SSLv2 | |
226 | }, | |
673cd7db | 227 | #endif |
228 | #ifdef SSL_OP_NO_SSLv3 | |
79d4ccdf | 229 | { |
230 | "NO_SSLv3", SSL_OP_NO_SSLv3 | |
231 | }, | |
673cd7db | 232 | #endif |
233 | #ifdef SSL_OP_NO_TLSv1 | |
79d4ccdf | 234 | { |
235 | "NO_TLSv1", SSL_OP_NO_TLSv1 | |
236 | }, | |
673cd7db | 237 | #endif |
79d4ccdf | 238 | { |
239 | "", 0 | |
240 | }, | |
241 | { | |
242 | NULL, 0 | |
243 | } | |
244 | }; | |
245 | ||
84f2d773 | 246 | static long |
79d4ccdf | 247 | ssl_parse_options(const char *options) |
248 | { | |
249 | long op = SSL_OP_ALL; | |
250 | char *tmp; | |
251 | char *option; | |
252 | ||
253 | if (!options) | |
254 | goto no_options; | |
255 | ||
256 | tmp = xstrdup(options); | |
257 | option = strtok(tmp, ":,"); | |
258 | while (option) { | |
259 | struct ssl_option *opt = NULL, *opttmp; | |
260 | long value = 0; | |
261 | enum { | |
262 | MODE_ADD, MODE_REMOVE | |
263 | } mode; | |
264 | switch (*option) { | |
265 | case '!': | |
266 | case '-': | |
267 | mode = MODE_REMOVE; | |
268 | option++; | |
269 | break; | |
270 | case '+': | |
271 | mode = MODE_ADD; | |
272 | option++; | |
273 | break; | |
274 | default: | |
275 | mode = MODE_ADD; | |
276 | break; | |
277 | } | |
278 | for (opttmp = ssl_options; opttmp->name; opttmp++) { | |
279 | if (strcmp(opttmp->name, option) == 0) { | |
280 | opt = opttmp; | |
281 | break; | |
282 | } | |
283 | } | |
284 | if (opt) | |
285 | value = opt->value; | |
286 | else if (strncmp(option, "0x", 2) == 0) { | |
287 | /* Special case.. hex specification */ | |
288 | value = strtol(option + 2, NULL, 16); | |
289 | } else { | |
290 | fatalf("Unknown SSL option '%s'", option); | |
291 | value = 0; /* Keep GCC happy */ | |
292 | } | |
293 | switch (mode) { | |
294 | case MODE_ADD: | |
295 | op |= value; | |
296 | break; | |
297 | case MODE_REMOVE: | |
298 | op &= ~value; | |
299 | break; | |
300 | } | |
301 | option = strtok(NULL, ":,"); | |
302 | } | |
303 | ||
304 | safe_free(tmp); | |
305 | no_options: | |
306 | return op; | |
307 | } | |
308 | ||
a7ad6e4e | 309 | #define SSL_FLAG_NO_DEFAULT_CA (1<<0) |
310 | #define SSL_FLAG_DELAYED_AUTH (1<<1) | |
311 | #define SSL_FLAG_DONT_VERIFY_PEER (1<<2) | |
312 | #define SSL_FLAG_DONT_VERIFY_DOMAIN (1<<3) | |
313 | ||
314 | static long | |
315 | ssl_parse_flags(const char *flags) | |
316 | { | |
317 | long fl = 0; | |
318 | char *tmp; | |
319 | char *flag; | |
320 | ||
321 | if (!flags) | |
322 | return 0; | |
323 | ||
324 | tmp = xstrdup(flags); | |
325 | flag = strtok(tmp, ":,"); | |
326 | while (flag) { | |
327 | if (strcmp(flag, "NO_DEFAULT_CA") == 0) | |
328 | fl |= SSL_FLAG_NO_DEFAULT_CA; | |
329 | else if (strcmp(flag, "DELAYED_AUTH") == 0) | |
330 | fl |= SSL_FLAG_DELAYED_AUTH; | |
331 | else if (strcmp(flag, "DONT_VERIFY_PEER") == 0) | |
332 | fl |= SSL_FLAG_DONT_VERIFY_PEER; | |
333 | else if (strcmp(flag, "DONT_VERIFY_DOMAIN") == 0) | |
334 | fl |= SSL_FLAG_DONT_VERIFY_DOMAIN; | |
335 | else | |
336 | fatalf("Unknown ssl flag '%s'", flag); | |
337 | flag = strtok(NULL, ":,"); | |
338 | } | |
339 | safe_free(tmp); | |
340 | return fl; | |
341 | } | |
342 | ||
343 | ||
344 | static void | |
345 | ssl_initialize(void) | |
f484cdf5 | 346 | { |
d193a436 | 347 | static int ssl_initialized = 0; |
348 | if (!ssl_initialized) { | |
349 | ssl_initialized = 1; | |
350 | SSL_load_error_strings(); | |
351 | SSLeay_add_ssl_algorithms(); | |
a7ad6e4e | 352 | #ifdef HAVE_OPENSSL_ENGINE_H |
353 | if (Config.SSL.ssl_engine) { | |
354 | ENGINE *e; | |
355 | if (!(e = ENGINE_by_id(Config.SSL.ssl_engine))) { | |
356 | fatalf("Unable to find SSL engine '%s'\n", Config.SSL.ssl_engine); | |
357 | } | |
358 | if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { | |
359 | int ssl_error = ERR_get_error(); | |
360 | fatalf("Failed to initialise SSL engine: %s\n", | |
361 | ERR_error_string(ssl_error, NULL)); | |
362 | } | |
363 | } | |
364 | #else | |
365 | if (Config.SSL.ssl_engine) { | |
366 | fatalf("Your OpenSSL has no SSL engine support\n"); | |
367 | } | |
368 | #endif | |
d193a436 | 369 | } |
a7ad6e4e | 370 | ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, NULL); |
371 | ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL); | |
372 | ||
373 | } | |
374 | ||
375 | SSL_CTX * | |
376 | sslCreateServerContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *clientCA, const char *CAfile, const char *CApath) | |
377 | { | |
378 | int ssl_error; | |
379 | SSL_METHOD *method; | |
380 | SSL_CTX *sslContext; | |
381 | long fl = ssl_parse_flags(flags); | |
382 | ||
383 | ssl_initialize(); | |
384 | ||
f484cdf5 | 385 | if (!keyfile) |
386 | keyfile = certfile; | |
387 | if (!certfile) | |
388 | certfile = keyfile; | |
a7ad6e4e | 389 | if (!CAfile) |
390 | CAfile = clientCA; | |
f484cdf5 | 391 | |
27d8545c | 392 | debug(83, 1) ("Initialising SSL.\n"); |
79d4ccdf | 393 | switch (version) { |
f484cdf5 | 394 | case 2: |
27d8545c | 395 | debug(83, 5) ("Using SSLv2.\n"); |
f484cdf5 | 396 | method = SSLv2_server_method(); |
397 | break; | |
398 | case 3: | |
27d8545c | 399 | debug(83, 5) ("Using SSLv3.\n"); |
f484cdf5 | 400 | method = SSLv3_server_method(); |
401 | break; | |
402 | case 4: | |
27d8545c | 403 | debug(83, 5) ("Using TLSv1.\n"); |
f484cdf5 | 404 | method = TLSv1_server_method(); |
405 | break; | |
406 | case 1: | |
407 | default: | |
27d8545c | 408 | debug(83, 5) ("Using SSLv2/SSLv3.\n"); |
f484cdf5 | 409 | method = SSLv23_server_method(); |
410 | break; | |
411 | } | |
412 | ||
413 | sslContext = SSL_CTX_new(method); | |
414 | if (sslContext == NULL) { | |
415 | ssl_error = ERR_get_error(); | |
416 | fatalf("Failed to allocate SSL context: %s\n", | |
417 | ERR_error_string(ssl_error, NULL)); | |
418 | } | |
79d4ccdf | 419 | SSL_CTX_set_options(sslContext, ssl_parse_options(options)); |
f484cdf5 | 420 | |
79d4ccdf | 421 | if (cipher) { |
27d8545c | 422 | debug(83, 5) ("Using chiper suite %s.\n", cipher); |
79d4ccdf | 423 | if (!SSL_CTX_set_cipher_list(sslContext, cipher)) { |
424 | ssl_error = ERR_get_error(); | |
a7ad6e4e | 425 | fatalf("Failed to set SSL cipher suite '%s': %s\n", |
426 | cipher, ERR_error_string(ssl_error, NULL)); | |
79d4ccdf | 427 | } |
428 | } | |
27d8545c | 429 | debug(83, 1) ("Using certificate in %s\n", certfile); |
a7ad6e4e | 430 | if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) { |
f484cdf5 | 431 | ssl_error = ERR_get_error(); |
87ec2a09 | 432 | debug(83, 0) ("Failed to acquire SSL certificate '%s': %s\n", |
a7ad6e4e | 433 | certfile, ERR_error_string(ssl_error, NULL)); |
434 | goto error; | |
f484cdf5 | 435 | } |
27d8545c | 436 | debug(83, 1) ("Using private key in %s\n", keyfile); |
f484cdf5 | 437 | if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) { |
438 | ssl_error = ERR_get_error(); | |
87ec2a09 | 439 | debug(83, 0) ("Failed to acquire SSL private key '%s': %s\n", |
a7ad6e4e | 440 | keyfile, ERR_error_string(ssl_error, NULL)); |
441 | goto error; | |
f484cdf5 | 442 | } |
27d8545c | 443 | debug(83, 5) ("Comparing private and public SSL keys.\n"); |
a7ad6e4e | 444 | if (!SSL_CTX_check_private_key(sslContext)) { |
445 | ssl_error = ERR_get_error(); | |
446 | debug(83, 0) ("SSL private key '%s' does not match public key '%s': %s\n", | |
447 | certfile, keyfile, ERR_error_string(ssl_error, NULL)); | |
448 | goto error; | |
449 | } | |
450 | debug(83, 9) ("Setting RSA key generation callback.\n"); | |
451 | SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb); | |
452 | ||
453 | debug(83, 9) ("Setting CA certificate locations.\n"); | |
454 | if ((!SSL_CTX_load_verify_locations(sslContext, CAfile, CApath))) { | |
455 | ssl_error = ERR_get_error(); | |
456 | debug(83, 1) ("Error error setting CA certificate locations: %s\n", | |
457 | ERR_error_string(ssl_error, NULL)); | |
458 | debug(83, 1) ("continuing anyway...\n"); | |
459 | } | |
460 | if (!(fl & SSL_FLAG_NO_DEFAULT_CA) && | |
461 | !SSL_CTX_set_default_verify_paths(sslContext)) { | |
462 | ssl_error = ERR_get_error(); | |
463 | debug(83, 1) ("Error error setting default CA certificate location: %s\n", | |
464 | ERR_error_string(ssl_error, NULL)); | |
465 | debug(83, 1) ("continuing anyway...\n"); | |
466 | } | |
467 | if (clientCA) { | |
468 | debug(83, 9) ("Set client certifying authority list.\n"); | |
469 | SSL_CTX_set_client_CA_list(sslContext, SSL_load_client_CA_file(clientCA)); | |
470 | if (fl & SSL_FLAG_DELAYED_AUTH) { | |
471 | debug(83, 9) ("Not requesting client certificates until acl processing requires one\n"); | |
472 | SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL); | |
473 | } else { | |
474 | debug(83, 9) ("Requiring client certificates.\n"); | |
475 | SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_cb); | |
476 | } | |
477 | } else { | |
478 | debug(83, 9) ("Not requiring any client certificates\n"); | |
479 | SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL); | |
480 | } | |
f484cdf5 | 481 | |
a7ad6e4e | 482 | if (fl & SSL_FLAG_DONT_VERIFY_DOMAIN) |
483 | SSL_CTX_set_ex_data(sslContext, ssl_ctx_ex_index_dont_verify_domain, (void *) -1); | |
484 | return sslContext; | |
485 | error: | |
486 | SSL_CTX_free(sslContext); | |
487 | return NULL; | |
488 | } | |
489 | ||
490 | SSL_CTX * | |
491 | sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath) | |
492 | { | |
493 | int ssl_error; | |
494 | SSL_METHOD *method; | |
495 | SSL_CTX *sslContext; | |
496 | long fl = ssl_parse_flags(flags); | |
497 | ||
498 | ssl_initialize(); | |
499 | ||
500 | if (!keyfile) | |
501 | keyfile = certfile; | |
502 | if (!certfile) | |
503 | certfile = keyfile; | |
504 | ||
505 | debug(83, 1) ("Initialising SSL.\n"); | |
506 | switch (version) { | |
507 | case 2: | |
508 | debug(83, 5) ("Using SSLv2.\n"); | |
509 | method = SSLv2_client_method(); | |
510 | break; | |
511 | case 3: | |
512 | debug(83, 5) ("Using SSLv3.\n"); | |
513 | method = SSLv3_client_method(); | |
514 | break; | |
515 | case 4: | |
516 | debug(83, 5) ("Using TLSv1.\n"); | |
517 | method = TLSv1_client_method(); | |
518 | break; | |
519 | case 1: | |
520 | default: | |
521 | debug(83, 5) ("Using SSLv2/SSLv3.\n"); | |
522 | method = SSLv23_client_method(); | |
523 | break; | |
524 | } | |
525 | ||
526 | sslContext = SSL_CTX_new(method); | |
527 | if (sslContext == NULL) { | |
528 | ssl_error = ERR_get_error(); | |
529 | fatalf("Failed to allocate SSL context: %s\n", | |
530 | ERR_error_string(ssl_error, NULL)); | |
531 | } | |
532 | SSL_CTX_set_options(sslContext, ssl_parse_options(options)); | |
533 | ||
534 | if (cipher) { | |
535 | debug(83, 5) ("Using chiper suite %s.\n", cipher); | |
536 | if (!SSL_CTX_set_cipher_list(sslContext, cipher)) { | |
537 | ssl_error = ERR_get_error(); | |
538 | fatalf("Failed to set SSL cipher suite '%s': %s\n", | |
539 | cipher, ERR_error_string(ssl_error, NULL)); | |
540 | } | |
541 | } | |
542 | if (certfile) { | |
543 | debug(83, 1) ("Using certificate in %s\n", certfile); | |
544 | if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) { | |
545 | ssl_error = ERR_get_error(); | |
546 | fatalf("Failed to acquire SSL certificate '%s': %s\n", | |
547 | certfile, ERR_error_string(ssl_error, NULL)); | |
548 | } | |
549 | debug(83, 1) ("Using private key in %s\n", keyfile); | |
550 | if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) { | |
551 | ssl_error = ERR_get_error(); | |
552 | fatalf("Failed to acquire SSL private key '%s': %s\n", | |
553 | keyfile, ERR_error_string(ssl_error, NULL)); | |
554 | } | |
555 | debug(83, 5) ("Comparing private and public SSL keys.\n"); | |
556 | if (!SSL_CTX_check_private_key(sslContext)) { | |
557 | ssl_error = ERR_get_error(); | |
558 | fatalf("SSL private key '%s' does not match public key '%s': %s\n", | |
559 | certfile, keyfile, ERR_error_string(ssl_error, NULL)); | |
560 | } | |
561 | } | |
27d8545c | 562 | debug(83, 9) ("Setting RSA key generation callback.\n"); |
f484cdf5 | 563 | SSL_CTX_set_tmp_rsa_callback(sslContext, ssl_temp_rsa_cb); |
564 | ||
a7ad6e4e | 565 | if (fl & SSL_FLAG_DONT_VERIFY_PEER) { |
566 | debug(83, 1) ("NOTICE: Peer certificates are not verified for validity!\n"); | |
567 | SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, NULL); | |
568 | } else { | |
569 | debug(83, 9) ("Setting certificate verification callback.\n"); | |
570 | SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_cb); | |
571 | } | |
f484cdf5 | 572 | |
a7ad6e4e | 573 | debug(83, 9) ("Setting CA certificate locations.\n"); |
574 | if ((!SSL_CTX_load_verify_locations(sslContext, CAfile, CApath))) { | |
575 | ssl_error = ERR_get_error(); | |
576 | debug(83, 1) ("Error error setting CA certificate locations: %s\n", | |
577 | ERR_error_string(ssl_error, NULL)); | |
578 | debug(83, 1) ("continuing anyway...\n"); | |
579 | } | |
580 | if (!(fl & SSL_FLAG_NO_DEFAULT_CA) && | |
581 | !SSL_CTX_set_default_verify_paths(sslContext)) { | |
f484cdf5 | 582 | ssl_error = ERR_get_error(); |
27d8545c | 583 | debug(83, 1) ("Error error setting default CA certificate location: %s\n", |
f484cdf5 | 584 | ERR_error_string(ssl_error, NULL)); |
27d8545c | 585 | debug(83, 1) ("continuing anyway...\n"); |
f484cdf5 | 586 | } |
d193a436 | 587 | return sslContext; |
f484cdf5 | 588 | } |
589 | ||
d193a436 | 590 | int |
e6ccf245 | 591 | ssl_read_method(int fd, char *buf, int len) |
f484cdf5 | 592 | { |
79d4ccdf | 593 | int i; |
594 | ||
595 | i = SSL_read(fd_table[fd].ssl, buf, len); | |
596 | ||
597 | if (i > 0 && SSL_pending(fd_table[fd].ssl) > 0) { | |
27d8545c | 598 | debug(83, 2) ("SSL fd %d is pending\n", fd); |
79d4ccdf | 599 | fd_table[fd].flags.read_pending = 1; |
600 | } else | |
601 | fd_table[fd].flags.read_pending = 0; | |
602 | ||
603 | return i; | |
f484cdf5 | 604 | } |
605 | ||
d193a436 | 606 | int |
e6ccf245 | 607 | ssl_write_method(int fd, const char *buf, int len) |
f484cdf5 | 608 | { |
609 | return (SSL_write(fd_table[fd].ssl, buf, len)); | |
610 | } | |
79d4ccdf | 611 | |
612 | void | |
e6ccf245 | 613 | ssl_shutdown_method(int fd) |
79d4ccdf | 614 | { |
615 | SSL *ssl = fd_table[fd].ssl; | |
616 | if (!fd_table[fd].ssl_shutdown) { | |
617 | fd_table[fd].ssl_shutdown = 1; | |
618 | if (Config.SSL.unclean_shutdown) | |
619 | SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); | |
620 | else | |
621 | SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN); | |
622 | } | |
623 | SSL_shutdown(ssl); | |
624 | } | |
a7ad6e4e | 625 | |
626 | static const char * | |
627 | ssl_get_attribute(X509_NAME * name, const char *attribute_name) | |
628 | { | |
629 | static char buffer[1024]; | |
630 | int nid; | |
631 | ||
632 | buffer[0] = '\0'; | |
633 | ||
634 | if (strcmp(attribute_name, "DN") == 0) { | |
635 | X509_NAME_oneline(name, buffer, sizeof(buffer)); | |
636 | goto done; | |
637 | } | |
638 | nid = OBJ_txt2nid((char *) attribute_name); | |
639 | if (nid == 0) { | |
640 | debug(83, 1) ("WARNING: Unknown SSL attribute name '%s'\n", attribute_name); | |
641 | return NULL; | |
642 | } | |
643 | X509_NAME_get_text_by_NID(name, nid, buffer, sizeof(buffer)); | |
644 | done: | |
645 | return *buffer ? buffer : NULL; | |
646 | } | |
647 | ||
648 | const char * | |
649 | sslGetUserAttribute(SSL * ssl, const char *attribute_name) | |
650 | { | |
651 | X509 *cert; | |
652 | X509_NAME *name; | |
653 | ||
654 | if (!ssl) | |
655 | return NULL; | |
656 | ||
657 | cert = SSL_get_peer_certificate(ssl); | |
658 | if (!cert) | |
659 | return NULL; | |
660 | ||
661 | name = X509_get_issuer_name(cert); | |
662 | ||
663 | return ssl_get_attribute(name, attribute_name); | |
664 | } | |
665 | ||
666 | const char * | |
667 | sslGetCAAttribute(SSL * ssl, const char *attribute_name) | |
668 | { | |
669 | X509 *cert; | |
670 | X509_NAME *name; | |
671 | ||
672 | if (!ssl) | |
673 | return NULL; | |
674 | ||
675 | cert = SSL_get_peer_certificate(ssl); | |
676 | if (!cert) | |
677 | return NULL; | |
678 | ||
679 | name = X509_get_subject_name(cert); | |
680 | ||
681 | return ssl_get_attribute(name, attribute_name); | |
682 | } | |
683 | ||
684 | #if 0 | |
685 | char * | |
686 | sslGetUserEmail(SSL * ssl) | |
687 | { | |
688 | X509 *cert; | |
689 | X509_NAME *name; | |
690 | ||
691 | static char email[128]; | |
692 | ||
693 | if (!ssl) | |
694 | return NULL; | |
695 | cert = SSL_get_peer_certificate(ssl); | |
696 | if (!cert) | |
697 | return NULL; | |
698 | ||
699 | name = X509_get_subject_name(cert); | |
700 | ||
701 | if (X509_NAME_get_text_by_NID(name, NID_pkcs9_emailAddress, email, sizeof(email)) > 0) | |
702 | return email; | |
703 | else | |
704 | return NULL; | |
705 | } | |
706 | #endif | |
707 | ||
708 | const char * | |
709 | sslGetUserEmail(SSL * ssl) | |
710 | { | |
711 | return sslGetUserAttribute(ssl, "Email"); | |
712 | } |