bind() allowed to non-local addresses

From: Matt Peterson (mpeterson@calderasystems.com)
Date: Wed Oct 18 2000 - 12:24:56 EST


Using linux-2.4.0-test9, bind() incorrectly allows a bind to a non-local
address. The correct behavior should be a return code of -1 with errno
set to EADDRNOTAVAIL. (Simple snippet to reproduce/debug the problem is
available on request)

There appears to be significant differences between the
net/ipv4/af_inet.c:inet_bind() in the 2.2 and 2.4 sources. The
inet_bind() in the 2.2 sources contains a check to ensure that the value
returned by inet_addr_type() is RTN_LOCAL as well as a #ifdef'ed check
to allow the bind in certain cases if the kernel was compiled with
transparent proxy is enabled. In inet_bind() of the 2.4 tree all of
these checks are conspicuously absent.

The following patch fixes the problem for me, but I am still concerned
possibly breaking transparent proxy. Basically the patch is nothing
more than the addition of a simple check for RTN_LOCAL. (Moving call to
inet_addr_type() is a *very* nit-picky "optimization" that eliminates
wasted time in the call in the event that subsequent port and capability
check fails).

*** af_inet.c Wed Oct 18 11:06:15 2000
--- af_inet.c.orig Mon Sep 18 16:04:13 2000
***************
*** 459,471 ****
        if (addr_len < sizeof(struct sockaddr_in))
                return -EINVAL;

        snum = ntohs(addr->sin_port);
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
                return -EACCES;

! chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
!
         /* We keep a pair of addresses. rcv_saddr is the one
         * used by hash lookups, and saddr is used for transmit.
         *
         * In the BSD API these are the same except where it
--- 459,471 ----
        if (addr_len < sizeof(struct sockaddr_in))
                return -EINVAL;

+ chk_addr_ret = inet_addr_type(addr->sin_addr.s_addr);
+
        snum = ntohs(addr->sin_port);
        if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
                return -EACCES;

        /* We keep a pair of addresses. rcv_saddr is the one
         * used by hash lookups, and saddr is used for transmit.
         *
         * In the BSD API these are the same except where it
***************
*** 483,493 ****
        sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
        if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret ==
RTN_BROADCAST)
                sk->saddr = 0; /* Use device */
! else if(chk_addr_ret != RTN_LOCAL){
! err = -EADDERNOTAVAIL;
! goto out;
! }
!
        /* Make sure we are allowed to bind here. */
        if (sk->prot->get_port(sk, snum) != 0) {
                sk->saddr = sk->rcv_saddr = 0;
--- 483,489 ----
        sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
        if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret ==
RTN_BROADCAST)
                sk->saddr = 0; /* Use device */
!
        /* Make sure we are allowed to bind here. */
        if (sk->prot->get_port(sk, snum) != 0) {
                sk->saddr = sk->rcv_saddr = 0;

Discussion? Please personally CC me <mpeterson@caldera.com> as I am not
currently subscribed to the linux-kernel list.

Thanks.

--
Matthew Peterson
Software Engineer
Caldera Systems, Inc
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Mon Oct 23 2000 - 21:00:13 EST