summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorJerry Jelinek <jerry.jelinek@joyent.com>2016-12-22 18:57:04 +0000
committerJerry Jelinek <jerry.jelinek@joyent.com>2016-12-22 19:26:43 +0000
commit5a859e8d1c090b10cf82408f7c51c78f93dd543f (patch)
tree8eb3a646b675ef6c3abfb91f86a5872ab11f317d /usr
parent826aed25de4c9b911d0fd07fa6236d199eaff2a6 (diff)
downloadillumos-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.c77
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);