Date: Tue, 31 Jul 2001 04:40:45 -0700 From: Dima Dorfman <dima@unixfreak.org> To: audit@freebsd.org Cc: dwmalone@freebsd.org Subject: Peer credentials on a Unix domain socket Message-ID: <20010731114050.B730E3E31@bazooka.unixfreak.org>
next in thread | raw e-mail | index | archive | help
As discussed on -arch...
Attached are two things: one is a patch that implements the socket
option and documents it in unix(4). The second is a sharball of the
getpeereid() implementation and man page. Here's an excerpt from
unix(4) that pretty much sums up what's there:
The effective credentials (i.e., the user ID and group list) the of a
peer on a SOCK_STREAM socket may be obtained using the LOCAL_PEERCRED
socket option. This may be used by a server to obtain and verify the
credentials of its client, and vice versa by the client to verify the
credentials of the server. These will arrive in the form of a filled in
struct xucred (defined in sys/ucred.h). The credentials presented to the
server (the listen(2) caller) are those of the client when it called
connect(2); the credentials presented to the client (the connect(2)
caller) are those of the server when it called listen(2). This mechanism
is reliable; there is no way for either party to influence the creden-
tials presented to its peer except by calling the appropriate system call
(e.g., connect(2) or listen(2)) under different effective credentials.
The above, with s/LOCAL_PEERCRED/SO_PEERCRED/ also describes that
Linux does. They obviously don't use struct xucred, but the idea is
the same. I.e., they also record the credentials at connect(2) and
listen(2) time.
Please review. Thanks in advance.
Index: sys/sys/un.h
===================================================================
RCS file: /stl/src/FreeBSD/src/sys/sys/un.h,v
retrieving revision 1.17
diff -u -r1.17 un.h
--- sys/sys/un.h 1999/12/29 04:24:49 1.17
+++ sys/sys/un.h 2001/07/15 11:21:14
@@ -46,12 +46,16 @@
char sun_path[104]; /* path name (gag) */
};
+/* Socket options. */
+#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */
+
#ifdef _KERNEL
struct mbuf;
struct socket;
int uipc_usrreq __P((struct socket *so, int req, struct mbuf *m,
struct mbuf *nam, struct mbuf *control));
+int uipc_ctloutput __P((struct socket *so, struct sockopt *sopt));
int unp_connect2 __P((struct socket *so, struct socket *so2));
void unp_dispose __P((struct mbuf *m));
int unp_externalize __P((struct mbuf *rights));
Index: sys/sys/unpcb.h
===================================================================
RCS file: /stl/src/FreeBSD/src/sys/sys/unpcb.h,v
retrieving revision 1.11
diff -u -r1.11 unpcb.h
--- sys/sys/unpcb.h 2000/05/26 02:06:59 1.11
+++ sys/sys/unpcb.h 2001/07/15 11:21:14
@@ -38,6 +38,7 @@
#define _SYS_UNPCB_H_
#include <sys/queue.h>
+#include <sys/ucred.h>
/*
* Protocol control block for an active
@@ -80,7 +81,25 @@
int unp_cc; /* copy of rcv.sb_cc */
int unp_mbcnt; /* copy of rcv.sb_mbcnt */
unp_gen_t unp_gencnt; /* generation count of this instance */
+ int unp_flags; /* flags */
+ struct xucred unp_peercred; /* peer credentials, if applicable */
};
+
+/*
+ * Flags in unp_flags.
+ *
+ * UNP_HAVEPC - indicates that the unp_peercred member is filled in
+ * and is really the credentials of the connected peer. This is used
+ * to determine whether the contents should be sent to the user or
+ * not.
+ *
+ * UNP_HAVEPCCACHED - indicates that the unp_peercred member is filled
+ * in, but does *not* contain the credentials of the connected peer
+ * (there may not even be a peer). This is set in unp_listen() when
+ * it fills in unp_peercred for later consumption by unp_connect().
+ */
+#define UNP_HAVEPC 0x001
+#define UNP_HAVEPCCACHED 0x002
#define sotounpcb(so) ((struct unpcb *)((so)->so_pcb))
Index: sys/kern/uipc_proto.c
===================================================================
RCS file: /stl/src/FreeBSD/src/sys/kern/uipc_proto.c,v
retrieving revision 1.21
diff -u -r1.21 uipc_proto.c
--- sys/kern/uipc_proto.c 1999/10/11 15:19:11 1.21
+++ sys/kern/uipc_proto.c 2001/07/15 11:21:14
@@ -51,7 +51,7 @@
static struct protosw localsw[] = {
{ SOCK_STREAM, &localdomain, 0, PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS,
- 0, 0, 0, 0,
+ 0, 0, 0, &uipc_ctloutput,
0,
0, 0, 0, 0,
&uipc_usrreqs
Index: sys/kern/uipc_usrreq.c
===================================================================
RCS file: /stl/src/FreeBSD/src/sys/kern/uipc_usrreq.c,v
retrieving revision 1.66
diff -u -r1.66 uipc_usrreq.c
--- sys/kern/uipc_usrreq.c 2001/05/25 16:59:07 1.66
+++ sys/kern/uipc_usrreq.c 2001/07/15 11:21:14
@@ -91,6 +91,7 @@
static void unp_mark __P((struct file *));
static void unp_discard __P((struct file *));
static int unp_internalize __P((struct mbuf *, struct proc *));
+static int unp_listen __P((struct unpcb *, struct proc *));
static int
uipc_abort(struct socket *so)
@@ -199,7 +200,7 @@
if (unp == 0 || unp->unp_vnode == 0)
return EINVAL;
- return 0;
+ return unp_listen(unp, p);
}
static int
@@ -434,6 +435,41 @@
uipc_send, uipc_sense, uipc_shutdown, uipc_sockaddr,
sosend, soreceive, sopoll
};
+
+int
+uipc_ctloutput(so, sopt)
+ struct socket *so;
+ struct sockopt *sopt;
+{
+ struct unpcb *unp = sotounpcb(so);
+ int error;
+
+ switch (sopt->sopt_dir) {
+ case SOPT_GET:
+ switch (sopt->sopt_name) {
+ case LOCAL_PEERCRED:
+ if (unp->unp_flags & UNP_HAVEPC)
+ error = sooptcopyout(sopt, &unp->unp_peercred,
+ sizeof(unp->unp_peercred));
+ else {
+ if (so->so_type == SOCK_STREAM)
+ error = ENOTCONN;
+ else
+ error = EINVAL;
+ }
+ break;
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+ break;
+ case SOPT_SET:
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+ return (error);
+}
/*
* Both send and receive buffers are allocated PIPSIZ bytes of buffering
@@ -609,7 +645,7 @@
register struct sockaddr_un *soun = (struct sockaddr_un *)nam;
register struct vnode *vp;
register struct socket *so2, *so3;
- struct unpcb *unp2, *unp3;
+ struct unpcb *unp, *unp2, *unp3;
int error, len;
struct nameidata nd;
char buf[SOCK_MAXADDRLEN];
@@ -648,12 +684,40 @@
error = ECONNREFUSED;
goto bad;
}
+ unp = sotounpcb(so);
unp2 = sotounpcb(so2);
unp3 = sotounpcb(so3);
if (unp2->unp_addr)
unp3->unp_addr = (struct sockaddr_un *)
dup_sockaddr((struct sockaddr *)
unp2->unp_addr, 1);
+
+ /*
+ * unp_peercred management:
+ *
+ * The connecter's (client's) credentials are copied
+ * from its process structure at the time of connect()
+ * (which is now).
+ */
+ memset(&unp3->unp_peercred, '\0', sizeof(unp3->unp_peercred));
+ unp3->unp_peercred.cr_uid = p->p_ucred->cr_uid;
+ unp3->unp_peercred.cr_ngroups = p->p_ucred->cr_ngroups;
+ memcpy(unp3->unp_peercred.cr_groups, p->p_ucred->cr_groups,
+ sizeof(unp3->unp_peercred.cr_groups));
+ unp3->unp_flags |= UNP_HAVEPC;
+ /*
+ * The receiver's (server's) credentials are copied
+ * from the unp_peercred member of socket on which the
+ * former called listen(); unp_listen() cached that
+ * process's credentials at that time so we can use
+ * them now.
+ */
+ KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED,
+ ("unp_connect: listener without cached peercred"));
+ memcpy(&unp->unp_peercred, &unp2->unp_peercred,
+ sizeof(unp->unp_peercred));
+ unp->unp_flags |= UNP_HAVEPC;
+
so2 = so3;
}
error = unp_connect2(so, so2);
@@ -1242,6 +1306,21 @@
if (m)
unp_scan(m, unp_discard);
+}
+
+static int
+unp_listen(unp, p)
+ struct unpcb *unp;
+ struct proc *p;
+{
+
+ bzero(&unp->unp_peercred, sizeof(unp->unp_peercred));
+ unp->unp_peercred.cr_uid = p->p_ucred->cr_uid;
+ unp->unp_peercred.cr_ngroups = p->p_ucred->cr_ngroups;
+ bcopy(p->p_ucred->cr_groups, unp->unp_peercred.cr_groups,
+ sizeof(unp->unp_peercred.cr_groups));
+ unp->unp_flags |= UNP_HAVEPCCACHED;
+ return (0);
}
static void
Index: share/man/man4/unix.4
===================================================================
RCS file: /stl/src/FreeBSD/src/share/man/man4/unix.4,v
retrieving revision 1.4
diff -u -r1.4 unix.4
--- share/man/man4/unix.4 2001/07/14 19:40:48 1.4
+++ share/man/man4/unix.4 2001/07/15 11:21:14
@@ -32,7 +32,7 @@
.\" @(#)unix.4 8.1 (Berkeley) 6/9/93
.\" $FreeBSD: src/share/man/man4/unix.4,v 1.4 2001/07/14 19:40:48 schweikh Exp $
.\"
-.Dd June 9, 1993
+.Dd July 15, 2001
.Dt UNIX 4
.Os
.Sh NAME
@@ -147,6 +147,35 @@
Descriptors that are awaiting delivery, or that are
purposely not received, are automatically closed by the system
when the destination socket is closed.
+.Pp
+The effective credentials (i.e., the user ID and group list) the of a
+peer on a
+.Dv SOCK_STREAM
+socket may be obtained using the
+.Dv LOCAL_PEERCRED
+socket option.
+This may be used by a server to obtain and verify the credentials of
+its client, and vice versa by the client to verify the credentials
+of the server.
+These will arrive in the form of a filled in
+.Ar struct xucred
+(defined in
+.Pa sys/ucred.h ) .
+The credentials presented to the server (the
+.Xr listen 2
+caller) are those of the client when it called
+.Xr connect 2 ;
+the credentials presented to the client (the
+.Xr connect 2
+caller) are those of the server when it called
+.Xr listen 2 .
+This mechanism is reliable; there is no way for either party to influence
+the credentials presented to its peer except by calling the appropriate
+system call (e.g.,
+.Xr connect 2
+or
+.Xr listen 2 )
+under different effective credentials.
.Sh SEE ALSO
.Xr socket 2 ,
.Xr intro 4
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# getpeereid.3
# getpeereid.c
#
echo x - getpeereid.3
sed 's/^X//' >getpeereid.3 << 'END-of-getpeereid.3'
X.\"
X.\" Copyright (c) 2001 Dima Dorfman.
X.\" All rights reserved.
X.\"
X.\" Redistribution and use in source and binary forms, with or without
X.\" modification, are permitted provided that the following conditions
X.\" are met:
X.\" 1. Redistributions of source code must retain the above copyright
X.\" notice, this list of conditions and the following disclaimer.
X.\" 2. Redistributions in binary form must reproduce the above copyright
X.\" notice, this list of conditions and the following disclaimer in the
X.\" documentation and/or other materials provided with the distribution.
X.\"
X.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X.\" SUCH DAMAGE.
X.\"
X.\" $FreeBSD$
X.\"
X.Dd July 15, 2001
X.Dt GETPEEREID 3
X.Os
X.Sh NAME
X.Nm getpeereid ,
X.Nd get the effective credentials of a UNIX-domain peer
X.Sh LIBRARY
X.Lb libc
X.Sh SYNOPSIS
X.Fd #include <sys/types.h>
X.Fd #include <unistd.h>
X.Ft int
X.Fn getpeereid "int s" "uid_t *euid" "gid_t *egid"
X.Sh DESCRIPTION
XThe
X.Fn getpeereid
Xroutine returns the effective user and group IDs of the
Xpeer connected to a UNIX-domain socket.
XThe argument
X.Fa s
Xmust be a UNIX-domain socket
X.Pq Xr unix 4
Xof type
X.Dv SOCK_STREAM
Xon which either
X.Xr connect 2
Xor
X.Xr listen 2
Xhave been called.
XThe effective used ID is placed in
X.Fa euid ,
Xand the effective group ID in
X.Fa egid .
X.Pp
XThe credentials returned to the
X.Xr listen 2
Xcaller are those of its peer at the time it called
X.Xr connect 2 ;
Xthe credentials returned to the
X.Xr connect 2
Xcaller are those of its peer at the time it called
X.Xr listen 2 .
XThis mechanism is reliable; there is no way for either side to influence
Xthe credentials returned to its peer except by calling the appropriate
Xsystem call (i.e., either
X.Xr connect 2
Xor
X.Xr listen 2 )
Xunder different effective credentials.
X.Pp
XOne common use of this routine is for a UNIX-domain server
Xto verify the credentials of its client.
XLikewise, the client can verify the credentials of the server.
X.Sh IMPLEMENTATION NOTES
XOn
X.Fx ,
X.Fn getpeereid
Xis implemented in terms of the
X.Dv LOCAL_PEERCRED
X.Xr unix 4
Xsocket option.
X.Sh RETURN VALUES
XIf the call succeeds, a value of 0 is returned and
X.Fa euid
Xand
X.Fa egid
Xcontain the effective user and group IDs of the peer on
X.Fa s ,
Xrespectively.
XIf the call fails, a value of \-1 is returned and
X.Va errno
Xis set to indicate the error.
X.Sh ERRORS
XThe call succeeds unless:
X.Bl -tag -width Er
X.It Bq Er EBADF
XThe argument
X.Fa s
Xis not a valid descriptor.
X.It Bq Er ENOTSOCK
XThe argument
X.Fa s
Xis a file, not a socket.
X.It Bq Er ENOTCONN
XThe argument
X.Fa s
Xdoes not refer to a socket on which
X.Xr connect 2
Xor
X.Xr listen 2
Xhave been called.
X.It Bq Er EINVAL
XThe argument
X.Fa s
Xdoes not refer to a socket of type
X.Dv SOCK_STREAM .
X.El
X.Sh SEE ALSO
X.Xr connect 2 ,
X.Xr getpeername 2 ,
X.Xr getsockname 2 ,
X.Xr getsockopt 2 ,
X.Xr listen 2 ,
X.Xr unix 4
X.Sh HISTORY
XThe
X.Fn getpeereid
Xroutine appeared in
X.Fx 5.0 .
END-of-getpeereid.3
echo x - getpeereid.c
sed 's/^X//' >getpeereid.c << 'END-of-getpeereid.c'
X/*
X * Copyright (c) 2001 Dima Dorfman.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X * notice, this list of conditions and the following disclaimer in the
X * documentation and/or other materials provided with the distribution.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X */
X
X#if defined(LIBC_RCS) && !defined(lint)
Xstatic const char rcsid[] =
X "$FreeBSD$";
X#endif /* LIBC_RCS and not lint */
X
X#include <sys/param.h>
X#include <sys/socket.h>
X#include <sys/ucred.h>
X#include <sys/un.h>
X
X#include <unistd.h>
X
Xint
Xgetpeereid(int s, uid_t *euid, gid_t *egid)
X{
X struct xucred xuc;
X socklen_t xuclen;
X int error;
X
X xuclen = sizeof(xuc);
X error = getsockopt(s, LOCAL_PEERCRED, 1, &xuc, &xuclen);
X if (error != 0)
X return (error);
X *euid = xuc.cr_uid;
X *egid = xuc.cr_gid;
X return (0);
X}
END-of-getpeereid.c
exit
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010731114050.B730E3E31>
