At guenther's suggestion replace dnssocket() with a SOCK_DNS flag on
authorderaadt <deraadt@openbsd.org>
Tue, 20 Oct 2015 18:04:03 +0000 (18:04 +0000)
committerderaadt <deraadt@openbsd.org>
Tue, 20 Oct 2015 18:04:03 +0000 (18:04 +0000)
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

sys/kern/kern_pledge.c
sys/kern/uipc_syscalls.c
sys/netinet/in_pcb.c
sys/netinet6/in6_pcb.c
sys/sys/pledge.h
sys/sys/socket.h

index 1eab94e..a5c996e 100644 (file)
@@ -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 <nicm@openbsd.org>
@@ -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);
 }
 
index c95a77a..cbebf61 100644 (file)
@@ -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 <sys/mount.h>
 #include <sys/syscallargs.h>
 
+#include <sys/domain.h>
+#include <netinet/in.h>
 #include <net/route.h>
 
 /*
@@ -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)) {
index b7aa013..1d0ca68 100644 (file)
@@ -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)
index 2f6d4c0..95ae08f 100644 (file)
@@ -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;
index 68b0ca1..a241305 100644 (file)
@@ -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 <nicm@openbsd.org>
@@ -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);
 
index 8ecfaf3..a0b01ba 100644 (file)
@@ -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.