diff options
Diffstat (limited to 'src/assuan-socket.c')
-rw-r--r-- | src/assuan-socket.c | 260 |
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); } |