broken ping to ipv6 linklocal addresses on debian buster
authorCasey Schaufler <casey@schaufler-ca.com>
Mon, 3 Feb 2020 17:15:00 +0000 (09:15 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 11 Feb 2020 12:35:43 +0000 (04:35 -0800)
commit 87fbfffcc89b92a4281b0aa53bd06af714087889 upstream.

I am seeing ping failures to IPv6 linklocal addresses with Debian
buster. Easiest example to reproduce is:

$ ping -c1 -w1 ff02::1%eth1
connect: Invalid argument

$ ping -c1 -w1 ff02::1%eth1
PING ff02::01%eth1(ff02::1%eth1) 56 data bytes
64 bytes from fe80::e0:f9ff:fe0c:37%eth1: icmp_seq=1 ttl=64 time=0.059 ms

git bisect traced the failure to
commit b9ef5513c99b ("smack: Check address length before reading address family")

Arguably ping is being stupid since the buster version is not setting
the address family properly (ping on stretch for example does):

$ strace -e connect ping6 -c1 -w1 ff02::1%eth1
connect(5, {sa_family=AF_UNSPEC,
sa_data="\4\1\0\0\0\0\377\2\0\0\0\0\0\0\0\0\0\0\0\0\0\1\3\0\0\0"}, 28)
= -1 EINVAL (Invalid argument)

but the command works fine on kernels prior to this commit, so this is
breakage which goes against the Linux paradigm of "don't break userspace"

Cc: stable@vger.kernel.org
Reported-by: David Ahern <dsahern@gmail.com>
Suggested-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Signed-off-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 security/smack/smack_lsm.c | 41 +++++++++++++++++++----------------------
 security/smack/smack_lsm.c |   41 +++++++++++++++++++----------------------
 1 file changed, 19 insertions(+), 22 deletions(-)

security/smack/smack_lsm.c

index abeb09c..ad22066 100644 (file)
@@ -2832,42 +2832,39 @@ static int smack_socket_connect(struct socket *sock, struct sockaddr *sap,
                                int addrlen)
 {
        int rc = 0;
-#if IS_ENABLED(CONFIG_IPV6)
-       struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
-#endif
-#ifdef SMACK_IPV6_SECMARK_LABELING
-       struct smack_known *rsp;
-       struct socket_smack *ssp;
-#endif
 
        if (sock->sk == NULL)
                return 0;
-
+       if (sock->sk->sk_family != PF_INET &&
+           (!IS_ENABLED(CONFIG_IPV6) || sock->sk->sk_family != PF_INET6))
+               return 0;
+       if (addrlen < offsetofend(struct sockaddr, sa_family))
+               return 0;
+       if (IS_ENABLED(CONFIG_IPV6) && sap->sa_family == AF_INET6) {
+               struct sockaddr_in6 *sip = (struct sockaddr_in6 *)sap;
 #ifdef SMACK_IPV6_SECMARK_LABELING
-       ssp = sock->sk->sk_security;
+               struct smack_known *rsp;
 #endif
 
-       switch (sock->sk->sk_family) {
-       case PF_INET:
-               if (addrlen < sizeof(struct sockaddr_in) ||
-                   sap->sa_family != AF_INET)
-                       return -EINVAL;
-               rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap);
-               break;
-       case PF_INET6:
-               if (addrlen < SIN6_LEN_RFC2133 || sap->sa_family != AF_INET6)
-                       return -EINVAL;
+               if (addrlen < SIN6_LEN_RFC2133)
+                       return 0;
 #ifdef SMACK_IPV6_SECMARK_LABELING
                rsp = smack_ipv6host_label(sip);
-               if (rsp != NULL)
+               if (rsp != NULL) {
+                       struct socket_smack *ssp = sock->sk->sk_security;
+
                        rc = smk_ipv6_check(ssp->smk_out, rsp, sip,
-                                               SMK_CONNECTING);
+                                           SMK_CONNECTING);
+               }
 #endif
 #ifdef SMACK_IPV6_PORT_LABELING
                rc = smk_ipv6_port_check(sock->sk, sip, SMK_CONNECTING);
 #endif
-               break;
+               return rc;
        }
+       if (sap->sa_family != AF_INET || addrlen < sizeof(struct sockaddr_in))
+               return 0;
+       rc = smack_netlabel_send(sock->sk, (struct sockaddr_in *)sap);
        return rc;
 }