-/* $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>
[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,
};
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 */
}
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:
}
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) {
}
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);
}
-/* $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 $ */
/*
#include <sys/mount.h>
#include <sys/syscallargs.h>
+#include <sys/domain.h>
+#include <netinet/in.h>
#include <net/route.h>
/*
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;
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);
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);
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;
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
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) {
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;
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);
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;
struct iovec *iov;
int i;
struct mbuf *to, *control;
+ struct socket *so;
size_t len;
int error;
#ifdef KTRACE
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);
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;
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) {
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)) {