What mechanism is used by MSYS/Cygwin to emulate Unix domain sockets?
Asked Answered
X

3

17

I'm attempting to write (in C#) a piece of software that communicates with another piece of software, built with MSYS, over (MSYS emulated) Unix domain sockets. I've learned that the "socket server" (I'm not clear on what the proper terminology is) creates a temporary file with contents such as this:

!<socket >59108 282F93E1-9E2D051A-46B57EFC-64A1852F

The 59108 corresponds to a TCP port, which the "socket server" is listening on on the loopback interface. Using a packet capture tool, I've been able to determine that the "socket client" connects to this port, and information is exchanged over the loopback interface.

I replicated this behavior in my software, and the "socket client" connects to my listening port, but no information is transmitted. I believe there's another step here, one most likely involving the GUID in the "socket" file, but I've been unable to determine what it is. What do I need to do to trigger the communication from the client?

It seems that MSYS is using Cygwin's mechanism, which involves a named event, that is (probably?) created by the "server", and signaled (apparently) by the "server", but my naive attempt at an implementation doesn't seem to be working.

I've located an email written by Conrad Scott which describes various shortcomings in the "handshaking" process, and proposes a patch which allegedly solves them. In this email, Conrad describes somewhat the process used, and he indicates that there are actually TWO events, one managed by the "server" and one managed by the "client". I've used API Monitor to look for calls to CreateEvent(), and while there are several, I cannot find one that looks like the "smoking gun" here. There are no interesting calls to CreateSemaphore() either, so it seems like Conrad's patch was never applied (or, at least, it was applied some time AFTER MSYS forked Cygwin).

Xyloid answered 15/4, 2014 at 14:3 Comment(0)
B
10

It appears that both the answers from divB and Mark are correct, but they both leave out some details, so this is hopefully a bit more complete.

There are 2 different implementations here. I have not done an exhaustive investigation of who implements which implementation, but as of this writing, the current version of cygwin uses the implementation described by divB and MsysGit uses the implementation described by Mark.

Initializing the server:

  1. Create a socket (AddressFamily = IPv4, Type = Stream, Protocol = TCP). (.NET/MFC)

  2. Bind it to loopback (127.0.0.1). (.NET/MFC)

  3. Tell the socket to listen. (.NET/MFC)

  4. Generate a random 16-byte GUID.

  5. Create a file with the following contents based on the TCP port and the GUID. Using the original example where 59108 is the TCP port and 282F93E1-9E2D051A-46B57EFC-64A1852F is the GUID.

    In the cygwin implementation, the socket file contents are:

    !<socket >59108 s 282F93E1-9E2D051A-46B57EFC-64A1852F
    

    And in the msysgit implementation, the socket file contents are:

    !<socket >59108 282F93E1-9E2D051A-46B57EFC-64A1852F
    

    The difference being the extra "s" between the port and the GUID.

  6. Set the System attribute on this file.

  7. In msysgit implementation only, Create a named wait handle with the name cygwin.local_socket.secret.58598.282F93E1-9E2D051A-46B57EFC-64A1852F (InitalState = False, Reset = AutoReset). (.NET/MFC)

    58598 is derived by using a HostToNetworkOrder function on the port (as 16-bit unsigned integer). i.e. 59108 == 0xE6E4 and 58598 == 0xE4E6.

Handling connections:

  1. Accept the incoming socket. (.NET/MFC).
  2. In the case of the cygwin implementation only, do handshake that consists of:
    1. Read 16 bytes. If these do not match the GUID, then fail.
    2. Send the same 16 bytes.
    3. Read 12 bytes as 3 32-bit integers. They are the pid, uid and gid of the calling process.
    4. Send 12 bytes (3 32-bit integers) back. Use the pid of the server and the uid and gid that were received.
  3. In the case of the msysgit implementation only, synchronize with the client by:
    1. Get the port of the incoming socket. For this example, we'll say it is 63524.
    2. Open existing wait handle for the client. (.NET/MFC). You need to convert the port to network byte order just like we did for the server. So, for this example, the name is cygwin.local_socket.secret.9464.282F93E1-9E2D051A-46B57EFC-64A1852F
    3. Signal the server and wait for the client (ToSignal = server, WaitOn = client, Timeout = 10000 msec, ExitContext/Alertable = False). (.NET/MFC). Not 100% sure about the ExitContext/Alertable parameter, but False seems to work.
  4. Hand off the socket to the (hopefully already existing) code for whatever it is you are doing (which in the case of all three of us, seems to be an ssh agent).
Berylberyle answered 13/10, 2014 at 21:22 Comment(2)
According to cygwin.com/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=winsup/… the seq of process credentials would be "pid, uid, gid" and not "pid, gid, uid" as specified here, in step 2.3 and 2.4Cordiform
For the interested parties, I wrote a successful implementation in Go here: github.com/abourget/secrets-bridgeCordiform
P
7

So at least for cygwin I can answer your question now: I just implemented a cygwin compatible socket server using MFC. I did it by looking into cygwin source. It seems that there are not even events. So the patch you mentioned does not seem to have been implemented.

All that happens is:

1.) The socket file is created, the GUID ("shared key") are just random numbers. 2.) The file MUST have "system" attribute. The cygwin code does some weird permission stuff if it's on NTFS, haven't looked into that. 3.) a network socket on localhost is created with the port indicated in the socket file.

So, then, when a client connects to the socket (via TCP/IP):

4.) It first sends the 4 random numbers to the server; the server checks if they are valid 5.) The server sends them back 6.) The client sends 3 32 bit numbers: The pid, the uid and gid 7.) The server sends back his own version of these numbers.

I don't understand what's the purpose of this handshake because from a security point of view it's completely worthless.

Phonography answered 10/5, 2014 at 9:47 Comment(1)
JFYI: That's also true for MSYS, my socket server (an SSH agent) works for both the cygwin and the MSYS ssh/rsync clients.Phonography
X
6

I've worked out something that functions correctly for the build of OpenSSH (ssh-agent.exe) that comes with Git:

Setup on the server side consists of these steps: 1. Create a "secret string" that consists of four groups of eight hex digits separated by a dash ("-") 2. Listen on a local port 3. Create an EventWaitHandle with mode EventResetMode.AutoReset named cygwin.local_socket.secret.[secret string].[listen port number here with byte order reversed] 4. Write out the "socket" file, which consists of the string ![port number here, byte order NOT reversed] [secret string]

When a connection comes in, the following steps must be undertaken: 1. Open the client's event handle with EventWaitHandle.OpenExisting(), using the event name cygwin.local_socket.secret.[remote port number with byte order reversed].[secret string] 2. Signal the server's event handle and wait for the client's wait handle to be signaled with `EventWaitHandle.SignalAndWait()

I agree that it looks like the patch discussed on the mailing list was never applied. The sequence I worked out seems closer to the sequence discussed on that list, and as well, it matches the code I dug up from Cygwin.

I don't understand the disparity between what I found to work vs what divB found to work, but I did confirm that it functioned with the software I was using (Git's OpenSSH)

Xyloid answered 21/5, 2014 at 20:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.