From ef82f76c30c81149fe8cc22126843717d94352ad Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 21 Mar 2005 18:56:50 +0000 Subject: [PATCH] * fhandler.h (class cygthread): Forward declare. (fhandler_socket::sec_pipe): Remove. (fhandler_socket::eid_pipe_name): Remove. (fhandler_socket::eid_setblocking): New private method. (fhandler_socket::eid_unsetblocking): Ditto (fhandler_socket::eid_recv): Ditto (fhandler_socket::eid_send): Ditto (fhandler_socket::eid_accept): Ditto (fhandler_socket::eid_connect): New public method. * fhandler_socket.cc (ASYNC_MASK): Move to beginning of file. (fhandler_socket::eid_pipe_name): Remove. (fhandler_socket::set_socketpair_eids): Move down to fhandler_socket methods. (fhandler_socket::fhandler_socket): Drop initializing sec_pipe. (fhandler_socket::~fhandler_socket): Drop closing sec_pipe. (fhandler_socket::eid_setblocking): New method. (fhandler_socket::eid_unsetblocking): New method. (fhandler_socket::eid_recv): New method. (fhandler_socket::eid_send): New method. (fhandler_socket::eid_connect): New method. (fhandler_socket::eid_accept): New method. (fhandler_socket::dup): Drop sec_pipe handling. (fhandler_socket::connect): Fix WinSock error handling. Prepare eid credential transaction. Call eid_connect on successful connect. (fhandler_socket::listen): Drop creating sec_pipe. (fhandler_socket::accept): Slightly simplify code. Call eid_accept on accepted socket. (fhandler_socket::getpeereid): Reshuffle code for readability. Fix test for invalid pid. * select.cc (set_bits): Call eid_connect on successfully connected socket. --- winsup/cygwin/ChangeLog | 34 ++++ winsup/cygwin/fhandler.h | 9 +- winsup/cygwin/fhandler_socket.cc | 300 +++++++++++++++++-------------- winsup/cygwin/select.cc | 20 ++- 4 files changed, 226 insertions(+), 137 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 45269f0d4..238f9958b 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,37 @@ +2005-03-21 Corinna Vinschen + + * fhandler.h (class cygthread): Forward declare. + (fhandler_socket::sec_pipe): Remove. + (fhandler_socket::eid_pipe_name): Remove. + (fhandler_socket::eid_setblocking): New private method. + (fhandler_socket::eid_unsetblocking): Ditto + (fhandler_socket::eid_recv): Ditto + (fhandler_socket::eid_send): Ditto + (fhandler_socket::eid_accept): Ditto + (fhandler_socket::eid_connect): New public method. + * fhandler_socket.cc (ASYNC_MASK): Move to beginning of file. + (fhandler_socket::eid_pipe_name): Remove. + (fhandler_socket::set_socketpair_eids): Move down to fhandler_socket + methods. + (fhandler_socket::fhandler_socket): Drop initializing sec_pipe. + (fhandler_socket::~fhandler_socket): Drop closing sec_pipe. + (fhandler_socket::eid_setblocking): New method. + (fhandler_socket::eid_unsetblocking): New method. + (fhandler_socket::eid_recv): New method. + (fhandler_socket::eid_send): New method. + (fhandler_socket::eid_connect): New method. + (fhandler_socket::eid_accept): New method. + (fhandler_socket::dup): Drop sec_pipe handling. + (fhandler_socket::connect): Fix WinSock error handling. Prepare + eid credential transaction. Call eid_connect on successful connect. + (fhandler_socket::listen): Drop creating sec_pipe. + (fhandler_socket::accept): Slightly simplify code. Call eid_accept + on accepted socket. + (fhandler_socket::getpeereid): Reshuffle code for readability. Fix + test for invalid pid. + * select.cc (set_bits): Call eid_connect on successfully connected + socket. + 2005-03-19 Christopher Faylor * child_info.h (fork_info): Use different method to alias variable. diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h index 4b0fbb795..5569bf4c4 100644 --- a/winsup/cygwin/fhandler.h +++ b/winsup/cygwin/fhandler.h @@ -37,6 +37,7 @@ typedef struct __DIR DIR; struct dirent; struct iovec; struct __acl32; +class cygthread; enum dirent_states { @@ -365,15 +366,19 @@ class fhandler_socket: public fhandler_base int connect_secret [4]; HANDLE secret_event; - HANDLE sec_pipe; pid_t sec_pid; __uid32_t sec_uid; __gid32_t sec_gid; pid_t sec_peer_pid; __uid32_t sec_peer_uid; __gid32_t sec_peer_gid; - char *eid_pipe_name (char *buf); + void eid_setblocking (bool &, bool &); + void eid_unsetblocking (bool, bool); + bool eid_recv (void); + bool eid_send (void); + void eid_accept (void); public: + void eid_connect (void); void set_socketpair_eids (void); private: diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 866e46695..c2e87478d 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -36,6 +36,8 @@ #include #include +#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT) + extern bool fdsock (cygheap_fdmanip& fd, const device *, SOCKET soc); extern "C" { int sscanf (const char *, const char *, ...); @@ -53,22 +55,6 @@ secret_event_name (char *buf, short port, int *secret_ptr) secret_ptr [2], secret_ptr [3]); } -char * -fhandler_socket::eid_pipe_name (char *buf) -{ - __small_sprintf (buf, "\\\\.\\pipe\\cygwin-unix-%s", get_sun_path ()); - debug_printf ("%s", buf); - return buf; -} - -void -fhandler_socket::set_socketpair_eids (void) -{ - sec_pid = sec_peer_pid = getpid (); - sec_uid = sec_peer_uid = geteuid32 (); - sec_gid = sec_peer_gid = getegid32 (); -} - /* cygwin internal: map sockaddr into internet domain address */ static int get_inet_addr (const struct sockaddr *in, int inlen, @@ -140,7 +126,6 @@ get_inet_addr (const struct sockaddr *in, int inlen, fhandler_socket::fhandler_socket () : fhandler_base (), - sec_pipe (INVALID_HANDLE_VALUE), sun_path (NULL), status () { @@ -163,12 +148,119 @@ fhandler_socket::~fhandler_socket () cfree (prot_info_ptr); if (sun_path) cfree (sun_path); - /* Close eid credentials pipe handle. */ - if (sec_pipe != INVALID_HANDLE_VALUE) - CloseHandle (sec_pipe); } -char *fhandler_socket::get_proc_fd_name (char *buf) +void +fhandler_socket::set_socketpair_eids (void) +{ + sec_pid = sec_peer_pid = getpid (); + sec_uid = sec_peer_uid = geteuid32 (); + sec_gid = sec_peer_gid = getegid32 (); +} + +void +fhandler_socket::eid_setblocking (bool &async, bool &nonblocking) +{ + async = async_io (); + nonblocking = is_nonblocking (); + if (async || nonblocking) + WSAAsyncSelect (get_socket (), winmsg, 0, 0); + unsigned long p = 0; + ioctlsocket (get_socket (), FIONBIO, &p); + set_nonblocking (false); + async_io (false); +} + +void +fhandler_socket::eid_unsetblocking (bool async, bool nonblocking) +{ + if (nonblocking) + { + unsigned long p = 1; + ioctlsocket (get_socket (), FIONBIO, &p); + set_nonblocking (true); + } + if (async) + { + WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK); + async_io (true); + } +} + +bool +fhandler_socket::eid_recv (void) +{ + struct ucred out = { (pid_t) 0, (__uid32_t) -1, (__gid32_t) -1 }; + int rest = sizeof out; + char *ptr = (char *) &out; + while (rest > 0) + { + int ret = recvfrom (ptr, rest, 0, NULL, NULL); + if (ret <= 0) + break; + rest -= ret; + ptr += ret; + } + if (rest == 0) + { + debug_printf ("Received eid credentials: pid: %d, uid: %d, gid: %d", + out.pid, out.uid, out.gid); + sec_peer_pid = out.pid; + sec_peer_uid = out.uid; + sec_peer_gid = out.gid; + } + else + debug_printf ("Receiving eid credentials failed"); + return rest == 0; +} + +bool +fhandler_socket::eid_send (void) +{ + struct ucred in = { sec_pid, sec_uid, sec_gid }; + int rest = sizeof in; + char *ptr = (char *) ∈ + while (rest > 0) + { + int ret = sendto (ptr, rest, 0, NULL, 0); + if (ret <= 0) + break; + rest -= ret; + ptr += ret; + } + if (rest == 0) + debug_printf ("Sending eid credentials succeeded"); + else + debug_printf ("Sending eid credentials failed"); + return rest == 0; +} + +void +fhandler_socket::eid_connect (void) +{ + /* This test allows to keep select.cc clean from boring implementation + details. */ + if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) + return; + debug_printf ("eid_connect called"); + bool orig_async_io, orig_is_nonblocking; + eid_setblocking (orig_async_io, orig_is_nonblocking); + eid_send () && eid_recv (); + eid_unsetblocking (orig_async_io, orig_is_nonblocking); +} + +void +fhandler_socket::eid_accept (void) +{ + debug_printf ("eid_accept called"); + bool orig_async_io, orig_is_nonblocking; + eid_setblocking (orig_async_io, orig_is_nonblocking); + eid_recv () && eid_send (); + eid_unsetblocking (orig_async_io, orig_is_nonblocking); +} + +char * +fhandler_socket::get_proc_fd_name (char *buf) { __small_sprintf (buf, "socket:[%d]", get_socket ()); return buf; @@ -370,18 +462,6 @@ fhandler_socket::dup (fhandler_base *child) fhs->sec_peer_pid = sec_peer_pid; fhs->sec_peer_uid = sec_peer_uid; fhs->sec_peer_gid = sec_peer_gid; - if (sec_pipe != INVALID_HANDLE_VALUE) - { - if (!DuplicateHandle (hMainProc, sec_pipe, hMainProc, &nh, 0, - TRUE, DUPLICATE_SAME_ACCESS)) - { - system_printf ("!DuplicateHandle(%x) failed, %E", sec_pipe); - __seterrno (); - return -1; - } - else - fhs->sec_pipe = nh; - } } } fhs->connect_state (connect_state ()); @@ -420,8 +500,6 @@ fhandler_socket::dup (fhandler_base *child) { system_printf ("!DuplicateHandle(%x) failed, %E", get_io_handle ()); __seterrno (); - if (fhs->sec_pipe != INVALID_HANDLE_VALUE) - CloseHandle (fhs->sec_pipe); return -1; } VerifyHandle (nh); @@ -622,18 +700,18 @@ fhandler_socket::connect (const struct sockaddr *name, int namelen) if (res) { + err = WSAGetLastError (); /* Special handling for connect to return the correct error code when called on a non-blocking socket. */ if (is_nonblocking () || connect_state () == connect_pending) { - err = WSAGetLastError (); if (err == WSAEWOULDBLOCK || err == WSAEALREADY) in_progress = true; if (err == WSAEWOULDBLOCK) - WSASetLastError (WSAEINPROGRESS); + WSASetLastError (err = WSAEINPROGRESS); else if (err == WSAEINVAL) - WSASetLastError (WSAEISCONN); + WSASetLastError (err = WSAEISCONN); } set_winsock_errno (); } @@ -671,38 +749,23 @@ fhandler_socket::connect (const struct sockaddr *name, int namelen) res = -1; } - if (!res || in_progress) + /* Prepare eid credential transaction. */ + sec_pid = getpid (); + sec_uid = geteuid32 (); + sec_gid = getegid32 (); + sec_peer_pid = (pid_t) 0; + sec_peer_uid = (__uid32_t) -1; + sec_peer_gid = (__gid32_t) -1; + + if (!res) { - /* eid credential transaction. */ - if (wincap.has_named_pipes ()) - { - struct ucred in = { getpid (), geteuid32 (), getegid32 () }; - struct ucred out = { (pid_t) 0, (__uid32_t) -1, (__gid32_t) -1 }; - DWORD bytes = 0; - debug_printf ("Calling CallNamedPipe"); - if (CallNamedPipe(eid_pipe_name ((char *) alloca (CYG_MAX_PATH + 1)), - &in, sizeof in, &out, sizeof out, &bytes, 1000)) - { - debug_printf ("Received eid credentials: pid: %d, uid: %d" - ", gid: %d", out.pid, out.uid, out.gid); - sec_peer_pid = out.pid; - sec_peer_uid = out.uid; - sec_peer_gid = out.gid; - } - else - debug_printf ("Receiving eid credentials failed: %E"); - } - else /* 9x */ - { - /* Incorrect but wrong pid at least doesn't break getpeereid. */ - sec_peer_pid = getpid (); - sec_peer_uid = geteuid32 (); - sec_peer_gid = getegid32 (); - } + /* eid credential transaction. If connect is in progress, + we're deferring the eid transaction to the successful select, + see select.cc, function set_bits(). */ + eid_connect (); } } - err = WSAGetLastError (); if (err == WSAEINPROGRESS || err == WSAEALREADY) connect_state (connect_pending); else @@ -728,16 +791,6 @@ fhandler_socket::listen (int backlog) sec_peer_pid = (pid_t) 0; sec_peer_uid = (__uid32_t) -1; sec_peer_gid = (__gid32_t) -1; - /* A listening socket can call listen again, but that shouldn't - result in trying to create another pipe. */ - if (wincap.has_named_pipes () && sec_pipe == INVALID_HANDLE_VALUE) - sec_pipe = - CreateNamedPipe (eid_pipe_name ((char *) alloca (CYG_MAX_PATH + 1)), - PIPE_ACCESS_DUPLEX, - PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, - PIPE_UNLIMITED_INSTANCES, sizeof (struct ucred), - sizeof (struct ucred), 1000, &sec_all); - debug_printf ("sec_pipe: %x", sec_pipe); } connect_state (connected); } @@ -749,8 +802,6 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) { int res = -1; bool secret_check_failed = false; - struct ucred in = { sec_pid, sec_uid, sec_gid }; - struct ucred out = { (pid_t) 0, (__uid32_t) -1, (__gid32_t) -1 }; /* Allows NULL peer and len parameters. */ struct sockaddr_in peer_dummy; @@ -794,34 +845,6 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) set_errno (ECONNABORTED); return -1; } - - /* eid credential transaction. */ - if (wincap.has_named_pipes ()) - { - DWORD bytes = 0; - debug_printf ("Calling ConnectNamedPipe"); - bool ret = ConnectNamedPipe (sec_pipe, NULL); - if (ret || GetLastError () == ERROR_PIPE_CONNECTED) - { - if (!ReadFile (sec_pipe, &out, sizeof out, &bytes, NULL)) - debug_printf ("Receiving eid credentials failed: %E"); - else - debug_printf ("Received eid credentials: pid: %d, uid: %d" - ", gid: %d", out.pid, out.uid, out.gid); - if (!WriteFile (sec_pipe, &in, sizeof in, &bytes, NULL)) - debug_printf ("Sending eid credentials failed: %E"); - DisconnectNamedPipe (sec_pipe); - } - else - debug_printf ("Connecting the eid credential pipe failed: %E"); - } - else /* 9x */ - { - /* Incorrect but wrong pid at least doesn't break getpeereid. */ - out.pid = sec_pid; - out.uid = sec_uid; - out.gid = sec_gid; - } } if ((SOCKET) res == INVALID_SOCKET) @@ -831,19 +854,29 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) cygheap_fdnew res_fd; if (res_fd >= 0 && fdsock (res_fd, &dev (), res)) { - ((fhandler_socket *) res_fd)->set_addr_family (get_addr_family ()); - ((fhandler_socket *) res_fd)->set_socket_type (get_socket_type ()); + fhandler_socket *sock = (fhandler_socket *) res_fd; + sock->set_addr_family (get_addr_family ()); + sock->set_socket_type (get_socket_type ()); + sock->async_io (async_io ()); + sock->set_nonblocking (is_nonblocking ()); if (get_addr_family () == AF_LOCAL) { - ((fhandler_socket *) res_fd)->set_sun_path (get_sun_path ()); + sock->set_sun_path (get_sun_path ()); if (get_socket_type () == SOCK_STREAM) - { - ((fhandler_socket *) res_fd)->sec_peer_pid = out.pid; - ((fhandler_socket *) res_fd)->sec_peer_uid = out.uid; - ((fhandler_socket *) res_fd)->sec_peer_gid = out.gid; - } + { + /* Don't forget to copy credentials from accepting + socket to accepted socket and start transaction + on accepted socket! */ + sock->sec_pid = sec_pid; + sock->sec_uid = sec_uid; + sock->sec_gid = sec_gid; + sock->sec_peer_pid = sec_peer_pid; + sock->sec_peer_uid = sec_peer_uid; + sock->sec_peer_gid = sec_peer_gid; + sock->eid_accept (); + } } - ((fhandler_socket *) res_fd)->connect_state (connected); + sock->connect_state (connected); res = res_fd; } else @@ -1475,8 +1508,6 @@ fhandler_socket::close () return res; } -#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT) - int fhandler_socket::ioctl (unsigned int cmd, void *p) { @@ -1673,21 +1704,28 @@ fhandler_socket::set_sun_path (const char *path) int fhandler_socket::getpeereid (pid_t *pid, __uid32_t *euid, __gid32_t *egid) { - if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) + if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM) + { + set_errno (EINVAL); + return -1; + } + if (connect_state () != connected) { - if (connect_state () == connected && sec_peer_pid != (pid_t) -1) - { - if (!check_null_invalid_struct (pid)) - *pid = sec_peer_pid; - if (!check_null_invalid_struct (euid)) - *euid = sec_peer_uid; - if (!check_null_invalid_struct (egid)) - *egid = sec_peer_gid; - return 0; - } set_errno (ENOTCONN); + return -1; } - else - set_errno (EINVAL); - return -1; + if (sec_peer_pid == (pid_t) 0) + { + set_errno (ENOTCONN); /* Usually when calling getpeereid on + accepting (instead of accepted) socket. */ + return -1; + } + + if (!check_null_invalid_struct (pid)) + *pid = sec_peer_pid; + if (!check_null_invalid_struct (euid)) + *euid = sec_peer_uid; + if (!check_null_invalid_struct (egid)) + *egid = sec_peer_gid; + return 0; } diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc index eb86d7444..50489aa1d 100644 --- a/winsup/cygwin/select.cc +++ b/winsup/cygwin/select.cc @@ -326,6 +326,7 @@ set_bits (select_record *me, fd_set *readfds, fd_set *writefds, fd_set *exceptfds) { int ready = 0; + fhandler_socket *sock; select_printf ("me %p, testing fd %d (%s)", me, me->fd, me->fh->get_name ()); if (me->read_selected && me->read_ready) { @@ -335,8 +336,15 @@ set_bits (select_record *me, fd_set *readfds, fd_set *writefds, if (me->write_selected && me->write_ready) { UNIX_FD_SET (me->fd, writefds); - if (me->except_on_write && me->fh->is_socket ()) - ((fhandler_socket *) me->fh)->connect_state (connected); + if (me->except_on_write && (sock = me->fh->is_socket ())) + { + /* eid credential transaction on successful non-blocking connect. + Since the read bit indicates an error, don't start transaction + if it's set. */ + if (!me->read_ready && sock->connect_state () == connect_pending) + sock->eid_connect (); + sock->connect_state (connected); + } ready++; } if ((me->except_selected || me->except_on_write) && me->except_ready) @@ -344,8 +352,12 @@ set_bits (select_record *me, fd_set *readfds, fd_set *writefds, if (me->except_on_write) /* Only on sockets */ { UNIX_FD_SET (me->fd, writefds); - if (me->fh->is_socket ()) - ((fhandler_socket *) me->fh)->connect_state (connected); + if ((sock = me->fh->is_socket ())) + { + if (!me->read_ready && sock->connect_state () == connect_pending) + sock->eid_connect (); + sock->connect_state (connected); + } } if (me->except_selected) UNIX_FD_SET (me->fd, exceptfds); -- 2.43.5