From owner-svn-src-projects@freebsd.org Fri Apr 3 22:21:11 2020 Return-Path: Delivered-To: svn-src-projects@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id A7E11274769 for ; Fri, 3 Apr 2020 22:21:11 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 48vDsy0PF0z4Tsc; Fri, 3 Apr 2020 22:21:09 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 518E3CEE2; Fri, 3 Apr 2020 22:13:54 +0000 (UTC) (envelope-from rmacklem@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id 033MDsv5005583; Fri, 3 Apr 2020 22:13:54 GMT (envelope-from rmacklem@FreeBSD.org) Received: (from rmacklem@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id 033MDsuR005582; Fri, 3 Apr 2020 22:13:54 GMT (envelope-from rmacklem@FreeBSD.org) Message-Id: <202004032213.033MDsuR005582@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: rmacklem set sender to rmacklem@FreeBSD.org using -f From: Rick Macklem Date: Fri, 3 Apr 2020 22:13:54 +0000 (UTC) To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r359618 - projects/nfs-over-tls/usr.sbin/rpctlscd X-SVN-Group: projects X-SVN-Commit-Author: rmacklem X-SVN-Commit-Paths: projects/nfs-over-tls/usr.sbin/rpctlscd X-SVN-Commit-Revision: 359618 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-projects@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the src " projects" tree" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 03 Apr 2020 22:21:11 -0000 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 #include #include +#include #include #include #include @@ -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 and/or " + "-p 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); }