From 697b9afe00c21df9fc74c748ea35113b4b37c4b4 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Sat, 11 Oct 2014 18:28:04 +0000 Subject: [PATCH] Add setsockopt(sd, SOL_SOCKET, SO_PEERCRED, NULL, 0) to disable initial handshake on AF_LOCAL sockets. * fhandler.h (class fhandler_socket): Add no_getpeereid status flag. (fhandler_socket::af_local_set_no_getpeereid): New prototype. * fhandler_socket.cc (fhandler_socket::af_local_connect): Skip handshake if no_getpeereid is set. Add debug output. (fhandler_socket::af_local_accept): Likewise. (fhandler_socket::af_local_set_no_getpeereid): New function. (fhandler_socket::af_local_copy): Copy no_getpeereid. (fhandler_socket::getpeereid): Fail if no_getpeereid is set. * net.cc (cygwin_setsockopt): Add SO_PEERCRED for AF_LOCAL/SOCK_STREAM sockets. Add comment to explain why we need it. * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. --- winsup/cygwin/ChangeLog | 16 ++++++++++++ winsup/cygwin/fhandler.h | 5 +++- winsup/cygwin/fhandler_socket.cc | 35 ++++++++++++++++++++++++-- winsup/cygwin/include/cygwin/version.h | 3 ++- winsup/cygwin/net.cc | 19 ++++++++++++++ 5 files changed, 74 insertions(+), 4 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index ce4102ca9..b9e11747c 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,19 @@ +2014-10-11 Christian Franke + + Add setsockopt(sd, SOL_SOCKET, SO_PEERCRED, NULL, 0) to disable + initial handshake on AF_LOCAL sockets. + * fhandler.h (class fhandler_socket): Add no_getpeereid status flag. + (fhandler_socket::af_local_set_no_getpeereid): New prototype. + * fhandler_socket.cc (fhandler_socket::af_local_connect): Skip handshake + if no_getpeereid is set. Add debug output. + (fhandler_socket::af_local_accept): Likewise. + (fhandler_socket::af_local_set_no_getpeereid): New function. + (fhandler_socket::af_local_copy): Copy no_getpeereid. + (fhandler_socket::getpeereid): Fail if no_getpeereid is set. + * net.cc (cygwin_setsockopt): Add SO_PEERCRED for AF_LOCAL/SOCK_STREAM + sockets. Add comment to explain why we need it. + * include/cygwin/version.h (CYGWIN_VERSION_API_MINOR): Bump. + 2014-10-11 Corinna Vinschen * fhandler_socket.cc (fhandler_socket::af_local_connect): Drop outdated diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index cf4de0746..aff8d85e0 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -504,6 +504,7 @@ class fhandler_socket: public fhandler_base int af_local_accept (); public: int af_local_connect (); + int af_local_set_no_getpeereid (); void af_local_set_sockpair_cred (); private: @@ -531,10 +532,11 @@ class fhandler_socket: public fhandler_base unsigned saw_shutdown_write : 1; /* Socket saw a SHUT_WR */ unsigned saw_reuseaddr : 1; /* Socket saw SO_REUSEADDR call */ unsigned connect_state : 3; + unsigned no_getpeereid : 1; public: status_flags () : async_io (0), saw_shutdown_read (0), saw_shutdown_write (0), - connect_state (unconnected) + connect_state (unconnected), no_getpeereid (0) {} } status; @@ -556,6 +558,7 @@ class fhandler_socket: public fhandler_base IMPLEMENT_STATUS_FLAG (bool, saw_shutdown_write) IMPLEMENT_STATUS_FLAG (bool, saw_reuseaddr) IMPLEMENT_STATUS_FLAG (conn_state, connect_state) + IMPLEMENT_STATUS_FLAG (bool, no_getpeereid) int bind (const struct sockaddr *name, int namelen); int connect (const struct sockaddr *name, int namelen); diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index e2750ea72..ff150448e 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -401,7 +401,10 @@ fhandler_socket::af_local_connect () if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) return 0; - debug_printf ("af_local_connect called"); + debug_printf ("af_local_connect called, no_getpeereid=%d", no_getpeereid ()); + if (no_getpeereid ()) + return 0; + connect_state (connect_credxchg); af_local_setblocking (orig_async_io, orig_is_nonblocking); if (!af_local_send_secret () || !af_local_recv_secret () @@ -421,7 +424,10 @@ fhandler_socket::af_local_accept () { bool orig_async_io, orig_is_nonblocking; - debug_printf ("af_local_accept called"); + debug_printf ("af_local_accept called, no_getpeereid=%d", no_getpeereid ()); + if (no_getpeereid ()) + return 0; + connect_state (connect_credxchg); af_local_setblocking (orig_async_io, orig_is_nonblocking); if (!af_local_recv_secret () || !af_local_send_secret () @@ -437,6 +443,25 @@ fhandler_socket::af_local_accept () return 0; } +int +fhandler_socket::af_local_set_no_getpeereid () +{ + if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) + { + set_errno (EINVAL); + return -1; + } + if (connect_state () != unconnected) + { + set_errno (EALREADY); + return -1; + } + + debug_printf ("no_getpeereid set"); + no_getpeereid (true); + return 0; +} + void fhandler_socket::af_local_set_cred () { @@ -461,6 +486,7 @@ fhandler_socket::af_local_copy (fhandler_socket *sock) sock->sec_peer_pid = sec_peer_pid; sock->sec_peer_uid = sec_peer_uid; sock->sec_peer_gid = sec_peer_gid; + sock->no_getpeereid (no_getpeereid ()); } void @@ -2303,6 +2329,11 @@ fhandler_socket::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid) set_errno (EINVAL); return -1; } + if (no_getpeereid ()) + { + set_errno (ENOTSUP); + return -1; + } if (connect_state () != connected) { set_errno (ENOTCONN); diff --git a/winsup/cygwin/include/cygwin/version.h b/winsup/cygwin/include/cygwin/version.h index a5c482da9..66fad063a 100644 --- a/winsup/cygwin/include/cygwin/version.h +++ b/winsup/cygwin/include/cygwin/version.h @@ -452,12 +452,13 @@ details. */ CW_GETENT, CW_ENDENT, CW_GETNSSSEP, CW_GETPWSID, CW_GETGRSID, CW_CYGNAME_FROM_WINNAME. 276: Export ffsl, ffsll. + 277: Add setsockopt(SO_PEERCRED). */ /* Note that we forgot to bump the api for ualarm, strtoll, strtoull */ #define CYGWIN_VERSION_API_MAJOR 0 -#define CYGWIN_VERSION_API_MINOR 276 +#define CYGWIN_VERSION_API_MINOR 277 /* There is also a compatibity version number associated with the shared memory regions. It is incremented when incompatible diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index b6c0f7257..a01dbedf1 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -810,6 +810,25 @@ cygwin_setsockopt (int fd, int level, int optname, const void *optval, fhandler_socket *fh = get (fd); if (!fh) __leave; + + /* Switch off the AF_LOCAL handshake and thus SO_PEERCRED handling + for AF_LOCAL/SOCK_STREAM sockets. This allows to handle special + situations in which connect is called before a listening socket + accepts connections. + FIXME: In the long run we should find a more generic solution which + doesn't require a blocking handshake in accept/connect to exchange + SO_PEERCRED credentials. */ + if (level == SOL_SOCKET && optname == SO_PEERCRED) + { + if (optval || optlen + || fh->get_addr_family () != AF_LOCAL + || fh->get_socket_type () != SOCK_STREAM) + set_errno (EINVAL); + else + res = fh->af_local_set_no_getpeereid (); + __leave; + } + /* Old applications still use the old WinSock1 IPPROTO_IP values. */ if (level == IPPROTO_IP && CYGWIN_VERSION_CHECK_FOR_USING_WINSOCK1_VALUES) optname = convert_ws1_ip_optname (optname);