linux compat: improve and fix sendmsg/recvmsg compatibility
- implement baseic stubs for capget, capset, prctl PR_GET_KEEPCAPS and prctl PR_SET_KEEPCAPS. - add SCM_CREDS support to sendmsg and recvmsg - modify sendmsg to ignore control messages if not using UNIX domain sockets This should allow linux pulse audio daemon and client work on FreeBSD and interoperate with native counter-parts modulo the differences in pulseaudio versions. PR: kern/149168 Submitted by: John Wehle <john@feith.com> Reviewed by: netchild MFC after: 2 weeks
This commit is contained in:
parent
931f0826ea
commit
605da56bc3
@ -1679,6 +1679,100 @@ linux_exit_group(struct thread *td, struct linux_exit_group_args *args)
|
||||
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)
|
||||
{
|
||||
@ -1712,6 +1806,21 @@ linux_prctl(struct thread *td, struct linux_prctl_args *args)
|
||||
(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
|
||||
|
@ -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. */
|
||||
|
||||
|
@ -433,6 +433,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);
|
||||
}
|
||||
@ -444,6 +446,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, const struct l_msghdr *lhdr)
|
||||
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 *bhdr, struct l_msghdr *lhdr)
|
||||
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);
|
||||
}
|
||||
@ -1092,6 +1114,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;
|
||||
@ -1099,13 +1122,12 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
|
||||
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);
|
||||
|
||||
@ -1116,8 +1138,12 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
|
||||
* 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,
|
||||
@ -1128,13 +1154,21 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
|
||||
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,
|
||||
@ -1147,28 +1181,58 @@ linux_sendmsg(struct thread *td, struct linux_sendmsg_args *args)
|
||||
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;
|
||||
|
||||
datalen = linux_cmsg.cmsg_len - L_CMSG_HDRSZ;
|
||||
cmsg->cmsg_len = CMSG_LEN(datalen);
|
||||
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);
|
||||
|
||||
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;
|
||||
@ -1193,9 +1257,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;
|
||||
@ -1252,39 +1318,35 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
|
||||
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;
|
||||
|
||||
msg.msg_control = mtod(control, struct cmsghdr *);
|
||||
msg.msg_controllen = control->m_len;
|
||||
|
||||
cm = CMSG_FIRSTHDR(&msg);
|
||||
|
||||
while (cm != NULL) {
|
||||
|
||||
if ((linux_cmsg->cmsg_type =
|
||||
bsd_to_linux_cmsg_type(cm->cmsg_type)) == -1)
|
||||
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;
|
||||
@ -1295,11 +1357,40 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
|
||||
}
|
||||
}
|
||||
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)
|
||||
@ -1312,18 +1403,13 @@ linux_recvmsg(struct thread *td, struct linux_recvmsg_args *args)
|
||||
|
||||
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:
|
||||
|
@ -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) + \
|
||||
|
Loading…
Reference in New Issue
Block a user