Date: Sat, 9 Apr 2011 09:33:36 +0000 (UTC) From: Andriy Gapon <avg@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-7@freebsd.org Subject: svn commit: r220474 - stable/7/sys/compat/linux Message-ID: <201104090933.p399XaxL007848@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: avg Date: Sat Apr 9 09:33:36 2011 New Revision: 220474 URL: http://svn.freebsd.org/changeset/base/220474 Log: MFC r220031: linux compat: improve and fix sendmsg/recvmsg compatibility PR: kern/149168 Modified: stable/7/sys/compat/linux/linux_misc.c stable/7/sys/compat/linux/linux_misc.h stable/7/sys/compat/linux/linux_socket.c stable/7/sys/compat/linux/linux_socket.h Directory Properties: stable/7/sys/ (props changed) stable/7/sys/cddl/contrib/opensolaris/ (props changed) stable/7/sys/contrib/dev/acpica/ (props changed) stable/7/sys/contrib/pf/ (props changed) Modified: stable/7/sys/compat/linux/linux_misc.c ============================================================================== --- stable/7/sys/compat/linux/linux_misc.c Sat Apr 9 09:27:11 2011 (r220473) +++ stable/7/sys/compat/linux/linux_misc.c Sat Apr 9 09:33:36 2011 (r220474) @@ -1633,6 +1633,100 @@ linux_exit_group(struct thread *td, stru return (0); } +#define _LINUX_CAPABILITY_VERSION 0x19980330 + +struct l_user_cap_header { + l_int version; + l_int pid; +}; + +struct l_user_cap_data { + l_int effective; + l_int permitted; + l_int inheritable; +}; + +int +linux_capget(struct thread *td, struct linux_capget_args *args) +{ + struct l_user_cap_header luch; + struct l_user_cap_data lucd; + int error; + + if (args->hdrp == NULL) + return (EFAULT); + + error = copyin(args->hdrp, &luch, sizeof(luch)); + if (error != 0) + return (error); + + if (luch.version != _LINUX_CAPABILITY_VERSION) { + luch.version = _LINUX_CAPABILITY_VERSION; + error = copyout(&luch, args->hdrp, sizeof(luch)); + if (error) + return (error); + return (EINVAL); + } + + if (luch.pid) + return (EPERM); + + if (args->datap) { + /* + * The current implementation doesn't support setting + * a capability (it's essentially a stub) so indicate + * that no capabilities are currently set or available + * to request. + */ + bzero (&lucd, sizeof(lucd)); + error = copyout(&lucd, args->datap, sizeof(lucd)); + } + + return (error); +} + +int +linux_capset(struct thread *td, struct linux_capset_args *args) +{ + struct l_user_cap_header luch; + struct l_user_cap_data lucd; + int error; + + if (args->hdrp == NULL || args->datap == NULL) + return (EFAULT); + + error = copyin(args->hdrp, &luch, sizeof(luch)); + if (error != 0) + return (error); + + if (luch.version != _LINUX_CAPABILITY_VERSION) { + luch.version = _LINUX_CAPABILITY_VERSION; + error = copyout(&luch, args->hdrp, sizeof(luch)); + if (error) + return (error); + return (EINVAL); + } + + if (luch.pid) + return (EPERM); + + error = copyin(args->datap, &lucd, sizeof(lucd)); + if (error != 0) + return (error); + + /* We currently don't support setting any capabilities. */ + if (lucd.effective || lucd.permitted || lucd.inheritable) { + linux_msg(td, + "capset effective=0x%x, permitted=0x%x, " + "inheritable=0x%x is not implemented", + (int)lucd.effective, (int)lucd.permitted, + (int)lucd.inheritable); + return (EPERM); + } + + return (0); +} + int linux_prctl(struct thread *td, struct linux_prctl_args *args) { @@ -1666,6 +1760,21 @@ linux_prctl(struct thread *td, struct li (void *)(register_t)args->arg2, sizeof(pdeath_signal)); break; + case LINUX_PR_GET_KEEPCAPS: + /* + * Indicate that we always clear the effective and + * permitted capability sets when the user id becomes + * non-zero (actually the capability sets are simply + * always zero in the current implementation). + */ + td->td_retval[0] = 0; + break; + case LINUX_PR_SET_KEEPCAPS: + /* + * Ignore requests to keep the effective and permitted + * capability sets when the user id becomes non-zero. + */ + break; case LINUX_PR_SET_NAME: /* * To be on the safe side we need to make sure to not Modified: stable/7/sys/compat/linux/linux_misc.h ============================================================================== --- stable/7/sys/compat/linux/linux_misc.h Sat Apr 9 09:27:11 2011 (r220473) +++ stable/7/sys/compat/linux/linux_misc.h Sat Apr 9 09:33:36 2011 (r220474) @@ -37,6 +37,8 @@ * Second arg is a ptr to return the * signal. */ +#define LINUX_PR_GET_KEEPCAPS 7 /* Get drop capabilities on setuid */ +#define LINUX_PR_SET_KEEPCAPS 8 /* Set drop capabilities on setuid */ #define LINUX_PR_SET_NAME 15 /* Set process name. */ #define LINUX_PR_GET_NAME 16 /* Get process name. */ Modified: stable/7/sys/compat/linux/linux_socket.c ============================================================================== --- stable/7/sys/compat/linux/linux_socket.c Sat Apr 9 09:27:11 2011 (r220473) +++ stable/7/sys/compat/linux/linux_socket.c Sat Apr 9 09:33:36 2011 (r220474) @@ -431,6 +431,8 @@ linux_to_bsd_cmsg_type(int cmsg_type) switch (cmsg_type) { case LINUX_SCM_RIGHTS: return (SCM_RIGHTS); + case LINUX_SCM_CREDENTIALS: + return (SCM_CREDS); } return (-1); } @@ -442,6 +444,8 @@ bsd_to_linux_cmsg_type(int cmsg_type) switch (cmsg_type) { case SCM_RIGHTS: return (LINUX_SCM_RIGHTS); + case SCM_CREDS: + return (LINUX_SCM_CREDENTIALS); } return (-1); } @@ -459,7 +463,16 @@ linux_to_bsd_msghdr(struct msghdr *bhdr, bhdr->msg_iov = PTRIN(lhdr->msg_iov); bhdr->msg_iovlen = lhdr->msg_iovlen; bhdr->msg_control = PTRIN(lhdr->msg_control); - bhdr->msg_controllen = lhdr->msg_controllen; + + /* + * msg_controllen is skipped since BSD and LINUX control messages + * are potentially different sizes (e.g. the cred structure used + * by SCM_CREDS is different between the two operating system). + * + * The caller can set it (if necessary) after converting all the + * control messages. + */ + bhdr->msg_flags = linux_to_bsd_msg_flags(lhdr->msg_flags); return (0); } @@ -472,7 +485,16 @@ bsd_to_linux_msghdr(const struct msghdr lhdr->msg_iov = PTROUT(bhdr->msg_iov); lhdr->msg_iovlen = bhdr->msg_iovlen; lhdr->msg_control = PTROUT(bhdr->msg_control); - lhdr->msg_controllen = bhdr->msg_controllen; + + /* + * msg_controllen is skipped since BSD and LINUX control messages + * are potentially different sizes (e.g. the cred structure used + * by SCM_CREDS is different between the two operating system). + * + * The caller can set it (if necessary) after converting all the + * control messages. + */ + /* msg_flags skipped */ return (0); } @@ -1034,6 +1056,7 @@ static int linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args) { struct cmsghdr *cmsg; + struct cmsgcred cmcred; struct mbuf *control; struct msghdr msg; struct l_cmsghdr linux_cmsg; @@ -1041,15 +1064,14 @@ linux_sendmsg(struct thread *td, struct struct l_msghdr linux_msg; struct iovec *iov; socklen_t datalen; + struct sockaddr *sa; + sa_family_t sa_family; void *data; int error; error = copyin(PTRIN(args->msg), &linux_msg, sizeof(linux_msg)); if (error) return (error); - error = linux_to_bsd_msghdr(&msg, &linux_msg); - if (error) - return (error); /* * Some Linux applications (ping) define a non-NULL control data @@ -1058,8 +1080,12 @@ linux_sendmsg(struct thread *td, struct * order to handle this case. This should be checked, but allows the * Linux ping to work. */ - if (msg.msg_control != NULL && msg.msg_controllen == 0) - msg.msg_control = NULL; + if (PTRIN(linux_msg.msg_control) != NULL && linux_msg.msg_controllen == 0) + linux_msg.msg_control = PTROUT(NULL); + + error = linux_to_bsd_msghdr(&msg, &linux_msg); + if (error) + return (error); #ifdef COMPAT_LINUX32 error = linux32_copyiniov(PTRIN(msg.msg_iov), msg.msg_iovlen, @@ -1070,13 +1096,21 @@ linux_sendmsg(struct thread *td, struct if (error) return (error); - if (msg.msg_control != NULL) { + control = NULL; + cmsg = NULL; + + if ((ptr_cmsg = LINUX_CMSG_FIRSTHDR(&linux_msg)) != NULL) { + error = kern_getsockname(td, args->s, &sa, &datalen); + if (error) + goto bad; + sa_family = sa->sa_family; + free(sa, M_SONAME); + error = ENOBUFS; cmsg = malloc(CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO); control = m_get(M_WAIT, MT_CONTROL); if (control == NULL) goto bad; - ptr_cmsg = LINUX_CMSG_FIRSTHDR(&msg); do { error = copyin(ptr_cmsg, &linux_cmsg, @@ -1089,28 +1123,58 @@ linux_sendmsg(struct thread *td, struct goto bad; /* - * Now we support only SCM_RIGHTS, so return EINVAL - * in any other cmsg_type + * Now we support only SCM_RIGHTS and SCM_CRED, + * so return EINVAL in any other cmsg_type */ - if ((cmsg->cmsg_type = - linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type)) == -1) - goto bad; + cmsg->cmsg_type = + linux_to_bsd_cmsg_type(linux_cmsg.cmsg_type); cmsg->cmsg_level = linux_to_bsd_sockopt_level(linux_cmsg.cmsg_level); + if (cmsg->cmsg_type == -1 + || cmsg->cmsg_level != SOL_SOCKET) + goto bad; + /* + * Some applications (e.g. pulseaudio) attempt to + * send ancillary data even if the underlying protocol + * doesn't support it which is not allowed in the + * FreeBSD system call interface. + */ + if (sa_family != AF_UNIX) + continue; + + data = LINUX_CMSG_DATA(ptr_cmsg); datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ; + + switch (cmsg->cmsg_type) + { + case SCM_RIGHTS: + break; + + case SCM_CREDS: + data = &cmcred; + datalen = sizeof(cmcred); + + /* + * The lower levels will fill in the structure + */ + bzero(data, datalen); + break; + } + cmsg->cmsg_len = CMSG_LEN(datalen); - data = LINUX_CMSG_DATA(ptr_cmsg); error = ENOBUFS; if (!m_append(control, CMSG_HDRSZ, (c_caddr_t) cmsg)) goto bad; if (!m_append(control, datalen, (c_caddr_t) data)) goto bad; - } while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&msg, ptr_cmsg))); - } else { - control = NULL; - cmsg = NULL; + } while ((ptr_cmsg = LINUX_CMSG_NXTHDR(&linux_msg, ptr_cmsg))); + + if (m_length(control, NULL) == 0) { + m_freem(control); + control = NULL; + } } msg.msg_iov = iov; @@ -1135,9 +1199,11 @@ static int linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args) { struct cmsghdr *cm; + struct cmsgcred *cmcred; struct msghdr msg; struct l_cmsghdr *linux_cmsg = NULL; - socklen_t datalen, outlen, clen; + struct l_ucred linux_ucred; + socklen_t datalen, outlen; struct l_msghdr linux_msg; struct iovec *iov, *uiov; struct mbuf *control = NULL; @@ -1194,39 +1260,35 @@ linux_recvmsg(struct thread *td, struct goto bad; } - if (control) { + outbuf = PTRIN(linux_msg.msg_control); + outlen = 0; + if (control) { linux_cmsg = malloc(L_CMSG_HDRSZ, M_TEMP, M_WAITOK | M_ZERO); - outbuf = PTRIN(linux_msg.msg_control); - cm = mtod(control, struct cmsghdr *); - outlen = 0; - clen = control->m_len; - while (cm != NULL) { + msg.msg_control = mtod(control, struct cmsghdr *); + msg.msg_controllen = control->m_len; + + cm = CMSG_FIRSTHDR(&msg); - if ((linux_cmsg->cmsg_type = - bsd_to_linux_cmsg_type(cm->cmsg_type)) == -1) + while (cm != NULL) { + linux_cmsg->cmsg_type = + bsd_to_linux_cmsg_type(cm->cmsg_type); + linux_cmsg->cmsg_level = + bsd_to_linux_sockopt_level(cm->cmsg_level); + if (linux_cmsg->cmsg_type == -1 + || cm->cmsg_level != SOL_SOCKET) { error = EINVAL; goto bad; } + data = CMSG_DATA(cm); datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data; - switch (linux_cmsg->cmsg_type) + switch (cm->cmsg_type) { - case LINUX_SCM_RIGHTS: - if (outlen + LINUX_CMSG_LEN(datalen) > - linux_msg.msg_controllen) { - if (outlen == 0) { - error = EMSGSIZE; - goto bad; - } else { - linux_msg.msg_flags |= - LINUX_MSG_CTRUNC; - goto out; - } - } + case SCM_RIGHTS: if (args->flags & LINUX_MSG_CMSG_CLOEXEC) { fds = datalen / sizeof(int); fdp = data; @@ -1237,11 +1299,40 @@ linux_recvmsg(struct thread *td, struct } } break; + + case SCM_CREDS: + /* + * Currently LOCAL_CREDS is never in + * effect for Linux so no need to worry + * about sockcred + */ + if (datalen != sizeof (*cmcred)) { + error = EMSGSIZE; + goto bad; + } + cmcred = (struct cmsgcred *)data; + bzero(&linux_ucred, sizeof(linux_ucred)); + linux_ucred.pid = cmcred->cmcred_pid; + linux_ucred.uid = cmcred->cmcred_uid; + linux_ucred.gid = cmcred->cmcred_gid; + data = &linux_ucred; + datalen = sizeof(linux_ucred); + break; + } + + if (outlen + LINUX_CMSG_LEN(datalen) > + linux_msg.msg_controllen) { + if (outlen == 0) { + error = EMSGSIZE; + goto bad; + } else { + linux_msg.msg_flags |= + LINUX_MSG_CTRUNC; + goto out; + } } linux_cmsg->cmsg_len = LINUX_CMSG_LEN(datalen); - linux_cmsg->cmsg_level = - bsd_to_linux_sockopt_level(cm->cmsg_level); error = copyout(linux_cmsg, outbuf, L_CMSG_HDRSZ); if (error) @@ -1254,18 +1345,13 @@ linux_recvmsg(struct thread *td, struct outbuf += LINUX_CMSG_ALIGN(datalen); outlen += LINUX_CMSG_LEN(datalen); - linux_msg.msg_controllen = outlen; - if (CMSG_SPACE(datalen) < clen) { - clen -= CMSG_SPACE(datalen); - cm = (struct cmsghdr *) - ((caddr_t)cm + CMSG_SPACE(datalen)); - } else - cm = NULL; + cm = CMSG_NXTHDR(&msg, cm); } } out: + linux_msg.msg_controllen = outlen; error = copyout(&linux_msg, PTRIN(args->msg), sizeof(linux_msg)); bad: Modified: stable/7/sys/compat/linux/linux_socket.h ============================================================================== --- stable/7/sys/compat/linux/linux_socket.h Sat Apr 9 09:27:11 2011 (r220473) +++ stable/7/sys/compat/linux/linux_socket.h Sat Apr 9 09:33:36 2011 (r220474) @@ -53,6 +53,7 @@ /* Socket-level control message types */ #define LINUX_SCM_RIGHTS 0x01 +#define LINUX_SCM_CREDENTIALS 0x02 /* Ancilliary data object information macros */ @@ -66,13 +67,14 @@ #define LINUX_CMSG_FIRSTHDR(msg) \ ((msg)->msg_controllen >= \ sizeof(struct l_cmsghdr) ? \ - (struct l_cmsghdr *)((msg)->msg_control) : \ + (struct l_cmsghdr *) \ + PTRIN((msg)->msg_control) : \ (struct l_cmsghdr *)(NULL)) #define LINUX_CMSG_NXTHDR(msg, cmsg) \ ((((char *)(cmsg) + \ LINUX_CMSG_ALIGN((cmsg)->cmsg_len) + \ sizeof(*(cmsg))) > \ - (((char *)(msg)->msg_control) + \ + (((char *)PTRIN((msg)->msg_control)) + \ (msg)->msg_controllen)) ? \ (struct l_cmsghdr *) NULL : \ (struct l_cmsghdr *)((char *)(cmsg) + \
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201104090933.p399XaxL007848>