Re: RFC: [PATCH] SCM_CREDS support
- To: Samuel Thibault <samuel.thibault@gnu.org>
- Cc: debian-hurd <debian-hurd@lists.debian.org>, bug-hurd <bug-hurd@gnu.org>
- Subject: Re: RFC: [PATCH] SCM_CREDS support
- From: Svante Signell <svante.signell@gmail.com>
- Date: Sat, 21 Feb 2015 16:09:46 +0100
- Message-id: <[🔎] 1424531386.3653.10.camel@gmail.com>
- In-reply-to: <1386285510.9777.84.camel@G3620.my.own.domain>
- References: <1382614802.21791.60.camel@G3620.my.own.domain> <20131024123435.GM5536@type.youpi.perso.aquilenet.fr> <1382621891.4928.40.camel@s1499.it.kth.se> <20131024140839.GE5532@type.bordeaux.inria.fr> <1382627098.4928.50.camel@s1499.it.kth.se> <20131024152215.GJ5532@type.bordeaux.inria.fr> <1382631259.4928.55.camel@s1499.it.kth.se> <20131024161555.GR5532@type.bordeaux.inria.fr> <1382631853.4928.59.camel@s1499.it.kth.se> <1386285510.9777.84.camel@G3620.my.own.domain>
On Fri, 2013-12-06 at 00:18 +0100, Svante Signell wrote:
> On Thu, 2013-10-24 at 18:24 +0200, Svante Signell wrote:
> > On Thu, 2013-10-24 at 18:15 +0200, Samuel Thibault wrote:
...
> New patches attached, this time using the auth_user_authenticate() and
> auth_server_authenticate() pair to get the credentials: *uid, *gid and
> groups. For PID checks, proc_getallpids() and proc_getprocinfo() are
> used, until proc_user_authenticate() proc_server_authenticate() is
> implemented (if needed?).
>
> With these patches gamin and glib2.0 work (including tests), Emacs in X,
> several Window Managers and browsers work and the test code sent in
> http://lists.gnu.org/archive/html/bug-hurd/2013-11/msg00346.html
> works. Several rights srtuctures are supported but only one credentials
> structure request is supported as implemented, but can be extended to
> more is needed (is more than one reasonable!?)
...
> Comments/bugs/issues
New patches attached (with changelog). Open issues are which error codes
to return, see the patch. Desktops tried so far and working: xfce4 and
lxde.
Most glib2.0 and dbus tests pass (after bootstrapping). And no fakeroot
passing wrong credentials (as in the current versions of hurd/glibc: a
BUG). Test code almost the same as submitted in November 2013.
Thanks!
sysdeps/mach/hurd/Changelog
2015-02-19 Svante Signell <svante.signell@gmail.com>
* 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 <errno.h>
#include <string.h>
+#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -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 <errno.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/mman.h>
+#include <unistd.h>
#include <hurd.h>
#include <hurd/fd.h>
#include <hurd/socket.h>
+#include <hurd/id.h>
/* 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);
Reply to: