Skip to content

Commit 4a0b33f

Browse files
Al Viropcmoore
authored andcommitted
selinux: saner handling of policy reloads
On policy reload selinuxfs replaces two subdirectories (/booleans and /class) with new variants. Unfortunately, that's done with serious abuses of directory locking. 1) lock_rename() should be done to parents, not to objects being exchanged 2) there's a bunch of reasons why it should not be done for directories that do not have a common ancestor; most of those do not apply to selinuxfs, but even in the best case the proof is subtle and brittle. 3) failure halfway through the creation of /class will leak names and values arrays. 4) use of d_genocide() is also rather brittle; it's probably not much of a bug per se, but e.g. an overmount of /sys/fs/selinuxfs/classes/shm/index with any regular file will end up with leaked mount on policy reload. Sure, don't do it, but... Let's stop messing with disconnected directories; just create a temporary (/.swapover) with no permissions for anyone (on the level of ->permission() returing -EPERM, no matter who's calling it) and build the new /booleans and /class in there; then lock_rename on root and that temporary directory and d_exchange() old and new both for class and booleans. Then unlock and use simple_recursive_removal() to take the temporary out; it's much more robust. And instead of bothering with separate pathways for freeing new (on failure halfway through) and old (on success) names/values, do all freeing in one place. With temporaries swapped with the old ones when we are past all possible failures. The only user-visible difference is that /.swapover shows up (but isn't possible to open, look up into, etc.) for the duration of policy reload. Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> [PM: applied some fixes from Al post merge] Signed-off-by: Paul Moore <paul@paul-moore.com>
1 parent b85ea95 commit 4a0b33f

File tree

1 file changed

+66
-78
lines changed

1 file changed

+66
-78
lines changed

security/selinux/selinuxfs.c

Lines changed: 66 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -336,12 +336,9 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
336336
unsigned long *ino);
337337

338338
/* declaration for sel_make_policy_nodes */
339-
static struct dentry *sel_make_disconnected_dir(struct super_block *sb,
339+
static struct dentry *sel_make_swapover_dir(struct super_block *sb,
340340
unsigned long *ino);
341341

342-
/* declaration for sel_make_policy_nodes */
343-
static void sel_remove_entries(struct dentry *de);
344-
345342
static ssize_t sel_read_mls(struct file *filp, char __user *buf,
346343
size_t count, loff_t *ppos)
347344
{
@@ -508,13 +505,13 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
508505
struct selinux_policy *newpolicy)
509506
{
510507
int ret = 0;
511-
struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir, *old_dentry;
512-
unsigned int tmp_bool_num, old_bool_num;
513-
char **tmp_bool_names, **old_bool_names;
514-
int *tmp_bool_values, *old_bool_values;
508+
struct dentry *tmp_parent, *tmp_bool_dir, *tmp_class_dir;
509+
unsigned int bool_num = 0;
510+
char **bool_names = NULL;
511+
int *bool_values = NULL;
515512
unsigned long tmp_ino = fsi->last_ino; /* Don't increment last_ino in this function */
516513

517-
tmp_parent = sel_make_disconnected_dir(fsi->sb, &tmp_ino);
514+
tmp_parent = sel_make_swapover_dir(fsi->sb, &tmp_ino);
518515
if (IS_ERR(tmp_parent))
519516
return PTR_ERR(tmp_parent);
520517

@@ -532,8 +529,8 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
532529
goto out;
533530
}
534531

535-
ret = sel_make_bools(newpolicy, tmp_bool_dir, &tmp_bool_num,
536-
&tmp_bool_names, &tmp_bool_values);
532+
ret = sel_make_bools(newpolicy, tmp_bool_dir, &bool_num,
533+
&bool_names, &bool_values);
537534
if (ret)
538535
goto out;
539536

@@ -542,38 +539,30 @@ static int sel_make_policy_nodes(struct selinux_fs_info *fsi,
542539
if (ret)
543540
goto out;
544541

542+
lock_rename(tmp_parent, fsi->sb->s_root);
543+
545544
/* booleans */
546-
old_dentry = fsi->bool_dir;
547-
lock_rename(tmp_bool_dir, old_dentry);
548545
d_exchange(tmp_bool_dir, fsi->bool_dir);
549546

550-
old_bool_num = fsi->bool_num;
551-
old_bool_names = fsi->bool_pending_names;
552-
old_bool_values = fsi->bool_pending_values;
553-
554-
fsi->bool_num = tmp_bool_num;
555-
fsi->bool_pending_names = tmp_bool_names;
556-
fsi->bool_pending_values = tmp_bool_values;
557-
558-
sel_remove_old_bool_data(old_bool_num, old_bool_names, old_bool_values);
547+
swap(fsi->bool_num, bool_num);
548+
swap(fsi->bool_pending_names, bool_names);
549+
swap(fsi->bool_pending_values, bool_values);
559550

560551
fsi->bool_dir = tmp_bool_dir;
561-
unlock_rename(tmp_bool_dir, old_dentry);
562552

563553
/* classes */
564-
old_dentry = fsi->class_dir;
565-
lock_rename(tmp_class_dir, old_dentry);
566554
d_exchange(tmp_class_dir, fsi->class_dir);
567555
fsi->class_dir = tmp_class_dir;
568-
unlock_rename(tmp_class_dir, old_dentry);
556+
557+
unlock_rename(tmp_parent, fsi->sb->s_root);
569558

570559
out:
560+
sel_remove_old_bool_data(bool_num, bool_names, bool_values);
571561
/* Since the other temporary dirs are children of tmp_parent
572562
* this will handle all the cleanup in the case of a failure before
573563
* the swapover
574564
*/
575-
sel_remove_entries(tmp_parent);
576-
dput(tmp_parent); /* d_genocide() only handles the children */
565+
simple_recursive_removal(tmp_parent, NULL);
577566

578567
return ret;
579568
}
@@ -1351,54 +1340,48 @@ static const struct file_operations sel_commit_bools_ops = {
13511340
.llseek = generic_file_llseek,
13521341
};
13531342

1354-
static void sel_remove_entries(struct dentry *de)
1355-
{
1356-
d_genocide(de);
1357-
shrink_dcache_parent(de);
1358-
}
1359-
13601343
static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_dir,
13611344
unsigned int *bool_num, char ***bool_pending_names,
13621345
int **bool_pending_values)
13631346
{
13641347
int ret;
1365-
ssize_t len;
1366-
struct dentry *dentry = NULL;
1367-
struct inode *inode = NULL;
1368-
struct inode_security_struct *isec;
1369-
char **names = NULL, *page;
1348+
char **names, *page;
13701349
u32 i, num;
1371-
int *values = NULL;
1372-
u32 sid;
13731350

1374-
ret = -ENOMEM;
13751351
page = (char *)get_zeroed_page(GFP_KERNEL);
13761352
if (!page)
1377-
goto out;
1353+
return -ENOMEM;
13781354

1379-
ret = security_get_bools(newpolicy, &num, &names, &values);
1355+
ret = security_get_bools(newpolicy, &num, &names, bool_pending_values);
13801356
if (ret)
13811357
goto out;
13821358

1359+
*bool_num = num;
1360+
*bool_pending_names = names;
1361+
13831362
for (i = 0; i < num; i++) {
1384-
ret = -ENOMEM;
1363+
struct dentry *dentry;
1364+
struct inode *inode;
1365+
struct inode_security_struct *isec;
1366+
ssize_t len;
1367+
u32 sid;
1368+
1369+
len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
1370+
if (len >= PAGE_SIZE) {
1371+
ret = -ENAMETOOLONG;
1372+
break;
1373+
}
13851374
dentry = d_alloc_name(bool_dir, names[i]);
1386-
if (!dentry)
1387-
goto out;
1375+
if (!dentry) {
1376+
ret = -ENOMEM;
1377+
break;
1378+
}
13881379

1389-
ret = -ENOMEM;
13901380
inode = sel_make_inode(bool_dir->d_sb, S_IFREG | S_IRUGO | S_IWUSR);
13911381
if (!inode) {
13921382
dput(dentry);
1393-
goto out;
1394-
}
1395-
1396-
ret = -ENAMETOOLONG;
1397-
len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]);
1398-
if (len >= PAGE_SIZE) {
1399-
dput(dentry);
1400-
iput(inode);
1401-
goto out;
1383+
ret = -ENOMEM;
1384+
break;
14021385
}
14031386

14041387
isec = selinux_inode(inode);
@@ -1416,23 +1399,8 @@ static int sel_make_bools(struct selinux_policy *newpolicy, struct dentry *bool_
14161399
inode->i_ino = i|SEL_BOOL_INO_OFFSET;
14171400
d_add(dentry, inode);
14181401
}
1419-
*bool_num = num;
1420-
*bool_pending_names = names;
1421-
*bool_pending_values = values;
1422-
1423-
free_page((unsigned long)page);
1424-
return 0;
14251402
out:
14261403
free_page((unsigned long)page);
1427-
1428-
if (names) {
1429-
for (i = 0; i < num; i++)
1430-
kfree(names[i]);
1431-
kfree(names);
1432-
}
1433-
kfree(values);
1434-
sel_remove_entries(bool_dir);
1435-
14361404
return ret;
14371405
}
14381406

@@ -1961,20 +1929,40 @@ static struct dentry *sel_make_dir(struct dentry *dir, const char *name,
19611929
return dentry;
19621930
}
19631931

1964-
static struct dentry *sel_make_disconnected_dir(struct super_block *sb,
1932+
static int reject_all(struct mnt_idmap *idmap, struct inode *inode, int mask)
1933+
{
1934+
return -EPERM; // no access for anyone, root or no root.
1935+
}
1936+
1937+
static const struct inode_operations swapover_dir_inode_operations = {
1938+
.lookup = simple_lookup,
1939+
.permission = reject_all,
1940+
};
1941+
1942+
static struct dentry *sel_make_swapover_dir(struct super_block *sb,
19651943
unsigned long *ino)
19661944
{
1967-
struct inode *inode = sel_make_inode(sb, S_IFDIR | S_IRUGO | S_IXUGO);
1945+
struct dentry *dentry = d_alloc_name(sb->s_root, ".swapover");
1946+
struct inode *inode;
19681947

1969-
if (!inode)
1948+
if (!dentry)
19701949
return ERR_PTR(-ENOMEM);
19711950

1972-
inode->i_op = &simple_dir_inode_operations;
1973-
inode->i_fop = &simple_dir_operations;
1951+
inode = sel_make_inode(sb, S_IFDIR);
1952+
if (!inode) {
1953+
dput(dentry);
1954+
return ERR_PTR(-ENOMEM);
1955+
}
1956+
1957+
inode->i_op = &swapover_dir_inode_operations;
19741958
inode->i_ino = ++(*ino);
19751959
/* directory inodes start off with i_nlink == 2 (for "." entry) */
19761960
inc_nlink(inode);
1977-
return d_obtain_alias(inode);
1961+
inode_lock(sb->s_root->d_inode);
1962+
d_add(dentry, inode);
1963+
inc_nlink(sb->s_root->d_inode);
1964+
inode_unlock(sb->s_root->d_inode);
1965+
return dentry;
19781966
}
19791967

19801968
#define NULL_FILE_NAME "null"

0 commit comments

Comments
 (0)