diff mbox

[1/1] net: convert %p usage to %pK

Message ID 1306220681.2638.40.camel@edumazet-laptop
State Rejected, archived
Delegated to: David Miller
Headers show

Commit Message

Eric Dumazet May 24, 2011, 7:04 a.m. UTC
Le mardi 24 mai 2011 à 08:57 +0200, Ingo Molnar a écrit :
> * Joe Perches <joe@perches.com> wrote:
> 
> > Maybe for clarity it'd be better to use a switch/case
> > or something like:
> > 
> > 	if (kptr_restrict == 0)
> > 		return ptr;
> > 	if (ptr_restrict == 1 &&
> > 	    has_capability_noaudit(current, CAP_SYSLOG))
> > 		return ptr;
> > 	return NULL;
> 
> While not breaking that line really - so something like:
> 
> 	if (kptr_restrict == 0)
> 		return ptr;
> 
> 	if (kptr_restrict == 1 && has_capability_noaudit(current, CAP_SYSLOG))
> 		return ptr;
> 
> 	return NULL;
> 
> Thanks,
> 
> 	Ingo

Here we are, thanks.

[PATCH v2] inet_diag: hide socket pointers

Provide a mayber_hide_ptr() helper and use it in inet_diag to not
disclose kernel pointers to user, with following kptr_restrict logic :

kptr_restrict = 0 : kernel pointers are not mangled
kptr_restrict = 1 : if the current user does not have CAP_SYSLOG,
		    kernel pointers are replaced by 0
kptr_restrict = 2 : kernel pointers are replaced by 0

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
v2: kerneldoc and cleanup (Joe & Ingo feedback)

 include/linux/printk.h |    1 +
 lib/vsprintf.c         |   25 +++++++++++++++++++++----
 net/ipv4/inet_diag.c   |    6 ++++--
 3 files changed, 26 insertions(+), 6 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

Joe Perches May 24, 2011, 7:35 a.m. UTC | #1
On Tue, 2011-05-24 at 09:04 +0200, Eric Dumazet wrote:
> Le mardi 24 mai 2011 à 08:57 +0200, Ingo Molnar a écrit :
> > * Joe Perches <joe@perches.com> wrote:
> > > Maybe for clarity it'd be better to use a switch/case
> > > or something like:
> Here we are, thanks.

Hey Eric.  Just more trivia:

> [PATCH v2] inet_diag: hide socket pointers
> Provide a mayber_hide_ptr() helper and use it in inet_diag to not

typo maybe_hide_ptr

> diff --git a/lib/vsprintf.c b/lib/vsprintf.c
> @@ -798,6 +798,26 @@ char *uuid_string(char *buf, char *end, const u8 *addr,
>  }
>  
>  int kptr_restrict __read_mostly;
> +/**
> + * maybe_hide_ptr - Eventually nullify a kernel pointer given to user

Not a great description.
Maybe something like:
 * maybe_hide_ptr - Set user values of kernel pointers to null
 *                  when appropriate
[]
> diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
> index 6ffe94c..b5646a3 100644
> --- a/net/ipv4/inet_diag.c
> +++ b/net/ipv4/inet_diag.c
> @@ -84,6 +84,7 @@ static int inet_csk_diag_fill(struct sock *sk,
>  	struct inet_diag_meminfo  *minfo = NULL;
>  	unsigned char	 *b = skb_tail_pointer(skb);
>  	const struct inet_diag_handler *handler;
> +	u64 ptr;
>  
>  	handler = inet_diag_table[unlh->nlmsg_type];
>  	BUG_ON(handler == NULL);
> @@ -114,8 +115,9 @@ static int inet_csk_diag_fill(struct sock *sk,
>  	r->idiag_retrans = 0;
>  
>  	r->id.idiag_if = sk->sk_bound_dev_if;
> -	r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
> -	r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);
> +	ptr = (u64)maybe_hide_ptr(sk);
> +	r->id.idiag_cookie[0] = (u32)ptr;
> +	r->id.idiag_cookie[1] = (u32)(ptr >> 32);

I think it's be better without the casts
using the standard kernel.h macros.

	void *ptr;

	ptr = maybe_hide_ptr(sk);
	r->id.idiag_cookie[0] = lower_32_bits(ptr);
	r->id.idiag_cookie[1] = upper_32_bits(ptr);


--
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
Eric Dumazet May 24, 2011, 7:45 a.m. UTC | #2
Le mardi 24 mai 2011 à 00:35 -0700, Joe Perches a écrit :

> I think it's be better without the casts
> using the standard kernel.h macros.
> 
> 	void *ptr;
> 
> 	ptr = maybe_hide_ptr(sk);
> 	r->id.idiag_cookie[0] = lower_32_bits(ptr);
> 	r->id.idiag_cookie[1] = upper_32_bits(ptr);
> 

I am not sure I want to patch lower_32_bits() and upper_32_bits() for
this.

They dont work on pointers, but on "numbers", according to kerneldoc
Andrew wrote years ago. gcc agrees :

net/ipv4/inet_diag.c: In function ‘inet_csk_diag_fill’:
net/ipv4/inet_diag.c:119: warning: cast from pointer to integer of different size
net/ipv4/inet_diag.c:120: error: invalid operands to binary >>
make[1]: *** [net/ipv4/inet_diag.o] Error 1



--
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
stephen hemminger May 24, 2011, 2:43 p.m. UTC | #3
On Tue, 24 May 2011 03:58:01 -0400 (EDT)
David Miller <davem@davemloft.net> wrote:

> From: Eric Dumazet <eric.dumazet@gmail.com>
> Date: Tue, 24 May 2011 09:45:01 +0200
> 
> > Le mardi 24 mai 2011 à 00:35 -0700, Joe Perches a écrit :
> > 
> >> I think it's be better without the casts
> >> using the standard kernel.h macros.
> >> 
> >> 	void *ptr;
> >> 
> >> 	ptr = maybe_hide_ptr(sk);
> >> 	r->id.idiag_cookie[0] = lower_32_bits(ptr);
> >> 	r->id.idiag_cookie[1] = upper_32_bits(ptr);
> >> 
> > 
> > I am not sure I want to patch lower_32_bits() and upper_32_bits() for
> > this.
> > 
> > They dont work on pointers, but on "numbers", according to kerneldoc
> > Andrew wrote years ago. gcc agrees :
> > 
> > net/ipv4/inet_diag.c: In function ‘inet_csk_diag_fill’:
> > net/ipv4/inet_diag.c:119: warning: cast from pointer to integer of different size
> > net/ipv4/inet_diag.c:120: error: invalid operands to binary >>
> > make[1]: *** [net/ipv4/inet_diag.o] Error 1
> 
> Also you can't do this, the "cookie" is used by the kernel future
> lookups to find sockets.
> 
> The kernel pointer is part of the API, so sorry you can't "hide"
> kernel pointers in this case without really breaking user visible
> things.
> [Error decoding BASE64]

Couldn't the pointer be replaced by a socket index?
David Miller May 24, 2011, 5:18 p.m. UTC | #4
From: Stephen Hemminger <shemminger@vyatta.com>
Date: Tue, 24 May 2011 07:43:55 -0700

> Couldn't the pointer be replaced by a socket index?

Well, it's a pointer exactly so we don't have to index into a table.
--
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
Kees Cook May 25, 2011, 11:29 p.m. UTC | #5
Hi David,

On Tue, May 24, 2011 at 03:58:01AM -0400, David Miller wrote:
> From: Eric Dumazet <eric.dumazet@gmail.com>
> Date: Tue, 24 May 2011 09:45:01 +0200
> 
> > Le mardi 24 mai 2011 à 00:35 -0700, Joe Perches a écrit :
> > 
> >> I think it's be better without the casts
> >> using the standard kernel.h macros.
> >> 
> >> 	void *ptr;
> >> 
> >> 	ptr = maybe_hide_ptr(sk);
> >> 	r->id.idiag_cookie[0] = lower_32_bits(ptr);
> >> 	r->id.idiag_cookie[1] = upper_32_bits(ptr);
> >> 
> > 
> > I am not sure I want to patch lower_32_bits() and upper_32_bits() for
> > this.
> > 
> > They dont work on pointers, but on "numbers", according to kerneldoc
> > Andrew wrote years ago. gcc agrees :
> > 
> > net/ipv4/inet_diag.c: In function ‘inet_csk_diag_fill’:
> > net/ipv4/inet_diag.c:119: warning: cast from pointer to integer of different size
> > net/ipv4/inet_diag.c:120: error: invalid operands to binary >>
> > make[1]: *** [net/ipv4/inet_diag.o] Error 1
> 
> Also you can't do this, the "cookie" is used by the kernel future
> lookups to find sockets.
> 
> The kernel pointer is part of the API, so sorry you can't "hide"
> kernel pointers in this case without really breaking user visible
> things.

But this is precisely what we're trying to control with kptr_restrict.
Setting kptr_restrict will make inet_diag (and some details of similar
things in /proc) meaningless. Based on the name, "diag" isn't going to be
used in normal operation, and kptr_restrict is 0 by default, so only system
owners interested in this will enable it and effectively disable inet_diag.

It seems like everything that fills idiag_cookie needs to be adjusted, not
just the one instance, too:

$ fgrep 'idiag_cookie[0] = ' net/ipv4/inet_diag.c
	r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
	r->id.idiag_cookie[0] = (u32)(unsigned long)tw;
	r->id.idiag_cookie[0] = (u32)(unsigned long)req;

-Kees
David Miller May 26, 2011, 1:50 a.m. UTC | #6
From: Kees Cook <kees.cook@canonical.com>

Date: Wed, 25 May 2011 16:29:21 -0700

> Hi David,

> 

> On Tue, May 24, 2011 at 03:58:01AM -0400, David Miller wrote:

>> From: Eric Dumazet <eric.dumazet@gmail.com>

>> Date: Tue, 24 May 2011 09:45:01 +0200

>> 

>> > Le mardi 24 mai 2011 à 00:35 -0700, Joe Perches a écrit :

>> > 

>> >> I think it's be better without the casts

>> >> using the standard kernel.h macros.

>> >> 

>> >> 	void *ptr;

>> >> 

>> >> 	ptr = maybe_hide_ptr(sk);

>> >> 	r->id.idiag_cookie[0] = lower_32_bits(ptr);

>> >> 	r->id.idiag_cookie[1] = upper_32_bits(ptr);

>> >> 

>> > 

>> > I am not sure I want to patch lower_32_bits() and upper_32_bits() for

>> > this.

>> > 

>> > They dont work on pointers, but on "numbers", according to kerneldoc

>> > Andrew wrote years ago. gcc agrees :

>> > 

>> > net/ipv4/inet_diag.c: In function ‘inet_csk_diag_fill’:

>> > net/ipv4/inet_diag.c:119: warning: cast from pointer to integer of different size

>> > net/ipv4/inet_diag.c:120: error: invalid operands to binary >>

>> > make[1]: *** [net/ipv4/inet_diag.o] Error 1

>> 

>> Also you can't do this, the "cookie" is used by the kernel future

>> lookups to find sockets.

>> 

>> The kernel pointer is part of the API, so sorry you can't "hide"

>> kernel pointers in this case without really breaking user visible

>> things.

> 

> But this is precisely what we're trying to control with kptr_restrict.

> Setting kptr_restrict will make inet_diag (and some details of similar

> things in /proc) meaningless. Based on the name, "diag" isn't going to be

> used in normal operation, and kptr_restrict is 0 by default, so only system

> owners interested in this will enable it and effectively disable inet_diag.


Are you kidding me?

inet_diag is the standard way to dump sockets using netlink.
It's not a special obscure debugging facility, it's for real
users.

And the encoded kernel pointer here is used as a shortcut to looking
up precise sockets.
Kees Cook May 27, 2011, 12:14 a.m. UTC | #7
On Wed, May 25, 2011 at 09:50:40PM -0400, David Miller wrote:
> From: Kees Cook <kees.cook@canonical.com>
> Date: Wed, 25 May 2011 16:29:21 -0700
> 
> > Hi David,
> > 
> > On Tue, May 24, 2011 at 03:58:01AM -0400, David Miller wrote:
> >> From: Eric Dumazet <eric.dumazet@gmail.com>
> >> Date: Tue, 24 May 2011 09:45:01 +0200
> >> 
> >> > Le mardi 24 mai 2011 à 00:35 -0700, Joe Perches a écrit :
> >> > 
> >> >> I think it's be better without the casts
> >> >> using the standard kernel.h macros.
> >> >> 
> >> >> 	void *ptr;
> >> >> 
> >> >> 	ptr = maybe_hide_ptr(sk);
> >> >> 	r->id.idiag_cookie[0] = lower_32_bits(ptr);
> >> >> 	r->id.idiag_cookie[1] = upper_32_bits(ptr);
> >> >> 
> >> > 
> >> > I am not sure I want to patch lower_32_bits() and upper_32_bits() for
> >> > this.
> >> > 
> >> > They dont work on pointers, but on "numbers", according to kerneldoc
> >> > Andrew wrote years ago. gcc agrees :
> >> > 
> >> > net/ipv4/inet_diag.c: In function ‘inet_csk_diag_fill’:
> >> > net/ipv4/inet_diag.c:119: warning: cast from pointer to integer of different size
> >> > net/ipv4/inet_diag.c:120: error: invalid operands to binary >>
> >> > make[1]: *** [net/ipv4/inet_diag.o] Error 1
> >> 
> >> Also you can't do this, the "cookie" is used by the kernel future
> >> lookups to find sockets.
> >> 
> >> The kernel pointer is part of the API, so sorry you can't "hide"
> >> kernel pointers in this case without really breaking user visible
> >> things.
> > 
> > But this is precisely what we're trying to control with kptr_restrict.
> > Setting kptr_restrict will make inet_diag (and some details of similar
> > things in /proc) meaningless. Based on the name, "diag" isn't going to be
> > used in normal operation, and kptr_restrict is 0 by default, so only system
> > owners interested in this will enable it and effectively disable inet_diag.
> 
> Are you kidding me?
> 
> inet_diag is the standard way to dump sockets using netlink.
> It's not a special obscure debugging facility, it's for real
> users.
> 
> And the encoded kernel pointer here is used as a shortcut to looking
> up precise sockets.

We got this dropped from the /proc view; why can't we do the same for
this netlink interface?

-Kees
Eric Dumazet May 27, 2011, 3:10 a.m. UTC | #8
Le jeudi 26 mai 2011 à 22:44 -0400, David Miller a écrit :
> From: Kees Cook <kees.cook@canonical.com>
> Date: Thu, 26 May 2011 17:14:49 -0700
> > 
> > We got this dropped from the /proc view; why can't we do the same for
> > this netlink interface?
> 
> Because it's not only an opaque "output" blob, it's also an input key
> for lookups which the user can trigger.

Yes, we wan add a layer to obfuscate the real pointers. We dont trust
values given by user, only match them.

Either we use a XOR with a boot time random value (but let the NULL
cookie being the NULL one), or we generate an unique 64bit socket id for
the cookie (and keep a 64bit cookie in all sockets, increasing ram
usage)



--
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
Ingo Molnar May 27, 2011, 6:37 a.m. UTC | #9
* Eric Dumazet <eric.dumazet@gmail.com> wrote:

> Le jeudi 26 mai 2011 à 22:44 -0400, David Miller a écrit :
> > From: Kees Cook <kees.cook@canonical.com>
> > Date: Thu, 26 May 2011 17:14:49 -0700
> > > 
> > > We got this dropped from the /proc view; why can't we do the same for
> > > this netlink interface?
> > 
> > Because it's not only an opaque "output" blob, it's also an input key
> > for lookups which the user can trigger.
> 
> Yes, we wan add a layer to obfuscate the real pointers. We dont 
> trust values given by user, only match them.
> 
> Either we use a XOR with a boot time random value (but let the NULL 
> cookie being the NULL one), or we generate an unique 64bit socket 
> id for the cookie (and keep a 64bit cookie in all sockets, 
> increasing ram usage)

FYI, Dan Rosenberg is currently working on a kernel image 
randomization feature, see this lkml thread:

   [RFC][PATCH] Randomize kernel base address on boot

That will come with an easy vsprintf method to 'unrandomize' IPs. 

( this will be used to display a real-looking /proc/kallsyms and all 
  IPs that the kernel passes to user-space (via perf, etc.) will be 
  unrandomized as well, protecting the randomization seed. )

Once that code goes upstream the networking code could rather simply 
use it to 'randomize' these real data pointers as well. (Assuming you 
never ever pass in zero, that would expose the secret seed.)

The only worry would be statistical analysis performed by local 
attackers: by creating and closing enough sockets on a busy system 
you can over time cover almost all ranges of RAM, so if you can 
observe a pattern of 'over the address space limit' addresses at the 
top or the bottom of the address space you can estimate the random 
seed to within 4-5 bits realistically, maybe even more.

The upper and lower limit of the address space (big holes are useful 
too) can be calculated rather precisely based on RAM size, the 
identity of the system and the kernel version.

So maybe networking real-pointer randomization should be separate 
from kernel IP randomization after all.

Thanks,

	Ingo
--
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/printk.h b/include/linux/printk.h
index ee048e7..47c0cef 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -111,6 +111,7 @@  extern bool printk_timed_ratelimit(unsigned long *caller_jiffies,
 extern int printk_delay_msec;
 extern int dmesg_restrict;
 extern int kptr_restrict;
+void *maybe_hide_ptr(void *ptr);
 
 void log_buf_kexec_setup(void);
 #else
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 1d659d7..db1a193 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -798,6 +798,26 @@  char *uuid_string(char *buf, char *end, const u8 *addr,
 }
 
 int kptr_restrict __read_mostly;
+/**
+ * maybe_hide_ptr - Eventually nullify a kernel pointer given to user
+ * @ptr: kernel pointer
+ *
+ * kptr_restrict = 0 : kernel pointer not changed
+ * kptr_restrict = 1 : if the current user does not have CAP_SYSLOG,
+ *		       kernel pointer is replaced by 0
+ * kptr_restrict = 2 : kernel pointer is replaced by 0 for all users.
+ */
+void *maybe_hide_ptr(void *ptr)
+{
+	if (!kptr_restrict)
+		return ptr;
+
+	if (kptr_restrict == 1 && has_capability_noaudit(current, CAP_SYSLOG))
+		return ptr;
+
+	return NULL;
+}
+EXPORT_SYMBOL(maybe_hide_ptr);
 
 /*
  * Show a '%p' thing.  A kernel extension is that the '%p' is followed
@@ -911,10 +931,7 @@  char *pointer(const char *fmt, char *buf, char *end, void *ptr,
 				spec.field_width = 2 * sizeof(void *);
 			return string(buf, end, "pK-error", spec);
 		}
-		if (!((kptr_restrict == 0) ||
-		      (kptr_restrict == 1 &&
-		       has_capability_noaudit(current, CAP_SYSLOG))))
-			ptr = NULL;
+		ptr = maybe_hide_ptr(ptr);
 		break;
 	}
 	spec.flags |= SMALL;
diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c
index 6ffe94c..b5646a3 100644
--- a/net/ipv4/inet_diag.c
+++ b/net/ipv4/inet_diag.c
@@ -84,6 +84,7 @@  static int inet_csk_diag_fill(struct sock *sk,
 	struct inet_diag_meminfo  *minfo = NULL;
 	unsigned char	 *b = skb_tail_pointer(skb);
 	const struct inet_diag_handler *handler;
+	u64 ptr;
 
 	handler = inet_diag_table[unlh->nlmsg_type];
 	BUG_ON(handler == NULL);
@@ -114,8 +115,9 @@  static int inet_csk_diag_fill(struct sock *sk,
 	r->idiag_retrans = 0;
 
 	r->id.idiag_if = sk->sk_bound_dev_if;
-	r->id.idiag_cookie[0] = (u32)(unsigned long)sk;
-	r->id.idiag_cookie[1] = (u32)(((unsigned long)sk >> 31) >> 1);
+	ptr = (u64)maybe_hide_ptr(sk);
+	r->id.idiag_cookie[0] = (u32)ptr;
+	r->id.idiag_cookie[1] = (u32)(ptr >> 32);
 
 	r->id.idiag_sport = inet->inet_sport;
 	r->id.idiag_dport = inet->inet_dport;