Skip site navigation (1)Skip section navigation (2)
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>