From 03914b0bb26916a22b74d01c561dc74743e27d29 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Tue, 26 May 2009 15:19:04 +0000 Subject: [PATCH] Add two new utilities and two new daemons to /usr/src/usr.sbin that are specifically used by the experimental nfsv4 subsystem. nfscbd - The NFSv4 client callback daemon. nfsuserd - The NFSv4 daemon that maps between user and group name and their corresponding uid/gid numbers. nfsdumpstate - A utility that dumps out the NFSv4 Open/Lock state. nfsrevoke - Administratively revokes an NFSv4 client, releasing all NFSv4 Open/Lock state it holds on the server. Approved by: kib (mentor) --- usr.sbin/Makefile | 4 + usr.sbin/nfscbd/Makefile | 6 + usr.sbin/nfscbd/nfscbd.8 | 87 ++++ usr.sbin/nfscbd/nfscbd.c | 380 +++++++++++++++ usr.sbin/nfsdumpstate/Makefile | 6 + usr.sbin/nfsdumpstate/nfsdumpstate.8 | 71 +++ usr.sbin/nfsdumpstate/nfsdumpstate.c | 280 +++++++++++ usr.sbin/nfsrevoke/Makefile | 6 + usr.sbin/nfsrevoke/nfsrevoke.8 | 64 +++ usr.sbin/nfsrevoke/nfsrevoke.c | 124 +++++ usr.sbin/nfsuserd/Makefile | 6 + usr.sbin/nfsuserd/nfsuserd.8 | 114 +++++ usr.sbin/nfsuserd/nfsuserd.c | 665 +++++++++++++++++++++++++++ 13 files changed, 1813 insertions(+) create mode 100644 usr.sbin/nfscbd/Makefile create mode 100644 usr.sbin/nfscbd/nfscbd.8 create mode 100644 usr.sbin/nfscbd/nfscbd.c create mode 100644 usr.sbin/nfsdumpstate/Makefile create mode 100644 usr.sbin/nfsdumpstate/nfsdumpstate.8 create mode 100644 usr.sbin/nfsdumpstate/nfsdumpstate.c create mode 100644 usr.sbin/nfsrevoke/Makefile create mode 100644 usr.sbin/nfsrevoke/nfsrevoke.8 create mode 100644 usr.sbin/nfsrevoke/nfsrevoke.c create mode 100644 usr.sbin/nfsuserd/Makefile create mode 100644 usr.sbin/nfsuserd/nfsuserd.8 create mode 100644 usr.sbin/nfsuserd/nfsuserd.c diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index eb13bf1ff3b3..1a3d9f6232f3 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -110,7 +110,11 @@ SUBDIR= ${_ac} \ ${_ndiscvt} \ ${_ndp} \ newsyslog \ + nfscbd \ nfsd \ + nfsdumpstate \ + nfsrevoke \ + nfsuserd \ ${_ngctl} \ ${_nghook} \ nologin \ diff --git a/usr.sbin/nfscbd/Makefile b/usr.sbin/nfscbd/Makefile new file mode 100644 index 000000000000..1e11e3a81711 --- /dev/null +++ b/usr.sbin/nfscbd/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= nfscbd +MAN= nfscbd.8 + +.include diff --git a/usr.sbin/nfscbd/nfscbd.8 b/usr.sbin/nfscbd/nfscbd.8 new file mode 100644 index 000000000000..802dff39465e --- /dev/null +++ b/usr.sbin/nfscbd/nfscbd.8 @@ -0,0 +1,87 @@ +.\" Copyright (c) 2009 Rick Macklem, University of Guelph +.\" 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. +.\" +.\" 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$ +.\" +.Dd April 25, 2009 +.Dt NFSCBD 8 +.Os +.Sh NAME +.Nm nfscbd +.Tn NFSv4 +client side callback daemon +.Sh SYNOPSIS +.Nm nfscbd +.Op Fl p Ar port_number +.Op Fl P Ar client_principal +.Sh DESCRIPTION +.Nm +runs on a client using +.Tn NFSv4 +to handle callback requests from the NFSv4 server. +If no +.Nm +is running, NFSv4 mounts will still work, but the server will never issue +Open Delegations to the client. +.Pp +One callback server and one master server +are always started. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl p Ar port_number +Specifies what port# the callback server should use. +.It Fl P Ar client_principal +Specifies the host based principal name to be used as the target for +callbacks over RPCSEC_GSS. For KerberosV, it must be in the client's +default keytab file. +This client_principal should be the same one specified by the +.Cm gssname +argument being used by nfsv4 mounts. +If you do not specify this argument, callbacks will still work over AUTH_SYS, +which is what many extant servers use even for RPCSEC_GSS mounts, as of 2009. +.El +.Pp +For example, +.Dq Li "nfscbd -p 7654 -P root" +starts the daemon to handle callbacks on port# 7654 and is using the host based +principal root@. as the callback target. +.Pp +.Nm +listens for service requests at the port +defined by NFSV4_CBPORT in /usr/include/fs/nfs/nfs.h, unless +.Fl p +has been specified. +For more information on what callbacks and Open Delegations do, see +.%T "Network File System (NFS) Version 4 Protocol" , +RFC3530 . +.Pp +The +.Nm +utility exits 0 on success or >0 if an error occurred. +.Sh SEE ALSO +.Xr nfsv4 4 , +.Xr mount_nfs 8 +.Sh HISTORY +First introduced with the experimental nfs client for NFSv4 support in 2009. diff --git a/usr.sbin/nfscbd/nfscbd.c b/usr.sbin/nfscbd/nfscbd.c new file mode 100644 index 000000000000..3920360d2a1f --- /dev/null +++ b/usr.sbin/nfscbd/nfscbd.c @@ -0,0 +1,380 @@ +/*- + * Copyright (c) 2009 Rick Macklem, University of Guelph + * 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. + * + * 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. + * + */ + +#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 + +/* Global defs */ +#ifdef DEBUG +#define syslog(e, s) fprintf(stderr,(s)) +int debug = 1; +#else +int debug = 0; +#endif + +pid_t children; + +void nonfs(int); +void reapchild(int); +void usage(void); +void cleanup(int); +void child_cleanup(int); +void nfscbd_exit(int); +void killchildren(void); + +/* + * Nfs callback server daemon. + * + * 1 - do file descriptor and signal cleanup + * 2 - fork the nfscbd(s) + * 4 - create callback server socket(s) + * 5 - set up server socket for rpc + * + * For connectionless protocols, just pass the socket into the kernel via. + * nfssvc(). + * For connection based sockets, loop doing accepts. When you get a new + * socket from accept, pass the msgsock into the kernel via. nfssvc(). + */ +int +main(int argc, char *argv[], char **envp) +{ + struct group *grp; + struct nfscbd_args nfscbdargs; + struct nfsd_nfscbd_args nfscbdargs2; + struct passwd *pwd; + struct ucred *cr; + struct sockaddr_in inetaddr, inetpeer; + struct timeval ktv; + fd_set ready, sockbits; + int ch, connect_type_cnt, i, len, maxsock, msgsock, error; + int nfssvc_flag, on, sock, tcpsock, ret, mustfreeai = 0; + char *cp, **cpp, princname[128]; + char myname[MAXHOSTNAMELEN], *myfqdnname = NULL; + struct addrinfo *aip, hints; + pid_t pid; + sigset_t signew; + short myport = NFSV4_CBPORT; + + if (modfind("nfscl") < 0) { + /* Not present in kernel, try loading it */ + if (kldload("nfscl") < 0 || + modfind("nfscl") < 0) + errx(1, "nfscl is not available"); + } + /* + * First, get our fully qualified host name, if possible. + */ + if (gethostname(myname, MAXHOSTNAMELEN) >= 0) { + cp = strchr(myname, '.'); + if (cp != NULL && *(cp + 1) != '\0') { + cp = myname; + } else { + /* + * No domain on myname, so try looking it up. + */ + cp = NULL; + memset((void *)&hints, 0, sizeof (hints)); + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(myname, NULL, &hints, &aip); + if (error == 0) { + if (aip->ai_canonname != NULL && + (cp = strchr(aip->ai_canonname, '.')) != NULL + && *(cp + 1) != '\0') { + cp = aip->ai_canonname; + mustfreeai = 1; + } else { + freeaddrinfo(aip); + } + } + } + if (cp == NULL) + warnx("Can't get fully qualified host name"); + myfqdnname = cp; + } + + princname[0] = '\0'; +#define GETOPT "p:P:" +#define USAGE "[ -p port_num ] [ -P client_principal ]" + while ((ch = getopt(argc, argv, GETOPT)) != -1) + switch (ch) { + case 'p': + myport = atoi(optarg); + if (myport < 1) { + warnx("port# non-positive, reset to %d", + NFSV4_CBPORT); + myport = NFSV4_CBPORT; + } + break; + case 'P': + cp = optarg; + if (cp != NULL && strlen(cp) > 0 && + strlen(cp) < sizeof (princname)) { + if (strchr(cp, '@') == NULL && + myfqdnname != NULL) + snprintf(princname, sizeof (princname), + "%s@%s", cp, myfqdnname); + else + strlcpy(princname, cp, + sizeof (princname)); + } else { + warnx("client princ invalid. ignored\n"); + } + break; + default: + case '?': + usage(); + }; + argv += optind; + argc -= optind; + + if (argc > 0) + usage(); + + if (mustfreeai) + freeaddrinfo(aip); + nfscbdargs2.principal = (const char *)princname; + if (debug == 0) { + daemon(0, 0); + (void)signal(SIGTERM, SIG_IGN); + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + } + (void)signal(SIGSYS, nonfs); + (void)signal(SIGCHLD, reapchild); + + openlog("nfscbd:", LOG_PID, LOG_DAEMON); + + pid = fork(); + if (pid < 0) { + syslog(LOG_ERR, "fork: %m"); + nfscbd_exit(1); + } else if (pid > 0) { + children = pid; + } else { + (void)signal(SIGUSR1, child_cleanup); + setproctitle("server"); + nfssvc_flag = NFSSVC_NFSCBD; + if (nfssvc(nfssvc_flag, &nfscbdargs2) < 0) { + syslog(LOG_ERR, "nfssvc: %m"); + nfscbd_exit(1); + } + exit(0); + } + (void)signal(SIGUSR1, cleanup); + + if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + syslog(LOG_ERR, "can't create udp socket"); + nfscbd_exit(1); + } + memset(&inetaddr, 0, sizeof inetaddr); + inetaddr.sin_family = AF_INET; + inetaddr.sin_addr.s_addr = INADDR_ANY; + inetaddr.sin_port = htons(myport); + inetaddr.sin_len = sizeof(inetaddr); + ret = bind(sock, (struct sockaddr *)&inetaddr, sizeof(inetaddr)); + /* If bind() fails, this is a restart, so just skip UDP. */ + if (ret == 0) { + len = sizeof(inetaddr); + if (getsockname(sock, (struct sockaddr *)&inetaddr, &len) < 0){ + syslog(LOG_ERR, "can't get bound addr"); + nfscbd_exit(1); + } + nfscbdargs.port = ntohs(inetaddr.sin_port); + if (nfscbdargs.port != myport) { + syslog(LOG_ERR, "BAD PORT#"); + nfscbd_exit(1); + } + nfscbdargs.sock = sock; + nfscbdargs.name = NULL; + nfscbdargs.namelen = 0; + if (nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs) < 0) { + syslog(LOG_ERR, "can't Add UDP socket"); + nfscbd_exit(1); + } + } + (void)close(sock); + + /* Now set up the master server socket waiting for tcp connections. */ + on = 1; + FD_ZERO(&sockbits); + connect_type_cnt = 0; + if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + syslog(LOG_ERR, "can't create tcp socket"); + nfscbd_exit(1); + } + if (setsockopt(tcpsock, + SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m"); + /* sin_port is already set */ + inetaddr.sin_family = AF_INET; + inetaddr.sin_addr.s_addr = INADDR_ANY; + inetaddr.sin_port = htons(myport); + inetaddr.sin_len = sizeof(inetaddr); + if (bind(tcpsock, + (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) { + syslog(LOG_ERR, "can't bind tcp addr"); + nfscbd_exit(1); + } + if (listen(tcpsock, 5) < 0) { + syslog(LOG_ERR, "listen failed"); + nfscbd_exit(1); + } + FD_SET(tcpsock, &sockbits); + maxsock = tcpsock; + connect_type_cnt++; + + setproctitle("master"); + + /* + * Loop forever accepting connections and passing the sockets + * into the kernel for the mounts. + */ + for (;;) { + ready = sockbits; + if (connect_type_cnt > 1) { + if (select(maxsock + 1, + &ready, NULL, NULL, NULL) < 1) { + syslog(LOG_ERR, "select failed: %m"); + nfscbd_exit(1); + } + } + if (FD_ISSET(tcpsock, &ready)) { + len = sizeof(inetpeer); + if ((msgsock = accept(tcpsock, + (struct sockaddr *)&inetpeer, &len)) < 0) { + syslog(LOG_ERR, "accept failed: %m"); + nfscbd_exit(1); + } + memset(inetpeer.sin_zero, 0, + sizeof (inetpeer.sin_zero)); + if (setsockopt(msgsock, SOL_SOCKET, + SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0) + syslog(LOG_ERR, + "setsockopt SO_KEEPALIVE: %m"); + nfscbdargs.sock = msgsock; + nfscbdargs.name = (caddr_t)&inetpeer; + nfscbdargs.namelen = sizeof(inetpeer); + nfssvc(NFSSVC_CBADDSOCK, &nfscbdargs); + (void)close(msgsock); + } + } +} + +void +usage(void) +{ + + errx(1, "usage: nfscbd %s", USAGE); +} + +void +nonfs(int signo) +{ + syslog(LOG_ERR, "missing system call: NFS not available"); +} + +void +reapchild(int signo) +{ + pid_t pid; + int i; + + while ((pid = wait3(NULL, WNOHANG, NULL)) > 0) { + if (pid == children) + children = -1; + } +} + +void +killchildren(void) +{ + int i; + + if (children > 0) + kill(children, SIGKILL); +} + +/* + * Cleanup master after SIGUSR1. + */ +void +cleanup(int signo) +{ + nfscbd_exit(0); +} + +/* + * Cleanup child after SIGUSR1. + */ +void +child_cleanup(int signo) +{ + exit(0); +} + +void +nfscbd_exit(int status) +{ + killchildren(); + exit(status); +} diff --git a/usr.sbin/nfsdumpstate/Makefile b/usr.sbin/nfsdumpstate/Makefile new file mode 100644 index 000000000000..938fb072ed9e --- /dev/null +++ b/usr.sbin/nfsdumpstate/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= nfsdumpstate +MAN= nfsdumpstate.8 + +.include diff --git a/usr.sbin/nfsdumpstate/nfsdumpstate.8 b/usr.sbin/nfsdumpstate/nfsdumpstate.8 new file mode 100644 index 000000000000..ca48c020cd9a --- /dev/null +++ b/usr.sbin/nfsdumpstate/nfsdumpstate.8 @@ -0,0 +1,71 @@ +.\" Copyright (c) 2009 Rick Macklem, University of Guelph +.\" 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. +.\" +.\" 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$ +.\" +.Dd April 25, 2009 +.Dt NFSDUMPSTATE 8 +.Os +.Sh NAME +.Nm nfsdumpstate +.Nd display +.Tn NFSv4 +open/lock state +.Sh SYNOPSIS +.Nm nfsdumpstate +.Op Fl o +.Op Fl l Ar filename +.Sh DESCRIPTION +.Nm +displays open/lock state for the +.Tn NFSv4 +client and server in the experimental nfs subsystem. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl o +Displays a summary of Clients for NFSv4. Each line lists a Client with +the ClientID being the last field of the line. +.sp +.nf +The following are the client flag values displayed: +NC - Needs Confirmation +CB - Callbacks are enabled +GSS - Using RPCSEC_GSS +REV - Administratively Revoked, via nfsrevoke(8) +.fi +.sp +.It Fl l Ar filename +Displays a list of all NFSv4 Opens and Locks on the file specified by +the +.Ar filename . +The ClientID is the last field of each line. +.El +.Sh SEE ALSO +.Xr nfsv4 4 , +.Xr nfsrevoke 8 +.Sh HISTORY +The +.Nm +utility was introduced with the NFSv4 experimental subsystem in 2009. diff --git a/usr.sbin/nfsdumpstate/nfsdumpstate.c b/usr.sbin/nfsdumpstate/nfsdumpstate.c new file mode 100644 index 000000000000..54841dcba94e --- /dev/null +++ b/usr.sbin/nfsdumpstate/nfsdumpstate.c @@ -0,0 +1,280 @@ +/*- + * Copyright (c) 2009 Rick Macklem, University of Guelph + * 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. + * + * 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. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DUMPSIZE 10000 + +static void dump_lockstate(char *); +static void dump_openstate(void); +static void usage(void); +static char *open_flags(uint32_t); +static char *deleg_flags(uint32_t); +static char *lock_flags(uint32_t); +static char *client_flags(uint32_t); + +static struct nfsd_dumpclients dp[DUMPSIZE]; +static struct nfsd_dumplocks lp[DUMPSIZE]; +static char flag_string[20]; + +int +main(int argc, char **argv) +{ + int ch, openstate; + char *lockfile; + + if (modfind("nfsd") < 0) + errx(1, "nfsd not loaded - self terminating"); + openstate = 0; + lockfile = NULL; + while ((ch = getopt(argc, argv, "ol")) != -1) + switch (ch) { + case 'o': + openstate = 1; + break; + case 'l': + lockfile = optarg; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (openstate == 0 && lockfile == NULL) + openstate = 1; + else if (openstate != 0 && lockfile != NULL) + errx(1, "-o and -l cannot both be specified"); + + /* + * For -o, dump all open/lock state. + * For -l, dump lock state for that file. + */ + if (openstate != 0) + dump_openstate(); + else + dump_lockstate(lockfile); + exit(0); +} + +static void +usage(void) +{ + + errx(1, "usage: nfsdumpstate [-o] [-l]"); +} + +/* + * Dump all open/lock state. + */ +static void +dump_openstate(void) +{ + struct nfsd_dumplist dumplist; + int cnt, i; + + dumplist.ndl_size = DUMPSIZE; + dumplist.ndl_list = (void *)dp; + if (nfssvc(NFSSVC_DUMPCLIENTS, &dumplist) < 0) + errx(1, "Can't perform dump clients syscall"); + + printf("%-13s %9.9s %9.9s %9.9s %9.9s %9.9s %9.9s %-15s %s\n", + "Flags", "OpenOwner", "Open", "LockOwner", + "Lock", "Deleg", "OldDeleg", "Clientaddr", "ClientID"); + /* + * Loop through results, printing them out. + */ + cnt = 0; + while (dp[cnt].ndcl_clid.nclid_idlen > 0 && cnt < DUMPSIZE) { + printf("%-13s ", client_flags(dp[cnt].ndcl_flags)); + printf("%9d %9d %9d %9d %9d %9d ", + dp[cnt].ndcl_nopenowners, + dp[cnt].ndcl_nopens, + dp[cnt].ndcl_nlockowners, + dp[cnt].ndcl_nlocks, + dp[cnt].ndcl_ndelegs, + dp[cnt].ndcl_nolddelegs); + if (dp[cnt].ndcl_addrfam == AF_INET) + printf("%-15s ", + inet_ntoa(dp[cnt].ndcl_cbaddr.sin_addr)); + for (i = 0; i < dp[cnt].ndcl_clid.nclid_idlen; i++) + printf("%02x", dp[cnt].ndcl_clid.nclid_id[i]); + printf("\n"); + cnt++; + } +} + +/* + * Dump the lock state for a file. + */ +static void +dump_lockstate(char *fname) +{ + struct nfsd_dumplocklist dumplocklist; + int cnt, i; + + dumplocklist.ndllck_size = DUMPSIZE; + dumplocklist.ndllck_list = (void *)lp; + dumplocklist.ndllck_fname = fname; + if (nfssvc(NFSSVC_DUMPLOCKS, &dumplocklist) < 0) + errx(1, "Can't dump locks for %s\n", fname); + + printf("%-11s %-36s %-15s %s\n", + "Open/Lock", + " Stateid or Lock Range", + "Clientaddr", + "Owner and ClientID"); + /* + * Loop through results, printing them out. + */ + cnt = 0; + while (lp[cnt].ndlck_clid.nclid_idlen > 0 && cnt < DUMPSIZE) { + if (lp[cnt].ndlck_flags & NFSLCK_OPEN) + printf("%-11s %9d %08x %08x %08x ", + open_flags(lp[cnt].ndlck_flags), + lp[cnt].ndlck_stateid.seqid, + lp[cnt].ndlck_stateid.other[0], + lp[cnt].ndlck_stateid.other[1], + lp[cnt].ndlck_stateid.other[2]); + else if (lp[cnt].ndlck_flags & (NFSLCK_DELEGREAD | + NFSLCK_DELEGWRITE)) + printf("%-11s %9d %08x %08x %08x ", + deleg_flags(lp[cnt].ndlck_flags), + lp[cnt].ndlck_stateid.seqid, + lp[cnt].ndlck_stateid.other[0], + lp[cnt].ndlck_stateid.other[1], + lp[cnt].ndlck_stateid.other[2]); + else + printf("%-11s %17lld %17lld ", + lock_flags(lp[cnt].ndlck_flags), + lp[cnt].ndlck_first, + lp[cnt].ndlck_end); + if (lp[cnt].ndlck_addrfam == AF_INET) + printf("%-15s ", + inet_ntoa(lp[cnt].ndlck_cbaddr.sin_addr)); + else + printf("%-15s ", " "); + for (i = 0; i < lp[cnt].ndlck_owner.nclid_idlen; i++) + printf("%02x", lp[cnt].ndlck_owner.nclid_id[i]); + printf(" "); + for (i = 0; i < lp[cnt].ndlck_clid.nclid_idlen; i++) + printf("%02x", lp[cnt].ndlck_clid.nclid_id[i]); + printf("\n"); + cnt++; + } +} + +/* + * Parse the Open/Lock flag bits and create a string to be printed. + */ +static char * +open_flags(uint32_t flags) +{ + int i, j; + + strlcpy(flag_string, "Open ", sizeof (flag_string)); + i = 5; + if (flags & NFSLCK_READACCESS) + flag_string[i++] = 'R'; + if (flags & NFSLCK_WRITEACCESS) + flag_string[i++] = 'W'; + flag_string[i++] = ' '; + flag_string[i++] = 'D'; + flag_string[i] = 'N'; + j = i; + if (flags & NFSLCK_READDENY) + flag_string[i++] = 'R'; + if (flags & NFSLCK_WRITEDENY) + flag_string[i++] = 'W'; + if (i == j) + i++; + flag_string[i] = '\0'; + return (flag_string); +} + +static char * +deleg_flags(uint32_t flags) +{ + + if (flags & NFSLCK_DELEGREAD) + strlcpy(flag_string, "Deleg R", sizeof (flag_string)); + else + strlcpy(flag_string, "Deleg W", sizeof (flag_string)); + return (flag_string); +} + +static char * +lock_flags(uint32_t flags) +{ + + if (flags & NFSLCK_READ) + strlcpy(flag_string, "Lock R", sizeof (flag_string)); + else + strlcpy(flag_string, "Lock W", sizeof (flag_string)); + return (flag_string); +} + +static char * +client_flags(uint32_t flags) +{ + + flag_string[0] = '\0'; + if (flags & LCL_NEEDSCONFIRM) + strlcat(flag_string, "NC ", sizeof (flag_string)); + if (flags & LCL_CALLBACKSON) + strlcat(flag_string, "CB ", sizeof (flag_string)); + if (flags & LCL_GSS) + strlcat(flag_string, "GSS ", sizeof (flag_string)); + if (flags & LCL_ADMINREVOKED) + strlcat(flag_string, "REV", sizeof (flag_string)); + return (flag_string); +} diff --git a/usr.sbin/nfsrevoke/Makefile b/usr.sbin/nfsrevoke/Makefile new file mode 100644 index 000000000000..ab78aa3300c3 --- /dev/null +++ b/usr.sbin/nfsrevoke/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= nfsrevoke +MAN= nfsrevoke.8 + +.include diff --git a/usr.sbin/nfsrevoke/nfsrevoke.8 b/usr.sbin/nfsrevoke/nfsrevoke.8 new file mode 100644 index 000000000000..00a8915e361b --- /dev/null +++ b/usr.sbin/nfsrevoke/nfsrevoke.8 @@ -0,0 +1,64 @@ +.\" Copyright (c) 2009 Rick Macklem, University of Guelph +.\" 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. +.\" +.\" 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$ +.\" +.Dd April 25, 2009 +.Dt NFSREVOKE 8 +.Os +.Sh NAME +.Nm nfsrevoke +.Nd revoke +.Tn NFS +V4 client +.Sh SYNOPSIS +.Nm nfsrevoke +.Ar ClientId +.Sh DESCRIPTION +.Nm +This command is used by a system administrator to revoke a client's access +to the NFS Version 4 server. All Open/Lock state held by the client will +be released. +After revocation, the client will no longer be able to use state on the server +until it does a fresh SetClientID/SetClientIDConfirm operations sequence. +THIS SHOULD BE DONE AS A LAST RESORT ONLY, when clients are holding state +that must be released on the server. +.Pp +The +.Ar ClientId +argument is a hexadecimal string, which is the last field +of the +.Xr nfsdumpstate 8 +command's +.Fl o +and +.Fl l +options output. +.Sh SEE ALSO +.Xr nfsv4 4 , +.Xr nfsdumpstate 8 +.Sh HISTORY +The +.Nm +command was introduced as a part of the experimental nfs server subsystem. diff --git a/usr.sbin/nfsrevoke/nfsrevoke.c b/usr.sbin/nfsrevoke/nfsrevoke.c new file mode 100644 index 000000000000..f6ba813f0ea4 --- /dev/null +++ b/usr.sbin/nfsrevoke/nfsrevoke.c @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2009 Rick Macklem, University of Guelph + * 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. + * + * 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. + * + */ + +#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 + +void usage(void); +extern int errno; + +int +main(int argc, char **argv) +{ + char *cp; + u_char val; + int cnt, even; + struct nfsd_clid revoke; + + if (modfind("nfsd") < 0) + errx(1, "nfsd not loaded - self terminating"); + if (argc != 2) + usage(); + cnt = 0; + cp = argv[1]; + if (strlen(cp) % 2) + even = 0; + else + even = 1; + val = 0; + while (*cp) { + if (*cp >= '0' & *cp <= '9') + val += (u_char)(*cp - '0'); + else if (*cp >= 'A' && *cp <= 'F') + val += ((u_char)(*cp - 'A')) + 0xa; + else if (*cp >= 'a' && *cp <= 'f') + val += ((u_char)(*cp - 'a')) + 0xa; + else + errx(1, "Non hexadecimal digit in %s", argv[1]); + if (even) { + val <<= 4; + even = 0; + } else { + revoke.nclid_id[cnt++] = val; + if (cnt > NFSV4_OPAQUELIMIT) + errx(1, "Clientid %s, loo long", argv[1]); + val = 0; + even = 1; + } + cp++; + } + + /* + * Do the revocation system call. + */ + revoke.nclid_idlen = cnt; +#ifdef DEBUG + printf("Idlen=%d\n", revoke.nclid_idlen); + for (cnt = 0; cnt < revoke.nclid_idlen; cnt++) + printf("%02x", revoke.nclid_id[cnt]); + printf("\n"); +#else + if (nfssvc(NFSSVC_ADMINREVOKE, &revoke) < 0) + err(1, "Admin revoke failed"); +#endif +} + +void +usage(void) +{ + + errx(1, "Usage: nfsrevoke "); +} diff --git a/usr.sbin/nfsuserd/Makefile b/usr.sbin/nfsuserd/Makefile new file mode 100644 index 000000000000..377b5af7fd59 --- /dev/null +++ b/usr.sbin/nfsuserd/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +PROG= nfsuserd +MAN= nfsuserd.8 + +.include diff --git a/usr.sbin/nfsuserd/nfsuserd.8 b/usr.sbin/nfsuserd/nfsuserd.8 new file mode 100644 index 000000000000..b98c34afd0db --- /dev/null +++ b/usr.sbin/nfsuserd/nfsuserd.8 @@ -0,0 +1,114 @@ +.\" Copyright (c) 2009 Rick Macklem, University of Guelph +.\" 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. +.\" +.\" 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$ +.\" +.Dd April 25, 2009 +.Dt NFSUSERD 8 +.Os +.Sh NAME +.Nm nfsuserd +.Nd load user and group information into the kernel for +.Tn NFSv4 +services +.Sh SYNOPSIS +.Nm nfsuserd +.Op Fl domain Ar domain_name +.Op Fl usertimeout Ar minutes +.Op Fl usermax Ar max_cache_size +.Op Fl verbose +.Op Fl force +.Op Ar num_servers +.Sh DESCRIPTION +.Nm +loads user and group information into the kernel for NFSv4. +It must be running for NFSv4 to function correctly, either client or server. +.Pp +Upon startup, it loads the machines DNS domain name, plus timeout and +cache size limit into the kernel. It then preloads the cache with group +and user information, up to the cache size limit and forks off N children +(default 4), that service requests from the kernel for cache misses. The +master server is there for the sole purpose of killing off the slaves. +To stop the nfsuserd, send a SIGUSR1 to the master server. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl domain Ar domain_name +This option allows you to override the default DNS domain name, which +is acquired by taking either the suffix on the machine's hostname or, +if that name is not a fully qualified host name, the cannonical name as +reported by +.Xr getaddrinfo 3 . +.It Fl usertimeout Ar minutes +Overrides the default timeout for cache entries, in minutes. If the +timeout is specified as 0, cache entries never time out. The longer the +time out, the better the performance, but the longer it takes for replaced +entries to be seen. If your user/group database management system almost +never re-uses the same names or id numbers, a large timeout is recommended. +The default is 1 minute. +.It Fl usermax Ar max_cache_size +Overrides the default upper bound on the cache size. The larger the cache, +the more kernel memory is used, but the better the performance. If your +system can afford the memory use, make this the sum of the number of +entries in your group and password databases. +The default is 200 entries. +.It Fl verbose +When set, the server logs a bunch of information to syslog. +.It Fl force +This flag option must be set to restart the daemon after it has gone away +abnormally and refuses to start, because it thinks nfsuserd is already +running. +.It Ar num_servers +Specifies how many servers to create (max 20). +The default of 4 may be sufficient. You should run enough servers, so that +.Xr ps 1 +shows almost no running time for one or two of the slaves after the system +has been running for a long period. Running too few will have a major +performance impact, whereas running too many will only tie up some resources, +such as a process table entry and swap space. +.El +.Sh SEE ALSO +.Xr getpwent 3 , +.Xr getgrent 3 , +.Xr nfsv4 4 , +.Xr group 5 , +.Xr passwd 5 , +.Xr nfsd 8 . +.Sh HISTORY +The +.Nm +utility was introduced with the NFSv4 experimental subsystem in 2009. +.Sh BUGS +The +.Nm +use +.Xr getgrent 3 +and +.Xr getpwent 3 +library calls to resolve requests and will hang if the servers handling +those requests fail and the library functions don't return. See +.Xr group 5 +and +.Xr passwd 5 +for more information on how the databases are accessed. diff --git a/usr.sbin/nfsuserd/nfsuserd.c b/usr.sbin/nfsuserd/nfsuserd.c new file mode 100644 index 000000000000..7928a75261f1 --- /dev/null +++ b/usr.sbin/nfsuserd/nfsuserd.c @@ -0,0 +1,665 @@ +/*- + * Copyright (c) 2009 Rick Macklem, University of Guelph + * 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. + * + * 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. + * + */ + +#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 + +/* + * This program loads the password and group databases into the kernel + * for NFS V4. + */ + +void cleanup_term(int); +void usage(void); +void nfsuserdsrv(struct svc_req *, SVCXPRT *); +bool_t xdr_getid(XDR *, caddr_t); +bool_t xdr_getname(XDR *, caddr_t); +bool_t xdr_retval(XDR *, caddr_t); + +#define MAXNAME 1024 +#define MAXNFSUSERD 20 +#define DEFNFSUSERD 4 +#define DEFUSERMAX 200 +#define DEFUSERTIMEOUT (1 * 60) +struct info { + long id; + long retval; + char name[MAXNAME + 1]; +}; + +u_char *dnsname = "default.domain"; +u_char *defaultuser = "nobody"; +uid_t defaultuid = (uid_t)32767; +u_char *defaultgroup = "nogroup"; +gid_t defaultgid = (gid_t)32767; +int verbose = 0, im_a_slave = 0, nfsuserdcnt = -1, forcestart = 0; +int defusertimeout = DEFUSERTIMEOUT; +pid_t slaves[MAXNFSUSERD]; + +int +main(int argc, char *argv[], char *envp[]) +{ + int i; + int error, len, mustfreeai = 0; + struct nfsd_idargs nid; + struct passwd *pwd; + struct group *grp; + int sock, one = 1; + SVCXPRT *udptransp, *tcptransp; + struct passwd *pw; + u_short portnum; + sigset_t signew; + char hostname[MAXHOSTNAMELEN + 1], *cp, **aliases; + struct addrinfo *aip, hints; + + if (modfind("nfscommon") < 0) { + /* Not present in kernel, try loading it */ + if (kldload("nfscommon") < 0 || + modfind("nfscommon") < 0) + errx(1, "Experimental nfs subsystem is not available"); + } + + /* + * First, figure out what our domain name and Kerberos Realm + * seem to be. Command line args may override these later. + */ + if (gethostname(hostname, MAXHOSTNAMELEN) == 0) { + if ((cp = strchr(hostname, '.')) != NULL && + *(cp + 1) != '\0') { + dnsname = cp + 1; + } 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') { + dnsname = cp + 1; + mustfreeai = 1; + } else { + freeaddrinfo(aip); + } + } + } + } + nid.nid_usermax = DEFUSERMAX; + nid.nid_usertimeout = defusertimeout; + + argc--; + argv++; + while (argc >= 1) { + if (!strcmp(*argv, "-domain")) { + if (argc == 1) + usage(); + argc--; + argv++; + strncpy(hostname, *argv, MAXHOSTNAMELEN); + hostname[MAXHOSTNAMELEN] = '\0'; + dnsname = hostname; + } else if (!strcmp(*argv, "-verbose")) { + verbose = 1; + } else if (!strcmp(*argv, "-force")) { + forcestart = 1; + } else if (!strcmp(*argv, "-usermax")) { + if (argc == 1) + usage(); + argc--; + argv++; + i = atoi(*argv); + if (i < 10 || i > 100000) { + fprintf(stderr, + "usermax out of range 10<->100000\n", i); + usage(); + } + nid.nid_usermax = i; + } else if (!strcmp(*argv, "-usertimeout")) { + if (argc == 1) + usage(); + argc--; + argv++; + i = atoi(*argv); + if (i < 0 || i > 100000) { + fprintf(stderr, + "usertimeout out of range 0<->100000\n", + i); + usage(); + } + nid.nid_usertimeout = defusertimeout = i * 60; + } else if (nfsuserdcnt == -1) { + nfsuserdcnt = atoi(*argv); + if (nfsuserdcnt < 1) + usage(); + if (nfsuserdcnt > MAXNFSUSERD) { + warnx("nfsuserd count %d; reset to %d", + nfsuserdcnt, DEFNFSUSERD); + nfsuserdcnt = DEFNFSUSERD; + } + } else { + usage(); + } + argc--; + argv++; + } + if (nfsuserdcnt < 1) + nfsuserdcnt = DEFNFSUSERD; + + /* + * Strip off leading and trailing '.'s in domain name and map + * alphabetics to lower case. + */ + while (*dnsname == '.') + dnsname++; + if (*dnsname == '\0') + errx(1, "Domain name all '.'"); + len = strlen(dnsname); + cp = dnsname + len - 1; + while (*cp == '.') { + *cp = '\0'; + len--; + cp--; + } + for (i = 0; i < len; i++) { + if (!isascii(dnsname[i])) + errx(1, "Domain name has non-ascii char"); + if (isupper(dnsname[i])) + dnsname[i] = tolower(dnsname[i]); + } + + /* + * If the nfsuserd died off ungracefully, this is necessary to + * get them to start again. + */ + if (forcestart && nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0) + errx(1, "Can't do nfssvc() to delete the port"); + + if (verbose) + fprintf(stderr, + "nfsuserd: domain=%s usermax=%d usertimeout=%d\n", + dnsname, nid.nid_usermax, nid.nid_usertimeout); + + for (i = 0; i < nfsuserdcnt; i++) + slaves[i] = (pid_t)-1; + + /* + * Set up the service port to accept requests via UDP from + * localhost (127.0.0.1). + */ + if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) + err(1, "cannot create udp socket"); + + /* + * Not sure what this does, so I'll leave it here for now. + */ + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); + + if ((udptransp = svcudp_create(sock)) == NULL) + err(1, "Can't set up socket"); + + /* + * By not specifying a protocol, it is linked into the + * dispatch queue, but not registered with portmapper, + * which is just what I want. + */ + if (!svc_register(udptransp, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS, + nfsuserdsrv, 0)) + err(1, "Can't register nfsuserd"); + + /* + * Tell the kernel what my port# is. + */ + portnum = htons(udptransp->xp_port); +#ifdef DEBUG + printf("portnum=0x%x\n", portnum); +#else + if (nfssvc(NFSSVC_NFSUSERDPORT, (caddr_t)&portnum) < 0) { + if (errno == EPERM) { + fprintf(stderr, + "Can't start nfsuserd when already running"); + fprintf(stderr, + " If not running, use the -force option.\n"); + } else { + fprintf(stderr, "Can't do nfssvc() to add port\n"); + } + exit(1); + } +#endif + + pwd = getpwnam(defaultuser); + if (pwd) + nid.nid_uid = pwd->pw_uid; + else + nid.nid_uid = defaultuid; + grp = getgrnam(defaultgroup); + if (grp) + nid.nid_gid = grp->gr_gid; + else + nid.nid_gid = defaultgid; + nid.nid_name = dnsname; + nid.nid_namelen = strlen(nid.nid_name); + nid.nid_flag = NFSID_INITIALIZE; +#ifdef DEBUG + printf("Initialize uid=%d gid=%d dns=%s\n", nid.nid_uid, nid.nid_gid, + nid.nid_name); +#else + error = nfssvc(NFSSVC_IDNAME, &nid); + if (error) + errx(1, "Can't initialize nfs user/groups"); +#endif + + i = 0; + /* + * Loop around adding all groups. + */ + setgrent(); + while (i < nid.nid_usermax && (grp = getgrent())) { + nid.nid_gid = grp->gr_gid; + nid.nid_name = grp->gr_name; + nid.nid_namelen = strlen(grp->gr_name); + nid.nid_flag = NFSID_ADDGID; +#ifdef DEBUG + printf("add gid=%d name=%s\n", nid.nid_gid, nid.nid_name); +#else + error = nfssvc(NFSSVC_IDNAME, &nid); + if (error) + errx(1, "Can't add group %s", grp->gr_name); +#endif + i++; + } + + /* + * Loop around adding all users. + */ + setpwent(); + while (i < nid.nid_usermax && (pwd = getpwent())) { + nid.nid_uid = pwd->pw_uid; + nid.nid_name = pwd->pw_name; + nid.nid_namelen = strlen(pwd->pw_name); + nid.nid_flag = NFSID_ADDUID; +#ifdef DEBUG + printf("add uid=%d name=%s\n", nid.nid_uid, nid.nid_name); +#else + error = nfssvc(NFSSVC_IDNAME, &nid); + if (error) + errx(1, "Can't add user %s", pwd->pw_name); +#endif + i++; + } + + /* + * I should feel guilty for not calling this for all the above exit() + * upon error cases, but I don't. + */ + if (mustfreeai) + freeaddrinfo(aip); + +#ifdef DEBUG + exit(0); +#endif + /* + * Temporarily block SIGUSR1 and SIGCHLD, so slaves[] can't + * end up bogus. + */ + sigemptyset(&signew); + sigaddset(&signew, SIGUSR1); + sigaddset(&signew, SIGCHLD); + sigprocmask(SIG_BLOCK, &signew, NULL); + + daemon(0, 0); + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + (void)signal(SIGTERM, SIG_IGN); + (void)signal(SIGUSR1, cleanup_term); + (void)signal(SIGCHLD, cleanup_term); + + openlog("nfsuserd:", LOG_PID, LOG_DAEMON); + + /* + * Fork off the slave daemons that do the work. All the master + * does is kill them off and cleanup. + */ + for (i = 0; i < nfsuserdcnt; i++) { + slaves[i] = fork(); + if (slaves[i] == 0) { + im_a_slave = 1; + setproctitle("slave"); + sigemptyset(&signew); + sigaddset(&signew, SIGUSR1); + sigprocmask(SIG_UNBLOCK, &signew, NULL); + + /* + * and away we go. + */ + svc_run(); + syslog(LOG_ERR, "nfsuserd died: %m"); + exit(1); + } else if (slaves[i] < 0) { + syslog(LOG_ERR, "fork: %m"); + } + } + + /* + * Just wait for SIGUSR1 or a child to die and then... + * As the Governor of California would say, "Terminate them". + */ + setproctitle("master"); + sigemptyset(&signew); + while (1) + sigsuspend(&signew); +} + +/* + * The nfsuserd rpc service + */ +void +nfsuserdsrv(struct svc_req *rqstp, SVCXPRT *transp) +{ + int i; + char *cp; + struct passwd *pwd; + struct group *grp; + int error; + u_short sport; + struct info info; + struct nfsd_idargs nid; + u_int32_t saddr; + + /* + * Only handle requests from 127.0.0.1 on a reserved port number. + * (Since a reserved port # at localhost implies a client with + * local root, there won't be a security breach. This is about + * the only case I can think of where a reserved port # means + * something.) + */ + sport = ntohs(transp->xp_raddr.sin_port); + saddr = ntohl(transp->xp_raddr.sin_addr.s_addr); + if ((rqstp->rq_proc != NULLPROC && sport >= IPPORT_RESERVED) || + saddr != 0x7f000001) { + syslog(LOG_ERR, "req from ip=0x%x port=%d\n", saddr, sport); + svcerr_weakauth(transp); + return; + } + switch (rqstp->rq_proc) { + case NULLPROC: + if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCNFSUSERD_GETUID: + if (!svc_getargs(transp, (xdrproc_t)xdr_getid, + (caddr_t)&info)) { + svcerr_decode(transp); + return; + } + pwd = getpwuid((uid_t)info.id); + info.retval = 0; + if (pwd != NULL) { + nid.nid_usertimeout = defusertimeout; + nid.nid_uid = pwd->pw_uid; + nid.nid_name = pwd->pw_name; + } else { + nid.nid_usertimeout = 5; + nid.nid_uid = (uid_t)info.id; + nid.nid_name = defaultuser; + } + nid.nid_namelen = strlen(nid.nid_name); + nid.nid_flag = NFSID_ADDUID; + error = nfssvc(NFSSVC_IDNAME, &nid); + if (error) { + info.retval = error; + syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name); + } else if (verbose) { + syslog(LOG_ERR,"Added uid=%d name=%s\n", + nid.nid_uid, nid.nid_name); + } + if (!svc_sendreply(transp, (xdrproc_t)xdr_retval, + (caddr_t)&info)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCNFSUSERD_GETGID: + if (!svc_getargs(transp, (xdrproc_t)xdr_getid, + (caddr_t)&info)) { + svcerr_decode(transp); + return; + } + grp = getgrgid((gid_t)info.id); + info.retval = 0; + if (grp != NULL) { + nid.nid_usertimeout = defusertimeout; + nid.nid_gid = grp->gr_gid; + nid.nid_name = grp->gr_name; + } else { + nid.nid_usertimeout = 5; + nid.nid_gid = (gid_t)info.id; + nid.nid_name = defaultgroup; + } + nid.nid_namelen = strlen(nid.nid_name); + nid.nid_flag = NFSID_ADDGID; + error = nfssvc(NFSSVC_IDNAME, &nid); + if (error) { + info.retval = error; + syslog(LOG_ERR, "Can't add group %s\n", + grp->gr_name); + } else if (verbose) { + syslog(LOG_ERR,"Added gid=%d name=%s\n", + nid.nid_gid, nid.nid_name); + } + if (!svc_sendreply(transp, (xdrproc_t)xdr_retval, + (caddr_t)&info)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCNFSUSERD_GETUSER: + if (!svc_getargs(transp, (xdrproc_t)xdr_getname, + (caddr_t)&info)) { + svcerr_decode(transp); + return; + } + pwd = getpwnam(info.name); + info.retval = 0; + if (pwd != NULL) { + nid.nid_usertimeout = defusertimeout; + nid.nid_uid = pwd->pw_uid; + nid.nid_name = pwd->pw_name; + } else { + nid.nid_usertimeout = 5; + nid.nid_uid = defaultuid; + nid.nid_name = info.name; + } + nid.nid_namelen = strlen(nid.nid_name); + nid.nid_flag = NFSID_ADDUSERNAME; + error = nfssvc(NFSSVC_IDNAME, &nid); + if (error) { + info.retval = error; + syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name); + } else if (verbose) { + syslog(LOG_ERR,"Added uid=%d name=%s\n", + nid.nid_uid, nid.nid_name); + } + if (!svc_sendreply(transp, (xdrproc_t)xdr_retval, + (caddr_t)&info)) + syslog(LOG_ERR, "Can't send reply"); + return; + case RPCNFSUSERD_GETGROUP: + if (!svc_getargs(transp, (xdrproc_t)xdr_getname, + (caddr_t)&info)) { + svcerr_decode(transp); + return; + } + grp = getgrnam(info.name); + info.retval = 0; + if (grp != NULL) { + nid.nid_usertimeout = defusertimeout; + nid.nid_gid = grp->gr_gid; + nid.nid_name = grp->gr_name; + } else { + nid.nid_usertimeout = 5; + nid.nid_gid = defaultgid; + nid.nid_name = info.name; + } + nid.nid_namelen = strlen(nid.nid_name); + nid.nid_flag = NFSID_ADDGROUPNAME; + error = nfssvc(NFSSVC_IDNAME, &nid); + if (error) { + info.retval = error; + syslog(LOG_ERR, "Can't add group %s\n", + grp->gr_name); + } else if (verbose) { + syslog(LOG_ERR,"Added gid=%d name=%s\n", + nid.nid_gid, nid.nid_name); + } + if (!svc_sendreply(transp, (xdrproc_t)xdr_retval, + (caddr_t)&info)) + syslog(LOG_ERR, "Can't send reply"); + return; + default: + svcerr_noproc(transp); + return; + }; +} + +/* + * Xdr routine to get an id number + */ +bool_t +xdr_getid(XDR *xdrsp, caddr_t cp) +{ + struct info *ifp = (struct info *)cp; + + return (xdr_long(xdrsp, &ifp->id)); +} + +/* + * Xdr routine to get a user name + */ +bool_t +xdr_getname(XDR *xdrsp, caddr_t cp) +{ + struct info *ifp = (struct info *)cp; + long len; + + if (!xdr_long(xdrsp, &len)) + return (0); + if (len > MAXNAME) + return (0); + if (!xdr_opaque(xdrsp, ifp->name, len)) + return (0); + ifp->name[len] = '\0'; + return (1); +} + +/* + * Xdr routine to return the value. + */ +bool_t +xdr_retval(XDR *xdrsp, caddr_t cp) +{ + struct info *ifp = (struct info *)cp; + long val; + + val = ifp->retval; + return (xdr_long(xdrsp, &val)); +} + +/* + * cleanup_term() called via SIGUSR1. + */ +void +cleanup_term(int signo) +{ + int i, cnt; + + if (im_a_slave) + exit(0); + + /* + * Ok, so I'm the master. + * As the Governor of California might say, "Terminate them". + */ + cnt = 0; + for (i = 0; i < nfsuserdcnt; i++) { + if (slaves[i] != (pid_t)-1) { + cnt++; + kill(slaves[i], SIGUSR1); + } + } + + /* + * and wait for them to die + */ + for (i = 0; i < cnt; i++) + wait3(NULL, 0, NULL); + + /* + * Finally, get rid of the socket + */ + if (nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0) { + syslog(LOG_ERR, "Can't do nfssvc() to delete the port\n"); + exit(1); + } + exit(0); +} + +void +usage(void) +{ + + errx(1, + "usage: nfsuserd [-usermax cache_size] [-usertimeout minutes] [-verbose] [-domain domain_name] [n]"); +}