From 5e416567e18401664948a984b8208f0d39a1f0fd Mon Sep 17 00:00:00 2001 From: dd Date: Fri, 17 Aug 2001 22:01:18 +0000 Subject: 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) --- sys/kern/uipc_proto.c | 2 +- sys/kern/uipc_usrreq.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++-- sys/sys/un.h | 4 +++ sys/sys/unpcb.h | 19 ++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) (limited to 'sys') 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 +#include /* * 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 . */ -- cgit v1.1