summaryrefslogtreecommitdiff
path: root/src/assuan-socket.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2014-11-28 16:41:16 +0100
committerWerner Koch <wk@gnupg.org>2014-11-28 16:41:16 +0100
commit1f99031cb55e88840d98dd71381b2bc2618406fc (patch)
tree3c8ede8dea4c583bd48055c0b6476e8aa8ef8451 /src/assuan-socket.c
parent2f040c0b7bcfdddf5e3597fbcdae9cc04fafe653 (diff)
downloadlibassuan-1f99031cb55e88840d98dd71381b2bc2618406fc.tar.gz
Implement socket file redirection.
* configure.ac (AC_CHECK_FUNC): Check for stat. * src/assuan-socket.c (SUN_LEN): Add. (eval_redirection): New. (_assuan_sock_connect) [!W32]: Implement socket file redirection. (_assuan_sock_set_sockaddr_un): New. (assuan_sock_set_sockaddr_un): New. -- This feature is useful to allow sockets with standard names even on file system which do not supports socket and to help with shared home directories. For example to use GnuPG 2.1 with shared home directories one will be abale to do: rm ~/.gnupg/S.gpg-agent || true printf "%%Assuan%%\nsocket=${HOME}/.gnupg/S.gpg-agent_${HOSTNAME}\n" \ > ~/.gnupg/S.gpg-agent Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'src/assuan-socket.c')
-rw-r--r--src/assuan-socket.c260
1 files changed, 238 insertions, 22 deletions
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);
}