Skip to content

feature: add client SSL certificiate support #957

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions src/ngx_http_lua_socket_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
#include "ngx_http_lua_output.h"
#include "ngx_http_lua_contentby.h"
#include "ngx_http_lua_probe.h"
#include "ngx_http_lua_ssl.h"


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_setsslcert(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);
Expand Down Expand Up @@ -285,6 +287,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_setsslcert);
lua_setfield(L, -2, "setsslcert");

#endif

lua_pushcfunction(L, ngx_http_lua_socket_tcp_receive);
Expand Down Expand Up @@ -1200,6 +1205,81 @@ ngx_http_lua_socket_conn_error_retval_handler(ngx_http_request_t *r,

#if (NGX_HTTP_SSL)

static int
ngx_http_lua_socket_tcp_setsslcert(lua_State *L)
{
ngx_str_t password = ngx_null_string;

int n;
ngx_int_t rc;
ngx_str_t cert, priv_key;
ngx_connection_t *c;
ngx_http_request_t *r;
ngx_http_lua_socket_tcp_upstream_t *u;

/* Lua function arguments: self ,cert ,priv_key [,password] */

n = lua_gettop(L);
if (n < 1 || n > 4) {
return luaL_error(L, "ngx.socket setsslcert: expecting 1 ~ 4 "
"arguments (including the object), but seen %d", n);
}

r = ngx_http_lua_get_req(L);
if (r == NULL) {
return luaL_error(L, "no request found");
}

ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"lua tcp socket ssl set certificate");

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->request != r) {
return luaL_error(L, "bad request");
}

c = u->peer.connection;

if (c->ssl) {
lua_pushnil(L);
lua_pushliteral(L, "sslhandshaked");
return 2;
}

cert.data = (u_char *) luaL_checklstring(L, 2, &cert.len);
priv_key.data = (u_char *) luaL_checklstring(L, 3, &priv_key.len);

if (n == 4) {
password.data = (u_char *) luaL_checklstring(L, 4, &password.len);
}

rc = ngx_http_lua_ssl_certificate(u->conf->ssl, &cert, &priv_key,
&password, r->connection->log);
if (rc != NGX_OK) {
lua_pushnil(L);
lua_pushliteral(L, "failed to set ssl certificate");
return 2;
}

lua_pushinteger(L, 1);
return 1;
}


static int
ngx_http_lua_socket_tcp_sslhandshake(lua_State *L)
{
Expand Down
109 changes: 108 additions & 1 deletion src/ngx_http_lua_ssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

#if (NGX_HTTP_SSL)


int ngx_http_lua_ssl_ctx_index = -1;


Expand All @@ -34,4 +33,112 @@ ngx_http_lua_ssl_init(ngx_log_t *log)
}


int
ngx_http_lua_ssl_password_callback(char *buf, int size, int rwflag,
void *userdata)
{
ngx_str_t *pwd = userdata;

if (rwflag) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"ngx_http_lua_ssl_password_callback() "
"is called for encryption");
return 0;
}

if (pwd->len == 0) {
return 0;
}

if (pwd->len > (size_t) size) {
ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
"password is truncated to %d bytes", size);

} else {
size = pwd->len;
}

ngx_memcpy(buf, pwd->data, size);

return size;
}


ngx_int_t
ngx_http_lua_ssl_certificate(ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_str_t *priv_key, ngx_str_t *password, ngx_log_t *log)
{
BIO *cbio = NULL;
BIO *pbio = NULL;
X509 *x509 = NULL;
EVP_PKEY *pkey = NULL;
ngx_int_t rc = NGX_ERROR;

cbio = BIO_new_mem_buf((char *)cert->data, cert->len);
if (cbio == NULL) {
ngx_ssl_error(NGX_LOG_ERR, log, 0, "BIO_new_mem_buf() failed");
goto done;
}

/*
* Reading the PEM-formatted certificate from memory into an X509
*/

x509 = PEM_read_bio_X509(cbio, NULL, 0, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that we already have PEM (and DER) parsing functions, should users maybe use those and pass the already parsed chain to this function?

Copy link
Contributor Author

@detailyang detailyang Feb 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ghedo Yesterday I have try to reuse the already parsed chain. Finally I have found these data is type of Luajit's cdata and we cannot reference these data in Lua CFunction.:(

if (x509 == NULL) {
ngx_ssl_error(NGX_LOG_ERR, log, 0, "PEM_read_bio_X509() failed");
goto done;
}

if (!SSL_CTX_use_certificate(ssl->ctx, x509)) {
ngx_ssl_error(NGX_LOG_ERR, log, 0, "SSL_CTX_use_certificate() failed");
goto done;
}

pbio = BIO_new_mem_buf((char *)priv_key->data, priv_key->len);
if (pbio == NULL) {
ngx_ssl_error(NGX_LOG_ERR, log, 0, "BIO_new_mem_buf() failed");
goto done;
}

pkey = PEM_read_bio_PrivateKey(pbio, NULL,
Copy link
Contributor

@ghedo ghedo Feb 2, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above. We don't yet support parsing keys with passwords, but that should be added to the existing API instead IMO, so it would be more generally useful. Or better yet, add an additional function (e.g. ngx_http_lua_ffi_parse_pem_priv_key_with_password()) to do just that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I think this should be a separate PR, so this one would get smaller and easier to review.

ngx_http_lua_ssl_password_callback,
(void *)password);
if (pkey == NULL) {
ngx_ssl_error(NGX_LOG_ERR, log, 0, "PEM_read_bio_PrivateKey() failed");
goto done;
}

if (!SSL_CTX_use_PrivateKey(ssl->ctx, pkey)) {
ngx_ssl_error(NGX_LOG_ERR, log, 0, "SSL_CTX_use_PrivateKey() failed");
goto done;
}

rc = NGX_OK;

done:

if (pkey) {
EVP_PKEY_free(pkey);
}

if (x509) {
X509_free(x509);
}

if (pbio) {
BIO_free(pbio);
}

if (cbio) {
BIO_free(cbio);
}

if (rc == NGX_ERROR) {
ERR_clear_error();
}

return rc;
}

#endif /* NGX_HTTP_SSL */
4 changes: 4 additions & 0 deletions src/ngx_http_lua_ssl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ typedef struct {


ngx_int_t ngx_http_lua_ssl_init(ngx_log_t *log);
int ngx_http_lua_ssl_password_callback(char *buf, int size, int rwflag,
void *userdata);
ngx_int_t ngx_http_lua_ssl_certificate(ngx_ssl_t *ssl, ngx_str_t *cert,
ngx_str_t *priv_key, ngx_str_t *password, ngx_log_t *log);


extern int ngx_http_lua_ssl_ctx_index;
Expand Down
Loading