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: |
