xref: /unit/src/nxt_openssl.c (revision 1952)
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>
141942Sa.suvorov@f5.com #include <openssl/bio.h>
151942Sa.suvorov@f5.com #include <openssl/evp.h>
160Sigor@sysoev.ru 
170Sigor@sysoev.ru 
180Sigor@sysoev.ru typedef struct {
19*1952Svbart@nginx.com     SSL               *session;
20*1952Svbart@nginx.com     nxt_conn_t        *conn;
21*1952Svbart@nginx.com 
22*1952Svbart@nginx.com     int               ssl_error;
23*1952Svbart@nginx.com     uint8_t           times;      /* 2 bits */
24*1952Svbart@nginx.com     uint8_t           handshake;  /* 1 bit  */
25*1952Svbart@nginx.com 
26*1952Svbart@nginx.com     nxt_tls_conf_t    *conf;
27*1952Svbart@nginx.com     nxt_buf_mem_t     buffer;
28*1952Svbart@nginx.com } nxt_openssl_conn_t;
29*1952Svbart@nginx.com 
300Sigor@sysoev.ru 
31*1952Svbart@nginx.com struct nxt_tls_ticket_s {
32*1952Svbart@nginx.com     u_char            name[16];
33*1952Svbart@nginx.com     u_char            hmac_key[32];
34*1952Svbart@nginx.com     u_char            aes_key[32];
35*1952Svbart@nginx.com     uint8_t           size;
36*1952Svbart@nginx.com };
370Sigor@sysoev.ru 
38*1952Svbart@nginx.com 
39*1952Svbart@nginx.com struct nxt_tls_tickets_s {
40*1952Svbart@nginx.com     nxt_uint_t        count;
41*1952Svbart@nginx.com     nxt_tls_ticket_t  tickets[];
42*1952Svbart@nginx.com };
430Sigor@sysoev.ru 
440Sigor@sysoev.ru 
45771Sigor@sysoev.ru typedef enum {
46771Sigor@sysoev.ru     NXT_OPENSSL_HANDSHAKE = 0,
47771Sigor@sysoev.ru     NXT_OPENSSL_READ,
48771Sigor@sysoev.ru     NXT_OPENSSL_WRITE,
49771Sigor@sysoev.ru     NXT_OPENSSL_SHUTDOWN,
50771Sigor@sysoev.ru } nxt_openssl_io_t;
51771Sigor@sysoev.ru 
520Sigor@sysoev.ru 
53771Sigor@sysoev.ru static nxt_int_t nxt_openssl_library_init(nxt_task_t *task);
54771Sigor@sysoev.ru static void nxt_openssl_library_free(nxt_task_t *task);
55771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER < 0x10100004L
56771Sigor@sysoev.ru static nxt_int_t nxt_openssl_locks_init(void);
57771Sigor@sysoev.ru static void nxt_openssl_lock(int mode, int type, const char *file, int line);
58771Sigor@sysoev.ru static unsigned long nxt_openssl_thread_id(void);
59771Sigor@sysoev.ru static void nxt_openssl_locks_free(void);
60771Sigor@sysoev.ru #endif
611920Sa.suvorov@f5.com static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
621920Sa.suvorov@f5.com     nxt_tls_init_t *tls_init, nxt_bool_t last);
631828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
641828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
651885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
661885Sa.suvorov@f5.com static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx,
671885Sa.suvorov@f5.com     nxt_conf_value_t *value, nxt_mp_t *mp);
681885Sa.suvorov@f5.com #endif
691942Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_TLSEXT)
701942Sa.suvorov@f5.com static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx,
711942Sa.suvorov@f5.com     nxt_tls_init_t *tls_init, nxt_mp_t *mp);
721942Sa.suvorov@f5.com static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name,
731942Sa.suvorov@f5.com     unsigned char *iv, EVP_CIPHER_CTX *ectx,HMAC_CTX *hctx, int enc);
741942Sa.suvorov@f5.com #endif
751920Sa.suvorov@f5.com static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size,
761920Sa.suvorov@f5.com     time_t timeout);
771828Sa.suvorov@f5.com static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
781828Sa.suvorov@f5.com     nxt_tls_conf_t *conf, nxt_mp_t *mp);
791828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
801828Sa.suvorov@f5.com     void *data);
811828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_bundle_hash_insert(nxt_task_t *task,
821828Sa.suvorov@f5.com     nxt_lvlhsh_t *lvlhsh, nxt_tls_bundle_hash_item_t *item, nxt_mp_t * mp);
831828Sa.suvorov@f5.com static nxt_int_t nxt_openssl_servername(SSL *s, int *ad, void *arg);
841828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *nxt_openssl_find_ctx(nxt_tls_conf_t *conf,
851828Sa.suvorov@f5.com     nxt_str_t *sn);
86771Sigor@sysoev.ru static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf);
87771Sigor@sysoev.ru static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
8862Sigor@sysoev.ru     nxt_conn_t *c);
891Sigor@sysoev.ru static void nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data);
90771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b);
91771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb);
92771Sigor@sysoev.ru static ssize_t nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb,
93771Sigor@sysoev.ru     void *buf, size_t size);
941Sigor@sysoev.ru static void nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj,
950Sigor@sysoev.ru     void *data);
96771Sigor@sysoev.ru static nxt_int_t nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c,
97771Sigor@sysoev.ru     int ret, nxt_err_t sys_err, nxt_openssl_io_t io);
981884Sa.suvorov@f5.com static void nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj,
991884Sa.suvorov@f5.com     void *data);
100771Sigor@sysoev.ru static void nxt_cdecl nxt_openssl_conn_error(nxt_task_t *task,
101771Sigor@sysoev.ru     nxt_err_t err, const char *fmt, ...);
102771Sigor@sysoev.ru static nxt_uint_t nxt_openssl_log_error_level(nxt_err_t err);
1030Sigor@sysoev.ru 
1040Sigor@sysoev.ru 
105771Sigor@sysoev.ru const nxt_tls_lib_t  nxt_openssl_lib = {
106771Sigor@sysoev.ru     .library_init = nxt_openssl_library_init,
107771Sigor@sysoev.ru     .library_free = nxt_openssl_library_free,
108771Sigor@sysoev.ru 
109771Sigor@sysoev.ru     .server_init = nxt_openssl_server_init,
110771Sigor@sysoev.ru     .server_free = nxt_openssl_server_free,
1110Sigor@sysoev.ru };
1120Sigor@sysoev.ru 
1130Sigor@sysoev.ru 
11462Sigor@sysoev.ru static nxt_conn_io_t  nxt_openssl_conn_io = {
115771Sigor@sysoev.ru     .read = nxt_conn_io_read,
116771Sigor@sysoev.ru     .recvbuf = nxt_openssl_conn_io_recvbuf,
1170Sigor@sysoev.ru 
118771Sigor@sysoev.ru     .write = nxt_conn_io_write,
119771Sigor@sysoev.ru     .sendbuf = nxt_openssl_conn_io_sendbuf,
1200Sigor@sysoev.ru 
121771Sigor@sysoev.ru     .shutdown = nxt_openssl_conn_io_shutdown,
1220Sigor@sysoev.ru };
1230Sigor@sysoev.ru 
1240Sigor@sysoev.ru 
1250Sigor@sysoev.ru static long  nxt_openssl_version;
1260Sigor@sysoev.ru static int   nxt_openssl_connection_index;
1270Sigor@sysoev.ru 
1280Sigor@sysoev.ru 
1290Sigor@sysoev.ru static nxt_int_t
130771Sigor@sysoev.ru nxt_openssl_library_init(nxt_task_t *task)
1310Sigor@sysoev.ru {
1320Sigor@sysoev.ru     int  index;
1330Sigor@sysoev.ru 
1340Sigor@sysoev.ru     if (nxt_fast_path(nxt_openssl_version != 0)) {
1350Sigor@sysoev.ru         return NXT_OK;
1360Sigor@sysoev.ru     }
1370Sigor@sysoev.ru 
138771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
139771Sigor@sysoev.ru 
140771Sigor@sysoev.ru     OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL);
1410Sigor@sysoev.ru 
142771Sigor@sysoev.ru #else
143771Sigor@sysoev.ru     {
144771Sigor@sysoev.ru         nxt_int_t  ret;
145771Sigor@sysoev.ru 
146771Sigor@sysoev.ru         SSL_load_error_strings();
147771Sigor@sysoev.ru 
148771Sigor@sysoev.ru         OPENSSL_config(NULL);
1490Sigor@sysoev.ru 
150771Sigor@sysoev.ru         /*
151771Sigor@sysoev.ru          * SSL_library_init(3):
152771Sigor@sysoev.ru          *
153771Sigor@sysoev.ru          *   SSL_library_init() always returns "1",
154771Sigor@sysoev.ru          *   so it is safe to discard the return value.
155771Sigor@sysoev.ru          */
156771Sigor@sysoev.ru         (void) SSL_library_init();
157771Sigor@sysoev.ru 
158771Sigor@sysoev.ru         ret = nxt_openssl_locks_init();
159771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
160771Sigor@sysoev.ru             return ret;
161771Sigor@sysoev.ru         }
162771Sigor@sysoev.ru     }
163771Sigor@sysoev.ru 
164771Sigor@sysoev.ru #endif
1650Sigor@sysoev.ru 
1660Sigor@sysoev.ru     nxt_openssl_version = SSLeay();
1670Sigor@sysoev.ru 
168771Sigor@sysoev.ru     nxt_log(task, NXT_LOG_INFO, "%s, %xl",
169771Sigor@sysoev.ru             SSLeay_version(SSLEAY_VERSION), nxt_openssl_version);
1700Sigor@sysoev.ru 
1710Sigor@sysoev.ru #ifndef SSL_OP_NO_COMPRESSION
1720Sigor@sysoev.ru     {
1730Sigor@sysoev.ru         /*
1740Sigor@sysoev.ru          * Disable gzip compression in OpenSSL prior to 1.0.0
1750Sigor@sysoev.ru          * version, this saves about 522K per connection.
1760Sigor@sysoev.ru          */
1770Sigor@sysoev.ru         int                 n;
1780Sigor@sysoev.ru         STACK_OF(SSL_COMP)  *ssl_comp_methods;
1790Sigor@sysoev.ru 
1800Sigor@sysoev.ru         ssl_comp_methods = SSL_COMP_get_compression_methods();
1810Sigor@sysoev.ru 
1820Sigor@sysoev.ru         for (n = sk_SSL_COMP_num(ssl_comp_methods); n != 0; n--) {
1830Sigor@sysoev.ru             (void) sk_SSL_COMP_pop(ssl_comp_methods);
1840Sigor@sysoev.ru         }
1850Sigor@sysoev.ru     }
1860Sigor@sysoev.ru #endif
1870Sigor@sysoev.ru 
1880Sigor@sysoev.ru     index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
1890Sigor@sysoev.ru 
1900Sigor@sysoev.ru     if (index == -1) {
191771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
1920Sigor@sysoev.ru                               "SSL_get_ex_new_index() failed");
1930Sigor@sysoev.ru         return NXT_ERROR;
1940Sigor@sysoev.ru     }
1950Sigor@sysoev.ru 
1960Sigor@sysoev.ru     nxt_openssl_connection_index = index;
1970Sigor@sysoev.ru 
1980Sigor@sysoev.ru     return NXT_OK;
1990Sigor@sysoev.ru }
2000Sigor@sysoev.ru 
2010Sigor@sysoev.ru 
202771Sigor@sysoev.ru #if OPENSSL_VERSION_NUMBER >= 0x10100003L
203771Sigor@sysoev.ru 
204771Sigor@sysoev.ru static void
205771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
206771Sigor@sysoev.ru {
207771Sigor@sysoev.ru }
208771Sigor@sysoev.ru 
209771Sigor@sysoev.ru #else
210771Sigor@sysoev.ru 
211771Sigor@sysoev.ru static nxt_thread_mutex_t  *nxt_openssl_locks;
212771Sigor@sysoev.ru 
2130Sigor@sysoev.ru static nxt_int_t
214771Sigor@sysoev.ru nxt_openssl_locks_init(void)
215771Sigor@sysoev.ru {
216771Sigor@sysoev.ru     int        i, n;
217771Sigor@sysoev.ru     nxt_int_t  ret;
218771Sigor@sysoev.ru 
219771Sigor@sysoev.ru     n = CRYPTO_num_locks();
220771Sigor@sysoev.ru 
221771Sigor@sysoev.ru     nxt_openssl_locks = OPENSSL_malloc(n * sizeof(nxt_thread_mutex_t));
222771Sigor@sysoev.ru     if (nxt_slow_path(nxt_openssl_locks == NULL)) {
223771Sigor@sysoev.ru         return NXT_ERROR;
224771Sigor@sysoev.ru     }
225771Sigor@sysoev.ru 
226771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
227771Sigor@sysoev.ru         ret = nxt_thread_mutex_create(&nxt_openssl_locks[i]);
228771Sigor@sysoev.ru         if (nxt_slow_path(ret != NXT_OK)) {
229771Sigor@sysoev.ru             return ret;
230771Sigor@sysoev.ru         }
231771Sigor@sysoev.ru     }
232771Sigor@sysoev.ru 
233771Sigor@sysoev.ru     CRYPTO_set_locking_callback(nxt_openssl_lock);
234771Sigor@sysoev.ru 
235771Sigor@sysoev.ru     CRYPTO_set_id_callback(nxt_openssl_thread_id);
236771Sigor@sysoev.ru 
237771Sigor@sysoev.ru     return NXT_OK;
238771Sigor@sysoev.ru }
239771Sigor@sysoev.ru 
240771Sigor@sysoev.ru 
241771Sigor@sysoev.ru static void
242771Sigor@sysoev.ru nxt_openssl_lock(int mode, int type, const char *file, int line)
243771Sigor@sysoev.ru {
244771Sigor@sysoev.ru     nxt_thread_mutex_t  *lock;
245771Sigor@sysoev.ru 
246771Sigor@sysoev.ru     lock = &nxt_openssl_locks[type];
247771Sigor@sysoev.ru 
248771Sigor@sysoev.ru     if ((mode & CRYPTO_LOCK) != 0) {
249771Sigor@sysoev.ru         (void) nxt_thread_mutex_lock(lock);
250771Sigor@sysoev.ru 
251771Sigor@sysoev.ru     } else {
252771Sigor@sysoev.ru         (void) nxt_thread_mutex_unlock(lock);
253771Sigor@sysoev.ru     }
254771Sigor@sysoev.ru }
255771Sigor@sysoev.ru 
256771Sigor@sysoev.ru 
257771Sigor@sysoev.ru static u_long
258771Sigor@sysoev.ru nxt_openssl_thread_id(void)
259771Sigor@sysoev.ru {
260771Sigor@sysoev.ru     return (u_long) nxt_thread_handle();
261771Sigor@sysoev.ru }
262771Sigor@sysoev.ru 
263771Sigor@sysoev.ru 
264771Sigor@sysoev.ru static void
265771Sigor@sysoev.ru nxt_openssl_library_free(nxt_task_t *task)
266771Sigor@sysoev.ru {
267771Sigor@sysoev.ru     nxt_openssl_locks_free();
268771Sigor@sysoev.ru }
269771Sigor@sysoev.ru 
270771Sigor@sysoev.ru 
271771Sigor@sysoev.ru static void
272771Sigor@sysoev.ru nxt_openssl_locks_free(void)
273771Sigor@sysoev.ru {
274771Sigor@sysoev.ru     int  i, n;
275771Sigor@sysoev.ru 
276771Sigor@sysoev.ru     n = CRYPTO_num_locks();
277771Sigor@sysoev.ru 
278771Sigor@sysoev.ru     CRYPTO_set_locking_callback(NULL);
279771Sigor@sysoev.ru 
280771Sigor@sysoev.ru     for (i = 0; i < n; i++) {
281771Sigor@sysoev.ru         nxt_thread_mutex_destroy(&nxt_openssl_locks[i]);
282771Sigor@sysoev.ru     }
283771Sigor@sysoev.ru 
284771Sigor@sysoev.ru     OPENSSL_free(nxt_openssl_locks);
285771Sigor@sysoev.ru }
286771Sigor@sysoev.ru 
287771Sigor@sysoev.ru #endif
288771Sigor@sysoev.ru 
289771Sigor@sysoev.ru 
290771Sigor@sysoev.ru static nxt_int_t
2911920Sa.suvorov@f5.com nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
2921920Sa.suvorov@f5.com     nxt_tls_init_t *tls_init, nxt_bool_t last)
2930Sigor@sysoev.ru {
2941828Sa.suvorov@f5.com     SSL_CTX                *ctx;
2951828Sa.suvorov@f5.com     const char             *ciphers, *ca_certificate;
2961920Sa.suvorov@f5.com     nxt_tls_conf_t         *conf;
2971828Sa.suvorov@f5.com     STACK_OF(X509_NAME)    *list;
2981828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
2990Sigor@sysoev.ru 
3000Sigor@sysoev.ru     ctx = SSL_CTX_new(SSLv23_server_method());
3010Sigor@sysoev.ru     if (ctx == NULL) {
302771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_CTX_new() failed");
3030Sigor@sysoev.ru         return NXT_ERROR;
3040Sigor@sysoev.ru     }
3050Sigor@sysoev.ru 
3061920Sa.suvorov@f5.com     conf = tls_init->conf;
3071920Sa.suvorov@f5.com 
3081828Sa.suvorov@f5.com     bundle = conf->bundle;
3091828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
3101828Sa.suvorov@f5.com 
3111828Sa.suvorov@f5.com     bundle->ctx = ctx;
3120Sigor@sysoev.ru 
313771Sigor@sysoev.ru #ifdef SSL_OP_NO_RENEGOTIATION
314771Sigor@sysoev.ru     /* Renegration is not currently supported. */
315771Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION);
316771Sigor@sysoev.ru #endif
317771Sigor@sysoev.ru 
3180Sigor@sysoev.ru #ifdef SSL_OP_NO_COMPRESSION
3190Sigor@sysoev.ru     /*
3200Sigor@sysoev.ru      * Disable gzip compression in OpenSSL 1.0.0,
3210Sigor@sysoev.ru      * this saves about 522K per connection.
3220Sigor@sysoev.ru      */
3230Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
3240Sigor@sysoev.ru #endif
3250Sigor@sysoev.ru 
3260Sigor@sysoev.ru #ifdef SSL_MODE_RELEASE_BUFFERS
3270Sigor@sysoev.ru 
3280Sigor@sysoev.ru     if (nxt_openssl_version >= 10001078) {
3290Sigor@sysoev.ru         /*
3300Sigor@sysoev.ru          * Allow to release read and write buffers in OpenSSL 1.0.0,
3310Sigor@sysoev.ru          * this saves about 34K per idle connection.  It is not safe
3320Sigor@sysoev.ru          * before OpenSSL 1.0.1h (CVE-2010-5298).
3330Sigor@sysoev.ru          */
3340Sigor@sysoev.ru         SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
3350Sigor@sysoev.ru     }
3360Sigor@sysoev.ru 
3370Sigor@sysoev.ru #endif
3380Sigor@sysoev.ru 
3391828Sa.suvorov@f5.com     if (nxt_openssl_chain_file(task, ctx, conf, mp,
3401828Sa.suvorov@f5.com                                last && bundle->next == NULL)
3411828Sa.suvorov@f5.com         != NXT_OK)
3421828Sa.suvorov@f5.com     {
3430Sigor@sysoev.ru         goto fail;
3440Sigor@sysoev.ru     }
345774Svbart@nginx.com /*
3460Sigor@sysoev.ru     key = conf->certificate_key;
3470Sigor@sysoev.ru 
3480Sigor@sysoev.ru     if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
349771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3500Sigor@sysoev.ru                               "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
3510Sigor@sysoev.ru                               key);
3520Sigor@sysoev.ru         goto fail;
3530Sigor@sysoev.ru     }
354774Svbart@nginx.com */
3551885Sa.suvorov@f5.com 
3560Sigor@sysoev.ru     ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
3570Sigor@sysoev.ru 
3580Sigor@sysoev.ru     if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
359771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT,
3600Sigor@sysoev.ru                               "SSL_CTX_set_cipher_list(\"%s\") failed",
3610Sigor@sysoev.ru                               ciphers);
3620Sigor@sysoev.ru         goto fail;
3630Sigor@sysoev.ru     }
3640Sigor@sysoev.ru 
3651885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
3661920Sa.suvorov@f5.com     if (tls_init->conf_cmds != NULL
3671920Sa.suvorov@f5.com         && nxt_ssl_conf_commands(task, ctx, tls_init->conf_cmds, mp) != NXT_OK)
3681885Sa.suvorov@f5.com     {
3691885Sa.suvorov@f5.com         goto fail;
3701885Sa.suvorov@f5.com     }
3711885Sa.suvorov@f5.com #endif
3721885Sa.suvorov@f5.com 
3731920Sa.suvorov@f5.com     nxt_ssl_session_cache(ctx, tls_init->cache_size, tls_init->timeout);
3741920Sa.suvorov@f5.com 
3751942Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_TLSEXT)
3761942Sa.suvorov@f5.com     if (nxt_tls_ticket_keys(task, ctx, tls_init, mp) != NXT_OK) {
3771942Sa.suvorov@f5.com         goto fail;
3781942Sa.suvorov@f5.com     }
3791942Sa.suvorov@f5.com #endif
3801942Sa.suvorov@f5.com 
3810Sigor@sysoev.ru     SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
3820Sigor@sysoev.ru 
3830Sigor@sysoev.ru     if (conf->ca_certificate != NULL) {
3840Sigor@sysoev.ru 
3850Sigor@sysoev.ru         /* TODO: verify callback */
3860Sigor@sysoev.ru         SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
3870Sigor@sysoev.ru 
3880Sigor@sysoev.ru         /* TODO: verify depth */
3890Sigor@sysoev.ru         SSL_CTX_set_verify_depth(ctx, 1);
3900Sigor@sysoev.ru 
3910Sigor@sysoev.ru         ca_certificate = conf->ca_certificate;
3920Sigor@sysoev.ru 
3930Sigor@sysoev.ru         if (SSL_CTX_load_verify_locations(ctx, ca_certificate, NULL) == 0) {
394771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
3950Sigor@sysoev.ru                               "SSL_CTX_load_verify_locations(\"%s\") failed",
3960Sigor@sysoev.ru                               ca_certificate);
3970Sigor@sysoev.ru             goto fail;
3980Sigor@sysoev.ru         }
3990Sigor@sysoev.ru 
4000Sigor@sysoev.ru         list = SSL_load_client_CA_file(ca_certificate);
4010Sigor@sysoev.ru 
4020Sigor@sysoev.ru         if (list == NULL) {
403771Sigor@sysoev.ru             nxt_openssl_log_error(task, NXT_LOG_ALERT,
4040Sigor@sysoev.ru                               "SSL_load_client_CA_file(\"%s\") failed",
4050Sigor@sysoev.ru                               ca_certificate);
4060Sigor@sysoev.ru             goto fail;
4070Sigor@sysoev.ru         }
4080Sigor@sysoev.ru 
4090Sigor@sysoev.ru         /*
4100Sigor@sysoev.ru          * SSL_load_client_CA_file() in OpenSSL prior to 0.9.7h and
4110Sigor@sysoev.ru          * 0.9.8 versions always leaves an error in the error queue.
4120Sigor@sysoev.ru          */
4130Sigor@sysoev.ru         ERR_clear_error();
4140Sigor@sysoev.ru 
4150Sigor@sysoev.ru         SSL_CTX_set_client_CA_list(ctx, list);
4160Sigor@sysoev.ru     }
4170Sigor@sysoev.ru 
4181828Sa.suvorov@f5.com     if (last) {
4191828Sa.suvorov@f5.com         conf->conn_init = nxt_openssl_conn_init;
4201828Sa.suvorov@f5.com 
4211828Sa.suvorov@f5.com         if (bundle->next != NULL) {
4221828Sa.suvorov@f5.com             SSL_CTX_set_tlsext_servername_callback(ctx, nxt_openssl_servername);
4231828Sa.suvorov@f5.com         }
4241828Sa.suvorov@f5.com     }
4251828Sa.suvorov@f5.com 
4260Sigor@sysoev.ru     return NXT_OK;
4270Sigor@sysoev.ru 
4280Sigor@sysoev.ru fail:
4290Sigor@sysoev.ru 
4300Sigor@sysoev.ru     SSL_CTX_free(ctx);
4310Sigor@sysoev.ru 
4321818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
4331818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
4341818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
4351818Smax.romanov@nginx.com #endif
4361818Smax.romanov@nginx.com 
4370Sigor@sysoev.ru     return NXT_ERROR;
4380Sigor@sysoev.ru }
4390Sigor@sysoev.ru 
4400Sigor@sysoev.ru 
441833Svbart@nginx.com static nxt_int_t
4421828Sa.suvorov@f5.com nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf,
4431828Sa.suvorov@f5.com     nxt_mp_t *mp, nxt_bool_t single)
444774Svbart@nginx.com {
4451828Sa.suvorov@f5.com     BIO                    *bio;
4461828Sa.suvorov@f5.com     X509                   *cert, *ca;
4471828Sa.suvorov@f5.com     long                   reason;
4481828Sa.suvorov@f5.com     EVP_PKEY               *key;
4491828Sa.suvorov@f5.com     nxt_int_t              ret;
4501828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
4511828Sa.suvorov@f5.com 
4521828Sa.suvorov@f5.com     ret = NXT_ERROR;
4531828Sa.suvorov@f5.com     cert = NULL;
454774Svbart@nginx.com 
455774Svbart@nginx.com     bio = BIO_new(BIO_s_fd());
456774Svbart@nginx.com     if (bio == NULL) {
4571828Sa.suvorov@f5.com         goto end;
458774Svbart@nginx.com     }
459774Svbart@nginx.com 
4601828Sa.suvorov@f5.com     bundle = conf->bundle;
461774Svbart@nginx.com 
4621828Sa.suvorov@f5.com     BIO_set_fd(bio, bundle->chain_file, BIO_CLOSE);
463774Svbart@nginx.com 
464774Svbart@nginx.com     cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
465774Svbart@nginx.com     if (cert == NULL) {
466774Svbart@nginx.com         goto end;
467774Svbart@nginx.com     }
468774Svbart@nginx.com 
469774Svbart@nginx.com     if (SSL_CTX_use_certificate(ctx, cert) != 1) {
470774Svbart@nginx.com         goto end;
471774Svbart@nginx.com     }
472774Svbart@nginx.com 
4731828Sa.suvorov@f5.com     if (!single && nxt_openssl_cert_get_names(task, cert, conf, mp) != NXT_OK) {
4741828Sa.suvorov@f5.com         goto clean;
4751828Sa.suvorov@f5.com     }
4761828Sa.suvorov@f5.com 
477774Svbart@nginx.com     for ( ;; ) {
478774Svbart@nginx.com         ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
479774Svbart@nginx.com 
480774Svbart@nginx.com         if (ca == NULL) {
481774Svbart@nginx.com             reason = ERR_GET_REASON(ERR_peek_last_error());
482774Svbart@nginx.com             if (reason != PEM_R_NO_START_LINE) {
483774Svbart@nginx.com                 goto end;
484774Svbart@nginx.com             }
485774Svbart@nginx.com 
486774Svbart@nginx.com             ERR_clear_error();
487774Svbart@nginx.com             break;
488774Svbart@nginx.com         }
489774Svbart@nginx.com 
490774Svbart@nginx.com         /*
491774Svbart@nginx.com          * Note that ca isn't freed if it was successfully added to the chain,
492774Svbart@nginx.com          * while the main certificate needs a X509_free() call, since
493774Svbart@nginx.com          * its reference count is increased by SSL_CTX_use_certificate().
494774Svbart@nginx.com          */
495808Spluknet@nginx.com #ifdef SSL_CTX_add0_chain_cert
496774Svbart@nginx.com         if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) {
497774Svbart@nginx.com #else
498774Svbart@nginx.com         if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) {
499774Svbart@nginx.com #endif
500774Svbart@nginx.com             X509_free(ca);
501774Svbart@nginx.com             goto end;
502774Svbart@nginx.com         }
503774Svbart@nginx.com     }
504774Svbart@nginx.com 
505774Svbart@nginx.com     if (BIO_reset(bio) != 0) {
506774Svbart@nginx.com         goto end;
507774Svbart@nginx.com     }
508774Svbart@nginx.com 
509774Svbart@nginx.com     key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
510774Svbart@nginx.com     if (key == NULL) {
511774Svbart@nginx.com         goto end;
512774Svbart@nginx.com     }
513774Svbart@nginx.com 
514774Svbart@nginx.com     if (SSL_CTX_use_PrivateKey(ctx, key) == 1) {
515774Svbart@nginx.com         ret = NXT_OK;
516774Svbart@nginx.com     }
517774Svbart@nginx.com 
518774Svbart@nginx.com     EVP_PKEY_free(key);
519774Svbart@nginx.com 
520774Svbart@nginx.com end:
521774Svbart@nginx.com 
5221828Sa.suvorov@f5.com     if (ret != NXT_OK) {
5231828Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
5241828Sa.suvorov@f5.com                               "nxt_openssl_chain_file() failed");
5251828Sa.suvorov@f5.com     }
5261828Sa.suvorov@f5.com 
5271828Sa.suvorov@f5.com clean:
5281828Sa.suvorov@f5.com 
5291828Sa.suvorov@f5.com     BIO_free(bio);
530774Svbart@nginx.com     X509_free(cert);
531774Svbart@nginx.com 
532774Svbart@nginx.com     return ret;
533774Svbart@nginx.com }
534774Svbart@nginx.com 
535774Svbart@nginx.com 
5361885Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_CONF_CMD)
5371885Sa.suvorov@f5.com 
5381885Sa.suvorov@f5.com static nxt_int_t
5391885Sa.suvorov@f5.com nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx, nxt_conf_value_t *conf,
5401885Sa.suvorov@f5.com     nxt_mp_t *mp)
5411885Sa.suvorov@f5.com {
5421885Sa.suvorov@f5.com     int               ret;
5431885Sa.suvorov@f5.com     char              *zcmd, *zvalue;
5441885Sa.suvorov@f5.com     uint32_t          index;
5451885Sa.suvorov@f5.com     nxt_str_t         cmd, value;
5461885Sa.suvorov@f5.com     SSL_CONF_CTX      *cctx;
5471885Sa.suvorov@f5.com     nxt_conf_value_t  *member;
5481885Sa.suvorov@f5.com 
5491885Sa.suvorov@f5.com     cctx = SSL_CONF_CTX_new();
5501885Sa.suvorov@f5.com     if (nxt_slow_path(cctx == NULL)) {
5511885Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
5521885Sa.suvorov@f5.com                               "SSL_CONF_CTX_new() failed");
5531885Sa.suvorov@f5.com         return NXT_ERROR;
5541885Sa.suvorov@f5.com     }
5551885Sa.suvorov@f5.com 
5561885Sa.suvorov@f5.com     SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE
5571885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_SERVER
5581885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_CERTIFICATE
5591885Sa.suvorov@f5.com                                  | SSL_CONF_FLAG_SHOW_ERRORS);
5601885Sa.suvorov@f5.com 
5611885Sa.suvorov@f5.com     SSL_CONF_CTX_set_ssl_ctx(cctx, ctx);
5621885Sa.suvorov@f5.com 
5631885Sa.suvorov@f5.com     index = 0;
5641885Sa.suvorov@f5.com 
5651885Sa.suvorov@f5.com     for ( ;; ) {
5661885Sa.suvorov@f5.com         member = nxt_conf_next_object_member(conf, &cmd, &index);
5671885Sa.suvorov@f5.com         if (nxt_slow_path(member == NULL)) {
5681885Sa.suvorov@f5.com             break;
5691885Sa.suvorov@f5.com         }
5701885Sa.suvorov@f5.com 
5711885Sa.suvorov@f5.com         nxt_conf_get_string(member, &value);
5721885Sa.suvorov@f5.com 
5731885Sa.suvorov@f5.com         zcmd = nxt_str_cstrz(mp, &cmd);
5741885Sa.suvorov@f5.com         zvalue = nxt_str_cstrz(mp, &value);
5751885Sa.suvorov@f5.com 
5761885Sa.suvorov@f5.com         if (nxt_slow_path(zcmd == NULL || zvalue == NULL)) {
5771885Sa.suvorov@f5.com             goto fail;
5781885Sa.suvorov@f5.com         }
5791885Sa.suvorov@f5.com 
5801885Sa.suvorov@f5.com         ret = SSL_CONF_cmd(cctx, zcmd, zvalue);
5811885Sa.suvorov@f5.com         if (ret == -2) {
5821885Sa.suvorov@f5.com             nxt_openssl_log_error(task, NXT_LOG_ERR,
5831885Sa.suvorov@f5.com                                   "unknown command \"%s\" in "
5841885Sa.suvorov@f5.com                                   "\"conf_commands\" option", zcmd);
5851885Sa.suvorov@f5.com             goto fail;
5861885Sa.suvorov@f5.com         }
5871885Sa.suvorov@f5.com 
5881885Sa.suvorov@f5.com         if (ret <= 0) {
5891885Sa.suvorov@f5.com             nxt_openssl_log_error(task, NXT_LOG_ERR,
5901885Sa.suvorov@f5.com                                   "invalid value \"%s\" for command \"%s\" "
5911885Sa.suvorov@f5.com                                   "in \"conf_commands\" option",
5921885Sa.suvorov@f5.com                                   zvalue, zcmd);
5931885Sa.suvorov@f5.com             goto fail;
5941885Sa.suvorov@f5.com         }
5951885Sa.suvorov@f5.com 
5961885Sa.suvorov@f5.com         nxt_debug(task, "SSL_CONF_cmd(\"%s\", \"%s\")", zcmd, zvalue);
5971885Sa.suvorov@f5.com     }
5981885Sa.suvorov@f5.com 
5991885Sa.suvorov@f5.com     if (SSL_CONF_CTX_finish(cctx) != 1) {
6001885Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
6011885Sa.suvorov@f5.com                               "SSL_CONF_finish() failed");
6021885Sa.suvorov@f5.com         goto fail;
6031885Sa.suvorov@f5.com     }
6041885Sa.suvorov@f5.com 
6051885Sa.suvorov@f5.com     SSL_CONF_CTX_free(cctx);
6061885Sa.suvorov@f5.com 
6071885Sa.suvorov@f5.com     return NXT_OK;
6081885Sa.suvorov@f5.com 
6091885Sa.suvorov@f5.com fail:
6101885Sa.suvorov@f5.com 
6111885Sa.suvorov@f5.com     SSL_CONF_CTX_free(cctx);
6121885Sa.suvorov@f5.com 
6131885Sa.suvorov@f5.com     return NXT_ERROR;
6141885Sa.suvorov@f5.com }
6151885Sa.suvorov@f5.com 
6161885Sa.suvorov@f5.com #endif
6171885Sa.suvorov@f5.com 
6181942Sa.suvorov@f5.com #if (NXT_HAVE_OPENSSL_TLSEXT)
6191942Sa.suvorov@f5.com 
6201942Sa.suvorov@f5.com static nxt_int_t
6211942Sa.suvorov@f5.com nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init,
6221942Sa.suvorov@f5.com     nxt_mp_t *mp)
6231942Sa.suvorov@f5.com {
6241942Sa.suvorov@f5.com     uint32_t           i;
6251942Sa.suvorov@f5.com     nxt_int_t          ret;
6261942Sa.suvorov@f5.com     nxt_str_t          value;
6271942Sa.suvorov@f5.com     nxt_uint_t         count;
6281942Sa.suvorov@f5.com     nxt_conf_value_t   *member, *tickets_conf;
6291942Sa.suvorov@f5.com     nxt_tls_ticket_t   *ticket;
6301942Sa.suvorov@f5.com     nxt_tls_tickets_t  *tickets;
6311942Sa.suvorov@f5.com     u_char             buf[80];
6321942Sa.suvorov@f5.com 
6331942Sa.suvorov@f5.com     tickets_conf = tls_init->tickets_conf;
6341942Sa.suvorov@f5.com 
6351942Sa.suvorov@f5.com     if (tickets_conf == NULL) {
6361942Sa.suvorov@f5.com         goto no_ticket;
6371942Sa.suvorov@f5.com     }
6381942Sa.suvorov@f5.com 
6391942Sa.suvorov@f5.com     if (nxt_conf_type(tickets_conf) == NXT_CONF_BOOLEAN) {
6401942Sa.suvorov@f5.com         if (nxt_conf_get_boolean(tickets_conf) == 0) {
6411942Sa.suvorov@f5.com             goto no_ticket;
6421942Sa.suvorov@f5.com         }
6431942Sa.suvorov@f5.com 
6441942Sa.suvorov@f5.com         return NXT_OK;
6451942Sa.suvorov@f5.com     }
6461942Sa.suvorov@f5.com 
6471942Sa.suvorov@f5.com     if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) {
6481942Sa.suvorov@f5.com         count = nxt_conf_array_elements_count(tickets_conf);
6491942Sa.suvorov@f5.com 
6501942Sa.suvorov@f5.com         if (count == 0) {
6511942Sa.suvorov@f5.com             goto no_ticket;
6521942Sa.suvorov@f5.com         }
6531942Sa.suvorov@f5.com 
6541942Sa.suvorov@f5.com     } else {
6551942Sa.suvorov@f5.com         /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */
6561942Sa.suvorov@f5.com         count = 1;
6571942Sa.suvorov@f5.com     }
6581942Sa.suvorov@f5.com 
6591942Sa.suvorov@f5.com #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
6601942Sa.suvorov@f5.com 
6611942Sa.suvorov@f5.com     tickets = nxt_mp_get(mp, sizeof(nxt_tls_tickets_t)
6621942Sa.suvorov@f5.com                              + count * sizeof(nxt_tls_ticket_t));
6631942Sa.suvorov@f5.com     if (nxt_slow_path(tickets == NULL)) {
6641942Sa.suvorov@f5.com         return NXT_ERROR;
6651942Sa.suvorov@f5.com     }
6661942Sa.suvorov@f5.com 
6671942Sa.suvorov@f5.com     tickets->count = count;
6681942Sa.suvorov@f5.com     tls_init->conf->tickets = tickets;
6691942Sa.suvorov@f5.com     i = 0;
6701942Sa.suvorov@f5.com 
6711942Sa.suvorov@f5.com     do {
6721942Sa.suvorov@f5.com         ticket = &tickets->tickets[i];
6731942Sa.suvorov@f5.com 
6741942Sa.suvorov@f5.com         i++;
6751942Sa.suvorov@f5.com 
6761942Sa.suvorov@f5.com         if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) {
6771942Sa.suvorov@f5.com             member = nxt_conf_get_array_element(tickets_conf, count - i);
6781942Sa.suvorov@f5.com             if (member == NULL) {
6791942Sa.suvorov@f5.com                 break;
6801942Sa.suvorov@f5.com             }
6811942Sa.suvorov@f5.com 
6821942Sa.suvorov@f5.com         } else {
6831942Sa.suvorov@f5.com             /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */
6841942Sa.suvorov@f5.com             member = tickets_conf;
6851942Sa.suvorov@f5.com         }
6861942Sa.suvorov@f5.com 
6871942Sa.suvorov@f5.com         nxt_conf_get_string(member, &value);
6881942Sa.suvorov@f5.com 
6891942Sa.suvorov@f5.com         ret = nxt_openssl_base64_decode(buf, 80, value.start, value.length);
6901942Sa.suvorov@f5.com         if (nxt_slow_path(ret == NXT_ERROR)) {
6911942Sa.suvorov@f5.com             return NXT_ERROR;
6921942Sa.suvorov@f5.com         }
6931942Sa.suvorov@f5.com 
694*1952Svbart@nginx.com         nxt_memcpy(ticket->name, buf, 16);
695*1952Svbart@nginx.com 
6961942Sa.suvorov@f5.com         if (ret == 48) {
6971942Sa.suvorov@f5.com             nxt_memcpy(ticket->aes_key, buf + 16, 16);
6981942Sa.suvorov@f5.com             nxt_memcpy(ticket->hmac_key, buf + 32, 16);
699*1952Svbart@nginx.com             ticket->size = 16;
7001942Sa.suvorov@f5.com 
7011942Sa.suvorov@f5.com         } else {
7021942Sa.suvorov@f5.com             nxt_memcpy(ticket->hmac_key, buf + 16, 32);
7031942Sa.suvorov@f5.com             nxt_memcpy(ticket->aes_key, buf + 48, 32);
704*1952Svbart@nginx.com             ticket->size = 32;
7051942Sa.suvorov@f5.com         }
7061942Sa.suvorov@f5.com 
7071942Sa.suvorov@f5.com     } while (i < count);
7081942Sa.suvorov@f5.com 
7091942Sa.suvorov@f5.com     if (SSL_CTX_set_tlsext_ticket_key_cb(ctx, nxt_tls_ticket_key_callback)
7101942Sa.suvorov@f5.com         == 0)
7111942Sa.suvorov@f5.com     {
7121942Sa.suvorov@f5.com         nxt_openssl_log_error(task, NXT_LOG_ALERT,
7131942Sa.suvorov@f5.com                       "Unit was built with Session Tickets support, however, "
7141942Sa.suvorov@f5.com                       "now it is linked dynamically to an OpenSSL library "
7151942Sa.suvorov@f5.com                       "which has no tlsext support, therefore Session Tickets "
7161942Sa.suvorov@f5.com                       "are not available");
7171942Sa.suvorov@f5.com 
7181942Sa.suvorov@f5.com         return NXT_ERROR;
7191942Sa.suvorov@f5.com     }
7201942Sa.suvorov@f5.com 
7211942Sa.suvorov@f5.com     return NXT_OK;
7221942Sa.suvorov@f5.com 
7231942Sa.suvorov@f5.com #else
7241942Sa.suvorov@f5.com     nxt_alert(task, "Setting custom session ticket keys is not supported with "
7251942Sa.suvorov@f5.com                     "this version of OpenSSL library");
7261942Sa.suvorov@f5.com 
7271942Sa.suvorov@f5.com     return NXT_ERROR;
7281942Sa.suvorov@f5.com 
7291942Sa.suvorov@f5.com #endif
7301942Sa.suvorov@f5.com 
7311942Sa.suvorov@f5.com no_ticket:
7321942Sa.suvorov@f5.com 
7331942Sa.suvorov@f5.com     SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
7341942Sa.suvorov@f5.com 
7351942Sa.suvorov@f5.com     return NXT_OK;
7361942Sa.suvorov@f5.com }
7371942Sa.suvorov@f5.com 
7381942Sa.suvorov@f5.com 
7391942Sa.suvorov@f5.com #ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
7401942Sa.suvorov@f5.com 
7411942Sa.suvorov@f5.com static int
7421942Sa.suvorov@f5.com nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv,
7431942Sa.suvorov@f5.com     EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc)
7441942Sa.suvorov@f5.com {
7451942Sa.suvorov@f5.com     nxt_uint_t          i;
7461942Sa.suvorov@f5.com     nxt_conn_t          *c;
7471942Sa.suvorov@f5.com     const EVP_MD        *digest;
7481942Sa.suvorov@f5.com     const EVP_CIPHER    *cipher;
7491942Sa.suvorov@f5.com     nxt_tls_ticket_t    *ticket;
7501942Sa.suvorov@f5.com     nxt_openssl_conn_t  *tls;
7511942Sa.suvorov@f5.com 
7521942Sa.suvorov@f5.com     c = SSL_get_ex_data(s, nxt_openssl_connection_index);
7531942Sa.suvorov@f5.com 
7541942Sa.suvorov@f5.com     if (nxt_slow_path(c == NULL)) {
7551942Sa.suvorov@f5.com         nxt_thread_log_alert("SSL_get_ex_data() failed");
7561942Sa.suvorov@f5.com         return -1;
7571942Sa.suvorov@f5.com     }
7581942Sa.suvorov@f5.com 
7591942Sa.suvorov@f5.com     tls = c->u.tls;
7601942Sa.suvorov@f5.com     ticket = tls->conf->tickets->tickets;
7611942Sa.suvorov@f5.com 
762*1952Svbart@nginx.com     i = 0;
7631942Sa.suvorov@f5.com 
7641942Sa.suvorov@f5.com     if (enc == 1) {
7651942Sa.suvorov@f5.com         /* encrypt session ticket */
7661942Sa.suvorov@f5.com 
7671942Sa.suvorov@f5.com         nxt_debug(c->socket.task, "TLS session ticket encrypt");
7681942Sa.suvorov@f5.com 
769*1952Svbart@nginx.com         cipher = (ticket[0].size == 16) ? EVP_aes_128_cbc() : EVP_aes_256_cbc();
7701942Sa.suvorov@f5.com 
7711942Sa.suvorov@f5.com         if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
7721942Sa.suvorov@f5.com             nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
7731942Sa.suvorov@f5.com                                   "RAND_bytes() failed");
7741942Sa.suvorov@f5.com             return -1;
7751942Sa.suvorov@f5.com         }
7761942Sa.suvorov@f5.com 
7771942Sa.suvorov@f5.com         nxt_memcpy(name, ticket[0].name, 16);
7781942Sa.suvorov@f5.com 
7791942Sa.suvorov@f5.com     } else {
7801942Sa.suvorov@f5.com         /* decrypt session ticket */
7811942Sa.suvorov@f5.com 
782*1952Svbart@nginx.com         do {
7831942Sa.suvorov@f5.com             if (nxt_memcmp(name, ticket[i].name, 16) == 0) {
7841942Sa.suvorov@f5.com                 goto found;
7851942Sa.suvorov@f5.com             }
786*1952Svbart@nginx.com 
787*1952Svbart@nginx.com         } while (++i < tls->conf->tickets->count);
7881942Sa.suvorov@f5.com 
7891942Sa.suvorov@f5.com         nxt_debug(c->socket.task, "TLS session ticket decrypt, key not found");
7901942Sa.suvorov@f5.com 
7911942Sa.suvorov@f5.com         return 0;
7921942Sa.suvorov@f5.com 
7931942Sa.suvorov@f5.com     found:
7941942Sa.suvorov@f5.com 
7951942Sa.suvorov@f5.com         nxt_debug(c->socket.task,
7961942Sa.suvorov@f5.com                   "TLS session ticket decrypt, key number: \"%d\"", i);
7971942Sa.suvorov@f5.com 
798*1952Svbart@nginx.com         enc = (i == 0) ? 1 : 2 /* renew */;
799*1952Svbart@nginx.com 
800*1952Svbart@nginx.com         cipher = (ticket[i].size == 16) ? EVP_aes_128_cbc() : EVP_aes_256_cbc();
801*1952Svbart@nginx.com     }
8021942Sa.suvorov@f5.com 
803*1952Svbart@nginx.com     if (EVP_DecryptInit_ex(ectx, cipher, NULL, ticket[i].aes_key, iv) != 1) {
804*1952Svbart@nginx.com         nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
805*1952Svbart@nginx.com                               "EVP_DecryptInit_ex() failed");
806*1952Svbart@nginx.com         return -1;
807*1952Svbart@nginx.com     }
8081942Sa.suvorov@f5.com 
809*1952Svbart@nginx.com #ifdef OPENSSL_NO_SHA256
810*1952Svbart@nginx.com     digest = EVP_sha1();
811*1952Svbart@nginx.com #else
812*1952Svbart@nginx.com     digest = EVP_sha256();
813*1952Svbart@nginx.com #endif
8141942Sa.suvorov@f5.com 
815*1952Svbart@nginx.com     if (HMAC_Init_ex(hctx, ticket[i].hmac_key, ticket[i].size, digest, NULL)
816*1952Svbart@nginx.com         != 1)
817*1952Svbart@nginx.com     {
818*1952Svbart@nginx.com         nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
819*1952Svbart@nginx.com                               "HMAC_Init_ex() failed");
820*1952Svbart@nginx.com         return -1;
821*1952Svbart@nginx.com     }
8221942Sa.suvorov@f5.com 
823*1952Svbart@nginx.com     return enc;
8241942Sa.suvorov@f5.com }
8251942Sa.suvorov@f5.com 
8261942Sa.suvorov@f5.com #endif /* SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB */
8271942Sa.suvorov@f5.com 
8281942Sa.suvorov@f5.com #endif /* NXT_HAVE_OPENSSL_TLSEXT */
8291942Sa.suvorov@f5.com 
8301885Sa.suvorov@f5.com 
8311920Sa.suvorov@f5.com static void
8321920Sa.suvorov@f5.com nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout)
8331920Sa.suvorov@f5.com {
8341920Sa.suvorov@f5.com     if (cache_size == 0) {
8351920Sa.suvorov@f5.com         SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
8361920Sa.suvorov@f5.com         return;
8371920Sa.suvorov@f5.com     }
8381920Sa.suvorov@f5.com 
8391920Sa.suvorov@f5.com     SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
8401920Sa.suvorov@f5.com 
8411920Sa.suvorov@f5.com     SSL_CTX_sess_set_cache_size(ctx, cache_size);
8421920Sa.suvorov@f5.com 
8431920Sa.suvorov@f5.com     SSL_CTX_set_timeout(ctx, (long) timeout);
8441920Sa.suvorov@f5.com }
8451920Sa.suvorov@f5.com 
8461920Sa.suvorov@f5.com 
8471828Sa.suvorov@f5.com static nxt_uint_t
8481828Sa.suvorov@f5.com nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
8491828Sa.suvorov@f5.com     nxt_mp_t *mp)
8501828Sa.suvorov@f5.com {
8511828Sa.suvorov@f5.com     int                         len;
8521828Sa.suvorov@f5.com     nxt_str_t                   domain, str;
8531828Sa.suvorov@f5.com     X509_NAME                   *x509_name;
8541828Sa.suvorov@f5.com     nxt_uint_t                  i, n;
8551828Sa.suvorov@f5.com     GENERAL_NAME                *name;
8561828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t       *bundle;
8571828Sa.suvorov@f5.com     STACK_OF(GENERAL_NAME)      *alt_names;
8581828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
8591828Sa.suvorov@f5.com 
8601828Sa.suvorov@f5.com     bundle = conf->bundle;
8611828Sa.suvorov@f5.com 
8621828Sa.suvorov@f5.com     alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
8631828Sa.suvorov@f5.com 
8641828Sa.suvorov@f5.com     if (alt_names != NULL) {
8651828Sa.suvorov@f5.com         n = sk_GENERAL_NAME_num(alt_names);
8661828Sa.suvorov@f5.com 
8671828Sa.suvorov@f5.com         for (i = 0; i != n; i++) {
8681828Sa.suvorov@f5.com             name = sk_GENERAL_NAME_value(alt_names, i);
8691828Sa.suvorov@f5.com 
8701828Sa.suvorov@f5.com             if (name->type != GEN_DNS) {
8711828Sa.suvorov@f5.com                 continue;
8721828Sa.suvorov@f5.com             }
8731828Sa.suvorov@f5.com 
8741828Sa.suvorov@f5.com             str.length = ASN1_STRING_length(name->d.dNSName);
8751828Sa.suvorov@f5.com #if OPENSSL_VERSION_NUMBER > 0x10100000L
8761828Sa.suvorov@f5.com             str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
8771828Sa.suvorov@f5.com #else
8781828Sa.suvorov@f5.com             str.start = ASN1_STRING_data(name->d.dNSName);
8791828Sa.suvorov@f5.com #endif
8801828Sa.suvorov@f5.com 
8811828Sa.suvorov@f5.com             domain.start = nxt_mp_nget(mp, str.length);
8821828Sa.suvorov@f5.com             if (nxt_slow_path(domain.start == NULL)) {
8831828Sa.suvorov@f5.com                 goto fail;
8841828Sa.suvorov@f5.com             }
8851828Sa.suvorov@f5.com 
8861828Sa.suvorov@f5.com             domain.length = str.length;
8871828Sa.suvorov@f5.com             nxt_memcpy_lowcase(domain.start, str.start, str.length);
8881828Sa.suvorov@f5.com 
8891828Sa.suvorov@f5.com             item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
8901828Sa.suvorov@f5.com             if (nxt_slow_path(item == NULL)) {
8911828Sa.suvorov@f5.com                 goto fail;
8921828Sa.suvorov@f5.com             }
8931828Sa.suvorov@f5.com 
8941828Sa.suvorov@f5.com             item->name = domain;
8951828Sa.suvorov@f5.com             item->bundle = bundle;
8961828Sa.suvorov@f5.com 
8971828Sa.suvorov@f5.com             if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash,
8981828Sa.suvorov@f5.com                                                item, mp)
8991828Sa.suvorov@f5.com                 == NXT_ERROR)
9001828Sa.suvorov@f5.com             {
9011828Sa.suvorov@f5.com                 goto fail;
9021828Sa.suvorov@f5.com             }
9031828Sa.suvorov@f5.com         }
9041828Sa.suvorov@f5.com 
9051828Sa.suvorov@f5.com         sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
9061828Sa.suvorov@f5.com 
9071828Sa.suvorov@f5.com     } else {
9081828Sa.suvorov@f5.com         x509_name = X509_get_subject_name(cert);
9091828Sa.suvorov@f5.com         len = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
9101828Sa.suvorov@f5.com                                         NULL, 0);
9111828Sa.suvorov@f5.com         if (len <= 0) {
9121828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither "
9131885Sa.suvorov@f5.com                     "Subject Alternative Name nor Common Name", &bundle->name);
9141828Sa.suvorov@f5.com             return NXT_OK;
9151828Sa.suvorov@f5.com         }
9161828Sa.suvorov@f5.com 
9171828Sa.suvorov@f5.com         domain.start = nxt_mp_nget(mp, len + 1);
9181828Sa.suvorov@f5.com         if (nxt_slow_path(domain.start == NULL)) {
9191828Sa.suvorov@f5.com             return NXT_ERROR;
9201828Sa.suvorov@f5.com         }
9211828Sa.suvorov@f5.com 
9221828Sa.suvorov@f5.com         domain.length = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
9231828Sa.suvorov@f5.com                                                   (char *) domain.start,
9241828Sa.suvorov@f5.com                                                   len + 1);
9251828Sa.suvorov@f5.com         nxt_memcpy_lowcase(domain.start, domain.start, domain.length);
9261828Sa.suvorov@f5.com 
9271828Sa.suvorov@f5.com         item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
9281828Sa.suvorov@f5.com         if (nxt_slow_path(item == NULL)) {
9291828Sa.suvorov@f5.com             return NXT_ERROR;
9301828Sa.suvorov@f5.com         }
9311828Sa.suvorov@f5.com 
9321828Sa.suvorov@f5.com         item->name = domain;
9331828Sa.suvorov@f5.com         item->bundle = bundle;
9341828Sa.suvorov@f5.com 
9351828Sa.suvorov@f5.com         if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash, item,
9361828Sa.suvorov@f5.com                                            mp)
9371828Sa.suvorov@f5.com             == NXT_ERROR)
9381828Sa.suvorov@f5.com         {
9391828Sa.suvorov@f5.com             return NXT_ERROR;
9401828Sa.suvorov@f5.com         }
9411828Sa.suvorov@f5.com     }
9421828Sa.suvorov@f5.com 
9431828Sa.suvorov@f5.com     return NXT_OK;
9441828Sa.suvorov@f5.com 
9451828Sa.suvorov@f5.com fail:
9461828Sa.suvorov@f5.com 
9471828Sa.suvorov@f5.com     sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
9481828Sa.suvorov@f5.com 
9491828Sa.suvorov@f5.com     return NXT_ERROR;
9501828Sa.suvorov@f5.com }
9511828Sa.suvorov@f5.com 
9521828Sa.suvorov@f5.com 
9531828Sa.suvorov@f5.com static const nxt_lvlhsh_proto_t  nxt_openssl_bundle_hash_proto
9541828Sa.suvorov@f5.com     nxt_aligned(64) =
9551828Sa.suvorov@f5.com {
9561828Sa.suvorov@f5.com     NXT_LVLHSH_DEFAULT,
9571828Sa.suvorov@f5.com     nxt_openssl_bundle_hash_test,
9581828Sa.suvorov@f5.com     nxt_mp_lvlhsh_alloc,
9591828Sa.suvorov@f5.com     nxt_mp_lvlhsh_free,
9601828Sa.suvorov@f5.com };
9611828Sa.suvorov@f5.com 
9621828Sa.suvorov@f5.com 
9631828Sa.suvorov@f5.com static nxt_int_t
9641828Sa.suvorov@f5.com nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
9651828Sa.suvorov@f5.com {
9661828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
9671828Sa.suvorov@f5.com 
9681828Sa.suvorov@f5.com     item = data;
9691828Sa.suvorov@f5.com 
9701828Sa.suvorov@f5.com     return nxt_strstr_eq(&lhq->key, &item->name) ? NXT_OK : NXT_DECLINED;
9711828Sa.suvorov@f5.com }
9721828Sa.suvorov@f5.com 
9731828Sa.suvorov@f5.com 
9741828Sa.suvorov@f5.com static nxt_int_t
9751828Sa.suvorov@f5.com nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
9761828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t *item, nxt_mp_t *mp)
9771828Sa.suvorov@f5.com {
9781828Sa.suvorov@f5.com     nxt_str_t                   str;
9791828Sa.suvorov@f5.com     nxt_int_t                   ret;
9801828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
9811828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *old;
9821828Sa.suvorov@f5.com 
9831828Sa.suvorov@f5.com     str = item->name;
9841828Sa.suvorov@f5.com 
9851828Sa.suvorov@f5.com     if (item->name.start[0] == '*') {
9861828Sa.suvorov@f5.com         item->name.start++;
9871828Sa.suvorov@f5.com         item->name.length--;
9881828Sa.suvorov@f5.com 
9891828Sa.suvorov@f5.com         if (item->name.length == 0 || item->name.start[0] != '.') {
9901828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" "
9911828Sa.suvorov@f5.com                     "in certificate \"%V\": missing \".\" "
9921885Sa.suvorov@f5.com                     "after wildcard symbol", &str, &item->bundle->name);
9931828Sa.suvorov@f5.com             return NXT_OK;
9941828Sa.suvorov@f5.com         }
9951828Sa.suvorov@f5.com     }
9961828Sa.suvorov@f5.com 
9971828Sa.suvorov@f5.com     lhq.pool = mp;
9981828Sa.suvorov@f5.com     lhq.key = item->name;
9991828Sa.suvorov@f5.com     lhq.value = item;
10001828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
10011828Sa.suvorov@f5.com     lhq.replace = 0;
10021828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(item->name.start, item->name.length);
10031828Sa.suvorov@f5.com 
10041828Sa.suvorov@f5.com     ret = nxt_lvlhsh_insert(lvlhsh, &lhq);
10051828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_OK)) {
10061828Sa.suvorov@f5.com         nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted",
10071885Sa.suvorov@f5.com                   &str, &item->bundle->name);
10081828Sa.suvorov@f5.com         return NXT_OK;
10091828Sa.suvorov@f5.com     }
10101828Sa.suvorov@f5.com 
10111828Sa.suvorov@f5.com     if (nxt_fast_path(ret == NXT_DECLINED)) {
10121828Sa.suvorov@f5.com         old = lhq.value;
10131828Sa.suvorov@f5.com         if (old->bundle != item->bundle) {
10141828Sa.suvorov@f5.com             nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" "
10151828Sa.suvorov@f5.com                     "in certificate \"%V\", identical name appears in \"%V\"",
10161885Sa.suvorov@f5.com                     &str, &old->bundle->name, &item->bundle->name);
10171828Sa.suvorov@f5.com 
10181828Sa.suvorov@f5.com             old->bundle = item->bundle;
10191828Sa.suvorov@f5.com         }
10201828Sa.suvorov@f5.com 
10211828Sa.suvorov@f5.com         return NXT_OK;
10221828Sa.suvorov@f5.com     }
10231828Sa.suvorov@f5.com 
10241828Sa.suvorov@f5.com     return NXT_ERROR;
10251828Sa.suvorov@f5.com }
10261828Sa.suvorov@f5.com 
10271828Sa.suvorov@f5.com 
10281828Sa.suvorov@f5.com static nxt_int_t
10291828Sa.suvorov@f5.com nxt_openssl_servername(SSL *s, int *ad, void *arg)
10301828Sa.suvorov@f5.com {
10311828Sa.suvorov@f5.com     nxt_str_t              str;
10321828Sa.suvorov@f5.com     nxt_uint_t             i;
10331828Sa.suvorov@f5.com     nxt_conn_t             *c;
10341828Sa.suvorov@f5.com     const char             *servername;
10351828Sa.suvorov@f5.com     nxt_tls_conf_t         *conf;
10361828Sa.suvorov@f5.com     nxt_openssl_conn_t     *tls;
10371828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
10381828Sa.suvorov@f5.com 
10391828Sa.suvorov@f5.com     c = SSL_get_ex_data(s, nxt_openssl_connection_index);
10401828Sa.suvorov@f5.com 
10411828Sa.suvorov@f5.com     if (nxt_slow_path(c == NULL)) {
10421828Sa.suvorov@f5.com         nxt_thread_log_alert("SSL_get_ex_data() failed");
10431828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
10441828Sa.suvorov@f5.com     }
10451828Sa.suvorov@f5.com 
10461828Sa.suvorov@f5.com     servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
10471921Sa.suvorov@f5.com 
10481921Sa.suvorov@f5.com     if (servername == NULL) {
10491921Sa.suvorov@f5.com         nxt_debug(c->socket.task, "SSL_get_servername(): NULL");
10501921Sa.suvorov@f5.com         goto done;
10511828Sa.suvorov@f5.com     }
10521828Sa.suvorov@f5.com 
10531828Sa.suvorov@f5.com     str.length = nxt_strlen(servername);
10541828Sa.suvorov@f5.com     if (str.length == 0) {
10551921Sa.suvorov@f5.com         nxt_debug(c->socket.task, "SSL_get_servername(): \"\" is empty");
10561828Sa.suvorov@f5.com         goto done;
10571828Sa.suvorov@f5.com     }
10581828Sa.suvorov@f5.com 
10591828Sa.suvorov@f5.com     if (servername[0] == '.') {
10601828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "ignored the server name \"%s\": "
10611828Sa.suvorov@f5.com                                   "leading \".\"", servername);
10621828Sa.suvorov@f5.com         goto done;
10631828Sa.suvorov@f5.com     }
10641828Sa.suvorov@f5.com 
10651828Sa.suvorov@f5.com     nxt_debug(c->socket.task, "tls with servername \"%s\"", servername);
10661828Sa.suvorov@f5.com 
10671828Sa.suvorov@f5.com     str.start = nxt_mp_nget(c->mem_pool, str.length);
10681828Sa.suvorov@f5.com     if (nxt_slow_path(str.start == NULL)) {
10691828Sa.suvorov@f5.com         return SSL_TLSEXT_ERR_ALERT_FATAL;
10701828Sa.suvorov@f5.com     }
10711828Sa.suvorov@f5.com 
10721828Sa.suvorov@f5.com     nxt_memcpy_lowcase(str.start, (const u_char *) servername, str.length);
10731828Sa.suvorov@f5.com 
10741828Sa.suvorov@f5.com     tls = c->u.tls;
10751828Sa.suvorov@f5.com     conf = tls->conf;
10761828Sa.suvorov@f5.com 
10771828Sa.suvorov@f5.com     bundle = nxt_openssl_find_ctx(conf, &str);
10781828Sa.suvorov@f5.com 
10791828Sa.suvorov@f5.com     if (bundle == NULL) {
10801828Sa.suvorov@f5.com         for (i = 1; i < str.length; i++) {
10811828Sa.suvorov@f5.com             if (str.start[i] == '.') {
10821828Sa.suvorov@f5.com                 str.start += i;
10831828Sa.suvorov@f5.com                 str.length -= i;
10841828Sa.suvorov@f5.com 
10851828Sa.suvorov@f5.com                 bundle = nxt_openssl_find_ctx(conf, &str);
10861828Sa.suvorov@f5.com                 break;
10871828Sa.suvorov@f5.com             }
10881828Sa.suvorov@f5.com         }
10891828Sa.suvorov@f5.com     }
10901828Sa.suvorov@f5.com 
10911828Sa.suvorov@f5.com     if (bundle != NULL) {
10921828Sa.suvorov@f5.com         nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" "
10931885Sa.suvorov@f5.com                                   "(old: \"%V\")", &str, &bundle->name,
10941885Sa.suvorov@f5.com                                   &conf->bundle->name);
10951828Sa.suvorov@f5.com 
10961828Sa.suvorov@f5.com         if (bundle != conf->bundle) {
10971828Sa.suvorov@f5.com             if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) {
10981828Sa.suvorov@f5.com                 nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
10991828Sa.suvorov@f5.com                                       "SSL_set_SSL_CTX() failed");
11001828Sa.suvorov@f5.com 
11011828Sa.suvorov@f5.com                 return SSL_TLSEXT_ERR_ALERT_FATAL;
11021828Sa.suvorov@f5.com             }
11031828Sa.suvorov@f5.com         }
11041828Sa.suvorov@f5.com     }
11051828Sa.suvorov@f5.com 
11061828Sa.suvorov@f5.com done:
11071828Sa.suvorov@f5.com 
11081828Sa.suvorov@f5.com     return SSL_TLSEXT_ERR_OK;
11091828Sa.suvorov@f5.com }
11101828Sa.suvorov@f5.com 
11111828Sa.suvorov@f5.com 
11121828Sa.suvorov@f5.com static nxt_tls_bundle_conf_t *
11131828Sa.suvorov@f5.com nxt_openssl_find_ctx(nxt_tls_conf_t *conf, nxt_str_t *sn)
11141828Sa.suvorov@f5.com {
11151828Sa.suvorov@f5.com     nxt_int_t                   ret;
11161828Sa.suvorov@f5.com     nxt_lvlhsh_query_t          lhq;
11171828Sa.suvorov@f5.com     nxt_tls_bundle_hash_item_t  *item;
11181828Sa.suvorov@f5.com 
11191828Sa.suvorov@f5.com     lhq.key_hash = nxt_murmur_hash2(sn->start, sn->length);
11201828Sa.suvorov@f5.com     lhq.key = *sn;
11211828Sa.suvorov@f5.com     lhq.proto = &nxt_openssl_bundle_hash_proto;
11221828Sa.suvorov@f5.com 
11231828Sa.suvorov@f5.com     ret = nxt_lvlhsh_find(&conf->bundle_hash, &lhq);
11241828Sa.suvorov@f5.com     if (ret != NXT_OK) {
11251828Sa.suvorov@f5.com         return NULL;
11261828Sa.suvorov@f5.com     }
11271828Sa.suvorov@f5.com 
11281828Sa.suvorov@f5.com     item = lhq.value;
11291828Sa.suvorov@f5.com 
11301828Sa.suvorov@f5.com     return item->bundle;
11311828Sa.suvorov@f5.com }
11321828Sa.suvorov@f5.com 
11331828Sa.suvorov@f5.com 
11340Sigor@sysoev.ru static void
1135771Sigor@sysoev.ru nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
11360Sigor@sysoev.ru {
11371828Sa.suvorov@f5.com     nxt_tls_bundle_conf_t  *bundle;
11381828Sa.suvorov@f5.com 
11391828Sa.suvorov@f5.com     bundle = conf->bundle;
11401828Sa.suvorov@f5.com     nxt_assert(bundle != NULL);
11411828Sa.suvorov@f5.com 
11421828Sa.suvorov@f5.com     do {
11431828Sa.suvorov@f5.com         SSL_CTX_free(bundle->ctx);
11441828Sa.suvorov@f5.com         bundle = bundle->next;
11451828Sa.suvorov@f5.com     } while (bundle != NULL);
11461818Smax.romanov@nginx.com 
11471942Sa.suvorov@f5.com     if (conf->tickets) {
11481942Sa.suvorov@f5.com         nxt_memzero(conf->tickets->tickets,
11491942Sa.suvorov@f5.com                     conf->tickets->count * sizeof(nxt_tls_ticket_t));
11501942Sa.suvorov@f5.com     }
11511942Sa.suvorov@f5.com 
11521818Smax.romanov@nginx.com #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
11531818Smax.romanov@nginx.com      && OPENSSL_VERSION_NUMBER < 0x1010101fL)
11541818Smax.romanov@nginx.com     RAND_keep_random_devices_open(0);
11551818Smax.romanov@nginx.com #endif
1156771Sigor@sysoev.ru }
1157771Sigor@sysoev.ru 
1158771Sigor@sysoev.ru 
1159771Sigor@sysoev.ru static void
1160771Sigor@sysoev.ru nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
1161771Sigor@sysoev.ru {
1162771Sigor@sysoev.ru     int                 ret;
1163771Sigor@sysoev.ru     SSL                 *s;
1164771Sigor@sysoev.ru     SSL_CTX             *ctx;
1165771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
11660Sigor@sysoev.ru 
11670Sigor@sysoev.ru     nxt_log_debug(c->socket.log, "openssl conn init");
11680Sigor@sysoev.ru 
1169771Sigor@sysoev.ru     tls = nxt_mp_zget(c->mem_pool, sizeof(nxt_openssl_conn_t));
1170771Sigor@sysoev.ru     if (tls == NULL) {
11710Sigor@sysoev.ru         goto fail;
11720Sigor@sysoev.ru     }
11730Sigor@sysoev.ru 
1174771Sigor@sysoev.ru     c->u.tls = tls;
1175771Sigor@sysoev.ru     nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size);
11760Sigor@sysoev.ru 
11771828Sa.suvorov@f5.com     ctx = conf->bundle->ctx;
11780Sigor@sysoev.ru 
11790Sigor@sysoev.ru     s = SSL_new(ctx);
11800Sigor@sysoev.ru     if (s == NULL) {
1181771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_new() failed");
11820Sigor@sysoev.ru         goto fail;
11830Sigor@sysoev.ru     }
11840Sigor@sysoev.ru 
1185771Sigor@sysoev.ru     tls->session = s;
11861828Sa.suvorov@f5.com     /* To pass TLS config to the nxt_openssl_servername() callback. */
11871828Sa.suvorov@f5.com     tls->conf = conf;
1188771Sigor@sysoev.ru     tls->conn = c;
11890Sigor@sysoev.ru 
11900Sigor@sysoev.ru     ret = SSL_set_fd(s, c->socket.fd);
11910Sigor@sysoev.ru 
11920Sigor@sysoev.ru     if (ret == 0) {
1193771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_fd(%d) failed",
1194771Sigor@sysoev.ru                               c->socket.fd);
11950Sigor@sysoev.ru         goto fail;
11960Sigor@sysoev.ru     }
11970Sigor@sysoev.ru 
11980Sigor@sysoev.ru     SSL_set_accept_state(s);
11990Sigor@sysoev.ru 
12000Sigor@sysoev.ru     if (SSL_set_ex_data(s, nxt_openssl_connection_index, c) == 0) {
1201771Sigor@sysoev.ru         nxt_openssl_log_error(task, NXT_LOG_ALERT, "SSL_set_ex_data() failed");
12020Sigor@sysoev.ru         goto fail;
12030Sigor@sysoev.ru     }
12040Sigor@sysoev.ru 
120562Sigor@sysoev.ru     c->io = &nxt_openssl_conn_io;
12060Sigor@sysoev.ru     c->sendfile = NXT_CONN_SENDFILE_OFF;
12070Sigor@sysoev.ru 
12081Sigor@sysoev.ru     nxt_openssl_conn_handshake(task, c, c->socket.data);
12091884Sa.suvorov@f5.com 
12100Sigor@sysoev.ru     return;
12110Sigor@sysoev.ru 
12120Sigor@sysoev.ru fail:
12130Sigor@sysoev.ru 
121413Sigor@sysoev.ru     nxt_work_queue_add(c->read_work_queue, c->read_state->error_handler,
121513Sigor@sysoev.ru                        task, c, c->socket.data);
12160Sigor@sysoev.ru }
12170Sigor@sysoev.ru 
12180Sigor@sysoev.ru 
1219771Sigor@sysoev.ru nxt_inline void
1220836Sigor@sysoev.ru nxt_openssl_conn_free(nxt_task_t *task, nxt_conn_t *c)
12210Sigor@sysoev.ru {
1222836Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
1223836Sigor@sysoev.ru 
1224771Sigor@sysoev.ru     nxt_debug(task, "openssl conn free");
12250Sigor@sysoev.ru 
1226836Sigor@sysoev.ru     tls = c->u.tls;
12270Sigor@sysoev.ru 
1228836Sigor@sysoev.ru     if (tls != NULL) {
1229836Sigor@sysoev.ru         c->u.tls = NULL;
1230836Sigor@sysoev.ru         nxt_free(tls->buffer.start);
1231836Sigor@sysoev.ru         SSL_free(tls->session);
1232836Sigor@sysoev.ru     }
12330Sigor@sysoev.ru }
12340Sigor@sysoev.ru 
12350Sigor@sysoev.ru 
12360Sigor@sysoev.ru static void
12371Sigor@sysoev.ru nxt_openssl_conn_handshake(nxt_task_t *task, void *obj, void *data)
12380Sigor@sysoev.ru {
1239771Sigor@sysoev.ru     int                     ret;
1240771Sigor@sysoev.ru     nxt_int_t               n;
1241771Sigor@sysoev.ru     nxt_err_t               err;
1242771Sigor@sysoev.ru     nxt_conn_t              *c;
1243771Sigor@sysoev.ru     nxt_work_queue_t        *wq;
1244771Sigor@sysoev.ru     nxt_work_handler_t      handler;
1245771Sigor@sysoev.ru     nxt_openssl_conn_t      *tls;
1246771Sigor@sysoev.ru     const nxt_conn_state_t  *state;
12470Sigor@sysoev.ru 
12480Sigor@sysoev.ru     c = obj;
1249836Sigor@sysoev.ru 
1250836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake fd:%d", c->socket.fd);
1251836Sigor@sysoev.ru 
1252836Sigor@sysoev.ru     if (c->socket.error != 0) {
1253836Sigor@sysoev.ru         return;
1254836Sigor@sysoev.ru     }
1255836Sigor@sysoev.ru 
1256771Sigor@sysoev.ru     tls = c->u.tls;
12570Sigor@sysoev.ru 
1258836Sigor@sysoev.ru     if (tls == NULL) {
1259836Sigor@sysoev.ru         return;
1260836Sigor@sysoev.ru     }
1261836Sigor@sysoev.ru 
1262836Sigor@sysoev.ru     nxt_debug(task, "openssl conn handshake: %d times", tls->times);
12630Sigor@sysoev.ru 
1264771Sigor@sysoev.ru     /* "tls->times == 1" is suitable to run SSL_do_handshake() in job. */
12650Sigor@sysoev.ru 
1266771Sigor@sysoev.ru     ret = SSL_do_handshake(tls->session);
12670Sigor@sysoev.ru 
12680Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
12690Sigor@sysoev.ru 
12701Sigor@sysoev.ru     nxt_thread_time_debug_update(task->thread);
12710Sigor@sysoev.ru 
12721Sigor@sysoev.ru     nxt_debug(task, "SSL_do_handshake(%d): %d err:%d", c->socket.fd, ret, err);
12730Sigor@sysoev.ru 
1274771Sigor@sysoev.ru     state = (c->read_state != NULL) ? c->read_state : c->write_state;
1275771Sigor@sysoev.ru 
12760Sigor@sysoev.ru     if (ret > 0) {
12770Sigor@sysoev.ru         /* ret == 1, the handshake was successfully completed. */
1278771Sigor@sysoev.ru         tls->handshake = 1;
12790Sigor@sysoev.ru 
1280771Sigor@sysoev.ru         if (c->read_state != NULL) {
1281771Sigor@sysoev.ru             if (state->io_read_handler != NULL || c->read != NULL) {
1282771Sigor@sysoev.ru                 nxt_conn_read(task->thread->engine, c);
12830Sigor@sysoev.ru                 return;
12840Sigor@sysoev.ru             }
12850Sigor@sysoev.ru 
1286771Sigor@sysoev.ru         } else {
1287771Sigor@sysoev.ru             if (c->write != NULL) {
1288771Sigor@sysoev.ru                 nxt_conn_write(task->thread->engine, c);
1289771Sigor@sysoev.ru                 return;
1290771Sigor@sysoev.ru             }
1291771Sigor@sysoev.ru         }
1292771Sigor@sysoev.ru 
1293771Sigor@sysoev.ru         handler = state->ready_handler;
1294771Sigor@sysoev.ru 
1295771Sigor@sysoev.ru     } else {
1296771Sigor@sysoev.ru         c->socket.read_handler = nxt_openssl_conn_handshake;
1297771Sigor@sysoev.ru         c->socket.write_handler = nxt_openssl_conn_handshake;
1298771Sigor@sysoev.ru 
1299771Sigor@sysoev.ru         n = nxt_openssl_conn_test_error(task, c, ret, err,
1300771Sigor@sysoev.ru                                         NXT_OPENSSL_HANDSHAKE);
1301771Sigor@sysoev.ru         switch (n) {
13020Sigor@sysoev.ru 
1303771Sigor@sysoev.ru         case NXT_AGAIN:
1304771Sigor@sysoev.ru             if (tls->ssl_error == SSL_ERROR_WANT_READ && tls->times < 2) {
1305771Sigor@sysoev.ru                 tls->times++;
1306771Sigor@sysoev.ru             }
1307771Sigor@sysoev.ru 
1308771Sigor@sysoev.ru             return;
1309771Sigor@sysoev.ru 
1310771Sigor@sysoev.ru         case 0:
1311771Sigor@sysoev.ru             handler = state->close_handler;
1312771Sigor@sysoev.ru             break;
1313771Sigor@sysoev.ru 
1314771Sigor@sysoev.ru         default:
1315771Sigor@sysoev.ru         case NXT_ERROR:
1316771Sigor@sysoev.ru             nxt_openssl_conn_error(task, err, "SSL_do_handshake(%d) failed",
1317771Sigor@sysoev.ru                                    c->socket.fd);
1318771Sigor@sysoev.ru 
1319771Sigor@sysoev.ru             handler = state->error_handler;
1320771Sigor@sysoev.ru             break;
13210Sigor@sysoev.ru         }
13220Sigor@sysoev.ru     }
13230Sigor@sysoev.ru 
1324771Sigor@sysoev.ru     wq = (c->read_state != NULL) ? c->read_work_queue : c->write_work_queue;
1325771Sigor@sysoev.ru 
1326771Sigor@sysoev.ru     nxt_work_queue_add(wq, handler, task, c, data);
13270Sigor@sysoev.ru }
13280Sigor@sysoev.ru 
13290Sigor@sysoev.ru 
13300Sigor@sysoev.ru static ssize_t
1331771Sigor@sysoev.ru nxt_openssl_conn_io_recvbuf(nxt_conn_t *c, nxt_buf_t *b)
13320Sigor@sysoev.ru {
1333771Sigor@sysoev.ru     int                 ret;
1334771Sigor@sysoev.ru     size_t              size;
1335771Sigor@sysoev.ru     nxt_int_t           n;
1336771Sigor@sysoev.ru     nxt_err_t           err;
1337771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
1338771Sigor@sysoev.ru 
1339771Sigor@sysoev.ru     tls = c->u.tls;
1340771Sigor@sysoev.ru     size = b->mem.end - b->mem.free;
1341771Sigor@sysoev.ru 
1342771Sigor@sysoev.ru     ret = SSL_read(tls->session, b->mem.free, size);
1343771Sigor@sysoev.ru 
1344771Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
1345771Sigor@sysoev.ru 
1346771Sigor@sysoev.ru     nxt_debug(c->socket.task, "SSL_read(%d, %p, %uz): %d err:%d",
1347771Sigor@sysoev.ru               c->socket.fd, b->mem.free, size, ret, err);
13480Sigor@sysoev.ru 
1349771Sigor@sysoev.ru     if (ret > 0) {
1350771Sigor@sysoev.ru         return ret;
1351771Sigor@sysoev.ru     }
13520Sigor@sysoev.ru 
1353771Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(c->socket.task, c, ret, err,
1354771Sigor@sysoev.ru                                     NXT_OPENSSL_READ);
1355771Sigor@sysoev.ru     if (n == NXT_ERROR) {
1356771Sigor@sysoev.ru         nxt_openssl_conn_error(c->socket.task, err,
1357771Sigor@sysoev.ru                                "SSL_read(%d, %p, %uz) failed",
1358771Sigor@sysoev.ru                                c->socket.fd, b->mem.free, size);
1359771Sigor@sysoev.ru     }
13600Sigor@sysoev.ru 
1361771Sigor@sysoev.ru     return n;
13620Sigor@sysoev.ru }
13630Sigor@sysoev.ru 
13640Sigor@sysoev.ru 
13650Sigor@sysoev.ru static ssize_t
1366771Sigor@sysoev.ru nxt_openssl_conn_io_sendbuf(nxt_task_t *task, nxt_sendbuf_t *sb)
1367771Sigor@sysoev.ru {
1368771Sigor@sysoev.ru     nxt_uint_t    niov;
1369771Sigor@sysoev.ru     struct iovec  iov;
1370771Sigor@sysoev.ru 
1371771Sigor@sysoev.ru     niov = nxt_sendbuf_mem_coalesce0(task, sb, &iov, 1);
1372771Sigor@sysoev.ru 
1373771Sigor@sysoev.ru     if (niov == 0 && sb->sync) {
1374771Sigor@sysoev.ru         return 0;
1375771Sigor@sysoev.ru     }
1376771Sigor@sysoev.ru 
1377771Sigor@sysoev.ru     return nxt_openssl_conn_io_send(task, sb, iov.iov_base, iov.iov_len);
1378771Sigor@sysoev.ru }
1379771Sigor@sysoev.ru 
1380771Sigor@sysoev.ru 
1381771Sigor@sysoev.ru static ssize_t
1382771Sigor@sysoev.ru nxt_openssl_conn_io_send(nxt_task_t *task, nxt_sendbuf_t *sb, void *buf,
1383771Sigor@sysoev.ru     size_t size)
13840Sigor@sysoev.ru {
13850Sigor@sysoev.ru     int                 ret;
13860Sigor@sysoev.ru     nxt_err_t           err;
13870Sigor@sysoev.ru     nxt_int_t           n;
1388771Sigor@sysoev.ru     nxt_conn_t          *c;
1389771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
13900Sigor@sysoev.ru 
1391771Sigor@sysoev.ru     tls = sb->tls;
13920Sigor@sysoev.ru 
1393771Sigor@sysoev.ru     ret = SSL_write(tls->session, buf, size);
13940Sigor@sysoev.ru 
13951212Sigor@sysoev.ru     err = (ret <= 0) ? nxt_socket_errno : 0;
13960Sigor@sysoev.ru 
1397771Sigor@sysoev.ru     nxt_debug(task, "SSL_write(%d, %p, %uz): %d err:%d",
1398771Sigor@sysoev.ru               sb->socket, buf, size, ret, err);
13990Sigor@sysoev.ru 
14000Sigor@sysoev.ru     if (ret > 0) {
14010Sigor@sysoev.ru         return ret;
14020Sigor@sysoev.ru     }
14030Sigor@sysoev.ru 
1404771Sigor@sysoev.ru     c = tls->conn;
1405771Sigor@sysoev.ru     c->socket.write_ready = sb->ready;
1406771Sigor@sysoev.ru 
1407771Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(task, c, ret, err, NXT_OPENSSL_WRITE);
1408771Sigor@sysoev.ru 
1409771Sigor@sysoev.ru     sb->ready = c->socket.write_ready;
14100Sigor@sysoev.ru 
14110Sigor@sysoev.ru     if (n == NXT_ERROR) {
14121212Sigor@sysoev.ru         sb->error = c->socket.error;
1413771Sigor@sysoev.ru         nxt_openssl_conn_error(task, err, "SSL_write(%d, %p, %uz) failed",
1414771Sigor@sysoev.ru                                sb->socket, buf, size);
14150Sigor@sysoev.ru     }
14160Sigor@sysoev.ru 
14170Sigor@sysoev.ru     return n;
14180Sigor@sysoev.ru }
14190Sigor@sysoev.ru 
14200Sigor@sysoev.ru 
14210Sigor@sysoev.ru static void
14221Sigor@sysoev.ru nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data)
14230Sigor@sysoev.ru {
14240Sigor@sysoev.ru     int                 ret, mode;
14250Sigor@sysoev.ru     SSL                 *s;
14260Sigor@sysoev.ru     nxt_err_t           err;
14270Sigor@sysoev.ru     nxt_int_t           n;
14280Sigor@sysoev.ru     nxt_bool_t          quiet, once;
142962Sigor@sysoev.ru     nxt_conn_t          *c;
1430771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
14310Sigor@sysoev.ru     nxt_work_handler_t  handler;
14320Sigor@sysoev.ru 
14330Sigor@sysoev.ru     c = obj;
14340Sigor@sysoev.ru 
1435836Sigor@sysoev.ru     nxt_debug(task, "openssl conn shutdown fd:%d", c->socket.fd);
1436836Sigor@sysoev.ru 
1437771Sigor@sysoev.ru     c->read_state = NULL;
1438771Sigor@sysoev.ru     tls = c->u.tls;
1439836Sigor@sysoev.ru 
1440836Sigor@sysoev.ru     if (tls == NULL) {
1441836Sigor@sysoev.ru         return;
1442836Sigor@sysoev.ru     }
1443836Sigor@sysoev.ru 
1444771Sigor@sysoev.ru     s = tls->session;
14450Sigor@sysoev.ru 
1446771Sigor@sysoev.ru     if (s == NULL || !tls->handshake) {
1447771Sigor@sysoev.ru         handler = c->write_state->ready_handler;
14480Sigor@sysoev.ru         goto done;
14490Sigor@sysoev.ru     }
14500Sigor@sysoev.ru 
14510Sigor@sysoev.ru     mode = SSL_get_shutdown(s);
14520Sigor@sysoev.ru 
14530Sigor@sysoev.ru     if (c->socket.timedout || c->socket.error != 0) {
14540Sigor@sysoev.ru         quiet = 1;
14550Sigor@sysoev.ru 
14560Sigor@sysoev.ru     } else if (c->socket.closed && !(mode & SSL_RECEIVED_SHUTDOWN)) {
14570Sigor@sysoev.ru         quiet = 1;
14580Sigor@sysoev.ru 
14590Sigor@sysoev.ru     } else {
14600Sigor@sysoev.ru         quiet = 0;
14610Sigor@sysoev.ru     }
14620Sigor@sysoev.ru 
14630Sigor@sysoev.ru     SSL_set_quiet_shutdown(s, quiet);
14640Sigor@sysoev.ru 
14651884Sa.suvorov@f5.com     if (tls->conf->no_wait_shutdown) {
14661884Sa.suvorov@f5.com         mode |= SSL_RECEIVED_SHUTDOWN;
14671884Sa.suvorov@f5.com     }
14681884Sa.suvorov@f5.com 
14690Sigor@sysoev.ru     once = 1;
14700Sigor@sysoev.ru 
14710Sigor@sysoev.ru     for ( ;; ) {
14720Sigor@sysoev.ru         SSL_set_shutdown(s, mode);
14730Sigor@sysoev.ru 
14740Sigor@sysoev.ru         ret = SSL_shutdown(s);
14750Sigor@sysoev.ru 
14760Sigor@sysoev.ru         err = (ret <= 0) ? nxt_socket_errno : 0;
14770Sigor@sysoev.ru 
14781Sigor@sysoev.ru         nxt_debug(task, "SSL_shutdown(%d, %d, %b): %d err:%d",
14791Sigor@sysoev.ru                   c->socket.fd, mode, quiet, ret, err);
14800Sigor@sysoev.ru 
14810Sigor@sysoev.ru         if (ret > 0) {
14820Sigor@sysoev.ru             /* ret == 1, the shutdown was successfully completed. */
1483771Sigor@sysoev.ru             handler = c->write_state->ready_handler;
14840Sigor@sysoev.ru             goto done;
14850Sigor@sysoev.ru         }
14860Sigor@sysoev.ru 
14870Sigor@sysoev.ru         if (ret == 0) {
14880Sigor@sysoev.ru             /*
14890Sigor@sysoev.ru              * If SSL_shutdown() returns 0 then it should be called
1490771Sigor@sysoev.ru              * again.  The second SSL_shutdown() call should return
14910Sigor@sysoev.ru              * -1/SSL_ERROR_WANT_READ or -1/SSL_ERROR_WANT_WRITE.
14920Sigor@sysoev.ru              * OpenSSL prior to 0.9.8m version however never returns
1493771Sigor@sysoev.ru              * -1 at all.  Fortunately, OpenSSL preserves internally
14940Sigor@sysoev.ru              * correct status available via SSL_get_error(-1).
14950Sigor@sysoev.ru              */
14960Sigor@sysoev.ru             if (once) {
1497771Sigor@sysoev.ru                 once = 0;
14980Sigor@sysoev.ru                 mode = SSL_get_shutdown(s);
14990Sigor@sysoev.ru                 continue;
15000Sigor@sysoev.ru             }
15010Sigor@sysoev.ru 
15020Sigor@sysoev.ru             ret = -1;
15030Sigor@sysoev.ru         }
15040Sigor@sysoev.ru 
15050Sigor@sysoev.ru         /* ret == -1 */
15060Sigor@sysoev.ru 
15070Sigor@sysoev.ru         break;
15080Sigor@sysoev.ru     }
15090Sigor@sysoev.ru 
1510771Sigor@sysoev.ru     c->socket.read_handler = nxt_openssl_conn_io_shutdown;
1511771Sigor@sysoev.ru     c->socket.write_handler = nxt_openssl_conn_io_shutdown;
1512771Sigor@sysoev.ru     c->socket.error_handler = c->write_state->error_handler;
1513771Sigor@sysoev.ru 
1514771Sigor@sysoev.ru     n = nxt_openssl_conn_test_error(task, c, ret, err, NXT_OPENSSL_SHUTDOWN);
1515771Sigor@sysoev.ru 
1516771Sigor@sysoev.ru     switch (n) {
15170Sigor@sysoev.ru 
1518771Sigor@sysoev.ru     case 0:
1519771Sigor@sysoev.ru         handler = c->write_state->close_handler;
1520771Sigor@sysoev.ru         break;
1521771Sigor@sysoev.ru 
1522771Sigor@sysoev.ru     case NXT_AGAIN:
15231884Sa.suvorov@f5.com         c->write_timer.handler = nxt_openssl_conn_io_shutdown_timeout;
15241884Sa.suvorov@f5.com         nxt_timer_add(task->thread->engine, &c->write_timer, 5000);
15250Sigor@sysoev.ru         return;
1526771Sigor@sysoev.ru 
1527771Sigor@sysoev.ru     default:
1528771Sigor@sysoev.ru     case NXT_ERROR:
1529771Sigor@sysoev.ru         nxt_openssl_conn_error(task, err, "SSL_shutdown(%d) failed",
1530771Sigor@sysoev.ru                                c->socket.fd);
1531771Sigor@sysoev.ru         handler = c->write_state->error_handler;
15320Sigor@sysoev.ru     }
15330Sigor@sysoev.ru 
1534771Sigor@sysoev.ru done:
15350Sigor@sysoev.ru 
1536836Sigor@sysoev.ru     nxt_openssl_conn_free(task, c);
15370Sigor@sysoev.ru 
153813Sigor@sysoev.ru     nxt_work_queue_add(c->write_work_queue, handler, task, c, data);
15390Sigor@sysoev.ru }
15400Sigor@sysoev.ru 
15410Sigor@sysoev.ru 
15420Sigor@sysoev.ru static nxt_int_t
154362Sigor@sysoev.ru nxt_openssl_conn_test_error(nxt_task_t *task, nxt_conn_t *c, int ret,
1544771Sigor@sysoev.ru     nxt_err_t sys_err, nxt_openssl_io_t io)
15450Sigor@sysoev.ru {
15460Sigor@sysoev.ru     u_long              lib_err;
1547771Sigor@sysoev.ru     nxt_openssl_conn_t  *tls;
15480Sigor@sysoev.ru 
1549771Sigor@sysoev.ru     tls = c->u.tls;
15500Sigor@sysoev.ru 
1551771Sigor@sysoev.ru     tls->ssl_error = SSL_get_error(tls->session, ret);
15520Sigor@sysoev.ru 
1553771Sigor@sysoev.ru     nxt_debug(task, "SSL_get_error(): %d", tls->ssl_error);
15540Sigor@sysoev.ru 
1555771Sigor@sysoev.ru     switch (tls->ssl_error) {
15560Sigor@sysoev.ru 
15570Sigor@sysoev.ru     case SSL_ERROR_WANT_READ:
1558990Sigor@sysoev.ru         c->socket.read_ready = 0;
1559771Sigor@sysoev.ru 
1560771Sigor@sysoev.ru         if (io != NXT_OPENSSL_READ) {
1561771Sigor@sysoev.ru             nxt_fd_event_block_write(task->thread->engine, &c->socket);
15620Sigor@sysoev.ru 
1563771Sigor@sysoev.ru             if (nxt_fd_event_is_disabled(c->socket.read)) {
1564771Sigor@sysoev.ru                 nxt_fd_event_enable_read(task->thread->engine, &c->socket);
1565771Sigor@sysoev.ru             }
15660Sigor@sysoev.ru         }
15670Sigor@sysoev.ru 
15680Sigor@sysoev.ru         return NXT_AGAIN;
15690Sigor@sysoev.ru 
15700Sigor@sysoev.ru     case SSL_ERROR_WANT_WRITE:
1571990Sigor@sysoev.ru         c->socket.write_ready = 0;
1572771Sigor@sysoev.ru 
1573771Sigor@sysoev.ru         if (io != NXT_OPENSSL_WRITE) {
1574771Sigor@sysoev.ru             nxt_fd_event_block_read(task->thread->engine, &c->socket);
15750Sigor@sysoev.ru 
1576771Sigor@sysoev.ru             if (nxt_fd_event_is_disabled(c->socket.write)) {
1577771Sigor@sysoev.ru                 nxt_fd_event_enable_write(task->thread->engine, &c->socket);
1578771Sigor@sysoev.ru             }
15790Sigor@sysoev.ru         }
15800Sigor@sysoev.ru 
15810Sigor@sysoev.ru         return NXT_AGAIN;
15820Sigor@sysoev.ru 
15830Sigor@sysoev.ru     case SSL_ERROR_SYSCALL:
15840Sigor@sysoev.ru         lib_err = ERR_peek_error();
15850Sigor@sysoev.ru 
15861Sigor@sysoev.ru         nxt_debug(task, "ERR_peek_error(): %l", lib_err);
15870Sigor@sysoev.ru 
15880Sigor@sysoev.ru         if (sys_err != 0 || lib_err != 0) {
15891212Sigor@sysoev.ru             c->socket.error = sys_err;
15900Sigor@sysoev.ru             return NXT_ERROR;
15910Sigor@sysoev.ru         }
15920Sigor@sysoev.ru 
15930Sigor@sysoev.ru         /* A connection was just closed. */
15940Sigor@sysoev.ru         c->socket.closed = 1;
1595771Sigor@sysoev.ru         return 0;
15960Sigor@sysoev.ru 
15970Sigor@sysoev.ru     case SSL_ERROR_ZERO_RETURN:
15980Sigor@sysoev.ru         /* A "close notify" alert. */
15990Sigor@sysoev.ru         return 0;
16000Sigor@sysoev.ru 
16010Sigor@sysoev.ru     default: /* SSL_ERROR_SSL, etc. */
16020Sigor@sysoev.ru         c->socket.error = 1000;  /* Nonexistent errno code. */
16030Sigor@sysoev.ru         return NXT_ERROR;
16040Sigor@sysoev.ru     }
16050Sigor@sysoev.ru }
16060Sigor@sysoev.ru 
16070Sigor@sysoev.ru 
16081884Sa.suvorov@f5.com static void
16091884Sa.suvorov@f5.com nxt_openssl_conn_io_shutdown_timeout(nxt_task_t *task, void *obj, void *data)
16101884Sa.suvorov@f5.com {
16111884Sa.suvorov@f5.com     nxt_conn_t   *c;
16121884Sa.suvorov@f5.com     nxt_timer_t  *timer;
16131884Sa.suvorov@f5.com 
16141884Sa.suvorov@f5.com     timer = obj;
16151884Sa.suvorov@f5.com 
16161884Sa.suvorov@f5.com     nxt_debug(task, "openssl conn shutdown timeout");
16171884Sa.suvorov@f5.com 
16181884Sa.suvorov@f5.com     c = nxt_write_timer_conn(timer);
16191884Sa.suvorov@f5.com 
16201884Sa.suvorov@f5.com     c->socket.timedout = 1;
16211884Sa.suvorov@f5.com     nxt_openssl_conn_io_shutdown(task, c, NULL);
16221884Sa.suvorov@f5.com }
16231884Sa.suvorov@f5.com 
16241884Sa.suvorov@f5.com 
16250Sigor@sysoev.ru static void nxt_cdecl
1626771Sigor@sysoev.ru nxt_openssl_conn_error(nxt_task_t *task, nxt_err_t err, const char *fmt, ...)
16270Sigor@sysoev.ru {
16280Sigor@sysoev.ru     u_char      *p, *end;
16290Sigor@sysoev.ru     va_list     args;
16300Sigor@sysoev.ru     nxt_uint_t  level;
16310Sigor@sysoev.ru     u_char      msg[NXT_MAX_ERROR_STR];
16320Sigor@sysoev.ru 
1633771Sigor@sysoev.ru     level = nxt_openssl_log_error_level(err);
16340Sigor@sysoev.ru 
1635771Sigor@sysoev.ru     if (nxt_log_level_enough(task->log, level)) {
16360Sigor@sysoev.ru 
16370Sigor@sysoev.ru         end = msg + sizeof(msg);
16380Sigor@sysoev.ru 
16390Sigor@sysoev.ru         va_start(args, fmt);
16400Sigor@sysoev.ru         p = nxt_vsprintf(msg, end, fmt, args);
16410Sigor@sysoev.ru         va_end(args);
16420Sigor@sysoev.ru 
16430Sigor@sysoev.ru         if (err != 0) {
16440Sigor@sysoev.ru             p = nxt_sprintf(p, end, " %E", err);
16450Sigor@sysoev.ru         }
16460Sigor@sysoev.ru 
16470Sigor@sysoev.ru         p = nxt_openssl_copy_error(p, end);
16480Sigor@sysoev.ru 
1649771Sigor@sysoev.ru         nxt_log(task, level, "%*s", p - msg, msg);
16500Sigor@sysoev.ru 
16510Sigor@sysoev.ru     } else {
16520Sigor@sysoev.ru         ERR_clear_error();
16530Sigor@sysoev.ru     }
16540Sigor@sysoev.ru }
16550Sigor@sysoev.ru 
16560Sigor@sysoev.ru 
16570Sigor@sysoev.ru static nxt_uint_t
1658771Sigor@sysoev.ru nxt_openssl_log_error_level(nxt_err_t err)
16590Sigor@sysoev.ru {
16600Sigor@sysoev.ru     switch (ERR_GET_REASON(ERR_peek_error())) {
16610Sigor@sysoev.ru 
16620Sigor@sysoev.ru     case 0:
166313Sigor@sysoev.ru         return nxt_socket_error_level(err);
16640Sigor@sysoev.ru 
16650Sigor@sysoev.ru     case SSL_R_BAD_CHANGE_CIPHER_SPEC:                    /*  103 */
16660Sigor@sysoev.ru     case SSL_R_BLOCK_CIPHER_PAD_IS_WRONG:                 /*  129 */
16670Sigor@sysoev.ru     case SSL_R_DIGEST_CHECK_FAILED:                       /*  149 */
16680Sigor@sysoev.ru     case SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST:             /*  151 */
16690Sigor@sysoev.ru     case SSL_R_EXCESSIVE_MESSAGE_SIZE:                    /*  152 */
16700Sigor@sysoev.ru     case SSL_R_LENGTH_MISMATCH:                           /*  159 */
1671771Sigor@sysoev.ru #ifdef SSL_R_NO_CIPHERS_PASSED
16720Sigor@sysoev.ru     case SSL_R_NO_CIPHERS_PASSED:                         /*  182 */
1673771Sigor@sysoev.ru #endif
16740Sigor@sysoev.ru     case SSL_R_NO_CIPHERS_SPECIFIED:                      /*  183 */
16750Sigor@sysoev.ru     case SSL_R_NO_COMPRESSION_SPECIFIED:                  /*  187 */
16760Sigor@sysoev.ru     case SSL_R_NO_SHARED_CIPHER:                          /*  193 */
16770Sigor@sysoev.ru     case SSL_R_RECORD_LENGTH_MISMATCH:                    /*  213 */
16780Sigor@sysoev.ru #ifdef SSL_R_PARSE_TLSEXT
16790Sigor@sysoev.ru     case SSL_R_PARSE_TLSEXT:                              /*  227 */
16800Sigor@sysoev.ru #endif
16810Sigor@sysoev.ru     case SSL_R_UNEXPECTED_MESSAGE:                        /*  244 */
16820Sigor@sysoev.ru     case SSL_R_UNEXPECTED_RECORD:                         /*  245 */
16830Sigor@sysoev.ru     case SSL_R_UNKNOWN_ALERT_TYPE:                        /*  246 */
16840Sigor@sysoev.ru     case SSL_R_UNKNOWN_PROTOCOL:                          /*  252 */
16850Sigor@sysoev.ru     case SSL_R_WRONG_VERSION_NUMBER:                      /*  267 */
16860Sigor@sysoev.ru     case SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC:       /*  281 */
16870Sigor@sysoev.ru #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG
16880Sigor@sysoev.ru     case SSL_R_RENEGOTIATE_EXT_TOO_LONG:                  /*  335 */
16890Sigor@sysoev.ru     case SSL_R_RENEGOTIATION_ENCODING_ERR:                /*  336 */
16900Sigor@sysoev.ru     case SSL_R_RENEGOTIATION_MISMATCH:                    /*  337 */
16910Sigor@sysoev.ru #endif
16920Sigor@sysoev.ru #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED
16930Sigor@sysoev.ru     case SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED:      /*  338 */
16940Sigor@sysoev.ru #endif
16950Sigor@sysoev.ru #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING
16960Sigor@sysoev.ru     case SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING:          /*  345 */
16970Sigor@sysoev.ru #endif
16980Sigor@sysoev.ru     case 1000:/* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
16990Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE:            /* 1010 */
17000Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_BAD_RECORD_MAC:                /* 1020 */
17010Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECRYPTION_FAILED:             /* 1021 */
17020Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_RECORD_OVERFLOW:               /* 1022 */
17030Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE:         /* 1030 */
17040Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE:             /* 1040 */
17050Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER:             /* 1047 */
17060Sigor@sysoev.ru         break;
17070Sigor@sysoev.ru 
17080Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_NO_CERTIFICATE:                /* 1041 */
17090Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_BAD_CERTIFICATE:               /* 1042 */
17100Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE:       /* 1043 */
17110Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED:           /* 1044 */
17120Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED:           /* 1045 */
17130Sigor@sysoev.ru     case SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN:           /* 1046 */
17140Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_UNKNOWN_CA:                    /* 1048 */
17150Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_ACCESS_DENIED:                 /* 1049 */
17160Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECODE_ERROR:                  /* 1050 */
17170Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_DECRYPT_ERROR:                 /* 1051 */
17180Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION:            /* 1060 */
17190Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_PROTOCOL_VERSION:              /* 1070 */
17200Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY:         /* 1071 */
17210Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_INTERNAL_ERROR:                /* 1080 */
17220Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_USER_CANCELLED:                /* 1090 */
17230Sigor@sysoev.ru     case SSL_R_TLSV1_ALERT_NO_RENEGOTIATION:              /* 1100 */
17240Sigor@sysoev.ru         return NXT_LOG_ERR;
17250Sigor@sysoev.ru 
17260Sigor@sysoev.ru     default:
1727564Svbart@nginx.com         return NXT_LOG_ALERT;
17280Sigor@sysoev.ru     }
17290Sigor@sysoev.ru 
17300Sigor@sysoev.ru     return NXT_LOG_INFO;
17310Sigor@sysoev.ru }
17320Sigor@sysoev.ru 
17330Sigor@sysoev.ru 
1734771Sigor@sysoev.ru void nxt_cdecl
1735771Sigor@sysoev.ru nxt_openssl_log_error(nxt_task_t *task, nxt_uint_t level, const char *fmt, ...)
17360Sigor@sysoev.ru {
17370Sigor@sysoev.ru     u_char   *p, *end;
17380Sigor@sysoev.ru     va_list  args;
17390Sigor@sysoev.ru     u_char   msg[NXT_MAX_ERROR_STR];
17400Sigor@sysoev.ru 
17410Sigor@sysoev.ru     end = msg + sizeof(msg);
17420Sigor@sysoev.ru 
17430Sigor@sysoev.ru     va_start(args, fmt);
17440Sigor@sysoev.ru     p = nxt_vsprintf(msg, end, fmt, args);
17450Sigor@sysoev.ru     va_end(args);
17460Sigor@sysoev.ru 
17470Sigor@sysoev.ru     p = nxt_openssl_copy_error(p, end);
17480Sigor@sysoev.ru 
1749771Sigor@sysoev.ru     nxt_log(task, level, "%*s", p - msg, msg);
17500Sigor@sysoev.ru }
17510Sigor@sysoev.ru 
17520Sigor@sysoev.ru 
1753771Sigor@sysoev.ru u_char *
17540Sigor@sysoev.ru nxt_openssl_copy_error(u_char *p, u_char *end)
17550Sigor@sysoev.ru {
17560Sigor@sysoev.ru     int         flags;
17570Sigor@sysoev.ru     u_long      err;
17580Sigor@sysoev.ru     nxt_bool_t  clear;
17590Sigor@sysoev.ru     const char  *data, *delimiter;
17600Sigor@sysoev.ru 
17610Sigor@sysoev.ru     err = ERR_peek_error();
17620Sigor@sysoev.ru     if (err == 0) {
17630Sigor@sysoev.ru         return p;
17640Sigor@sysoev.ru     }
17650Sigor@sysoev.ru 
17660Sigor@sysoev.ru     /* Log the most relevant error message ... */
17670Sigor@sysoev.ru     data = ERR_reason_error_string(err);
17680Sigor@sysoev.ru 
17690Sigor@sysoev.ru     p = nxt_sprintf(p, end, " (%d: %s) (OpenSSL: ", ERR_GET_REASON(err), data);
17700Sigor@sysoev.ru 
17710Sigor@sysoev.ru     /*
1772771Sigor@sysoev.ru      * ... followed by all queued cumbersome OpenSSL error messages
1773771Sigor@sysoev.ru      * and drain the error queue.
17740Sigor@sysoev.ru      */
17750Sigor@sysoev.ru     delimiter = "";
17760Sigor@sysoev.ru     clear = 0;
17770Sigor@sysoev.ru 
17780Sigor@sysoev.ru     for ( ;; ) {
17790Sigor@sysoev.ru         err = ERR_get_error_line_data(NULL, NULL, &data, &flags);
17800Sigor@sysoev.ru         if (err == 0) {
17810Sigor@sysoev.ru             break;
17820Sigor@sysoev.ru         }
17830Sigor@sysoev.ru 
17840Sigor@sysoev.ru         p = nxt_sprintf(p, end, "%s", delimiter);
17850Sigor@sysoev.ru 
17860Sigor@sysoev.ru         ERR_error_string_n(err, (char *) p, end - p);
17870Sigor@sysoev.ru 
17880Sigor@sysoev.ru         while (p < end && *p != '\0') {
17890Sigor@sysoev.ru             p++;
17900Sigor@sysoev.ru         }
17910Sigor@sysoev.ru 
17920Sigor@sysoev.ru         if ((flags & ERR_TXT_STRING) != 0) {
17930Sigor@sysoev.ru             p = nxt_sprintf(p, end, ":%s", data);
17940Sigor@sysoev.ru         }
17950Sigor@sysoev.ru 
17960Sigor@sysoev.ru         clear |= ((flags & ERR_TXT_MALLOCED) != 0);
17970Sigor@sysoev.ru 
17980Sigor@sysoev.ru         delimiter = "; ";
17990Sigor@sysoev.ru     }
18000Sigor@sysoev.ru 
18010Sigor@sysoev.ru     /* Deallocate additional data. */
18020Sigor@sysoev.ru 
18030Sigor@sysoev.ru     if (clear) {
18040Sigor@sysoev.ru         ERR_clear_error();
18050Sigor@sysoev.ru     }
18060Sigor@sysoev.ru 
18070Sigor@sysoev.ru     if (p < end) {
18080Sigor@sysoev.ru         *p++ = ')';
18090Sigor@sysoev.ru     }
18100Sigor@sysoev.ru 
18110Sigor@sysoev.ru     return p;
18120Sigor@sysoev.ru }
18131942Sa.suvorov@f5.com 
18141942Sa.suvorov@f5.com 
18151942Sa.suvorov@f5.com nxt_int_t
18161942Sa.suvorov@f5.com nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, size_t slen)
18171942Sa.suvorov@f5.com {
18181942Sa.suvorov@f5.com     BIO        *bio, *b64;
18191942Sa.suvorov@f5.com     nxt_int_t  count, ret;
18201942Sa.suvorov@f5.com     u_char     buf[128];
18211942Sa.suvorov@f5.com 
18221942Sa.suvorov@f5.com     b64 = BIO_new(BIO_f_base64());
18231942Sa.suvorov@f5.com     if (nxt_slow_path(b64 == NULL)) {
18241942Sa.suvorov@f5.com         goto error;
18251942Sa.suvorov@f5.com     }
18261942Sa.suvorov@f5.com 
18271942Sa.suvorov@f5.com     bio = BIO_new_mem_buf(s, slen);
18281942Sa.suvorov@f5.com     if (nxt_slow_path(bio == NULL)) {
18291942Sa.suvorov@f5.com         goto error;
18301942Sa.suvorov@f5.com     }
18311942Sa.suvorov@f5.com 
18321942Sa.suvorov@f5.com     bio = BIO_push(b64, bio);
18331942Sa.suvorov@f5.com 
18341942Sa.suvorov@f5.com     BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
18351942Sa.suvorov@f5.com 
18361942Sa.suvorov@f5.com     count = 0;
18371942Sa.suvorov@f5.com 
18381942Sa.suvorov@f5.com     if (d == NULL) {
18391942Sa.suvorov@f5.com 
18401942Sa.suvorov@f5.com         for ( ;; ) {
18411942Sa.suvorov@f5.com             ret = BIO_read(bio, buf, 128);
18421942Sa.suvorov@f5.com 
18431942Sa.suvorov@f5.com             if (ret < 0) {
18441942Sa.suvorov@f5.com                 goto invalid;
18451942Sa.suvorov@f5.com             }
18461942Sa.suvorov@f5.com 
18471942Sa.suvorov@f5.com             count += ret;
18481942Sa.suvorov@f5.com 
18491942Sa.suvorov@f5.com             if (ret != 128) {
18501942Sa.suvorov@f5.com                 break;
18511942Sa.suvorov@f5.com             }
18521942Sa.suvorov@f5.com         }
18531942Sa.suvorov@f5.com 
18541942Sa.suvorov@f5.com     } else {
18551942Sa.suvorov@f5.com         count = BIO_read(bio, d, dlen);
18561942Sa.suvorov@f5.com 
18571942Sa.suvorov@f5.com         if (count < 0) {
18581942Sa.suvorov@f5.com             goto invalid;
18591942Sa.suvorov@f5.com         }
18601942Sa.suvorov@f5.com     }
18611942Sa.suvorov@f5.com 
18621942Sa.suvorov@f5.com     BIO_free_all(bio);
18631942Sa.suvorov@f5.com 
18641942Sa.suvorov@f5.com     return count;
18651942Sa.suvorov@f5.com 
18661942Sa.suvorov@f5.com error:
18671942Sa.suvorov@f5.com 
18681942Sa.suvorov@f5.com     BIO_vfree(b64);
18691942Sa.suvorov@f5.com     ERR_clear_error();
18701942Sa.suvorov@f5.com 
18711942Sa.suvorov@f5.com     return NXT_ERROR;
18721942Sa.suvorov@f5.com 
18731942Sa.suvorov@f5.com invalid:
18741942Sa.suvorov@f5.com 
18751942Sa.suvorov@f5.com     BIO_free_all(bio);
18761942Sa.suvorov@f5.com     ERR_clear_error();
18771942Sa.suvorov@f5.com 
18781942Sa.suvorov@f5.com     return NXT_DECLINED;
18791942Sa.suvorov@f5.com }
1880