aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLucian Adrian Grijincu <lucian.grijincu@gmail.com>2011-02-01 11:42:22 -0500
committerEric Paris <eparis@redhat.com>2011-02-01 11:53:54 -0500
commit8e6c96935fcc1ed3dbebc96fddfef3f2f2395afc (patch)
treec26297c8ca479972010cadf2058aacd63ce1744f
parent652bb9b0d6ce007f37c098947b2cc0c45efa3f66 (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>
-rw-r--r--fs/proc/proc_sysctl.c1
-rw-r--r--security/selinux/hooks.c120
2 files changed, 18 insertions, 103 deletions
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index 09a1f92a34ef..fb707e018a81 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -32,7 +32,6 @@ static struct inode *proc_sys_make_inode(struct super_block *sb,
32 ei->sysctl_entry = table; 32 ei->sysctl_entry = table;
33 33
34 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; 34 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
35 inode->i_flags |= S_PRIVATE; /* tell selinux to ignore this inode */
36 inode->i_mode = table->mode; 35 inode->i_mode = table->mode;
37 if (!table->child) { 36 if (!table->child) {
38 inode->i_mode |= S_IFREG; 37 inode->i_mode |= S_IFREG;
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
1124static int selinux_proc_get_sid(struct proc_dir_entry *de, 1122static 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
1156static int selinux_proc_get_sid(struct proc_dir_entry *de, 1150static 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
1864static 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);
1899out_free:
1900 free_page((unsigned long)buffer);
1901out:
1902 return rc;
1903}
1904
1905static 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
1940static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb) 1857static 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,