diff mbox

don't allow CAP_NET_ADMIN to load non-netdev kernel modules

Message ID 20110225151414.GA5211@albatros
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Vasiliy Kulikov Feb. 25, 2011, 3:14 p.m. UTC
Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with
CAP_NET_ADMIN may load any module from /lib/modules/.  This doesn't mean
that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited
to /lib/modules/**.  However, CAP_NET_ADMIN capability shouldn't allow
anybody load any module not related to networking.

This patch restricts an ability of autoloading modules to netdev modules
with explicit aliases.  Currently there are only three users of the
feature: ipip, ip_gre and sit.

Before the patch:

    root@albatros:~# grep Cap /proc/$$/status
    CapInh: 0000000000000000
    CapPrm: fffffffc00001000
    CapEff: fffffffc00001000
    CapBnd: fffffffc00001000
    root@albatros:~# lsmod | grep xfs
    root@albatros:~# ifconfig xfs
    xfs: error fetching interface information: Device not found
    root@albatros:~# lsmod | grep xfs
    xfs                   767011  0
    exportfs                4226  2 xfs,nfsd

After:

    root@albatros:~# grep Cap /proc/$$/status
    CapInh: 0000000000000000
    CapPrm: ffffffffffffffff
    CapEff: ffffffffffffffff
    CapBnd: ffffffffffffffff
    root@albatros:~# lsmod | grep scsi_wait_scan
    root@albatros:~# ifconfig scsi_wait_scan
    scsi_wait_scan: error fetching interface information: Device not found
    root@albatros:~# lsmod | grep scsi_wait_scan
    root@albatros:~# modprobe scsi_wait_scan
    root@albatros:~# lsmod | grep scsi_wait_scan
    scsi_wait_scan           829  0

Reference: http://www.openwall.com/lists/oss-security/2011/02/24/17

Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
---
 include/linux/netdevice.h |    3 +++
 net/core/dev.c            |    2 +-
 net/ipv4/ip_gre.c         |    2 +-
 net/ipv4/ipip.c           |    2 +-
 net/ipv6/sit.c            |    2 +-
 5 files changed, 7 insertions(+), 4 deletions(-)

Comments

Valdis Kl ē tnieks Feb. 25, 2011, 5:25 p.m. UTC | #1
On Fri, 25 Feb 2011 18:14:14 +0300, Vasiliy Kulikov said:
> Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with
> CAP_NET_ADMIN may load any module from /lib/modules/.  This doesn't mean
> that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited
> to /lib/modules/**.  However, CAP_NET_ADMIN capability shouldn't allow
> anybody load any module not related to networking.
> 
> This patch restricts an ability of autoloading modules to netdev modules
> with explicit aliases.  Currently there are only three users of the
> feature: ipip, ip_gre and sit.

And you stop an attacker from simply recompiling the module with a suitable
MODULE_ALIAS line added, how, exactly?  This patch may make sense down the
road, but not while it's still trivial for a malicious root user to drop stuff
into /lib/modules.

And if you're going the route "but SELinux/SMACK/Tomoyo will prevent a malicious
root user from doing that", then the obvious reply is "this should be part of those
subsystems rather than something done one-off like this (especially as it has a chance
of breaking legitimate setups that use the current scheme).
Vasiliy Kulikov Feb. 25, 2011, 5:47 p.m. UTC | #2
On Fri, Feb 25, 2011 at 12:25 -0500, Valdis.Kletnieks@vt.edu wrote:
> And you stop an attacker from simply recompiling the module with a suitable
> MODULE_ALIAS line added, how, exactly?  This patch may make sense down the
> road, but not while it's still trivial for a malicious root user to drop stuff
> into /lib/modules.

The threat is not a malicious root, but non-root with CAP_NET_ADMIN.
It's hardly possible to load arbitrary module into the kernel having
CAP_NET_ADMIN without other capabilities.

> And if you're going the route "but SELinux/SMACK/Tomoyo will prevent a malicious
> root user from doing that", then the obvious reply is "this should be part of those
> subsystems rather than something done one-off like this (especially as it has a chance
> of breaking legitimate setups that use the current scheme).

No, I don't want to add anything about LSMs at all.


Thanks,
Ben Hutchings Feb. 25, 2011, 5:48 p.m. UTC | #3
On Fri, 2011-02-25 at 12:25 -0500, Valdis.Kletnieks@vt.edu wrote:
> On Fri, 25 Feb 2011 18:14:14 +0300, Vasiliy Kulikov said:
> > Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with
> > CAP_NET_ADMIN may load any module from /lib/modules/.  This doesn't mean
> > that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited
> > to /lib/modules/**.  However, CAP_NET_ADMIN capability shouldn't allow
> > anybody load any module not related to networking.
> > 
> > This patch restricts an ability of autoloading modules to netdev modules
> > with explicit aliases.  Currently there are only three users of the
> > feature: ipip, ip_gre and sit.
> 
> And you stop an attacker from simply recompiling the module with a suitable
> MODULE_ALIAS line added, how, exactly?  This patch may make sense down the
> road, but not while it's still trivial for a malicious root user to drop stuff
> into /lib/modules.

A process running as root normally has CAP_NET_ADMIN, but not every
process with CAP_NET_ADMIN will be running as root.

> And if you're going the route "but SELinux/SMACK/Tomoyo will prevent a malicious
> root user from doing that", then the obvious reply is "this should be part of those
> subsystems rather than something done one-off like this (especially as it has a chance
> of breaking legitimate setups that use the current scheme).

The notional attacker has CAP_NET_ADMIN, perhaps through a vulnerable
service or a vulnerable set-capability executable.  They do not yet have
full root access and so cannot install a module, even in the absence of
an LSM.

So long as the attacker is able to load arbitrary modules, however, they
could exploit a vulnerability in any installed (not loaded) module.
Again, LSMs are irrelevant to this as they do not protect against kernel
bugs.

Ben.
David Miller Feb. 25, 2011, 6:47 p.m. UTC | #4
From: Vasiliy Kulikov <segoon@openwall.com>
Date: Fri, 25 Feb 2011 18:14:14 +0300

> Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with
> CAP_NET_ADMIN may load any module from /lib/modules/.  This doesn't mean
> that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited
> to /lib/modules/**.  However, CAP_NET_ADMIN capability shouldn't allow
> anybody load any module not related to networking.

Why go through this naming change, which does break things, instead of
simply adding a capability mask tag or similar to modules somehow.  You
could stick it into a special elf section or similar.

Doesn't that make tons more sense than this?
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vasiliy Kulikov Feb. 25, 2011, 7:02 p.m. UTC | #5
On Fri, Feb 25, 2011 at 10:47 -0800, David Miller wrote:
> From: Vasiliy Kulikov <segoon@openwall.com>
> Date: Fri, 25 Feb 2011 18:14:14 +0300
> 
> > Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with
> > CAP_NET_ADMIN may load any module from /lib/modules/.  This doesn't mean
> > that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited
> > to /lib/modules/**.  However, CAP_NET_ADMIN capability shouldn't allow
> > anybody load any module not related to networking.
> 
> Why go through this naming change, which does break things, instead of
> simply adding a capability mask tag or similar to modules somehow.  You
> could stick it into a special elf section or similar.
>
> Doesn't that make tons more sense than this?

This is not "simply", adding special section for a single workaround
seems like an overkill for me - this touches the core (modules'
internals), which is not related to the initial CAP_* problem at all.

I'd be happy with not breaking anything, but I don't see any acceptable
solution.


Thanks,
David Miller Feb. 25, 2011, 7:05 p.m. UTC | #6
From: Vasiliy Kulikov <segoon@openwall.com>
Date: Fri, 25 Feb 2011 22:02:05 +0300

> On Fri, Feb 25, 2011 at 10:47 -0800, David Miller wrote:
>> From: Vasiliy Kulikov <segoon@openwall.com>
>> Date: Fri, 25 Feb 2011 18:14:14 +0300
>> 
>> > Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with
>> > CAP_NET_ADMIN may load any module from /lib/modules/.  This doesn't mean
>> > that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited
>> > to /lib/modules/**.  However, CAP_NET_ADMIN capability shouldn't allow
>> > anybody load any module not related to networking.
>> 
>> Why go through this naming change, which does break things, instead of
>> simply adding a capability mask tag or similar to modules somehow.  You
>> could stick it into a special elf section or similar.
>>
>> Doesn't that make tons more sense than this?
> 
> This is not "simply", adding special section for a single workaround
> seems like an overkill for me - this touches the core (modules'
> internals), which is not related to the initial CAP_* problem at all.
> 
> I'd be happy with not breaking anything, but I don't see any acceptable
> solution.

I think it's warranted given that it allows us to avoid breaking things.

I don't understand there is resistence in response to the first idea
I've seen proprosed that actually allows to fix the problem and not
break anything at the same time.

That seems silly.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings Feb. 25, 2011, 7:07 p.m. UTC | #7
On Fri, 2011-02-25 at 11:05 -0800, David Miller wrote:
> From: Vasiliy Kulikov <segoon@openwall.com>
> Date: Fri, 25 Feb 2011 22:02:05 +0300
> 
> > On Fri, Feb 25, 2011 at 10:47 -0800, David Miller wrote:
> >> From: Vasiliy Kulikov <segoon@openwall.com>
> >> Date: Fri, 25 Feb 2011 18:14:14 +0300
> >> 
> >> > Since a8f80e8ff94ecba629542d9b4b5f5a8ee3eb565c any process with
> >> > CAP_NET_ADMIN may load any module from /lib/modules/.  This doesn't mean
> >> > that CAP_NET_ADMIN is a superset of CAP_SYS_MODULE as modules are limited
> >> > to /lib/modules/**.  However, CAP_NET_ADMIN capability shouldn't allow
> >> > anybody load any module not related to networking.
> >> 
> >> Why go through this naming change, which does break things, instead of
> >> simply adding a capability mask tag or similar to modules somehow.  You
> >> could stick it into a special elf section or similar.
> >>
> >> Doesn't that make tons more sense than this?
> > 
> > This is not "simply", adding special section for a single workaround
> > seems like an overkill for me - this touches the core (modules'
> > internals), which is not related to the initial CAP_* problem at all.
> > 
> > I'd be happy with not breaking anything, but I don't see any acceptable
> > solution.
> 
> I think it's warranted given that it allows us to avoid breaking things.
> 
> I don't understand there is resistence in response to the first idea
> I've seen proprosed that actually allows to fix the problem and not
> break anything at the same time.
> 
> That seems silly.

You realise that module loading doesn't actually run in the context of
request_module(), right?

Ben.
David Miller Feb. 25, 2011, 7:16 p.m. UTC | #8
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Fri, 25 Feb 2011 19:07:59 +0000

> You realise that module loading doesn't actually run in the context of
> request_module(), right?

Why is that a barrier?  We could simply pass a capability mask into
request_module if necessary.

It's an implementation detail, and not a deterrant to my suggested
scheme.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings Feb. 25, 2011, 7:30 p.m. UTC | #9
On Fri, 2011-02-25 at 11:16 -0800, David Miller wrote:
> From: Ben Hutchings <bhutchings@solarflare.com>
> Date: Fri, 25 Feb 2011 19:07:59 +0000
> 
> > You realise that module loading doesn't actually run in the context of
> > request_module(), right?
> 
> Why is that a barrier?  We could simply pass a capability mask into
> request_module if necessary.
> 
> It's an implementation detail, and not a deterrant to my suggested
> scheme.

It's not an implementation detail.  modprobe currently runs with full
capabilities; your proposal requires its capabilities to be limited to
those of the capabilities of the process that triggered the
request_module() (plus, presumably, CAP_SYS_MODULE).

Now modprobe doesn't have CAP_DAC_OVERRIDE and can't read modprobe
configuration files that belong to users other than root.

It doesn't have CAP_SYS_MKNOD so it can't run hooks that call mknod.

etc.

Ben.
David Miller Feb. 25, 2011, 7:43 p.m. UTC | #10
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Fri, 25 Feb 2011 19:30:16 +0000

> On Fri, 2011-02-25 at 11:16 -0800, David Miller wrote:
>> From: Ben Hutchings <bhutchings@solarflare.com>
>> Date: Fri, 25 Feb 2011 19:07:59 +0000
>> 
>> > You realise that module loading doesn't actually run in the context of
>> > request_module(), right?
>> 
>> Why is that a barrier?  We could simply pass a capability mask into
>> request_module if necessary.
>> 
>> It's an implementation detail, and not a deterrant to my suggested
>> scheme.
> 
> It's not an implementation detail.  modprobe currently runs with full
> capabilities; your proposal requires its capabilities to be limited to
> those of the capabilities of the process that triggered the
> request_module() (plus, presumably, CAP_SYS_MODULE).

The idea was that the kernel will be the entity that will inspect the
elf sections and validate the capability bits, not the userspace
module loader.

Surely we if we can pass an arbitrary string out to the loading
process as part of the module loading context, we can pass along
capability bits as well.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ben Hutchings Feb. 25, 2011, 7:53 p.m. UTC | #11
On Fri, 2011-02-25 at 11:43 -0800, David Miller wrote:
> From: Ben Hutchings <bhutchings@solarflare.com>
> Date: Fri, 25 Feb 2011 19:30:16 +0000
> 
> > On Fri, 2011-02-25 at 11:16 -0800, David Miller wrote:
> >> From: Ben Hutchings <bhutchings@solarflare.com>
> >> Date: Fri, 25 Feb 2011 19:07:59 +0000
> >> 
> >> > You realise that module loading doesn't actually run in the context of
> >> > request_module(), right?
> >> 
> >> Why is that a barrier?  We could simply pass a capability mask into
> >> request_module if necessary.
> >> 
> >> It's an implementation detail, and not a deterrant to my suggested
> >> scheme.
> > 
> > It's not an implementation detail.  modprobe currently runs with full
> > capabilities; your proposal requires its capabilities to be limited to
> > those of the capabilities of the process that triggered the
> > request_module() (plus, presumably, CAP_SYS_MODULE).
> 
> The idea was that the kernel will be the entity that will inspect the
> elf sections and validate the capability bits, not the userspace
> module loader.

Yes, I understand that.

> Surely we if we can pass an arbitrary string out to the loading
> process as part of the module loading context, we can pass along
> capability bits as well.

If you want insert_module() to be able to deny loading some modules
based on the capabilities of the process calling request_module() then
you either have to *reduce* the capabilities given to modprobe or create
some extra process state, separate from the usual capability state,
specifically for this purpose.

Ben.
David Miller Feb. 25, 2011, 8:37 p.m. UTC | #12
From: Ben Hutchings <bhutchings@solarflare.com>
Date: Fri, 25 Feb 2011 19:53:05 +0000

> On Fri, 2011-02-25 at 11:43 -0800, David Miller wrote:
>> Surely we if we can pass an arbitrary string out to the loading
>> process as part of the module loading context, we can pass along
>> capability bits as well.
> 
> If you want insert_module() to be able to deny loading some modules
> based on the capabilities of the process calling request_module() then
> you either have to *reduce* the capabilities given to modprobe or create
> some extra process state, separate from the usual capability state,
> specifically for this purpose.

How is this any different from the patch posted which ties
capabilities to the prefix of name of the module to be loaded?

There is simply no difference, except that in my proposal existing
things do not break since the module name will not change.

I don't see where the complexity is, if the only place we can pass the
capability bits is in the execv args, then in the worst case we could
take a peek at those in the module load system call.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vasiliy Kulikov Feb. 27, 2011, 11:44 a.m. UTC | #13
David,

On Fri, Feb 25, 2011 at 11:16 -0800, David Miller wrote:
> From: Ben Hutchings <bhutchings@solarflare.com>
> Date: Fri, 25 Feb 2011 19:07:59 +0000
> 
> > You realise that module loading doesn't actually run in the context of
> > request_module(), right?
> 
> Why is that a barrier?  We could simply pass a capability mask into
> request_module if necessary.
> 
> It's an implementation detail, and not a deterrant to my suggested
> scheme.

Let's discuss your scheme.  AFAIU, you suggest to change:


1. a) request_module("%s", devname) =>
request_module_with_caps(CAP_NET_ADMIN, "%s", devname)

   b) call_usermodehelper() => call_usermodehelper_with_caps()

   c) add some bits/sections into kernel module image indicating that
this module is safe to be loaded via CAP_NET_ADMIN

   d) run modprobe with CAP_NET_ADMIN only

   e) in load_module() check whether (the process has CAP_SYS_MODULE) or
(the process has CAP_NET_ADMIN and bit SAFE_NET_MODULE is raised in
the module image)

This obviously doesn't work - the kernel is not able to verify whether
the bit/section is not malformed by user with CAP_NET_ADMIN.


-OR-


1. a) request_module("%s", devname) => request_module_with_argument("--netdev", "%s", devname)

   b) patch modprobe to add "--netmodule-only" argument (or bitmask,
whatever), this would indicate that only net/** modules may be loaded.

Then the things are still broken - a user has to update modprobe
together with the kernel, otherwise the updated kernel would call
"modprobe" with unsupported argument and even "sit0" wouldn't work.


Additionally this touches module loading process, which is not buggy.


Or you propose something else besides these 2 ways?  Please clarify.


Thanks,
David Miller Feb. 27, 2011, 11:18 p.m. UTC | #14
From: Vasiliy Kulikov <segoon@openwall.com>
Date: Sun, 27 Feb 2011 14:44:38 +0300

>    d) run modprobe with CAP_NET_ADMIN only

This is not part of my scheme.

The module loading will run with existing module loading privileges,
the "allowed capability" bits will be passed along back into the
kernel at module load time (via modprobe arguments or similar)
and validated by the kernel as it walks the ELF sections anyways
to perform relocations and whatnot.

--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Miller Feb. 27, 2011, 11:19 p.m. UTC | #15
From: Vasiliy Kulikov <segoon@openwall.com>
Date: Sun, 27 Feb 2011 14:44:38 +0300

> Then the things are still broken - a user has to update modprobe
> together with the kernel, otherwise the updated kernel would call
> "modprobe" with unsupported argument and even "sit0" wouldn't work.

The capability bits get passed on the modprobe command line.

The module loading system call in the kernel inspects the command
line looking for the argument, and uses it to validate the module
load by comparing the mask with the special ELF section.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index d971346..71caf7a 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2392,6 +2392,9 @@  extern int netdev_notice(const struct net_device *dev, const char *format, ...)
 extern int netdev_info(const struct net_device *dev, const char *format, ...)
 	__attribute__ ((format (printf, 2, 3)));
 
+#define MODULE_ALIAS_NETDEV(device) \
+	MODULE_ALIAS("netdev-" device)
+
 #if defined(DEBUG)
 #define netdev_dbg(__dev, format, args...)			\
 	netdev_printk(KERN_DEBUG, __dev, format, ##args)
diff --git a/net/core/dev.c b/net/core/dev.c
index 8ae6631..79b33d9 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1120,7 +1120,7 @@  void dev_load(struct net *net, const char *name)
 	rcu_read_unlock();
 
 	if (!dev && capable(CAP_NET_ADMIN))
-		request_module("%s", name);
+		request_module("netdev-%s", name);
 }
 EXPORT_SYMBOL(dev_load);
 
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index 6613edf..d1d0e2c 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -1765,4 +1765,4 @@  module_exit(ipgre_fini);
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_RTNL_LINK("gre");
 MODULE_ALIAS_RTNL_LINK("gretap");
-MODULE_ALIAS("gre0");
+MODULE_ALIAS_NETDEV("gre0");
diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c
index 988f52f..a5f58e7 100644
--- a/net/ipv4/ipip.c
+++ b/net/ipv4/ipip.c
@@ -913,4 +913,4 @@  static void __exit ipip_fini(void)
 module_init(ipip_init);
 module_exit(ipip_fini);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("tunl0");
+MODULE_ALIAS_NETDEV("tunl0");
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 8ce38f1..d2c16e1 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -1290,4 +1290,4 @@  static int __init sit_init(void)
 module_init(sit_init);
 module_exit(sit_cleanup);
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("sit0");
+MODULE_ALIAS_NETDEV("sit0");