Date: Sun, 8 Mar 2020 18:12:54 +0000 (UTC) From: Rick Macklem <rmacklem@FreeBSD.org> To: src-committers@freebsd.org, svn-src-projects@freebsd.org Subject: svn commit: r358755 - projects/nfs-over-tls/usr.sbin/rpctlscd Message-ID: <202003081812.028ICsts004880@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: rmacklem Date: Sun Mar 8 18:12:54 2020 New Revision: 358755 URL: https://svnweb.freebsd.org/changeset/base/358755 Log: Update rpctlscd to add options for handling of the certificate provided by the server when the handshake (SSL_connect()) is done. Also, temporarily switch it to use TLS1.2, since that is what will initially be supported by the KERN_TLS. 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 Sun Mar 8 18:12:07 2020 (r358754) +++ projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c Sun Mar 8 18:12:54 2020 (r358755) @@ -33,16 +33,22 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> +#include <sys/types.h> +#include <sys/socket.h> #include <sys/stat.h> #include <sys/syslog.h> #include <err.h> +#include <netdb.h> #include <signal.h> #include <stdarg.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <arpa/inet.h> + #include <rpc/rpc.h> #include <rpc/rpc_com.h> #include <rpc/rpcsec_tls.h> @@ -50,21 +56,30 @@ __FBSDID("$FreeBSD$"); #include <openssl/bio.h> #include <openssl/ssl.h> #include <openssl/err.h> +#include <openssl/x509v3.h> #include "rpctlscd.h" #ifndef _PATH_RPCTLSCDSOCK #define _PATH_RPCTLSCDSOCK "/var/run/rpctlscd.sock" #endif +#ifndef _PATH_CERTANDKEY +#define _PATH_CERTANDKEY "/etc/rpctlscd/" +#endif -static int rpctls_debug_level; -static int rpctls_verbose; +static int rpctls_debug_level; +static bool rpctls_verbose; static int testnossl; -static SSL_CTX *rpctls_ctx = NULL; +static SSL_CTX *rpctls_ctx = NULL; +static const char *rpctls_verify_cafile = NULL; +static const char *rpctls_certdir = _PATH_CERTANDKEY; +static bool rpctls_verify = false; +static bool rpctls_comparehost = false; -static void rpctlscd_terminate(int); -static SSL_CTX *rpctls_setupcl_ssl(char *certpath); -static SSL *rpctls_connect(SSL_CTX *ctx, int s); +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); extern void rpctlscd_1(struct svc_req *rqstp, SVCXPRT *transp); extern int gssd_syscall(const char *path); @@ -80,26 +95,42 @@ main(int argc, char **argv) struct sockaddr_un sun; int fd, oldmask, ch; SVCXPRT *xprt; - char *certpath; + bool cert; - rpctls_verbose = 0; + rpctls_verbose = false; testnossl = 0; - certpath = NULL; - while ((ch = getopt(argc, argv, "c:dtv")) != -1) { + cert = false; + while ((ch = getopt(argc, argv, "cD:dhl:tVv")) != -1) { switch (ch) { case 'c': - certpath = optarg; + cert = true; + break; + case 'D': + rpctls_certdir = optarg; + break; case 'd': rpctls_debug_level++; break; + case 'h': + rpctls_comparehost = true; + break; + case 'l': + rpctls_verify_cafile = optarg; + break; case 't': testnossl = 1; break; + case 'V': + rpctls_verify = true; + break; case 'v': - rpctls_verbose = 1; + rpctls_verbose = true; break; default: - fprintf(stderr, "usage: %s [-d] [-v]\n", argv[0]); + fprintf(stderr, "usage: %s [-c] " + "[-D certdir] [-d] [-h] " + "[-l verify_locations_file] " + "[-V] [-v]\n", argv[0]); exit(1); break; } @@ -164,7 +195,7 @@ main(int argc, char **argv) } /* Set up the OpenSSL TSL stuff. */ - rpctls_ctx = rpctls_setupcl_ssl(certpath); + rpctls_ctx = rpctls_setupcl_ssl(cert); if (rpctls_ctx == NULL) { if (rpctls_debug_level == 0) { syslog(LOG_ERR, "Can't set up TSL context"); @@ -187,7 +218,7 @@ rpctlscd_verbose_out(const char *fmt, ...) { va_list ap; - if (rpctls_verbose != 0) { + if (rpctls_verbose) { va_start(ap, fmt); if (rpctls_debug_level == 0) vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); @@ -259,10 +290,12 @@ rpctlscd_terminate(int sig __unused) } static SSL_CTX * -rpctls_setupcl_ssl(char *certpath) +rpctls_setupcl_ssl(bool cert) { SSL_CTX *ctx; long flags; + char path[PATH_MAX]; + size_t len, rlen; int ret; OpenSSL_add_all_algorithms(); @@ -276,23 +309,56 @@ rpctls_setupcl_ssl(char *certpath) SSL_CTX_set_ecdh_auto(ctx, 1); /* - * If certpath is set, it refers to the certifcate file to be used - * during an SSL_connect(). + * If cert is true, a certificate and key exists in + * rpctls_certdir, so that it can do mutual authentication. */ - if (certpath != NULL) { - ret = SSL_CTX_use_certificate_file(ctx, certpath, + if (cert) { + /* Get the cert.pem and key.pem files. */ + len = strlcpy(path, rpctls_certdir, sizeof(path)); + rlen = sizeof(path) - len; + if (strlcpy(&path[len], "cert.pem", rlen) != 8) { + SSL_CTX_free(ctx); + return (NULL); + } + ret = SSL_CTX_use_certificate_file(ctx, path, SSL_FILETYPE_PEM); if (ret != 1) { rpctlscd_verbose_out("rpctls_setupcl_ssl: can't use " - "the certificate file %s\n", certpath); + "certificate file path=%s ret=%d\n", path, ret); SSL_CTX_free(ctx); return (NULL); } + if (strlcpy(&path[len], "key.pem", rlen) != 7) { + SSL_CTX_free(ctx); + return (NULL); + } + ret = SSL_CTX_use_PrivateKey_file(ctx, path, + SSL_FILETYPE_PEM); + if (ret != 1) { + rpctlscd_verbose_out("rpctls_setupcl_ssl: Can't use " + "private key path=%s ret=%d\n", path, ret); + SSL_CTX_free(ctx); + return (NULL); + } } + if (rpctls_verify_cafile != NULL) { + ret = SSL_CTX_load_verify_locations(ctx, + rpctls_verify_cafile, NULL); + if (ret != 1) { + rpctlscd_verbose_out("rpctls_setupcl_ssl: " + "Can't load verify locations\n"); + SSL_CTX_free(ctx); + return (NULL); + } + } /* RPC-over-TLS must use TLSv1.3. */ +#ifdef notyet flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; +#else + flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_3; +#endif SSL_CTX_set_options(ctx, flags); return (ctx); } @@ -303,20 +369,24 @@ rpctls_connect(SSL_CTX *ctx, int s) SSL *ssl; X509 *cert; int ret; + char *cp; ssl = SSL_new(ctx); if (ssl == NULL) { - rpctlscd_verbose_out("rpctls_connect: SSL_new failed\n"); + rpctlscd_verbose_out("rpctls_connect: " + "SSL_new failed\n"); return (NULL); } if (SSL_set_fd(ssl, s) != 1) { - rpctlscd_verbose_out("rpctls_connect: SSL_set_fd failed\n"); + rpctlscd_verbose_out("rpctls_connect: " + "SSL_set_fd failed\n"); SSL_free(ssl); return (NULL); } ret = SSL_connect(ssl); if (ret != 1) { - rpctlscd_verbose_out("rpctls_connect: SSL_connect failed %d\n", + rpctlscd_verbose_out("rpctls_connect: " + "SSL_connect failed %d\n", ret); SSL_free(ssl); return (NULL); @@ -324,13 +394,26 @@ rpctls_connect(SSL_CTX *ctx, int s) cert = SSL_get_peer_certificate(ssl); if (cert == NULL) { - rpctlscd_verbose_out("rpctls_connect: get peer certificate " - "failed\n"); + rpctlscd_verbose_out("rpctls_connect: get peer" + " certificate failed\n"); SSL_shutdown(ssl); SSL_free(ssl); return (NULL); } + cp = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); + rpctlscd_verbose_out("rpctls_connect: cert subjectName=%s\n", cp); + 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) + ret = X509_V_ERR_HOSTNAME_MISMATCH; X509_free(cert); + if (rpctls_verify && ret != X509_V_OK) { + SSL_shutdown(ssl); + SSL_free(ssl); + return (NULL); + } #ifdef notnow ret = BIO_get_ktls_send(SSL_get_wbio(ssl)); @@ -339,5 +422,62 @@ rpctls_connect(SSL_CTX *ctx, int s) fprintf(stderr, "ktls_recv=%d\n", ret); #endif return (ssl); +} + +/* + * Check a client IP address against any host address in the + * certificate. Basically getpeername(2), getnameinfo(3) and + * X509_check_host(). + */ +static int +rpctls_checkhost(int s, X509 *cert) +{ + struct sockaddr *sad; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + struct sockaddr_storage ad; + char hostnam[NI_MAXHOST + 1], addrstr[INET6_ADDRSTRLEN + 1]; + const char *cp; + socklen_t slen; + int ret; + + sad = (struct sockaddr *)&ad; + slen = sizeof(ad); + if (getpeername(s, sad, &slen) < 0) + return (0); + switch (sad->sa_family) { + case AF_INET: + sin = (struct sockaddr_in *)sad; + cp = inet_ntop(sad->sa_family, &sin->sin_addr.s_addr, + addrstr, sizeof(addrstr)); + if (cp != NULL) + rpctlscd_verbose_out("rpctls_checkhost: " + "peer ip %s\n", cp); + if (getnameinfo((const struct sockaddr *)sad, + sizeof(struct sockaddr_in), hostnam, + sizeof(hostnam), NULL, 0, NI_NAMEREQD) != 0) + return (0); + break; + case AF_INET6: + sin6 = (struct sockaddr_in6 *)sad; + cp = inet_ntop(sad->sa_family, &sin6->sin6_addr, + addrstr, sizeof(addrstr)); + if (cp != NULL) + rpctlscd_verbose_out("rpctls_checkhost: " + "peer ip %s\n", cp); + if (getnameinfo((const struct sockaddr *)sad, + sizeof(struct sockaddr_in6), hostnam, + sizeof(hostnam), NULL, 0, NI_NAMEREQD) != 0) + return (0); + break; + default: + return (0); + } + rpctlscd_verbose_out("rpctls_checkhost: hostname %s\n", + hostnam); + ret = X509_check_host(cert, hostnam, strlen(hostnam), 0, NULL); + rpctlscd_verbose_out("rpctls_checkhost: X509_check_host ret=%d\n", + ret); + return (ret); }
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202003081812.028ICsts004880>