From 88d6f3283946754c6900367d275c4da84b397486 Mon Sep 17 00:00:00 2001 From: deraadt Date: Fri, 15 Jul 2022 17:20:24 +0000 Subject: [PATCH] pledge "getpw" would notice access to /var/run/ypbind.lock, and grant "inet" 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 | 4 +- sys/kern/uipc_syscalls.c | 225 ++++++++++++++++++++++++++++++++++++++- sys/sys/socketvar.h | 3 +- 3 files changed, 228 insertions(+), 4 deletions(-) diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index ae187f3fdd4..463441dcbe0 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -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 @@ -286,7 +286,7 @@ 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 diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index c99ee6116be..f51f077ef73 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -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 $ */ /* @@ -35,9 +35,13 @@ #include #include #include +#include +#include #include #include +#include #include +#include #include #include #include @@ -52,6 +56,7 @@ #ifdef KTRACE #include #endif +#include #include #include @@ -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 */ +} diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h index f82c1b0ac11..6a3b7d4ad9e 100644 --- a/sys/sys/socketvar.h +++ b/sys/sys/socketvar.h @@ -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 -- 2.20.1