aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Gruenbacher2016-12-14 06:24:47 -0600
committerSasha Levin2016-12-23 07:56:34 -0600
commit2ee3ceeccd34c3fe589506c0fd822a8773a828bf (patch)
treef6aa7fbbabf87440552f7364518dcaf9d0cc7c5d
parent820bc4582ab98a15f6f08aec443dbe731ef04874 (diff)
downloadti-linux-kernel-2ee3ceeccd34c3fe589506c0fd822a8773a828bf.tar.gz
ti-linux-kernel-2ee3ceeccd34c3fe589506c0fd822a8773a828bf.tar.xz
ti-linux-kernel-2ee3ceeccd34c3fe589506c0fd822a8773a828bf.zip
nfsd: Disable NFSv2 timestamp workaround for NFSv3+
NFSv2 can set the atime and/or mtime of a file to specific timestamps but not to the server's current time. To implement the equivalent of utimes("file", NULL), it uses a heuristic. NFSv3 and later do support setting the atime and/or mtime to the server's current time directly. The NFSv2 heuristic is still enabled, and causes timestamps to be set wrong sometimes. Fix this by moving the heuristic into the NFSv2 specific code. We can leave it out of the create code path: the owner can always set timestamps arbitrarily, and the workaround would never trigger. References: CVE-2015-1350 Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: J. Bruce Fields <bfields@redhat.com> Signed-off-by: Philipp Hahn <hahn@univention.de> Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
-rw-r--r--fs/nfsd/nfsproc.c52
-rw-r--r--fs/nfsd/vfs.c36
2 files changed, 50 insertions, 38 deletions
diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index aecbcd34d336..4cd78ef4c95c 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -59,13 +59,61 @@ static __be32
59nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp, 59nfsd_proc_setattr(struct svc_rqst *rqstp, struct nfsd_sattrargs *argp,
60 struct nfsd_attrstat *resp) 60 struct nfsd_attrstat *resp)
61{ 61{
62 struct iattr *iap = &argp->attrs;
63 struct svc_fh *fhp;
62 __be32 nfserr; 64 __be32 nfserr;
65
63 dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n", 66 dprintk("nfsd: SETATTR %s, valid=%x, size=%ld\n",
64 SVCFH_fmt(&argp->fh), 67 SVCFH_fmt(&argp->fh),
65 argp->attrs.ia_valid, (long) argp->attrs.ia_size); 68 argp->attrs.ia_valid, (long) argp->attrs.ia_size);
66 69
67 fh_copy(&resp->fh, &argp->fh); 70 fhp = fh_copy(&resp->fh, &argp->fh);
68 nfserr = nfsd_setattr(rqstp, &resp->fh, &argp->attrs,0, (time_t)0); 71
72 /*
73 * NFSv2 does not differentiate between "set-[ac]time-to-now"
74 * which only requires access, and "set-[ac]time-to-X" which
75 * requires ownership.
76 * So if it looks like it might be "set both to the same time which
77 * is close to now", and if inode_change_ok fails, then we
78 * convert to "set to now" instead of "set to explicit time"
79 *
80 * We only call inode_change_ok as the last test as technically
81 * it is not an interface that we should be using.
82 */
83#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
84#define MAX_TOUCH_TIME_ERROR (30*60)
85 if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
86 iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) {
87 /*
88 * Looks probable.
89 *
90 * Now just make sure time is in the right ballpark.
91 * Solaris, at least, doesn't seem to care what the time
92 * request is. We require it be within 30 minutes of now.
93 */
94 time_t delta = iap->ia_atime.tv_sec - get_seconds();
95 struct inode *inode;
96
97 nfserr = fh_verify(rqstp, fhp, 0, NFSD_MAY_NOP);
98 if (nfserr)
99 goto done;
100 inode = d_inode(fhp->fh_dentry);
101
102 if (delta < 0)
103 delta = -delta;
104 if (delta < MAX_TOUCH_TIME_ERROR &&
105 inode_change_ok(inode, iap) != 0) {
106 /*
107 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
108 * This will cause notify_change to set these times
109 * to "now"
110 */
111 iap->ia_valid &= ~BOTH_TIME_SET;
112 }
113 }
114
115 nfserr = nfsd_setattr(rqstp, fhp, iap, 0, (time_t)0);
116done:
69 return nfsd_return_attrs(nfserr, resp); 117 return nfsd_return_attrs(nfserr, resp);
70} 118}
71 119
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 84d770be056e..92de3747ea8b 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -302,42 +302,6 @@ commit_metadata(struct svc_fh *fhp)
302static void 302static void
303nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap) 303nfsd_sanitize_attrs(struct inode *inode, struct iattr *iap)
304{ 304{
305 /*
306 * NFSv2 does not differentiate between "set-[ac]time-to-now"
307 * which only requires access, and "set-[ac]time-to-X" which
308 * requires ownership.
309 * So if it looks like it might be "set both to the same time which
310 * is close to now", and if inode_change_ok fails, then we
311 * convert to "set to now" instead of "set to explicit time"
312 *
313 * We only call inode_change_ok as the last test as technically
314 * it is not an interface that we should be using.
315 */
316#define BOTH_TIME_SET (ATTR_ATIME_SET | ATTR_MTIME_SET)
317#define MAX_TOUCH_TIME_ERROR (30*60)
318 if ((iap->ia_valid & BOTH_TIME_SET) == BOTH_TIME_SET &&
319 iap->ia_mtime.tv_sec == iap->ia_atime.tv_sec) {
320 /*
321 * Looks probable.
322 *
323 * Now just make sure time is in the right ballpark.
324 * Solaris, at least, doesn't seem to care what the time
325 * request is. We require it be within 30 minutes of now.
326 */
327 time_t delta = iap->ia_atime.tv_sec - get_seconds();
328 if (delta < 0)
329 delta = -delta;
330 if (delta < MAX_TOUCH_TIME_ERROR &&
331 inode_change_ok(inode, iap) != 0) {
332 /*
333 * Turn off ATTR_[AM]TIME_SET but leave ATTR_[AM]TIME.
334 * This will cause notify_change to set these times
335 * to "now"
336 */
337 iap->ia_valid &= ~BOTH_TIME_SET;
338 }
339 }
340
341 /* sanitize the mode change */ 305 /* sanitize the mode change */
342 if (iap->ia_valid & ATTR_MODE) { 306 if (iap->ia_valid & ATTR_MODE) {
343 iap->ia_mode &= S_IALLUGO; 307 iap->ia_mode &= S_IALLUGO;