Subject: [bug#50698] [PATCH] WIP patches for recently-known hurd security vulnerabilities

From: Maxime Devos <[email-masked]>
Date: Mon, 20 Sep 2021 12:40:48 +0200

Hi,

I've tried to patch the glibc package for the problems noted at
<https://lists.gnu.org/archive/html/bug-hurd/2021-08/msg00007.html>;.

I've found two recent patches (glibc-hurd-proc-reauth.patch and
glibc-hurd-sendmsg-SCM_CREDS.patch) that appeared relevant.  I tried
to patch our glibc package with those patches.

The modified tarball builds fine for --system=x86_64-linux, but not
for --system=i586-gnu (tested with ./pre-inst-env guix build hello
--system=i586-gnu).  Any idea what's happening here?

Greetings,
Maxime.
From cdf38fbfcba4c87777d7ba2175f08e877dafe86a Mon Sep 17 00:00:00 2001
From: Maxime Devos <[email-masked]>
Date: Mon, 13 Sep 2021 11:23:21 +0200
Subject: [PATCH] WIP gnu: glibc: New security patches.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The existence of the vulnerabilities was noted at
<https://lists.gnu.org/archive/html/bug-hurd/2021-08/msg00007.html>.

TODO: check if these are all necessary packages for glibc.
TODO: why does the glibc tarball build for --system=x86_64-linux but not
for --system=i586-gnu?

Build error:
‘patching file hurd/hurdinit.c
Hunk #1 FAILED at 177.
1 out of 1 hunk FAILED -- saving rejects to file hurd/hurdinit.c.rej’

but this file isn't modified by the new patches!

* gnu/local.mk (dist_patch_DATA): Register new patches.
* gnu/packages/base.scm (glibc)[replacement]: Register replacement.
  (glibc/fixed): New variable.
* gnu/packages/patches/glibc-hurd-proc-reauth.patch: New file.
* gnu/packages/patches/glibc-hurd-sendmsg-SCM_CREDS.patch.
---
 gnu/local.mk                                  |   2 +
 gnu/packages/base.scm                         |   7 +
 .../patches/glibc-hurd-proc-reauth.patch      | 114 ++++++++
 .../glibc-hurd-sendmsg-SCM_CREDS.patch        | 261 ++++++++++++++++++
 4 files changed, 384 insertions(+)
 create mode 100644 gnu/packages/patches/glibc-hurd-proc-reauth.patch
 create mode 100644 gnu/packages/patches/glibc-hurd-sendmsg-SCM_CREDS.patch

diff --git a/gnu/local.mk b/gnu/local.mk
index a7255e8df7..abad3ad777 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -1150,9 +1150,11 @@ dist_patch_DATA =						\
   %D%/packages/patches/glibc-hurd-clock_gettime_monotonic.patch	\
   %D%/packages/patches/glibc-hurd-clock_t_centiseconds.patch	\
   %D%/packages/patches/glibc-hurd-gettyent.patch		\
+  %D%/packages/patches/glibc-hurd-proc-reauth.patch		\
   %D%/packages/patches/glibc-hurd-mach-print.patch		\
   %D%/packages/patches/glibc-hurd-magic-pid.patch		\
   %D%/packages/patches/glibc-hurd-signal-sa-siginfo.patch	\
+  %D%/packages/patches/glibc-hurd-sendmsg-SCM_CREDS.patch	\
   %D%/packages/patches/glibc-ldd-powerpc.patch			\
   %D%/packages/patches/glibc-ldd-x86_64.patch			\
   %D%/packages/patches/glibc-locales.patch			\
diff --git a/gnu/packages/base.scm b/gnu/packages/base.scm
index ea2e102c15..ab6f13ec88 100644
--- a/gnu/packages/base.scm
+++ b/gnu/packages/base.scm
@@ -706,6 +706,7 @@ the store.")
   (package
    (name "glibc")
    (version "2.31")
+   (replacement glibc/fixed)
    (source (origin
             (method url-fetch)
             (uri (string-append "mirror://gnu/glibc/glibc-" version ".tar.xz"))
@@ -966,6 +967,12 @@ with the Linux kernel.")
    (license lgpl2.0+)
    (home-page "https://www.gnu.org/software/libc/")))
 
+(define glibc/fixed
+  (package-with-extra-patches
+   glibc
+   (search-patches "glibc-hurd-sendmsg-SCM_CREDS.patch"
+                   "glibc-hurd-proc-reauth.patch")))
+
 ;; Below are old libc versions, which we use mostly to build locale data in
 ;; the old format (which the new libc cannot cope with.)
 
diff --git a/gnu/packages/patches/glibc-hurd-proc-reauth.patch b/gnu/packages/patches/glibc-hurd-proc-reauth.patch
new file mode 100644
index 0000000000..fa3b0f1403
--- /dev/null
+++ b/gnu/packages/patches/glibc-hurd-proc-reauth.patch
@@ -0,0 +1,114 @@
+Index: glibc-2.31/hurd/hurdsig.c
+===================================================================
+--- glibc-2.31.orig/hurd/hurdsig.c
++++ glibc-2.31/hurd/hurdsig.c
+@@ -1580,28 +1580,53 @@ _hurdsig_init (const int *intarray, size
+ static void
+ reauth_proc (mach_port_t new)
+ {
+-  mach_port_t ref, ignore;
++  error_t err;
++  mach_port_t ref, newproc;
+ 
+   ref = __mach_reply_port ();
+-  if (! HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
++  err = HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
+ 		       __proc_reauthenticate (port, ref,
+-					      MACH_MSG_TYPE_MAKE_SEND)
+-		       || __auth_user_authenticate (new, ref,
+-						    MACH_MSG_TYPE_MAKE_SEND,
+-						    &ignore))
+-      && ignore != MACH_PORT_NULL)
+-    __mach_port_deallocate (__mach_task_self (), ignore);
++					      MACH_MSG_TYPE_MAKE_SEND));
++  if (err)
++    {
++      __mach_port_destroy (__mach_task_self (), ref);
++      return;
++    }
++
++  err = __auth_user_authenticate (new, ref,
++                                  MACH_MSG_TYPE_MAKE_SEND,
++                                  &newproc);
+   __mach_port_destroy (__mach_task_self (), ref);
++  if (err)
++    return;
++
++  if (newproc == MACH_PORT_NULL)
++    {
++      /* Old versions of the proc server did not recreate the process
++         port when reauthenticating, and passed MACH_PORT_NULL through
++         the auth server.  That must be what we're dealing with.  */
++
++      /* Set the owner of the process here too. */
++      __mutex_lock (&_hurd_id.lock);
++      if (!_hurd_check_ids ())
++	HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
++		       __proc_setowner (port,
++					(_hurd_id.gen.nuids
++					 ? _hurd_id.gen.uids[0] : 0),
++					!_hurd_id.gen.nuids));
++      __mutex_unlock (&_hurd_id.lock);
++
++      return;
++    }
++
++  err = __proc_reauthenticate_complete (newproc);
++  if (err)
++    {
++      __mach_port_deallocate (__mach_task_self (), newproc);
++      return;
++    }
+ 
+-  /* Set the owner of the process here too. */
+-  __mutex_lock (&_hurd_id.lock);
+-  if (!_hurd_check_ids ())
+-    HURD_PORT_USE (&_hurd_ports[INIT_PORT_PROC],
+-		   __proc_setowner (port,
+-				    (_hurd_id.gen.nuids
+-				     ? _hurd_id.gen.uids[0] : 0),
+-				    !_hurd_id.gen.nuids));
+-  __mutex_unlock (&_hurd_id.lock);
++  _hurd_port_set (&_hurd_ports[INIT_PORT_PROC], newproc);
+ 
+   (void) &reauth_proc;		/* Silence compiler warning.  */
+ }
+Index: glibc-2.31/sysdeps/mach/hurd/spawni.c
+===================================================================
+--- glibc-2.31.orig/sysdeps/mach/hurd/spawni.c
++++ glibc-2.31/sysdeps/mach/hurd/spawni.c
+@@ -651,11 +651,29 @@ retry:
+ 					ref, MACH_MSG_TYPE_MAKE_SEND,
+ 					&newproc);
+       __mach_port_destroy (__mach_task_self (), ref);
+-      if (!err)
+-	{
+-	  __mach_port_deallocate (__mach_task_self (), proc);
+-	  proc = newproc;
+-	}
++      if (err)
++        goto out;
++      if (newproc == MACH_PORT_NULL)
++        {
++          /* Old versions of the proc server did not recreate the process
++             port when reauthenticating, and passed MACH_PORT_NULL through
++             the auth server.  That must be what we're dealing with.  Just
++             keep the existing proc port in this case.  */
++        }
++      else
++        {
++          err = __proc_reauthenticate_complete (newproc);
++          if (err)
++            {
++              __mach_port_deallocate (__mach_task_self (), newproc);
++              goto out;
++            }
++          else
++	    {
++	      __mach_port_deallocate (__mach_task_self (), proc);
++	      proc = newproc;
++	    }
++        }
+ 
+       if (!err)
+ 	err = reauthenticate (INIT_PORT_CRDIR, &rcrdir);
diff --git a/gnu/packages/patches/glibc-hurd-sendmsg-SCM_CREDS.patch b/gnu/packages/patches/glibc-hurd-sendmsg-SCM_CREDS.patch
new file mode 100644
index 0000000000..67de2e1829
--- /dev/null
+++ b/gnu/packages/patches/glibc-hurd-sendmsg-SCM_CREDS.patch
@@ -0,0 +1,261 @@
+Subject: [PATCH] hurd: SCM_CREDS support
+
+Adjusted for use in Guix by removing #include <sysdep-cancel.h>.
+
+
+Svante Signell  <[email-masked]>
+Samuel Thibault  <[email-masked]>
+
+	* sysdeps/mach/hurd/sendmsg.c (__libc_sendmsg): On SCM_CREDS
+	control messages, record uids, pass a rendez-vous port in the
+	control message, and call __auth_user_authenticate_request to
+	make auth send credentials on that port.  Do not wait for a
+	reply.
+	* sysdeps/mach/hurd/recvmsg.c (contains_uid, contains_gid,
+	check_auth): New functions.
+	(__libc_recvmsg): On SCM_CREDS control messages, call check_auth
+	to check the passed credentials thanks to the answer from the
+	auth server.
+	* hurd/Makefile (user-interfaces): Add auth_request and
+	auth_reply.
+
+---
+ hurd/Makefile               |    2 
+ sysdeps/mach/hurd/recvmsg.c |  137 ++++++++++++++++++++++++++++++++++++++++++++
+ sysdeps/mach/hurd/sendmsg.c |   36 +++++++++++
+ 3 files changed, 174 insertions(+), 1 deletion(-)
+
+--- a/sysdeps/mach/hurd/recvmsg.c
++++ b/sysdeps/mach/hurd/recvmsg.c
+@@ -24,6 +24,123 @@
+ #include <hurd/socket.h>
+ #include <sysdep-cancel.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
+@@ -211,6 +328,21 @@
+ 	    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++)
+@@ -241,6 +373,11 @@
+ 		__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++;
++	    }
+ 	}
+     }
+ 
+--- a/sysdeps/mach/hurd/sendmsg.c
++++ b/sysdeps/mach/hurd/sendmsg.c
+@@ -19,10 +19,12 @@
+ #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"
+ 
+@@ -113,6 +115,8 @@
+     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));
+@@ -147,6 +151,38 @@
+ 		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)
+--- a/hurd/Makefile
++++ b/hurd/Makefile
+@@ -29,7 +29,7 @@
+ # 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 \
-- 
2.33.0