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)
This commit is contained in:
parent
90eec3a264
commit
5e416567e1
@ -32,7 +32,7 @@
|
||||
.\" @(#)unix.4 8.1 (Berkeley) 6/9/93
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 9, 1993
|
||||
.Dd July 15, 2001
|
||||
.Dt UNIX 4
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -147,6 +147,35 @@ passed to a receiver.
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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>. */
|
||||
|
Loading…
Reference in New Issue
Block a user