diff mbox

[1/3] Networking: use CAP_NET_ADMIN when deciding to call request_module

Message ID 20090813134451.29186.41664.stgit@paris.rdu.redhat.com
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Eric Paris Aug. 13, 2009, 1:44 p.m. UTC
The networking code checks CAP_SYS_MODULE before using request_module() to
try to load a kernel module.  While this seems reasonable it's actually
weakening system security since we have to allow CAP_SYS_MODULE for things
like /sbin/ip and bluetoothd which need to be able to trigger module loads.
CAP_SYS_MODULE actually grants those binaries the ability to directly load
any code into the kernel.  We should instead be protecting modprobe and the
modules on disk, rather than granting random programs the ability to load code
directly into the kernel.  Instead we are going to gate those networking checks
on CAP_NET_ADMIN which still limits them to root but which does not grant
those processes the ability to load arbitrary code into the kernel.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 drivers/staging/comedi/comedi_fops.c |    8 ++++----
 net/core/dev.c                       |    2 +-
 net/ipv4/tcp_cong.c                  |    4 ++--
 3 files changed, 7 insertions(+), 7 deletions(-)


--
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

Comments

Serge E. Hallyn Aug. 13, 2009, 2:01 p.m. UTC | #1
Quoting Eric Paris (eparis@redhat.com):
> The networking code checks CAP_SYS_MODULE before using request_module() to
> try to load a kernel module.  While this seems reasonable it's actually
> weakening system security since we have to allow CAP_SYS_MODULE for things
> like /sbin/ip and bluetoothd which need to be able to trigger module loads.
> CAP_SYS_MODULE actually grants those binaries the ability to directly load
> any code into the kernel.  We should instead be protecting modprobe and the
> modules on disk, rather than granting random programs the ability to load code
> directly into the kernel.  Instead we are going to gate those networking checks
> on CAP_NET_ADMIN which still limits them to root but which does not grant
> those processes the ability to load arbitrary code into the kernel.

Right, so we want to check that the caller has the rights to perform the
action which (in the end) requires the module load, not the module load
itself.  CAP_SYS_MODULE should be reserved for callers which specify a
module (full pathname) to load.

> Signed-off-by: Eric Paris <eparis@redhat.com>

Acked-by: Serge Hallyn <serue@us.ibm.com>

> ---
> 
>  drivers/staging/comedi/comedi_fops.c |    8 ++++----
>  net/core/dev.c                       |    2 +-
>  net/ipv4/tcp_cong.c                  |    4 ++--
>  3 files changed, 7 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
> index 42e4bc4..f54bb9b 100644
> --- a/drivers/staging/comedi/comedi_fops.c
> +++ b/drivers/staging/comedi/comedi_fops.c
> @@ -1772,12 +1772,12 @@ static int comedi_open(struct inode *inode, struct file *file)
>  	mutex_lock(&dev->mutex);
>  	if (dev->attached)
>  		goto ok;
> -	if (!capable(CAP_SYS_MODULE) && dev->in_request_module) {
> +	if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
>  		DPRINTK("in request module\n");
>  		mutex_unlock(&dev->mutex);
>  		return -ENODEV;
>  	}
> -	if (capable(CAP_SYS_MODULE) && dev->in_request_module)
> +	if (capable(CAP_NET_ADMIN) && dev->in_request_module)
>  		goto ok;
> 
>  	dev->in_request_module = 1;
> @@ -1790,8 +1790,8 @@ static int comedi_open(struct inode *inode, struct file *file)
> 
>  	dev->in_request_module = 0;
> 
> -	if (!dev->attached && !capable(CAP_SYS_MODULE)) {
> -		DPRINTK("not attached and not CAP_SYS_MODULE\n");
> +	if (!dev->attached && !capable(CAP_NET_ADMIN)) {
> +		DPRINTK("not attached and not CAP_NET_ADMIN\n");
>  		mutex_unlock(&dev->mutex);
>  		return -ENODEV;
>  	}
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 09fb03f..2604db9 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -1031,7 +1031,7 @@ void dev_load(struct net *net, const char *name)
>  	dev = __dev_get_by_name(net, name);
>  	read_unlock(&dev_base_lock);
> 
> -	if (!dev && capable(CAP_SYS_MODULE))
> +	if (!dev && capable(CAP_NET_ADMIN))
>  		request_module("%s", name);
>  }
> 
> diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
> index e92beb9..6428b34 100644
> --- a/net/ipv4/tcp_cong.c
> +++ b/net/ipv4/tcp_cong.c
> @@ -116,7 +116,7 @@ int tcp_set_default_congestion_control(const char *name)
>  	spin_lock(&tcp_cong_list_lock);
>  	ca = tcp_ca_find(name);
>  #ifdef CONFIG_MODULES
> -	if (!ca && capable(CAP_SYS_MODULE)) {
> +	if (!ca && capable(CAP_NET_ADMIN)) {
>  		spin_unlock(&tcp_cong_list_lock);
> 
>  		request_module("tcp_%s", name);
> @@ -246,7 +246,7 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
> 
>  #ifdef CONFIG_MODULES
>  	/* not found attempt to autoload module */
> -	if (!ca && capable(CAP_SYS_MODULE)) {
> +	if (!ca && capable(CAP_NET_ADMIN)) {
>  		rcu_read_unlock();
>  		request_module("tcp_%s", name);
>  		rcu_read_lock();
> 
> --
> 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
--
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
Paul Moore Aug. 13, 2009, 6:45 p.m. UTC | #2
On Thursday 13 August 2009 09:44:51 am Eric Paris wrote:
> The networking code checks CAP_SYS_MODULE before using request_module() to
> try to load a kernel module.  While this seems reasonable it's actually
> weakening system security since we have to allow CAP_SYS_MODULE for things
> like /sbin/ip and bluetoothd which need to be able to trigger module loads.
> CAP_SYS_MODULE actually grants those binaries the ability to directly load
> any code into the kernel.  We should instead be protecting modprobe and the
> modules on disk, rather than granting random programs the ability to load
> code directly into the kernel.  Instead we are going to gate those
> networking checks on CAP_NET_ADMIN which still limits them to root but
> which does not grant those processes the ability to load arbitrary code
> into the kernel.
>
> Signed-off-by: Eric Paris <eparis@redhat.com>

Sounds and looks reasonable to me.

Acked-by: Paul Moore <paul.moore@hp.com>

> ---
>
>  drivers/staging/comedi/comedi_fops.c |    8 ++++----
>  net/core/dev.c                       |    2 +-
>  net/ipv4/tcp_cong.c                  |    4 ++--
>  3 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/staging/comedi/comedi_fops.c
> b/drivers/staging/comedi/comedi_fops.c index 42e4bc4..f54bb9b 100644
> --- a/drivers/staging/comedi/comedi_fops.c
> +++ b/drivers/staging/comedi/comedi_fops.c
> @@ -1772,12 +1772,12 @@ static int comedi_open(struct inode *inode, struct
> file *file) mutex_lock(&dev->mutex);
>  	if (dev->attached)
>  		goto ok;
> -	if (!capable(CAP_SYS_MODULE) && dev->in_request_module) {
> +	if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
>  		DPRINTK("in request module\n");
>  		mutex_unlock(&dev->mutex);
>  		return -ENODEV;
>  	}
> -	if (capable(CAP_SYS_MODULE) && dev->in_request_module)
> +	if (capable(CAP_NET_ADMIN) && dev->in_request_module)
>  		goto ok;
>
>  	dev->in_request_module = 1;
> @@ -1790,8 +1790,8 @@ static int comedi_open(struct inode *inode, struct
> file *file)
>
>  	dev->in_request_module = 0;
>
> -	if (!dev->attached && !capable(CAP_SYS_MODULE)) {
> -		DPRINTK("not attached and not CAP_SYS_MODULE\n");
> +	if (!dev->attached && !capable(CAP_NET_ADMIN)) {
> +		DPRINTK("not attached and not CAP_NET_ADMIN\n");
>  		mutex_unlock(&dev->mutex);
>  		return -ENODEV;
>  	}
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 09fb03f..2604db9 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -1031,7 +1031,7 @@ void dev_load(struct net *net, const char *name)
>  	dev = __dev_get_by_name(net, name);
>  	read_unlock(&dev_base_lock);
>
> -	if (!dev && capable(CAP_SYS_MODULE))
> +	if (!dev && capable(CAP_NET_ADMIN))
>  		request_module("%s", name);
>  }
>
> diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
> index e92beb9..6428b34 100644
> --- a/net/ipv4/tcp_cong.c
> +++ b/net/ipv4/tcp_cong.c
> @@ -116,7 +116,7 @@ int tcp_set_default_congestion_control(const char
> *name) spin_lock(&tcp_cong_list_lock);
>  	ca = tcp_ca_find(name);
>  #ifdef CONFIG_MODULES
> -	if (!ca && capable(CAP_SYS_MODULE)) {
> +	if (!ca && capable(CAP_NET_ADMIN)) {
>  		spin_unlock(&tcp_cong_list_lock);
>
>  		request_module("tcp_%s", name);
> @@ -246,7 +246,7 @@ int tcp_set_congestion_control(struct sock *sk, const
> char *name)
>
>  #ifdef CONFIG_MODULES
>  	/* not found attempt to autoload module */
> -	if (!ca && capable(CAP_SYS_MODULE)) {
> +	if (!ca && capable(CAP_NET_ADMIN)) {
>  		rcu_read_unlock();
>  		request_module("tcp_%s", name);
>  		rcu_read_lock();
>
> --
> 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
James Morris Aug. 13, 2009, 10:55 p.m. UTC | #3
Any nacks/acks from netdev folk?

It looks like the right thing to me.

On Thu, 13 Aug 2009, Paul Moore wrote:

> On Thursday 13 August 2009 09:44:51 am Eric Paris wrote:
> > The networking code checks CAP_SYS_MODULE before using request_module() to
> > try to load a kernel module.  While this seems reasonable it's actually
> > weakening system security since we have to allow CAP_SYS_MODULE for things
> > like /sbin/ip and bluetoothd which need to be able to trigger module loads.
> > CAP_SYS_MODULE actually grants those binaries the ability to directly load
> > any code into the kernel.  We should instead be protecting modprobe and the
> > modules on disk, rather than granting random programs the ability to load
> > code directly into the kernel.  Instead we are going to gate those
> > networking checks on CAP_NET_ADMIN which still limits them to root but
> > which does not grant those processes the ability to load arbitrary code
> > into the kernel.
> >
> > Signed-off-by: Eric Paris <eparis@redhat.com>
> 
> Sounds and looks reasonable to me.
> 
> Acked-by: Paul Moore <paul.moore@hp.com>
> 
> > ---
> >
> >  drivers/staging/comedi/comedi_fops.c |    8 ++++----
> >  net/core/dev.c                       |    2 +-
> >  net/ipv4/tcp_cong.c                  |    4 ++--
> >  3 files changed, 7 insertions(+), 7 deletions(-)
> >
> > diff --git a/drivers/staging/comedi/comedi_fops.c
> > b/drivers/staging/comedi/comedi_fops.c index 42e4bc4..f54bb9b 100644
> > --- a/drivers/staging/comedi/comedi_fops.c
> > +++ b/drivers/staging/comedi/comedi_fops.c
> > @@ -1772,12 +1772,12 @@ static int comedi_open(struct inode *inode, struct
> > file *file) mutex_lock(&dev->mutex);
> >  	if (dev->attached)
> >  		goto ok;
> > -	if (!capable(CAP_SYS_MODULE) && dev->in_request_module) {
> > +	if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
> >  		DPRINTK("in request module\n");
> >  		mutex_unlock(&dev->mutex);
> >  		return -ENODEV;
> >  	}
> > -	if (capable(CAP_SYS_MODULE) && dev->in_request_module)
> > +	if (capable(CAP_NET_ADMIN) && dev->in_request_module)
> >  		goto ok;
> >
> >  	dev->in_request_module = 1;
> > @@ -1790,8 +1790,8 @@ static int comedi_open(struct inode *inode, struct
> > file *file)
> >
> >  	dev->in_request_module = 0;
> >
> > -	if (!dev->attached && !capable(CAP_SYS_MODULE)) {
> > -		DPRINTK("not attached and not CAP_SYS_MODULE\n");
> > +	if (!dev->attached && !capable(CAP_NET_ADMIN)) {
> > +		DPRINTK("not attached and not CAP_NET_ADMIN\n");
> >  		mutex_unlock(&dev->mutex);
> >  		return -ENODEV;
> >  	}
> > diff --git a/net/core/dev.c b/net/core/dev.c
> > index 09fb03f..2604db9 100644
> > --- a/net/core/dev.c
> > +++ b/net/core/dev.c
> > @@ -1031,7 +1031,7 @@ void dev_load(struct net *net, const char *name)
> >  	dev = __dev_get_by_name(net, name);
> >  	read_unlock(&dev_base_lock);
> >
> > -	if (!dev && capable(CAP_SYS_MODULE))
> > +	if (!dev && capable(CAP_NET_ADMIN))
> >  		request_module("%s", name);
> >  }
> >
> > diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
> > index e92beb9..6428b34 100644
> > --- a/net/ipv4/tcp_cong.c
> > +++ b/net/ipv4/tcp_cong.c
> > @@ -116,7 +116,7 @@ int tcp_set_default_congestion_control(const char
> > *name) spin_lock(&tcp_cong_list_lock);
> >  	ca = tcp_ca_find(name);
> >  #ifdef CONFIG_MODULES
> > -	if (!ca && capable(CAP_SYS_MODULE)) {
> > +	if (!ca && capable(CAP_NET_ADMIN)) {
> >  		spin_unlock(&tcp_cong_list_lock);
> >
> >  		request_module("tcp_%s", name);
> > @@ -246,7 +246,7 @@ int tcp_set_congestion_control(struct sock *sk, const
> > char *name)
> >
> >  #ifdef CONFIG_MODULES
> >  	/* not found attempt to autoload module */
> > -	if (!ca && capable(CAP_SYS_MODULE)) {
> > +	if (!ca && capable(CAP_NET_ADMIN)) {
> >  		rcu_read_unlock();
> >  		request_module("tcp_%s", name);
> >  		rcu_read_lock();
> >
> > --
> > 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
> 
> -- 
> paul moore
> linux @ hp
> 
> --
> 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
>
David Miller Aug. 13, 2009, 11:14 p.m. UTC | #4
From: Paul Moore <paul.moore@hp.com>
Date: Thu, 13 Aug 2009 14:45:37 -0400

> On Thursday 13 August 2009 09:44:51 am Eric Paris wrote:
>> The networking code checks CAP_SYS_MODULE before using request_module() to
>> try to load a kernel module.  While this seems reasonable it's actually
>> weakening system security since we have to allow CAP_SYS_MODULE for things
>> like /sbin/ip and bluetoothd which need to be able to trigger module loads.
>> CAP_SYS_MODULE actually grants those binaries the ability to directly load
>> any code into the kernel.  We should instead be protecting modprobe and the
>> modules on disk, rather than granting random programs the ability to load
>> code directly into the kernel.  Instead we are going to gate those
>> networking checks on CAP_NET_ADMIN which still limits them to root but
>> which does not grant those processes the ability to load arbitrary code
>> into the kernel.
>>
>> Signed-off-by: Eric Paris <eparis@redhat.com>
> 
> Sounds and looks reasonable to me.
> 
> Acked-by: Paul Moore <paul.moore@hp.com>

Looks fine to me:

Acked-by: David S. Miller <davem@davemloft.net>
--
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
James Morris Aug. 14, 2009, 1:56 a.m. UTC | #5
On Thu, 13 Aug 2009, Eric Paris wrote:

> The networking code checks CAP_SYS_MODULE before using request_module() to


I applied all three patches to
git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next

and fixed up the documentation per Serge's suggestion.
diff mbox

Patch

diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index 42e4bc4..f54bb9b 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -1772,12 +1772,12 @@  static int comedi_open(struct inode *inode, struct file *file)
 	mutex_lock(&dev->mutex);
 	if (dev->attached)
 		goto ok;
-	if (!capable(CAP_SYS_MODULE) && dev->in_request_module) {
+	if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
 		DPRINTK("in request module\n");
 		mutex_unlock(&dev->mutex);
 		return -ENODEV;
 	}
-	if (capable(CAP_SYS_MODULE) && dev->in_request_module)
+	if (capable(CAP_NET_ADMIN) && dev->in_request_module)
 		goto ok;
 
 	dev->in_request_module = 1;
@@ -1790,8 +1790,8 @@  static int comedi_open(struct inode *inode, struct file *file)
 
 	dev->in_request_module = 0;
 
-	if (!dev->attached && !capable(CAP_SYS_MODULE)) {
-		DPRINTK("not attached and not CAP_SYS_MODULE\n");
+	if (!dev->attached && !capable(CAP_NET_ADMIN)) {
+		DPRINTK("not attached and not CAP_NET_ADMIN\n");
 		mutex_unlock(&dev->mutex);
 		return -ENODEV;
 	}
diff --git a/net/core/dev.c b/net/core/dev.c
index 09fb03f..2604db9 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1031,7 +1031,7 @@  void dev_load(struct net *net, const char *name)
 	dev = __dev_get_by_name(net, name);
 	read_unlock(&dev_base_lock);
 
-	if (!dev && capable(CAP_SYS_MODULE))
+	if (!dev && capable(CAP_NET_ADMIN))
 		request_module("%s", name);
 }
 
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index e92beb9..6428b34 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -116,7 +116,7 @@  int tcp_set_default_congestion_control(const char *name)
 	spin_lock(&tcp_cong_list_lock);
 	ca = tcp_ca_find(name);
 #ifdef CONFIG_MODULES
-	if (!ca && capable(CAP_SYS_MODULE)) {
+	if (!ca && capable(CAP_NET_ADMIN)) {
 		spin_unlock(&tcp_cong_list_lock);
 
 		request_module("tcp_%s", name);
@@ -246,7 +246,7 @@  int tcp_set_congestion_control(struct sock *sk, const char *name)
 
 #ifdef CONFIG_MODULES
 	/* not found attempt to autoload module */
-	if (!ca && capable(CAP_SYS_MODULE)) {
+	if (!ca && capable(CAP_NET_ADMIN)) {
 		rcu_read_unlock();
 		request_module("tcp_%s", name);
 		rcu_read_lock();