Commit 02c6be615f1fcd37ac5ed93a3ad6692ad8991cd9
Committed by
Linus Torvalds
1 parent
2850699c59
Exists in
master
and in
7 other branches
vfs: fix permission checking in sys_utimensat
If utimensat() is called with both times set to UTIME_NOW or one of them to UTIME_NOW and the other to UTIME_OMIT, then it will update the file time without any permission checking. I don't think this can be used for anything other than a local DoS, but could be quite bewildering at that (e.g. "Why was that large source tree rebuilt when I didn't modify anything???") This affects all kernels from 2.6.22, when the utimensat() syscall was introduced. Fix by doing the same permission checking as for the "times == NULL" case. Thanks to Michael Kerrisk, whose utimensat-non-conformances-and-fixes.patch in -mm also fixes this (and breaks other stuff), only he didn't realize the security implications of this bug. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Cc: Ulrich Drepper <drepper@redhat.com> Cc: Michael Kerrisk <mtk-manpages@gmx.net> Cc: <stable@kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 1 changed file with 15 additions and 2 deletions Side-by-side Diff
fs/utimes.c
... | ... | @@ -40,9 +40,14 @@ |
40 | 40 | |
41 | 41 | #endif |
42 | 42 | |
43 | +static bool nsec_special(long nsec) | |
44 | +{ | |
45 | + return nsec == UTIME_OMIT || nsec == UTIME_NOW; | |
46 | +} | |
47 | + | |
43 | 48 | static bool nsec_valid(long nsec) |
44 | 49 | { |
45 | - if (nsec == UTIME_OMIT || nsec == UTIME_NOW) | |
50 | + if (nsec_special(nsec)) | |
46 | 51 | return true; |
47 | 52 | |
48 | 53 | return nsec >= 0 && nsec <= 999999999; |
... | ... | @@ -119,7 +124,15 @@ |
119 | 124 | newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; |
120 | 125 | newattrs.ia_valid |= ATTR_MTIME_SET; |
121 | 126 | } |
122 | - } else { | |
127 | + } | |
128 | + | |
129 | + /* | |
130 | + * If times is NULL or both times are either UTIME_OMIT or | |
131 | + * UTIME_NOW, then need to check permissions, because | |
132 | + * inode_change_ok() won't do it. | |
133 | + */ | |
134 | + if (!times || (nsec_special(times[0].tv_nsec) && | |
135 | + nsec_special(times[1].tv_nsec))) { | |
123 | 136 | error = -EACCES; |
124 | 137 | if (IS_IMMUTABLE(inode)) |
125 | 138 | goto mnt_drop_write_and_out; |