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

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
...@@ -24,14 +24,14 @@ ...@@ -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) ...@@ -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);
......
...@@ -179,7 +179,6 @@ static int filelayout_async_handle_error(struct rpc_task *task, ...@@ -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:
......
...@@ -333,7 +333,6 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc ...@@ -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 ...@@ -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, ...@@ -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) ...@@ -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); */
......
...@@ -1437,14 +1437,6 @@ static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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;
......
...@@ -46,39 +46,6 @@ ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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;
......
...@@ -1381,6 +1381,7 @@ call_refreshresult(struct rpc_task *task) ...@@ -1381,6 +1381,7 @@ call_refreshresult(struct rpc_task *task)
return; return;
case -ETIMEDOUT: case -ETIMEDOUT:
rpc_delay(task, 3*HZ); rpc_delay(task, 3*HZ);
case -EKEYEXPIRED:
case -EAGAIN: case -EAGAIN:
status = -EACCES; status = -EACCES;
if (!task->tk_cred_retry) if (!task->tk_cred_retry)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment