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: Andy Adamson <andros@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
This commit is contained in:
parent
620038f6d2
commit
eb96d5c97b
@ -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);
|
||||||
|
@ -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
|
|||||||
}
|
}
|
||||||
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); */
|
||||||
|
@ -1437,14 +1437,6 @@ restart:
|
|||||||
/* 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 @@ again:
|
|||||||
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 @@
|
|||||||
|
|
||||||
#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;
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user