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 <gnutls/gnutls.h> 90Sigor@sysoev.ru 100Sigor@sysoev.ru 110Sigor@sysoev.ru typedef struct { 120Sigor@sysoev.ru gnutls_session_t session; 130Sigor@sysoev.ru 140Sigor@sysoev.ru uint8_t times; /* 2 bits */ 150Sigor@sysoev.ru uint8_t no_shutdown; /* 1 bit */ 160Sigor@sysoev.ru 170Sigor@sysoev.ru nxt_buf_mem_t buffer; 180Sigor@sysoev.ru } nxt_gnutls_conn_t; 190Sigor@sysoev.ru 200Sigor@sysoev.ru 210Sigor@sysoev.ru typedef struct { 220Sigor@sysoev.ru gnutls_priority_t ciphers; 230Sigor@sysoev.ru gnutls_certificate_credentials_t certificate; 240Sigor@sysoev.ru } nxt_gnutls_ctx_t; 250Sigor@sysoev.ru 260Sigor@sysoev.ru 270Sigor@sysoev.ru 280Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME) 290Sigor@sysoev.ru time_t nxt_gnutls_time(time_t *tp); 300Sigor@sysoev.ru #endif 310Sigor@sysoev.ru static nxt_int_t nxt_gnutls_server_init(nxt_ssltls_conf_t *conf); 320Sigor@sysoev.ru static nxt_int_t nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf); 330Sigor@sysoev.ru 340Sigor@sysoev.ru static void nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf, 350Sigor@sysoev.ru nxt_event_conn_t *c); 360Sigor@sysoev.ru static void nxt_gnutls_session_cleanup(void *data); 370Sigor@sysoev.ru static ssize_t nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf, 380Sigor@sysoev.ru size_t size); 390Sigor@sysoev.ru static ssize_t nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf, 400Sigor@sysoev.ru size_t size); 410Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH) 420Sigor@sysoev.ru static ssize_t nxt_gnutls_vec_push(gnutls_transport_ptr_t data, 430Sigor@sysoev.ru const giovec_t *iov, int iovcnt); 440Sigor@sysoev.ru #endif 450Sigor@sysoev.ru static void nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data); 460Sigor@sysoev.ru static void nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data); 470Sigor@sysoev.ru static ssize_t nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr, 480Sigor@sysoev.ru nxt_event_conn_t *c, nxt_buf_t *b, size_t limit); 490Sigor@sysoev.ru static ssize_t nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf, 500Sigor@sysoev.ru size_t size); 510Sigor@sysoev.ru static void nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj, 520Sigor@sysoev.ru void *data); 530Sigor@sysoev.ru static nxt_int_t nxt_gnutls_conn_test_error(nxt_thread_t *thr, 540Sigor@sysoev.ru nxt_event_conn_t *c, ssize_t err, nxt_work_handler_t handler); 550Sigor@sysoev.ru static void nxt_cdecl nxt_gnutls_conn_log_error(nxt_event_conn_t *c, 560Sigor@sysoev.ru ssize_t err, const char *fmt, ...); 570Sigor@sysoev.ru static nxt_uint_t nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err); 580Sigor@sysoev.ru static void nxt_cdecl nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log, 590Sigor@sysoev.ru int err, const char *fmt, ...); 600Sigor@sysoev.ru 610Sigor@sysoev.ru 620Sigor@sysoev.ru const nxt_ssltls_lib_t nxt_gnutls_lib = { 630Sigor@sysoev.ru nxt_gnutls_server_init, 640Sigor@sysoev.ru NULL, 650Sigor@sysoev.ru }; 660Sigor@sysoev.ru 670Sigor@sysoev.ru 680Sigor@sysoev.ru static nxt_event_conn_io_t nxt_gnutls_event_conn_io = { 690Sigor@sysoev.ru NULL, 700Sigor@sysoev.ru NULL, 710Sigor@sysoev.ru 720Sigor@sysoev.ru nxt_gnutls_conn_io_read, 730Sigor@sysoev.ru NULL, 740Sigor@sysoev.ru NULL, 750Sigor@sysoev.ru 760Sigor@sysoev.ru nxt_event_conn_io_write, 770Sigor@sysoev.ru nxt_gnutls_conn_io_write_chunk, 780Sigor@sysoev.ru NULL, 790Sigor@sysoev.ru NULL, 800Sigor@sysoev.ru nxt_gnutls_conn_io_send, 810Sigor@sysoev.ru 820Sigor@sysoev.ru nxt_gnutls_conn_io_shutdown, 830Sigor@sysoev.ru }; 840Sigor@sysoev.ru 850Sigor@sysoev.ru 860Sigor@sysoev.ru static nxt_int_t 870Sigor@sysoev.ru nxt_gnutls_start(void) 880Sigor@sysoev.ru { 890Sigor@sysoev.ru int ret; 900Sigor@sysoev.ru static nxt_bool_t started; 910Sigor@sysoev.ru 920Sigor@sysoev.ru if (nxt_fast_path(started)) { 930Sigor@sysoev.ru return NXT_OK; 940Sigor@sysoev.ru } 950Sigor@sysoev.ru 960Sigor@sysoev.ru started = 1; 970Sigor@sysoev.ru 980Sigor@sysoev.ru /* TODO: gnutls_global_deinit */ 990Sigor@sysoev.ru 1000Sigor@sysoev.ru ret = gnutls_global_init(); 1010Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) { 102*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, nxt_thread_log(), ret, 1030Sigor@sysoev.ru "gnutls_global_init() failed"); 1040Sigor@sysoev.ru return NXT_ERROR; 1050Sigor@sysoev.ru } 1060Sigor@sysoev.ru 1070Sigor@sysoev.ru nxt_thread_log_error(NXT_LOG_INFO, "GnuTLS version: %s", 1080Sigor@sysoev.ru gnutls_check_version(NULL)); 1090Sigor@sysoev.ru 1100Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME) 1110Sigor@sysoev.ru gnutls_global_set_time_function(nxt_gnutls_time); 1120Sigor@sysoev.ru #endif 1130Sigor@sysoev.ru 1140Sigor@sysoev.ru return NXT_OK; 1150Sigor@sysoev.ru } 1160Sigor@sysoev.ru 1170Sigor@sysoev.ru 1180Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_SET_TIME) 1190Sigor@sysoev.ru 1200Sigor@sysoev.ru /* GnuTLS 2.12.0 */ 1210Sigor@sysoev.ru 1220Sigor@sysoev.ru time_t 1230Sigor@sysoev.ru nxt_gnutls_time(time_t *tp) 1240Sigor@sysoev.ru { 1250Sigor@sysoev.ru time_t t; 1260Sigor@sysoev.ru nxt_thread_t *thr; 1270Sigor@sysoev.ru 1280Sigor@sysoev.ru thr = nxt_thread(); 1290Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls time"); 1300Sigor@sysoev.ru 1310Sigor@sysoev.ru t = (time_t) nxt_thread_time(thr); 1320Sigor@sysoev.ru 1330Sigor@sysoev.ru if (tp != NULL) { 1340Sigor@sysoev.ru *tp = t; 1350Sigor@sysoev.ru } 1360Sigor@sysoev.ru 1370Sigor@sysoev.ru return t; 1380Sigor@sysoev.ru } 1390Sigor@sysoev.ru 1400Sigor@sysoev.ru #endif 1410Sigor@sysoev.ru 1420Sigor@sysoev.ru 1430Sigor@sysoev.ru static nxt_int_t 1440Sigor@sysoev.ru nxt_gnutls_server_init(nxt_ssltls_conf_t *conf) 1450Sigor@sysoev.ru { 1460Sigor@sysoev.ru int ret; 1470Sigor@sysoev.ru char *certificate, *key, *ca_certificate; 1480Sigor@sysoev.ru nxt_thread_t *thr; 1490Sigor@sysoev.ru nxt_gnutls_ctx_t *ctx; 1500Sigor@sysoev.ru 1510Sigor@sysoev.ru if (nxt_slow_path(nxt_gnutls_start() != NXT_OK)) { 1520Sigor@sysoev.ru return NXT_ERROR; 1530Sigor@sysoev.ru } 1540Sigor@sysoev.ru 1550Sigor@sysoev.ru /* TODO: mem_pool, cleanup: gnutls_certificate_free_credentials, 1560Sigor@sysoev.ru gnutls_priority_deinit */ 1570Sigor@sysoev.ru 1580Sigor@sysoev.ru ctx = nxt_zalloc(sizeof(nxt_gnutls_ctx_t)); 1590Sigor@sysoev.ru if (ctx == NULL) { 1600Sigor@sysoev.ru return NXT_ERROR; 1610Sigor@sysoev.ru } 1620Sigor@sysoev.ru 1630Sigor@sysoev.ru conf->ctx = ctx; 1640Sigor@sysoev.ru conf->conn_init = nxt_gnutls_conn_init; 1650Sigor@sysoev.ru 1660Sigor@sysoev.ru thr = nxt_thread(); 1670Sigor@sysoev.ru 1680Sigor@sysoev.ru ret = gnutls_certificate_allocate_credentials(&ctx->certificate); 1690Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) { 170*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, thr->log, ret, 1710Sigor@sysoev.ru "gnutls_certificate_allocate_credentials() failed"); 1720Sigor@sysoev.ru return NXT_ERROR; 1730Sigor@sysoev.ru } 1740Sigor@sysoev.ru 1750Sigor@sysoev.ru certificate = conf->certificate; 1760Sigor@sysoev.ru key = conf->certificate_key; 1770Sigor@sysoev.ru 1780Sigor@sysoev.ru ret = gnutls_certificate_set_x509_key_file(ctx->certificate, certificate, 1790Sigor@sysoev.ru key, GNUTLS_X509_FMT_PEM); 1800Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) { 181*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, thr->log, ret, 1820Sigor@sysoev.ru "gnutls_certificate_set_x509_key_file(\"%s\", \"%s\") failed", 1830Sigor@sysoev.ru certificate, key); 1840Sigor@sysoev.ru goto certificate_fail; 1850Sigor@sysoev.ru } 1860Sigor@sysoev.ru 1870Sigor@sysoev.ru if (nxt_gnutls_set_ciphers(conf) != NXT_OK) { 1880Sigor@sysoev.ru goto ciphers_fail; 1890Sigor@sysoev.ru } 1900Sigor@sysoev.ru 1910Sigor@sysoev.ru if (conf->ca_certificate != NULL) { 1920Sigor@sysoev.ru ca_certificate = conf->ca_certificate; 1930Sigor@sysoev.ru 1940Sigor@sysoev.ru ret = gnutls_certificate_set_x509_trust_file(ctx->certificate, 1950Sigor@sysoev.ru ca_certificate, 1960Sigor@sysoev.ru GNUTLS_X509_FMT_PEM); 1970Sigor@sysoev.ru if (ret < 0) { 198*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, thr->log, ret, 1990Sigor@sysoev.ru "gnutls_certificate_set_x509_trust_file(\"%s\") failed", 2000Sigor@sysoev.ru ca_certificate); 2010Sigor@sysoev.ru goto ca_certificate_fail; 2020Sigor@sysoev.ru } 2030Sigor@sysoev.ru } 2040Sigor@sysoev.ru 2050Sigor@sysoev.ru return NXT_OK; 2060Sigor@sysoev.ru 2070Sigor@sysoev.ru ca_certificate_fail: 2080Sigor@sysoev.ru 2090Sigor@sysoev.ru gnutls_priority_deinit(ctx->ciphers); 2100Sigor@sysoev.ru 2110Sigor@sysoev.ru ciphers_fail: 2120Sigor@sysoev.ru 2130Sigor@sysoev.ru certificate_fail: 2140Sigor@sysoev.ru 2150Sigor@sysoev.ru gnutls_certificate_free_credentials(ctx->certificate); 2160Sigor@sysoev.ru 2170Sigor@sysoev.ru return NXT_ERROR; 2180Sigor@sysoev.ru } 2190Sigor@sysoev.ru 2200Sigor@sysoev.ru 2210Sigor@sysoev.ru static nxt_int_t 2220Sigor@sysoev.ru nxt_gnutls_set_ciphers(nxt_ssltls_conf_t *conf) 2230Sigor@sysoev.ru { 2240Sigor@sysoev.ru int ret; 2250Sigor@sysoev.ru const char *ciphers; 2260Sigor@sysoev.ru const char *err; 2270Sigor@sysoev.ru nxt_gnutls_ctx_t *ctx; 2280Sigor@sysoev.ru 2290Sigor@sysoev.ru ciphers = (conf->ciphers != NULL) ? conf->ciphers : "NORMAL:!COMP-DEFLATE"; 2300Sigor@sysoev.ru ctx = conf->ctx; 2310Sigor@sysoev.ru 2320Sigor@sysoev.ru ret = gnutls_priority_init(&ctx->ciphers, ciphers, &err); 2330Sigor@sysoev.ru 2340Sigor@sysoev.ru switch (ret) { 2350Sigor@sysoev.ru 2360Sigor@sysoev.ru case GNUTLS_E_SUCCESS: 2370Sigor@sysoev.ru return NXT_OK; 2380Sigor@sysoev.ru 2390Sigor@sysoev.ru case GNUTLS_E_INVALID_REQUEST: 240*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, nxt_thread_log(), ret, 2410Sigor@sysoev.ru "gnutls_priority_init(\"%s\") failed at \"%s\"", 2420Sigor@sysoev.ru ciphers, err); 2430Sigor@sysoev.ru return NXT_ERROR; 2440Sigor@sysoev.ru 2450Sigor@sysoev.ru default: 246*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, nxt_thread_log(), ret, 2470Sigor@sysoev.ru "gnutls_priority_init() failed"); 2480Sigor@sysoev.ru return NXT_ERROR; 2490Sigor@sysoev.ru } 2500Sigor@sysoev.ru } 2510Sigor@sysoev.ru 2520Sigor@sysoev.ru 2530Sigor@sysoev.ru static void 2540Sigor@sysoev.ru nxt_gnutls_conn_init(nxt_thread_t *thr, nxt_ssltls_conf_t *conf, 2550Sigor@sysoev.ru nxt_event_conn_t *c) 2560Sigor@sysoev.ru { 2570Sigor@sysoev.ru int ret; 2580Sigor@sysoev.ru gnutls_session_t sess; 2590Sigor@sysoev.ru nxt_gnutls_ctx_t *ctx; 2600Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 2610Sigor@sysoev.ru nxt_mem_pool_cleanup_t *mpcl; 2620Sigor@sysoev.ru 2630Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls conn init"); 2640Sigor@sysoev.ru 26565Sigor@sysoev.ru ssltls = nxt_mp_zget(c->mem_pool, sizeof(nxt_gnutls_conn_t)); 2660Sigor@sysoev.ru if (ssltls == NULL) { 2670Sigor@sysoev.ru goto fail; 2680Sigor@sysoev.ru } 2690Sigor@sysoev.ru 2700Sigor@sysoev.ru c->u.ssltls = ssltls; 2710Sigor@sysoev.ru nxt_buf_mem_set_size(&ssltls->buffer, conf->buffer_size); 2720Sigor@sysoev.ru 2730Sigor@sysoev.ru mpcl = nxt_mem_pool_cleanup(c->mem_pool, 0); 2740Sigor@sysoev.ru if (mpcl == NULL) { 2750Sigor@sysoev.ru goto fail; 2760Sigor@sysoev.ru } 2770Sigor@sysoev.ru 2780Sigor@sysoev.ru ret = gnutls_init(&ssltls->session, GNUTLS_SERVER); 2790Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) { 280*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, c->socket.log, ret, 2810Sigor@sysoev.ru "gnutls_init() failed"); 2820Sigor@sysoev.ru goto fail; 2830Sigor@sysoev.ru } 2840Sigor@sysoev.ru 2850Sigor@sysoev.ru sess = ssltls->session; 2860Sigor@sysoev.ru mpcl->handler = nxt_gnutls_session_cleanup; 2870Sigor@sysoev.ru mpcl->data = ssltls; 2880Sigor@sysoev.ru 2890Sigor@sysoev.ru ctx = conf->ctx; 2900Sigor@sysoev.ru 2910Sigor@sysoev.ru ret = gnutls_priority_set(sess, ctx->ciphers); 2920Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) { 293*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, c->socket.log, ret, 2940Sigor@sysoev.ru "gnutls_priority_set() failed"); 2950Sigor@sysoev.ru goto fail; 2960Sigor@sysoev.ru } 2970Sigor@sysoev.ru 2980Sigor@sysoev.ru /* 2990Sigor@sysoev.ru * Disable TLS random padding of records in CBC ciphers, 3000Sigor@sysoev.ru * which may be up to 255 bytes. 3010Sigor@sysoev.ru */ 3020Sigor@sysoev.ru gnutls_record_disable_padding(sess); 3030Sigor@sysoev.ru 3040Sigor@sysoev.ru ret = gnutls_credentials_set(sess, GNUTLS_CRD_CERTIFICATE, 3050Sigor@sysoev.ru ctx->certificate); 3060Sigor@sysoev.ru if (ret != GNUTLS_E_SUCCESS) { 307*564Svbart@nginx.com nxt_gnutls_log_error(NXT_LOG_ALERT, c->socket.log, ret, 3080Sigor@sysoev.ru "gnutls_credentials_set() failed"); 3090Sigor@sysoev.ru goto fail; 3100Sigor@sysoev.ru } 3110Sigor@sysoev.ru 3120Sigor@sysoev.ru if (conf->ca_certificate != NULL) { 3130Sigor@sysoev.ru gnutls_certificate_server_set_request(sess, GNUTLS_CERT_REQUEST); 3140Sigor@sysoev.ru } 3150Sigor@sysoev.ru 3160Sigor@sysoev.ru gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t) c); 3170Sigor@sysoev.ru gnutls_transport_set_pull_function(sess, nxt_gnutls_pull); 3180Sigor@sysoev.ru gnutls_transport_set_push_function(sess, nxt_gnutls_push); 3190Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH) 3200Sigor@sysoev.ru gnutls_transport_set_vec_push_function(sess, nxt_gnutls_vec_push); 3210Sigor@sysoev.ru #endif 3220Sigor@sysoev.ru 3230Sigor@sysoev.ru c->io = &nxt_gnutls_event_conn_io; 3240Sigor@sysoev.ru c->sendfile = NXT_CONN_SENDFILE_OFF; 3250Sigor@sysoev.ru 3260Sigor@sysoev.ru nxt_gnutls_conn_handshake(thr, c, c->socket.data); 3270Sigor@sysoev.ru return; 3280Sigor@sysoev.ru 3290Sigor@sysoev.ru fail: 3300Sigor@sysoev.ru 3310Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->read_work_queue, 3320Sigor@sysoev.ru c->read_state->error_handler, c, c->socket.data); 3330Sigor@sysoev.ru } 3340Sigor@sysoev.ru 3350Sigor@sysoev.ru 3360Sigor@sysoev.ru static void 3370Sigor@sysoev.ru nxt_gnutls_session_cleanup(void *data) 3380Sigor@sysoev.ru { 3390Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 3400Sigor@sysoev.ru 3410Sigor@sysoev.ru ssltls = data; 3420Sigor@sysoev.ru 3430Sigor@sysoev.ru nxt_thread_log_debug("gnutls session cleanup"); 3440Sigor@sysoev.ru 3450Sigor@sysoev.ru nxt_free(ssltls->buffer.start); 3460Sigor@sysoev.ru 3470Sigor@sysoev.ru gnutls_deinit(ssltls->session); 3480Sigor@sysoev.ru } 3490Sigor@sysoev.ru 3500Sigor@sysoev.ru 3510Sigor@sysoev.ru static ssize_t 3520Sigor@sysoev.ru nxt_gnutls_pull(gnutls_transport_ptr_t data, void *buf, size_t size) 3530Sigor@sysoev.ru { 3540Sigor@sysoev.ru ssize_t n; 3550Sigor@sysoev.ru nxt_thread_t *thr; 3560Sigor@sysoev.ru nxt_event_conn_t *c; 3570Sigor@sysoev.ru 3580Sigor@sysoev.ru c = data; 3590Sigor@sysoev.ru thr = nxt_thread(); 3600Sigor@sysoev.ru 3610Sigor@sysoev.ru n = thr->engine->event->io->recv(c, buf, size, 0); 3620Sigor@sysoev.ru 3630Sigor@sysoev.ru if (n == NXT_AGAIN) { 3640Sigor@sysoev.ru nxt_set_errno(NXT_EAGAIN); 3650Sigor@sysoev.ru return -1; 3660Sigor@sysoev.ru } 3670Sigor@sysoev.ru 3680Sigor@sysoev.ru return n; 3690Sigor@sysoev.ru } 3700Sigor@sysoev.ru 3710Sigor@sysoev.ru 3720Sigor@sysoev.ru static ssize_t 3730Sigor@sysoev.ru nxt_gnutls_push(gnutls_transport_ptr_t data, const void *buf, size_t size) 3740Sigor@sysoev.ru { 3750Sigor@sysoev.ru ssize_t n; 3760Sigor@sysoev.ru nxt_thread_t *thr; 3770Sigor@sysoev.ru nxt_event_conn_t *c; 3780Sigor@sysoev.ru 3790Sigor@sysoev.ru c = data; 3800Sigor@sysoev.ru thr = nxt_thread(); 3810Sigor@sysoev.ru 3820Sigor@sysoev.ru n = thr->engine->event->io->send(c, (u_char *) buf, size); 3830Sigor@sysoev.ru 3840Sigor@sysoev.ru if (n == NXT_AGAIN) { 3850Sigor@sysoev.ru nxt_set_errno(NXT_EAGAIN); 3860Sigor@sysoev.ru return -1; 3870Sigor@sysoev.ru } 3880Sigor@sysoev.ru 3890Sigor@sysoev.ru return n; 3900Sigor@sysoev.ru } 3910Sigor@sysoev.ru 3920Sigor@sysoev.ru 3930Sigor@sysoev.ru #if (NXT_HAVE_GNUTLS_VEC_PUSH) 3940Sigor@sysoev.ru 3950Sigor@sysoev.ru /* GnuTLS 2.12.0 */ 3960Sigor@sysoev.ru 3970Sigor@sysoev.ru static ssize_t 3980Sigor@sysoev.ru nxt_gnutls_vec_push(gnutls_transport_ptr_t data, const giovec_t *iov, 3990Sigor@sysoev.ru int iovcnt) 4000Sigor@sysoev.ru { 4010Sigor@sysoev.ru ssize_t n; 4020Sigor@sysoev.ru nxt_thread_t *thr; 4030Sigor@sysoev.ru nxt_event_conn_t *c; 4040Sigor@sysoev.ru 4050Sigor@sysoev.ru c = data; 4060Sigor@sysoev.ru thr = nxt_thread(); 4070Sigor@sysoev.ru 4080Sigor@sysoev.ru /* 4090Sigor@sysoev.ru * This code assumes that giovec_t is the same as "struct iovec" 4100Sigor@sysoev.ru * and nxt_iobuf_t. It is not true for Windows. 4110Sigor@sysoev.ru */ 4120Sigor@sysoev.ru n = thr->engine->event->io->writev(c, (nxt_iobuf_t *) iov, iovcnt); 4130Sigor@sysoev.ru 4140Sigor@sysoev.ru if (n == NXT_AGAIN) { 4150Sigor@sysoev.ru nxt_set_errno(NXT_EAGAIN); 4160Sigor@sysoev.ru return -1; 4170Sigor@sysoev.ru } 4180Sigor@sysoev.ru 4190Sigor@sysoev.ru return n; 4200Sigor@sysoev.ru } 4210Sigor@sysoev.ru 4220Sigor@sysoev.ru #endif 4230Sigor@sysoev.ru 4240Sigor@sysoev.ru 4250Sigor@sysoev.ru static void 4260Sigor@sysoev.ru nxt_gnutls_conn_handshake(nxt_thread_t *thr, void *obj, void *data) 4270Sigor@sysoev.ru { 4280Sigor@sysoev.ru int err; 4290Sigor@sysoev.ru nxt_int_t ret; 4300Sigor@sysoev.ru nxt_event_conn_t *c; 4310Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 4320Sigor@sysoev.ru 4330Sigor@sysoev.ru c = obj; 4340Sigor@sysoev.ru ssltls = c->u.ssltls; 4350Sigor@sysoev.ru 4360Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls conn handshake: %d", ssltls->times); 4370Sigor@sysoev.ru 4380Sigor@sysoev.ru /* "ssltls->times == 1" is suitable to run gnutls_handshake() in job. */ 4390Sigor@sysoev.ru 4400Sigor@sysoev.ru err = gnutls_handshake(ssltls->session); 4410Sigor@sysoev.ru 4420Sigor@sysoev.ru nxt_thread_time_debug_update(thr); 4430Sigor@sysoev.ru 4440Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls_handshake(): %d", err); 4450Sigor@sysoev.ru 4460Sigor@sysoev.ru if (err == GNUTLS_E_SUCCESS) { 4470Sigor@sysoev.ru nxt_gnutls_conn_io_read(thr, c, data); 4480Sigor@sysoev.ru return; 4490Sigor@sysoev.ru } 4500Sigor@sysoev.ru 4510Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(thr, c, err, nxt_gnutls_conn_handshake); 4520Sigor@sysoev.ru 4530Sigor@sysoev.ru if (ret == NXT_ERROR) { 4540Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, err, "gnutls_handshake() failed"); 4550Sigor@sysoev.ru 4560Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->read_work_queue, 4570Sigor@sysoev.ru c->read_state->error_handler, c, data); 4580Sigor@sysoev.ru 4590Sigor@sysoev.ru } else if (err == GNUTLS_E_AGAIN 4600Sigor@sysoev.ru && ssltls->times < 2 4610Sigor@sysoev.ru && gnutls_record_get_direction(ssltls->session) == 0) 4620Sigor@sysoev.ru { 4630Sigor@sysoev.ru ssltls->times++; 4640Sigor@sysoev.ru } 4650Sigor@sysoev.ru } 4660Sigor@sysoev.ru 4670Sigor@sysoev.ru 4680Sigor@sysoev.ru static void 4690Sigor@sysoev.ru nxt_gnutls_conn_io_read(nxt_thread_t *thr, void *obj, void *data) 4700Sigor@sysoev.ru { 4710Sigor@sysoev.ru ssize_t n; 4720Sigor@sysoev.ru nxt_buf_t *b; 4730Sigor@sysoev.ru nxt_int_t ret; 4740Sigor@sysoev.ru nxt_event_conn_t *c; 4750Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 4760Sigor@sysoev.ru nxt_work_handler_t handler; 4770Sigor@sysoev.ru 4780Sigor@sysoev.ru c = obj; 4790Sigor@sysoev.ru 4800Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls conn read"); 4810Sigor@sysoev.ru 4820Sigor@sysoev.ru handler = c->read_state->ready_handler; 4830Sigor@sysoev.ru b = c->read; 4840Sigor@sysoev.ru 4850Sigor@sysoev.ru /* b == NULL is used to test descriptor readiness. */ 4860Sigor@sysoev.ru 4870Sigor@sysoev.ru if (b != NULL) { 4880Sigor@sysoev.ru ssltls = c->u.ssltls; 4890Sigor@sysoev.ru 4900Sigor@sysoev.ru n = gnutls_record_recv(ssltls->session, b->mem.free, 4910Sigor@sysoev.ru b->mem.end - b->mem.free); 4920Sigor@sysoev.ru 4930Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls_record_recv(%d, %p, %uz): %z", 4940Sigor@sysoev.ru c->socket.fd, b->mem.free, b->mem.end - b->mem.free, n); 4950Sigor@sysoev.ru 4960Sigor@sysoev.ru if (n > 0) { 4970Sigor@sysoev.ru /* c->socket.read_ready is kept. */ 4980Sigor@sysoev.ru b->mem.free += n; 4990Sigor@sysoev.ru handler = c->read_state->ready_handler; 5000Sigor@sysoev.ru 5010Sigor@sysoev.ru } else if (n == 0) { 5020Sigor@sysoev.ru handler = c->read_state->close_handler; 5030Sigor@sysoev.ru 5040Sigor@sysoev.ru } else { 5050Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(thr, c, n, 5060Sigor@sysoev.ru nxt_gnutls_conn_io_read); 5070Sigor@sysoev.ru 5080Sigor@sysoev.ru if (nxt_fast_path(ret != NXT_ERROR)) { 5090Sigor@sysoev.ru return; 5100Sigor@sysoev.ru } 5110Sigor@sysoev.ru 5120Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, n, 5130Sigor@sysoev.ru "gnutls_record_recv(%d, %p, %uz): failed", 5140Sigor@sysoev.ru c->socket.fd, b->mem.free, 5150Sigor@sysoev.ru b->mem.end - b->mem.free); 5160Sigor@sysoev.ru 5170Sigor@sysoev.ru handler = c->read_state->error_handler; 5180Sigor@sysoev.ru } 5190Sigor@sysoev.ru } 5200Sigor@sysoev.ru 5210Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->read_work_queue, handler, c, data); 5220Sigor@sysoev.ru } 5230Sigor@sysoev.ru 5240Sigor@sysoev.ru 5250Sigor@sysoev.ru static ssize_t 5260Sigor@sysoev.ru nxt_gnutls_conn_io_write_chunk(nxt_thread_t *thr, nxt_event_conn_t *c, 5270Sigor@sysoev.ru nxt_buf_t *b, size_t limit) 5280Sigor@sysoev.ru { 5290Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 5300Sigor@sysoev.ru 5310Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls conn write chunk"); 5320Sigor@sysoev.ru 5330Sigor@sysoev.ru ssltls = c->u.ssltls; 5340Sigor@sysoev.ru 5350Sigor@sysoev.ru return nxt_sendbuf_copy_coalesce(c, &ssltls->buffer, b, limit); 5360Sigor@sysoev.ru } 5370Sigor@sysoev.ru 5380Sigor@sysoev.ru 5390Sigor@sysoev.ru static ssize_t 5400Sigor@sysoev.ru nxt_gnutls_conn_io_send(nxt_event_conn_t *c, void *buf, size_t size) 5410Sigor@sysoev.ru { 5420Sigor@sysoev.ru ssize_t n; 5430Sigor@sysoev.ru nxt_int_t ret; 5440Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 5450Sigor@sysoev.ru 5460Sigor@sysoev.ru ssltls = c->u.ssltls; 5470Sigor@sysoev.ru 5480Sigor@sysoev.ru n = gnutls_record_send(ssltls->session, buf, size); 5490Sigor@sysoev.ru 5500Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls_record_send(%d, %p, %uz): %z", 5510Sigor@sysoev.ru c->socket.fd, buf, size, n); 5520Sigor@sysoev.ru 5530Sigor@sysoev.ru if (n > 0) { 5540Sigor@sysoev.ru return n; 5550Sigor@sysoev.ru } 5560Sigor@sysoev.ru 5570Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(nxt_thread(), c, n, 5580Sigor@sysoev.ru nxt_event_conn_io_write); 5590Sigor@sysoev.ru 5600Sigor@sysoev.ru if (nxt_slow_path(ret == NXT_ERROR)) { 5610Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, n, 5620Sigor@sysoev.ru "gnutls_record_send(%d, %p, %uz): failed", 5630Sigor@sysoev.ru c->socket.fd, buf, size); 5640Sigor@sysoev.ru } 5650Sigor@sysoev.ru 5660Sigor@sysoev.ru return ret; 5670Sigor@sysoev.ru } 5680Sigor@sysoev.ru 5690Sigor@sysoev.ru 5700Sigor@sysoev.ru static void 5710Sigor@sysoev.ru nxt_gnutls_conn_io_shutdown(nxt_thread_t *thr, void *obj, void *data) 5720Sigor@sysoev.ru { 5730Sigor@sysoev.ru int err; 5740Sigor@sysoev.ru nxt_int_t ret; 5750Sigor@sysoev.ru nxt_event_conn_t *c; 5760Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 5770Sigor@sysoev.ru nxt_work_handler_t handler; 5780Sigor@sysoev.ru gnutls_close_request_t how; 5790Sigor@sysoev.ru 5800Sigor@sysoev.ru c = obj; 5810Sigor@sysoev.ru ssltls = c->u.ssltls; 5820Sigor@sysoev.ru 5830Sigor@sysoev.ru if (ssltls->session == NULL || ssltls->no_shutdown) { 5840Sigor@sysoev.ru handler = c->write_state->close_handler; 5850Sigor@sysoev.ru goto done; 5860Sigor@sysoev.ru } 5870Sigor@sysoev.ru 5880Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls conn shutdown"); 5890Sigor@sysoev.ru 5900Sigor@sysoev.ru if (c->socket.timedout || c->socket.error != 0) { 5910Sigor@sysoev.ru how = GNUTLS_SHUT_WR; 5920Sigor@sysoev.ru 5930Sigor@sysoev.ru } else if (c->socket.closed) { 5940Sigor@sysoev.ru how = GNUTLS_SHUT_RDWR; 5950Sigor@sysoev.ru 5960Sigor@sysoev.ru } else { 5970Sigor@sysoev.ru how = GNUTLS_SHUT_RDWR; 5980Sigor@sysoev.ru } 5990Sigor@sysoev.ru 6000Sigor@sysoev.ru err = gnutls_bye(ssltls->session, how); 6010Sigor@sysoev.ru 6020Sigor@sysoev.ru nxt_log_debug(c->socket.log, "gnutls_bye(%d, %d): %d", 6030Sigor@sysoev.ru c->socket.fd, how, err); 6040Sigor@sysoev.ru 6050Sigor@sysoev.ru if (err == GNUTLS_E_SUCCESS) { 6060Sigor@sysoev.ru handler = c->write_state->close_handler; 6070Sigor@sysoev.ru 6080Sigor@sysoev.ru } else { 6090Sigor@sysoev.ru ret = nxt_gnutls_conn_test_error(thr, c, err, 6100Sigor@sysoev.ru nxt_gnutls_conn_io_shutdown); 6110Sigor@sysoev.ru 6120Sigor@sysoev.ru if (ret != NXT_ERROR) { /* ret == NXT_AGAIN */ 6130Sigor@sysoev.ru c->socket.error_handler = c->read_state->error_handler; 6140Sigor@sysoev.ru nxt_event_timer_add(thr->engine, &c->read_timer, 5000); 6150Sigor@sysoev.ru return; 6160Sigor@sysoev.ru } 6170Sigor@sysoev.ru 6180Sigor@sysoev.ru nxt_gnutls_conn_log_error(c, err, "gnutls_bye(%d) failed", 6190Sigor@sysoev.ru c->socket.fd); 6200Sigor@sysoev.ru 6210Sigor@sysoev.ru handler = c->write_state->error_handler; 6220Sigor@sysoev.ru } 6230Sigor@sysoev.ru 6240Sigor@sysoev.ru done: 6250Sigor@sysoev.ru 6260Sigor@sysoev.ru nxt_event_conn_io_handle(thr, c->write_work_queue, handler, c, data); 6270Sigor@sysoev.ru } 6280Sigor@sysoev.ru 6290Sigor@sysoev.ru 6300Sigor@sysoev.ru static nxt_int_t 6310Sigor@sysoev.ru nxt_gnutls_conn_test_error(nxt_thread_t *thr, nxt_event_conn_t *c, ssize_t err, 6320Sigor@sysoev.ru nxt_work_handler_t handler) 6330Sigor@sysoev.ru { 6340Sigor@sysoev.ru int ret; 6350Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 6360Sigor@sysoev.ru 6370Sigor@sysoev.ru switch (err) { 6380Sigor@sysoev.ru 6390Sigor@sysoev.ru case GNUTLS_E_REHANDSHAKE: 6400Sigor@sysoev.ru case GNUTLS_E_AGAIN: 6410Sigor@sysoev.ru ssltls = c->u.ssltls; 6420Sigor@sysoev.ru ret = gnutls_record_get_direction(ssltls->session); 6430Sigor@sysoev.ru 6440Sigor@sysoev.ru nxt_log_debug(thr->log, "gnutls_record_get_direction(): %d", ret); 6450Sigor@sysoev.ru 6460Sigor@sysoev.ru if (ret == 0) { 6470Sigor@sysoev.ru /* A read direction. */ 6480Sigor@sysoev.ru 6490Sigor@sysoev.ru nxt_event_fd_block_write(thr->engine, &c->socket); 6500Sigor@sysoev.ru 6510Sigor@sysoev.ru c->socket.read_ready = 0; 6520Sigor@sysoev.ru c->socket.read_handler = handler; 6530Sigor@sysoev.ru 6540Sigor@sysoev.ru if (nxt_event_fd_is_disabled(c->socket.read)) { 6550Sigor@sysoev.ru nxt_event_fd_enable_read(thr->engine, &c->socket); 6560Sigor@sysoev.ru } 6570Sigor@sysoev.ru 6580Sigor@sysoev.ru } else { 6590Sigor@sysoev.ru /* A write direction. */ 6600Sigor@sysoev.ru 6610Sigor@sysoev.ru nxt_event_fd_block_read(thr->engine, &c->socket); 6620Sigor@sysoev.ru 6630Sigor@sysoev.ru c->socket.write_ready = 0; 6640Sigor@sysoev.ru c->socket.write_handler = handler; 6650Sigor@sysoev.ru 6660Sigor@sysoev.ru if (nxt_event_fd_is_disabled(c->socket.write)) { 6670Sigor@sysoev.ru nxt_event_fd_enable_write(thr->engine, &c->socket); 6680Sigor@sysoev.ru } 6690Sigor@sysoev.ru } 6700Sigor@sysoev.ru 6710Sigor@sysoev.ru return NXT_AGAIN; 6720Sigor@sysoev.ru 6730Sigor@sysoev.ru default: 6740Sigor@sysoev.ru c->socket.error = 1000; /* Nonexistent errno code. */ 6750Sigor@sysoev.ru return NXT_ERROR; 6760Sigor@sysoev.ru } 6770Sigor@sysoev.ru } 6780Sigor@sysoev.ru 6790Sigor@sysoev.ru 6800Sigor@sysoev.ru static void 6810Sigor@sysoev.ru nxt_gnutls_conn_log_error(nxt_event_conn_t *c, ssize_t err, 6820Sigor@sysoev.ru const char *fmt, ...) 6830Sigor@sysoev.ru { 6840Sigor@sysoev.ru va_list args; 6850Sigor@sysoev.ru nxt_uint_t level; 6860Sigor@sysoev.ru u_char *p, msg[NXT_MAX_ERROR_STR]; 6870Sigor@sysoev.ru 6880Sigor@sysoev.ru level = nxt_gnutls_log_error_level(c, err); 6890Sigor@sysoev.ru 6900Sigor@sysoev.ru if (nxt_log_level_enough(c->socket.log, level)) { 6910Sigor@sysoev.ru 6920Sigor@sysoev.ru va_start(args, fmt); 6930Sigor@sysoev.ru p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args); 6940Sigor@sysoev.ru va_end(args); 6950Sigor@sysoev.ru 6960Sigor@sysoev.ru nxt_log_error(level, c->socket.log, "%*s (%d: %s)", 6970Sigor@sysoev.ru p - msg, msg, err, gnutls_strerror(err)); 6980Sigor@sysoev.ru } 6990Sigor@sysoev.ru } 7000Sigor@sysoev.ru 7010Sigor@sysoev.ru 7020Sigor@sysoev.ru static nxt_uint_t 7030Sigor@sysoev.ru nxt_gnutls_log_error_level(nxt_event_conn_t *c, ssize_t err) 7040Sigor@sysoev.ru { 7050Sigor@sysoev.ru nxt_gnutls_conn_t *ssltls; 7060Sigor@sysoev.ru 7070Sigor@sysoev.ru switch (err) { 7080Sigor@sysoev.ru 7090Sigor@sysoev.ru case GNUTLS_E_UNKNOWN_CIPHER_SUITE: /* -21 */ 7100Sigor@sysoev.ru 7110Sigor@sysoev.ru /* Disable gnutls_bye(), because it returns GNUTLS_E_INTERNAL_ERROR. */ 7120Sigor@sysoev.ru ssltls = c->u.ssltls; 7130Sigor@sysoev.ru ssltls->no_shutdown = 1; 7140Sigor@sysoev.ru 7150Sigor@sysoev.ru /* Fall through. */ 7160Sigor@sysoev.ru 7170Sigor@sysoev.ru case GNUTLS_E_UNEXPECTED_PACKET_LENGTH: /* -9 */ 7180Sigor@sysoev.ru c->socket.error = 1000; /* Nonexistent errno code. */ 7190Sigor@sysoev.ru break; 7200Sigor@sysoev.ru 7210Sigor@sysoev.ru default: 722*564Svbart@nginx.com return NXT_LOG_ALERT; 7230Sigor@sysoev.ru } 7240Sigor@sysoev.ru 7250Sigor@sysoev.ru return NXT_LOG_INFO; 7260Sigor@sysoev.ru } 7270Sigor@sysoev.ru 7280Sigor@sysoev.ru 7290Sigor@sysoev.ru static void 7300Sigor@sysoev.ru nxt_gnutls_log_error(nxt_uint_t level, nxt_log_t *log, int err, 7310Sigor@sysoev.ru const char *fmt, ...) 7320Sigor@sysoev.ru { 7330Sigor@sysoev.ru va_list args; 7340Sigor@sysoev.ru u_char *p, msg[NXT_MAX_ERROR_STR]; 7350Sigor@sysoev.ru 7360Sigor@sysoev.ru va_start(args, fmt); 7370Sigor@sysoev.ru p = nxt_vsprintf(msg, msg + sizeof(msg), fmt, args); 7380Sigor@sysoev.ru va_end(args); 7390Sigor@sysoev.ru 7400Sigor@sysoev.ru nxt_log_error(level, log, "%*s (%d: %s)", 7410Sigor@sysoev.ru p - msg, msg, err, gnutls_strerror(err)); 7420Sigor@sysoev.ru } 743