[PATCH] hurd: SCM_CREDS support
authorGNU Libc Maintainers <debian-glibc@lists.debian.org>
Wed, 18 Jun 2025 04:49:31 +0000 (06:49 +0200)
committerAurelien Jarno <aurel32@debian.org>
Wed, 18 Jun 2025 04:49:31 +0000 (06:49 +0200)
Svante Signell  <svante.signell@gmail.com>
Samuel Thibault  <samuel.thibault@ens-lyon.org>

* 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.

FIXME: this doesn't actually check uid/gid.

Gbp-Pq: Topic hurd-i386
Gbp-Pq: Name tg-sendmsg-SCM_CREDS.diff

hurd/Makefile
sysdeps/mach/hurd/recvmsg.c
sysdeps/mach/hurd/sendmsg.c

index a4e229d4f90637ac895ad1cb88ce347ffe31afba..a81e215a4c5ef2b83fe1042b4b72b7fa906f5b98 100644 (file)
@@ -50,6 +50,8 @@ inline-headers = \
 interface-library := libhurduser
 user-interfaces := \
   hurd/auth \
+  hurd/auth_reply \
+  hurd/auth_request \
   hurd/crash \
   hurd/exec \
   hurd/exec_startup \
index b77e0c8c34c1a29f14d24689cfd0cee5c5d5822b..a91607ff5a7afd6dc68c20b3f4c41f54e5bf6f68 100644 (file)
 #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;
+  mach_msg_type_number_t neuids = CMGROUP_MAX, nauids = CMGROUP_MAX;
+  mach_msg_type_number_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;
+  mach_msg_type_number_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
@@ -216,6 +333,21 @@ __libc_recvmsg (int fd, struct msghdr *message, int flags)
            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++)
@@ -246,6 +378,11 @@ cleanup:
                __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++;
+           }
        }
     }
 
index 4aca09fa13b4f49e66d3c773adfd96d4fc96a950..557d618509a23d95063c7b2ff877aca92e6245f5 100644 (file)
 #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 <sysdep-cancel.h>
 #include "hurd/hurdsocket.h"
 
@@ -110,6 +112,8 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags)
     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));
@@ -144,6 +148,38 @@ __libc_sendmsg (int fd, const struct msghdr *message, int flags)
                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)