[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: RFC: [PATCH] SCM_CREDS support



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: