Skip to content

Commit

Permalink
OS-5866 incorrect pid in cmsg for Unix socket with SO_PASSCRED
Browse files Browse the repository at this point in the history
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Approved by: Patrick Mooney <patrick.mooney@joyent.com>
  • Loading branch information
jjelinek committed Dec 22, 2016
1 parent 826aed2 commit 5a859e8
Showing 1 changed file with 73 additions and 4 deletions.
77 changes: 73 additions & 4 deletions usr/src/uts/common/brand/lx/syscall/lx_socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,13 @@ typedef struct lx_socket_aux_data
LXSS_CONNECTING,
LXSS_CONNECTED
} lxsad_status;
boolean_t lxsad_stream_cred;
uint_t lxsad_flags;
} lx_socket_aux_data_t;

/* lxsad_flags */
#define LXSAD_FL_STRCRED 0x1
#define LXSAD_FL_EMULSEQPKT 0x2

static lx_socket_aux_data_t *lx_sad_acquire(vnode_t *);

/* VSD key for lx-specific socket information */
Expand Down Expand Up @@ -1159,7 +1163,7 @@ lx_cmsg_try_ucred(sonode_t *so, struct nmsghdr *msg, socklen_t origlen)
return (0);
}
sad = lx_sad_acquire(SOTOV(so));
if (!sad->lxsad_stream_cred) {
if ((sad->lxsad_flags & LXSAD_FL_STRCRED) == 0) {
mutex_exit(&sad->lxsad_lock);
return (0);
}
Expand Down Expand Up @@ -2968,6 +2972,17 @@ lx_setsockopt_socket(sonode_t *so, int optname, void *optval, socklen_t optlen)
* that option. Instead, we track the setting internally and,
* when there is appropriate cmsg space, emulate the credential
* passing by querying the STREAMS ioctl.
*
* Note: this approach is broken for the case when a process
* sets up a Unix-domain socket with SO_PASSCRED, then forks
* one or more children, and expects to use the cmsg cred to
* accurately know which child pid sent the message (currently
* a pid is recorded when the socket is connected, not for each
* msg sent). getpeerucred(3c) suffers from the same problem.
* We have a workaround in lx_socketpair (use DGRAM if
* SEQPACKET), but the general case requires enhancing our
* streams support to allow passing credential cmsgs on a
* connection-oriented Unix socket.
*/
if (so->so_family == AF_UNIX &&
(so->so_mode & SM_CONNREQUIRED) != 0) {
Expand All @@ -2978,7 +2993,11 @@ lx_setsockopt_socket(sonode_t *so, int optname, void *optval, socklen_t optlen)
}
intval = (int *)optval;
sad = lx_sad_acquire(SOTOV(so));
sad->lxsad_stream_cred = !(*intval == 0);
if (*intval == 0) {
sad->lxsad_flags &= ~LXSAD_FL_STRCRED;
} else {
sad->lxsad_flags |= LXSAD_FL_STRCRED;
}
mutex_exit(&sad->lxsad_lock);
return (0);
}
Expand Down Expand Up @@ -3212,6 +3231,28 @@ lx_getsockopt_socket(sonode_t *so, int optname, void *optval,
lx_proto_opts_t sockopts_tbl = PROTO_SOCKOPTS(ltos_socket_sockopts);

switch (optname) {
case LX_SO_TYPE:
/*
* Special handling for connectionless AF_UNIX sockets.
* See lx_socketpair for more details.
*/
if (so->so_family == AF_UNIX &&
(so->so_mode & SM_CONNREQUIRED) == 0) {
lx_socket_aux_data_t *sad;

if (*optlen < sizeof (int))
return (EINVAL);
sad = lx_sad_acquire(SOTOV(so));
if ((sad->lxsad_flags & LXSAD_FL_EMULSEQPKT) != 0) {
*intval = LX_SOCK_SEQPACKET;
*optlen = sizeof (int);
mutex_exit(&sad->lxsad_lock);
return (0);
}
mutex_exit(&sad->lxsad_lock);
}
break;

case LX_SO_PASSSEC:
/*
* Communicate value of 0 since selinux-related functionality
Expand All @@ -3238,7 +3279,8 @@ lx_getsockopt_socket(sonode_t *so, int optname, void *optval,
return (EINVAL);
}
sad = lx_sad_acquire(SOTOV(so));
*intval = sad->lxsad_stream_cred;
*intval = ((sad->lxsad_flags & LXSAD_FL_STRCRED) == 0 ?
0 : 1);
*optlen = sizeof (int);
mutex_exit(&sad->lxsad_lock);
return (0);
Expand Down Expand Up @@ -3849,6 +3891,22 @@ lx_socketpair(int domain, int type, int protocol, int *sv)
{
int err, options, fds[2];
file_t *fps[2];
boolean_t emul_seqp = B_FALSE;

/*
* For the special case of SOCK_SEQPACKET for AF_UNIX, we want to treat
* this as a SOCK_DGRAM. The semantics are similar, but our native code
* will not pass cmsg creds over a connection-oriented socket, unlike a
* connectionless one. Some Linux code depends on this for Unix-domain
* sockets. In particular, a sockopt of SO_PASSCRED, which we map into
* our native SO_RECVUCRED, must work across fork so that the correct
* pid of the sender is available in the cmsg. See the comment in
* lx_setsockopt_socket().
*/
if (domain == LX_AF_UNIX && type == LX_SOCK_SEQPACKET) {
type = LX_SOCK_DGRAM;
emul_seqp = B_TRUE;
}

if ((err = lx_convert_sock_args(domain, type, protocol, &domain, &type,
&options, &protocol)) != 0) {
Expand Down Expand Up @@ -3884,8 +3942,19 @@ lx_socketpair(int domain, int type, int protocol, int *sv)
return (set_errno(err));
}

if (emul_seqp) {
int i;
for (i = 0; i < 2; i++) {
sonode_t *so = VTOSO(fps[i]->f_vnode);
lx_socket_aux_data_t *sad = lx_sad_acquire(SOTOV(so));
sad->lxsad_flags |= LXSAD_FL_EMULSEQPKT;
mutex_exit(&sad->lxsad_lock);
}
}

setf(fds[0], fps[0]);
setf(fds[1], fps[1]);

if ((options & SOCK_CLOEXEC) != 0) {
f_setfd(fds[0], FD_CLOEXEC);
f_setfd(fds[1], FD_CLOEXEC);
Expand Down

0 comments on commit 5a859e8

Please sign in to comment.