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 | |
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>
-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 | ||||
-rw-r--r-- | net/sunrpc/clnt.c | 1 |
6 files changed, 4 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; |
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index c69e199b1082..55e174f2d02f 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c | |||
@@ -1381,6 +1381,7 @@ call_refreshresult(struct rpc_task *task) | |||
1381 | return; | 1381 | return; |
1382 | case -ETIMEDOUT: | 1382 | case -ETIMEDOUT: |
1383 | rpc_delay(task, 3*HZ); | 1383 | rpc_delay(task, 3*HZ); |
1384 | case -EKEYEXPIRED: | ||
1384 | case -EAGAIN: | 1385 | case -EAGAIN: |
1385 | status = -EACCES; | 1386 | status = -EACCES; |
1386 | if (!task->tk_cred_retry) | 1387 | if (!task->tk_cred_retry) |