diff options
Diffstat (limited to 'security/selinux/selinuxfs.c')
-rw-r--r-- | security/selinux/selinuxfs.c | 269 |
1 files changed, 259 insertions, 10 deletions
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index aca099aa2ed3..c9e92daedee2 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c | |||
@@ -67,6 +67,10 @@ static struct dentry *bool_dir = NULL; | |||
67 | static int bool_num = 0; | 67 | static int bool_num = 0; |
68 | static int *bool_pending_values = NULL; | 68 | static int *bool_pending_values = NULL; |
69 | 69 | ||
70 | /* global data for classes */ | ||
71 | static struct dentry *class_dir = NULL; | ||
72 | static unsigned long last_class_ino; | ||
73 | |||
70 | extern void selnl_notify_setenforce(int val); | 74 | extern void selnl_notify_setenforce(int val); |
71 | 75 | ||
72 | /* Check whether a task is allowed to use a security operation. */ | 76 | /* Check whether a task is allowed to use a security operation. */ |
@@ -106,6 +110,7 @@ static unsigned long sel_last_ino = SEL_INO_NEXT - 1; | |||
106 | 110 | ||
107 | #define SEL_INITCON_INO_OFFSET 0x01000000 | 111 | #define SEL_INITCON_INO_OFFSET 0x01000000 |
108 | #define SEL_BOOL_INO_OFFSET 0x02000000 | 112 | #define SEL_BOOL_INO_OFFSET 0x02000000 |
113 | #define SEL_CLASS_INO_OFFSET 0x04000000 | ||
109 | #define SEL_INO_MASK 0x00ffffff | 114 | #define SEL_INO_MASK 0x00ffffff |
110 | 115 | ||
111 | #define TMPBUFLEN 12 | 116 | #define TMPBUFLEN 12 |
@@ -237,6 +242,11 @@ static const struct file_operations sel_policyvers_ops = { | |||
237 | 242 | ||
238 | /* declaration for sel_write_load */ | 243 | /* declaration for sel_write_load */ |
239 | static int sel_make_bools(void); | 244 | static int sel_make_bools(void); |
245 | static int sel_make_classes(void); | ||
246 | |||
247 | /* declaration for sel_make_class_dirs */ | ||
248 | static int sel_make_dir(struct inode *dir, struct dentry *dentry, | ||
249 | unsigned long *ino); | ||
240 | 250 | ||
241 | static ssize_t sel_read_mls(struct file *filp, char __user *buf, | 251 | static ssize_t sel_read_mls(struct file *filp, char __user *buf, |
242 | size_t count, loff_t *ppos) | 252 | size_t count, loff_t *ppos) |
@@ -287,10 +297,18 @@ static ssize_t sel_write_load(struct file * file, const char __user * buf, | |||
287 | goto out; | 297 | goto out; |
288 | 298 | ||
289 | ret = sel_make_bools(); | 299 | ret = sel_make_bools(); |
300 | if (ret) { | ||
301 | length = ret; | ||
302 | goto out1; | ||
303 | } | ||
304 | |||
305 | ret = sel_make_classes(); | ||
290 | if (ret) | 306 | if (ret) |
291 | length = ret; | 307 | length = ret; |
292 | else | 308 | else |
293 | length = count; | 309 | length = count; |
310 | |||
311 | out1: | ||
294 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, | 312 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_MAC_POLICY_LOAD, |
295 | "policy loaded auid=%u", | 313 | "policy loaded auid=%u", |
296 | audit_get_loginuid(current->audit_context)); | 314 | audit_get_loginuid(current->audit_context)); |
@@ -940,9 +958,8 @@ static const struct file_operations sel_commit_bools_ops = { | |||
940 | .write = sel_commit_bools_write, | 958 | .write = sel_commit_bools_write, |
941 | }; | 959 | }; |
942 | 960 | ||
943 | /* delete booleans - partial revoke() from | 961 | /* partial revoke() from fs/proc/generic.c proc_kill_inodes */ |
944 | * fs/proc/generic.c proc_kill_inodes */ | 962 | static void sel_remove_entries(struct dentry *de) |
945 | static void sel_remove_bools(struct dentry *de) | ||
946 | { | 963 | { |
947 | struct list_head *p, *node; | 964 | struct list_head *p, *node; |
948 | struct super_block *sb = de->d_sb; | 965 | struct super_block *sb = de->d_sb; |
@@ -998,7 +1015,7 @@ static int sel_make_bools(void) | |||
998 | kfree(bool_pending_values); | 1015 | kfree(bool_pending_values); |
999 | bool_pending_values = NULL; | 1016 | bool_pending_values = NULL; |
1000 | 1017 | ||
1001 | sel_remove_bools(dir); | 1018 | sel_remove_entries(dir); |
1002 | 1019 | ||
1003 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) | 1020 | if (!(page = (char*)get_zeroed_page(GFP_KERNEL))) |
1004 | return -ENOMEM; | 1021 | return -ENOMEM; |
@@ -1048,7 +1065,7 @@ out: | |||
1048 | return ret; | 1065 | return ret; |
1049 | err: | 1066 | err: |
1050 | kfree(values); | 1067 | kfree(values); |
1051 | sel_remove_bools(dir); | 1068 | sel_remove_entries(dir); |
1052 | ret = -ENOMEM; | 1069 | ret = -ENOMEM; |
1053 | goto out; | 1070 | goto out; |
1054 | } | 1071 | } |
@@ -1294,7 +1311,227 @@ out: | |||
1294 | return ret; | 1311 | return ret; |
1295 | } | 1312 | } |
1296 | 1313 | ||
1297 | static int sel_make_dir(struct inode *dir, struct dentry *dentry) | 1314 | static inline unsigned int sel_div(unsigned long a, unsigned long b) |
1315 | { | ||
1316 | return a / b - (a % b < 0); | ||
1317 | } | ||
1318 | |||
1319 | static inline unsigned long sel_class_to_ino(u16 class) | ||
1320 | { | ||
1321 | return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET; | ||
1322 | } | ||
1323 | |||
1324 | static inline u16 sel_ino_to_class(unsigned long ino) | ||
1325 | { | ||
1326 | return sel_div(ino & SEL_INO_MASK, SEL_VEC_MAX + 1); | ||
1327 | } | ||
1328 | |||
1329 | static inline unsigned long sel_perm_to_ino(u16 class, u32 perm) | ||
1330 | { | ||
1331 | return (class * (SEL_VEC_MAX + 1) + perm) | SEL_CLASS_INO_OFFSET; | ||
1332 | } | ||
1333 | |||
1334 | static inline u32 sel_ino_to_perm(unsigned long ino) | ||
1335 | { | ||
1336 | return (ino & SEL_INO_MASK) % (SEL_VEC_MAX + 1); | ||
1337 | } | ||
1338 | |||
1339 | static ssize_t sel_read_class(struct file * file, char __user *buf, | ||
1340 | size_t count, loff_t *ppos) | ||
1341 | { | ||
1342 | ssize_t rc, len; | ||
1343 | char *page; | ||
1344 | unsigned long ino = file->f_path.dentry->d_inode->i_ino; | ||
1345 | |||
1346 | page = (char *)__get_free_page(GFP_KERNEL); | ||
1347 | if (!page) { | ||
1348 | rc = -ENOMEM; | ||
1349 | goto out; | ||
1350 | } | ||
1351 | |||
1352 | len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino)); | ||
1353 | rc = simple_read_from_buffer(buf, count, ppos, page, len); | ||
1354 | free_page((unsigned long)page); | ||
1355 | out: | ||
1356 | return rc; | ||
1357 | } | ||
1358 | |||
1359 | static const struct file_operations sel_class_ops = { | ||
1360 | .read = sel_read_class, | ||
1361 | }; | ||
1362 | |||
1363 | static ssize_t sel_read_perm(struct file * file, char __user *buf, | ||
1364 | size_t count, loff_t *ppos) | ||
1365 | { | ||
1366 | ssize_t rc, len; | ||
1367 | char *page; | ||
1368 | unsigned long ino = file->f_path.dentry->d_inode->i_ino; | ||
1369 | |||
1370 | page = (char *)__get_free_page(GFP_KERNEL); | ||
1371 | if (!page) { | ||
1372 | rc = -ENOMEM; | ||
1373 | goto out; | ||
1374 | } | ||
1375 | |||
1376 | len = snprintf(page, PAGE_SIZE,"%d", sel_ino_to_perm(ino)); | ||
1377 | rc = simple_read_from_buffer(buf, count, ppos, page, len); | ||
1378 | free_page((unsigned long)page); | ||
1379 | out: | ||
1380 | return rc; | ||
1381 | } | ||
1382 | |||
1383 | static const struct file_operations sel_perm_ops = { | ||
1384 | .read = sel_read_perm, | ||
1385 | }; | ||
1386 | |||
1387 | static int sel_make_perm_files(char *objclass, int classvalue, | ||
1388 | struct dentry *dir) | ||
1389 | { | ||
1390 | int i, rc = 0, nperms; | ||
1391 | char **perms; | ||
1392 | |||
1393 | rc = security_get_permissions(objclass, &perms, &nperms); | ||
1394 | if (rc) | ||
1395 | goto out; | ||
1396 | |||
1397 | for (i = 0; i < nperms; i++) { | ||
1398 | struct inode *inode; | ||
1399 | struct dentry *dentry; | ||
1400 | |||
1401 | dentry = d_alloc_name(dir, perms[i]); | ||
1402 | if (!dentry) { | ||
1403 | rc = -ENOMEM; | ||
1404 | goto out1; | ||
1405 | } | ||
1406 | |||
1407 | inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); | ||
1408 | if (!inode) { | ||
1409 | rc = -ENOMEM; | ||
1410 | goto out1; | ||
1411 | } | ||
1412 | inode->i_fop = &sel_perm_ops; | ||
1413 | /* i+1 since perm values are 1-indexed */ | ||
1414 | inode->i_ino = sel_perm_to_ino(classvalue, i+1); | ||
1415 | d_add(dentry, inode); | ||
1416 | } | ||
1417 | |||
1418 | out1: | ||
1419 | for (i = 0; i < nperms; i++) | ||
1420 | kfree(perms[i]); | ||
1421 | kfree(perms); | ||
1422 | out: | ||
1423 | return rc; | ||
1424 | } | ||
1425 | |||
1426 | static int sel_make_class_dir_entries(char *classname, int index, | ||
1427 | struct dentry *dir) | ||
1428 | { | ||
1429 | struct dentry *dentry = NULL; | ||
1430 | struct inode *inode = NULL; | ||
1431 | int rc; | ||
1432 | |||
1433 | dentry = d_alloc_name(dir, "index"); | ||
1434 | if (!dentry) { | ||
1435 | rc = -ENOMEM; | ||
1436 | goto out; | ||
1437 | } | ||
1438 | |||
1439 | inode = sel_make_inode(dir->d_sb, S_IFREG|S_IRUGO); | ||
1440 | if (!inode) { | ||
1441 | rc = -ENOMEM; | ||
1442 | goto out; | ||
1443 | } | ||
1444 | |||
1445 | inode->i_fop = &sel_class_ops; | ||
1446 | inode->i_ino = sel_class_to_ino(index); | ||
1447 | d_add(dentry, inode); | ||
1448 | |||
1449 | dentry = d_alloc_name(dir, "perms"); | ||
1450 | if (!dentry) { | ||
1451 | rc = -ENOMEM; | ||
1452 | goto out; | ||
1453 | } | ||
1454 | |||
1455 | rc = sel_make_dir(dir->d_inode, dentry, &last_class_ino); | ||
1456 | if (rc) | ||
1457 | goto out; | ||
1458 | |||
1459 | rc = sel_make_perm_files(classname, index, dentry); | ||
1460 | |||
1461 | out: | ||
1462 | return rc; | ||
1463 | } | ||
1464 | |||
1465 | static void sel_remove_classes(void) | ||
1466 | { | ||
1467 | struct list_head *class_node; | ||
1468 | |||
1469 | list_for_each(class_node, &class_dir->d_subdirs) { | ||
1470 | struct dentry *class_subdir = list_entry(class_node, | ||
1471 | struct dentry, d_u.d_child); | ||
1472 | struct list_head *class_subdir_node; | ||
1473 | |||
1474 | list_for_each(class_subdir_node, &class_subdir->d_subdirs) { | ||
1475 | struct dentry *d = list_entry(class_subdir_node, | ||
1476 | struct dentry, d_u.d_child); | ||
1477 | |||
1478 | if (d->d_inode) | ||
1479 | if (d->d_inode->i_mode & S_IFDIR) | ||
1480 | sel_remove_entries(d); | ||
1481 | } | ||
1482 | |||
1483 | sel_remove_entries(class_subdir); | ||
1484 | } | ||
1485 | |||
1486 | sel_remove_entries(class_dir); | ||
1487 | } | ||
1488 | |||
1489 | static int sel_make_classes(void) | ||
1490 | { | ||
1491 | int rc = 0, nclasses, i; | ||
1492 | char **classes; | ||
1493 | |||
1494 | /* delete any existing entries */ | ||
1495 | sel_remove_classes(); | ||
1496 | |||
1497 | rc = security_get_classes(&classes, &nclasses); | ||
1498 | if (rc < 0) | ||
1499 | goto out; | ||
1500 | |||
1501 | /* +2 since classes are 1-indexed */ | ||
1502 | last_class_ino = sel_class_to_ino(nclasses+2); | ||
1503 | |||
1504 | for (i = 0; i < nclasses; i++) { | ||
1505 | struct dentry *class_name_dir; | ||
1506 | |||
1507 | class_name_dir = d_alloc_name(class_dir, classes[i]); | ||
1508 | if (!class_name_dir) { | ||
1509 | rc = -ENOMEM; | ||
1510 | goto out1; | ||
1511 | } | ||
1512 | |||
1513 | rc = sel_make_dir(class_dir->d_inode, class_name_dir, | ||
1514 | &last_class_ino); | ||
1515 | if (rc) | ||
1516 | goto out1; | ||
1517 | |||
1518 | /* i+1 since class values are 1-indexed */ | ||
1519 | rc = sel_make_class_dir_entries(classes[i], i+1, | ||
1520 | class_name_dir); | ||
1521 | if (rc) | ||
1522 | goto out1; | ||
1523 | } | ||
1524 | |||
1525 | out1: | ||
1526 | for (i = 0; i < nclasses; i++) | ||
1527 | kfree(classes[i]); | ||
1528 | kfree(classes); | ||
1529 | out: | ||
1530 | return rc; | ||
1531 | } | ||
1532 | |||
1533 | static int sel_make_dir(struct inode *dir, struct dentry *dentry, | ||
1534 | unsigned long *ino) | ||
1298 | { | 1535 | { |
1299 | int ret = 0; | 1536 | int ret = 0; |
1300 | struct inode *inode; | 1537 | struct inode *inode; |
@@ -1306,7 +1543,7 @@ static int sel_make_dir(struct inode *dir, struct dentry *dentry) | |||
1306 | } | 1543 | } |
1307 | inode->i_op = &simple_dir_inode_operations; | 1544 | inode->i_op = &simple_dir_inode_operations; |
1308 | inode->i_fop = &simple_dir_operations; | 1545 | inode->i_fop = &simple_dir_operations; |
1309 | inode->i_ino = ++sel_last_ino; | 1546 | inode->i_ino = ++(*ino); |
1310 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ | 1547 | /* directory inodes start off with i_nlink == 2 (for "." entry) */ |
1311 | inc_nlink(inode); | 1548 | inc_nlink(inode); |
1312 | d_add(dentry, inode); | 1549 | d_add(dentry, inode); |
@@ -1352,7 +1589,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
1352 | goto err; | 1589 | goto err; |
1353 | } | 1590 | } |
1354 | 1591 | ||
1355 | ret = sel_make_dir(root_inode, dentry); | 1592 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); |
1356 | if (ret) | 1593 | if (ret) |
1357 | goto err; | 1594 | goto err; |
1358 | 1595 | ||
@@ -1385,7 +1622,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
1385 | goto err; | 1622 | goto err; |
1386 | } | 1623 | } |
1387 | 1624 | ||
1388 | ret = sel_make_dir(root_inode, dentry); | 1625 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); |
1389 | if (ret) | 1626 | if (ret) |
1390 | goto err; | 1627 | goto err; |
1391 | 1628 | ||
@@ -1399,7 +1636,7 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
1399 | goto err; | 1636 | goto err; |
1400 | } | 1637 | } |
1401 | 1638 | ||
1402 | ret = sel_make_dir(root_inode, dentry); | 1639 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); |
1403 | if (ret) | 1640 | if (ret) |
1404 | goto err; | 1641 | goto err; |
1405 | 1642 | ||
@@ -1407,6 +1644,18 @@ static int sel_fill_super(struct super_block * sb, void * data, int silent) | |||
1407 | if (ret) | 1644 | if (ret) |
1408 | goto err; | 1645 | goto err; |
1409 | 1646 | ||
1647 | dentry = d_alloc_name(sb->s_root, "class"); | ||
1648 | if (!dentry) { | ||
1649 | ret = -ENOMEM; | ||
1650 | goto err; | ||
1651 | } | ||
1652 | |||
1653 | ret = sel_make_dir(root_inode, dentry, &sel_last_ino); | ||
1654 | if (ret) | ||
1655 | goto err; | ||
1656 | |||
1657 | class_dir = dentry; | ||
1658 | |||
1410 | out: | 1659 | out: |
1411 | return ret; | 1660 | return ret; |
1412 | err: | 1661 | err: |