aboutsummaryrefslogtreecommitdiffstats
path: root/src/assuan-socket.c
diff options
context:
space:
mode:
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);
}