This is the mail archive of the cygwin-developers mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Cygwin AF_UNIX emulation


Hi Christian,

On Oct 16 23:34, Christian Franke wrote:
> Hi Corinna,
> 
> Corinna Vinschen wrote:
> >On Oct 13 07:37, Christian Franke wrote:
> >>>I
> >>>also added a comment to explain why we do this and a FIXME comment so we
> >>>don't forget we're still looking for a more generic solution for the
> >>>SO_PEERCRED exchange.
> >>Definitely, at least because the current AF_LOCAL emulation has some
> >>security issues.
> >-v?
> 
> With the secret+cred exchange, the current implementation is IMO reasonably
> safe. The client cannot connect without access to the socket file.
> 
> Nasty detail: At least postfix sets the all AF_UNIX sockets to rw-rw-rw- and
> relies only on directory permissions (private: rwx------, public: rwx--x---)
> for access control. This is not effective on Cygwin. Due to the rw-rw-rw-,
> the 'secret' is world readable on Cygwin and another Cygwin specific patch
> is required :-)

Yeah, thanks to Windows which enables the "Bypass Traverse checking"
privilege for everyone :(  At one point in 2005 I toyed with traverse
checking but eventually gave up in 2006 and reverted the stuff.

I'm still once in a while dreaming of how to enable traverse checking
again, in a limited fashion only affecting Cygwin processes.  Combined
with a mount flag, maybe.

But then again, if it is configurable by the user, and the user and
admin will expect this, and the default expectation will be that it's
switched off, the applications will have to adapt to bypassed traverse
checking anyway :-P

> After new setsockopt(sd, ., SO_PEERCRED, .), AF_UNIX sockets are definitely
> vulnerable. Any local process could "guess" the TCP port and connect to any
> emulated AF_UNIX server regardless of user account.
> 
> 
> Two draft ideas for a new AF_UNIX emulation:
> 
> 1)
> Keep the current secret+cred exchange, but handle accept() and connect()
> differently:
> 
> After actual accept():
> 
> if (! recv client secret+cred)
>   return abort_connection();
> send(server secret+cred);
> set_state(connected).
> 
> 
> After actual connect():
> 
> send(client secret+cred);
> set_state(connected_but_secret_missing)
> 
> 
> Before actual recv() and getpeerid():
> 
> if (state == connected_but_secret_missing) {
>   if (! recv server secret+cred)
>     return abort_connection()
>   set_state(connected)
> }
> 
> 
> Before actual send(): Do nothing special.

Yeah, both peers simply send their stuff and expect the same from
the peer, thus it's not actually a handshake but just an exchange
of information.

On the plus side, it's pretty unlikely that a non-Cygwin process is
*expecting* a Cygwin process on the other end and as such, the info
exchange is unexpected.

> Secrets should be different such that knowledge of client secret (send
> unconditionally) does not expose the server secret.

How so?  The binding server creates the file, the secret is in the file.
Only a process which knows that this socket is connected to this file's
content would know how to fetch the secret, a malicious non-Cygwin
client wouldn't know where to look for this info.

> In contrast to my first more 'symmetric' approach, this should support
> 'client send() before server accept()'. Could not test it with postfix yet.
> 
> Unfortunately this leaves one security issue open: Client may send
> confidential data to malicious server if original server died. The client
> will recognize it too late in first receive.

In theory, a malicious server could wait for the client package and
read the content, thus it knows the socket secret and send its own
package with the secret gained from the client.

> 2)
> Don't exchange anything over TCP. Rely on a connection table in the socket
> file. Use 'bind before connect()' to avoid races.
> 
> local_bind:
> 
> bind(localhost:0);
> getsockname(&server_port);
> 
> create_socket_file(path);
> lock_socket_file(path) {
>     socket_file.slot[0] = (server_port, my_creds);
> }
> 
> 
> local_connect:
> 
> bind(localhost:0)
> getsockname(&client_port);
> 
> lock_socket_file(path) {
>   server_port = socket_file.slot[0].port;
>   peer_creds = socket_file.slot[0].credentials;
>   i = find_free_slot();
>   socket_file.slot[i] = (client_port, my_creds, timestamp);
> }
> 
> return connect(localhost:serverport);
> 
> 
> local_accept:
> 
> accept(&client_port);
> 
> lock_socket_file(path) {
>   i = find_slot(client_port));
>   peer_creds = socket_file.slot[i].credentials;
> }
> 
> 
> Problem: There is no real proof that the TCP peer is the actual peer listed
> in the file.

Right.

Btw., considering my change to call the connect side of the handshake
only when an FD_CONNECT arrives, what exactly is postfix still missing?

The connect call itself doesn't hang anymore, and the FD_CONNECT
handshake is independent of the actually requested events (FD_READ,
FD_WRITE, etc).  So, right now I don't understand why postfix would
still need to switch off the handshake.

Independently of that, I'm mulling over the idea to introduce a
sidechannel via pipe.  Pipes can be used to fetch the windows PID of the
peer (GetNamedPipeClientProcessId, GetNamedPipeServerProcessId), and the
server can impersonate the client and thus fetch the user and group
information from the client's token.  The client would get the uid/gid
info back from the server via the pipe or, even more secure, via
CreateRemoteThread.

I'm still somewhat fuzzy about the details, so for now it's just
wild brainstorming...


Corinna

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat

Attachment: pgpEJKGsyWTxd.pgp
Description: PGP signature


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]