aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Smalley <sds@tycho.nsa.gov>2017-02-17 07:57:00 -0500
committerJames Morris <james.l.morris@oracle.com>2017-03-05 18:43:47 -0500
commit791ec491c372f49cea3ea7a7143454a9023ac9d4 (patch)
tree61ebe35fe137240666588ae13723c3ae971558ac
parentc1ae3cfa0e89fa1a7ecc4c99031f5e9ae99d9201 (diff)
prlimit,security,selinux: add a security hook for prlimit
When SELinux was first added to the kernel, a process could only get and set its own resource limits via getrlimit(2) and setrlimit(2), so no MAC checks were required for those operations, and thus no security hooks were defined for them. Later, SELinux introduced a hook for setlimit(2) with a check if the hard limit was being changed in order to be able to rely on the hard limit value as a safe reset point upon context transitions. Later on, when prlimit(2) was added to the kernel with the ability to get or set resource limits (hard or soft) of another process, LSM/SELinux was not updated other than to pass the target process to the setrlimit hook. This resulted in incomplete control over both getting and setting the resource limits of another process. Add a new security_task_prlimit() hook to the check_prlimit_permission() function to provide complete mediation. The hook is only called when acting on another task, and only if the existing DAC/capability checks would allow access. Pass flags down to the hook to indicate whether the prlimit(2) call will read, write, or both read and write the resource limits of the target process. The existing security_task_setrlimit() hook is left alone; it continues to serve a purpose in supporting the ability to make decisions based on the old and/or new resource limit values when setting limits. This is consistent with the DAC/capability logic, where check_prlimit_permission() performs generic DAC/capability checks for acting on another task, while do_prlimit() performs a capability check based on a comparison of the old and new resource limits. Fix the inline documentation for the hook to match the code. Implement the new hook for SELinux. For setting resource limits, we reuse the existing setrlimit permission. Note that this does overload the setrlimit permission to mean the ability to set the resource limit (soft or hard) of another process or the ability to change one's own hard limit. For getting resource limits, a new getrlimit permission is defined. This was not originally defined since getrlimit(2) could only be used to obtain a process' own limits. Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov> Signed-off-by: James Morris <james.l.morris@oracle.com>
-rw-r--r--include/linux/lsm_hooks.h18
-rw-r--r--include/linux/security.h13
-rw-r--r--kernel/sys.c30
-rw-r--r--security/security.c8
-rw-r--r--security/selinux/hooks.c14
-rw-r--r--security/selinux/include/classmap.h2
6 files changed, 69 insertions, 16 deletions
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index e29d4c62a3c8..ba3049f05aea 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -630,10 +630,19 @@
630 * Check permission before getting the ioprio value of @p. 630 * Check permission before getting the ioprio value of @p.
631 * @p contains the task_struct of process. 631 * @p contains the task_struct of process.
632 * Return 0 if permission is granted. 632 * Return 0 if permission is granted.
633 * @task_prlimit:
634 * Check permission before getting and/or setting the resource limits of
635 * another task.
636 * @cred points to the cred structure for the current task.
637 * @tcred points to the cred structure for the target task.
638 * @flags contains the LSM_PRLIMIT_* flag bits indicating whether the
639 * resource limits are being read, modified, or both.
640 * Return 0 if permission is granted.
633 * @task_setrlimit: 641 * @task_setrlimit:
634 * Check permission before setting the resource limits of the current 642 * Check permission before setting the resource limits of process @p
635 * process for @resource to @new_rlim. The old resource limit values can 643 * for @resource to @new_rlim. The old resource limit values can
636 * be examined by dereferencing (current->signal->rlim + resource). 644 * be examined by dereferencing (p->signal->rlim + resource).
645 * @p points to the task_struct for the target task's group leader.
637 * @resource contains the resource whose limit is being set. 646 * @resource contains the resource whose limit is being set.
638 * @new_rlim contains the new limits for @resource. 647 * @new_rlim contains the new limits for @resource.
639 * Return 0 if permission is granted. 648 * Return 0 if permission is granted.
@@ -1494,6 +1503,8 @@ union security_list_options {
1494 int (*task_setnice)(struct task_struct *p, int nice); 1503 int (*task_setnice)(struct task_struct *p, int nice);
1495 int (*task_setioprio)(struct task_struct *p, int ioprio); 1504 int (*task_setioprio)(struct task_struct *p, int ioprio);
1496 int (*task_getioprio)(struct task_struct *p); 1505 int (*task_getioprio)(struct task_struct *p);
1506 int (*task_prlimit)(const struct cred *cred, const struct cred *tcred,
1507 unsigned int flags);
1497 int (*task_setrlimit)(struct task_struct *p, unsigned int resource, 1508 int (*task_setrlimit)(struct task_struct *p, unsigned int resource,
1498 struct rlimit *new_rlim); 1509 struct rlimit *new_rlim);
1499 int (*task_setscheduler)(struct task_struct *p); 1510 int (*task_setscheduler)(struct task_struct *p);
@@ -1755,6 +1766,7 @@ struct security_hook_heads {
1755 struct list_head task_setnice; 1766 struct list_head task_setnice;
1756 struct list_head task_setioprio; 1767 struct list_head task_setioprio;
1757 struct list_head task_getioprio; 1768 struct list_head task_getioprio;
1769 struct list_head task_prlimit;
1758 struct list_head task_setrlimit; 1770 struct list_head task_setrlimit;
1759 struct list_head task_setscheduler; 1771 struct list_head task_setscheduler;
1760 struct list_head task_getscheduler; 1772 struct list_head task_getscheduler;
diff --git a/include/linux/security.h b/include/linux/security.h
index 96899fad7016..97df7bac5b48 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -133,6 +133,10 @@ extern unsigned long dac_mmap_min_addr;
133/* setfsuid or setfsgid, id0 == fsuid or fsgid */ 133/* setfsuid or setfsgid, id0 == fsuid or fsgid */
134#define LSM_SETID_FS 8 134#define LSM_SETID_FS 8
135 135
136/* Flags for security_task_prlimit(). */
137#define LSM_PRLIMIT_READ 1
138#define LSM_PRLIMIT_WRITE 2
139
136/* forward declares to avoid warnings */ 140/* forward declares to avoid warnings */
137struct sched_param; 141struct sched_param;
138struct request_sock; 142struct request_sock;
@@ -324,6 +328,8 @@ void security_task_getsecid(struct task_struct *p, u32 *secid);
324int security_task_setnice(struct task_struct *p, int nice); 328int security_task_setnice(struct task_struct *p, int nice);
325int security_task_setioprio(struct task_struct *p, int ioprio); 329int security_task_setioprio(struct task_struct *p, int ioprio);
326int security_task_getioprio(struct task_struct *p); 330int security_task_getioprio(struct task_struct *p);
331int security_task_prlimit(const struct cred *cred, const struct cred *tcred,
332 unsigned int flags);
327int security_task_setrlimit(struct task_struct *p, unsigned int resource, 333int security_task_setrlimit(struct task_struct *p, unsigned int resource,
328 struct rlimit *new_rlim); 334 struct rlimit *new_rlim);
329int security_task_setscheduler(struct task_struct *p); 335int security_task_setscheduler(struct task_struct *p);
@@ -949,6 +955,13 @@ static inline int security_task_getioprio(struct task_struct *p)
949 return 0; 955 return 0;
950} 956}
951 957
958static inline int security_task_prlimit(const struct cred *cred,
959 const struct cred *tcred,
960 unsigned int flags)
961{
962 return 0;
963}
964
952static inline int security_task_setrlimit(struct task_struct *p, 965static inline int security_task_setrlimit(struct task_struct *p,
953 unsigned int resource, 966 unsigned int resource,
954 struct rlimit *new_rlim) 967 struct rlimit *new_rlim)
diff --git a/kernel/sys.c b/kernel/sys.c
index 7ff6d1b10cec..196c7134bee6 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1432,25 +1432,26 @@ out:
1432} 1432}
1433 1433
1434/* rcu lock must be held */ 1434/* rcu lock must be held */
1435static int check_prlimit_permission(struct task_struct *task) 1435static int check_prlimit_permission(struct task_struct *task,
1436 unsigned int flags)
1436{ 1437{
1437 const struct cred *cred = current_cred(), *tcred; 1438 const struct cred *cred = current_cred(), *tcred;
1439 bool id_match;
1438 1440
1439 if (current == task) 1441 if (current == task)
1440 return 0; 1442 return 0;
1441 1443
1442 tcred = __task_cred(task); 1444 tcred = __task_cred(task);
1443 if (uid_eq(cred->uid, tcred->euid) && 1445 id_match = (uid_eq(cred->uid, tcred->euid) &&
1444 uid_eq(cred->uid, tcred->suid) && 1446 uid_eq(cred->uid, tcred->suid) &&
1445 uid_eq(cred->uid, tcred->uid) && 1447 uid_eq(cred->uid, tcred->uid) &&
1446 gid_eq(cred->gid, tcred->egid) && 1448 gid_eq(cred->gid, tcred->egid) &&
1447 gid_eq(cred->gid, tcred->sgid) && 1449 gid_eq(cred->gid, tcred->sgid) &&
1448 gid_eq(cred->gid, tcred->gid)) 1450 gid_eq(cred->gid, tcred->gid));
1449 return 0; 1451 if (!id_match && !ns_capable(tcred->user_ns, CAP_SYS_RESOURCE))
1450 if (ns_capable(tcred->user_ns, CAP_SYS_RESOURCE)) 1452 return -EPERM;
1451 return 0;
1452 1453
1453 return -EPERM; 1454 return security_task_prlimit(cred, tcred, flags);
1454} 1455}
1455 1456
1456SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource, 1457SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
@@ -1460,12 +1461,17 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
1460 struct rlimit64 old64, new64; 1461 struct rlimit64 old64, new64;
1461 struct rlimit old, new; 1462 struct rlimit old, new;
1462 struct task_struct *tsk; 1463 struct task_struct *tsk;
1464 unsigned int checkflags = 0;
1463 int ret; 1465 int ret;
1464 1466
1467 if (old_rlim)
1468 checkflags |= LSM_PRLIMIT_READ;
1469
1465 if (new_rlim) { 1470 if (new_rlim) {
1466 if (copy_from_user(&new64, new_rlim, sizeof(new64))) 1471 if (copy_from_user(&new64, new_rlim, sizeof(new64)))
1467 return -EFAULT; 1472 return -EFAULT;
1468 rlim64_to_rlim(&new64, &new); 1473 rlim64_to_rlim(&new64, &new);
1474 checkflags |= LSM_PRLIMIT_WRITE;
1469 } 1475 }
1470 1476
1471 rcu_read_lock(); 1477 rcu_read_lock();
@@ -1474,7 +1480,7 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
1474 rcu_read_unlock(); 1480 rcu_read_unlock();
1475 return -ESRCH; 1481 return -ESRCH;
1476 } 1482 }
1477 ret = check_prlimit_permission(tsk); 1483 ret = check_prlimit_permission(tsk, checkflags);
1478 if (ret) { 1484 if (ret) {
1479 rcu_read_unlock(); 1485 rcu_read_unlock();
1480 return ret; 1486 return ret;
diff --git a/security/security.c b/security/security.c
index d0e07f269b2d..905dad2811d3 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1036,6 +1036,12 @@ int security_task_getioprio(struct task_struct *p)
1036 return call_int_hook(task_getioprio, 0, p); 1036 return call_int_hook(task_getioprio, 0, p);
1037} 1037}
1038 1038
1039int security_task_prlimit(const struct cred *cred, const struct cred *tcred,
1040 unsigned int flags)
1041{
1042 return call_int_hook(task_prlimit, 0, cred, tcred, flags);
1043}
1044
1039int security_task_setrlimit(struct task_struct *p, unsigned int resource, 1045int security_task_setrlimit(struct task_struct *p, unsigned int resource,
1040 struct rlimit *new_rlim) 1046 struct rlimit *new_rlim)
1041{ 1047{
@@ -1793,6 +1799,8 @@ struct security_hook_heads security_hook_heads = {
1793 LIST_HEAD_INIT(security_hook_heads.task_setioprio), 1799 LIST_HEAD_INIT(security_hook_heads.task_setioprio),
1794 .task_getioprio = 1800 .task_getioprio =
1795 LIST_HEAD_INIT(security_hook_heads.task_getioprio), 1801 LIST_HEAD_INIT(security_hook_heads.task_getioprio),
1802 .task_prlimit =
1803 LIST_HEAD_INIT(security_hook_heads.task_prlimit),
1796 .task_setrlimit = 1804 .task_setrlimit =
1797 LIST_HEAD_INIT(security_hook_heads.task_setrlimit), 1805 LIST_HEAD_INIT(security_hook_heads.task_setrlimit),
1798 .task_setscheduler = 1806 .task_setscheduler =
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 0c2ac318aa7f..870d24ecc2de 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -3920,6 +3920,19 @@ static int selinux_task_getioprio(struct task_struct *p)
3920 PROCESS__GETSCHED, NULL); 3920 PROCESS__GETSCHED, NULL);
3921} 3921}
3922 3922
3923int selinux_task_prlimit(const struct cred *cred, const struct cred *tcred,
3924 unsigned int flags)
3925{
3926 u32 av = 0;
3927
3928 if (flags & LSM_PRLIMIT_WRITE)
3929 av |= PROCESS__SETRLIMIT;
3930 if (flags & LSM_PRLIMIT_READ)
3931 av |= PROCESS__GETRLIMIT;
3932 return avc_has_perm(cred_sid(cred), cred_sid(tcred),
3933 SECCLASS_PROCESS, av, NULL);
3934}
3935
3923static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, 3936static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource,
3924 struct rlimit *new_rlim) 3937 struct rlimit *new_rlim)
3925{ 3938{
@@ -6206,6 +6219,7 @@ static struct security_hook_list selinux_hooks[] = {
6206 LSM_HOOK_INIT(task_setnice, selinux_task_setnice), 6219 LSM_HOOK_INIT(task_setnice, selinux_task_setnice),
6207 LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio), 6220 LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio),
6208 LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio), 6221 LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio),
6222 LSM_HOOK_INIT(task_prlimit, selinux_task_prlimit),
6209 LSM_HOOK_INIT(task_setrlimit, selinux_task_setrlimit), 6223 LSM_HOOK_INIT(task_setrlimit, selinux_task_setrlimit),
6210 LSM_HOOK_INIT(task_setscheduler, selinux_task_setscheduler), 6224 LSM_HOOK_INIT(task_setscheduler, selinux_task_setscheduler),
6211 LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), 6225 LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler),
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index d429c4a1c551..1e0cc9b5de20 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -47,7 +47,7 @@ struct security_class_mapping secclass_map[] = {
47 "getattr", "setexec", "setfscreate", "noatsecure", "siginh", 47 "getattr", "setexec", "setfscreate", "noatsecure", "siginh",
48 "setrlimit", "rlimitinh", "dyntransition", "setcurrent", 48 "setrlimit", "rlimitinh", "dyntransition", "setcurrent",
49 "execmem", "execstack", "execheap", "setkeycreate", 49 "execmem", "execstack", "execheap", "setkeycreate",
50 "setsockcreate", NULL } }, 50 "setsockcreate", "getrlimit", NULL } },
51 { "system", 51 { "system",
52 { "ipc_info", "syslog_read", "syslog_mod", 52 { "ipc_info", "syslog_read", "syslog_mod",
53 "syslog_console", "module_request", "module_load", NULL } }, 53 "syslog_console", "module_request", "module_load", NULL } },