pledge "getpw" would notice access to /var/run/ypbind.lock, and grant "inet"
authorderaadt <deraadt@openbsd.org>
Fri, 15 Jul 2022 17:20:24 +0000 (17:20 +0000)
committerderaadt <deraadt@openbsd.org>
Fri, 15 Jul 2022 17:20:24 +0000 (17:20 +0000)
rights, so that libc/yp could access YP services via a fairly complex 'protocol'
including file access, sockets, etc.  This YP protocol is also used by ypldap --
this is our way of bringing 'NIS' services into libc without monster sub-libraries.
I have managed to remove this "inet" right by creating a new ypconnect() system
call, which performs parts of the yp_bind.c dance inside the kernel..  It checks if
domainname is set, looks for a binding file with advisory lock, reads it to
get the IP and udp/tcp port numbers, and then establishes a connnected socket
direct to that ypserv.  This socket has a SS_YP flag set, and non-required system
calls are prohibited. libc maintains lifetime on this socket so a process
should never see it, but it seems safer to block udp re-connect and other calls
even in non-pledge mode.
Userland changes to use this will follow in a few days.
Lots of help from claudio and jmatthew, also ok miod

sys/kern/syscalls.master
sys/kern/uipc_syscalls.c
sys/sys/socketvar.h

index ae187f3..463441d 100644 (file)
@@ -1,4 +1,4 @@
-;      $OpenBSD: syscalls.master,v 1.226 2022/06/29 12:17:31 jca Exp $
+;      $OpenBSD: syscalls.master,v 1.227 2022/07/15 17:20:24 deraadt Exp $
 ;      $NetBSD: syscalls.master,v 1.32 1996/04/23 10:24:21 mycroft Exp $
 
 ;      @(#)syscalls.master     8.2 (Berkeley) 1/13/94
 148    STD             { int sys_quotactl(const char *path, int cmd, \
                            int uid, char *arg); }
 149    OBSOL           oquota
-150    OBSOL           ogetsockname
+150    STD NOLOCK      { int sys_ypconnect(int type); }
 
 ; Syscalls 151-180 inclusive are reserved for vendor-specific
 ; system calls.  (This includes various calls added for compatibility
index c99ee61..f51f077 100644 (file)
@@ -1,4 +1,4 @@
-/*     $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>
@@ -52,6 +56,7 @@
 #ifdef KTRACE
 #include <sys/ktrace.h>
 #endif
+#include <sys/unistd.h>
 
 #include <sys/mount.h>
 #include <sys/syscallargs.h>
@@ -168,6 +173,10 @@ sys_bind(struct proc *p, void *v, register_t *retval)
            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)
@@ -199,6 +208,8 @@ sys_listen(struct proc *p, void *v, register_t *retval)
        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);
@@ -386,6 +397,10 @@ sys_connect(struct proc *p, void *v, register_t *retval)
            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)
@@ -1072,9 +1087,17 @@ sys_getsockname(struct proc *p, void *v, register_t *retval)
        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);
@@ -1111,6 +1134,10 @@ sys_getpeername(struct proc *p, void *v, register_t *retval)
        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;
@@ -1236,3 +1263,199 @@ copyaddrout(struct proc *p, struct mbuf *name, struct sockaddr *sa,
 
        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 */
+}
index f82c1b0..6a3b7d4 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: socketvar.h,v 1.105 2022/07/01 09:56:17 mvs Exp $     */
+/*     $OpenBSD: socketvar.h,v 1.106 2022/07/15 17:20:24 deraadt Exp $ */
 /*     $NetBSD: socketvar.h,v 1.18 1996/02/09 18:25:38 christos Exp $  */
 
 /*-
@@ -153,6 +153,7 @@ struct socket {
 #define        SS_ISSENDING            0x2000  /* hint for lower layer */
 #define        SS_DNS                  0x4000  /* created using SOCK_DNS socket(2) */
 #define        SS_NEWCONN_WAIT         0x8000  /* waiting sonewconn() relock */
+#define        SS_YP                   0x10000 /* created using ypconnect(2) */
 
 #ifdef _KERNEL