-/* $OpenBSD: uipc_syscalls.c,v 1.196 2022/07/01 09:56:17 mvs Exp $ */
+/* $OpenBSD: uipc_syscalls.c,v 1.197 2022/07/15 17:20:24 deraadt Exp $ */
/* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */
/*
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/filedesc.h>
+#include <sys/namei.h>
+#include <sys/pool.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
+#include <sys/kernel.h>
#include <sys/file.h>
+#include <sys/vnode.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/event.h>
#ifdef KTRACE
#include <sys/ktrace.h>
#endif
+#include <sys/unistd.h>
#include <sys/mount.h>
#include <sys/syscallargs.h>
so->so_state);
if (error)
goto out;
+ if (so->so_state & SS_YP) {
+ error = ENOTSOCK;
+ goto out;
+ }
error = sockargs(&nam, SCARG(uap, name), SCARG(uap, namelen),
MT_SONAME);
if (error)
if ((error = getsock(p, SCARG(uap, s), &fp)) != 0)
return (error);
so = fp->f_data;
+ if (so->so_state & SS_YP)
+ return ENOTSOCK;
solock(so);
error = solisten(so, SCARG(uap, backlog));
sounlock(so);
so->so_state);
if (error)
goto out;
+ if (so->so_state & SS_YP) {
+ error = ENOTSOCK;
+ goto out;
+ }
error = sockargs(&nam, SCARG(uap, name), SCARG(uap, namelen),
MT_SONAME);
if (error)
if (error)
goto bad;
so = fp->f_data;
+ if (so->so_state & SS_YP) {
+ error = ENOTSOCK;
+ goto bad;
+ }
error = pledge_socket(p, -1, so->so_state);
if (error)
goto bad;
+ if (so->so_state & SS_YP) {
+ error = ENOTSOCK;
+ goto bad;
+ }
m = m_getclr(M_WAIT, MT_SONAME);
solock(so);
error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKADDR, NULL, m, NULL, p);
error = pledge_socket(p, -1, so->so_state);
if (error)
goto bad;
+ if (so->so_state & SS_YP) {
+ error = ENOTSOCK;
+ goto bad;
+ }
if ((so->so_state & SS_ISCONNECTED) == 0) {
error = ENOTCONN;
goto bad;
return (error);
}
+
+#ifndef SMALL_KERNEL
+int
+ypsockargs(struct mbuf **mp, const void *buf, size_t buflen, int type)
+{
+ struct sockaddr *sa;
+ struct mbuf *m;
+
+ /*
+ * We can't allow socket names > UCHAR_MAX in length, since that
+ * will overflow sa_len. Also, control data more than MCLBYTES in
+ * length is just too much.
+ * Memory for sa_len and sa_family must exist.
+ */
+ if ((buflen > (type == MT_SONAME ? UCHAR_MAX : MCLBYTES)) ||
+ (type == MT_SONAME && buflen < offsetof(struct sockaddr, sa_data)))
+ return (EINVAL);
+
+ /* Allocate an mbuf to hold the arguments. */
+ m = m_get(M_WAIT, type);
+ if (buflen > MLEN) {
+ MCLGET(m, M_WAITOK);
+ if ((m->m_flags & M_EXT) == 0) {
+ m_free(m);
+ return ENOBUFS;
+ }
+ }
+ m->m_len = buflen;
+ bcopy(buf, mtod(m, caddr_t), buflen);
+ *mp = m;
+ if (type == MT_SONAME) {
+ sa = mtod(m, struct sockaddr *);
+ sa->sa_len = buflen;
+ }
+ return (0);
+}
+#endif /* SMALL_KERNEL */
+
+int
+sys_ypconnect(struct proc *p, void *v, register_t *retval)
+{
+#ifdef SMALL_KERNEL
+ return EAFNOSUPPORT;
+#else
+ struct sys_ypconnect_args /* {
+ syscallarg(int) type;
+ } */ *uap = v;
+ struct nameidata nid;
+ struct vattr va;
+ struct uio uio;
+ struct iovec iov;
+ struct filedesc *fdp = p->p_fd;
+ struct socket *so;
+ struct file *fp;
+ struct flock fl;
+ char *name;
+ struct mbuf *nam = NULL;
+ int error, fd = -1;
+ struct ypbinding {
+ u_short ypbind_port;
+ int status;
+ in_addr_t in;
+ u_short ypserv_udp_port;
+ u_short garbage;
+ u_short ypserv_tcp_port;
+ } __packed data;
+ struct sockaddr_in ypsin;
+
+ if (!domainname[0])
+ return EAFNOSUPPORT;
+
+ switch (SCARG(uap, type)) {
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ break;
+ default:
+ return EAFNOSUPPORT;
+ }
+
+ name = pool_get(&namei_pool, PR_WAITOK);
+ snprintf(name, MAXPATHLEN, "/var/yp/binding/%s.2", domainname);
+ NDINIT(&nid, 0, NOFOLLOW|LOCKLEAF|KERNELPATH, UIO_SYSSPACE, name, p);
+ nid.ni_pledge = PLEDGE_RPATH;
+
+ KERNEL_LOCK();
+ error = namei(&nid);
+ pool_put(&namei_pool, name);
+ if (error)
+ goto out;
+ error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p);
+ if (error)
+ goto verror;
+ if (nid.ni_vp->v_type != VREG || va.va_size != sizeof data) {
+ error = EFTYPE;
+ goto verror;
+ }
+
+ /*
+ * Check that a lock is held on the file (hopefully by ypbind),
+ * otherwise the file might be old
+ */
+ fl.l_start = 0;
+ fl.l_len = 0;
+ fl.l_pid = 0;
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+ error = VOP_ADVLOCK(nid.ni_vp, fdp, F_GETLK, &fl, F_POSIX);
+ if (error)
+ goto verror;
+ if (fl.l_type == F_UNLCK) {
+ error = EOWNERDEAD;
+ goto verror;
+ }
+
+ iov.iov_base = &data;
+ iov.iov_len = sizeof data;
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ uio.uio_offset = 0;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_rw = UIO_READ;
+ uio.uio_procp = p;
+
+ error = VOP_READ(nid.ni_vp, &uio, 0, p->p_ucred);
+ if (error) {
+verror:
+ if (nid.ni_vp)
+ vput(nid.ni_vp);
+out:
+ KERNEL_UNLOCK();
+ return (error);
+ }
+ vput(nid.ni_vp);
+ KERNEL_UNLOCK();
+
+ bzero(&ypsin, sizeof ypsin);
+ ypsin.sin_len = sizeof ypsin;
+ ypsin.sin_family = AF_INET;
+ if (SCARG(uap, type) == SOCK_STREAM)
+ ypsin.sin_port = data.ypserv_tcp_port;
+ else
+ ypsin.sin_port = data.ypserv_udp_port;
+ if (ntohs(ypsin.sin_port) >= IPPORT_RESERVED || ntohs(ypsin.sin_port) == 20)
+ return EPERM;
+ memcpy(&ypsin.sin_addr.s_addr, &data.in, sizeof ypsin.sin_addr.s_addr);
+
+ error = socreate(AF_INET, &so, SCARG(uap, type), 0);
+ if (error)
+ return (error);
+
+ error = ypsockargs(&nam, &ypsin, sizeof ypsin, MT_SONAME);
+ if (error) {
+ soclose(so, MSG_DONTWAIT);
+ return (error);
+ }
+
+#ifdef KTRACE
+ if (KTRPOINT(p, KTR_STRUCT))
+ ktrsockaddr(p, mtod(nam, caddr_t), sizeof(struct sockaddr_in));
+#endif
+ solock(so);
+ error = soconnect(so, nam);
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ error = sosleep_nsec(so, &so->so_timeo, PSOCK | PCATCH,
+ "netcon2", INFSLP);
+ if (error)
+ break;
+ }
+ m_freem(nam);
+ so->so_state |= SS_YP; /* impose some restrictions */
+ sounlock(so);
+ if (error) {
+ soclose(so, MSG_DONTWAIT);
+ return (error);
+ }
+
+ fdplock(fdp);
+ error = falloc(p, &fp, &fd);
+ if (error) {
+ fdpunlock(fdp);
+ soclose(so, MSG_DONTWAIT);
+ return (error);
+ }
+
+ fp->f_flag = FREAD | FWRITE | FNONBLOCK;
+ fp->f_type = DTYPE_SOCKET;
+ fp->f_ops = &socketops;
+ fp->f_data = so;
+ fdinsert(fdp, fd, UF_EXCLOSE, fp);
+ fdpunlock(fdp);
+ FRELE(fp, p);
+ *retval = fd;
+ return (error);
+#endif /* SMALL_KERNEL */
+}