File Coverage

amqp_openssl.c
Criterion Covered Total %
statement 153 394 38.8
branch 54 190 28.4
condition n/a
subroutine n/a
pod n/a
total 207 584 35.4


line stmt bran cond sub pod time code
1             /*
2             * Portions created by Alan Antonuk are Copyright (c) 2012-2014 Alan Antonuk.
3             * All Rights Reserved.
4             *
5             * Portions created by Michael Steinert are Copyright (c) 2012-2014 Michael
6             * Steinert. All Rights Reserved.
7             *
8             * Permission is hereby granted, free of charge, to any person obtaining a
9             * copy of this software and associated documentation files (the "Software"),
10             * to deal in the Software without restriction, including without limitation
11             * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12             * and/or sell copies of the Software, and to permit persons to whom the
13             * Software is furnished to do so, subject to the following conditions:
14             *
15             * The above copyright notice and this permission notice shall be included in
16             * all copies or substantial portions of the Software.
17             *
18             * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19             * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20             * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21             * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22             * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23             * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24             * DEALINGS IN THE SOFTWARE.
25             */
26              
27             #ifdef HAVE_CONFIG_H
28             #include "config.h"
29             #endif
30              
31             #ifdef _MSC_VER
32             #define _CRT_SECURE_NO_WARNINGS
33             #endif
34              
35             #include "amqp_openssl_bio.h"
36             #include "amqp_openssl_hostname_validation.h"
37             #include "amqp_private.h"
38             #include "amqp_socket.h"
39             #include "amqp_ssl_socket.h"
40             #include "amqp_time.h"
41             #include "threads.h"
42              
43             #include
44             #include
45             #include
46             #include
47             #include
48             #include
49             #include
50             #include
51             #include
52             #include
53              
54             static int initialize_ssl_and_increment_connections(void);
55             static int decrement_ssl_connections(void);
56              
57             static unsigned long ssl_threadid_callback(void);
58             static void ssl_locking_callback(int mode, int n, const char *file, int line);
59             static pthread_mutex_t *amqp_openssl_lockarray = NULL;
60              
61             static pthread_mutex_t openssl_init_mutex = PTHREAD_MUTEX_INITIALIZER;
62             static amqp_boolean_t do_initialize_openssl = 1;
63             static amqp_boolean_t openssl_initialized = 0;
64             static amqp_boolean_t openssl_bio_initialized = 0;
65             static int openssl_connections = 0;
66             static ENGINE *openssl_engine = NULL;
67              
68             #define CHECK_SUCCESS(condition) \
69             do { \
70             int check_success_ret = (condition); \
71             if (check_success_ret) { \
72             amqp_abort("Check %s failed <%d>: %s", #condition, check_success_ret, \
73             strerror(check_success_ret)); \
74             } \
75             } while (0)
76              
77             struct amqp_ssl_socket_t {
78             const struct amqp_socket_class_t *klass;
79             SSL_CTX *ctx;
80             int sockfd;
81             SSL *ssl;
82             amqp_boolean_t verify_peer;
83             amqp_boolean_t verify_hostname;
84             int internal_error;
85             };
86              
87 18           static ssize_t amqp_ssl_socket_send(void *base, const void *buf, size_t len,
88             AMQP_UNUSED int flags) {
89 18           struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
90             int res;
91 18 50         if (-1 == self->sockfd) {
92 0           return AMQP_STATUS_SOCKET_CLOSED;
93             }
94              
95             /* SSL_write takes an int for length of buffer, protect against len being
96             * larger than larger than what SSL_write can take */
97 18 50         if (len > INT_MAX) {
98 0           return AMQP_STATUS_INVALID_PARAMETER;
99             }
100              
101 18           ERR_clear_error();
102 18           self->internal_error = 0;
103              
104             /* This will only return on error, or once the whole buffer has been
105             * written to the SSL stream. See SSL_MODE_ENABLE_PARTIAL_WRITE */
106 18           res = SSL_write(self->ssl, buf, (int)len);
107 18 50         if (0 >= res) {
108 0           self->internal_error = SSL_get_error(self->ssl, res);
109             /* TODO: Close connection if it isn't already? */
110             /* TODO: Possibly be more intelligent in reporting WHAT went wrong */
111 0           switch (self->internal_error) {
112             case SSL_ERROR_WANT_READ:
113 0           res = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
114 0           break;
115             case SSL_ERROR_WANT_WRITE:
116 0           res = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
117 0           break;
118             case SSL_ERROR_ZERO_RETURN:
119 0           res = AMQP_STATUS_CONNECTION_CLOSED;
120 0           break;
121             default:
122 0           res = AMQP_STATUS_SSL_ERROR;
123 0           break;
124             }
125             } else {
126 18           self->internal_error = 0;
127             }
128              
129 18           return (ssize_t)res;
130             }
131              
132 30           static ssize_t amqp_ssl_socket_recv(void *base, void *buf, size_t len,
133             AMQP_UNUSED int flags) {
134 30           struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
135             int received;
136 30 50         if (-1 == self->sockfd) {
137 0           return AMQP_STATUS_SOCKET_CLOSED;
138             }
139              
140             /* SSL_read takes an int for length of buffer, protect against len being
141             * larger than larger than what SSL_read can take */
142 30 50         if (len > INT_MAX) {
143 0           return AMQP_STATUS_INVALID_PARAMETER;
144             }
145              
146 30           ERR_clear_error();
147 30           self->internal_error = 0;
148              
149 30           received = SSL_read(self->ssl, buf, (int)len);
150 30 100         if (0 >= received) {
151 15           self->internal_error = SSL_get_error(self->ssl, received);
152 15           switch (self->internal_error) {
153             case SSL_ERROR_WANT_READ:
154 15           received = AMQP_PRIVATE_STATUS_SOCKET_NEEDREAD;
155 15           break;
156             case SSL_ERROR_WANT_WRITE:
157 0           received = AMQP_PRIVATE_STATUS_SOCKET_NEEDWRITE;
158 0           break;
159             case SSL_ERROR_ZERO_RETURN:
160 0           received = AMQP_STATUS_CONNECTION_CLOSED;
161 0           break;
162             default:
163 0           received = AMQP_STATUS_SSL_ERROR;
164 0           break;
165             }
166             }
167              
168 30           return (ssize_t)received;
169             }
170              
171 1           static int amqp_ssl_socket_open(void *base, const char *host, int port,
172             const struct timeval *timeout) {
173 1           struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
174             long result;
175             int status;
176             amqp_time_t deadline;
177             X509 *cert;
178             BIO *bio;
179 1 50         if (-1 != self->sockfd) {
180 0           return AMQP_STATUS_SOCKET_INUSE;
181             }
182 1           ERR_clear_error();
183              
184 1           self->ssl = SSL_new(self->ctx);
185 1 50         if (!self->ssl) {
186 0           self->internal_error = ERR_peek_error();
187 0           status = AMQP_STATUS_SSL_ERROR;
188 0           goto exit;
189             }
190              
191 1           status = amqp_time_from_now(&deadline, timeout);
192 1 50         if (AMQP_STATUS_OK != status) {
193 0           return status;
194             }
195              
196 1           self->sockfd = amqp_open_socket_inner(host, port, deadline);
197 1 50         if (0 > self->sockfd) {
198 0           status = self->sockfd;
199 0           self->internal_error = amqp_os_socket_error();
200 0           self->sockfd = -1;
201 0           goto error_out1;
202             }
203              
204 1           bio = BIO_new(amqp_openssl_bio());
205 1 50         if (!bio) {
206 0           status = AMQP_STATUS_NO_MEMORY;
207 0           goto error_out2;
208             }
209              
210 1           BIO_set_fd(bio, self->sockfd, BIO_NOCLOSE);
211 1           SSL_set_bio(self->ssl, bio, bio);
212              
213 1           status = SSL_set_tlsext_host_name(self->ssl, host);
214 1 50         if (!status) {
215 0           self->internal_error = SSL_get_error(self->ssl, status);
216 0           status = AMQP_STATUS_SSL_ERROR;
217 0           goto error_out2;
218             }
219              
220             start_connect:
221 3           status = SSL_connect(self->ssl);
222 3 100         if (status != 1) {
223 2           self->internal_error = SSL_get_error(self->ssl, status);
224 2           switch (self->internal_error) {
225             case SSL_ERROR_WANT_READ:
226 2           status = amqp_poll(self->sockfd, AMQP_SF_POLLIN, deadline);
227 2           break;
228             case SSL_ERROR_WANT_WRITE:
229 0           status = amqp_poll(self->sockfd, AMQP_SF_POLLOUT, deadline);
230 0           break;
231             default:
232 0           status = AMQP_STATUS_SSL_CONNECTION_FAILED;
233             }
234 2 50         if (AMQP_STATUS_OK == status) {
235 2           goto start_connect;
236             }
237 0           goto error_out2;
238             }
239              
240 1           cert = SSL_get_peer_certificate(self->ssl);
241              
242 1 50         if (self->verify_peer) {
243 1 50         if (!cert) {
244 0           self->internal_error = 0;
245 0           status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
246 0           goto error_out3;
247             }
248              
249 1           result = SSL_get_verify_result(self->ssl);
250 1 50         if (X509_V_OK != result) {
251 0           self->internal_error = result;
252 0           status = AMQP_STATUS_SSL_PEER_VERIFY_FAILED;
253 0           goto error_out4;
254             }
255             }
256 1 50         if (self->verify_hostname) {
257 1 50         if (!cert) {
258 0           self->internal_error = 0;
259 0           status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
260 0           goto error_out3;
261             }
262              
263 1 50         if (AMQP_HVR_MATCH_FOUND != amqp_ssl_validate_hostname(host, cert)) {
264 0           self->internal_error = 0;
265 0           status = AMQP_STATUS_SSL_HOSTNAME_VERIFY_FAILED;
266 0           goto error_out4;
267             }
268             }
269              
270 1           X509_free(cert);
271 1           self->internal_error = 0;
272 1           status = AMQP_STATUS_OK;
273              
274             exit:
275 1           return status;
276              
277             error_out4:
278 0           X509_free(cert);
279             error_out3:
280 0           SSL_shutdown(self->ssl);
281             error_out2:
282 0           amqp_os_socket_close(self->sockfd);
283 0           self->sockfd = -1;
284             error_out1:
285 0           SSL_free(self->ssl);
286 0           self->ssl = NULL;
287 1           goto exit;
288             }
289              
290 1           static int amqp_ssl_socket_close(void *base, amqp_socket_close_enum force) {
291 1           struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
292              
293 1 50         if (-1 == self->sockfd) {
294 0           return AMQP_STATUS_SOCKET_CLOSED;
295             }
296              
297 1 50         if (AMQP_SC_NONE == force) {
298             /* don't try too hard to shutdown the connection */
299 1           SSL_shutdown(self->ssl);
300             }
301              
302 1           SSL_free(self->ssl);
303 1           self->ssl = NULL;
304              
305 1 50         if (amqp_os_socket_close(self->sockfd)) {
306 0           return AMQP_STATUS_SOCKET_ERROR;
307             }
308 1           self->sockfd = -1;
309              
310 1           return AMQP_STATUS_OK;
311             }
312              
313 31           static int amqp_ssl_socket_get_sockfd(void *base) {
314 31           struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
315 31           return self->sockfd;
316             }
317              
318 1           static void amqp_ssl_socket_delete(void *base) {
319 1           struct amqp_ssl_socket_t *self = (struct amqp_ssl_socket_t *)base;
320              
321 1 50         if (self) {
322 1           amqp_ssl_socket_close(self, AMQP_SC_NONE);
323              
324 1           SSL_CTX_free(self->ctx);
325 1           free(self);
326             }
327 1           decrement_ssl_connections();
328 1           }
329              
330             static const struct amqp_socket_class_t amqp_ssl_socket_class = {
331             amqp_ssl_socket_send, /* send */
332             amqp_ssl_socket_recv, /* recv */
333             amqp_ssl_socket_open, /* open */
334             amqp_ssl_socket_close, /* close */
335             amqp_ssl_socket_get_sockfd, /* get_sockfd */
336             amqp_ssl_socket_delete /* delete */
337             };
338              
339 1           amqp_socket_t *amqp_ssl_socket_new(amqp_connection_state_t state) {
340 1           struct amqp_ssl_socket_t *self = calloc(1, sizeof(*self));
341             int status;
342 1 50         if (!self) {
343 0           return NULL;
344             }
345              
346 1           self->sockfd = -1;
347 1           self->klass = &amqp_ssl_socket_class;
348 1           self->verify_peer = 1;
349 1           self->verify_hostname = 1;
350              
351 1           status = initialize_ssl_and_increment_connections();
352 1 50         if (status) {
353 0           goto error;
354             }
355              
356 1           self->ctx = SSL_CTX_new(SSLv23_client_method());
357 1 50         if (!self->ctx) {
358 0           goto error;
359             }
360             /* Disable SSLv2 and SSLv3 */
361 1           SSL_CTX_set_options(self->ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
362              
363 1           SSL_CTX_set_mode(self->ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
364             /* OpenSSL v1.1.1 turns this on by default, which makes the non-blocking
365             * logic not behave as expected, so turn this back off */
366 1           SSL_CTX_clear_mode(self->ctx, SSL_MODE_AUTO_RETRY);
367              
368 1           amqp_set_socket(state, (amqp_socket_t *)self);
369              
370 1           return (amqp_socket_t *)self;
371             error:
372 0           amqp_ssl_socket_delete((amqp_socket_t *)self);
373 0           return NULL;
374             }
375              
376 0           void *amqp_ssl_socket_get_context(amqp_socket_t *base) {
377 0 0         if (base->klass != &amqp_ssl_socket_class) {
378 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
379             }
380 0           return ((struct amqp_ssl_socket_t *)base)->ctx;
381             }
382              
383 1           int amqp_ssl_socket_set_cacert(amqp_socket_t *base, const char *cacert) {
384             int status;
385             struct amqp_ssl_socket_t *self;
386 1 50         if (base->klass != &amqp_ssl_socket_class) {
387 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
388             }
389 1           self = (struct amqp_ssl_socket_t *)base;
390 1           status = SSL_CTX_load_verify_locations(self->ctx, cacert, NULL);
391 1 50         if (1 != status) {
392 0           return AMQP_STATUS_SSL_ERROR;
393             }
394 1           return AMQP_STATUS_OK;
395             }
396              
397 0           int amqp_ssl_socket_set_key(amqp_socket_t *base, const char *cert,
398             const char *key) {
399             int status;
400             struct amqp_ssl_socket_t *self;
401 0 0         if (base->klass != &amqp_ssl_socket_class) {
402 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
403             }
404 0           self = (struct amqp_ssl_socket_t *)base;
405 0           status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
406 0 0         if (1 != status) {
407 0           return AMQP_STATUS_SSL_ERROR;
408             }
409 0           status = SSL_CTX_use_PrivateKey_file(self->ctx, key, SSL_FILETYPE_PEM);
410 0 0         if (1 != status) {
411 0           return AMQP_STATUS_SSL_ERROR;
412             }
413 0           return AMQP_STATUS_OK;
414             }
415              
416 0           int amqp_ssl_socket_set_key_engine(amqp_socket_t *base, const char *cert,
417             const char *key) {
418             int status;
419             struct amqp_ssl_socket_t *self;
420 0           EVP_PKEY *pkey = NULL;
421 0 0         if (base->klass != &amqp_ssl_socket_class) {
422 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
423             }
424 0           self = (struct amqp_ssl_socket_t *)base;
425 0           status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
426 0 0         if (1 != status) {
427 0           return AMQP_STATUS_SSL_ERROR;
428             }
429              
430 0           pkey = ENGINE_load_private_key(openssl_engine, key, NULL, NULL);
431 0 0         if (pkey == NULL) {
432 0           return AMQP_STATUS_SSL_ERROR;
433             }
434              
435 0           status = SSL_CTX_use_PrivateKey(self->ctx, pkey);
436 0           EVP_PKEY_free(pkey);
437              
438 0 0         if (1 != status) {
439 0           return AMQP_STATUS_SSL_ERROR;
440             }
441 0           return AMQP_STATUS_OK;
442             }
443              
444 0           static int password_cb(AMQP_UNUSED char *buffer, AMQP_UNUSED int length,
445             AMQP_UNUSED int rwflag, AMQP_UNUSED void *user_data) {
446 0           amqp_abort("rabbitmq-c does not support password protected keys");
447             }
448              
449 0           int amqp_ssl_socket_set_key_buffer(amqp_socket_t *base, const char *cert,
450             const void *key, size_t n) {
451 0           int status = AMQP_STATUS_OK;
452 0           BIO *buf = NULL;
453 0           RSA *rsa = NULL;
454             struct amqp_ssl_socket_t *self;
455 0 0         if (base->klass != &amqp_ssl_socket_class) {
456 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
457             }
458 0 0         if (n > INT_MAX) {
459 0           return AMQP_STATUS_INVALID_PARAMETER;
460             }
461 0           self = (struct amqp_ssl_socket_t *)base;
462 0           status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
463 0 0         if (1 != status) {
464 0           return AMQP_STATUS_SSL_ERROR;
465             }
466 0           buf = BIO_new_mem_buf((void *)key, (int)n);
467 0 0         if (!buf) {
468 0           goto error;
469             }
470 0           rsa = PEM_read_bio_RSAPrivateKey(buf, NULL, password_cb, NULL);
471 0 0         if (!rsa) {
472 0           goto error;
473             }
474 0           status = SSL_CTX_use_RSAPrivateKey(self->ctx, rsa);
475 0 0         if (1 != status) {
476 0           goto error;
477             }
478             exit:
479 0           BIO_vfree(buf);
480 0           RSA_free(rsa);
481 0           return status;
482             error:
483 0           status = AMQP_STATUS_SSL_ERROR;
484 0           goto exit;
485             }
486              
487 0           int amqp_ssl_socket_set_cert(amqp_socket_t *base, const char *cert) {
488             int status;
489             struct amqp_ssl_socket_t *self;
490 0 0         if (base->klass != &amqp_ssl_socket_class) {
491 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
492             }
493 0           self = (struct amqp_ssl_socket_t *)base;
494 0           status = SSL_CTX_use_certificate_chain_file(self->ctx, cert);
495 0 0         if (1 != status) {
496 0           return AMQP_STATUS_SSL_ERROR;
497             }
498 0           return AMQP_STATUS_OK;
499             }
500              
501 0           void amqp_ssl_socket_set_key_passwd(amqp_socket_t *base, const char *passwd) {
502             struct amqp_ssl_socket_t *self;
503 0 0         if (base->klass != &amqp_ssl_socket_class) {
504 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
505             }
506 0           self = (struct amqp_ssl_socket_t *)base;
507 0           SSL_CTX_set_default_passwd_cb_userdata(self->ctx, (void *)passwd);
508 0           }
509              
510 0           void amqp_ssl_socket_set_verify(amqp_socket_t *base, amqp_boolean_t verify) {
511 0           amqp_ssl_socket_set_verify_peer(base, verify);
512 0           amqp_ssl_socket_set_verify_hostname(base, verify);
513 0           }
514              
515 0           void amqp_ssl_socket_set_verify_peer(amqp_socket_t *base,
516             amqp_boolean_t verify) {
517             struct amqp_ssl_socket_t *self;
518 0 0         if (base->klass != &amqp_ssl_socket_class) {
519 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
520             }
521 0           self = (struct amqp_ssl_socket_t *)base;
522 0           self->verify_peer = verify;
523 0           }
524              
525 1           void amqp_ssl_socket_set_verify_hostname(amqp_socket_t *base,
526             amqp_boolean_t verify) {
527             struct amqp_ssl_socket_t *self;
528 1 50         if (base->klass != &amqp_ssl_socket_class) {
529 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
530             }
531 1           self = (struct amqp_ssl_socket_t *)base;
532 1           self->verify_hostname = verify;
533 1           }
534              
535 0           int amqp_ssl_socket_set_ssl_versions(amqp_socket_t *base,
536             amqp_tls_version_t min,
537             amqp_tls_version_t max) {
538             struct amqp_ssl_socket_t *self;
539 0 0         if (base->klass != &amqp_ssl_socket_class) {
540 0           amqp_abort("<%p> is not of type amqp_ssl_socket_t", base);
541             }
542 0           self = (struct amqp_ssl_socket_t *)base;
543              
544             {
545             long clear_options;
546 0           long set_options = 0;
547             #if defined(SSL_OP_NO_TLSv1_2)
548 0           amqp_tls_version_t max_supported = AMQP_TLSv1_2;
549 0           clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2;
550             #elif defined(SSL_OP_NO_TLSv1_1)
551             amqp_tls_version_t max_supported = AMQP_TLSv1_1;
552             clear_options = SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
553             #elif defined(SSL_OP_NO_TLSv1)
554             amqp_tls_version_t max_supported = AMQP_TLSv1;
555             clear_options = SSL_OP_NO_TLSv1;
556             #else
557             #error "Need a version of OpenSSL that can support TLSv1 or greater."
558             #endif
559              
560 0 0         if (AMQP_TLSvLATEST == max) {
561 0           max = max_supported;
562             }
563 0 0         if (AMQP_TLSvLATEST == min) {
564 0           min = max_supported;
565             }
566              
567 0 0         if (min > max) {
568 0           return AMQP_STATUS_INVALID_PARAMETER;
569             }
570              
571 0 0         if (max > max_supported || min > max_supported) {
    0          
572 0           return AMQP_STATUS_UNSUPPORTED;
573             }
574              
575 0 0         if (min > AMQP_TLSv1) {
576 0           set_options |= SSL_OP_NO_TLSv1;
577             }
578             #ifdef SSL_OP_NO_TLSv1_1
579 0 0         if (min > AMQP_TLSv1_1 || max < AMQP_TLSv1_1) {
    0          
580 0           set_options |= SSL_OP_NO_TLSv1_1;
581             }
582             #endif
583             #ifdef SSL_OP_NO_TLSv1_2
584 0 0         if (max < AMQP_TLSv1_2) {
585 0           set_options |= SSL_OP_NO_TLSv1_2;
586             }
587             #endif
588 0           SSL_CTX_clear_options(self->ctx, clear_options);
589 0           SSL_CTX_set_options(self->ctx, set_options);
590             }
591              
592 0           return AMQP_STATUS_OK;
593             }
594              
595 1           void amqp_set_initialize_ssl_library(amqp_boolean_t do_initialize) {
596 1 50         CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
597              
598 1 50         if (openssl_connections == 0 && !openssl_initialized) {
    50          
599 1           do_initialize_openssl = do_initialize;
600             }
601 1 50         CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
602 1           }
603              
604 141           static unsigned long ssl_threadid_callback(void) {
605 141           return (unsigned long)pthread_self();
606             }
607              
608 57116           static void ssl_locking_callback(int mode, int n, AMQP_UNUSED const char *file,
609             AMQP_UNUSED int line) {
610 57116 100         if (mode & CRYPTO_LOCK) {
611 28558 50         CHECK_SUCCESS(pthread_mutex_lock(&amqp_openssl_lockarray[n]));
612             } else {
613 28558 50         CHECK_SUCCESS(pthread_mutex_unlock(&amqp_openssl_lockarray[n]));
614             }
615 57116           }
616              
617 1           static int setup_openssl(void) {
618             int status;
619              
620             int i;
621 1           amqp_openssl_lockarray = calloc(CRYPTO_num_locks(), sizeof(pthread_mutex_t));
622 1 50         if (!amqp_openssl_lockarray) {
623 0           status = AMQP_STATUS_NO_MEMORY;
624 0           goto out;
625             }
626 42 100         for (i = 0; i < CRYPTO_num_locks(); i++) {
627 41 50         if (pthread_mutex_init(&amqp_openssl_lockarray[i], NULL)) {
628             int j;
629 0 0         for (j = 0; j < i; j++) {
630 0           pthread_mutex_destroy(&amqp_openssl_lockarray[j]);
631             }
632 0           free(amqp_openssl_lockarray);
633 0           status = AMQP_STATUS_SSL_ERROR;
634 0           goto out;
635             }
636             }
637 1           CRYPTO_set_id_callback(ssl_threadid_callback);
638 1           CRYPTO_set_locking_callback(ssl_locking_callback);
639              
640             #ifdef AMQP_OPENSSL_V110
641             if (OPENSSL_init_ssl(0, NULL) <= 0) {
642             status = AMQP_STATUS_SSL_ERROR;
643             goto out;
644             }
645             #else
646 1           OPENSSL_config(NULL);
647             #endif
648 1           SSL_library_init();
649 1           SSL_load_error_strings();
650              
651 1           status = AMQP_STATUS_OK;
652             out:
653 1           return status;
654             }
655              
656 0           int amqp_initialize_ssl_library(void) {
657             int status;
658 0 0         CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
659              
660 0 0         if (!openssl_initialized) {
661 0           status = setup_openssl();
662 0 0         if (status) {
663 0           goto out;
664             }
665 0           openssl_initialized = 1;
666             }
667              
668 0           status = AMQP_STATUS_OK;
669             out:
670 0 0         CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
671 0           return status;
672             }
673              
674 0           int amqp_set_ssl_engine(const char *engine) {
675 0           int status = AMQP_STATUS_OK;
676 0 0         CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
677              
678 0 0         if (!openssl_initialized) {
679 0           status = AMQP_STATUS_SSL_ERROR;
680 0           goto out;
681             }
682              
683 0 0         if (openssl_engine != NULL) {
684 0           ENGINE_free(openssl_engine);
685 0           openssl_engine = NULL;
686             }
687              
688 0 0         if (engine == NULL) {
689 0           goto out;
690             }
691              
692 0           ENGINE_load_builtin_engines();
693 0           openssl_engine = ENGINE_by_id(engine);
694 0 0         if (openssl_engine == NULL) {
695 0           status = AMQP_STATUS_SSL_SET_ENGINE_FAILED;
696 0           goto out;
697             }
698              
699 0 0         if (ENGINE_set_default(openssl_engine, ENGINE_METHOD_ALL) == 0) {
700 0           ENGINE_free(openssl_engine);
701 0           openssl_engine = NULL;
702 0           status = AMQP_STATUS_SSL_SET_ENGINE_FAILED;
703 0           goto out;
704             }
705              
706             out:
707 0 0         CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
708 0           return status;
709             }
710              
711 1           static int initialize_ssl_and_increment_connections() {
712             int status;
713 1 50         CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
714              
715 1 50         if (do_initialize_openssl && !openssl_initialized) {
    50          
716 1           status = setup_openssl();
717 1 50         if (status) {
718 0           goto exit;
719             }
720 1           openssl_initialized = 1;
721             }
722              
723 1 50         if (!openssl_bio_initialized) {
724 1           status = amqp_openssl_bio_init();
725 1 50         if (status) {
726 0           goto exit;
727             }
728 1           openssl_bio_initialized = 1;
729             }
730              
731 1           openssl_connections += 1;
732 1           status = AMQP_STATUS_OK;
733             exit:
734 1 50         CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
735 1           return status;
736             }
737              
738 1           static int decrement_ssl_connections(void) {
739 1 50         CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
740              
741 1 50         if (openssl_connections > 0) {
742 1           openssl_connections--;
743             }
744              
745 1 50         CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
746 1           return AMQP_STATUS_OK;
747             }
748              
749 0           int amqp_uninitialize_ssl_library(void) {
750             int status;
751 0 0         CHECK_SUCCESS(pthread_mutex_lock(&openssl_init_mutex));
752              
753 0 0         if (openssl_connections > 0) {
754 0           status = AMQP_STATUS_SOCKET_INUSE;
755 0           goto out;
756             }
757              
758 0           amqp_openssl_bio_destroy();
759 0           openssl_bio_initialized = 0;
760              
761             #ifndef AMQP_OPENSSL_V110
762 0           ERR_remove_state(0);
763             #endif
764              
765 0           CRYPTO_set_locking_callback(NULL);
766 0           CRYPTO_set_id_callback(NULL);
767             {
768             int i;
769 0 0         for (i = 0; i < CRYPTO_num_locks(); i++) {
770 0           pthread_mutex_destroy(&amqp_openssl_lockarray[i]);
771             }
772 0           free(amqp_openssl_lockarray);
773             }
774              
775 0 0         if (openssl_engine != NULL) {
776 0           ENGINE_free(openssl_engine);
777 0           openssl_engine = NULL;
778             }
779              
780 0           ENGINE_cleanup();
781 0           CONF_modules_free();
782 0           EVP_cleanup();
783 0           CRYPTO_cleanup_all_ex_data();
784 0           ERR_free_strings();
785             #if (OPENSSL_VERSION_NUMBER >= 0x10002003L) && !defined(LIBRESSL_VERSION_NUMBER)
786 0           SSL_COMP_free_compression_methods();
787             #endif
788              
789 0           openssl_initialized = 0;
790              
791 0           status = AMQP_STATUS_OK;
792             out:
793 0 0         CHECK_SUCCESS(pthread_mutex_unlock(&openssl_init_mutex));
794 0           return status;
795             }