Index: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c =================================================================== --- projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c (revision 365351) +++ projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c (nonexistent) @@ -1,764 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ - * Authors: Doug Rabson - * Developed with Red Inc: Alfred Perlstein - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* Modified from gssd.c for the client side of kernel RPC-over-TLS. */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "rpctlscd.h" - -#ifndef _PATH_RPCTLSCDSOCK -#define _PATH_RPCTLSCDSOCK "/var/run/rpctlscd.sock" -#endif -#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 "AES128-GCM-SHA256" -#endif - -static struct pidfh *rpctls_pfh = NULL; -static int rpctls_debug_level; -static bool rpctls_verbose; -static SSL_CTX *rpctls_ctx = NULL; -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 uint64_t rpctls_ssl_refno = 0; -static uint64_t rpctls_ssl_sec = 0; -static uint64_t rpctls_ssl_usec = 0; -static bool rpctls_gothup = false; - -/* - * A linked list of all current "SSL *"s and socket "fd"s - * for kernel RPC TLS connections is maintained. - * The "refno" field is a unique 64bit value used to - * identify which entry a kernel RPC upcall refers to. - */ -LIST_HEAD(ssl_list, ssl_entry); -struct ssl_entry { - LIST_ENTRY(ssl_entry) next; - uint64_t refno; - int s; - SSL *ssl; -}; -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_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); - -int -main(int argc, char **argv) -{ - /* - * We provide an RPC service on a local-domain socket. The - * kernel rpctls code will upcall to this daemon to do the initial - * TLS handshake. - */ - struct sockaddr_un sun; - int fd, oldmask, ch; - SVCXPRT *xprt; - 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"); - } - - if (modfind("ktls_ocf") < 0) { - /* Not present in kernel, try loading it */ - if (kldload("ktls_ocf") < 0 || modfind("ktls_ocf") < 0) - errx(1, "Cannot load ktls_ocf"); - } - if (modfind("aesni") < 0) { - /* Not present in kernel, try loading it */ - kldload("aesni"); - } - - /* Get the time when this daemon is started. */ - gettimeofday(&tm, &tz); - rpctls_ssl_sec = tm.tv_sec; - rpctls_ssl_usec = tm.tv_usec; - - rpctls_verbose = false; - cert = false; - while ((ch = getopt(argc, argv, "D:dl:mp:r:v")) != -1) { - switch (ch) { - case 'D': - rpctls_certdir = optarg; - break; - case 'd': - rpctls_debug_level++; - break; - case 'l': - rpctls_verify_cafile = optarg; - break; - case 'm': - cert = true; - break; - case 'p': - rpctls_verify_capath = optarg; - break; - case 'r': - rpctls_crlfile = optarg; - break; - case 'v': - rpctls_verbose = true; - break; - default: - fprintf(stderr, "usage: %s " - "[-D certdir] [-d] " - "[-l CAfile] [-m] " - "[-p CApath] [-r CRLfile] " - "[-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 */ - if (kldload("krpc") < 0 || modfind("krpc") < 0) - 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"); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGHUP, SIG_IGN); - } - signal(SIGTERM, rpctlscd_terminate); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, rpctls_huphandler); - - pidfile_write(rpctls_pfh); - - memset(&sun, 0, sizeof sun); - sun.sun_family = AF_LOCAL; - unlink(_PATH_RPCTLSCDSOCK); - strcpy(sun.sun_path, _PATH_RPCTLSCDSOCK); - sun.sun_len = SUN_LEN(&sun); - fd = socket(AF_LOCAL, SOCK_STREAM, 0); - if (fd < 0) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, "Can't create local rpctlscd socket"); - exit(1); - } - err(1, "Can't create local rpctlscd socket"); - } - oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO); - if (bind(fd, (struct sockaddr *)&sun, sun.sun_len) < 0) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, "Can't bind local rpctlscd socket"); - exit(1); - } - err(1, "Can't bind local rpctlscd socket"); - } - umask(oldmask); - if (listen(fd, SOMAXCONN) < 0) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, - "Can't listen on local rpctlscd socket"); - exit(1); - } - err(1, "Can't listen on local rpctlscd socket"); - } - xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); - if (!xprt) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, - "Can't create transport for local rpctlscd socket"); - exit(1); - } - err(1, "Can't create transport for local rpctlscd socket"); - } - if (!svc_reg(xprt, RPCTLSCD, RPCTLSCDVERS, rpctlscd_1, NULL)) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, - "Can't register service for local rpctlscd socket"); - exit(1); - } - err(1, "Can't register service for local rpctlscd socket"); - } - - rpctls_syscall(RPCTLS_SYSC_CLSETPATH, _PATH_RPCTLSCDSOCK); - svc_run(); - rpctls_syscall(RPCTLS_SYSC_CLSHUTDOWN, ""); - - SSL_CTX_free(rpctls_ctx); - EVP_cleanup(); - return (0); -} - -static void -rpctlscd_verbose_out(const char *fmt, ...) -{ - va_list ap; - - if (rpctls_verbose) { - va_start(ap, fmt); - if (rpctls_debug_level == 0) - vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); - else - vfprintf(stderr, fmt, ap); - va_end(ap); - } -} - -bool_t -rpctlscd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) -{ - - rpctlscd_verbose_out("rpctlscd_null: done\n"); - return (TRUE); -} - -bool_t -rpctlscd_connect_1_svc(void *argp, - struct rpctlscd_connect_res *result, struct svc_req *rqstp) -{ - int s; - bool_t res; - SSL *ssl; - char buf[1024]; - ssize_t siz, ret; - struct ssl_entry *newslp; - - rpctlscd_verbose_out("rpctlsd_connect: started\n"); - /* Get the socket fd from the kernel. */ - s = rpctls_syscall(RPCTLS_SYSC_CLSOCKET, ""); - if (s < 0) { - result->reterr = RPCTLSERR_NOSOCKET; - return (TRUE); - } - - /* Do a TLS connect handshake. */ - ssl = rpctls_connect(rpctls_ctx, s); - if (ssl == NULL) { - rpctlscd_verbose_out("rpctlsd_connect: can't do TLS " - "handshake\n"); - result->reterr = RPCTLSERR_NOSSL; - } else { - result->reterr = RPCTLSERR_OK; - result->sec = rpctls_ssl_sec; - result->usec = rpctls_ssl_usec; - result->ssl = ++rpctls_ssl_refno; - /* Hard to believe this will ever wrap around.. */ - if (rpctls_ssl_refno == 0) - result->ssl = ++rpctls_ssl_refno; - } - - if (ssl == NULL) { - /* - * For RPC-over-TLS, this upcall is expected - * to close off the socket. - */ - close(s); - return (TRUE); - } - - /* Maintain list of all current SSL *'s */ - newslp = malloc(sizeof(*newslp)); - newslp->refno = rpctls_ssl_refno; - newslp->s = s; - newslp->ssl = ssl; - LIST_INSERT_HEAD(&rpctls_ssllist, newslp, next); - return (TRUE); -} - -bool_t -rpctlscd_handlerecord_1_svc(struct rpctlscd_handlerecord_arg *argp, - struct rpctlscd_handlerecord_res *result, struct svc_req *rqstp) -{ - struct ssl_entry *slp; - int ret; - char junk; - - slp = NULL; - if (argp->sec == rpctls_ssl_sec && argp->usec == - rpctls_ssl_usec) { - LIST_FOREACH(slp, &rpctls_ssllist, next) { - if (slp->refno == argp->ssl) - break; - } - } - - if (slp != NULL) { - rpctlscd_verbose_out("rpctlscd_handlerecord fd=%d\n", - slp->s); - /* - * An SSL_read() of 0 bytes should fail, but it should - * handle the non-application data record before doing so. - */ - ret = SSL_read(slp->ssl, &junk, 0); - if (ret <= 0) { - /* Check to see if this was a close alert. */ - ret = SSL_get_shutdown(slp->ssl); - if ((ret & (SSL_SENT_SHUTDOWN | - SSL_RECEIVED_SHUTDOWN)) == SSL_RECEIVED_SHUTDOWN) - SSL_shutdown(slp->ssl); - } else { - if (rpctls_debug_level == 0) - syslog(LOG_ERR, "SSL_read returned %d", ret); - else - fprintf(stderr, "SSL_read returned %d\n", ret); - } - result->reterr = RPCTLSERR_OK; - } else - result->reterr = RPCTLSERR_NOSSL; - return (TRUE); -} - -bool_t -rpctlscd_disconnect_1_svc(struct rpctlscd_disconnect_arg *argp, - struct rpctlscd_disconnect_res *result, struct svc_req *rqstp) -{ - struct ssl_entry *slp; - int ret; - - slp = NULL; - if (argp->sec == rpctls_ssl_sec && argp->usec == - rpctls_ssl_usec) { - LIST_FOREACH(slp, &rpctls_ssllist, next) { - if (slp->refno == argp->ssl) - break; - } - } - - if (slp != NULL) { - rpctlscd_verbose_out("rpctlscd_disconnect: fd=%d closed\n", - slp->s); - LIST_REMOVE(slp, next); - ret = SSL_get_shutdown(slp->ssl); - /* - * Do an SSL_shutdown() unless a close alert has - * already been sent. - */ - if ((ret & SSL_SENT_SHUTDOWN) == 0) - SSL_shutdown(slp->ssl); - SSL_free(slp->ssl); - /* - * For RPC-over-TLS, this upcall is expected - * to close off the socket. - */ - shutdown(slp->s, SHUT_WR); - close(slp->s); - free(slp); - result->reterr = RPCTLSERR_OK; - } else - result->reterr = RPCTLSERR_NOCLOSE; - return (TRUE); -} - -int -rpctlscd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) -{ - - return (TRUE); -} - -static void -rpctlscd_terminate(int sig __unused) -{ - - rpctls_syscall(RPCTLS_SYSC_CLSHUTDOWN, ""); - pidfile_remove(rpctls_pfh); - exit(0); -} - -static SSL_CTX * -rpctls_setupcl_ssl(bool cert) -{ - SSL_CTX *ctx; - long flags; - char path[PATH_MAX]; - size_t len, rlen; - int ret; - - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - - ctx = SSL_CTX_new(TLS_client_method()); - if (ctx == NULL) { - rpctlscd_verbose_out("rpctls_setupcl_ssl: SSL_CTX_new " - "failed\n"); - return (NULL); - } - SSL_CTX_set_ecdh_auto(ctx, 1); - - /* - * Set preferred ciphers, since KERN_TLS only supports a - * few of them. - */ - ret = SSL_CTX_set_cipher_list(ctx, _PREFERRED_CIPHERS); - if (ret == 0) { - rpctlscd_verbose_out("rpctls_setupcl_ssl: " - "SSL_CTX_set_cipher_list failed to set any ciphers\n"); - SSL_CTX_free(ctx); - return (NULL); - } - - /* - * If cert is true, a certificate and key exists in - * rpctls_certdir, so that it can do mutual authentication. - */ - 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 " - "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 || rpctls_verify_capath != NULL) { - if (rpctls_crlfile != NULL) { - ret = rpctls_loadcrlfile(ctx); - if (ret == 0) { - rpctlscd_verbose_out("rpctls_setupcl_ssl: " - "Load CRLfile failed\n"); - SSL_CTX_free(ctx); - return (NULL); - } - } -#if OPENSSL_VERSION_NUMBER >= 0x30000000 - ret = 1; - if (rpctls_verify_cafile != NULL) - ret = SSL_CTX_load_verify_file(ctx, - rpctls_verify_cafile); - if (ret != 0 && rpctls_verify_capath != NULL) - ret = SSL_CTX_load_verify_dir(ctx, - rpctls_verify_capath); -#else - ret = SSL_CTX_load_verify_locations(ctx, - rpctls_verify_cafile, rpctls_verify_capath); -#endif - if (ret == 0) { - rpctlscd_verbose_out("rpctls_setupcl_ssl: " - "Can't load verify locations\n"); - SSL_CTX_free(ctx); - return (NULL); - } - /* - * The man page says that the - * SSL_CTX_set0_CA_list() call is not normally - * needed, but I believe it is harmless. - */ - if (rpctls_verify_cafile != NULL) - SSL_CTX_set0_CA_list(ctx, - SSL_load_client_CA_file(rpctls_verify_cafile)); - } - - /* 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); -} - -static SSL * -rpctls_connect(SSL_CTX *ctx, int s) -{ - SSL *ssl; - X509 *cert; - 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_loadcrlfile(ctx); - if (ret == 0) - rpctlscd_verbose_out("rpctls_connect: Can't " - "reload CRLfile\n"); - } - ssl = SSL_new(ctx); - if (ssl == NULL) { - 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"); - SSL_free(ssl); - return (NULL); - } - ret = SSL_connect(ssl); - if (ret != 1) { - rpctlscd_verbose_out("rpctls_connect: " - "SSL_connect failed %d\n", - ret); - SSL_free(ssl); - return (NULL); - } - - cert = SSL_get_peer_certificate(ssl); - if (cert == NULL) { - rpctlscd_verbose_out("rpctls_connect: get peer" - " certificate failed\n"); - SSL_free(ssl); - return (NULL); - } - gethostret = rpctls_gethost(s, sad, hostnam, sizeof(hostnam)); - if (gethostret == 0) - hostnam[0] = '\0'; - ret = SSL_get_verify_result(ssl); - 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 (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); - if (rpctls_debug_level == 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)); - else - fprintf(stderr, - "rpctls_connect: client IP %s " - "issuerName=%s subjectName=%s verify " - "failed %s\n", hostnam, cp, cp2, - X509_verify_cert_error_string(ret)); - } - SSL_free(ssl); - return (NULL); - } - - /* Check to see if ktls is enabled on the connection. */ - ret = BIO_get_ktls_send(SSL_get_wbio(ssl)); - rpctlscd_verbose_out("rpctls_connect: BIO_get_ktls_send=%d\n", ret); - if (ret != 0) { - ret = BIO_get_ktls_recv(SSL_get_rbio(ssl)); - rpctlscd_verbose_out("rpctls_connect: BIO_get_ktls_recv=%d\n", - ret); - } - if (ret == 0) { - if (rpctls_debug_level == 0) - syslog(LOG_ERR, "ktls not working\n"); - else - fprintf(stderr, "ktls not working\n"); - SSL_free(ssl); - return (NULL); - } - - return (ssl); -} - -/* - * Get the server's IP address. - */ -static int -rpctls_gethost(int s, struct sockaddr *sad, char *hostip, size_t hostlen) -{ - socklen_t slen; - int ret; - - slen = sizeof(struct sockaddr_storage); - if (getpeername(s, sad, &slen) < 0) - return (0); - ret = 0; - if (getnameinfo((const struct sockaddr *)sad, - 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), - X509_CHECK_FLAG_NO_WILDCARDS, NULL); - return (ret); -} - -/* - * (re)load the CRLfile into the certificate verification store. - */ -static int -rpctls_loadcrlfile(SSL_CTX *ctx) -{ - X509_STORE *certstore; - X509_LOOKUP *certlookup; - int ret; - - 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_loadcrlfile: Can't" - " load CRLfile=%s\n", - rpctls_crlfile); - return (ret); - } - } - return (1); -} - -static void -rpctls_huphandler(int sig __unused) -{ - - rpctls_gothup = true; -} - Property changes on: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8 =================================================================== --- projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8 (revision 365351) +++ projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8 (nonexistent) @@ -1,176 +0,0 @@ -.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/ -.\" Authors: Doug Rabson -.\" Developed with Red Inc: Alfred Perlstein -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.\" Modified from gssd.8 for rpctlscd.8 by Rick Macklem. -.Dd March 11, 2020 -.Dt RPCTLSCD 8 -.Os -.Sh NAME -.Nm rpctlscd -.Nd "Sun RPC over TLS Client Daemon" -.Sh SYNOPSIS -.Nm -.Op Fl D Ar certdir -.Op Fl d -.Op Fl l Ar CAfile -.Op Fl m -.Op Fl p Ar CApath -.Op Fl r Ar CRLfile -.Op Fl v -.Sh DESCRIPTION -The -.Nm -program provides support for the client side of the kernel Sun RPC over TLS -implementation. -This daemon must be running for the kernel RPC to be able to do a TLS -connection to a server for an NFS over TLS mount. -This daemon requires that the kernel be built with -.Dq options KERNEL_TLS -and be running on an architecture such as -.Dq amd64 -that supports a direct map (not i386). -.Pp -If either of the -.Fl l -or -.Fl p -options have been specified, the daemon will require the server's -certificate to verify -and have a Fully Qualified Domain Name (FQDN) in it. -This FQDN must match -the reverse DNS name for the IP address that -the server is using for the TCP connection. -The FQDN may be -in either the DNS field of the subjectAltName or the CN field of the -subjectName in the certificate and -cannot have a wildcard -.Dq * -in it. -.Pp -If a SIGHUP signal is sent to the daemon it will reload the -.Dq CRLfile . -If the -.Fl r -option was not specified, the SIGHUP signal will be ignored. -.Pp -The daemon will log failed certificate verifications via -.Xr syslogd 8 -using LOG_INFO | LOG_DAEMON when the -.Fl l -or -.Fl p -option has been specified. -.Pp -The options are as follows: -.Bl -tag -width indent -.It Fl D Ar certdir -Use -.Dq certdir -instead of /etc/rpctlscd for the -.Fl m -option. -.It Fl d -Run in debug mode. -In this mode, -.Nm -will not fork when it starts. -.It Fl l Ar CAfile -This specifies the path name of a CAfile which holds the information -for server certificate verification. -This path name is used in -.Dq SSL_CTX_load_verify_locations(ctx,CAfile,NULL) -and -.Dq SSL_CTX_set0_CA_list(ctx,SSL_load_client_CA_file(CAfile)) -openssl library calls. -Note that this is a path name for the file and is not assumed to be -in -.Dq certdir . -.It Fl m -Enable support for mutual authentication. -A certificate and associated key must be found in /etc/rpctlscd -(or the directory specified by the -.Fl D -option) -in case a server requests a peer certificate. -The certificate needs to be in a file named -.Dq cert.pem -and the key in a file named -.Dq key.pem . -If there is a passphrase on the -.Dq key.pem -file, this daemon will prompt for the passphrase during startup. -.It Fl p Ar CApath -This option is similar to the -.Fl l -option, but specifies the path of a directory with CA -certificates in it. -When this option is used, -.Dq SSL_CTX_set0_CA_list(ctx,SSL_load_client_CA_file()) -is not called, so a list of CA names is not be passed -to the server during the TLS handshake. -The openssl documentation indicates this call is rarely needed. -(However, I was not able to determine if/when this matters, so -if in doubt, use the -.Fl l -option instead of this option.) -.It Fl r Ar CRLfile -This option specifies a Certificate Revocation List (CRL) file -that is to be loaded into the verify certificate store and -checked during verification of the server's certificate. -This option is meaningless unless either the -.Fl l -or -.Fl p -have been specified. -.It Fl v -Run in verbose mode. -In this mode, -.Nm -will log activity messages to syslog using LOG_INFO | LOG_DAEMON or to -stderr, if the -.Fl d -option has also been specified. -.El -.Sh EXIT STATUS -.Ex -std -.Sh SEE ALSO -.Xr openssl 1 , -.Xr mount_nfs 8 , -.Xr rpctlssd 8 , -.Xr syslogd 8 -.Sh BUGS -This daemon cannot be safely shut down and restarted if there are -any active RPC-over-TLS connections. -Doing so will orphan the KERNEL_TLS connections, so that they -can no longer do upcalls successfully, since the -.Dq SSL * -structures in userspace have been lost. -.Sh HISTORY -The -.Nm -manual page first appeared in -.Fx 13.0 . Property changes on: projects/nfs-over-tls/usr.sbin/rpctlscd/rpctlscd.8 ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile =================================================================== --- projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile (revision 365351) +++ projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile (nonexistent) @@ -1,32 +0,0 @@ -# $FreeBSD$ - -.include - -PROG= rpctlscd -MAN= rpctlscd.8 -SRCS= rpctlscd.c rpctlscd.h rpctlscd_svc.c rpctlscd_xdr.c - -CFLAGS+= -I. - -CFLAGS+= -I/usr/ktls/include -LDFLAGS+= -L/usr/ktls/lib - -LIBADD= ssl crypto util - -CLEANFILES= rpctlscd_svc.c rpctlscd_xdr.c rpctlscd.h - -RPCSRC= ${SRCTOP}/sys/rpc/rpcsec_tls/rpctlscd.x -RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -L -C -M - -rpctlscd_svc.c: ${RPCSRC} rpctlscd.h - ${RPCGEN} -m -o ${.TARGET} ${RPCSRC} - -rpctlscd_xdr.c: ${RPCSRC} rpctlscd.h - ${RPCGEN} -c -o ${.TARGET} ${RPCSRC} - -rpctlscd.h: ${RPCSRC} - ${RPCGEN} -h -o ${.TARGET} ${RPCSRC} - -.PATH: ${SRCTOP}/sys/rpc/rpcsec_tls - -.include Property changes on: projects/nfs-over-tls/usr.sbin/rpctlscd/Makefile ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.c =================================================================== --- projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.c (revision 365351) +++ projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.c (nonexistent) @@ -1,973 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ - * Authors: Doug Rabson - * Developed with Red Inc: Alfred Perlstein - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* Modified from gssd.c for the server side of kernel RPC-over-TLS. */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "rpctlssd.h" - -#ifndef _PATH_RPCTLSSDSOCK -#define _PATH_RPCTLSSDSOCK "/var/run/rpctlssd.sock" -#endif -#ifndef _PATH_CERTANDKEY -#define _PATH_CERTANDKEY "/etc/rpctlssd/" -#endif -#ifndef _PATH_RPCTLSSDPID -#define _PATH_RPCTLSSDPID "/var/run/rpctlssd.pid" -#endif -#ifndef _PREFERRED_CIPHERS -#define _PREFERRED_CIPHERS "AES128-GCM-SHA256" -#endif - -static struct pidfh *rpctls_pfh = NULL; -static int rpctls_debug_level; -static bool rpctls_verbose; -static SSL_CTX *rpctls_ctx = NULL; -static bool rpctls_do_mutual = false; -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_comparehost = false; -static unsigned int rpctls_wildcard = X509_CHECK_FLAG_NO_WILDCARDS; -static uint64_t rpctls_ssl_refno = 0; -static uint64_t rpctls_ssl_sec = 0; -static uint64_t rpctls_ssl_usec = 0; -static bool rpctls_gothup = false; -static bool rpctls_cnuser = false; -static char *rpctls_dnsname; -static const char *rpctls_cnuseroid = "1.3.6.1.4.1.2238.1.1.1"; - -/* - * A linked list of all current "SSL *"s and socket "fd"s - * for kernel RPC TLS connections is maintained. - * The "refno" field is a unique 64bit value used to - * identify which entry a kernel RPC upcall refers to. - */ -LIST_HEAD(ssl_list, ssl_entry); -struct ssl_entry { - LIST_ENTRY(ssl_entry) next; - uint64_t refno; - int s; - SSL *ssl; -}; -static struct ssl_list rpctls_ssllist; - -static void rpctlssd_terminate(int); -static SSL_CTX *rpctls_setup_ssl(const char *certdir); -static SSL *rpctls_server(SSL_CTX *ctx, int s, - uint32_t *flags, uint32_t *uidp, - int *ngrps, uint32_t *gidp); -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 int rpctls_cnname(X509 *cert, uint32_t *uidp, - int *ngrps, uint32_t *gidp); -static char *rpctls_getdnsname(char *dnsname); -static void rpctls_huphandler(int sig __unused); - -extern void rpctlssd_1(struct svc_req *rqstp, SVCXPRT *transp); - -int -main(int argc, char **argv) -{ - /* - * We provide an RPC service on a local-domain socket. The - * kernel rpctls code will upcall to this daemon to do the initial - * TLS handshake. - */ - struct sockaddr_un sun; - int fd, oldmask, ch, debug; - SVCXPRT *xprt; - struct timeval tm; - struct timezone tz; - char hostname[MAXHOSTNAMELEN + 2]; - pid_t otherpid; - - /* Check that another rpctlssd isn't already running. */ - rpctls_pfh = pidfile_open(_PATH_RPCTLSSDPID, 0600, &otherpid); - if (rpctls_pfh == NULL) { - if (errno == EEXIST) - errx(1, "rpctlssd already running, pid: %d.", otherpid); - warn("cannot open or create pidfile"); - } - - if (modfind("ktls_ocf") < 0) { - /* Not present in kernel, try loading it */ - if (kldload("ktls_ocf") < 0 || modfind("ktls_ocf") < 0) - errx(1, "Cannot load ktls_ocf"); - } - if (modfind("aesni") < 0) { - /* Not present in kernel, try loading it */ - kldload("aesni"); - } - - /* Get the time when this daemon is started. */ - gettimeofday(&tm, &tz); - rpctls_ssl_sec = tm.tv_sec; - rpctls_ssl_usec = tm.tv_usec; - - /* Set the dns name for the server. */ - rpctls_dnsname = rpctls_getdnsname(hostname); - if (rpctls_dnsname == NULL) { - strcpy(hostname, "@default.domain"); - rpctls_dnsname = hostname; - } -fprintf(stderr, "dnsname=%s\n", rpctls_dnsname); - - debug = 0; - rpctls_verbose = false; - while ((ch = getopt(argc, argv, "D:dhl:n:mp:r:uvWw")) != -1) { - switch (ch) { - 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 'm': - rpctls_do_mutual = true; - break; - case 'n': - hostname[0] = '@'; - strlcpy(&hostname[1], optarg, MAXHOSTNAMELEN + 1); - rpctls_dnsname = hostname; - break; - case 'p': - rpctls_verify_capath = optarg; - break; - case 'r': - rpctls_crlfile = optarg; - break; - case 'u': - rpctls_cnuser = true; - break; - case 'v': - rpctls_verbose = true; - break; - case 'W': - if (rpctls_wildcard != X509_CHECK_FLAG_NO_WILDCARDS) - errx(1, "options -w and -W are mutually " - "exclusive"); - rpctls_wildcard = X509_CHECK_FLAG_MULTI_LABEL_WILDCARDS; - break; - case 'w': - if (rpctls_wildcard != X509_CHECK_FLAG_NO_WILDCARDS) - errx(1, "options -w and -W are mutually " - "exclusive"); - rpctls_wildcard = 0; - break; - default: - fprintf(stderr, "usage: %s " - "[-D certdir] [-d] [-h] " - "[-l CAfile] [-m] " - "[-n domain_name] " - "[-p CApath] [-r CRLfile] " - "[-u] [-v] [-W] [-w]\n", argv[0]); - exit(1); - } - } - if (rpctls_do_mutual && rpctls_verify_cafile == NULL && - rpctls_verify_capath == NULL) - errx(1, "-m requires the -l and/or " - "-p options"); - if (rpctls_comparehost && (!rpctls_do_mutual || - (rpctls_verify_cafile == NULL && rpctls_verify_capath == NULL))) - errx(1, "-h requires the -m plus the " - "-l and/or -p options"); - if (!rpctls_comparehost && rpctls_wildcard != - X509_CHECK_FLAG_NO_WILDCARDS) - errx(1, "The -w or -W options require the -h option"); - if (rpctls_cnuser && (!rpctls_do_mutual || - (rpctls_verify_cafile == NULL && rpctls_verify_capath == NULL))) - errx(1, "-u requires the -m plus the " - "-l and/or -p options"); - - if (modfind("krpc") < 0) { - /* Not present in kernel, try loading it */ - if (kldload("krpc") < 0 || modfind("krpc") < 0) - errx(1, "Kernel RPC is not available"); - } - - if (rpctls_debug_level == 0) { - if (daemon(0, 0) != 0) - err(1, "Can't daemonize"); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGHUP, SIG_IGN); - } - signal(SIGTERM, rpctlssd_terminate); - signal(SIGPIPE, SIG_IGN); - signal(SIGHUP, rpctls_huphandler); - - pidfile_write(rpctls_pfh); - - memset(&sun, 0, sizeof sun); - sun.sun_family = AF_LOCAL; - unlink(_PATH_RPCTLSSDSOCK); - strcpy(sun.sun_path, _PATH_RPCTLSSDSOCK); - sun.sun_len = SUN_LEN(&sun); - fd = socket(AF_LOCAL, SOCK_STREAM, 0); - if (fd < 0) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, "Can't create local rpctlssd socket"); - exit(1); - } - err(1, "Can't create local rpctlssd socket"); - } - oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO); - if (bind(fd, (struct sockaddr *)&sun, sun.sun_len) < 0) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, "Can't bind local rpctlssd socket"); - exit(1); - } - err(1, "Can't bind local rpctlssd socket"); - } - umask(oldmask); - if (listen(fd, SOMAXCONN) < 0) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, - "Can't listen on local rpctlssd socket"); - exit(1); - } - err(1, "Can't listen on local rpctlssd socket"); - } - xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); - if (!xprt) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, - "Can't create transport for local rpctlssd socket"); - exit(1); - } - err(1, "Can't create transport for local rpctlssd socket"); - } - if (!svc_reg(xprt, RPCTLSSD, RPCTLSSDVERS, rpctlssd_1, NULL)) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, - "Can't register service for local rpctlssd socket"); - exit(1); - } - err(1, "Can't register service for local rpctlssd socket"); - } - - rpctls_ctx = rpctls_setup_ssl(rpctls_certdir); - if (rpctls_ctx == NULL) { - if (rpctls_debug_level == 0) { - syslog(LOG_ERR, "Can't create SSL context"); - exit(1); - } - err(1, "Can't create SSL context"); - } - rpctls_gothup = false; - LIST_INIT(&rpctls_ssllist); - - rpctls_syscall(RPCTLS_SYSC_SRVSETPATH, _PATH_RPCTLSSDSOCK); - svc_run(); - rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, ""); - - SSL_CTX_free(rpctls_ctx); - EVP_cleanup(); - return (0); -} - -static void -rpctlssd_verbose_out(const char *fmt, ...) -{ - va_list ap; - - if (rpctls_verbose) { - va_start(ap, fmt); - if (rpctls_debug_level == 0) - vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); - else - vfprintf(stderr, fmt, ap); - va_end(ap); - } -} - -bool_t -rpctlssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) -{ - - rpctlssd_verbose_out("rpctlssd_null_svc: done\n"); - return (TRUE); -} - -bool_t -rpctlssd_connect_1_svc(void *argp, - struct rpctlssd_connect_res *result, struct svc_req *rqstp) -{ - int ngrps, s; - SSL *ssl; - uint32_t flags; - struct ssl_entry *newslp; - uint32_t uid; - uint32_t *gidp; - - rpctlssd_verbose_out("rpctlsd_connect_svc: started\n"); - memset(result, 0, sizeof(*result)); - /* Get the socket fd from the kernel. */ - s = rpctls_syscall(RPCTLS_SYSC_SRVSOCKET, ""); -rpctlssd_verbose_out("rpctlsd_connect_svc s=%d\n", s); - if (s < 0) - return (FALSE); - - /* Do the server side of a TLS handshake. */ - gidp = calloc(NGROUPS, sizeof(*gidp)); - ssl = rpctls_server(rpctls_ctx, s, &flags, &uid, &ngrps, gidp); - if (ssl == NULL) { - free(gidp); - rpctlssd_verbose_out("rpctlssd_connect_svc: ssl " - "accept failed\n"); - /* - * For RPC-over-TLS, this upcall is expected - * to close off the socket upon handshake failure. - */ - close(s); - return (FALSE); - } else { - rpctlssd_verbose_out("rpctlssd_connect_svc: " - "succeeded flags=0x%x\n", flags); - result->flags = flags; - result->sec = rpctls_ssl_sec; - result->usec = rpctls_ssl_usec; - result->ssl = ++rpctls_ssl_refno; - /* Hard to believe this could ever wrap around.. */ - if (rpctls_ssl_refno == 0) - result->ssl = ++rpctls_ssl_refno; - if ((flags & RPCTLS_FLAGS_CERTUSER) != 0) { - result->uid = uid; - result->gid.gid_len = ngrps; - result->gid.gid_val = gidp; - } else { - result->uid = 0; - result->gid.gid_len = 0; - result->gid.gid_val = gidp; - } - } - - /* Maintain list of all current SSL *'s */ - newslp = malloc(sizeof(*newslp)); - newslp->ssl = ssl; - newslp->s = s; - newslp->refno = rpctls_ssl_refno; - LIST_INSERT_HEAD(&rpctls_ssllist, newslp, next); - return (TRUE); -} - -bool_t -rpctlssd_handlerecord_1_svc(struct rpctlssd_handlerecord_arg *argp, - struct rpctlssd_handlerecord_res *result, struct svc_req *rqstp) -{ - struct ssl_entry *slp; - int ret; - char junk; - - slp = NULL; - if (argp->sec == rpctls_ssl_sec && argp->usec == - rpctls_ssl_usec) { - LIST_FOREACH(slp, &rpctls_ssllist, next) { - if (slp->refno == argp->ssl) - break; - } - } - - if (slp != NULL) { - rpctlssd_verbose_out("rpctlssd_handlerecord fd=%d\n", - slp->s); - /* - * An SSL_read() of 0 bytes should fail, but it should - * handle the non-application data record before doing so. - */ - ret = SSL_read(slp->ssl, &junk, 0); - if (ret <= 0) { - /* Check to see if this was a close alert. */ - ret = SSL_get_shutdown(slp->ssl); -rpctlssd_verbose_out("get_shutdown=%d\n", ret); - if ((ret & (SSL_SENT_SHUTDOWN | - SSL_RECEIVED_SHUTDOWN)) == SSL_RECEIVED_SHUTDOWN) - SSL_shutdown(slp->ssl); - } else { - if (rpctls_debug_level == 0) - syslog(LOG_ERR, "SSL_read returned %d", ret); - else - fprintf(stderr, "SSL_read returned %d\n", ret); - } - result->reterr = RPCTLSERR_OK; - } else - result->reterr = RPCTLSERR_NOSSL; - return (TRUE); -} - -bool_t -rpctlssd_disconnect_1_svc(struct rpctlssd_disconnect_arg *argp, - struct rpctlssd_disconnect_res *result, struct svc_req *rqstp) -{ - struct ssl_entry *slp; - int ret; - - slp = NULL; - if (argp->sec == rpctls_ssl_sec && argp->usec == - rpctls_ssl_usec) { - LIST_FOREACH(slp, &rpctls_ssllist, next) { - if (slp->refno == argp->ssl) - break; - } - } - - if (slp != NULL) { - rpctlssd_verbose_out("rpctlssd_disconnect fd=%d closed\n", - slp->s); - LIST_REMOVE(slp, next); - ret = SSL_get_shutdown(slp->ssl); -rpctlssd_verbose_out("get_shutdown1=%d\n", ret); - /* - * Do an SSL_shutdown() unless a close alert has - * already been sent. - */ - if ((ret & SSL_SENT_SHUTDOWN) == 0) - SSL_shutdown(slp->ssl); - SSL_free(slp->ssl); - /* - * For RPC-over-TLS, this upcall is expected - * to close off the socket. - */ - shutdown(slp->s, SHUT_WR); - close(slp->s); - free(slp); - result->reterr = RPCTLSERR_OK; - } else - result->reterr = RPCTLSERR_NOCLOSE; - return (TRUE); -} - -int -rpctlssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) -{ - rpctlssd_connect_res *res; - - if (xdr_result == (xdrproc_t)xdr_rpctlssd_connect_res) { - res = (rpctlssd_connect_res *)result; - if (res->gid.gid_val != NULL) - free(res->gid.gid_val); - } - return (TRUE); -} - -static void -rpctlssd_terminate(int sig __unused) -{ - struct ssl_entry *slp; - - rpctls_syscall(RPCTLS_SYSC_SRVSHUTDOWN, ""); - pidfile_remove(rpctls_pfh); - - /* - * Shut down all TCP connections, so that any compromised TLS - * connection is no longer usable. - */ - LIST_FOREACH(slp, &rpctls_ssllist, next) - shutdown(slp->s, SHUT_RD); - exit(0); -} - -/* Allow the handshake to proceed. */ -static int -rpctls_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - - return (1); -} - -static SSL_CTX * -rpctls_setup_ssl(const char *certdir) -{ - SSL_CTX *ctx; - char path[PATH_MAX]; - size_t len, rlen; - int ret; - - SSL_library_init(); - SSL_load_error_strings(); - OpenSSL_add_all_algorithms(); - - ctx = SSL_CTX_new(TLS_server_method()); - if (ctx == NULL) { - rpctlssd_verbose_out("rpctls_setup_ssl: SSL_CTX_new failed\n"); - return (NULL); - } - SSL_CTX_set_ecdh_auto(ctx, 1); - - /* - * Set preferred ciphers, since KERN_TLS only supports a - * few of them. - */ - ret = SSL_CTX_set_cipher_list(ctx, _PREFERRED_CIPHERS); - if (ret == 0) { - rpctlssd_verbose_out("rpctls_setup_ssl: " - "SSL_CTX_set_cipher_list failed to set any ciphers\n"); - SSL_CTX_free(ctx); - return (NULL); - } - - /* Get the cert.pem and key.pem files from the directory certdir. */ - len = strlcpy(path, 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) { - rpctlssd_verbose_out("rpctls_setup_ssl: can't use 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) { - rpctlssd_verbose_out("rpctls_setup_ssl: Can't use private " - "key path=%s ret=%d\n", path, ret); - SSL_CTX_free(ctx); - return (NULL); - } - - /* Set Mutual authentication, as required. */ - if (rpctls_do_mutual) { - if (rpctls_verify_cafile != NULL || - rpctls_verify_capath != NULL) { - if (rpctls_crlfile != NULL) { - ret = rpctls_loadcrlfile(ctx); - if (ret == 0) { - rpctlssd_verbose_out("rpctls_setup_ssl:" - " Load CRLfile failed\n"); - SSL_CTX_free(ctx); - return (NULL); - } - } -#if OPENSSL_VERSION_NUMBER >= 0x30000000 - ret = 1; - if (rpctls_verify_cafile != NULL) - ret = SSL_CTX_load_verify_file(ctx, - rpctls_verify_cafile); - if (ret != 0 && rpctls_verify_capath != NULL) - ret = SSL_CTX_load_verify_dir(ctx, - rpctls_verify_capath); -#else - ret = SSL_CTX_load_verify_locations(ctx, - rpctls_verify_cafile, rpctls_verify_capath); -#endif - if (ret == 0) { - rpctlssd_verbose_out("rpctls_setup_ssl: " - "Can't load verify locations\n"); - SSL_CTX_free(ctx); - return (NULL); - } - if (rpctls_verify_cafile != NULL) - SSL_CTX_set_client_CA_list(ctx, - SSL_load_client_CA_file( - rpctls_verify_cafile)); - } - SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, - rpctls_verify_callback); - } - return (ctx); -} - -static SSL * -rpctls_server(SSL_CTX *ctx, int s, uint32_t *flags, uint32_t *uidp, - int *ngrps, uint32_t *gidp) -{ - SSL *ssl; - X509 *cert; - struct sockaddr *sad; - struct sockaddr_storage ad; - char hostnam[NI_MAXHOST]; - int gethostret, ret; - char *cp, *cp2; - - *flags = 0; - sad = (struct sockaddr *)&ad; - if (rpctls_gothup) { - rpctls_gothup = false; - ret = rpctls_loadcrlfile(ctx); - if (ret == 0) - rpctlssd_verbose_out("rpctls_server: Can't " - "reload CRLfile\n"); - } - ssl = SSL_new(ctx); - if (ssl == NULL) { - rpctlssd_verbose_out("rpctls_server: SSL_new failed\n"); - return (NULL); - } - if (SSL_set_fd(ssl, s) != 1) { - rpctlssd_verbose_out("rpctls_server: SSL_set_fd failed\n"); - SSL_free(ssl); - return (NULL); - } - ret = SSL_accept(ssl); - if (ret != 1) { - rpctlssd_verbose_out("rpctls_server: SSL_accept " - "failed ret=%d\n", ret); - SSL_free(ssl); - return (NULL); - } - *flags |= RPCTLS_FLAGS_HANDSHAKE; - if (rpctls_do_mutual) { - cert = SSL_get_peer_certificate(ssl); - if (cert != NULL) { - gethostret = rpctls_gethost(s, sad, hostnam, - sizeof(hostnam)); - if (gethostret == 0) - hostnam[0] = '\0'; - cp2 = X509_NAME_oneline( - X509_get_subject_name(cert), NULL, 0); -rpctlssd_verbose_out("%s\n", cp2); - *flags |= RPCTLS_FLAGS_GOTCERT; - ret = SSL_get_verify_result(ssl); - if (ret != X509_V_OK) { - cp = X509_NAME_oneline( - X509_get_issuer_name(cert), NULL, 0); - if (rpctls_debug_level == 0) - syslog(LOG_INFO | LOG_DAEMON, - "rpctls_server: client IP %s " - "issuerName=%s subjectName=%s" - " verify failed %s\n", hostnam, - cp, cp2, - X509_verify_cert_error_string(ret)); - else - fprintf(stderr, - "rpctls_server: client IP %s " - "issuerName=%s subjectName=%s" - " verify failed %s\n", hostnam, - cp, cp2, - X509_verify_cert_error_string(ret)); - } - if (ret == - X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT || - ret == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) - *flags |= RPCTLS_FLAGS_SELFSIGNED; - else if (ret == X509_V_OK) { - if (rpctls_comparehost) { - ret = 0; - if (gethostret != 0) - ret = rpctls_checkhost(sad, - cert); - if (ret != 1) { - *flags |= - RPCTLS_FLAGS_DISABLED; - rpctlssd_verbose_out( - "rpctls_server: " - "checkhost " - "failed\n"); - } - } - if (rpctls_cnuser) { - ret = rpctls_cnname(cert, uidp, - ngrps, gidp); - if (ret != 0) - *flags |= RPCTLS_FLAGS_CERTUSER; - } - *flags |= RPCTLS_FLAGS_VERIFIED; - } - X509_free(cert); - } else - rpctlssd_verbose_out("rpctls_server: " - "No peer certificate\n"); - } - - /* Check to see that ktls is working for the connection. */ - ret = BIO_get_ktls_send(SSL_get_wbio(ssl)); - rpctlssd_verbose_out("rpctls_server: BIO_get_ktls_send=%d\n", ret); - if (ret != 0) { - ret = BIO_get_ktls_recv(SSL_get_rbio(ssl)); - rpctlssd_verbose_out("rpctls_server: BIO_get_ktls_recv=%d\n", ret); - } - if (ret == 0) { - if (rpctls_debug_level == 0) - syslog(LOG_ERR, "ktls not working"); - else - fprintf(stderr, "ktls not working\n"); - /* - * The handshake has completed, so all that can be - * done is disable the connection. - */ - *flags |= RPCTLS_FLAGS_DISABLED; - } - - return (ssl); -} - -/* - * Get the client's IP address. - */ -static int -rpctls_gethost(int s, struct sockaddr *sad, char *hostip, size_t hostlen) -{ - socklen_t slen; - int ret; - - slen = sizeof(struct sockaddr_storage); - if (getpeername(s, sad, &slen) < 0) - return (0); - ret = 0; - if (getnameinfo((const struct sockaddr *)sad, - sad->sa_len, hostip, hostlen, - NULL, 0, NI_NUMERICHOST) == 0) { - rpctlssd_verbose_out("rpctls_gethost: %s\n", - hostip); - ret = 1; - } - return (ret); -} - -/* - * Check a client 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); - rpctlssd_verbose_out("rpctls_checkhost: DNS %s\n", - hostnam); - ret = X509_check_host(cert, hostnam, strlen(hostnam), - rpctls_wildcard, NULL); - return (ret); -} - -/* - * Acquire the dnsname for this server. - */ -static char * -rpctls_getdnsname(char *hostname) -{ - char *cp, *dnsname; - struct addrinfo *aip, hints; - int error; - - dnsname = NULL; - if (gethostname(hostname, MAXHOSTNAMELEN) == 0) { - if ((cp = strchr(hostname, '.')) != NULL && - *(cp + 1) != '\0') { - *cp = '@'; - dnsname = cp; - } else { - memset((void *)&hints, 0, sizeof (hints)); - hints.ai_flags = AI_CANONNAME; - error = getaddrinfo(hostname, NULL, &hints, &aip); - if (error == 0) { - if (aip->ai_canonname != NULL && - (cp = strchr(aip->ai_canonname, '.')) != - NULL && *(cp + 1) != '\0') { - hostname[0] = '@'; - strlcpy(&hostname[1], cp + 1, - MAXHOSTNAMELEN + 1); - dnsname = hostname; - } - freeaddrinfo(aip); - } - } - } - return (dnsname); -} - -/* - * Check a commonName to see if it maps to "user@domain" and - * acquire a for it if it does. - */ -static int -rpctls_cnname(X509 *cert, uint32_t *uidp, int *ngrps, uint32_t *gidp) -{ - char *cp, usern[1024 + 1]; - struct passwd *pwd; - gid_t gids[NGROUPS]; - int i; - GENERAL_NAMES *genlist; - GENERAL_NAME *genname; - OTHERNAME *val; - - /* First, find the otherName in the subjectAltName. */ - genlist = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); -rpctlssd_verbose_out("genlist=%p\n", genlist); - if (genlist == NULL) - return (0); - val = NULL; - for (i = 0; i < sk_GENERAL_NAME_num(genlist); i++) { - genname = sk_GENERAL_NAME_value(genlist, i); - if (genname->type != GEN_OTHERNAME) - continue; - val = genname->d.otherName; - break; - } - if (val == NULL) - return (0); -rpctlssd_verbose_out("fnd type=0x%x len=%d anstyp=0x%x data=%s\n", val->value->type, val->value->value.utf8string->length, val->value->value.utf8string->type, val->value->value.utf8string->data); - - /* Check to see that it is the correct OID. */ - i = i2t_ASN1_OBJECT(usern, sizeof(usern), val->type_id); -rpctlssd_verbose_out("obj=%d str=%s\n", i, usern); - if (i != strlen(rpctls_cnuseroid) || memcmp(usern, rpctls_cnuseroid, - i) != 0) { - rpctlssd_verbose_out("rpctls_cnname: invalid cnuser " - "oid len=%d val=%s\n", i, usern); - return (0); - } - - /* Sanity check the otherName. */ - if (val->value->type != V_ASN1_UTF8STRING || - val->value->value.utf8string->length < 3 || - val->value->value.utf8string->length > sizeof(usern) - 1) { - rpctlssd_verbose_out("rpctls_cnname: invalid cnuser " - "type=%d\n", val->value->type); - return (0); - } - - /* Look for a "user" in the otherName */ - memcpy(usern, val->value->value.utf8string->data, - val->value->value.utf8string->length); - usern[val->value->value.utf8string->length] = '\0'; - rpctlssd_verbose_out("rpctls_cnname: userstr %s\n", usern); - - /* Now, look for the @dnsname suffix in the commonName. */ - cp = strcasestr(usern, rpctls_dnsname); - if (cp == NULL) - return (0); -rpctlssd_verbose_out("dns=%s\n", cp); - if (*(cp + strlen(rpctls_dnsname)) != '\0') - return (0); - *cp = '\0'; - - /* See if the "user" is in the passwd database. */ -rpctlssd_verbose_out("user=%s\n", usern); - pwd = getpwnam(usern); - if (pwd == NULL) - return (0); -rpctlssd_verbose_out("pwname=%s\n", pwd->pw_name); - *uidp = pwd->pw_uid; - *ngrps = NGROUPS; - if (getgrouplist(pwd->pw_name, pwd->pw_gid, gids, ngrps) < 0) - return (0); - for (i = 0; i < *ngrps; i++) - gidp[i] = gids[i]; - return (1); -} - -/* - * (re)load the CRLfile into the certificate verification store. - */ -static int -rpctls_loadcrlfile(SSL_CTX *ctx) -{ - X509_STORE *certstore; - X509_LOOKUP *certlookup; - int ret; - - 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) { - rpctlssd_verbose_out( - "rpctls_loadcrlfile: Can't" - " load CRLfile=%s\n", - rpctls_crlfile); - return (ret); - } - } - return (1); -} - -static void -rpctls_huphandler(int sig __unused) -{ - - rpctls_gothup = true; -} - Property changes on: projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/rpctlssd/Makefile =================================================================== --- projects/nfs-over-tls/usr.sbin/rpctlssd/Makefile (revision 365351) +++ projects/nfs-over-tls/usr.sbin/rpctlssd/Makefile (nonexistent) @@ -1,32 +0,0 @@ -# $FreeBSD$ - -.include - -PROG= rpctlssd -MAN= rpctlssd.8 -SRCS= rpctlssd.c rpctlssd.h rpctlssd_svc.c rpctlssd_xdr.c - -CFLAGS+= -I. - -CFLAGS+= -I/usr/ktls/include -LDFLAGS+= -L/usr/ktls/lib - -LIBADD= ssl crypto util - -CLEANFILES= rpctlssd_svc.c rpctlssd_xdr.c rpctlssd.h - -RPCSRC= ${SRCTOP}/sys/rpc/rpcsec_tls/rpctlssd.x -RPCGEN= RPCGEN_CPP=${CPP:Q} rpcgen -L -C -M - -rpctlssd_svc.c: ${RPCSRC} rpctlssd.h - ${RPCGEN} -m -o ${.TARGET} ${RPCSRC} - -rpctlssd_xdr.c: ${RPCSRC} rpctlssd.h - ${RPCGEN} -c -o ${.TARGET} ${RPCSRC} - -rpctlssd.h: ${RPCSRC} - ${RPCGEN} -h -o ${.TARGET} ${RPCSRC} - -.PATH: ${SRCTOP}/sys/rpc/rpcsec_tls - -.include Property changes on: projects/nfs-over-tls/usr.sbin/rpctlssd/Makefile ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8 =================================================================== --- projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8 (revision 365351) +++ projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8 (nonexistent) @@ -1,332 +0,0 @@ -.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/ -.\" Authors: Doug Rabson -.\" Developed with Red Inc: Alfred Perlstein -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" $FreeBSD$ -.\" -.\" Modified from gssd.8 for rpctlssd.8 by Rick Macklem. -.Dd March 11, 2020 -.Dt RPCTLSSD 8 -.Os -.Sh NAME -.Nm rpctlssd -.Nd "Sun RPC over TLS Server Daemon" -.Sh SYNOPSIS -.Nm -.Op Fl D Ar certdir -.Op Fl d -.Op Fl h -.Op Fl l Ar CAfile -.Op Fl m -.Op Fl n Ar domain_name -.Op Fl p Ar CApath -.Op Fl r Ar CRLfile -.Op Fl u -.Op Fl v -.Op Fl W -.Op Fl w -.Sh DESCRIPTION -The -.Nm -program provides support for the server side of the kernel Sun RPC over TLS -implementation. -This daemon must be running to allow the kernel RPC to perform the TLS -handshake after a TCP client has sent the STARTTLS Null RPC request to -the server. -This daemon requires that the kernel be built with -.Dq options KERNEL_TLS -and be running on an architecture such as -.Dq amd64 -that supports a direct map (not i386). -Note that the -.Fl tls -option in the -.Xr exports 5 -file specifies that the client must use RPC over TLS. -The -.Fl tlscert -option in the -.Xr exports 5 -file specifies that the client must provide a certificate -that verifies. -The -.Fl tlscertuser -option in the -.Xr exports 5 -file specifies that the client must provide a certificate -that verifies and has a otherName:1.3.6.1.4.1.2238.1.1.1;UTF8: field of -subjectAltName of the form -.Dq user@dns_domain -that maps to a . -For the latter two cases, the -.Fl m -and either the -.Fl l -or -.Fl p -options must be specified. -The -.Fl tlscertuser -option also requires that the -.Fl u -option on this daemon be specified. -.Pp -Also, if the IP address used by the client cannot be trusted, -the rules in -.Xr exports 5 -cannot be applied safely. -As such, the -.Fl h -option can be used along with -.Fl m -and either the -.Fl l -or -.Fl p -options to require that the client certificate have the correct -Fully Qualified Domain Name (FQDN) in it. -.Pp -A certificate and associated key must exist in /etc/rpctlssd -(or the -.Dq certdir -specified by the -.Fl D -option) -in files named -.Dq cert.pem -and -.Dq key.pem . -.Pp -If a SIGHUP signal is sent to the daemon it will reload the -.Dq CRLfile . -If the -.Fl r -option was not specified, the SIGHUP signal will be ignored. -.Pp -The daemon will log failed certificate verifications via -.Xr syslogd 8 -using LOG_INFO | LOG_DAEMON when the -.Fl m -option has been specified. -.Pp -The options are as follows: -.Bl -tag -width indent -.It Fl D Ar certdir -Use -.Dq certdir -instead of /etc/rpctlssd as the location for the -certificate in a file called -.Dq cert.pem -and key in -.Dq key.pem . -.It Fl d -Run in debug mode. -In this mode, -.Nm -will not fork when it starts. -.It Fl h -This option specifies that the client must provide a certificate -that both verifies and has a FQDN that matches the reverse -DNS name for the IP address that -the client uses to connect to the server. -The FQDN should be -in the DNS field of the subjectAltName, but is also allowed -to be in the CN field of the -subjectName in the certificate. -By default, a wildcard "*" in the FQDN is not allowed. -With this option, a failure to verify the client certificate -or match the FQDN will result in the -server sending AUTH_REJECTEDCRED replies to all client RPCs. -This option requires the -.Fl m -and either the -.Fl l -or -.Fl p -options. -.It Fl l Ar CAfile -This option specifies the path name of a CA certificate(s) file -in pem format, which is used to verify client certificates and to -set the list of CA(s) sent to the client so that it knows which -certificate to send to the server during the TLS handshake. -This path name is used in -.Dq SSL_CTX_load_verify_locations(ctx,CAfile,NULL) -and -.Dq SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file(CAfile)) -openssl library calls. -Note that this is a path name for the file and is not assumed to be -in -.Dq certdir . -Either this option or the -.Fl p -option must be specified when the -.Fl m -option is specified so that the daemon can verify the client's -certificate. -.It Fl m -This option specifies that the server is to request a certificate -from the client during the TLS handshake. -It does not require that the client provide a certificate. -It should be specified unless no client doing RPC over TLS is -required to have a certificate. -For NFS, either the export option -.Fl tlscert -or -.Fl tlscertuser -may be used to require a client to provide a certificate -that verifies. -See -.Xr exports 5 . -.It Fl n Ar domain_name -This option specifies what the -.Dq domain_name -is for use with the -.Fl u -option, overriding the domain_name of the server this daemon is running on. -If you have specified the -.Fl domain -command line option for -.Xr nfsuserd 8 -then you should specify this option with the same -.Dq domain_name -that was specified for -.Xr nfsuserd 8 . -.It Fl p Ar CApath -This option is similar to the -.Fl l -option, but specifies the path of a directory with CA -certificates in it. -When this option is used, -.Dq SSL_CTX_set_client_CA_list(ctx,SSL_load_client_CA_file()) -is not called, so a list of CA names might not be passed -to the client during the TLS handshake. -(I was not able to determine if/when this matters, but -if in doubt, use the -.Fl l -option instead of this option.) -.It Fl r Ar CRLfile -This option specifies a Certificate Revocation List (CRL) file -that is to be loaded into the verify certificate store and -checked during verification. -This option is meaningless unless either the -.Fl l -or -.Fl p -have been specified. -.It Fl u -This option specifies that if the client provides a certificate -that both verifies and has a subjectAltName with an otherName of the form -.Dq otherName:1.3.6.1.4.1.2238.1.1.1;UTF8:user@dns_domain -the daemon will attempt to map -.Dq user@dns_domain -in the above -to a . -The mapping of -.Dq user@dns_domain -is done in the same manner as the -.Xr nfsuserd 8 -daemon, where -.Dq dns_domain -is the domain of the NFS server (or the one set via the -.Fl n -option) and -.Dq user -is a valid username in the password database. -If this mapping is successful, then the for -.Dq user -will be used for all -RPCs on the mount instead of the credentials in the RPC request -header. -This option requires the -.Fl m -and either the -.Fl l -or -.Fl p -options. -Use of this option does not conform to RFC-X, which does -not allow certificates to be used for user authentication. -.It Fl v -Run in verbose mode. -In this mode, -.Nm -will log activity messages to -.Xr syslogd 8 -using LOG_INFO | LOG_DAEMON or to -stderr, if the -.Fl d -option has also been specified. -.It Fl W -This option is used with the -.Fl h -option to allow use of a wildcard -.Dq * -that matches multiple -components of the reverse DNS name for the client's IP -address. -For example, the FQDN -.Dq *.uoguelph.ca -would match both -.Dq laptop21.uoguelph.ca -and -.Dq laptop3.cis.uoguelph.ca . -.It Fl w -Similar to -.Fl W -but allows the wildcard -.Dq * -to match a single component of the reverse DNS name. -For example, the FQDN -.Dq *.uoguelph.ca -would match -.Dq laptop21.uoguelph.ca -but not -.Dq laptop3.cis.uoguelph.ca . -Only one of the -.Fl W -and -.Fl w -options is allowed. -.El -.Sh EXIT STATUS -.Ex -std -.Sh SEE ALSO -.Xr openssl 1 , -.Xr exports 5 , -.Xr mount_nfs 8 , -.Xr nfsuserd 8 , -.Xr rpctlscd 8 , -.Xr syslogd 8 -.Sh BUGS -This daemon cannot be safely shut down and restarted if there are -any active RPC-over-TLS connections. -Doing so will orphan the KERNEL_TLS connections, so that they -can no longer do upcalls successfully, since the -.Dq SSL * -structures in userspace have been lost. -.Sh HISTORY -The -.Nm -manual page first appeared in -.Fx 13.0 . Property changes on: projects/nfs-over-tls/usr.sbin/rpctlssd/rpctlssd.8 ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/mountd/mountd.8 =================================================================== --- projects/nfs-over-tls/usr.sbin/mountd/mountd.8 (revision 365351) +++ projects/nfs-over-tls/usr.sbin/mountd/mountd.8 (nonexistent) @@ -1,198 +0,0 @@ -.\" Copyright (c) 1989, 1991, 1993 -.\" The Regents of the University of California. All rights reserved. -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" 3. Neither the name of the University nor the names of its contributors -.\" may be used to endorse or promote products derived from this software -.\" without specific prior written permission. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95 -.\" $FreeBSD$ -.\" -.Dd August 1, 2019 -.Dt MOUNTD 8 -.Os -.Sh NAME -.Nm mountd -.Nd service remote -.Tn NFS -mount requests -.Sh SYNOPSIS -.Nm -.Op Fl 2delnrS -.Op Fl h Ar bindip -.Op Fl p Ar port -.Op Ar exportsfile ... -.Sh DESCRIPTION -The -.Nm -utility is the server for -.Tn NFS -mount requests from other client machines. -It listens for service requests at the port indicated in the -.Tn NFS -server specification; see -.%T "Network File System Protocol Specification" , -RFC1094, Appendix A and -.%T "NFS: Network File System Version 3 Protocol Specification" , -RFC1813, Appendix I. -.Pp -The following options are available: -.Bl -tag -width indent -.It Fl 2 -Allow the administrator to force clients to use only the -version 2 -.Tn NFS -protocol to mount file systems from this server. -.It Fl d -Output debugging information. -.Nm -will not detach from the controlling terminal and will print -debugging messages to stderr. -.It Fl e -Ignored; included for backward compatibility. -.It Fl h Ar bindip -Specify specific IP addresses to bind to for TCP and UDP requests. -This option may be specified multiple times. -If no -.Fl h -option is specified, -.Nm -will bind to -.Dv INADDR_ANY . -Note that when specifying IP addresses with -.Fl h , -.Nm -will automatically add -.Li 127.0.0.1 -and if IPv6 is enabled, -.Li ::1 -to the list. -.It Fl l -Cause all succeeded -.Nm -requests to be logged. -.It Fl n -Allow non-root mount requests to be served. -This should only be specified if there are clients such as PC's, -that require it. -It will automatically clear the vfs.nfsd.nfs_privport sysctl flag, which -controls if the kernel will accept NFS requests from reserved ports only. -.It Fl p Ar port -Force -.Nm -to bind to the specified port, for both -.Dv AF_INET -and -.Dv AF_INET6 -address families. -This is typically done to ensure that the port which -.Nm -binds to is a known quantity which can be used in firewall rulesets. -If -.Nm -cannot bind to this port, an appropriate error will be recorded in -the system log, and the daemon will then exit. -.It Fl r -Allow mount RPCs requests for regular files to be served. -Although this seems to violate the mount protocol specification, -some diskless workstations do mount requests for -their swapfiles and expect them to be regular files. -Since a regular file cannot be specified in -.Pa /etc/exports , -the entire file system in which the swapfiles resides -will have to be exported with the -.Fl alldirs -flag. -.It Ar exportsfile -Specify an alternate location -for the exports file. -More than one exports file can be specified. -.It Fl S -Tell mountd to suspend/resume execution of the nfsd threads whenever -the exports list is being reloaded. -This avoids intermittent access -errors for clients that do NFS RPCs while the exports are being -reloaded, but introduces a delay in RPC response while the reload -is in progress. -If -.Nm -crashes while an exports load is in progress, -.Nm -must be restarted to get the nfsd threads running again, if this -option is used. -.El -.Pp -When -.Nm -is started, -it loads the export host addresses and options into the kernel -using the -.Xr mount 2 -system call. -After changing the exports file, -a hangup signal should be sent to the -.Nm -daemon -to get it to reload the export information. -After sending the SIGHUP -(kill \-s HUP `cat /var/run/mountd.pid`), -check the syslog output to see if -.Nm -logged any parsing -errors in the exports file. -.Pp -If -.Nm -detects that the running kernel does not include -.Tn NFS -support, it will attempt to load a loadable kernel module containing -.Tn NFS -code, using -.Xr kldload 2 . -If this fails, or no -.Tn NFS -KLD was available, -.Nm -exits with an error. -.Sh FILES -.Bl -tag -width /var/run/mountd.pid -compact -.It Pa /etc/exports -the list of exported file systems -.It Pa /var/run/mountd.pid -the pid of the currently running mountd -.It Pa /var/db/mountdtab -the current list of remote mounted file systems -.El -.Sh SEE ALSO -.Xr nfsstat 1 , -.Xr kldload 2 , -.Xr nfsv4 4 , -.Xr exports 5 , -.Xr nfsd 8 , -.Xr rpcbind 8 , -.Xr showmount 8 -.Sh HISTORY -The -.Nm -utility first appeared in -.Bx 4.4 . Property changes on: projects/nfs-over-tls/usr.sbin/mountd/mountd.8 ___________________________________________________________________ Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Index: projects/nfs-over-tls/usr.sbin/mountd/mountd.c =================================================================== --- projects/nfs-over-tls/usr.sbin/mountd/mountd.c (revision 365351) +++ projects/nfs-over-tls/usr.sbin/mountd/mountd.c (nonexistent) @@ -1,3865 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1989, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Herb Hasler and Rick Macklem at The University of Guelph. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifndef lint -static const char copyright[] = -"@(#) Copyright (c) 1989, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif /*not lint*/ - -#if 0 -#ifndef lint -static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; -#endif /*not lint*/ -#endif - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pathnames.h" -#include "mntopts.h" - -#ifdef DEBUG -#include -#endif - -/* - * Structures for keeping the mount list and export list - */ -struct mountlist { - char ml_host[MNTNAMLEN+1]; - char ml_dirp[MNTPATHLEN+1]; - - SLIST_ENTRY(mountlist) next; -}; - -struct dirlist { - struct dirlist *dp_left; - struct dirlist *dp_right; - int dp_flag; - struct hostlist *dp_hosts; /* List of hosts this dir exported to */ - char *dp_dirp; -}; -/* dp_flag bits */ -#define DP_DEFSET 0x1 -#define DP_HOSTSET 0x2 - -/* - * maproot/mapall credentials. - */ -struct expcred { - uid_t cr_uid; - int cr_ngroups; - gid_t cr_groups[NGROUPS_MAX + 1]; -}; - -struct exportlist { - struct dirlist *ex_dirl; - struct dirlist *ex_defdir; - struct grouplist *ex_grphead; - int ex_flag; - fsid_t ex_fs; - char *ex_fsdir; - char *ex_indexfile; - struct expcred ex_defanon; - uint64_t ex_defexflags; - int ex_numsecflavors; - int ex_secflavors[MAXSECFLAVORS]; - int ex_defnumsecflavors; - int ex_defsecflavors[MAXSECFLAVORS]; - - SLIST_ENTRY(exportlist) entries; -}; -/* ex_flag bits */ -#define EX_LINKED 0x1 -#define EX_DONE 0x2 -#define EX_DEFSET 0x4 -#define EX_PUBLICFH 0x8 - -SLIST_HEAD(exportlisthead, exportlist); - -struct netmsk { - struct sockaddr_storage nt_net; - struct sockaddr_storage nt_mask; - char *nt_name; -}; - -union grouptypes { - struct addrinfo *gt_addrinfo; - struct netmsk gt_net; -}; - -struct grouplist { - int gr_type; - union grouptypes gr_ptr; - struct grouplist *gr_next; - struct expcred gr_anon; - uint64_t gr_exflags; - int gr_flag; - int gr_numsecflavors; - int gr_secflavors[MAXSECFLAVORS]; -}; -/* Group types */ -#define GT_NULL 0x0 -#define GT_HOST 0x1 -#define GT_NET 0x2 -#define GT_DEFAULT 0x3 -#define GT_IGNORE 0x5 - -/* Group flags */ -#define GR_FND 0x1 - -struct hostlist { - int ht_flag; /* Uses DP_xx bits */ - struct grouplist *ht_grp; - struct hostlist *ht_next; -}; - -struct fhreturn { - int fhr_flag; - int fhr_vers; - nfsfh_t fhr_fh; - int fhr_numsecflavors; - int *fhr_secflavors; -}; - -#define GETPORT_MAXTRY 20 /* Max tries to get a port # */ - -/* - * How long to delay a reload of exports when there are RPC request(s) - * to process, in usec. Must be less than 1second. - */ -#define RELOADDELAY 250000 - -/* Global defs */ -static char *add_expdir(struct dirlist **, char *, int); -static void add_dlist(struct dirlist **, struct dirlist *, - struct grouplist *, int, struct exportlist *, - struct expcred *, uint64_t); -static void add_mlist(char *, char *); -static int check_dirpath(char *); -static int check_options(struct dirlist *); -static int checkmask(struct sockaddr *sa); -static int chk_host(struct dirlist *, struct sockaddr *, int *, int *, - int *, int **); -static char *strsep_quote(char **stringp, const char *delim); -static int create_service(struct netconfig *nconf); -static void complete_service(struct netconfig *nconf, char *port_str); -static void clearout_service(void); -static void del_mlist(char *hostp, char *dirp); -static struct dirlist *dirp_search(struct dirlist *, char *); -static int do_export_mount(struct exportlist *, struct statfs *); -static int do_mount(struct exportlist *, struct grouplist *, uint64_t, - struct expcred *, char *, int, struct statfs *, int, int *); -static int do_opt(char **, char **, struct exportlist *, - struct grouplist *, int *, uint64_t *, struct expcred *); -static struct exportlist *ex_search(fsid_t *, struct exportlisthead *); -static struct exportlist *get_exp(void); -static void free_dir(struct dirlist *); -static void free_exp(struct exportlist *); -static void free_grp(struct grouplist *); -static void free_host(struct hostlist *); -static void free_v4rootexp(void); -static void get_exportlist_one(int); -static void get_exportlist(int); -static void insert_exports(struct exportlist *, struct exportlisthead *); -static void free_exports(struct exportlisthead *); -static void read_exportfile(int); -static int compare_nmount_exportlist(struct iovec *, int, char *); -static int compare_export(struct exportlist *, struct exportlist *); -static int compare_cred(struct expcred *, struct expcred *); -static int compare_secflavor(int *, int *, int); -static void delete_export(struct iovec *, int, struct statfs *, char *); -static int get_host(char *, struct grouplist *, struct grouplist *); -static struct hostlist *get_ht(void); -static int get_line(void); -static void get_mountlist(void); -static int get_net(char *, struct netmsk *, int); -static void getexp_err(struct exportlist *, struct grouplist *, const char *); -static struct grouplist *get_grp(void); -static void hang_dirp(struct dirlist *, struct grouplist *, - struct exportlist *, int, struct expcred *, uint64_t); -static void huphandler(int sig); -static int makemask(struct sockaddr_storage *ssp, int bitlen); -static void mntsrv(struct svc_req *, SVCXPRT *); -static void nextfield(char **, char **); -static void out_of_mem(void); -static void parsecred(char *, struct expcred *); -static int parsesec(char *, struct exportlist *); -static int put_exlist(struct dirlist *, XDR *, struct dirlist *, - int *, int); -static void *sa_rawaddr(struct sockaddr *sa, int *nbytes); -static int sacmp(struct sockaddr *sa1, struct sockaddr *sa2, - struct sockaddr *samask); -static int scan_tree(struct dirlist *, struct sockaddr *); -static void usage(void); -static int xdr_dir(XDR *, char *); -static int xdr_explist(XDR *, caddr_t); -static int xdr_explist_brief(XDR *, caddr_t); -static int xdr_explist_common(XDR *, caddr_t, int); -static int xdr_fhs(XDR *, caddr_t); -static int xdr_mlist(XDR *, caddr_t); -static void terminate(int); -static void cp_cred(struct expcred *, struct expcred *); - -#define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize) -static struct exportlisthead *exphead = NULL; -static struct exportlisthead *oldexphead = NULL; -static int exphashsize = 0; -static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead); -static char *exnames_default[2] = { _PATH_EXPORTS, NULL }; -static char **exnames; -static char **hosts = NULL; -static int force_v2 = 0; -static int resvport_only = 1; -static int nhosts = 0; -static int dir_only = 1; -static int dolog = 0; -static int got_sighup = 0; -static int xcreated = 0; - -static char *svcport_str = NULL; -static int mallocd_svcport = 0; -static int *sock_fd; -static int sock_fdcnt; -static int sock_fdpos; -static int suspend_nfsd = 0; - -static int opt_flags; -static int have_v6 = 1; - -static int v4root_phase = 0; -static char v4root_dirpath[PATH_MAX + 1]; -static struct exportlist *v4root_ep = NULL; -static int has_publicfh = 0; -static int has_set_publicfh = 0; - -static struct pidfh *pfh = NULL; -/* Bits for opt_flags above */ -#define OP_MAPROOT 0x01 -#define OP_MAPALL 0x02 -/* 0x4 free */ -#define OP_MASK 0x08 -#define OP_NET 0x10 -#define OP_ALLDIRS 0x40 -#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */ -#define OP_QUIET 0x100 -#define OP_MASKLEN 0x200 -#define OP_SEC 0x400 - -#ifdef DEBUG -static int debug = 1; -static void SYSLOG(int, const char *, ...) __printflike(2, 3); -#define syslog SYSLOG -#else -static int debug = 0; -#endif - -/* - * The LOGDEBUG() syslog() calls are always compiled into the daemon. - * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty. - * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG. - */ -static int logdebug = 0; -#define LOGDEBUG(format, ...) \ - (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0) - -/* - * Similar to strsep(), but it allows for quoted strings - * and escaped characters. - * - * It returns the string (or NULL, if *stringp is NULL), - * which is a de-quoted version of the string if necessary. - * - * It modifies *stringp in place. - */ -static char * -strsep_quote(char **stringp, const char *delim) -{ - char *srcptr, *dstptr, *retval; - char quot = 0; - - if (stringp == NULL || *stringp == NULL) - return (NULL); - - srcptr = dstptr = retval = *stringp; - - while (*srcptr) { - /* - * We're looking for several edge cases here. - * First: if we're in quote state (quot != 0), - * then we ignore the delim characters, but otherwise - * process as normal, unless it is the quote character. - * Second: if the current character is a backslash, - * we take the next character as-is, without checking - * for delim, quote, or backslash. Exception: if the - * next character is a NUL, that's the end of the string. - * Third: if the character is a quote character, we toggle - * quote state. - * Otherwise: check the current character for NUL, or - * being in delim, and end the string if either is true. - */ - if (*srcptr == '\\') { - srcptr++; - /* - * The edge case here is if the next character - * is NUL, we want to stop processing. But if - * it's not NUL, then we simply want to copy it. - */ - if (*srcptr) { - *dstptr++ = *srcptr++; - } - continue; - } - if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) { - quot = *srcptr++; - continue; - } - if (quot && *srcptr == quot) { - /* End of the quoted part */ - quot = 0; - srcptr++; - continue; - } - if (!quot && strchr(delim, *srcptr)) - break; - *dstptr++ = *srcptr++; - } - - *stringp = (*srcptr == '\0') ? NULL : srcptr + 1; - *dstptr = 0; /* Terminate the string */ - return (retval); -} - -/* - * Mountd server for NFS mount protocol as described in: - * NFS: Network File System Protocol Specification, RFC1094, Appendix A - * The optional arguments are the exports file name - * default: _PATH_EXPORTS - * and "-n" to allow nonroot mount. - */ -int -main(int argc, char **argv) -{ - fd_set readfds; - struct netconfig *nconf; - char *endptr, **hosts_bak; - void *nc_handle; - pid_t otherpid; - in_port_t svcport; - int c, k, s; - int maxrec = RPC_MAXDATASIZE; - int attempt_cnt, port_len, port_pos, ret; - char **port_list; - uint64_t curtime, nexttime; - struct timeval tv; - struct timespec tp; - sigset_t sighup_mask; - - /* Check that another mountd isn't already running. */ - pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid); - if (pfh == NULL) { - if (errno == EEXIST) - errx(1, "mountd already running, pid: %d.", otherpid); - warn("cannot open or create pidfile"); - } - - s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (s < 0) - have_v6 = 0; - else - close(s); - - while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1) - switch (c) { - case '2': - force_v2 = 1; - break; - case 'e': - /* now a no-op, since this is the default */ - break; - case 'n': - resvport_only = 0; - break; - case 'r': - dir_only = 0; - break; - case 'd': - debug = debug ? 0 : 1; - break; - case 'l': - dolog = 1; - break; - case 'p': - endptr = NULL; - svcport = (in_port_t)strtoul(optarg, &endptr, 10); - if (endptr == NULL || *endptr != '\0' || - svcport == 0 || svcport >= IPPORT_MAX) - usage(); - svcport_str = strdup(optarg); - break; - case 'h': - ++nhosts; - hosts_bak = hosts; - hosts_bak = realloc(hosts, nhosts * sizeof(char *)); - if (hosts_bak == NULL) { - if (hosts != NULL) { - for (k = 0; k < nhosts; k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } - } - hosts = hosts_bak; - hosts[nhosts - 1] = strdup(optarg); - if (hosts[nhosts - 1] == NULL) { - for (k = 0; k < (nhosts - 1); k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } - break; - case 'S': - suspend_nfsd = 1; - break; - default: - usage(); - } - - if (modfind("nfsd") < 0) { - /* Not present in kernel, try loading it */ - if (kldload("nfsd") < 0 || modfind("nfsd") < 0) - errx(1, "NFS server is not available"); - } - - argc -= optind; - argv += optind; - if (argc > 0) - exnames = argv; - else - exnames = exnames_default; - openlog("mountd", LOG_PID, LOG_DAEMON); - if (debug) - warnx("getting export list"); - get_exportlist(0); - if (debug) - warnx("getting mount list"); - get_mountlist(); - if (debug) - warnx("here we go"); - if (debug == 0) { - daemon(0, 0); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - } - signal(SIGHUP, huphandler); - signal(SIGTERM, terminate); - signal(SIGPIPE, SIG_IGN); - - pidfile_write(pfh); - - rpcb_unset(MOUNTPROG, MOUNTVERS, NULL); - rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL); - rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec); - - if (!resvport_only) { - if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL, - &resvport_only, sizeof(resvport_only)) != 0 && - errno != ENOENT) { - syslog(LOG_ERR, "sysctl: %m"); - exit(1); - } - } - - /* - * If no hosts were specified, add a wildcard entry to bind to - * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the - * list. - */ - if (nhosts == 0) { - hosts = malloc(sizeof(char *)); - if (hosts == NULL) - out_of_mem(); - hosts[0] = "*"; - nhosts = 1; - } else { - hosts_bak = hosts; - if (have_v6) { - hosts_bak = realloc(hosts, (nhosts + 2) * - sizeof(char *)); - if (hosts_bak == NULL) { - for (k = 0; k < nhosts; k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } else - hosts = hosts_bak; - nhosts += 2; - hosts[nhosts - 2] = "::1"; - } else { - hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *)); - if (hosts_bak == NULL) { - for (k = 0; k < nhosts; k++) - free(hosts[k]); - free(hosts); - out_of_mem(); - } else { - nhosts += 1; - hosts = hosts_bak; - } - } - - hosts[nhosts - 1] = "127.0.0.1"; - } - - attempt_cnt = 1; - sock_fdcnt = 0; - sock_fd = NULL; - port_list = NULL; - port_len = 0; - nc_handle = setnetconfig(); - while ((nconf = getnetconfig(nc_handle))) { - if (nconf->nc_flag & NC_VISIBLE) { - if (have_v6 == 0 && strcmp(nconf->nc_protofmly, - "inet6") == 0) { - /* DO NOTHING */ - } else { - ret = create_service(nconf); - if (ret == 1) - /* Ignore this call */ - continue; - if (ret < 0) { - /* - * Failed to bind port, so close off - * all sockets created and try again - * if the port# was dynamically - * assigned via bind(2). - */ - clearout_service(); - if (mallocd_svcport != 0 && - attempt_cnt < GETPORT_MAXTRY) { - free(svcport_str); - svcport_str = NULL; - mallocd_svcport = 0; - } else { - errno = EADDRINUSE; - syslog(LOG_ERR, - "bindresvport_sa: %m"); - exit(1); - } - - /* Start over at the first service. */ - free(sock_fd); - sock_fdcnt = 0; - sock_fd = NULL; - nc_handle = setnetconfig(); - attempt_cnt++; - } else if (mallocd_svcport != 0 && - attempt_cnt == GETPORT_MAXTRY) { - /* - * For the last attempt, allow - * different port #s for each nconf - * by saving the svcport_str and - * setting it back to NULL. - */ - port_list = realloc(port_list, - (port_len + 1) * sizeof(char *)); - if (port_list == NULL) - out_of_mem(); - port_list[port_len++] = svcport_str; - svcport_str = NULL; - mallocd_svcport = 0; - } - } - } - } - - /* - * Successfully bound the ports, so call complete_service() to - * do the rest of the setup on the service(s). - */ - sock_fdpos = 0; - port_pos = 0; - nc_handle = setnetconfig(); - while ((nconf = getnetconfig(nc_handle))) { - if (nconf->nc_flag & NC_VISIBLE) { - if (have_v6 == 0 && strcmp(nconf->nc_protofmly, - "inet6") == 0) { - /* DO NOTHING */ - } else if (port_list != NULL) { - if (port_pos >= port_len) { - syslog(LOG_ERR, "too many port#s"); - exit(1); - } - complete_service(nconf, port_list[port_pos++]); - } else - complete_service(nconf, svcport_str); - } - } - endnetconfig(nc_handle); - free(sock_fd); - if (port_list != NULL) { - for (port_pos = 0; port_pos < port_len; port_pos++) - free(port_list[port_pos]); - free(port_list); - } - - if (xcreated == 0) { - syslog(LOG_ERR, "could not create any services"); - exit(1); - } - - /* Expand svc_run() here so that we can call get_exportlist(). */ - curtime = nexttime = 0; - sigemptyset(&sighup_mask); - sigaddset(&sighup_mask, SIGHUP); - for (;;) { - clock_gettime(CLOCK_MONOTONIC, &tp); - curtime = tp.tv_sec; - curtime = curtime * 1000000 + tp.tv_nsec / 1000; - sigprocmask(SIG_BLOCK, &sighup_mask, NULL); - if (got_sighup && curtime >= nexttime) { - got_sighup = 0; - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - get_exportlist(1); - clock_gettime(CLOCK_MONOTONIC, &tp); - nexttime = tp.tv_sec; - nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 + - RELOADDELAY; - } else - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - - /* - * If a reload is pending, poll for received request(s), - * otherwise set a RELOADDELAY timeout, since a SIGHUP - * could be processed between the got_sighup test and - * the select() system call. - */ - tv.tv_sec = 0; - if (got_sighup) - tv.tv_usec = 0; - else - tv.tv_usec = RELOADDELAY; - readfds = svc_fdset; - switch (select(svc_maxfd + 1, &readfds, NULL, NULL, &tv)) { - case -1: - if (errno == EINTR) { - /* Allow a reload now. */ - nexttime = 0; - continue; - } - syslog(LOG_ERR, "mountd died: select: %m"); - exit(1); - case 0: - /* Allow a reload now. */ - nexttime = 0; - continue; - default: - svc_getreqset(&readfds); - } - } -} - -/* - * This routine creates and binds sockets on the appropriate - * addresses. It gets called one time for each transport. - * It returns 0 upon success, 1 for ingore the call and -1 to indicate - * bind failed with EADDRINUSE. - * Any file descriptors that have been created are stored in sock_fd and - * the total count of them is maintained in sock_fdcnt. - */ -static int -create_service(struct netconfig *nconf) -{ - struct addrinfo hints, *res = NULL; - struct sockaddr_in *sin; - struct sockaddr_in6 *sin6; - struct __rpc_sockinfo si; - int aicode; - int fd; - int nhostsbak; - int one = 1; - int r; - u_int32_t host_addr[4]; /* IPv4 or IPv6 */ - int mallocd_res; - - if ((nconf->nc_semantics != NC_TPI_CLTS) && - (nconf->nc_semantics != NC_TPI_COTS) && - (nconf->nc_semantics != NC_TPI_COTS_ORD)) - return (1); /* not my type */ - - /* - * XXX - using RPC library internal functions. - */ - if (!__rpc_nconf2sockinfo(nconf, &si)) { - syslog(LOG_ERR, "cannot get information for %s", - nconf->nc_netid); - return (1); - } - - /* Get mountd's address on this transport */ - memset(&hints, 0, sizeof hints); - hints.ai_family = si.si_af; - hints.ai_socktype = si.si_socktype; - hints.ai_protocol = si.si_proto; - - /* - * Bind to specific IPs if asked to - */ - nhostsbak = nhosts; - while (nhostsbak > 0) { - --nhostsbak; - sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int)); - if (sock_fd == NULL) - out_of_mem(); - sock_fd[sock_fdcnt++] = -1; /* Set invalid for now. */ - mallocd_res = 0; - - hints.ai_flags = AI_PASSIVE; - - /* - * XXX - using RPC library internal functions. - */ - if ((fd = __rpc_nconf2fd(nconf)) < 0) { - int non_fatal = 0; - if (errno == EAFNOSUPPORT && - nconf->nc_semantics != NC_TPI_CLTS) - non_fatal = 1; - - syslog(non_fatal ? LOG_DEBUG : LOG_ERR, - "cannot create socket for %s", nconf->nc_netid); - if (non_fatal != 0) - continue; - exit(1); - } - - switch (hints.ai_family) { - case AF_INET: - if (inet_pton(AF_INET, hosts[nhostsbak], - host_addr) == 1) { - hints.ai_flags |= AI_NUMERICHOST; - } else { - /* - * Skip if we have an AF_INET6 address. - */ - if (inet_pton(AF_INET6, hosts[nhostsbak], - host_addr) == 1) { - close(fd); - continue; - } - } - break; - case AF_INET6: - if (inet_pton(AF_INET6, hosts[nhostsbak], - host_addr) == 1) { - hints.ai_flags |= AI_NUMERICHOST; - } else { - /* - * Skip if we have an AF_INET address. - */ - if (inet_pton(AF_INET, hosts[nhostsbak], - host_addr) == 1) { - close(fd); - continue; - } - } - - /* - * We're doing host-based access checks here, so don't - * allow v4-in-v6 to confuse things. The kernel will - * disable it by default on NFS sockets too. - */ - if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one, - sizeof one) < 0) { - syslog(LOG_ERR, - "can't disable v4-in-v6 on IPv6 socket"); - exit(1); - } - break; - default: - break; - } - - /* - * If no hosts were specified, just bind to INADDR_ANY - */ - if (strcmp("*", hosts[nhostsbak]) == 0) { - if (svcport_str == NULL) { - res = malloc(sizeof(struct addrinfo)); - if (res == NULL) - out_of_mem(); - mallocd_res = 1; - res->ai_flags = hints.ai_flags; - res->ai_family = hints.ai_family; - res->ai_protocol = hints.ai_protocol; - switch (res->ai_family) { - case AF_INET: - sin = malloc(sizeof(struct sockaddr_in)); - if (sin == NULL) - out_of_mem(); - sin->sin_family = AF_INET; - sin->sin_port = htons(0); - sin->sin_addr.s_addr = htonl(INADDR_ANY); - res->ai_addr = (struct sockaddr*) sin; - res->ai_addrlen = (socklen_t) - sizeof(struct sockaddr_in); - break; - case AF_INET6: - sin6 = malloc(sizeof(struct sockaddr_in6)); - if (sin6 == NULL) - out_of_mem(); - sin6->sin6_family = AF_INET6; - sin6->sin6_port = htons(0); - sin6->sin6_addr = in6addr_any; - res->ai_addr = (struct sockaddr*) sin6; - res->ai_addrlen = (socklen_t) - sizeof(struct sockaddr_in6); - break; - default: - syslog(LOG_ERR, "bad addr fam %d", - res->ai_family); - exit(1); - } - } else { - if ((aicode = getaddrinfo(NULL, svcport_str, - &hints, &res)) != 0) { - syslog(LOG_ERR, - "cannot get local address for %s: %s", - nconf->nc_netid, - gai_strerror(aicode)); - close(fd); - continue; - } - } - } else { - if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str, - &hints, &res)) != 0) { - syslog(LOG_ERR, - "cannot get local address for %s: %s", - nconf->nc_netid, gai_strerror(aicode)); - close(fd); - continue; - } - } - - /* Store the fd. */ - sock_fd[sock_fdcnt - 1] = fd; - - /* Now, attempt the bind. */ - r = bindresvport_sa(fd, res->ai_addr); - if (r != 0) { - if (errno == EADDRINUSE && mallocd_svcport != 0) { - if (mallocd_res != 0) { - free(res->ai_addr); - free(res); - } else - freeaddrinfo(res); - return (-1); - } - syslog(LOG_ERR, "bindresvport_sa: %m"); - exit(1); - } - - if (svcport_str == NULL) { - svcport_str = malloc(NI_MAXSERV * sizeof(char)); - if (svcport_str == NULL) - out_of_mem(); - mallocd_svcport = 1; - - if (getnameinfo(res->ai_addr, - res->ai_addr->sa_len, NULL, NI_MAXHOST, - svcport_str, NI_MAXSERV * sizeof(char), - NI_NUMERICHOST | NI_NUMERICSERV)) - errx(1, "Cannot get port number"); - } - if (mallocd_res != 0) { - free(res->ai_addr); - free(res); - } else - freeaddrinfo(res); - res = NULL; - } - return (0); -} - -/* - * Called after all the create_service() calls have succeeded, to complete - * the setup and registration. - */ -static void -complete_service(struct netconfig *nconf, char *port_str) -{ - struct addrinfo hints, *res = NULL; - struct __rpc_sockinfo si; - struct netbuf servaddr; - SVCXPRT *transp = NULL; - int aicode, fd, nhostsbak; - int registered = 0; - - if ((nconf->nc_semantics != NC_TPI_CLTS) && - (nconf->nc_semantics != NC_TPI_COTS) && - (nconf->nc_semantics != NC_TPI_COTS_ORD)) - return; /* not my type */ - - /* - * XXX - using RPC library internal functions. - */ - if (!__rpc_nconf2sockinfo(nconf, &si)) { - syslog(LOG_ERR, "cannot get information for %s", - nconf->nc_netid); - return; - } - - nhostsbak = nhosts; - while (nhostsbak > 0) { - --nhostsbak; - if (sock_fdpos >= sock_fdcnt) { - /* Should never happen. */ - syslog(LOG_ERR, "Ran out of socket fd's"); - return; - } - fd = sock_fd[sock_fdpos++]; - if (fd < 0) - continue; - - /* - * Using -1 tells listen(2) to use - * kern.ipc.soacceptqueue for the backlog. - */ - if (nconf->nc_semantics != NC_TPI_CLTS) - listen(fd, -1); - - if (nconf->nc_semantics == NC_TPI_CLTS ) - transp = svc_dg_create(fd, 0, 0); - else - transp = svc_vc_create(fd, RPC_MAXDATASIZE, - RPC_MAXDATASIZE); - - if (transp != (SVCXPRT *) NULL) { - if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv, - NULL)) - syslog(LOG_ERR, - "can't register %s MOUNTVERS service", - nconf->nc_netid); - if (!force_v2) { - if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3, - mntsrv, NULL)) - syslog(LOG_ERR, - "can't register %s MOUNTVERS3 service", - nconf->nc_netid); - } - } else - syslog(LOG_WARNING, "can't create %s services", - nconf->nc_netid); - - if (registered == 0) { - registered = 1; - memset(&hints, 0, sizeof hints); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = si.si_af; - hints.ai_socktype = si.si_socktype; - hints.ai_protocol = si.si_proto; - - if ((aicode = getaddrinfo(NULL, port_str, &hints, - &res)) != 0) { - syslog(LOG_ERR, "cannot get local address: %s", - gai_strerror(aicode)); - exit(1); - } - - servaddr.buf = malloc(res->ai_addrlen); - memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen); - servaddr.len = res->ai_addrlen; - - rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr); - rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr); - - xcreated++; - freeaddrinfo(res); - } - } /* end while */ -} - -/* - * Clear out sockets after a failure to bind one of them, so that the - * cycle of socket creation/binding can start anew. - */ -static void -clearout_service(void) -{ - int i; - - for (i = 0; i < sock_fdcnt; i++) { - if (sock_fd[i] >= 0) { - shutdown(sock_fd[i], SHUT_RDWR); - close(sock_fd[i]); - } - } -} - -static void -usage(void) -{ - fprintf(stderr, - "usage: mountd [-2] [-d] [-e] [-l] [-n] [-p ] [-r] " - "[-S] [-h ] [export_file ...]\n"); - exit(1); -} - -/* - * The mount rpc service - */ -void -mntsrv(struct svc_req *rqstp, SVCXPRT *transp) -{ - struct exportlist *ep; - struct dirlist *dp; - struct fhreturn fhr; - struct stat stb; - struct statfs fsb; - char host[NI_MAXHOST], numerichost[NI_MAXHOST]; - int lookup_failed = 1; - struct sockaddr *saddr; - u_short sport; - char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN]; - int defset, hostset; - long bad = 0; - sigset_t sighup_mask; - int numsecflavors, *secflavorsp; - - sigemptyset(&sighup_mask); - sigaddset(&sighup_mask, SIGHUP); - saddr = svc_getrpccaller(transp)->buf; - switch (saddr->sa_family) { - case AF_INET6: - sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); - break; - case AF_INET: - sport = ntohs(((struct sockaddr_in *)saddr)->sin_port); - break; - default: - syslog(LOG_ERR, "request from unknown address family"); - return; - } - switch (rqstp->rq_proc) { - case MOUNTPROC_MNT: - case MOUNTPROC_UMNT: - case MOUNTPROC_UMNTALL: - lookup_failed = getnameinfo(saddr, saddr->sa_len, host, - sizeof host, NULL, 0, 0); - } - getnameinfo(saddr, saddr->sa_len, numerichost, - sizeof numerichost, NULL, 0, NI_NUMERICHOST); - switch (rqstp->rq_proc) { - case NULLPROC: - if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) - syslog(LOG_ERR, "can't send reply"); - return; - case MOUNTPROC_MNT: - if (sport >= IPPORT_RESERVED && resvport_only) { - syslog(LOG_NOTICE, - "mount request from %s from unprivileged port", - numerichost); - svcerr_weakauth(transp); - return; - } - if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { - syslog(LOG_NOTICE, "undecodable mount request from %s", - numerichost); - svcerr_decode(transp); - return; - } - - /* - * Get the real pathname and make sure it is a directory - * or a regular file if the -r option was specified - * and it exists. - */ - if (realpath(rpcpath, dirpath) == NULL || - stat(dirpath, &stb) < 0 || - statfs(dirpath, &fsb) < 0) { - chdir("/"); /* Just in case realpath doesn't */ - syslog(LOG_NOTICE, - "mount request from %s for non existent path %s", - numerichost, dirpath); - if (debug) - warnx("stat failed on %s", dirpath); - bad = ENOENT; /* We will send error reply later */ - } - if (!bad && - !S_ISDIR(stb.st_mode) && - (dir_only || !S_ISREG(stb.st_mode))) { - syslog(LOG_NOTICE, - "mount request from %s for non-directory path %s", - numerichost, dirpath); - if (debug) - warnx("mounting non-directory %s", dirpath); - bad = ENOTDIR; /* We will send error reply later */ - } - - /* Check in the exports list */ - sigprocmask(SIG_BLOCK, &sighup_mask, NULL); - if (bad) - ep = NULL; - else - ep = ex_search(&fsb.f_fsid, exphead); - hostset = defset = 0; - if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset, - &numsecflavors, &secflavorsp) || - ((dp = dirp_search(ep->ex_dirl, dirpath)) && - chk_host(dp, saddr, &defset, &hostset, &numsecflavors, - &secflavorsp)) || - (defset && scan_tree(ep->ex_defdir, saddr) == 0 && - scan_tree(ep->ex_dirl, saddr) == 0))) { - if (bad) { - if (!svc_sendreply(transp, (xdrproc_t)xdr_long, - (caddr_t)&bad)) - syslog(LOG_ERR, "can't send reply"); - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return; - } - if (hostset & DP_HOSTSET) { - fhr.fhr_flag = hostset; - fhr.fhr_numsecflavors = numsecflavors; - fhr.fhr_secflavors = secflavorsp; - } else { - fhr.fhr_flag = defset; - fhr.fhr_numsecflavors = ep->ex_defnumsecflavors; - fhr.fhr_secflavors = ep->ex_defsecflavors; - } - fhr.fhr_vers = rqstp->rq_vers; - /* Get the file handle */ - memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t)); - if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) { - bad = errno; - syslog(LOG_ERR, "can't get fh for %s", dirpath); - if (!svc_sendreply(transp, (xdrproc_t)xdr_long, - (caddr_t)&bad)) - syslog(LOG_ERR, "can't send reply"); - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return; - } - if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs, - (caddr_t)&fhr)) - syslog(LOG_ERR, "can't send reply"); - if (!lookup_failed) - add_mlist(host, dirpath); - else - add_mlist(numerichost, dirpath); - if (debug) - warnx("mount successful"); - if (dolog) - syslog(LOG_NOTICE, - "mount request succeeded from %s for %s", - numerichost, dirpath); - } else { - if (!bad) - bad = EACCES; - syslog(LOG_NOTICE, - "mount request denied from %s for %s", - numerichost, dirpath); - } - - if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long, - (caddr_t)&bad)) - syslog(LOG_ERR, "can't send reply"); - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return; - case MOUNTPROC_DUMP: - if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - else if (dolog) - syslog(LOG_NOTICE, - "dump request succeeded from %s", - numerichost); - return; - case MOUNTPROC_UMNT: - if (sport >= IPPORT_RESERVED && resvport_only) { - syslog(LOG_NOTICE, - "umount request from %s from unprivileged port", - numerichost); - svcerr_weakauth(transp); - return; - } - if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) { - syslog(LOG_NOTICE, "undecodable umount request from %s", - numerichost); - svcerr_decode(transp); - return; - } - if (realpath(rpcpath, dirpath) == NULL) { - syslog(LOG_NOTICE, "umount request from %s " - "for non existent path %s", - numerichost, dirpath); - } - if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - if (!lookup_failed) - del_mlist(host, dirpath); - del_mlist(numerichost, dirpath); - if (dolog) - syslog(LOG_NOTICE, - "umount request succeeded from %s for %s", - numerichost, dirpath); - return; - case MOUNTPROC_UMNTALL: - if (sport >= IPPORT_RESERVED && resvport_only) { - syslog(LOG_NOTICE, - "umountall request from %s from unprivileged port", - numerichost); - svcerr_weakauth(transp); - return; - } - if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - if (!lookup_failed) - del_mlist(host, NULL); - del_mlist(numerichost, NULL); - if (dolog) - syslog(LOG_NOTICE, - "umountall request succeeded from %s", - numerichost); - return; - case MOUNTPROC_EXPORT: - if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL)) - if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief, - (caddr_t)NULL)) - syslog(LOG_ERR, "can't send reply"); - if (dolog) - syslog(LOG_NOTICE, - "export request succeeded from %s", - numerichost); - return; - default: - svcerr_noproc(transp); - return; - } -} - -/* - * Xdr conversion for a dirpath string - */ -static int -xdr_dir(XDR *xdrsp, char *dirp) -{ - return (xdr_string(xdrsp, &dirp, MNTPATHLEN)); -} - -/* - * Xdr routine to generate file handle reply - */ -static int -xdr_fhs(XDR *xdrsp, caddr_t cp) -{ - struct fhreturn *fhrp = (struct fhreturn *)cp; - u_long ok = 0, len, auth; - int i; - - if (!xdr_long(xdrsp, &ok)) - return (0); - switch (fhrp->fhr_vers) { - case 1: - return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH)); - case 3: - len = NFSX_V3FH; - if (!xdr_long(xdrsp, &len)) - return (0); - if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len)) - return (0); - if (fhrp->fhr_numsecflavors) { - if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors)) - return (0); - for (i = 0; i < fhrp->fhr_numsecflavors; i++) - if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i])) - return (0); - return (1); - } else { - auth = AUTH_SYS; - len = 1; - if (!xdr_long(xdrsp, &len)) - return (0); - return (xdr_long(xdrsp, &auth)); - } - } - return (0); -} - -static int -xdr_mlist(XDR *xdrsp, caddr_t cp __unused) -{ - struct mountlist *mlp; - int true = 1; - int false = 0; - char *strp; - - SLIST_FOREACH(mlp, &mlhead, next) { - if (!xdr_bool(xdrsp, &true)) - return (0); - strp = &mlp->ml_host[0]; - if (!xdr_string(xdrsp, &strp, MNTNAMLEN)) - return (0); - strp = &mlp->ml_dirp[0]; - if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) - return (0); - } - if (!xdr_bool(xdrsp, &false)) - return (0); - return (1); -} - -/* - * Xdr conversion for export list - */ -static int -xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief) -{ - struct exportlist *ep; - int false = 0; - int putdef; - sigset_t sighup_mask; - int i; - - sigemptyset(&sighup_mask); - sigaddset(&sighup_mask, SIGHUP); - sigprocmask(SIG_BLOCK, &sighup_mask, NULL); - - for (i = 0; i < exphashsize; i++) - SLIST_FOREACH(ep, &exphead[i], entries) { - putdef = 0; - if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, - &putdef, brief)) - goto errout; - if (ep->ex_defdir && putdef == 0 && - put_exlist(ep->ex_defdir, xdrsp, NULL, - &putdef, brief)) - goto errout; - } - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - if (!xdr_bool(xdrsp, &false)) - return (0); - return (1); -errout: - sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL); - return (0); -} - -/* - * Called from xdr_explist() to traverse the tree and export the - * directory paths. - */ -static int -put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp, - int brief) -{ - struct grouplist *grp; - struct hostlist *hp; - int true = 1; - int false = 0; - int gotalldir = 0; - char *strp; - - if (dp) { - if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief)) - return (1); - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = dp->dp_dirp; - if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) - return (1); - if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) { - gotalldir = 1; - *putdefp = 1; - } - if (brief) { - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = "(...)"; - if (!xdr_string(xdrsp, &strp, MNTPATHLEN)) - return (1); - } else if ((dp->dp_flag & DP_DEFSET) == 0 && - (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) { - hp = dp->dp_hosts; - while (hp) { - grp = hp->ht_grp; - if (grp->gr_type == GT_HOST) { - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = grp->gr_ptr.gt_addrinfo->ai_canonname; - if (!xdr_string(xdrsp, &strp, - MNTNAMLEN)) - return (1); - } else if (grp->gr_type == GT_NET) { - if (!xdr_bool(xdrsp, &true)) - return (1); - strp = grp->gr_ptr.gt_net.nt_name; - if (!xdr_string(xdrsp, &strp, - MNTNAMLEN)) - return (1); - } - hp = hp->ht_next; - if (gotalldir && hp == (struct hostlist *)NULL) { - hp = adp->dp_hosts; - gotalldir = 0; - } - } - } - if (!xdr_bool(xdrsp, &false)) - return (1); - if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief)) - return (1); - } - return (0); -} - -static int -xdr_explist(XDR *xdrsp, caddr_t cp) -{ - - return xdr_explist_common(xdrsp, cp, 0); -} - -static int -xdr_explist_brief(XDR *xdrsp, caddr_t cp) -{ - - return xdr_explist_common(xdrsp, cp, 1); -} - -static char *line; -static size_t linesize; -static FILE *exp_file; - -/* - * Get the export list from one, currently open file - */ -static void -get_exportlist_one(int passno) -{ - struct exportlist *ep; - struct grouplist *grp, *tgrp, *savgrp; - struct dirlist *dirhead; - struct statfs fsb; - struct expcred anon; - char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc; - int len, has_host, got_nondir, dirplen, netgrp; - uint64_t exflags; - - v4root_phase = 0; - dirhead = (struct dirlist *)NULL; - while (get_line()) { - if (debug) - warnx("got line %s", line); - cp = line; - nextfield(&cp, &endcp); - if (*cp == '#') - goto nextline; - - /* - * Set defaults. - */ - has_host = FALSE; - anon.cr_uid = UID_NOBODY; - anon.cr_ngroups = 1; - anon.cr_groups[0] = GID_NOGROUP; - exflags = MNT_EXPORTED; - got_nondir = 0; - opt_flags = 0; - ep = (struct exportlist *)NULL; - dirp = NULL; - - /* - * Handle the V4 root dir. - */ - if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') { - /* - * V4: just indicates that it is the v4 root point, - * so skip over that and set v4root_phase. - */ - if (v4root_phase > 0) { - syslog(LOG_ERR, "V4:duplicate line, ignored"); - goto nextline; - } - v4root_phase = 1; - cp += 3; - nextfield(&cp, &endcp); - } - - /* - * Create new exports list entry - */ - len = endcp-cp; - tgrp = grp = get_grp(); - while (len > 0) { - if (len > MNTNAMLEN) { - getexp_err(ep, tgrp, "mountpoint too long"); - goto nextline; - } - if (*cp == '-') { - if (ep == (struct exportlist *)NULL) { - getexp_err(ep, tgrp, - "flag before export path definition"); - goto nextline; - } - if (debug) - warnx("doing opt %s", cp); - got_nondir = 1; - if (do_opt(&cp, &endcp, ep, grp, &has_host, - &exflags, &anon)) { - getexp_err(ep, tgrp, NULL); - goto nextline; - } - } else if (*cp == '/') { - savedc = *endcp; - *endcp = '\0'; - if (v4root_phase > 1) { - if (dirp != NULL) { - getexp_err(ep, tgrp, "Multiple V4 dirs"); - goto nextline; - } - } - if (check_dirpath(cp) && - statfs(cp, &fsb) >= 0) { - if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0) - syslog(LOG_ERR, "Warning: exporting of " - "automounted fs %s not supported", cp); - if (got_nondir) { - getexp_err(ep, tgrp, "dirs must be first"); - goto nextline; - } - if (v4root_phase == 1) { - if (dirp != NULL) { - getexp_err(ep, tgrp, "Multiple V4 dirs"); - goto nextline; - } - if (strlen(v4root_dirpath) == 0) { - strlcpy(v4root_dirpath, cp, - sizeof (v4root_dirpath)); - } else if (strcmp(v4root_dirpath, cp) - != 0) { - syslog(LOG_ERR, - "different V4 dirpath %s", cp); - getexp_err(ep, tgrp, NULL); - goto nextline; - } - dirp = cp; - v4root_phase = 2; - got_nondir = 1; - ep = get_exp(); - } else { - if (ep) { - if (fsidcmp(&ep->ex_fs, &fsb.f_fsid) - != 0) { - getexp_err(ep, tgrp, - "fsid mismatch"); - goto nextline; - } - } else { - /* - * See if this directory is already - * in the list. - */ - ep = ex_search(&fsb.f_fsid, exphead); - if (ep == (struct exportlist *)NULL) { - ep = get_exp(); - ep->ex_fs = fsb.f_fsid; - ep->ex_fsdir = strdup(fsb.f_mntonname); - if (ep->ex_fsdir == NULL) - out_of_mem(); - if (debug) - warnx( - "making new ep fs=0x%x,0x%x", - fsb.f_fsid.val[0], - fsb.f_fsid.val[1]); - } else if (debug) - warnx("found ep fs=0x%x,0x%x", - fsb.f_fsid.val[0], - fsb.f_fsid.val[1]); - } - - /* - * Add dirpath to export mount point. - */ - dirp = add_expdir(&dirhead, cp, len); - dirplen = len; - } - } else { - getexp_err(ep, tgrp, - "symbolic link in export path or statfs failed"); - goto nextline; - } - *endcp = savedc; - } else { - savedc = *endcp; - *endcp = '\0'; - got_nondir = 1; - if (ep == (struct exportlist *)NULL) { - getexp_err(ep, tgrp, - "host(s) before export path definition"); - goto nextline; - } - - /* - * Get the host or netgroup. - */ - setnetgrent(cp); - netgrp = getnetgrent(&hst, &usr, &dom); - do { - if (has_host) { - grp->gr_next = get_grp(); - grp = grp->gr_next; - } - if (netgrp) { - if (hst == 0) { - syslog(LOG_ERR, - "null hostname in netgroup %s, skipping", cp); - grp->gr_type = GT_IGNORE; - } else if (get_host(hst, grp, tgrp)) { - syslog(LOG_ERR, - "bad host %s in netgroup %s, skipping", hst, cp); - grp->gr_type = GT_IGNORE; - } - } else if (get_host(cp, grp, tgrp)) { - syslog(LOG_ERR, "bad host %s, skipping", cp); - grp->gr_type = GT_IGNORE; - } - has_host = TRUE; - } while (netgrp && getnetgrent(&hst, &usr, &dom)); - endnetgrent(); - *endcp = savedc; - } - cp = endcp; - nextfield(&cp, &endcp); - len = endcp - cp; - } - if (check_options(dirhead)) { - getexp_err(ep, tgrp, NULL); - goto nextline; - } - if (!has_host) { - grp->gr_type = GT_DEFAULT; - if (debug) - warnx("adding a default entry"); - - /* - * Don't allow a network export coincide with a list of - * host(s) on the same line. - */ - } else if ((opt_flags & OP_NET) && tgrp->gr_next) { - getexp_err(ep, tgrp, "network/host conflict"); - goto nextline; - - /* - * If an export list was specified on this line, make sure - * that we have at least one valid entry, otherwise skip it. - */ - } else { - grp = tgrp; - while (grp && grp->gr_type == GT_IGNORE) - grp = grp->gr_next; - if (! grp) { - getexp_err(ep, tgrp, "no valid entries"); - goto nextline; - } - } - - if (v4root_phase == 1) { - getexp_err(ep, tgrp, "V4:root, no dirp, ignored"); - goto nextline; - } - - /* - * Loop through hosts, pushing the exports into the kernel. - * After loop, tgrp points to the start of the list and - * grp points to the last entry in the list. - * Do not do the do_mount() for passno == 1, since the - * second pass will do it, as required. - */ - grp = tgrp; - do { - grp->gr_exflags = exflags; - cp_cred(&grp->gr_anon, &anon); - if (v4root_phase == 2 && passno == 0) - LOGDEBUG("do_mount v4root"); - if (passno == 0 && do_mount(ep, grp, exflags, &anon, - dirp, dirplen, &fsb, ep->ex_numsecflavors, - ep->ex_secflavors)) { - getexp_err(ep, tgrp, NULL); - goto nextline; - } - } while (grp->gr_next && (grp = grp->gr_next)); - - /* - * For V4: don't enter in mount lists. - */ - if (v4root_phase > 0 && v4root_phase <= 2) { - /* - * These structures are used for the reload, - * so save them for that case. Otherwise, just - * free them up now. - */ - if (passno == 1 && ep != NULL) { - savgrp = tgrp; - while (tgrp != NULL) { - /* - * Save the security flavors and exflags - * for this host set in the groups. - */ - tgrp->gr_numsecflavors = - ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(tgrp->gr_secflavors, - ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - tgrp = tgrp->gr_next; - } - if (v4root_ep == NULL) { - v4root_ep = ep; - ep = NULL; /* Don't free below. */ - } - grp->gr_next = v4root_ep->ex_grphead; - v4root_ep->ex_grphead = savgrp; - } - if (ep != NULL) - free_exp(ep); - while (tgrp != NULL) { - grp = tgrp; - tgrp = tgrp->gr_next; - free_grp(grp); - } - goto nextline; - } - - /* - * Success. Update the data structures. - */ - if (has_host) { - hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags); - grp->gr_next = ep->ex_grphead; - ep->ex_grphead = tgrp; - } else { - hang_dirp(dirhead, (struct grouplist *)NULL, ep, - opt_flags, &anon, exflags); - free_grp(grp); - } - dirhead = (struct dirlist *)NULL; - if ((ep->ex_flag & EX_LINKED) == 0) { - insert_exports(ep, exphead); - - ep->ex_flag |= EX_LINKED; - } -nextline: - v4root_phase = 0; - if (dirhead) { - free_dir(dirhead); - dirhead = (struct dirlist *)NULL; - } - } -} - -/* - * Get the export list from all specified files - */ -static void -get_exportlist(int passno) -{ - struct export_args export; - struct iovec *iov; - struct statfs *mntbufp; - char errmsg[255]; - int num, i; - int iovlen; - struct nfsex_args eargs; - FILE *debug_file; - - if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) { - fclose(debug_file); - logdebug = 1; - } else - logdebug = 0; - LOGDEBUG("passno=%d", passno); - v4root_dirpath[0] = '\0'; - free_v4rootexp(); - if (passno == 1) { - /* - * Save the current lists as old ones, so that the new lists - * can be compared with the old ones in the 2nd pass. - */ - for (i = 0; i < exphashsize; i++) { - SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]); - SLIST_INIT(&exphead[i]); - } - - /* Note that the public fh has not yet been set. */ - has_set_publicfh = 0; - - /* Read the export file(s) and process them */ - read_exportfile(passno); - } else { - /* - * Just make the old lists empty. - * exphashsize == 0 for the first call, before oldexphead - * has been initialized-->loop won't be executed. - */ - for (i = 0; i < exphashsize; i++) - SLIST_INIT(&oldexphead[i]); - } - - bzero(&export, sizeof(export)); - export.ex_flags = MNT_DELEXPORT; - iov = NULL; - iovlen = 0; - bzero(errmsg, sizeof(errmsg)); - - if (suspend_nfsd != 0) - (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); - /* - * Delete the old V4 root dir. - */ - bzero(&eargs, sizeof (eargs)); - eargs.export.ex_flags = MNT_DELEXPORT; - if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, (caddr_t)&eargs) < 0 && - errno != ENOENT) - syslog(LOG_ERR, "Can't delete exports for V4:"); - - build_iovec(&iov, &iovlen, "fstype", NULL, 0); - build_iovec(&iov, &iovlen, "fspath", NULL, 0); - build_iovec(&iov, &iovlen, "from", NULL, 0); - build_iovec(&iov, &iovlen, "update", NULL, 0); - build_iovec(&iov, &iovlen, "export", &export, - sizeof(export)); - build_iovec(&iov, &iovlen, "errmsg", errmsg, - sizeof(errmsg)); - - /* - * For passno == 1, compare the old and new lists updating the kernel - * exports for any cases that have changed. - * This call is doing the second pass through the lists. - * If it fails, fall back on the bulk reload. - */ - if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) == - 0) { - LOGDEBUG("compareok"); - /* Free up the old lists. */ - free_exports(oldexphead); - } else { - LOGDEBUG("doing passno=0"); - /* - * Clear flag that notes if a public fh has been exported. - * It is set by do_mount() if MNT_EXPUBLIC is set for the entry. - */ - has_publicfh = 0; - - /* exphead == NULL if not yet allocated (first call). */ - if (exphead != NULL) { - /* - * First, get rid of the old lists. - */ - free_exports(exphead); - free_exports(oldexphead); - } - - /* - * And delete exports that are in the kernel for all local - * filesystems. - * XXX: Should know how to handle all local exportable - * filesystems. - */ - num = getmntinfo(&mntbufp, MNT_NOWAIT); - - /* Allocate hash tables, for first call. */ - if (exphead == NULL) { - /* Target an average linked list length of 10. */ - exphashsize = num / 10; - if (exphashsize < 1) - exphashsize = 1; - else if (exphashsize > 100000) - exphashsize = 100000; - exphead = malloc(exphashsize * sizeof(*exphead)); - oldexphead = malloc(exphashsize * sizeof(*oldexphead)); - if (exphead == NULL || oldexphead == NULL) - errx(1, "Can't malloc hash tables"); - - for (i = 0; i < exphashsize; i++) { - SLIST_INIT(&exphead[i]); - SLIST_INIT(&oldexphead[i]); - } - } - - for (i = 0; i < num; i++) - delete_export(iov, iovlen, &mntbufp[i], errmsg); - - - /* Read the export file(s) and process them */ - read_exportfile(0); - } - - if (iov != NULL) { - /* Free strings allocated by strdup() in getmntopts.c */ - free(iov[0].iov_base); /* fstype */ - free(iov[2].iov_base); /* fspath */ - free(iov[4].iov_base); /* from */ - free(iov[6].iov_base); /* update */ - free(iov[8].iov_base); /* export */ - free(iov[10].iov_base); /* errmsg */ - - /* free iov, allocated by realloc() */ - free(iov); - iovlen = 0; - } - - /* - * If there was no public fh, clear any previous one set. - */ - if (has_publicfh == 0) { - LOGDEBUG("clear public fh"); - (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); - } - - /* Resume the nfsd. If they weren't suspended, this is harmless. */ - (void)nfssvc(NFSSVC_RESUMENFSD, NULL); - LOGDEBUG("eo get_exportlist"); -} - -/* - * Insert an export entry in the appropriate list. - */ -static void -insert_exports(struct exportlist *ep, struct exportlisthead *exhp) -{ - uint32_t i; - - i = EXPHASH(&ep->ex_fs); - LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i); - SLIST_INSERT_HEAD(&exhp[i], ep, entries); -} - -/* - * Free up the exports lists passed in as arguments. - */ -static void -free_exports(struct exportlisthead *exhp) -{ - struct exportlist *ep, *ep2; - int i; - - for (i = 0; i < exphashsize; i++) { - SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) { - SLIST_REMOVE(&exhp[i], ep, exportlist, entries); - free_exp(ep); - } - SLIST_INIT(&exhp[i]); - } -} - -/* - * Read the exports file(s) and call get_exportlist_one() for each line. - */ -static void -read_exportfile(int passno) -{ - int done, i; - - /* - * Read in the exports file and build the list, calling - * nmount() as we go along to push the export rules into the kernel. - */ - done = 0; - for (i = 0; exnames[i] != NULL; i++) { - if (debug) - warnx("reading exports from %s", exnames[i]); - if ((exp_file = fopen(exnames[i], "r")) == NULL) { - syslog(LOG_WARNING, "can't open %s", exnames[i]); - continue; - } - get_exportlist_one(passno); - fclose(exp_file); - done++; - } - if (done == 0) { - syslog(LOG_ERR, "can't open any exports file"); - exit(2); - } -} - -/* - * Compare the export lists against the old ones and do nmount() operations - * for any cases that have changed. This avoids doing nmount() for entries - * that have not changed. - * Return 0 upon success, 1 otherwise. - */ -static int -compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg) -{ - struct exportlist *ep, *oep; - struct grouplist *grp; - struct statfs fs, ofs; - int i, ret; - - /* - * Loop through the current list and look for an entry in the old - * list. - * If found, check to see if it the same. - * If it is not the same, delete and re-export. - * Then mark it done on the old list. - * else (not found) - * export it. - * Any entries left in the old list after processing must have their - * exports deleted. - */ - for (i = 0; i < exphashsize; i++) - SLIST_FOREACH(ep, &exphead[i], entries) { - LOGDEBUG("foreach ep=%s", ep->ex_fsdir); - oep = ex_search(&ep->ex_fs, oldexphead); - if (oep != NULL) { - /* - * Check the mount paths are the same. - * If not, return 1 so that the reload of the - * exports will be done in bulk, the - * passno == 0 way. - */ - LOGDEBUG("found old exp"); - if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) - return (1); - LOGDEBUG("same fsdir"); - /* - * Test to see if the entry is the same. - * If not the same delete exports and - * re-export. - */ - if (compare_export(ep, oep) != 0) { - /* - * Clear has_publicfh if if was set - * in the old exports, but only if it - * has not been set during processing of - * the exports for this pass, as - * indicated by has_set_publicfh. - */ - if (has_set_publicfh == 0 && - (oep->ex_flag & EX_PUBLICFH) != 0) - has_publicfh = 0; - - /* Delete and re-export. */ - if (statfs(ep->ex_fsdir, &fs) < 0) - return (1); - delete_export(iov, iovlen, &fs, errmsg); - ret = do_export_mount(ep, &fs); - if (ret != 0) - return (ret); - } - oep->ex_flag |= EX_DONE; - LOGDEBUG("exdone"); - } else { - LOGDEBUG("not found so export"); - /* Not found, so do export. */ - if (statfs(ep->ex_fsdir, &fs) < 0) - return (1); - ret = do_export_mount(ep, &fs); - if (ret != 0) - return (ret); - } - } - - /* Delete exports not done. */ - for (i = 0; i < exphashsize; i++) - SLIST_FOREACH(oep, &oldexphead[i], entries) { - if ((oep->ex_flag & EX_DONE) == 0) { - LOGDEBUG("not done delete=%s", oep->ex_fsdir); - if (statfs(oep->ex_fsdir, &ofs) >= 0 && - fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) { - LOGDEBUG("do delete"); - /* - * Clear has_publicfh if if was set - * in the old exports, but only if it - * has not been set during processing of - * the exports for this pass, as - * indicated by has_set_publicfh. - */ - if (has_set_publicfh == 0 && - (oep->ex_flag & EX_PUBLICFH) != 0) - has_publicfh = 0; - - delete_export(iov, iovlen, &ofs, - errmsg); - } - } - } - - /* Do the V4 root exports, as required. */ - grp = NULL; - if (v4root_ep != NULL) - grp = v4root_ep->ex_grphead; - v4root_phase = 2; - while (v4root_ep != NULL && grp != NULL) { - LOGDEBUG("v4root expath=%s", v4root_dirpath); - ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon, - v4root_dirpath, strlen(v4root_dirpath), &fs, - grp->gr_numsecflavors, grp->gr_secflavors); - if (ret != 0) { - v4root_phase = 0; - return (ret); - } - grp = grp->gr_next; - } - v4root_phase = 0; - free_v4rootexp(); - return (0); -} - -/* - * Compare old and current exportlist entries for the fsid and return 0 - * if they are the same, 1 otherwise. - */ -static int -compare_export(struct exportlist *ep, struct exportlist *oep) -{ - struct grouplist *grp, *ogrp; - - if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0) - return (1); - if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET)) - return (1); - if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) || - (ep->ex_defdir == NULL && oep->ex_defdir != NULL)) - return (1); - if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) != - (oep->ex_defdir->dp_flag & DP_DEFSET)) - return (1); - if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors != - oep->ex_defnumsecflavors || ep->ex_defexflags != - oep->ex_defexflags || compare_cred(&ep->ex_defanon, - &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors, - oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0)) - return (1); - - /* Now, check all the groups. */ - for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) - ogrp->gr_flag = 0; - for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) { - for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = - ogrp->gr_next) - if ((ogrp->gr_flag & GR_FND) == 0 && - grp->gr_numsecflavors == ogrp->gr_numsecflavors && - grp->gr_exflags == ogrp->gr_exflags && - compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 && - compare_secflavor(grp->gr_secflavors, - ogrp->gr_secflavors, grp->gr_numsecflavors) == 0) - break; - if (ogrp != NULL) - ogrp->gr_flag |= GR_FND; - else - return (1); - } - for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next) - if ((ogrp->gr_flag & GR_FND) == 0) - return (1); - return (0); -} - -/* - * This algorithm compares two arrays of "n" items. It returns 0 if they are - * the "same" and 1 otherwise. Although suboptimal, it is always safe to - * return 1, which makes compare_nmount_export() reload the exports entry. - * "same" refers to having the same set of values in the two arrays. - * The arrays are in no particular order and duplicates (multiple entries - * in an array with the same value) is allowed. - * The algorithm is inefficient, but the common case of indentical arrays is - * handled first and "n" is normally fairly small. - * Since the two functions need the same algorithm but for arrays of - * different types (gid_t vs int), this is done as a macro. - */ -#define COMPARE_ARRAYS(a1, a2, n) \ - do { \ - int fnd, fndarray[(n)], i, j; \ - /* Handle common case of identical arrays. */ \ - for (i = 0; i < (n); i++) \ - if ((a1)[i] != (a2)[i]) \ - break; \ - if (i == (n)) \ - return (0); \ - for (i = 0; i < (n); i++) \ - fndarray[i] = 0; \ - for (i = 0; i < (n); i++) { \ - fnd = 0; \ - for (j = 0; j < (n); j++) { \ - if ((a1)[i] == (a2)[j]) { \ - fndarray[j] = 1; \ - fnd = 1; \ - } \ - } \ - if (fnd == 0) \ - return (1); \ - } \ - for (i = 0; i < (n); i++) \ - if (fndarray[i] == 0) \ - return (1); \ - return (0); \ - } while (0) - -/* - * Compare two struct expcred's. Return 0 if the same and 1 otherwise. - */ -static int -compare_cred(struct expcred *cr0, struct expcred *cr1) -{ - - if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups) - return (1); - - COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups); -} - -/* - * Compare two lists of security flavors. Return 0 if the same and 1 otherwise. - */ -static int -compare_secflavor(int *sec1, int *sec2, int nsec) -{ - - COMPARE_ARRAYS(sec1, sec2, nsec); -} - -/* - * Delete an exports entry. - */ -static void -delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg) -{ - struct xvfsconf vfc; - - if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) { - syslog(LOG_ERR, "getvfsbyname() failed for %s", - fsp->f_fstypename); - return; - } - - /* - * We do not need to delete "export" flag from - * filesystems that do not have it set. - */ - if (!(fsp->f_flags & MNT_EXPORTED)) - return; - /* - * Do not delete export for network filesystem by - * passing "export" arg to nmount(). - * It only makes sense to do this for local filesystems. - */ - if (vfc.vfc_flags & VFCF_NETWORK) - return; - - iov[1].iov_base = fsp->f_fstypename; - iov[1].iov_len = strlen(fsp->f_fstypename) + 1; - iov[3].iov_base = fsp->f_mntonname; - iov[3].iov_len = strlen(fsp->f_mntonname) + 1; - iov[5].iov_base = fsp->f_mntfromname; - iov[5].iov_len = strlen(fsp->f_mntfromname) + 1; - errmsg[0] = '\0'; - - /* - * EXDEV is returned when path exists but is not a - * mount point. May happens if raced with unmount. - */ - if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT && - errno != ENOTSUP && errno != EXDEV) { - syslog(LOG_ERR, - "can't delete exports for %s: %m %s", - fsp->f_mntonname, errmsg); - } -} - -/* - * Allocate an export list element - */ -static struct exportlist * -get_exp(void) -{ - struct exportlist *ep; - - ep = (struct exportlist *)calloc(1, sizeof (struct exportlist)); - if (ep == (struct exportlist *)NULL) - out_of_mem(); - return (ep); -} - -/* - * Allocate a group list element - */ -static struct grouplist * -get_grp(void) -{ - struct grouplist *gp; - - gp = (struct grouplist *)calloc(1, sizeof (struct grouplist)); - if (gp == (struct grouplist *)NULL) - out_of_mem(); - return (gp); -} - -/* - * Clean up upon an error in get_exportlist(). - */ -static void -getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason) -{ - struct grouplist *tgrp; - - if (!(opt_flags & OP_QUIET)) { - if (reason != NULL) - syslog(LOG_ERR, "bad exports list line '%s': %s", line, - reason); - else - syslog(LOG_ERR, "bad exports list line '%s'", line); - } - if (ep && (ep->ex_flag & EX_LINKED) == 0) - free_exp(ep); - while (grp) { - tgrp = grp; - grp = grp->gr_next; - free_grp(tgrp); - } -} - -/* - * Search the export list for a matching fs. - */ -static struct exportlist * -ex_search(fsid_t *fsid, struct exportlisthead *exhp) -{ - struct exportlist *ep; - uint32_t i; - - i = EXPHASH(fsid); - SLIST_FOREACH(ep, &exhp[i], entries) { - if (fsidcmp(&ep->ex_fs, fsid) == 0) - return (ep); - } - - return (ep); -} - -/* - * Add a directory path to the list. - */ -static char * -add_expdir(struct dirlist **dpp, char *cp, int len) -{ - struct dirlist *dp; - - dp = malloc(sizeof (struct dirlist)); - if (dp == (struct dirlist *)NULL) - out_of_mem(); - dp->dp_left = *dpp; - dp->dp_right = (struct dirlist *)NULL; - dp->dp_flag = 0; - dp->dp_hosts = (struct hostlist *)NULL; - dp->dp_dirp = strndup(cp, len); - if (dp->dp_dirp == NULL) - out_of_mem(); - *dpp = dp; - return (dp->dp_dirp); -} - -/* - * Hang the dir list element off the dirpath binary tree as required - * and update the entry for host. - */ -static void -hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep, - int flags, struct expcred *anoncrp, uint64_t exflags) -{ - struct hostlist *hp; - struct dirlist *dp2; - - if (flags & OP_ALLDIRS) { - if (ep->ex_defdir) - free((caddr_t)dp); - else - ep->ex_defdir = dp; - if (grp == (struct grouplist *)NULL) { - ep->ex_flag |= EX_DEFSET; - ep->ex_defdir->dp_flag |= DP_DEFSET; - /* Save the default security flavors list. */ - ep->ex_defnumsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(ep->ex_defsecflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - cp_cred(&ep->ex_defanon, anoncrp); - ep->ex_defexflags = exflags; - } else while (grp) { - hp = get_ht(); - hp->ht_grp = grp; - hp->ht_next = ep->ex_defdir->dp_hosts; - ep->ex_defdir->dp_hosts = hp; - /* Save the security flavors list for this host set. */ - grp->gr_numsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(grp->gr_secflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - grp = grp->gr_next; - } - } else { - - /* - * Loop through the directories adding them to the tree. - */ - while (dp) { - dp2 = dp->dp_left; - add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp, - exflags); - dp = dp2; - } - } -} - -/* - * Traverse the binary tree either updating a node that is already there - * for the new directory or adding the new node. - */ -static void -add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp, - int flags, struct exportlist *ep, struct expcred *anoncrp, - uint64_t exflags) -{ - struct dirlist *dp; - struct hostlist *hp; - int cmp; - - dp = *dpp; - if (dp) { - cmp = strcmp(dp->dp_dirp, newdp->dp_dirp); - if (cmp > 0) { - add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp, - exflags); - return; - } else if (cmp < 0) { - add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp, - exflags); - return; - } else - free((caddr_t)newdp); - } else { - dp = newdp; - dp->dp_left = (struct dirlist *)NULL; - *dpp = dp; - } - if (grp) { - - /* - * Hang all of the host(s) off of the directory point. - */ - do { - hp = get_ht(); - hp->ht_grp = grp; - hp->ht_next = dp->dp_hosts; - dp->dp_hosts = hp; - /* Save the security flavors list for this host set. */ - grp->gr_numsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(grp->gr_secflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - grp = grp->gr_next; - } while (grp); - } else { - ep->ex_flag |= EX_DEFSET; - dp->dp_flag |= DP_DEFSET; - /* Save the default security flavors list. */ - ep->ex_defnumsecflavors = ep->ex_numsecflavors; - if (ep->ex_numsecflavors > 0) - memcpy(ep->ex_defsecflavors, ep->ex_secflavors, - sizeof(ep->ex_secflavors)); - cp_cred(&ep->ex_defanon, anoncrp); - ep->ex_defexflags = exflags; - } -} - -/* - * Search for a dirpath on the export point. - */ -static struct dirlist * -dirp_search(struct dirlist *dp, char *dirp) -{ - int cmp; - - if (dp) { - cmp = strcmp(dp->dp_dirp, dirp); - if (cmp > 0) - return (dirp_search(dp->dp_left, dirp)); - else if (cmp < 0) - return (dirp_search(dp->dp_right, dirp)); - else - return (dp); - } - return (dp); -} - -/* - * Scan for a host match in a directory tree. - */ -static int -chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp, - int *hostsetp, int *numsecflavors, int **secflavorsp) -{ - struct hostlist *hp; - struct grouplist *grp; - struct addrinfo *ai; - - if (dp) { - if (dp->dp_flag & DP_DEFSET) - *defsetp = dp->dp_flag; - hp = dp->dp_hosts; - while (hp) { - grp = hp->ht_grp; - switch (grp->gr_type) { - case GT_HOST: - ai = grp->gr_ptr.gt_addrinfo; - for (; ai; ai = ai->ai_next) { - if (!sacmp(ai->ai_addr, saddr, NULL)) { - *hostsetp = - (hp->ht_flag | DP_HOSTSET); - if (numsecflavors != NULL) { - *numsecflavors = - grp->gr_numsecflavors; - *secflavorsp = - grp->gr_secflavors; - } - return (1); - } - } - break; - case GT_NET: - if (!sacmp(saddr, (struct sockaddr *) - &grp->gr_ptr.gt_net.nt_net, - (struct sockaddr *) - &grp->gr_ptr.gt_net.nt_mask)) { - *hostsetp = (hp->ht_flag | DP_HOSTSET); - if (numsecflavors != NULL) { - *numsecflavors = - grp->gr_numsecflavors; - *secflavorsp = - grp->gr_secflavors; - } - return (1); - } - break; - } - hp = hp->ht_next; - } - } - return (0); -} - -/* - * Scan tree for a host that matches the address. - */ -static int -scan_tree(struct dirlist *dp, struct sockaddr *saddr) -{ - int defset, hostset; - - if (dp) { - if (scan_tree(dp->dp_left, saddr)) - return (1); - if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL)) - return (1); - if (scan_tree(dp->dp_right, saddr)) - return (1); - } - return (0); -} - -/* - * Traverse the dirlist tree and free it up. - */ -static void -free_dir(struct dirlist *dp) -{ - - if (dp) { - free_dir(dp->dp_left); - free_dir(dp->dp_right); - free_host(dp->dp_hosts); - free(dp->dp_dirp); - free(dp); - } -} - -/* - * Parse a colon separated list of security flavors - */ -static int -parsesec(char *seclist, struct exportlist *ep) -{ - char *cp, savedc; - int flavor; - - ep->ex_numsecflavors = 0; - for (;;) { - cp = strchr(seclist, ':'); - if (cp) { - savedc = *cp; - *cp = '\0'; - } - - if (!strcmp(seclist, "sys")) - flavor = AUTH_SYS; - else if (!strcmp(seclist, "krb5")) - flavor = RPCSEC_GSS_KRB5; - else if (!strcmp(seclist, "krb5i")) - flavor = RPCSEC_GSS_KRB5I; - else if (!strcmp(seclist, "krb5p")) - flavor = RPCSEC_GSS_KRB5P; - else { - if (cp) - *cp = savedc; - syslog(LOG_ERR, "bad sec flavor: %s", seclist); - return (1); - } - if (ep->ex_numsecflavors == MAXSECFLAVORS) { - if (cp) - *cp = savedc; - syslog(LOG_ERR, "too many sec flavors: %s", seclist); - return (1); - } - ep->ex_secflavors[ep->ex_numsecflavors] = flavor; - ep->ex_numsecflavors++; - if (cp) { - *cp = savedc; - seclist = cp + 1; - } else { - break; - } - } - return (0); -} - -/* - * Parse the option string and update fields. - * Option arguments may either be -