summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authordd <dd@FreeBSD.org>2001-08-17 22:01:18 +0000
committerdd <dd@FreeBSD.org>2001-08-17 22:01:18 +0000
commit5e416567e18401664948a984b8208f0d39a1f0fd (patch)
treea9fd833a0d5be8077a8bafc85a497fe0bae21cfa /sys
parent90eec3a264887cf9fc47c0654b2012231223b681 (diff)
downloadFreeBSD-src-5e416567e18401664948a984b8208f0d39a1f0fd.zip
FreeBSD-src-5e416567e18401664948a984b8208f0d39a1f0fd.tar.gz
Implement a LOCAL_PEERCRED socket option which returns a
`struct xucred` with the credentials of the connected peer. Obviously this only works (and makes sense) on SOCK_STREAM sockets. This works for both the connect(2) and listen(2) callers. There is precise documentation of the semantics in unix(4). Reviewed by: dwmalone (eyeballed)
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/uipc_proto.c2
-rw-r--r--sys/kern/uipc_usrreq.c83
-rw-r--r--sys/sys/un.h4
-rw-r--r--sys/sys/unpcb.h19
4 files changed, 105 insertions, 3 deletions
diff --git a/sys/kern/uipc_proto.c b/sys/kern/uipc_proto.c
index ac01aaf..74dab78 100644
--- a/sys/kern/uipc_proto.c
+++ b/sys/kern/uipc_proto.c
@@ -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
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 2907b4e..600fb5c 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -91,6 +91,7 @@ static void unp_scan __P((struct mbuf *, void (*)(struct file *)));
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 @@ uipc_listen(struct socket *so, struct proc *p)
if (unp == 0 || unp->unp_vnode == 0)
return EINVAL;
- return 0;
+ return unp_listen(unp, p);
}
static int
@@ -434,6 +435,41 @@ struct pr_usrreqs uipc_usrreqs = {
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 @@ unp_connect(so, nam, p)
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 @@ unp_connect(so, nam, p)
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);
@@ -1244,6 +1308,21 @@ unp_dispose(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
unp_scan(m0, op)
register struct mbuf *m0;
diff --git a/sys/sys/un.h b/sys/sys/un.h
index e7c3701..710f995 100644
--- a/sys/sys/un.h
+++ b/sys/sys/un.h
@@ -46,12 +46,16 @@ struct sockaddr_un {
char sun_path[104]; /* path name (gag) */
};
+/* Socket options. */
+#define LOCAL_PEERCRED 0x001 /* retrieve peer credentails */
+
#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));
diff --git a/sys/sys/unpcb.h b/sys/sys/unpcb.h
index c0b8a37..cb9d1fa 100644
--- a/sys/sys/unpcb.h
+++ b/sys/sys/unpcb.h
@@ -38,6 +38,7 @@
#define _SYS_UNPCB_H_
#include <sys/queue.h>
+#include <sys/ucred.h>
/*
* Protocol control block for an active
@@ -80,8 +81,26 @@ struct unpcb {
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))
/* Hack alert -- this structure depends on <sys/socketvar.h>. */
OpenPOWER on IntegriCloud