xref: /unit/src/nxt_openssl.c (revision 1920)
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>
81885Sa.suvorov@f5.com #include <nxt_conf.h>
90Sigor@sysoev.ru #include <openssl/ssl.h>
100Sigor@sysoev.ru #include <openssl/conf.h>
110Sigor@sysoev.ru #include <openssl/err.h>
121818Smax.romanov@nginx.com #include <openssl/rand.h>
131828Sa.suvorov@f5.com #include <openssl/x509v3.h>
140Sigor@sysoev.ru 
150Sigor@sysoev.ru 
160Sigor@sysoev.ru typedef struct {
171828Sa.suvorov@f5.com     SSL             *session;
181828Sa.suvorov@f5.com     nxt_conn_t      *conn;
190Sigor@sysoev.ru 
201828Sa.suvorov@f5.com     int             ssl_error;
211828Sa.suvorov@f5.com     uint8_t         times;      /* 2 bits */
221828Sa.suvorov@f5.com     uint8_t         handshake;  /* 1 bit  */
230Sigor@sysoev.ru 
241828Sa.suvorov@f5.com     nxt_tls_conf_t  *conf;
251828Sa.suvorov@f5.com     nxt_buf_mem_t   buffer;
260Sigor@sysoev.ru } nxt_openssl_conn_t;
270Sigor@sysoev.ru 
280Sigor@sysoev.ru 
29771Sigor@sysoev.ru typedef enum {
30771Sigor@sysoev.ru     NXT_OPENSSL_HANDSHAKE = 0,
31771Sigor@sysoev.ru     NXT_OPENSSL_READ,
32771Sigor@sysoev.ru     NXT_OPENSSL_WRITE,
33771Sigor@sysoev.ru     NXT_OPENSSL_SHUTDOWN,
34771Sigor@sysoev.ru } nxt_openssl_io_t;
35771Sigor@sysoev.ru 
360Sigor@sysoev.ru 
37771Sigor@sysoev.ru static nxt_int_t nxt_openssl_library_init(nxt_task_t *task);
38771Sigor@sysoev.ru static void nxt_openssl_library_free(nxt_task_t *task);
39771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER < 0x10100004L
40771Sigor@sysoev.ru static nxt_int_t nxt_openssl_locks_init(void);
41771Sigor@sysoev.ru static void nxt_openssl_lock(int mode, int type, const char *file, int line);
42771Sigor@sysoev.ru static unsigned long nxt_openssl_thread_id(void);
43771Sigor@sysoev.ru static void nxt_openssl_locks_free(void);
44771Sigor@sysoev.ru #endif
45*1920Sa.suvorov@f5.com static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
46*1920Sa.suvorov@f5.com     nxt_tls_init_t *tls_init, nxt_bool_t last);
471828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
481828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
491885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
501885Sa.suvorov@f5.com static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx,
511885Sa.suvorov@f5.com     nxt_conf_value_t *value, nxt_mp_t *mp);
521885Sa.suvorov@f5.com #endif
53*1920Sa.suvorov@f5.com static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size,
54*1920Sa.suvorov@f5.com     time_t timeout);
551828Sa.suvorov@f5.com static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
561828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp);
571828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
581828Sa.suvorov@f5.com     void *data);
591828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_insert(nxt_task_t *task,
601828Sa.suvorov@f5.com     nxt_lvlhsh_t *lvlhsh, nxt_tls_bundle_hash_item_t *item, nxt_mp_t * mp);
611828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_servername(SSL *s, int *ad, void *arg);
621828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *nxt_openssl_find_ctx(nxt_tls_conf_t *conf,
631828Sa.suvorov@f5.com     nxt_str_t *sn);
64771Sigor@sysoev.ru static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf);
65771Sigor@sysoev.ru static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
6662Sigor@sysoev.ru     nxt_conn_t *c);
671Sigor@sysoev.ru static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data);
68771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
69771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb);
70771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb,
71771Sigor@sysoev.ru     void *buf, size_t size);
721Sigor@sysoev.ru static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
730Sigor@sysoev.ru     void *data);
74771Sigor@sysoev.ru static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c,
75771Sigor@sysoev.ru     int ret, nxt_err_t sys_err, nxt_openssl_io_t io);
761884Sa.suvorov@f5.com static void nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj,
771884Sa.suvorov@f5.com     void *data);
78771Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
79771Sigor@sysoev.ru     nxt_err_t err, const char *fmt, ...);
80771Sigor@sysoev.ru static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
810Sigor@sysoev.ru 
820Sigor@sysoev.ru 
83771Sigor@sysoev.ru const nxt_tls_lib_t  nxt_openssl_lib = {
84771Sigor@sysoev.ru     .library_init = nxt_openssl_library_init,
85771Sigor@sysoev.ru     .library_free = nxt_openssl_library_free,
86771Sigor@sysoev.ru 
87771Sigor@sysoev.ru     .server_init = nxt_openssl_server_init,
88771Sigor@sysoev.ru     .server_free = nxt_openssl_server_free,
890Sigor@sysoev.ru };
900Sigor@sysoev.ru 
910Sigor@sysoev.ru 
9262Sigor@sysoev.ru static nxt_conn_io_t  nxt_openssl_conn_io = {
93771Sigor@sysoev.ru     .read = nxt_conn_io_read,
94771Sigor@sysoev.ru     .recvbuf = nxt_openssl_conn_io_recvbuf,
950Sigor@sysoev.ru 
96771Sigor@sysoev.ru     .write = nxt_conn_io_write,
97771Sigor@sysoev.ru     .sendbuf = nxt_openssl_conn_io_sendbuf,
980Sigor@sysoev.ru 
99771Sigor@sysoev.ru     .shutdown = nxt_openssl_conn_io_shutdown,
1000Sigor@sysoev.ru };
1010Sigor@sysoev.ru 
1020Sigor@sysoev.ru 
1030Sigor@sysoev.ru static long  nxt_openssl_version;
1040Sigor@sysoev.ru static int   nxt_openssl_connection_index;
1050Sigor@sysoev.ru 
1060Sigor@sysoev.ru 
1070Sigor@sysoev.ru static nxt_int_t
108771Sigor@sysoev.ru nxt_openssl_library_init(nxt_task_t *task)
1090Sigor@sysoev.ru {
1100Sigor@sysoev.ru     int  index;
1110Sigor@sysoev.ru 
1120Sigor@sysoev.ru     if (nxt_fast_path(nxt_openssl_version != 0)) {
1130Sigor@sysoev.ru         return NXT_OK;
1140Sigor@sysoev.ru     }
1150Sigor@sysoev.ru 
116771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
117771Sigor@sysoev.ru 
118771Sigor@sysoev.ru     OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
1190Sigor@sysoev.ru 
120771Sigor@sysoev.ru #else
121771Sigor@sysoev.ru     {
122771Sigor@sysoev.ru         nxt_int_t  ret;
123771Sigor@sysoev.ru 
124771Sigor@sysoev.ru         SSL_load_error_strings();
125771Sigor@sysoev.ru 
126771Sigor@sysoev.ru         OPENSSL_config(NULL);
1270Sigor@sysoev.ru 
128771Sigor@sysoev.ru         /*
129771Sigor@sysoev.ru          * SSL_library_init(3):
130771Sigor@sysoev.ru          *
131771Sigor@sysoev.ru          *   SSL_library_init() always returns "1",
132771Sigor@sysoev.ru          *   so it is safe to discard the return value.
133771Sigor@sysoev.ru          */
134771Sigor@sysoev.ru         (void) SSL_library_init();
135771Sigor@sysoev.ru 
136771Sigor@sysoev.ru         ret = nxt_openssl_locks_init();
137771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
138771Sigor@sysoev.ru             return ret;
139771Sigor@sysoev.ru         }
140771Sigor@sysoev.ru     }
141771Sigor@sysoev.ru 
142771Sigor@sysoev.ru #endif
1430Sigor@sysoev.ru 
1440Sigor@sysoev.ru     nxt_openssl_version = SSLeay();
1450Sigor@sysoev.ru 
146771Sigor@sysoev.ru     nxt_log(task, NXT_LOG_INFO, "%s, %xl",
147771Sigor@sysoev.ru             SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
1480Sigor@sysoev.ru 
1490Sigor@sysoev.ru #ifndef SSL_OP_NO_COMPRESSION
1500Sigor@sysoev.ru     {
1510Sigor@sysoev.ru         /*
1520Sigor@sysoev.ru          * Disable gzip compression in OpenSSL prior to 1.0.0
1530Sigor@sysoev.ru          * version, this saves about 522K per connection.
1540Sigor@sysoev.ru          */
1550Sigor@sysoev.ru         int                 n;
1560Sigor@sysoev.ru         STACK_OF(SSL_COMP)  *ssl_comp_methods;
1570Sigor@sysoev.ru 
1580Sigor@sysoev.ru         ssl_comp_methods = SSL_COMP_get_compression_methods();
1590Sigor@sysoev.ru 
1600Sigor@sysoev.ru         for (n = sk_SSL_COMP_num(ssl_comp_methods); n != 0; n--) {
1610Sigor@sysoev.ru             (void) sk_SSL_COMP_pop(ssl_comp_methods);
1620Sigor@sysoev.ru         }
1630Sigor@sysoev.ru     }
1640Sigor@sysoev.ru #endif
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru     index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1670Sigor@sysoev.ru 
1680Sigor@sysoev.ru     if (index == -1) {
169771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
1700Sigor@sysoev.ru                               "SSL_get_ex_new_index() failed");
1710Sigor@sysoev.ru         return NXT_ERROR;
1720Sigor@sysoev.ru     }
1730Sigor@sysoev.ru 
1740Sigor@sysoev.ru     nxt_openssl_connection_index = index;
1750Sigor@sysoev.ru 
1760Sigor@sysoev.ru     return NXT_OK;
1770Sigor@sysoev.ru }
1780Sigor@sysoev.ru 
1790Sigor@sysoev.ru 
180771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
181771Sigor@sysoev.ru 
182771Sigor@sysoev.ru static void
183771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
184771Sigor@sysoev.ru {
185771Sigor@sysoev.ru }
186771Sigor@sysoev.ru 
187771Sigor@sysoev.ru #else
188771Sigor@sysoev.ru 
189771Sigor@sysoev.ru static nxt_thread_mutex_t  *nxt_openssl_locks;
190771Sigor@sysoev.ru 
1910Sigor@sysoev.ru static nxt_int_t
192771Sigor@sysoev.ru nxt_openssl_locks_init(void)
193771Sigor@sysoev.ru {
194771Sigor@sysoev.ru     int        i, n;
195771Sigor@sysoev.ru     nxt_int_t  ret;
196771Sigor@sysoev.ru 
197771Sigor@sysoev.ru     n = CRYPTO_num_locks();
198771Sigor@sysoev.ru 
199771Sigor@sysoev.ru     nxt_openssl_locks = OPENSSL_malloc(n * sizeof(nxt_thread_mutex_t));
200771Sigor@sysoev.ru     if (nxt_slow_path(nxt_openssl_locks == NULL)) {
201771Sigor@sysoev.ru         return NXT_ERROR;
202771Sigor@sysoev.ru     }
203771Sigor@sysoev.ru 
204771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
205771Sigor@sysoev.ru         ret = nxt_thread_mutex_create(&nxt_openssl_locks[i]);
206771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
207771Sigor@sysoev.ru             return ret;
208771Sigor@sysoev.ru         }
209771Sigor@sysoev.ru     }
210771Sigor@sysoev.ru 
211771Sigor@sysoev.ru     CRYPTO_set_locking_callback(nxt_openssl_lock);
212771Sigor@sysoev.ru 
213771Sigor@sysoev.ru     CRYPTO_set_id_callback(nxt_openssl_thread_id);
214771Sigor@sysoev.ru 
215771Sigor@sysoev.ru     return NXT_OK;
216771Sigor@sysoev.ru }
217771Sigor@sysoev.ru 
218771Sigor@sysoev.ru 
219771Sigor@sysoev.ru static void
220771Sigor@sysoev.ru nxt_openssl_lock(int mode, int type, const char *file, int line)
221771Sigor@sysoev.ru {
222771Sigor@sysoev.ru     nxt_thread_mutex_t  *lock;
223771Sigor@sysoev.ru 
224771Sigor@sysoev.ru     lock = &nxt_openssl_locks[type];
225771Sigor@sysoev.ru 
226771Sigor@sysoev.ru     if ((mode & CRYPTO_LOCK) != 0) {
227771Sigor@sysoev.ru         (void) nxt_thread_mutex_lock(lock);
228771Sigor@sysoev.ru 
229771Sigor@sysoev.ru     } else {
230771Sigor@sysoev.ru         (void) nxt_thread_mutex_unlock(lock);
231771Sigor@sysoev.ru     }
232771Sigor@sysoev.ru }
233771Sigor@sysoev.ru 
234771Sigor@sysoev.ru 
235771Sigor@sysoev.ru static u_long
236771Sigor@sysoev.ru nxt_openssl_thread_id(void)
237771Sigor@sysoev.ru {
238771Sigor@sysoev.ru     return (u_long) nxt_thread_handle();
239771Sigor@sysoev.ru }
240771Sigor@sysoev.ru 
241771Sigor@sysoev.ru 
242771Sigor@sysoev.ru static void
243771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
244771Sigor@sysoev.ru {
245771Sigor@sysoev.ru     nxt_openssl_locks_free();
246771Sigor@sysoev.ru }
247771Sigor@sysoev.ru 
248771Sigor@sysoev.ru 
249771Sigor@sysoev.ru static void
250771Sigor@sysoev.ru nxt_openssl_locks_free(void)
251771Sigor@sysoev.ru {
252771Sigor@sysoev.ru     int  i, n;
253771Sigor@sysoev.ru 
254771Sigor@sysoev.ru     n = CRYPTO_num_locks();
255771Sigor@sysoev.ru 
256771Sigor@sysoev.ru     CRYPTO_set_locking_callback(NULL);
257771Sigor@sysoev.ru 
258771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
259771Sigor@sysoev.ru         nxt_thread_mutex_destroy(&nxt_openssl_locks[i]);
260771Sigor@sysoev.ru     }
261771Sigor@sysoev.ru 
262771Sigor@sysoev.ru     OPENSSL_free(nxt_openssl_locks);
263771Sigor@sysoev.ru }
264771Sigor@sysoev.ru 
265771Sigor@sysoev.ru #endif
266771Sigor@sysoev.ru 
267771Sigor@sysoev.ru 
268771Sigor@sysoev.ru static nxt_int_t
269*1920Sa.suvorov@f5.com nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
270*1920Sa.suvorov@f5.com     nxt_tls_init_t *tls_init, nxt_bool_t last)
2710Sigor@sysoev.ru {
2721828Sa.suvorov@f5.com     SSL_CTX                *ctx;
2731828Sa.suvorov@f5.com     const char             *ciphers, *ca_certificate;
274*1920Sa.suvorov@f5.com     nxt_tls_conf_t         *conf;
2751828Sa.suvorov@f5.com     STACK_OF(X509_NAME)    *list;
2761828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
2770Sigor@sysoev.ru 
2780Sigor@sysoev.ru     ctx = SSL_CTX_new(SSLv23_server_method());
2790Sigor@sysoev.ru     if (ctx == NULL) {
280771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_new() failed");
2810Sigor@sysoev.ru         return NXT_ERROR;
2820Sigor@sysoev.ru     }
2830Sigor@sysoev.ru 
284*1920Sa.suvorov@f5.com     conf = tls_init->conf;
285*1920Sa.suvorov@f5.com 
2861828Sa.suvorov@f5.com     bundle = conf->bundle;
2871828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
2881828Sa.suvorov@f5.com 
2891828Sa.suvorov@f5.com     bundle->ctx = ctx;
2900Sigor@sysoev.ru 
291771Sigor@sysoev.ru #ifdef SSL_OP_NO_RENEGOTIATION
292771Sigor@sysoev.ru     /* Renegration is not currently supported. */
293771Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
294771Sigor@sysoev.ru #endif
295771Sigor@sysoev.ru 
2960Sigor@sysoev.ru #ifdef SSL_OP_NO_COMPRESSION
2970Sigor@sysoev.ru     /*
2980Sigor@sysoev.ru      * Disable gzip compression in OpenSSL 1.0.0,
2990Sigor@sysoev.ru      * this saves about 522K per connection.
3000Sigor@sysoev.ru      */
3010Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
3020Sigor@sysoev.ru #endif
3030Sigor@sysoev.ru 
3040Sigor@sysoev.ru #ifdef SSL_MODE_RELEASE_BUFFERS
3050Sigor@sysoev.ru 
3060Sigor@sysoev.ru     if (nxt_openssl_version >= 10001078) {
3070Sigor@sysoev.ru         /*
3080Sigor@sysoev.ru          * Allow to release read and write buffers in OpenSSL 1.0.0,
3090Sigor@sysoev.ru          * this saves about 34K per idle connection.  It is not safe
3100Sigor@sysoev.ru          * before OpenSSL 1.0.1h (CVE-2010-5298).
3110Sigor@sysoev.ru          */
3120Sigor@sysoev.ru         SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
3130Sigor@sysoev.ru     }
3140Sigor@sysoev.ru 
3150Sigor@sysoev.ru #endif
3160Sigor@sysoev.ru 
3171828Sa.suvorov@f5.com     if (nxt_openssl_chain_file(task, ctx, conf, mp,
3181828Sa.suvorov@f5.com                                last && bundle->next == NULL)
3191828Sa.suvorov@f5.com         != NXT_OK)
3201828Sa.suvorov@f5.com     {
3210Sigor@sysoev.ru         goto fail;
3220Sigor@sysoev.ru     }
323774Svbart@nginx.com /*
3240Sigor@sysoev.ru     key = conf->certificate_key;
3250Sigor@sysoev.ru 
3260Sigor@sysoev.ru     if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
327771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3280Sigor@sysoev.ru                               "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
3290Sigor@sysoev.ru                               key);
3300Sigor@sysoev.ru         goto fail;
3310Sigor@sysoev.ru     }
332774Svbart@nginx.com */
3331885Sa.suvorov@f5.com 
3340Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
3350Sigor@sysoev.ru 
3360Sigor@sysoev.ru     if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
337771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3380Sigor@sysoev.ru                               "SSL_CTX_set_cipher_list(\"%s\") failed",
3390Sigor@sysoev.ru                               ciphers);
3400Sigor@sysoev.ru         goto fail;
3410Sigor@sysoev.ru     }
3420Sigor@sysoev.ru 
3431885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
344*1920Sa.suvorov@f5.com     if (tls_init->conf_cmds != NULL
345*1920Sa.suvorov@f5.com         && nxt_ssl_conf_commands(task, ctx, tls_init->conf_cmds, mp) != NXT_OK)
3461885Sa.suvorov@f5.com     {
3471885Sa.suvorov@f5.com         goto fail;
3481885Sa.suvorov@f5.com     }
3491885Sa.suvorov@f5.com #endif
3501885Sa.suvorov@f5.com 
351*1920Sa.suvorov@f5.com     nxt_ssl_session_cache(ctx, tls_init->cache_size, tls_init->timeout);
352*1920Sa.suvorov@f5.com 
3530Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
3540Sigor@sysoev.ru 
3550Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
3560Sigor@sysoev.ru 
3570Sigor@sysoev.ru         /* TODO: verify callback */
3580Sigor@sysoev.ru         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
3590Sigor@sysoev.ru 
3600Sigor@sysoev.ru         /* TODO: verify depth */
3610Sigor@sysoev.ru         SSL_CTX_set_verify_depth(ctx, 1);
3620Sigor@sysoev.ru 
3630Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
3640Sigor@sysoev.ru 
3650Sigor@sysoev.ru         if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
366771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3670Sigor@sysoev.ru                               "SSL_CTX_load_verify_locations(\"%s\") failed",
3680Sigor@sysoev.ru                               ca_certificate);
3690Sigor@sysoev.ru             goto fail;
3700Sigor@sysoev.ru         }
3710Sigor@sysoev.ru 
3720Sigor@sysoev.ru         list = SSL_load_client_CA_file(ca_certificate);
3730Sigor@sysoev.ru 
3740Sigor@sysoev.ru         if (list == NULL) {
375771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3760Sigor@sysoev.ru                               "SSL_load_client_CA_file(\"%s\") failed",
3770Sigor@sysoev.ru                               ca_certificate);
3780Sigor@sysoev.ru             goto fail;
3790Sigor@sysoev.ru         }
3800Sigor@sysoev.ru 
3810Sigor@sysoev.ru         /*
3820Sigor@sysoev.ru          * SSL_load_client_CA_file() in OpenSSL prior to 0.9.7h and
3830Sigor@sysoev.ru          * 0.9.8 versions always leaves an error in the error queue.
3840Sigor@sysoev.ru          */
3850Sigor@sysoev.ru         ERR_clear_error();
3860Sigor@sysoev.ru 
3870Sigor@sysoev.ru         SSL_CTX_set_client_CA_list(ctx, list);
3880Sigor@sysoev.ru     }
3890Sigor@sysoev.ru 
3901828Sa.suvorov@f5.com     if (last) {
3911828Sa.suvorov@f5.com         conf->conn_init = nxt_openssl_conn_init;
3921828Sa.suvorov@f5.com 
3931828Sa.suvorov@f5.com         if (bundle->next != NULL) {
3941828Sa.suvorov@f5.com             SSL_CTX_set_tlsext_servername_callback(ctx, nxt_openssl_servername);
3951828Sa.suvorov@f5.com         }
3961828Sa.suvorov@f5.com     }
3971828Sa.suvorov@f5.com 
3980Sigor@sysoev.ru     return NXT_OK;
3990Sigor@sysoev.ru 
4000Sigor@sysoev.ru fail:
4010Sigor@sysoev.ru 
4020Sigor@sysoev.ru     SSL_CTX_free(ctx);
4030Sigor@sysoev.ru 
4041818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
4051818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
4061818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
4071818Smax.romanov@nginx.com #endif
4081818Smax.romanov@nginx.com 
4090Sigor@sysoev.ru     return NXT_ERROR;
4100Sigor@sysoev.ru }
4110Sigor@sysoev.ru 
4120Sigor@sysoev.ru 
413833Svbart@nginx.com static nxt_int_t
4141828Sa.suvorov@f5.com nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf,
4151828Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_bool_t single)
416774Svbart@nginx.com {
4171828Sa.suvorov@f5.com     BIO                    *bio;
4181828Sa.suvorov@f5.com     X509                   *cert, *ca;
4191828Sa.suvorov@f5.com     long                   reason;
4201828Sa.suvorov@f5.com     EVP_PKEY               *key;
4211828Sa.suvorov@f5.com     nxt_int_t              ret;
4221828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
4231828Sa.suvorov@f5.com 
4241828Sa.suvorov@f5.com     ret = NXT_ERROR;
4251828Sa.suvorov@f5.com     cert = NULL;
426774Svbart@nginx.com 
427774Svbart@nginx.com     bio = BIO_new(BIO_s_fd());
428774Svbart@nginx.com     if (bio == NULL) {
4291828Sa.suvorov@f5.com         goto end;
430774Svbart@nginx.com     }
431774Svbart@nginx.com 
4321828Sa.suvorov@f5.com     bundle = conf->bundle;
433774Svbart@nginx.com 
4341828Sa.suvorov@f5.com     BIO_set_fd(bio, bundle->chain_file, BIO_CLOSE);
435774Svbart@nginx.com 
436774Svbart@nginx.com     cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
437774Svbart@nginx.com     if (cert == NULL) {
438774Svbart@nginx.com         goto end;
439774Svbart@nginx.com     }
440774Svbart@nginx.com 
441774Svbart@nginx.com     if (SSL_CTX_use_certificate(ctx, cert) != 1) {
442774Svbart@nginx.com         goto end;
443774Svbart@nginx.com     }
444774Svbart@nginx.com 
4451828Sa.suvorov@f5.com     if (!single && nxt_openssl_cert_get_names(task, cert, conf, mp) != NXT_OK) {
4461828Sa.suvorov@f5.com         goto clean;
4471828Sa.suvorov@f5.com     }
4481828Sa.suvorov@f5.com 
449774Svbart@nginx.com     for ( ;; ) {
450774Svbart@nginx.com         ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
451774Svbart@nginx.com 
452774Svbart@nginx.com         if (ca == NULL) {
453774Svbart@nginx.com             reason = ERR_GET_REASON(ERR_peek_last_error());
454774Svbart@nginx.com             if (reason != PEM_R_NO_START_LINE) {
455774Svbart@nginx.com                 goto end;
456774Svbart@nginx.com             }
457774Svbart@nginx.com 
458774Svbart@nginx.com             ERR_clear_error();
459774Svbart@nginx.com             break;
460774Svbart@nginx.com         }
461774Svbart@nginx.com 
462774Svbart@nginx.com         /*
463774Svbart@nginx.com          * Note that ca isn't freed if it was successfully added to the chain,
464774Svbart@nginx.com          * while the main certificate needs a X509_free() call, since
465774Svbart@nginx.com          * its reference count is increased by SSL_CTX_use_certificate().
466774Svbart@nginx.com          */
467808Spluknet@nginx.com #ifdef SSL_CTX_add0_chain_cert
468774Svbart@nginx.com         if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) {
469774Svbart@nginx.com #else
470774Svbart@nginx.com         if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) {
471774Svbart@nginx.com #endif
472774Svbart@nginx.com             X509_free(ca);
473774Svbart@nginx.com             goto end;
474774Svbart@nginx.com         }
475774Svbart@nginx.com     }
476774Svbart@nginx.com 
477774Svbart@nginx.com     if (BIO_reset(bio) != 0) {
478774Svbart@nginx.com         goto end;
479774Svbart@nginx.com     }
480774Svbart@nginx.com 
481774Svbart@nginx.com     key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
482774Svbart@nginx.com     if (key == NULL) {
483774Svbart@nginx.com         goto end;
484774Svbart@nginx.com     }
485774Svbart@nginx.com 
486774Svbart@nginx.com     if (SSL_CTX_use_PrivateKey(ctx, key) == 1) {
487774Svbart@nginx.com         ret = NXT_OK;
488774Svbart@nginx.com     }
489774Svbart@nginx.com 
490774Svbart@nginx.com     EVP_PKEY_free(key);
491774Svbart@nginx.com 
492774Svbart@nginx.com end:
493774Svbart@nginx.com 
4941828Sa.suvorov@f5.com     if (ret != NXT_OK) {
4951828Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
4961828Sa.suvorov@f5.com                               "nxt_openssl_chain_file() failed");
4971828Sa.suvorov@f5.com     }
4981828Sa.suvorov@f5.com 
4991828Sa.suvorov@f5.com clean:
5001828Sa.suvorov@f5.com 
5011828Sa.suvorov@f5.com     BIO_free(bio);
502774Svbart@nginx.com     X509_free(cert);
503774Svbart@nginx.com 
504774Svbart@nginx.com     return ret;
505774Svbart@nginx.com }
506774Svbart@nginx.com 
507774Svbart@nginx.com 
5081885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
5091885Sa.suvorov@f5.com 
5101885Sa.suvorov@f5.com static nxt_int_t
5111885Sa.suvorov@f5.com nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, nxt_conf_value_t *conf,
5121885Sa.suvorov@f5.com     nxt_mp_t *mp)
5131885Sa.suvorov@f5.com {
5141885Sa.suvorov@f5.com     int               ret;
5151885Sa.suvorov@f5.com     char              *zcmd, *zvalue;
5161885Sa.suvorov@f5.com     uint32_t          index;
5171885Sa.suvorov@f5.com     nxt_str_t         cmd, value;
5181885Sa.suvorov@f5.com     SSL_CONF_CTX      *cctx;
5191885Sa.suvorov@f5.com     nxt_conf_value_t  *member;
5201885Sa.suvorov@f5.com 
5211885Sa.suvorov@f5.com     cctx = SSL_CONF_CTX_new();
5221885Sa.suvorov@f5.com     if (nxt_slow_path(cctx == NULL)) {
5231885Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
5241885Sa.suvorov@f5.com                               "SSL_CONF_CTX_new() failed");
5251885Sa.suvorov@f5.com         return NXT_ERROR;
5261885Sa.suvorov@f5.com     }
5271885Sa.suvorov@f5.com 
5281885Sa.suvorov@f5.com     SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE
5291885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_SERVER
5301885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_CERTIFICATE
5311885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_SHOW_ERRORS);
5321885Sa.suvorov@f5.com 
5331885Sa.suvorov@f5.com     SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
5341885Sa.suvorov@f5.com 
5351885Sa.suvorov@f5.com     index = 0;
5361885Sa.suvorov@f5.com 
5371885Sa.suvorov@f5.com     for ( ;; ) {
5381885Sa.suvorov@f5.com         member = nxt_conf_next_object_member(conf, &cmd, &index);
5391885Sa.suvorov@f5.com         if (nxt_slow_path(member == NULL)) {
5401885Sa.suvorov@f5.com             break;
5411885Sa.suvorov@f5.com         }
5421885Sa.suvorov@f5.com 
5431885Sa.suvorov@f5.com         nxt_conf_get_string(member, &value);
5441885Sa.suvorov@f5.com 
5451885Sa.suvorov@f5.com         zcmd = nxt_str_cstrz(mp, &cmd);
5461885Sa.suvorov@f5.com         zvalue = nxt_str_cstrz(mp, &value);
5471885Sa.suvorov@f5.com 
5481885Sa.suvorov@f5.com         if (nxt_slow_path(zcmd == NULL || zvalue == NULL)) {
5491885Sa.suvorov@f5.com             goto fail;
5501885Sa.suvorov@f5.com         }
5511885Sa.suvorov@f5.com 
5521885Sa.suvorov@f5.com         ret = SSL_CONF_cmd(cctx, zcmd, zvalue);
5531885Sa.suvorov@f5.com         if (ret == -2) {
5541885Sa.suvorov@f5.com             nxt_openssl_log_error(task, NXT_LOG_ERR,
5551885Sa.suvorov@f5.com                                   "unknown command \"%s\" in "
5561885Sa.suvorov@f5.com                                   "\"conf_commands\" option", zcmd);
5571885Sa.suvorov@f5.com             goto fail;
5581885Sa.suvorov@f5.com         }
5591885Sa.suvorov@f5.com 
5601885Sa.suvorov@f5.com         if (ret <= 0) {
5611885Sa.suvorov@f5.com             nxt_openssl_log_error(task, NXT_LOG_ERR,
5621885Sa.suvorov@f5.com                                   "invalid value \"%s\" for command \"%s\" "
5631885Sa.suvorov@f5.com                                   "in \"conf_commands\" option",
5641885Sa.suvorov@f5.com                                   zvalue, zcmd);
5651885Sa.suvorov@f5.com             goto fail;
5661885Sa.suvorov@f5.com         }
5671885Sa.suvorov@f5.com 
5681885Sa.suvorov@f5.com         nxt_debug(task, "SSL_CONF_cmd(\"%s\", \"%s\")", zcmd, zvalue);
5691885Sa.suvorov@f5.com     }
5701885Sa.suvorov@f5.com 
5711885Sa.suvorov@f5.com     if (SSL_CONF_CTX_finish(cctx) != 1) {
5721885Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
5731885Sa.suvorov@f5.com                               "SSL_CONF_finish() failed");
5741885Sa.suvorov@f5.com         goto fail;
5751885Sa.suvorov@f5.com     }
5761885Sa.suvorov@f5.com 
5771885Sa.suvorov@f5.com     SSL_CONF_CTX_free(cctx);
5781885Sa.suvorov@f5.com 
5791885Sa.suvorov@f5.com     return NXT_OK;
5801885Sa.suvorov@f5.com 
5811885Sa.suvorov@f5.com fail:
5821885Sa.suvorov@f5.com 
5831885Sa.suvorov@f5.com     SSL_CONF_CTX_free(cctx);
5841885Sa.suvorov@f5.com 
5851885Sa.suvorov@f5.com     return NXT_ERROR;
5861885Sa.suvorov@f5.com }
5871885Sa.suvorov@f5.com 
5881885Sa.suvorov@f5.com #endif
5891885Sa.suvorov@f5.com 
5901885Sa.suvorov@f5.com 
591*1920Sa.suvorov@f5.com static void
592*1920Sa.suvorov@f5.com nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout)
593*1920Sa.suvorov@f5.com {
594*1920Sa.suvorov@f5.com     if (cache_size == 0) {
595*1920Sa.suvorov@f5.com         SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
596*1920Sa.suvorov@f5.com         return;
597*1920Sa.suvorov@f5.com     }
598*1920Sa.suvorov@f5.com 
599*1920Sa.suvorov@f5.com     SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
600*1920Sa.suvorov@f5.com 
601*1920Sa.suvorov@f5.com     SSL_CTX_sess_set_cache_size(ctx, cache_size);
602*1920Sa.suvorov@f5.com 
603*1920Sa.suvorov@f5.com     SSL_CTX_set_timeout(ctx, (long) timeout);
604*1920Sa.suvorov@f5.com }
605*1920Sa.suvorov@f5.com 
606*1920Sa.suvorov@f5.com 
6071828Sa.suvorov@f5.com static nxt_uint_t
6081828Sa.suvorov@f5.com nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
6091828Sa.suvorov@f5.com     nxt_mp_t *mp)
6101828Sa.suvorov@f5.com {
6111828Sa.suvorov@f5.com     int                         len;
6121828Sa.suvorov@f5.com     nxt_str_t                   domain, str;
6131828Sa.suvorov@f5.com     X509_NAME                   *x509_name;
6141828Sa.suvorov@f5.com     nxt_uint_t                  i, n;
6151828Sa.suvorov@f5.com     GENERAL_NAME                *name;
6161828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t       *bundle;
6171828Sa.suvorov@f5.com     STACK_OF(GENERAL_NAME)      *alt_names;
6181828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
6191828Sa.suvorov@f5.com 
6201828Sa.suvorov@f5.com     bundle = conf->bundle;
6211828Sa.suvorov@f5.com 
6221828Sa.suvorov@f5.com     alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
6231828Sa.suvorov@f5.com 
6241828Sa.suvorov@f5.com     if (alt_names != NULL) {
6251828Sa.suvorov@f5.com         n = sk_GENERAL_NAME_num(alt_names);
6261828Sa.suvorov@f5.com 
6271828Sa.suvorov@f5.com         for (i = 0; i != n; i++) {
6281828Sa.suvorov@f5.com             name = sk_GENERAL_NAME_value(alt_names, i);
6291828Sa.suvorov@f5.com 
6301828Sa.suvorov@f5.com             if (name->type != GEN_DNS) {
6311828Sa.suvorov@f5.com                 continue;
6321828Sa.suvorov@f5.com             }
6331828Sa.suvorov@f5.com 
6341828Sa.suvorov@f5.com             str.length = ASN1_STRING_length(name->d.dNSName);
6351828Sa.suvorov@f5.com #if OPENSSL_VERSION_NUMBER > 0x10100000L
6361828Sa.suvorov@f5.com             str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
6371828Sa.suvorov@f5.com #else
6381828Sa.suvorov@f5.com             str.start = ASN1_STRING_data(name->d.dNSName);
6391828Sa.suvorov@f5.com #endif
6401828Sa.suvorov@f5.com 
6411828Sa.suvorov@f5.com             domain.start = nxt_mp_nget(mp, str.length);
6421828Sa.suvorov@f5.com             if (nxt_slow_path(domain.start == NULL)) {
6431828Sa.suvorov@f5.com                 goto fail;
6441828Sa.suvorov@f5.com             }
6451828Sa.suvorov@f5.com 
6461828Sa.suvorov@f5.com             domain.length = str.length;
6471828Sa.suvorov@f5.com             nxt_memcpy_lowcase(domain.start, str.start, str.length);
6481828Sa.suvorov@f5.com 
6491828Sa.suvorov@f5.com             item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
6501828Sa.suvorov@f5.com             if (nxt_slow_path(item == NULL)) {
6511828Sa.suvorov@f5.com                 goto fail;
6521828Sa.suvorov@f5.com             }
6531828Sa.suvorov@f5.com 
6541828Sa.suvorov@f5.com             item->name = domain;
6551828Sa.suvorov@f5.com             item->bundle = bundle;
6561828Sa.suvorov@f5.com 
6571828Sa.suvorov@f5.com             if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash,
6581828Sa.suvorov@f5.com                                                item, mp)
6591828Sa.suvorov@f5.com                 == NXT_ERROR)
6601828Sa.suvorov@f5.com             {
6611828Sa.suvorov@f5.com                 goto fail;
6621828Sa.suvorov@f5.com             }
6631828Sa.suvorov@f5.com         }
6641828Sa.suvorov@f5.com 
6651828Sa.suvorov@f5.com         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
6661828Sa.suvorov@f5.com 
6671828Sa.suvorov@f5.com     } else {
6681828Sa.suvorov@f5.com         x509_name = X509_get_subject_name(cert);
6691828Sa.suvorov@f5.com         len = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
6701828Sa.suvorov@f5.com                                         NULL, 0);
6711828Sa.suvorov@f5.com         if (len <= 0) {
6721828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither "
6731885Sa.suvorov@f5.com                     "Subject Alternative Name nor Common Name", &bundle->name);
6741828Sa.suvorov@f5.com             return NXT_OK;
6751828Sa.suvorov@f5.com         }
6761828Sa.suvorov@f5.com 
6771828Sa.suvorov@f5.com         domain.start = nxt_mp_nget(mp, len + 1);
6781828Sa.suvorov@f5.com         if (nxt_slow_path(domain.start == NULL)) {
6791828Sa.suvorov@f5.com             return NXT_ERROR;
6801828Sa.suvorov@f5.com         }
6811828Sa.suvorov@f5.com 
6821828Sa.suvorov@f5.com         domain.length = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
6831828Sa.suvorov@f5.com                                                   (char *) domain.start,
6841828Sa.suvorov@f5.com                                                   len + 1);
6851828Sa.suvorov@f5.com         nxt_memcpy_lowcase(domain.start, domain.start, domain.length);
6861828Sa.suvorov@f5.com 
6871828Sa.suvorov@f5.com         item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
6881828Sa.suvorov@f5.com         if (nxt_slow_path(item == NULL)) {
6891828Sa.suvorov@f5.com             return NXT_ERROR;
6901828Sa.suvorov@f5.com         }
6911828Sa.suvorov@f5.com 
6921828Sa.suvorov@f5.com         item->name = domain;
6931828Sa.suvorov@f5.com         item->bundle = bundle;
6941828Sa.suvorov@f5.com 
6951828Sa.suvorov@f5.com         if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash, item,
6961828Sa.suvorov@f5.com                                            mp)
6971828Sa.suvorov@f5.com             == NXT_ERROR)
6981828Sa.suvorov@f5.com         {
6991828Sa.suvorov@f5.com             return NXT_ERROR;
7001828Sa.suvorov@f5.com         }
7011828Sa.suvorov@f5.com     }
7021828Sa.suvorov@f5.com 
7031828Sa.suvorov@f5.com     return NXT_OK;
7041828Sa.suvorov@f5.com 
7051828Sa.suvorov@f5.com fail:
7061828Sa.suvorov@f5.com 
7071828Sa.suvorov@f5.com     sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
7081828Sa.suvorov@f5.com 
7091828Sa.suvorov@f5.com     return NXT_ERROR;
7101828Sa.suvorov@f5.com }
7111828Sa.suvorov@f5.com 
7121828Sa.suvorov@f5.com 
7131828Sa.suvorov@f5.com static const nxt_lvlhsh_proto_t  nxt_openssl_bundle_hash_proto
7141828Sa.suvorov@f5.com     nxt_aligned(64) =
7151828Sa.suvorov@f5.com {
7161828Sa.suvorov@f5.com     NXT_LVLHSH_DEFAULT,
7171828Sa.suvorov@f5.com     nxt_openssl_bundle_hash_test,
7181828Sa.suvorov@f5.com     nxt_mp_lvlhsh_alloc,
7191828Sa.suvorov@f5.com     nxt_mp_lvlhsh_free,
7201828Sa.suvorov@f5.com };
7211828Sa.suvorov@f5.com 
7221828Sa.suvorov@f5.com 
7231828Sa.suvorov@f5.com static nxt_int_t
7241828Sa.suvorov@f5.com nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
7251828Sa.suvorov@f5.com {
7261828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
7271828Sa.suvorov@f5.com 
7281828Sa.suvorov@f5.com     item = data;
7291828Sa.suvorov@f5.com 
7301828Sa.suvorov@f5.com     return nxt_strstr_eq(&lhq->key, &item->name) ? NXT_OK : NXT_DECLINED;
7311828Sa.suvorov@f5.com }
7321828Sa.suvorov@f5.com 
7331828Sa.suvorov@f5.com 
7341828Sa.suvorov@f5.com static nxt_int_t
7351828Sa.suvorov@f5.com nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
7361828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t *item, nxt_mp_t *mp)
7371828Sa.suvorov@f5.com {
7381828Sa.suvorov@f5.com     nxt_str_t                   str;
7391828Sa.suvorov@f5.com     nxt_int_t                   ret;
7401828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
7411828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *old;
7421828Sa.suvorov@f5.com 
7431828Sa.suvorov@f5.com     str = item->name;
7441828Sa.suvorov@f5.com 
7451828Sa.suvorov@f5.com     if (item->name.start[0] == '*') {
7461828Sa.suvorov@f5.com         item->name.start++;
7471828Sa.suvorov@f5.com         item->name.length--;
7481828Sa.suvorov@f5.com 
7491828Sa.suvorov@f5.com         if (item->name.length == 0 || item->name.start[0] != '.') {
7501828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" "
7511828Sa.suvorov@f5.com                     "in certificate \"%V\": missing \".\" "
7521885Sa.suvorov@f5.com                     "after wildcard symbol", &str, &item->bundle->name);
7531828Sa.suvorov@f5.com             return NXT_OK;
7541828Sa.suvorov@f5.com         }
7551828Sa.suvorov@f5.com     }
7561828Sa.suvorov@f5.com 
7571828Sa.suvorov@f5.com     lhq.pool = mp;
7581828Sa.suvorov@f5.com     lhq.key = item->name;
7591828Sa.suvorov@f5.com     lhq.value = item;
7601828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
7611828Sa.suvorov@f5.com     lhq.replace = 0;
7621828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(item->name.start, item->name.length);
7631828Sa.suvorov@f5.com 
7641828Sa.suvorov@f5.com     ret = nxt_lvlhsh_insert(lvlhsh, &lhq);
7651828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_OK)) {
7661828Sa.suvorov@f5.com         nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted",
7671885Sa.suvorov@f5.com                   &str, &item->bundle->name);
7681828Sa.suvorov@f5.com         return NXT_OK;
7691828Sa.suvorov@f5.com     }
7701828Sa.suvorov@f5.com 
7711828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_DECLINED)) {
7721828Sa.suvorov@f5.com         old = lhq.value;
7731828Sa.suvorov@f5.com         if (old->bundle != item->bundle) {
7741828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" "
7751828Sa.suvorov@f5.com                     "in certificate \"%V\", identical name appears in \"%V\"",
7761885Sa.suvorov@f5.com                     &str, &old->bundle->name, &item->bundle->name);
7771828Sa.suvorov@f5.com 
7781828Sa.suvorov@f5.com             old->bundle = item->bundle;
7791828Sa.suvorov@f5.com         }
7801828Sa.suvorov@f5.com 
7811828Sa.suvorov@f5.com         return NXT_OK;
7821828Sa.suvorov@f5.com     }
7831828Sa.suvorov@f5.com 
7841828Sa.suvorov@f5.com     return NXT_ERROR;
7851828Sa.suvorov@f5.com }
7861828Sa.suvorov@f5.com 
7871828Sa.suvorov@f5.com 
7881828Sa.suvorov@f5.com static nxt_int_t
7891828Sa.suvorov@f5.com nxt_openssl_servername(SSL *s, int *ad, void *arg)
7901828Sa.suvorov@f5.com {
7911828Sa.suvorov@f5.com     nxt_str_t              str;
7921828Sa.suvorov@f5.com     nxt_uint_t             i;
7931828Sa.suvorov@f5.com     nxt_conn_t             *c;
7941828Sa.suvorov@f5.com     const char             *servername;
7951828Sa.suvorov@f5.com     nxt_tls_conf_t         *conf;
7961828Sa.suvorov@f5.com     nxt_openssl_conn_t     *tls;
7971828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
7981828Sa.suvorov@f5.com 
7991828Sa.suvorov@f5.com     c = SSL_get_ex_data(s, nxt_openssl_connection_index);
8001828Sa.suvorov@f5.com 
8011828Sa.suvorov@f5.com     if (nxt_slow_path(c == NULL)) {
8021828Sa.suvorov@f5.com         nxt_thread_log_alert("SSL_get_ex_data() failed");
8031828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
8041828Sa.suvorov@f5.com     }
8051828Sa.suvorov@f5.com 
8061828Sa.suvorov@f5.com     servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
8071828Sa.suvorov@f5.com     if (nxt_slow_path(servername == NULL)) {
8081828Sa.suvorov@f5.com         nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned "
8091828Sa.suvorov@f5.com                                                "NULL in server name callback");
8101828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
8111828Sa.suvorov@f5.com     }
8121828Sa.suvorov@f5.com 
8131828Sa.suvorov@f5.com     str.length = nxt_strlen(servername);
8141828Sa.suvorov@f5.com     if (str.length == 0) {
8151828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "client sent zero-length server name");
8161828Sa.suvorov@f5.com         goto done;
8171828Sa.suvorov@f5.com     }
8181828Sa.suvorov@f5.com 
8191828Sa.suvorov@f5.com     if (servername[0] == '.') {
8201828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "ignored the server name \"%s\": "
8211828Sa.suvorov@f5.com                                   "leading \".\"", servername);
8221828Sa.suvorov@f5.com         goto done;
8231828Sa.suvorov@f5.com     }
8241828Sa.suvorov@f5.com 
8251828Sa.suvorov@f5.com     nxt_debug(c->socket.task, "tls with servername \"%s\"", servername);
8261828Sa.suvorov@f5.com 
8271828Sa.suvorov@f5.com     str.start = nxt_mp_nget(c->mem_pool, str.length);
8281828Sa.suvorov@f5.com     if (nxt_slow_path(str.start == NULL)) {
8291828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
8301828Sa.suvorov@f5.com     }
8311828Sa.suvorov@f5.com 
8321828Sa.suvorov@f5.com     nxt_memcpy_lowcase(str.start, (const u_char *) servername, str.length);
8331828Sa.suvorov@f5.com 
8341828Sa.suvorov@f5.com     tls = c->u.tls;
8351828Sa.suvorov@f5.com     conf = tls->conf;
8361828Sa.suvorov@f5.com 
8371828Sa.suvorov@f5.com     bundle = nxt_openssl_find_ctx(conf, &str);
8381828Sa.suvorov@f5.com 
8391828Sa.suvorov@f5.com     if (bundle == NULL) {
8401828Sa.suvorov@f5.com         for (i = 1; i < str.length; i++) {
8411828Sa.suvorov@f5.com             if (str.start[i] == '.') {
8421828Sa.suvorov@f5.com                 str.start += i;
8431828Sa.suvorov@f5.com                 str.length -= i;
8441828Sa.suvorov@f5.com 
8451828Sa.suvorov@f5.com                 bundle = nxt_openssl_find_ctx(conf, &str);
8461828Sa.suvorov@f5.com                 break;
8471828Sa.suvorov@f5.com             }
8481828Sa.suvorov@f5.com         }
8491828Sa.suvorov@f5.com     }
8501828Sa.suvorov@f5.com 
8511828Sa.suvorov@f5.com     if (bundle != NULL) {
8521828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" "
8531885Sa.suvorov@f5.com                                   "(old: \"%V\")", &str, &bundle->name,
8541885Sa.suvorov@f5.com                                   &conf->bundle->name);
8551828Sa.suvorov@f5.com 
8561828Sa.suvorov@f5.com         if (bundle != conf->bundle) {
8571828Sa.suvorov@f5.com             if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) {
8581828Sa.suvorov@f5.com                 nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
8591828Sa.suvorov@f5.com                                       "SSL_set_SSL_CTX() failed");
8601828Sa.suvorov@f5.com 
8611828Sa.suvorov@f5.com                 return SSL_TLSEXT_ERR_ALERT_FATAL;
8621828Sa.suvorov@f5.com             }
8631828Sa.suvorov@f5.com         }
8641828Sa.suvorov@f5.com     }
8651828Sa.suvorov@f5.com 
8661828Sa.suvorov@f5.com done:
8671828Sa.suvorov@f5.com 
8681828Sa.suvorov@f5.com     return SSL_TLSEXT_ERR_OK;
8691828Sa.suvorov@f5.com }
8701828Sa.suvorov@f5.com 
8711828Sa.suvorov@f5.com 
8721828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *
8731828Sa.suvorov@f5.com nxt_openssl_find_ctx(nxt_tls_conf_t *conf, nxt_str_t *sn)
8741828Sa.suvorov@f5.com {
8751828Sa.suvorov@f5.com     nxt_int_t                   ret;
8761828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
8771828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
8781828Sa.suvorov@f5.com 
8791828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(sn->start, sn->length);
8801828Sa.suvorov@f5.com     lhq.key = *sn;
8811828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
8821828Sa.suvorov@f5.com 
8831828Sa.suvorov@f5.com     ret = nxt_lvlhsh_find(&conf->bundle_hash, &lhq);
8841828Sa.suvorov@f5.com     if (ret != NXT_OK) {
8851828Sa.suvorov@f5.com         return NULL;
8861828Sa.suvorov@f5.com     }
8871828Sa.suvorov@f5.com 
8881828Sa.suvorov@f5.com     item = lhq.value;
8891828Sa.suvorov@f5.com 
8901828Sa.suvorov@f5.com     return item->bundle;
8911828Sa.suvorov@f5.com }
8921828Sa.suvorov@f5.com 
8931828Sa.suvorov@f5.com 
8940Sigor@sysoev.ru static void
895771Sigor@sysoev.ru nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
8960Sigor@sysoev.ru {
8971828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
8981828Sa.suvorov@f5.com 
8991828Sa.suvorov@f5.com     bundle = conf->bundle;
9001828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
9011828Sa.suvorov@f5.com 
9021828Sa.suvorov@f5.com     do {
9031828Sa.suvorov@f5.com         SSL_CTX_free(bundle->ctx);
904