Exemple #1
0
static int
klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
{
	door_arg_t da;
	int res;
	int dres;
	klpd_head_t *klh;

	if (p->klpd_door_pid == curproc->p_pid)
		return (-1);

	klh = klpd_marshall(p, req, ap);

	if (klh == NULL)
		return (-1);

	da.data_ptr = (char *)klh;
	da.data_size = klh->klh_len;
	da.desc_ptr = NULL;
	da.desc_num = 0;
	da.rbuf = (char *)&res;
	da.rsize = sizeof (res);

	while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
	    SIZE_MAX, 0)) != 0) {
		switch (dres) {
		case EAGAIN:
			delay(1);
			continue;
		case EINVAL:
		case EBADF:
			/* Bad door, don't call it again. */
			(void) klpd_unreg_dh(p->klpd_door);
			/* FALLTHROUGH */
		case EINTR:
			/* Pending signal, nothing we can do. */
			/* FALLTHROUGH */
		default:
			kmem_free(klh, klh->klh_len);
			return (-1);
		}
	}
	kmem_free(klh, klh->klh_len);
	/* Bogus return value, must be a failure */
	if (da.rbuf != (char *)&res) {
		kmem_free(da.rbuf, da.rsize);
		return (-1);
	}
	return (res);
}
/*
 * log_event_upcall - Perform the upcall to syseventd for event buffer delivery.
 * 			Check for rebinding errors
 * 			This buffer is reused to by the syseventd door_return
 *			to hold the result code
 */
static int
log_event_upcall(log_event_upcall_arg_t *arg)
{
	int error;
	size_t size;
	sysevent_t *ev;
	door_arg_t darg, save_arg;
	int retry;
	int neagain = 0;
	int neintr = 0;
	int nticks = LOG_EVENT_MIN_PAUSE;

	/* Initialize door args */
	ev = (sysevent_t *)&arg->buf;
	size = sizeof (log_event_upcall_arg_t) + SE_PAYLOAD_SZ(ev);

	darg.rbuf = (char *)arg;
	darg.data_ptr = (char *)arg;
	darg.rsize = size;
	darg.data_size = size;
	darg.desc_ptr = NULL;
	darg.desc_num = 0;

	if ((event_door == NULL) &&
	    ((error = log_event_upcall_lookup()) != 0)) {
		LOG_DEBUG((CE_CONT,
		    "log_event_upcall: event_door error (%d)\n", error));

		return (error);
	}

	LOG_DEBUG1((CE_CONT, "log_event_upcall: 0x%llx\n",
	    (longlong_t)SE_SEQ((sysevent_t *)&arg->buf)));

	save_arg = darg;
	for (retry = 0; ; retry++) {
		if ((error = door_ki_upcall_limited(event_door, &darg, NULL,
		    SIZE_MAX, 0)) == 0) {
			break;
		}
		switch (error) {
		case EINTR:
			neintr++;
			log_event_pause(2);
			darg = save_arg;
			break;
		case EAGAIN:
			/* cannot deliver upcall - process may be forking */
			neagain++;
			log_event_pause(nticks);
			nticks <<= 1;
			if (nticks > LOG_EVENT_MAX_PAUSE)
				nticks = LOG_EVENT_MAX_PAUSE;
			darg = save_arg;
			break;
		case EBADF:
			LOG_DEBUG((CE_CONT, "log_event_upcall: rebinding\n"));
			/* Server may have died. Try rebinding */
			if ((error = log_event_upcall_lookup()) != 0) {
				LOG_DEBUG((CE_CONT,
				    "log_event_upcall: lookup error %d\n",
				    error));
				return (EBADF);
			}
			if (retry > 4) {
				LOG_DEBUG((CE_CONT,
				    "log_event_upcall: ebadf\n"));
				return (EBADF);
			}
			LOG_DEBUG((CE_CONT, "log_event_upcall: "
			    "retrying upcall after lookup\n"));
			darg = save_arg;
			break;
		default:
			cmn_err(CE_CONT,
			    "log_event_upcall: door_ki_upcall error %d\n",
			    error);
			return (error);
		}
	}

	if (neagain > 0 || neintr > 0) {
		LOG_DEBUG((CE_CONT, "upcall: eagain=%d eintr=%d nticks=%d\n",
		    neagain, neintr, nticks));
	}

	LOG_DEBUG1((CE_CONT, "log_event_upcall:\n\t"
	    "error=%d rptr1=%p rptr2=%p dptr2=%p ret1=%x ret2=%x\n",
	    error, (void *)arg, (void *)darg.rbuf,
	    (void *)darg.data_ptr,
	    *((int *)(darg.rbuf)), *((int *)(darg.data_ptr))));

	if (!error) {
		/*
		 * upcall was successfully executed. Check return code.
		 */
		error = *((int *)(darg.rbuf));
	}

	return (error);
}
/*
 * Get the access information from the cache or callup to the mountd
 * to get and cache the access information in the kernel.
 */
int
nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor)
{
	struct netbuf		  addr;
	struct netbuf		 *claddr;
	struct auth_cache	**head;
	struct auth_cache	 *ap;
	int			  access;
	varg_t			  varg = {0};
	nfsauth_res_t		  res = {0};
	XDR			  xdrs_a;
	XDR			  xdrs_r;
	size_t			  absz;
	caddr_t			  abuf;
	size_t			  rbsz = (size_t)(BYTES_PER_XDR_UNIT * 2);
	char			  result[BYTES_PER_XDR_UNIT * 2] = {0};
	caddr_t			  rbuf = (caddr_t)&result;
	int			  last = 0;
	door_arg_t		  da;
	door_info_t		  di;
	door_handle_t		  dh;
	uint_t			  ntries = 0;

	/*
	 * Now check whether this client already
	 * has an entry for this flavor in the cache
	 * for this export.
	 * Get the caller's address, mask off the
	 * parts of the address that do not identify
	 * the host (port number, etc), and then hash
	 * it to find the chain of cache entries.
	 */

	claddr = svc_getrpccaller(req->rq_xprt);
	addr = *claddr;
	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
	bcopy(claddr->buf, addr.buf, claddr->len);
	addrmask(&addr, svc_getaddrmask(req->rq_xprt));
	head = &exi->exi_cache[hash(&addr)];

	rw_enter(&exi->exi_cache_lock, RW_READER);
	for (ap = *head; ap; ap = ap->auth_next) {
		if (EQADDR(&addr, &ap->auth_addr) && flavor == ap->auth_flavor)
			break;
	}
	if (ap) {				/* cache hit */
		access = ap->auth_access;
		ap->auth_time = gethrestime_sec();
		nfsauth_cache_hit++;
	}

	rw_exit(&exi->exi_cache_lock);

	if (ap) {
		kmem_free(addr.buf, addr.len);
		return (access);
	}

	nfsauth_cache_miss++;

	/*
	 * No entry in the cache for this client/flavor
	 * so we need to call the nfsauth service in the
	 * mount daemon.
	 */
retry:
	mutex_enter(&mountd_lock);
	dh = mountd_dh;
	if (dh)
		door_ki_hold(dh);
	mutex_exit(&mountd_lock);

	if (dh == NULL) {
		/*
		 * The rendezvous point has not been established yet !
		 * This could mean that either mountd(1m) has not yet
		 * been started or that _this_ routine nuked the door
		 * handle after receiving an EINTR for a REVOKED door.
		 *
		 * Returning NFSAUTH_DROP will cause the NFS client
		 * to retransmit the request, so let's try to be more
		 * rescillient and attempt for ntries before we bail.
		 */
		if (++ntries % NFSAUTH_DR_TRYCNT) {
			delay(hz);
			goto retry;
		}
		sys_log("nfsauth: mountd has not established door");
		kmem_free(addr.buf, addr.len);
		return (NFSAUTH_DROP);
	}
	ntries = 0;
	varg.vers = V_PROTO;
	varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
	varg.arg_u.arg.areq.req_client.n_len = addr.len;
	varg.arg_u.arg.areq.req_client.n_bytes = addr.buf;
	varg.arg_u.arg.areq.req_netid = svc_getnetid(req->rq_xprt);
	varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
	varg.arg_u.arg.areq.req_flavor = flavor;

	/*
	 * Setup the XDR stream for encoding the arguments. Notice that
	 * in addition to the args having variable fields (req_netid and
	 * req_path), the argument data structure is itself versioned,
	 * so we need to make sure we can size the arguments buffer
	 * appropriately to encode all the args. If we can't get sizing
	 * info _or_ properly encode the arguments, there's really no
	 * point in continuting, so we fail the request.
	 */
	DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
	if ((absz = xdr_sizeof(xdr_varg, (void *)&varg)) == 0) {
		door_ki_rele(dh);
		kmem_free(addr.buf, addr.len);
		return (NFSAUTH_DENIED);
	}
	abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
	xdrmem_create(&xdrs_a, abuf, absz, XDR_ENCODE);
	if (!xdr_varg(&xdrs_a, &varg)) {
		door_ki_rele(dh);
		goto fail;
	}
	XDR_DESTROY(&xdrs_a);

	/*
	 * The result (nfsauth_res_t) is always two int's, so we don't
	 * have to dynamically size (or allocate) the results buffer.
	 * Now that we've got what we need, we prep the door arguments
	 * and place the call.
	 */
	da.data_ptr = (char *)abuf;
	da.data_size = absz;
	da.desc_ptr = NULL;
	da.desc_num = 0;
	da.rbuf = (char *)rbuf;
	da.rsize = rbsz;

	switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
		case 0:				/* Success */
			if (da.data_ptr != da.rbuf && da.data_size == 0) {
				/*
				 * The door_return that contained the data
				 * failed ! We're here because of the 2nd
				 * door_return (w/o data) such that we can
				 * get control of the thread (and exit
				 * gracefully).
				 */
				DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
				    door_arg_t *, &da);
				door_ki_rele(dh);
				goto fail;

			} else if (rbuf != da.rbuf) {
Exemple #4
0
/*
 * This is a special interface that will be utilized by ZFS to cause
 * a share to be added/removed
 *
 * arg is either a smb_share_t or share_name from userspace.
 * It will need to be copied into the kernel.   It is smb_share_t
 * for add operations and share_name for delete operations.
 */
int
smb_kshare_upcall(door_handle_t dhdl, void *arg, boolean_t add_share)
{
	door_arg_t	doorarg = { 0 };
	char		*buf = NULL;
	char		*str = NULL;
	int		error;
	int		rc;
	unsigned int	used;
	smb_dr_ctx_t	*dec_ctx;
	smb_dr_ctx_t	*enc_ctx;
	smb_share_t	*lmshare = NULL;
	int		opcode;

	opcode = (add_share) ? SMB_SHROP_ADD : SMB_SHROP_DELETE;

	buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
	enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
	smb_dr_put_uint32(enc_ctx, opcode);

	switch (opcode) {
	case SMB_SHROP_ADD:
		lmshare = kmem_alloc(sizeof (smb_share_t), KM_SLEEP);
		error = xcopyin(arg, lmshare, sizeof (smb_share_t));
		if (error != 0) {
			kmem_free(lmshare, sizeof (smb_share_t));
			kmem_free(buf, SMB_SHARE_DSIZE);
			return (error);
		}
		smb_dr_put_share(enc_ctx, lmshare);
		break;

	case SMB_SHROP_DELETE:
		str = kmem_alloc(MAXPATHLEN, KM_SLEEP);
		error = copyinstr(arg, str, MAXPATHLEN, NULL);
		if (error != 0) {
			kmem_free(str, MAXPATHLEN);
			kmem_free(buf, SMB_SHARE_DSIZE);
			return (error);
		}
		smb_dr_put_string(enc_ctx, str);
		kmem_free(str, MAXPATHLEN);
		break;
	}

	if ((error = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
		kmem_free(buf, SMB_SHARE_DSIZE);
		if (lmshare)
			kmem_free(lmshare, sizeof (smb_share_t));
		return (NERR_InternalError);
	}

	doorarg.data_ptr = buf;
	doorarg.data_size = used;
	doorarg.rbuf = buf;
	doorarg.rsize = SMB_SHARE_DSIZE;

	error = door_ki_upcall_limited(dhdl, &doorarg, NULL, SIZE_MAX, 0);

	if (error) {
		kmem_free(buf, SMB_SHARE_DSIZE);
		if (lmshare)
			kmem_free(lmshare, sizeof (smb_share_t));
		return (error);
	}

	dec_ctx = smb_dr_decode_start(doorarg.data_ptr, doorarg.data_size);
	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
		kmem_free(buf, SMB_SHARE_DSIZE);
		if (lmshare)
			kmem_free(lmshare, sizeof (smb_share_t));
		return (NERR_InternalError);
	}

	rc = smb_dr_get_uint32(dec_ctx);
	if (opcode == SMB_SHROP_ADD)
		smb_dr_get_share(dec_ctx, lmshare);

	if (smb_dr_decode_finish(dec_ctx))
		rc = NERR_InternalError;

	kmem_free(buf, SMB_SHARE_DSIZE);
	if (lmshare)
		kmem_free(lmshare, sizeof (smb_share_t));

	return ((rc == NERR_DuplicateShare && add_share) ? 0 : rc);
}
Exemple #5
0
/*
 * Callup to the mountd to get access information in the kernel.
 */
static bool_t
nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor,
    struct netbuf *addr, int *access, uid_t clnt_uid, gid_t clnt_gid,
    uint_t clnt_gids_cnt, const gid_t *clnt_gids, uid_t *srv_uid,
    gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids)
{
	varg_t			  varg = {0};
	nfsauth_res_t		  res = {0};
	XDR			  xdrs;
	size_t			  absz;
	caddr_t			  abuf;
	int			  last = 0;
	door_arg_t		  da;
	door_info_t		  di;
	door_handle_t		  dh;
	uint_t			  ntries = 0;

	/*
	 * No entry in the cache for this client/flavor
	 * so we need to call the nfsauth service in the
	 * mount daemon.
	 */

	varg.vers = V_PROTO;
	varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
	varg.arg_u.arg.areq.req_client.n_len = addr->len;
	varg.arg_u.arg.areq.req_client.n_bytes = addr->buf;
	varg.arg_u.arg.areq.req_netid = req_netid;
	varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
	varg.arg_u.arg.areq.req_flavor = flavor;
	varg.arg_u.arg.areq.req_clnt_uid = clnt_uid;
	varg.arg_u.arg.areq.req_clnt_gid = clnt_gid;
	varg.arg_u.arg.areq.req_clnt_gids.len = clnt_gids_cnt;
	varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)clnt_gids;

	DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);

	/*
	 * Setup the XDR stream for encoding the arguments. Notice that
	 * in addition to the args having variable fields (req_netid and
	 * req_path), the argument data structure is itself versioned,
	 * so we need to make sure we can size the arguments buffer
	 * appropriately to encode all the args. If we can't get sizing
	 * info _or_ properly encode the arguments, there's really no
	 * point in continuting, so we fail the request.
	 */
	if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
		*access = NFSAUTH_DENIED;
		return (FALSE);
	}

	abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
	xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
	if (!xdr_varg(&xdrs, &varg)) {
		XDR_DESTROY(&xdrs);
		goto fail;
	}
	XDR_DESTROY(&xdrs);

	/*
	 * Prepare the door arguments
	 *
	 * We don't know the size of the message the daemon
	 * will pass back to us.  By setting rbuf to NULL,
	 * we force the door code to allocate a buf of the
	 * appropriate size.  We must set rsize > 0, however,
	 * else the door code acts as if no response was
	 * expected and doesn't pass the data to us.
	 */
	da.data_ptr = (char *)abuf;
	da.data_size = absz;
	da.desc_ptr = NULL;
	da.desc_num = 0;
	da.rbuf = NULL;
	da.rsize = 1;

retry:
	mutex_enter(&mountd_lock);
	dh = mountd_dh;
	if (dh != NULL)
		door_ki_hold(dh);
	mutex_exit(&mountd_lock);

	if (dh == NULL) {
		/*
		 * The rendezvous point has not been established yet!
		 * This could mean that either mountd(1m) has not yet
		 * been started or that _this_ routine nuked the door
		 * handle after receiving an EINTR for a REVOKED door.
		 *
		 * Returning NFSAUTH_DROP will cause the NFS client
		 * to retransmit the request, so let's try to be more
		 * rescillient and attempt for ntries before we bail.
		 */
		if (++ntries % NFSAUTH_DR_TRYCNT) {
			delay(hz);
			goto retry;
		}

		kmem_free(abuf, absz);

		sys_log("nfsauth: mountd has not established door");
		*access = NFSAUTH_DROP;
		return (FALSE);
	}

	ntries = 0;

	/*
	 * Now that we've got what we need, place the call.
	 */
	switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
	case 0:				/* Success */
		door_ki_rele(dh);

		if (da.data_ptr == NULL && da.data_size == 0) {
			/*
			 * The door_return that contained the data
			 * failed! We're here because of the 2nd
			 * door_return (w/o data) such that we can
			 * get control of the thread (and exit
			 * gracefully).
			 */
			DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
			    door_arg_t *, &da);
			goto fail;
		}

		break;

	case EAGAIN:
		/*
		 * Server out of resources; back off for a bit
		 */
		door_ki_rele(dh);
		delay(hz);
		goto retry;
		/* NOTREACHED */

	case EINTR:
		if (!door_ki_info(dh, &di)) {
			door_ki_rele(dh);

			if (di.di_attributes & DOOR_REVOKED) {
				/*
				 * The server barfed and revoked
				 * the (existing) door on us; we
				 * want to wait to give smf(5) a
				 * chance to restart mountd(1m)
				 * and establish a new door handle.
				 */
				mutex_enter(&mountd_lock);
				if (dh == mountd_dh) {
					door_ki_rele(mountd_dh);
					mountd_dh = NULL;
				}
				mutex_exit(&mountd_lock);
				delay(hz);
				goto retry;
			}
			/*
			 * If the door was _not_ revoked on us,
			 * then more than likely we took an INTR,
			 * so we need to fail the operation.
			 */
			goto fail;
		}
		/*
		 * The only failure that can occur from getting
		 * the door info is EINVAL, so we let the code
		 * below handle it.
		 */
		/* FALLTHROUGH */

	case EBADF:
	case EINVAL:
	default:
		/*
		 * If we have a stale door handle, give smf a last
		 * chance to start it by sleeping for a little bit.
		 * If we're still hosed, we'll fail the call.
		 *
		 * Since we're going to reacquire the door handle
		 * upon the retry, we opt to sleep for a bit and
		 * _not_ to clear mountd_dh. If mountd restarted
		 * and was able to set mountd_dh, we should see
		 * the new instance; if not, we won't get caught
		 * up in the retry/DELAY loop.
		 */
		door_ki_rele(dh);
		if (!last) {
			delay(hz);
			last++;
			goto retry;
		}
		sys_log("nfsauth: stale mountd door handle");
		goto fail;
	}
Exemple #6
0
/*
 * log_event_upcall - Perform the upcall to syseventd for event buffer delivery.
 * 			Check for rebinding errors
 * 			This buffer is reused to by the syseventd door_return
 *			to hold the result code
 */
static int
log_event_upcall(log_event_upcall_arg_t *arg)
{
	int error;
	size_t size;
	sysevent_t *ev;
	door_arg_t darg, save_arg;
	int retry;
	int neagain = 0;
	int neintr = 0;
	int nticks = LOG_EVENT_MIN_PAUSE;

	/* Initialize door args */
	ev = (sysevent_t *)&arg->buf;
	size = sizeof (log_event_upcall_arg_t) + SE_PAYLOAD_SZ(ev);

	darg.rbuf = (char *)arg;
	darg.data_ptr = (char *)arg;
	darg.rsize = size;
	darg.data_size = size;
	darg.desc_ptr = NULL;
	darg.desc_num = 0;

	LOG_DEBUG1((CE_CONT, "log_event_upcall: 0x%llx\n",
	    (longlong_t)SE_SEQ((sysevent_t *)&arg->buf)));

	save_arg = darg;
	for (retry = 0; ; retry++) {

		mutex_enter(&event_door_mutex);
		if (event_door == NULL) {
			mutex_exit(&event_door_mutex);

			return (EBADF);
		}

		if ((error = door_ki_upcall_limited(event_door, &darg, NULL,
		    SIZE_MAX, 0)) == 0) {
			mutex_exit(&event_door_mutex);
			break;
		}

		/*
		 * EBADF is handled outside the switch below because we need to
		 * hold event_door_mutex a bit longer
		 */
		if (error == EBADF) {
			/* Server died */
			door_ki_rele(event_door);
			event_door = NULL;

			mutex_exit(&event_door_mutex);
			return (error);
		}

		mutex_exit(&event_door_mutex);

		/*
		 * The EBADF case is already handled above with event_door_mutex
		 * held
		 */
		switch (error) {
		case EINTR:
			neintr++;
			log_event_pause(2);
			darg = save_arg;
			break;
		case EAGAIN:
			/* cannot deliver upcall - process may be forking */
			neagain++;
			log_event_pause(nticks);
			nticks <<= 1;
			if (nticks > LOG_EVENT_MAX_PAUSE)
				nticks = LOG_EVENT_MAX_PAUSE;
			darg = save_arg;
			break;
		default:
			cmn_err(CE_CONT,
			    "log_event_upcall: door_ki_upcall error %d\n",
			    error);
			return (error);
		}
	}

	if (neagain > 0 || neintr > 0) {
		LOG_DEBUG((CE_CONT, "upcall: eagain=%d eintr=%d nticks=%d\n",
		    neagain, neintr, nticks));
	}

	LOG_DEBUG1((CE_CONT, "log_event_upcall:\n\t"
	    "error=%d rptr1=%p rptr2=%p dptr2=%p ret1=%x ret2=%x\n",
	    error, (void *)arg, (void *)darg.rbuf,
	    (void *)darg.data_ptr,
	    *((int *)(darg.rbuf)), *((int *)(darg.data_ptr))));

	if (!error) {
		/*
		 * upcall was successfully executed. Check return code.
		 */
		error = *((int *)(darg.rbuf));
	}

	return (error);
}