svn commit: r358755 - projects/nfs-over-tls/usr.sbin/rpctlscd
Rick Macklem
rmacklem at FreeBSD.org
Sun Mar 8 18:12:55 UTC 2020
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);
}
More information about the svn-src-projects
mailing list