diff options
author | Lucian Adrian Grijincu <lucian.grijincu@gmail.com> | 2011-02-01 11:42:22 -0500 |
---|---|---|
committer | Eric Paris <eparis@redhat.com> | 2011-02-01 11:53:54 -0500 |
commit | 8e6c96935fcc1ed3dbebc96fddfef3f2f2395afc (patch) | |
tree | c26297c8ca479972010cadf2058aacd63ce1744f /security | |
parent | 652bb9b0d6ce007f37c098947b2cc0c45efa3f66 (diff) |
security/selinux: fix /proc/sys/ labeling
This fixes an old (2007) selinux regression: filesystem labeling for
/proc/sys returned
-r--r--r-- unknown /proc/sys/fs/file-nr
instead of
-r--r--r-- system_u:object_r:sysctl_fs_t:s0 /proc/sys/fs/file-nr
Events that lead to breaking of /proc/sys/ selinux labeling:
1) sysctl was reimplemented to route all calls through /proc/sys/
commit 77b14db502cb85a031fe8fde6c85d52f3e0acb63
[PATCH] sysctl: reimplement the sysctl proc support
2) proc_dir_entry was removed from ctl_table:
commit 3fbfa98112fc3962c416452a0baf2214381030e6
[PATCH] sysctl: remove the proc_dir_entry member for the sysctl tables
3) selinux still walked the proc_dir_entry tree to apply
labeling. Because ctl_tables don't have a proc_dir_entry, we did
not label /proc/sys/ inodes any more. To achieve this the /proc/sys/
inodes were marked private and private inodes were ignored by
selinux.
commit bbaca6c2e7ef0f663bc31be4dad7cf530f6c4962
[PATCH] selinux: enhance selinux to always ignore private inodes
commit 86a71dbd3e81e8870d0f0e56b87875f57e58222b
[PATCH] sysctl: hide the sysctl proc inodes from selinux
Access control checks have been done by means of a special sysctl hook
that was called for read/write accesses to any /proc/sys/ entry.
We don't have to do this because, instead of walking the
proc_dir_entry tree we can walk the dentry tree (as done in this
patch). With this patch:
* we don't mark /proc/sys/ inodes as private
* we don't need the sysclt security hook
* we walk the dentry tree to find the path to the inode.
We have to strip the PID in /proc/PID/ entries that have a
proc_dir_entry because selinux does not know how to label paths like
'/1/net/rpc/nfsd.fh' (and defaults to 'proc_t' labeling). Selinux does
know of '/net/rpc/nfsd.fh' (and applies the 'sysctl_rpc_t' label).
PID stripping from the path was done implicitly in the previous code
because the proc_dir_entry tree had the root in '/net' in the example
from above. The dentry tree has the root in '/1'.
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
Signed-off-by: Lucian Adrian Grijincu <lucian.grijincu@gmail.com>
Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'security')
-rw-r--r-- | security/selinux/hooks.c | 120 |
1 files changed, 18 insertions, 102 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6ae19fd28be5..c8b359fc2949 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -44,7 +44,6 @@ | |||
44 | #include <linux/fdtable.h> | 44 | #include <linux/fdtable.h> |
45 | #include <linux/namei.h> | 45 | #include <linux/namei.h> |
46 | #include <linux/mount.h> | 46 | #include <linux/mount.h> |
47 | #include <linux/proc_fs.h> | ||
48 | #include <linux/netfilter_ipv4.h> | 47 | #include <linux/netfilter_ipv4.h> |
49 | #include <linux/netfilter_ipv6.h> | 48 | #include <linux/netfilter_ipv6.h> |
50 | #include <linux/tty.h> | 49 | #include <linux/tty.h> |
@@ -71,7 +70,6 @@ | |||
71 | #include <net/ipv6.h> | 70 | #include <net/ipv6.h> |
72 | #include <linux/hugetlb.h> | 71 | #include <linux/hugetlb.h> |
73 | #include <linux/personality.h> | 72 | #include <linux/personality.h> |
74 | #include <linux/sysctl.h> | ||
75 | #include <linux/audit.h> | 73 | #include <linux/audit.h> |
76 | #include <linux/string.h> | 74 | #include <linux/string.h> |
77 | #include <linux/selinux.h> | 75 | #include <linux/selinux.h> |
@@ -1121,39 +1119,35 @@ static inline u16 socket_type_to_security_class(int family, int type, int protoc | |||
1121 | } | 1119 | } |
1122 | 1120 | ||
1123 | #ifdef CONFIG_PROC_FS | 1121 | #ifdef CONFIG_PROC_FS |
1124 | static int selinux_proc_get_sid(struct proc_dir_entry *de, | 1122 | static int selinux_proc_get_sid(struct dentry *dentry, |
1125 | u16 tclass, | 1123 | u16 tclass, |
1126 | u32 *sid) | 1124 | u32 *sid) |
1127 | { | 1125 | { |
1128 | int buflen, rc; | 1126 | int rc; |
1129 | char *buffer, *path, *end; | 1127 | char *buffer, *path; |
1130 | 1128 | ||
1131 | buffer = (char *)__get_free_page(GFP_KERNEL); | 1129 | buffer = (char *)__get_free_page(GFP_KERNEL); |
1132 | if (!buffer) | 1130 | if (!buffer) |
1133 | return -ENOMEM; | 1131 | return -ENOMEM; |
1134 | 1132 | ||
1135 | buflen = PAGE_SIZE; | 1133 | path = dentry_path_raw(dentry, buffer, PAGE_SIZE); |
1136 | end = buffer+buflen; | 1134 | if (IS_ERR(path)) |
1137 | *--end = '\0'; | 1135 | rc = PTR_ERR(path); |
1138 | buflen--; | 1136 | else { |
1139 | path = end-1; | 1137 | /* each process gets a /proc/PID/ entry. Strip off the |
1140 | *path = '/'; | 1138 | * PID part to get a valid selinux labeling. |
1141 | while (de && de != de->parent) { | 1139 | * e.g. /proc/1/net/rpc/nfs -> /net/rpc/nfs */ |
1142 | buflen -= de->namelen + 1; | 1140 | while (path[1] >= '0' && path[1] <= '9') { |
1143 | if (buflen < 0) | 1141 | path[1] = '/'; |
1144 | break; | 1142 | path++; |
1145 | end -= de->namelen; | 1143 | } |
1146 | memcpy(end, de->name, de->namelen); | 1144 | rc = security_genfs_sid("proc", path, tclass, sid); |
1147 | *--end = '/'; | ||
1148 | path = end; | ||
1149 | de = de->parent; | ||
1150 | } | 1145 | } |
1151 | rc = security_genfs_sid("proc", path, tclass, sid); | ||
1152 | free_page((unsigned long)buffer); | 1146 | free_page((unsigned long)buffer); |
1153 | return rc; | 1147 | return rc; |
1154 | } | 1148 | } |
1155 | #else | 1149 | #else |
1156 | static int selinux_proc_get_sid(struct proc_dir_entry *de, | 1150 | static int selinux_proc_get_sid(struct dentry *dentry, |
1157 | u16 tclass, | 1151 | u16 tclass, |
1158 | u32 *sid) | 1152 | u32 *sid) |
1159 | { | 1153 | { |
@@ -1315,10 +1309,9 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
1315 | isec->sid = sbsec->sid; | 1309 | isec->sid = sbsec->sid; |
1316 | 1310 | ||
1317 | if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { | 1311 | if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) { |
1318 | struct proc_inode *proci = PROC_I(inode); | 1312 | if (opt_dentry) { |
1319 | if (proci->pde) { | ||
1320 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | 1313 | isec->sclass = inode_mode_to_security_class(inode->i_mode); |
1321 | rc = selinux_proc_get_sid(proci->pde, | 1314 | rc = selinux_proc_get_sid(opt_dentry, |
1322 | isec->sclass, | 1315 | isec->sclass, |
1323 | &sid); | 1316 | &sid); |
1324 | if (rc) | 1317 | if (rc) |
@@ -1861,82 +1854,6 @@ static int selinux_capable(struct task_struct *tsk, const struct cred *cred, | |||
1861 | return task_has_capability(tsk, cred, cap, audit); | 1854 | return task_has_capability(tsk, cred, cap, audit); |
1862 | } | 1855 | } |
1863 | 1856 | ||
1864 | static int selinux_sysctl_get_sid(ctl_table *table, u16 tclass, u32 *sid) | ||
1865 | { | ||
1866 | int buflen, rc; | ||
1867 | char *buffer, *path, *end; | ||
1868 | |||
1869 | rc = -ENOMEM; | ||
1870 | buffer = (char *)__get_free_page(GFP_KERNEL); | ||
1871 | if (!buffer) | ||
1872 | goto out; | ||
1873 | |||
1874 | buflen = PAGE_SIZE; | ||
1875 | end = buffer+buflen; | ||
1876 | *--end = '\0'; | ||
1877 | buflen--; | ||
1878 | path = end-1; | ||
1879 | *path = '/'; | ||
1880 | while (table) { | ||
1881 | const char *name = table->procname; | ||
1882 | size_t namelen = strlen(name); | ||
1883 | buflen -= namelen + 1; | ||
1884 | if (buflen < 0) | ||
1885 | goto out_free; | ||
1886 | end -= namelen; | ||
1887 | memcpy(end, name, namelen); | ||
1888 | *--end = '/'; | ||
1889 | path = end; | ||
1890 | table = table->parent; | ||
1891 | } | ||
1892 | buflen -= 4; | ||
1893 | if (buflen < 0) | ||
1894 | goto out_free; | ||
1895 | end -= 4; | ||
1896 | memcpy(end, "/sys", 4); | ||
1897 | path = end; | ||
1898 | rc = security_genfs_sid("proc", path, tclass, sid); | ||
1899 | out_free: | ||
1900 | free_page((unsigned long)buffer); | ||
1901 | out: | ||
1902 | return rc; | ||
1903 | } | ||
1904 | |||
1905 | static int selinux_sysctl(ctl_table *table, int op) | ||
1906 | { | ||
1907 | int error = 0; | ||
1908 | u32 av; | ||
1909 | u32 tsid, sid; | ||
1910 | int rc; | ||
1911 | |||
1912 | sid = current_sid(); | ||
1913 | |||
1914 | rc = selinux_sysctl_get_sid(table, (op == 0001) ? | ||
1915 | SECCLASS_DIR : SECCLASS_FILE, &tsid); | ||
1916 | if (rc) { | ||
1917 | /* Default to the well-defined sysctl SID. */ | ||
1918 | tsid = SECINITSID_SYSCTL; | ||
1919 | } | ||
1920 | |||
1921 | /* The op values are "defined" in sysctl.c, thereby creating | ||
1922 | * a bad coupling between this module and sysctl.c */ | ||
1923 | if (op == 001) { | ||
1924 | error = avc_has_perm(sid, tsid, | ||
1925 | SECCLASS_DIR, DIR__SEARCH, NULL); | ||
1926 | } else { | ||
1927 | av = 0; | ||
1928 | if (op & 004) | ||
1929 | av |= FILE__READ; | ||
1930 | if (op & 002) | ||
1931 | av |= FILE__WRITE; | ||
1932 | if (av) | ||
1933 | error = avc_has_perm(sid, tsid, | ||
1934 | SECCLASS_FILE, av, NULL); | ||
1935 | } | ||
1936 | |||
1937 | return error; | ||
1938 | } | ||
1939 | |||
1940 | static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) | 1857 | static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) |
1941 | { | 1858 | { |
1942 | const struct cred *cred = current_cred(); | 1859 | const struct cred *cred = current_cred(); |
@@ -5398,7 +5315,6 @@ static struct security_operations selinux_ops = { | |||
5398 | .ptrace_traceme = selinux_ptrace_traceme, | 5315 | .ptrace_traceme = selinux_ptrace_traceme, |
5399 | .capget = selinux_capget, | 5316 | .capget = selinux_capget, |
5400 | .capset = selinux_capset, | 5317 | .capset = selinux_capset, |
5401 | .sysctl = selinux_sysctl, | ||
5402 | .capable = selinux_capable, | 5318 | .capable = selinux_capable, |
5403 | .quotactl = selinux_quotactl, | 5319 | .quotactl = selinux_quotactl, |
5404 | .quota_on = selinux_quota_on, | 5320 | .quota_on = selinux_quota_on, |