mountd.c revision 9b241b4ed1cf882400b069ff9853cdd310d469bf
/*
* 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 2011 Nexenta Systems, Inc. All rights reserved.
*/
/* 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.
*/
#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>
#include <libscf.h>
#include <limits.h>
#include <attr.h>
#include "smfcfg.h"
extern int daemonize_init(void);
extern void daemonize_fini(int fd);
struct sh_list *share_list;
static mutex_t logging_queue_lock;
static cond_t logging_queue_cv;
static share_t *find_lofsentry(char *, int *);
static int getclientsnames_lazy(char *, struct netbuf **,
struct nd_hostservlist **);
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;
/* Needs to be accessed by nfscmd.c */
struct nd_hostservlist **, char *);
typedef struct logging_data {
char *ld_host;
char *ld_path;
char *ld_rpath;
int ld_status;
char *ld_netid;
struct logging_data *ld_next;
} logging_data;
/* 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);
}
/*
* NFS command service thread code for setup and handling of the
* nfs_cmd requests for character set conversion and other future
* events.
*/
static void *
{
int doorfd = -1;
exit(10);
}
/*
* Must pass the doorfd down to the kernel.
*/
/*
* Wait for incoming calls
*/
/*CONSTCOND*/
for (;;)
(void) pause();
/*NOTREACHED*/
return (NULL);
}
static void
{
}
}
}
static logging_data *
remove_head_of_queue(void)
{
/*
* Pull it off the queue.
*/
lq = logging_head;
if (lq) {
/*
* Drained it.
*/
if (logging_head == NULL) {
logging_tail = NULL;
}
}
return (lq);
}
static void
{
int cleared = 0;
char *host;
struct nd_hostservlist *clnames;
while (lq) {
else
} else
/* add entry to mount list */
(void) mutex_lock(&logging_queue_lock);
lq = remove_head_of_queue();
(void) mutex_unlock(&logging_queue_lock);
}
while (lq_clean) {
cleared++;
}
}
static void *
logging_svc(void *arg)
{
for (;;) {
(void) mutex_lock(&logging_queue_lock);
while (logging_head == NULL) {
(void) cond_wait(&logging_queue_cv,
}
lq = remove_head_of_queue();
(void) mutex_unlock(&logging_queue_lock);
}
/*NOTREACHED*/
return (NULL);
}
int
{
int pid;
int c;
int rpc_svc_mode = RPC_SVC_MT_AUTO;
int maxthreads;
int maxrecsz = RPC_MAXDATASIZE;
char defval[4];
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.
*/
bufsz = 4;
errno = 0;
if (errno == 0) {
/*
* special because NFSv2 is
* supported by mount v1 & v2
*/
if (defvers == NFS_VERSION)
}
}
bufsz = 4;
errno = 0;
if (errno == 0) {
}
}
/*
* 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 the cmd service 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 an additional thread to service the rmtab and
* audit_mountd_mount logging for mount requests. Use the same
* signal disposition as the main thread. We create
* a separate thread to allow the mount request threads to
* clear as soon as possible.
*/
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);
}
static int
struct nd_hostservlist **serv)
{
char host[MAXIPADDRLEN];
/*
* Use the this API instead of the netdir_getbyaddr()
* to avoid service lookup.
*/
struct sockaddr_in *sa;
/* LINTED pointer alignment */
struct sockaddr_in6 *sa;
/* LINTED pointer alignment */
} else {
"Client's address is neither IPv4 nor IPv6"));
return (EINVAL);
}
return (ENOMEM);
}
return (0);
}
/*
* Get the client's hostname from the copy of the
* relevant transport handle parts.
* If the name is not available then return "(anon)".
*/
static int
struct nd_hostservlist **serv)
{
int rc;
return (ENOMEM);
return (0);
}
return (rc);
}
/*
* Get the client's hostname from the transport handle.
* If the name is not available then return "(anon)".
*/
int
struct nd_hostservlist **serv)
{
int rc;
return (ENOMEM);
return (0);
}
return (ENOMEM);
return (0);
}
return (rc);
}
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);
}
/*
* We need to inform the caller whether or not we were
* able to add a node to the queue. If we are not, then
* it is up to the caller to go ahead and log the data.
*/
static int
{
goto cleanup;
/*
* We might not yet have the host...
*/
if (host) {
goto cleanup;
} else {
goto cleanup;
goto cleanup;
goto cleanup;
}
goto cleanup;
}
goto cleanup;
if (!error) {
goto cleanup;
}
/*
* Add to the tail of the logging queue.
*/
(void) mutex_lock(&logging_queue_lock);
if (logging_tail == NULL) {
} else {
logging_tail = lq;
}
(void) cond_signal(&logging_queue_cv);
(void) mutex_unlock(&logging_queue_lock);
return (TRUE);
return (FALSE);
}
/*
* Check mount requests, add to mounted list if ok
*/
static int
{
int flavor_list[MAX_FLAVORS];
int flavor_count;
int audit_status;
return (EACCES);
}
/*
* Put off getting the name for the client until we
* need it. This is a performance gain. If we are logging,
* then we don't care about performance and might as well
* get the host name now in case we need to spit out an
* error message.
*/
if (verbose) {
/*
* 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 (clnames)
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 we can not create a queue entry, go ahead and do it
* in the context of this thread.
*/
}
if (!error)
}
done:
if (sh)
return (error);
}
/*
* Determine whether two paths are within the same file system.
* Returns nonzero (true) if paths are the same, zero (false) if
* they are different. If an error occurs, return false.
*
* Use the actual FSID if it's available (via getattrat()); otherwise,
* fall back on st_dev.
*
* With ZFS snapshots, st_dev differs from the regular file system
* versus the snapshot. But the fsid is the same throughout. Thus
* the fsid is a better test.
*/
static int
{
/*
* We have found fsid's for both paths.
*/
return (B_TRUE);
return (B_FALSE);
}
/*
* We were unable to find fsid's for at least one of the paths.
* fall back on st_dev.
*/
return (B_FALSE);
}
return (B_FALSE);
}
return (B_TRUE);
return (B_FALSE);
}
share_t *
{
(void) rw_rdlock(&sharetab_lock);
if (*p1 == '\0')
goto done; /* exact match */
/*
* Now compare the pathnames for three cases:
*
*
*
*/
/*
* We have a subdirectory. Test whether the
* subdirectory is in the same file system.
*/
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 share_t *
{
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, then try netgroups.
*
* If pnb and pclnames are NULL, it means that we have to use transp
* to resolve client IP address to hostname. If they aren't NULL
* then transp argument won't be used and can be NULL.
*/
int
struct nd_hostservlist **pclnames,
char *access_list) /* N.B. we clobber this "input" parameter */
{
char addr[INET_ADDRSTRLEN];
char buff[256];
int nentries = 0;
char *cstr = access_list;
char *gr = access_list;
char *host;
int off;
int i;
int response;
int sbr = 0;
struct nd_hostservlist *clnames;
/* If no access list - then it's unrestricted */
return (1);
/* Get client address if it wasn't provided */
/* Don't grant access if client address isn't known */
return (0);
/* Try to lookup client hostname if it wasn't provided */
for (;;) {
switch (*cstr) {
case '[':
case ']':
cstr++;
continue;
case ':':
if (sbr) {
cstr++;
continue;
}
*cstr = '\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;
}
/*
* First check if we have '@' entry, as it doesn't
* require client hostname.
*/
if (*gr == '@') {
gr++;
/* Netname support */
INET_ADDRSTRLEN) == NULL)
break;
return (response);
}
} else {
return (response);
}
break;
continue;
}
/*
* No other checks can be performed if client address
* can't be resolved.
*/
break;
continue;
}
/* Otherwise loop through all client hostname aliases */
/*
* 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 {
/* Just do a hostname match */
return (response);
}
}
nentries++;
break;
}
return (0);
}
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
#define OPT_NONE 9
};
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 defaultaccess = 1;
return (0);
}
p = opts;
while (*p) {
case OPT_SECURE:
break;
case OPT_RO:
case OPT_RW:
defaultaccess = 0;
ok++;
break;
case OPT_NONE:
defaultaccess = 0;
}
}
/* none takes precedence over everything else */
if (reject)
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;
/* default access is rw */
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 */
break;
case OPT_RO:
case OPT_RW:
count = c;
} else {
}
break;
case OPT_NONE:
break;
}
}
if (reject)
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;
case OPT_NONE:
/*
* Check if the client should have no access
* to this share at all. This option behaves
* more like "root" than either "rw" or "ro".
*/
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;
case OPT_NONE:
/*
* Check if the client should have no access
* to this share at all. This option behaves
* more like "root" than either "rw" or "ro".
*/
perm |= NFSAUTH_DENIED;
break;
}
}
done:
/*
* If no match then set the perm accordingly
*/
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);
}
}