diff --git a/src/assuan-socket-connect.c b/src/assuan-socket-connect.c index 019a41c..457edfe 100644 --- a/src/assuan-socket-connect.c +++ b/src/assuan-socket-connect.c @@ -1,349 +1,373 @@ /* assuan-socket-connect.c - Assuan socket based client * Copyright (C) 2002, 2003, 2004, 2009 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #include #include #include #include #include #include #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #else # include # include # include # include #endif #include "assuan-defs.h" #include "debug.h" /* Hacks for Slowaris. */ #ifndef PF_LOCAL # ifdef PF_UNIX # define PF_LOCAL PF_UNIX # else # define PF_LOCAL AF_UNIX # endif #endif #ifndef AF_LOCAL # define AF_LOCAL AF_UNIX #endif #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long)(-1)) #endif /*INADDR_NONE*/ #ifndef SUN_LEN # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif #undef WITH_IPV6 #if defined (AF_INET6) && defined(PF_INET) \ && defined (INET6_ADDRSTRLEN) && defined(HAVE_INET_PTON) # define WITH_IPV6 1 #endif /* Returns true if STR represents a valid port number in decimal * notation and no garbage is following. */ static int parse_portno (const char *str, uint16_t *r_port) { unsigned int value; for (value=0; *str && (*str >= '0' && *str <= '9'); str++) { value = value * 10 + (*str - '0'); if (value > 65535) return 0; } if (*str || !value) return 0; *r_port = value; return 1; } static gpg_error_t _assuan_connect_finalize (assuan_context_t ctx, assuan_fd_t fd, unsigned int flags) { gpg_error_t err; ctx->engine.release = _assuan_client_release; ctx->engine.readfnc = _assuan_simple_read; ctx->engine.writefnc = _assuan_simple_write; ctx->engine.sendfd = NULL; ctx->engine.receivefd = NULL; ctx->finish_handler = _assuan_client_finish; ctx->inbound.fd = fd; ctx->outbound.fd = fd; ctx->max_accepts = -1; ctx->flags.is_socket = 1; +#ifdef HAVE_W32_SYSTEM + ctx->engine.sendfd = w32_fdpass_send; +#else if (flags & ASSUAN_SOCKET_CONNECT_FDPASSING) _assuan_init_uds_io (ctx); +#endif /* initial handshake */ { assuan_response_t response; int off; err = _assuan_read_from_server (ctx, &response, &off, 0); if (err) TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s\n", gpg_strerror (err)); - else if (response != ASSUAN_RESPONSE_OK) + else if (response == ASSUAN_RESPONSE_OK) + { + const char *line = ctx->inbound.line + off; + int pid = ASSUAN_INVALID_PID; + + /* Parse the message: OK ..., process %i */ + line = strchr (line, ','); + if (line) + { + line = strchr (line + 1, ' '); + if (line) + { + line = strchr (line + 1, ' '); + if (line) + pid = atoi (line + 1); + } + } + if (pid != ASSUAN_INVALID_PID) + ctx->pid = pid; + } + else { char *sname = _assuan_encode_c_string (ctx, ctx->inbound.line); if (sname) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to server: %s", sname); _assuan_free (ctx, sname); } err = _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } } return err; } /* Attach an existing connected file descriptor FD to an allocated handle CTX * and initialize the connection. */ gpg_error_t assuan_socket_connect_fd (assuan_context_t ctx, assuan_fd_t fd, unsigned int flags) { gpg_error_t err; if (!ctx) return GPG_ERR_INV_ARG; if (fd == ASSUAN_INVALID_FD) return GPG_ERR_INV_ARG; err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; } /* Make a connection to the Unix domain socket NAME and return a new * Assuan context in CTX. SERVER_PID is currently not used but may * become handy in the future. Defined flag bits are: * * ASSUAN_SOCKET_CONNECT_FDPASSING * sendmsg and recvmsg are used. * * NAME must either start with a slash and optional with a drive * prefix ("c:") or use one of these URL schemata: * * file:// * * This is the same as the default just with an explicit schemata. * * assuan://: * assuan://[]: * * Connect using TCP to PORT of the server with the numerical * IPADDR. Note that '[' and ']' are literal characters. */ gpg_error_t assuan_socket_connect (assuan_context_t ctx, const char *name, pid_t server_pid, unsigned int flags) { gpg_error_t err = 0; assuan_fd_t fd; #ifdef WITH_IPV6 struct sockaddr_in6 srvr_addr_in6; #endif struct sockaddr_un srvr_addr_un; struct sockaddr_in srvr_addr_in; struct sockaddr *srvr_addr = NULL; uint16_t port = 0; size_t len = 0; const char *s; int af = AF_LOCAL; int pf = PF_LOCAL; TRACE2 (ctx, ASSUAN_LOG_CTX, "assuan_socket_connect", ctx, "name=%s, flags=0x%x", name ? name : "(null)", flags); if (!ctx || !name) return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); if (!strncmp (name, "file://", 7) && name[7]) name += 7; else if (!strncmp (name, "assuan://", 9) && name[9]) { name += 9; af = AF_INET; pf = PF_INET; } else /* Default. */ { /* We require that the name starts with a slash if no URL schemata is used. To make things easier we allow an optional drive prefix. */ s = name; if (*s && s[1] == ':') s += 2; if (*s != DIRSEP_C && *s != '/') return _assuan_error (ctx, GPG_ERR_ASS_INV_VALUE); } if (af == AF_LOCAL) { int redirected; if (_assuan_sock_set_sockaddr_un (name, (struct sockaddr *)&srvr_addr_un, &redirected)) return _assuan_error (ctx, gpg_err_code_from_syserror ()); len = SUN_LEN (&srvr_addr_un); srvr_addr = (struct sockaddr *)&srvr_addr_un; } else { char *addrstr, *p; #ifdef HAVE_INET_PTON void *addrbuf = NULL; #endif addrstr = _assuan_malloc (ctx, strlen (name) + 1); if (!addrstr) return _assuan_error (ctx, gpg_err_code_from_syserror ()); if (*name == '[') { strcpy (addrstr, name+1); p = strchr (addrstr, ']'); if (!p || p[1] != ':' || !parse_portno (p+2, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; #ifdef WITH_IPV6 af = AF_INET6; pf = PF_INET6; memset (&srvr_addr_in6, 0, sizeof srvr_addr_in6); srvr_addr_in6.sin6_family = af; srvr_addr_in6.sin6_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in6.sin6_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in6; len = sizeof srvr_addr_in6; #else err = _assuan_error (ctx, GPG_ERR_EAFNOSUPPORT); #endif } } else { strcpy (addrstr, name); p = strchr (addrstr, ':'); if (!p || !parse_portno (p+1, &port)) err = _assuan_error (ctx, GPG_ERR_BAD_URI); else { *p = 0; memset (&srvr_addr_in, 0, sizeof srvr_addr_in); srvr_addr_in.sin_family = af; srvr_addr_in.sin_port = htons (port); #ifdef HAVE_INET_PTON addrbuf = &srvr_addr_in.sin_addr; #endif srvr_addr = (struct sockaddr *)&srvr_addr_in; len = sizeof srvr_addr_in; } } if (!err) { #ifdef HAVE_INET_PTON switch (inet_pton (af, addrstr, addrbuf)) { case 1: break; case 0: err = _assuan_error (ctx, GPG_ERR_BAD_URI); break; default: err = _assuan_error (ctx, gpg_err_code_from_syserror ()); } #else /*!HAVE_INET_PTON*/ /* We need to use the old function. If we are here v6 support isn't enabled anyway and thus we can do fine without. Note that Windows as a compatible inet_pton function named inetPton, but only since Vista. */ srvr_addr_in.sin_addr.s_addr = inet_addr (addrstr); if (srvr_addr_in.sin_addr.s_addr == INADDR_NONE) err = _assuan_error (ctx, GPG_ERR_BAD_URI); #endif /*!HAVE_INET_PTON*/ } _assuan_free (ctx, addrstr); if (err) return err; } fd = _assuan_sock_new (ctx, pf, SOCK_STREAM, 0); if (fd == ASSUAN_INVALID_FD) { err = _assuan_error (ctx, gpg_err_code_from_syserror ()); TRACE1 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't create socket: %s", strerror (errno)); return err; } if (_assuan_sock_connect (ctx, fd, srvr_addr, len) == -1) { TRACE2 (ctx, ASSUAN_LOG_SYSIO, "assuan_socket_connect", ctx, "can't connect to `%s': %s\n", name, strerror (errno)); _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_CONNECT_FAILED); } err = _assuan_connect_finalize (ctx, fd, flags); if (err) _assuan_reset (ctx); return err; } diff --git a/src/assuan-socket-server.c b/src/assuan-socket-server.c index a8330a8..f5fe038 100644 --- a/src/assuan-socket-server.c +++ b/src/assuan-socket-server.c @@ -1,259 +1,263 @@ /* assuan-socket-server.c - Assuan socket based server * Copyright (C) 2002, 2007, 2009 Free Software Foundation, Inc. * * This file is part of Assuan. * * Assuan is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * Assuan is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, see . * SPDX-License-Identifier: LGPL-2.1+ */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include # if HAVE_SYS_SOCKET_H # include # elif HAVE_WS2TCPIP_H # include # endif #else # include # include #endif #ifdef HAVE_SYS_UCRED_H #include #endif #ifdef HAVE_UCRED_H #include #endif #include "debug.h" #include "assuan-defs.h" static gpg_error_t accept_connection_bottom (assuan_context_t ctx) { assuan_fd_t fd = ctx->connected_fd; TRACE (ctx, ASSUAN_LOG_SYSIO, "accept_connection_bottom", ctx); ctx->peercred_valid = 0; #ifdef SO_PEERCRED { #ifdef HAVE_STRUCT_SOCKPEERCRED_PID struct sockpeercred cr; /* OpenBSD */ #else struct ucred cr; /* GNU/Linux */ #endif socklen_t cl = sizeof cr; if (!getsockopt (fd, SOL_SOCKET, SO_PEERCRED, &cr, &cl)) { ctx->peercred_valid = 1; ctx->peercred.pid = cr.pid; ctx->peercred.uid = cr.uid; ctx->peercred.gid = cr.gid; } } #elif defined (LOCAL_PEERPID) { /* macOS */ socklen_t len = sizeof (pid_t); if (!getsockopt (fd, SOL_LOCAL, LOCAL_PEERPID, &ctx->peercred.pid, &len)) { ctx->peercred_valid = 1; #if defined (LOCAL_PEERCRED) { struct xucred cr; len = sizeof (struct xucred); if (!getsockopt (fd, SOL_LOCAL, LOCAL_PEERCRED, &cr, &len)) { ctx->peercred.uid = cr.cr_uid; ctx->peercred.gid = cr.cr_gid; } } #endif } } #elif defined (LOCAL_PEEREID) { /* NetBSD */ struct unpcbid unp; socklen_t unpl = sizeof unp; if (getsockopt (fd, 0, LOCAL_PEEREID, &unp, &unpl) != -1) { ctx->peercred_valid = 1; ctx->peercred.pid = unp.unp_pid; ctx->peercred.uid = unp.unp_euid; ctx->peercred.gid = unp.unp_egid; } } #elif defined (HAVE_GETPEERUCRED) { /* Solaris */ ucred_t *ucred = NULL; if (getpeerucred (fd, &ucred) != -1) { ctx->peercred_valid = 1; ctx->peercred.pid = ucred_getpid (ucred); ctx->peercred.uid = ucred_geteuid (ucred); ctx->peercred.gid = ucred_getegid (ucred); ucred_free (ucred); } } #elif defined(HAVE_GETPEEREID) { /* FreeBSD */ if (getpeereid (fd, &ctx->peercred.uid, &ctx->peercred.gid) != -1) { ctx->peercred_valid = 1; ctx->peercred.pid = ASSUAN_INVALID_PID; } } #endif /* This overrides any already set PID if the function returns a valid one. */ if (ctx->peercred_valid && ctx->peercred.pid != ASSUAN_INVALID_PID) ctx->pid = ctx->peercred.pid; ctx->inbound.fd = fd; ctx->inbound.eof = 0; ctx->inbound.linelen = 0; ctx->inbound.attic.linelen = 0; ctx->inbound.attic.pending = 0; ctx->outbound.fd = fd; ctx->outbound.data.linelen = 0; ctx->outbound.data.error = 0; ctx->flags.confidential = 0; return 0; } static gpg_error_t accept_connection (assuan_context_t ctx) { assuan_fd_t fd; struct sockaddr_un clnt_addr; socklen_t len = sizeof clnt_addr; TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx, "listen_fd=0x%x", ctx->listen_fd); fd = SOCKET2HANDLE(accept (HANDLE2SOCKET(ctx->listen_fd), (struct sockaddr*)&clnt_addr, &len )); if (fd == ASSUAN_INVALID_FD) { return _assuan_error (ctx, gpg_err_code_from_syserror ()); } TRACE1 (ctx, ASSUAN_LOG_SYSIO, "accept_connection", ctx, "fd->0x%x", fd); if (_assuan_sock_check_nonce (ctx, fd, &ctx->listen_nonce)) { _assuan_close (ctx, fd); return _assuan_error (ctx, GPG_ERR_ASS_ACCEPT_FAILED); } ctx->connected_fd = fd; return accept_connection_bottom (ctx); } /* Flag bits: 0 - use sendmsg/recvmsg to allow descriptor passing 1 - FD has already been accepted. */ gpg_error_t assuan_init_socket_server (assuan_context_t ctx, assuan_fd_t fd, unsigned int flags) { gpg_error_t rc; TRACE_BEG2 (ctx, ASSUAN_LOG_CTX, "assuan_init_socket_server", ctx, "fd=0x%x, flags=0x%x", fd, flags); ctx->flags.is_socket = 1; rc = _assuan_register_std_commands (ctx); if (rc) return TRACE_ERR (rc); ctx->engine.release = _assuan_server_release; ctx->engine.readfnc = _assuan_simple_read; ctx->engine.writefnc = _assuan_simple_write; ctx->engine.sendfd = NULL; ctx->engine.receivefd = NULL; ctx->flags.is_server = 1; if (flags & ASSUAN_SOCKET_SERVER_ACCEPTED) /* We want a second accept to indicate EOF. */ ctx->max_accepts = 1; else ctx->max_accepts = -1; ctx->input_fd = ASSUAN_INVALID_FD; ctx->output_fd = ASSUAN_INVALID_FD; ctx->inbound.fd = ASSUAN_INVALID_FD; ctx->outbound.fd = ASSUAN_INVALID_FD; if (flags & ASSUAN_SOCKET_SERVER_ACCEPTED) { ctx->listen_fd = ASSUAN_INVALID_FD; ctx->connected_fd = fd; } else { ctx->listen_fd = fd; ctx->connected_fd = ASSUAN_INVALID_FD; } ctx->accept_handler = ((flags & ASSUAN_SOCKET_SERVER_ACCEPTED) ? accept_connection_bottom : accept_connection); ctx->finish_handler = _assuan_server_finish; +#ifdef HAVE_W32_SYSTEM + ctx->engine.receivefd = w32_fdpass_recv; +#else if (flags & ASSUAN_SOCKET_SERVER_FDPASSING) _assuan_init_uds_io (ctx); +#endif rc = _assuan_register_std_commands (ctx); if (rc) _assuan_reset (ctx); return TRACE_ERR (rc); } /* Save a copy of NONCE in context CTX. This should be used to register the server's nonce with an context established by assuan_init_socket_server. */ void assuan_set_sock_nonce (assuan_context_t ctx, assuan_sock_nonce_t *nonce) { if (ctx && nonce) ctx->listen_nonce = *nonce; }