Date: Wed, 27 Jun 2001 00:06:28 -0700 From: Dima Dorfman <dima@unixfreak.org> To: arch@freebsd.org Subject: Peer credentials on a Unix domain socket Message-ID: <20010627070628.AB5F13E2F@bazooka.unixfreak.org>
next in thread | raw e-mail | index | archive | help
Hi folks, Currently, there is no reliable way for a server listening on a Unix domain socket to find out the credentials of its peer until the peer sends something over the socket. Finding its credentials can be useful if the server only wants to accept connections from certain users. We already have SCM_CREDS, which will send the peer's credentials along with a message, but this is *not* sufficient as it may be unacceptable for the server to wait until the peer sends something; think of DoS attacked. Times don't help, either; think of SYN flood-like attacks. I would like to propose implementing such a facility as a socket option, LOCAL_PEERCRED. The payload would be am xucred structure with the effective credentials of the connect(2) caller. Granted these may not be the credentials of the process using the socket (think descriptor passing), but it doesn't matter; if a process hands a descriptor off to something else, it should be trusting it not to abuse it (this is a feature: think of opening a privileged port and dropping privileges). This has been discussed at least twice before, and nobody has a better idea. Again, I would like to stress the two requirements: (1) the accept(2) caller must be able to reliably obtain the effective credentials of the connect(2) caller, and (2) the accept(2) caller must be able to do (1) without relying on the connect(2) caller to send data (SCM_CREDS doesn't meet (2)). Patch attached. Comments? Suggestions? Thanks in advance, Dima Dorfman dima@unixfreak.org Index: 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/un.h 1999/12/29 04:24:49 1.17 +++ sys/un.h 2001/06/27 06:51:18 @@ -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/unpcb.h =================================================================== RCS file: /stl/src/FreeBSD/src/sys/sys/unpcb.h,v retrieving revision 1.11 diff -u -r1.11 unpcb.h --- sys/unpcb.h 2000/05/26 02:06:59 1.11 +++ sys/unpcb.h 2001/06/27 06:51:18 @@ -80,7 +80,14 @@ 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. + */ +#define UNP_HAVEPC 0x001 /* unp_peercred filled in? */ #define sotounpcb(so) ((struct unpcb *)((so)->so_pcb)) Index: 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 --- kern/uipc_proto.c 1999/10/11 15:19:11 1.21 +++ kern/uipc_proto.c 2001/06/27 06:51:18 @@ -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: 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 --- kern/uipc_usrreq.c 2001/05/25 16:59:07 1.66 +++ kern/uipc_usrreq.c 2001/06/27 06:51:18 @@ -434,6 +434,23 @@ 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; + + if (sopt->sopt_dir == SOPT_GET && sopt->sopt_name == LOCAL_PEERCRED && + unp->unp_flags & UNP_HAVEPC) + error = sooptcopyout(sopt, &unp->unp_peercred, + sizeof(unp->unp_peercred)); + else + error = EOPNOTSUPP; + return (error); +} /* * Both send and receive buffers are allocated PIPSIZ bytes of buffering @@ -654,6 +671,12 @@ unp3->unp_addr = (struct sockaddr_un *) dup_sockaddr((struct sockaddr *) unp2->unp_addr, 1); + bzero(&unp3->unp_peercred, sizeof(unp3->unp_peercred)); + unp3->unp_peercred.cr_uid = p->p_ucred->cr_uid; + unp3->unp_peercred.cr_ngroups = p->p_ucred->cr_ngroups; + bcopy(p->p_ucred->cr_groups, unp3->unp_peercred.cr_groups, + sizeof(unp3->unp_peercred.cr_groups)); + unp3->unp_flags |= UNP_HAVEPC; so2 = so3; } error = unp_connect2(so, so2); To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-arch" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010627070628.AB5F13E2F>