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