Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/rpc.tlsservd/rpc.tlsservd.c
Show All 38 Lines | |||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/linker.h> | #include <sys/linker.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/stat.h> | #include <sys/stat.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/syslog.h> | #include <sys/syslog.h> | ||||
#include <sys/time.h> | #include <sys/time.h> | ||||
#include <sys/wait.h> | |||||
#include <err.h> | #include <err.h> | ||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <libutil.h> | #include <libutil.h> | ||||
#include <netdb.h> | #include <netdb.h> | ||||
#include <pwd.h> | #include <pwd.h> | ||||
#include <signal.h> | #include <signal.h> | ||||
#include <stdarg.h> | #include <stdarg.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
▲ Show 20 Lines • Show All 46 Lines • ▼ Show 20 Lines | |||||
static uint64_t rpctls_ssl_refno = 0; | static uint64_t rpctls_ssl_refno = 0; | ||||
static uint64_t rpctls_ssl_sec = 0; | static uint64_t rpctls_ssl_sec = 0; | ||||
static uint64_t rpctls_ssl_usec = 0; | static uint64_t rpctls_ssl_usec = 0; | ||||
static bool rpctls_cnuser = false; | static bool rpctls_cnuser = false; | ||||
static char *rpctls_dnsname; | static char *rpctls_dnsname; | ||||
static const char *rpctls_cnuseroid = "1.3.6.1.4.1.2238.1.1.1"; | static const char *rpctls_cnuseroid = "1.3.6.1.4.1.2238.1.1.1"; | ||||
static const char *rpctls_ciphers = NULL; | static const char *rpctls_ciphers = NULL; | ||||
static int rpctls_mintls = TLS1_3_VERSION; | static int rpctls_mintls = TLS1_3_VERSION; | ||||
static int rpctls_procs = 1; | |||||
static char *rpctls_sockname[RPCTLS_SRV_MAXNPROCS]; | |||||
static pid_t rpctls_workers[RPCTLS_SRV_MAXNPROCS - 1]; | |||||
static bool rpctls_im_a_worker = false; | |||||
static void rpctlssd_terminate(int); | static void rpctls_cleanup_term(int sig); | ||||
static SSL_CTX *rpctls_setup_ssl(const char *certdir); | static SSL_CTX *rpctls_setup_ssl(const char *certdir); | ||||
static SSL *rpctls_server(SSL_CTX *ctx, int s, | static SSL *rpctls_server(SSL_CTX *ctx, int s, | ||||
uint32_t *flags, uint32_t *uidp, | uint32_t *flags, uint32_t *uidp, | ||||
int *ngrps, uint32_t *gidp, X509 **certp); | int *ngrps, uint32_t *gidp, X509 **certp); | ||||
static int rpctls_cnname(X509 *cert, uint32_t *uidp, | static int rpctls_cnname(X509 *cert, uint32_t *uidp, | ||||
int *ngrps, uint32_t *gidp); | int *ngrps, uint32_t *gidp); | ||||
static char *rpctls_getdnsname(char *dnsname); | static char *rpctls_getdnsname(char *dnsname); | ||||
static void rpctls_huphandler(int sig __unused); | static void rpctls_huphandler(int sig __unused); | ||||
extern void rpctlssd_1(struct svc_req *rqstp, SVCXPRT *transp); | extern void rpctlssd_1(struct svc_req *rqstp, SVCXPRT *transp); | ||||
static struct option longopts[] = { | static struct option longopts[] = { | ||||
{ "allowtls1_2", no_argument, NULL, '2' }, | { "allowtls1_2", no_argument, NULL, '2' }, | ||||
{ "ciphers", required_argument, NULL, 'C' }, | { "ciphers", required_argument, NULL, 'C' }, | ||||
{ "certdir", required_argument, NULL, 'D' }, | { "certdir", required_argument, NULL, 'D' }, | ||||
{ "debuglevel", no_argument, NULL, 'd' }, | { "debuglevel", no_argument, NULL, 'd' }, | ||||
{ "checkhost", no_argument, NULL, 'h' }, | { "checkhost", no_argument, NULL, 'h' }, | ||||
{ "verifylocs", required_argument, NULL, 'l' }, | { "verifylocs", required_argument, NULL, 'l' }, | ||||
{ "mutualverf", no_argument, NULL, 'm' }, | { "mutualverf", no_argument, NULL, 'm' }, | ||||
{ "numdaemons", required_argument, NULL, 'N' }, | |||||
{ "domain", required_argument, NULL, 'n' }, | { "domain", required_argument, NULL, 'n' }, | ||||
{ "verifydir", required_argument, NULL, 'p' }, | { "verifydir", required_argument, NULL, 'p' }, | ||||
{ "crl", required_argument, NULL, 'r' }, | { "crl", required_argument, NULL, 'r' }, | ||||
{ "certuser", no_argument, NULL, 'u' }, | { "certuser", no_argument, NULL, 'u' }, | ||||
{ "verbose", no_argument, NULL, 'v' }, | { "verbose", no_argument, NULL, 'v' }, | ||||
{ "multiwild", no_argument, NULL, 'W' }, | { "multiwild", no_argument, NULL, 'W' }, | ||||
{ "singlewild", no_argument, NULL, 'w' }, | { "singlewild", no_argument, NULL, 'w' }, | ||||
{ NULL, 0, NULL, 0 } | { NULL, 0, NULL, 0 } | ||||
}; | }; | ||||
int | int | ||||
main(int argc, char **argv) | main(int argc, char **argv) | ||||
{ | { | ||||
/* | /* | ||||
* We provide an RPC service on a local-domain socket. The | * We provide an RPC service on a local-domain socket. The | ||||
* kernel rpctls code will upcall to this daemon to do the initial | * kernel rpctls code will upcall to this daemon to do the initial | ||||
* TLS handshake. | * TLS handshake. | ||||
*/ | */ | ||||
struct sockaddr_un sun; | struct sockaddr_un sun; | ||||
int ch, fd, oldmask; | int ch, fd, i, mypos, oldmask; | ||||
SVCXPRT *xprt; | SVCXPRT *xprt; | ||||
struct timeval tm; | struct timeval tm; | ||||
struct timezone tz; | struct timezone tz; | ||||
char hostname[MAXHOSTNAMELEN + 2]; | char hostname[MAXHOSTNAMELEN + 2]; | ||||
pid_t otherpid; | pid_t otherpid; | ||||
bool tls_enable; | bool tls_enable; | ||||
size_t tls_enable_len; | size_t tls_enable_len; | ||||
sigset_t signew; | |||||
/* Check that another rpctlssd isn't already running. */ | /* Check that another rpctlssd isn't already running. */ | ||||
rpctls_pfh = pidfile_open(_PATH_RPCTLSSDPID, 0600, &otherpid); | rpctls_pfh = pidfile_open(_PATH_RPCTLSSDPID, 0600, &otherpid); | ||||
if (rpctls_pfh == NULL) { | if (rpctls_pfh == NULL) { | ||||
if (errno == EEXIST) | if (errno == EEXIST) | ||||
errx(1, "rpctlssd already running, pid: %d.", otherpid); | errx(1, "rpctlssd already running, pid: %d.", otherpid); | ||||
warn("cannot open or create pidfile"); | warn("cannot open or create pidfile"); | ||||
} | } | ||||
Show All 11 Lines | main(int argc, char **argv) | ||||
/* Set the dns name for the server. */ | /* Set the dns name for the server. */ | ||||
rpctls_dnsname = rpctls_getdnsname(hostname); | rpctls_dnsname = rpctls_getdnsname(hostname); | ||||
if (rpctls_dnsname == NULL) { | if (rpctls_dnsname == NULL) { | ||||
strcpy(hostname, "@default.domain"); | strcpy(hostname, "@default.domain"); | ||||
rpctls_dnsname = hostname; | rpctls_dnsname = hostname; | ||||
} | } | ||||
/* Initialize socket names. */ | |||||
for (i = 0; i < RPCTLS_SRV_MAXNPROCS; i++) { | |||||
asprintf(&rpctls_sockname[i], "%s.%d", _PATH_RPCTLSSDSOCK, i); | |||||
if (rpctls_sockname[i] == NULL) | |||||
emaste: perhaps asprintf()? it takes care of the malloc for you | |||||
errx(1, "Cannot malloc socknames"); | |||||
} | |||||
rpctls_verbose = false; | rpctls_verbose = false; | ||||
while ((ch = getopt_long(argc, argv, "2C:D:dhl:n:mp:r:uvWw", longopts, | while ((ch = getopt_long(argc, argv, "2C:D:dhl:N:n:mp:r:uvWw", longopts, | ||||
NULL)) != -1) { | NULL)) != -1) { | ||||
switch (ch) { | switch (ch) { | ||||
case '2': | case '2': | ||||
rpctls_mintls = TLS1_2_VERSION; | rpctls_mintls = TLS1_2_VERSION; | ||||
break; | break; | ||||
case 'C': | case 'C': | ||||
rpctls_ciphers = optarg; | rpctls_ciphers = optarg; | ||||
break; | break; | ||||
case 'D': | case 'D': | ||||
rpctls_certdir = optarg; | rpctls_certdir = optarg; | ||||
break; | break; | ||||
case 'd': | case 'd': | ||||
rpctls_debug_level++; | rpctls_debug_level++; | ||||
break; | break; | ||||
case 'h': | case 'h': | ||||
rpctls_comparehost = true; | rpctls_comparehost = true; | ||||
break; | break; | ||||
case 'l': | case 'l': | ||||
rpctls_verify_cafile = optarg; | rpctls_verify_cafile = optarg; | ||||
break; | break; | ||||
case 'm': | case 'm': | ||||
rpctls_do_mutual = true; | rpctls_do_mutual = true; | ||||
break; | break; | ||||
case 'N': | |||||
rpctls_procs = atoi(optarg); | |||||
if (rpctls_procs < 1 || | |||||
rpctls_procs > RPCTLS_SRV_MAXNPROCS) | |||||
Done Inline ActionsIMO line splitting after the || makes it more clear (even if style(9) suggests the current form) if (rpctls_procs < 1 || rpctls_procs > RPCTLS_SRV_MAXNPROCS) emaste: IMO line splitting after the `||` makes it more clear (even if style(9) suggests the current… | |||||
errx(1, "numdaemons/-N must be between 1 and " | |||||
"%d", RPCTLS_SRV_MAXNPROCS); | |||||
break; | |||||
case 'n': | case 'n': | ||||
hostname[0] = '@'; | hostname[0] = '@'; | ||||
strlcpy(&hostname[1], optarg, MAXHOSTNAMELEN + 1); | strlcpy(&hostname[1], optarg, MAXHOSTNAMELEN + 1); | ||||
rpctls_dnsname = hostname; | rpctls_dnsname = hostname; | ||||
break; | break; | ||||
case 'p': | case 'p': | ||||
rpctls_verify_capath = optarg; | rpctls_verify_capath = optarg; | ||||
break; | break; | ||||
Show All 20 Lines | case 'w': | ||||
break; | break; | ||||
default: | default: | ||||
fprintf(stderr, "usage: %s " | fprintf(stderr, "usage: %s " | ||||
"[-2/--allowtls1_2] " | "[-2/--allowtls1_2] " | ||||
"[-C/--ciphers available_ciphers] " | "[-C/--ciphers available_ciphers] " | ||||
"[-D/--certdir certdir] [-d/--debuglevel] " | "[-D/--certdir certdir] [-d/--debuglevel] " | ||||
"[-h/--checkhost] " | "[-h/--checkhost] " | ||||
"[-l/--verifylocs CAfile] [-m/--mutualverf] " | "[-l/--verifylocs CAfile] [-m/--mutualverf] " | ||||
"[-N/--numdaemons num] " | |||||
"[-n/--domain domain_name] " | "[-n/--domain domain_name] " | ||||
"[-p/--verifydir CApath] [-r/--crl CRLfile] " | "[-p/--verifydir CApath] [-r/--crl CRLfile] " | ||||
"[-u/--certuser] [-v/--verbose] [-W/--multiwild] " | "[-u/--certuser] [-v/--verbose] [-W/--multiwild] " | ||||
"[-w/--singlewild]\n", argv[0]); | "[-w/--singlewild]\n", argv[0]); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
} | } | ||||
if (rpctls_do_mutual && rpctls_verify_cafile == NULL && | if (rpctls_do_mutual && rpctls_verify_cafile == NULL && | ||||
Show All 13 Lines | errx(1, "-u requires the -m plus the " | ||||
"-l <CAfile> and/or -p <CApath> options"); | "-l <CAfile> and/or -p <CApath> options"); | ||||
if (modfind("krpc") < 0) { | if (modfind("krpc") < 0) { | ||||
/* Not present in kernel, try loading it */ | /* Not present in kernel, try loading it */ | ||||
if (kldload("krpc") < 0 || modfind("krpc") < 0) | if (kldload("krpc") < 0 || modfind("krpc") < 0) | ||||
errx(1, "Kernel RPC is not available"); | errx(1, "Kernel RPC is not available"); | ||||
} | } | ||||
for (i = 0; i < rpctls_procs - 1; i++) | |||||
rpctls_workers[i] = -1; | |||||
mypos = 0; | |||||
if (rpctls_debug_level == 0) { | 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) | if (daemon(0, 0) != 0) | ||||
err(1, "Can't daemonize"); | err(1, "Can't daemonize"); | ||||
signal(SIGINT, SIG_IGN); | signal(SIGINT, SIG_IGN); | ||||
signal(SIGQUIT, SIG_IGN); | signal(SIGQUIT, SIG_IGN); | ||||
signal(SIGHUP, SIG_IGN); | |||||
} | } | ||||
signal(SIGTERM, rpctlssd_terminate); | |||||
signal(SIGPIPE, SIG_IGN); | signal(SIGPIPE, SIG_IGN); | ||||
signal(SIGHUP, rpctls_huphandler); | signal(SIGHUP, rpctls_huphandler); | ||||
signal(SIGTERM, rpctls_cleanup_term); | |||||
signal(SIGCHLD, rpctls_cleanup_term); | |||||
pidfile_write(rpctls_pfh); | pidfile_write(rpctls_pfh); | ||||
rpctls_syscall(RPCTLS_SYSC_SRVSTARTUP, ""); | |||||
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; | |||||
mypos = i + 1; | |||||
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); | |||||
memset(&sun, 0, sizeof sun); | memset(&sun, 0, sizeof sun); | ||||
sun.sun_family = AF_LOCAL; | sun.sun_family = AF_LOCAL; | ||||
unlink(_PATH_RPCTLSSDSOCK); | unlink(rpctls_sockname[mypos]); | ||||
strcpy(sun.sun_path, _PATH_RPCTLSSDSOCK); | strcpy(sun.sun_path, rpctls_sockname[mypos]); | ||||
sun.sun_len = SUN_LEN(&sun); | sun.sun_len = SUN_LEN(&sun); | ||||
fd = socket(AF_LOCAL, SOCK_STREAM, 0); | fd = socket(AF_LOCAL, SOCK_STREAM, 0); | ||||
if (fd < 0) { | if (fd < 0) { | ||||
if (rpctls_debug_level == 0) { | if (rpctls_debug_level == 0) { | ||||
syslog(LOG_ERR, "Can't create local rpctlssd socket"); | syslog(LOG_ERR, "Can't create local rpctlssd socket"); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
err(1, "Can't create local rpctlssd socket"); | err(1, "Can't create local rpctlssd socket"); | ||||
Show All 39 Lines | if (rpctls_debug_level == 0) { | ||||
syslog(LOG_ERR, "Can't create SSL context"); | syslog(LOG_ERR, "Can't create SSL context"); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
err(1, "Can't create SSL context"); | err(1, "Can't create SSL context"); | ||||
} | } | ||||
rpctls_gothup = false; | rpctls_gothup = false; | ||||
LIST_INIT(&rpctls_ssllist); | LIST_INIT(&rpctls_ssllist); | ||||
rpctls_syscall(RPCTLS_SYSC_SRVSETPATH, _PATH_RPCTLSSDSOCK); | rpctls_syscall(RPCTLS_SYSC_SRVSETPATH, rpctls_sockname[mypos]); | ||||
rpctls_svc_run(); | rpctls_svc_run(); | ||||
rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, ""); | |||||
SSL_CTX_free(rpctls_ctx); | SSL_CTX_free(rpctls_ctx); | ||||
EVP_cleanup(); | EVP_cleanup(); | ||||
return (0); | return (0); | ||||
} | } | ||||
bool_t | bool_t | ||||
rpctlssd_null_1_svc(__unused void *argp, __unused void *result, | rpctlssd_null_1_svc(__unused void *argp, __unused void *result, | ||||
__unused struct svc_req *rqstp) | __unused struct svc_req *rqstp) | ||||
▲ Show 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | rpctlssd_1_freeresult(__unused SVCXPRT *transp, xdrproc_t xdr_result, | ||||
if (xdr_result == (xdrproc_t)xdr_rpctlssd_connect_res) { | if (xdr_result == (xdrproc_t)xdr_rpctlssd_connect_res) { | ||||
res = (rpctlssd_connect_res *)(void *)result; | res = (rpctlssd_connect_res *)(void *)result; | ||||
free(res->gid.gid_val); | free(res->gid.gid_val); | ||||
} | } | ||||
return (TRUE); | return (TRUE); | ||||
} | } | ||||
/* | |||||
* cleanup_term() called via SIGTERM (or SIGCHLD if a child dies). | |||||
*/ | |||||
static void | static void | ||||
rpctlssd_terminate(int sig __unused) | rpctls_cleanup_term(int sig) | ||||
{ | { | ||||
struct ssl_entry *slp; | 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); | |||||
rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, ""); | rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, ""); | ||||
pidfile_remove(rpctls_pfh); | pidfile_remove(rpctls_pfh); | ||||
LIST_FOREACH(slp, &rpctls_ssllist, next) | |||||
shutdown(slp->s, SHUT_RD); | |||||
exit(0); | exit(0); | ||||
} | } | ||||
/* Allow the handshake to proceed. */ | /* Allow the handshake to proceed. */ | ||||
static int | static int | ||||
rpctls_verify_callback(__unused int preverify_ok, | rpctls_verify_callback(__unused int preverify_ok, | ||||
__unused X509_STORE_CTX *x509_ctx) | __unused X509_STORE_CTX *x509_ctx) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 382 Lines • Show Last 20 Lines |
perhaps asprintf()? it takes care of the malloc for you