4
0
mirror of git://sourceware.org/git/newlib-cygwin.git synced 2025-02-02 12:30:24 +08:00

Cygwin: AF_UNIX: improve control message handling

This includes various changes to create_cmsg_data and
evaluate_cmsg_data.  The most important are:

- create_cmsg_data now allows only one SCM_RIGHTS message and one
  SCM_CREDENTIALS message.

- evaluate_cmsg_data now truncates the ancillary data to the number of
  control messages that will fit in the supplied buffer.  Previously
  it discarded all control messages if the buffer was too small.

See https://man7.org/linux/man-pages/man7/unix.7.html.
This commit is contained in:
Ken Brown 2020-11-02 10:05:47 -05:00
parent d42acb9bc8
commit 3d37fd9cef

View File

@ -1948,7 +1948,7 @@ fhandler_socket_unix::evaluate_cmsg_data (af_unix_pkt_hdr_t *packet,
/* Massage the received control messages. */ /* Massage the received control messages. */
msg1.msg_control = tp.w_get (); msg1.msg_control = tp.w_get ();
msg1.msg_controllen = packet->cmsg_len + CMSG_SPACE (0); msg1.msg_controllen = MIN (msg->msg_controllen, packet->cmsg_len);
memset (msg1.msg_control, 0, msg1.msg_controllen); memset (msg1.msg_control, 0, msg1.msg_controllen);
msg2.msg_control = AF_UNIX_PKT_CMSG (packet); msg2.msg_control = AF_UNIX_PKT_CMSG (packet);
msg2.msg_controllen = packet->cmsg_len; msg2.msg_controllen = packet->cmsg_len;
@ -1958,40 +1958,30 @@ fhandler_socket_unix::evaluate_cmsg_data (af_unix_pkt_hdr_t *packet,
for (struct cmsghdr *q = CMSG_FIRSTHDR (&msg2); q != NULL; for (struct cmsghdr *q = CMSG_FIRSTHDR (&msg2); q != NULL;
q = CMSG_NXTHDR (&msg2, q)) q = CMSG_NXTHDR (&msg2, q))
{ {
struct cmsghdr *p1; /* Lags behind p. */
switch (q->cmsg_type) switch (q->cmsg_type)
{ {
case SCM_CREDENTIALS: case SCM_CREDENTIALS:
if (so_passcred ()) if (!so_passcred ())
memcpy (p, q, q->cmsg_len);
else
continue; continue;
break;
case SCM_RIGHTS: case SCM_RIGHTS:
/* Deserialize, check length, copy new cmsghdr block, update /* FIXME: Deserialize and change fds. */
len. For now, just copy without deserialization for
testing purposes. */
memcpy (p, q, q->cmsg_len);
break; break;
default: default:
set_errno (EINVAL); set_errno (EINVAL);
return false; return false;
} }
p1 = p; if (!p || len + CMSG_ALIGN (q->cmsg_len) > (size_t) msg1.msg_controllen)
{
msg->msg_flags |= MSG_CTRUNC;
goto out;
}
memcpy (p, q, q->cmsg_len);
len += CMSG_ALIGN (q->cmsg_len);
p = CMSG_NXTHDR (&msg1, p); p = CMSG_NXTHDR (&msg1, p);
len += (PBYTE) p - (PBYTE) p1;
} }
if (msg->msg_controllen < (int) len) out:
{
msg->msg_flags |= MSG_TRUNC;
msg->msg_controllen = 0;
}
else
{
memcpy (msg->msg_control, msg1.msg_control, len); memcpy (msg->msg_control, msg1.msg_control, len);
msg->msg_controllen = len; msg->msg_controllen = len;
}
return true; return true;
} }
@ -2551,17 +2541,15 @@ bool
fhandler_socket_unix::create_cmsg_data (af_unix_pkt_hdr_t *packet, fhandler_socket_unix::create_cmsg_data (af_unix_pkt_hdr_t *packet,
const struct msghdr *msg) const struct msghdr *msg)
{ {
bool saw_scm_cred = false; bool saw_scm_cred = false, saw_scm_rights = false;
size_t len = 0; size_t len = 0;
struct msghdr msgh; struct msghdr msgh;
tmp_pathbuf tp; tmp_pathbuf tp;
/* Massage the specified control messages. We need to serialize any /* Massage the specified control messages. */
SCM_RIGHTS message and, if necessary, append an SCM_CREDENTIALS
message. */
msgh.msg_control = tp.w_get (); msgh.msg_control = tp.w_get ();
msgh.msg_controllen = 2 * NT_MAX_PATH; msgh.msg_controllen = MAX_AF_PKT_LEN - packet->pckt_len;
memset (msgh.msg_control, 0, msgh.msg_controllen); memset (msgh.msg_control, 0, msgh.msg_controllen);
/* Copy from msg to msgh. */ /* Copy from msg to msgh. */
@ -2569,15 +2557,20 @@ fhandler_socket_unix::create_cmsg_data (af_unix_pkt_hdr_t *packet,
for (struct cmsghdr *q = CMSG_FIRSTHDR (msg); q != NULL; for (struct cmsghdr *q = CMSG_FIRSTHDR (msg); q != NULL;
q = CMSG_NXTHDR (msg, q)) q = CMSG_NXTHDR (msg, q))
{ {
struct cmsghdr *p1; /* Lags behind p. */
struct ucred *cred = NULL; struct ucred *cred = NULL;
bool admin = false; bool admin = false;
switch (q->cmsg_type) switch (q->cmsg_type)
{ {
case SCM_CREDENTIALS: case SCM_CREDENTIALS:
if (saw_scm_cred)
{
set_errno (EINVAL);
return false;
}
saw_scm_cred = true; saw_scm_cred = true;
if (q->cmsg_len != CMSG_LEN (sizeof (struct ucred))) if (q->cmsg_len != CMSG_LEN (sizeof (struct ucred))
|| q->cmsg_level != SOL_SOCKET)
{ {
set_errno (EINVAL); set_errno (EINVAL);
return false; return false;
@ -2617,31 +2610,34 @@ fhandler_socket_unix::create_cmsg_data (af_unix_pkt_hdr_t *packet,
set_errno (EPERM); set_errno (EPERM);
return false; return false;
} }
memcpy (p, q, q->cmsg_len);
break; break;
case SCM_RIGHTS: case SCM_RIGHTS:
/* Serialize the fds. For now, just send the list of fds. */ if (saw_scm_rights)
memcpy (p, q, q->cmsg_len); {
set_errno (EINVAL);
return false;
}
saw_scm_rights = true;
/* FIXME: Serialize the fds. */
break; break;
default: default:
set_errno (EINVAL); set_errno (EINVAL);
return false; return false;
} }
p1 = p; if (!p || len + CMSG_ALIGN (q->cmsg_len) > (size_t) msgh.msg_controllen)
p = CMSG_NXTHDR (&msgh, p);
if (p)
len += (PBYTE) p - (PBYTE) p1;
if (!p || packet->pckt_len + len > MAX_AF_PKT_LEN)
{ {
set_errno (EMSGSIZE); set_errno (EMSGSIZE);
return false; return false;
} }
memcpy (p, q, q->cmsg_len);
len += CMSG_ALIGN (q->cmsg_len);
p = CMSG_NXTHDR (&msgh, p);
} }
if (!saw_scm_cred) if (!saw_scm_cred)
{ {
/* Append a credentials block. */ /* Append a credentials block. */
if (packet->pckt_len + len + CMSG_SPACE (sizeof (struct ucred)) if (!p || len + CMSG_SPACE (sizeof (struct ucred))
> MAX_AF_PKT_LEN) > (size_t) msgh.msg_controllen)
{ {
set_errno (EMSGSIZE); set_errno (EMSGSIZE);
return false; return false;