From: deraadt Date: Tue, 20 Oct 2015 18:04:03 +0000 (+0000) Subject: At guenther's suggestion replace dnssocket() with a SOCK_DNS flag on X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=603fdb405f123334738434f7ba091788a76df90b;p=openbsd At guenther's suggestion replace dnssocket() with a SOCK_DNS flag on socket(). Without pledge, all other socket behaviours become permitted, except this one case: connect/send* only works to *:53. In pledge mode, a very few are further restricted. Some backwards compatibility for the dnssocket/dnsconnect calls will remain in the tree temporarily so that people can build through the transition. ok tedu guenther semarie --- diff --git a/sys/kern/kern_pledge.c b/sys/kern/kern_pledge.c index 1eab94e4267..a5c996e94af 100644 --- a/sys/kern/kern_pledge.c +++ b/sys/kern/kern_pledge.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_pledge.c,v 1.61 2015/10/20 17:44:48 reyk Exp $ */ +/* $OpenBSD: kern_pledge.c,v 1.62 2015/10/20 18:04:03 deraadt Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott @@ -233,19 +233,19 @@ const u_int pledge_syscalls[SYS_MAXSYSCALL] = { [SYS_lchown] = PLEDGE_FATTR, [SYS_fchown] = PLEDGE_FATTR, - /* XXX remove PLEDGE_DNS from socket/connect in 1 week */ [SYS_socket] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YP_ACTIVE, [SYS_connect] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS | PLEDGE_YP_ACTIVE, + [SYS_bind] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS, + [SYS_getsockname] = PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS, + /* XXX remove this, and the code in uipc_syscalls.c */ [SYS_dnssocket] = PLEDGE_DNS, [SYS_dnsconnect] = PLEDGE_DNS, [SYS_listen] = PLEDGE_INET | PLEDGE_UNIX, - [SYS_bind] = PLEDGE_INET | PLEDGE_UNIX, [SYS_accept4] = PLEDGE_INET | PLEDGE_UNIX, [SYS_accept] = PLEDGE_INET | PLEDGE_UNIX, [SYS_getpeername] = PLEDGE_INET | PLEDGE_UNIX, - [SYS_getsockname] = PLEDGE_INET | PLEDGE_UNIX, [SYS_flock] = PLEDGE_FLOCK | PLEDGE_YP_ACTIVE, }; @@ -935,26 +935,13 @@ pledge_adjtime_check(struct proc *p, const void *v) return (0); } -int -pledge_recvit_check(struct proc *p, const void *from) -{ - if ((p->p_p->ps_flags & PS_PLEDGE) == 0) - return (0); - - if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX))) - return (0); /* may use address */ - if (from == NULL) - return (0); /* behaves just like read */ - return (EPERM); -} - int pledge_sendit_check(struct proc *p, const void *to) { if ((p->p_p->ps_flags & PS_PLEDGE) == 0) return (0); - if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX))) + if ((p->p_p->ps_pledge & (PLEDGE_INET | PLEDGE_UNIX | PLEDGE_DNS))) return (0); /* may use address */ if (to == NULL) return (0); /* behaves just like write */ @@ -1103,7 +1090,7 @@ pledge_sockopt_check(struct proc *p, int level, int optname) } if ((p->p_p->ps_pledge & (PLEDGE_INET|PLEDGE_UNIX|PLEDGE_DNS)) == 0) - return (EPERM); + return (EINVAL); /* In use by some service libraries */ switch (level) { case SOL_SOCKET: @@ -1115,18 +1102,18 @@ pledge_sockopt_check(struct proc *p, int level, int optname) } if ((p->p_p->ps_pledge & (PLEDGE_INET|PLEDGE_UNIX)) == 0) - return (EPERM); + return (EINVAL); switch (level) { case SOL_SOCKET: switch (optname) { case SO_RTABLE: - return (EPERM); + return (EINVAL); } return (0); } if ((p->p_p->ps_pledge & PLEDGE_INET) == 0) - return (EPERM); + return (EINVAL); switch (level) { case IPPROTO_TCP: switch (optname) { @@ -1183,15 +1170,15 @@ pledge_sockopt_check(struct proc *p, int level, int optname) } int -pledge_dns_check(struct proc *p, in_port_t port) +pledge_socket_check(struct proc *p, int dns) { if ((p->p_p->ps_flags & PS_PLEDGE) == 0) return (0); - if ((p->p_p->ps_pledge & PLEDGE_INET)) + if (dns && (p->p_p->ps_pledge & PLEDGE_DNS)) + return (0); + if ((p->p_p->ps_pledge & (PLEDGE_INET|PLEDGE_UNIX|PLEDGE_YP_ACTIVE))) return (0); - if ((p->p_p->ps_pledge & PLEDGE_DNS) && port == htons(53)) - return (0); /* Allow a DNS connect outbound */ return (EPERM); } diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index c95a77afea6..cbebf61d8f7 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -1,4 +1,4 @@ -/* $OpenBSD: uipc_syscalls.c,v 1.116 2015/10/20 01:44:00 deraadt Exp $ */ +/* $OpenBSD: uipc_syscalls.c,v 1.117 2015/10/20 18:04:03 deraadt Exp $ */ /* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */ /* @@ -55,6 +55,8 @@ #include #include +#include +#include #include /* @@ -64,23 +66,23 @@ extern struct fileops socketops; int copyaddrout(struct proc *, struct mbuf *, struct sockaddr *, socklen_t, socklen_t *); -int socketit(struct proc *p, void *v, register_t *retval, int); -int connectit(struct proc *p, void *v, register_t *retval, int); +/* XXX dnssocket() - temporary backwards compat */ int sys_dnssocket(struct proc *p, void *v, register_t *retval) { - return socketit(p, v, retval, 1); -} + struct sys_socket_args /* { + syscallarg(int) domain; + syscallarg(int) type; + syscallarg(int) protocol; + } */ *uap = v; -int -sys_socket(struct proc *p, void *v, register_t *retval) -{ - return socketit(p, v, retval, 0); + SCARG(uap, type) |= SOCK_DNS; + return sys_socket(p, v, retval); } int -socketit(struct proc *p, void *v, register_t *retval, int dns) +sys_socket(struct proc *p, void *v, register_t *retval) { struct sys_socket_args /* { syscallarg(int) domain; @@ -94,8 +96,11 @@ socketit(struct proc *p, void *v, register_t *retval, int dns) int domain = SCARG(uap, domain); int fd, error; - if (dns && !(domain == AF_INET || domain == AF_INET6)) + if ((type & SOCK_DNS) && !(domain == AF_INET || domain == AF_INET6)) return (EINVAL); + error = pledge_socket_check(p, type & SOCK_DNS); + if (error) + return (pledge_fail(p, EPERM, PLEDGE_DNS)); fdplock(fdp); error = falloc(p, &fp, &fd); @@ -109,7 +114,7 @@ socketit(struct proc *p, void *v, register_t *retval, int dns) fp->f_type = DTYPE_SOCKET; fp->f_ops = &socketops; error = socreate(SCARG(uap, domain), &so, - type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK), SCARG(uap, protocol)); + type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK | SOCK_DNS), SCARG(uap, protocol)); if (error) { fdplock(fdp); fdremove(fdp, fd); @@ -119,7 +124,7 @@ socketit(struct proc *p, void *v, register_t *retval, int dns) fp->f_data = so; if (type & SOCK_NONBLOCK) (*fp->f_ops->fo_ioctl)(fp, FIONBIO, (caddr_t)&type, p); - if (dns) + if (type & SOCK_DNS) so->so_state |= SS_DNS; FILE_SET_MATURE(fp, p); *retval = fd; @@ -134,6 +139,27 @@ isdnssocket(struct socket *so) return (so->so_state & SS_DNS); } +/* For SS_DNS sockets, only allow port DNS (port 53) */ +static int +dns_portcheck(struct proc *p, struct socket *so, void *nam, size_t namelen) +{ + switch (so->so_proto->pr_domain->dom_family) { + case AF_INET: + if (namelen < sizeof(struct sockaddr_in)) + break; + if (((struct sockaddr_in *)nam)->sin_port == htons(53)) + return (0); + break; + case AF_INET6: + if (namelen < sizeof(struct sockaddr_in6)) + break; + if (((struct sockaddr_in6 *)nam)->sin6_port == htons(53)) + return (0); + } + if (p->p_p->ps_flags & PS_PLEDGE) + return (pledge_fail(p, EPERM, PLEDGE_DNS)); + return (EINVAL); +} /* ARGSUSED */ int @@ -150,10 +176,6 @@ sys_bind(struct proc *p, void *v, register_t *retval) if ((error = getsock(p, SCARG(uap, s), &fp)) != 0) return (error); - if (isdnssocket((struct socket *)fp->f_data)) { - FRELE(fp, p); - return (EINVAL); - } error = sockargs(&nam, SCARG(uap, name), SCARG(uap, namelen), MT_SONAME); if (error == 0) { @@ -337,22 +359,16 @@ bad: return (error); } -/* ARGSUSED */ +/* XXX dnsconnect() - temporary backwards compat */ int sys_dnsconnect(struct proc *p, void *v, register_t *retval) { - return connectit(p, v, retval, 1); + return sys_connect(p, v, retval); } /* ARGSUSED */ int sys_connect(struct proc *p, void *v, register_t *retval) -{ - return connectit(p, v, retval, 0); -} - -int -connectit(struct proc *p, void *v, register_t *retval, int dns) { struct sys_connect_args /* { syscallarg(int) s; @@ -367,11 +383,6 @@ connectit(struct proc *p, void *v, register_t *retval, int dns) if ((error = getsock(p, SCARG(uap, s), &fp)) != 0) return (error); so = fp->f_data; - if ((dns && !isdnssocket(so)) || (!dns && isdnssocket(so))) { - FRELE(fp, p); - return EINVAL; - } - if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { FRELE(fp, p); return (EALREADY); @@ -384,6 +395,16 @@ connectit(struct proc *p, void *v, register_t *retval, int dns) if (KTRPOINT(p, KTR_STRUCT)) ktrsockaddr(p, mtod(nam, caddr_t), SCARG(uap, namelen)); #endif + + if (isdnssocket(so)) { + error = dns_portcheck(p, so, mtod(nam, void *), nam->m_len); + if (error) { + FRELE(fp, p); + m_freem(nam); + return (error); + } + } + error = soconnect(so, nam); if (error) goto bad; @@ -572,6 +593,7 @@ sendit(struct proc *p, int s, struct msghdr *mp, int flags, register_t *retsize) struct iovec *iov; int i; struct mbuf *to, *control; + struct socket *so; size_t len; int error; #ifdef KTRACE @@ -583,9 +605,12 @@ sendit(struct proc *p, int s, struct msghdr *mp, int flags, register_t *retsize) if ((error = getsock(p, s, &fp)) != 0) return (error); - if (mp->msg_name && isdnssocket((struct socket *)fp->f_data)) { - error = EINVAL; - goto bad; + so = fp->f_data; + + if (mp->msg_name && mp->msg_namelen && isdnssocket(so)) { + error = dns_portcheck(p, so, mp->msg_name, mp->msg_namelen); + if (error) + return (error); } if (pledge_sendit_check(p, mp->msg_name)) { error = pledge_fail(p, EPERM, PLEDGE_RW); @@ -771,14 +796,6 @@ recvit(struct proc *p, int s, struct msghdr *mp, caddr_t namelenp, if ((error = getsock(p, s, &fp)) != 0) return (error); - if (mp->msg_name && isdnssocket((struct socket *)fp->f_data)) { - FRELE(fp, p); - return (EINVAL); - } - if (pledge_recvit_check(p, mp->msg_name)) { - FRELE(fp, p); - return (pledge_fail(p, EPERM, PLEDGE_RW)); - } auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; @@ -926,12 +943,9 @@ sys_setsockopt(struct proc *p, void *v, register_t *retval) if ((error = getsock(p, SCARG(uap, s), &fp)) != 0) return (error); - if (isdnssocket((struct socket *)fp->f_data)) { - error = EINVAL; - goto bad; - } - if (pledge_sockopt_check(p, SCARG(uap, level), SCARG(uap, name))) { - error = pledge_fail(p, EPERM, PLEDGE_INET); + error = pledge_sockopt_check(p, SCARG(uap, level), SCARG(uap, name)); + if (error) { + error = pledge_fail(p, error, PLEDGE_INET); goto bad; } if (SCARG(uap, valsize) > MCLBYTES) { @@ -985,12 +999,9 @@ sys_getsockopt(struct proc *p, void *v, register_t *retval) if ((error = getsock(p, SCARG(uap, s), &fp)) != 0) return (error); - if (pledge_sockopt_check(p, SCARG(uap, level), SCARG(uap, name))) { - error = pledge_fail(p, EPERM, PLEDGE_INET); - goto out; - } - if (isdnssocket((struct socket *)fp->f_data)) { - error = EINVAL; + error = pledge_sockopt_check(p, SCARG(uap, level), SCARG(uap, name)); + if (error) { + error = pledge_fail(p, error, PLEDGE_INET); goto out; } if (SCARG(uap, val)) { diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index b7aa013330a..1d0ca68f174 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in_pcb.c,v 1.184 2015/10/19 12:10:05 mpi Exp $ */ +/* $OpenBSD: in_pcb.c,v 1.185 2015/10/20 18:04:03 deraadt Exp $ */ /* $NetBSD: in_pcb.c,v 1.25 1996/02/13 23:41:53 christos Exp $ */ /* @@ -418,7 +418,6 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam) { struct in_addr *ina = NULL; struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *); - struct proc *p = curproc; int error; #ifdef INET6 @@ -435,9 +434,6 @@ in_pcbconnect(struct inpcb *inp, struct mbuf *nam) if (sin->sin_port == 0) return (EADDRNOTAVAIL); - if (pledge_dns_check(p, sin->sin_port)) - return (pledge_fail(p, EPERM, PLEDGE_DNS)); - error = in_selectsrc(&ina, sin, inp->inp_moptions, &inp->inp_route, &inp->inp_laddr, inp->inp_rtableid); if (error) diff --git a/sys/netinet6/in6_pcb.c b/sys/netinet6/in6_pcb.c index 2f6d4c0fdf4..95ae08f8a90 100644 --- a/sys/netinet6/in6_pcb.c +++ b/sys/netinet6/in6_pcb.c @@ -1,4 +1,4 @@ -/* $OpenBSD: in6_pcb.c,v 1.80 2015/10/19 12:11:28 mpi Exp $ */ +/* $OpenBSD: in6_pcb.c,v 1.81 2015/10/20 18:04:03 deraadt Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. @@ -370,7 +370,6 @@ in6_pcbconnect(struct inpcb *inp, struct mbuf *nam) struct in6_addr *in6a = NULL; struct sockaddr_in6 *sin6 = mtod(nam, struct sockaddr_in6 *); struct ifnet *ifp = NULL; /* outgoing interface */ - struct proc *p = curproc; int error = 0; struct sockaddr_in6 tmp; @@ -383,9 +382,6 @@ in6_pcbconnect(struct inpcb *inp, struct mbuf *nam) if (sin6->sin6_port == 0) return (EADDRNOTAVAIL); - if (pledge_dns_check(p, sin6->sin6_port)) - return (pledge_fail(p, EPERM, PLEDGE_DNS)); - /* reject IPv4 mapped address, we have no support for it */ if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return EADDRNOTAVAIL; diff --git a/sys/sys/pledge.h b/sys/sys/pledge.h index 68b0ca13853..a241305980a 100644 --- a/sys/sys/pledge.h +++ b/sys/sys/pledge.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pledge.h,v 1.8 2015/10/20 01:44:00 deraadt Exp $ */ +/* $OpenBSD: pledge.h,v 1.9 2015/10/20 18:04:03 deraadt Exp $ */ /* * Copyright (c) 2015 Nicholas Marriott @@ -66,11 +66,10 @@ int pledge_recvfd_check(struct proc *p, struct file *); int pledge_sysctl_check(struct proc *p, int namelen, int *name, void *new); int pledge_chown_check(struct proc *p, uid_t, gid_t); int pledge_adjtime_check(struct proc *p, const void *v); -int pledge_recvit_check(struct proc *p, const void *from); int pledge_sendit_check(struct proc *p, const void *to); int pledge_socket_check(struct proc *p, int domain); int pledge_sockopt_check(struct proc *p, int level, int optname); -int pledge_dns_check(struct proc *p, in_port_t port); +int pledge_socket_check(struct proc *p, int dns); int pledge_ioctl_check(struct proc *p, long com, void *); int pledge_flock_check(struct proc *p); diff --git a/sys/sys/socket.h b/sys/sys/socket.h index 8ecfaf34d8c..a0b01ba7c8a 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -1,4 +1,4 @@ -/* $OpenBSD: socket.h,v 1.88 2015/07/17 15:23:59 guenther Exp $ */ +/* $OpenBSD: socket.h,v 1.89 2015/10/20 18:04:03 deraadt Exp $ */ /* $NetBSD: socket.h,v 1.14 1996/02/09 18:25:36 christos Exp $ */ /* @@ -78,10 +78,11 @@ typedef __sa_family_t sa_family_t; /* sockaddr address family type */ #if __BSD_VISIBLE #define SOCK_CLOEXEC 0x8000 /* set FD_CLOEXEC */ #define SOCK_NONBLOCK 0x4000 /* set O_NONBLOCK */ -#endif #ifdef _KERNEL #define SOCK_NONBLOCK_INHERIT 0x2000 /* inherit O_NONBLOCK from listener */ #endif +#define SOCK_DNS 0x1000 /* set SS_DNS */ +#endif /* __BSD_VISIBLE */ /* * Option flags per-socket.