Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb96d5c9 authored by Andy Adamson's avatar Andy Adamson Committed by Trond Myklebust
Browse files

SUNRPC handle EKEYEXPIRED in call_refreshresult



Currently, when an RPCSEC_GSS context has expired or is non-existent
and the users (Kerberos) credentials have also expired or are non-existent,
the client receives the -EKEYEXPIRED error and tries to refresh the context
forever.  If an application is performing I/O, or other work against the share,
the application hangs, and the user is not prompted to refresh/establish their
credentials. This can result in a denial of service for other users.

Users are expected to manage their Kerberos credential lifetimes to mitigate
this issue.

Move the -EKEYEXPIRED handling into the RPC layer. Try tk_cred_retry number
of times to refresh the gss_context, and then return -EACCES to the application.

Signed-off-by: default avatarAndy Adamson <andros@netapp.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 620038f6
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -24,14 +24,14 @@


#define NFSDBG_FACILITY		NFSDBG_PROC
#define NFSDBG_FACILITY		NFSDBG_PROC


/* A wrapper to handle the EJUKEBOX and EKEYEXPIRED error messages */
/* A wrapper to handle the EJUKEBOX error messages */
static int
static int
nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
{
{
	int res;
	int res;
	do {
	do {
		res = rpc_call_sync(clnt, msg, flags);
		res = rpc_call_sync(clnt, msg, flags);
		if (res != -EJUKEBOX && res != -EKEYEXPIRED)
		if (res != -EJUKEBOX)
			break;
			break;
		freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
		freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
		res = -ERESTARTSYS;
		res = -ERESTARTSYS;
@@ -44,7 +44,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
static int
static int
nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode)
nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode)
{
{
	if (task->tk_status != -EJUKEBOX && task->tk_status != -EKEYEXPIRED)
	if (task->tk_status != -EJUKEBOX)
		return 0;
		return 0;
	if (task->tk_status == -EJUKEBOX)
	if (task->tk_status == -EJUKEBOX)
		nfs_inc_stats(inode, NFSIOS_DELAY);
		nfs_inc_stats(inode, NFSIOS_DELAY);
+0 −1
Original line number Original line Diff line number Diff line
@@ -179,7 +179,6 @@ static int filelayout_async_handle_error(struct rpc_task *task,
		break;
		break;
	case -NFS4ERR_DELAY:
	case -NFS4ERR_DELAY:
	case -NFS4ERR_GRACE:
	case -NFS4ERR_GRACE:
	case -EKEYEXPIRED:
		rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX);
		rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX);
		break;
		break;
	case -NFS4ERR_RETRY_UNCACHED_REP:
	case -NFS4ERR_RETRY_UNCACHED_REP:
+0 −18
Original line number Original line Diff line number Diff line
@@ -333,7 +333,6 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc
			}
			}
		case -NFS4ERR_GRACE:
		case -NFS4ERR_GRACE:
		case -NFS4ERR_DELAY:
		case -NFS4ERR_DELAY:
		case -EKEYEXPIRED:
			ret = nfs4_delay(server->client, &exception->timeout);
			ret = nfs4_delay(server->client, &exception->timeout);
			if (ret != 0)
			if (ret != 0)
				break;
				break;
@@ -1343,13 +1342,6 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
				nfs_inode_find_state_and_recover(state->inode,
				nfs_inode_find_state_and_recover(state->inode,
						stateid);
						stateid);
				nfs4_schedule_stateid_recovery(server, state);
				nfs4_schedule_stateid_recovery(server, state);
			case -EKEYEXPIRED:
				/*
				 * User RPCSEC_GSS context has expired.
				 * We cannot recover this stateid now, so
				 * skip it and allow recovery thread to
				 * proceed.
				 */
			case -ENOMEM:
			case -ENOMEM:
				err = 0;
				err = 0;
				goto out;
				goto out;
@@ -3946,7 +3938,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server,
		case -NFS4ERR_DELAY:
		case -NFS4ERR_DELAY:
			nfs_inc_server_stats(server, NFSIOS_DELAY);
			nfs_inc_server_stats(server, NFSIOS_DELAY);
		case -NFS4ERR_GRACE:
		case -NFS4ERR_GRACE:
		case -EKEYEXPIRED:
			rpc_delay(task, NFS4_POLL_RETRY_MAX);
			rpc_delay(task, NFS4_POLL_RETRY_MAX);
			task->tk_status = 0;
			task->tk_status = 0;
			return -EAGAIN;
			return -EAGAIN;
@@ -4946,15 +4937,6 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl)
				nfs4_schedule_stateid_recovery(server, state);
				nfs4_schedule_stateid_recovery(server, state);
				err = 0;
				err = 0;
				goto out;
				goto out;
			case -EKEYEXPIRED:
				/*
				 * User RPCSEC_GSS context has expired.
				 * We cannot recover this stateid now, so
				 * skip it and allow recovery thread to
				 * proceed.
				 */
				err = 0;
				goto out;
			case -ENOMEM:
			case -ENOMEM:
			case -NFS4ERR_DENIED:
			case -NFS4ERR_DENIED:
				/* kill_proc(fl->fl_pid, SIGLOST, 1); */
				/* kill_proc(fl->fl_pid, SIGLOST, 1); */
+0 −23
Original line number Original line Diff line number Diff line
@@ -1437,14 +1437,6 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs
				/* Mark the file as being 'closed' */
				/* Mark the file as being 'closed' */
				state->state = 0;
				state->state = 0;
				break;
				break;
			case -EKEYEXPIRED:
				/*
				 * User RPCSEC_GSS context has expired.
				 * We cannot recover this stateid now, so
				 * skip it and allow recovery thread to
				 * proceed.
				 */
				break;
			case -NFS4ERR_ADMIN_REVOKED:
			case -NFS4ERR_ADMIN_REVOKED:
			case -NFS4ERR_STALE_STATEID:
			case -NFS4ERR_STALE_STATEID:
			case -NFS4ERR_BAD_STATEID:
			case -NFS4ERR_BAD_STATEID:
@@ -1597,14 +1589,6 @@ static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
	nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
	nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
}
}


static void nfs4_warn_keyexpired(const char *s)
{
	printk_ratelimited(KERN_WARNING "Error: state manager"
			" encountered RPCSEC_GSS session"
			" expired against NFSv4 server %s.\n",
			s);
}

static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
{
{
	switch (error) {
	switch (error) {
@@ -1638,10 +1622,6 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error)
		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
		case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION:
			set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
			set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state);
			break;
			break;
		case -EKEYEXPIRED:
			/* Nothing we can do */
			nfs4_warn_keyexpired(clp->cl_hostname);
			break;
		default:
		default:
			dprintk("%s: failed to handle error %d for server %s\n",
			dprintk("%s: failed to handle error %d for server %s\n",
					__func__, error, clp->cl_hostname);
					__func__, error, clp->cl_hostname);
@@ -1758,8 +1738,6 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status)
		dprintk("%s: exit with error %d for server %s\n",
		dprintk("%s: exit with error %d for server %s\n",
				__func__, -EPROTONOSUPPORT, clp->cl_hostname);
				__func__, -EPROTONOSUPPORT, clp->cl_hostname);
		return -EPROTONOSUPPORT;
		return -EPROTONOSUPPORT;
	case -EKEYEXPIRED:
		nfs4_warn_keyexpired(clp->cl_hostname);
	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
				 * in nfs4_exchange_id */
				 * in nfs4_exchange_id */
	default:
	default:
@@ -1912,7 +1890,6 @@ int nfs4_discover_server_trunking(struct nfs_client *clp,
		break;
		break;


	case -EKEYEXPIRED:
	case -EKEYEXPIRED:
		nfs4_warn_keyexpired(clp->cl_hostname);
	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
	case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
				 * in nfs4_exchange_id */
				 * in nfs4_exchange_id */
		status = -EKEYEXPIRED;
		status = -EKEYEXPIRED;
+0 −43
Original line number Original line Diff line number Diff line
@@ -46,39 +46,6 @@


#define NFSDBG_FACILITY		NFSDBG_PROC
#define NFSDBG_FACILITY		NFSDBG_PROC


/*
 * wrapper to handle the -EKEYEXPIRED error message. This should generally
 * only happen if using krb5 auth and a user's TGT expires. NFSv2 doesn't
 * support the NFSERR_JUKEBOX error code, but we handle this situation in the
 * same way that we handle that error with NFSv3.
 */
static int
nfs_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags)
{
	int res;
	do {
		res = rpc_call_sync(clnt, msg, flags);
		if (res != -EKEYEXPIRED)
			break;
		freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME);
		res = -ERESTARTSYS;
	} while (!fatal_signal_pending(current));
	return res;
}

#define rpc_call_sync(clnt, msg, flags)	nfs_rpc_wrapper(clnt, msg, flags)

static int
nfs_async_handle_expired_key(struct rpc_task *task)
{
	if (task->tk_status != -EKEYEXPIRED)
		return 0;
	task->tk_status = 0;
	rpc_restart_call(task);
	rpc_delay(task, NFS_JUKEBOX_RETRY_TIME);
	return 1;
}

/*
/*
 * Bare-bones access to getattr: this is for nfs_read_super.
 * Bare-bones access to getattr: this is for nfs_read_super.
 */
 */
@@ -364,8 +331,6 @@ static void nfs_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlink


static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir)
{
{
	if (nfs_async_handle_expired_key(task))
		return 0;
	nfs_mark_for_revalidate(dir);
	nfs_mark_for_revalidate(dir);
	return 1;
	return 1;
}
}
@@ -385,8 +350,6 @@ static int
nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir,
		     struct inode *new_dir)
		     struct inode *new_dir)
{
{
	if (nfs_async_handle_expired_key(task))
		return 0;
	nfs_mark_for_revalidate(old_dir);
	nfs_mark_for_revalidate(old_dir);
	nfs_mark_for_revalidate(new_dir);
	nfs_mark_for_revalidate(new_dir);
	return 1;
	return 1;
@@ -642,9 +605,6 @@ static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data)
{
{
	struct inode *inode = data->header->inode;
	struct inode *inode = data->header->inode;


	if (nfs_async_handle_expired_key(task))
		return -EAGAIN;

	nfs_invalidate_atime(inode);
	nfs_invalidate_atime(inode);
	if (task->tk_status >= 0) {
	if (task->tk_status >= 0) {
		nfs_refresh_inode(inode, data->res.fattr);
		nfs_refresh_inode(inode, data->res.fattr);
@@ -671,9 +631,6 @@ static int nfs_write_done(struct rpc_task *task, struct nfs_write_data *data)
{
{
	struct inode *inode = data->header->inode;
	struct inode *inode = data->header->inode;


	if (nfs_async_handle_expired_key(task))
		return -EAGAIN;

	if (task->tk_status >= 0)
	if (task->tk_status >= 0)
		nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
		nfs_post_op_update_inode_force_wcc(inode, data->res.fattr);
	return 0;
	return 0;
Loading