mountd.c revision 250a073308fb9258903f57b76eeb2470c6926efe
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* Portions of this source code were derived from Berkeley 4.3 BSD
* under license from the Regents of the University of California.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <syslog.h>
#include <netconfig.h>
#include <netdir.h>
#include <sys/pathconf.h>
#include <sys/systeminfo.h>
#include <signal.h>
#include <locale.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <thread.h>
#include <assert.h>
#include <priv_utils.h>
#include <rpcsvc/daemon_utils.h>
#include <deflt.h>
#include "../../fslib.h"
#include <sharefs/sharetab.h>
#include "../lib/sharetab.h"
#include "mountd.h"
#include <libtsnet.h>
extern int daemonize_init(void);
extern void daemonize_fini(int fd);
struct sh_list *share_list;
static struct share *find_lofsentry(char *, int *);
struct nd_hostservlist **);
struct nd_hostservlist *, int *);
struct nd_hostservlist *, int *);
struct nd_hostservlist *, int);
struct nd_hostservlist *, int);
static void mnt_pathconf(struct svc_req *);
static void sigexit(int);
static int newopts(char *);
static int verbose;
static int rejecting;
static int mount_vers_min = MOUNTVERS;
static int mount_vers_max = MOUNTVERS3;
/* ARGSUSED */
static void *
nfsauth_svc(void *arg)
{
int doorfd = -1;
#ifdef DEBUG
int dfd;
#endif
exit(10);
}
#ifdef DEBUG
/*
* Create a file system path for the door
*/
exit(11);
}
/*
* Clean up any stale namespace associations
*/
(void) fdetach(MOUNTD_DOOR);
/*
* Register in namespace to pass to the kernel to door_ki_open
*/
exit(12);
}
#endif
/*
* Must pass the doorfd down to the kernel.
*/
/*
* Wait for incoming calls
*/
/*CONSTCOND*/
for (;;)
(void) pause();
/*NOTREACHED*/
return (NULL);
}
int
{
int pid;
int c;
int rpc_svc_mode = RPC_SVC_MT_AUTO;
int maxthreads;
int maxrecsz = RPC_MAXDATASIZE;
int pipe_fd = -1;
/*
* Mountd requires uid 0 for:
* doesn't do any locking before first truncate;
* NFS share does; should use fcntl locking instead)
* Needed privileges:
* auditing
* nfs syscall
* file dac search (so it can stat all files)
* Optional privileges:
* MLP
*/
"%s: must be run with sufficient privileges\n",
argv[0]);
exit(1);
}
maxthreads = 0;
switch (c) {
case 'v':
verbose++;
break;
case 'r':
rejecting = 1;
break;
case 'm':
if (maxthreads < 1) {
"%s: must specify positive maximum threads count, using default\n",
argv[0]);
maxthreads = 0;
}
break;
}
}
/*
* Read in the NFS version values from config file.
*/
char *defval;
int defvers;
errno = 0;
if (errno == 0) {
/*
* special because NFSv2 is
* supported by mount v1 & v2
*/
if (defvers == NFS_VERSION)
}
}
errno = 0;
if (errno == 0) {
}
}
/* close defaults file */
}
/*
* Sanity check versions,
* even though we may get versions > MOUNTVERS3, we still need
* to start nfsauth service, so continue on regardless of values.
*/
if (mount_vers_min > mount_vers_max) {
}
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
/* Don't drop core if the NFS module isn't loaded. */
pipe_fd = daemonize_init();
/*
* If we coredump it'll be in /core
*/
if (chdir("/") < 0)
/*
* establish our lock on the lock file and write our pid to it.
* exit if some other process holds the lock, or if there's any
*/
switch (pid) {
case 0:
break;
case -1:
exit(2);
default:
/* daemon was already running */
exit(0);
}
audit_mountd_setup(); /* BSM */
/*
* Tell RPC that we want automatic thread mode.
* A new thread will be spawned for each request.
*/
exit(1);
}
/*
* Enable non-blocking mode and maximum record size checks for
* connection oriented transports.
*/
}
/*
* from being hijacked by a bind to a more specific addr.
*/
}
/*
* If the -m argument was specified, then set the
* maximum number of threads to the value specified.
*/
exit(1);
}
/*
* Make sure to unregister any previous versions in case the
* user is reconfiguring the server in interesting ways.
*/
/*
* Create the nfsauth thread with same signal disposition
* as the main thread. We need to create a separate thread
* since mountd() will be both an RPC server (for remote
* traffic) _and_ a doors server (for kernel upcalls).
*/
exit(2);
}
/*
* Create datagram and connection oriented services
*/
if (mount_vers_max >= MOUNTVERS) {
"couldn't register datagram_v MOUNTVERS");
exit(1);
}
"couldn't register circuit_v MOUNTVERS");
exit(1);
}
}
if (mount_vers_max >= MOUNTVERS_POSIX) {
"datagram_v") == 0) {
"couldn't register datagram_v MOUNTVERS_POSIX");
exit(1);
}
"circuit_v") == 0) {
"couldn't register circuit_v MOUNTVERS_POSIX");
exit(1);
}
}
if (mount_vers_max >= MOUNTVERS3) {
"couldn't register datagram_v MOUNTVERS3");
exit(1);
}
"couldn't register circuit_v MOUNTVERS3");
exit(1);
}
}
/*
* Start serving
*/
rmtab_load();
/* Get rid of the most dangerous basic privileges. */
(char *)NULL);
svc_run();
abort();
/* NOTREACHED */
return (0);
}
/*
* Server procedure switch routine
*/
void
{
case NULLPROC:
errno = 0;
return;
case MOUNTPROC_MNT:
return;
case MOUNTPROC_DUMP:
return;
case MOUNTPROC_UMNT:
return;
case MOUNTPROC_UMNTALL:
return;
case MOUNTPROC_EXPORT:
case MOUNTPROC_EXPORTALL:
return;
case MOUNTPROC_PATHCONF:
else
return;
default:
return;
}
}
/* Set up anonymous client */
struct nd_hostservlist *
anon_client(char *host)
{
struct nd_hostservlist *anon_hsl;
struct nd_hostserv *anon_hs;
return (NULL);
return (NULL);
}
else
return (NULL);
}
return (anon_hsl);
}
/*
* Get the client's hostname from the transport handle
* If the name is not available then return "(anon)".
*/
void
struct nd_hostservlist **serv)
{
char tmp[MAXIPADDRLEN];
return;
}
return;
}
/*
* Use the this API instead of the netdir_getbyaddr()
* to avoid service lookup.
*/
struct sockaddr_in *sa;
/* LINTED pointer alignment */
return;
struct sockaddr_in6 *sa;
/* LINTED pointer alignment */
return;
}
return;
}
}
void
{
int saverrno;
register char *host;
return;
if (errno == 0)
else
}
/*
* Answer pathconf questions for the mount point fs
*/
static void
{
struct pathcnf p;
return;
}
goto done;
}
/*
* Get a path without symbolic links.
*/
"mount request: realpath failed on %s: %m",
path);
goto done;
}
/*
* can't ask about devices over NFS
*/
errno = 0;
if (errno)
if (errno)
if (errno)
done:
errno = 0;
}
/*
* If the rootmount (export) option is specified, the all mount requests for
* subdirectories return EACCES.
*/
static int
{
char *val;
return (0);
else
return (1);
} else
return (1);
}
#define MAX_FLAVORS 128
/*
* Return only EACCES if client does not have access
* to this directory.
* "If the server exports only /a/b, an attempt to
* mount a/b/c will fail with ENOENT if the directory
* does not exist"... However, if the client
* does not have access to /a/b, an attacker can
* determine whether the directory exists.
* This routine checks either existence of the file or
* existence of the file name entry in the mount table.
* If the file exists and there is no file name entry,
* the error returned should be EACCES.
* If the file does not exist, it must be determined
* whether the client has access to a parent
* directory. If the client has access to a parent
* directory, the error returned should be ENOENT,
* otherwise EACCES.
*/
static int
{
int flavor_count;
return (EACCES);
}
/* CONSTCOND */
while (1) {
if (sh) {
}
/*
* There is no file name entry.
* If the file (with symbolic links resolved) exists,
* the error returned should be EACCES.
*/
if (realpath_error == 0)
break;
/*
* This is a "nosub" only export, in which case,
* mounting subdirectories isn't allowed.
* If the file (with symbolic links resolved) exists,
* the error returned should be EACCES.
*/
if (realpath_error == 0)
break;
} else {
/*
* Check permissions in mount table.
*/
else
if (flavor_count != 0) {
/*
* Found entry in table and
* client has correct permissions.
*/
break;
}
}
/*
* Check all parent directories.
*/
break;
*dp = '\0';
break;
/*
* Get the real path (no symbolic links in it)
*/
break;
} else {
realpath_error = 0;
}
}
if (sh)
return (reply_error);
}
/*
* Check mount requests, add to mounted list if ok
*/
static void
{
int error = 0, lofs_tried = 0;
int flavor_list[MAX_FLAVORS];
int flavor_count;
return;
}
/*
* We failed to get a name for the client, even 'anon',
* probably because we ran out of memory. In this situation
* it doesn't make sense to allow the mount to succeed.
*/
goto reply;
}
/*
* If the version being used is less than the minimum version,
* the filehandle translation should not be provided to the
* client.
*/
if (verbose)
goto reply;
}
/*
* Trusted Extension doesn't support nfsv2. nfsv2 client
* uses MOUNT protocol v1 and v2. To prevent circumventing
* TX label policy via using nfsv2 client, reject a mount
* request with version less than 3 and log an error.
*/
if (is_system_labeled()) {
if (version < 3) {
if (verbose)
"Rejected mount: TX doesn't support NFSv2");
goto reply;
}
}
/*
* Get the real path (no symbolic links in it)
*/
if (verbose)
"mount request: realpath: %s: %m", path);
goto reply;
}
goto reply;
}
/*
* Check if this is a "nosub" only export, in which case, mounting
* subdirectories isn't allowed. Bug 1184573.
*/
goto reply;
}
else
if (flavor_count == 0) {
goto reply;
}
/*
* Check MAC policy here. The server side policy should be
* consistent with client side mount policy, i.e.
* - we disallow an admin_low unlabeled client to mount
* - we disallow mount from a lower labeled client.
*/
if (is_system_labeled()) {
"mount request: Failed to get caller's ucred : %m");
goto reply;
}
"mount request: can't get client label from ucred");
goto reply;
}
goto reply;
}
/*
* get trusted network template associated
* with the client.
*/
goto reply;
}
} else {
goto reply;
}
goto reply;
}
goto reply;
}
}
}
/*
* Now get the filehandle.
*
* NFS V2 clients get a 32 byte filehandle.
* NFS V3 clients get a 32 or 64 byte filehandle, depending on
* the embedded FIDs.
*/
/* LINTED pointer alignment */
errno = 0;
continue;
}
path);
break;
}
if (version == MOUNTVERS3) {
} else {
}
ucred_free(uc);
switch (version) {
case MOUNTVERS:
case MOUNTVERS_POSIX:
else
break;
case MOUNTVERS3:
if (!error) {
} else if (error == ENAMETOOLONG)
break;
}
if (verbose)
if (!error)
done:
if (sh)
}
struct share *
{
(void) rw_rdlock(&sharetab_lock);
if (*p1 == '\0')
goto done; /* exact match */
/*
* Now compare the pathnames for three cases:
*
*
*
*
* Then compare the dev_t of the parent and child to
* make sure that they're both in the same filesystem.
*/
if (verbose)
goto done;
}
/*
* Use stat64 on "path" since it might be larger
* than 2 Gb and 32 bit stat would fail EOVERFLOW
*/
if (verbose)
goto done;
}
goto done;
}
}
done:
(void) rw_unlock(&sharetab_lock);
return (sh);
}
static int
{
return (1);
return (1);
return (1);
} else if (*p2 == '\0') {
while (*p1 == '/')
p1++;
return (1);
}
return (0);
}
/*
* find_lofsentry() searches for the real path which this requested LOFS path
* (rpath) shadows. If found, it will return the sharetab entry of
* the real path that corresponds to the LOFS path.
* We first search mnttab to see if the requested path is an automounted
* path. If it is an automounted path, it will trigger the mount by stat()ing
* the requested path. Note that it is important to check that this path is
* actually an automounted path, otherwise we would stat() a path which may
* turn out to be NFS and block indefinitely on a dead server. The automounter
* times-out if the server is dead, so there's no risk of hanging this
* thread waiting for stat().
* After the mount has been triggered (if necessary), we look for a
* is a substring of the rpath. If found, we construct a new path by
* concatenating the mnt_special and the remaining of rpath, call findentry()
* to make sure the 'real path' is shared.
*/
static struct share *
{
char tmp_path[MAXPATHLEN];
int mntpnt_len = 0, tmp;
if ((*done_flag)++)
return (retcode);
/*
* While fsgetmntlist() uses lockf() to
* lock the mnttab before reading it in,
* the lock ignores threads in the same process.
* Read in the mnttab with the protection of a mutex.
*/
(void) mutex_lock(&mnttab_lock);
mntl = fsgetmntlist();
(void) mutex_unlock(&mnttab_lock);
/*
* Obtain the mountpoint for the requested path.
*/
;
mntpnt_len = tmp;
}
}
/*
* If the path needs to be autoFS mounted, trigger the mount by
* stat()ing it. This is determined by checking whether the
* mountpoint we just found is of type autofs.
*/
/*
* The requested path is a substring of an autoFS filesystem.
* Trigger the mount.
*/
if (verbose)
goto done;
}
/*
* The requested path is a directory, stat(2) it
* again with a trailing '.' to force the autoFS
* module to trigger the mount of indirect
*/
if (verbose) {
"%s/.: exceeds MAXPATHLEN %d",
rpath, MAXPATHLEN);
}
goto done;
}
if (verbose)
goto done;
}
}
/*
* The mount has been triggered, re-read mnttab to pick up
* the changes made by autoFS.
*/
(void) mutex_lock(&mnttab_lock);
mntl = fsgetmntlist();
(void) mutex_unlock(&mnttab_lock);
}
/*
* The autoFS mountpoint has been triggered if necessary,
* now search mnttab again to determine if the requested path
* is an LOFS mount of a shared path.
*/
mntpnt_len = 0;
continue;
;
mntpnt_len = tmp;
MAXPATHLEN) {
if (verbose) {
}
if (retcode)
goto done;
}
if (retcode)
}
}
if (retcode) {
}
done:
return (retcode);
}
/*
* Determine whether an access list grants rights to a particular host.
* We match on aliases of the hostname as well as on the canonical name.
* Names in the access list may be either hosts or netgroups; they're
* not distinguished syntactically. We check for hosts first because
* it's cheaper (just M*N strcmp()s), then try netgroups.
*/
int
char *access_list) /* N.B. we clobber this "input" parameter */
{
int nentries;
char *gr;
char *lasts;
char *host;
int off;
int i;
int netgroup_match;
int response;
/*
* If no access list - then it's unrestricted
*/
return (1);
nentries = 0;
/*
* If the list name has a '-' prepended
* then a match of the following name
* implies failure instead of success.
*/
if (*gr == '-') {
response = 0;
gr++;
} else
response = 1;
/*
* The following loops through all the
* client's aliases. Usually it's just one name.
*/
/*
* If the list name begins with a dot then
* do a domain name suffix comparison.
* A single dot matches any name with no
* suffix.
*/
if (*gr == '.') {
return (response);
} else {
if (off > 0 &&
return (response);
}
}
} else
/*
* If the list name begins with an at
* sign then do a network comparison.
*/
if (*gr == '@') {
return (response);
} else
/*
* Just do a hostname match
*/
return (response); /* Matched a hostname */
}
}
nentries++;
}
return (netgroup_match);
}
int
{
char *mp, *p;
int i, bits;
char buff[256];
/*
* Check if it's an IPv4 addr
*/
return (0);
/* LINTED pointer alignment */
sizeof (struct in_addr));
if (mp)
*mp++ = '\0';
/*
* Convert a dotted IP address
* to an IP address. The conversion
* is not the same as that in inet_addr().
*/
p = name;
addr = 0;
for (i = 0; i < 4; i++) {
p = strchr(p, '.');
if (p == NULL)
break;
p++;
}
} else {
/*
* Turn the netname into
* an IP address.
*/
return (0);
}
}
/*
* If the mask is specified explicitly then
* use that value, e.g.
*
* @109.104.56/28
*
* otherwise assume a mask from the zero octets
* in the least significant bits of the address, e.g.
*
* @109.104 or @109.104.0.0
*/
if (mp) {
: 0;
} else {
if ((addr & 0x00ffffff) == 0)
mask = 0xff000000;
else if ((addr & 0x0000ffff) == 0)
mask = 0xffff0000;
else if ((addr & 0x000000ff) == 0)
mask = 0xffffff00;
}
}
static char *optlist[] = {
#define OPT_RO 0
#define OPT_RW 1
#define OPT_ROOT 2
#define OPT_SECURE 3
#define OPT_ANON 4
#define OPT_WINDOW 5
#define OPT_NOSUID 6
#define OPT_ACLOK 7
#define OPT_SEC 8
};
static int
map_flavor(char *str)
{
return (-1);
}
/*
* If the option string contains a "sec="
* option, then use new option syntax.
*/
static int
{
return (0);
return (0);
}
p = head;
while (*p) {
return (1);
}
}
return (0);
}
/*
* Given an export and the clients hostname(s)
* determine the security flavors that this
* client is permitted to use.
*
* This routine is called only for "old" syntax, i.e.
* only one security flavor is allowed. So we need
* to determine two things: the particular flavor,
* and whether the client is allowed to use this
* flavor, i.e. is in the access list.
*
* Note that if there is no access list, then the
* default is that access is granted.
*/
static int
{
int ok = 0;
int defaultaccess = 1;
return (0);
}
p = opts;
while (*p) {
case OPT_SECURE:
break;
case OPT_RO:
case OPT_RW:
defaultaccess = 0;
ok++;
break;
}
}
return (defaultaccess || ok);
}
/*
* Given an export and the clients hostname(s)
* determine the security flavors that this
* client is permitted to use.
*
* This is somewhat more complicated than the "old"
* routine because the options may contain multiple
* security flavors (sec=) each with its own access
* lists. So a client could be granted access based
* on a number of security flavors. Note that the
* type of access might not always be the same, the
* client may get readonly access with one flavor
* and readwrite with another, however the client
* is not told this detail, it gets only the list
* of flavors, and only if the client is using
* version 3 of the mount protocol.
*/
static int
{
char *lasts;
char *f;
return (0);
}
p = opts;
count = c = 0;
/* default access is rw */
access_ok = 1;
while (*p) {
case OPT_SEC:
/*
* Before a new sec=xxx option, check if we need
* to move the c index back to the previous count.
*/
if (!access_ok) {
c = count;
}
/* get all the sec=f1[:f2] flavors */
!= NULL) {
flavors[c++] = map_flavor(f);
}
/* for a new sec=xxx option, default is rw access */
access_ok = 1;
break;
case OPT_RO:
case OPT_RW:
count = c;
access_ok = 1;
} else {
access_ok = 0;
}
break;
}
}
if (!access_ok) {
c = count;
}
return (c);
}
/*
* This is a tricky piece of code that parses the
* share options looking for a match on the auth
* flavor that the client is using. If it finds
* a match, then the client is given ro, rw, or
* no access depending whether it is in the access
* list. There is a special case for "secure"
* flavor. Other flavors are values of the new "sec=" option.
*/
int
{
else
}
static int
{
int match; /* Set when a flavor is matched */
int perm = 0; /* Set when "ro", "rw" or "root" is matched */
int list = 0; /* Set when "ro", "rw" is found */
int ro_val = 0; /* Set if ro option is 'ro=' */
int rw_val = 0; /* Set if rw option is 'rw=' */
return (0);
}
p = opts;
while (*p) {
case OPT_SECURE:
break;
case OPT_RO:
list++;
perm |= NFSAUTH_RO;
break;
case OPT_RW:
list++;
perm |= NFSAUTH_RW;
break;
case OPT_ROOT:
/*
* Check if the client is in
* the root list. Only valid
* for AUTH_SYS.
*/
break;
break;
perm |= NFSAUTH_ROOT;
break;
}
}
return (NFSAUTH_DENIED);
if (list) {
/*
* If the client doesn't match an "ro" or "rw"
* list then set no access.
*/
perm |= NFSAUTH_DENIED;
} else {
/*
* The client matched a flavor entry that
* has no explicit "rw" or "ro" determination.
* Default it to "rw".
*/
perm |= NFSAUTH_RW;
}
/*
* The client may show up in both ro= and rw=
* lists. If so, then turn off the RO access
* bit leaving RW access.
*/
/*
* Logically cover all permutations of rw=,ro=.
* In the case where, rw,ro=<host> we would like
* to remove RW access for the host. In all other cases
* RW wins the precedence battle.
*/
perm &= ~(NFSAUTH_RW);
} else {
perm &= ~(NFSAUTH_RO);
}
}
return (perm);
}
/*
* Check if the client has access by using a flavor different from
* the given "flavor". If "flavor" is not in the flavor list,
* return TRUE to indicate that this "flavor" is a wrong sec.
*/
static bool_t
{
int flavor_list[MAX_FLAVORS];
int flavor_count, i;
/* get the flavor list that the client has access with */
if (flavor_count == 0)
return (FALSE);
/*
* Check if the given "flavor" is in the flavor_list.
*/
for (i = 0; i < flavor_count; i++) {
if (flavor == flavor_list[i])
return (FALSE);
}
/*
* If "flavor" is not in the flavor_list, return TRUE to indicate
* that the client should have access by using a security flavor
* different from this "flavor".
*/
return (TRUE);
}
/*
* Given an export and the client's hostname, we
* check the security options to see whether the
* client is allowed to use the given security flavor.
*
* The strategy is to proceed through the options looking
* for a flavor match, then pay attention to the ro, rw,
* and root options.
*
* Note that an entry may list several flavors in a
* single entry, e.g.
*
* sec=krb5,rw=clnt1:clnt2,ro,sec=sys,ro
*
*/
static int
{
char *lasts;
char *f;
int match = 0; /* Set when a flavor is matched */
int perm = 0; /* Set when "ro", "rw" or "root" is matched */
int list = 0; /* Set when "ro", "rw" is found */
int ro_val = 0; /* Set if ro option is 'ro=' */
int rw_val = 0; /* Set if rw option is 'rw=' */
return (0);
}
p = opts;
while (*p) {
case OPT_SEC:
if (match)
goto done;
!= NULL) {
if (flavor == map_flavor(f)) {
match = 1;
break;
}
}
break;
case OPT_RO:
if (!match)
break;
list++;
perm |= NFSAUTH_RO;
break;
case OPT_RW:
if (!match)
break;
list++;
perm |= NFSAUTH_RW;
break;
case OPT_ROOT:
/*
* Check if the client is in
* the root list. Only valid
* for AUTH_SYS.
*/
break;
if (!match)
break;
break;
perm |= NFSAUTH_ROOT;
break;
}
}
done:
/*
* If no match then set the perm accordingly
*/
if (!match)
return (NFSAUTH_DENIED);
if (list) {
/*
* If the client doesn't match an "ro" or "rw" list then
* check if it may have access by using a different flavor.
* If so, return NFSAUTH_WRONGSEC.
* If not, return NFSAUTH_DENIED.
*/
perm |= NFSAUTH_WRONGSEC;
else
perm |= NFSAUTH_DENIED;
}
} else {
/*
* The client matched a flavor entry that
* has no explicit "rw" or "ro" determination.
* Make sure it defaults to "rw".
*/
perm |= NFSAUTH_RW;
}
/*
* The client may show up in both ro= and rw=
* lists. If so, then turn off the RO access
* bit leaving RW access.
*/
/*
* Logically cover all permutations of rw=,ro=.
* In the case where, rw,ro=<host> we would like
* to remove RW access for the host. In all other cases
* RW wins the precedence battle.
*/
perm &= ~(NFSAUTH_RW);
} else {
perm &= ~(NFSAUTH_RO);
}
}
return (perm);
}
void
{
FILE *f;
static timestruc_t last_sharetab_time;
int res, c = 0;
/*
*/
return;
}
/*
* No change.
*/
return;
}
/*
* Remember the mod time, then after getting the
* write lock check again. If another thread
* already did the update, then there's no
* work to do.
*/
(void) rw_wrlock(&sharetab_lock);
(void) rw_unlock(&sharetab_lock);
return;
}
/*
* Note that since the sharetab is now in memory
* and a snapshot is taken, we no longer have to
* lock the file.
*/
if (f == NULL) {
(void) rw_unlock(&sharetab_lock);
return;
}
/*
* modified, flush netgroup cache entries.
*/
share_list = NULL;
c++;
continue;
goto alloc_failed;
if (share_list == NULL)
share_list = shp;
else
/* LINTED not used before set */
goto alloc_failed;
}
if (res < 0)
SHARETAB, c + 1);
(void) fclose(f);
(void) rw_unlock(&sharetab_lock);
return;
}
(void) fclose(f);
(void) rw_unlock(&sharetab_lock);
return;
share_list = NULL;
(void) fclose(f);
(void) rw_unlock(&sharetab_lock);
}
static void
{
while (shp) {
}
}
/*
* Remove an entry from mounted list
*/
static void
{
char rpath[MAXPATHLEN];
return;
}
errno = 0;
/*
* Without the hostname we can't do audit or delete
* this host from the mount entries.
*/
return;
}
if (verbose)
if (verbose)
}
}
/*
* Remove all entries for one machine from mounted list
*/
static void
{
char *host;
return;
}
/*
* We assume that this call is asynchronous and made via rpcbind
* callit routine. Therefore return control immediately. The error
* causes rpcbind to remain silent, as opposed to every machine
* on the net blasting the requester with a response.
*/
/* Can't do anything without the name of the client */
return;
}
/*
* Remove all hosts entries from mount list
*/
if (verbose)
}
void *
{
void *ret;
exit(1);
}
return (ret);
}
static void
{
_exit(0);
_exit(1);
}
static tsol_tpent_t *
{
char v4_addr[INET_ADDRSTRLEN];
char v6_addr[INET6_ADDRSTRLEN];
case AF_INET:
NULL)
return (NULL);
return (NULL);
return (tp);
break;
case AF_INET6:
NULL)
return (NULL);
return (NULL);
return (tp);
break;
default:
return (NULL);
}
}