Page MenuHomeFreeBSD

D48570.id149617.diff
No OneTemporary

D48570.id149617.diff

diff --git a/sys/rpc/rpcsec_tls.h b/sys/rpc/rpcsec_tls.h
--- a/sys/rpc/rpcsec_tls.h
+++ b/sys/rpc/rpcsec_tls.h
@@ -32,9 +32,6 @@
#define RPCTLS_SYSC_CLSOCKET 2
#define RPCTLS_SYSC_SRVSOCKET 5
-/* Max nprocs for SRV startup */
-#define RPCTLS_SRV_MAXNPROCS 16
-
/* System call used by the rpctlscd, rpctlssd daemons. */
int rpctls_syscall(int, const char *);
diff --git a/usr.sbin/rpc.tlsservd/Makefile b/usr.sbin/rpc.tlsservd/Makefile
--- a/usr.sbin/rpc.tlsservd/Makefile
+++ b/usr.sbin/rpc.tlsservd/Makefile
@@ -6,7 +6,7 @@
CFLAGS+= -I.
-LIBADD= ssl crypto util
+LIBADD= ssl crypto util pthread
CLEANFILES= rpctlssd_svc.c rpctlssd_xdr.c rpctlssd.h
diff --git a/usr.sbin/rpc.tlsservd/rpc.tlsservd.c b/usr.sbin/rpc.tlsservd/rpc.tlsservd.c
--- a/usr.sbin/rpc.tlsservd/rpc.tlsservd.c
+++ b/usr.sbin/rpc.tlsservd/rpc.tlsservd.c
@@ -4,6 +4,7 @@
* Copyright (c) 2008 Isilon Inc http://www.isilon.com/
* Authors: Doug Rabson <dfr@rabson.org>
* Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
+ * Copyright (c) 2025 Gleb Smirnoff <glebius@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -32,27 +33,20 @@
* the server side of kernel RPC-over-TLS by Rick Macklem.
*/
-#include <sys/param.h>
#include <sys/types.h>
#include <sys/linker.h>
#include <sys/module.h>
#include <sys/queue.h>
-#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/syslog.h>
-#include <sys/time.h>
-#include <sys/wait.h>
+#include <assert.h>
#include <err.h>
#include <getopt.h>
#include <libutil.h>
#include <netdb.h>
+#include <pthread.h>
#include <pwd.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <stdbool.h>
-#include <string.h>
#include <unistd.h>
#include <rpc/rpc.h>
@@ -86,7 +80,11 @@
const char *rpctls_verify_capath = NULL;
char *rpctls_crlfile = NULL;
bool rpctls_gothup = false;
-struct ssl_list rpctls_ssllist;
+
+static SVCXPRT *xprt;
+static pthread_key_t xidkey;
+static pthread_rwlock_t rpctls_lock;
+struct ssl_list rpctls_ssllist;
static struct pidfh *rpctls_pfh = NULL;
static bool rpctls_do_mutual = false;
@@ -98,9 +96,6 @@
static const char *rpctls_cnuseroid = "1.3.6.1.4.1.2238.1.1.1";
static const char *rpctls_ciphers = NULL;
static int rpctls_mintls = TLS1_3_VERSION;
-static int rpctls_procs = 1;
-static pid_t rpctls_workers[RPCTLS_SRV_MAXNPROCS - 1];
-static bool rpctls_im_a_worker = false;
static void rpctls_cleanup_term(int sig);
static SSL_CTX *rpctls_setup_ssl(const char *certdir);
@@ -114,6 +109,8 @@
extern void rpctlssd_2(struct svc_req *rqstp, SVCXPRT *transp);
+static void * dummy_thread(void *v __unused) { return (NULL); }
+
static struct option longopts[] = {
{ "allowtls1_2", no_argument, NULL, '2' },
{ "ciphers", required_argument, NULL, 'C' },
@@ -136,13 +133,12 @@
int
main(int argc, char **argv)
{
- int ch, i;
- SVCXPRT *xprt;
+ int ch;
char hostname[MAXHOSTNAMELEN + 2];
pid_t otherpid;
+ pthread_t tid;
bool tls_enable;
size_t tls_enable_len;
- sigset_t signew;
/* Check that another rpctlssd isn't already running. */
rpctls_pfh = pidfile_open(_PATH_RPCTLSSDPID, 0600, &otherpid);
@@ -191,11 +187,7 @@
rpctls_do_mutual = true;
break;
case 'N':
- rpctls_procs = atoi(optarg);
- if (rpctls_procs < 1 ||
- rpctls_procs > RPCTLS_SRV_MAXNPROCS)
- errx(1, "numdaemons/-N must be between 1 and "
- "%d", RPCTLS_SRV_MAXNPROCS);
+ warnx("%s: -N option is obsolete", argv[0]);
break;
case 'n':
hostname[0] = '@';
@@ -233,7 +225,6 @@
"[-D/--certdir certdir] [-d/--debuglevel] "
"[-h/--checkhost] "
"[-l/--verifylocs CAfile] [-m/--mutualverf] "
- "[-N/--numdaemons num] "
"[-n/--domain domain_name] "
"[-p/--verifydir CApath] [-r/--crl CRLfile] "
"[-u/--certuser] [-v/--verbose] [-W/--multiwild] "
@@ -262,53 +253,19 @@
if (kldload("krpc") < 0 || modfind("krpc") < 0)
errx(1, "Kernel RPC is not available");
}
-
- for (i = 0; i < rpctls_procs - 1; i++)
- rpctls_workers[i] = -1;
-
- if (rpctls_debug_level == 0) {
- /*
- * Temporarily block SIGTERM and SIGCHLD, so workers[] can't
- * end up bogus.
- */
- sigemptyset(&signew);
- sigaddset(&signew, SIGTERM);
- sigaddset(&signew, SIGCHLD);
- sigprocmask(SIG_BLOCK, &signew, NULL);
-
- if (daemon(0, 0) != 0)
- err(1, "Can't daemonize");
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- }
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, rpctls_huphandler);
signal(SIGTERM, rpctls_cleanup_term);
- signal(SIGCHLD, rpctls_cleanup_term);
+ if (rpctls_debug_level == 0 && daemon(0, 0) != 0)
+ err(1, "Can't daemonize");
pidfile_write(rpctls_pfh);
- if (rpctls_debug_level == 0) {
- /* Fork off the worker daemons. */
- for (i = 0; i < rpctls_procs - 1; i++) {
- rpctls_workers[i] = fork();
- if (rpctls_workers[i] == 0) {
- rpctls_im_a_worker = true;
- setproctitle("server");
- break;
- } else if (rpctls_workers[i] < 0) {
- syslog(LOG_ERR, "fork: %m");
- }
- }
-
- if (!rpctls_im_a_worker && rpctls_procs > 1)
- setproctitle("master");
- }
- sigemptyset(&signew);
- sigaddset(&signew, SIGTERM);
- sigaddset(&signew, SIGCHLD);
- sigprocmask(SIG_UNBLOCK, &signew, NULL);
-
+ /*
+ * XXX: Push libc internal state into threaded mode before creating
+ * the threaded svc_nl xprt.
+ */
+ (void)pthread_create(&tid, NULL, dummy_thread, NULL);
if ((xprt = svc_nl_create("tlsserv")) == NULL) {
if (rpctls_debug_level == 0) {
syslog(LOG_ERR,
@@ -317,6 +274,8 @@
}
err(1, "Can't create transport for local rpctlssd socket");
}
+ if (!SVC_CONTROL(xprt, SVCNL_GET_XIDKEY, &xidkey))
+ err(1, "Failed to obtain pthread key for xid from svc_nl");
if (!svc_reg(xprt, RPCTLSSD, RPCTLSSDVERS, rpctlssd_2, NULL)) {
if (rpctls_debug_level == 0) {
syslog(LOG_ERR,
@@ -335,6 +294,7 @@
err(1, "Can't create SSL context");
}
rpctls_gothup = false;
+ pthread_rwlock_init(&rpctls_lock, NULL);
LIST_INIT(&rpctls_ssllist);
rpctls_svc_run();
@@ -352,24 +312,81 @@
return (TRUE);
}
+/*
+ * To parallelize SSL handshakes we will launch a thread per handshake. Thread
+ * creation/destruction shall be order(s) of magnitude cheaper than a crypto
+ * handshake, so we are not keeping a pool of workers here.
+ *
+ * Marrying rpc(3) and pthread(3):
+ *
+ * Normally the rpcgen(1) generated rpctlssd_V() calls rpctlssd_connect_V_svc(),
+ * and the latter processes the RPC all the way to the end and returns a TRUE
+ * value and populates the result. The generated code immediately calls
+ * svc_sendreply() transmitting the result back.
+ *
+ * We will make a private copy of arguments and return FALSE. Then it is our
+ * obligation to call svc_sendreply() once we do the work in the thread.
+ */
+
+static void * rpctlssd_connect_thread(void *);
+struct rpctlssd_connect_thread_ctx {
+ struct rpctlssd_connect_arg arg;
+ uint32_t xid;
+};
+
bool_t
rpctlssd_connect_2_svc(struct rpctlssd_connect_arg *argp,
- struct rpctlssd_connect_res *result, __unused struct svc_req *rqstp)
+ struct rpctlssd_connect_res *result __unused, struct svc_req *rqstp)
{
+ struct rpctlssd_connect_thread_ctx *ctx;
+ pthread_t tid;
+
+ assert(rqstp->rq_xprt == xprt);
+
+ ctx = malloc(sizeof(*ctx));
+ memcpy(&ctx->arg, argp, sizeof(ctx->arg));
+ ctx->xid = *(uint32_t *)pthread_getspecific(xidkey);
+
+ rpctls_verbose_out("rpctlsd_connect_svc: xid %u\n", ctx->xid);
+
+ if (pthread_create(&tid, NULL, rpctlssd_connect_thread, ctx) != 0)
+ warn("failed to start handshake thread");
+
+ /* Intentionally, so that RPC generated code doesn't try to reply. */
+ return (FALSE);
+}
+
+static void *
+rpctlssd_connect_thread(void *v)
+{
+ struct rpctlssd_connect_thread_ctx *ctx = v;
+ struct rpctlssd_connect_res result;
+ uint64_t socookie;
int ngrps, s;
SSL *ssl;
uint32_t flags;
struct ssl_entry *newslp;
- uint32_t uid;
+ uint32_t xid, uid;
uint32_t *gidp;
X509 *cert;
- rpctls_verbose_out("rpctlsd_connect_svc: started\n");
- memset(result, 0, sizeof(*result));
+ socookie = ctx->arg.socookie;
+ xid = ctx->xid;
+ free(ctx);
+
+ if (pthread_setspecific(xidkey, &xid) != 0) {
+ rpctls_verbose_out("rpctlssd_connect_svc: pthread_setspecific "
+ "failed\n");
+ return (NULL);
+ }
+
/* Get the socket fd from the kernel. */
- s = rpctls_syscall(RPCTLS_SYSC_SRVSOCKET, (char *)argp->socookie);
- if (s < 0)
- return (FALSE);
+ s = rpctls_syscall(RPCTLS_SYSC_SRVSOCKET, (char *)socookie);
+ if (s < 0) {
+ rpctls_verbose_out("rpctlssd_connect_svc: rpctls_syscall "
+ "accept failed\n");
+ return (NULL);
+ }
/* Do the server side of a TLS handshake. */
gidp = calloc(NGROUPS, sizeof(*gidp));
@@ -383,20 +400,24 @@
* to close off the socket upon handshake failure.
*/
close(s);
- return (FALSE);
+ return (NULL);
} else {
rpctls_verbose_out("rpctlssd_connect_svc: "
"succeeded flags=0x%x\n", flags);
- result->flags = flags;
- if ((flags & RPCTLS_FLAGS_CERTUSER) != 0) {
- result->uid = uid;
- result->gid.gid_len = ngrps;
- result->gid.gid_val = gidp;
- } else {
- result->uid = 0;
- result->gid.gid_len = 0;
- result->gid.gid_val = gidp;
- }
+ if ((flags & RPCTLS_FLAGS_CERTUSER) != 0)
+ result = (struct rpctlssd_connect_res){
+ .flags = flags,
+ .uid = uid,
+ .gid.gid_len = ngrps,
+ .gid.gid_val = gidp,
+ };
+ else
+ result = (struct rpctlssd_connect_res){
+ .flags = flags,
+ .uid = 0,
+ .gid.gid_len = 0,
+ .gid.gid_val = gidp,
+ };
}
/* Maintain list of all current SSL *'s */
@@ -404,10 +425,20 @@
newslp->ssl = ssl;
newslp->s = s;
newslp->shutoff = false;
- newslp->refno = argp->socookie;
+ newslp->refno = socookie;
newslp->cert = cert;
+ pthread_rwlock_wrlock(&rpctls_lock);
LIST_INSERT_HEAD(&rpctls_ssllist, newslp, next);
- return (TRUE);
+ pthread_rwlock_unlock(&rpctls_lock);
+
+ if (!svc_sendreply(xprt, (xdrproc_t)xdr_rpctlssd_connect_res, &result))
+ svcerr_systemerr(xprt);
+
+ free(result.gid.gid_val);
+ rpctls_verbose_out("rpctlsd_connect_svc: xid %u: thread finished\n",
+ xid);
+
+ return (NULL);
}
bool_t
@@ -418,9 +449,11 @@
int ret;
char junk;
+ pthread_rwlock_rdlock(&rpctls_lock);
LIST_FOREACH(slp, &rpctls_ssllist, next)
if (slp->refno == argp->socookie)
break;
+ pthread_rwlock_unlock(&rpctls_lock);
if (slp != NULL) {
rpctls_verbose_out("rpctlssd_handlerecord fd=%d\n",
@@ -455,14 +488,17 @@
struct ssl_entry *slp;
int ret;
+ pthread_rwlock_wrlock(&rpctls_lock);
LIST_FOREACH(slp, &rpctls_ssllist, next)
- if (slp->refno == argp->socookie)
+ if (slp->refno == argp->socookie) {
+ LIST_REMOVE(slp, next);
break;
+ }
+ pthread_rwlock_unlock(&rpctls_lock);
if (slp != NULL) {
rpctls_verbose_out("rpctlssd_disconnect fd=%d closed\n",
slp->s);
- LIST_REMOVE(slp, next);
if (!slp->shutoff) {
ret = SSL_get_shutdown(slp->ssl);
/*
@@ -490,15 +526,9 @@
}
int
-rpctlssd_2_freeresult(__unused SVCXPRT *transp, xdrproc_t xdr_result,
- caddr_t result)
+rpctlssd_2_freeresult(__unused SVCXPRT *transp, xdrproc_t xdr_result __unused,
+ caddr_t result __unused)
{
- rpctlssd_connect_res *res;
-
- if (xdr_result == (xdrproc_t)xdr_rpctlssd_connect_res) {
- res = (rpctlssd_connect_res *)(void *)result;
- free(res->gid.gid_val);
- }
return (TRUE);
}
@@ -506,36 +536,14 @@
* cleanup_term() called via SIGTERM (or SIGCHLD if a child dies).
*/
static void
-rpctls_cleanup_term(int sig)
+rpctls_cleanup_term(int sig __unused)
{
struct ssl_entry *slp;
- int i, cnt;
- if (rpctls_im_a_worker && sig == SIGCHLD)
- return;
LIST_FOREACH(slp, &rpctls_ssllist, next)
shutdown(slp->s, SHUT_RD);
SSL_CTX_free(rpctls_ctx);
EVP_cleanup();
-
- if (rpctls_im_a_worker)
- exit(0);
-
- /* I'm the server, so terminate the workers. */
- cnt = 0;
- for (i = 0; i < rpctls_procs - 1; i++) {
- if (rpctls_workers[i] != -1) {
- cnt++;
- kill(rpctls_workers[i], SIGTERM);
- }
- }
-
- /*
- * Wait for them to die.
- */
- for (i = 0; i < cnt; i++)
- wait3(NULL, 0, NULL);
-
pidfile_remove(rpctls_pfh);
exit(0);

File Metadata

Mime Type
text/plain
Expires
Thu, May 1, 6:20 PM (16 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17888146
Default Alt Text
D48570.id149617.diff (11 KB)

Event Timeline