Skip to content
Draft
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
6 changes: 6 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@

Changes with FreeUnit 1.35.4 xx xxx 2026

*) Feature: TLS OCSP stapling. Setting "ocsp_staple": true on a listener's
"tls" object enables stapling; the OCSP response (DER) is loaded from
<name>.ocsp alongside the certificate in the certificate store and
served in the TLS handshake. Operator refreshes the response file
out-of-band (e.g. via cron + openssl ocsp).

*) Bugfix: fix router process CPU spin and connection hang under port
scanning load; CLOSE-WAIT sockets are now cleaned up properly on
client FIN, idle connection queue iteration fixed, systemd file
Expand Down
19 changes: 19 additions & 0 deletions auto/ssltls
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,25 @@ if [ $NXT_OPENSSL = YES ]; then
#endif
}"
. auto/feature


nxt_feature="OpenSSL OCSP stapling"
nxt_feature_name=NXT_HAVE_OPENSSL_OCSP
nxt_feature_run=
nxt_feature_incs=
nxt_feature_libs="$NXT_OPENSSL_LIBS"
nxt_feature_test="#include <openssl/ssl.h>
#include <openssl/tls1.h>

int main(void) {
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
SSL_CTX_set_tlsext_status_cb(ctx, NULL);
SSL_CTX_set_tlsext_status_arg(ctx, NULL);
SSL_set_tlsext_status_ocsp_resp(ssl, NULL, 0);
return 0;
}"
. auto/feature
fi


Expand Down
136 changes: 136 additions & 0 deletions src/nxt_cert.c
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,142 @@ nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
}


void
nxt_cert_store_get_ocsp(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
nxt_port_rpc_handler_t handler, void *ctx)
{
uint32_t stream;
nxt_int_t ret;
nxt_buf_t *b;
nxt_port_t *main_port, *recv_port;
nxt_runtime_t *rt;

b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
if (nxt_slow_path(b == NULL)) {
goto fail;
}

b->completion_handler = nxt_cert_buf_completion;

nxt_buf_cpystr(b, name);
*b->mem.free++ = '\0';

rt = task->thread->runtime;
main_port = rt->port_by_type[NXT_PROCESS_MAIN];
recv_port = rt->port_by_type[rt->type];

stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler,
-1, ctx);
if (nxt_slow_path(stream == 0)) {
goto fail;
}

ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_OCSP_GET, -1,
stream, recv_port->id, b);

if (nxt_slow_path(ret != NXT_OK)) {
nxt_port_rpc_cancel(task, recv_port, stream);
goto fail;
}

/*
* Retain only after the buffer has been handed off to the port
* machinery; the failure paths above otherwise leave the pool with a
* refcount that the completion handler can never release.
*/
nxt_mp_retain(mp);

return;

fail:

handler(task, NULL, ctx);
}


void
nxt_cert_store_get_ocsp_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
{
u_char *p;
nxt_int_t ret;
nxt_str_t name;
nxt_file_t file;
nxt_port_t *port;
nxt_runtime_t *rt;
nxt_port_msg_type_t type;
static const char ocsp_suffix[] = ".ocsp";

port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
msg->port_msg.reply_port);

if (nxt_slow_path(port == NULL)) {
nxt_alert(task, "process port not found (pid %PI, reply_port %d)",
msg->port_msg.pid, msg->port_msg.reply_port);
return;
}

if (nxt_slow_path(port->type != NXT_PROCESS_ROUTER)) {
nxt_alert(task, "process %PI cannot fetch OCSP responses",
msg->port_msg.pid);
return;
}

nxt_memzero(&file, sizeof(nxt_file_t));

file.fd = -1;
/*
* RPC_READY_LAST with fd == -1 signals "no OCSP file present"
* to the router; missing siblings are not errors.
*/
type = NXT_PORT_MSG_RPC_READY_LAST;

rt = task->thread->runtime;

if (nxt_slow_path(rt->certs.start == NULL)) {
goto reply;
}

name.start = msg->buf->mem.pos;
name.length = nxt_strlen(name.start);

file.name = nxt_malloc(rt->certs.length + name.length
+ sizeof(ocsp_suffix));
Comment thread
andypost marked this conversation as resolved.

if (nxt_slow_path(file.name == NULL)) {
goto reply;
}

p = nxt_cpymem(file.name, rt->certs.start, rt->certs.length);
p = nxt_cpymem(p, name.start, name.length);
nxt_memcpy(p, ocsp_suffix, sizeof(ocsp_suffix));

ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN,
NXT_FILE_OWNER_ACCESS);

nxt_free(file.name);

if (ret == NXT_OK) {
type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
}

reply:

if (nxt_port_socket_write(task, port, type, file.fd,
msg->port_msg.stream, 0, NULL)
!= NXT_OK
&& file.fd != -1)
{
/*
* On send failure (e.g. malloc failure inside the port machinery)
* the port layer never takes ownership of the fd, so close it
* here to avoid leaking an open file descriptor in the privileged
* main process.
*/
nxt_fd_close(file.fd);
}
}


void
nxt_cert_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp)
{
Expand Down
4 changes: 4 additions & 0 deletions src/nxt_cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ void nxt_cert_store_release(nxt_array_t *certs);

void nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
nxt_port_rpc_handler_t handler, void *ctx);
void nxt_cert_store_get_ocsp(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
nxt_port_rpc_handler_t handler, void *ctx);
void nxt_cert_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp);

void nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg);
void nxt_cert_store_get_ocsp_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg);
void nxt_cert_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg);

#endif /* _NXT_CERT_INCLUDED_ */
9 changes: 9 additions & 0 deletions src/nxt_conf_validation.c
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,15 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
.type = NXT_CONF_VLDT_OBJECT,
.validator = nxt_conf_vldt_object,
.u.members = nxt_conf_vldt_session_members,
}, {
.name = nxt_string("ocsp_staple"),
.type = NXT_CONF_VLDT_BOOLEAN,
#if (NXT_HAVE_OPENSSL_OCSP)
.validator = NULL,
#else
.validator = nxt_conf_vldt_unsupported,
.u.string = "ocsp_staple",
#endif
},

NXT_CONF_VLDT_END
Expand Down
1 change: 1 addition & 0 deletions src/nxt_main_process.c
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,7 @@ static nxt_port_handlers_t nxt_main_process_port_handlers = {
.conf_store = nxt_main_port_conf_store_handler,
#if (NXT_TLS)
.cert_get = nxt_cert_store_get_handler,
.cert_ocsp_get = nxt_cert_store_get_ocsp_handler,
.cert_delete = nxt_cert_store_delete_handler,
#endif
#if (NXT_HAVE_NJS)
Expand Down
Loading
Loading