diff options
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | doc/assuan.texi | 37 | ||||
-rw-r--r-- | src/assuan-defs.h | 2 | ||||
-rw-r--r-- | src/assuan-socket.c | 260 | ||||
-rw-r--r-- | src/assuan.h.in | 2 | ||||
-rw-r--r-- | src/libassuan.def | 1 | ||||
-rw-r--r-- | src/libassuan.vers | 1 |
8 files changed, 286 insertions, 24 deletions
@@ -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; |