From 08f63a0d1856606c05d8acbbdf2842f53f0a5075 Mon Sep 17 00:00:00 2001 From: rmacklem Date: Wed, 27 May 2009 19:56:51 +0000 Subject: [PATCH] Add support for the experimental nfs client to mount_nfs. The experimental client is used when the fstype is "newnfs" or the "nfsv4" option is specified. It includes the addition of the option: gssname - to specify a client side initiator host based principal name which is specific to NFSv4. It also includes a change to mount.c, so that it knows about mount_newnfs, but not mount_nfs4. Reviewed by: dfr Approved by: kib (mentor) --- sbin/mount/mount.c | 2 +- sbin/mount_nfs/Makefile | 4 +- sbin/mount_nfs/mount_nfs.8 | 79 ++++++++++++++++++++++++++++++- sbin/mount_nfs/mount_nfs.c | 96 ++++++++++++++++++++++++++++++++++++-- 4 files changed, 173 insertions(+), 8 deletions(-) diff --git a/sbin/mount/mount.c b/sbin/mount/mount.c index e11c1c28d7b3..40a3e96e9a98 100644 --- a/sbin/mount/mount.c +++ b/sbin/mount/mount.c @@ -140,7 +140,7 @@ use_mountprog(const char *vfstype) */ unsigned int i; const char *fs[] = { - "cd9660", "mfs", "msdosfs", "nfs", "nfs4", "ntfs", + "cd9660", "mfs", "msdosfs", "newnfs", "nfs", "ntfs", "nwfs", "nullfs", "portalfs", "smbfs", "udf", "unionfs", NULL }; diff --git a/sbin/mount_nfs/Makefile b/sbin/mount_nfs/Makefile index 960f97c59c1d..2e255b27271e 100644 --- a/sbin/mount_nfs/Makefile +++ b/sbin/mount_nfs/Makefile @@ -5,13 +5,15 @@ PROG= mount_nfs SRCS= mount_nfs.c getmntopts.c mounttab.c MAN= mount_nfs.8 -MLINKS= mount_nfs.8 +MLINKS= mount_nfs.8 mount_newnfs.8 MOUNT= ${.CURDIR}/../mount UMNTALL= ${.CURDIR}/../../usr.sbin/rpc.umntall CFLAGS+= -DNFS -I${MOUNT} -I${UMNTALL} WARNS?= 3 +LINKS= ${BINDIR}/mount_nfs ${BINDIR}/mount_newnfs + .PATH: ${MOUNT} ${UMNTALL} .include diff --git a/sbin/mount_nfs/mount_nfs.8 b/sbin/mount_nfs/mount_nfs.8 index 78a3b1967729..b1199d408e1c 100644 --- a/sbin/mount_nfs/mount_nfs.8 +++ b/sbin/mount_nfs/mount_nfs.8 @@ -132,6 +132,47 @@ short. .It Cm fg Same as not specifying .Cm bg . +.It Cm gssname Ns = Ns Aq Ar name +For the RPCSEC_GSS security flavors, such as krb5, krb5i and krb5p when being +used for an NFSv4 mount, this option specifies the host based principal +name to be used for the state related operations SetClientID, +SetClientIDConfirm, ReleaseLockOwner and Renew. +It is also used for other operations, such as Getattr for +.Xr statfs 2 +information and during open/lock state recovery. +An entry for this principal must exist +in the client machine's default keytab file. +If possible, the keytab entry should be created using DES_CBC_CRC +encryption. If another encryption algorithm is used, the sysctl variable +.Va vfs.newnfs.keytab_enctype +must be set to the numeric value representing that encryption algorithm. +(The numeric values can be found in /usr/include/krb5_asn1.h. Look +for constants named ETYPE_xxx.) +If this option is given +as a name without an ``@'', such as ``root'' or ``nfs'', +``@'' will be appended to it. +.sp +If this option is not specified +for NFSv4 mounts using krb5[ip], the above operations will be done using the +user principal for the user that performed the mount. This +only works for mounts done by a user other than ``root'' and the user must +have a valid TGT in their credentials cache at the time the mount is done. +(Setting the +.Va vfs.usermount +to non-zero will allow users to do mounts.) +Because the user's TGT is used to acquire credentials for these operations, +it is important that that user's TGT does not expire before +.Xr umount 8 +is done. +.It Cm allgssname +This option can be used along with +.Cm gssname +to indicate that all accesses to the mount point are to be done using +the host based principal specified by the +.Cm gssname +option. +This might be useful for nfsv4 mounts using sec=krb5[ip] that are being accessed +by batch utilities over long periods of time. .It Cm hard Same as not specifying .Cm soft . @@ -157,6 +198,12 @@ then version 2). Note that NFS version 2 has a file size limit of 2 gigabytes. .It Cm nfsv3 Use the NFS Version 3 protocol. +.It Cm nfsv4 +Use the NFS Version 4 protocol. +This option will force the mount to use the experimental nfs subsystem and +TCP transport. +To use the experimental nfs subsystem for nfsv2 and nfsv3 mounts, you +must specify the ``newnfs'' file system type instead of ``nfs''. .It Cm noconn For UDP mount points, do not do a .Xr connect 2 . @@ -192,6 +239,11 @@ servers on the client. Note that this option will only be honored when performing the initial mount, it will be silently ignored if used while updating the mount options. +.It Cm principal +For the RPCSEC_GSS security flavors, such as krb5, krb5i and krb5p, +this option sets the name of the host based principal name expected +by the server. This option overrides the default, which will be +``nfs@'' and should normally be sufficient. .It Cm noresvport Do .Em not @@ -200,8 +252,10 @@ use a reserved socket port number (see below). Use specified port number for NFS requests. The default is to query the portmapper for the NFS port. .It Cm rdirplus -Used with NQNFS and NFSV3 to specify that the \fBReaddirPlus\fR RPC should +Used with NFSV3 to specify that the \fBReaddirPlus\fR RPC should be used. +For NFSV4, setting this option has a similar effect, in that it will make +the Readdir Operation get more attributes. This option reduces RPC traffic for cases such as .Dq "ls -l" , but tends to flood the attribute and name caches with prefetched entries. @@ -248,6 +302,18 @@ with the option to see what the .Dq "fragments dropped due to timeout" value is.) +.It Cm sec Ns = Ns Aq Ar flavor +This option specifies what security flavor should be used for the mount. +Currently, they are: +.Bd -literal +krb5 - Use KerberosV authentication +krb5i - Use KerberosV authentication and + apply integrity checksums to RPCs +krb5p - Use KerberosV authentication and + encrypt the RPC data +sys - The default AUTH_SYS, which uses a + uid + gid list authenticator +.Ed .It Cm soft A soft mount, which implies that file system calls will fail after @@ -368,8 +434,19 @@ Same as .Sh SEE ALSO .Xr nmount 2 , .Xr unmount 2 , +.Xr nfsv4 4 , .Xr fstab 5 , +.Xr gssd 8 , .Xr mount 8 , .Xr nfsd 8 , .Xr nfsiod 8 , .Xr showmount 8 +.Sh BUGS +Since nfsv4 performs open/lock operations that have their ordering strictly +enforced by the server, the options +.Cm intr +and +.Cm soft +cannot be safely used. +.Cm hard +nfsv4 mounts are strongly recommended. diff --git a/sbin/mount_nfs/mount_nfs.c b/sbin/mount_nfs/mount_nfs.c index 102c51d35a9d..d7305e5f1dee 100644 --- a/sbin/mount_nfs/mount_nfs.c +++ b/sbin/mount_nfs/mount_nfs.c @@ -45,6 +45,8 @@ static char sccsid[] = "@(#)mount_nfs.c 8.11 (Berkeley) 5/4/95"; __FBSDID("$FreeBSD$"); #include +#include +#include #include #include #include @@ -111,11 +113,13 @@ int addrlen = 0; u_char *fh = NULL; int fhsize = 0; int secflavor = -1; +int got_principal = 0; enum mountmode { ANY, V2, V3, + V4 } mountmode = ANY; /* Return codes for nfs_tryproto. */ @@ -150,11 +154,13 @@ main(int argc, char *argv[]) int osversion; char *name, *p, *spec, *fstype; char mntpath[MAXPATHLEN], errmsg[255]; + char hostname[MAXHOSTNAMELEN + 1], *gssname, gssn[MAXHOSTNAMELEN + 50]; mntflags = 0; iov = NULL; iovlen = 0; memset(errmsg, 0, sizeof(errmsg)); + gssname = NULL; fstype = strrchr(argv[0], '_'); if (fstype == NULL) @@ -242,6 +248,9 @@ main(int argc, char *argv[]) } else if (strcmp(opt, "fg") == 0) { /* same as not specifying -o bg */ pass_flag_to_nmount=0; + } else if (strcmp(opt, "gssname") == 0) { + pass_flag_to_nmount = 0; + gssname = val; } else if (strcmp(opt, "mntudp") == 0) { mnttcp_ok = 0; nfsproto = IPPROTO_UDP; @@ -262,12 +271,21 @@ main(int argc, char *argv[]) mountmode = V2; } else if (strcmp(opt, "nfsv3") == 0) { mountmode = V3; + } else if (strcmp(opt, "nfsv4") == 0) { + pass_flag_to_nmount=0; + mountmode = V4; + fstype = "newnfs"; + nfsproto = IPPROTO_TCP; + if (portspec == NULL) + portspec = "2049"; } else if (strcmp(opt, "port") == 0) { pass_flag_to_nmount=0; asprintf(&portspec, "%d", atoi(val)); if (portspec == NULL) err(1, "asprintf"); + } else if (strcmp(opt, "principal") == 0) { + got_principal = 1; } else if (strcmp(opt, "sec") == 0) { /* * Don't add this option to @@ -363,6 +381,37 @@ main(int argc, char *argv[]) /* The default is to keep retrying forever. */ retrycnt = 0; + /* + * If the experimental nfs subsystem is loaded into the kernel + * and the regular one is not, use it. Otherwise, use it if the + * fstype is set to "newnfs", either via "mount -t newnfs ..." + * or by specifying an nfsv4 mount. + */ + if (modfind("nfscl") >= 0 && modfind("nfs") < 0) { + fstype = "newnfs"; + } else if (strcmp(fstype, "newnfs") == 0) { + if (modfind("nfscl") < 0) { + /* Not present in kernel, try loading it */ + if (kldload("nfscl") < 0 || + modfind("nfscl") < 0) + errx(1, "nfscl is not available"); + } + } + + /* + * Add the fqdn to the gssname, as required. + */ + if (gssname != NULL) { + if (strchr(gssname, '@') == NULL && + gethostname(hostname, MAXHOSTNAMELEN) == 0) { + snprintf(gssn, sizeof (gssn), "%s@%s", gssname, + hostname); + gssname = gssn; + } + build_iovec(&iov, &iovlen, "gssname", gssname, + strlen(gssname) + 1); + } + if (!getnfsargs(spec, &iov, &iovlen)) exit(1); @@ -652,7 +701,7 @@ getnfsargs(char *spec, struct iovec **iov, int *iovlen) int ecode, speclen, remoteerr; char *hostp, *delimp, *errstr; size_t len; - static char nam[MNAMELEN + 1]; + static char nam[MNAMELEN + 1], pname[MAXHOSTNAMELEN + 5]; if ((delimp = strrchr(spec, ':')) != NULL) { hostp = spec; @@ -699,7 +748,7 @@ getnfsargs(char *spec, struct iovec **iov, int *iovlen) hints.ai_socktype = SOCK_DGRAM; if (getaddrinfo(hostp, portspec, &hints, &ai_nfs) != 0) { - hints.ai_flags = 0; + hints.ai_flags = AI_CANONNAME; if ((ecode = getaddrinfo(hostp, portspec, &hints, &ai_nfs)) != 0) { if (portspec == NULL) @@ -709,6 +758,18 @@ getnfsargs(char *spec, struct iovec **iov, int *iovlen) gai_strerror(ecode)); return (0); } + + /* + * For a Kerberized nfs mount where the "principal" + * argument has not been set, add it here. + */ + if (got_principal == 0 && secflavor >= 0 && + secflavor != AUTH_SYS && ai_nfs->ai_canonname != NULL) { + snprintf(pname, sizeof (pname), "nfs@%s", + ai_nfs->ai_canonname); + build_iovec(iov, iovlen, "principal", pname, + strlen(pname) + 1); + } } ret = TRYRET_LOCALERR; @@ -834,7 +895,9 @@ nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, } tryagain: - if (trymntmode == V2) { + if (trymntmode == V4) { + nfsvers = 4; + } else if (trymntmode == V2) { nfsvers = 2; mntvers = 1; } else { @@ -894,8 +957,7 @@ nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, try.tv_sec = 10; try.tv_usec = 0; stat = clnt_call(clp, NFSPROC_NULL, (xdrproc_t)xdr_void, NULL, - (xdrproc_t)xdr_void, NULL, - try); + (xdrproc_t)xdr_void, NULL, try); if (stat != RPC_SUCCESS) { if (stat == RPC_PROGVERSMISMATCH && trymntmode == ANY) { clnt_destroy(clp); @@ -910,6 +972,30 @@ nfs_tryproto(struct addrinfo *ai, char *hostp, char *spec, char **errstr, } clnt_destroy(clp); + /* + * For NFSv4, there is no mount protocol. + */ + if (trymntmode == V4) { + /* + * Store the server address in nfsargsp, making + * sure to copy any locally allocated structures. + */ + addrlen = nfs_nb.len; + addr = malloc(addrlen); + if (addr == NULL) + err(1, "malloc"); + bcopy(nfs_nb.buf, addr, addrlen); + + build_iovec(iov, iovlen, "addr", addr, addrlen); + secname = sec_num_to_name(secflavor); + if (secname != NULL) + build_iovec(iov, iovlen, "sec", secname, (size_t)-1); + build_iovec(iov, iovlen, "nfsv4", NULL, 0); + build_iovec(iov, iovlen, "dirpath", spec, (size_t)-1); + + return (TRYRET_SUCCESS); + } + /* Send the RPCMNT_MOUNT RPC to get the root filehandle. */ try.tv_sec = 10; try.tv_usec = 0;