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
This commit is contained in:
Mark Johnston 2021-04-07 14:19:52 -04:00
parent 12db51d208
commit 274579831b
11 changed files with 46 additions and 14 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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,
},

View file

@ -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. */

View file

@ -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

View file

@ -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);

View file

@ -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().

View file

@ -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,

View file

@ -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.
*/

View file

@ -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,

View file

@ -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);