git: 274579831b61 - main - capsicum: Limit socket operations in capability mode

Mark Johnston markj at FreeBSD.org
Wed Apr 7 18:33:10 UTC 2021


The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=274579831b61fccd5ce849350430e5167d0024f0

commit 274579831b61fccd5ce849350430e5167d0024f0
Author:     Mark Johnston <markj at FreeBSD.org>
AuthorDate: 2021-04-07 18:19:52 +0000
Commit:     Mark Johnston <markj at FreeBSD.org>
CommitDate: 2021-04-07 18:32:56 +0000

    capsicum: Limit socket operations in capability mode
    
    Capsicum did not prevent certain privileged networking operations,
    specifically creation of raw sockets and network configuration ioctls.
    However, these facilities can be used to circumvent some of the
    restrictions that capability mode is supposed to enforce.
    
    Add capability mode checks to disallow network configuration ioctls and
    creation of sockets other than PF_LOCAL and SOCK_DGRAM/STREAM/SEQPACKET
    internet sockets.
    
    Reviewed by:    oshogbo
    Discussed with: emaste
    Reported by:    manu
    Sponsored by:   The FreeBSD Foundation
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D29423
---
 sys/kern/sys_socket.c    |  2 +-
 sys/kern/uipc_socket.c   |  4 ++++
 sys/kern/uipc_usrreq.c   |  9 +++++----
 sys/net/if.c             | 10 ++++++++++
 sys/net/route.c          |  5 ++++-
 sys/net/route.h          |  4 +++-
 sys/netinet/in.c         |  4 ++++
 sys/netinet/in_proto.c   |  8 +++++---
 sys/netinet6/in6.c       |  4 ++++
 sys/netinet6/in6_proto.c |  8 +++++---
 sys/sys/protosw.h        |  2 +-
 11 files changed, 46 insertions(+), 14 deletions(-)

diff --git a/sys/kern/sys_socket.c b/sys/kern/sys_socket.c
index e53b0367960b..52f4b6cdf7f9 100644
--- a/sys/kern/sys_socket.c
+++ b/sys/kern/sys_socket.c
@@ -271,7 +271,7 @@ soo_ioctl(struct file *fp, u_long cmd, void *data, struct ucred *active_cred,
 			error = ifioctl(so, cmd, data, td);
 		else if (IOCGROUP(cmd) == 'r') {
 			CURVNET_SET(so->so_vnet);
-			error = rtioctl_fib(cmd, data, so->so_fibnum);
+			error = rtioctl_fib(cmd, data, so->so_fibnum, td);
 			CURVNET_RESTORE();
 		} else {
 			CURVNET_SET(so->so_vnet);
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 7f06b51cf096..92a204aafef0 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -112,6 +112,7 @@ __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/capsicum.h>
 #include <sys/fcntl.h>
 #include <sys/limits.h>
 #include <sys/lock.h>
@@ -526,6 +527,9 @@ socreate(int dom, struct socket **aso, int type, int proto,
 	    prp->pr_usrreqs->pru_attach == pru_attach_notsupp)
 		return (EPROTONOSUPPORT);
 
+	if (IN_CAPABILITY_MODE(td) && (prp->pr_flags & PR_CAPATTACH) == 0)
+		return (ECAPMODE);
+
 	if (prison_check_af(cred, prp->pr_domain->dom_family) != 0)
 		return (EPROTONOSUPPORT);
 
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 4466ae8822cd..3f7198c2f3ae 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -428,14 +428,15 @@ static struct protosw localsw[] = {
 {
 	.pr_type =		SOCK_STREAM,
 	.pr_domain =		&localdomain,
-	.pr_flags =		PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS,
+	.pr_flags =		PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS|
+				    PR_CAPATTACH,
 	.pr_ctloutput =		&uipc_ctloutput,
 	.pr_usrreqs =		&uipc_usrreqs_stream
 },
 {
 	.pr_type =		SOCK_DGRAM,
 	.pr_domain =		&localdomain,
-	.pr_flags =		PR_ATOMIC|PR_ADDR|PR_RIGHTS,
+	.pr_flags =		PR_ATOMIC|PR_ADDR|PR_RIGHTS|PR_CAPATTACH,
 	.pr_ctloutput =		&uipc_ctloutput,
 	.pr_usrreqs =		&uipc_usrreqs_dgram
 },
@@ -448,8 +449,8 @@ static struct protosw localsw[] = {
 	 * due to our use of sbappendaddr.  A new sbappend variants is needed
 	 * that supports both atomic record writes and control data.
 	 */
-	.pr_flags =		PR_ADDR|PR_ATOMIC|PR_CONNREQUIRED|PR_WANTRCVD|
-				    PR_RIGHTS,
+	.pr_flags =		PR_ADDR|PR_ATOMIC|PR_CONNREQUIRED|
+				    PR_WANTRCVD|PR_RIGHTS|PR_CAPATTACH,
 	.pr_ctloutput =		&uipc_ctloutput,
 	.pr_usrreqs =		&uipc_usrreqs_seqpacket,
 },
diff --git a/sys/net/if.c b/sys/net/if.c
index bbd677e0153c..5bf44d014db3 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -37,6 +37,7 @@
 #include "opt_inet.h"
 
 #include <sys/param.h>
+#include <sys/capsicum.h>
 #include <sys/conf.h>
 #include <sys/eventhandler.h>
 #include <sys/malloc.h>
@@ -2967,6 +2968,15 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct thread *td)
 	bool shutdown;
 #endif
 
+	/*
+	 * Interface ioctls access a global namespace.  There is currently no
+	 * capability-based representation for interfaces, so the configuration
+	 * interface is simply unaccessible from capability mode.  If necessary,
+	 * select ioctls may be permitted here.
+	 */
+	if (IN_CAPABILITY_MODE(td))
+		return (ECAPMODE);
+
 	CURVNET_SET(so->so_vnet);
 #ifdef VIMAGE
 	/* Make sure the VNET is stable. */
diff --git a/sys/net/route.c b/sys/net/route.c
index 2416aa9a983f..f093a71b7585 100644
--- a/sys/net/route.c
+++ b/sys/net/route.c
@@ -43,6 +43,7 @@
 
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/capsicum.h>
 #include <sys/malloc.h>
 #include <sys/mbuf.h>
 #include <sys/socket.h>
@@ -245,8 +246,10 @@ rib_add_redirect(u_int fibnum, struct sockaddr *dst, struct sockaddr *gateway,
  * Routing table ioctl interface.
  */
 int
-rtioctl_fib(u_long req, caddr_t data, u_int fibnum)
+rtioctl_fib(u_long req, caddr_t data, u_int fibnum, struct thread *td)
 {
+	if (IN_CAPABILITY_MODE(td))
+		return (ECAPMODE);
 
 	/*
 	 * If more ioctl commands are added here, make sure the proper
diff --git a/sys/net/route.h b/sys/net/route.h
index 3fdca303596e..64e89965f9cd 100644
--- a/sys/net/route.h
+++ b/sys/net/route.h
@@ -431,11 +431,13 @@ void	rt_updatemtu(struct ifnet *);
 
 void	rt_flushifroutes(struct ifnet *ifp);
 
+struct thread;
+
 /* XXX MRT NEW VERSIONS THAT USE FIBs
  * For now the protocol indepedent versions are the same as the AF_INET ones
  * but this will change.. 
  */
-int	 rtioctl_fib(u_long, caddr_t, u_int);
+int	rtioctl_fib(u_long, caddr_t, u_int, struct thread *);
 int	rib_lookup_info(uint32_t, const struct sockaddr *, uint32_t, uint32_t,
 	    struct rt_addrinfo *);
 void	rib_free_info(struct rt_addrinfo *info);
diff --git a/sys/netinet/in.c b/sys/netinet/in.c
index bcf071a81e0e..5f70dd1ec824 100644
--- a/sys/netinet/in.c
+++ b/sys/netinet/in.c
@@ -36,6 +36,7 @@
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
+#include <sys/capsicum.h>
 #include <sys/eventhandler.h>
 #include <sys/systm.h>
 #include <sys/sockio.h>
@@ -237,6 +238,9 @@ in_control(struct socket *so, u_long cmd, caddr_t data, struct ifnet *ifp,
 	if (ifp == NULL)
 		return (EADDRNOTAVAIL);
 
+	if (td != NULL && IN_CAPABILITY_MODE(td))
+		return (ECAPMODE);
+
 	/*
 	 * Filter out 4 ioctls we implement directly.  Forward the rest
 	 * to specific functions and ifp->if_ioctl().
diff --git a/sys/netinet/in_proto.c b/sys/netinet/in_proto.c
index 11688e3cb1d2..351c90699fc2 100644
--- a/sys/netinet/in_proto.c
+++ b/sys/netinet/in_proto.c
@@ -112,6 +112,7 @@ struct protosw inetsw[] = {
 	.pr_type =		0,
 	.pr_domain =		&inetdomain,
 	.pr_protocol =		IPPROTO_IP,
+	.pr_flags =		PR_CAPATTACH,
 	.pr_init =		ip_init,
 	.pr_slowtimo =		ip_slowtimo,
 	.pr_drain =		ip_drain,
@@ -121,7 +122,7 @@ struct protosw inetsw[] = {
 	.pr_type =		SOCK_DGRAM,
 	.pr_domain =		&inetdomain,
 	.pr_protocol =		IPPROTO_UDP,
-	.pr_flags =		PR_ATOMIC|PR_ADDR,
+	.pr_flags =		PR_ATOMIC|PR_ADDR|PR_CAPATTACH,
 	.pr_input =		udp_input,
 	.pr_ctlinput =		udp_ctlinput,
 	.pr_ctloutput =		udp_ctloutput,
@@ -132,7 +133,8 @@ struct protosw inetsw[] = {
 	.pr_type =		SOCK_STREAM,
 	.pr_domain =		&inetdomain,
 	.pr_protocol =		IPPROTO_TCP,
-	.pr_flags =		PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD,
+	.pr_flags =		PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD|
+				    PR_CAPATTACH,
 	.pr_input =		tcp_input,
 	.pr_ctlinput =		tcp_ctlinput,
 	.pr_ctloutput =		tcp_ctloutput,
@@ -170,7 +172,7 @@ struct protosw inetsw[] = {
 	.pr_type =		SOCK_DGRAM,
 	.pr_domain =		&inetdomain,
 	.pr_protocol =		IPPROTO_UDPLITE,
-	.pr_flags =		PR_ATOMIC|PR_ADDR,
+	.pr_flags =		PR_ATOMIC|PR_ADDR|PR_CAPATTACH,
 	.pr_input =		udp_input,
 	.pr_ctlinput =		udplite_ctlinput,
 	.pr_ctloutput =		udp_ctloutput,
diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 02cb9df7da3a..de3db6dc7d33 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -69,6 +69,7 @@ __FBSDID("$FreeBSD$");
 #include "opt_inet6.h"
 
 #include <sys/param.h>
+#include <sys/capsicum.h>
 #include <sys/eventhandler.h>
 #include <sys/errno.h>
 #include <sys/jail.h>
@@ -254,6 +255,9 @@ in6_control(struct socket *so, u_long cmd, caddr_t data,
 	int error;
 	u_long ocmd = cmd;
 
+	if (td != NULL && IN_CAPABILITY_MODE(td))
+		return (ECAPMODE);
+
 	/*
 	 * Compat to make pre-10.x ifconfig(8) operable.
 	 */
diff --git a/sys/netinet6/in6_proto.c b/sys/netinet6/in6_proto.c
index 21b7d660676f..56ef41a27e88 100644
--- a/sys/netinet6/in6_proto.c
+++ b/sys/netinet6/in6_proto.c
@@ -145,6 +145,7 @@ struct protosw inet6sw[] = {
 	.pr_type =		0,
 	.pr_domain =		&inet6domain,
 	.pr_protocol =		IPPROTO_IPV6,
+	.pr_flags =		PR_CAPATTACH,
 	.pr_init =		ip6_init,
 	.pr_slowtimo =		frag6_slowtimo,
 	.pr_drain =		frag6_drain,
@@ -154,7 +155,7 @@ struct protosw inet6sw[] = {
 	.pr_type =		SOCK_DGRAM,
 	.pr_domain =		&inet6domain,
 	.pr_protocol =		IPPROTO_UDP,
-	.pr_flags =		PR_ATOMIC|PR_ADDR,
+	.pr_flags =		PR_ATOMIC|PR_ADDR|PR_CAPATTACH,
 	.pr_input =		udp6_input,
 	.pr_ctlinput =		udp6_ctlinput,
 	.pr_ctloutput =		ip6_ctloutput,
@@ -167,7 +168,8 @@ struct protosw inet6sw[] = {
 	.pr_type =		SOCK_STREAM,
 	.pr_domain =		&inet6domain,
 	.pr_protocol =		IPPROTO_TCP,
-	.pr_flags =		PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD|PR_LISTEN,
+	.pr_flags =		PR_CONNREQUIRED|PR_IMPLOPCL|PR_WANTRCVD|
+				    PR_LISTEN|PR_CAPATTACH,
 	.pr_input =		tcp6_input,
 	.pr_ctlinput =		tcp6_ctlinput,
 	.pr_ctloutput =		tcp_ctloutput,
@@ -209,7 +211,7 @@ struct protosw inet6sw[] = {
 	.pr_type =		SOCK_DGRAM,
 	.pr_domain =		&inet6domain,
 	.pr_protocol =		IPPROTO_UDPLITE,
-	.pr_flags =		PR_ATOMIC|PR_ADDR,
+	.pr_flags =		PR_ATOMIC|PR_ADDR|PR_CAPATTACH,
 	.pr_input =		udp6_input,
 	.pr_ctlinput =		udplite6_ctlinput,
 	.pr_ctloutput =		udp_ctloutput,
diff --git a/sys/sys/protosw.h b/sys/sys/protosw.h
index 9c3139c866bd..5c2fa2d4077e 100644
--- a/sys/sys/protosw.h
+++ b/sys/sys/protosw.h
@@ -121,6 +121,7 @@ struct protosw {
 #define	PR_RIGHTS	0x10		/* passes capabilities */
 #define PR_IMPLOPCL	0x20		/* implied open/close */
 #define	PR_LASTHDR	0x40		/* enforce ipsec policy; last header */
+#define	PR_CAPATTACH	0x80		/* socket can attach in cap mode */
 
 /*
  * In earlier BSD network stacks, a single pr_usrreq() function pointer was
@@ -183,7 +184,6 @@ struct uio;
  * should eventually be merged back into struct protosw.
  *
  * Some fields initialized to defaults if they are NULL.
- * See uipc_domain.c:net_init_domain()
  */
 struct pr_usrreqs {
 	void	(*pru_abort)(struct socket *so);


More information about the dev-commits-src-all mailing list