Dear openbsd-tech,

On GNU HURD and FreeBSD, the control message SCM_CREDS may be allocated by a
client of a Unix datagram socket. When the kernel encounters this, it fills
out
a struct cmsgcred containing PID, UID, GID, effective UID, and effective
GIDs of
the sender.

This patch implements this for OpenBSD.

Kind regards,
David


diff --git sys/kern/uipc_usrreq.c sys/kern/uipc_usrreq.c
index 8a735f3..7df3846 100644
--- sys/kern/uipc_usrreq.c
+++ sys/kern/uipc_usrreq.c
@@ -688,9 +688,10 @@ unp_externalize(struct mbuf *rights, socklen_t
controllen, int flags)
        /*
         * This code only works because SCM_RIGHTS is the only supported
         * control message type on unix sockets. Enforce this here.
+        * If it isn't, then return.
         */
        if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET)
-               return EINVAL;
+               return (0);
 
        nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) /
            sizeof(struct fdpass);
@@ -831,10 +832,26 @@ unp_internalize(struct mbuf *control, struct proc *p)
         */
        if (control->m_len < CMSG_LEN(0) || cm->cmsg_len < CMSG_LEN(0))
                return (EINVAL);
-       if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
-           !(cm->cmsg_len == control->m_len ||
+       if (!(cm->cmsg_type == SCM_RIGHTS || cm->cmsg_type == SCM_CREDS) ||
+           cm->cmsg_level != SOL_SOCKET || !(cm->cmsg_len == control->m_len
||
            control->m_len == CMSG_ALIGN(cm->cmsg_len)))
                return (EINVAL);
+
+       if(cm->cmsg_type == SCM_CREDS) {
+               struct cmsgcred * cmcred = (struct cmsgcred *)CMSG_DATA(cm);
+
+               cmcred->cmcred_pid = p->p_p->ps_pid;
+               cmcred->cmcred_uid = p->p_ucred->cr_ruid;
+               cmcred->cmcred_euid = p->p_ucred->cr_uid;
+               cmcred->cmcred_gid = p->p_ucred->cr_rgid;
+               cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
+
CMGROUP_MAX);
+               for (i = 0; i < cmcred->cmcred_ngroups; i++)
+                       cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
+
+               return (0);
+       }
+
        nfds = (cm->cmsg_len - CMSG_ALIGN(sizeof(*cm))) / sizeof (int);
 
        if (unp_rights + nfds > maxfiles / 10)
diff --git sys/sys/socket.h sys/sys/socket.h
index 927fd26..75c2319 100644
--- sys/sys/socket.h
+++ sys/sys/socket.h
@@ -486,6 +486,26 @@ struct cmsghdr {
 /* followed by u_char  cmsg_data[]; */
 };
 
+/*
+ * Only 16 groups are sent because struct mbuf is a fixed size; these must
fit.
+ */
+#define CMGROUP_MAX 16
+
+/*
+ * Credentials as attached to control messages of type SCM_CREDS.
+ * The sender must specify this control message, but the kernel
+ * will overwrite anything the sender put in the actual structure
+ * with the real values.
+ */
+struct cmsgcred {
+       pid_t   cmcred_pid;                     /* sender PID */
+       uid_t   cmcred_uid;                     /* sender real UID */
+       uid_t   cmcred_euid;            /* sender effective UID */
+       gid_t   cmcred_gid;                     /* sender real GID*/
+       short   cmcred_ngroups;         /* number of GIDs */
+       gid_t   cmcred_groups[CMGROUP_MAX];     /* GIDs */
+}
+
 /* given pointer to struct cmsghdr, return pointer to data */
 #define        CMSG_DATA(cmsg) \
        ((unsigned char *)(cmsg) + _ALIGN(sizeof(struct cmsghdr)))
@@ -520,6 +540,7 @@ struct cmsghdr {
 
 /* "Socket"-level control message types: */
 #define        SCM_RIGHTS      0x01            /* access rights (array of
int) */
+#define        SCM_CREDS       0x02            /* sender credentials
(struct cmsgcred) */
 #define        SCM_TIMESTAMP   0x04            /* timestamp (struct
timeval) */
 
 #ifndef _KERNEL

Reply via email to