diff options
-rw-r--r-- | Documentation/sysctl/fs.txt | 36 | ||||
-rw-r--r-- | fs/namei.c | 53 | ||||
-rw-r--r-- | include/linux/fs.h | 2 | ||||
-rw-r--r-- | kernel/sysctl.c | 18 |
4 files changed, 106 insertions, 3 deletions
diff --git a/Documentation/sysctl/fs.txt b/Documentation/sysctl/fs.txt index 6c00c1e2743f..819caf8ca05f 100644 --- a/Documentation/sysctl/fs.txt +++ b/Documentation/sysctl/fs.txt | |||
@@ -34,7 +34,9 @@ Currently, these files are in /proc/sys/fs: | |||
34 | - overflowgid | 34 | - overflowgid |
35 | - pipe-user-pages-hard | 35 | - pipe-user-pages-hard |
36 | - pipe-user-pages-soft | 36 | - pipe-user-pages-soft |
37 | - protected_fifos | ||
37 | - protected_hardlinks | 38 | - protected_hardlinks |
39 | - protected_regular | ||
38 | - protected_symlinks | 40 | - protected_symlinks |
39 | - suid_dumpable | 41 | - suid_dumpable |
40 | - super-max | 42 | - super-max |
@@ -182,6 +184,24 @@ applied. | |||
182 | 184 | ||
183 | ============================================================== | 185 | ============================================================== |
184 | 186 | ||
187 | protected_fifos: | ||
188 | |||
189 | The intent of this protection is to avoid unintentional writes to | ||
190 | an attacker-controlled FIFO, where a program expected to create a regular | ||
191 | file. | ||
192 | |||
193 | When set to "0", writing to FIFOs is unrestricted. | ||
194 | |||
195 | When set to "1" don't allow O_CREAT open on FIFOs that we don't own | ||
196 | in world writable sticky directories, unless they are owned by the | ||
197 | owner of the directory. | ||
198 | |||
199 | When set to "2" it also applies to group writable sticky directories. | ||
200 | |||
201 | This protection is based on the restrictions in Openwall. | ||
202 | |||
203 | ============================================================== | ||
204 | |||
185 | protected_hardlinks: | 205 | protected_hardlinks: |
186 | 206 | ||
187 | A long-standing class of security issues is the hardlink-based | 207 | A long-standing class of security issues is the hardlink-based |
@@ -202,6 +222,22 @@ This protection is based on the restrictions in Openwall and grsecurity. | |||
202 | 222 | ||
203 | ============================================================== | 223 | ============================================================== |
204 | 224 | ||
225 | protected_regular: | ||
226 | |||
227 | This protection is similar to protected_fifos, but it | ||
228 | avoids writes to an attacker-controlled regular file, where a program | ||
229 | expected to create one. | ||
230 | |||
231 | When set to "0", writing to regular files is unrestricted. | ||
232 | |||
233 | When set to "1" don't allow O_CREAT open on regular files that we | ||
234 | don't own in world writable sticky directories, unless they are | ||
235 | owned by the owner of the directory. | ||
236 | |||
237 | When set to "2" it also applies to group writable sticky directories. | ||
238 | |||
239 | ============================================================== | ||
240 | |||
205 | protected_symlinks: | 241 | protected_symlinks: |
206 | 242 | ||
207 | A long-standing class of security issues is the symlink-based | 243 | A long-standing class of security issues is the symlink-based |
diff --git a/fs/namei.c b/fs/namei.c index ae6aa9ae757c..0cab6494978c 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -887,6 +887,8 @@ static inline void put_link(struct nameidata *nd) | |||
887 | 887 | ||
888 | int sysctl_protected_symlinks __read_mostly = 0; | 888 | int sysctl_protected_symlinks __read_mostly = 0; |
889 | int sysctl_protected_hardlinks __read_mostly = 0; | 889 | int sysctl_protected_hardlinks __read_mostly = 0; |
890 | int sysctl_protected_fifos __read_mostly; | ||
891 | int sysctl_protected_regular __read_mostly; | ||
890 | 892 | ||
891 | /** | 893 | /** |
892 | * may_follow_link - Check symlink following for unsafe situations | 894 | * may_follow_link - Check symlink following for unsafe situations |
@@ -1003,6 +1005,45 @@ static int may_linkat(struct path *link) | |||
1003 | return -EPERM; | 1005 | return -EPERM; |
1004 | } | 1006 | } |
1005 | 1007 | ||
1008 | /** | ||
1009 | * may_create_in_sticky - Check whether an O_CREAT open in a sticky directory | ||
1010 | * should be allowed, or not, on files that already | ||
1011 | * exist. | ||
1012 | * @dir: the sticky parent directory | ||
1013 | * @inode: the inode of the file to open | ||
1014 | * | ||
1015 | * Block an O_CREAT open of a FIFO (or a regular file) when: | ||
1016 | * - sysctl_protected_fifos (or sysctl_protected_regular) is enabled | ||
1017 | * - the file already exists | ||
1018 | * - we are in a sticky directory | ||
1019 | * - we don't own the file | ||
1020 | * - the owner of the directory doesn't own the file | ||
1021 | * - the directory is world writable | ||
1022 | * If the sysctl_protected_fifos (or sysctl_protected_regular) is set to 2 | ||
1023 | * the directory doesn't have to be world writable: being group writable will | ||
1024 | * be enough. | ||
1025 | * | ||
1026 | * Returns 0 if the open is allowed, -ve on error. | ||
1027 | */ | ||
1028 | static int may_create_in_sticky(struct dentry * const dir, | ||
1029 | struct inode * const inode) | ||
1030 | { | ||
1031 | if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) || | ||
1032 | (!sysctl_protected_regular && S_ISREG(inode->i_mode)) || | ||
1033 | likely(!(dir->d_inode->i_mode & S_ISVTX)) || | ||
1034 | uid_eq(inode->i_uid, dir->d_inode->i_uid) || | ||
1035 | uid_eq(current_fsuid(), inode->i_uid)) | ||
1036 | return 0; | ||
1037 | |||
1038 | if (likely(dir->d_inode->i_mode & 0002) || | ||
1039 | (dir->d_inode->i_mode & 0020 && | ||
1040 | ((sysctl_protected_fifos >= 2 && S_ISFIFO(inode->i_mode)) || | ||
1041 | (sysctl_protected_regular >= 2 && S_ISREG(inode->i_mode))))) { | ||
1042 | return -EACCES; | ||
1043 | } | ||
1044 | return 0; | ||
1045 | } | ||
1046 | |||
1006 | static __always_inline | 1047 | static __always_inline |
1007 | const char *get_link(struct nameidata *nd) | 1048 | const char *get_link(struct nameidata *nd) |
1008 | { | 1049 | { |
@@ -3348,9 +3389,15 @@ finish_open: | |||
3348 | if (error) | 3389 | if (error) |
3349 | return error; | 3390 | return error; |
3350 | audit_inode(nd->name, nd->path.dentry, 0); | 3391 | audit_inode(nd->name, nd->path.dentry, 0); |
3351 | error = -EISDIR; | 3392 | if (open_flag & O_CREAT) { |
3352 | if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) | 3393 | error = -EISDIR; |
3353 | goto out; | 3394 | if (d_is_dir(nd->path.dentry)) |
3395 | goto out; | ||
3396 | error = may_create_in_sticky(dir, | ||
3397 | d_backing_inode(nd->path.dentry)); | ||
3398 | if (unlikely(error)) | ||
3399 | goto out; | ||
3400 | } | ||
3354 | error = -ENOTDIR; | 3401 | error = -ENOTDIR; |
3355 | if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry)) | 3402 | if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry)) |
3356 | goto out; | 3403 | goto out; |
diff --git a/include/linux/fs.h b/include/linux/fs.h index e5710541183b..33322702c910 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -74,6 +74,8 @@ extern struct inodes_stat_t inodes_stat; | |||
74 | extern int leases_enable, lease_break_time; | 74 | extern int leases_enable, lease_break_time; |
75 | extern int sysctl_protected_symlinks; | 75 | extern int sysctl_protected_symlinks; |
76 | extern int sysctl_protected_hardlinks; | 76 | extern int sysctl_protected_hardlinks; |
77 | extern int sysctl_protected_fifos; | ||
78 | extern int sysctl_protected_regular; | ||
77 | 79 | ||
78 | typedef __kernel_rwf_t rwf_t; | 80 | typedef __kernel_rwf_t rwf_t; |
79 | 81 | ||
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 71ceb6c13c1a..cc02050fd0c4 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c | |||
@@ -1808,6 +1808,24 @@ static struct ctl_table fs_table[] = { | |||
1808 | .extra2 = &one, | 1808 | .extra2 = &one, |
1809 | }, | 1809 | }, |
1810 | { | 1810 | { |
1811 | .procname = "protected_fifos", | ||
1812 | .data = &sysctl_protected_fifos, | ||
1813 | .maxlen = sizeof(int), | ||
1814 | .mode = 0600, | ||
1815 | .proc_handler = proc_dointvec_minmax, | ||
1816 | .extra1 = &zero, | ||
1817 | .extra2 = &two, | ||
1818 | }, | ||
1819 | { | ||
1820 | .procname = "protected_regular", | ||
1821 | .data = &sysctl_protected_regular, | ||
1822 | .maxlen = sizeof(int), | ||
1823 | .mode = 0600, | ||
1824 | .proc_handler = proc_dointvec_minmax, | ||
1825 | .extra1 = &zero, | ||
1826 | .extra2 = &two, | ||
1827 | }, | ||
1828 | { | ||
1811 | .procname = "suid_dumpable", | 1829 | .procname = "suid_dumpable", |
1812 | .data = &suid_dumpable, | 1830 | .data = &suid_dumpable, |
1813 | .maxlen = sizeof(int), | 1831 | .maxlen = sizeof(int), |