diff --git a/.travis.yml b/.travis.yml index 7448f65ac2..86581be635 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,7 +84,7 @@ install: - git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module - git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module - git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module - - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core + - git clone -b ssl-psk https://github.com/vartiait/lua-resty-core.git ../lua-resty-core - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git diff --git a/README.markdown b/README.markdown index 28ba6e68c0..8562a227c0 100644 --- a/README.markdown +++ b/README.markdown @@ -1076,6 +1076,7 @@ Directives * [lua_need_request_body](#lua_need_request_body) * [ssl_certificate_by_lua_block](#ssl_certificate_by_lua_block) * [ssl_certificate_by_lua_file](#ssl_certificate_by_lua_file) +* [ssl_psk_identity_hint](#ssl_psk_identity_hint) * [ssl_session_fetch_by_lua_block](#ssl_session_fetch_by_lua_block) * [ssl_session_fetch_by_lua_file](#ssl_session_fetch_by_lua_file) * [ssl_session_store_by_lua_block](#ssl_session_store_by_lua_block) @@ -1094,6 +1095,8 @@ Directives * [lua_ssl_protocols](#lua_ssl_protocols) * [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate) * [lua_ssl_verify_depth](#lua_ssl_verify_depth) +* [lua_ssl_psk_identity](#lua_ssl_psk_identity) +* [lua_ssl_psk_key](#lua_ssl_psk_key) * [lua_http10_buffering](#lua_http10_buffering) * [rewrite_by_lua_no_postpone](#rewrite_by_lua_no_postpone) * [access_by_lua_no_postpone](#access_by_lua_no_postpone) @@ -2564,6 +2567,22 @@ This directive was first introduced in the `v0.10.0` release. [Back to TOC](#directives) +ssl_psk_identity_hint +--------------------- + +**syntax:** *ssl_psk_identity_hint <tls_psk_identity_hint>* + +**default:** *no* + +**context:** *http, server* + +Specifies the TLS-PSK identity hint string which NGINX will send to a client during +the SSL handshake for the downstream SSL (https) connections. + +This directive was first introduced in the `v0.XX.YY` release. + +[Back to TOC](#directives) + ssl_session_fetch_by_lua_block ------------------------------ @@ -2958,6 +2977,36 @@ See also [lua_ssl_trusted_certificate](#lua_ssl_trusted_certificate). [Back to TOC](#directives) +lua_ssl_psk_identity +-------------------- + +**syntax:** *lua_ssl_psk_identity <tls_psk_identity>* + +**default:** *no* + +**context:** *http, server, location* + +Specifies the TLS-PSK identity string which NGINX will send to a SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +This directive was first introduced in the `v0.XX.YY` release. + +[Back to TOC](#directives) + +lua_ssl_psk_key +--------------- + +**syntax:** *lua_ssl_psk_key <tls_psk_key>* + +**default:** *no* + +**context:** *http, server, location* + +Specifies the TLS-PSK key string which NGINX will try use with a SSL/TLS server in the [tcpsock:sslhandshake](#tcpsocksslhandshake) method. + +This directive was first introduced in the `v0.XX.YY` release. + +[Back to TOC](#directives) + lua_http10_buffering -------------------- diff --git a/config b/config index 044deb974e..143ad92d12 100644 --- a/config +++ b/config @@ -359,6 +359,7 @@ HTTP_LUA_SRCS=" \ $ngx_addon_dir/src/ngx_http_lua_balancer.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.c \ + $ngx_addon_dir/src/ngx_http_lua_ssl_pskby.c \ $ngx_addon_dir/src/ngx_http_lua_ssl.c \ $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.c \ " @@ -420,6 +421,7 @@ HTTP_LUA_DEPS=" \ $ngx_addon_dir/src/ngx_http_lua_balancer.h \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_storeby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl_session_fetchby.h \ + $ngx_addon_dir/src/ngx_http_lua_ssl_pskby.h \ $ngx_addon_dir/src/ngx_http_lua_ssl.h \ $ngx_addon_dir/src/ngx_http_lua_log_ringbuf.h \ " diff --git a/doc/HttpLuaModule.wiki b/doc/HttpLuaModule.wiki index 8ad2b2d4a9..23aba0d30e 100644 --- a/doc/HttpLuaModule.wiki +++ b/doc/HttpLuaModule.wiki @@ -2159,6 +2159,19 @@ When a relative path like foo/bar.lua is given, they will be turned This directive was first introduced in the v0.10.0 release. +== ssl_psk_identity_hint == + +'''syntax:''' ''ssl_psk_identity_hint '' + +'''default:''' ''no'' + +'''context:''' ''http, server'' + +Specifies the TLS-PSK identity hint string which NGINX will send to a client during +the SSL handshake for the downstream SSL (https) connections. + +This directive was first introduced in the v0.XX.YY release. + == ssl_session_fetch_by_lua_block == '''syntax:''' ''ssl_session_fetch_by_lua_block { lua-script }'' @@ -2498,6 +2511,30 @@ This directive was first introduced in the v0.9.11 release. See also [[#lua_ssl_trusted_certificate|lua_ssl_trusted_certificate]]. +== lua_ssl_psk_identity == + +'''syntax:''' ''lua_ssl_psk_identity '' + +'''default:''' ''no'' + +'''context:''' ''http, server, location'' + +Specifies the TLS-PSK identity string which NGINX will send to a SSL/TLS server in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method. + +This directive was first introduced in the v0.XX.YY release. + +== lua_ssl_psk_key == + +'''syntax:''' ''lua_ssl_psk_key '' + +'''default:''' ''no'' + +'''context:''' ''http, server, location'' + +Specifies the TLS-PSK key string which NGINX will try use with a SSL/TLS server in the [[#tcpsock:sslhandshake|tcpsock:sslhandshake]] method. + +This directive was first introduced in the v0.XX.YY release. + == lua_http10_buffering == '''syntax:''' ''lua_http10_buffering on|off'' diff --git a/src/ngx_http_lua_common.h b/src/ngx_http_lua_common.h index e38978389c..2ed06b96ff 100644 --- a/src/ngx_http_lua_common.h +++ b/src/ngx_http_lua_common.h @@ -129,6 +129,7 @@ typedef struct { #define NGX_HTTP_LUA_CONTEXT_SSL_CERT 0x0400 #define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE 0x0800 #define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH 0x1000 +#define NGX_HTTP_LUA_CONTEXT_SSL_PSK 0x2000 #ifndef NGX_LUA_NO_FFI_API @@ -248,6 +249,8 @@ union ngx_http_lua_srv_conf_u { ngx_http_lua_srv_conf_handler_pt ssl_sess_fetch_handler; ngx_str_t ssl_sess_fetch_src; u_char *ssl_sess_fetch_src_key; + + ngx_str_t ssl_psk_identity_hint; } srv; #endif @@ -268,6 +271,8 @@ typedef struct { ngx_uint_t ssl_verify_depth; ngx_str_t ssl_trusted_certificate; ngx_str_t ssl_crl; + ngx_str_t ssl_psk_identity; + ngx_str_t ssl_psk_key; #endif ngx_flag_t force_read_body; /* whether force request body to diff --git a/src/ngx_http_lua_control.c b/src/ngx_http_lua_control.c index ae36505f9d..45c31262d9 100644 --- a/src/ngx_http_lua_control.c +++ b/src/ngx_http_lua_control.c @@ -322,13 +322,15 @@ ngx_http_lua_ngx_exit(lua_State *L) | NGX_HTTP_LUA_CONTEXT_BALANCER | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH); + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH + | NGX_HTTP_LUA_CONTEXT_SSL_PSK); rc = (ngx_int_t) luaL_checkinteger(L, 1); if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH + | NGX_HTTP_LUA_CONTEXT_SSL_PSK)) { #if (NGX_HTTP_SSL) @@ -339,7 +341,8 @@ ngx_http_lua_ngx_exit(lua_State *L) ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "lua exit with code %i", rc); - if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE) { + if (ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE + || ctx->context == NGX_HTTP_LUA_CONTEXT_SSL_PSK) { return 0; } @@ -473,7 +476,8 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, | NGX_HTTP_LUA_CONTEXT_BALANCER | NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH, + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH + | NGX_HTTP_LUA_CONTEXT_SSL_PSK, err, errlen) != NGX_OK) { @@ -482,7 +486,8 @@ ngx_http_lua_ffi_exit(ngx_http_request_t *r, int status, u_char *err, if (ctx->context & (NGX_HTTP_LUA_CONTEXT_SSL_CERT | NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE - | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH)) + | NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH + | NGX_HTTP_LUA_CONTEXT_SSL_PSK)) { #if (NGX_HTTP_SSL) diff --git a/src/ngx_http_lua_module.c b/src/ngx_http_lua_module.c index 9d914e86e8..c3e67acedc 100644 --- a/src/ngx_http_lua_module.c +++ b/src/ngx_http_lua_module.c @@ -28,6 +28,7 @@ #include "ngx_http_lua_ssl_certby.h" #include "ngx_http_lua_ssl_session_storeby.h" #include "ngx_http_lua_ssl_session_fetchby.h" +#include "ngx_http_lua_ssl_pskby.h" #include "ngx_http_lua_headers.h" @@ -557,6 +558,27 @@ static ngx_command_t ngx_http_lua_cmds[] = { 0, (void *) ngx_http_lua_ssl_sess_fetch_handler_file }, + { ngx_string("ssl_psk_identity_hint"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_SRV_CONF_OFFSET, + offsetof(ngx_http_lua_srv_conf_t, srv.ssl_psk_identity_hint), + NULL }, + + { ngx_string("lua_ssl_psk_identity"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_psk_identity), + NULL }, + + { ngx_string("lua_ssl_psk_key"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_str_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_lua_loc_conf_t, ssl_psk_key), + NULL }, + { ngx_string("lua_ssl_verify_depth"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_num_slot, @@ -922,6 +944,8 @@ ngx_http_lua_create_srv_conf(ngx_conf_t *cf) * lscf->srv.ssl_session_fetch_src = { 0, NULL }; * lscf->srv.ssl_session_fetch_src_key = NULL; * + * lscf->srv.ssl_psk_identity_hint = { 0, NULL }; + * * lscf->balancer.handler = NULL; * lscf->balancer.src = { 0, NULL }; * lscf->balancer.src_key = NULL; @@ -969,6 +993,8 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) SSL_CTX_set_cert_cb(sscf->ssl.ctx, ngx_http_lua_ssl_cert_handler, NULL); + SSL_CTX_set_psk_server_callback(sscf->ssl.ctx, + ngx_http_lua_ssl_psk_server_handler); # else ngx_log_error(NGX_LOG_EMERG, cf->log, 0, @@ -1024,6 +1050,42 @@ ngx_http_lua_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) } } + if (conf->srv.ssl_psk_identity_hint.len == 0) { + conf->srv.ssl_psk_identity_hint = prev->srv.ssl_psk_identity_hint; + } + + if (conf->srv.ssl_psk_identity_hint.len) { + dd("ssl psk identity hint: %.*s", + (int) conf->srv.ssl_psk_identity_hint.len, + conf->srv.ssl_psk_identity_hint.data); + + # if OPENSSL_VERSION_NUMBER >= 0x1000000fL + + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); + if (sscf == NULL || sscf->ssl.ctx == NULL) { + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, + "no ssl configured for the server"); + + return NGX_CONF_ERROR; + } + + if (SSL_CTX_use_psk_identity_hint(sscf->ssl.ctx, + (const char *) conf->srv.ssl_psk_identity_hint.data) == 0) { + ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, + "SSL_CTX_use_psk_identity_hint(\"%V\") failed", + &conf->srv.ssl_psk_identity_hint); + return NGX_CONF_ERROR; + } + + # else + + ngx_log_error(NGX_LOG_CRIT, cf->log, 0, + "OpenSSL too old to support ssl_psk_identity_hint"); + return NGX_CONF_ERROR; + + # endif + } + #endif /* NGX_HTTP_SSL */ return NGX_CONF_OK; } @@ -1067,6 +1129,8 @@ ngx_http_lua_create_loc_conf(ngx_conf_t *cf) * conf->ssl_ciphers = { 0, NULL }; * conf->ssl_trusted_certificate = { 0, NULL }; * conf->ssl_crl = { 0, NULL }; + * conf->ssl_psk_identity = { 0, NULL }; + * conf->ssl_psk_key = {0, NULL }; */ conf->force_read_body = NGX_CONF_UNSET; @@ -1160,6 +1224,11 @@ ngx_http_lua_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) prev->ssl_trusted_certificate, ""); ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, ""); + ngx_conf_merge_str_value(conf->ssl_psk_identity, + prev->ssl_psk_identity, ""); + ngx_conf_merge_str_value(conf->ssl_psk_key, + prev->ssl_psk_key, ""); + if (ngx_http_lua_set_ssl(cf, conf) != NGX_OK) { return NGX_CONF_ERROR; } @@ -1266,6 +1335,26 @@ ngx_http_lua_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf) return NGX_ERROR; } + if (llcf->ssl_psk_identity.len && llcf->ssl_psk_key.len) { + dd("ssl psk identity: %.*s", (int) llcf->ssl_psk_identity.len, + llcf->ssl_psk_identity.data); + dd("ssl psk key: %.*s", (int) llcf->ssl_psk_key.len, + llcf->ssl_psk_key.data); + +# if OPENSSL_VERSION_NUMBER >= 0x1000000fL + + SSL_CTX_set_psk_client_callback(llcf->ssl->ctx, + ngx_http_lua_ssl_psk_client_handler); + +# else + + ngx_log_error(NGX_LOG_CRIT, cf->log, 0, + "OpenSSL too old to support ssl_psk_identity"); + return NGX_ERROR; + +# endif + } + return NGX_OK; } diff --git a/src/ngx_http_lua_phase.c b/src/ngx_http_lua_phase.c index 50c53110b8..7ed0ad105d 100644 --- a/src/ngx_http_lua_phase.c +++ b/src/ngx_http_lua_phase.c @@ -80,6 +80,10 @@ ngx_http_lua_ngx_get_phase(lua_State *L) lua_pushliteral(L, "balancer"); break; + case NGX_HTTP_LUA_CONTEXT_SSL_PSK: + lua_pushliteral(L, "ssl_psk"); + break; + case NGX_HTTP_LUA_CONTEXT_SSL_CERT: lua_pushliteral(L, "ssl_cert"); break; diff --git a/src/ngx_http_lua_socket_tcp.c b/src/ngx_http_lua_socket_tcp.c index 85f9f6a04a..826cba11b5 100644 --- a/src/ngx_http_lua_socket_tcp.c +++ b/src/ngx_http_lua_socket_tcp.c @@ -22,6 +22,7 @@ static int ngx_http_lua_socket_tcp(lua_State *L); static int ngx_http_lua_socket_tcp_connect(lua_State *L); #if (NGX_HTTP_SSL) static int ngx_http_lua_socket_tcp_sslhandshake(lua_State *L); +static int ngx_http_lua_socket_tcp_sslgetpskidhint(lua_State *L); #endif static int ngx_http_lua_socket_tcp_receive(lua_State *L); static int ngx_http_lua_socket_tcp_send(lua_State *L); @@ -283,6 +284,9 @@ ngx_http_lua_inject_socket_tcp_api(ngx_log_t *log, lua_State *L) lua_pushcfunction(L, ngx_http_lua_socket_tcp_sslhandshake); lua_setfield(L, -2, "sslhandshake"); + lua_pushcfunction(L, ngx_http_lua_socket_tcp_sslgetpskidhint); + lua_setfield(L, -2, "sslgetpskidhint"); + #endif lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive); @@ -1599,6 +1603,41 @@ ngx_http_lua_ssl_handshake_retval_handler(ngx_http_request_t *r, return 1; } + +static int +ngx_http_lua_socket_tcp_sslgetpskidhint(lua_State *L) +{ + ngx_http_lua_socket_tcp_upstream_t *u; + + if (lua_gettop(L) != 1) { + return luaL_error(L, "expecting 1 argument " + "(including the object), but got %d", lua_gettop(L)); + } + + luaL_checktype(L, 1, LUA_TTABLE); + + lua_rawgeti(L, 1, SOCKET_CTX_INDEX); + u = lua_touserdata(L, -1); + + if (u == NULL + || u->peer.connection == NULL + || (u->read_closed && u->write_closed)) + { + lua_pushnil(L); + lua_pushliteral(L, "closed"); + return 2; + } + + if (u->ssl_psk_identity_hint.len == 0) { + lua_pushliteral(L, ""); + return 1; + } + + lua_pushlstring(L, (char *) u->ssl_psk_identity_hint.data, + u->ssl_psk_identity_hint.len); + return 1; +} + #endif /* NGX_HTTP_SSL */ @@ -3493,6 +3532,11 @@ ngx_http_lua_socket_tcp_finalize(ngx_http_request_t *r, u->ssl_name.data = NULL; u->ssl_name.len = 0; } + if (u->ssl_psk_identity_hint.data) { + ngx_free(u->ssl_psk_identity_hint.data); + u->ssl_psk_identity_hint.data = NULL; + u->ssl_psk_identity_hint.len = 0; + } #endif c = u->peer.connection; diff --git a/src/ngx_http_lua_socket_tcp.h b/src/ngx_http_lua_socket_tcp.h index dbdee41c6e..51f93ab921 100644 --- a/src/ngx_http_lua_socket_tcp.h +++ b/src/ngx_http_lua_socket_tcp.h @@ -92,6 +92,7 @@ struct ngx_http_lua_socket_tcp_upstream_s { #if (NGX_HTTP_SSL) ngx_str_t ssl_name; + ngx_str_t ssl_psk_identity_hint; #endif unsigned ft_type:16; diff --git a/src/ngx_http_lua_ssl.h b/src/ngx_http_lua_ssl.h index acb8c4b16c..a612316d93 100644 --- a/src/ngx_http_lua_ssl.h +++ b/src/ngx_http_lua_ssl.h @@ -24,6 +24,9 @@ typedef struct { ngx_str_t session_id; + ngx_str_t psk_identity; + ngx_str_t psk_key; + int exit_code; /* exit code for openssl's set_cert_cb callback */ @@ -32,6 +35,7 @@ typedef struct { unsigned entered_cert_handler:1; unsigned entered_sess_fetch_handler:1; + unsigned entered_psk_handler:1; } ngx_http_lua_ssl_ctx_t; diff --git a/src/ngx_http_lua_ssl_certby.c b/src/ngx_http_lua_ssl_certby.c index 95be47f6b5..708756b77e 100644 --- a/src/ngx_http_lua_ssl_certby.c +++ b/src/ngx_http_lua_ssl_certby.c @@ -461,6 +461,7 @@ ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r) lua_State *co; ngx_http_lua_ctx_t *ctx; ngx_http_cleanup_t *cln; + ngx_http_lua_ssl_ctx_t *cctx; ctx = ngx_http_get_module_ctx(r, ngx_http_lua_module); @@ -522,13 +523,19 @@ ngx_http_lua_ssl_cert_by_chunk(lua_State *L, ngx_http_request_t *r) ctx->cleanup = &cln->handler; } - ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_CERT; + cctx = ngx_http_lua_ssl_get_ctx(r->connection->ssl->connection); + + if (cctx != NULL && cctx->entered_psk_handler) { + ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_PSK; + } + else { + ctx->context = NGX_HTTP_LUA_CONTEXT_SSL_CERT; + } rc = ngx_http_lua_run_thread(L, r, ctx, 0); if (rc == NGX_ERROR || rc >= NGX_OK) { /* do nothing */ - } else if (rc == NGX_AGAIN) { rc = ngx_http_lua_content_run_posted_threads(L, r, ctx, 0); diff --git a/src/ngx_http_lua_ssl_pskby.c b/src/ngx_http_lua_ssl_pskby.c new file mode 100644 index 0000000000..e075a7b950 --- /dev/null +++ b/src/ngx_http_lua_ssl_pskby.c @@ -0,0 +1,515 @@ + +/* + * Based on ngx_http_lua_ssl_certby.c and ngx_http_lua_ssl_session_storeby.c + * by Yichun Zhang (agentzh) + * + * Author: Tuure Vartiainen (vartiait) + */ + + +#ifndef DDEBUG +#define DDEBUG 0 +#endif +#include "ddebug.h" + + +#if (NGX_HTTP_SSL) + +#include "ngx_http_lua_cache.h" +#include "ngx_http_lua_initworkerby.h" +#include "ngx_http_lua_util.h" +#include "ngx_http_ssl_module.h" +#include "ngx_http_lua_contentby.h" +#include "ngx_http_lua_ssl_pskby.h" +#include "ngx_http_lua_directive.h" +#include "ngx_http_lua_ssl.h" +#include "ngx_http_lua_socket_tcp.h" + + +static u_char *ngx_http_lua_log_ssl_psk_error(ngx_log_t *log, u_char *buf, + size_t len); + + +unsigned int ngx_http_lua_ssl_psk_server_handler(ngx_ssl_conn_t *ssl_conn, + const char *identity, unsigned char *psk, unsigned int max_psk_len) +{ + lua_State *L; + ngx_int_t rc; + ngx_connection_t *c, *fc; + ngx_http_request_t *r = NULL; + ngx_http_connection_t *hc; + ngx_http_lua_srv_conf_t *lscf; + ngx_http_core_loc_conf_t *clcf; + ngx_http_lua_ssl_ctx_t *cctx; + + c = ngx_ssl_get_connection(ssl_conn); + + dd("c = %p", c); + + cctx = ngx_http_lua_ssl_get_ctx(c->ssl->connection); + + dd("ssl server psk handler, psk-ctx=%p", cctx); + + hc = c->data; + + fc = ngx_http_lua_create_fake_connection(NULL); + if (fc == NULL) { + goto failed; + } + + fc->log->handler = ngx_http_lua_log_ssl_psk_error; + fc->log->data = fc; + + fc->addr_text = c->addr_text; + fc->listening = c->listening; + + r = ngx_http_lua_create_fake_request(fc); + if (r == NULL) { + goto failed; + } + + r->main_conf = hc->conf_ctx->main_conf; + r->srv_conf = hc->conf_ctx->srv_conf; + r->loc_conf = hc->conf_ctx->loc_conf; + + fc->log->file = c->log->file; + fc->log->log_level = c->log->log_level; + fc->ssl = c->ssl; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + +#if defined(nginx_version) && nginx_version >= 1003014 + +# if nginx_version >= 1009000 + + ngx_set_connection_log(fc, clcf->error_log); + +# else + + ngx_http_set_connection_log(fc, clcf->error_log); + +# endif + +#else + + fc->log->file = clcf->error_log->file; + + if (!(fc->log->log_level & NGX_LOG_DEBUG_CONNECTION)) { + fc->log->log_level = clcf->error_log->log_level; + } + +#endif + + if (cctx == NULL) { + cctx = ngx_pcalloc(c->pool, sizeof(ngx_http_lua_ssl_ctx_t)); + if (cctx == NULL) { + goto failed; /* error */ + } + } + + if (identity == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "client did not send TLS-PSK identity"); + goto failed; + } + + cctx->exit_code = 0; /* unsuccessful by default */ + cctx->connection = c; + cctx->request = r; + cctx->psk_identity.data = (u_char *) identity; + cctx->psk_identity.len = ngx_strlen(identity); + cctx->entered_psk_handler = 1; + cctx->done = 0; + + dd("setting cctx = %p", cctx); + + if (SSL_set_ex_data(c->ssl->connection, ngx_http_lua_ssl_ctx_index, cctx) + == 0) + { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "SSL_set_ex_data() failed"); + goto failed; + } + + lscf = ngx_http_get_module_srv_conf(r, ngx_http_lua_module); + + /* TODO honor lua_code_cache off */ + L = ngx_http_lua_get_lua_vm(r, NULL); + + c->log->action = "setting SSL PSK by lua"; + + rc = lscf->srv.ssl_cert_handler(r, lscf, L); + + if (rc >= NGX_OK || rc == NGX_ERROR) { + cctx->done = 1; + + if (cctx->cleanup) { + *cctx->cleanup = NULL; + } + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, + "lua_certificate_by_lua: handler return value: %i, " + "psk server cb exit code: %d", rc, cctx->exit_code); + + c->log->action = "SSL handshaking"; + + if (rc == NGX_ERROR || cctx->exit_code != NGX_OK) { + /* 0 == unknown_psk_identity */ + return 0; + } + + if (cctx->psk_key.data == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "psk_key.data == NULL"); + + return 0; + } + + if (cctx->psk_key.len == 0) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "psk_key.len == 0"); + + return 0; + } + + if (cctx->psk_key.len > (size_t) max_psk_len) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, + "psk_key.len: %i > max_psk_len: %i", + cctx->psk_key.len, max_psk_len); + + return 0; + } + + ngx_memcpy(psk, cctx->psk_key.data, cctx->psk_key.len); + + /* return length of psk key */ + return cctx->psk_key.len; + } + + /* impossible to reach here */ + ngx_http_lua_assert(0); + +failed: + + if (r && r->pool) { + ngx_http_lua_free_fake_request(r); + } + + if (fc) { + ngx_http_lua_close_fake_connection(fc); + } + + return 0; +} + + +unsigned int ngx_http_lua_ssl_psk_client_handler(ngx_ssl_conn_t *ssl_conn, + const char *hint, char *identity, unsigned int max_identity_len, + unsigned char *psk, unsigned int max_psk_len) +{ + ngx_connection_t *c; + ngx_connection_t *dc; /* downstream connection */ + ngx_http_request_t *r = NULL; + ngx_http_lua_loc_conf_t *llcf; + + ngx_http_lua_socket_tcp_upstream_t *u; + + c = ngx_ssl_get_connection(ssl_conn); + + if (c == NULL) { + goto failed; + } + + dd("ssl psk client handler, c = %p", c); + + u = c->data; + + if (u == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "no upstream socket found"); + goto failed; + } + + r = u->request; + + if (r == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "no http request found"); + goto failed; + } + + dc = r->connection; + + if (dc == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, "no downstream socket found"); + goto failed; + } + + llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module); + + if (llcf == NULL) { + ngx_ssl_error(NGX_LOG_ALERT, dc->log, 0, + "getting module loc conf failed"); + goto failed; + } + + if (hint == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "sslhandshake: psk server hint was null"); + } + else { + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "sslhandshake: psk server hint: %s", hint); + + size_t hint_len = ngx_strlen(hint); + if (u->ssl_psk_identity_hint.data) { + /* buffer already allocated */ + + if (u->ssl_psk_identity_hint.len > hint_len) { + /* reuse it */ + ngx_memcpy(u->ssl_psk_identity_hint.data, hint, hint_len); + u->ssl_psk_identity_hint.len = hint_len; + } else { + ngx_free(u->ssl_psk_identity_hint.data); + goto new_ssl_psk_identity_hint; + } + } + else { + +new_ssl_psk_identity_hint: + + u->ssl_psk_identity_hint.data = ngx_alloc(hint_len + 1, + ngx_cycle->log); + if (u->ssl_psk_identity_hint.data == NULL) { + u->ssl_psk_identity_hint.len = 0; + + ngx_ssl_error(NGX_LOG_ALERT, dc->log, 0, + "could not allocate memory for ssl_psk_identity_hint"); + goto failed; + } + + ngx_memcpy(u->ssl_psk_identity_hint.data, hint, hint_len); + u->ssl_psk_identity_hint.len = hint_len; + } + } + + if (llcf->ssl_psk_identity.len) { + if (llcf->ssl_psk_identity.len <= max_identity_len) { + ngx_snprintf((u_char *) identity, max_identity_len, "%V", + &llcf->ssl_psk_identity); + } + else { + ngx_ssl_error(NGX_LOG_ALERT, dc->log, 0, + "ssl_psk_identity.len: %i > max_identity_len: %i", + llcf->ssl_psk_identity.len, max_identity_len); + goto failed; + } + } + else { + ngx_ssl_error(NGX_LOG_ALERT, dc->log, 0, + "no ssl_psk_identity defined"); + goto failed; + } + + if (llcf->ssl_psk_key.len) { + if (llcf->ssl_psk_key.len <= max_psk_len) { + ngx_memcpy(psk, llcf->ssl_psk_key.data, llcf->ssl_psk_key.len); + return llcf->ssl_psk_key.len; + } + else { + ngx_ssl_error(NGX_LOG_ALERT, dc->log, 0, + "ssl_psk_key.len: %i > max_psk_len: %i", + llcf->ssl_psk_key.len, max_psk_len); + goto failed; + } + } + else { + ngx_ssl_error(NGX_LOG_ALERT, dc->log, 0, "no ssl_psk_key defined"); + goto failed; + } + + /* impossible to reach here */ + ngx_http_lua_assert(0); + +failed: + + return 0; +} + +static u_char * +ngx_http_lua_log_ssl_psk_error(ngx_log_t *log, u_char *buf, size_t len) +{ + u_char *p; + ngx_connection_t *c; + + if (log->action) { + p = ngx_snprintf(buf, len, " while %s", log->action); + len -= p - buf; + buf = p; + } + + p = ngx_snprintf(buf, len, ", context: ssl_psk_by_lua*"); + len -= p - buf; + buf = p; + + c = log->data; + + if (c->addr_text.len) { + p = ngx_snprintf(buf, len, ", client: %V", &c->addr_text); + len -= p - buf; + buf = p; + } + + if (c && c->listening && c->listening->addr_text.len) { + p = ngx_snprintf(buf, len, ", server: %V", &c->listening->addr_text); + /* len -= p - buf; */ + buf = p; + } + + return buf; +} + + +#ifndef NGX_LUA_NO_FFI_API + +/* set psk key from key to lua context. */ +int +ngx_http_lua_ffi_ssl_set_psk_key(ngx_http_request_t *r, + const char *key, size_t len, char **err) +{ + u_char *buf; + ngx_connection_t *c; + ngx_ssl_conn_t *ssl_conn; + ngx_http_lua_ssl_ctx_t *cctx; + + c = r->connection; + + if (c == NULL || c->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + dd("set cctx psk key"); + cctx = ngx_http_lua_ssl_get_ctx(ssl_conn); + if (cctx == NULL) { + *err = "bad lua context"; + return NGX_ERROR; + } + + buf = ngx_palloc(cctx->connection->pool, len); + if (buf == NULL) { + *err = "unable to alloc memory for buffer"; + return NGX_ERROR; + } + ngx_memcpy(buf, key, len); + + cctx->psk_key.data = buf; + cctx->psk_key.len = len; + + return NGX_OK; +} + + +/* get psk identity from lua context into buf. + * the memory allocation of buf should be handled externally. */ +int +ngx_http_lua_ffi_ssl_get_psk_identity(ngx_http_request_t *r, + char *buf, char **err) +{ + int id_len; + u_char *id; + ngx_connection_t *c; + ngx_ssl_conn_t *ssl_conn; + ngx_http_lua_ssl_ctx_t *cctx; + + c = r->connection; + + if (c == NULL || c->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + dd("get cctx psk identity"); + cctx = ngx_http_lua_ssl_get_ctx(ssl_conn); + if (cctx == NULL) { + *err = "bad lua context"; + return NGX_ERROR; + } + + if(!cctx->entered_psk_handler) { + *err = "not in psk context"; + return NGX_ERROR; + } + + id = cctx->psk_identity.data; + if (id == NULL) { + *err = "uninitialized psk identity in lua context"; + return NGX_ERROR; + } + + id_len = cctx->psk_identity.len; + if (id_len == 0) { + *err = "uninitialized psk identity len in lua context"; + return NGX_ERROR; + } + + ngx_memcpy(buf, id, id_len); + + return NGX_OK; +} + + +/* return the size of psk identity. */ +int +ngx_http_lua_ffi_ssl_get_psk_identity_size(ngx_http_request_t *r, + char **err) +{ + ngx_connection_t *c; + ngx_ssl_conn_t *ssl_conn; + ngx_http_lua_ssl_ctx_t *cctx; + + c = r->connection; + + if (c == NULL || c->ssl == NULL) { + *err = "bad request"; + return NGX_ERROR; + } + + ssl_conn = c->ssl->connection; + if (ssl_conn == NULL) { + *err = "bad ssl conn"; + return NGX_ERROR; + } + + dd("get cctx psk identity"); + cctx = ngx_http_lua_ssl_get_ctx(ssl_conn); + if (cctx == NULL) { + *err = "bad lua context"; + return NGX_ERROR; + } + + if(!cctx->entered_psk_handler) { + *err = "not in psk context"; + return NGX_ERROR; + } + + if (cctx->psk_identity.len == 0) { + *err = "uninitialized psk identity len in lua context"; + return NGX_ERROR; + } + + return cctx->psk_identity.len; +} + + +#endif /* NGX_LUA_NO_FFI_API */ + + +#endif /* NGX_HTTP_SSL */ diff --git a/src/ngx_http_lua_ssl_pskby.h b/src/ngx_http_lua_ssl_pskby.h new file mode 100644 index 0000000000..fcda5bee3b --- /dev/null +++ b/src/ngx_http_lua_ssl_pskby.h @@ -0,0 +1,32 @@ + +/* + * Based on ngx_http_lua_ssl_certby.h and ngx_http_lua_ssl_session_storeby.h + * by Yichun Zhang (agentzh) + * + * Author: Tuure Vartiainen (vartiait) + */ + + +#ifndef _NGX_HTTP_LUA_SSL_PSKBY_H_INCLUDED_ +#define _NGX_HTTP_LUA_SSL_PSKBY_H_INCLUDED_ + + +#include "ngx_http_lua_common.h" + + +#if (NGX_HTTP_SSL) + + +unsigned int ngx_http_lua_ssl_psk_server_handler(ngx_ssl_conn_t *ssl_conn, + const char *identity, unsigned char *psk, unsigned int max_psk_len); + +unsigned int ngx_http_lua_ssl_psk_client_handler(ngx_ssl_conn_t *ssl_conn, + const char *hint, char *identity, unsigned int max_identity_len, + unsigned char *psk, unsigned int max_psk_len); + +#endif /* NGX_HTTP_SSL */ + + +#endif /* _NGX_HTTP_LUA_SSL_PSKBY_H_INCLUDED_ */ + +/* vi:set ft=c ts=4 sw=4 et fdm=marker: */ diff --git a/src/ngx_http_lua_util.h b/src/ngx_http_lua_util.h index 2f995e0455..9b731cfd9a 100644 --- a/src/ngx_http_lua_util.h +++ b/src/ngx_http_lua_util.h @@ -91,6 +91,7 @@ extern char ngx_http_lua_headers_metatable_key; "ssl_session_store_by_lua*" \ : (c) == NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH ? \ "ssl_session_fetch_by_lua*" \ + : (c) == NGX_HTTP_LUA_CONTEXT_SSL_PSK ? "ssl_psk_by_lua*" \ : "(unknown)") diff --git a/t/129-ssl-socket.t b/t/129-ssl-socket.t index e7447a7ced..8d7d97b789 100644 --- a/t/129-ssl-socket.t +++ b/t/129-ssl-socket.t @@ -1181,7 +1181,7 @@ lua ssl free session: ([0-9A-F]+):1 $/ --- error_log lua ssl server name: "openresty.org" -SSL: TLSv1.2, cipher: "ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2 +SSL: TLSv1.2, cipher: "ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 --- no_error_log SSL reused session [error] @@ -1341,7 +1341,7 @@ lua ssl free session: ([0-9A-F]+):1 $/ --- error_log lua ssl server name: "openresty.org" -SSL: TLSv1, cipher: "ECDHE-RSA-AES128-SHA SSLv3 +SSL: TLSv1, cipher: "ECDHE-RSA-AES256-SHA SSLv3 --- no_error_log SSL reused session [error] @@ -2034,7 +2034,7 @@ SSL reused session content_by_lua ' local sock = ngx.socket.tcp() - sock:settimeout(2000) + sock:settimeout(3000) do local ok, err = sock:connect("openresty.org", 443) @@ -2103,7 +2103,7 @@ lua ssl server name: "openresty.org" SSL reused session [error] [alert] ---- timeout: 5 +--- timeout: 6 diff --git a/t/140-ssl-c-api.t b/t/140-ssl-c-api.t index 8734d1477d..d2ffee97cc 100644 --- a/t/140-ssl-c-api.t +++ b/t/140-ssl-c-api.t @@ -62,6 +62,15 @@ ffi.cdef[[ void ngx_http_lua_ffi_free_priv_key(void *cdata); int ngx_http_lua_ffi_ssl_clear_certs(void *r, char **err); + + int ngx_http_lua_ffi_ssl_set_psk_key(void *r, + const char *key, size_t len, char **err); + + int ngx_http_lua_ffi_ssl_get_psk_identity(void *r, + char *buf, char **err); + + int ngx_http_lua_ffi_ssl_get_psk_identity_size(void *r, + char **err); ]] _EOC_ } @@ -811,3 +820,160 @@ lua ssl server name: "test.com" --- no_error_log [error] [alert] + + + +=== TEST 6: TLS-PSK +--- http_config + server { + listen unix:$TEST_NGINX_HTML_DIR/nginx.sock ssl; + server_name test.com; + + ssl_protocols TLSv1; + ssl_ciphers PSK; + + ssl_certificate_by_lua_block { + collectgarbage() + + local ffi = require "ffi" + require "defines" + + local errmsg = ffi.new("char *[1]") + + local r = getfenv(0).__ngx_req + if not r then + ngx.log(ngx.ERR, "no request found") + return + end + + local len = ffi.C.ngx_http_lua_ffi_ssl_get_psk_identity_size(r, errmsg) + + if len < 0 then + local error_msg = ffi.string(errmsg[0]) + if error_msg and error_msg == "not in psk context" then + -- handler was not called by TLS-PSK callback + return + end + ngx.log(ngx.ERR, "failed to get psk identity size: ", error_msg) + return + end + + if len > 4096 then + ngx.log(ngx.ERR, "psk identity size too long") + return + end + + local buf = ffi.new("char[?]", 4096) + + local rc = ffi.C.ngx_http_lua_ffi_ssl_get_psk_identity(r, buf, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to get psk identity: ", ffi.string(errmsg[0])) + return + end + + local psk_identity = ffi.string(buf, len) + if not psk_identity then + ngx.log(ngx.ERR, "psk_identity is undefined") + return + end + + local psk_key = "psk_test_key" + + local rc = ffi.C.ngx_http_lua_ffi_ssl_set_psk_key(r, psk_key, #psk_key, errmsg) + if rc ~= 0 then + ngx.log(ngx.ERR, "failed to set psk key: ", ffi.string(errmsg[0])) + return + end + + return ngx.OK + } + + ssl_certificate ../../cert/test.crt; + ssl_certificate_key ../../cert/test.key; + + ssl_psk_identity_hint psk_test_identity_hint; + + server_tokens off; + location /foo { + default_type 'text/plain'; + content_by_lua_block { ngx.status = 201 ngx.say("foo") ngx.exit(201) } + more_clear_headers Date; + } + } +--- config + server_tokens off; + + location /t { + lua_ssl_ciphers PSK; + lua_ssl_psk_identity psk_test_identity; + lua_ssl_psk_key psk_test_key; + + content_by_lua_block { + do + local sock = ngx.socket.tcp() + + sock:settimeout(2000) + + local ok, err = sock:connect("unix:$TEST_NGINX_HTML_DIR/nginx.sock") + if not ok then + ngx.say("failed to connect: ", err) + return + end + + ngx.say("connected: ", ok) + + local sess, err = sock:sslhandshake(nil, "test.com", false) + if not sess then + ngx.say("failed to do SSL handshake: ", err) + return + end + + ngx.say("ssl handshake: ", type(sess)) + + local req = "GET /foo HTTP/1.0\r\nHost: test.com\r\nConnection: close\r\n\r\n" + local bytes, err = sock:send(req) + if not bytes then + ngx.say("failed to send http request: ", err) + return + end + + ngx.say("sent http request: ", bytes, " bytes.") + + while true do + local line, err = sock:receive() + if not line then + -- ngx.say("failed to recieve response status line: ", err) + break + end + + ngx.say("received: ", line) + end + + local ok, err = sock:close() + ngx.say("close: ", ok, " ", err) + end -- do + -- collectgarbage() + } + } + +--- request +GET /t +--- response_body +connected: 1 +ssl handshake: userdata +sent http request: 56 bytes. +received: HTTP/1.1 201 Created +received: Server: nginx +received: Content-Type: text/plain +received: Content-Length: 4 +received: Connection: close +received: +received: foo +close: 1 nil + +--- error_log +lua ssl server name: "test.com" + +--- no_error_log +[error] +[alert]