LSM: add SafeSetID module that gates setid calls
This change ensures that the set*uid family of syscalls in kernel/sys.c (setreuid, setuid, setresuid, setfsuid) all call ns_capable_common with the CAP_OPT_INSETID flag, so capability checks in the security_capable hook can know whether they are being called from within a set*uid syscall. This change is a no-op by itself, but is needed for the proposed SafeSetID LSM. Signed-off-by: Micah Morton <mortonm@chromium.org> Acked-by: Kees Cook <keescook@chromium.org> Signed-off-by: James Morris <james.morris@microsoft.com>
This commit is contained in:
parent
4b42564181
commit
40852275a9
|
@ -209,6 +209,7 @@ extern bool has_ns_capability_noaudit(struct task_struct *t,
|
||||||
extern bool capable(int cap);
|
extern bool capable(int cap);
|
||||||
extern bool ns_capable(struct user_namespace *ns, int cap);
|
extern bool ns_capable(struct user_namespace *ns, int cap);
|
||||||
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
|
extern bool ns_capable_noaudit(struct user_namespace *ns, int cap);
|
||||||
|
extern bool ns_capable_setid(struct user_namespace *ns, int cap);
|
||||||
#else
|
#else
|
||||||
static inline bool has_capability(struct task_struct *t, int cap)
|
static inline bool has_capability(struct task_struct *t, int cap)
|
||||||
{
|
{
|
||||||
|
@ -240,6 +241,10 @@ static inline bool ns_capable_noaudit(struct user_namespace *ns, int cap)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
static inline bool ns_capable_setid(struct user_namespace *ns, int cap)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif /* CONFIG_MULTIUSER */
|
#endif /* CONFIG_MULTIUSER */
|
||||||
extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
|
extern bool privileged_wrt_inode_uidgid(struct user_namespace *ns, const struct inode *inode);
|
||||||
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
|
extern bool capable_wrt_inode_uidgid(const struct inode *inode, int cap);
|
||||||
|
|
|
@ -415,6 +415,25 @@ bool ns_capable_noaudit(struct user_namespace *ns, int cap)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(ns_capable_noaudit);
|
EXPORT_SYMBOL(ns_capable_noaudit);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ns_capable_setid - Determine if the current task has a superior capability
|
||||||
|
* in effect, while signalling that this check is being done from within a
|
||||||
|
* setid syscall.
|
||||||
|
* @ns: The usernamespace we want the capability in
|
||||||
|
* @cap: The capability to be tested for
|
||||||
|
*
|
||||||
|
* Return true if the current task has the given superior capability currently
|
||||||
|
* available for use, false if not.
|
||||||
|
*
|
||||||
|
* This sets PF_SUPERPRIV on the task if the capability is available on the
|
||||||
|
* assumption that it's about to be used.
|
||||||
|
*/
|
||||||
|
bool ns_capable_setid(struct user_namespace *ns, int cap)
|
||||||
|
{
|
||||||
|
return ns_capable_common(ns, cap, CAP_OPT_INSETID);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(ns_capable_setid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* capable - Determine if the current task has a superior capability in effect
|
* capable - Determine if the current task has a superior capability in effect
|
||||||
* @cap: The capability to be tested for
|
* @cap: The capability to be tested for
|
||||||
|
|
10
kernel/sys.c
10
kernel/sys.c
|
@ -516,7 +516,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid)
|
||||||
new->uid = kruid;
|
new->uid = kruid;
|
||||||
if (!uid_eq(old->uid, kruid) &&
|
if (!uid_eq(old->uid, kruid) &&
|
||||||
!uid_eq(old->euid, kruid) &&
|
!uid_eq(old->euid, kruid) &&
|
||||||
!ns_capable(old->user_ns, CAP_SETUID))
|
!ns_capable_setid(old->user_ns, CAP_SETUID))
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,7 +525,7 @@ long __sys_setreuid(uid_t ruid, uid_t euid)
|
||||||
if (!uid_eq(old->uid, keuid) &&
|
if (!uid_eq(old->uid, keuid) &&
|
||||||
!uid_eq(old->euid, keuid) &&
|
!uid_eq(old->euid, keuid) &&
|
||||||
!uid_eq(old->suid, keuid) &&
|
!uid_eq(old->suid, keuid) &&
|
||||||
!ns_capable(old->user_ns, CAP_SETUID))
|
!ns_capable_setid(old->user_ns, CAP_SETUID))
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -584,7 +584,7 @@ long __sys_setuid(uid_t uid)
|
||||||
old = current_cred();
|
old = current_cred();
|
||||||
|
|
||||||
retval = -EPERM;
|
retval = -EPERM;
|
||||||
if (ns_capable(old->user_ns, CAP_SETUID)) {
|
if (ns_capable_setid(old->user_ns, CAP_SETUID)) {
|
||||||
new->suid = new->uid = kuid;
|
new->suid = new->uid = kuid;
|
||||||
if (!uid_eq(kuid, old->uid)) {
|
if (!uid_eq(kuid, old->uid)) {
|
||||||
retval = set_user(new);
|
retval = set_user(new);
|
||||||
|
@ -646,7 +646,7 @@ long __sys_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||||
old = current_cred();
|
old = current_cred();
|
||||||
|
|
||||||
retval = -EPERM;
|
retval = -EPERM;
|
||||||
if (!ns_capable(old->user_ns, CAP_SETUID)) {
|
if (!ns_capable_setid(old->user_ns, CAP_SETUID)) {
|
||||||
if (ruid != (uid_t) -1 && !uid_eq(kruid, old->uid) &&
|
if (ruid != (uid_t) -1 && !uid_eq(kruid, old->uid) &&
|
||||||
!uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
|
!uid_eq(kruid, old->euid) && !uid_eq(kruid, old->suid))
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -814,7 +814,7 @@ long __sys_setfsuid(uid_t uid)
|
||||||
|
|
||||||
if (uid_eq(kuid, old->uid) || uid_eq(kuid, old->euid) ||
|
if (uid_eq(kuid, old->uid) || uid_eq(kuid, old->euid) ||
|
||||||
uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) ||
|
uid_eq(kuid, old->suid) || uid_eq(kuid, old->fsuid) ||
|
||||||
ns_capable(old->user_ns, CAP_SETUID)) {
|
ns_capable_setid(old->user_ns, CAP_SETUID)) {
|
||||||
if (!uid_eq(kuid, old->fsuid)) {
|
if (!uid_eq(kuid, old->fsuid)) {
|
||||||
new->fsuid = kuid;
|
new->fsuid = kuid;
|
||||||
if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
|
if (security_task_fix_setuid(new, old, LSM_SETID_FS) == 0)
|
||||||
|
|
Loading…
Reference in a new issue