| From: |
| "Andrew G. Morgan" <morgan@kernel.org> |
| To: |
| linux-security-module@vger.kernel.org |
| Subject: |
| [RFC PATCH] Add secure bit to prevent set[ug]id executables from exec()ing. |
| Date: |
| Sun, 3 Jan 2010 19:26:36 +0000 |
| Cc: |
| "Eric W. Biederman" <ebiederm@xmission.com>,
"Serge E. Hallyn" <serue@us.ibm.com>,
"Andrew G. Morgan" <morgan@kernel.org> |
| Archive-link: |
| Article, Thread
|
Capabilities are an alternative privilege implementation to the traditional
UN*X model of an all powerful superuser ('root'). Per-process secure-bits
exist to turn off the default privilege for the superuser, but they do not
do anything about the fact that, in a typical system, most of the important
system files are owned by root (and other system users). So, while it is
possible to protect the kernel from root with secure-bits, it has been
harder to protect the system from a rogue setuid-root program.
This patch adds a new per-process secure-bit (bit 6) with a corresponding
lock-bit (bit 7) to deny exec()ution of any setuid program that would
modify the euid/egid of the invoking user.
Here is a fully worked example of how to use this new feature:
// --8<----- cut here [this is limiter.c]
/*
* Quick demo of blocking privilege (without accompanying kernel patch
* change the 0xef to 0x2f below, which will not attempt to disable
* setuid executables).
*/
#include <stdio.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <stdlib.h>
int main(int argc, char *argv[], char *envp[])
{
if (argc < 2) {
fprintf(stderr, "usage: %s <execv args>\n", argv[0]);
exit(1);
}
cap_t needed = cap_from_text("cap_setpcap=ep");
if (cap_set_proc(needed) != 0) {
perror("cap_set_proc failed");
exit(1);
}
int cap = 0;
int set;
while ((set = prctl(PR_CAPBSET_READ, cap)) >= 0) {
if (set && prctl(PR_CAPBSET_DROP, cap)) {
fprintf(stderr, "failed to drop bset capability: %s\n",
cap_to_name(cap));
exit(1);
}
cap++;
}
if (prctl(PR_SET_SECUREBITS, 0xef /* magic combination */)) {
perror("unable lock secure-bits");
exit(1);
}
fprintf(stderr, "[feeling powerless]\n");
execve(argv[1], argv + 1, envp);
fprintf(stderr, "[execve(\"%s\",...) failed - try something else.]\n",
argv[1]);
exit(1);
}
// ---- >8-- [end of limiter.c]
To compile this use libcap2 which can be found here:
http://www.kernel.org/pub/linux/libs/security/linux-privs...
Compilation:
admin> cc -o limiter limiter.c -lcap
admin> sudo /usr/sbin/setcap cap_setpcap=p ./limiter
Use:
luser> ./limiter /bin/bash
[feeling powerless]
luser> ...try something privileged, like 'su' should get not permitted.
luser> ...look at /proc/self/status etc.
luser> ...or try capsh --print
luser> exit
luser> ...back in parent shell
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
---
include/linux/securebits.h | 15 ++++++++++++++-
security/commoncap.c | 13 ++++++++++++-
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/include/linux/securebits.h b/include/linux/securebits.h
index 3340617..c931442 100644
--- a/include/linux/securebits.h
+++ b/include/linux/securebits.h
@@ -46,9 +46,22 @@
#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS))
#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED))
+/*
+ * When set, any setuid program that would otherwise cause e[ug]id of
+ * an exec()d application to differ from the current [ug]id of the
+ * exec()ing process, prevent the exec() from succeeding - return with
+ * -EPERM.
+ */
+#define SECURE_NO_SETUID_EXEC 6
+#define SECURE_NO_SETUID_EXECLOCKED 7 /* make bit-6 immutable */
+
+#define SECBIT_NO_SETUID_EXEC (issecure_mask(SECURE_NO_SETUID_EXEC))
+#define SECBIT_NO_SETUID_LOCKED (issecure_mask(SECURE_NO_SETUID_EXEC_LOCKED))
+
#define SECURE_ALL_BITS (issecure_mask(SECURE_NOROOT) | \
issecure_mask(SECURE_NO_SETUID_FIXUP) | \
- issecure_mask(SECURE_KEEP_CAPS))
+ issecure_mask(SECURE_KEEP_CAPS) | \
+ issecure_mask(SECURE_NO_SETUID_EXEC))
#define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
#endif /* !_LINUX_SECUREBITS_H */
diff --git a/security/commoncap.c b/security/commoncap.c
index f800fdb..3386f09 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -432,6 +432,10 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
bool effective;
int ret;
+ if (issecure(SECURE_NO_SETUID_EXEC) &&
+ ((new->euid != old->uid) || (new->egid != old->gid)))
+ return -EPERM; /* refuse to honor set[ug]id transitions */
+
effective = false;
ret = get_file_caps(bprm, &effective);
if (ret < 0)
@@ -569,7 +573,7 @@ int cap_inode_setxattr(struct dentry *dentry, const char *name,
}
if (!strncmp(name, XATTR_SECURITY_PREFIX,
- sizeof(XATTR_SECURITY_PREFIX) - 1) &&
+ sizeof(XATTR_SECURITY_PREFIX) - 1) &&
!capable(CAP_SYS_ADMIN))
return -EPERM;
return 0;
@@ -825,6 +829,13 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3,
* will ensure that the current process and all of its
* children will be locked into a pure
* capability-based-privilege environment.
+ *
+ * In addition, some DAC policies include permitting users to
+ * create setuid applications that permit third-party users
+ * access to their resources. We provide a secure-bit to
+ * prevent the execution of such applications by a
+ * third-party. This bit with the corresponding LOCKED bit is:
+ * issecure_mask(SECURE_NO_SETUID_EXEC).
*/
case PR_SET_SECUREBITS:
error = -EPERM;
--
1.6.2.5
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html