xref: /unit/src/nxt_openssl.c (revision 1828)
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>
12*1828Sa.suvorov@f5.com #include <openssl/x509v3.h>
130Sigor@sysoev.ru 
140Sigor@sysoev.ru 
150Sigor@sysoev.ru typedef struct {
16*1828Sa.suvorov@f5.com     SSL             *session;
17*1828Sa.suvorov@f5.com     nxt_conn_t      *conn;
180Sigor@sysoev.ru 
19*1828Sa.suvorov@f5.com     int             ssl_error;
20*1828Sa.suvorov@f5.com     uint8_t         times;      /* 2 bits */
21*1828Sa.suvorov@f5.com     uint8_t         handshake;  /* 1 bit  */
220Sigor@sysoev.ru 
23*1828Sa.suvorov@f5.com     nxt_tls_conf_t  *conf;
24*1828Sa.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,
45*1828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t last);
46*1828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
47*1828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
48*1828Sa.suvorov@f5.com static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
49*1828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp);
50*1828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
51*1828Sa.suvorov@f5.com     void *data);
52*1828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_insert(nxt_task_t *task,
53*1828Sa.suvorov@f5.com     nxt_lvlhsh_t *lvlhsh, nxt_tls_bundle_hash_item_t *item, nxt_mp_t * mp);
54*1828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_servername(SSL *s, int *ad, void *arg);
55*1828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *nxt_openssl_find_ctx(nxt_tls_conf_t *conf,
56*1828Sa.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);
69771Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
70771Sigor@sysoev.ru     nxt_err_t err, const char *fmt, ...);
71771Sigor@sysoev.ru static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
720Sigor@sysoev.ru 
730Sigor@sysoev.ru 
74771Sigor@sysoev.ru const nxt_tls_lib_t  nxt_openssl_lib = {
75771Sigor@sysoev.ru     .library_init = nxt_openssl_library_init,
76771Sigor@sysoev.ru     .library_free = nxt_openssl_library_free,
77771Sigor@sysoev.ru 
78771Sigor@sysoev.ru     .server_init = nxt_openssl_server_init,
79771Sigor@sysoev.ru     .server_free = nxt_openssl_server_free,
800Sigor@sysoev.ru };
810Sigor@sysoev.ru 
820Sigor@sysoev.ru 
8362Sigor@sysoev.ru static nxt_conn_io_t  nxt_openssl_conn_io = {
84771Sigor@sysoev.ru     .read = nxt_conn_io_read,
85771Sigor@sysoev.ru     .recvbuf = nxt_openssl_conn_io_recvbuf,
860Sigor@sysoev.ru 
87771Sigor@sysoev.ru     .write = nxt_conn_io_write,
88771Sigor@sysoev.ru     .sendbuf = nxt_openssl_conn_io_sendbuf,
890Sigor@sysoev.ru 
90771Sigor@sysoev.ru     .shutdown = nxt_openssl_conn_io_shutdown,
910Sigor@sysoev.ru };
920Sigor@sysoev.ru 
930Sigor@sysoev.ru 
940Sigor@sysoev.ru static long  nxt_openssl_version;
950Sigor@sysoev.ru static int   nxt_openssl_connection_index;
960Sigor@sysoev.ru 
970Sigor@sysoev.ru 
980Sigor@sysoev.ru static nxt_int_t
99771Sigor@sysoev.ru nxt_openssl_library_init(nxt_task_t *task)
1000Sigor@sysoev.ru {
1010Sigor@sysoev.ru     int  index;
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru     if (nxt_fast_path(nxt_openssl_version != 0)) {
1040Sigor@sysoev.ru         return NXT_OK;
1050Sigor@sysoev.ru     }
1060Sigor@sysoev.ru 
107771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
108771Sigor@sysoev.ru 
109771Sigor@sysoev.ru     OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
1100Sigor@sysoev.ru 
111771Sigor@sysoev.ru #else
112771Sigor@sysoev.ru     {
113771Sigor@sysoev.ru         nxt_int_t  ret;
114771Sigor@sysoev.ru 
115771Sigor@sysoev.ru         SSL_load_error_strings();
116771Sigor@sysoev.ru 
117771Sigor@sysoev.ru         OPENSSL_config(NULL);
1180Sigor@sysoev.ru 
119771Sigor@sysoev.ru         /*
120771Sigor@sysoev.ru          * SSL_library_init(3):
121771Sigor@sysoev.ru          *
122771Sigor@sysoev.ru          *   SSL_library_init() always returns "1",
123771Sigor@sysoev.ru          *   so it is safe to discard the return value.
124771Sigor@sysoev.ru          */
125771Sigor@sysoev.ru         (void) SSL_library_init();
126771Sigor@sysoev.ru 
127771Sigor@sysoev.ru         ret = nxt_openssl_locks_init();
128771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
129771Sigor@sysoev.ru             return ret;
130771Sigor@sysoev.ru         }
131771Sigor@sysoev.ru     }
132771Sigor@sysoev.ru 
133771Sigor@sysoev.ru #endif
1340Sigor@sysoev.ru 
1350Sigor@sysoev.ru     nxt_openssl_version = SSLeay();
1360Sigor@sysoev.ru 
137771Sigor@sysoev.ru     nxt_log(task, NXT_LOG_INFO, "%s, %xl",
138771Sigor@sysoev.ru             SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
1390Sigor@sysoev.ru 
1400Sigor@sysoev.ru #ifndef SSL_OP_NO_COMPRESSION
1410Sigor@sysoev.ru     {
1420Sigor@sysoev.ru         /*
1430Sigor@sysoev.ru          * Disable gzip compression in OpenSSL prior to 1.0.0
1440Sigor@sysoev.ru          * version, this saves about 522K per connection.
1450Sigor@sysoev.ru          */
1460Sigor@sysoev.ru         int                 n;
1470Sigor@sysoev.ru         STACK_OF(SSL_COMP)  *ssl_comp_methods;
1480Sigor@sysoev.ru 
1490Sigor@sysoev.ru         ssl_comp_methods = SSL_COMP_get_compression_methods();
1500Sigor@sysoev.ru 
1510Sigor@sysoev.ru         for (n = sk_SSL_COMP_num(ssl_comp_methods); n != 0; n--) {
1520Sigor@sysoev.ru             (void) sk_SSL_COMP_pop(ssl_comp_methods);
1530Sigor@sysoev.ru         }
1540Sigor@sysoev.ru     }
1550Sigor@sysoev.ru #endif
1560Sigor@sysoev.ru 
1570Sigor@sysoev.ru     index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1580Sigor@sysoev.ru 
1590Sigor@sysoev.ru     if (index == -1) {
160771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
1610Sigor@sysoev.ru                               "SSL_get_ex_new_index() failed");
1620Sigor@sysoev.ru         return NXT_ERROR;
1630Sigor@sysoev.ru     }
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru     nxt_openssl_connection_index = index;
1660Sigor@sysoev.ru 
1670Sigor@sysoev.ru     return NXT_OK;
1680Sigor@sysoev.ru }
1690Sigor@sysoev.ru 
1700Sigor@sysoev.ru 
171771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
172771Sigor@sysoev.ru 
173771Sigor@sysoev.ru static void
174771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
175771Sigor@sysoev.ru {
176771Sigor@sysoev.ru }
177771Sigor@sysoev.ru 
178771Sigor@sysoev.ru #else
179771Sigor@sysoev.ru 
180771Sigor@sysoev.ru static nxt_thread_mutex_t  *nxt_openssl_locks;
181771Sigor@sysoev.ru 
1820Sigor@sysoev.ru static nxt_int_t
183771Sigor@sysoev.ru nxt_openssl_locks_init(void)
184771Sigor@sysoev.ru {
185771Sigor@sysoev.ru     int        i, n;
186771Sigor@sysoev.ru     nxt_int_t  ret;
187771Sigor@sysoev.ru 
188771Sigor@sysoev.ru     n = CRYPTO_num_locks();
189771Sigor@sysoev.ru 
190771Sigor@sysoev.ru     nxt_openssl_locks = OPENSSL_malloc(n * sizeof(nxt_thread_mutex_t));
191771Sigor@sysoev.ru     if (nxt_slow_path(nxt_openssl_locks == NULL)) {
192771Sigor@sysoev.ru         return NXT_ERROR;
193771Sigor@sysoev.ru     }
194771Sigor@sysoev.ru 
195771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
196771Sigor@sysoev.ru         ret = nxt_thread_mutex_create(&nxt_openssl_locks[i]);
197771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
198771Sigor@sysoev.ru             return ret;
199771Sigor@sysoev.ru         }
200771Sigor@sysoev.ru     }
201771Sigor@sysoev.ru 
202771Sigor@sysoev.ru     CRYPTO_set_locking_callback(nxt_openssl_lock);
203771Sigor@sysoev.ru 
204771Sigor@sysoev.ru     CRYPTO_set_id_callback(nxt_openssl_thread_id);
205771Sigor@sysoev.ru 
206771Sigor@sysoev.ru     return NXT_OK;
207771Sigor@sysoev.ru }
208771Sigor@sysoev.ru 
209771Sigor@sysoev.ru 
210771Sigor@sysoev.ru static void
211771Sigor@sysoev.ru nxt_openssl_lock(int mode, int type, const char *file, int line)
212771Sigor@sysoev.ru {
213771Sigor@sysoev.ru     nxt_thread_mutex_t  *lock;
214771Sigor@sysoev.ru 
215771Sigor@sysoev.ru     lock = &nxt_openssl_locks[type];
216771Sigor@sysoev.ru 
217771Sigor@sysoev.ru     if ((mode & CRYPTO_LOCK) != 0) {
218771Sigor@sysoev.ru         (void) nxt_thread_mutex_lock(lock);
219771Sigor@sysoev.ru 
220771Sigor@sysoev.ru     } else {
221771Sigor@sysoev.ru         (void) nxt_thread_mutex_unlock(lock);
222771Sigor@sysoev.ru     }
223771Sigor@sysoev.ru }
224771Sigor@sysoev.ru 
225771Sigor@sysoev.ru 
226771Sigor@sysoev.ru static u_long
227771Sigor@sysoev.ru nxt_openssl_thread_id(void)
228771Sigor@sysoev.ru {
229771Sigor@sysoev.ru     return (u_long) nxt_thread_handle();
230771Sigor@sysoev.ru }
231771Sigor@sysoev.ru 
232771Sigor@sysoev.ru 
233771Sigor@sysoev.ru static void
234771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
235771Sigor@sysoev.ru {
236771Sigor@sysoev.ru     nxt_openssl_locks_free();
237771Sigor@sysoev.ru }
238771Sigor@sysoev.ru 
239771Sigor@sysoev.ru 
240771Sigor@sysoev.ru static void
241771Sigor@sysoev.ru nxt_openssl_locks_free(void)
242771Sigor@sysoev.ru {
243771Sigor@sysoev.ru     int  i, n;
244771Sigor@sysoev.ru 
245771Sigor@sysoev.ru     n = CRYPTO_num_locks();
246771Sigor@sysoev.ru 
247771Sigor@sysoev.ru     CRYPTO_set_locking_callback(NULL);
248771Sigor@sysoev.ru 
249771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
250771Sigor@sysoev.ru         nxt_thread_mutex_destroy(&nxt_openssl_locks[i]);
251771Sigor@sysoev.ru     }
252771Sigor@sysoev.ru 
253771Sigor@sysoev.ru     OPENSSL_free(nxt_openssl_locks);
254771Sigor@sysoev.ru }
255771Sigor@sysoev.ru 
256771Sigor@sysoev.ru #endif
257771Sigor@sysoev.ru 
258771Sigor@sysoev.ru 
259771Sigor@sysoev.ru static nxt_int_t
260*1828Sa.suvorov@f5.com nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
261*1828Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_bool_t last)
2620Sigor@sysoev.ru {
263*1828Sa.suvorov@f5.com     SSL_CTX                *ctx;
264*1828Sa.suvorov@f5.com     const char             *ciphers, *ca_certificate;
265*1828Sa.suvorov@f5.com     STACK_OF(X509_NAME)    *list;
266*1828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
2670Sigor@sysoev.ru 
2680Sigor@sysoev.ru     ctx = SSL_CTX_new(SSLv23_server_method());
2690Sigor@sysoev.ru     if (ctx == NULL) {
270771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_new() failed");
2710Sigor@sysoev.ru         return NXT_ERROR;
2720Sigor@sysoev.ru     }
2730Sigor@sysoev.ru 
274*1828Sa.suvorov@f5.com     bundle = conf->bundle;
275*1828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
276*1828Sa.suvorov@f5.com 
277*1828Sa.suvorov@f5.com     bundle->ctx = ctx;
2780Sigor@sysoev.ru 
279771Sigor@sysoev.ru #ifdef SSL_OP_NO_RENEGOTIATION
280771Sigor@sysoev.ru     /* Renegration is not currently supported. */
281771Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
282771Sigor@sysoev.ru #endif
283771Sigor@sysoev.ru 
2840Sigor@sysoev.ru #ifdef SSL_OP_NO_COMPRESSION
2850Sigor@sysoev.ru     /*
2860Sigor@sysoev.ru      * Disable gzip compression in OpenSSL 1.0.0,
2870Sigor@sysoev.ru      * this saves about 522K per connection.
2880Sigor@sysoev.ru      */
2890Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
2900Sigor@sysoev.ru #endif
2910Sigor@sysoev.ru 
2920Sigor@sysoev.ru #ifdef SSL_MODE_RELEASE_BUFFERS
2930Sigor@sysoev.ru 
2940Sigor@sysoev.ru     if (nxt_openssl_version >= 10001078) {
2950Sigor@sysoev.ru         /*
2960Sigor@sysoev.ru          * Allow to release read and write buffers in OpenSSL 1.0.0,
2970Sigor@sysoev.ru          * this saves about 34K per idle connection.  It is not safe
2980Sigor@sysoev.ru          * before OpenSSL 1.0.1h (CVE-2010-5298).
2990Sigor@sysoev.ru          */
3000Sigor@sysoev.ru         SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
3010Sigor@sysoev.ru     }
3020Sigor@sysoev.ru 
3030Sigor@sysoev.ru #endif
3040Sigor@sysoev.ru 
305*1828Sa.suvorov@f5.com     if (nxt_openssl_chain_file(task, ctx, conf, mp,
306*1828Sa.suvorov@f5.com                                last && bundle->next == NULL)
307*1828Sa.suvorov@f5.com         != NXT_OK)
308*1828Sa.suvorov@f5.com     {
3090Sigor@sysoev.ru         goto fail;
3100Sigor@sysoev.ru     }
311774Svbart@nginx.com /*
3120Sigor@sysoev.ru     key = conf->certificate_key;
3130Sigor@sysoev.ru 
3140Sigor@sysoev.ru     if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
315771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3160Sigor@sysoev.ru                               "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
3170Sigor@sysoev.ru                               key);
3180Sigor@sysoev.ru         goto fail;
3190Sigor@sysoev.ru     }
320774Svbart@nginx.com */
3210Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
3220Sigor@sysoev.ru 
3230Sigor@sysoev.ru     if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
324771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3250Sigor@sysoev.ru                               "SSL_CTX_set_cipher_list(\"%s\") failed",
3260Sigor@sysoev.ru                               ciphers);
3270Sigor@sysoev.ru         goto fail;
3280Sigor@sysoev.ru     }
3290Sigor@sysoev.ru 
3300Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
3310Sigor@sysoev.ru 
3320Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
3330Sigor@sysoev.ru 
3340Sigor@sysoev.ru         /* TODO: verify callback */
3350Sigor@sysoev.ru         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru         /* TODO: verify depth */
3380Sigor@sysoev.ru         SSL_CTX_set_verify_depth(ctx, 1);
3390Sigor@sysoev.ru 
3400Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
3410Sigor@sysoev.ru 
3420Sigor@sysoev.ru         if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
343771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3440Sigor@sysoev.ru                               "SSL_CTX_load_verify_locations(\"%s\") failed",
3450Sigor@sysoev.ru                               ca_certificate);
3460Sigor@sysoev.ru             goto fail;
3470Sigor@sysoev.ru         }
3480Sigor@sysoev.ru 
3490Sigor@sysoev.ru         list = SSL_load_client_CA_file(ca_certificate);
3500Sigor@sysoev.ru 
3510Sigor@sysoev.ru         if (list == NULL) {
352771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3530Sigor@sysoev.ru                               "SSL_load_client_CA_file(\"%s\") failed",
3540Sigor@sysoev.ru                               ca_certificate);
3550Sigor@sysoev.ru             goto fail;
3560Sigor@sysoev.ru         }
3570Sigor@sysoev.ru 
3580Sigor@sysoev.ru         /*
3590Sigor@sysoev.ru          * SSL_load_client_CA_file() in OpenSSL prior to 0.9.7h and
3600Sigor@sysoev.ru          * 0.9.8 versions always leaves an error in the error queue.
3610Sigor@sysoev.ru          */
3620Sigor@sysoev.ru         ERR_clear_error();
3630Sigor@sysoev.ru 
3640Sigor@sysoev.ru         SSL_CTX_set_client_CA_list(ctx, list);
3650Sigor@sysoev.ru     }
3660Sigor@sysoev.ru 
367*1828Sa.suvorov@f5.com     if (last) {
368*1828Sa.suvorov@f5.com         conf->conn_init = nxt_openssl_conn_init;
369*1828Sa.suvorov@f5.com 
370*1828Sa.suvorov@f5.com         if (bundle->next != NULL) {
371*1828Sa.suvorov@f5.com             SSL_CTX_set_tlsext_servername_callback(ctx, nxt_openssl_servername);
372*1828Sa.suvorov@f5.com         }
373*1828Sa.suvorov@f5.com     }
374*1828Sa.suvorov@f5.com 
3750Sigor@sysoev.ru     return NXT_OK;
3760Sigor@sysoev.ru 
3770Sigor@sysoev.ru fail:
3780Sigor@sysoev.ru 
3790Sigor@sysoev.ru     SSL_CTX_free(ctx);
3800Sigor@sysoev.ru 
3811818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
3821818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
3831818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
3841818Smax.romanov@nginx.com #endif
3851818Smax.romanov@nginx.com 
3860Sigor@sysoev.ru     return NXT_ERROR;
3870Sigor@sysoev.ru }
3880Sigor@sysoev.ru 
3890Sigor@sysoev.ru 
390833Svbart@nginx.com static nxt_int_t
391*1828Sa.suvorov@f5.com nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf,
392*1828Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_bool_t single)
393774Svbart@nginx.com {
394*1828Sa.suvorov@f5.com     BIO                    *bio;
395*1828Sa.suvorov@f5.com     X509                   *cert, *ca;
396*1828Sa.suvorov@f5.com     long                   reason;
397*1828Sa.suvorov@f5.com     EVP_PKEY               *key;
398*1828Sa.suvorov@f5.com     nxt_int_t              ret;
399*1828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
400*1828Sa.suvorov@f5.com 
401*1828Sa.suvorov@f5.com     ret = NXT_ERROR;
402*1828Sa.suvorov@f5.com     cert = NULL;
403774Svbart@nginx.com 
404774Svbart@nginx.com     bio = BIO_new(BIO_s_fd());
405774Svbart@nginx.com     if (bio == NULL) {
406*1828Sa.suvorov@f5.com         goto end;
407774Svbart@nginx.com     }
408774Svbart@nginx.com 
409*1828Sa.suvorov@f5.com     bundle = conf->bundle;
410774Svbart@nginx.com 
411*1828Sa.suvorov@f5.com     BIO_set_fd(bio, bundle->chain_file, BIO_CLOSE);
412774Svbart@nginx.com 
413774Svbart@nginx.com     cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
414774Svbart@nginx.com     if (cert == NULL) {
415774Svbart@nginx.com         goto end;
416774Svbart@nginx.com     }
417774Svbart@nginx.com 
418774Svbart@nginx.com     if (SSL_CTX_use_certificate(ctx, cert) != 1) {
419774Svbart@nginx.com         goto end;
420774Svbart@nginx.com     }
421774Svbart@nginx.com 
422*1828Sa.suvorov@f5.com     if (!single && nxt_openssl_cert_get_names(task, cert, conf, mp) != NXT_OK) {
423*1828Sa.suvorov@f5.com         goto clean;
424*1828Sa.suvorov@f5.com     }
425*1828Sa.suvorov@f5.com 
426774Svbart@nginx.com     for ( ;; ) {
427774Svbart@nginx.com         ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
428774Svbart@nginx.com 
429774Svbart@nginx.com         if (ca == NULL) {
430774Svbart@nginx.com             reason = ERR_GET_REASON(ERR_peek_last_error());
431774Svbart@nginx.com             if (reason != PEM_R_NO_START_LINE) {
432774Svbart@nginx.com                 goto end;
433774Svbart@nginx.com             }
434774Svbart@nginx.com 
435774Svbart@nginx.com             ERR_clear_error();
436774Svbart@nginx.com             break;
437774Svbart@nginx.com         }
438774Svbart@nginx.com 
439774Svbart@nginx.com         /*
440774Svbart@nginx.com          * Note that ca isn't freed if it was successfully added to the chain,
441774Svbart@nginx.com          * while the main certificate needs a X509_free() call, since
442774Svbart@nginx.com          * its reference count is increased by SSL_CTX_use_certificate().
443774Svbart@nginx.com          */
444808Spluknet@nginx.com #ifdef SSL_CTX_add0_chain_cert
445774Svbart@nginx.com         if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) {
446774Svbart@nginx.com #else
447774Svbart@nginx.com         if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) {
448774Svbart@nginx.com #endif
449774Svbart@nginx.com             X509_free(ca);
450774Svbart@nginx.com             goto end;
451774Svbart@nginx.com         }
452774Svbart@nginx.com     }
453774Svbart@nginx.com 
454774Svbart@nginx.com     if (BIO_reset(bio) != 0) {
455774Svbart@nginx.com         goto end;
456774Svbart@nginx.com     }
457774Svbart@nginx.com 
458774Svbart@nginx.com     key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
459774Svbart@nginx.com     if (key == NULL) {
460774Svbart@nginx.com         goto end;
461774Svbart@nginx.com     }
462774Svbart@nginx.com 
463774Svbart@nginx.com     if (SSL_CTX_use_PrivateKey(ctx, key) == 1) {
464774Svbart@nginx.com         ret = NXT_OK;
465774Svbart@nginx.com     }
466774Svbart@nginx.com 
467774Svbart@nginx.com     EVP_PKEY_free(key);
468774Svbart@nginx.com 
469774Svbart@nginx.com end:
470774Svbart@nginx.com 
471*1828Sa.suvorov@f5.com     if (ret != NXT_OK) {
472*1828Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
473*1828Sa.suvorov@f5.com                               "nxt_openssl_chain_file() failed");
474*1828Sa.suvorov@f5.com     }
475*1828Sa.suvorov@f5.com 
476*1828Sa.suvorov@f5.com clean:
477*1828Sa.suvorov@f5.com 
478*1828Sa.suvorov@f5.com     BIO_free(bio);
479774Svbart@nginx.com     X509_free(cert);
480774Svbart@nginx.com 
481774Svbart@nginx.com     return ret;
482774Svbart@nginx.com }
483774Svbart@nginx.com 
484774Svbart@nginx.com 
485*1828Sa.suvorov@f5.com static nxt_uint_t
486*1828Sa.suvorov@f5.com nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
487*1828Sa.suvorov@f5.com     nxt_mp_t *mp)
488*1828Sa.suvorov@f5.com {
489*1828Sa.suvorov@f5.com     int                         len;
490*1828Sa.suvorov@f5.com     nxt_str_t                   domain, str;
491*1828Sa.suvorov@f5.com     X509_NAME                   *x509_name;
492*1828Sa.suvorov@f5.com     nxt_uint_t                  i, n;
493*1828Sa.suvorov@f5.com     GENERAL_NAME                *name;
494*1828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t       *bundle;
495*1828Sa.suvorov@f5.com     STACK_OF(GENERAL_NAME)      *alt_names;
496*1828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
497*1828Sa.suvorov@f5.com 
498*1828Sa.suvorov@f5.com     bundle = conf->bundle;
499*1828Sa.suvorov@f5.com 
500*1828Sa.suvorov@f5.com     alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
501*1828Sa.suvorov@f5.com 
502*1828Sa.suvorov@f5.com     if (alt_names != NULL) {
503*1828Sa.suvorov@f5.com         n = sk_GENERAL_NAME_num(alt_names);
504*1828Sa.suvorov@f5.com 
505*1828Sa.suvorov@f5.com         for (i = 0; i != n; i++) {
506*1828Sa.suvorov@f5.com             name = sk_GENERAL_NAME_value(alt_names, i);
507*1828Sa.suvorov@f5.com 
508*1828Sa.suvorov@f5.com             if (name->type != GEN_DNS) {
509*1828Sa.suvorov@f5.com                 continue;
510*1828Sa.suvorov@f5.com             }
511*1828Sa.suvorov@f5.com 
512*1828Sa.suvorov@f5.com             str.length = ASN1_STRING_length(name->d.dNSName);
513*1828Sa.suvorov@f5.com #if OPENSSL_VERSION_NUMBER > 0x10100000L
514*1828Sa.suvorov@f5.com             str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
515*1828Sa.suvorov@f5.com #else
516*1828Sa.suvorov@f5.com             str.start = ASN1_STRING_data(name->d.dNSName);
517*1828Sa.suvorov@f5.com #endif
518*1828Sa.suvorov@f5.com 
519*1828Sa.suvorov@f5.com             domain.start = nxt_mp_nget(mp, str.length);
520*1828Sa.suvorov@f5.com             if (nxt_slow_path(domain.start == NULL)) {
521*1828Sa.suvorov@f5.com                 goto fail;
522*1828Sa.suvorov@f5.com             }
523*1828Sa.suvorov@f5.com 
524*1828Sa.suvorov@f5.com             domain.length = str.length;
525*1828Sa.suvorov@f5.com             nxt_memcpy_lowcase(domain.start, str.start, str.length);
526*1828Sa.suvorov@f5.com 
527*1828Sa.suvorov@f5.com             item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
528*1828Sa.suvorov@f5.com             if (nxt_slow_path(item == NULL)) {
529*1828Sa.suvorov@f5.com                 goto fail;
530*1828Sa.suvorov@f5.com             }
531*1828Sa.suvorov@f5.com 
532*1828Sa.suvorov@f5.com             item->name = domain;
533*1828Sa.suvorov@f5.com             item->bundle = bundle;
534*1828Sa.suvorov@f5.com 
535*1828Sa.suvorov@f5.com             if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash,
536*1828Sa.suvorov@f5.com                                                item, mp)
537*1828Sa.suvorov@f5.com                 == NXT_ERROR)
538*1828Sa.suvorov@f5.com             {
539*1828Sa.suvorov@f5.com                 goto fail;
540*1828Sa.suvorov@f5.com             }
541*1828Sa.suvorov@f5.com         }
542*1828Sa.suvorov@f5.com 
543*1828Sa.suvorov@f5.com         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
544*1828Sa.suvorov@f5.com 
545*1828Sa.suvorov@f5.com     } else {
546*1828Sa.suvorov@f5.com         x509_name = X509_get_subject_name(cert);
547*1828Sa.suvorov@f5.com         len = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
548*1828Sa.suvorov@f5.com                                         NULL, 0);
549*1828Sa.suvorov@f5.com         if (len <= 0) {
550*1828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither "
551*1828Sa.suvorov@f5.com                     "Subject Alternative Name nor Common Name", bundle->name);
552*1828Sa.suvorov@f5.com             return NXT_OK;
553*1828Sa.suvorov@f5.com         }
554*1828Sa.suvorov@f5.com 
555*1828Sa.suvorov@f5.com         domain.start = nxt_mp_nget(mp, len + 1);
556*1828Sa.suvorov@f5.com         if (nxt_slow_path(domain.start == NULL)) {
557*1828Sa.suvorov@f5.com             return NXT_ERROR;
558*1828Sa.suvorov@f5.com         }
559*1828Sa.suvorov@f5.com 
560*1828Sa.suvorov@f5.com         domain.length = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
561*1828Sa.suvorov@f5.com                                                   (char *) domain.start,
562*1828Sa.suvorov@f5.com                                                   len + 1);
563*1828Sa.suvorov@f5.com         nxt_memcpy_lowcase(domain.start, domain.start, domain.length);
564*1828Sa.suvorov@f5.com 
565*1828Sa.suvorov@f5.com         item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
566*1828Sa.suvorov@f5.com         if (nxt_slow_path(item == NULL)) {
567*1828Sa.suvorov@f5.com             return NXT_ERROR;
568*1828Sa.suvorov@f5.com         }
569*1828Sa.suvorov@f5.com 
570*1828Sa.suvorov@f5.com         item->name = domain;
571*1828Sa.suvorov@f5.com         item->bundle = bundle;
572*1828Sa.suvorov@f5.com 
573*1828Sa.suvorov@f5.com         if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash, item,
574*1828Sa.suvorov@f5.com                                            mp)
575*1828Sa.suvorov@f5.com             == NXT_ERROR)
576*1828Sa.suvorov@f5.com         {
577*1828Sa.suvorov@f5.com             return NXT_ERROR;
578*1828Sa.suvorov@f5.com         }
579*1828Sa.suvorov@f5.com     }
580*1828Sa.suvorov@f5.com 
581*1828Sa.suvorov@f5.com     return NXT_OK;
582*1828Sa.suvorov@f5.com 
583*1828Sa.suvorov@f5.com fail:
584*1828Sa.suvorov@f5.com 
585*1828Sa.suvorov@f5.com     sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
586*1828Sa.suvorov@f5.com 
587*1828Sa.suvorov@f5.com     return NXT_ERROR;
588*1828Sa.suvorov@f5.com }
589*1828Sa.suvorov@f5.com 
590*1828Sa.suvorov@f5.com 
591*1828Sa.suvorov@f5.com static const nxt_lvlhsh_proto_t  nxt_openssl_bundle_hash_proto
592*1828Sa.suvorov@f5.com     nxt_aligned(64) =
593*1828Sa.suvorov@f5.com {
594*1828Sa.suvorov@f5.com     NXT_LVLHSH_DEFAULT,
595*1828Sa.suvorov@f5.com     nxt_openssl_bundle_hash_test,
596*1828Sa.suvorov@f5.com     nxt_mp_lvlhsh_alloc,
597*1828Sa.suvorov@f5.com     nxt_mp_lvlhsh_free,
598*1828Sa.suvorov@f5.com };
599*1828Sa.suvorov@f5.com 
600*1828Sa.suvorov@f5.com 
601*1828Sa.suvorov@f5.com static nxt_int_t
602*1828Sa.suvorov@f5.com nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
603*1828Sa.suvorov@f5.com {
604*1828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
605*1828Sa.suvorov@f5.com 
606*1828Sa.suvorov@f5.com     item = data;
607*1828Sa.suvorov@f5.com 
608*1828Sa.suvorov@f5.com     return nxt_strstr_eq(&lhq->key, &item->name) ? NXT_OK : NXT_DECLINED;
609*1828Sa.suvorov@f5.com }
610*1828Sa.suvorov@f5.com 
611*1828Sa.suvorov@f5.com 
612*1828Sa.suvorov@f5.com static nxt_int_t
613*1828Sa.suvorov@f5.com nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
614*1828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t *item, nxt_mp_t *mp)
615*1828Sa.suvorov@f5.com {
616*1828Sa.suvorov@f5.com     nxt_str_t                   str;
617*1828Sa.suvorov@f5.com     nxt_int_t                   ret;
618*1828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
619*1828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *old;
620*1828Sa.suvorov@f5.com 
621*1828Sa.suvorov@f5.com     str = item->name;
622*1828Sa.suvorov@f5.com 
623*1828Sa.suvorov@f5.com     if (item->name.start[0] == '*') {
624*1828Sa.suvorov@f5.com         item->name.start++;
625*1828Sa.suvorov@f5.com         item->name.length--;
626*1828Sa.suvorov@f5.com 
627*1828Sa.suvorov@f5.com         if (item->name.length == 0 || item->name.start[0] != '.') {
628*1828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" "
629*1828Sa.suvorov@f5.com                     "in certificate \"%V\": missing \".\" "
630*1828Sa.suvorov@f5.com                     "after wildcard symbol", &str, item->bundle->name);
631*1828Sa.suvorov@f5.com             return NXT_OK;
632*1828Sa.suvorov@f5.com         }
633*1828Sa.suvorov@f5.com     }
634*1828Sa.suvorov@f5.com 
635*1828Sa.suvorov@f5.com     lhq.pool = mp;
636*1828Sa.suvorov@f5.com     lhq.key = item->name;
637*1828Sa.suvorov@f5.com     lhq.value = item;
638*1828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
639*1828Sa.suvorov@f5.com     lhq.replace = 0;
640*1828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(item->name.start, item->name.length);
641*1828Sa.suvorov@f5.com 
642*1828Sa.suvorov@f5.com     ret = nxt_lvlhsh_insert(lvlhsh, &lhq);
643*1828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_OK)) {
644*1828Sa.suvorov@f5.com         nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted",
645*1828Sa.suvorov@f5.com                   &str, item->bundle->name);
646*1828Sa.suvorov@f5.com         return NXT_OK;
647*1828Sa.suvorov@f5.com     }
648*1828Sa.suvorov@f5.com 
649*1828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_DECLINED)) {
650*1828Sa.suvorov@f5.com         old = lhq.value;
651*1828Sa.suvorov@f5.com         if (old->bundle != item->bundle) {
652*1828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" "
653*1828Sa.suvorov@f5.com                     "in certificate \"%V\", identical name appears in \"%V\"",
654*1828Sa.suvorov@f5.com                     &str, old->bundle->name, item->bundle->name);
655*1828Sa.suvorov@f5.com 
656*1828Sa.suvorov@f5.com             old->bundle = item->bundle;
657*1828Sa.suvorov@f5.com         }
658*1828Sa.suvorov@f5.com 
659*1828Sa.suvorov@f5.com         return NXT_OK;
660*1828Sa.suvorov@f5.com     }
661*1828Sa.suvorov@f5.com 
662*1828Sa.suvorov@f5.com     return NXT_ERROR;
663*1828Sa.suvorov@f5.com }
664*1828Sa.suvorov@f5.com 
665*1828Sa.suvorov@f5.com 
666*1828Sa.suvorov@f5.com static nxt_int_t
667*1828Sa.suvorov@f5.com nxt_openssl_servername(SSL *s, int *ad, void *arg)
668*1828Sa.suvorov@f5.com {
669*1828Sa.suvorov@f5.com     nxt_str_t              str;
670*1828Sa.suvorov@f5.com     nxt_uint_t             i;
671*1828Sa.suvorov@f5.com     nxt_conn_t             *c;
672*1828Sa.suvorov@f5.com     const char             *servername;
673*1828Sa.suvorov@f5.com     nxt_tls_conf_t         *conf;
674*1828Sa.suvorov@f5.com     nxt_openssl_conn_t     *tls;
675*1828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
676*1828Sa.suvorov@f5.com 
677*1828Sa.suvorov@f5.com     c = SSL_get_ex_data(s, nxt_openssl_connection_index);
678*1828Sa.suvorov@f5.com 
679*1828Sa.suvorov@f5.com     if (nxt_slow_path(c == NULL)) {
680*1828Sa.suvorov@f5.com         nxt_thread_log_alert("SSL_get_ex_data() failed");
681*1828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
682*1828Sa.suvorov@f5.com     }
683*1828Sa.suvorov@f5.com 
684*1828Sa.suvorov@f5.com     servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
685*1828Sa.suvorov@f5.com     if (nxt_slow_path(servername == NULL)) {
686*1828Sa.suvorov@f5.com         nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned "
687*1828Sa.suvorov@f5.com                                                "NULL in server name callback");
688*1828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
689*1828Sa.suvorov@f5.com     }
690*1828Sa.suvorov@f5.com 
691*1828Sa.suvorov@f5.com     str.length = nxt_strlen(servername);
692*1828Sa.suvorov@f5.com     if (str.length == 0) {
693*1828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "client sent zero-length server name");
694*1828Sa.suvorov@f5.com         goto done;
695*1828Sa.suvorov@f5.com     }
696*1828Sa.suvorov@f5.com 
697*1828Sa.suvorov@f5.com     if (servername[0] == '.') {
698*1828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "ignored the server name \"%s\": "
699*1828Sa.suvorov@f5.com                                   "leading \".\"", servername);
700*1828Sa.suvorov@f5.com         goto done;
701*1828Sa.suvorov@f5.com     }
702*1828Sa.suvorov@f5.com 
703*1828Sa.suvorov@f5.com     nxt_debug(c->socket.task, "tls with servername \"%s\"", servername);
704*1828Sa.suvorov@f5.com 
705*1828Sa.suvorov@f5.com     str.start = nxt_mp_nget(c->mem_pool, str.length);
706*1828Sa.suvorov@f5.com     if (nxt_slow_path(str.start == NULL)) {
707*1828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
708*1828Sa.suvorov@f5.com     }
709*1828Sa.suvorov@f5.com 
710*1828Sa.suvorov@f5.com     nxt_memcpy_lowcase(str.start, (const u_char *) servername, str.length);
711*1828Sa.suvorov@f5.com 
712*1828Sa.suvorov@f5.com     tls = c->u.tls;
713*1828Sa.suvorov@f5.com     conf = tls->conf;
714*1828Sa.suvorov@f5.com 
715*1828Sa.suvorov@f5.com     bundle = nxt_openssl_find_ctx(conf, &str);
716*1828Sa.suvorov@f5.com 
717*1828Sa.suvorov@f5.com     if (bundle == NULL) {
718*1828Sa.suvorov@f5.com         for (i = 1; i < str.length; i++) {
719*1828Sa.suvorov@f5.com             if (str.start[i] == '.') {
720*1828Sa.suvorov@f5.com                 str.start += i;
721*1828Sa.suvorov@f5.com                 str.length -= i;
722*1828Sa.suvorov@f5.com 
723*1828Sa.suvorov@f5.com                 bundle = nxt_openssl_find_ctx(conf, &str);
724*1828Sa.suvorov@f5.com                 break;
725*1828Sa.suvorov@f5.com             }
726*1828Sa.suvorov@f5.com         }
727*1828Sa.suvorov@f5.com     }
728*1828Sa.suvorov@f5.com 
729*1828Sa.suvorov@f5.com     if (bundle != NULL) {
730*1828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" "
731*1828Sa.suvorov@f5.com                                   "(old: \"%V\")", &str, bundle->name,
732*1828Sa.suvorov@f5.com                                   conf->bundle->name);
733*1828Sa.suvorov@f5.com 
734*1828Sa.suvorov@f5.com         if (bundle != conf->bundle) {
735*1828Sa.suvorov@f5.com             if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) {
736*1828Sa.suvorov@f5.com                 nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
737*1828Sa.suvorov@f5.com                                       "SSL_set_SSL_CTX() failed");
738*1828Sa.suvorov@f5.com 
739*1828Sa.suvorov@f5.com                 return SSL_TLSEXT_ERR_ALERT_FATAL;
740*1828Sa.suvorov@f5.com             }
741*1828Sa.suvorov@f5.com         }
742*1828Sa.suvorov@f5.com     }
743*1828Sa.suvorov@f5.com 
744*1828Sa.suvorov@f5.com done:
745*1828Sa.suvorov@f5.com 
746*1828Sa.suvorov@f5.com     return SSL_TLSEXT_ERR_OK;
747*1828Sa.suvorov@f5.com }
748*1828Sa.suvorov@f5.com 
749*1828Sa.suvorov@f5.com 
750*1828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *
751*1828Sa.suvorov@f5.com nxt_openssl_find_ctx(nxt_tls_conf_t *conf, nxt_str_t *sn)
752*1828Sa.suvorov@f5.com {
753*1828Sa.suvorov@f5.com     nxt_int_t                   ret;
754*1828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
755*1828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
756*1828Sa.suvorov@f5.com 
757*1828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(sn->start, sn->length);
758*1828Sa.suvorov@f5.com     lhq.key = *sn;
759*1828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
760*1828Sa.suvorov@f5.com 
761*1828Sa.suvorov@f5.com     ret = nxt_lvlhsh_find(&conf->bundle_hash, &lhq);
762*1828Sa.suvorov@f5.com     if (ret != NXT_OK) {
763*1828Sa.suvorov@f5.com         return NULL;
764*1828Sa.suvorov@f5.com     }
765*1828Sa.suvorov@f5.com 
766*1828Sa.suvorov@f5.com     item = lhq.value;
767*1828Sa.suvorov@f5.com 
768*1828Sa.suvorov@f5.com     return item->bundle;
769*1828Sa.suvorov@f5.com }
770*1828Sa.suvorov@f5.com 
771*1828Sa.suvorov@f5.com 
7720Sigor@sysoev.ru static void
773771Sigor@sysoev.ru nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
7740Sigor@sysoev.ru {
775*1828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
776*1828Sa.suvorov@f5.com 
777*1828Sa.suvorov@f5.com     bundle = conf->bundle;
778*1828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
779*1828Sa.suvorov@f5.com 
780*1828Sa.suvorov@f5.com     do {
781*1828Sa.suvorov@f5.com         SSL_CTX_free(bundle->ctx);
782*1828Sa.suvorov@f5.com         bundle = bundle->next;
783*1828Sa.suvorov@f5.com     } while (bundle != NULL);
7841818Smax.romanov@nginx.com 
7851818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
7861818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
7871818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
7881818Smax.romanov@nginx.com #endif
789771Sigor@sysoev.ru }
790771Sigor@sysoev.ru 
791771Sigor@sysoev.ru 
792771Sigor@sysoev.ru static void
793771Sigor@sysoev.ru nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
794771Sigor@sysoev.ru {
795771Sigor@sysoev.ru     int                 ret;
796771Sigor@sysoev.ru     SSL                 *s;
797771Sigor@sysoev.ru     SSL_CTX             *ctx;
798771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
7990Sigor@sysoev.ru 
8000Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "openssl conn init");
8010Sigor@sysoev.ru 
802771Sigor@sysoev.ru     tls = nxt_mp_zget(c->mem_pool, sizeof(nxt_openssl_conn_t));
803771Sigor@sysoev.ru     if (tls == NULL) {
8040Sigor@sysoev.ru         goto fail;
8050Sigor@sysoev.ru     }
8060Sigor@sysoev.ru 
807771Sigor@sysoev.ru     c->u.tls = tls;
808771Sigor@sysoev.ru     nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size);
8090Sigor@sysoev.ru 
810*1828Sa.suvorov@f5.com     ctx = conf->bundle->ctx;
8110Sigor@sysoev.ru 
8120Sigor@sysoev.ru     s = SSL_new(ctx);
8130Sigor@sysoev.ru     if (s == NULL) {
814771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_new() failed");
8150Sigor@sysoev.ru         goto fail;
8160Sigor@sysoev.ru     }
8170Sigor@sysoev.ru 
818771Sigor@sysoev.ru     tls->session = s;
819*1828Sa.suvorov@f5.com     /* To pass TLS config to the nxt_openssl_servername() callback. */
820*1828Sa.suvorov@f5.com     tls->conf = conf;
821771Sigor@sysoev.ru     tls->conn = c;
8220Sigor@sysoev.ru 
8230Sigor@sysoev.ru     ret = SSL_set_fd(s, c->socket.fd);
8240Sigor@sysoev.ru 
8250Sigor@sysoev.ru     if (ret == 0) {
826771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_fd(%d) failed",
827771Sigor@sysoev.ru                               c->socket.fd);
8280Sigor@sysoev.ru         goto fail;
8290Sigor@sysoev.ru     }
8300Sigor@sysoev.ru 
8310Sigor@sysoev.ru     SSL_set_accept_state(s);
8320Sigor@sysoev.ru 
8330Sigor@sysoev.ru     if (SSL_set_ex_data(s, nxt_openssl_connection_index, c) == 0) {
834771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_ex_data() failed");
8350Sigor@sysoev.ru         goto fail;
8360Sigor@sysoev.ru     }
8370Sigor@sysoev.ru 
83862Sigor@sysoev.ru     c->io = &nxt_openssl_conn_io;
8390Sigor@sysoev.ru     c->sendfile = NXT_CONN_SENDFILE_OFF;
8400Sigor@sysoev.ru 
8411Sigor@sysoev.ru     nxt_openssl_conn_handshake(task, c, c->socket.data);
842*1828Sa.suvorov@f5.com     /*
843*1828Sa.suvorov@f5.com      * TLS configuration might be destroyed after the TLS connection
844*1828Sa.suvorov@f5.com      * is established.
845*1828Sa.suvorov@f5.com      */
846*1828Sa.suvorov@f5.com     tls->conf = NULL;
8470Sigor@sysoev.ru     return;
8480Sigor@sysoev.ru 
8490Sigor@sysoev.ru fail:
8500Sigor@sysoev.ru 
85113Sigor@sysoev.ru     nxt_work_queue_add(c->read_work_queue, c->read_state->error_handler,
85213Sigor@sysoev.ru                        task, c, c->socket.data);
8530Sigor@sysoev.ru }
8540Sigor@sysoev.ru 
8550Sigor@sysoev.ru 
856771Sigor@sysoev.ru nxt_inline void
857836Sigor@sysoev.ru nxt_openssl_conn_free(nxt_task_t *task, nxt_conn_t *c)
8580Sigor@sysoev.ru {
859836Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
860836Sigor@sysoev.ru 
861771Sigor@sysoev.ru     nxt_debug(task, "openssl conn free");
8620Sigor@sysoev.ru 
863836Sigor@sysoev.ru     tls = c->u.tls;
8640Sigor@sysoev.ru 
865836Sigor@sysoev.ru     if (tls != NULL) {
866836Sigor@sysoev.ru         c->u.tls = NULL;
867836Sigor@sysoev.ru         nxt_free(tls->buffer.start);
868836Sigor@sysoev.ru         SSL_free(tls->session);
869836Sigor@sysoev.ru     }
8700Sigor@sysoev.ru }
8710Sigor@sysoev.ru 
8720Sigor@sysoev.ru 
8730Sigor@sysoev.ru static void
8741Sigor@sysoev.ru nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data)
8750Sigor@sysoev.ru {
876771Sigor@sysoev.ru     int                     ret;
877771Sigor@sysoev.ru     nxt_int_t               n;
878771Sigor@sysoev.ru     nxt_err_t               err;
879771Sigor@sysoev.ru     nxt_conn_t              *c;
880771Sigor@sysoev.ru     nxt_work_queue_t        *wq;
881771Sigor@sysoev.ru     nxt_work_handler_t      handler;
882771Sigor@sysoev.ru     nxt_openssl_conn_t      *tls;
883771Sigor@sysoev.ru     const nxt_conn_state_t  *state;
8840Sigor@sysoev.ru 
8850Sigor@sysoev.ru     c = obj;
886836Sigor@sysoev.ru 
887836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake fd:%d", c->socket.fd);
888836Sigor@sysoev.ru 
889836Sigor@sysoev.ru     if (c->socket.error != 0) {
890836Sigor@sysoev.ru         return;
891836Sigor@sysoev.ru     }
892836Sigor@sysoev.ru 
893771Sigor@sysoev.ru     tls = c->u.tls;
8940Sigor@sysoev.ru 
895836Sigor@sysoev.ru     if (tls == NULL) {
896836Sigor@sysoev.ru         return;
897836Sigor@sysoev.ru     }
898836Sigor@sysoev.ru 
899836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake: %d times", tls->times);
9000Sigor@sysoev.ru 
901771Sigor@sysoev.ru     /* "tls->times == 1" is suitable to run SSL_do_handshake() in job. */
9020Sigor@sysoev.ru 
903771Sigor@sysoev.ru     ret = SSL_do_handshake(tls->session);
9040Sigor@sysoev.ru 
9050Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
9060Sigor@sysoev.ru 
9071Sigor@sysoev.ru     nxt_thread_time_debug_update(task->thread);
9080Sigor@sysoev.ru 
9091Sigor@sysoev.ru     nxt_debug(task, "SSL_do_handshake(%d): %d err:%d", c->socket.fd, ret, err);
9100Sigor@sysoev.ru 
911771Sigor@sysoev.ru     state = (c->read_state != NULL) ? c->read_state : c->write_state;
912771Sigor@sysoev.ru 
9130Sigor@sysoev.ru     if (ret > 0) {
9140Sigor@sysoev.ru         /* ret == 1, the handshake was successfully completed. */
915771Sigor@sysoev.ru         tls->handshake = 1;
9160Sigor@sysoev.ru 
917771Sigor@sysoev.ru         if (c->read_state != NULL) {
918771Sigor@sysoev.ru             if (state->io_read_handler != NULL || c->read != NULL) {
919771Sigor@sysoev.ru                 nxt_conn_read(task->thread->engine, c);
9200Sigor@sysoev.ru                 return;
9210Sigor@sysoev.ru             }
9220Sigor@sysoev.ru 
923771Sigor@sysoev.ru         } else {
924771Sigor@sysoev.ru             if (c->write != NULL) {
925771Sigor@sysoev.ru                 nxt_conn_write(task->thread->engine, c);
926771Sigor@sysoev.ru                 return;
927771Sigor@sysoev.ru             }
928771Sigor@sysoev.ru         }
929771Sigor@sysoev.ru 
930771Sigor@sysoev.ru         handler = state->ready_handler;
931771Sigor@sysoev.ru 
932771Sigor@sysoev.ru     } else {
933771Sigor@sysoev.ru         c->socket.read_handler = nxt_openssl_conn_handshake;
934771Sigor@sysoev.ru         c->socket.write_handler = nxt_openssl_conn_handshake;
935771Sigor@sysoev.ru 
936771Sigor@sysoev.ru         n = nxt_openssl_conn_test_error(task, c, ret, err,
937771Sigor@sysoev.ru                                         NXT_OPENSSL_HANDSHAKE);
938771Sigor@sysoev.ru         switch (n) {
9390Sigor@sysoev.ru 
940771Sigor@sysoev.ru         case NXT_AGAIN:
941771Sigor@sysoev.ru             if (tls->ssl_error == SSL_ERROR_WANT_READ && tls->times < 2) {
942771Sigor@sysoev.ru                 tls->times++;
943771Sigor@sysoev.ru             }
944771Sigor@sysoev.ru 
945771Sigor@sysoev.ru             return;
946771Sigor@sysoev.ru 
947771Sigor@sysoev.ru         case 0:
948771Sigor@sysoev.ru             handler = state->close_handler;
949771Sigor@sysoev.ru             break;
950771Sigor@sysoev.ru 
951771Sigor@sysoev.ru         default:
952771Sigor@sysoev.ru         case NXT_ERROR:
953771Sigor@sysoev.ru             nxt_openssl_conn_error(task, err, "SSL_do_handshake(%d) failed",
954771Sigor@sysoev.ru                                    c->socket.fd);
955771Sigor@sysoev.ru 
956771Sigor@sysoev.ru             handler = state->error_handler;
957771Sigor@sysoev.ru