diff options
-rw-r--r-- | security/selinux/include/security.h | 1 | ||||
-rw-r--r-- | security/selinux/selinuxfs.c | 249 |
2 files changed, 250 insertions, 0 deletions
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 731a173f5a5f..83bdd4d2a29e 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h | |||
@@ -41,6 +41,7 @@ extern int selinux_mls_enabled; | |||
41 | 41 | ||
42 | int security_load_policy(void * data, size_t len); | 42 | int security_load_policy(void * data, size_t len); |
43 | 43 | ||
44 | #define SEL_VEC_MAX 32 | ||
44 | struct av_decision { | 45 | struct av_decision { |
45 | u32 allowed; | 46 | u32 allowed; |
46 | u32 decided; | 47 | u32 decided; |
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index cf1acde778de..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)); |
@@ -1293,6 +1311,225 @@ out: | |||
1293 | return ret; | 1311 | return ret; |
1294 | } | 1312 | } |
1295 | 1313 | ||
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 | |||
1296 | static int sel_make_dir(struct inode *dir, struct dentry *dentry, | 1533 | static int sel_make_dir(struct inode *dir, struct dentry *dentry, |
1297 | unsigned long *ino) | 1534 | unsigned long *ino) |
1298 | { | 1535 | { |
@@ -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: |