diff options
author | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-12-22 18:57:04 +0000 |
---|---|---|
committer | Jerry Jelinek <jerry.jelinek@joyent.com> | 2016-12-22 19:26:43 +0000 |
commit | 5a859e8d1c090b10cf82408f7c51c78f93dd543f (patch) | |
tree | 8eb3a646b675ef6c3abfb91f86a5872ab11f317d /usr | |
parent | 826aed25de4c9b911d0fd07fa6236d199eaff2a6 (diff) | |
download | illumos-joyent-5a859e8d1c090b10cf82408f7c51c78f93dd543f.tar.gz |
OS-5866 incorrect pid in cmsg for Unix socket with SO_PASSCRED
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Approved by: Patrick Mooney <patrick.mooney@joyent.com>
Diffstat (limited to 'usr')
-rw-r--r-- | usr/src/uts/common/brand/lx/syscall/lx_socket.c | 77 |
1 files changed, 73 insertions, 4 deletions
diff --git a/usr/src/uts/common/brand/lx/syscall/lx_socket.c b/usr/src/uts/common/brand/lx/syscall/lx_socket.c index 4bb7440eca..430ed17692 100644 --- a/usr/src/uts/common/brand/lx/syscall/lx_socket.c +++ b/usr/src/uts/common/brand/lx/syscall/lx_socket.c @@ -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 */ @@ -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); } @@ -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) { @@ -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); } @@ -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 @@ -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); @@ -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) { @@ -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); |