xref: /unit/src/nxt_openssl.c (revision 1818)
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>
11*1818Smax.romanov@nginx.com #include <openssl/rand.h>
120Sigor@sysoev.ru 
130Sigor@sysoev.ru 
140Sigor@sysoev.ru typedef struct {
150Sigor@sysoev.ru     SSL            *session;
16771Sigor@sysoev.ru     nxt_conn_t     *conn;
170Sigor@sysoev.ru 
180Sigor@sysoev.ru     int            ssl_error;
190Sigor@sysoev.ru     uint8_t        times;      /* 2 bits */
20771Sigor@sysoev.ru     uint8_t        handshake;  /* 1 bit  */
210Sigor@sysoev.ru 
220Sigor@sysoev.ru     nxt_buf_mem_t  buffer;
230Sigor@sysoev.ru } nxt_openssl_conn_t;
240Sigor@sysoev.ru 
250Sigor@sysoev.ru 
26771Sigor@sysoev.ru typedef enum {
27771Sigor@sysoev.ru     NXT_OPENSSL_HANDSHAKE = 0,
28771Sigor@sysoev.ru     NXT_OPENSSL_READ,
29771Sigor@sysoev.ru     NXT_OPENSSL_WRITE,
30771Sigor@sysoev.ru     NXT_OPENSSL_SHUTDOWN,
31771Sigor@sysoev.ru } nxt_openssl_io_t;
32771Sigor@sysoev.ru 
330Sigor@sysoev.ru 
34771Sigor@sysoev.ru static nxt_int_t nxt_openssl_library_init(nxt_task_t *task);
35771Sigor@sysoev.ru static void nxt_openssl_library_free(nxt_task_t *task);
36771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER < 0x10100004L
37771Sigor@sysoev.ru static nxt_int_t nxt_openssl_locks_init(void);
38771Sigor@sysoev.ru static void nxt_openssl_lock(int mode, int type, const char *file, int line);
39771Sigor@sysoev.ru static unsigned long nxt_openssl_thread_id(void);
40771Sigor@sysoev.ru static void nxt_openssl_locks_free(void);
41771Sigor@sysoev.ru #endif
42771Sigor@sysoev.ru static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
43771Sigor@sysoev.ru     nxt_tls_conf_t *conf);
44833Svbart@nginx.com static nxt_int_t nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd);
45771Sigor@sysoev.ru static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf);
46771Sigor@sysoev.ru static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
4762Sigor@sysoev.ru     nxt_conn_t *c);
481Sigor@sysoev.ru static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data);
49771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
50771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb);
51771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb,
52771Sigor@sysoev.ru     void *buf, size_t size);
531Sigor@sysoev.ru static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
540Sigor@sysoev.ru     void *data);
55771Sigor@sysoev.ru static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c,
56771Sigor@sysoev.ru     int ret, nxt_err_t sys_err, nxt_openssl_io_t io);
57771Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
58771Sigor@sysoev.ru     nxt_err_t err, const char *fmt, ...);
59771Sigor@sysoev.ru static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
600Sigor@sysoev.ru 
610Sigor@sysoev.ru 
62771Sigor@sysoev.ru const nxt_tls_lib_t  nxt_openssl_lib = {
63771Sigor@sysoev.ru     .library_init = nxt_openssl_library_init,
64771Sigor@sysoev.ru     .library_free = nxt_openssl_library_free,
65771Sigor@sysoev.ru 
66771Sigor@sysoev.ru     .server_init = nxt_openssl_server_init,
67771Sigor@sysoev.ru     .server_free = nxt_openssl_server_free,
680Sigor@sysoev.ru };
690Sigor@sysoev.ru 
700Sigor@sysoev.ru 
7162Sigor@sysoev.ru static nxt_conn_io_t  nxt_openssl_conn_io = {
72771Sigor@sysoev.ru     .read = nxt_conn_io_read,
73771Sigor@sysoev.ru     .recvbuf = nxt_openssl_conn_io_recvbuf,
740Sigor@sysoev.ru 
75771Sigor@sysoev.ru     .write = nxt_conn_io_write,
76771Sigor@sysoev.ru     .sendbuf = nxt_openssl_conn_io_sendbuf,
770Sigor@sysoev.ru 
78771Sigor@sysoev.ru     .shutdown = nxt_openssl_conn_io_shutdown,
790Sigor@sysoev.ru };
800Sigor@sysoev.ru 
810Sigor@sysoev.ru 
820Sigor@sysoev.ru static long  nxt_openssl_version;
830Sigor@sysoev.ru static int   nxt_openssl_connection_index;
840Sigor@sysoev.ru 
850Sigor@sysoev.ru 
860Sigor@sysoev.ru static nxt_int_t
87771Sigor@sysoev.ru nxt_openssl_library_init(nxt_task_t *task)
880Sigor@sysoev.ru {
890Sigor@sysoev.ru     int  index;
900Sigor@sysoev.ru 
910Sigor@sysoev.ru     if (nxt_fast_path(nxt_openssl_version != 0)) {
920Sigor@sysoev.ru         return NXT_OK;
930Sigor@sysoev.ru     }
940Sigor@sysoev.ru 
95771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
96771Sigor@sysoev.ru 
97771Sigor@sysoev.ru     OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
980Sigor@sysoev.ru 
99771Sigor@sysoev.ru #else
100771Sigor@sysoev.ru     {
101771Sigor@sysoev.ru         nxt_int_t  ret;
102771Sigor@sysoev.ru 
103771Sigor@sysoev.ru         SSL_load_error_strings();
104771Sigor@sysoev.ru 
105771Sigor@sysoev.ru         OPENSSL_config(NULL);
1060Sigor@sysoev.ru 
107771Sigor@sysoev.ru         /*
108771Sigor@sysoev.ru          * SSL_library_init(3):
109771Sigor@sysoev.ru          *
110771Sigor@sysoev.ru          *   SSL_library_init() always returns "1",
111771Sigor@sysoev.ru          *   so it is safe to discard the return value.
112771Sigor@sysoev.ru          */
113771Sigor@sysoev.ru         (void) SSL_library_init();
114771Sigor@sysoev.ru 
115771Sigor@sysoev.ru         ret = nxt_openssl_locks_init();
116771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
117771Sigor@sysoev.ru             return ret;
118771Sigor@sysoev.ru         }
119771Sigor@sysoev.ru     }
120771Sigor@sysoev.ru 
121771Sigor@sysoev.ru #endif
1220Sigor@sysoev.ru 
1230Sigor@sysoev.ru     nxt_openssl_version = SSLeay();
1240Sigor@sysoev.ru 
125771Sigor@sysoev.ru     nxt_log(task, NXT_LOG_INFO, "%s, %xl",
126771Sigor@sysoev.ru             SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
1270Sigor@sysoev.ru 
1280Sigor@sysoev.ru #ifndef SSL_OP_NO_COMPRESSION
1290Sigor@sysoev.ru     {
1300Sigor@sysoev.ru         /*
1310Sigor@sysoev.ru          * Disable gzip compression in OpenSSL prior to 1.0.0
1320Sigor@sysoev.ru          * version, this saves about 522K per connection.
1330Sigor@sysoev.ru          */
1340Sigor@sysoev.ru         int                 n;
1350Sigor@sysoev.ru         STACK_OF(SSL_COMP)  *ssl_comp_methods;
1360Sigor@sysoev.ru 
1370Sigor@sysoev.ru         ssl_comp_methods = SSL_COMP_get_compression_methods();
1380Sigor@sysoev.ru 
1390Sigor@sysoev.ru         for (n = sk_SSL_COMP_num(ssl_comp_methods); n != 0; n--) {
1400Sigor@sysoev.ru             (void) sk_SSL_COMP_pop(ssl_comp_methods);
1410Sigor@sysoev.ru         }
1420Sigor@sysoev.ru     }
1430Sigor@sysoev.ru #endif
1440Sigor@sysoev.ru 
1450Sigor@sysoev.ru     index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1460Sigor@sysoev.ru 
1470Sigor@sysoev.ru     if (index == -1) {
148771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
1490Sigor@sysoev.ru                               "SSL_get_ex_new_index() failed");
1500Sigor@sysoev.ru         return NXT_ERROR;
1510Sigor@sysoev.ru     }
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru     nxt_openssl_connection_index = index;
1540Sigor@sysoev.ru 
1550Sigor@sysoev.ru     return NXT_OK;
1560Sigor@sysoev.ru }
1570Sigor@sysoev.ru 
1580Sigor@sysoev.ru 
159771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
160771Sigor@sysoev.ru 
161771Sigor@sysoev.ru static void
162771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
163771Sigor@sysoev.ru {
164771Sigor@sysoev.ru }
165771Sigor@sysoev.ru 
166771Sigor@sysoev.ru #else
167771Sigor@sysoev.ru 
168771Sigor@sysoev.ru static nxt_thread_mutex_t  *nxt_openssl_locks;
169771Sigor@sysoev.ru 
1700Sigor@sysoev.ru static nxt_int_t
171771Sigor@sysoev.ru nxt_openssl_locks_init(void)
172771Sigor@sysoev.ru {
173771Sigor@sysoev.ru     int        i, n;
174771Sigor@sysoev.ru     nxt_int_t  ret;
175771Sigor@sysoev.ru 
176771Sigor@sysoev.ru     n = CRYPTO_num_locks();
177771Sigor@sysoev.ru 
178771Sigor@sysoev.ru     nxt_openssl_locks = OPENSSL_malloc(n * sizeof(nxt_thread_mutex_t));
179771Sigor@sysoev.ru     if (nxt_slow_path(nxt_openssl_locks == NULL)) {
180771Sigor@sysoev.ru         return NXT_ERROR;
181771Sigor@sysoev.ru     }
182771Sigor@sysoev.ru 
183771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
184771Sigor@sysoev.ru         ret = nxt_thread_mutex_create(&nxt_openssl_locks[i]);
185771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
186771Sigor@sysoev.ru             return ret;
187771Sigor@sysoev.ru         }
188771Sigor@sysoev.ru     }
189771Sigor@sysoev.ru 
190771Sigor@sysoev.ru     CRYPTO_set_locking_callback(nxt_openssl_lock);
191771Sigor@sysoev.ru 
192771Sigor@sysoev.ru     CRYPTO_set_id_callback(nxt_openssl_thread_id);
193771Sigor@sysoev.ru 
194771Sigor@sysoev.ru     return NXT_OK;
195771Sigor@sysoev.ru }
196771Sigor@sysoev.ru 
197771Sigor@sysoev.ru 
198771Sigor@sysoev.ru static void
199771Sigor@sysoev.ru nxt_openssl_lock(int mode, int type, const char *file, int line)
200771Sigor@sysoev.ru {
201771Sigor@sysoev.ru     nxt_thread_mutex_t  *lock;
202771Sigor@sysoev.ru 
203771Sigor@sysoev.ru     lock = &nxt_openssl_locks[type];
204771Sigor@sysoev.ru 
205771Sigor@sysoev.ru     if ((mode & CRYPTO_LOCK) != 0) {
206771Sigor@sysoev.ru         (void) nxt_thread_mutex_lock(lock);
207771Sigor@sysoev.ru 
208771Sigor@sysoev.ru     } else {
209771Sigor@sysoev.ru         (void) nxt_thread_mutex_unlock(lock);
210771Sigor@sysoev.ru     }
211771Sigor@sysoev.ru }
212771Sigor@sysoev.ru 
213771Sigor@sysoev.ru 
214771Sigor@sysoev.ru static u_long
215771Sigor@sysoev.ru nxt_openssl_thread_id(void)
216771Sigor@sysoev.ru {
217771Sigor@sysoev.ru     return (u_long) nxt_thread_handle();
218771Sigor@sysoev.ru }
219771Sigor@sysoev.ru 
220771Sigor@sysoev.ru 
221771Sigor@sysoev.ru static void
222771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
223771Sigor@sysoev.ru {
224771Sigor@sysoev.ru     nxt_openssl_locks_free();
225771Sigor@sysoev.ru }
226771Sigor@sysoev.ru 
227771Sigor@sysoev.ru 
228771Sigor@sysoev.ru static void
229771Sigor@sysoev.ru nxt_openssl_locks_free(void)
230771Sigor@sysoev.ru {
231771Sigor@sysoev.ru     int  i, n;
232771Sigor@sysoev.ru 
233771Sigor@sysoev.ru     n = CRYPTO_num_locks();
234771Sigor@sysoev.ru 
235771Sigor@sysoev.ru     CRYPTO_set_locking_callback(NULL);
236771Sigor@sysoev.ru 
237771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
238771Sigor@sysoev.ru         nxt_thread_mutex_destroy(&nxt_openssl_locks[i]);
239771Sigor@sysoev.ru     }
240771Sigor@sysoev.ru 
241771Sigor@sysoev.ru     OPENSSL_free(nxt_openssl_locks);
242771Sigor@sysoev.ru }
243771Sigor@sysoev.ru 
244771Sigor@sysoev.ru #endif
245771Sigor@sysoev.ru 
246771Sigor@sysoev.ru 
247771Sigor@sysoev.ru static nxt_int_t
248771Sigor@sysoev.ru nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
2490Sigor@sysoev.ru {
2500Sigor@sysoev.ru     SSL_CTX              *ctx;
251774Svbart@nginx.com     nxt_fd_t             fd;
252774Svbart@nginx.com     const char           *ciphers, *ca_certificate;
2530Sigor@sysoev.ru     STACK_OF(X509_NAME)  *list;
2540Sigor@sysoev.ru 
2550Sigor@sysoev.ru     ctx = SSL_CTX_new(SSLv23_server_method());
2560Sigor@sysoev.ru     if (ctx == NULL) {
257771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_new() failed");
2580Sigor@sysoev.ru         return NXT_ERROR;
2590Sigor@sysoev.ru     }
2600Sigor@sysoev.ru 
2610Sigor@sysoev.ru     conf->ctx = ctx;
2620Sigor@sysoev.ru     conf->conn_init = nxt_openssl_conn_init;
2630Sigor@sysoev.ru 
264771Sigor@sysoev.ru #ifdef SSL_OP_NO_RENEGOTIATION
265771Sigor@sysoev.ru     /* Renegration is not currently supported. */
266771Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
267771Sigor@sysoev.ru #endif
268771Sigor@sysoev.ru 
2690Sigor@sysoev.ru #ifdef SSL_OP_NO_COMPRESSION
2700Sigor@sysoev.ru     /*
2710Sigor@sysoev.ru      * Disable gzip compression in OpenSSL 1.0.0,
2720Sigor@sysoev.ru      * this saves about 522K per connection.
2730Sigor@sysoev.ru      */
2740Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
2750Sigor@sysoev.ru #endif
2760Sigor@sysoev.ru 
2770Sigor@sysoev.ru #ifdef SSL_MODE_RELEASE_BUFFERS
2780Sigor@sysoev.ru 
2790Sigor@sysoev.ru     if (nxt_openssl_version >= 10001078) {
2800Sigor@sysoev.ru         /*
2810Sigor@sysoev.ru          * Allow to release read and write buffers in OpenSSL 1.0.0,
2820Sigor@sysoev.ru          * this saves about 34K per idle connection.  It is not safe
2830Sigor@sysoev.ru          * before OpenSSL 1.0.1h (CVE-2010-5298).
2840Sigor@sysoev.ru          */
2850Sigor@sysoev.ru         SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
2860Sigor@sysoev.ru     }
2870Sigor@sysoev.ru 
2880Sigor@sysoev.ru #endif
2890Sigor@sysoev.ru 
290774Svbart@nginx.com     fd = conf->chain_file;
2910Sigor@sysoev.ru 
292774Svbart@nginx.com     if (nxt_openssl_chain_file(ctx, fd) != NXT_OK) {
293771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
294774Svbart@nginx.com                               "nxt_openssl_chain_file() failed");
2950Sigor@sysoev.ru         goto fail;
2960Sigor@sysoev.ru     }
297774Svbart@nginx.com /*
2980Sigor@sysoev.ru     key = conf->certificate_key;
2990Sigor@sysoev.ru 
3000Sigor@sysoev.ru     if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
301771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3020Sigor@sysoev.ru                               "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
3030Sigor@sysoev.ru                               key);
3040Sigor@sysoev.ru         goto fail;
3050Sigor@sysoev.ru     }
306774Svbart@nginx.com */
3070Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
3080Sigor@sysoev.ru 
3090Sigor@sysoev.ru     if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
310771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3110Sigor@sysoev.ru                               "SSL_CTX_set_cipher_list(\"%s\") failed",
3120Sigor@sysoev.ru                               ciphers);
3130Sigor@sysoev.ru         goto fail;
3140Sigor@sysoev.ru     }
3150Sigor@sysoev.ru 
3160Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
3170Sigor@sysoev.ru 
3180Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
3190Sigor@sysoev.ru 
3200Sigor@sysoev.ru         /* TODO: verify callback */
3210Sigor@sysoev.ru         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
3220Sigor@sysoev.ru 
3230Sigor@sysoev.ru         /* TODO: verify depth */
3240Sigor@sysoev.ru         SSL_CTX_set_verify_depth(ctx, 1);
3250Sigor@sysoev.ru 
3260Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
3270Sigor@sysoev.ru 
3280Sigor@sysoev.ru         if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
329771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3300Sigor@sysoev.ru                               "SSL_CTX_load_verify_locations(\"%s\") failed",
3310Sigor@sysoev.ru                               ca_certificate);
3320Sigor@sysoev.ru             goto fail;
3330Sigor@sysoev.ru         }
3340Sigor@sysoev.ru 
3350Sigor@sysoev.ru         list = SSL_load_client_CA_file(ca_certificate);
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru         if (list == NULL) {
338771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3390Sigor@sysoev.ru                               "SSL_load_client_CA_file(\"%s\") failed",
3400Sigor@sysoev.ru                               ca_certificate);
3410Sigor@sysoev.ru             goto fail;
3420Sigor@sysoev.ru         }
3430Sigor@sysoev.ru 
3440Sigor@sysoev.ru         /*
3450Sigor@sysoev.ru          * SSL_load_client_CA_file() in OpenSSL prior to 0.9.7h and
3460Sigor@sysoev.ru          * 0.9.8 versions always leaves an error in the error queue.
3470Sigor@sysoev.ru          */
3480Sigor@sysoev.ru         ERR_clear_error();
3490Sigor@sysoev.ru 
3500Sigor@sysoev.ru         SSL_CTX_set_client_CA_list(ctx, list);
3510Sigor@sysoev.ru     }
3520Sigor@sysoev.ru 
3530Sigor@sysoev.ru     return NXT_OK;
3540Sigor@sysoev.ru 
3550Sigor@sysoev.ru fail:
3560Sigor@sysoev.ru 
3570Sigor@sysoev.ru     SSL_CTX_free(ctx);
3580Sigor@sysoev.ru 
359*1818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
360*1818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
361*1818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
362*1818Smax.romanov@nginx.com #endif
363*1818Smax.romanov@nginx.com 
3640Sigor@sysoev.ru     return NXT_ERROR;
3650Sigor@sysoev.ru }
3660Sigor@sysoev.ru 
3670Sigor@sysoev.ru 
368833Svbart@nginx.com static nxt_int_t
369774Svbart@nginx.com nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd)
370774Svbart@nginx.com {
371774Svbart@nginx.com     BIO            *bio;
372774Svbart@nginx.com     X509           *cert, *ca;
373774Svbart@nginx.com     long           reason;
374774Svbart@nginx.com     EVP_PKEY       *key;
375833Svbart@nginx.com     nxt_int_t      ret;
376774Svbart@nginx.com 
377774Svbart@nginx.com     bio = BIO_new(BIO_s_fd());
378774Svbart@nginx.com     if (bio == NULL) {
379774Svbart@nginx.com         return NXT_ERROR;
380774Svbart@nginx.com     }
381774Svbart@nginx.com 
382774Svbart@nginx.com     BIO_set_fd(bio, fd, BIO_CLOSE);
383774Svbart@nginx.com 
384774Svbart@nginx.com     ret = NXT_ERROR;
385774Svbart@nginx.com 
386774Svbart@nginx.com     cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
387774Svbart@nginx.com     if (cert == NULL) {
388774Svbart@nginx.com         goto end;
389774Svbart@nginx.com     }
390774Svbart@nginx.com 
391774Svbart@nginx.com     if (SSL_CTX_use_certificate(ctx, cert) != 1) {
392774Svbart@nginx.com         goto end;
393774Svbart@nginx.com     }
394774Svbart@nginx.com 
395774Svbart@nginx.com     for ( ;; ) {
396774Svbart@nginx.com         ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
397774Svbart@nginx.com 
398774Svbart@nginx.com         if (ca == NULL) {
399774Svbart@nginx.com             reason = ERR_GET_REASON(ERR_peek_last_error());
400774Svbart@nginx.com             if (reason != PEM_R_NO_START_LINE) {
401774Svbart@nginx.com                 goto end;
402774Svbart@nginx.com             }
403774Svbart@nginx.com 
404774Svbart@nginx.com             ERR_clear_error();
405774Svbart@nginx.com             break;
406774Svbart@nginx.com         }
407774Svbart@nginx.com 
408774Svbart@nginx.com         /*
409774Svbart@nginx.com          * Note that ca isn't freed if it was successfully added to the chain,
410774Svbart@nginx.com          * while the main certificate needs a X509_free() call, since
411774Svbart@nginx.com          * its reference count is increased by SSL_CTX_use_certificate().
412774Svbart@nginx.com          */
413808Spluknet@nginx.com #ifdef SSL_CTX_add0_chain_cert
414774Svbart@nginx.com         if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) {
415774Svbart@nginx.com #else
416774Svbart@nginx.com         if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) {
417774Svbart@nginx.com #endif
418774Svbart@nginx.com             X509_free(ca);
419774Svbart@nginx.com             goto end;
420774Svbart@nginx.com         }
421774Svbart@nginx.com     }
422774Svbart@nginx.com 
423774Svbart@nginx.com     if (BIO_reset(bio) != 0) {
424774Svbart@nginx.com         goto end;
425774Svbart@nginx.com     }
426774Svbart@nginx.com 
427774Svbart@nginx.com     key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
428774Svbart@nginx.com     if (key == NULL) {
429774Svbart@nginx.com         goto end;
430774Svbart@nginx.com     }
431774Svbart@nginx.com 
432774Svbart@nginx.com     if (SSL_CTX_use_PrivateKey(ctx, key) == 1) {
433774Svbart@nginx.com         ret = NXT_OK;
434774Svbart@nginx.com     }
435774Svbart@nginx.com 
436774Svbart@nginx.com     EVP_PKEY_free(key);
437774Svbart@nginx.com 
438774Svbart@nginx.com end:
439774Svbart@nginx.com 
440774Svbart@nginx.com     X509_free(cert);
441774Svbart@nginx.com     BIO_free(bio);
442774Svbart@nginx.com 
443774Svbart@nginx.com     return ret;
444774Svbart@nginx.com }
445774Svbart@nginx.com 
446774Svbart@nginx.com 
4470Sigor@sysoev.ru static void
448771Sigor@sysoev.ru nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
4490Sigor@sysoev.ru {
450771Sigor@sysoev.ru     SSL_CTX_free(conf->ctx);
451*1818Smax.romanov@nginx.com 
452*1818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
453*1818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
454*1818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
455*1818Smax.romanov@nginx.com #endif
456771Sigor@sysoev.ru }
457771Sigor@sysoev.ru 
458771Sigor@sysoev.ru 
459771Sigor@sysoev.ru static void
460771Sigor@sysoev.ru nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
461771Sigor@sysoev.ru {
462771Sigor@sysoev.ru     int                 ret;
463771Sigor@sysoev.ru     SSL                 *s;
464771Sigor@sysoev.ru     SSL_CTX             *ctx;
465771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
4660Sigor@sysoev.ru 
4670Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "openssl conn init");
4680Sigor@sysoev.ru 
469771Sigor@sysoev.ru     tls = nxt_mp_zget(c->mem_pool, sizeof(nxt_openssl_conn_t));
470771Sigor@sysoev.ru     if (tls == NULL) {
4710Sigor@sysoev.ru         goto fail;
4720Sigor@sysoev.ru     }
4730Sigor@sysoev.ru 
474771Sigor@sysoev.ru     c->u.tls = tls;
475771Sigor@sysoev.ru     nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size);
4760Sigor@sysoev.ru 
4770Sigor@sysoev.ru     ctx = conf->ctx;
4780Sigor@sysoev.ru 
4790Sigor@sysoev.ru     s = SSL_new(ctx);
4800Sigor@sysoev.ru     if (s == NULL) {
481771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_new() failed");
4820Sigor@sysoev.ru         goto fail;
4830Sigor@sysoev.ru     }
4840Sigor@sysoev.ru 
485771Sigor@sysoev.ru     tls->session = s;
486771Sigor@sysoev.ru     tls->conn = c;
4870Sigor@sysoev.ru 
4880Sigor@sysoev.ru     ret = SSL_set_fd(s, c->socket.fd);
4890Sigor@sysoev.ru 
4900Sigor@sysoev.ru     if (ret == 0) {
491771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_fd(%d) failed",
492771Sigor@sysoev.ru                               c->socket.fd);
4930Sigor@sysoev.ru         goto fail;
4940Sigor@sysoev.ru     }
4950Sigor@sysoev.ru 
4960Sigor@sysoev.ru     SSL_set_accept_state(s);
4970Sigor@sysoev.ru 
4980Sigor@sysoev.ru     if (SSL_set_ex_data(s, nxt_openssl_connection_index, c) == 0) {
499771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_ex_data() failed");
5000Sigor@sysoev.ru         goto fail;
5010Sigor@sysoev.ru     }
5020Sigor@sysoev.ru 
50362Sigor@sysoev.ru     c->io = &nxt_openssl_conn_io;
5040Sigor@sysoev.ru     c->sendfile = NXT_CONN_SENDFILE_OFF;
5050Sigor@sysoev.ru 
5061Sigor@sysoev.ru     nxt_openssl_conn_handshake(task, c, c->socket.data);
5070Sigor@sysoev.ru     return;
5080Sigor@sysoev.ru 
5090Sigor@sysoev.ru fail:
5100Sigor@sysoev.ru 
51113Sigor@sysoev.ru     nxt_work_queue_add(c->read_work_queue, c->read_state->error_handler,
51213Sigor@sysoev.ru                        task, c, c->socket.data);
5130Sigor@sysoev.ru }
5140Sigor@sysoev.ru 
5150Sigor@sysoev.ru 
516771Sigor@sysoev.ru nxt_inline void
517836Sigor@sysoev.ru nxt_openssl_conn_free(nxt_task_t *task, nxt_conn_t *c)
5180Sigor@sysoev.ru {
519836Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
520836Sigor@sysoev.ru 
521771Sigor@sysoev.ru     nxt_debug(task, "openssl conn free");
5220Sigor@sysoev.ru 
523836Sigor@sysoev.ru     tls = c->u.tls;
5240Sigor@sysoev.ru 
525836Sigor@sysoev.ru     if (tls != NULL) {
526836Sigor@sysoev.ru         c->u.tls = NULL;
527836Sigor@sysoev.ru         nxt_free(tls->buffer.start);
528836Sigor@sysoev.ru         SSL_free(tls->session);
529836Sigor@sysoev.ru     }
5300Sigor@sysoev.ru }
5310Sigor@sysoev.ru 
5320Sigor@sysoev.ru 
5330Sigor@sysoev.ru static void
5341Sigor@sysoev.ru nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data)
5350Sigor@sysoev.ru {
536771Sigor@sysoev.ru     int                     ret;
537771Sigor@sysoev.ru     nxt_int_t               n;
538771Sigor@sysoev.ru     nxt_err_t               err;
539771Sigor@sysoev.ru     nxt_conn_t              *c;
540771Sigor@sysoev.ru     nxt_work_queue_t        *wq;
541771Sigor@sysoev.ru     nxt_work_handler_t      handler;
542771Sigor@sysoev.ru     nxt_openssl_conn_t      *tls;
543771Sigor@sysoev.ru     const nxt_conn_state_t  *state;
5440Sigor@sysoev.ru 
5450Sigor@sysoev.ru     c = obj;
546836Sigor@sysoev.ru 
547836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake fd:%d", c->socket.fd);
548836Sigor@sysoev.ru 
549836Sigor@sysoev.ru     if (c->socket.error != 0) {
550836Sigor@sysoev.ru         return;
551836Sigor@sysoev.ru     }
552836Sigor@sysoev.ru 
553771Sigor@sysoev.ru     tls = c->u.tls;
5540Sigor@sysoev.ru 
555836Sigor@sysoev.ru     if (tls == NULL) {
556836Sigor@sysoev.ru         return;
557836Sigor@sysoev.ru     }
558836Sigor@sysoev.ru 
559836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake: %d times", tls->times);
5600Sigor@sysoev.ru 
561771Sigor@sysoev.ru     /* "tls->times == 1" is suitable to run SSL_do_handshake() in job. */
5620Sigor@sysoev.ru 
563771Sigor@sysoev.ru     ret = SSL_do_handshake(tls->session);
5640Sigor@sysoev.ru 
5650Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
5660Sigor@sysoev.ru 
5671Sigor@sysoev.ru     nxt_thread_time_debug_update(task->thread);
5680Sigor@sysoev.ru 
5691Sigor@sysoev.ru     nxt_debug(task, "SSL_do_handshake(%d): %d err:%d", c->socket.fd, ret, err);
5700Sigor@sysoev.ru 
571771Sigor@sysoev.ru     state = (c->read_state != NULL) ? c->read_state : c->write_state;
572771Sigor@sysoev.ru 
5730Sigor@sysoev.ru     if (ret > 0) {
5740Sigor@sysoev.ru         /* ret == 1, the handshake was successfully completed. */
575771Sigor@sysoev.ru         tls->handshake = 1;
5760Sigor@sysoev.ru 
577771Sigor@sysoev.ru         if (c->read_state != NULL) {
578771Sigor@sysoev.ru             if (state->io_read_handler != NULL || c->read != NULL) {
579771Sigor@sysoev.ru                 nxt_conn_read(task->thread->engine, c);
5800Sigor@sysoev.ru                 return;
5810Sigor@sysoev.ru             }
5820Sigor@sysoev.ru 
583771Sigor@sysoev.ru         } else {
584771Sigor@sysoev.ru             if (c->write != NULL) {
585771Sigor@sysoev.ru                 nxt_conn_write(task->thread->engine, c);
586771Sigor@sysoev.ru                 return;
587771Sigor@sysoev.ru             }
588771Sigor@sysoev.ru         }
589771Sigor@sysoev.ru 
590771Sigor@sysoev.ru         handler = state->ready_handler;
591771Sigor@sysoev.ru 
592771Sigor@sysoev.ru     } else {
593771Sigor@sysoev.ru         c->socket.read_handler = nxt_openssl_conn_handshake;
594771Sigor@sysoev.ru         c->socket.write_handler = nxt_openssl_conn_handshake;
595771Sigor@sysoev.ru 
596771Sigor@sysoev.ru         n = nxt_openssl_conn_test_error(task, c, ret, err,
597771Sigor@sysoev.ru                                         NXT_OPENSSL_HANDSHAKE);
598771Sigor@sysoev.ru         switch (n) {
5990Sigor@sysoev.ru 
600771Sigor@sysoev.ru         case NXT_AGAIN:
601771Sigor@sysoev.ru             if (tls->ssl_error == SSL_ERROR_WANT_READ && tls->times < 2) {
602771Sigor@sysoev.ru                 tls->times++;
603771Sigor@sysoev.ru             }
604771Sigor@sysoev.ru 
605771Sigor@sysoev.ru             return;
606771Sigor@sysoev.ru 
607771Sigor@sysoev.ru         case 0:
608771Sigor@sysoev.ru             handler = state->close_handler;
609771Sigor@sysoev.ru             break;
610771Sigor@sysoev.ru 
611771Sigor@sysoev.ru         default:
612771Sigor@sysoev.ru         case NXT_ERROR:
613771Sigor@sysoev.ru             nxt_openssl_conn_error(task, err, "SSL_do_handshake(%d) failed",
614771Sigor@sysoev.ru                                    c->socket.fd);
615771Sigor@sysoev.ru 
616771Sigor@sysoev.ru             handler = state->error_handler;
617771Sigor@sysoev.ru             break;
6180Sigor@sysoev.ru         }
6190Sigor@sysoev.ru     }
6200Sigor@sysoev.ru 
621771Sigor@sysoev.ru     wq = (c->read_state != NULL) ? c->read_work_queue : c->write_work_queue;
622771Sigor@sysoev.ru 
623771Sigor@sysoev.ru     nxt_work_queue_add(wq, handler, task, c, data);
6240Sigor@sysoev.ru }
6250Sigor@sysoev.ru 
6260Sigor@sysoev.ru 
6270Sigor@sysoev.ru static ssize_t
628771Sigor@sysoev.ru nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b)
6290Sigor@sysoev.ru {
630771Sigor@sysoev.ru     int                 ret;
631771Sigor@sysoev.ru     size_t              size;
632771Sigor@sysoev.ru     nxt_int_t           n;
633771Sigor@sysoev.ru     nxt_err_t           err;
634771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
635771Sigor@sysoev.ru 
636771Sigor@sysoev.ru     tls = c->u.tls;
637771Sigor@sysoev.ru     size = b->mem.end - b->mem.free;
638771Sigor@sysoev.ru 
639771Sigor@sysoev.ru     ret = SSL_read(tls->session, b->mem.free, size);
640771Sigor@sysoev.ru 
641771Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
642771Sigor@sysoev.ru 
643771Sigor@sysoev.ru     nxt_debug(c->socket.task, "SSL_read(%d, %p, %uz): %d err:%d",
644771Sigor@sysoev.ru               c->socket.fd, b->mem.free, size, ret, err);
6450Sigor@sysoev.ru 
646771Sigor@sysoev.ru     if (ret > 0) {
647771Sigor@sysoev.ru         return ret;
648771Sigor@sysoev.ru     }
6490Sigor@sysoev.ru 
650771Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(c->socket.task, c, ret, err,
651771Sigor@sysoev.ru                                     NXT_OPENSSL_READ);
652771Sigor@sysoev.ru     if (n == NXT_ERROR) {
653771Sigor@sysoev.ru         nxt_openssl_conn_error(c->socket.task, err,
654771Sigor@sysoev.ru                                "SSL_read(%d, %p, %uz) failed",
655771Sigor@sysoev.ru                                c->socket.fd, b->mem.free, size);
656771Sigor@sysoev.ru     }
6570Sigor@sysoev.ru 
658771Sigor@sysoev.ru     return n;
6590Sigor@sysoev.ru }
6600Sigor@sysoev.ru 
6610Sigor@sysoev.ru 
6620Sigor@sysoev.ru static ssize_t
663771Sigor@sysoev.ru nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb)
664771Sigor@sysoev.ru {
665771Sigor@sysoev.ru     nxt_uint_t    niov;
666771Sigor@sysoev.ru     struct iovec  iov;
667771Sigor@sysoev.ru 
668771Sigor@sysoev.ru     niov = nxt_sendbuf_mem_coalesce0(task, sb, &iov, 1);
669771Sigor@sysoev.ru 
670771Sigor@sysoev.ru     if (niov == 0 && sb->sync) {
671771Sigor@sysoev.ru         return 0;
672771Sigor@sysoev.ru     }
673771Sigor@sysoev.ru 
674771Sigor@sysoev.ru     return nxt_openssl_conn_io_send(task, sb, iov.iov_base, iov.iov_len);
675771Sigor@sysoev.ru }
676771Sigor@sysoev.ru 
677771Sigor@sysoev.ru 
678771Sigor@sysoev.ru static ssize_t
679771Sigor@sysoev.ru nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb, void *buf,
680771Sigor@sysoev.ru     size_t size)
6810Sigor@sysoev.ru {
6820Sigor@sysoev.ru     int                 ret;
6830Sigor@sysoev.ru     nxt_err_t           err;
6840Sigor@sysoev.ru     nxt_int_t           n;
685771Sigor@sysoev.ru     nxt_conn_t          *c;
686771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
6870Sigor@sysoev.ru 
688771Sigor@sysoev.ru     tls = sb->tls;
6890Sigor@sysoev.ru 
690771Sigor@sysoev.ru     ret = SSL_write(tls->session, buf, size);
6910Sigor@sysoev.ru 
6921212Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
6930Sigor@sysoev.ru 
694771Sigor@sysoev.ru     nxt_debug(task, "SSL_write(%d, %p, %uz): %d err:%d",
695771Sigor@sysoev.ru               sb->socket, buf, size, ret, err);
6960Sigor@sysoev.ru 
6970Sigor@sysoev.ru     if (ret > 0) {
6980Sigor@sysoev.ru         return ret;
6990Sigor@sysoev.ru     }
7000Sigor@sysoev.ru 
701771Sigor@sysoev.ru     c = tls->conn;
702771Sigor@sysoev.ru     c->socket.write_ready = sb->ready;
703771Sigor@sysoev.ru 
704771Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(task, c, ret, err, NXT_OPENSSL_WRITE);
705771Sigor@sysoev.ru 
706771Sigor@sysoev.ru     sb->ready = c->socket.write_ready;
7070Sigor@sysoev.ru 
7080Sigor@sysoev.ru     if (n == NXT_ERROR) {
7091212Sigor@sysoev.ru         sb->error = c->socket.error;
710771Sigor@sysoev.ru         nxt_openssl_conn_error(task, err, "SSL_write(%d, %p, %uz) failed",
711771Sigor@sysoev.ru                                sb->socket, buf, size);
7120Sigor@sysoev.ru     }
7130Sigor@sysoev.ru 
7140Sigor@sysoev.ru     return n;
7150Sigor@sysoev.ru }
7160Sigor@sysoev.ru 
7170Sigor@sysoev.ru 
7180Sigor@sysoev.ru static void
7191Sigor@sysoev.ru nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
7200Sigor@sysoev.ru {
7210Sigor@sysoev.ru     int                 ret, mode;
7220Sigor@sysoev.ru     SSL                 *s;
7230Sigor@sysoev.ru     nxt_err_t           err;
7240Sigor@sysoev.ru     nxt_int_t           n;
7250Sigor@sysoev.ru     nxt_bool_t          quiet, once;
72662Sigor@sysoev.ru     nxt_conn_t          *c;
727771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
7280Sigor@sysoev.ru     nxt_work_handler_t  handler;
7290Sigor@sysoev.ru 
7300Sigor@sysoev.ru     c = obj;
7310Sigor@sysoev.ru 
732836Sigor@sysoev.ru     nxt_debug(task, "openssl conn shutdown fd:%d", c->socket.fd);
733836Sigor@sysoev.ru 
734771Sigor@sysoev.ru     c->read_state = NULL;
735771Sigor@sysoev.ru     tls = c->u.tls;
736836Sigor@sysoev.ru 
737836Sigor@sysoev.ru     if (tls == NULL) {
738836Sigor@sysoev.ru         return;
739836Sigor@sysoev.ru     }
740836Sigor@sysoev.ru 
741771Sigor@sysoev.ru     s = tls->session;
7420Sigor@sysoev.ru 
743771Sigor@sysoev.ru     if (s == NULL || !tls->handshake) {
744771Sigor@sysoev.ru         handler = c->write_state->ready_handler;
7450Sigor@sysoev.ru         goto done;
7460Sigor@sysoev.ru     }
7470Sigor@sysoev.ru 
7480Sigor@sysoev.ru     mode = SSL_get_shutdown(s);
7490Sigor@sysoev.ru 
7500Sigor@sysoev.ru     if (c->socket.timedout || c->socket.error != 0) {
7510Sigor@sysoev.ru         quiet = 1;
7520Sigor@sysoev.ru 
7530Sigor@sysoev.ru     } else if (c->socket.closed && !(mode & SSL_RECEIVED_SHUTDOWN)) {
7540Sigor@sysoev.ru         quiet = 1;
7550Sigor@sysoev.ru 
7560Sigor@sysoev.ru     } else {
7570Sigor@sysoev.ru         quiet = 0;
7580Sigor@sysoev.ru     }
7590Sigor@sysoev.ru 
7600Sigor@sysoev.ru     SSL_set_quiet_shutdown(s, quiet);
7610Sigor@sysoev.ru 
7620Sigor@sysoev.ru     once = 1;
7630Sigor@sysoev.ru 
7640Sigor@sysoev.ru     for ( ;; ) {
7650Sigor@sysoev.ru         SSL_set_shutdown(s, mode);
7660Sigor@sysoev.ru 
7670Sigor@sysoev.ru         ret = SSL_shutdown(s);
7680Sigor@sysoev.ru 
7690Sigor@sysoev.ru         err = (ret <= 0) ? nxt_socket_errno : 0;
7700Sigor@sysoev.ru 
7711Sigor@sysoev.ru         nxt_debug(task, "SSL_shutdown(%d, %d, %b): %d err:%d",
7721Sigor@sysoev.ru                   c->socket.fd, mode, quiet, ret, err);
7730Sigor@sysoev.ru 
7740Sigor@sysoev.ru         if (ret > 0) {
7750Sigor@sysoev.ru             /* ret == 1, the shutdown was successfully completed. */
776771Sigor@sysoev.ru             handler = c->write_state->ready_handler;
7770Sigor@sysoev.ru             goto done;
7780Sigor@sysoev.ru         }
7790Sigor@sysoev.ru 
7800Sigor@sysoev.ru         if (ret == 0) {
7810Sigor@sysoev.ru             /*
7820Sigor@sysoev.ru              * If SSL_shutdown() returns 0 then it should be called
783771Sigor@sysoev.ru              * again.  The second SSL_shutdown() call should return
7840Sigor@sysoev.ru              * -1/SSL_ERROR_WANT_READ or -1/SSL_ERROR_WANT_WRITE.
7850Sigor@sysoev.ru              * OpenSSL prior to 0.9.8m version however never returns
786771Sigor@sysoev.ru              * -1 at all.  Fortunately, OpenSSL preserves internally
7870Sigor@sysoev.ru              * correct status available via SSL_get_error(-1).
7880Sigor@sysoev.ru              */
7890Sigor@sysoev.ru             if (once) {
790771Sigor@sysoev.ru                 once = 0;
7910Sigor@sysoev.ru                 mode = SSL_get_shutdown(s);
7920Sigor@sysoev.ru                 continue;
7930Sigor@sysoev.ru             }
7940Sigor@sysoev.ru 
7950Sigor@sysoev.ru             ret = -1;
7960Sigor@sysoev.ru         }
7970Sigor@sysoev.ru 
7980Sigor@sysoev.ru         /* ret == -1 */
7990Sigor@sysoev.ru 
8000Sigor@sysoev.ru         break;
8010Sigor@sysoev.ru     }
8020Sigor@sysoev.ru 
803771Sigor@sysoev.ru     c->socket.read_handler = nxt_openssl_conn_io_shutdown;
804771Sigor@sysoev.ru     c->socket.write_handler = nxt_openssl_conn_io_shutdown;
805771Sigor@sysoev.ru     c->socket.error_handler = c->write_state->error_handler;
806771Sigor@sysoev.ru 
807771Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(task, c, ret, err, NXT_OPENSSL_SHUTDOWN);
808771Sigor@sysoev.ru 
809771Sigor@sysoev.ru     switch (n) {
8100Sigor@sysoev.ru 
811771Sigor@sysoev.ru     case 0:
812771Sigor@sysoev.ru         handler = c->write_state->close_handler;
813771Sigor@sysoev.ru         break;
814771Sigor@sysoev.ru 
815771Sigor@sysoev.ru     case NXT_AGAIN:
816771Sigor@sysoev.ru         nxt_timer_add(task->thread->engine, &c->read_timer, 5000);
8170Sigor@sysoev.ru         return;
818771Sigor@sysoev.ru 
819771Sigor@sysoev.ru     default:
820771Sigor@sysoev.ru     case NXT_ERROR:
821771Sigor@sysoev.ru         nxt_openssl_conn_error(task, err, "SSL_shutdown(%d) failed",
822771Sigor@sysoev.ru                                c->socket.fd);
823771Sigor@sysoev.ru         handler = c->write_state->error_handler;
8240Sigor@sysoev.ru     }
8250Sigor@sysoev.ru 
826771Sigor@sysoev.ru done:
8270Sigor@sysoev.ru 
828836Sigor@sysoev.ru     nxt_openssl_conn_free(task, c);
8290Sigor@sysoev.ru 
83013Sigor@sysoev.ru     nxt_work_queue_add(c->write_work_queue, handler, task, c, data);
8310Sigor@sysoev.ru }
8320Sigor@sysoev.ru 
8330Sigor@sysoev.ru 
8340Sigor@sysoev.ru static nxt_int_t
83562Sigor@sysoev.ru nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret,
836771Sigor@sysoev.ru     nxt_err_t sys_err, nxt_openssl_io_t io)
8370Sigor@sysoev.ru {
8380Sigor@sysoev.ru     u_long              lib_err;
839771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
8400Sigor@sysoev.ru 
841771Sigor@sysoev.ru     tls = c->u.tls;
8420Sigor@sysoev.ru 
843771Sigor@sysoev.ru     tls->ssl_error = SSL_get_error(tls->session, ret);
8440Sigor@sysoev.ru 
845771Sigor@sysoev.ru     nxt_debug(task, "SSL_get_error(): %d", tls->ssl_error);
8460Sigor@sysoev.ru 
847771Sigor@sysoev.ru     switch (tls->ssl_error) {
8480Sigor@sysoev.ru 
8490Sigor@sysoev.ru     case SSL_ERROR_WANT_READ:
850990Sigor@sysoev.ru         c->socket.read_ready = 0;
851771Sigor@sysoev.ru 
852771Sigor@sysoev.ru         if (io != NXT_OPENSSL_READ) {
853771Sigor@sysoev.ru             nxt_fd_event_block_write(task->thread->engine, &c->socket);
8540Sigor@sysoev.ru 
855771Sigor@sysoev.ru             if (nxt_fd_event_is_disabled(c->socket.read)) {
856771Sigor@sysoev.ru                 nxt_fd_event_enable_read(task->thread->engine, &c->socket);
857771Sigor@sysoev.ru             }
8580Sigor@sysoev.ru         }
8590Sigor@sysoev.ru 
8600Sigor@sysoev.ru         return NXT_AGAIN;
8610Sigor@sysoev.ru 
8620Sigor@sysoev.ru     case SSL_ERROR_WANT_WRITE:
863990Sigor@sysoev.ru         c->socket.write_ready = 0;
864771Sigor@sysoev.ru 
865771Sigor@sysoev.ru         if (io != NXT_OPENSSL_WRITE) {
866771Sigor@sysoev.ru             nxt_fd_event_block_read(task->thread->engine, &c->socket);
8670Sigor@sysoev.ru 
868771Sigor@sysoev.ru             if (nxt_fd_event_is_disabled(c->socket.write)) {
869771Sigor@sysoev.ru                 nxt_fd_event_enable_write(task->thread->engine, &c->socket);
870771Sigor@sysoev.ru             }
8710Sigor@sysoev.ru         }
8720Sigor@sysoev.ru 
8730Sigor@sysoev.ru         return NXT_AGAIN;
8740Sigor@sysoev.ru 
8750Sigor@sysoev.ru     case SSL_ERROR_SYSCALL:
8760Sigor@sysoev.ru         lib_err = ERR_peek_error();
8770Sigor@sysoev.ru 
8781Sigor@sysoev.ru         nxt_debug(task, "ERR_peek_error(): %l", lib_err);
8790Sigor@sysoev.ru 
8800Sigor@sysoev.ru         if (sys_err != 0 || lib_err != 0) {
8811212Sigor@sysoev.ru             c->socket.error = sys_err;
8820Sigor@sysoev.ru             return NXT_ERROR;
8830Sigor@sysoev.ru         }
8840Sigor@sysoev.ru 
8850Sigor@sysoev.ru         /* A connection was just closed. */
8860Sigor@sysoev.ru         c->socket.closed = 1;
887771Sigor@sysoev.ru         return 0;
8880Sigor@sysoev.ru 
8890Sigor@sysoev.ru     case SSL_ERROR_ZERO_RETURN:
8900Sigor@sysoev.ru         /* A "close notify" alert. */
8910Sigor@sysoev.ru         return 0;
8920Sigor@sysoev.ru 
8930Sigor@sysoev.ru     default: /* SSL_ERROR_SSL, etc. */
8940Sigor@sysoev.ru         c->socket.error = 1000;  /* Nonexistent errno code. */
8950Sigor@sysoev.ru         return NXT_ERROR;
8960Sigor@sysoev.ru     }
8970Sigor@sysoev.ru }
8980Sigor@sysoev.ru 
8990Sigor@sysoev.ru 
9000Sigor@sysoev.ru static void nxt_cdecl
901771Sigor@sysoev.ru nxt_openssl_conn_error(nxt_task_t *task, nxt_err_t err, const char *fmt, ...)
9020Sigor@sysoev.ru {
9030Sigor@sysoev.ru     u_char      *p, *end;
9040Sigor@sysoev.ru     va_list     args;
9050Sigor@sysoev.ru     nxt_uint_t  level;
9060Sigor@sysoev.ru     u_char      msg[NXT_MAX_ERROR_STR];
9070Sigor@sysoev.ru 
908771Sigor@sysoev.ru     level = nxt_openssl_log_error_level(err);
9090Sigor@sysoev.ru 
910771Sigor@sysoev.ru     if (nxt_log_level_enough(task->log, level)) {
9110Sigor@sysoev.ru 
9120Sigor@sysoev.ru         end = msg + sizeof(msg);
9130Sigor@sysoev.ru 
9140Sigor@sysoev.ru         va_start(args, fmt);
9150Sigor@sysoev.ru         p = nxt_vsprintf(msg, end, fmt, args);
9160Sigor@sysoev.ru         va_end(args);
9170Sigor@sysoev.ru 
9180Sigor@sysoev.ru         if (err != 0) {
9190Sigor@sysoev.ru             p = nxt_sprintf(p, end, " %E", err);
9200Sigor@sysoev.ru         }
9210Sigor@sysoev.ru 
9220Sigor@sysoev.ru         p = nxt_openssl_copy_error(p, end);
9230Sigor@sysoev.ru 
924771Sigor@sysoev.ru         nxt_log(task, level, "%*s", p - msg, msg);
9250Sigor@sysoev.ru 
9260Sigor@sysoev.ru     } else {
9270Sigor@sysoev.ru         ERR_clear_error();
9280Sigor@sysoev.ru     }
9290Sigor@sysoev.ru }
9300Sigor@sysoev.ru 
9310Sigor@sysoev.ru 
9320Sigor@sysoev.ru static nxt_uint_t
933771Sigor@sysoev.ru nxt_openssl_log_error_level(nxt_err_t err)
9340Sigor@sysoev.ru {
9350Sigor@sysoev.ru     switch (ERR_GET_REASON(ERR_peek_error())) {
9360Sigor@sysoev.ru 
9370Sigor@sysoev.ru     case 0:
93813Sigor@sysoev.ru         return nxt_socket_error_level(err);
9390Sigor@sysoev.ru 
9400Sigor@sysoev.ru     case SSL_R_BAD_CHANGE_CIPHER_SPEC:                    /*  103 */
9410Sigor@sysoev.ru     case SSL_R_BLOCK_CIPHER_PAD_IS_WRONG:                 /*  129 */
9420Sigor@sysoev.ru     case SSL_R_DIGEST_CHECK_FAILED:                       /*  149 */
9430Sigor@sysoev.ru     case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST:             /*  151 */
9440Sigor@sysoev.ru     case SSL_R_EXCESSIVE_MESSAGE_SIZE:                    /*  152 */
9450Sigor@sysoev.ru     case SSL_R_LENGTH_MISMATCH:                           /*  159 */
946771Sigor@sysoev.ru #ifdef SSL_R_NO_CIPHERS_PASSED
9470Sigor@sysoev.ru     case SSL_R_NO_CIPHERS_PASSED:                         /*  182 */
948771Sigor@sysoev.ru #endif
9490Sigor@sysoev.ru     case SSL_R_NO_CIPHERS_SPECIFIED:                      /*  183 */
9500Sigor@sysoev.ru     case SSL_R_NO_COMPRESSION_SPECIFIED:                  /*  187 */
9510Sigor@sysoev.ru     case SSL_R_NO_SHARED_CIPHER:                          /*  193 */
9520Sigor@sysoev.ru     case SSL_R_RECORD_LENGTH_MISMATCH:                    /*  213 */
9530Sigor@sysoev.ru #ifdef SSL_R_PARSE_TLSEXT
9540Sigor@sysoev.ru     case SSL_R_PARSE_TLSEXT:                              /*  227 */
9550Sigor@sysoev.ru #endif
9560Sigor@sysoev.ru     case SSL_R_UNEXPECTED_MESSAGE:                        /*  244 */
9570Sigor@sysoev.ru     case SSL_R_UNEXPECTED_RECORD:                         /*  245 */
9580Sigor@sysoev.ru     case SSL_R_UNKNOWN_ALERT_TYPE:                        /*  246 */
9590Sigor@sysoev.ru     case SSL_R_UNKNOWN_PROTOCOL:                          /*  252 */
9600Sigor@sysoev.ru     case SSL_R_WRONG_VERSION_NUMBER:                      /*  267 */
9610Sigor@sysoev.ru     case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC:       /*  281 */
9620Sigor@sysoev.ru #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
9630Sigor@sysoev.ru     case SSL_R_RENEGOTIATE_EXT_TOO_LONG:                  /*  335 */
9640Sigor@sysoev.ru     case SSL_R_RENEGOTIATION_ENCODING_ERR:                /*  336 */
9650Sigor@sysoev.ru     case SSL_R_RENEGOTIATION_MISMATCH:                    /*  337 */
9660Sigor@sysoev.ru #endif
9670Sigor@sysoev.ru #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
9680Sigor@sysoev.ru     case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED:      /*  338 */
9690Sigor@sysoev.ru #endif
9700Sigor@sysoev.ru #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING
9710Sigor@sysoev.ru     case SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING:          /*  345 */
9720Sigor@sysoev.ru #endif
9730Sigor@sysoev.ru     case 1000:/* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
9740Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:            /* 1010 */
9750Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:                /* 1020 */
9760Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:             /* 1021 */
9770Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:               /* 1022 */
9780Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:         /* 1030 */
9790Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:             /* 1040 */
9800Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:             /* 1047 */
9810Sigor@sysoev.ru         break;
9820Sigor@sysoev.ru 
9830Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:                /* 1041 */
9840Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:               /* 1042 */
9850Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:       /* 1043 */
9860Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:           /* 1044 */
9870Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:           /* 1045 */
9880Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:           /* 1046 */
9890Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_UNKNOWN_CA:                    /* 1048 */
9900Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_ACCESS_DENIED:                 /* 1049 */
9910Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECODE_ERROR:                  /* 1050 */
9920Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:                 /* 1051 */
9930Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:            /* 1060 */
9940Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:              /* 1070 */
9950Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:         /* 1071 */
9960Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:                /* 1080 */
9970Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_USER_CANCELLED:                /* 1090 */
9980Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION:              /* 1100 */
9990Sigor@sysoev.ru         return NXT_LOG_ERR;
10000Sigor@sysoev.ru 
10010Sigor@sysoev.ru     default:
1002564Svbart@nginx.com         return NXT_LOG_ALERT;
10030Sigor@sysoev.ru     }
10040Sigor@sysoev.ru 
10050Sigor@sysoev.ru     return NXT_LOG_INFO;
10060Sigor@sysoev.ru }
10070Sigor@sysoev.ru 
10080Sigor@sysoev.ru 
1009771Sigor@sysoev.ru void nxt_cdecl
1010771Sigor@sysoev.ru nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level, const char *fmt, ...)
10110Sigor@sysoev.ru {
10120Sigor@sysoev.ru     u_char   *p, *end;
10130Sigor@sysoev.ru     va_list  args;
10140Sigor@sysoev.ru     u_char   msg[NXT_MAX_ERROR_STR];
10150Sigor@sysoev.ru 
10160Sigor@sysoev.ru     end = msg + sizeof(msg);
10170Sigor@sysoev.ru 
10180Sigor@sysoev.ru     va_start(args, fmt);
10190Sigor@sysoev.ru     p = nxt_vsprintf(msg, end, fmt, args);
10200Sigor@sysoev.ru     va_end(args);
10210Sigor@sysoev.ru 
10220Sigor@sysoev.ru     p = nxt_openssl_copy_error(p, end);
10230Sigor@sysoev.ru 
1024771Sigor@sysoev.ru     nxt_log(task, level, "%*s", p - msg, msg);
10250Sigor@sysoev.ru }
10260Sigor@sysoev.ru 
10270Sigor@sysoev.ru 
1028771Sigor@sysoev.ru u_char *
10290Sigor@sysoev.ru nxt_openssl_copy_error(u_char *p, u_char *end)
10300Sigor@sysoev.ru {
10310Sigor@sysoev.ru     int         flags;
10320Sigor@sysoev.ru     u_long      err;
10330Sigor@sysoev.ru     nxt_bool_t  clear;
10340Sigor@sysoev.ru     const char  *data, *delimiter;
10350Sigor@sysoev.ru 
10360Sigor@sysoev.ru     err = ERR_peek_error();
10370Sigor@sysoev.ru     if (err == 0) {
10380Sigor@sysoev.ru         return p;
10390Sigor@sysoev.ru     }
10400Sigor@sysoev.ru 
10410Sigor@sysoev.ru     /* Log the most relevant error message ... */
10420Sigor@sysoev.ru     data = ERR_reason_error_string(err);
10430Sigor@sysoev.ru 
10440Sigor@sysoev.ru     p = nxt_sprintf(p, end, " (%d: %s) (OpenSSL: ", ERR_GET_REASON(err), data);
10450Sigor@sysoev.ru 
10460Sigor@sysoev.ru     /*
1047771Sigor@sysoev.ru      * ... followed by all queued cumbersome OpenSSL error messages
1048771Sigor@sysoev.ru      * and drain the error queue.
10490Sigor@sysoev.ru      */
10500Sigor@sysoev.ru     delimiter = "";
10510Sigor@sysoev.ru     clear = 0;
10520Sigor@sysoev.ru 
10530Sigor@sysoev.ru     for ( ;; ) {
10540Sigor@sysoev.ru         err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
10550Sigor@sysoev.ru         if (err == 0) {
10560Sigor@sysoev.ru             break;
10570Sigor@sysoev.ru         }
10580Sigor@sysoev.ru 
10590Sigor@sysoev.ru         p = nxt_sprintf(p, end, "%s", delimiter);
10600Sigor@sysoev.ru 
10610Sigor@sysoev.ru         ERR_error_string_n(err, (char *) p, end - p);
10620Sigor@sysoev.ru 
10630Sigor@sysoev.ru         while (p < end && *p != '\0') {
10640Sigor@sysoev.ru             p++;
10650Sigor@sysoev.ru         }
10660Sigor@sysoev.ru 
10670Sigor@sysoev.ru         if ((flags & ERR_TXT_STRING) != 0) {
10680Sigor@sysoev.ru             p = nxt_sprintf(p, end, ":%s", data);
10690Sigor@sysoev.ru         }
10700Sigor@sysoev.ru 
10710Sigor@sysoev.ru         clear |= ((flags & ERR_TXT_MALLOCED) != 0);
10720Sigor@sysoev.ru 
10730Sigor@sysoev.ru         delimiter = "; ";
10740Sigor@sysoev.ru     }
10750Sigor@sysoev.ru 
10760Sigor@sysoev.ru     /* Deallocate additional data. */
10770Sigor@sysoev.ru 
10780Sigor@sysoev.ru     if (clear) {
10790Sigor@sysoev.ru         ERR_clear_error();
10800Sigor@sysoev.ru     }
10810Sigor@sysoev.ru 
10820Sigor@sysoev.ru     if (p < end) {
10830Sigor@sysoev.ru         *p++ = ')';
10840Sigor@sysoev.ru     }
10850Sigor@sysoev.ru 
10860Sigor@sysoev.ru     return p;
10870Sigor@sysoev.ru }
1088