diff options
| author | Andy Adamson <andros@netapp.com> | 2012-11-27 10:34:19 -0500 |
|---|---|---|
| committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-12-12 15:36:02 -0500 |
| commit | eb96d5c97b0825d542e9c4ba5e0a22b519355166 (patch) | |
| tree | 62c98e2bdbcc7334a7043725d1fd81a589a75177 /fs/nfs | |
| parent | 620038f6d2304475dce800dc5c75fc335a19613a (diff) | |
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>
Diffstat (limited to 'fs/nfs')
| -rw-r--r-- | fs/nfs/nfs3proc.c | 6 | ||||
| -rw-r--r-- | fs/nfs/nfs4filelayout.c | 1 | ||||
| -rw-r--r-- | fs/nfs/nfs4proc.c | 18 | ||||
| -rw-r--r-- | fs/nfs/nfs4state.c | 23 | ||||
| -rw-r--r-- | fs/nfs/proc.c | 43 |
5 files changed, 3 insertions, 88 deletions
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index 69322096c325..70efb63b1e42 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
| @@ -24,14 +24,14 @@ | |||
| 24 | 24 | ||
| 25 | #define NFSDBG_FACILITY NFSDBG_PROC | 25 | #define NFSDBG_FACILITY NFSDBG_PROC |
| 26 | 26 | ||
| 27 | /* A wrapper to handle the EJUKEBOX and EKEYEXPIRED error messages */ | 27 | /* A wrapper to handle the EJUKEBOX error messages */ |
| 28 | static int | 28 | static int |
| 29 | nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) | 29 | nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) |
| 30 | { | 30 | { |
| 31 | int res; | 31 | int res; |
| 32 | do { | 32 | do { |
| 33 | res = rpc_call_sync(clnt, msg, flags); | 33 | res = rpc_call_sync(clnt, msg, flags); |
| 34 | if (res != -EJUKEBOX && res != -EKEYEXPIRED) | 34 | if (res != -EJUKEBOX) |
| 35 | break; | 35 | break; |
| 36 | freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); | 36 | freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); |
| 37 | res = -ERESTARTSYS; | 37 | res = -ERESTARTSYS; |
| @@ -44,7 +44,7 @@ nfs3_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) | |||
| 44 | static int | 44 | static int |
| 45 | nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode) | 45 | nfs3_async_handle_jukebox(struct rpc_task *task, struct inode *inode) |
| 46 | { | 46 | { |
| 47 | if (task->tk_status != -EJUKEBOX && task->tk_status != -EKEYEXPIRED) | 47 | if (task->tk_status != -EJUKEBOX) |
| 48 | return 0; | 48 | return 0; |
| 49 | if (task->tk_status == -EJUKEBOX) | 49 | if (task->tk_status == -EJUKEBOX) |
| 50 | nfs_inc_stats(inode, NFSIOS_DELAY); | 50 | nfs_inc_stats(inode, NFSIOS_DELAY); |
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index 1e42413fab8f..194c48410336 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c | |||
| @@ -179,7 +179,6 @@ static int filelayout_async_handle_error(struct rpc_task *task, | |||
| 179 | break; | 179 | break; |
| 180 | case -NFS4ERR_DELAY: | 180 | case -NFS4ERR_DELAY: |
| 181 | case -NFS4ERR_GRACE: | 181 | case -NFS4ERR_GRACE: |
| 182 | case -EKEYEXPIRED: | ||
| 183 | rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX); | 182 | rpc_delay(task, FILELAYOUT_POLL_RETRY_MAX); |
| 184 | break; | 183 | break; |
| 185 | case -NFS4ERR_RETRY_UNCACHED_REP: | 184 | case -NFS4ERR_RETRY_UNCACHED_REP: |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a4692e97bc19..b0963aeceeda 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
| @@ -333,7 +333,6 @@ static int nfs4_handle_exception(struct nfs_server *server, int errorcode, struc | |||
| 333 | } | 333 | } |
| 334 | case -NFS4ERR_GRACE: | 334 | case -NFS4ERR_GRACE: |
| 335 | case -NFS4ERR_DELAY: | 335 | case -NFS4ERR_DELAY: |
| 336 | case -EKEYEXPIRED: | ||
| 337 | ret = nfs4_delay(server->client, &exception->timeout); | 336 | ret = nfs4_delay(server->client, &exception->timeout); |
| 338 | if (ret != 0) | 337 | if (ret != 0) |
| 339 | break; | 338 | break; |
| @@ -1343,13 +1342,6 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state | |||
| 1343 | nfs_inode_find_state_and_recover(state->inode, | 1342 | nfs_inode_find_state_and_recover(state->inode, |
| 1344 | stateid); | 1343 | stateid); |
| 1345 | nfs4_schedule_stateid_recovery(server, state); | 1344 | nfs4_schedule_stateid_recovery(server, state); |
| 1346 | case -EKEYEXPIRED: | ||
| 1347 | /* | ||
| 1348 | * User RPCSEC_GSS context has expired. | ||
| 1349 | * We cannot recover this stateid now, so | ||
| 1350 | * skip it and allow recovery thread to | ||
| 1351 | * proceed. | ||
| 1352 | */ | ||
| 1353 | case -ENOMEM: | 1345 | case -ENOMEM: |
| 1354 | err = 0; | 1346 | err = 0; |
| 1355 | goto out; | 1347 | goto out; |
| @@ -3946,7 +3938,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, | |||
| 3946 | case -NFS4ERR_DELAY: | 3938 | case -NFS4ERR_DELAY: |
| 3947 | nfs_inc_server_stats(server, NFSIOS_DELAY); | 3939 | nfs_inc_server_stats(server, NFSIOS_DELAY); |
| 3948 | case -NFS4ERR_GRACE: | 3940 | case -NFS4ERR_GRACE: |
| 3949 | case -EKEYEXPIRED: | ||
| 3950 | rpc_delay(task, NFS4_POLL_RETRY_MAX); | 3941 | rpc_delay(task, NFS4_POLL_RETRY_MAX); |
| 3951 | task->tk_status = 0; | 3942 | task->tk_status = 0; |
| 3952 | return -EAGAIN; | 3943 | return -EAGAIN; |
| @@ -4946,15 +4937,6 @@ int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl) | |||
| 4946 | nfs4_schedule_stateid_recovery(server, state); | 4937 | nfs4_schedule_stateid_recovery(server, state); |
| 4947 | err = 0; | 4938 | err = 0; |
| 4948 | goto out; | 4939 | goto out; |
| 4949 | case -EKEYEXPIRED: | ||
| 4950 | /* | ||
| 4951 | * User RPCSEC_GSS context has expired. | ||
| 4952 | * We cannot recover this stateid now, so | ||
| 4953 | * skip it and allow recovery thread to | ||
| 4954 | * proceed. | ||
| 4955 | */ | ||
| 4956 | err = 0; | ||
| 4957 | goto out; | ||
| 4958 | case -ENOMEM: | 4940 | case -ENOMEM: |
| 4959 | case -NFS4ERR_DENIED: | 4941 | case -NFS4ERR_DENIED: |
| 4960 | /* kill_proc(fl->fl_pid, SIGLOST, 1); */ | 4942 | /* kill_proc(fl->fl_pid, SIGLOST, 1); */ |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 78e90a80fc3a..8dcbd9a0367d 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
| @@ -1437,14 +1437,6 @@ restart: | |||
| 1437 | /* Mark the file as being 'closed' */ | 1437 | /* Mark the file as being 'closed' */ |
| 1438 | state->state = 0; | 1438 | state->state = 0; |
| 1439 | break; | 1439 | break; |
| 1440 | case -EKEYEXPIRED: | ||
| 1441 | /* | ||
| 1442 | * User RPCSEC_GSS context has expired. | ||
| 1443 | * We cannot recover this stateid now, so | ||
| 1444 | * skip it and allow recovery thread to | ||
| 1445 | * proceed. | ||
| 1446 | */ | ||
| 1447 | break; | ||
| 1448 | case -NFS4ERR_ADMIN_REVOKED: | 1440 | case -NFS4ERR_ADMIN_REVOKED: |
| 1449 | case -NFS4ERR_STALE_STATEID: | 1441 | case -NFS4ERR_STALE_STATEID: |
| 1450 | case -NFS4ERR_BAD_STATEID: | 1442 | case -NFS4ERR_BAD_STATEID: |
| @@ -1597,14 +1589,6 @@ static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) | |||
| 1597 | nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); | 1589 | nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); |
| 1598 | } | 1590 | } |
| 1599 | 1591 | ||
| 1600 | static void nfs4_warn_keyexpired(const char *s) | ||
| 1601 | { | ||
| 1602 | printk_ratelimited(KERN_WARNING "Error: state manager" | ||
| 1603 | " encountered RPCSEC_GSS session" | ||
| 1604 | " expired against NFSv4 server %s.\n", | ||
| 1605 | s); | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) | 1592 | static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) |
| 1609 | { | 1593 | { |
| 1610 | switch (error) { | 1594 | switch (error) { |
| @@ -1638,10 +1622,6 @@ static int nfs4_recovery_handle_error(struct nfs_client *clp, int error) | |||
| 1638 | case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: | 1622 | case -NFS4ERR_CONN_NOT_BOUND_TO_SESSION: |
| 1639 | set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); | 1623 | set_bit(NFS4CLNT_BIND_CONN_TO_SESSION, &clp->cl_state); |
| 1640 | break; | 1624 | break; |
| 1641 | case -EKEYEXPIRED: | ||
| 1642 | /* Nothing we can do */ | ||
| 1643 | nfs4_warn_keyexpired(clp->cl_hostname); | ||
| 1644 | break; | ||
| 1645 | default: | 1625 | default: |
| 1646 | dprintk("%s: failed to handle error %d for server %s\n", | 1626 | dprintk("%s: failed to handle error %d for server %s\n", |
| 1647 | __func__, error, clp->cl_hostname); | 1627 | __func__, error, clp->cl_hostname); |
| @@ -1758,8 +1738,6 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status) | |||
| 1758 | dprintk("%s: exit with error %d for server %s\n", | 1738 | dprintk("%s: exit with error %d for server %s\n", |
| 1759 | __func__, -EPROTONOSUPPORT, clp->cl_hostname); | 1739 | __func__, -EPROTONOSUPPORT, clp->cl_hostname); |
| 1760 | return -EPROTONOSUPPORT; | 1740 | return -EPROTONOSUPPORT; |
| 1761 | case -EKEYEXPIRED: | ||
| 1762 | nfs4_warn_keyexpired(clp->cl_hostname); | ||
| 1763 | case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery | 1741 | case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery |
| 1764 | * in nfs4_exchange_id */ | 1742 | * in nfs4_exchange_id */ |
| 1765 | default: | 1743 | default: |
| @@ -1912,7 +1890,6 @@ again: | |||
| 1912 | break; | 1890 | break; |
| 1913 | 1891 | ||
| 1914 | case -EKEYEXPIRED: | 1892 | case -EKEYEXPIRED: |
| 1915 | nfs4_warn_keyexpired(clp->cl_hostname); | ||
| 1916 | case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery | 1893 | case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery |
| 1917 | * in nfs4_exchange_id */ | 1894 | * in nfs4_exchange_id */ |
| 1918 | status = -EKEYEXPIRED; | 1895 | status = -EKEYEXPIRED; |
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index 50a88c3546ed..f084dac948e1 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c | |||
| @@ -47,39 +47,6 @@ | |||
| 47 | #define NFSDBG_FACILITY NFSDBG_PROC | 47 | #define NFSDBG_FACILITY NFSDBG_PROC |
| 48 | 48 | ||
| 49 | /* | 49 | /* |
| 50 | * wrapper to handle the -EKEYEXPIRED error message. This should generally | ||
| 51 | * only happen if using krb5 auth and a user's TGT expires. NFSv2 doesn't | ||
| 52 | * support the NFSERR_JUKEBOX error code, but we handle this situation in the | ||
| 53 | * same way that we handle that error with NFSv3. | ||
| 54 | */ | ||
| 55 | static int | ||
| 56 | nfs_rpc_wrapper(struct rpc_clnt *clnt, struct rpc_message *msg, int flags) | ||
| 57 | { | ||
| 58 | int res; | ||
| 59 | do { | ||
| 60 | res = rpc_call_sync(clnt, msg, flags); | ||
| 61 | if (res != -EKEYEXPIRED) | ||
| 62 | break; | ||
| 63 | freezable_schedule_timeout_killable(NFS_JUKEBOX_RETRY_TIME); | ||
| 64 | res = -ERESTARTSYS; | ||
| 65 | } while (!fatal_signal_pending(current)); | ||
| 66 | return res; | ||
| 67 | } | ||
| 68 | |||
| 69 | #define rpc_call_sync(clnt, msg, flags) nfs_rpc_wrapper(clnt, msg, flags) | ||
| 70 | |||
| 71 | static int | ||
| 72 | nfs_async_handle_expired_key(struct rpc_task *task) | ||
| 73 | { | ||
| 74 | if (task->tk_status != -EKEYEXPIRED) | ||
| 75 | return 0; | ||
| 76 | task->tk_status = 0; | ||
| 77 | rpc_restart_call(task); | ||
| 78 | rpc_delay(task, NFS_JUKEBOX_RETRY_TIME); | ||
| 79 | return 1; | ||
| 80 | } | ||
| 81 | |||
| 82 | /* | ||
| 83 | * Bare-bones access to getattr: this is for nfs_read_super. | 50 | * Bare-bones access to getattr: this is for nfs_read_super. |
| 84 | */ | 51 | */ |
| 85 | static int | 52 | static int |
| @@ -364,8 +331,6 @@ static void nfs_proc_unlink_rpc_prepare(struct rpc_task *task, struct nfs_unlink | |||
| 364 | 331 | ||
| 365 | static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) | 332 | static int nfs_proc_unlink_done(struct rpc_task *task, struct inode *dir) |
| 366 | { | 333 | { |
| 367 | if (nfs_async_handle_expired_key(task)) | ||
| 368 | return 0; | ||
| 369 | nfs_mark_for_revalidate(dir); | 334 | nfs_mark_for_revalidate(dir); |
| 370 | return 1; | 335 | return 1; |
| 371 | } | 336 | } |
| @@ -385,8 +350,6 @@ static int | |||
| 385 | nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir, | 350 | nfs_proc_rename_done(struct rpc_task *task, struct inode *old_dir, |
| 386 | struct inode *new_dir) | 351 | struct inode *new_dir) |
| 387 | { | 352 | { |
| 388 | if (nfs_async_handle_expired_key(task)) | ||
| 389 | return 0; | ||
| 390 | nfs_mark_for_revalidate(old_dir); | 353 | nfs_mark_for_revalidate(old_dir); |
| 391 | nfs_mark_for_revalidate(new_dir); | 354 | nfs_mark_for_revalidate(new_dir); |
| 392 | return 1; | 355 | return 1; |
| @@ -642,9 +605,6 @@ static int nfs_read_done(struct rpc_task *task, struct nfs_read_data *data) | |||
| 642 | { | 605 | { |
| 643 | struct inode *inode = data->header->inode; | 606 | struct inode *inode = data->header->inode; |
| 644 | 607 | ||
| 645 | if (nfs_async_handle_expired_key(task)) | ||
| 646 | return -EAGAIN; | ||
| 647 | |||
| 648 | nfs_invalidate_atime(inode); | 608 | nfs_invalidate_atime(inode); |
| 649 | if (task->tk_status >= 0) { | 609 | if (task->tk_status >= 0) { |
| 650 | nfs_refresh_inode(inode, data->res.fattr); | 610 | 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 | { | 631 | { |
| 672 | struct inode *inode = data->header->inode; | 632 | struct inode *inode = data->header->inode; |
| 673 | 633 | ||
| 674 | if (nfs_async_handle_expired_key(task)) | ||
| 675 | return -EAGAIN; | ||
| 676 | |||
| 677 | if (task->tk_status >= 0) | 634 | if (task->tk_status >= 0) |
| 678 | nfs_post_op_update_inode_force_wcc(inode, data->res.fattr); | 635 | nfs_post_op_update_inode_force_wcc(inode, data->res.fattr); |
| 679 | return 0; | 636 | return 0; |
