Subject: Re: getpeereid() or equivalent
To: Jason Thorpe <thorpej@shagadelic.org>
From: Arne H. Juul <arnej@pvv.ntnu.no>
List: tech-net
Date: 08/06/2007 11:08:33
On Sun, 5 Aug 2007, Arne H. Juul wrote:
> Here's a patch implementing a LOCAL_PEEREID option for getsockopt().
and here's an updated version with some comments fixed, one
change to fstat (has to include sys/un.h before sys/unpcb.h)
and getpeereid() library function added.
- Arne H. J.
Index: sys/sys/un.h
===================================================================
RCS file: /usr/cvs/src/sys/sys/un.h,v
retrieving revision 1.39
diff -u -r1.39 un.h
--- sys/sys/un.h 23 Jul 2006 22:06:14 -0000 1.39
+++ sys/sys/un.h 6 Aug 2007 08:11:39 -0000
@@ -57,8 +57,18 @@
#if defined(_NETBSD_SOURCE)
#define LOCAL_CREDS 0x0001 /* pass credentials to receiver */
#define LOCAL_CONNWAIT 0x0002 /* connects block until accepted */
+#define LOCAL_PEEREID 0x0003 /* get peer identification */
#endif
+/*
+ * data automatically stored inside connect() for use by LOCAL_PEEREID
+ */
+struct unpcbid {
+ pid_t unp_pid; /* process id */
+ uid_t unp_euid; /* effective user id */
+ gid_t unp_egid; /* effective group id */
+};
+
#ifdef _KERNEL
struct unpcb;
struct socket;
Index: sys/sys/unpcb.h
===================================================================
RCS file: /usr/cvs/src/sys/sys/unpcb.h,v
retrieving revision 1.14
diff -u -r1.14 unpcb.h
--- sys/sys/unpcb.h 11 Dec 2005 12:25:21 -0000 1.14
+++ sys/sys/unpcb.h 6 Aug 2007 08:18:11 -0000
@@ -78,11 +78,25 @@
int unp_mbcnt; /* copy of rcv.sb_mbcnt */
struct timespec unp_ctime; /* holds creation time */
int unp_flags; /* misc flags; see below*/
+ struct unpcbid unp_connid; /* pid and eids of peer */
};
-/* unp_flags */
+/*
+ * Flags in unp_flags.
+ *
+ * UNP_EIDSVALID - indicates that the unp_connid member is filled in
+ * and is really the effective ids of the connected peer. This is used
+ * to determine whether the contents should be sent to the user or
+ * not.
+ *
+ * UNP_EIDSBIND - indicates that the unp_connid member is filled
+ * in with data for the listening process. This is set in unp_bind() when
+ * it fills in unp_connid for later consumption by unp_connect().
+ */
#define UNP_WANTCRED 0x0001 /* credentials wanted */
#define UNP_CONNWAIT 0x0002 /* connect blocks until accepted */
+#define UNP_EIDSVALID 0x0004 /* unp_connid contains valid data */
+#define UNP_EIDSBIND 0x0008 /* unp_connid was set by a bind */
#define sotounpcb(so) ((struct unpcb *)((so)->so_pcb))
Index: sys/kern/uipc_usrreq.c
===================================================================
RCS file: /usr/cvs/src/sys/kern/uipc_usrreq.c,v
retrieving revision 1.98
diff -u -r1.98 uipc_usrreq.c
--- sys/kern/uipc_usrreq.c 3 Aug 2007 20:49:45 -0000 1.98
+++ sys/kern/uipc_usrreq.c 5 Aug 2007 17:07:33 -0000
@@ -113,8 +113,8 @@
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
-#include <sys/unpcb.h>
#include <sys/un.h>
+#include <sys/unpcb.h>
#include <sys/namei.h>
#include <sys/vnode.h>
#include <sys/file.h>
@@ -499,17 +499,22 @@
case PRCO_GETOPT:
switch (optname) {
+ case LOCAL_PEEREID:
+ if (unp->unp_flags & UNP_EIDSVALID) {
+ *mp = m = m_get(M_WAIT, MT_SOOPTS);
+ m->m_len = sizeof(struct unpcbid);
+ *mtod(m, struct unpcbid *) = unp->unp_connid;
+ } else {
+ error = EINVAL;
+ }
+ break;
case LOCAL_CREDS:
*mp = m = m_get(M_WAIT, MT_SOOPTS);
m->m_len = sizeof(int);
- switch (optname) {
#define OPTBIT(bit) (unp->unp_flags & (bit) ? 1 : 0)
- case LOCAL_CREDS:
- optval = OPTBIT(UNP_WANTCRED);
- break;
- }
+ optval = OPTBIT(UNP_WANTCRED);
*mtod(m, int *) = optval;
break;
#undef OPTBIT
@@ -658,6 +663,10 @@
unp->unp_vnode = vp;
unp->unp_addrlen = addrlen;
unp->unp_addr = sun;
+ unp->unp_connid.unp_pid = p->p_pid;
+ unp->unp_connid.unp_euid = kauth_cred_geteuid(p->p_cred);
+ unp->unp_connid.unp_egid = kauth_cred_getegid(p->p_cred);
+ unp->unp_flags |= UNP_EIDSBIND;
VOP_UNLOCK(vp, 0);
return (0);
@@ -672,11 +681,13 @@
struct sockaddr_un *sun;
struct vnode *vp;
struct socket *so2, *so3;
- struct unpcb *unp2, *unp3;
+ struct unpcb *unp, *unp2, *unp3;
size_t addrlen;
+ struct proc *p;
int error;
struct nameidata nd;
+ p = l->l_proc;
/*
* Allocate a temporary sockaddr. We have to allocate one extra
* byte so that we can ensure that the pathname is nul-terminated.
@@ -714,6 +725,7 @@
error = ECONNREFUSED;
goto bad;
}
+ unp = sotounpcb(so);
unp2 = sotounpcb(so2);
unp3 = sotounpcb(so3);
if (unp2->unp_addr) {
@@ -724,7 +736,15 @@
unp3->unp_addrlen = unp2->unp_addrlen;
}
unp3->unp_flags = unp2->unp_flags;
+ unp3->unp_connid.unp_pid = p->p_pid;
+ unp3->unp_connid.unp_euid = kauth_cred_geteuid(p->p_cred);
+ unp3->unp_connid.unp_egid = kauth_cred_getegid(p->p_cred);
+ unp3->unp_flags |= UNP_EIDSVALID;
so2 = so3;
+ if (unp2->unp_flags & UNP_EIDSBIND) {
+ unp->unp_connid = unp2->unp_connid;
+ unp->unp_flags |= UNP_EIDSVALID;
+ }
}
error = unp_connect2(so, so2, PRU_CONNECT);
bad:
Index: usr.bin/fstat/fstat.c
===================================================================
RCS file: /usr/cvs/src/usr.bin/fstat/fstat.c,v
retrieving revision 1.75
diff -u -r1.75 fstat.c
--- usr.bin/fstat/fstat.c 11 May 2006 11:56:38 -0000 1.75
+++ usr.bin/fstat/fstat.c 5 Aug 2007 20:26:53 -0000
@@ -52,6 +52,7 @@
#include <sys/socketvar.h>
#include <sys/domain.h>
#include <sys/protosw.h>
+#include <sys/un.h>
#include <sys/unpcb.h>
#include <sys/sysctl.h>
#include <sys/filedesc.h>
Index: include/unistd.h
===================================================================
RCS file: /usr/cvs/src/include/unistd.h,v
retrieving revision 1.112
diff -u -r1.112 unistd.h
--- include/unistd.h 2 Aug 2007 21:49:10 -0000 1.112
+++ include/unistd.h 5 Aug 2007 20:33:06 -0000
@@ -312,6 +312,7 @@
int getgrouplist(const char *, gid_t, gid_t *, int *);
int getgroupmembership(const char *, gid_t, gid_t *, int, int *);
mode_t getmode(const void *, mode_t);
+int getpeereid(int, uid_t *, gid_t *);
int getsubopt(char **, char * const *, char **);
__aconst char *getusershell(void);
int initgroups(const char *, gid_t);
Index: lib/libc/net/Makefile.inc
===================================================================
RCS file: /usr/cvs/src/lib/libc/net/Makefile.inc,v
retrieving revision 1.71
diff -u -r1.71 Makefile.inc
--- lib/libc/net/Makefile.inc 26 Aug 2006 16:11:05 -0000 1.71
+++ lib/libc/net/Makefile.inc 5 Aug 2007 20:40:48 -0000
@@ -5,7 +5,7 @@
.PATH: ${ARCHDIR}/net ${.CURDIR}/net
SRCS+= __cmsg_alignbytes.c base64.c ethers.c gethnamaddr.c getifaddrs.c \
- getnetnamadr.c getnetent.c \
+ getnetnamadr.c getnetent.c getpeereid.c \
getprotobyname.c getprotobynumber.c getprotoent.c \
getprotobyname_r.c getprotobynumber_r.c getprotoent_r.c \
getservbyname.c getservbyport.c getservent.c \
@@ -42,7 +42,7 @@
.include "${ARCHDIR}/net/Makefile.inc"
MAN+= byteorder.3 ethers.3 gethostbyname.3 getifaddrs.3 \
- getnetent.3 getprotoent.3 \
+ getnetent.3 getprotoent.3 getpeereid.3 \
getservent.3 inet.3 inet_net.3 iso_addr.3 linkaddr.3 \
nsdispatch.3 rcmd.3 resolver.3 sockatmark.3
Index: lib/libc/net/getpeereid.c
===================================================================
RCS file: lib/libc/net/getpeereid.c
diff -N lib/libc/net/getpeereid.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/libc/net/getpeereid.c 5 Aug 2007 20:34:50 -0000
@@ -0,0 +1,18 @@
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+
+int
+getpeereid(int s, uid_t *euid, gid_t *egid)
+{
+ struct unpcbid cred;
+ socklen_t len = sizeof(cred);
+ if (getsockopt(s, 0, LOCAL_PEEREID, &cred, &len) < 0) {
+ return -1;
+ } else {
+ *euid = cred.unp_euid;
+ *egid = cred.unp_egid;
+ return 0;
+ }
+}
Index: lib/libc/net/getpeereid.3
===================================================================
RCS file: lib/libc/net/getpeereid.3
diff -N lib/libc/net/getpeereid.3
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ lib/libc/net/getpeereid.3 5 Aug 2007 20:43:08 -0000
@@ -0,0 +1,140 @@
+.\"
+.\" Copyright (c) 2001 Dima Dorfman.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/lib/libc/gen/getpeereid.3,v 1.6 2002/12/18 10:13:54 ru Exp $
+.\"
+.Dd July 15, 2001
+.Dt GETPEEREID 3
+.Os
+.Sh NAME
+.Nm getpeereid
+.Nd get the effective credentials of a UNIX-domain peer
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In sys/types.h
+.In unistd.h
+.Ft int
+.Fn getpeereid "int s" "uid_t *euid" "gid_t *egid"
+.Sh DESCRIPTION
+The
+.Fn getpeereid
+function returns the effective user and group IDs of the
+peer connected to a
+.Ux Ns -domain
+socket.
+The argument
+.Fa s
+must be a
+.Ux Ns -domain
+socket
+.Pq Xr unix 4
+of type
+.Dv SOCK_STREAM
+on which either
+.Xr connect 2
+has been called, or one returned from
+.Xr accept 2
+after
+.Xr bind 2
+and
+.Xr listen 2
+have been called.
+The effective used ID is placed in
+.Fa euid ,
+and the effective group ID in
+.Fa egid .
+.Pp
+The credentials returned to the
+.Xr accept 2
+caller are those of its peer at the time it called
+.Xr connect 2 ;
+the credentials returned to the
+.Xr connect 2
+caller are those of its peer at the time it called
+.Xr bind 2 .
+This mechanism is reliable; there is no way for either side to influence
+the credentials returned to its peer except by calling the appropriate
+system call (i.e., either
+.Xr connect 2
+or
+.Xr bind 2 )
+under different effective credentials.
+.Pp
+One common use of this routine is for a
+.Ux Ns -domain
+server
+to verify the credentials of its client.
+Likewise, the client can verify the credentials of the server.
+.Sh IMPLEMENTATION NOTES
+On
+.Nx ,
+.Fn getpeereid
+is implemented in terms of the
+.Dv LOCAL_PEEREID
+.Xr unix 4
+socket option.
+.Sh RETURN VALUES
+.Rv -std getpeereid
+.Sh ERRORS
+The
+.Fn getpeereid
+function
+fails if:
+.Bl -tag -width Er
+.It Bq Er EBADF
+The argument
+.Fa s
+is not a valid descriptor.
+.It Bq Er ENOTSOCK
+The argument
+.Fa s
+is a file, not a socket.
+.It Bq Er ENOTCONN
+The argument
+.Fa s
+does not refer to a socket on which
+.Xr connect 2
+have been called nor one returned from
+.Xr listen 2 .
+.It Bq Er EINVAL
+The argument
+.Fa s
+does not refer to a socket of type
+.Dv SOCK_STREAM ,
+or the kernel returned invalid data.
+.El
+.Sh SEE ALSO
+.Xr connect 2 ,
+.Xr getpeername 2 ,
+.Xr getsockname 2 ,
+.Xr getsockopt 2 ,
+.Xr listen 2 ,
+.Xr unix 4
+.Sh HISTORY
+The
+.Fn getpeereid
+function appeared in
+.Fx 4.6 .