diff options
-rw-r--r-- | hurd/Makefile | 2 | ||||
-rw-r--r-- | hurd/hurd/fd.h | 21 | ||||
-rw-r--r-- | hurd/lookup-at.c | 13 | ||||
-rw-r--r-- | include/unistd.h | 1 | ||||
-rw-r--r-- | sysdeps/mach/hurd/access.c | 120 | ||||
-rw-r--r-- | sysdeps/mach/hurd/euidaccess.c | 31 | ||||
-rw-r--r-- | sysdeps/mach/hurd/faccessat.c | 150 | ||||
-rw-r--r-- | sysdeps/mach/hurd/recvmsg.c | 234 | ||||
-rw-r--r-- | sysdeps/mach/hurd/sendmsg.c | 109 |
9 files changed, 495 insertions, 186 deletions
diff --git a/hurd/Makefile b/hurd/Makefile index 2e5a649d87..61ad0d11df 100644 --- a/hurd/Makefile +++ b/hurd/Makefile @@ -29,7 +29,7 @@ inline-headers = hurd.h $(addprefix hurd/,fd.h signal.h \ # The RPC interfaces go in a separate library. interface-library := libhurduser user-interfaces := $(addprefix hurd/,\ - auth startup \ + auth auth_request auth_reply startup \ process process_request \ msg msg_reply msg_request \ exec exec_startup crash interrupt \ diff --git a/hurd/hurd/fd.h b/hurd/hurd/fd.h index 54951024a4..6c67b254b0 100644 --- a/hurd/hurd/fd.h +++ b/hurd/hurd/fd.h @@ -26,6 +26,7 @@ #include <hurd/hurd_types.h> #include <hurd/port.h> #include <sys/socket.h> +#include <fcntl.h> /* Structure representing a file descriptor. */ @@ -254,6 +255,26 @@ extern int _hurd_select (int nfds, struct pollfd *pollfds, const struct timespec *timeout, const sigset_t *sigmask); +/* Apply AT_FLAGS on FLAGS, in preparation for calling + __hurd_file_name_lookup. */ + +_HURD_FD_H_EXTERN_INLINE error_t +__hurd_at_flags (int *at_flags, int *flags) +{ + if ((*at_flags & AT_SYMLINK_FOLLOW) && (*at_flags & AT_SYMLINK_NOFOLLOW)) + return EINVAL; + + *flags |= (*at_flags & AT_SYMLINK_NOFOLLOW) ? O_NOLINK : 0; + *at_flags &= ~AT_SYMLINK_NOFOLLOW; + if (*at_flags & AT_SYMLINK_FOLLOW) + *flags &= ~O_NOLINK; + *at_flags &= ~AT_SYMLINK_FOLLOW; + if (*at_flags != 0) + return EINVAL; + + return 0; +} + /* Variant of file_name_lookup used in *at function implementations. AT_FLAGS may only contain AT_SYMLINK_FOLLOW or AT_SYMLINK_NOFOLLOW, which will remove and add O_NOLINK from FLAGS respectively. diff --git a/hurd/lookup-at.c b/hurd/lookup-at.c index 3528ba8ac3..5b03a4c9d0 100644 --- a/hurd/lookup-at.c +++ b/hurd/lookup-at.c @@ -29,16 +29,9 @@ __file_name_lookup_at (int fd, int at_flags, error_t err; file_t result; - if ((at_flags & AT_SYMLINK_FOLLOW) && (at_flags & AT_SYMLINK_NOFOLLOW)) - return (__hurd_fail (EINVAL), MACH_PORT_NULL); - - flags |= (at_flags & AT_SYMLINK_NOFOLLOW) ? O_NOLINK : 0; - at_flags &= ~AT_SYMLINK_NOFOLLOW; - if (at_flags & AT_SYMLINK_FOLLOW) - flags &= ~O_NOLINK; - at_flags &= ~AT_SYMLINK_FOLLOW; - if (at_flags != 0) - return (__hurd_fail (EINVAL), MACH_PORT_NULL); + err = __hurd_at_flags (&at_flags, &flags); + if (err) + return (__hurd_fail (err), MACH_PORT_NULL); if (fd == AT_FDCWD || file_name[0] == '/') return __file_name_lookup (file_name, flags, mode); diff --git a/include/unistd.h b/include/unistd.h index 5152f64f91..d58e815263 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -26,6 +26,7 @@ libc_hidden_proto (readlinkat) /* Now define the internal interfaces. */ extern int __access (const char *__name, int __type); extern int __euidaccess (const char *__name, int __type); +extern int __faccessat (int __fd, const char *__file, int __type, int __flag); extern __off64_t __lseek64 (int __fd, __off64_t __offset, int __whence); extern __off_t __lseek (int __fd, __off_t __offset, int __whence); libc_hidden_proto (__lseek) diff --git a/sysdeps/mach/hurd/access.c b/sysdeps/mach/hurd/access.c index c308340329..dc64183f96 100644 --- a/sysdeps/mach/hurd/access.c +++ b/sysdeps/mach/hurd/access.c @@ -16,131 +16,13 @@ <http://www.gnu.org/licenses/>. */ #include <unistd.h> -#include <hurd.h> -#include <hurd/port.h> -#include <hurd/id.h> -#include <hurd/lookup.h> #include <fcntl.h> /* Test for access to FILE by our real user and group IDs. */ int __access (const char *file, int type) { - error_t err; - file_t rcrdir, rcwdir, io; - int flags, allowed; - - error_t reauthenticate (int which, file_t *result) - { - /* Get a port to our root directory, authenticated with the real IDs. */ - error_t err; - mach_port_t ref; - ref = __mach_reply_port (); - err = HURD_PORT_USE - (&_hurd_ports[which], - ({ - err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND); - if (!err) - err = __auth_user_authenticate (_hurd_id.rid_auth, - ref, MACH_MSG_TYPE_MAKE_SEND, - result); - err; - })); - __mach_port_destroy (__mach_task_self (), ref); - return err; - } - - error_t init_port (int which, error_t (*operate) (mach_port_t)) - { - switch (which) - { - case INIT_PORT_AUTH: - return (*operate) (_hurd_id.rid_auth); - case INIT_PORT_CRDIR: - return (reauthenticate (INIT_PORT_CRDIR, &rcrdir) ?: - (*operate) (rcrdir)); - case INIT_PORT_CWDIR: - return (reauthenticate (INIT_PORT_CWDIR, &rcwdir) ?: - (*operate) (rcwdir)); - default: - return _hurd_ports_use (which, operate); - } - } - - rcrdir = rcwdir = MACH_PORT_NULL; - - HURD_CRITICAL_BEGIN; - - __mutex_lock (&_hurd_id.lock); - /* Get _hurd_id up to date. */ - if (err = _hurd_check_ids ()) - goto lose; - - if (_hurd_id.rid_auth == MACH_PORT_NULL) - { - /* Set up _hurd_id.rid_auth. This is a special auth server port - which uses the real uid and gid (the first aux uid and gid) as - the only effective uid and gid. */ - - if (_hurd_id.aux.nuids < 1 || _hurd_id.aux.ngids < 1) - { - /* We do not have a real UID and GID. Lose, lose, lose! */ - err = EGRATUITOUS; - goto lose; - } - - /* Create a new auth port using our real UID and GID (the first - auxiliary UID and GID) as the only effective IDs. */ - if (err = __USEPORT (AUTH, - __auth_makeauth (port, - NULL, MACH_MSG_TYPE_COPY_SEND, 0, - _hurd_id.aux.uids, 1, - _hurd_id.aux.uids, - _hurd_id.aux.nuids, - _hurd_id.aux.gids, 1, - _hurd_id.aux.gids, - _hurd_id.aux.ngids, - &_hurd_id.rid_auth))) - goto lose; - } - - if (!err) - /* Look up the file name using the modified init ports. */ - err = __hurd_file_name_lookup (&init_port, &__getdport, 0, - file, 0, 0, &io); - - /* We are done with _hurd_id.rid_auth now. */ - lose: - __mutex_unlock (&_hurd_id.lock); - - HURD_CRITICAL_END; - - if (rcrdir != MACH_PORT_NULL) - __mach_port_deallocate (__mach_task_self (), rcrdir); - if (rcwdir != MACH_PORT_NULL) - __mach_port_deallocate (__mach_task_self (), rcwdir); - if (err) - return __hurd_fail (err); - - /* Find out what types of access we are allowed to this file. */ - err = __file_check_access (io, &allowed); - __mach_port_deallocate (__mach_task_self (), io); - if (err) - return __hurd_fail (err); - - flags = 0; - if (type & R_OK) - flags |= O_READ; - if (type & W_OK) - flags |= O_WRITE; - if (type & X_OK) - flags |= O_EXEC; - - if (flags & ~allowed) - /* We are not allowed all the requested types of access. */ - return __hurd_fail (EACCES); - - return 0; + return __faccessat (AT_FDCWD, file, type, 0); } weak_alias (__access, access) diff --git a/sysdeps/mach/hurd/euidaccess.c b/sysdeps/mach/hurd/euidaccess.c index ee512ff5e3..4949aa9365 100644 --- a/sysdeps/mach/hurd/euidaccess.c +++ b/sysdeps/mach/hurd/euidaccess.c @@ -16,42 +16,13 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ -#include <errno.h> -#include <stddef.h> #include <unistd.h> #include <fcntl.h> -#include <hurd.h> int __euidaccess (const char *file, int type) { - error_t err; - file_t port; - int allowed, flags; - - port = __file_name_lookup (file, 0, 0); - if (port == MACH_PORT_NULL) - return -1; - - /* Find out what types of access we are allowed to this file. */ - err = __file_check_access (port, &allowed); - __mach_port_deallocate (__mach_task_self (), port); - if (err) - return __hurd_fail (err); - - flags = 0; - if (type & R_OK) - flags |= O_READ; - if (type & W_OK) - flags |= O_WRITE; - if (type & X_OK) - flags |= O_EXEC; - - if (flags & ~allowed) - /* We are not allowed all the requested types of access. */ - return __hurd_fail (EACCES); - - return 0; + return __faccessat (AT_FDCWD, file, type, AT_EACCESS); } weak_alias (__euidaccess, euidaccess) weak_alias (__euidaccess, eaccess) diff --git a/sysdeps/mach/hurd/faccessat.c b/sysdeps/mach/hurd/faccessat.c index 0a57ab627c..d88fa4ae57 100644 --- a/sysdeps/mach/hurd/faccessat.c +++ b/sysdeps/mach/hurd/faccessat.c @@ -23,29 +23,152 @@ #include <sys/types.h> #include <hurd.h> #include <hurd/fd.h> +#include <hurd/port.h> +#include <hurd/id.h> +#include <hurd/lookup.h> int -faccessat (int fd, const char *file, int type, int flag) +__faccessat (int fd, const char *file, int type, int at_flags) { error_t err; - file_t port; - int allowed, flags; + file_t rcrdir, rcwdir, io; + int flags, allowed; - if ((flag & AT_EACCESS) == 0) + if ((at_flags & AT_EACCESS) == AT_EACCESS) { - if (fd == AT_FDCWD || file[0] == '/') - return __access (file, type); - __set_errno (ENOTSUP); /* XXX later */ - return -1; + /* Use effective permissions. */ + io = __file_name_lookup_at (fd, at_flags &~ AT_EACCESS, file, 0, 0); + if (io == MACH_PORT_NULL) + return -1; } + else + { + /* We have to use real permissions instead of the + usual effective permissions. */ + + int hurd_flags = 0; + __hurd_at_flags (&at_flags, &hurd_flags); + + error_t reauthenticate_cwdir_at (file_t *result) + { + /* Get a port to the FD directory, authenticated with the real IDs. */ + error_t err; + mach_port_t ref; + ref = __mach_reply_port (); + err = HURD_DPORT_USE + (fd, + ({ + err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND); + if (!err) + err = __auth_user_authenticate (_hurd_id.rid_auth, + ref, MACH_MSG_TYPE_MAKE_SEND, + result); + err; + })); + __mach_port_destroy (__mach_task_self (), ref); + return err; + } + + error_t reauthenticate (int which, file_t *result) + { + /* Get a port to our root directory, authenticated with the real IDs. */ + error_t err; + mach_port_t ref; + ref = __mach_reply_port (); + err = HURD_PORT_USE + (&_hurd_ports[which], + ({ + err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND); + if (!err) + err = __auth_user_authenticate (_hurd_id.rid_auth, + ref, MACH_MSG_TYPE_MAKE_SEND, + result); + err; + })); + __mach_port_destroy (__mach_task_self (), ref); + return err; + } + + error_t init_port (int which, error_t (*operate) (mach_port_t)) + { + switch (which) + { + case INIT_PORT_AUTH: + return (*operate) (_hurd_id.rid_auth); + case INIT_PORT_CRDIR: + return (reauthenticate (INIT_PORT_CRDIR, &rcrdir) ?: + (*operate) (rcrdir)); + case INIT_PORT_CWDIR: + if (fd == AT_FDCWD || file[0] == '/') + return (reauthenticate (INIT_PORT_CWDIR, &rcwdir) ?: + (*operate) (rcwdir)); + else + return (reauthenticate_cwdir_at (&rcwdir) ?: + (*operate) (rcwdir)); + default: + return _hurd_ports_use (which, operate); + } + } + + rcrdir = rcwdir = MACH_PORT_NULL; + + HURD_CRITICAL_BEGIN; + + __mutex_lock (&_hurd_id.lock); + /* Get _hurd_id up to date. */ + if (err = _hurd_check_ids ()) + goto lose; - port = __file_name_lookup_at (fd, flag &~ AT_EACCESS, file, 0, 0); - if (port == MACH_PORT_NULL) - return -1; + if (_hurd_id.rid_auth == MACH_PORT_NULL) + { + /* Set up _hurd_id.rid_auth. This is a special auth server port + which uses the real uid and gid (the first aux uid and gid) as + the only effective uid and gid. */ + + if (_hurd_id.aux.nuids < 1 || _hurd_id.aux.ngids < 1) + { + /* We do not have a real UID and GID. Lose, lose, lose! */ + err = EGRATUITOUS; + goto lose; + } + + /* Create a new auth port using our real UID and GID (the first + auxiliary UID and GID) as the only effective IDs. */ + if (err = __USEPORT (AUTH, + __auth_makeauth (port, + NULL, MACH_MSG_TYPE_COPY_SEND, 0, + _hurd_id.aux.uids, 1, + _hurd_id.aux.uids, + _hurd_id.aux.nuids, + _hurd_id.aux.gids, 1, + _hurd_id.aux.gids, + _hurd_id.aux.ngids, + &_hurd_id.rid_auth))) + goto lose; + } + + if (!err) + /* Look up the file name using the modified init ports. */ + err = __hurd_file_name_lookup (&init_port, &__getdport, 0, + file, hurd_flags, 0, &io); + + /* We are done with _hurd_id.rid_auth now. */ + lose: + __mutex_unlock (&_hurd_id.lock); + + HURD_CRITICAL_END; + + if (rcrdir != MACH_PORT_NULL) + __mach_port_deallocate (__mach_task_self (), rcrdir); + if (rcwdir != MACH_PORT_NULL) + __mach_port_deallocate (__mach_task_self (), rcwdir); + if (err) + return __hurd_fail (err); + } /* Find out what types of access we are allowed to this file. */ - err = __file_check_access (port, &allowed); - __mach_port_deallocate (__mach_task_self (), port); + err = __file_check_access (io, &allowed); + __mach_port_deallocate (__mach_task_self (), io); if (err) return __hurd_fail (err); @@ -63,3 +186,4 @@ faccessat (int fd, const char *file, int type, int flag) return 0; } +weak_alias (__faccessat, faccessat) diff --git a/sysdeps/mach/hurd/recvmsg.c b/sysdeps/mach/hurd/recvmsg.c index 770a42e05e..a174794913 100644 --- a/sysdeps/mach/hurd/recvmsg.c +++ b/sysdeps/mach/hurd/recvmsg.c @@ -23,6 +23,123 @@ #include <hurd/fd.h> #include <hurd/socket.h> +static unsigned +contains_uid (unsigned int n, __uid_t uids[n], __uid_t uid) +{ + unsigned i; + + for (i = 0; i < n; i++) + if (uids[i] == uid) + return 1; + return 0; +} + +static unsigned +contains_gid (unsigned int n, __gid_t gids[n], __gid_t gid) +{ + unsigned i; + + for (i = 0; i < n; i++) + if (gids[i] == gid) + return 1; + return 0; +} + +/* Check the passed credentials. */ +static error_t +check_auth (mach_port_t rendezvous, + __pid_t pid, + __uid_t uid, __uid_t euid, + __gid_t gid, + int ngroups, __gid_t groups[ngroups]) +{ + error_t err; + size_t neuids = CMGROUP_MAX, nauids = CMGROUP_MAX; + size_t negids = CMGROUP_MAX, nagids = CMGROUP_MAX; + __uid_t euids_buf[neuids], auids_buf[nauids]; + __gid_t egids_buf[negids], agids_buf[nagids]; + __uid_t *euids = euids_buf, *auids = auids_buf; + __gid_t *egids = egids_buf, *agids = agids_buf; + + struct procinfo *pi = NULL; + mach_msg_type_number_t pi_size = 0; + int flags = PI_FETCH_TASKINFO; + char *tw = NULL; + size_t tw_size = 0; + unsigned i; + + err = mach_port_mod_refs (mach_task_self (), rendezvous, + MACH_PORT_RIGHT_SEND, 1); + if (err) + goto out; + + do + err = __USEPORT + (AUTH, __auth_server_authenticate (port, + rendezvous, MACH_MSG_TYPE_COPY_SEND, + MACH_PORT_NULL, 0, + &euids, &neuids, &auids, &nauids, + &egids, &negids, &agids, &nagids)); + while (err == EINTR); + if (err) + goto out; + + /* Check whether this process indeed has these IDs */ + if ( !contains_uid (neuids, euids, uid) + && !contains_uid (nauids, auids, uid) + || !contains_uid (neuids, euids, euid) + && !contains_uid (nauids, auids, euid) + || !contains_gid (negids, egids, gid) + && !contains_gid (nagids, agids, gid) + ) + { + err = EIO; + goto out; + } + + /* Check groups */ + for (i = 0; i < ngroups; i++) + if ( !contains_gid (negids, egids, groups[i]) + && !contains_gid (nagids, agids, groups[i])) + { + err = EIO; + goto out; + } + + /* Check PID */ + /* XXX: Using proc_getprocinfo until + proc_user_authenticate proc_server_authenticate is implemented + */ + /* Get procinfo to check the owner. Maybe he faked the pid, but at least we + check the owner. */ + err = __USEPORT (PROC, __proc_getprocinfo (port, pid, &flags, + (procinfo_t *)&pi, + &pi_size, &tw, &tw_size)); + if (err) + goto out; + + if ( !contains_uid (neuids, euids, pi->owner) + && !contains_uid (nauids, auids, pi->owner)) + err = EIO; + +out: + mach_port_deallocate (mach_task_self (), rendezvous); + if (euids != euids_buf) + __vm_deallocate (__mach_task_self(), (vm_address_t) euids, neuids * sizeof(uid_t)); + if (auids != auids_buf) + __vm_deallocate (__mach_task_self(), (vm_address_t) auids, nauids * sizeof(uid_t)); + if (egids != egids_buf) + __vm_deallocate (__mach_task_self(), (vm_address_t) egids, negids * sizeof(uid_t)); + if (agids != agids_buf) + __vm_deallocate (__mach_task_self(), (vm_address_t) agids, nagids * sizeof(uid_t)); + if (tw_size) + __vm_deallocate (__mach_task_self(), (vm_address_t) tw, tw_size); + if (pi_size) + __vm_deallocate (__mach_task_self(), (vm_address_t) pi, pi_size); + + return err; +} + /* Receive a message as described by MESSAGE from socket FD. Returns the number of bytes read or -1 for errors. */ ssize_t @@ -32,13 +149,34 @@ __libc_recvmsg (int fd, struct msghdr *message, int flags) addr_port_t aport; char *data = NULL; mach_msg_type_number_t len = 0; - mach_port_t *ports; + mach_port_t *ports, *newports = NULL; mach_msg_type_number_t nports = 0; + struct cmsghdr *cmsg; char *cdata = NULL; mach_msg_type_number_t clen = 0; size_t amount; char *buf; - int i; + int nfds, *opened_fds = NULL; + int i, ii, j; + int newfds; + + error_t reauthenticate (mach_port_t port, mach_port_t *result) + { + error_t err; + mach_port_t ref; + ref = __mach_reply_port (); + do + err = __io_reauthenticate (port, ref, MACH_MSG_TYPE_MAKE_SEND); + while (err == EINTR); + if (!err) + do + err = __USEPORT (AUTH, __auth_user_authenticate (port, + ref, MACH_MSG_TYPE_MAKE_SEND, + result)); + while (err == EINTR); + __mach_port_destroy (__mach_task_self (), ref); + return err; + } /* Find the total number of bytes to be read. */ amount = 0; @@ -135,9 +273,101 @@ __libc_recvmsg (int fd, struct msghdr *message, int flags) message->msg_controllen = clen; memcpy (message->msg_control, cdata, message->msg_controllen); + if (nports > 0) + { + newports = __alloca (nports * sizeof (mach_port_t)); + opened_fds = __alloca (nports * sizeof (int)); + } + + /* This counts how many ports we processed completely. */ + i = 0; + /* This counts how many new fds we create. */ + newfds = 0; + + for (cmsg = CMSG_FIRSTHDR (message); + cmsg; + cmsg = CMSG_NXTHDR (message, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + { + /* SCM_RIGHTS support. */ + /* The fd's flags are passed in the control data. */ + int *fds = (int *) CMSG_DATA (cmsg); + nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) + / sizeof (int); + + for (j = 0; j < nfds; j++) + { + err = reauthenticate (ports[i], &newports[newfds]); + if (err) + goto cleanup; + fds[j] = opened_fds[newfds] = _hurd_intern_fd (newports[newfds], + fds[j], 0); + if (fds[j] == -1) + { + err = errno; + __mach_port_deallocate (__mach_task_self (), newports[newfds]); + goto cleanup; + } + i++; + newfds++; + } + } + else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + { + /* SCM_CREDS support. */ + /* Check received credentials */ + struct cmsgcred *ucredp = (struct cmsgcred *) CMSG_DATA(cmsg); + + err = check_auth (ports[i], + ucredp->cmcred_pid, + ucredp->cmcred_uid, ucredp->cmcred_euid, + ucredp->cmcred_gid, + ucredp->cmcred_ngroups, ucredp->cmcred_groups); + if (err) + goto cleanup; + i++; + } + } + + for (i = 0; i < nports; i++) + __mach_port_deallocate (mach_task_self (), ports[i]); + __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen); return (buf - data); + +cleanup: + /* Clean up all the file descriptors from port 0 to i-1. */ + if (nports > 0) + { + ii = 0; + newfds = 0; + for (cmsg = CMSG_FIRSTHDR (message); + cmsg; + cmsg = CMSG_NXTHDR (message, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + { + nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) + / sizeof (int); + for (j = 0; j < nfds && ii < i; j++, ii++, newfds++) + { + _hurd_fd_close (_hurd_fd_get (opened_fds[newfds])); + __mach_port_deallocate (__mach_task_self (), newports[newfds]); + __mach_port_deallocate (__mach_task_self (), ports[ii]); + } + } + else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + { + __mach_port_deallocate (__mach_task_self (), ports[ii]); + ii++; + } + } + } + + __vm_deallocate (__mach_task_self (), (vm_address_t) cdata, clen); + return __hurd_fail (err); } weak_alias (__libc_recvmsg, recvmsg) diff --git a/sysdeps/mach/hurd/sendmsg.c b/sysdeps/mach/hurd/sendmsg.c index ce40764992..20db6048f2 100644 --- a/sysdeps/mach/hurd/sendmsg.c +++ b/sysdeps/mach/hurd/sendmsg.c @@ -19,11 +19,13 @@ #include <string.h> #include <sys/socket.h> #include <sys/un.h> +#include <unistd.h> #include <hurd.h> #include <hurd/fd.h> #include <hurd/ifsock.h> #include <hurd/socket.h> +#include <hurd/auth_request.h> #include "hurd/hurdsocket.h" /* Send a message described MESSAGE on socket FD. @@ -32,6 +34,10 @@ ssize_t __libc_sendmsg (int fd, const struct msghdr *message, int flags) { error_t err = 0; + struct cmsghdr *cmsg; + mach_port_t *ports = NULL; + mach_msg_type_number_t nports = 0; + int *fds, nfds; struct sockaddr_un *addr = message->msg_name; socklen_t addr_len = message->msg_namelen; addr_port_t aport = MACH_PORT_NULL; @@ -44,6 +50,7 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags) mach_msg_type_number_t len; mach_msg_type_number_t amount; int dealloc = 0; + int socketrpc = 0; int i; /* Find the total number of bytes to be written. */ @@ -101,6 +108,82 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags) } } + /* Allocate enough room for ports. */ + cmsg = CMSG_FIRSTHDR (message); + for (; cmsg; 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))) + / sizeof (int); + else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + nports++; + + if (nports) + ports = __alloca (nports * sizeof (mach_port_t)); + + nports = 0; + for (cmsg = CMSG_FIRSTHDR (message); + cmsg; + cmsg = CMSG_NXTHDR (message, cmsg)) + { + if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) + { + /* SCM_RIGHTS support: send FDs. */ + fds = (int *) CMSG_DATA (cmsg); + nfds = (cmsg->cmsg_len - CMSG_ALIGN (sizeof (struct cmsghdr))) + / sizeof (int); + + for (i = 0; i < nfds; i++) + { + err = HURD_DPORT_USE + (fds[i], + ({ + err = __io_restrict_auth (port, &ports[nports], + 0, 0, 0, 0); + if (! err) + nports++; + /* We pass the flags in the control data. */ + fds[i] = descriptor->flags; + err; + })); + + if (err) + goto out; + } + } + else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) + { + /* SCM_CREDS support: send credentials. */ + mach_port_t rendezvous = __mach_reply_port (), reply; + struct cmsgcred *ucredp; + + err = mach_port_insert_right (mach_task_self (), rendezvous, + rendezvous, MACH_MSG_TYPE_MAKE_SEND); + ports[nports++] = rendezvous; + if (err) + goto out; + + 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(); + ucredp->cmcred_ngroups = + __getgroups (sizeof (ucredp->cmcred_groups) / sizeof (gid_t), + ucredp->cmcred_groups); + + /* And make auth server authenticate us. */ + reply = __mach_reply_port(); + err = __USEPORT + (AUTH, __auth_user_authenticate_request (port, + reply, MACH_MSG_TYPE_MAKE_SEND_ONCE, + rendezvous, MACH_MSG_TYPE_MAKE_SEND)); + mach_port_deallocate (__mach_task_self (), reply); + if (err) + goto out; + } + } + if (addr) { if (addr->sun_family == AF_LOCAL) @@ -111,9 +194,8 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags) file_t file = __file_name_lookup (name, 0, 0); if (file == MACH_PORT_NULL) { - if (dealloc) - __vm_deallocate (__mach_task_self (), data.addr, len); - return -1; + err = errno; + goto out; } err = __ifsock_getsockaddr (file, &aport); __mach_port_deallocate (__mach_task_self (), file); @@ -121,11 +203,7 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags) /* The file did not grok the ifsock protocol. */ err = ENOTSOCK; if (err) - { - if (dealloc) - __vm_deallocate (__mach_task_self (), data.addr, len); - return __hurd_fail (err); - } + goto out; } else err = EIEIO; @@ -144,8 +222,9 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags) /* Send the data. */ err = __socket_send (port, aport, flags, data.ptr, len, - NULL, - MACH_MSG_TYPE_COPY_SEND, 0, + ports, + MACH_MSG_TYPE_COPY_SEND, + nports, message->msg_control, message->msg_controllen, &amount); @@ -154,11 +233,19 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags) } err; })); + socketrpc = 1; + + out: + for (i = 0; i < nports; i++) + __mach_port_deallocate (__mach_task_self (), ports[i]); if (dealloc) __vm_deallocate (__mach_task_self (), data.addr, len); - return err ? __hurd_sockfail (fd, flags, err) : amount; + if (socketrpc) + return err ? __hurd_sockfail (fd, flags, err) : amount; + else + return __hurd_fail (err); } weak_alias (__libc_sendmsg, sendmsg) |