--- sysdeps/mach/hurd/recvmsg.c | 257 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 231 insertions(+), 26 deletions(-) --- a/sysdeps/mach/hurd/recvmsg.c +++ b/sysdeps/mach/hurd/recvmsg.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2001, 2002, 2010 Free Software Foundation, Inc. +/* Copyright (C) 2001, 2002, 2010, 2013 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 @@ 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,121 @@ 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; + //auth_t auth_server = getauth (); + 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; + + //mach_port_t proc_server = getproc (); + 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; + + /* FIXME: In this the right lock? */ + /* FIXME: EPERM and/or EACCES? */ + 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 (), auth_server); + 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)); + // (int **)&pi, &pi_size, &tw, &twsz); + if (!err) + { + if (twsz) /* Gratuitous. */ + __munmap (tw, twsz); + + //REMOVE?? __vm_deallocate (__mach_task_self (), (vm_address_t) tw, twsz); + if (pi->owner == euid) + break; + } + } + /* FIXME: return code: + 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 +273,88 @@ 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) + { + /* FIXME: Which error to issue here? */ + err = EGRATUITOUS; + return __hurd_fail (err); + } + if (ncreds == 1) + cports = __alloca (ncreds * sizeof (mach_port_t)); + + k = 0, l = 0; + for (i = 0; i < nports; i++) + { + if (idx[i] == 'r') + { + rports[k] = ports[i]; + k++; + } + if (idx[i] == 'c') + { + 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 (nports * sizeof (mach_port_t)); + newports = __alloca (nrights * sizeof (mach_port_t)); - /* Reauthenticate all ports here. */ - for (i = 0; i < nports; i++) + /* Reauthenticate all SCM_RIGHTS ports here. */ + for (i = 0; i < nrights; i++) { - err = reauthenticate (ports[i], &newports[i]); - __mach_port_deallocate (__mach_task_self (), ports[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 +363,16 @@ 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 +385,71 @@ } } - 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))) + nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) / sizeof (int); - for (i = 0; i < nfds && j < nports; i++, j++) + 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; + 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);