nfs_auth.c revision b00681c89badc9c4b5dff8af3603e008b0cedd98
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * CDDL HEADER START
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * The contents of this file are subject to the terms of the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Common Development and Distribution License (the "License").
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * You may not use this file except in compliance with the License.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * or http://www.opensolaris.org/os/licensing.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * See the License for the specific language governing permissions
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * and limitations under the License.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * CDDL HEADER END
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
2c30fa4582c5d6c659e059e719c5f6163f7ef1e3Garrett D'Amore * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void exi_cache_reclaim(void *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void exi_cache_trim(struct exportinfo *exi);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * The lifetime of an auth cache entry:
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * ------------------------------------
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * An auth cache entry is created with both the auth_time
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * and auth_freshness times set to the current time.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Upon every client access which results in a hit, the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * auth_time will be updated.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * If a client access determines that the auth_freshness
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * indicates that the entry is STALE, then it will be
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * refreshed. Note that this will explicitly reset
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * When the REFRESH successfully occurs, then the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * auth_freshness is updated.
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * There are two ways for an entry to leave the cache:
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * 1) Purged by an action on the export (remove or changed)
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * 2) Memory backpressure from the kernel (check against NFSAUTH_CACHE_TRIM)
a702341c8e6cc834a456108a8bf5d22f031da3beGarrett D'Amore * For 2) we check the timeout value against auth_time.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Number of seconds until we mark for refresh an auth cache entry.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Number of idle seconds until we yield to backpressure
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * to trim a cache entry.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * While we could encapuslate the exi_list inside the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * exi structure, we can't do that for the auth_list.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * So, to keep things looking clean, we keep them both
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * in these external lists.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Used to manipulate things on the refreshq_queue.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Note that the refresh thread will effectively
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * pop a node off of the queue, at which point it
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * will no longer need to hold the mutex.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * If there is ever a problem with loading the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * module, then nfsauth_fini() needs to be called
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * to remove state. In that event, since the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * refreshq thread has been started, they need to
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * work together to get rid of state.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorerefreshq_thread_state = REFRESHQ_THREAD_HALTED;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic void nfsauth_free_node(struct auth_cache *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorestatic int nfsauth_cache_compar(const void *, const void *);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * mountd is a server-side only daemon. This will need to be
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * revisited if the NFS server is ever made zones-aware.
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * mountd can be restarted by smf(5). We need to make sure
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * the updated door handle will safely make it to mountd_dh
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore mutex_init(&mountd_lock, NULL, MUTEX_DEFAULT, NULL);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore mutex_init(&refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore list_create(&refreshq_queue, sizeof (refreshq_exi_node_t),
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore cv_init(&refreshq_cv, NULL, CV_DEFAULT, NULL);
a19bb1fa9f3f280f4a5e9125331ffb7f0825e4faGarrett D'Amore * Allocate nfsauth cache handle
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore exi_cache_handle = kmem_cache_create("exi_cache_handle",
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore refreshq_thread_state = REFRESHQ_THREAD_RUNNING;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore (void) zthread_create(NULL, 0, nfsauth_refresh_thread,
53a539a76c92f82105be30bb8827f8db4b79c7b0Garrett D'Amore * Finalization routine for nfsauth. It is important to call this routine
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * before destroying the exported_lock.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Prevent the nfsauth_refresh_thread from getting new
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Also, wait for nfsauth_refresh_thread() to exit.
a19bb1fa9f3f280f4a5e9125331ffb7f0825e4faGarrett D'Amore while (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Walk the exi_list and in turn, walk the auth_lists and free all
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * lists. In addition, free INVALID auth_cache entries.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore while ((ren = list_remove_head(&refreshq_queue))) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore kmem_free(ran, sizeof (refreshq_auth_node_t));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore kmem_free(ren, sizeof (refreshq_exi_node_t));
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Deallocate nfsauth cache handle
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Convert the address in a netbuf to
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * a hash index for the auth_cache table.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore int i, h = 0;
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore for (i = 0; i < a->len; i++)
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * Mask out the components of an
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * address that do not identify
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * a host. For socket addresses the
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * masking gets rid of the port number.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amoreaddrmask(struct netbuf *addr, struct netbuf *mask)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * nfsauth4_access is used for NFS V4 auth checking. Besides doing
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * the common nfsauth_access(), it will check if the client can
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * have a limited access to this vnode even if the security flavor
f9ead4a57883f3ef04ef20d83cc47987d98c0687Garrett D'Amore * used does not meet the policy.
a19bb1fa9f3f280f4a5e9125331ffb7f0825e4faGarrett D'Amorenfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req,
a19bb1fa9f3f280f4a5e9125331ffb7f0825e4faGarrett D'Amore cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * There are cases that the server needs to allow the client
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * to have a limited view.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * /export/home is shared as "sec=sys,rw"
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * When the client mounts /export with sec=sys, the client
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * would get a limited view with RO access on /export to see
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * "home" only because the client is allowed to access
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * /export/home with auth_sys.
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Allow ro permission with LIMITED view if there is a
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * sub-dir exported under vp.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * msg is shown (at most) once per minute
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Callup to the mountd to get access information in the kernel.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amorenfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore struct netbuf *addr, int *access, uid_t clnt_uid, gid_t clnt_gid,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore uint_t clnt_gids_cnt, const gid_t *clnt_gids, uid_t *srv_uid,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids)
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * No entry in the cache for this client/flavor
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * so we need to call the nfsauth service in the
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * mount daemon.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore varg.arg_u.arg.areq.req_client.n_len = addr->len;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore varg.arg_u.arg.areq.req_client.n_bytes = addr->buf;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore varg.arg_u.arg.areq.req_clnt_uid = clnt_uid;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore varg.arg_u.arg.areq.req_clnt_gid = clnt_gid;
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore varg.arg_u.arg.areq.req_clnt_gids.len = clnt_gids_cnt;
682cb1044237d21ad6810702564bec833b8c410cGarrett D'Amore varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)clnt_gids;
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Setup the XDR stream for encoding the arguments. Notice that
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * in addition to the args having variable fields (req_netid and
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * req_path), the argument data structure is itself versioned,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * so we need to make sure we can size the arguments buffer
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * appropriately to encode all the args. If we can't get sizing
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * info _or_ properly encode the arguments, there's really no
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * point in continuting, so we fail the request.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Prepare the door arguments
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * We don't know the size of the message the daemon
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * will pass back to us. By setting rbuf to NULL,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * we force the door code to allocate a buf of the
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * appropriate size. We must set rsize > 0, however,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * else the door code acts as if no response was
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * expected and doesn't pass the data to us.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * The rendezvous point has not been established yet!
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * This could mean that either mountd(1m) has not yet
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * been started or that _this_ routine nuked the door
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * handle after receiving an EINTR for a REVOKED door.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Returning NFSAUTH_DROP will cause the NFS client
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * to retransmit the request, so let's try to be more
2c30fa4582c5d6c659e059e719c5f6163f7ef1e3Garrett D'Amore * rescillient and attempt for ntries before we bail.
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore sys_log("nfsauth: mountd has not established door");
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * Now that we've got what we need, place the call.
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore case 0: /* Success */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore if (da.data_ptr == NULL && da.data_size == 0) {
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * The door_return that contained the data
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * failed! We're here because of the 2nd
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * door_return (w/o data) such that we can
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * get control of the thread (and exit
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * gracefully).
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore * Server out of resources; back off for a bit
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* NOTREACHED */
2c30fa4582c5d6c659e059e719c5f6163f7ef1e3Garrett D'Amore * The server barfed and revoked
2c30fa4582c5d6c659e059e719c5f6163f7ef1e3Garrett D'Amore * the (existing) door on us; we
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * want to wait to give smf(5) a
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * chance to restart mountd(1m)
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * and establish a new door handle.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * If the door was _not_ revoked on us,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * then more than likely we took an INTR,
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * so we need to fail the operation.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * The only failure that can occur from getting
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * the door info is EINVAL, so we let the code
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore * below handle it.
88447a05f537aabe9a1bc3d5313f22581ec992a7Garrett D'Amore /* FALLTHROUGH */
if (!last) {
last++;
goto retry;
goto fail;
goto fail;
case NFSAUTH_DR_OKAY:
KM_SLEEP);
case NFSAUTH_DR_EFAIL:
case NFSAUTH_DR_DECERR:
case NFSAUTH_DR_BADCMD:
fail:
return (FALSE);
return (TRUE);
nfsauth_refresh_thread(void)
int access;
struct auth_cache *, p);
zthread_exit();
struct auth_cache_clnt *c;
int access;
if (c == NULL) {
goto retrieve;
goto retrieve;
sizeof (struct auth_cache),
if (c == NULL) {
c = nc;
if (p == NULL) {
goto retrieve;
if (p == NULL) {
p = np;
wait:
goto wait;
p = NULL;
goto retrieve;
if (p->auth_clnt_ngids != 0 &&
p->auth_clnt_ngids = 0;
p = NULL;
goto retrieve;
struct auth_cache *, p,
KM_SLEEP);
sizeof (refreshq_exi_node_t),
KM_SLEEP);
sizeof (refreshq_auth_node_t),
ran_node));
ran);
return (access);
return (access);
int access;
return (NFSAUTH_DENIED);
return (NFSAUTH_RO);
return (NFSAUTH_RW);
NULL);
return (access);
*ngids = 0;
authnone_entry = i;
mapaccess = 0;
return (NFSAUTH_DENIED);
i = authnone_entry;
*ngids = 0;
NULL);
kmem_free(p, sizeof (*p));
for (i = 0; i < AUTH_TABLESIZE; i++) {
for (i = 0; i < EXPTABLESIZE; i++) {
struct auth_cache_clnt *c;
struct auth_cache *p;
for (i = 0; i < AUTH_TABLESIZE; i++) {
p = next) {