summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2015-06-30 16:24:52 +0200
committerWerner Koch <wk@gnupg.org>2015-08-28 14:27:58 +0200
commit6d4a8ee2a6c749eec70bd3ae804f21456e375727 (patch)
tree8fff6038643ad4e3def0420d72084aec57787bb9
parent5a52404c704d0e99629a2db79dda17e3b95c9680 (diff)
downloadlibassuan-6d4a8ee2a6c749eec70bd3ae804f21456e375727.tar.gz
Support Cygwin local sockets.
* src/assuan-socket.c (cygwin_fdtable, cygwin_fdtable_cs): New. (is_cygwin_fd, insert_cygwin_fd, delete_cygwin_fd): New. (assuan_sock_init) [W32]: Init the CS. (assuan_sock_deinit) [W32]: Deinit the CS. (read_port_and_nonce): Add arg cygwin and detect Cygwin socket files. (_assuan_sock_set_flag): Add "cygwin" flag. (_assuan_sock_get_flag): Ditto. (do_readn, do_writen): New. (_assuan_sock_bind): Create a Cygwin socket file depending on a socket flag. (_assuan_sock_connect): Handle the cygwin socket protocol. (_assuan_sock_check_nonce): Ditto. -- This code has not been tested. Signed-off-by: Werner Koch <wk@gnupg.org>
-rw-r--r--NEWS2
-rw-r--r--doc/assuan.texi14
-rw-r--r--src/assuan-socket.c341
3 files changed, 299 insertions, 58 deletions
diff --git a/NEWS b/NEWS
index 8007b29..a851b6a 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,8 @@
Noteworthy changes in version 2.3.0 (unreleased) [C5/A5/R_]
------------------------------------------------
+ * Support Cygwin's local sockets.
+
* Interface changes relative to the 2.2.1 release:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
assuan_sock_set_flag NEW.
diff --git a/doc/assuan.texi b/doc/assuan.texi
index 14f2cf0..0a49d71 100644
--- a/doc/assuan.texi
+++ b/doc/assuan.texi
@@ -2069,7 +2069,19 @@ Store the current value of the flag @var{name} for socket @var{fd} at
success; on failure sets ERRNO and returns -1.
@end deftypefun
-No flags are defined.
+The supported flags are:
+
+@table @code
+@item cygwin
+This flag has an effect only on Windows. If the value is 1, the
+socket is set into Cygwin mode so that Cygwin clients can connect to
+such a socket. This flag needs to be set before a bind and should not
+be changed during the lifetime of the socket. There is no need to set
+this flag for connecting to a Cygwin style socket because no state is
+required at the client. On non-Windows platforms setting this flag is
+ignored, reading the flag always returns a value of 0.
+
+@end table
@c ---------------------------------------------------------------------
diff --git a/src/assuan-socket.c b/src/assuan-socket.c
index 7bca0ae..ae90802 100644
--- a/src/assuan-socket.c
+++ b/src/assuan-socket.c
@@ -88,6 +88,87 @@ static assuan_context_t sock_ctx;
#ifdef HAVE_W32_SYSTEM
+/* A table of active Cygwin connections. This is only used for
+ listening socket which should be only a few. We do not enter
+ sockets after a connect into this table. */
+static assuan_fd_t cygwin_fdtable[16];
+/* A critical section to guard access to the table of Cygwin
+ connections. */
+static CRITICAL_SECTION cygwin_fdtable_cs;
+
+
+/* Return true if SOCKFD is listed as Cygwin socket. */
+static int
+is_cygwin_fd (assuan_fd_t sockfd)
+{
+ int ret = 0;
+ int i;
+
+ EnterCriticalSection (&cygwin_fdtable_cs);
+ for (i=0; i < DIM(cygwin_fdtable); i++)
+ {
+ if (cygwin_fdtable[i] == sockfd)
+ {
+ ret = 1;
+ break;
+ }
+ }
+ LeaveCriticalSection (&cygwin_fdtable_cs);
+ return ret;
+}
+
+
+/* Insert SOCKFD into the table of Cygwin sockets. Return 0 on
+ success or -1 on error. */
+static int
+insert_cygwin_fd (assuan_fd_t sockfd)
+{
+ int ret = 0;
+ int mark = -1;
+ int i;
+
+ EnterCriticalSection (&cygwin_fdtable_cs);
+
+ for (i=0; i < DIM(cygwin_fdtable); i++)
+ {
+ if (cygwin_fdtable[i] == sockfd)
+ goto leave; /* Already in table. */
+ else if (cygwin_fdtable[i] == ASSUAN_INVALID_FD)
+ mark = i;
+ }
+ if (mark == -1)
+ {
+ gpg_err_set_errno (EMFILE);
+ ret = -1;
+ }
+ else
+ cygwin_fdtable[mark] = sockfd;
+
+ leave:
+ LeaveCriticalSection (&cygwin_fdtable_cs);
+ return ret;
+}
+
+
+/* Delete SOCKFD from the table of Cygwin sockets. */
+static void
+delete_cygwin_fd (assuan_fd_t sockfd)
+{
+ int i;
+
+ EnterCriticalSection (&cygwin_fdtable_cs);
+ for (i=0; i < DIM(cygwin_fdtable); i++)
+ {
+ if (cygwin_fdtable[i] == sockfd)
+ {
+ cygwin_fdtable[i] = ASSUAN_INVALID_FD;
+ break;
+ }
+ }
+ LeaveCriticalSection (&cygwin_fdtable_cs);
+ return;
+}
+
#ifdef HAVE_W32CE_SYSTEM
static wchar_t *
@@ -210,16 +291,19 @@ get_nonce (char *buffer, size_t nbytes)
}
-/* W32: The buffer for NONCE needs to be at least 16 bytes. Returns 0 on
- success and sets errno on failure. */
+/* W32: The buffer for NONCE needs to be at least 16 bytes. Returns 0
+ on success and sets errno on failure. If FNAME has a Cygwin socket
+ descriptor True is stored at CYGWIN. */
static int
-read_port_and_nonce (const char *fname, unsigned short *port, char *nonce)
+read_port_and_nonce (const char *fname, unsigned short *port, char *nonce,
+ int *cygwin)
{
FILE *fp;
char buffer[50], *p;
size_t nread;
int aval;
+ *cygwin = 0;
fp = fopen (fname, "rb");
if (!fp)
return -1;
@@ -231,22 +315,52 @@ read_port_and_nonce (const char *fname, unsigned short *port, char *nonce)
return -1;
}
buffer[nread] = 0;
- aval = atoi (buffer);
- if (aval < 1 || aval > 65535)
+ if (!strncmp (buffer, "!<socket >", 10))
{
- gpg_err_set_errno (EINVAL);
- return -1;
+ /* This is the Cygwin compatible socket emulation. The format
+ * of the file is:
+ *
+ * "!<socket >%u %c %08x-%08x-%08x-%08x\x00"
+ *
+ * %d for port number, %c for kind of socket (s for STREAM), and
+ * we have 16-byte random bytes for nonce. We only support
+ * stream mode.
+ */
+ unsigned int u0;
+ int narr[4];
+
+ if (sscanf (buffer+10, "%u s %08x-%08x-%08x-%08x",
+ &u0, narr+0, narr+1, narr+2, narr+3) != 5
+ || u0 < 1 || u0 > 65535)
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ *port = u0;
+ memcpy (nonce, narr, 16);
+ *cygwin = 1;
}
- *port = (unsigned int)aval;
- for (p=buffer; nread && *p != '\n'; p++, nread--)
- ;
- if (*p != '\n' || nread != 17)
+ else
{
- gpg_err_set_errno (EINVAL);
- return -1;
+ /* This is our own socket emulation. */
+ aval = atoi (buffer);
+ if (aval < 1 || aval > 65535)
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ *port = (unsigned int)aval;
+ for (p=buffer; nread && *p != '\n'; p++, nread--)
+ ;
+ if (*p != '\n' || nread != 17)
+ {
+ gpg_err_set_errno (EINVAL);
+ return -1;
+ }
+ p++; nread--;
+ memcpy (nonce, p, 16);
}
- p++; nread--;
- memcpy (nonce, p, 16);
+
return 0;
}
#endif /*HAVE_W32_SYSTEM*/
@@ -386,8 +500,16 @@ int
_assuan_sock_set_flag (assuan_context_t ctx, assuan_fd_t sockfd,
const char *name, int value)
{
- if (0)
+ if (!strcmp (name, "cygwin"))
{
+#ifdef HAVE_W32_SYSTEM
+ if (!value)
+ delete_cygwin_fd (sockfd);
+ else if (insert_cygwin_fd (sockfd))
+ return -1;
+#else
+ /* Setting the Cygwin flag on non-Windows is ignored. */
+#endif
}
else
{
@@ -405,8 +527,13 @@ _assuan_sock_get_flag (assuan_context_t ctx, assuan_fd_t sockfd,
{
(void)ctx;
- if (0)
+ if (!strcmp (name, "cygwin"))
{
+#ifdef HAVE_W32_SYSTEM
+ *r_value = is_cygwin_fd (sockfd);
+#else
+ *r_value = 0;
+#endif
}
else
{
@@ -418,6 +545,62 @@ _assuan_sock_get_flag (assuan_context_t ctx, assuan_fd_t sockfd,
}
+/* Read NBYTES from SOCKFD into BUFFER. Return 0 on success. Handle
+ EAGAIN and EINTR. */
+#ifdef HAVE_W32_SYSTEM
+static int
+do_readn (assuan_context_t ctx, assuan_fd_t sockfd,
+ void *buffer, size_t nbytes)
+{
+ char *p = buffer;
+ size_t n;
+
+ while (nbytes)
+ {
+ n = _assuan_read (ctx, sockfd, p, nbytes);
+ if (n < 0 && errno == EINTR)
+ ;
+ else if (n < 0 && errno == EAGAIN)
+ Sleep (100);
+ else if (n < 0)
+ return -1;
+ else if (!n)
+ {
+ gpg_err_set_errno (EIO);
+ return -1;
+ }
+ else
+ {
+ p += n;
+ nbytes -= n;
+ }
+ }
+ return 0;
+}
+
+
+/* Write NBYTES from BUFFER to SOCKFD. Return 0 on success; on error
+ return -1 and set ERRNO. */
+static int
+do_writen (assuan_context_t ctx, assuan_fd_t sockfd,
+ const void *buffer, size_t nbytes)
+{
+ int ret;
+
+ ret = _assuan_write (ctx, sockfd, buffer, nbytes);
+ if (ret >= 0 && ret != nbytes)
+ {
+ gpg_err_set_errno (EIO);
+ ret = -1;
+ }
+ else if (ret >= 0)
+ ret = 0;
+
+ return ret;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
int
_assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
struct sockaddr *addr, int addrlen)
@@ -429,10 +612,11 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
struct sockaddr_un *unaddr;
unsigned short port;
char nonce[16];
+ int cygwin;
int ret;
unaddr = (struct sockaddr_un *)addr;
- if (read_port_and_nonce (unaddr->sun_path, &port, nonce))
+ if (read_port_and_nonce (unaddr->sun_path, &port, nonce, &cygwin))
return -1;
myaddr.sin_family = AF_INET;
@@ -449,20 +633,36 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
if (!ret)
{
/* Send the nonce. */
- ret = _assuan_write (ctx, sockfd, nonce, 16);
- if (ret >= 0 && ret != 16)
+ ret = do_writen (ctx, sockfd, nonce, 16);
+ if (!ret && cygwin)
{
- gpg_err_set_errno (EIO);
- ret = -1;
+ char buffer[16];
+
+ /* The client sends the nonce back - not useful. We do
+ a dummy read. */
+ ret = do_readn (ctx, sockfd, buffer, 16);
+ if (!ret)
+ {
+ /* Send our credentials. */
+ int n = getpid ();
+ memcpy (buffer, &n, 4);
+ memset (buffer+4, 0, 4); /* uid = gid = 0 */
+ ret = do_writen (ctx, sockfd, buffer, 8);
+ if (!ret)
+ {
+ /* Receive credentials. We don't need them. */
+ ret = do_readn (ctx, sockfd, buffer, 8);
+ }
+ }
}
}
return ret;
}
else
{
- int res;
- res = _assuan_connect (ctx, HANDLE2SOCKET (sockfd), addr, addrlen);
- return res;
+ int ret;
+ ret = _assuan_connect (ctx, HANDLE2SOCKET (sockfd), addr, addrlen);
+ return ret;
}
#else
# if HAVE_STAT
@@ -514,11 +714,14 @@ _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
HANDLE filehd;
int len = sizeof myaddr;
int rc;
- char nonce[16];
- char tmpbuf[33+16];
+ union {
+ char data[16];
+ int aint[4];
+ } nonce;
+ char tmpbuf[50+16];
DWORD nwritten;
- if (get_nonce (nonce, 16))
+ if (get_nonce (nonce.data, 16))
return -1;
unaddr = (struct sockaddr_un *)addr;
@@ -553,10 +756,22 @@ _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
gpg_err_set_errno (save_e);
return rc;
}
- snprintf (tmpbuf, sizeof tmpbuf, "%d\n", ntohs (myaddr.sin_port));
- len = strlen (tmpbuf);
- memcpy (tmpbuf+len, nonce,16);
- len += 16;
+
+ if (is_cygwin_fd (sockfd))
+ {
+ snprintf (tmpbuf, sizeof tmpbuf,
+ "!<socket >%d s %08x-%08x-%08x-%08x",
+ ntohs (myaddr.sin_port),
+ nonce.aint[0], nonce.aint[1], nonce.aint[2], nonce.aint[3]);
+ len = strlen (tmpbuf) + 1;
+ }
+ else
+ {
+ snprintf (tmpbuf, sizeof tmpbuf-16, "%d\n", ntohs (myaddr.sin_port));
+ len = strlen (tmpbuf);
+ memcpy (tmpbuf+len, nonce.data,16);
+ len += 16;
+ }
if (!WriteFile (filehd, tmpbuf, len, &nwritten, NULL))
{
@@ -653,6 +868,7 @@ _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr,
{
struct sockaddr_un *unaddr;
unsigned short port;
+ int dummy;
if (sizeof nonce->nonce != 16)
{
@@ -661,7 +877,7 @@ _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr,
}
nonce->length = 16;
unaddr = (struct sockaddr_un *)addr;
- if (read_port_and_nonce (unaddr->sun_path, &port, nonce->nonce))
+ if (read_port_and_nonce (unaddr->sun_path, &port, nonce->nonce, &dummy))
return -1;
}
else
@@ -683,8 +899,7 @@ _assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd,
assuan_sock_nonce_t *nonce)
{
#ifdef HAVE_W32_SYSTEM
- char buffer[16], *p;
- size_t nleft;
+ char buffer[16];
int n;
if (sizeof nonce->nonce != 16)
@@ -702,33 +917,33 @@ _assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd,
return -1;
}
- p = buffer;
- nleft = 16;
- while (nleft)
- {
- n = _assuan_read (ctx, SOCKET2HANDLE(fd), p, nleft);
- if (n < 0 && errno == EINTR)
- ;
- else if (n < 0 && errno == EAGAIN)
- Sleep (100);
- else if (n < 0)
- return -1;
- else if (!n)
- {
- gpg_err_set_errno (EIO);
- return -1;
- }
- else
- {
- p += n;
- nleft -= n;
- }
- }
+ if (do_readn (ctx, fd, buffer, 16))
+ return -1;
if (memcmp (buffer, nonce->nonce, 16))
{
gpg_err_set_errno (EACCES);
return -1;
}
+ if (is_cygwin_fd (fd))
+ {
+ /* Send the nonce back to the client. */
+ if (do_writen (ctx, fd, buffer, 16))
+ return -1;
+ /* Read the credentials. Cygwin uses the
+ struct ucred { pid_t pid; uid_t uid; gid_t gid; };
+ with pid_t being an int (4 bytes) and uid_t and gid_t being
+ shorts (2 bytes). Thus we need to read 8 bytes. However we
+ we ignore the values because they are not kernel controlled. */
+ if (do_readn (ctx, fd, buffer, 8))
+ return -1;
+ /* Send our credentials: We use the uid and gid we received but
+ our own pid. */
+ n = getpid ();
+ memcpy (buffer, &n, 4);
+ if (do_writen (ctx, fd, buffer, 8))
+ return -1;
+ }
+
#else
(void)fd;
(void)nonce;
@@ -750,6 +965,10 @@ assuan_sock_init ()
if (sock_ctx != NULL)
return 0;
+#ifdef HAVE_W32_SYSTEM
+ InitializeCriticalSection (&cygwin_fdtable_cs);
+#endif
+
err = assuan_new (&sock_ctx);
#ifdef HAVE_W32_SYSTEM
@@ -773,12 +992,20 @@ assuan_sock_deinit ()
assuan_release (sock_ctx);
sock_ctx = NULL;
+
+#ifdef HAVE_W32_SYSTEM
+ DeleteCriticalSection (&cygwin_fdtable_cs);
+#endif
}
int
assuan_sock_close (assuan_fd_t fd)
{
+#ifdef HAVE_W32_SYSTEM
+ if (fd != ASSUAN_INVALID_FD)
+ delete_cygwin_fd (fd);
+#endif
return _assuan_close (sock_ctx, fd);
}