Date: Fri, 3 Apr 2020 22:13:54 +0000 (UTC) From: Rick Macklem <rmacklem@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r359618 - projects/nfs-over-tls/usr.sbin/rpctlscd Message-ID: <202004032213.033MDsuR005582@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: rmacklem Date: Fri Apr 3 22:13:53 2020 New Revision: 359618 URL: https://svnweb.freebsd.org/changeset/base/359618 Log: Update the rpctlscd in several areas. This patch updates/fixes the rpctlscd in the following areas: - Fix handling of the CRL file and add code to reload it when a SIGHUP is posted to the daemon. - Move the creation of the SSL_CTX * to before the program daemonizes. This was done so that it can prompt for a passphrase for the case where the client has a certificate with an encrypted key. - Fix up options. - Make the handling of the server hostname in the certificate not accept a wildcard, as recommended by RFC6125. Modified: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c Modified: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c ============================================================================== --- projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c Fri Apr 3 22:06:55 2020 (r359617) +++ projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c Fri Apr 3 22:13:53 2020 (r359618) @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #include <sys/syslog.h> #include <sys/time.h> #include <err.h> +#include <libutil.h> #include <netdb.h> #include <signal.h> #include <stdarg.h> @@ -67,10 +68,14 @@ __FBSDID("$FreeBSD$"); #ifndef _PATH_CERTANDKEY #define _PATH_CERTANDKEY "/etc/rpctlscd/" #endif +#ifndef _PATH_RPCTLSCDPID +#define _PATH_RPCTLSCDPID "/var/run/rpctlscd.pid" +#endif #ifndef _PREFERRED_CIPHERS #define _PREFERRED_CIPHERS "SHA384:SHA256:!CAMELLIA" #endif +static struct pidfh *rpctls_pfh = NULL; static int rpctls_debug_level; static bool rpctls_verbose; static int testnossl; @@ -79,8 +84,6 @@ static const char *rpctls_verify_cafile = NULL; static const char *rpctls_verify_capath = NULL; static const char *rpctls_crlfile = NULL; static const char *rpctls_certdir = _PATH_CERTANDKEY; -static bool rpctls_verify = false; -static bool rpctls_comparehost = false; static uint64_t rpctls_ssl_refno = 0; static uint64_t rpctls_ssl_sec = 0; static uint64_t rpctls_ssl_usec = 0; @@ -104,8 +107,10 @@ static struct ssl_list rpctls_ssllist; static void rpctlscd_terminate(int); static SSL_CTX *rpctls_setupcl_ssl(bool cert); static SSL *rpctls_connect(SSL_CTX *ctx, int s); -static int rpctls_checkhost(int s, X509 *cert); -static int rpctls_loadfiles(SSL_CTX *ctx); +static int rpctls_gethost(int s, struct sockaddr *sad, + char *hostip, size_t hostlen); +static int rpctls_checkhost(struct sockaddr *sad, X509 *cert); +static int rpctls_loadcrlfile(SSL_CTX *ctx); static void rpctls_huphandler(int sig __unused); extern void rpctlscd_1(struct svc_req *rqstp, SVCXPRT *transp); @@ -125,7 +130,16 @@ main(int argc, char **argv) bool cert; struct timeval tm; struct timezone tz; + pid_t otherpid; + /* Check that another rpctlscd isn't already running. */ + rpctls_pfh = pidfile_open(_PATH_RPCTLSCDPID, 0600, &otherpid); + if (rpctls_pfh == NULL) { + if (errno == EEXIST) + errx(1, "rpctlscd already running, pid: %d.", otherpid); + warn("cannot open or create pidfile"); + } + /* Get the time when this daemon is started. */ gettimeofday(&tm, &tz); rpctls_ssl_sec = tm.tv_sec; @@ -134,7 +148,7 @@ main(int argc, char **argv) rpctls_verbose = false; testnossl = 0; cert = false; - while ((ch = getopt(argc, argv, "D:dhl:mp:rtVv")) != -1) { + while ((ch = getopt(argc, argv, "D:dl:mp:r:tv")) != -1) { switch (ch) { case 'D': rpctls_certdir = optarg; @@ -142,9 +156,6 @@ main(int argc, char **argv) case 'd': rpctls_debug_level++; break; - case 'h': - rpctls_comparehost = true; - break; case 'l': rpctls_verify_cafile = optarg; break; @@ -160,22 +171,23 @@ main(int argc, char **argv) case 't': testnossl = 1; break; - case 'V': - rpctls_verify = true; - break; case 'v': rpctls_verbose = true; break; default: fprintf(stderr, "usage: %s " - "[-D certdir] [-d] [-h] " + "[-D certdir] [-d] " "[-l CAfile] [-m] " "[-p CApath] [-r CRLfile] " - "[-V] [-v]\n", argv[0]); + "[-v]\n", argv[0]); exit(1); break; } } + if (rpctls_crlfile != NULL && rpctls_verify_cafile == NULL && + rpctls_verify_capath == NULL) + errx(1, "-r requires the -l <CAfile> and/or " + "-p <CApath> options"); if (modfind("krpc") < 0) { /* Not present in kernel, try loading it */ @@ -183,6 +195,21 @@ main(int argc, char **argv) errx(1, "Kernel RPC is not available"); } + /* + * Set up the SSL_CTX *. + * Do it now, before daemonizing, in case the private key + * is encrypted and requires a passphrase to be entered. + */ + rpctls_ctx = rpctls_setupcl_ssl(cert); + if (rpctls_ctx == NULL) { + if (rpctls_debug_level == 0) { + syslog(LOG_ERR, "Can't set up TSL context"); + exit(1); + } + err(1, "Can't set up TSL context"); + } + LIST_INIT(&rpctls_ssllist); + if (!rpctls_debug_level) { if (daemon(0, 0) != 0) err(1, "Can't daemonize"); @@ -194,6 +221,8 @@ main(int argc, char **argv) signal(SIGPIPE, rpctlscd_terminate); signal(SIGHUP, rpctls_huphandler); + pidfile_write(rpctls_pfh); + memset(&sun, 0, sizeof sun); sun.sun_family = AF_LOCAL; unlink(_PATH_RPCTLSCDSOCK); @@ -242,17 +271,6 @@ main(int argc, char **argv) err(1, "Can't register service for local rpctlscd socket"); } - /* Set up the OpenSSL TSL stuff. */ - rpctls_ctx = rpctls_setupcl_ssl(cert); - if (rpctls_ctx == NULL) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, "Can't set up TSL context"); - exit(1); - } - err(1, "Can't set up TSL context"); - } - LIST_INIT(&rpctls_ssllist); - gssd_syscall(_PATH_RPCTLSCDSOCK); svc_run(); gssd_syscall(""); @@ -390,6 +408,7 @@ rpctlscd_terminate(int sig __unused) { gssd_syscall(""); + pidfile_remove(rpctls_pfh); exit(0); } @@ -461,10 +480,10 @@ rpctls_setupcl_ssl(bool cert) } if (rpctls_verify_cafile != NULL || rpctls_verify_capath != NULL) { if (rpctls_crlfile != NULL) { - ret = rpctls_loadfiles(ctx); + ret = rpctls_loadcrlfile(ctx); if (ret == 0) { - rpctlscd_verbose_out("rpctls_setup_ssl: " - "Load CAfile, CRLfile failed\n"); + rpctlscd_verbose_out("rpctls_setupcl_ssl: " + "Load CRLfile failed\n"); SSL_CTX_free(ctx); return (NULL); } @@ -503,15 +522,18 @@ rpctls_connect(SSL_CTX *ctx, int s) { SSL *ssl; X509 *cert; - int ret; - char *cp; + struct sockaddr *sad; + struct sockaddr_storage ad; + char hostnam[NI_MAXHOST]; + int gethostret, ret; + char *cp, *cp2; if (rpctls_gothup) { rpctls_gothup = false; - ret = rpctls_loadfiles(ctx); + ret = rpctls_loadcrlfile(ctx); if (ret == 0) rpctlscd_verbose_out("rpctls_connect: Can't " - "load CAfile, CRLfile\n"); + "reload CRLfile\n"); } ssl = SSL_new(ctx); if (ssl == NULL) { @@ -542,18 +564,27 @@ rpctls_connect(SSL_CTX *ctx, int s) SSL_free(ssl); return (NULL); } - cp = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); - rpctlscd_verbose_out("rpctls_connect: cert issuerName=%s\n", cp); - cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); - rpctlscd_verbose_out("rpctls_connect: cert subjectName=%s\n", cp); + gethostret = rpctls_gethost(s, sad, hostnam, sizeof(hostnam)); + if (gethostret == 0) + hostnam[0] = '\0'; ret = SSL_get_verify_result(ssl); - rpctlscd_verbose_out("rpctls_connect: get " - "verify result=%d\n", ret); - if (ret == X509_V_OK && rpctls_comparehost && - rpctls_checkhost(s, cert) != 1) + if (ret == X509_V_OK && (rpctls_verify_cafile != NULL || + rpctls_verify_capath != NULL) && (gethostret == 0 || + rpctls_checkhost(sad, cert) != 1)) ret = X509_V_ERR_HOSTNAME_MISMATCH; X509_free(cert); - if (rpctls_verify && ret != X509_V_OK) { + if (ret != X509_V_OK && (rpctls_verify_cafile != NULL || + rpctls_verify_capath != NULL)) { + if (ret != X509_V_OK) { + cp = X509_NAME_oneline(X509_get_issuer_name(cert), + NULL, 0); + cp2 = X509_NAME_oneline(X509_get_subject_name(cert), + NULL, 0); + syslog(LOG_INFO | LOG_DAEMON, "rpctls_connect: client" + " IP %s issuerName=%s subjectName=%s verify " + "failed %s\n", hostnam, cp, cp2, + X509_verify_cert_error_string(ret)); + } SSL_shutdown(ssl); SSL_free(ssl); return (NULL); @@ -569,81 +600,81 @@ rpctls_connect(SSL_CTX *ctx, int s) } /* - * Check a client IP address against any host address in the - * certificate. Basically getpeername(2), getnameinfo(3) and - * X509_check_host(). + * Get the server's IP address. */ static int -rpctls_checkhost(int s, X509 *cert) +rpctls_gethost(int s, struct sockaddr *sad, char *hostip, size_t hostlen) { - struct sockaddr *sad; - struct sockaddr_storage ad; - char hostnam[NI_MAXHOST]; socklen_t slen; int ret; - sad = (struct sockaddr *)&ad; - slen = sizeof(ad); + slen = sizeof(struct sockaddr_storage); if (getpeername(s, sad, &slen) < 0) return (0); + ret = 0; if (getnameinfo((const struct sockaddr *)sad, - sad->sa_len, hostnam, sizeof(hostnam), - NULL, 0, NI_NUMERICHOST) == 0) - rpctlscd_verbose_out("rpctls_checkhost: %s\n", - hostnam); + sad->sa_len, hostip, hostlen, + NULL, 0, NI_NUMERICHOST) == 0) { + rpctlscd_verbose_out("rpctls_gethost: %s\n", + hostip); + ret = 1; + } + return (ret); +} + +/* + * Check a server IP address against any host address in the + * certificate. Basically getnameinfo(3) and + * X509_check_host(). + */ +static int +rpctls_checkhost(struct sockaddr *sad, X509 *cert) +{ + char hostnam[NI_MAXHOST]; + int ret; + if (getnameinfo((const struct sockaddr *)sad, sad->sa_len, hostnam, sizeof(hostnam), NULL, 0, NI_NAMEREQD) != 0) return (0); - rpctlscd_verbose_out("rpctls_checkhost: DNS %s\n", hostnam); - ret = X509_check_host(cert, hostnam, strlen(hostnam), 0, NULL); + rpctlscd_verbose_out("rpctls_checkhost: DNS %s\n", + hostnam); + ret = X509_check_host(cert, hostnam, strlen(hostnam), + X509_CHECK_FLAG_NO_WILDCARDS, NULL); return (ret); } /* - * Load the CAfile (and optionally CRLfile) into the certificate - * verification store. + * (re)load the CRLfile into the certificate verification store. */ static int -rpctls_loadfiles(SSL_CTX *ctx) +rpctls_loadcrlfile(SSL_CTX *ctx) { X509_STORE *certstore; X509_LOOKUP *certlookup; int ret; - if (rpctls_verify_cafile != NULL || - rpctls_verify_capath != NULL) { - if (rpctls_crlfile != NULL) { - certstore = SSL_CTX_get_cert_store(ctx); - certlookup = X509_STORE_add_lookup( - certstore, X509_LOOKUP_file()); - ret = 0; - if (certlookup != NULL) - ret = X509_load_crl_file(certlookup, - rpctls_crlfile, X509_FILETYPE_PEM); - if (ret != 0) - ret = X509_STORE_set_flags(certstore, - X509_V_FLAG_CRL_CHECK | - X509_V_FLAG_CRL_CHECK_ALL); - if (ret == 0) { - rpctlscd_verbose_out( - "rpctls_loadfiles: Can't" - " load CRLfile=%s\n", - rpctls_crlfile); - return (ret); - } - } - ret = SSL_CTX_load_verify_locations(ctx, - rpctls_verify_cafile, rpctls_verify_capath); + if ((rpctls_verify_cafile != NULL || + rpctls_verify_capath != NULL) && + rpctls_crlfile != NULL) { + certstore = SSL_CTX_get_cert_store(ctx); + certlookup = X509_STORE_add_lookup( + certstore, X509_LOOKUP_file()); + ret = 0; + if (certlookup != NULL) + ret = X509_load_crl_file(certlookup, + rpctls_crlfile, X509_FILETYPE_PEM); + if (ret != 0) + ret = X509_STORE_set_flags(certstore, + X509_V_FLAG_CRL_CHECK | + X509_V_FLAG_CRL_CHECK_ALL); if (ret == 0) { - rpctlscd_verbose_out("rpctls_loadfiles: " - "Can't load verify locations\n"); + rpctlscd_verbose_out( + "rpctls_loadcrlfile: Can't" + " load CRLfile=%s\n", + rpctls_crlfile); return (ret); } - if (rpctls_verify_cafile != NULL) - SSL_CTX_set_client_CA_list(ctx, - SSL_load_client_CA_file( - rpctls_verify_cafile)); } return (1); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202004032213.033MDsuR005582>