sk-filter: Add ability to lock a socket filter program
authorVincent Bernat <bernat@luffy.cx>
Wed, 16 Jan 2013 21:55:49 +0000 (22:55 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 17 Jan 2013 08:21:25 +0000 (03:21 -0500)
While a privileged program can open a raw socket, attach some
restrictive filter and drop its privileges (or send the socket to an
unprivileged program through some Unix socket), the filter can still
be removed or modified by the unprivileged program. This commit adds a
socket option to lock the filter (SO_LOCK_FILTER) preventing any
modification of a socket filter program.

This is similar to OpenBSD BIOCLOCK ioctl on bpf sockets, except even
root is not allowed change/drop the filter.

The state of the lock can be read with getsockopt(). No error is
triggered if the state is not changed. -EPERM is returned when a user
tries to remove the lock or to change/remove the filter while the lock
is active. The check is done directly in sk_attach_filter() and
sk_detach_filter() and does not affect only setsockopt() syscall.

Signed-off-by: Vincent Bernat <bernat@luffy.cx>
Signed-off-by: David S. Miller <davem@davemloft.net>
19 files changed:
Documentation/networking/filter.txt
arch/alpha/include/uapi/asm/socket.h
arch/avr32/include/uapi/asm/socket.h
arch/cris/include/uapi/asm/socket.h
arch/frv/include/uapi/asm/socket.h
arch/h8300/include/uapi/asm/socket.h
arch/ia64/include/uapi/asm/socket.h
arch/m32r/include/uapi/asm/socket.h
arch/mips/include/uapi/asm/socket.h
arch/mn10300/include/uapi/asm/socket.h
arch/parisc/include/uapi/asm/socket.h
arch/powerpc/include/uapi/asm/socket.h
arch/s390/include/uapi/asm/socket.h
arch/sparc/include/uapi/asm/socket.h
arch/xtensa/include/uapi/asm/socket.h
include/net/sock.h
include/uapi/asm-generic/socket.h
net/core/filter.c
net/core/sock.c

index bbf2005270b5d1c1d3e0ffeb44a1082b8c0b8d75..cdb3e40b9d14ee28095fb02a088154a8989996c4 100644 (file)
@@ -17,12 +17,12 @@ creating filters.
 
 LSF is much simpler than BPF. One does not have to worry about
 devices or anything like that. You simply create your filter
-code, send it to the kernel via the SO_ATTACH_FILTER ioctl and
+code, send it to the kernel via the SO_ATTACH_FILTER option and
 if your filter code passes the kernel check on it, you then
 immediately begin filtering data on that socket.
 
 You can also detach filters from your socket via the
-SO_DETACH_FILTER ioctl. This will probably not be used much
+SO_DETACH_FILTER option. This will probably not be used much
 since when you close a socket that has a filter on it the
 filter is automagically removed. The other less common case
 may be adding a different filter on the same socket where you had another
@@ -31,12 +31,19 @@ the old one and placing your new one in its place, assuming your
 filter has passed the checks, otherwise if it fails the old filter
 will remain on that socket.
 
+SO_LOCK_FILTER option allows to lock the filter attached to a
+socket. Once set, a filter cannot be removed or changed. This allows
+one process to setup a socket, attach a filter, lock it then drop
+privileges and be assured that the filter will be kept until the
+socket is closed.
+
 Examples
 ========
 
 Ioctls-
 setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &Filter, sizeof(Filter));
 setsockopt(sockfd, SOL_SOCKET, SO_DETACH_FILTER, &value, sizeof(value));
+setsockopt(sockfd, SOL_SOCKET, SO_LOCK_FILTER, &value, sizeof(value));
 
 See the BSD bpf.4 manpage and the BSD Packet Filter paper written by
 Steven McCanne and Van Jacobson of Lawrence Berkeley Laboratory.
index 097c1577735af85844b58ad1b4f6c71767f94d6c..755702eefd9cfa9c53e0ab465eb306fb58c02f3d 100644 (file)
@@ -77,5 +77,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
 
 #endif /* _UAPI_ASM_SOCKET_H */
index 486df68abeecc955757a352cf64e70ad9b39a020..f3f38a0e2ef91e79c0da872bcb83b2d85669b0cd 100644 (file)
@@ -70,4 +70,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* __ASM_AVR32_SOCKET_H */
index b681b043f6c819fedc4b819c5ffa59a72cd7059f..406b5838defd9dbcc28cc1dd6769735986eba7cf 100644 (file)
@@ -72,6 +72,8 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_SOCKET_H */
 
 
index 871f89b7fbdaf08a0629679f01d804337804cbc7..d8e1132a1ab63f99588c8e2cf388fdfbb4b475b7 100644 (file)
@@ -70,5 +70,7 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_SOCKET_H */
 
index 90a2e573c7e679ac92300734e3641f7d5d5b45e0..c8b87a828206885d424b28ecbdaf1fd90b685912 100644 (file)
@@ -70,4 +70,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_SOCKET_H */
index 23d6759bb57b8dd6af9a824e78ce7335dede1d80..f390896c31048c30135e9b77227c3c729b94e291 100644 (file)
@@ -79,4 +79,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_IA64_SOCKET_H */
index 5e7088a26726cc0133ae3a6f166f3597832646a6..6a895155e7a39107c20e4365b1a442a38840fa93 100644 (file)
@@ -70,4 +70,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_M32R_SOCKET_H */
index 17307ab90474271e4c77f9d48bfb775f2049db74..9d11a771392364c34704f40089d02d950af0b313 100644 (file)
@@ -90,5 +90,6 @@ To add: #define SO_REUSEPORT 0x0200   /* Allow local address and port reuse.  */
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
 
 #endif /* _UAPI_ASM_SOCKET_H */
index af5366bbfe62727b09e2d47474e78a7483bb3a13..ab702c40b30e8173f5ec2d8160d928cd83a5dff1 100644 (file)
@@ -70,4 +70,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_SOCKET_H */
index d9ff4731253bb6e8f6402686e2401e251126ecb2..da2c8d3c209efa2b7cb9c225283fc0ba5cd82510 100644 (file)
@@ -69,6 +69,7 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               0x4024
 
+#define SO_LOCK_FILTER         0x4025
 
 /* O_NONBLOCK clashes with the bits used for socket types.  Therefore we
  * have to define SOCK_NONBLOCK to a different value here.
index eb0b1864d400b2a01386ed3a77fedb300a560227..e6ca31816cc96e62557521eaa15006678fcd65f6 100644 (file)
@@ -77,4 +77,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_POWERPC_SOCKET_H */
index 436d07c23be8febc9ad245fb73e4f1671a004cd9..9ce60b68f0709468fa454f66d886bb48cb71c525 100644 (file)
@@ -76,4 +76,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _ASM_SOCKET_H */
index c83a937ead00676eefef950b6c00d77cd644c8a9..fbbba57547d1f7cf7fa495f36b5dea3696ecc49b 100644 (file)
@@ -66,6 +66,7 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               0x0027
 
+#define SO_LOCK_FILTER         0x0028
 
 /* Security levels - as per NRL IPv6 - don't actually do anything */
 #define SO_SECURITY_AUTHENTICATION             0x5001
index 38079be1cf1ebc98fa6d9a61aebd59ff81822f78..dbf316487b510e94ea6d95423404102cceebe0d5 100644 (file)
@@ -81,4 +81,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* _XTENSA_SOCKET_H */
index 182ca99405adfb30e7b72181847b4a5ffd5d69ca..5a34e2f036575fc2015589f958103d4feba2985f 100644 (file)
@@ -664,6 +664,7 @@ enum sock_flags {
                     * Will use last 4 bytes of packet sent from
                     * user-space instead.
                     */
+       SOCK_FILTER_LOCKED, /* Filter cannot be changed anymore */
 };
 
 static inline void sock_copy_flags(struct sock *nsk, struct sock *osk)
index 2d32d073a6f9e4275d47c27cbd63b169a3267b9a..3f6a992014101e004fb1fa64e6cd3bb5d973ef2e 100644 (file)
@@ -73,4 +73,6 @@
 /* Instruct lower device to use last 4-bytes of skb data as FCS */
 #define SO_NOFCS               43
 
+#define SO_LOCK_FILTER         44
+
 #endif /* __ASM_GENERIC_SOCKET_H */
index 2ead2a9b1859bd08d73114121f6f4ea9600d8f27..2e20b55a7830c43e25bdd4221cc134f374d7148e 100644 (file)
@@ -721,6 +721,9 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
        unsigned int fsize = sizeof(struct sock_filter) * fprog->len;
        int err;
 
+       if (sock_flag(sk, SOCK_FILTER_LOCKED))
+               return -EPERM;
+
        /* Make sure new filter is there and in the right amounts. */
        if (fprog->filter == NULL)
                return -EINVAL;
@@ -757,6 +760,9 @@ int sk_detach_filter(struct sock *sk)
        int ret = -ENOENT;
        struct sk_filter *filter;
 
+       if (sock_flag(sk, SOCK_FILTER_LOCKED))
+               return -EPERM;
+
        filter = rcu_dereference_protected(sk->sk_filter,
                                           sock_owned_by_user(sk));
        if (filter) {
index bc131d419683c11bd6f098c930e7d4f3cd9fb697..8258fb741e9a1557c1345110c9b15958523e6507 100644 (file)
@@ -861,6 +861,13 @@ set_rcvbuf:
                ret = sk_detach_filter(sk);
                break;
 
+       case SO_LOCK_FILTER:
+               if (sock_flag(sk, SOCK_FILTER_LOCKED) && !valbool)
+                       ret = -EPERM;
+               else
+                       sock_valbool_flag(sk, SOCK_FILTER_LOCKED, valbool);
+               break;
+
        case SO_PASSSEC:
                if (valbool)
                        set_bit(SOCK_PASSSEC, &sock->flags);
@@ -1140,6 +1147,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname,
 
                goto lenout;
 
+       case SO_LOCK_FILTER:
+               v.val = sock_flag(sk, SOCK_FILTER_LOCKED);
+               break;
+
        default:
                return -ENOPROTOOPT;
        }