aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--configure.ac2
-rw-r--r--doc/assuan.texi37
-rw-r--r--src/assuan-defs.h2
-rw-r--r--src/assuan-socket.c260
-rw-r--r--src/assuan.h.in2
-rw-r--r--src/libassuan.def1
-rw-r--r--src/libassuan.vers1
8 files changed, 286 insertions, 24 deletions
diff --git a/NEWS b/NEWS
index 326196d..a2611db 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,11 @@
Noteworthy changes in version 2.1.4 (unreleased) [C4/A4/R_]
------------------------------------------------
+ * Interface changes relative to the 2.1.3 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ assuan_sock_set_sockaddr_un NEW.
+
+
Noteworthy changes in version 2.1.3 (2014-11-07) [C4/A4/R3]
------------------------------------------------
diff --git a/configure.ac b/configure.ac
index d03fd58..2f09e98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -346,7 +346,7 @@ AM_PATH_GPG_ERROR(1.8,, AC_MSG_ERROR([libgpg-error was not found]))
#
# Checks for library functions.
#
-AC_CHECK_FUNCS([flockfile funlockfile inet_pton])
+AC_CHECK_FUNCS([flockfile funlockfile inet_pton stat])
# On some systems (e.g. Solaris) nanosleep requires linking to librl.
# Given that we use nanosleep only as an optimization over a select
diff --git a/doc/assuan.texi b/doc/assuan.texi
index ddac37a..b932be0 100644
--- a/doc/assuan.texi
+++ b/doc/assuan.texi
@@ -1935,7 +1935,10 @@ Wrapper around socket.
Wrapper around connect. For Unix domain sockets under Windows this
function also does a write immediately after the the connect to send the
-nonce as read from the socket's file.
+nonce as read from the socket's file. Under Unix this function check
+whether the socket file is a redirection file and connects to the
+redirected socket instead; see @code{assuan_sock_set_sockaddr_un} for
+details on the redirection file format.
@end deftypefun
@@ -1948,6 +1951,38 @@ Wrapper around bind. Under Windows this creates a file and writes the
port number and a random nonce to this file.
@end deftypefun
+@deftypefun int assuan_sock_set_sockaddr_un ( @
+ @w{const char *@var{fname}}, @
+ @w{struct sockaddr *@var{addr}}, @
+ @w{int *@var{r_redirected}})
+
+This is a helper function to initialize the Unix socket domain address
+structure @var{addr} and store the file name @var{fname} there. If
+@var{r_redirected} is not NULL the function checks whether @var{fname}
+already exists, is a regular file, and not a socket. In that case
+@var{fname} is read to see whether this is a redirection to a socket
+file. If that is the case 1 is stored at @var{r_redirected}. If the
+file does not look like a redirection file 0 will be stored there and
+@var{fname} will be used in the regular way.
+
+The format of a redirection file is
+
+@example
+%Assuan%
+socket=@var{name}
+@end example
+
+With @var{name} being is the actual socket to use. No white spaces
+are allowed, both lines must be terminated by a single linefeed, and
+extra lines are not allowed. Environment variables are interpreted in
+@var{name} if given in @code{$@{VAR@}} notation. No escape characters
+are defined; if the string @code{$@{} shall be used in file name, an
+environment variable with that content may be used. The length of the
+redirection file is limited to 511 bytes which is more than sufficient
+for any known implementation of Unix domain sockets.
+@end deftypefun
+
+
@deftypefun int assuan_sock_get_nonce ( @
@w{struct sockaddr *@var{addr}}, @
@w{int @var{addrlen}}, @
diff --git a/src/assuan-defs.h b/src/assuan-defs.h
index 364510f..3dec3df 100644
--- a/src/assuan-defs.h
+++ b/src/assuan-defs.h
@@ -346,6 +346,8 @@ int _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
struct sockaddr *addr, int addrlen);
int _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
struct sockaddr *addr, int addrlen);
+int _assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr,
+ int *r_redirected);
int _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr,
int addrlen, assuan_sock_nonce_t *nonce);
int _assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd,
diff --git a/src/assuan-socket.c b/src/assuan-socket.c
index 7eba6b4..c8af51b 100644
--- a/src/assuan-socket.c
+++ b/src/assuan-socket.c
@@ -69,6 +69,15 @@
#endif
#endif
+#ifndef ENAMETOOLONG
+# define ENAMETOOLONG EINVAL
+#endif
+
+#ifndef SUN_LEN
+# define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ + strlen ((ptr)->sun_path))
+#endif
+
#ifdef HAVE_W32_SYSTEM
@@ -88,7 +97,7 @@ utf8_to_wchar (const char *string)
return NULL;
nbytes = (size_t)(n+1) * sizeof(*result);
- if (nbytes / sizeof(*result) != (n+1))
+ if (nbytes / sizeof(*result) != (n+1))
{
SetLastError (ERROR_INVALID_PARAMETER);
return NULL;
@@ -173,15 +182,15 @@ _assuan_sock_wsa2errno (int err)
/* W32: Fill BUFFER with LENGTH bytes of random. Returns -1 on
failure, 0 on success. Sets errno on failure. */
static int
-get_nonce (char *buffer, size_t nbytes)
+get_nonce (char *buffer, size_t nbytes)
{
HCRYPTPROV prov;
int ret = -1;
- if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL,
+ if (!CryptAcquireContext (&prov, NULL, NULL, PROV_RSA_FULL,
(CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) )
gpg_err_set_errno (ENODEV);
- else
+ else
{
if (!CryptGenRandom (prov, nbytes, (unsigned char *) buffer))
gpg_err_set_errno (ENODEV);
@@ -235,9 +244,114 @@ read_port_and_nonce (const char *fname, unsigned short *port, char *nonce)
#endif /*HAVE_W32_SYSTEM*/
+#ifndef HAVE_W32_SYSTEM
+/* Find a redirected socket name for fname and return a malloced setup
+ filled sockaddr. If this does not work out NULL is returned and
+ ERRNO is set. If the file seems to be a redirect True is stored at
+ R_REDIRECT. Note that this function uses the standard malloc and
+ not the assuan wrapped one. The format of the file is:
+
+ %Assuan%
+ socket=NAME
+
+ where NAME is the actual socket to use. No white spaces are
+ allowed, both lines must be terminated by a single LF, extra lines
+ are not allowed. Environment variables are interpreted in NAME if
+ given in "${VAR} notation; no escape characters are defined, if
+ "${" shall be used verbatim, you need to use an environment
+ variable with that content.
+
+ The use of an absolute NAME is strongly suggested. The length of
+ the file is limited to 511 bytes which is more than sufficient for
+ that common value of 107 for sun_path. */
+static struct sockaddr_un *
+eval_redirection (const char *fname, int *r_redirect)
+{
+ FILE *fp;
+ char buffer[512], *name;
+ size_t n;
+ struct sockaddr_un *addr;
+ char *p, *pend;
+ const char *s;
+
+ *r_redirect = 0;
+
+ fp = fopen (fname, "rb");
+ if (!fp)
+ return NULL;
+ n = fread (buffer, 1, sizeof buffer - 1, fp);
+ fclose (fp);
+ if (!n)
+ {
+ gpg_err_set_errno (ENOENT);
+ return NULL;
+ }
+ buffer[n] = 0;
+ if (n < 17 || buffer[n-1] != '\n'
+ || memcmp (buffer, "%Assuan%\nsocket=", 16))
+ {
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+ }
+ buffer[n-1] = 0;
+ name = buffer + 16;
+
+ *r_redirect = 1;
+
+ addr = calloc (1, sizeof *addr);
+ if (!addr)
+ return NULL;
+ addr->sun_family = AF_LOCAL;
+
+ n = 0;
+ for (p=name; *p; p++)
+ {
+ if (*p == '$' && p[1] == '{')
+ {
+ p += 2;
+ pend = strchr (p, '}');
+ if (!pend)
+ {
+ free (addr);
+ gpg_err_set_errno (EINVAL);
+ return NULL;
+ }
+ *pend = 0;
+ if (*p && (s = getenv (p)))
+ {
+ for (; *s; s++)
+ {
+ if (n < sizeof addr->sun_path - 1)
+ addr->sun_path[n++] = *s;
+ else
+ {
+ free (addr);
+ gpg_err_set_errno (ENAMETOOLONG);
+ return NULL;
+ }
+ }
+ }
+ p = pend;
+ }
+ else if (n < sizeof addr->sun_path - 1)
+ addr->sun_path[n++] = *p;
+ else
+ {
+ free (addr);
+ gpg_err_set_errno (ENAMETOOLONG);
+ return NULL;
+ }
+ }
+
+ return addr;
+}
+#endif /*!HAVE_W32_SYSTEM*/
+
+
+
/* Return a new socket. Note that under W32 we consider a socket the
same as an System Handle; all functions using such a handle know
- about this dual use and act accordingly. */
+ about this dual use and act accordingly. */
assuan_fd_t
_assuan_sock_new (assuan_context_t ctx, int domain, int type, int proto)
{
@@ -265,21 +379,21 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
unsigned short port;
char nonce[16];
int ret;
-
+
unaddr = (struct sockaddr_un *)addr;
if (read_port_and_nonce (unaddr->sun_path, &port, nonce))
return -1;
-
+
myaddr.sin_family = AF_INET;
- myaddr.sin_port = htons (port);
+ myaddr.sin_port = htons (port);
myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
-
+
/* Set return values. */
unaddr->sun_family = myaddr.sin_family;
unaddr->sun_port = myaddr.sin_port;
unaddr->sun_addr.s_addr = myaddr.sin_addr.s_addr;
-
- ret = _assuan_connect (ctx, HANDLE2SOCKET(sockfd),
+
+ ret = _assuan_connect (ctx, HANDLE2SOCKET(sockfd),
(struct sockaddr *)&myaddr, sizeof myaddr);
if (!ret)
{
@@ -298,8 +412,40 @@ _assuan_sock_connect (assuan_context_t ctx, assuan_fd_t sockfd,
int res;
res = _assuan_connect (ctx, HANDLE2SOCKET (sockfd), addr, addrlen);
return res;
- }
+ }
#else
+# if HAVE_STAT
+ if (addr->sa_family == AF_LOCAL || addr->sa_family == AF_UNIX)
+ {
+ struct sockaddr_un *unaddr;
+ struct stat statbuf;
+ int redirect, res;
+
+ unaddr = (struct sockaddr_un *)addr;
+ if (!stat (unaddr->sun_path, &statbuf)
+ && !S_ISSOCK (statbuf.st_mode)
+ && S_ISREG (statbuf.st_mode))
+ {
+ /* The given socket file is not a socket but a regular file.
+ We use the content of that file to redirect to another
+ socket file. This can be used to use sockets on file
+ systems which do not support sockets or if for example a
+ home directory is shared by several machines. */
+ unaddr = eval_redirection (unaddr->sun_path, &redirect);
+ if (unaddr)
+ {
+ res = _assuan_connect (ctx, sockfd, (struct sockaddr *)unaddr,
+ SUN_LEN (unaddr));
+ free (unaddr);
+ return res;
+ }
+ if (redirect)
+ return -1;
+ /* Continue using the standard connect. */
+ }
+
+ }
+# endif /*HAVE_STAT*/
return _assuan_connect (ctx, sockfd, addr, addrlen);
#endif
}
@@ -330,7 +476,7 @@ _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- filehd = MyCreateFile (unaddr->sun_path,
+ filehd = MyCreateFile (unaddr->sun_path,
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
@@ -346,7 +492,7 @@ _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
rc = bind (HANDLE2SOCKET (sockfd), (struct sockaddr *)&myaddr, len);
if (!rc)
- rc = getsockname (HANDLE2SOCKET (sockfd),
+ rc = getsockname (HANDLE2SOCKET (sockfd),
(struct sockaddr *)&myaddr, &len);
if (rc)
{
@@ -360,7 +506,7 @@ _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
len = strlen (tmpbuf);
memcpy (tmpbuf+len, nonce,16);
len += 16;
-
+
if (!WriteFile (filehd, tmpbuf, len, &nwritten, NULL))
{
CloseHandle (filehd);
@@ -384,6 +530,69 @@ _assuan_sock_bind (assuan_context_t ctx, assuan_fd_t sockfd,
}
+/* Setup the ADDR structure for a Unix domain socket with the socket
+ name FNAME. If this is a redirected socket and R_REDIRECTED is not
+ NULL, it will be setup for the real socket. Returns 0 on success
+ and stores 1 at R_REDIRECTED if it is a redirected socket. On
+ error -1 is returned and ERRNO will be set. */
+int
+_assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr,
+ int *r_redirected)
+{
+ struct sockaddr_un *unaddr = (struct sockaddr_un *)addr;
+#if !defined(HAVE_W32_SYSTEM) && defined(HAVE_STAT)
+ struct stat statbuf;
+#endif
+
+ if (r_redirected)
+ *r_redirected = 0;
+
+#if !defined(HAVE_W32_SYSTEM) && defined(HAVE_STAT)
+ if (r_redirected
+ && !stat (fname, &statbuf)
+ && !S_ISSOCK (statbuf.st_mode)
+ && S_ISREG (statbuf.st_mode))
+ {
+ /* The given socket file is not a socket but a regular file. We
+ use the content of that file to redirect to another socket
+ file. This can be used to use sockets on file systems which
+ do not support sockets or if for example a home directory is
+ shared by several machines. */
+ struct sockaddr_un *unaddr_new;
+ int redirect;
+
+ unaddr_new = eval_redirection (fname, &redirect);
+ if (unaddr_new)
+ {
+ memcpy (unaddr, unaddr_new, sizeof *unaddr);
+ free (unaddr_new);
+ *r_redirected = 1;
+ return 0;
+ }
+ if (redirect)
+ {
+ *r_redirected = 1;
+ return -1; /* Error. */
+ }
+ /* Fallback to standard setup. */
+ }
+#endif /*!HAVE_W32_SYSTEM && HAVE_STAT*/
+
+ if (strlen (fname)+1 >= sizeof unaddr->sun_path)
+ {
+ gpg_err_set_errno (ENAMETOOLONG);
+ return -1;
+ }
+
+ memset (unaddr, 0, sizeof *unaddr);
+ unaddr->sun_family = AF_LOCAL;
+ strncpy (unaddr->sun_path, fname, sizeof unaddr->sun_path - 1);
+ unaddr->sun_path[sizeof unaddr->sun_path - 1] = 0;
+
+ return 0;
+}
+
+
int
_assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr,
int addrlen, assuan_sock_nonce_t *nonce)
@@ -416,8 +625,8 @@ _assuan_sock_get_nonce (assuan_context_t ctx, struct sockaddr *addr,
#endif
return 0;
}
-
-
+
+
int
_assuan_sock_check_nonce (assuan_context_t ctx, assuan_fd_t fd,
assuan_sock_nonce_t *nonce)
@@ -498,7 +707,7 @@ assuan_sock_init ()
return 0;
err = assuan_new (&sock_ctx);
-
+
#ifdef HAVE_W32_SYSTEM
if (! err)
WSAStartup (0x202, &wsadat);
@@ -521,7 +730,7 @@ assuan_sock_deinit ()
assuan_release (sock_ctx);
sock_ctx = NULL;
}
-
+
int
assuan_sock_close (assuan_fd_t fd)
@@ -529,7 +738,7 @@ assuan_sock_close (assuan_fd_t fd)
return _assuan_close (sock_ctx, fd);
}
-assuan_fd_t
+assuan_fd_t
assuan_sock_new (int domain, int type, int proto)
{
return _assuan_sock_new (sock_ctx, domain, type, proto);
@@ -548,9 +757,16 @@ assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen)
}
int
-assuan_sock_get_nonce (struct sockaddr *addr, int addrlen,
+assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr,
+ int *r_redirected)
+{
+ return _assuan_sock_set_sockaddr_un (fname, addr, r_redirected);
+}
+
+int
+assuan_sock_get_nonce (struct sockaddr *addr, int addrlen,
assuan_sock_nonce_t *nonce)
-{
+{
return _assuan_sock_get_nonce (sock_ctx, addr, addrlen, nonce);
}
diff --git a/src/assuan.h.in b/src/assuan.h.in
index d074a9f..f53c26e 100644
--- a/src/assuan.h.in
+++ b/src/assuan.h.in
@@ -470,6 +470,8 @@ assuan_fd_t assuan_sock_new (int domain, int type, int proto);
int assuan_sock_connect (assuan_fd_t sockfd,
struct sockaddr *addr, int addrlen);
int assuan_sock_bind (assuan_fd_t sockfd, struct sockaddr *addr, int addrlen);
+int assuan_sock_set_sockaddr_un (const char *fname, struct sockaddr *addr,
+ int *r_redirected);
int assuan_sock_get_nonce (struct sockaddr *addr, int addrlen,
assuan_sock_nonce_t *nonce);
int assuan_sock_check_nonce (assuan_fd_t fd, assuan_sock_nonce_t *nonce);
diff --git a/src/libassuan.def b/src/libassuan.def
index 5636bfc..d9997e3 100644
--- a/src/libassuan.def
+++ b/src/libassuan.def
@@ -111,6 +111,7 @@ EXPORTS
__assuan_sendmsg @90
__assuan_waitpid @91
assuan_check_version @92
+ assuan_sock_set_sockaddr_un @93
; END
diff --git a/src/libassuan.vers b/src/libassuan.vers
index 700c68c..e36c322 100644
--- a/src/libassuan.vers
+++ b/src/libassuan.vers
@@ -101,6 +101,7 @@ LIBASSUAN_1.0 {
assuan_free;
assuan_socket_connect_fd;
assuan_check_version;
+ assuan_sock_set_sockaddr_un;
__assuan_close;
__assuan_pipe;