diff options
Diffstat (limited to 'freebsd/sys/kern/uipc_syscalls.c')
-rw-r--r-- | freebsd/sys/kern/uipc_syscalls.c | 97 |
1 files changed, 76 insertions, 21 deletions
diff --git a/freebsd/sys/kern/uipc_syscalls.c b/freebsd/sys/kern/uipc_syscalls.c index 0872aa62..9c4c52e4 100644 --- a/freebsd/sys/kern/uipc_syscalls.c +++ b/freebsd/sys/kern/uipc_syscalls.c @@ -60,6 +60,8 @@ __FBSDID("$FreeBSD$"); #include <sys/socketvar.h> #include <sys/syscallsubr.h> #include <sys/uio.h> +#include <sys/un.h> +#include <sys/unpcb.h> #ifdef KTRACE #include <sys/ktrace.h> #endif @@ -831,6 +833,15 @@ kern_socketpair(struct thread *td, int domain, int type, int protocol, error = soconnect2(so2, so1); if (error != 0) goto free4; + } else if (so1->so_proto->pr_flags & PR_CONNREQUIRED) { + struct unpcb *unp, *unp2; + unp = sotounpcb(so1); + unp2 = sotounpcb(so2); + /* + * No need to lock the unps, because the sockets are brand-new. + * No other threads can be using them yet + */ + unp_copy_peercred(td, unp, unp2, unp); } finit(fp1, FREAD | FWRITE | fflag, DTYPE_SOCKET, fp1->f_data, &socketops); @@ -1260,7 +1271,7 @@ kern_recvit(struct thread *td, int s, struct msghdr *mp, enum uio_seg fromseg, { struct uio auio; struct iovec *iov; - struct mbuf *m, *control = NULL; + struct mbuf *control, *m; caddr_t ctlbuf; struct file *fp; struct socket *so; @@ -1307,6 +1318,7 @@ kern_recvit(struct thread *td, int s, struct msghdr *mp, enum uio_seg fromseg, if (KTRPOINT(td, KTR_GENIO)) ktruio = cloneuio(&auio); #endif + control = NULL; len = auio.uio_resid; error = soreceive(so, &fromsa, &auio, NULL, (mp->msg_control || controlp) ? &control : NULL, @@ -1370,30 +1382,22 @@ kern_recvit(struct thread *td, int s, struct msghdr *mp, enum uio_seg fromseg, control->m_data += sizeof (struct cmsghdr); } #endif + ctlbuf = mp->msg_control; len = mp->msg_controllen; - m = control; mp->msg_controllen = 0; - ctlbuf = mp->msg_control; - - while (m && len > 0) { - unsigned int tocopy; - - if (len >= m->m_len) - tocopy = m->m_len; - else { - mp->msg_flags |= MSG_CTRUNC; - tocopy = len; - } - - if ((error = copyout(mtod(m, caddr_t), - ctlbuf, tocopy)) != 0) + for (m = control; m != NULL && len >= m->m_len; m = m->m_next) { + if ((error = copyout(mtod(m, caddr_t), ctlbuf, + m->m_len)) != 0) goto out; - ctlbuf += tocopy; - len -= tocopy; - m = m->m_next; + ctlbuf += m->m_len; + len -= m->m_len; + mp->msg_controllen += m->m_len; + } + if (m != NULL) { + mp->msg_flags |= MSG_CTRUNC; + m_dispose_extcontrolm(m); } - mp->msg_controllen = ctlbuf - (caddr_t)mp->msg_control; } out: fdrop(fp, td); @@ -1405,8 +1409,11 @@ out: if (error == 0 && controlp != NULL) *controlp = control; - else if (control) + else if (control != NULL) { + if (error != 0) + m_dispose_extcontrolm(control); m_freem(control); + } return (error); } @@ -2134,3 +2141,51 @@ getsockaddr(struct sockaddr **namp, caddr_t uaddr, size_t len) return (0); #endif /* __rtems__ */ } + +/* + * Dispose of externalized rights from an SCM_RIGHTS message. This function + * should be used in error or truncation cases to avoid leaking file descriptors + * into the recipient's (the current thread's) table. + */ +void +m_dispose_extcontrolm(struct mbuf *m) +{ + struct cmsghdr *cm; + struct file *fp; + struct thread *td; + socklen_t clen, datalen; + int error, fd, *fds, nfd; + + td = curthread; + for (; m != NULL; m = m->m_next) { + if (m->m_type != MT_EXTCONTROL) + continue; + cm = mtod(m, struct cmsghdr *); + clen = m->m_len; + while (clen > 0) { + if (clen < sizeof(*cm)) + panic("%s: truncated mbuf %p", __func__, m); + datalen = CMSG_SPACE(cm->cmsg_len - CMSG_SPACE(0)); + if (clen < datalen) + panic("%s: truncated mbuf %p", __func__, m); + + if (cm->cmsg_level == SOL_SOCKET && + cm->cmsg_type == SCM_RIGHTS) { + fds = (int *)CMSG_DATA(cm); + nfd = (cm->cmsg_len - CMSG_SPACE(0)) / + sizeof(int); + + while (nfd-- > 0) { + fd = *fds++; + error = fget(td, fd, &cap_no_rights, + &fp); + if (error == 0) + fdclose(td, fp, fd); + } + } + clen -= datalen; + cm = (struct cmsghdr *)((uint8_t *)cm + datalen); + } + m_chtype(m, MT_CONTROL); + } +} |