From: "Andrew G. Morgan" <morgan@kernel.org>
To: Chris Wright <chrisw@sous-sol.org>
Cc: Dave Jones <davej@codemonkey.org.uk>,
Linux Kernel <linux-kernel@vger.kernel.org>,
bojan@rexursive.com, "Serge E. Hallyn" <serue@us.ibm.com>,
Andrew Morton <akpm@linux-foundation.org>,
Linux Security Modules List
<linux-security-module@vger.kernel.org>
Subject: [PATCH] security: was "Re: capget() overflows buffers."
Date: Mon, 26 May 2008 18:17:15 -0700 [thread overview]
Message-ID: <483B611B.6040006@kernel.org> (raw)
In-Reply-To: <20080524080734.GV30402@sequoia.sous-sol.org>
[-- Attachment #1: Type: text/plain, Size: 2479 bytes --]
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Chris Wright wrote:
| * Andrew G. Morgan (morgan@kernel.org) wrote:
|> Your concern is for the situation when the garbage happens to correspond
|> to an apparently meaningful setting for the upper capability bits? The
|> problem being that this privileged application is more privileged than
|> intended? I agree that this is not ideal.
|
| Yep, exactly.
|
|> In practice, however, this is only a real problem if named (or a
|> similarly structured program) has a security related bug in it. No?
|
| It's dropped privileges to help mitigate any security related bug it
| may contain. It's conceivable (albeit remote[1]) that fork/exec plus
| inheritable could leak privs w/out a security related bug.
|
|> Is this your concern, or have I missed something?
|
| That's it.
OK, so by way of summary, the kernel, per se, is *not* broken, but the
kernel include file is problematic for use by user space - ie., having
used it some recompiled programs may be subtly broken... [ Example,
https://bugzilla.redhat.com/show_bug.cgi?id=447518 ]
Basically I agree that we should err on the side of being conservative...
| thanks,
| -chris
|
| [1] Get lucky combo in the garbage bits and have not shed uid 0.
| Much less likely.
So far as I can tell, the two problems (for unprepared applications -
not using libcap etc.) are:
~ 1. what the capget() system call may be writing to data[1] may lead to
unpredictable reliability issues with the security of the running
program (when its only allocated space for data[0]).
~ 2. the garbage that the capset() system call may be setting in pI that
persists post-exec(). The security issue being (in the case that the
system has been configured with filesystem capability support) the leak
of inheritable bits that become effective through the subsequent
invocation of a filesystem-capable (fI != 0) application. The net result
being that this subsequent application gives capabilities to a user that
shouldn't wield them.
How about the attached for a combined patch? Chris, the only change over
last time is basically your suggested code change, with more comments
and a less cautious warning...
Cheers
Andrew
Ref: patch: 0c736c9f0ab16899df1803d5962287985e69a157
and libcap-2.10 supports this change.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.6 (GNU/Linux)
iD8DBQFIO2Ea+bHCR3gb8jsRAowdAJ9kMa15tXLyv6t1EfV0pyOsbqk49QCgsjRJ
+SCiUsbN7M5nfdehXBWjzt0=
=Ri1u
-----END PGP SIGNATURE-----
[-- Attachment #2: remain-source-compatible-with-32-bit-raw-legacy-capa.patch --]
[-- Type: text/plain, Size: 9735 bytes --]
From 0c736c9f0ab16899df1803d5962287985e69a157 Mon Sep 17 00:00:00 2001
From: Andrew G. Morgan <morgan@kernel.org>
Date: Mon, 26 May 2008 18:06:27 -0700
Subject: [PATCH] Remain source compatible with 32-bit raw legacy capability support.
Source code out there hard-codes a notion of what the
_LINUX_CAPABILITY_VERSION #define means in terms of the semantics of
the raw capability system calls capget() and capset(). Its unfortunate,
but true.
Since the confusing header file has been in a released kernel, there
is software that is erroneously using 64-bit capabilities with the
semantics of 32-bit compatibilities. These recently compiled programs
may suffer corruption of their memory when sys_getcap() overwrites
more memory than they are coded to expect, and the raising of added
capabilities when using sys_capset().
As such, this patch does a number of things to clean up the situation
for all. It
1. forces the _LINUX_CAPABILITY_VERSION define to always retain its
legacy value.
2. adopts a new #define strategy for the kernel's internal
implementation of the preferred magic.
3. depreciates v2 capability magic in favor of a new (v3) magic
number. The functionality of v3 is entirely equivalent to v2,
the only difference being that the v2 magic causes the kernel
to log a "depreciated" warning so the admin can find applications
that may be using v2 inappropriately.
[User space code continues to be encouraged to use the libcap API
which protects the application from details like this. libcap-2.10
is the first to support v3 capabilities.]
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Chris Wright <chrisw@sous-sol.org>
Cc: Serge E. Hallyn <serue@us.ibm.com>
---
fs/proc/array.c | 2 +-
include/linux/capability.h | 29 ++++++++---
kernel/capability.c | 110 +++++++++++++++++++++++++++++---------------
3 files changed, 94 insertions(+), 47 deletions(-)
diff --git a/fs/proc/array.c b/fs/proc/array.c
index 9e3b8c3..797d775 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -288,7 +288,7 @@ static void render_cap_t(struct seq_file *m, const char *header,
seq_printf(m, "%s", header);
CAP_FOR_EACH_U32(__capi) {
seq_printf(m, "%08x",
- a->cap[(_LINUX_CAPABILITY_U32S-1) - __capi]);
+ a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]);
}
seq_printf(m, "\n");
}
diff --git a/include/linux/capability.h b/include/linux/capability.h
index f4ea0dd..272e040 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -31,11 +31,11 @@ struct task_struct;
#define _LINUX_CAPABILITY_VERSION_1 0x19980330
#define _LINUX_CAPABILITY_U32S_1 1
-#define _LINUX_CAPABILITY_VERSION_2 0x20071026
+#define _LINUX_CAPABILITY_VERSION_2 0x20071026 /* depreciated - use v3 */
#define _LINUX_CAPABILITY_U32S_2 2
-#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_2
-#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_2
+#define _LINUX_CAPABILITY_VERSION_3 0x20080522
+#define _LINUX_CAPABILITY_U32S_3 2
typedef struct __user_cap_header_struct {
__u32 version;
@@ -77,10 +77,23 @@ struct vfs_cap_data {
} data[VFS_CAP_U32];
};
-#ifdef __KERNEL__
+#ifndef __KERNEL__
+
+/*
+ * Backwardly compatible definition for source code - trapped in a
+ * 32-bit world. If you find you need this, please consider using
+ * libcap to untrap yourself...
+ */
+#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_1
+#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_1
+
+#else
+
+#define _KERNEL_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_3
+#define _KERNEL_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3
typedef struct kernel_cap_struct {
- __u32 cap[_LINUX_CAPABILITY_U32S];
+ __u32 cap[_KERNEL_CAPABILITY_U32S];
} kernel_cap_t;
#define _USER_CAP_HEADER_SIZE (sizeof(struct __user_cap_header_struct))
@@ -351,7 +364,7 @@ typedef struct kernel_cap_struct {
*/
#define CAP_FOR_EACH_U32(__capi) \
- for (__capi = 0; __capi < _LINUX_CAPABILITY_U32S; ++__capi)
+ for (__capi = 0; __capi < _KERNEL_CAPABILITY_U32S; ++__capi)
# define CAP_FS_MASK_B0 (CAP_TO_MASK(CAP_CHOWN) \
| CAP_TO_MASK(CAP_DAC_OVERRIDE) \
@@ -361,7 +374,7 @@ typedef struct kernel_cap_struct {
# define CAP_FS_MASK_B1 (CAP_TO_MASK(CAP_MAC_OVERRIDE))
-#if _LINUX_CAPABILITY_U32S != 2
+#if _KERNEL_CAPABILITY_U32S != 2
# error Fix up hand-coded capability macro initializers
#else /* HAND-CODED capability initializers */
@@ -372,7 +385,7 @@ typedef struct kernel_cap_struct {
# define CAP_NFSD_SET ((kernel_cap_t){{ CAP_FS_MASK_B0|CAP_TO_MASK(CAP_SYS_RESOURCE), \
CAP_FS_MASK_B1 } })
-#endif /* _LINUX_CAPABILITY_U32S != 2 */
+#endif /* _KERNEL_CAPABILITY_U32S != 2 */
#define CAP_INIT_INH_SET CAP_EMPTY_SET
diff --git a/kernel/capability.c b/kernel/capability.c
index 39e8193..326406c 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -53,6 +53,68 @@ static void warn_legacy_capability_use(void)
}
/*
+ * Version 2 capabilities worked fine, but the linux/capability.h file
+ * that accompanied their introduction encouraged their use without
+ * the necessary user-space source code changes. As such, we have
+ * created a version 3 with equivalent functionality to version 2, but
+ * with a header change to protect legacy source code from using
+ * version 2 when it wanted to use version 1. If your system has code
+ * that trips the following warning, it is using version 2 specific
+ * capabilities and may be doing so insecurely.
+ *
+ * The remedy is to either upgrade your version of libcap (to 2.10+,
+ * if the application is linked against it), or recompile your
+ * application with modern kernel headers and this warning will go
+ * away.
+ */
+
+static void warn_depreciated_v2(void)
+{
+ static int warned = 0;
+ if (!warned) {
+ char name[sizeof(current->comm)];
+
+ printk(KERN_INFO "warning: `%s' uses depreciated v2"
+ " capabilities in a way that may be insecure.\n",
+ get_task_comm(name, current));
+ warned = 1;
+ }
+}
+
+/*
+ * Version check. Return the number of u32s in each capability flag
+ * array, or a negative value on error.
+ */
+static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
+{
+ __u32 version;
+
+ if (get_user(version, &header->version))
+ return -EFAULT;
+
+ switch (version) {
+ case _LINUX_CAPABILITY_VERSION_1:
+ warn_legacy_capability_use();
+ *tocopy = _LINUX_CAPABILITY_U32S_1;
+ break;
+ case _LINUX_CAPABILITY_VERSION_2:
+ warn_depreciated_v2();
+ /*
+ * fall through - v3 is otherwise equivalent to v2.
+ */
+ case _LINUX_CAPABILITY_VERSION_3:
+ *tocopy = _LINUX_CAPABILITY_U32S_3;
+ break;
+ default:
+ if (put_user(_KERNEL_CAPABILITY_VERSION, &header->version))
+ return -EFAULT;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
* For sys_getproccap() and sys_setproccap(), any of the three
* capability set pointers may be NULL -- indicating that that set is
* uninteresting and/or not to be changed.
@@ -71,27 +133,13 @@ asmlinkage long sys_capget(cap_user_header_t header, cap_user_data_t dataptr)
{
int ret = 0;
pid_t pid;
- __u32 version;
struct task_struct *target;
unsigned tocopy;
kernel_cap_t pE, pI, pP;
- if (get_user(version, &header->version))
- return -EFAULT;
-
- switch (version) {
- case _LINUX_CAPABILITY_VERSION_1:
- warn_legacy_capability_use();
- tocopy = _LINUX_CAPABILITY_U32S_1;
- break;
- case _LINUX_CAPABILITY_VERSION_2:
- tocopy = _LINUX_CAPABILITY_U32S_2;
- break;
- default:
- if (put_user(_LINUX_CAPABILITY_VERSION, &header->version))
- return -EFAULT;
- return -EINVAL;
- }
+ ret = cap_validate_magic(header, &tocopy);
+ if (ret != 0)
+ return ret;
if (get_user(pid, &header->pid))
return -EFAULT;
@@ -118,7 +166,7 @@ out:
spin_unlock(&task_capability_lock);
if (!ret) {
- struct __user_cap_data_struct kdata[_LINUX_CAPABILITY_U32S];
+ struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
unsigned i;
for (i = 0; i < tocopy; i++) {
@@ -128,7 +176,7 @@ out:
}
/*
- * Note, in the case, tocopy < _LINUX_CAPABILITY_U32S,
+ * Note, in the case, tocopy < _KERNEL_CAPABILITY_U32S,
* we silently drop the upper capabilities here. This
* has the effect of making older libcap
* implementations implicitly drop upper capability
@@ -240,30 +288,16 @@ static inline int cap_set_all(kernel_cap_t *effective,
*/
asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
{
- struct __user_cap_data_struct kdata[_LINUX_CAPABILITY_U32S];
+ struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
unsigned i, tocopy;
kernel_cap_t inheritable, permitted, effective;
- __u32 version;
struct task_struct *target;
int ret;
pid_t pid;
- if (get_user(version, &header->version))
- return -EFAULT;
-
- switch (version) {
- case _LINUX_CAPABILITY_VERSION_1:
- warn_legacy_capability_use();
- tocopy = _LINUX_CAPABILITY_U32S_1;
- break;
- case _LINUX_CAPABILITY_VERSION_2:
- tocopy = _LINUX_CAPABILITY_U32S_2;
- break;
- default:
- if (put_user(_LINUX_CAPABILITY_VERSION, &header->version))
- return -EFAULT;
- return -EINVAL;
- }
+ ret = cap_validate_magic(header, &tocopy);
+ if (ret != 0)
+ return ret;
if (get_user(pid, &header->pid))
return -EFAULT;
@@ -281,7 +315,7 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
permitted.cap[i] = kdata[i].permitted;
inheritable.cap[i] = kdata[i].inheritable;
}
- while (i < _LINUX_CAPABILITY_U32S) {
+ while (i < _KERNEL_CAPABILITY_U32S) {
effective.cap[i] = 0;
permitted.cap[i] = 0;
inheritable.cap[i] = 0;
--
1.5.3.7
next prev parent reply other threads:[~2008-05-27 1:17 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-05-22 14:04 capget() overflows buffers Dave Jones
2008-05-22 17:58 ` Chris Wright
2008-05-22 20:53 ` Chris Wright
2008-05-22 22:52 ` Andrew G. Morgan
2008-05-22 23:37 ` Chris Wright
2008-05-23 7:09 ` Andrew G. Morgan
2008-05-23 15:57 ` Chris Wright
2008-05-24 6:25 ` Andrew G. Morgan
2008-05-24 8:07 ` Chris Wright
2008-05-27 1:17 ` Andrew G. Morgan [this message]
2008-05-27 21:42 ` [PATCH] security: was "Re: capget() overflows buffers." Chris Wright
2008-05-28 3:33 ` Andrew Morton
2008-05-23 18:26 ` capget() overflows buffers Chris Wright
2008-05-24 0:02 ` Andrew G. Morgan
2008-05-24 1:09 ` Chris Wright
2008-05-24 4:40 ` Andrew G. Morgan
2008-05-24 8:17 ` Chris Wright
2008-05-23 1:20 ` Bojan Smojver
2008-05-23 2:06 ` Chris Wright
2008-05-23 4:01 ` Bojan Smojver
2008-05-22 21:20 ` Bojan Smojver
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=483B611B.6040006@kernel.org \
--to=morgan@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=bojan@rexursive.com \
--cc=chrisw@sous-sol.org \
--cc=davej@codemonkey.org.uk \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=serue@us.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.