CVE-2012-3520: Improper Authentication (CWE-287)
value: The Netlink implementation in the Linux kernel before 3.2.30 does not properly handle messages that lack SCM_CREDENTIALS data, which might allow local users to spoof Netlink communication via a crafted message, as demonstrated by a message to (1) Avahi or (2) NetworkManager.
CWE description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
Published: 2012-10-03T11:02Z
Repository: https://github.com/torvalds/linux
Language: C
Files changed: include/net/scm.h, net/netlink/af_netlink.c, net/unix/af_unix.c
Commit date: 2012-08-21 14:53:01 -0700
Commit message: af_netlink: force credentials passing [CVE-2012-3520]

Pablo Neira Ayuso discovered that avahi and
potentially NetworkManager accept spoofed Netlink messages because of a
kernel bug.  The kernel passes all-zero SCM_CREDENTIALS ancillary data
to the receiver if the sender did not provide such data, instead of not
including any such data at all or including the correct data from the
peer (as it is the case with AF_UNIX).

This bug was introduced in commit 16e572626961
(af_unix: dont send SCM_CREDENTIALS by default)

This patch forces passing credentials for netlink, as
before the regression.

Another fix would be to not add SCM_CREDENTIALS in
netlink messages if not provided by the sender, but it
might break some programs.

With help from Florian Weimer & Petr Matousek

This issue is designated as CVE-2012-3520

Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Petr Matousek <pmatouse@redhat.com>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: Pablo Neira Ayuso <pablo@netfilter.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

Diff: diff --git a/include/net/scm.h b/include/net/scm.h
index 079d7887dac13..7dc0854f0b389 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -68,13 +68,15 @@ static __inline__ void scm_destroy(struct scm_cookie *scm)
 	if (scm && scm->fp)
 		__scm_destroy(scm);
 }
 
 static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
-			       struct scm_cookie *scm)
+			       struct scm_cookie *scm, bool forcecreds)
 {
 	memset(scm, 0, sizeof(*scm));
+	if (forcecreds)
+		scm_set_cred(scm, task_tgid(current), current_cred());
 	unix_get_peersec_dgram(sock, scm);
 	if (msg->msg_controllen <= 0)
 		return 0;
 	return __scm_send(sock, msg, scm);
 }
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 5463969da45b9..1445d73533ed1 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1360,11 +1360,11 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
 		return -EOPNOTSUPP;
 
 	if (NULL == siocb->scm)
 		siocb->scm = &scm;
 
-	err = scm_send(sock, msg, siocb->scm);
+	err = scm_send(sock, msg, siocb->scm, true);
 	if (err < 0)
 		return err;
 
 	if (msg->msg_namelen) {
 		err = -EINVAL;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index e4768c180da23..c5ee4ff613641 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1448,11 +1448,11 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
 	int data_len = 0;
 
 	if (NULL == siocb->scm)
 		siocb->scm = &tmp_scm;
 	wait_for_unix_gc();
-	err = scm_send(sock, msg, siocb->scm);
+	err = scm_send(sock, msg, siocb->scm, false);
 	if (err < 0)
 		return err;
 
 	err = -EOPNOTSUPP;
 	if (msg->msg_flags&MSG_OOB)
@@ -1617,11 +1617,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
 	int max_level;
 
 	if (NULL == siocb->scm)
 		siocb->scm = &tmp_scm;
 	wait_for_unix_gc();
-	err = scm_send(sock, msg, siocb->scm);
+	err = scm_send(sock, msg, siocb->scm, false);
 	if (err < 0)
 		return err;
 
 	err = -EOPNOTSUPP;
 	if (msg->msg_flags&MSG_OOB)

Vulnerable code: 			       struct scm_cookie *scm)
	err = scm_send(sock, msg, siocb->scm);
	err = scm_send(sock, msg, siocb->scm);
	err = scm_send(sock, msg, siocb->scm);
Fixed code: 			       struct scm_cookie *scm, bool forcecreds)
	if (forcecreds)
		scm_set_cred(scm, task_tgid(current), current_cred());
	err = scm_send(sock, msg, siocb->scm, true);
	err = scm_send(sock, msg, siocb->scm, false);
	err = scm_send(sock, msg, siocb->scm, false);