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