[PATCH] hurd: SCM_CREDS support
authorGNU Libc Maintainers <debian-glibc@lists.debian.org>
Mon, 25 Aug 2025 19:11:05 +0000 (21:11 +0200)
committerAurelien Jarno <aurel32@debian.org>
Mon, 25 Aug 2025 19:11:05 +0000 (21:11 +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.

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 063e7bf6653e50b353bb3240a028d9b6414a1249..337dee0b0210bc1135aeee114d102c804be44f4a 100644 (file)
@@ -29,7 +29,7 @@ inline-headers = hurd.h $(addprefix hurd/,fd.h signal.h \
 # 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 \
index dfe5c2b36bbf780876a4df2b7a1af0a13479b845..a7387028f2cb40c46cafee593add18716d0a581f 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;
+  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 @@ __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++)
@@ -241,6 +373,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 56bcee733fbff91b3830540bcc8a2e35eb253c10..664f009574cde39f5e14c9c67a8e5e19ae3e3ccd 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"
 
@@ -113,6 +115,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));
@@ -147,6 +151,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)