xref: /unit/src/nxt_openssl.c (revision 1)
10Sigor@sysoev.ru 
20Sigor@sysoev.ru /*
30Sigor@sysoev.ru  * Copyright (C) Igor Sysoev
40Sigor@sysoev.ru  * Copyright (C) NGINX, Inc.
50Sigor@sysoev.ru  */
60Sigor@sysoev.ru 
70Sigor@sysoev.ru #include <nxt_main.h>
80Sigor@sysoev.ru #include <openssl/ssl.h>
90Sigor@sysoev.ru #include <openssl/conf.h>
100Sigor@sysoev.ru #include <openssl/err.h>
110Sigor@sysoev.ru 
120Sigor@sysoev.ru 
130Sigor@sysoev.ru typedef struct {
140Sigor@sysoev.ru     SSL            *session;
150Sigor@sysoev.ru 
160Sigor@sysoev.ru     int            ssl_error;
170Sigor@sysoev.ru     uint8_t        times;      /* 2 bits */
180Sigor@sysoev.ru 
190Sigor@sysoev.ru     nxt_buf_mem_t  buffer;
200Sigor@sysoev.ru } nxt_openssl_conn_t;
210Sigor@sysoev.ru 
220Sigor@sysoev.ru 
230Sigor@sysoev.ru static nxt_int_t nxt_openssl_server_init(nxt_ssltls_conf_t *conf);
240Sigor@sysoev.ru 
25*1Sigor@sysoev.ru static void nxt_openssl_conn_init(nxt_task_t *task, nxt_ssltls_conf_t *conf,
260Sigor@sysoev.ru     nxt_event_conn_t *c);
270Sigor@sysoev.ru static void nxt_openssl_session_cleanup(void *data);
28*1Sigor@sysoev.ru static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data);
29*1Sigor@sysoev.ru static void nxt_openssl_conn_io_read(nxt_task_t *task, void *obj, void *data);
30*1Sigor@sysoev.ru static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
310Sigor@sysoev.ru     void *data);
32*1Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_write_chunk(nxt_event_conn_t *c,
33*1Sigor@sysoev.ru     nxt_buf_t *b, size_t limit);
340Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_send(nxt_event_conn_t *c, void *buf,
350Sigor@sysoev.ru     size_t size);
36*1Sigor@sysoev.ru static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task,
370Sigor@sysoev.ru     nxt_event_conn_t *c, int ret, nxt_err_t sys_err,
380Sigor@sysoev.ru     nxt_work_handler_t handler);
390Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_conn_error(nxt_event_conn_t *c, nxt_err_t err,
400Sigor@sysoev.ru     const char *fmt, ...);
410Sigor@sysoev.ru static nxt_uint_t nxt_openssl_log_error_level(nxt_event_conn_t *c,
420Sigor@sysoev.ru     nxt_err_t err);
430Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_log_error(nxt_uint_t level, nxt_log_t *log,
440Sigor@sysoev.ru     const char *fmt, ...);
450Sigor@sysoev.ru static u_char *nxt_openssl_copy_error(u_char *p, u_char *end);
460Sigor@sysoev.ru 
470Sigor@sysoev.ru 
480Sigor@sysoev.ru const nxt_ssltls_lib_t  nxt_openssl_lib = {
490Sigor@sysoev.ru     nxt_openssl_server_init,
500Sigor@sysoev.ru     NULL,
510Sigor@sysoev.ru };
520Sigor@sysoev.ru 
530Sigor@sysoev.ru 
540Sigor@sysoev.ru static nxt_event_conn_io_t  nxt_openssl_event_conn_io = {
550Sigor@sysoev.ru     NULL,
560Sigor@sysoev.ru     NULL,
570Sigor@sysoev.ru 
580Sigor@sysoev.ru     nxt_openssl_conn_io_read,
590Sigor@sysoev.ru     NULL,
600Sigor@sysoev.ru     NULL,
610Sigor@sysoev.ru 
620Sigor@sysoev.ru     nxt_event_conn_io_write,
630Sigor@sysoev.ru     nxt_openssl_conn_io_write_chunk,
640Sigor@sysoev.ru     NULL,
650Sigor@sysoev.ru     NULL,
660Sigor@sysoev.ru     nxt_openssl_conn_io_send,
670Sigor@sysoev.ru 
680Sigor@sysoev.ru     nxt_openssl_conn_io_shutdown,
690Sigor@sysoev.ru };
700Sigor@sysoev.ru 
710Sigor@sysoev.ru 
720Sigor@sysoev.ru static long  nxt_openssl_version;
730Sigor@sysoev.ru static int   nxt_openssl_connection_index;
740Sigor@sysoev.ru 
750Sigor@sysoev.ru 
760Sigor@sysoev.ru static nxt_int_t
770Sigor@sysoev.ru nxt_openssl_start(nxt_thread_t *thr)
780Sigor@sysoev.ru {
790Sigor@sysoev.ru     int  index;
800Sigor@sysoev.ru 
810Sigor@sysoev.ru     if (nxt_fast_path(nxt_openssl_version != 0)) {
820Sigor@sysoev.ru         return NXT_OK;
830Sigor@sysoev.ru     }
840Sigor@sysoev.ru 
850Sigor@sysoev.ru     SSL_load_error_strings();
860Sigor@sysoev.ru 
870Sigor@sysoev.ru     OPENSSL_config(NULL);
880Sigor@sysoev.ru 
890Sigor@sysoev.ru     /*
900Sigor@sysoev.ru      * SSL_library_init(3):
910Sigor@sysoev.ru      *
920Sigor@sysoev.ru      *   SSL_library_init() always returns "1",
930Sigor@sysoev.ru      *   so it is safe to discard the return value.
940Sigor@sysoev.ru      */
950Sigor@sysoev.ru     (void) SSL_library_init();
960Sigor@sysoev.ru 
970Sigor@sysoev.ru     nxt_openssl_version = SSLeay();
980Sigor@sysoev.ru 
990Sigor@sysoev.ru     nxt_log_error(NXT_LOG_INFO, thr->log, "%s, %xl",
1000Sigor@sysoev.ru                   SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
1010Sigor@sysoev.ru 
1020Sigor@sysoev.ru #ifndef SSL_OP_NO_COMPRESSION
1030Sigor@sysoev.ru     {
1040Sigor@sysoev.ru         /*
1050Sigor@sysoev.ru          * Disable gzip compression in OpenSSL prior to 1.0.0
1060Sigor@sysoev.ru          * version, this saves about 522K per connection.
1070Sigor@sysoev.ru          */
1080Sigor@sysoev.ru         int                 n;
1090Sigor@sysoev.ru         STACK_OF(SSL_COMP)  *ssl_comp_methods;
1100Sigor@sysoev.ru 
1110Sigor@sysoev.ru         ssl_comp_methods = SSL_COMP_get_compression_methods();
1120Sigor@sysoev.ru 
1130Sigor@sysoev.ru         for (n = sk_SSL_COMP_num(ssl_comp_methods); n != 0; n--) {
1140Sigor@sysoev.ru             (void) sk_SSL_COMP_pop(ssl_comp_methods);
1150Sigor@sysoev.ru         }
1160Sigor@sysoev.ru     }
1170Sigor@sysoev.ru #endif
1180Sigor@sysoev.ru 
1190Sigor@sysoev.ru     index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1200Sigor@sysoev.ru 
1210Sigor@sysoev.ru     if (index == -1) {
1220Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, thr->log,
1230Sigor@sysoev.ru                               "SSL_get_ex_new_index() failed");
1240Sigor@sysoev.ru         return NXT_ERROR;
1250Sigor@sysoev.ru     }
1260Sigor@sysoev.ru 
1270Sigor@sysoev.ru     nxt_openssl_connection_index = index;
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru     return NXT_OK;
1300Sigor@sysoev.ru }
1310Sigor@sysoev.ru 
1320Sigor@sysoev.ru 
1330Sigor@sysoev.ru static nxt_int_t
1340Sigor@sysoev.ru nxt_openssl_server_init(nxt_ssltls_conf_t *conf)
1350Sigor@sysoev.ru {
1360Sigor@sysoev.ru     SSL_CTX              *ctx;
1370Sigor@sysoev.ru     const char           *certificate, *key, *ciphers, *ca_certificate;
1380Sigor@sysoev.ru     nxt_thread_t         *thr;
1390Sigor@sysoev.ru     STACK_OF(X509_NAME)  *list;
1400Sigor@sysoev.ru 
1410Sigor@sysoev.ru     thr = nxt_thread();
1420Sigor@sysoev.ru 
1430Sigor@sysoev.ru     if (nxt_openssl_start(thr) != NXT_OK) {
1440Sigor@sysoev.ru         return NXT_ERROR;
1450Sigor@sysoev.ru     }
1460Sigor@sysoev.ru 
1470Sigor@sysoev.ru     ctx = SSL_CTX_new(SSLv23_server_method());
1480Sigor@sysoev.ru     if (ctx == NULL) {
1490Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, thr->log, "SSL_CTX_new() failed");
1500Sigor@sysoev.ru         return NXT_ERROR;
1510Sigor@sysoev.ru     }
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru     conf->ctx = ctx;
1540Sigor@sysoev.ru     conf->conn_init = nxt_openssl_conn_init;
1550Sigor@sysoev.ru 
1560Sigor@sysoev.ru #ifdef SSL_OP_NO_COMPRESSION
1570Sigor@sysoev.ru     /*
1580Sigor@sysoev.ru      * Disable gzip compression in OpenSSL 1.0.0,
1590Sigor@sysoev.ru      * this saves about 522K per connection.
1600Sigor@sysoev.ru      */
1610Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
1620Sigor@sysoev.ru #endif
1630Sigor@sysoev.ru 
1640Sigor@sysoev.ru #ifdef SSL_MODE_RELEASE_BUFFERS
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru     if (nxt_openssl_version >= 10001078) {
1670Sigor@sysoev.ru         /*
1680Sigor@sysoev.ru          * Allow to release read and write buffers in OpenSSL 1.0.0,
1690Sigor@sysoev.ru          * this saves about 34K per idle connection.  It is not safe
1700Sigor@sysoev.ru          * before OpenSSL 1.0.1h (CVE-2010-5298).
1710Sigor@sysoev.ru          */
1720Sigor@sysoev.ru         SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
1730Sigor@sysoev.ru     }
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru #endif
1760Sigor@sysoev.ru 
1770Sigor@sysoev.ru     certificate = conf->certificate;
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru     if (SSL_CTX_use_certificate_chain_file(ctx, certificate) == 0) {
1800Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, thr->log,
1810Sigor@sysoev.ru                               "SSL_CTX_use_certificate_file(\"%s\") failed",
1820Sigor@sysoev.ru                               certificate);
1830Sigor@sysoev.ru         goto fail;
1840Sigor@sysoev.ru     }
1850Sigor@sysoev.ru 
1860Sigor@sysoev.ru     key = conf->certificate_key;
1870Sigor@sysoev.ru 
1880Sigor@sysoev.ru     if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
1890Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, thr->log,
1900Sigor@sysoev.ru                               "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
1910Sigor@sysoev.ru                               key);
1920Sigor@sysoev.ru         goto fail;
1930Sigor@sysoev.ru     }
1940Sigor@sysoev.ru 
1950Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
1960Sigor@sysoev.ru 
1970Sigor@sysoev.ru     if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
1980Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, thr->log,
1990Sigor@sysoev.ru                               "SSL_CTX_set_cipher_list(\"%s\") failed",
2000Sigor@sysoev.ru                               ciphers);
2010Sigor@sysoev.ru         goto fail;
2020Sigor@sysoev.ru     }
2030Sigor@sysoev.ru 
2040Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
2050Sigor@sysoev.ru 
2060Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
2070Sigor@sysoev.ru 
2080Sigor@sysoev.ru         /* TODO: verify callback */
2090Sigor@sysoev.ru         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
2100Sigor@sysoev.ru 
2110Sigor@sysoev.ru         /* TODO: verify depth */
2120Sigor@sysoev.ru         SSL_CTX_set_verify_depth(ctx, 1);
2130Sigor@sysoev.ru 
2140Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
2150Sigor@sysoev.ru 
2160Sigor@sysoev.ru         if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
2170Sigor@sysoev.ru             nxt_openssl_log_error(NXT_LOG_CRIT, thr->log,
2180Sigor@sysoev.ru                               "SSL_CTX_load_verify_locations(\"%s\") failed",
2190Sigor@sysoev.ru                               ca_certificate);
2200Sigor@sysoev.ru             goto fail;
2210Sigor@sysoev.ru         }
2220Sigor@sysoev.ru 
2230Sigor@sysoev.ru         list = SSL_load_client_CA_file(ca_certificate);
2240Sigor@sysoev.ru 
2250Sigor@sysoev.ru         if (list == NULL) {
2260Sigor@sysoev.ru             nxt_openssl_log_error(NXT_LOG_CRIT, thr->log,
2270Sigor@sysoev.ru                               "SSL_load_client_CA_file(\"%s\") failed",
2280Sigor@sysoev.ru                               ca_certificate);
2290Sigor@sysoev.ru             goto fail;
2300Sigor@sysoev.ru         }
2310Sigor@sysoev.ru 
2320Sigor@sysoev.ru         /*
2330Sigor@sysoev.ru          * SSL_load_client_CA_file() in OpenSSL prior to 0.9.7h and
2340Sigor@sysoev.ru          * 0.9.8 versions always leaves an error in the error queue.
2350Sigor@sysoev.ru          */
2360Sigor@sysoev.ru         ERR_clear_error();
2370Sigor@sysoev.ru 
2380Sigor@sysoev.ru         SSL_CTX_set_client_CA_list(ctx, list);
2390Sigor@sysoev.ru     }
2400Sigor@sysoev.ru 
2410Sigor@sysoev.ru     return NXT_OK;
2420Sigor@sysoev.ru 
2430Sigor@sysoev.ru fail:
2440Sigor@sysoev.ru 
2450Sigor@sysoev.ru     SSL_CTX_free(ctx);
2460Sigor@sysoev.ru 
2470Sigor@sysoev.ru     return NXT_ERROR;
2480Sigor@sysoev.ru }
2490Sigor@sysoev.ru 
2500Sigor@sysoev.ru 
2510Sigor@sysoev.ru static void
252*1Sigor@sysoev.ru nxt_openssl_conn_init(nxt_task_t *task, nxt_ssltls_conf_t *conf,
2530Sigor@sysoev.ru     nxt_event_conn_t *c)
2540Sigor@sysoev.ru {
2550Sigor@sysoev.ru     int                     ret;
2560Sigor@sysoev.ru     SSL                     *s;
2570Sigor@sysoev.ru     SSL_CTX                 *ctx;
2580Sigor@sysoev.ru     nxt_openssl_conn_t      *ssltls;
2590Sigor@sysoev.ru     nxt_mem_pool_cleanup_t  *mpcl;
2600Sigor@sysoev.ru 
2610Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "openssl conn init");
2620Sigor@sysoev.ru 
2630Sigor@sysoev.ru     ssltls = nxt_mem_zalloc(c->mem_pool, sizeof(nxt_openssl_conn_t));
2640Sigor@sysoev.ru     if (ssltls == NULL) {
2650Sigor@sysoev.ru         goto fail;
2660Sigor@sysoev.ru     }
2670Sigor@sysoev.ru 
2680Sigor@sysoev.ru     c->u.ssltls = ssltls;
2690Sigor@sysoev.ru     nxt_buf_mem_set_size(&ssltls->buffer, conf->buffer_size);
2700Sigor@sysoev.ru 
2710Sigor@sysoev.ru     mpcl = nxt_mem_pool_cleanup(c->mem_pool, 0);
2720Sigor@sysoev.ru     if (mpcl == NULL) {
2730Sigor@sysoev.ru         goto fail;
2740Sigor@sysoev.ru     }
2750Sigor@sysoev.ru 
2760Sigor@sysoev.ru     ctx = conf->ctx;
2770Sigor@sysoev.ru 
2780Sigor@sysoev.ru     s = SSL_new(ctx);
2790Sigor@sysoev.ru     if (s == NULL) {
2800Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, c->socket.log, "SSL_new() failed");
2810Sigor@sysoev.ru         goto fail;
2820Sigor@sysoev.ru     }
2830Sigor@sysoev.ru 
2840Sigor@sysoev.ru     ssltls->session = s;
2850Sigor@sysoev.ru     mpcl->handler = nxt_openssl_session_cleanup;
2860Sigor@sysoev.ru     mpcl->data = ssltls;
2870Sigor@sysoev.ru 
2880Sigor@sysoev.ru     ret = SSL_set_fd(s, c->socket.fd);
2890Sigor@sysoev.ru 
2900Sigor@sysoev.ru     if (ret == 0) {
2910Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, c->socket.log,
2920Sigor@sysoev.ru                               "SSL_set_fd(%d) failed", c->socket.fd);
2930Sigor@sysoev.ru         goto fail;
2940Sigor@sysoev.ru     }
2950Sigor@sysoev.ru 
2960Sigor@sysoev.ru     SSL_set_accept_state(s);
2970Sigor@sysoev.ru 
2980Sigor@sysoev.ru     if (SSL_set_ex_data(s, nxt_openssl_connection_index, c) == 0) {
2990Sigor@sysoev.ru         nxt_openssl_log_error(NXT_LOG_CRIT, c->socket.log,
3000Sigor@sysoev.ru                               "SSL_set_ex_data() failed");
3010Sigor@sysoev.ru         goto fail;
3020Sigor@sysoev.ru     }
3030Sigor@sysoev.ru 
3040Sigor@sysoev.ru     c->io = &nxt_openssl_event_conn_io;
3050Sigor@sysoev.ru     c->sendfile = NXT_CONN_SENDFILE_OFF;
3060Sigor@sysoev.ru 
307*1Sigor@sysoev.ru     nxt_openssl_conn_handshake(task, c, c->socket.data);
3080Sigor@sysoev.ru     return;
3090Sigor@sysoev.ru 
3100Sigor@sysoev.ru fail:
3110Sigor@sysoev.ru 
312*1Sigor@sysoev.ru     nxt_event_conn_io_handle(task->thread, c->read_work_queue,
313*1Sigor@sysoev.ru                              c->read_state->error_handler,
314*1Sigor@sysoev.ru                              task, c, c->socket.data);
3150Sigor@sysoev.ru }
3160Sigor@sysoev.ru 
3170Sigor@sysoev.ru 
3180Sigor@sysoev.ru static void
3190Sigor@sysoev.ru nxt_openssl_session_cleanup(void *data)
3200Sigor@sysoev.ru {
3210Sigor@sysoev.ru     nxt_openssl_conn_t  *ssltls;
3220Sigor@sysoev.ru 
3230Sigor@sysoev.ru     ssltls = data;
3240Sigor@sysoev.ru 
3250Sigor@sysoev.ru     nxt_thread_log_debug("openssl session cleanup");
3260Sigor@sysoev.ru 
3270Sigor@sysoev.ru     nxt_free(ssltls->buffer.start);
3280Sigor@sysoev.ru 
3290Sigor@sysoev.ru     SSL_free(ssltls->session);
3300Sigor@sysoev.ru }
3310Sigor@sysoev.ru 
3320Sigor@sysoev.ru 
3330Sigor@sysoev.ru static void
334*1Sigor@sysoev.ru nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data)
3350Sigor@sysoev.ru {
3360Sigor@sysoev.ru     int                 ret;
3370Sigor@sysoev.ru     nxt_int_t           n;
3380Sigor@sysoev.ru     nxt_err_t           err;
3390Sigor@sysoev.ru     nxt_event_conn_t    *c;
3400Sigor@sysoev.ru     nxt_openssl_conn_t  *ssltls;
3410Sigor@sysoev.ru 
3420Sigor@sysoev.ru     c = obj;
3430Sigor@sysoev.ru     ssltls = c->u.ssltls;
3440Sigor@sysoev.ru 
345*1Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake: %d", ssltls->times);
3460Sigor@sysoev.ru 
3470Sigor@sysoev.ru     /* "ssltls->times == 1" is suitable to run SSL_do_handshake() in job. */
3480Sigor@sysoev.ru 
3490Sigor@sysoev.ru     ret = SSL_do_handshake(ssltls->session);
3500Sigor@sysoev.ru 
3510Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
3520Sigor@sysoev.ru 
353*1Sigor@sysoev.ru     nxt_thread_time_debug_update(task->thread);
3540Sigor@sysoev.ru 
355*1Sigor@sysoev.ru     nxt_debug(task, "SSL_do_handshake(%d): %d err:%d", c->socket.fd, ret, err);
3560Sigor@sysoev.ru 
3570Sigor@sysoev.ru     if (ret > 0) {
3580Sigor@sysoev.ru         /* ret == 1, the handshake was successfully completed. */
359*1Sigor@sysoev.ru         nxt_openssl_conn_io_read(task, c, data);
3600Sigor@sysoev.ru         return;
3610Sigor@sysoev.ru     }
3620Sigor@sysoev.ru 
363*1Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(task, c, ret, err,
3640Sigor@sysoev.ru                                     nxt_openssl_conn_handshake);
3650Sigor@sysoev.ru 
3660Sigor@sysoev.ru     if (n == NXT_ERROR) {
3670Sigor@sysoev.ru         nxt_openssl_conn_error(c, err, "SSL_do_handshake(%d) failed",
3680Sigor@sysoev.ru                                c->socket.fd);
3690Sigor@sysoev.ru 
370*1Sigor@sysoev.ru         nxt_event_conn_io_handle(task->thread, c->read_work_queue,
371*1Sigor@sysoev.ru                                  c->read_state->error_handler,
372*1Sigor@sysoev.ru                                  task, c, data);
3730Sigor@sysoev.ru 
3740Sigor@sysoev.ru     } else if (ssltls->ssl_error == SSL_ERROR_WANT_READ && ssltls->times < 2) {
3750Sigor@sysoev.ru         ssltls->times++;
3760Sigor@sysoev.ru     }
3770Sigor@sysoev.ru }
3780Sigor@sysoev.ru 
3790Sigor@sysoev.ru 
3800Sigor@sysoev.ru static void
381*1Sigor@sysoev.ru nxt_openssl_conn_io_read(nxt_task_t *task, void *obj, void *data)
3820Sigor@sysoev.ru {
3830Sigor@sysoev.ru     int                 ret;
3840Sigor@sysoev.ru     nxt_buf_t           *b;
3850Sigor@sysoev.ru     nxt_int_t           n;
3860Sigor@sysoev.ru     nxt_err_t           err;
3870Sigor@sysoev.ru     nxt_event_conn_t    *c;
3880Sigor@sysoev.ru     nxt_work_handler_t  handler;
3890Sigor@sysoev.ru     nxt_openssl_conn_t  *ssltls;
3900Sigor@sysoev.ru 
3910Sigor@sysoev.ru     c = obj;
3920Sigor@sysoev.ru 
393*1Sigor@sysoev.ru     nxt_debug(task, "openssl conn read");
3940Sigor@sysoev.ru 
3950Sigor@sysoev.ru     handler = c->read_state->ready_handler;
3960Sigor@sysoev.ru     b = c->read;
3970Sigor@sysoev.ru 
3980Sigor@sysoev.ru     /* b == NULL is used to test descriptor readiness. */
3990Sigor@sysoev.ru 
4000Sigor@sysoev.ru     if (b != NULL) {
4010Sigor@sysoev.ru         ssltls = c->u.ssltls;
4020Sigor@sysoev.ru 
4030Sigor@sysoev.ru         ret = SSL_read(ssltls->session, b->mem.free, b->mem.end - b->mem.free);
4040Sigor@sysoev.ru 
4050Sigor@sysoev.ru         err = (ret <= 0) ? nxt_socket_errno : 0;
4060Sigor@sysoev.ru 
407*1Sigor@sysoev.ru         nxt_debug(task, "SSL_read(%d, %p, %uz): %d err:%d",
408*1Sigor@sysoev.ru                   c->socket.fd, b->mem.free, b->mem.end - b->mem.free,
409*1Sigor@sysoev.ru                   ret, err);
4100Sigor@sysoev.ru 
4110Sigor@sysoev.ru         if (ret > 0) {
4120Sigor@sysoev.ru             /* c->socket.read_ready is kept. */
4130Sigor@sysoev.ru             b->mem.free += ret;
4140Sigor@sysoev.ru             handler = c->read_state->ready_handler;
4150Sigor@sysoev.ru 
4160Sigor@sysoev.ru         } else {
417*1Sigor@sysoev.ru             n = nxt_openssl_conn_test_error(task, c, ret, err,
4180Sigor@sysoev.ru                                             nxt_openssl_conn_io_read);
4190Sigor@sysoev.ru 
4200Sigor@sysoev.ru             if (nxt_fast_path(n != NXT_ERROR)) {
4210Sigor@sysoev.ru                 return;
4220Sigor@sysoev.ru             }
4230Sigor@sysoev.ru 
4240Sigor@sysoev.ru             nxt_openssl_conn_error(c, err, "SSL_read(%d, %p, %uz) failed",
4250Sigor@sysoev.ru                                    c->socket.fd, b->mem.free,
4260Sigor@sysoev.ru                                    b->mem.end - b->mem.free);
4270Sigor@sysoev.ru 
4280Sigor@sysoev.ru             handler = c->read_state->error_handler;
4290Sigor@sysoev.ru         }
4300Sigor@sysoev.ru     }
4310Sigor@sysoev.ru 
432*1Sigor@sysoev.ru     nxt_event_conn_io_handle(task->thread, c->read_work_queue, handler,
433*1Sigor@sysoev.ru                              task, c, data);
4340Sigor@sysoev.ru }
4350Sigor@sysoev.ru 
4360Sigor@sysoev.ru 
4370Sigor@sysoev.ru static ssize_t
438*1Sigor@sysoev.ru nxt_openssl_conn_io_write_chunk(nxt_event_conn_t *c, nxt_buf_t *b, size_t limit)
4390Sigor@sysoev.ru {
4400Sigor@sysoev.ru     nxt_openssl_conn_t  *ssltls;
4410Sigor@sysoev.ru 
442*1Sigor@sysoev.ru     nxt_debug(c->socket.task, "openssl conn write chunk");
4430Sigor@sysoev.ru 
4440Sigor@sysoev.ru     ssltls = c->u.ssltls;
4450Sigor@sysoev.ru 
4460Sigor@sysoev.ru     return nxt_sendbuf_copy_coalesce(c, &ssltls->buffer, b, limit);
4470Sigor@sysoev.ru }
4480Sigor@sysoev.ru 
4490Sigor@sysoev.ru 
4500Sigor@sysoev.ru static ssize_t
4510Sigor@sysoev.ru nxt_openssl_conn_io_send(nxt_event_conn_t *c, void *buf, size_t size)
4520Sigor@sysoev.ru {
4530Sigor@sysoev.ru     int                 ret;
4540Sigor@sysoev.ru     nxt_err_t           err;
4550Sigor@sysoev.ru     nxt_int_t           n;
4560Sigor@sysoev.ru     nxt_openssl_conn_t  *ssltls;
4570Sigor@sysoev.ru 
4580Sigor@sysoev.ru     ssltls = c->u.ssltls;
4590Sigor@sysoev.ru 
4600Sigor@sysoev.ru     ret = SSL_write(ssltls->session, buf, size);
4610Sigor@sysoev.ru 
4620Sigor@sysoev.ru     if (ret <= 0) {
4630Sigor@sysoev.ru         err = nxt_socket_errno;
4640Sigor@sysoev.ru         c->socket.error = err;
4650Sigor@sysoev.ru 
4660Sigor@sysoev.ru     } else {
4670Sigor@sysoev.ru         err = 0;
4680Sigor@sysoev.ru     }
4690Sigor@sysoev.ru 
4700Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "SSL_write(%d, %p, %uz): %d err:%d",
4710Sigor@sysoev.ru                   c->socket.fd, buf, size, ret, err);
4720Sigor@sysoev.ru 
4730Sigor@sysoev.ru     if (ret > 0) {
4740Sigor@sysoev.ru         return ret;
4750Sigor@sysoev.ru     }
4760Sigor@sysoev.ru 
477*1Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(c->socket.task, c, ret, err,
4780Sigor@sysoev.ru                                     nxt_event_conn_io_write);
4790Sigor@sysoev.ru 
4800Sigor@sysoev.ru     if (n == NXT_ERROR) {
4810Sigor@sysoev.ru         nxt_openssl_conn_error(c, err, "SSL_write(%d, %p, %uz) failed",
4820Sigor@sysoev.ru                                c->socket.fd, buf, size);
4830Sigor@sysoev.ru     }
4840Sigor@sysoev.ru 
4850Sigor@sysoev.ru     return n;
4860Sigor@sysoev.ru }
4870Sigor@sysoev.ru 
4880Sigor@sysoev.ru 
4890Sigor@sysoev.ru static void
490*1Sigor@sysoev.ru nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
4910Sigor@sysoev.ru {
4920Sigor@sysoev.ru     int                 ret, mode;
4930Sigor@sysoev.ru     SSL                 *s;
4940Sigor@sysoev.ru     nxt_err_t           err;
4950Sigor@sysoev.ru     nxt_int_t           n;
4960Sigor@sysoev.ru     nxt_bool_t          quiet, once;
4970Sigor@sysoev.ru     nxt_event_conn_t    *c;
4980Sigor@sysoev.ru     nxt_work_handler_t  handler;
4990Sigor@sysoev.ru     nxt_openssl_conn_t  *ssltls;
5000Sigor@sysoev.ru 
5010Sigor@sysoev.ru     c = obj;
5020Sigor@sysoev.ru 
503*1Sigor@sysoev.ru     nxt_debug(task, "openssl conn shutdown");
5040Sigor@sysoev.ru 
5050Sigor@sysoev.ru     ssltls = c->u.ssltls;
5060Sigor@sysoev.ru     s = ssltls->session;
5070Sigor@sysoev.ru 
5080Sigor@sysoev.ru     if (s == NULL) {
5090Sigor@sysoev.ru         handler = c->write_state->close_handler;
5100Sigor@sysoev.ru         goto done;
5110Sigor@sysoev.ru     }
5120Sigor@sysoev.ru 
5130Sigor@sysoev.ru     mode = SSL_get_shutdown(s);
5140Sigor@sysoev.ru 
5150Sigor@sysoev.ru     if (c->socket.timedout || c->socket.error != 0) {
5160Sigor@sysoev.ru         quiet = 1;
5170Sigor@sysoev.ru 
5180Sigor@sysoev.ru     } else if (c->socket.closed && !(mode & SSL_RECEIVED_SHUTDOWN)) {
5190Sigor@sysoev.ru         quiet = 1;
5200Sigor@sysoev.ru 
5210Sigor@sysoev.ru     } else {
5220Sigor@sysoev.ru         quiet = 0;
5230Sigor@sysoev.ru     }
5240Sigor@sysoev.ru 
5250Sigor@sysoev.ru     SSL_set_quiet_shutdown(s, quiet);
5260Sigor@sysoev.ru 
5270Sigor@sysoev.ru     once = 1;
5280Sigor@sysoev.ru 
5290Sigor@sysoev.ru     for ( ;; ) {
5300Sigor@sysoev.ru         SSL_set_shutdown(s, mode);
5310Sigor@sysoev.ru 
5320Sigor@sysoev.ru         ret = SSL_shutdown(s);
5330Sigor@sysoev.ru 
5340Sigor@sysoev.ru         err = (ret <= 0) ? nxt_socket_errno : 0;
5350Sigor@sysoev.ru 
536*1Sigor@sysoev.ru         nxt_debug(task, "SSL_shutdown(%d, %d, %b): %d err:%d",
537*1Sigor@sysoev.ru                   c->socket.fd, mode, quiet, ret, err);
5380Sigor@sysoev.ru 
5390Sigor@sysoev.ru         if (ret > 0) {
5400Sigor@sysoev.ru             /* ret == 1, the shutdown was successfully completed. */
5410Sigor@sysoev.ru             handler = c->write_state->close_handler;
5420Sigor@sysoev.ru             goto done;
5430Sigor@sysoev.ru         }
5440Sigor@sysoev.ru 
5450Sigor@sysoev.ru         if (ret == 0) {
5460Sigor@sysoev.ru             /*
5470Sigor@sysoev.ru              * If SSL_shutdown() returns 0 then it should be called
5480Sigor@sysoev.ru              * again.  The second SSL_shutdown() call should returns
5490Sigor@sysoev.ru              * -1/SSL_ERROR_WANT_READ or -1/SSL_ERROR_WANT_WRITE.
5500Sigor@sysoev.ru              * OpenSSL prior to 0.9.8m version however never returns
5510Sigor@sysoev.ru              * -1 at all.  Fortunately, OpenSSL internals preserve
5520Sigor@sysoev.ru              * correct status available via SSL_get_error(-1).
5530Sigor@sysoev.ru              */
5540Sigor@sysoev.ru             if (once) {
5550Sigor@sysoev.ru                 mode = SSL_get_shutdown(s);
5560Sigor@sysoev.ru                 once = 0;
5570Sigor@sysoev.ru                 continue;
5580Sigor@sysoev.ru             }
5590Sigor@sysoev.ru 
5600Sigor@sysoev.ru             ret = -1;
5610Sigor@sysoev.ru         }
5620Sigor@sysoev.ru 
5630Sigor@sysoev.ru         /* ret == -1 */
5640Sigor@sysoev.ru 
5650Sigor@sysoev.ru         break;
5660Sigor@sysoev.ru     }
5670Sigor@sysoev.ru 
568*1Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(task, c, ret, err,
5690Sigor@sysoev.ru                                     nxt_openssl_conn_io_shutdown);
5700Sigor@sysoev.ru 
5710Sigor@sysoev.ru     if (nxt_fast_path(n == 0)) {
5720Sigor@sysoev.ru         return;
5730Sigor@sysoev.ru     }
5740Sigor@sysoev.ru 
5750Sigor@sysoev.ru     if (n != NXT_ERROR) {  /* n == NXT_AGAIN */
5760Sigor@sysoev.ru         c->socket.error_handler = c->read_state->error_handler;
577*1Sigor@sysoev.ru         nxt_event_timer_add(task->thread->engine, &c->read_timer, 5000);
5780Sigor@sysoev.ru         return;
5790Sigor@sysoev.ru     }
5800Sigor@sysoev.ru 
5810Sigor@sysoev.ru     nxt_openssl_conn_error(c, err, "SSL_shutdown(%d) failed", c->socket.fd);
5820Sigor@sysoev.ru 
5830Sigor@sysoev.ru     handler = c->write_state->error_handler;
5840Sigor@sysoev.ru 
5850Sigor@sysoev.ru done:
5860Sigor@sysoev.ru 
587*1Sigor@sysoev.ru     nxt_event_conn_io_handle(task->thread, c->write_work_queue, handler,
588*1Sigor@sysoev.ru                              task, c, data);
5890Sigor@sysoev.ru }
5900Sigor@sysoev.ru 
5910Sigor@sysoev.ru 
5920Sigor@sysoev.ru static nxt_int_t
593*1Sigor@sysoev.ru nxt_openssl_conn_test_error(nxt_task_t *task, nxt_event_conn_t *c, int ret,
5940Sigor@sysoev.ru     nxt_err_t sys_err, nxt_work_handler_t handler)
5950Sigor@sysoev.ru {
5960Sigor@sysoev.ru     u_long              lib_err;
5970Sigor@sysoev.ru     nxt_work_queue_t    *wq;
5980Sigor@sysoev.ru     nxt_openssl_conn_t  *ssltls;
5990Sigor@sysoev.ru 
6000Sigor@sysoev.ru     ssltls = c->u.ssltls;
6010Sigor@sysoev.ru 
6020Sigor@sysoev.ru     ssltls->ssl_error = SSL_get_error(ssltls->session, ret);
6030Sigor@sysoev.ru 
6040Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "SSL_get_error(): %d", ssltls->ssl_error);
6050Sigor@sysoev.ru 
6060Sigor@sysoev.ru     switch (ssltls->ssl_error) {
6070Sigor@sysoev.ru 
6080Sigor@sysoev.ru     case SSL_ERROR_WANT_READ:
609*1Sigor@sysoev.ru         nxt_event_fd_block_write(task->thread->engine, &c->socket);
6100Sigor@sysoev.ru 
6110Sigor@sysoev.ru         c->socket.read_ready = 0;
6120Sigor@sysoev.ru         c->socket.read_handler = handler;
6130Sigor@sysoev.ru 
6140Sigor@sysoev.ru         if (nxt_event_fd_is_disabled(c->socket.read)) {
615*1Sigor@sysoev.ru             nxt_event_fd_enable_read(task->thread->engine, &c->socket);
6160Sigor@sysoev.ru         }
6170Sigor@sysoev.ru 
6180Sigor@sysoev.ru         return NXT_AGAIN;
6190Sigor@sysoev.ru 
6200Sigor@sysoev.ru     case SSL_ERROR_WANT_WRITE:
621*1Sigor@sysoev.ru         nxt_event_fd_block_read(task->thread->engine, &c->socket);
6220Sigor@sysoev.ru 
6230Sigor@sysoev.ru         c->socket.write_ready = 0;
6240Sigor@sysoev.ru         c->socket.write_handler = handler;
6250Sigor@sysoev.ru 
6260Sigor@sysoev.ru         if (nxt_event_fd_is_disabled(c->socket.write)) {
627*1Sigor@sysoev.ru             nxt_event_fd_enable_write(task->thread->engine, &c->socket);
6280Sigor@sysoev.ru         }
6290Sigor@sysoev.ru 
6300Sigor@sysoev.ru         return NXT_AGAIN;
6310Sigor@sysoev.ru 
6320Sigor@sysoev.ru     case SSL_ERROR_SYSCALL:
6330Sigor@sysoev.ru 
6340Sigor@sysoev.ru         lib_err = ERR_peek_error();
6350Sigor@sysoev.ru 
636*1Sigor@sysoev.ru         nxt_debug(task, "ERR_peek_error(): %l", lib_err);
6370Sigor@sysoev.ru 
6380Sigor@sysoev.ru         if (sys_err != 0 || lib_err != 0) {
6390Sigor@sysoev.ru             return NXT_ERROR;
6400Sigor@sysoev.ru         }
6410Sigor@sysoev.ru 
6420Sigor@sysoev.ru         /* A connection was just closed. */
6430Sigor@sysoev.ru         c->socket.closed = 1;
6440Sigor@sysoev.ru 
6450Sigor@sysoev.ru         /* Fall through. */
6460Sigor@sysoev.ru 
6470Sigor@sysoev.ru     case SSL_ERROR_ZERO_RETURN:
6480Sigor@sysoev.ru         /* A "close notify" alert. */
6490Sigor@sysoev.ru 
6500Sigor@sysoev.ru         if (c->read_state != NULL) {
6510Sigor@sysoev.ru             wq = c->read_work_queue;
6520Sigor@sysoev.ru             handler = c->read_state->close_handler;
6530Sigor@sysoev.ru 
6540Sigor@sysoev.ru         } else {
6550Sigor@sysoev.ru             wq = c->write_work_queue;
6560Sigor@sysoev.ru             handler = c->write_state->close_handler;
6570Sigor@sysoev.ru         }
6580Sigor@sysoev.ru 
659*1Sigor@sysoev.ru         nxt_event_conn_io_handle(task->thread, wq, handler,
660*1Sigor@sysoev.ru                                  task, c, c->socket.data);
6610Sigor@sysoev.ru 
6620Sigor@sysoev.ru         return 0;
6630Sigor@sysoev.ru 
6640Sigor@sysoev.ru     default: /* SSL_ERROR_SSL, etc. */
6650Sigor@sysoev.ru         c->socket.error = 1000;  /* Nonexistent errno code. */
6660Sigor@sysoev.ru         return NXT_ERROR;
6670Sigor@sysoev.ru     }
6680Sigor@sysoev.ru }
6690Sigor@sysoev.ru 
6700Sigor@sysoev.ru 
6710Sigor@sysoev.ru static void nxt_cdecl
6720Sigor@sysoev.ru nxt_openssl_conn_error(nxt_event_conn_t *c, nxt_err_t err, const char *fmt, ...)
6730Sigor@sysoev.ru {
6740Sigor@sysoev.ru     u_char      *p, *end;
6750Sigor@sysoev.ru     va_list     args;
6760Sigor@sysoev.ru     nxt_uint_t  level;
6770Sigor@sysoev.ru     u_char      msg[NXT_MAX_ERROR_STR];
6780Sigor@sysoev.ru 
6790Sigor@sysoev.ru     c->socket.error = err;
6800Sigor@sysoev.ru     level = nxt_openssl_log_error_level(c, err);
6810Sigor@sysoev.ru 
6820Sigor@sysoev.ru     if (nxt_log_level_enough(c->socket.log, level)) {
6830Sigor@sysoev.ru 
6840Sigor@sysoev.ru         end = msg + sizeof(msg);
6850Sigor@sysoev.ru 
6860Sigor@sysoev.ru         va_start(args, fmt);
6870Sigor@sysoev.ru         p = nxt_vsprintf(msg, end, fmt, args);
6880Sigor@sysoev.ru         va_end(args);
6890Sigor@sysoev.ru 
6900Sigor@sysoev.ru         if (err != 0) {
6910Sigor@sysoev.ru             p = nxt_sprintf(p, end, " %E", err);
6920Sigor@sysoev.ru         }
6930Sigor@sysoev.ru 
6940Sigor@sysoev.ru         p = nxt_openssl_copy_error(p, end);
6950Sigor@sysoev.ru 
6960Sigor@sysoev.ru         nxt_log_error(level, c->socket.log, "%*s", p - msg, msg);
6970Sigor@sysoev.ru 
6980Sigor@sysoev.ru     } else {
6990Sigor@sysoev.ru         ERR_clear_error();
7000Sigor@sysoev.ru     }
7010Sigor@sysoev.ru }
7020Sigor@sysoev.ru 
7030Sigor@sysoev.ru 
7040Sigor@sysoev.ru static nxt_uint_t
7050Sigor@sysoev.ru nxt_openssl_log_error_level(nxt_event_conn_t *c, nxt_err_t err)
7060Sigor@sysoev.ru {
7070Sigor@sysoev.ru     switch (ERR_GET_REASON(ERR_peek_error())) {
7080Sigor@sysoev.ru 
7090Sigor@sysoev.ru     case 0:
7100Sigor@sysoev.ru         return nxt_socket_error_level(err, c->socket.log_error);
7110Sigor@sysoev.ru 
7120Sigor@sysoev.ru     case SSL_R_BAD_CHANGE_CIPHER_SPEC:                    /*  103 */
7130Sigor@sysoev.ru     case SSL_R_BLOCK_CIPHER_PAD_IS_WRONG:                 /*  129 */
7140Sigor@sysoev.ru     case SSL_R_DIGEST_CHECK_FAILED:                       /*  149 */
7150Sigor@sysoev.ru     case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST:             /*  151 */
7160Sigor@sysoev.ru     case SSL_R_EXCESSIVE_MESSAGE_SIZE:                    /*  152 */
7170Sigor@sysoev.ru     case SSL_R_LENGTH_MISMATCH:                           /*  159 */
7180Sigor@sysoev.ru     case SSL_R_NO_CIPHERS_PASSED:                         /*  182 */
7190Sigor@sysoev.ru     case SSL_R_NO_CIPHERS_SPECIFIED:                      /*  183 */
7200Sigor@sysoev.ru     case SSL_R_NO_COMPRESSION_SPECIFIED:                  /*  187 */
7210Sigor@sysoev.ru     case SSL_R_NO_SHARED_CIPHER:                          /*  193 */
7220Sigor@sysoev.ru     case SSL_R_RECORD_LENGTH_MISMATCH:                    /*  213 */
7230Sigor@sysoev.ru #ifdef SSL_R_PARSE_TLSEXT
7240Sigor@sysoev.ru     case SSL_R_PARSE_TLSEXT:                              /*  227 */
7250Sigor@sysoev.ru #endif
7260Sigor@sysoev.ru     case SSL_R_UNEXPECTED_MESSAGE:                        /*  244 */
7270Sigor@sysoev.ru     case SSL_R_UNEXPECTED_RECORD:                         /*  245 */
7280Sigor@sysoev.ru     case SSL_R_UNKNOWN_ALERT_TYPE:                        /*  246 */
7290Sigor@sysoev.ru     case SSL_R_UNKNOWN_PROTOCOL:                          /*  252 */
7300Sigor@sysoev.ru     case SSL_R_WRONG_VERSION_NUMBER:                      /*  267 */
7310Sigor@sysoev.ru     case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC:       /*  281 */
7320Sigor@sysoev.ru #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
7330Sigor@sysoev.ru     case SSL_R_RENEGOTIATE_EXT_TOO_LONG:                  /*  335 */
7340Sigor@sysoev.ru     case SSL_R_RENEGOTIATION_ENCODING_ERR:                /*  336 */
7350Sigor@sysoev.ru     case SSL_R_RENEGOTIATION_MISMATCH:                    /*  337 */
7360Sigor@sysoev.ru #endif
7370Sigor@sysoev.ru #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
7380Sigor@sysoev.ru     case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED:      /*  338 */
7390Sigor@sysoev.ru #endif
7400Sigor@sysoev.ru #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING
7410Sigor@sysoev.ru     case SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING:          /*  345 */
7420Sigor@sysoev.ru #endif
7430Sigor@sysoev.ru     case 1000:/* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
7440Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:            /* 1010 */
7450Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:                /* 1020 */
7460Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:             /* 1021 */
7470Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:               /* 1022 */
7480Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:         /* 1030 */
7490Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:             /* 1040 */
7500Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:             /* 1047 */
7510Sigor@sysoev.ru         break;
7520Sigor@sysoev.ru 
7530Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:                /* 1041 */
7540Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:               /* 1042 */
7550Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:       /* 1043 */
7560Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:           /* 1044 */
7570Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:           /* 1045 */
7580Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:           /* 1046 */
7590Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_UNKNOWN_CA:                    /* 1048 */
7600Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_ACCESS_DENIED:                 /* 1049 */
7610Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECODE_ERROR:                  /* 1050 */
7620Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:                 /* 1051 */
7630Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:            /* 1060 */
7640Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:              /* 1070 */
7650Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:         /* 1071 */
7660Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:                /* 1080 */
7670Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_USER_CANCELLED:                /* 1090 */
7680Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION:              /* 1100 */
7690Sigor@sysoev.ru         return NXT_LOG_ERR;
7700Sigor@sysoev.ru 
7710Sigor@sysoev.ru     default:
7720Sigor@sysoev.ru         return NXT_LOG_CRIT;
7730Sigor@sysoev.ru     }
7740Sigor@sysoev.ru 
7750Sigor@sysoev.ru     return NXT_LOG_INFO;
7760Sigor@sysoev.ru }
7770Sigor@sysoev.ru 
7780Sigor@sysoev.ru 
7790Sigor@sysoev.ru static void nxt_cdecl
7800Sigor@sysoev.ru nxt_openssl_log_error(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...)
7810Sigor@sysoev.ru {
7820Sigor@sysoev.ru     u_char   *p, *end;
7830Sigor@sysoev.ru     va_list  args;
7840Sigor@sysoev.ru     u_char   msg[NXT_MAX_ERROR_STR];
7850Sigor@sysoev.ru 
7860Sigor@sysoev.ru     end = msg + sizeof(msg);
7870Sigor@sysoev.ru 
7880Sigor@sysoev.ru     va_start(args, fmt);
7890Sigor@sysoev.ru     p = nxt_vsprintf(msg, end, fmt, args);
7900Sigor@sysoev.ru     va_end(args);
7910Sigor@sysoev.ru 
7920Sigor@sysoev.ru     p = nxt_openssl_copy_error(p, end);
7930Sigor@sysoev.ru 
7940Sigor@sysoev.ru     nxt_log_error(level, log, "%*s", p - msg, msg);
7950Sigor@sysoev.ru }
7960Sigor@sysoev.ru 
7970Sigor@sysoev.ru 
7980Sigor@sysoev.ru static u_char *
7990Sigor@sysoev.ru nxt_openssl_copy_error(u_char *p, u_char *end)
8000Sigor@sysoev.ru {
8010Sigor@sysoev.ru     int         flags;
8020Sigor@sysoev.ru     u_long      err;
8030Sigor@sysoev.ru     nxt_bool_t  clear;
8040Sigor@sysoev.ru     const char  *data, *delimiter;
8050Sigor@sysoev.ru 
8060Sigor@sysoev.ru     err = ERR_peek_error();
8070Sigor@sysoev.ru     if (err == 0) {
8080Sigor@sysoev.ru         return p;
8090Sigor@sysoev.ru     }
8100Sigor@sysoev.ru 
8110Sigor@sysoev.ru     /* Log the most relevant error message ... */
8120Sigor@sysoev.ru     data = ERR_reason_error_string(err);
8130Sigor@sysoev.ru 
8140Sigor@sysoev.ru     p = nxt_sprintf(p, end, " (%d: %s) (OpenSSL: ", ERR_GET_REASON(err), data);
8150Sigor@sysoev.ru 
8160Sigor@sysoev.ru     /*
8170Sigor@sysoev.ru      * ... followed by all queued cumbersome OpenSSL
8180Sigor@sysoev.ru      * error messages and drain the error queue.
8190Sigor@sysoev.ru      */
8200Sigor@sysoev.ru     delimiter = "";
8210Sigor@sysoev.ru     clear = 0;
8220Sigor@sysoev.ru 
8230Sigor@sysoev.ru     for ( ;; ) {
8240Sigor@sysoev.ru         err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
8250Sigor@sysoev.ru         if (err == 0) {
8260Sigor@sysoev.ru             break;
8270Sigor@sysoev.ru         }
8280Sigor@sysoev.ru 
8290Sigor@sysoev.ru         p = nxt_sprintf(p, end, "%s", delimiter);
8300Sigor@sysoev.ru 
8310Sigor@sysoev.ru         ERR_error_string_n(err, (char *) p, end - p);
8320Sigor@sysoev.ru 
8330Sigor@sysoev.ru         while (p < end && *p != '\0') {
8340Sigor@sysoev.ru             p++;
8350Sigor@sysoev.ru         }
8360Sigor@sysoev.ru 
8370Sigor@sysoev.ru         if ((flags & ERR_TXT_STRING) != 0) {
8380Sigor@sysoev.ru             p = nxt_sprintf(p, end, ":%s", data);
8390Sigor@sysoev.ru         }
8400Sigor@sysoev.ru 
8410Sigor@sysoev.ru         clear |= ((flags & ERR_TXT_MALLOCED) != 0);
8420Sigor@sysoev.ru 
8430Sigor@sysoev.ru         delimiter = "; ";
8440Sigor@sysoev.ru     }
8450Sigor@sysoev.ru 
8460Sigor@sysoev.ru     /* Deallocate additional data. */
8470Sigor@sysoev.ru 
8480Sigor@sysoev.ru     if (clear) {
8490Sigor@sysoev.ru         ERR_clear_error();
8500Sigor@sysoev.ru     }
8510Sigor@sysoev.ru 
8520Sigor@sysoev.ru     if (p < end) {
8530Sigor@sysoev.ru         *p++ = ')';
8540Sigor@sysoev.ru     }
8550Sigor@sysoev.ru 
8560Sigor@sysoev.ru     return p;
8570Sigor@sysoev.ru }
858