sysdeps/mach/hurd/Changelog 2015-02-19 Svante Signell * Authentication: match a call to __auth_user_authenticate() in sendmsg.c with a call to __auth_server_authenticate() in recvmsg.c using the rendezvous port. * sendmsg.c (__libc_sendmsg): Add joint support for SCM_RIGHTS and SCM_CREDS. * recvmsg.c (__libc_recvmsg ): Add joint support for SCM_RIGHTS and SCM_CREDS. (check_auth): New function checking received PID, *IDs and groups data. --- sysdeps/mach/hurd/recvmsg.c | 264 +++++++++++++++++++++++++++++++++++++++----- sysdeps/mach/hurd/sendmsg.c | 147 ++++++++++++++++++++++-- 2 files changed, 371 insertions(+), 40 deletions(-) Index: glibc-2.19/sysdeps/mach/hurd/sendmsg.c =================================================================== --- glibc-2.19.orig/sysdeps/mach/hurd/sendmsg.c +++ glibc-2.19/sysdeps/mach/hurd/sendmsg.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001-2014 Free Software Foundation, Inc. +/* Copyright (C) 2001-2015 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -33,8 +34,10 @@ __libc_sendmsg (int fd, const struct msg { error_t err = 0; struct cmsghdr *cmsg; - mach_port_t *ports = NULL; - mach_msg_type_number_t nports = 0; + mach_port_t *ports = NULL, *rports= NULL, *cports = NULL; + mach_msg_type_number_t nports = 0, nrights = 0, ncreds = 0; + mach_port_t ref = NULL; + struct cmsgcred *ucredp = NULL; int *fds, nfds; struct sockaddr_un *addr = message->msg_name; socklen_t addr_len = message->msg_namelen; @@ -107,16 +110,17 @@ __libc_sendmsg (int fd, const struct msg } /* SCM_RIGHTS support: get the number of fds to send. */ - cmsg = CMSG_FIRSTHDR (message); - for (; cmsg; cmsg = CMSG_NXTHDR (message, cmsg)) + for (cmsg = CMSG_FIRSTHDR (message); + cmsg != NULL; + cmsg = CMSG_NXTHDR (message, cmsg)) if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) - nports += (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) + nrights += (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) / sizeof (int); - if (nports) - ports = __alloca (nports * sizeof (mach_port_t)); + if (nrights) + rports = __alloca (nrights * sizeof (mach_port_t)); - nports = 0; + nrights = 0; for (cmsg = CMSG_FIRSTHDR (message); cmsg; cmsg = CMSG_NXTHDR (message, cmsg)) @@ -132,10 +136,10 @@ __libc_sendmsg (int fd, const struct msg err = HURD_DPORT_USE (fds[i], ({ - err = __io_restrict_auth (port, &ports[nports], + err = __io_restrict_auth (port, &rports[nrights], 0, 0, 0, 0); if (! err) - nports++; + nrights++; /* We pass the flags in the control data. */ fds[i] = descriptor->flags; err; @@ -147,6 +151,51 @@ __libc_sendmsg (int fd, const struct msg } } + /* + SCM_CREDS support: Create and send the credentials data together + with the rendezvous port. + */ + ncreds = 0; + for (cmsg = CMSG_FIRSTHDR (message); + cmsg != NULL; + cmsg = CMSG_NXTHDR (message, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + ncreds += 1; + + /* FIXME: Currently only ONE port is supported, error out if more */ + if (ncreds != 0 && ncreds != 1) + { + /* FIXME: Which error to issue here? */ + err = EGRATUITOUS; + goto out; + } + if (ncreds == 1) + cports = __alloca (ncreds * sizeof (mach_port_t)); + + for (cmsg = CMSG_FIRSTHDR (message); + cmsg; + cmsg = CMSG_NXTHDR (message, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + { + rendezvous = __mach_reply_port (); + err = mach_port_insert_right (mach_task_self (), rendezvous, + rendezvous, MACH_MSG_TYPE_MAKE_SEND); + if (err) + goto out; + cports[0] = rendezvous; + + ucredp = (struct cmsgcred *) CMSG_DATA(cmsg); + /* Fill in credentials data */ + ucredp->cmcred_pid = __getpid(); + ucredp->cmcred_uid = __getuid(); + ucredp->cmcred_euid = __geteuid(); + ucredp->cmcred_gid = __getgid(); + /* egid not in struct csmgcred */ + ucredp->cmcred_ngroups = + __getgroups (sizeof (ucredp->cmcred_groups) / sizeof (gid_t), + ucredp->cmcred_groups); + } + if (addr) { if (addr->sun_family == AF_LOCAL) @@ -172,6 +221,52 @@ __libc_sendmsg (int fd, const struct msg err = EIEIO; } + int incr = 0, k, l; + int *idx = NULL; + nports = nrights + ncreds; + if (nports > 0) + { + ports = __alloca (nports * sizeof (mach_port_t)); + idx = __alloca (nports * sizeof (char)); + } + + k = 0; + for (cmsg = CMSG_FIRSTHDR (message); + cmsg != NULL; + cmsg = CMSG_NXTHDR (message, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET) + { + if (cmsg->cmsg_type == SCM_RIGHTS) + { + incr = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) + / sizeof (int); + for (l=0; l < incr; l++) + idx[k+l] = 'r'; + k++; + } + if (cmsg->cmsg_type == SCM_CREDS) + { + idx[k] = 'c'; + k++; + } + } + + /* Copy rports and cports arrays to ports array */ + k = 0, l = 0; + for (i = 0; i < nports; i++) + { + if (idx[i] == 'r') + { + ports[i] = rports[k]; + k++; + } + if (idx[i] == 'c') + { + ports[i] = cports[l]; + l++; + } + } + err = HURD_DPORT_USE (fd, ({ if (err) @@ -198,9 +293,35 @@ __libc_sendmsg (int fd, const struct msg })); socketrpc = 1; + /* + SCM_CREDS support: call __auth_user_authenticate matched with a + call to __auth_server_authenticate in recvmsg + */ + /* FIXME: Currently only ONE port is used */ + for (cmsg = CMSG_FIRSTHDR (message); + cmsg; + cmsg = CMSG_NXTHDR (message, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + { + mach_port_t newport; + + err = __USEPORT + (AUTH, __auth_user_authenticate (port, + rendezvous, MACH_MSG_TYPE_MAKE_SEND, + &newport)); + if (err) + goto out; + } + } + out: - for (i = 0; i < nports; i++) - __mach_port_deallocate (__mach_task_self (), ports[i]); + for (i = 0; i < nrights; i++) + __mach_port_deallocate (__mach_task_self (), rports[i]); + + if (ncreds == 1) + __mach_port_deallocate (__mach_task_self (), cports[0]); + __mach_port_destroy (__mach_task_self (), rendezvous); if (dealloc) __vm_deallocate (__mach_task_self (), data.addr, len); Index: glibc-2.19/sysdeps/mach/hurd/recvmsg.c =================================================================== --- glibc-2.19.orig/sysdeps/mach/hurd/recvmsg.c +++ glibc-2.19/sysdeps/mach/hurd/recvmsg.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001-2014 Free Software Foundation, Inc. +/* Copyright (C) 2001-2015 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -18,10 +18,13 @@ #include #include #include +#include +#include #include #include #include +#include /* Receive a message as described by MESSAGE from socket FD. Returns the number of bytes read or -1 for errors. */ @@ -32,15 +35,15 @@ __libc_recvmsg (int fd, struct msghdr *m addr_port_t aport; char *data = NULL; mach_msg_type_number_t len = 0; - mach_port_t *ports, *newports; - mach_msg_type_number_t nports = 0; + mach_port_t *ports = NULL, *newports = NULL, *rports= NULL, *cports = NULL; + mach_msg_type_number_t nports = 0, nrights = 0, ncreds = 0; struct cmsghdr *cmsg; char *cdata = NULL; mach_msg_type_number_t clen = 0; size_t amount; char *buf; int nfds, *fds; - int i, j; + int i, j = 0; error_t reauthenticate (mach_port_t port, mach_port_t *result) { @@ -60,6 +63,114 @@ __libc_recvmsg (int fd, struct msghdr *m return err; } + error_t check_auth (mach_port_t rendezvous, + __pid_t pid, + __uid_t euid, __uid_t auid, + __gid_t egid, __gid_t agid, + int ngroups, __gid_t *groups) + { + error_t err; + mach_port_t newport = MACH_PORT_NULL; +#define UIDSIZE sizeof (uid_t) * CMGROUP_MAX +#define GIDSIZE sizeof (gid_t) * CMGROUP_MAX + __uid_t euids_buf[UIDSIZE], auids_buf[UIDSIZE]; + __uid_t *euids = euids_buf, *auids = auids_buf; + __gid_t egids_buf[GIDSIZE], agids_buf[GIDSIZE]; + __gid_t *egids = egids_buf, *agids = agids_buf; + size_t neuids = CMGROUP_MAX, nauids = CMGROUP_MAX; + size_t negids = CMGROUP_MAX, nagids = CMGROUP_MAX; + + pidarray_t pids; + mach_msg_type_number_t npids = 0; + struct procinfo *pi = NULL; + mach_msg_type_number_t pi_size = 0; + int flags = PI_FETCH_TASKINFO; + char *tw = NULL; + size_t twsz = 0; + + HURD_CRITICAL_BEGIN; + __mutex_lock (&_hurd_id.lock); + + err = mach_port_mod_refs (mach_task_self (), rendezvous, + MACH_PORT_RIGHT_SEND, 1); + if (err) + { + mach_port_deallocate (mach_task_self (), rendezvous); + goto finish; + } + + do + err = __USEPORT + (AUTH, __auth_server_authenticate (port, + rendezvous, MACH_MSG_TYPE_COPY_SEND, + newport, MACH_MSG_TYPE_COPY_SEND, + &euids, &neuids, &auids, &nauids, + &egids, &negids, &agids, &nagids)); + while (err == EINTR); + mach_port_deallocate (mach_task_self (), rendezvous); + mach_port_deallocate (mach_task_self (), newport); + if (err) + goto finish; + + /* Check IDs */ + if (euid != euids_buf[0] || auid != auids_buf[0] || + egid != egids_buf[0] || agid != agids_buf[0]) + { + err = EPERM; + goto finish; + } + /* Check groups */ + for (int i = 0; i < negids; i++) + { + /* FIXME: is sorted check needed? */ + if (groups[i] != egids_buf[i]) + { + err = EPERM; + goto finish; + } + } + + /* Check PID */ + /* XXX: Use proc_getallpids and proc_getprocinfo until + proc_user_authenticate proc_server_authenticate is implemented + */ + err = __USEPORT (PROC, __proc_getallpids (port, &pids, &npids)); + if (err) + { + vm_deallocate (mach_task_self (), (vm_address_t) pids, npids * sizeof pids[0]); + goto finish; + } + for (i=0; i < npids; i++) + { + if (pids[i] == pid) + { + /* Get procinfo to check the owner. */ + err = __USEPORT (PROC, __proc_getprocinfo (port, pids[i], &flags, + (procinfo_t *)&pi, + &pi_size, &tw, &twsz)); + if (!err) + { + if (twsz) /* Gratuitous. */ + __munmap (tw, twsz); + + if (pi->owner == euid) + break; + } + } + /* FIXME: which return code to use? + EACCES = permission denied + ESRCH = no such process + */ + err = ESRCH; + } + vm_deallocate (mach_task_self (), (vm_address_t) pids, npids * sizeof pids[0]); + + finish: + __mutex_unlock (&_hurd_id.lock); + HURD_CRITICAL_END; + return err; + } + /* Find the total number of bytes to be read. */ amount = 0; for (i = 0; i < message->msg_iovlen; i++) @@ -155,22 +266,89 @@ __libc_recvmsg (int fd, struct msghdr *m message->msg_controllen = clen; memcpy (message->msg_control, cdata, message->msg_controllen); - /* SCM_RIGHTS ports. */ + /* SCM_RIGHTS support: Fill in the rights data */ + /* Find the number of SCM_RIGHTS ports to use */ + + int incr = 0, k, l; + int *idx = NULL; + if (nports > 0) + idx = __alloca (nports * sizeof (char)); + + k = 0; + for (cmsg = CMSG_FIRSTHDR (message); + cmsg != NULL; + cmsg = CMSG_NXTHDR (message, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET) + { + if (cmsg->cmsg_type == SCM_RIGHTS) + { + incr = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) + / sizeof (int); + for (l=0; l < incr; l++) + idx[k+l] = 'r'; + k++; + nrights += incr; + } + if (cmsg->cmsg_type == SCM_CREDS) + { + idx[k] = 'c'; + k++; + ncreds += 1; + } + } + + if (nrights > 0) + rports = __alloca (nrights * sizeof (int)); + + /* FIXME: Currently only ONE port can be used, error out if more */ + if (ncreds != 0 && ncreds != 1) { - newports = __alloca (nports * sizeof (mach_port_t)); + /* FIXME: Which error to issue here? */ + err = EGRATUITOUS; + return __hurd_fail (err); + } + if (ncreds == 1) + cports = __alloca (ncreds * sizeof (mach_port_t)); - /* Reauthenticate all ports here. */ - for (i = 0; i < nports; i++) + k = 0, l = 0; + for (i = 0; i < nports; i++) + { + if (idx[i] == 'r') + { + rports[k] = ports[i]; + k++; + } + if (idx[i] == 'c') { - err = reauthenticate (ports[i], &newports[i]); - __mach_port_deallocate (__mach_task_self (), ports[i]); + cports[l] = ports[i]; + l++; + } + } + + if (nrights + ncreds != nports) + { + /* FIXME: Which error to issue here? */ + err = EGRATUITOUS; + return __hurd_fail (err); + } + + /* SCM_RIGHTS ports. */ + if (nrights > 0) + { + newports = __alloca (nrights * sizeof (mach_port_t)); + + /* Reauthenticate all SCM_RIGHTS ports here. */ + for (i = 0; i < nrights; i++) + { + err = reauthenticate (rports[i], &newports[i]); + __mach_port_deallocate (__mach_task_self (), rports[i]); if (err) { for (j = 0; j < i; j++) __mach_port_deallocate (__mach_task_self (), newports[j]); - for (j = i+1; j < nports; j++) - __mach_port_deallocate (__mach_task_self (), ports[j]); + for (j = i+1; j < nrights; j++) + __mach_port_deallocate (__mach_task_self (), rports[j]); __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen); __hurd_fail (err); @@ -179,16 +357,16 @@ __libc_recvmsg (int fd, struct msghdr *m j = 0; for (cmsg = CMSG_FIRSTHDR (message); - cmsg; + cmsg != NULL; cmsg = CMSG_NXTHDR (message, cmsg)) { if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { fds = (int *) CMSG_DATA (cmsg); nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) - / sizeof (int); + / sizeof (int); - for (i = 0; i < nfds && j < nports; i++) + for (i = 0; i < nfds && j < nrights; i++) { /* The fd's flags are passed in the control data. */ fds[i] = _hurd_intern_fd (newports[j++], fds[i], 0); @@ -201,39 +379,71 @@ __libc_recvmsg (int fd, struct msghdr *m } } - if (j != nports) + if (j != nrights) err = EGRATUITOUS; if (err) cleanup: { /* Clean up all the file descriptors. */ - nports = j; + nrights = j; j = 0; for (cmsg = CMSG_FIRSTHDR (message); - cmsg; + cmsg != NULL; cmsg = CMSG_NXTHDR (message, cmsg)) { - if (cmsg->cmsg_level == SOL_SOCKET - && cmsg->cmsg_type == SCM_RIGHTS) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) { fds = (int *) CMSG_DATA (cmsg); - nfds = (cmsg->cmsg_len - - CMSG_ALIGN (sizeof (struct cmsghdr))) - / sizeof (int); - for (i = 0; i < nfds && j < nports; i++, j++) + nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) + / sizeof (int); + for (i = 0; i < nfds && j < nrights; i++, j++) _hurd_fd_close (_hurd_fd_get (fds[i])); } } - for (; j < nports; j++) - __mach_port_deallocate (__mach_task_self (), newports[j]); - + for (; j < nrights; j++) + { + __mach_port_deallocate (__mach_task_self (), newports[j]); + __mach_port_deallocate (__mach_task_self (), rports[j]); + } __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen); __hurd_fail (err); } } + /* SCM_CREDS support: Fill in the credentials data */ + /* Read received credentials */ + for (cmsg = CMSG_FIRSTHDR (message); + cmsg != NULL; + cmsg = CMSG_NXTHDR (message, cmsg)) + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + { + struct cmsgcred *ucredp = NULL; + __pid_t pid = 0; + __uid_t euid = 0, auid = 0; + __gid_t egid = 0, agid = 0; + int ngroups; + __gid_t groups[CMGROUP_MAX]; + + ucredp = (struct cmsgcred *) CMSG_DATA(cmsg); + pid = ucredp->cmcred_pid; + auid = ucredp->cmcred_uid; + euid = ucredp->cmcred_euid; + agid = ucredp->cmcred_gid; + egid = agid; /* Not in struct cmsgcred */ + ngroups = ucredp->cmcred_ngroups; + for (int i = 0; i < ngroups ; i++) + groups[i] = ucredp->cmcred_groups[i]; + + /* Check recieved credentials */ + err = check_auth (cports[0], pid, + euid, auid, egid, agid, ngroups, groups); + __mach_port_deallocate (mach_task_self (), cports[0]); + if (err) + return __hurd_fail (err); + } + __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen); return (buf - data);