xref: /unit/src/nxt_openssl.c (revision 1884)
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>
111818Smax.romanov@nginx.com #include <openssl/rand.h>
121828Sa.suvorov@f5.com #include <openssl/x509v3.h>
130Sigor@sysoev.ru 
140Sigor@sysoev.ru 
150Sigor@sysoev.ru typedef struct {
161828Sa.suvorov@f5.com     SSL             *session;
171828Sa.suvorov@f5.com     nxt_conn_t      *conn;
180Sigor@sysoev.ru 
191828Sa.suvorov@f5.com     int             ssl_error;
201828Sa.suvorov@f5.com     uint8_t         times;      /* 2 bits */
211828Sa.suvorov@f5.com     uint8_t         handshake;  /* 1 bit  */
220Sigor@sysoev.ru 
231828Sa.suvorov@f5.com     nxt_tls_conf_t  *conf;
241828Sa.suvorov@f5.com     nxt_buf_mem_t   buffer;
250Sigor@sysoev.ru } nxt_openssl_conn_t;
260Sigor@sysoev.ru 
270Sigor@sysoev.ru 
28771Sigor@sysoev.ru typedef enum {
29771Sigor@sysoev.ru     NXT_OPENSSL_HANDSHAKE = 0,
30771Sigor@sysoev.ru     NXT_OPENSSL_READ,
31771Sigor@sysoev.ru     NXT_OPENSSL_WRITE,
32771Sigor@sysoev.ru     NXT_OPENSSL_SHUTDOWN,
33771Sigor@sysoev.ru } nxt_openssl_io_t;
34771Sigor@sysoev.ru 
350Sigor@sysoev.ru 
36771Sigor@sysoev.ru static nxt_int_t nxt_openssl_library_init(nxt_task_t *task);
37771Sigor@sysoev.ru static void nxt_openssl_library_free(nxt_task_t *task);
38771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER < 0x10100004L
39771Sigor@sysoev.ru static nxt_int_t nxt_openssl_locks_init(void);
40771Sigor@sysoev.ru static void nxt_openssl_lock(int mode, int type, const char *file, int line);
41771Sigor@sysoev.ru static unsigned long nxt_openssl_thread_id(void);
42771Sigor@sysoev.ru static void nxt_openssl_locks_free(void);
43771Sigor@sysoev.ru #endif
44771Sigor@sysoev.ru static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
451828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t last);
461828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
471828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
481828Sa.suvorov@f5.com static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
491828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp);
501828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
511828Sa.suvorov@f5.com     void *data);
521828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_insert(nxt_task_t *task,
531828Sa.suvorov@f5.com     nxt_lvlhsh_t *lvlhsh, nxt_tls_bundle_hash_item_t *item, nxt_mp_t * mp);
541828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_servername(SSL *s, int *ad, void *arg);
551828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *nxt_openssl_find_ctx(nxt_tls_conf_t *conf,
561828Sa.suvorov@f5.com     nxt_str_t *sn);
57771Sigor@sysoev.ru static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf);
58771Sigor@sysoev.ru static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
5962Sigor@sysoev.ru     nxt_conn_t *c);
601Sigor@sysoev.ru static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data);
61771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
62771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb);
63771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb,
64771Sigor@sysoev.ru     void *buf, size_t size);
651Sigor@sysoev.ru static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
660Sigor@sysoev.ru     void *data);
67771Sigor@sysoev.ru static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c,
68771Sigor@sysoev.ru     int ret, nxt_err_t sys_err, nxt_openssl_io_t io);
69*1884Sa.suvorov@f5.com static void nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj,
70*1884Sa.suvorov@f5.com     void *data);
71771Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
72771Sigor@sysoev.ru     nxt_err_t err, const char *fmt, ...);
73771Sigor@sysoev.ru static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
740Sigor@sysoev.ru 
750Sigor@sysoev.ru 
76771Sigor@sysoev.ru const nxt_tls_lib_t  nxt_openssl_lib = {
77771Sigor@sysoev.ru     .library_init = nxt_openssl_library_init,
78771Sigor@sysoev.ru     .library_free = nxt_openssl_library_free,
79771Sigor@sysoev.ru 
80771Sigor@sysoev.ru     .server_init = nxt_openssl_server_init,
81771Sigor@sysoev.ru     .server_free = nxt_openssl_server_free,
820Sigor@sysoev.ru };
830Sigor@sysoev.ru 
840Sigor@sysoev.ru 
8562Sigor@sysoev.ru static nxt_conn_io_t  nxt_openssl_conn_io = {
86771Sigor@sysoev.ru     .read = nxt_conn_io_read,
87771Sigor@sysoev.ru     .recvbuf = nxt_openssl_conn_io_recvbuf,
880Sigor@sysoev.ru 
89771Sigor@sysoev.ru     .write = nxt_conn_io_write,
90771Sigor@sysoev.ru     .sendbuf = nxt_openssl_conn_io_sendbuf,
910Sigor@sysoev.ru 
92771Sigor@sysoev.ru     .shutdown = nxt_openssl_conn_io_shutdown,
930Sigor@sysoev.ru };
940Sigor@sysoev.ru 
950Sigor@sysoev.ru 
960Sigor@sysoev.ru static long  nxt_openssl_version;
970Sigor@sysoev.ru static int   nxt_openssl_connection_index;
980Sigor@sysoev.ru 
990Sigor@sysoev.ru 
1000Sigor@sysoev.ru static nxt_int_t
101771Sigor@sysoev.ru nxt_openssl_library_init(nxt_task_t *task)
1020Sigor@sysoev.ru {
1030Sigor@sysoev.ru     int  index;
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru     if (nxt_fast_path(nxt_openssl_version != 0)) {
1060Sigor@sysoev.ru         return NXT_OK;
1070Sigor@sysoev.ru     }
1080Sigor@sysoev.ru 
109771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
110771Sigor@sysoev.ru 
111771Sigor@sysoev.ru     OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
1120Sigor@sysoev.ru 
113771Sigor@sysoev.ru #else
114771Sigor@sysoev.ru     {
115771Sigor@sysoev.ru         nxt_int_t  ret;
116771Sigor@sysoev.ru 
117771Sigor@sysoev.ru         SSL_load_error_strings();
118771Sigor@sysoev.ru 
119771Sigor@sysoev.ru         OPENSSL_config(NULL);
1200Sigor@sysoev.ru 
121771Sigor@sysoev.ru         /*
122771Sigor@sysoev.ru          * SSL_library_init(3):
123771Sigor@sysoev.ru          *
124771Sigor@sysoev.ru          *   SSL_library_init() always returns "1",
125771Sigor@sysoev.ru          *   so it is safe to discard the return value.
126771Sigor@sysoev.ru          */
127771Sigor@sysoev.ru         (void) SSL_library_init();
128771Sigor@sysoev.ru 
129771Sigor@sysoev.ru         ret = nxt_openssl_locks_init();
130771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
131771Sigor@sysoev.ru             return ret;
132771Sigor@sysoev.ru         }
133771Sigor@sysoev.ru     }
134771Sigor@sysoev.ru 
135771Sigor@sysoev.ru #endif
1360Sigor@sysoev.ru 
1370Sigor@sysoev.ru     nxt_openssl_version = SSLeay();
1380Sigor@sysoev.ru 
139771Sigor@sysoev.ru     nxt_log(task, NXT_LOG_INFO, "%s, %xl",
140771Sigor@sysoev.ru             SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
1410Sigor@sysoev.ru 
1420Sigor@sysoev.ru #ifndef SSL_OP_NO_COMPRESSION
1430Sigor@sysoev.ru     {
1440Sigor@sysoev.ru         /*
1450Sigor@sysoev.ru          * Disable gzip compression in OpenSSL prior to 1.0.0
1460Sigor@sysoev.ru          * version, this saves about 522K per connection.
1470Sigor@sysoev.ru          */
1480Sigor@sysoev.ru         int                 n;
1490Sigor@sysoev.ru         STACK_OF(SSL_COMP)  *ssl_comp_methods;
1500Sigor@sysoev.ru 
1510Sigor@sysoev.ru         ssl_comp_methods = SSL_COMP_get_compression_methods();
1520Sigor@sysoev.ru 
1530Sigor@sysoev.ru         for (n = sk_SSL_COMP_num(ssl_comp_methods); n != 0; n--) {
1540Sigor@sysoev.ru             (void) sk_SSL_COMP_pop(ssl_comp_methods);
1550Sigor@sysoev.ru         }
1560Sigor@sysoev.ru     }
1570Sigor@sysoev.ru #endif
1580Sigor@sysoev.ru 
1590Sigor@sysoev.ru     index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1600Sigor@sysoev.ru 
1610Sigor@sysoev.ru     if (index == -1) {
162771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
1630Sigor@sysoev.ru                               "SSL_get_ex_new_index() failed");
1640Sigor@sysoev.ru         return NXT_ERROR;
1650Sigor@sysoev.ru     }
1660Sigor@sysoev.ru 
1670Sigor@sysoev.ru     nxt_openssl_connection_index = index;
1680Sigor@sysoev.ru 
1690Sigor@sysoev.ru     return NXT_OK;
1700Sigor@sysoev.ru }
1710Sigor@sysoev.ru 
1720Sigor@sysoev.ru 
173771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
174771Sigor@sysoev.ru 
175771Sigor@sysoev.ru static void
176771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
177771Sigor@sysoev.ru {
178771Sigor@sysoev.ru }
179771Sigor@sysoev.ru 
180771Sigor@sysoev.ru #else
181771Sigor@sysoev.ru 
182771Sigor@sysoev.ru static nxt_thread_mutex_t  *nxt_openssl_locks;
183771Sigor@sysoev.ru 
1840Sigor@sysoev.ru static nxt_int_t
185771Sigor@sysoev.ru nxt_openssl_locks_init(void)
186771Sigor@sysoev.ru {
187771Sigor@sysoev.ru     int        i, n;
188771Sigor@sysoev.ru     nxt_int_t  ret;
189771Sigor@sysoev.ru 
190771Sigor@sysoev.ru     n = CRYPTO_num_locks();
191771Sigor@sysoev.ru 
192771Sigor@sysoev.ru     nxt_openssl_locks = OPENSSL_malloc(n * sizeof(nxt_thread_mutex_t));
193771Sigor@sysoev.ru     if (nxt_slow_path(nxt_openssl_locks == NULL)) {
194771Sigor@sysoev.ru         return NXT_ERROR;
195771Sigor@sysoev.ru     }
196771Sigor@sysoev.ru 
197771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
198771Sigor@sysoev.ru         ret = nxt_thread_mutex_create(&nxt_openssl_locks[i]);
199771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
200771Sigor@sysoev.ru             return ret;
201771Sigor@sysoev.ru         }
202771Sigor@sysoev.ru     }
203771Sigor@sysoev.ru 
204771Sigor@sysoev.ru     CRYPTO_set_locking_callback(nxt_openssl_lock);
205771Sigor@sysoev.ru 
206771Sigor@sysoev.ru     CRYPTO_set_id_callback(nxt_openssl_thread_id);
207771Sigor@sysoev.ru 
208771Sigor@sysoev.ru     return NXT_OK;
209771Sigor@sysoev.ru }
210771Sigor@sysoev.ru 
211771Sigor@sysoev.ru 
212771Sigor@sysoev.ru static void
213771Sigor@sysoev.ru nxt_openssl_lock(int mode, int type, const char *file, int line)
214771Sigor@sysoev.ru {
215771Sigor@sysoev.ru     nxt_thread_mutex_t  *lock;
216771Sigor@sysoev.ru 
217771Sigor@sysoev.ru     lock = &nxt_openssl_locks[type];
218771Sigor@sysoev.ru 
219771Sigor@sysoev.ru     if ((mode & CRYPTO_LOCK) != 0) {
220771Sigor@sysoev.ru         (void) nxt_thread_mutex_lock(lock);
221771Sigor@sysoev.ru 
222771Sigor@sysoev.ru     } else {
223771Sigor@sysoev.ru         (void) nxt_thread_mutex_unlock(lock);
224771Sigor@sysoev.ru     }
225771Sigor@sysoev.ru }
226771Sigor@sysoev.ru 
227771Sigor@sysoev.ru 
228771Sigor@sysoev.ru static u_long
229771Sigor@sysoev.ru nxt_openssl_thread_id(void)
230771Sigor@sysoev.ru {
231771Sigor@sysoev.ru     return (u_long) nxt_thread_handle();
232771Sigor@sysoev.ru }
233771Sigor@sysoev.ru 
234771Sigor@sysoev.ru 
235771Sigor@sysoev.ru static void
236771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
237771Sigor@sysoev.ru {
238771Sigor@sysoev.ru     nxt_openssl_locks_free();
239771Sigor@sysoev.ru }
240771Sigor@sysoev.ru 
241771Sigor@sysoev.ru 
242771Sigor@sysoev.ru static void
243771Sigor@sysoev.ru nxt_openssl_locks_free(void)
244771Sigor@sysoev.ru {
245771Sigor@sysoev.ru     int  i, n;
246771Sigor@sysoev.ru 
247771Sigor@sysoev.ru     n = CRYPTO_num_locks();
248771Sigor@sysoev.ru 
249771Sigor@sysoev.ru     CRYPTO_set_locking_callback(NULL);
250771Sigor@sysoev.ru 
251771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
252771Sigor@sysoev.ru         nxt_thread_mutex_destroy(&nxt_openssl_locks[i]);
253771Sigor@sysoev.ru     }
254771Sigor@sysoev.ru 
255771Sigor@sysoev.ru     OPENSSL_free(nxt_openssl_locks);
256771Sigor@sysoev.ru }
257771Sigor@sysoev.ru 
258771Sigor@sysoev.ru #endif
259771Sigor@sysoev.ru 
260771Sigor@sysoev.ru 
261771Sigor@sysoev.ru static nxt_int_t
2621828Sa.suvorov@f5.com nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
2631828Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_bool_t last)
2640Sigor@sysoev.ru {
2651828Sa.suvorov@f5.com     SSL_CTX                *ctx;
2661828Sa.suvorov@f5.com     const char             *ciphers, *ca_certificate;
2671828Sa.suvorov@f5.com     STACK_OF(X509_NAME)    *list;
2681828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
2690Sigor@sysoev.ru 
2700Sigor@sysoev.ru     ctx = SSL_CTX_new(SSLv23_server_method());
2710Sigor@sysoev.ru     if (ctx == NULL) {
272771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_new() failed");
2730Sigor@sysoev.ru         return NXT_ERROR;
2740Sigor@sysoev.ru     }
2750Sigor@sysoev.ru 
2761828Sa.suvorov@f5.com     bundle = conf->bundle;
2771828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
2781828Sa.suvorov@f5.com 
2791828Sa.suvorov@f5.com     bundle->ctx = ctx;
2800Sigor@sysoev.ru 
281771Sigor@sysoev.ru #ifdef SSL_OP_NO_RENEGOTIATION
282771Sigor@sysoev.ru     /* Renegration is not currently supported. */
283771Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
284771Sigor@sysoev.ru #endif
285771Sigor@sysoev.ru 
2860Sigor@sysoev.ru #ifdef SSL_OP_NO_COMPRESSION
2870Sigor@sysoev.ru     /*
2880Sigor@sysoev.ru      * Disable gzip compression in OpenSSL 1.0.0,
2890Sigor@sysoev.ru      * this saves about 522K per connection.
2900Sigor@sysoev.ru      */
2910Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
2920Sigor@sysoev.ru #endif
2930Sigor@sysoev.ru 
2940Sigor@sysoev.ru #ifdef SSL_MODE_RELEASE_BUFFERS
2950Sigor@sysoev.ru 
2960Sigor@sysoev.ru     if (nxt_openssl_version >= 10001078) {
2970Sigor@sysoev.ru         /*
2980Sigor@sysoev.ru          * Allow to release read and write buffers in OpenSSL 1.0.0,
2990Sigor@sysoev.ru          * this saves about 34K per idle connection.  It is not safe
3000Sigor@sysoev.ru          * before OpenSSL 1.0.1h (CVE-2010-5298).
3010Sigor@sysoev.ru          */
3020Sigor@sysoev.ru         SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
3030Sigor@sysoev.ru     }
3040Sigor@sysoev.ru 
3050Sigor@sysoev.ru #endif
3060Sigor@sysoev.ru 
3071828Sa.suvorov@f5.com     if (nxt_openssl_chain_file(task, ctx, conf, mp,
3081828Sa.suvorov@f5.com                                last && bundle->next == NULL)
3091828Sa.suvorov@f5.com         != NXT_OK)
3101828Sa.suvorov@f5.com     {
3110Sigor@sysoev.ru         goto fail;
3120Sigor@sysoev.ru     }
313774Svbart@nginx.com /*
3140Sigor@sysoev.ru     key = conf->certificate_key;
3150Sigor@sysoev.ru 
3160Sigor@sysoev.ru     if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
317771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3180Sigor@sysoev.ru                               "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
3190Sigor@sysoev.ru                               key);
3200Sigor@sysoev.ru         goto fail;
3210Sigor@sysoev.ru     }
322774Svbart@nginx.com */
3230Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
3240Sigor@sysoev.ru 
3250Sigor@sysoev.ru     if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
326771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3270Sigor@sysoev.ru                               "SSL_CTX_set_cipher_list(\"%s\") failed",
3280Sigor@sysoev.ru                               ciphers);
3290Sigor@sysoev.ru         goto fail;
3300Sigor@sysoev.ru     }
3310Sigor@sysoev.ru 
3320Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
3330Sigor@sysoev.ru 
3340Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
3350Sigor@sysoev.ru 
3360Sigor@sysoev.ru         /* TODO: verify callback */
3370Sigor@sysoev.ru         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
3380Sigor@sysoev.ru 
3390Sigor@sysoev.ru         /* TODO: verify depth */
3400Sigor@sysoev.ru         SSL_CTX_set_verify_depth(ctx, 1);
3410Sigor@sysoev.ru 
3420Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
3430Sigor@sysoev.ru 
3440Sigor@sysoev.ru         if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
345771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3460Sigor@sysoev.ru                               "SSL_CTX_load_verify_locations(\"%s\") failed",
3470Sigor@sysoev.ru                               ca_certificate);
3480Sigor@sysoev.ru             goto fail;
3490Sigor@sysoev.ru         }
3500Sigor@sysoev.ru 
3510Sigor@sysoev.ru         list = SSL_load_client_CA_file(ca_certificate);
3520Sigor@sysoev.ru 
3530Sigor@sysoev.ru         if (list == NULL) {
354771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3550Sigor@sysoev.ru                               "SSL_load_client_CA_file(\"%s\") failed",
3560Sigor@sysoev.ru                               ca_certificate);
3570Sigor@sysoev.ru             goto fail;
3580Sigor@sysoev.ru         }
3590Sigor@sysoev.ru 
3600Sigor@sysoev.ru         /*
3610Sigor@sysoev.ru          * SSL_load_client_CA_file() in OpenSSL prior to 0.9.7h and
3620Sigor@sysoev.ru          * 0.9.8 versions always leaves an error in the error queue.
3630Sigor@sysoev.ru          */
3640Sigor@sysoev.ru         ERR_clear_error();
3650Sigor@sysoev.ru 
3660Sigor@sysoev.ru         SSL_CTX_set_client_CA_list(ctx, list);
3670Sigor@sysoev.ru     }
3680Sigor@sysoev.ru 
3691828Sa.suvorov@f5.com     if (last) {
3701828Sa.suvorov@f5.com         conf->conn_init = nxt_openssl_conn_init;
3711828Sa.suvorov@f5.com 
3721828Sa.suvorov@f5.com         if (bundle->next != NULL) {
3731828Sa.suvorov@f5.com             SSL_CTX_set_tlsext_servername_callback(ctx, nxt_openssl_servername);
3741828Sa.suvorov@f5.com         }
3751828Sa.suvorov@f5.com     }
3761828Sa.suvorov@f5.com 
3770Sigor@sysoev.ru     return NXT_OK;
3780Sigor@sysoev.ru 
3790Sigor@sysoev.ru fail:
3800Sigor@sysoev.ru 
3810Sigor@sysoev.ru     SSL_CTX_free(ctx);
3820Sigor@sysoev.ru 
3831818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
3841818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
3851818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
3861818Smax.romanov@nginx.com #endif
3871818Smax.romanov@nginx.com 
3880Sigor@sysoev.ru     return NXT_ERROR;
3890Sigor@sysoev.ru }
3900Sigor@sysoev.ru 
3910Sigor@sysoev.ru 
392833Svbart@nginx.com static nxt_int_t
3931828Sa.suvorov@f5.com nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf,
3941828Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_bool_t single)
395774Svbart@nginx.com {
3961828Sa.suvorov@f5.com     BIO                    *bio;
3971828Sa.suvorov@f5.com     X509                   *cert, *ca;
3981828Sa.suvorov@f5.com     long                   reason;
3991828Sa.suvorov@f5.com     EVP_PKEY               *key;
4001828Sa.suvorov@f5.com     nxt_int_t              ret;
4011828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
4021828Sa.suvorov@f5.com 
4031828Sa.suvorov@f5.com     ret = NXT_ERROR;
4041828Sa.suvorov@f5.com     cert = NULL;
405774Svbart@nginx.com 
406774Svbart@nginx.com     bio = BIO_new(BIO_s_fd());
407774Svbart@nginx.com     if (bio == NULL) {
4081828Sa.suvorov@f5.com         goto end;
409774Svbart@nginx.com     }
410774Svbart@nginx.com 
4111828Sa.suvorov@f5.com     bundle = conf->bundle;
412774Svbart@nginx.com 
4131828Sa.suvorov@f5.com     BIO_set_fd(bio, bundle->chain_file, BIO_CLOSE);
414774Svbart@nginx.com 
415774Svbart@nginx.com     cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
416774Svbart@nginx.com     if (cert == NULL) {
417774Svbart@nginx.com         goto end;
418774Svbart@nginx.com     }
419774Svbart@nginx.com 
420774Svbart@nginx.com     if (SSL_CTX_use_certificate(ctx, cert) != 1) {
421774Svbart@nginx.com         goto end;
422774Svbart@nginx.com     }
423774Svbart@nginx.com 
4241828Sa.suvorov@f5.com     if (!single && nxt_openssl_cert_get_names(task, cert, conf, mp) != NXT_OK) {
4251828Sa.suvorov@f5.com         goto clean;
4261828Sa.suvorov@f5.com     }
4271828Sa.suvorov@f5.com 
428774Svbart@nginx.com     for ( ;; ) {
429774Svbart@nginx.com         ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
430774Svbart@nginx.com 
431774Svbart@nginx.com         if (ca == NULL) {
432774Svbart@nginx.com             reason = ERR_GET_REASON(ERR_peek_last_error());
433774Svbart@nginx.com             if (reason != PEM_R_NO_START_LINE) {
434774Svbart@nginx.com                 goto end;
435774Svbart@nginx.com             }
436774Svbart@nginx.com 
437774Svbart@nginx.com             ERR_clear_error();
438774Svbart@nginx.com             break;
439774Svbart@nginx.com         }
440774Svbart@nginx.com 
441774Svbart@nginx.com         /*
442774Svbart@nginx.com          * Note that ca isn't freed if it was successfully added to the chain,
443774Svbart@nginx.com          * while the main certificate needs a X509_free() call, since
444774Svbart@nginx.com          * its reference count is increased by SSL_CTX_use_certificate().
445774Svbart@nginx.com          */
446808Spluknet@nginx.com #ifdef SSL_CTX_add0_chain_cert
447774Svbart@nginx.com         if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) {
448774Svbart@nginx.com #else
449774Svbart@nginx.com         if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) {
450774Svbart@nginx.com #endif
451774Svbart@nginx.com             X509_free(ca);
452774Svbart@nginx.com             goto end;
453774Svbart@nginx.com         }
454774Svbart@nginx.com     }
455774Svbart@nginx.com 
456774Svbart@nginx.com     if (BIO_reset(bio) != 0) {
457774Svbart@nginx.com         goto end;
458774Svbart@nginx.com     }
459774Svbart@nginx.com 
460774Svbart@nginx.com     key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
461774Svbart@nginx.com     if (key == NULL) {
462774Svbart@nginx.com         goto end;
463774Svbart@nginx.com     }
464774Svbart@nginx.com 
465774Svbart@nginx.com     if (SSL_CTX_use_PrivateKey(ctx, key) == 1) {
466774Svbart@nginx.com         ret = NXT_OK;
467774Svbart@nginx.com     }
468774Svbart@nginx.com 
469774Svbart@nginx.com     EVP_PKEY_free(key);
470774Svbart@nginx.com 
471774Svbart@nginx.com end:
472774Svbart@nginx.com 
4731828Sa.suvorov@f5.com     if (ret != NXT_OK) {
4741828Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
4751828Sa.suvorov@f5.com                               "nxt_openssl_chain_file() failed");
4761828Sa.suvorov@f5.com     }
4771828Sa.suvorov@f5.com 
4781828Sa.suvorov@f5.com clean:
4791828Sa.suvorov@f5.com 
4801828Sa.suvorov@f5.com     BIO_free(bio);
481774Svbart@nginx.com     X509_free(cert);
482774Svbart@nginx.com 
483774Svbart@nginx.com     return ret;
484774Svbart@nginx.com }
485774Svbart@nginx.com 
486774Svbart@nginx.com 
4871828Sa.suvorov@f5.com static nxt_uint_t
4881828Sa.suvorov@f5.com nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
4891828Sa.suvorov@f5.com     nxt_mp_t *mp)
4901828Sa.suvorov@f5.com {
4911828Sa.suvorov@f5.com     int                         len;
4921828Sa.suvorov@f5.com     nxt_str_t                   domain, str;
4931828Sa.suvorov@f5.com     X509_NAME                   *x509_name;
4941828Sa.suvorov@f5.com     nxt_uint_t                  i, n;
4951828Sa.suvorov@f5.com     GENERAL_NAME                *name;
4961828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t       *bundle;
4971828Sa.suvorov@f5.com     STACK_OF(GENERAL_NAME)      *alt_names;
4981828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
4991828Sa.suvorov@f5.com 
5001828Sa.suvorov@f5.com     bundle = conf->bundle;
5011828Sa.suvorov@f5.com 
5021828Sa.suvorov@f5.com     alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
5031828Sa.suvorov@f5.com 
5041828Sa.suvorov@f5.com     if (alt_names != NULL) {
5051828Sa.suvorov@f5.com         n = sk_GENERAL_NAME_num(alt_names);
5061828Sa.suvorov@f5.com 
5071828Sa.suvorov@f5.com         for (i = 0; i != n; i++) {
5081828Sa.suvorov@f5.com             name = sk_GENERAL_NAME_value(alt_names, i);
5091828Sa.suvorov@f5.com 
5101828Sa.suvorov@f5.com             if (name->type != GEN_DNS) {
5111828Sa.suvorov@f5.com                 continue;
5121828Sa.suvorov@f5.com             }
5131828Sa.suvorov@f5.com 
5141828Sa.suvorov@f5.com             str.length = ASN1_STRING_length(name->d.dNSName);
5151828Sa.suvorov@f5.com #if OPENSSL_VERSION_NUMBER > 0x10100000L
5161828Sa.suvorov@f5.com             str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
5171828Sa.suvorov@f5.com #else
5181828Sa.suvorov@f5.com             str.start = ASN1_STRING_data(name->d.dNSName);
5191828Sa.suvorov@f5.com #endif
5201828Sa.suvorov@f5.com 
5211828Sa.suvorov@f5.com             domain.start = nxt_mp_nget(mp, str.length);
5221828Sa.suvorov@f5.com             if (nxt_slow_path(domain.start == NULL)) {
5231828Sa.suvorov@f5.com                 goto fail;
5241828Sa.suvorov@f5.com             }
5251828Sa.suvorov@f5.com 
5261828Sa.suvorov@f5.com             domain.length = str.length;
5271828Sa.suvorov@f5.com             nxt_memcpy_lowcase(domain.start, str.start, str.length);
5281828Sa.suvorov@f5.com 
5291828Sa.suvorov@f5.com             item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
5301828Sa.suvorov@f5.com             if (nxt_slow_path(item == NULL)) {
5311828Sa.suvorov@f5.com                 goto fail;
5321828Sa.suvorov@f5.com             }
5331828Sa.suvorov@f5.com 
5341828Sa.suvorov@f5.com             item->name = domain;
5351828Sa.suvorov@f5.com             item->bundle = bundle;
5361828Sa.suvorov@f5.com 
5371828Sa.suvorov@f5.com             if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash,
5381828Sa.suvorov@f5.com                                                item, mp)
5391828Sa.suvorov@f5.com                 == NXT_ERROR)
5401828Sa.suvorov@f5.com             {
5411828Sa.suvorov@f5.com                 goto fail;
5421828Sa.suvorov@f5.com             }
5431828Sa.suvorov@f5.com         }
5441828Sa.suvorov@f5.com 
5451828Sa.suvorov@f5.com         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
5461828Sa.suvorov@f5.com 
5471828Sa.suvorov@f5.com     } else {
5481828Sa.suvorov@f5.com         x509_name = X509_get_subject_name(cert);
5491828Sa.suvorov@f5.com         len = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
5501828Sa.suvorov@f5.com                                         NULL, 0);
5511828Sa.suvorov@f5.com         if (len <= 0) {
5521828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither "
5531828Sa.suvorov@f5.com                     "Subject Alternative Name nor Common Name", bundle->name);
5541828Sa.suvorov@f5.com             return NXT_OK;
5551828Sa.suvorov@f5.com         }
5561828Sa.suvorov@f5.com 
5571828Sa.suvorov@f5.com         domain.start = nxt_mp_nget(mp, len + 1);
5581828Sa.suvorov@f5.com         if (nxt_slow_path(domain.start == NULL)) {
5591828Sa.suvorov@f5.com             return NXT_ERROR;
5601828Sa.suvorov@f5.com         }
5611828Sa.suvorov@f5.com 
5621828Sa.suvorov@f5.com         domain.length = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
5631828Sa.suvorov@f5.com                                                   (char *) domain.start,
5641828Sa.suvorov@f5.com                                                   len + 1);
5651828Sa.suvorov@f5.com         nxt_memcpy_lowcase(domain.start, domain.start, domain.length);
5661828Sa.suvorov@f5.com 
5671828Sa.suvorov@f5.com         item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
5681828Sa.suvorov@f5.com         if (nxt_slow_path(item == NULL)) {
5691828Sa.suvorov@f5.com             return NXT_ERROR;
5701828Sa.suvorov@f5.com         }
5711828Sa.suvorov@f5.com 
5721828Sa.suvorov@f5.com         item->name = domain;
5731828Sa.suvorov@f5.com         item->bundle = bundle;
5741828Sa.suvorov@f5.com 
5751828Sa.suvorov@f5.com         if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash, item,
5761828Sa.suvorov@f5.com                                            mp)
5771828Sa.suvorov@f5.com             == NXT_ERROR)
5781828Sa.suvorov@f5.com         {
5791828Sa.suvorov@f5.com             return NXT_ERROR;
5801828Sa.suvorov@f5.com         }
5811828Sa.suvorov@f5.com     }
5821828Sa.suvorov@f5.com 
5831828Sa.suvorov@f5.com     return NXT_OK;
5841828Sa.suvorov@f5.com 
5851828Sa.suvorov@f5.com fail:
5861828Sa.suvorov@f5.com 
5871828Sa.suvorov@f5.com     sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
5881828Sa.suvorov@f5.com 
5891828Sa.suvorov@f5.com     return NXT_ERROR;
5901828Sa.suvorov@f5.com }
5911828Sa.suvorov@f5.com 
5921828Sa.suvorov@f5.com 
5931828Sa.suvorov@f5.com static const nxt_lvlhsh_proto_t  nxt_openssl_bundle_hash_proto
5941828Sa.suvorov@f5.com     nxt_aligned(64) =
5951828Sa.suvorov@f5.com {
5961828Sa.suvorov@f5.com     NXT_LVLHSH_DEFAULT,
5971828Sa.suvorov@f5.com     nxt_openssl_bundle_hash_test,
5981828Sa.suvorov@f5.com     nxt_mp_lvlhsh_alloc,
5991828Sa.suvorov@f5.com     nxt_mp_lvlhsh_free,
6001828Sa.suvorov@f5.com };
6011828Sa.suvorov@f5.com 
6021828Sa.suvorov@f5.com 
6031828Sa.suvorov@f5.com static nxt_int_t
6041828Sa.suvorov@f5.com nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
6051828Sa.suvorov@f5.com {
6061828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
6071828Sa.suvorov@f5.com 
6081828Sa.suvorov@f5.com     item = data;
6091828Sa.suvorov@f5.com 
6101828Sa.suvorov@f5.com     return nxt_strstr_eq(&lhq->key, &item->name) ? NXT_OK : NXT_DECLINED;
6111828Sa.suvorov@f5.com }
6121828Sa.suvorov@f5.com 
6131828Sa.suvorov@f5.com 
6141828Sa.suvorov@f5.com static nxt_int_t
6151828Sa.suvorov@f5.com nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
6161828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t *item, nxt_mp_t *mp)
6171828Sa.suvorov@f5.com {
6181828Sa.suvorov@f5.com     nxt_str_t                   str;
6191828Sa.suvorov@f5.com     nxt_int_t                   ret;
6201828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
6211828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *old;
6221828Sa.suvorov@f5.com 
6231828Sa.suvorov@f5.com     str = item->name;
6241828Sa.suvorov@f5.com 
6251828Sa.suvorov@f5.com     if (item->name.start[0] == '*') {
6261828Sa.suvorov@f5.com         item->name.start++;
6271828Sa.suvorov@f5.com         item->name.length--;
6281828Sa.suvorov@f5.com 
6291828Sa.suvorov@f5.com         if (item->name.length == 0 || item->name.start[0] != '.') {
6301828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" "
6311828Sa.suvorov@f5.com                     "in certificate \"%V\": missing \".\" "
6321828Sa.suvorov@f5.com                     "after wildcard symbol", &str, item->bundle->name);
6331828Sa.suvorov@f5.com             return NXT_OK;
6341828Sa.suvorov@f5.com         }
6351828Sa.suvorov@f5.com     }
6361828Sa.suvorov@f5.com 
6371828Sa.suvorov@f5.com     lhq.pool = mp;
6381828Sa.suvorov@f5.com     lhq.key = item->name;
6391828Sa.suvorov@f5.com     lhq.value = item;
6401828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
6411828Sa.suvorov@f5.com     lhq.replace = 0;
6421828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(item->name.start, item->name.length);
6431828Sa.suvorov@f5.com 
6441828Sa.suvorov@f5.com     ret = nxt_lvlhsh_insert(lvlhsh, &lhq);
6451828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_OK)) {
6461828Sa.suvorov@f5.com         nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted",
6471828Sa.suvorov@f5.com                   &str, item->bundle->name);
6481828Sa.suvorov@f5.com         return NXT_OK;
6491828Sa.suvorov@f5.com     }
6501828Sa.suvorov@f5.com 
6511828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_DECLINED)) {
6521828Sa.suvorov@f5.com         old = lhq.value;
6531828Sa.suvorov@f5.com         if (old->bundle != item->bundle) {
6541828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" "
6551828Sa.suvorov@f5.com                     "in certificate \"%V\", identical name appears in \"%V\"",
6561828Sa.suvorov@f5.com                     &str, old->bundle->name, item->bundle->name);
6571828Sa.suvorov@f5.com 
6581828Sa.suvorov@f5.com             old->bundle = item->bundle;
6591828Sa.suvorov@f5.com         }
6601828Sa.suvorov@f5.com 
6611828Sa.suvorov@f5.com         return NXT_OK;
6621828Sa.suvorov@f5.com     }
6631828Sa.suvorov@f5.com 
6641828Sa.suvorov@f5.com     return NXT_ERROR;
6651828Sa.suvorov@f5.com }
6661828Sa.suvorov@f5.com 
6671828Sa.suvorov@f5.com 
6681828Sa.suvorov@f5.com static nxt_int_t
6691828Sa.suvorov@f5.com nxt_openssl_servername(SSL *s, int *ad, void *arg)
6701828Sa.suvorov@f5.com {
6711828Sa.suvorov@f5.com     nxt_str_t              str;
6721828Sa.suvorov@f5.com     nxt_uint_t             i;
6731828Sa.suvorov@f5.com     nxt_conn_t             *c;
6741828Sa.suvorov@f5.com     const char             *servername;
6751828Sa.suvorov@f5.com     nxt_tls_conf_t         *conf;
6761828Sa.suvorov@f5.com     nxt_openssl_conn_t     *tls;
6771828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
6781828Sa.suvorov@f5.com 
6791828Sa.suvorov@f5.com     c = SSL_get_ex_data(s, nxt_openssl_connection_index);
6801828Sa.suvorov@f5.com 
6811828Sa.suvorov@f5.com     if (nxt_slow_path(c == NULL)) {
6821828Sa.suvorov@f5.com         nxt_thread_log_alert("SSL_get_ex_data() failed");
6831828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
6841828Sa.suvorov@f5.com     }
6851828Sa.suvorov@f5.com 
6861828Sa.suvorov@f5.com     servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
6871828Sa.suvorov@f5.com     if (nxt_slow_path(servername == NULL)) {
6881828Sa.suvorov@f5.com         nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned "
6891828Sa.suvorov@f5.com                                                "NULL in server name callback");
6901828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
6911828Sa.suvorov@f5.com     }
6921828Sa.suvorov@f5.com 
6931828Sa.suvorov@f5.com     str.length = nxt_strlen(servername);
6941828Sa.suvorov@f5.com     if (str.length == 0) {
6951828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "client sent zero-length server name");
6961828Sa.suvorov@f5.com         goto done;
6971828Sa.suvorov@f5.com     }
6981828Sa.suvorov@f5.com 
6991828Sa.suvorov@f5.com     if (servername[0] == '.') {
7001828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "ignored the server name \"%s\": "
7011828Sa.suvorov@f5.com                                   "leading \".\"", servername);
7021828Sa.suvorov@f5.com         goto done;
7031828Sa.suvorov@f5.com     }
7041828Sa.suvorov@f5.com 
7051828Sa.suvorov@f5.com     nxt_debug(c->socket.task, "tls with servername \"%s\"", servername);
7061828Sa.suvorov@f5.com 
7071828Sa.suvorov@f5.com     str.start = nxt_mp_nget(c->mem_pool, str.length);
7081828Sa.suvorov@f5.com     if (nxt_slow_path(str.start == NULL)) {
7091828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
7101828Sa.suvorov@f5.com     }
7111828Sa.suvorov@f5.com 
7121828Sa.suvorov@f5.com     nxt_memcpy_lowcase(str.start, (const u_char *) servername, str.length);
7131828Sa.suvorov@f5.com 
7141828Sa.suvorov@f5.com     tls = c->u.tls;
7151828Sa.suvorov@f5.com     conf = tls->conf;
7161828Sa.suvorov@f5.com 
7171828Sa.suvorov@f5.com     bundle = nxt_openssl_find_ctx(conf, &str);
7181828Sa.suvorov@f5.com 
7191828Sa.suvorov@f5.com     if (bundle == NULL) {
7201828Sa.suvorov@f5.com         for (i = 1; i < str.length; i++) {
7211828Sa.suvorov@f5.com             if (str.start[i] == '.') {
7221828Sa.suvorov@f5.com                 str.start += i;
7231828Sa.suvorov@f5.com                 str.length -= i;
7241828Sa.suvorov@f5.com 
7251828Sa.suvorov@f5.com                 bundle = nxt_openssl_find_ctx(conf, &str);
7261828Sa.suvorov@f5.com                 break;
7271828Sa.suvorov@f5.com             }
7281828Sa.suvorov@f5.com         }
7291828Sa.suvorov@f5.com     }
7301828Sa.suvorov@f5.com 
7311828Sa.suvorov@f5.com     if (bundle != NULL) {
7321828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" "
7331828Sa.suvorov@f5.com                                   "(old: \"%V\")", &str, bundle->name,
7341828Sa.suvorov@f5.com                                   conf->bundle->name);
7351828Sa.suvorov@f5.com 
7361828Sa.suvorov@f5.com         if (bundle != conf->bundle) {
7371828Sa.suvorov@f5.com             if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) {
7381828Sa.suvorov@f5.com                 nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
7391828Sa.suvorov@f5.com                                       "SSL_set_SSL_CTX() failed");
7401828Sa.suvorov@f5.com 
7411828Sa.suvorov@f5.com                 return SSL_TLSEXT_ERR_ALERT_FATAL;
7421828Sa.suvorov@f5.com             }
7431828Sa.suvorov@f5.com         }
7441828Sa.suvorov@f5.com     }
7451828Sa.suvorov@f5.com 
7461828Sa.suvorov@f5.com done:
7471828Sa.suvorov@f5.com 
7481828Sa.suvorov@f5.com     return SSL_TLSEXT_ERR_OK;
7491828Sa.suvorov@f5.com }
7501828Sa.suvorov@f5.com 
7511828Sa.suvorov@f5.com 
7521828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *
7531828Sa.suvorov@f5.com nxt_openssl_find_ctx(nxt_tls_conf_t *conf, nxt_str_t *sn)
7541828Sa.suvorov@f5.com {
7551828Sa.suvorov@f5.com     nxt_int_t                   ret;
7561828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
7571828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
7581828Sa.suvorov@f5.com 
7591828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(sn->start, sn->length);
7601828Sa.suvorov@f5.com     lhq.key = *sn;
7611828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
7621828Sa.suvorov@f5.com 
7631828Sa.suvorov@f5.com     ret = nxt_lvlhsh_find(&conf->bundle_hash, &lhq);
7641828Sa.suvorov@f5.com     if (ret != NXT_OK) {
7651828Sa.suvorov@f5.com         return NULL;
7661828Sa.suvorov@f5.com     }
7671828Sa.suvorov@f5.com 
7681828Sa.suvorov@f5.com     item = lhq.value;
7691828Sa.suvorov@f5.com 
7701828Sa.suvorov@f5.com     return item->bundle;
7711828Sa.suvorov@f5.com }
7721828Sa.suvorov@f5.com 
7731828Sa.suvorov@f5.com 
7740Sigor@sysoev.ru static void
775771Sigor@sysoev.ru nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
7760Sigor@sysoev.ru {
7771828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
7781828Sa.suvorov@f5.com 
7791828Sa.suvorov@f5.com     bundle = conf->bundle;
7801828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
7811828Sa.suvorov@f5.com 
7821828Sa.suvorov@f5.com     do {
7831828Sa.suvorov@f5.com         SSL_CTX_free(bundle->ctx);
7841828Sa.suvorov@f5.com         bundle = bundle->next;
7851828Sa.suvorov@f5.com     } while (bundle != NULL);
7861818Smax.romanov@nginx.com 
7871818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
7881818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
7891818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
7901818Smax.romanov@nginx.com #endif
791771Sigor@sysoev.ru }
792771Sigor@sysoev.ru 
793771Sigor@sysoev.ru 
794771Sigor@sysoev.ru static void
795771Sigor@sysoev.ru nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
796771Sigor@sysoev.ru {
797771Sigor@sysoev.ru     int                 ret;
798771Sigor@sysoev.ru     SSL                 *s;
799771Sigor@sysoev.ru     SSL_CTX             *ctx;
800771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
8010Sigor@sysoev.ru 
8020Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "openssl conn init");
8030Sigor@sysoev.ru 
804771Sigor@sysoev.ru     tls = nxt_mp_zget(c->mem_pool, sizeof(nxt_openssl_conn_t));
805771Sigor@sysoev.ru     if (tls == NULL) {
8060Sigor@sysoev.ru         goto fail;
8070Sigor@sysoev.ru     }
8080Sigor@sysoev.ru 
809771Sigor@sysoev.ru     c->u.tls = tls;
810771Sigor@sysoev.ru     nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size);
8110Sigor@sysoev.ru 
8121828Sa.suvorov@f5.com     ctx = conf->bundle->ctx;
8130Sigor@sysoev.ru 
8140Sigor@sysoev.ru     s = SSL_new(ctx);
8150Sigor@sysoev.ru     if (s == NULL) {
816771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_new() failed");
8170Sigor@sysoev.ru         goto fail;
8180Sigor@sysoev.ru     }
8190Sigor@sysoev.ru 
820771Sigor@sysoev.ru     tls->session = s;
8211828Sa.suvorov@f5.com     /* To pass TLS config to the nxt_openssl_servername() callback. */
8221828Sa.suvorov@f5.com     tls->conf = conf;
823771Sigor@sysoev.ru     tls->conn = c;
8240Sigor@sysoev.ru 
8250Sigor@sysoev.ru     ret = SSL_set_fd(s, c->socket.fd);
8260Sigor@sysoev.ru 
8270Sigor@sysoev.ru     if (ret == 0) {
828771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_fd(%d) failed",
829771Sigor@sysoev.ru                               c->socket.fd);
8300Sigor@sysoev.ru         goto fail;
8310Sigor@sysoev.ru     }
8320Sigor@sysoev.ru 
8330Sigor@sysoev.ru     SSL_set_accept_state(s);
8340Sigor@sysoev.ru 
8350Sigor@sysoev.ru     if (SSL_set_ex_data(s, nxt_openssl_connection_index, c) == 0) {
836771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_ex_data() failed");
8370Sigor@sysoev.ru         goto fail;
8380Sigor@sysoev.ru     }
8390Sigor@sysoev.ru 
84062Sigor@sysoev.ru     c->io = &nxt_openssl_conn_io;
8410Sigor@sysoev.ru     c->sendfile = NXT_CONN_SENDFILE_OFF;
8420Sigor@sysoev.ru 
8431Sigor@sysoev.ru     nxt_openssl_conn_handshake(task, c, c->socket.data);
844*1884Sa.suvorov@f5.com 
8450Sigor@sysoev.ru     return;
8460Sigor@sysoev.ru 
8470Sigor@sysoev.ru fail:
8480Sigor@sysoev.ru 
84913Sigor@sysoev.ru     nxt_work_queue_add(c->read_work_queue, c->read_state->error_handler,
85013Sigor@sysoev.ru                        task, c, c->socket.data);
8510Sigor@sysoev.ru }
8520Sigor@sysoev.ru 
8530Sigor@sysoev.ru 
854771Sigor@sysoev.ru nxt_inline void
855836Sigor@sysoev.ru nxt_openssl_conn_free(nxt_task_t *task, nxt_conn_t *c)
8560Sigor@sysoev.ru {
857836Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
858836Sigor@sysoev.ru 
859771Sigor@sysoev.ru     nxt_debug(task, "openssl conn free");
8600Sigor@sysoev.ru 
861836Sigor@sysoev.ru     tls = c->u.tls;
8620Sigor@sysoev.ru 
863836Sigor@sysoev.ru     if (tls != NULL) {
864836Sigor@sysoev.ru         c->u.tls = NULL;
865836Sigor@sysoev.ru         nxt_free(tls->buffer.start);
866836Sigor@sysoev.ru         SSL_free(tls->session);
867836Sigor@sysoev.ru     }
8680Sigor@sysoev.ru }
8690Sigor@sysoev.ru 
8700Sigor@sysoev.ru 
8710Sigor@sysoev.ru static void
8721Sigor@sysoev.ru nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data)
8730Sigor@sysoev.ru {
874771Sigor@sysoev.ru     int                     ret;
875771Sigor@sysoev.ru     nxt_int_t               n;
876771Sigor@sysoev.ru     nxt_err_t               err;
877771Sigor@sysoev.ru     nxt_conn_t              *c;
878771Sigor@sysoev.ru     nxt_work_queue_t        *wq;
879771Sigor@sysoev.ru     nxt_work_handler_t      handler;
880771Sigor@sysoev.ru     nxt_openssl_conn_t      *tls;
881771Sigor@sysoev.ru     const nxt_conn_state_t  *state;
8820Sigor@sysoev.ru 
8830Sigor@sysoev.ru     c = obj;
884836Sigor@sysoev.ru 
885836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake fd:%d", c->socket.fd);
886836Sigor@sysoev.ru 
887836Sigor@sysoev.ru     if (c->socket.error != 0) {
888836Sigor@sysoev.ru         return;
889836Sigor@sysoev.ru     }
890836Sigor@sysoev.ru 
891771Sigor@sysoev.ru     tls = c->u.tls;
8920Sigor@sysoev.ru 
893836Sigor@sysoev.ru     if (tls == NULL) {
894836Sigor@sysoev.ru         return;
895836Sigor@sysoev.ru     }
896836Sigor@sysoev.ru 
897836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake: %d times", tls->times);
8980Sigor@sysoev.ru 
899771Sigor@sysoev.ru     /* "tls->times == 1" is suitable to run SSL_do_handshake() in job. */
9000Sigor@sysoev.ru 
901771Sigor@sysoev.ru     ret = SSL_do_handshake(tls->session);
9020Sigor@sysoev.ru 
9030Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
9040Sigor@sysoev.ru 
9051Sigor@sysoev.ru     nxt_thread_time_debug_update(task->thread);
9060Sigor@sysoev.ru 
9071Sigor@sysoev.ru     nxt_debug(task, "SSL_do_handshake(%d): %d err:%d", c->socket.fd, ret, err);
9080Sigor@sysoev.ru 
909771Sigor@sysoev.ru     state = (c->read_state != NULL) ? c->read_state : c->write_state;
910771Sigor@sysoev.ru 
9110Sigor@sysoev.ru     if (ret > 0) {
9120Sigor@sysoev.ru         /* ret == 1, the handshake was successfully completed. */
913771Sigor@sysoev.ru         tls->handshake = 1;
9140Sigor@sysoev.ru 
915771Sigor@sysoev.ru         if (c->read_state != NULL) {
916771Sigor@sysoev.ru             if (state->io_read_handler != NULL || c->read != NULL) {
917771Sigor@sysoev.ru                 nxt_conn_read(task->thread->engine, c);
9180Sigor@sysoev.ru                 return;
9190Sigor@sysoev.ru             }
9200Sigor@sysoev.ru 
921771Sigor@sysoev.ru         } else {
922771Sigor@sysoev.ru             if (c->write != NULL) {
923771Sigor@sysoev.ru                 nxt_conn_write(task->thread->engine, c);
924771Sigor@sysoev.ru                 return;
925771Sigor@sysoev.ru             }
926771Sigor@sysoev.ru         }
927771Sigor@sysoev.ru 
928771Sigor@sysoev.ru         handler = state->ready_handler;
929771Sigor@sysoev.ru 
930771Sigor@sysoev.ru     } else {
931771Sigor@sysoev.ru         c->socket.read_handler = nxt_openssl_conn_handshake;
932771Sigor@sysoev.ru         c->socket.write_handler = nxt_openssl_conn_handshake;
933771Sigor@sysoev.ru 
934771Sigor@sysoev.ru         n = nxt_openssl_conn_test_error(task, c, ret, err,
935771Sigor@sysoev.ru                                         NXT_OPENSSL_HANDSHAKE);
936771Sigor@sysoev.ru         switch (n) {
9370Sigor@sysoev.ru 
938771Sigor@sysoev.ru         case NXT_AGAIN:
939771Sigor@sysoev.ru             if (tls->ssl_error == SSL_ERROR_WANT_READ && tls->times < 2) {
940771Sigor@sysoev.ru                 tls->times++;
941771Sigor@sysoev.ru             }
942771Sigor@sysoev.ru 
943771Sigor@sysoev.ru             return;
944771Sigor@sysoev.ru 
945771Sigor@sysoev.ru         case 0:
946771Sigor@sysoev.ru             handler = state->close_handler;
947771Sigor@sysoev.ru             break;
948771Sigor@sysoev.ru 
949771Sigor@sysoev.ru         default:
950771Sigor@sysoev.ru         case NXT_ERROR:
951771Sigor@sysoev.ru             nxt_openssl_conn_error(task, err, "SSL_do_handshake(%d) failed",
952771Sigor@sysoev.ru                                    c->socket.fd);
953771Sigor@sysoev.ru 
954771Sigor@sysoev.ru             handler = state->error_handler;
955771Sigor@sysoev.ru             break;
9560Sigor@sysoev.ru         }
9570Sigor@sysoev.ru     }
9580Sigor@sysoev.ru 
959771Sigor@sysoev.ru     wq = (c->read_state != NULL) ? c->read_work_queue : c->write_work_queue;
960771Sigor@sysoev.ru 
961771Sigor@sysoev.ru     nxt_work_queue_add(wq, handler, task, c, data);
9620Sigor@sysoev.ru }
9630Sigor@sysoev.ru 
9640Sigor@sysoev.ru 
9650Sigor@sysoev.ru static ssize_t
966771Sigor@sysoev.ru nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b)
9670Sigor@sysoev.ru {
968771Sigor@sysoev.ru     int                 ret;
969771Sigor@sysoev.ru     size_t              size;
970771Sigor@sysoev.ru     nxt_int_t           n;
971771Sigor@sysoev.ru     nxt_err_t           err;
972771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
973771Sigor@sysoev.ru 
974771Sigor@sysoev.ru