xref: /unit/src/nxt_openssl.c (revision 1885)
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>
8*1885Sa.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
45771Sigor@sysoev.ru static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
46*1885Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_conf_value_t *conf_cmds,
47*1885Sa.suvorov@f5.com     nxt_bool_t last);
481828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
491828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
50*1885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
51*1885Sa.suvorov@f5.com static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx,
52*1885Sa.suvorov@f5.com     nxt_conf_value_t *value, nxt_mp_t *mp);
53*1885Sa.suvorov@f5.com #endif
541828Sa.suvorov@f5.com static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
551828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp);
561828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
571828Sa.suvorov@f5.com     void *data);
581828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_insert(nxt_task_t *task,
591828Sa.suvorov@f5.com     nxt_lvlhsh_t *lvlhsh, nxt_tls_bundle_hash_item_t *item, nxt_mp_t * mp);
601828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_servername(SSL *s, int *ad, void *arg);
611828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *nxt_openssl_find_ctx(nxt_tls_conf_t *conf,
621828Sa.suvorov@f5.com     nxt_str_t *sn);
63771Sigor@sysoev.ru static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf);
64771Sigor@sysoev.ru static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
6562Sigor@sysoev.ru     nxt_conn_t *c);
661Sigor@sysoev.ru static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data);
67771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
68771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb);
69771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb,
70771Sigor@sysoev.ru     void *buf, size_t size);
711Sigor@sysoev.ru static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
720Sigor@sysoev.ru     void *data);
73771Sigor@sysoev.ru static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c,
74771Sigor@sysoev.ru     int ret, nxt_err_t sys_err, nxt_openssl_io_t io);
751884Sa.suvorov@f5.com static void nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj,
761884Sa.suvorov@f5.com     void *data);
77771Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
78771Sigor@sysoev.ru     nxt_err_t err, const char *fmt, ...);
79771Sigor@sysoev.ru static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
800Sigor@sysoev.ru 
810Sigor@sysoev.ru 
82771Sigor@sysoev.ru const nxt_tls_lib_t  nxt_openssl_lib = {
83771Sigor@sysoev.ru     .library_init = nxt_openssl_library_init,
84771Sigor@sysoev.ru     .library_free = nxt_openssl_library_free,
85771Sigor@sysoev.ru 
86771Sigor@sysoev.ru     .server_init = nxt_openssl_server_init,
87771Sigor@sysoev.ru     .server_free = nxt_openssl_server_free,
880Sigor@sysoev.ru };
890Sigor@sysoev.ru 
900Sigor@sysoev.ru 
9162Sigor@sysoev.ru static nxt_conn_io_t  nxt_openssl_conn_io = {
92771Sigor@sysoev.ru     .read = nxt_conn_io_read,
93771Sigor@sysoev.ru     .recvbuf = nxt_openssl_conn_io_recvbuf,
940Sigor@sysoev.ru 
95771Sigor@sysoev.ru     .write = nxt_conn_io_write,
96771Sigor@sysoev.ru     .sendbuf = nxt_openssl_conn_io_sendbuf,
970Sigor@sysoev.ru 
98771Sigor@sysoev.ru     .shutdown = nxt_openssl_conn_io_shutdown,
990Sigor@sysoev.ru };
1000Sigor@sysoev.ru 
1010Sigor@sysoev.ru 
1020Sigor@sysoev.ru static long  nxt_openssl_version;
1030Sigor@sysoev.ru static int   nxt_openssl_connection_index;
1040Sigor@sysoev.ru 
1050Sigor@sysoev.ru 
1060Sigor@sysoev.ru static nxt_int_t
107771Sigor@sysoev.ru nxt_openssl_library_init(nxt_task_t *task)
1080Sigor@sysoev.ru {
1090Sigor@sysoev.ru     int  index;
1100Sigor@sysoev.ru 
1110Sigor@sysoev.ru     if (nxt_fast_path(nxt_openssl_version != 0)) {
1120Sigor@sysoev.ru         return NXT_OK;
1130Sigor@sysoev.ru     }
1140Sigor@sysoev.ru 
115771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
116771Sigor@sysoev.ru 
117771Sigor@sysoev.ru     OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
1180Sigor@sysoev.ru 
119771Sigor@sysoev.ru #else
120771Sigor@sysoev.ru     {
121771Sigor@sysoev.ru         nxt_int_t  ret;
122771Sigor@sysoev.ru 
123771Sigor@sysoev.ru         SSL_load_error_strings();
124771Sigor@sysoev.ru 
125771Sigor@sysoev.ru         OPENSSL_config(NULL);
1260Sigor@sysoev.ru 
127771Sigor@sysoev.ru         /*
128771Sigor@sysoev.ru          * SSL_library_init(3):
129771Sigor@sysoev.ru          *
130771Sigor@sysoev.ru          *   SSL_library_init() always returns "1",
131771Sigor@sysoev.ru          *   so it is safe to discard the return value.
132771Sigor@sysoev.ru          */
133771Sigor@sysoev.ru         (void) SSL_library_init();
134771Sigor@sysoev.ru 
135771Sigor@sysoev.ru         ret = nxt_openssl_locks_init();
136771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
137771Sigor@sysoev.ru             return ret;
138771Sigor@sysoev.ru         }
139771Sigor@sysoev.ru     }
140771Sigor@sysoev.ru 
141771Sigor@sysoev.ru #endif
1420Sigor@sysoev.ru 
1430Sigor@sysoev.ru     nxt_openssl_version = SSLeay();
1440Sigor@sysoev.ru 
145771Sigor@sysoev.ru     nxt_log(task, NXT_LOG_INFO, "%s, %xl",
146771Sigor@sysoev.ru             SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
1470Sigor@sysoev.ru 
1480Sigor@sysoev.ru #ifndef SSL_OP_NO_COMPRESSION
1490Sigor@sysoev.ru     {
1500Sigor@sysoev.ru         /*
1510Sigor@sysoev.ru          * Disable gzip compression in OpenSSL prior to 1.0.0
1520Sigor@sysoev.ru          * version, this saves about 522K per connection.
1530Sigor@sysoev.ru          */
1540Sigor@sysoev.ru         int                 n;
1550Sigor@sysoev.ru         STACK_OF(SSL_COMP)  *ssl_comp_methods;
1560Sigor@sysoev.ru 
1570Sigor@sysoev.ru         ssl_comp_methods = SSL_COMP_get_compression_methods();
1580Sigor@sysoev.ru 
1590Sigor@sysoev.ru         for (n = sk_SSL_COMP_num(ssl_comp_methods); n != 0; n--) {
1600Sigor@sysoev.ru             (void) sk_SSL_COMP_pop(ssl_comp_methods);
1610Sigor@sysoev.ru         }
1620Sigor@sysoev.ru     }
1630Sigor@sysoev.ru #endif
1640Sigor@sysoev.ru 
1650Sigor@sysoev.ru     index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1660Sigor@sysoev.ru 
1670Sigor@sysoev.ru     if (index == -1) {
168771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
1690Sigor@sysoev.ru                               "SSL_get_ex_new_index() failed");
1700Sigor@sysoev.ru         return NXT_ERROR;
1710Sigor@sysoev.ru     }
1720Sigor@sysoev.ru 
1730Sigor@sysoev.ru     nxt_openssl_connection_index = index;
1740Sigor@sysoev.ru 
1750Sigor@sysoev.ru     return NXT_OK;
1760Sigor@sysoev.ru }
1770Sigor@sysoev.ru 
1780Sigor@sysoev.ru 
179771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
180771Sigor@sysoev.ru 
181771Sigor@sysoev.ru static void
182771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
183771Sigor@sysoev.ru {
184771Sigor@sysoev.ru }
185771Sigor@sysoev.ru 
186771Sigor@sysoev.ru #else
187771Sigor@sysoev.ru 
188771Sigor@sysoev.ru static nxt_thread_mutex_t  *nxt_openssl_locks;
189771Sigor@sysoev.ru 
1900Sigor@sysoev.ru static nxt_int_t
191771Sigor@sysoev.ru nxt_openssl_locks_init(void)
192771Sigor@sysoev.ru {
193771Sigor@sysoev.ru     int        i, n;
194771Sigor@sysoev.ru     nxt_int_t  ret;
195771Sigor@sysoev.ru 
196771Sigor@sysoev.ru     n = CRYPTO_num_locks();
197771Sigor@sysoev.ru 
198771Sigor@sysoev.ru     nxt_openssl_locks = OPENSSL_malloc(n * sizeof(nxt_thread_mutex_t));
199771Sigor@sysoev.ru     if (nxt_slow_path(nxt_openssl_locks == NULL)) {
200771Sigor@sysoev.ru         return NXT_ERROR;
201771Sigor@sysoev.ru     }
202771Sigor@sysoev.ru 
203771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
204771Sigor@sysoev.ru         ret = nxt_thread_mutex_create(&nxt_openssl_locks[i]);
205771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
206771Sigor@sysoev.ru             return ret;
207771Sigor@sysoev.ru         }
208771Sigor@sysoev.ru     }
209771Sigor@sysoev.ru 
210771Sigor@sysoev.ru     CRYPTO_set_locking_callback(nxt_openssl_lock);
211771Sigor@sysoev.ru 
212771Sigor@sysoev.ru     CRYPTO_set_id_callback(nxt_openssl_thread_id);
213771Sigor@sysoev.ru 
214771Sigor@sysoev.ru     return NXT_OK;
215771Sigor@sysoev.ru }
216771Sigor@sysoev.ru 
217771Sigor@sysoev.ru 
218771Sigor@sysoev.ru static void
219771Sigor@sysoev.ru nxt_openssl_lock(int mode, int type, const char *file, int line)
220771Sigor@sysoev.ru {
221771Sigor@sysoev.ru     nxt_thread_mutex_t  *lock;
222771Sigor@sysoev.ru 
223771Sigor@sysoev.ru     lock = &nxt_openssl_locks[type];
224771Sigor@sysoev.ru 
225771Sigor@sysoev.ru     if ((mode & CRYPTO_LOCK) != 0) {
226771Sigor@sysoev.ru         (void) nxt_thread_mutex_lock(lock);
227771Sigor@sysoev.ru 
228771Sigor@sysoev.ru     } else {
229771Sigor@sysoev.ru         (void) nxt_thread_mutex_unlock(lock);
230771Sigor@sysoev.ru     }
231771Sigor@sysoev.ru }
232771Sigor@sysoev.ru 
233771Sigor@sysoev.ru 
234771Sigor@sysoev.ru static u_long
235771Sigor@sysoev.ru nxt_openssl_thread_id(void)
236771Sigor@sysoev.ru {
237771Sigor@sysoev.ru     return (u_long) nxt_thread_handle();
238771Sigor@sysoev.ru }
239771Sigor@sysoev.ru 
240771Sigor@sysoev.ru 
241771Sigor@sysoev.ru static void
242771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
243771Sigor@sysoev.ru {
244771Sigor@sysoev.ru     nxt_openssl_locks_free();
245771Sigor@sysoev.ru }
246771Sigor@sysoev.ru 
247771Sigor@sysoev.ru 
248771Sigor@sysoev.ru static void
249771Sigor@sysoev.ru nxt_openssl_locks_free(void)
250771Sigor@sysoev.ru {
251771Sigor@sysoev.ru     int  i, n;
252771Sigor@sysoev.ru 
253771Sigor@sysoev.ru     n = CRYPTO_num_locks();
254771Sigor@sysoev.ru 
255771Sigor@sysoev.ru     CRYPTO_set_locking_callback(NULL);
256771Sigor@sysoev.ru 
257771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
258771Sigor@sysoev.ru         nxt_thread_mutex_destroy(&nxt_openssl_locks[i]);
259771Sigor@sysoev.ru     }
260771Sigor@sysoev.ru 
261771Sigor@sysoev.ru     OPENSSL_free(nxt_openssl_locks);
262771Sigor@sysoev.ru }
263771Sigor@sysoev.ru 
264771Sigor@sysoev.ru #endif
265771Sigor@sysoev.ru 
266771Sigor@sysoev.ru 
267771Sigor@sysoev.ru static nxt_int_t
2681828Sa.suvorov@f5.com nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
269*1885Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, nxt_bool_t last)
2700Sigor@sysoev.ru {
2711828Sa.suvorov@f5.com     SSL_CTX                *ctx;
2721828Sa.suvorov@f5.com     const char             *ciphers, *ca_certificate;
2731828Sa.suvorov@f5.com     STACK_OF(X509_NAME)    *list;
2741828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
2750Sigor@sysoev.ru 
2760Sigor@sysoev.ru     ctx = SSL_CTX_new(SSLv23_server_method());
2770Sigor@sysoev.ru     if (ctx == NULL) {
278771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_new() failed");
2790Sigor@sysoev.ru         return NXT_ERROR;
2800Sigor@sysoev.ru     }
2810Sigor@sysoev.ru 
2821828Sa.suvorov@f5.com     bundle = conf->bundle;
2831828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
2841828Sa.suvorov@f5.com 
2851828Sa.suvorov@f5.com     bundle->ctx = ctx;
2860Sigor@sysoev.ru 
287771Sigor@sysoev.ru #ifdef SSL_OP_NO_RENEGOTIATION
288771Sigor@sysoev.ru     /* Renegration is not currently supported. */
289771Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
290771Sigor@sysoev.ru #endif
291771Sigor@sysoev.ru 
2920Sigor@sysoev.ru #ifdef SSL_OP_NO_COMPRESSION
2930Sigor@sysoev.ru     /*
2940Sigor@sysoev.ru      * Disable gzip compression in OpenSSL 1.0.0,
2950Sigor@sysoev.ru      * this saves about 522K per connection.
2960Sigor@sysoev.ru      */
2970Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
2980Sigor@sysoev.ru #endif
2990Sigor@sysoev.ru 
3000Sigor@sysoev.ru #ifdef SSL_MODE_RELEASE_BUFFERS
3010Sigor@sysoev.ru 
3020Sigor@sysoev.ru     if (nxt_openssl_version >= 10001078) {
3030Sigor@sysoev.ru         /*
3040Sigor@sysoev.ru          * Allow to release read and write buffers in OpenSSL 1.0.0,
3050Sigor@sysoev.ru          * this saves about 34K per idle connection.  It is not safe
3060Sigor@sysoev.ru          * before OpenSSL 1.0.1h (CVE-2010-5298).
3070Sigor@sysoev.ru          */
3080Sigor@sysoev.ru         SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
3090Sigor@sysoev.ru     }
3100Sigor@sysoev.ru 
3110Sigor@sysoev.ru #endif
3120Sigor@sysoev.ru 
3131828Sa.suvorov@f5.com     if (nxt_openssl_chain_file(task, ctx, conf, mp,
3141828Sa.suvorov@f5.com                                last && bundle->next == NULL)
3151828Sa.suvorov@f5.com         != NXT_OK)
3161828Sa.suvorov@f5.com     {
3170Sigor@sysoev.ru         goto fail;
3180Sigor@sysoev.ru     }
319774Svbart@nginx.com /*
3200Sigor@sysoev.ru     key = conf->certificate_key;
3210Sigor@sysoev.ru 
3220Sigor@sysoev.ru     if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
323771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3240Sigor@sysoev.ru                               "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
3250Sigor@sysoev.ru                               key);
3260Sigor@sysoev.ru         goto fail;
3270Sigor@sysoev.ru     }
328774Svbart@nginx.com */
329*1885Sa.suvorov@f5.com 
3300Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
3310Sigor@sysoev.ru 
3320Sigor@sysoev.ru     if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
333771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3340Sigor@sysoev.ru                               "SSL_CTX_set_cipher_list(\"%s\") failed",
3350Sigor@sysoev.ru                               ciphers);
3360Sigor@sysoev.ru         goto fail;
3370Sigor@sysoev.ru     }
3380Sigor@sysoev.ru 
339*1885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
340*1885Sa.suvorov@f5.com     if (conf_cmds != NULL
341*1885Sa.suvorov@f5.com         && nxt_ssl_conf_commands(task, ctx, conf_cmds, mp) != NXT_OK)
342*1885Sa.suvorov@f5.com     {
343*1885Sa.suvorov@f5.com         goto fail;
344*1885Sa.suvorov@f5.com     }
345*1885Sa.suvorov@f5.com #endif
346*1885Sa.suvorov@f5.com 
3470Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
3480Sigor@sysoev.ru 
3490Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
3500Sigor@sysoev.ru 
3510Sigor@sysoev.ru         /* TODO: verify callback */
3520Sigor@sysoev.ru         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
3530Sigor@sysoev.ru 
3540Sigor@sysoev.ru         /* TODO: verify depth */
3550Sigor@sysoev.ru         SSL_CTX_set_verify_depth(ctx, 1);
3560Sigor@sysoev.ru 
3570Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
3580Sigor@sysoev.ru 
3590Sigor@sysoev.ru         if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
360771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3610Sigor@sysoev.ru                               "SSL_CTX_load_verify_locations(\"%s\") failed",
3620Sigor@sysoev.ru                               ca_certificate);
3630Sigor@sysoev.ru             goto fail;
3640Sigor@sysoev.ru         }
3650Sigor@sysoev.ru 
3660Sigor@sysoev.ru         list = SSL_load_client_CA_file(ca_certificate);
3670Sigor@sysoev.ru 
3680Sigor@sysoev.ru         if (list == NULL) {
369771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3700Sigor@sysoev.ru                               "SSL_load_client_CA_file(\"%s\") failed",
3710Sigor@sysoev.ru                               ca_certificate);
3720Sigor@sysoev.ru             goto fail;
3730Sigor@sysoev.ru         }
3740Sigor@sysoev.ru 
3750Sigor@sysoev.ru         /*
3760Sigor@sysoev.ru          * SSL_load_client_CA_file() in OpenSSL prior to 0.9.7h and
3770Sigor@sysoev.ru          * 0.9.8 versions always leaves an error in the error queue.
3780Sigor@sysoev.ru          */
3790Sigor@sysoev.ru         ERR_clear_error();
3800Sigor@sysoev.ru 
3810Sigor@sysoev.ru         SSL_CTX_set_client_CA_list(ctx, list);
3820Sigor@sysoev.ru     }
3830Sigor@sysoev.ru 
3841828Sa.suvorov@f5.com     if (last) {
3851828Sa.suvorov@f5.com         conf->conn_init = nxt_openssl_conn_init;
3861828Sa.suvorov@f5.com 
3871828Sa.suvorov@f5.com         if (bundle->next != NULL) {
3881828Sa.suvorov@f5.com             SSL_CTX_set_tlsext_servername_callback(ctx, nxt_openssl_servername);
3891828Sa.suvorov@f5.com         }
3901828Sa.suvorov@f5.com     }
3911828Sa.suvorov@f5.com 
3920Sigor@sysoev.ru     return NXT_OK;
3930Sigor@sysoev.ru 
3940Sigor@sysoev.ru fail:
3950Sigor@sysoev.ru 
3960Sigor@sysoev.ru     SSL_CTX_free(ctx);
3970Sigor@sysoev.ru 
3981818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
3991818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
4001818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
4011818Smax.romanov@nginx.com #endif
4021818Smax.romanov@nginx.com 
4030Sigor@sysoev.ru     return NXT_ERROR;
4040Sigor@sysoev.ru }
4050Sigor@sysoev.ru 
4060Sigor@sysoev.ru 
407833Svbart@nginx.com static nxt_int_t
4081828Sa.suvorov@f5.com nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf,
4091828Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_bool_t single)
410774Svbart@nginx.com {
4111828Sa.suvorov@f5.com     BIO                    *bio;
4121828Sa.suvorov@f5.com     X509                   *cert, *ca;
4131828Sa.suvorov@f5.com     long                   reason;
4141828Sa.suvorov@f5.com     EVP_PKEY               *key;
4151828Sa.suvorov@f5.com     nxt_int_t              ret;
4161828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
4171828Sa.suvorov@f5.com 
4181828Sa.suvorov@f5.com     ret = NXT_ERROR;
4191828Sa.suvorov@f5.com     cert = NULL;
420774Svbart@nginx.com 
421774Svbart@nginx.com     bio = BIO_new(BIO_s_fd());
422774Svbart@nginx.com     if (bio == NULL) {
4231828Sa.suvorov@f5.com         goto end;
424774Svbart@nginx.com     }
425774Svbart@nginx.com 
4261828Sa.suvorov@f5.com     bundle = conf->bundle;
427774Svbart@nginx.com 
4281828Sa.suvorov@f5.com     BIO_set_fd(bio, bundle->chain_file, BIO_CLOSE);
429774Svbart@nginx.com 
430774Svbart@nginx.com     cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
431774Svbart@nginx.com     if (cert == NULL) {
432774Svbart@nginx.com         goto end;
433774Svbart@nginx.com     }
434774Svbart@nginx.com 
435774Svbart@nginx.com     if (SSL_CTX_use_certificate(ctx, cert) != 1) {
436774Svbart@nginx.com         goto end;
437774Svbart@nginx.com     }
438774Svbart@nginx.com 
4391828Sa.suvorov@f5.com     if (!single && nxt_openssl_cert_get_names(task, cert, conf, mp) != NXT_OK) {
4401828Sa.suvorov@f5.com         goto clean;
4411828Sa.suvorov@f5.com     }
4421828Sa.suvorov@f5.com 
443774Svbart@nginx.com     for ( ;; ) {
444774Svbart@nginx.com         ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
445774Svbart@nginx.com 
446774Svbart@nginx.com         if (ca == NULL) {
447774Svbart@nginx.com             reason = ERR_GET_REASON(ERR_peek_last_error());
448774Svbart@nginx.com             if (reason != PEM_R_NO_START_LINE) {
449774Svbart@nginx.com                 goto end;
450774Svbart@nginx.com             }
451774Svbart@nginx.com 
452774Svbart@nginx.com             ERR_clear_error();
453774Svbart@nginx.com             break;
454774Svbart@nginx.com         }
455774Svbart@nginx.com 
456774Svbart@nginx.com         /*
457774Svbart@nginx.com          * Note that ca isn't freed if it was successfully added to the chain,
458774Svbart@nginx.com          * while the main certificate needs a X509_free() call, since
459774Svbart@nginx.com          * its reference count is increased by SSL_CTX_use_certificate().
460774Svbart@nginx.com          */
461808Spluknet@nginx.com #ifdef SSL_CTX_add0_chain_cert
462774Svbart@nginx.com         if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) {
463774Svbart@nginx.com #else
464774Svbart@nginx.com         if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) {
465774Svbart@nginx.com #endif
466774Svbart@nginx.com             X509_free(ca);
467774Svbart@nginx.com             goto end;
468774Svbart@nginx.com         }
469774Svbart@nginx.com     }
470774Svbart@nginx.com 
471774Svbart@nginx.com     if (BIO_reset(bio) != 0) {
472774Svbart@nginx.com         goto end;
473774Svbart@nginx.com     }
474774Svbart@nginx.com 
475774Svbart@nginx.com     key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
476774Svbart@nginx.com     if (key == NULL) {
477774Svbart@nginx.com         goto end;
478774Svbart@nginx.com     }
479774Svbart@nginx.com 
480774Svbart@nginx.com     if (SSL_CTX_use_PrivateKey(ctx, key) == 1) {
481774Svbart@nginx.com         ret = NXT_OK;
482774Svbart@nginx.com     }
483774Svbart@nginx.com 
484774Svbart@nginx.com     EVP_PKEY_free(key);
485774Svbart@nginx.com 
486774Svbart@nginx.com end:
487774Svbart@nginx.com 
4881828Sa.suvorov@f5.com     if (ret != NXT_OK) {
4891828Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
4901828Sa.suvorov@f5.com                               "nxt_openssl_chain_file() failed");
4911828Sa.suvorov@f5.com     }
4921828Sa.suvorov@f5.com 
4931828Sa.suvorov@f5.com clean:
4941828Sa.suvorov@f5.com 
4951828Sa.suvorov@f5.com     BIO_free(bio);
496774Svbart@nginx.com     X509_free(cert);
497774Svbart@nginx.com 
498774Svbart@nginx.com     return ret;
499774Svbart@nginx.com }
500774Svbart@nginx.com 
501774Svbart@nginx.com 
502*1885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
503*1885Sa.suvorov@f5.com 
504*1885Sa.suvorov@f5.com static nxt_int_t
505*1885Sa.suvorov@f5.com nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, nxt_conf_value_t *conf,
506*1885Sa.suvorov@f5.com     nxt_mp_t *mp)
507*1885Sa.suvorov@f5.com {
508*1885Sa.suvorov@f5.com     int               ret;
509*1885Sa.suvorov@f5.com     char              *zcmd, *zvalue;
510*1885Sa.suvorov@f5.com     uint32_t          index;
511*1885Sa.suvorov@f5.com     nxt_str_t         cmd, value;
512*1885Sa.suvorov@f5.com     SSL_CONF_CTX      *cctx;
513*1885Sa.suvorov@f5.com     nxt_conf_value_t  *member;
514*1885Sa.suvorov@f5.com 
515*1885Sa.suvorov@f5.com     cctx = SSL_CONF_CTX_new();
516*1885Sa.suvorov@f5.com     if (nxt_slow_path(cctx == NULL)) {
517*1885Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
518*1885Sa.suvorov@f5.com                               "SSL_CONF_CTX_new() failed");
519*1885Sa.suvorov@f5.com         return NXT_ERROR;
520*1885Sa.suvorov@f5.com     }
521*1885Sa.suvorov@f5.com 
522*1885Sa.suvorov@f5.com     SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE
523*1885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_SERVER
524*1885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_CERTIFICATE
525*1885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_SHOW_ERRORS);
526*1885Sa.suvorov@f5.com 
527*1885Sa.suvorov@f5.com     SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
528*1885Sa.suvorov@f5.com 
529*1885Sa.suvorov@f5.com     index = 0;
530*1885Sa.suvorov@f5.com 
531*1885Sa.suvorov@f5.com     for ( ;; ) {
532*1885Sa.suvorov@f5.com         member = nxt_conf_next_object_member(conf, &cmd, &index);
533*1885Sa.suvorov@f5.com         if (nxt_slow_path(member == NULL)) {
534*1885Sa.suvorov@f5.com             break;
535*1885Sa.suvorov@f5.com         }
536*1885Sa.suvorov@f5.com 
537*1885Sa.suvorov@f5.com         nxt_conf_get_string(member, &value);
538*1885Sa.suvorov@f5.com 
539*1885Sa.suvorov@f5.com         zcmd = nxt_str_cstrz(mp, &cmd);
540*1885Sa.suvorov@f5.com         zvalue = nxt_str_cstrz(mp, &value);
541*1885Sa.suvorov@f5.com 
542*1885Sa.suvorov@f5.com         if (nxt_slow_path(zcmd == NULL || zvalue == NULL)) {
543*1885Sa.suvorov@f5.com             goto fail;
544*1885Sa.suvorov@f5.com         }
545*1885Sa.suvorov@f5.com 
546*1885Sa.suvorov@f5.com         ret = SSL_CONF_cmd(cctx, zcmd, zvalue);
547*1885Sa.suvorov@f5.com         if (ret == -2) {
548*1885Sa.suvorov@f5.com             nxt_openssl_log_error(task, NXT_LOG_ERR,
549*1885Sa.suvorov@f5.com                                   "unknown command \"%s\" in "
550*1885Sa.suvorov@f5.com                                   "\"conf_commands\" option", zcmd);
551*1885Sa.suvorov@f5.com             goto fail;
552*1885Sa.suvorov@f5.com         }
553*1885Sa.suvorov@f5.com 
554*1885Sa.suvorov@f5.com         if (ret <= 0) {
555*1885Sa.suvorov@f5.com             nxt_openssl_log_error(task, NXT_LOG_ERR,
556*1885Sa.suvorov@f5.com                                   "invalid value \"%s\" for command \"%s\" "
557*1885Sa.suvorov@f5.com                                   "in \"conf_commands\" option",
558*1885Sa.suvorov@f5.com                                   zvalue, zcmd);
559*1885Sa.suvorov@f5.com             goto fail;
560*1885Sa.suvorov@f5.com         }
561*1885Sa.suvorov@f5.com 
562*1885Sa.suvorov@f5.com         nxt_debug(task, "SSL_CONF_cmd(\"%s\", \"%s\")", zcmd, zvalue);
563*1885Sa.suvorov@f5.com     }
564*1885Sa.suvorov@f5.com 
565*1885Sa.suvorov@f5.com     if (SSL_CONF_CTX_finish(cctx) != 1) {
566*1885Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
567*1885Sa.suvorov@f5.com                               "SSL_CONF_finish() failed");
568*1885Sa.suvorov@f5.com         goto fail;
569*1885Sa.suvorov@f5.com     }
570*1885Sa.suvorov@f5.com 
571*1885Sa.suvorov@f5.com     SSL_CONF_CTX_free(cctx);
572*1885Sa.suvorov@f5.com 
573*1885Sa.suvorov@f5.com     return NXT_OK;
574*1885Sa.suvorov@f5.com 
575*1885Sa.suvorov@f5.com fail:
576*1885Sa.suvorov@f5.com 
577*1885Sa.suvorov@f5.com     SSL_CONF_CTX_free(cctx);
578*1885Sa.suvorov@f5.com 
579*1885Sa.suvorov@f5.com     return NXT_ERROR;
580*1885Sa.suvorov@f5.com }
581*1885Sa.suvorov@f5.com 
582*1885Sa.suvorov@f5.com #endif
583*1885Sa.suvorov@f5.com 
584*1885Sa.suvorov@f5.com 
5851828Sa.suvorov@f5.com static nxt_uint_t
5861828Sa.suvorov@f5.com nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
5871828Sa.suvorov@f5.com     nxt_mp_t *mp)
5881828Sa.suvorov@f5.com {
5891828Sa.suvorov@f5.com     int                         len;
5901828Sa.suvorov@f5.com     nxt_str_t                   domain, str;
5911828Sa.suvorov@f5.com     X509_NAME                   *x509_name;
5921828Sa.suvorov@f5.com     nxt_uint_t                  i, n;
5931828Sa.suvorov@f5.com     GENERAL_NAME                *name;
5941828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t       *bundle;
5951828Sa.suvorov@f5.com     STACK_OF(GENERAL_NAME)      *alt_names;
5961828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
5971828Sa.suvorov@f5.com 
5981828Sa.suvorov@f5.com     bundle = conf->bundle;
5991828Sa.suvorov@f5.com 
6001828Sa.suvorov@f5.com     alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
6011828Sa.suvorov@f5.com 
6021828Sa.suvorov@f5.com     if (alt_names != NULL) {
6031828Sa.suvorov@f5.com         n = sk_GENERAL_NAME_num(alt_names);
6041828Sa.suvorov@f5.com 
6051828Sa.suvorov@f5.com         for (i = 0; i != n; i++) {
6061828Sa.suvorov@f5.com             name = sk_GENERAL_NAME_value(alt_names, i);
6071828Sa.suvorov@f5.com 
6081828Sa.suvorov@f5.com             if (name->type != GEN_DNS) {
6091828Sa.suvorov@f5.com                 continue;
6101828Sa.suvorov@f5.com             }
6111828Sa.suvorov@f5.com 
6121828Sa.suvorov@f5.com             str.length = ASN1_STRING_length(name->d.dNSName);
6131828Sa.suvorov@f5.com #if OPENSSL_VERSION_NUMBER > 0x10100000L
6141828Sa.suvorov@f5.com             str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
6151828Sa.suvorov@f5.com #else
6161828Sa.suvorov@f5.com             str.start = ASN1_STRING_data(name->d.dNSName);
6171828Sa.suvorov@f5.com #endif
6181828Sa.suvorov@f5.com 
6191828Sa.suvorov@f5.com             domain.start = nxt_mp_nget(mp, str.length);
6201828Sa.suvorov@f5.com             if (nxt_slow_path(domain.start == NULL)) {
6211828Sa.suvorov@f5.com                 goto fail;
6221828Sa.suvorov@f5.com             }
6231828Sa.suvorov@f5.com 
6241828Sa.suvorov@f5.com             domain.length = str.length;
6251828Sa.suvorov@f5.com             nxt_memcpy_lowcase(domain.start, str.start, str.length);
6261828Sa.suvorov@f5.com 
6271828Sa.suvorov@f5.com             item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
6281828Sa.suvorov@f5.com             if (nxt_slow_path(item == NULL)) {
6291828Sa.suvorov@f5.com                 goto fail;
6301828Sa.suvorov@f5.com             }
6311828Sa.suvorov@f5.com 
6321828Sa.suvorov@f5.com             item->name = domain;
6331828Sa.suvorov@f5.com             item->bundle = bundle;
6341828Sa.suvorov@f5.com 
6351828Sa.suvorov@f5.com             if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash,
6361828Sa.suvorov@f5.com                                                item, mp)
6371828Sa.suvorov@f5.com                 == NXT_ERROR)
6381828Sa.suvorov@f5.com             {
6391828Sa.suvorov@f5.com                 goto fail;
6401828Sa.suvorov@f5.com             }
6411828Sa.suvorov@f5.com         }
6421828Sa.suvorov@f5.com 
6431828Sa.suvorov@f5.com         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
6441828Sa.suvorov@f5.com 
6451828Sa.suvorov@f5.com     } else {
6461828Sa.suvorov@f5.com         x509_name = X509_get_subject_name(cert);
6471828Sa.suvorov@f5.com         len = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
6481828Sa.suvorov@f5.com                                         NULL, 0);
6491828Sa.suvorov@f5.com         if (len <= 0) {
6501828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither "
651*1885Sa.suvorov@f5.com                     "Subject Alternative Name nor Common Name", &bundle->name);
6521828Sa.suvorov@f5.com             return NXT_OK;
6531828Sa.suvorov@f5.com         }
6541828Sa.suvorov@f5.com 
6551828Sa.suvorov@f5.com         domain.start = nxt_mp_nget(mp, len + 1);
6561828Sa.suvorov@f5.com         if (nxt_slow_path(domain.start == NULL)) {
6571828Sa.suvorov@f5.com             return NXT_ERROR;
6581828Sa.suvorov@f5.com         }
6591828Sa.suvorov@f5.com 
6601828Sa.suvorov@f5.com         domain.length = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
6611828Sa.suvorov@f5.com                                                   (char *) domain.start,
6621828Sa.suvorov@f5.com                                                   len + 1);
6631828Sa.suvorov@f5.com         nxt_memcpy_lowcase(domain.start, domain.start, domain.length);
6641828Sa.suvorov@f5.com 
6651828Sa.suvorov@f5.com         item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
6661828Sa.suvorov@f5.com         if (nxt_slow_path(item == NULL)) {
6671828Sa.suvorov@f5.com             return NXT_ERROR;
6681828Sa.suvorov@f5.com         }
6691828Sa.suvorov@f5.com 
6701828Sa.suvorov@f5.com         item->name = domain;
6711828Sa.suvorov@f5.com         item->bundle = bundle;
6721828Sa.suvorov@f5.com 
6731828Sa.suvorov@f5.com         if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash, item,
6741828Sa.suvorov@f5.com                                            mp)
6751828Sa.suvorov@f5.com             == NXT_ERROR)
6761828Sa.suvorov@f5.com         {
6771828Sa.suvorov@f5.com             return NXT_ERROR;
6781828Sa.suvorov@f5.com         }
6791828Sa.suvorov@f5.com     }
6801828Sa.suvorov@f5.com 
6811828Sa.suvorov@f5.com     return NXT_OK;
6821828Sa.suvorov@f5.com 
6831828Sa.suvorov@f5.com fail:
6841828Sa.suvorov@f5.com 
6851828Sa.suvorov@f5.com     sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
6861828Sa.suvorov@f5.com 
6871828Sa.suvorov@f5.com     return NXT_ERROR;
6881828Sa.suvorov@f5.com }
6891828Sa.suvorov@f5.com 
6901828Sa.suvorov@f5.com 
6911828Sa.suvorov@f5.com static const nxt_lvlhsh_proto_t  nxt_openssl_bundle_hash_proto
6921828Sa.suvorov@f5.com     nxt_aligned(64) =
6931828Sa.suvorov@f5.com {
6941828Sa.suvorov@f5.com     NXT_LVLHSH_DEFAULT,
6951828Sa.suvorov@f5.com     nxt_openssl_bundle_hash_test,
6961828Sa.suvorov@f5.com     nxt_mp_lvlhsh_alloc,
6971828Sa.suvorov@f5.com     nxt_mp_lvlhsh_free,
6981828Sa.suvorov@f5.com };
6991828Sa.suvorov@f5.com 
7001828Sa.suvorov@f5.com 
7011828Sa.suvorov@f5.com static nxt_int_t
7021828Sa.suvorov@f5.com nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
7031828Sa.suvorov@f5.com {
7041828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
7051828Sa.suvorov@f5.com 
7061828Sa.suvorov@f5.com     item = data;
7071828Sa.suvorov@f5.com 
7081828Sa.suvorov@f5.com     return nxt_strstr_eq(&lhq->key, &item->name) ? NXT_OK : NXT_DECLINED;
7091828Sa.suvorov@f5.com }
7101828Sa.suvorov@f5.com 
7111828Sa.suvorov@f5.com 
7121828Sa.suvorov@f5.com static nxt_int_t
7131828Sa.suvorov@f5.com nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
7141828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t *item, nxt_mp_t *mp)
7151828Sa.suvorov@f5.com {
7161828Sa.suvorov@f5.com     nxt_str_t                   str;
7171828Sa.suvorov@f5.com     nxt_int_t                   ret;
7181828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
7191828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *old;
7201828Sa.suvorov@f5.com 
7211828Sa.suvorov@f5.com     str = item->name;
7221828Sa.suvorov@f5.com 
7231828Sa.suvorov@f5.com     if (item->name.start[0] == '*') {
7241828Sa.suvorov@f5.com         item->name.start++;
7251828Sa.suvorov@f5.com         item->name.length--;
7261828Sa.suvorov@f5.com 
7271828Sa.suvorov@f5.com         if (item->name.length == 0 || item->name.start[0] != '.') {
7281828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" "
7291828Sa.suvorov@f5.com                     "in certificate \"%V\": missing \".\" "
730*1885Sa.suvorov@f5.com                     "after wildcard symbol", &str, &item->bundle->name);
7311828Sa.suvorov@f5.com             return NXT_OK;
7321828Sa.suvorov@f5.com         }
7331828Sa.suvorov@f5.com     }
7341828Sa.suvorov@f5.com 
7351828Sa.suvorov@f5.com     lhq.pool = mp;
7361828Sa.suvorov@f5.com     lhq.key = item->name;
7371828Sa.suvorov@f5.com     lhq.value = item;
7381828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
7391828Sa.suvorov@f5.com     lhq.replace = 0;
7401828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(item->name.start, item->name.length);
7411828Sa.suvorov@f5.com 
7421828Sa.suvorov@f5.com     ret = nxt_lvlhsh_insert(lvlhsh, &lhq);
7431828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_OK)) {
7441828Sa.suvorov@f5.com         nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted",
745*1885Sa.suvorov@f5.com                   &str, &item->bundle->name);
7461828Sa.suvorov@f5.com         return NXT_OK;
7471828Sa.suvorov@f5.com     }
7481828Sa.suvorov@f5.com 
7491828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_DECLINED)) {
7501828Sa.suvorov@f5.com         old = lhq.value;
7511828Sa.suvorov@f5.com         if (old->bundle != item->bundle) {
7521828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" "
7531828Sa.suvorov@f5.com                     "in certificate \"%V\", identical name appears in \"%V\"",
754*1885Sa.suvorov@f5.com                     &str, &old->bundle->name, &item->bundle->name);
7551828Sa.suvorov@f5.com 
7561828Sa.suvorov@f5.com             old->bundle = item->bundle;
7571828Sa.suvorov@f5.com         }
7581828Sa.suvorov@f5.com 
7591828Sa.suvorov@f5.com         return NXT_OK;
7601828Sa.suvorov@f5.com     }
7611828Sa.suvorov@f5.com 
7621828Sa.suvorov@f5.com     return NXT_ERROR;
7631828Sa.suvorov@f5.com }
7641828Sa.suvorov@f5.com 
7651828Sa.suvorov@f5.com 
7661828Sa.suvorov@f5.com static nxt_int_t
7671828Sa.suvorov@f5.com nxt_openssl_servername(SSL *s, int *ad, void *arg)
7681828Sa.suvorov@f5.com {
7691828Sa.suvorov@f5.com     nxt_str_t              str;
7701828Sa.suvorov@f5.com     nxt_uint_t             i;
7711828Sa.suvorov@f5.com     nxt_conn_t             *c;
7721828Sa.suvorov@f5.com     const char             *servername;
7731828Sa.suvorov@f5.com     nxt_tls_conf_t         *conf;
7741828Sa.suvorov@f5.com     nxt_openssl_conn_t     *tls;
7751828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
7761828Sa.suvorov@f5.com 
7771828Sa.suvorov@f5.com     c = SSL_get_ex_data(s, nxt_openssl_connection_index);
7781828Sa.suvorov@f5.com 
7791828Sa.suvorov@f5.com     if (nxt_slow_path(c == NULL)) {
7801828Sa.suvorov@f5.com         nxt_thread_log_alert("SSL_get_ex_data() failed");
7811828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
7821828Sa.suvorov@f5.com     }
7831828Sa.suvorov@f5.com 
7841828Sa.suvorov@f5.com     servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
7851828Sa.suvorov@f5.com     if (nxt_slow_path(servername == NULL)) {
7861828Sa.suvorov@f5.com         nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned "
7871828Sa.suvorov@f5.com                                                "NULL in server name callback");
7881828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
7891828Sa.suvorov@f5.com     }
7901828Sa.suvorov@f5.com 
7911828Sa.suvorov@f5.com     str.length = nxt_strlen(servername);
7921828Sa.suvorov@f5.com     if (str.length == 0) {
7931828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "client sent zero-length server name");
7941828Sa.suvorov@f5.com         goto done;
7951828Sa.suvorov@f5.com     }
7961828Sa.suvorov@f5.com 
7971828Sa.suvorov@f5.com     if (servername[0] == '.') {
7981828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "ignored the server name \"%s\": "
7991828Sa.suvorov@f5.com                                   "leading \".\"", servername);
8001828Sa.suvorov@f5.com         goto done;
8011828Sa.suvorov@f5.com     }
8021828Sa.suvorov@f5.com 
8031828Sa.suvorov@f5.com     nxt_debug(c->socket.task, "tls with servername \"%s\"", servername);
8041828Sa.suvorov@f5.com 
8051828Sa.suvorov@f5.com     str.start = nxt_mp_nget(c->mem_pool, str.length);
8061828Sa.suvorov@f5.com     if (nxt_slow_path(str.start == NULL)) {
8071828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
8081828Sa.suvorov@f5.com     }
8091828Sa.suvorov@f5.com 
8101828Sa.suvorov@f5.com     nxt_memcpy_lowcase(str.start, (const u_char *) servername, str.length);
8111828Sa.suvorov@f5.com 
8121828Sa.suvorov@f5.com     tls = c->u.tls;
8131828Sa.suvorov@f5.com     conf = tls->conf;
8141828Sa.suvorov@f5.com 
8151828Sa.suvorov@f5.com     bundle = nxt_openssl_find_ctx(conf, &str);
8161828Sa.suvorov@f5.com 
8171828Sa.suvorov@f5.com     if (bundle == NULL) {
8181828Sa.suvorov@f5.com         for (i = 1; i < str.length; i++) {
8191828Sa.suvorov@f5.com             if (str.start[i] == '.') {
8201828Sa.suvorov@f5.com                 str.start += i;
8211828Sa.suvorov@f5.com                 str.length -= i;
8221828Sa.suvorov@f5.com 
8231828Sa.suvorov@f5.com                 bundle = nxt_openssl_find_ctx(conf, &str);
8241828Sa.suvorov@f5.com                 break;
8251828Sa.suvorov@f5.com             }
8261828Sa.suvorov@f5.com         }
8271828Sa.suvorov@f5.com     }
8281828Sa.suvorov@f5.com 
8291828Sa.suvorov@f5.com     if (bundle != NULL) {
8301828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" "
831*1885Sa.suvorov@f5.com                                   "(old: \"%V\")", &str, &bundle->name,
832*1885Sa.suvorov@f5.com                                   &conf->bundle->name);
8331828Sa.suvorov@f5.com 
8341828Sa.suvorov@f5.com         if (bundle != conf->bundle) {
8351828Sa.suvorov@f5.com             if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) {
8361828Sa.suvorov@f5.com                 nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
8371828Sa.suvorov@f5.com                                       "SSL_set_SSL_CTX() failed");
8381828Sa.suvorov@f5.com 
8391828Sa.suvorov@f5.com                 return SSL_TLSEXT_ERR_ALERT_FATAL;
8401828Sa.suvorov@f5.com             }
8411828Sa.suvorov@f5.com         }
8421828Sa.suvorov@f5.com     }
8431828Sa.suvorov@f5.com 
8441828Sa.suvorov@f5.com done:
8451828Sa.suvorov@f5.com 
8461828Sa.suvorov@f5.com     return SSL_TLSEXT_ERR_OK;
8471828Sa.suvorov@f5.com }
8481828Sa.suvorov@f5.com 
8491828Sa.suvorov@f5.com 
8501828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *
8511828Sa.suvorov@f5.com nxt_openssl_find_ctx(nxt_tls_conf_t *conf, nxt_str_t *sn)
8521828Sa.suvorov@f5.com {
8531828Sa.suvorov@f5.com     nxt_int_t                   ret;
8541828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
8551828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
8561828Sa.suvorov@f5.com 
8571828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(sn->start, sn->length);
8581828Sa.suvorov@f5.com     lhq.key = *sn;
8591828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
8601828Sa.suvorov@f5.com 
8611828Sa.suvorov@f5.com     ret = nxt_lvlhsh_find(&conf->bundle_hash, &lhq);
8621828Sa.suvorov@f5.com     if (ret != NXT_OK) {
8631828Sa.suvorov@f5.com         return NULL;
8641828Sa.suvorov@f5.com     }
8651828Sa.suvorov@f5.com 
8661828Sa.suvorov@f5.com     item = lhq.value;
8671828Sa.suvorov@f5.com 
8681828Sa.suvorov@f5.com     return item->bundle;
8691828Sa.suvorov@f5.com }
8701828Sa.suvorov@f5.com 
8711828Sa.suvorov@f5.com 
8720Sigor@sysoev.ru static void
873771Sigor@sysoev.ru nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
8740Sigor@sysoev.ru {
8751828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
8761828Sa.suvorov@f5.com 
8771828Sa.suvorov@f5.com     bundle = conf->bundle;
8781828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
8791828Sa.suvorov@f5.com 
8801828Sa.suvorov@f5.com     do {
8811828Sa.suvorov@f5.com         SSL_CTX_free(bundle->ctx);
8821828Sa.suvorov@f5.com         bundle = bundle->next;
8831828Sa.suvorov@f5.com     } while (bundle != NULL);
8841818Smax.romanov@nginx.com 
8851818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
8861818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
8871818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
8881818Smax.romanov@nginx.com #endif
889771Sigor@sysoev.ru }
890771Sigor@sysoev.ru 
891771Sigor@sysoev.ru 
892771Sigor@sysoev.ru static void
893771Sigor@sysoev.ru nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
894771Sigor@sysoev.ru {
895771Sigor@sysoev.ru     int                 ret;
896771Sigor@sysoev.ru     SSL                 *s;
897771Sigor@sysoev.ru     SSL_CTX             *ctx;
898771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
8990Sigor@sysoev.ru 
9000Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "openssl conn init");
9010Sigor@sysoev.ru 
902771Sigor@sysoev.ru     tls = nxt_mp_zget(c->mem_pool, sizeof(nxt_openssl_conn_t));
903771Sigor@sysoev.ru     if (tls == NULL) {
9040Sigor@sysoev.ru         goto fail;
9050Sigor@sysoev.ru     }
906