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

Re: RFC: [PATCH] SCM_CREDS support



On Thu, 2013-10-24 at 18:24 +0200, Svante Signell wrote:
> On Thu, 2013-10-24 at 18:15 +0200, Samuel Thibault wrote:
> > Svante Signell, le Thu 24 Oct 2013 18:14:19 +0200, a écrit :
> 
> > Sure, but again, what is the relation between that and having both
> > SCM_RIGHT and SCM_CREDS in the same message?
> 
> It was a matter of constructing an if-then-else structure, the one for
> -n would check for cmsg==NULL and data=='\0', the other two not. Never
> mind, it's not relevant any longer.

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!?) 

dbus-daemon works, but some of the tests does not, since the
sendmsg():auth_user_authenticate() is called before
recvmsg():auth_server_authenticate(), causing a hang. They are
synchronous, i.e. recvmsg() must be running when sendmsg() is called,
and for these dbus tests they are called in reverse order.

Comments/bugs/issues?

Svante
---
 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 <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 @@
   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);
---
 sysdeps/mach/hurd/sendmsg.c |  140 +++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 127 insertions(+), 13 deletions(-)

--- a/sysdeps/mach/hurd/sendmsg.c
+++ b/sysdeps/mach/hurd/sendmsg.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001,2002,2004,2010 Free Software Foundation, Inc.
+/* Copyright (C) 2001,2002,2004,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
@@ -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 @@
 {
   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 @@
     }
 
   /* 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 @@
 	      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 @@
 	}
     }
 
+  /*
+    SCM_CREDS support: Create and send the credentials data together
+     with the rendezvous port ref.
+  */
+  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)
+      {
+	ref  = __mach_reply_port ();
+	err = mach_port_insert_right (mach_task_self (), ref,
+				      ref, MACH_MSG_TYPE_MAKE_SEND);
+	if (err)
+	  goto out;
+	cports[0] = ref;
+
+	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 @@
 	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 @@
 			}));
   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)
+	{
+	  //auth_t auth = getauth (); // deallocate auth if used!!
+	  mach_port_t newport;
+
+	  err = __USEPORT (AUTH, __auth_user_authenticate (port,
+							  ref, 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_deallocate (__mach_task_self (), ref);
+    __mach_port_destroy (__mach_task_self (), ref);
 
   if (dealloc)
     __vm_deallocate (__mach_task_self (), data.addr, len);

Reply to: