diff --git a/src/assuan-defs.h b/src/assuan-defs.h index 47ff4ef..de2a6d4 100644 --- a/src/assuan-defs.h +++ b/src/assuan-defs.h @@ -1,433 +1,434 @@ /* assuan-defs.h - Internal definitions to Assuan * Copyright (C) 2001, 2002, 2004, 2005, 2007, 2008, * 2009, 2010 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+ */ #ifndef ASSUAN_DEFS_H #define ASSUAN_DEFS_H #ifdef HAVE_SYS_TYPES_H # include #endif #ifndef HAVE_W32_SYSTEM # include # include #else # ifdef HAVE_WINSOCK2_H # /* Avoid inclusion of winsock.h via windows.h. */ # include # endif # include #endif #ifdef HAVE_UNISTD_H # include #endif #include "assuan.h" #if __GNUC__ > 2 # define ASSUAN_GCC_A_PURE __attribute__ ((__pure__)) #else # define ASSUAN_GCC_A_PURE #endif #ifndef HAVE_W32_SYSTEM #define DIRSEP_C '/' #else #define DIRSEP_C '\\' #endif #define LINELENGTH ASSUAN_LINELENGTH struct cmdtbl_s { const char *name; assuan_handler_t handler; const char *helpstr; }; /* The context we use with most functions. */ struct assuan_context_s { /* Members managed by the generic routines in assuan.c. */ /* The error source for errors generated from this context. */ gpg_err_source_t err_source; #ifdef HAVE_W32_SYSTEM /* The per-context w32 error string. */ char w32_strerror[256]; #endif /* The allocation hooks. */ struct assuan_malloc_hooks malloc_hooks; /* Logging callback handler. */ assuan_log_cb_t log_cb; void *log_cb_data; void *user_pointer; /* Context specific flags (cf. assuan_flag_t). */ struct { unsigned int no_waitpid : 1; unsigned int confidential : 1; unsigned int no_fixsignals : 1; unsigned int convey_comments : 1; unsigned int no_logging : 1; unsigned int force_close : 1; /* From here, we have internal flags, not defined by assuan_flag_t. */ unsigned int is_socket : 1; unsigned int is_server : 1; /* Set if this is context belongs to a server */ unsigned int in_inquire : 1; /* Server: inside assuan_inquire */ unsigned int in_process_next : 1; unsigned int process_complete : 1; unsigned int in_command : 1; unsigned int in_inq_cb : 1; /* Client: inquire callback is active */ unsigned int confidential_inquiry : 1; /* Client: inquiry is confidential */ } flags; /* If set, this is called right before logging an I/O line. */ assuan_io_monitor_t io_monitor; void *io_monitor_data; /* Callback handlers replacing system I/O functions. */ struct assuan_system_hooks system; int peercred_valid; /* Whether this structure has valid information. */ struct _assuan_peercred peercred; /* Now come the members specific to subsystems or engines. FIXME: This is not developed yet. See below for the legacy members. */ struct { void (*release) (assuan_context_t ctx); /* Routine to read from input_fd. Sets errno on failure. */ ssize_t (*readfnc) (assuan_context_t, void *, size_t); /* Routine to write to output_fd. Sets errno on failure. */ ssize_t (*writefnc) (assuan_context_t, const void *, size_t); /* Send a file descriptor. */ gpg_error_t (*sendfd) (assuan_context_t, assuan_fd_t); /* Receive a file descriptor. */ gpg_error_t (*receivefd) (assuan_context_t, assuan_fd_t *); } engine; /* Engine specific or other subsystem members. */ /* assuan-logging.c. Does not require deallocation from us. */ FILE *log_fp; /* assuan-util.c */ gpg_error_t err_no; const char *err_str; /* The following members are used by assuan_inquire_ext. */ gpg_error_t (*inquire_cb) (void *cb_data, gpg_error_t rc, unsigned char *buf, size_t len); void *inquire_cb_data; void *inquire_membuf; char *hello_line; char *okay_line; /* See assuan_set_okay_line() */ struct { assuan_fd_t fd; int eof; char line[LINELENGTH]; int linelen; /* w/o CR, LF - might not be the same as strlen(line) due to embedded nuls. However a nul is always written at this pos. */ struct { char line[LINELENGTH]; int linelen ; int pending; /* i.e. at least one line is available in the attic */ } attic; } inbound; struct { assuan_fd_t fd; struct { FILE *fp; char line[LINELENGTH]; int linelen; int error; } data; } outbound; int max_accepts; /* If we can not handle more than one connection, set this to 1, otherwise to -1. */ pid_t pid; /* The pid of the peer. */ assuan_fd_t listen_fd; /* The fd we are listening on (used by socket servers) */ assuan_sock_nonce_t listen_nonce; /* Used with LISTEN_FD. */ assuan_fd_t connected_fd; /* helper */ /* Used for Unix domain sockets. */ struct sockaddr_un myaddr; struct sockaddr_un serveraddr; /* Structure used for unix domain sockets. */ struct { assuan_fd_t pendingfds[5]; /* Array to save received descriptors. */ int pendingfdscount; /* Number of received descriptors. */ } uds; gpg_error_t (*accept_handler)(assuan_context_t); void (*finish_handler)(assuan_context_t); struct cmdtbl_s *cmdtbl; size_t cmdtbl_used; /* used entries */ size_t cmdtbl_size; /* allocated size of table */ /* The name of the command currently processed by a command handler. This is a pointer into CMDTBL. NULL if not in a command handler. */ const char *current_cmd_name; assuan_handler_t bye_notify_fnc; assuan_handler_t reset_notify_fnc; assuan_handler_t cancel_notify_fnc; gpg_error_t (*option_handler_fnc)(assuan_context_t,const char*, const char*); assuan_handler_t input_notify_fnc; assuan_handler_t output_notify_fnc; /* This function is called right before a command handler is called. */ gpg_error_t (*pre_cmd_notify_fnc)(assuan_context_t, const char *cmd); /* This function is called right after a command has been processed. It may be used to command related cleanup. */ void (*post_cmd_notify_fnc)(assuan_context_t, gpg_error_t); assuan_fd_t input_fd; /* Set by the INPUT command. */ assuan_fd_t output_fd; /* Set by the OUTPUT command. */ }; /* Generate an error code specific to a context. */ static GPG_ERR_INLINE gpg_error_t _assuan_error (assuan_context_t ctx, gpg_err_code_t errcode) { return gpg_err_make (ctx?ctx->err_source: GPG_ERR_SOURCE_ASSUAN, errcode); } /* Release all resources associated with an engine operation. */ void _assuan_reset (assuan_context_t ctx); /* Default log handler. */ int _assuan_log_handler (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg); /* Manage memory specific to a context. */ void *_assuan_malloc (assuan_context_t ctx, size_t cnt); void *_assuan_realloc (assuan_context_t ctx, void *ptr, size_t cnt); void *_assuan_calloc (assuan_context_t ctx, size_t cnt, size_t elsize); void _assuan_free (assuan_context_t ctx, void *ptr); /* System hooks. */ void _assuan_usleep (assuan_context_t ctx, unsigned int usec); int _assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx); int _assuan_close (assuan_context_t ctx, assuan_fd_t fd); int _assuan_close_inheritable (assuan_context_t ctx, assuan_fd_t fd); ssize_t _assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size); ssize_t _assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size); int _assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int _assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags); int _assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char *argv[], assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags); pid_t _assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options); int _assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]); assuan_fd_t _assuan_socket (assuan_context_t ctx, int namespace, int style, int protocol); int _assuan_connect (assuan_context_t ctx, assuan_fd_t sock, struct sockaddr *addr, socklen_t length); extern struct assuan_system_hooks _assuan_system_hooks; /* Copy the system hooks struct, paying attention to version differences. SRC is usually from the user, DST MUST be from the library. */ void _assuan_system_hooks_copy (assuan_system_hooks_t dst, assuan_system_hooks_t src); /*-- assuan-pipe-server.c --*/ void _assuan_release_context (assuan_context_t ctx); /*-- assuan-uds.c --*/ void _assuan_uds_close_fds (assuan_context_t ctx); void _assuan_uds_deinit (assuan_context_t ctx); void _assuan_init_uds_io (assuan_context_t ctx); /*-- assuan-handler.c --*/ gpg_error_t _assuan_register_std_commands (assuan_context_t ctx); /*-- assuan-buffer.c --*/ gpg_error_t _assuan_read_line (assuan_context_t ctx); int _assuan_cookie_write_data (void *cookie, const char *buffer, size_t size); int _assuan_cookie_write_flush (void *cookie); gpg_error_t _assuan_write_line (assuan_context_t ctx, const char *prefix, const char *line, size_t len); /*-- client.c --*/ gpg_error_t _assuan_read_from_server (assuan_context_t ctx, assuan_response_t *okay, int *off, int convey_comments); /*-- assuan-error.c --*/ /*-- assuan-inquire.c --*/ gpg_error_t _assuan_inquire_ext_cb (assuan_context_t ctx); void _assuan_inquire_release (assuan_context_t ctx); /* Check if ERR means EAGAIN. */ int _assuan_error_is_eagain (assuan_context_t ctx, gpg_error_t err); #define set_error(c,e,t) \ assuan_set_error ((c), _assuan_error (c,e), (t)) #ifdef HAVE_W32_SYSTEM char *_assuan_w32_strerror (assuan_context_t ctx, int ec); +gpg_error_t w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd); #endif /*HAVE_W32_SYSTEM*/ /*-- assuan-logging.c --*/ void _assuan_init_log_envvars (void); void _assuan_log_control_channel (assuan_context_t ctx, int outbound, const char *string, const void *buffer1, size_t length1, const void *buffer2, size_t length2); /*-- assuan-io.c --*/ ssize_t _assuan_simple_read (assuan_context_t ctx, void *buffer, size_t size); ssize_t _assuan_simple_write (assuan_context_t ctx, const void *buffer, size_t size); /*-- assuan-socket.c --*/ assuan_fd_t _assuan_sock_new (assuan_context_t ctx, int domain, int type, int proto); 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, assuan_sock_nonce_t *nonce); #ifdef HAVE_W32_SYSTEM wchar_t *_assuan_utf8_to_wchar (const char *string); int _assuan_sock_wsa2errno (int err); #endif #ifdef HAVE_FOPENCOOKIE /* We have to implement funopen in terms of glibc's fopencookie. */ FILE *_assuan_funopen(void *cookie, cookie_read_function_t *readfn, cookie_write_function_t *writefn, cookie_seek_function_t *seekfn, cookie_close_function_t *closefn); #define funopen(a,r,w,s,c) _assuan_funopen ((a), (r), (w), (s), (c)) #endif /*HAVE_FOPENCOOKIE*/ /*-- sysutils.c --*/ const char *_assuan_sysutils_blurb (void); /* Prototypes for replacement functions. */ #ifndef HAVE_MEMRCHR void *memrchr (const void *block, int c, size_t size); #endif #ifndef HAVE_STPCPY char *stpcpy (char *dest, const char *src); #endif #ifndef HAVE_SETENV #define setenv _assuan_setenv #define unsetenv _assuan_unsetenv #define clearenv _assuan_clearenv int setenv (const char *name, const char *value, int replace); #endif #ifndef HAVE_PUTC_UNLOCKED int putc_unlocked (int c, FILE *stream); #endif #define DIM(v) (sizeof(v)/sizeof((v)[0])) /* To avoid that a compiler optimizes memset calls away, these macros can be used. */ #define wipememory2(_ptr,_set,_len) do { \ volatile char *_vptr=(volatile char *)(_ptr); \ size_t _vlen=(_len); \ while(_vlen) { *_vptr=(_set); _vptr++; _vlen--; } \ } while(0) #define wipememory(_ptr,_len) wipememory2(_ptr,0,_len) #if HAVE_W64_SYSTEM # define SOCKET2HANDLE(s) ((void *)(s)) # define HANDLE2SOCKET(h) ((uintptr_t)(h)) #elif HAVE_W32_SYSTEM # define SOCKET2HANDLE(s) ((void *)(s)) # define HANDLE2SOCKET(h) ((unsigned int)(h)) #else # define SOCKET2HANDLE(s) (s) # define HANDLE2SOCKET(h) (h) #endif void _assuan_client_finish (assuan_context_t ctx); void _assuan_client_release (assuan_context_t ctx); void _assuan_server_finish (assuan_context_t ctx); void _assuan_server_release (assuan_context_t ctx); /* Encode the C formatted string SRC and return the malloc'ed result. */ char *_assuan_encode_c_string (assuan_context_t ctx, const char *src); void _assuan_pre_syscall (void); void _assuan_post_syscall (void); #endif /*ASSUAN_DEFS_H*/ diff --git a/src/assuan-socket-connect.c b/src/assuan-socket-connect.c index 019a41c..d7adc31 100644 --- a/src/assuan-socket-connect.c +++ b/src/assuan-socket-connect.c @@ -1,349 +1,353 @@ /* 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) { 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/system-w32.c b/src/system-w32.c index 87b0f9e..9694481 100644 --- a/src/system-w32.c +++ b/src/system-w32.c @@ -1,627 +1,642 @@ /* system-w32.c - System support functions for Windows. * Copyright (C) 2009, 2010 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 #include #include "assuan-defs.h" #include "debug.h" assuan_fd_t assuan_fdopen (int fd) { assuan_fd_t ifd = (assuan_fd_t) _get_osfhandle (fd); assuan_fd_t ofd; if (! DuplicateHandle(GetCurrentProcess(), ifd, GetCurrentProcess(), &ofd, 0, TRUE, DUPLICATE_SAME_ACCESS)) { gpg_err_set_errno (EIO); return ASSUAN_INVALID_FD; } return ofd; } /* Sleep for the given number of microseconds. Default implementation. */ void __assuan_usleep (assuan_context_t ctx, unsigned int usec) { if (!usec) return; Sleep (usec / 1000); } /* Three simple wrappers, only used because thes function are named in the def file. */ HANDLE _assuan_w32ce_prepare_pipe (int *r_rvid, int write_end) { (void)r_rvid; (void)write_end; return INVALID_HANDLE_VALUE; } HANDLE _assuan_w32ce_finish_pipe (int rvid, int write_end) { (void)rvid; (void)write_end; return INVALID_HANDLE_VALUE; } DWORD _assuan_w32ce_create_pipe (HANDLE *read_hd, HANDLE *write_hd, LPSECURITY_ATTRIBUTES sec_attr, DWORD size) { return CreatePipe (read_hd, write_hd, sec_attr, size); } /* Create a pipe with one inheritable end. Default implementation. */ int __assuan_pipe (assuan_context_t ctx, assuan_fd_t fd[2], int inherit_idx) { HANDLE rh; HANDLE wh; HANDLE th; SECURITY_ATTRIBUTES sec_attr; memset (&sec_attr, 0, sizeof (sec_attr)); sec_attr.nLength = sizeof (sec_attr); sec_attr.bInheritHandle = FALSE; if (!CreatePipe (&rh, &wh, &sec_attr, 0)) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, "CreatePipe failed: %s", _assuan_w32_strerror (ctx, -1)); gpg_err_set_errno (EIO); return -1; } if (! DuplicateHandle (GetCurrentProcess(), (inherit_idx == 0) ? rh : wh, GetCurrentProcess(), &th, 0, TRUE, DUPLICATE_SAME_ACCESS )) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_pipe", ctx, "DuplicateHandle failed: %s", _assuan_w32_strerror (ctx, -1)); CloseHandle (rh); CloseHandle (wh); gpg_err_set_errno (EIO); return -1; } if (inherit_idx == 0) { CloseHandle (rh); rh = th; } else { CloseHandle (wh); wh = th; } fd[0] = rh; fd[1] = wh; return 0; } /* Close the given file descriptor, created with _assuan_pipe or one of the socket functions. Default implementation. */ int __assuan_close (assuan_context_t ctx, assuan_fd_t fd) { int rc; if (ctx->flags.is_socket) { rc = closesocket (HANDLE2SOCKET(fd)); if (rc) gpg_err_set_errno ( _assuan_sock_wsa2errno (WSAGetLastError ()) ); } else { rc = CloseHandle (fd); if (rc) /* FIXME. */ gpg_err_set_errno (EIO); } return rc; } +gpg_error_t +w32_fdpass_send (assuan_context_t ctx, assuan_fd_t fd) +{ + char fdpass_msg[256]; + size_t msglen; + int res; + + /* not yet implemented. */ + (void)fd; + msglen = 6; + strcpy (fdpass_msg, "hello!"); + res = send (HANDLE2SOCKET (ctx->outbound.fd), fdpass_msg, msglen, MSG_OOB); + return 0; +} + static int process_fdpass_msg (const char *fdpass_msg, size_t msglen, assuan_fd_t *r_fd) { fprintf (stderr, "process_fdpass_msg: %s\n", fdpass_msg); *r_fd = NULL; /* not implemented yet, but pretending as if it's successful. */ return 0; } ssize_t __assuan_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size) { int res; int ec = 0; if (ctx->flags.is_socket) { fd_set fds; int tries = 3; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0; FD_ZERO (&fds); FD_SET (HANDLE2SOCKET (fd), &fds); res = select (0, NULL, NULL, &fds, &timeout); if (res < 0) { gpg_err_set_errno (EIO); return -1; } else if (res) { assuan_fd_t fd_recv; char fdpass_msg[256]; res = recv (HANDLE2SOCKET (fd), fdpass_msg, sizeof (fdpass_msg), MSG_OOB); if (res < 0) { gpg_err_set_errno (EIO); return -1; } res = process_fdpass_msg (fdpass_msg, res, &fd_recv); if (res) ctx->uds.pendingfds[ctx->uds.pendingfdscount++] = fd_recv; else { gpg_err_set_errno (EIO); return -1; } } again: ec = 0; res = recv (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) ec = WSAGetLastError (); if (ec == WSAEWOULDBLOCK && tries--) { /* EAGAIN: Use select to wait for resources and try again. We do this 3 times and then give up. The higher level layer then needs to take care of EAGAIN. No need to specify a timeout - the socket is not expected to be in blocking mode. */ FD_ZERO (&fds); FD_SET (HANDLE2SOCKET (fd), &fds); select (0, &fds, NULL, NULL, NULL); goto again; } } else { DWORD nread = 0; if (!ReadFile (fd, buffer, size, &nread, NULL)) { res = -1; ec = GetLastError (); } else res = nread; } if (res == -1) { switch (ec) { case WSAENOTSOCK: gpg_err_set_errno (EBADF); break; case WSAEWOULDBLOCK: gpg_err_set_errno (EAGAIN); break; case WSAECONNRESET: /* Due to the use of recv. */ case ERROR_BROKEN_PIPE: gpg_err_set_errno (EPIPE); break; default: gpg_err_set_errno (EIO); break; } } return res; } ssize_t __assuan_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size) { int res; int ec = 0; if (ctx->flags.is_socket) { int tries = 3; again: ec = 0; res = send (HANDLE2SOCKET (fd), buffer, size, 0); if (res == -1) ec = WSAGetLastError (); if (ec == WSAEWOULDBLOCK && tries--) { /* EAGAIN: Use select to wait for resources and try again. We do this 3 times and then give up. The higher level layer then needs to take care of EAGAIN. No need to specify a timeout - the socket is not expected to be in blocking mode. */ fd_set fds; FD_ZERO (&fds); FD_SET (HANDLE2SOCKET (fd), &fds); select (0, NULL, &fds, NULL, NULL); goto again; } } else { DWORD nwrite; if (!WriteFile (fd, buffer, size, &nwrite, NULL)) { res = -1; ec = GetLastError (); } else res = (int)nwrite; } if (res == -1) { switch (ec) { case WSAENOTSOCK: gpg_err_set_errno (EBADF); break; case WSAEWOULDBLOCK: gpg_err_set_errno (EAGAIN); break; case ERROR_BROKEN_PIPE: case ERROR_NO_DATA: gpg_err_set_errno (EPIPE); break; default: gpg_err_set_errno (EIO); break; } } return res; } int __assuan_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { gpg_err_set_errno (ENOSYS); return -1; } int __assuan_sendmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg, int flags) { gpg_err_set_errno (ENOSYS); return -1; } /* Build a command line for use with W32's CreateProcess. On success CMDLINE gets the address of a newly allocated string. */ static int build_w32_commandline (assuan_context_t ctx, const char * const *argv, char **cmdline) { int i, n; const char *s; char *buf, *p; *cmdline = NULL; n = 0; for (i=0; (s = argv[i]); i++) { n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */ for (; *s; s++) if (*s == '\"') n++; /* Need to double inner quotes. */ } n++; buf = p = _assuan_malloc (ctx, n); if (! buf) return -1; for (i = 0; argv[i]; i++) { if (i) p = stpcpy (p, " "); if (! *argv[i]) /* Empty string. */ p = stpcpy (p, "\"\""); else if (strpbrk (argv[i], " \t\n\v\f\"")) { p = stpcpy (p, "\""); for (s = argv[i]; *s; s++) { *p++ = *s; if (*s == '\"') *p++ = *s; } *p++ = '\"'; *p = 0; } else p = stpcpy (p, argv[i]); } *cmdline= buf; return 0; } int __assuan_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name, const char **argv, assuan_fd_t fd_in, assuan_fd_t fd_out, assuan_fd_t *fd_child_list, void (*atfork) (void *opaque, int reserved), void *atforkvalue, unsigned int flags) { SECURITY_ATTRIBUTES sec_attr; PROCESS_INFORMATION pi = { NULL, /* Returns process handle. */ 0, /* Returns primary thread handle. */ 0, /* Returns pid. */ 0 /* Returns tid. */ }; STARTUPINFOW si; assuan_fd_t fd; assuan_fd_t *fdp; char *cmdline; wchar_t *wcmdline = NULL; wchar_t *wname = NULL; HANDLE nullfd = INVALID_HANDLE_VALUE; int rc; /* fixme: Actually we should set the "_assuan_pipe_connect_pid" env variable. However this requires us to write a full environment handler, because the strings are expected in sorted order. The suggestion given in the MS Reference Library, to save the old value, change it, create process and restore it, is not thread safe. */ /* Build the command line. */ if (build_w32_commandline (ctx, argv, &cmdline)) return -1; /* Start the process. */ memset (&sec_attr, 0, sizeof sec_attr); sec_attr.nLength = sizeof sec_attr; sec_attr.bInheritHandle = FALSE; memset (&si, 0, sizeof si); si.cb = sizeof (si); si.dwFlags = STARTF_USESTDHANDLES; /* FIXME: Dup to nul if ASSUAN_INVALID_FD. */ si.hStdInput = fd_in; si.hStdOutput = fd_out; /* Dup stderr to /dev/null unless it is in the list of FDs to be passed to the child. */ fd = assuan_fd_from_posix_fd (fileno (stderr)); fdp = fd_child_list; if (fdp) { for (; *fdp != ASSUAN_INVALID_FD && *fdp != fd; fdp++) ; } if (!fdp || *fdp == ASSUAN_INVALID_FD) { nullfd = CreateFileW (L"nul", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (nullfd == INVALID_HANDLE_VALUE) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "__assuan_spawn", ctx, "can't open `nul': %s", _assuan_w32_strerror (ctx, -1)); _assuan_free (ctx, cmdline); gpg_err_set_errno (EIO); return -1; } si.hStdError = nullfd; } else si.hStdError = fd; /* Note: We inherit all handles flagged as inheritable. This seems to be a security flaw but there seems to be no way of selecting handles to inherit. A fix for this would be to use a helper process like we have in gpgme. Take care: CreateProcessW may modify wpgmname */ /* _assuan_log_printf ("CreateProcess, path=`%s' cmdline=`%s'\n", */ /* name, cmdline); */ if (name && !(wname = _assuan_utf8_to_wchar (name))) rc = 0; else if (!(wcmdline = _assuan_utf8_to_wchar (cmdline))) rc = 0; else rc = CreateProcessW (wname, /* Program to start. */ wcmdline, /* Command line arguments. */ &sec_attr, /* Process security attributes. */ &sec_attr, /* Thread security attributes. */ TRUE, /* Inherit handles. */ (CREATE_DEFAULT_ERROR_MODE | ((flags & 128)? DETACHED_PROCESS : 0) | GetPriorityClass (GetCurrentProcess ()) | CREATE_SUSPENDED), /* Creation flags. */ NULL, /* Environment. */ NULL, /* Use current drive/directory. */ &si, /* Startup information. */ &pi /* Returns process information. */ ); if (!rc) { TRACE1 (ctx, ASSUAN_LOG_SYSIO, "pipe_connect_w32", ctx, "CreateProcess failed%s: %s", _assuan_w32_strerror (ctx, -1)); free (wname); free (wcmdline); _assuan_free (ctx, cmdline); if (nullfd != INVALID_HANDLE_VALUE) CloseHandle (nullfd); gpg_err_set_errno (EIO); return -1; } free (wname); free (wcmdline); _assuan_free (ctx, cmdline); if (nullfd != INVALID_HANDLE_VALUE) CloseHandle (nullfd); ResumeThread (pi.hThread); CloseHandle (pi.hThread); /* _assuan_log_printf ("CreateProcess ready: hProcess=%p hThread=%p" */ /* " dwProcessID=%d dwThreadId=%d\n", */ /* pi.hProcess, pi.hThread, */ /* (int) pi.dwProcessId, (int) pi.dwThreadId); */ *r_pid = (pid_t) pi.hProcess; /* No need to modify peer process, as we don't change the handle names. However this also means we are not safe, as we inherit too many handles. Should use approach similar to gpgme and glib using a helper process. */ return 0; } /* FIXME: Add some sort of waitpid function that covers GPGME and gpg-agent's use of assuan. */ pid_t __assuan_waitpid (assuan_context_t ctx, pid_t pid, int nowait, int *status, int options) { CloseHandle ((HANDLE) pid); return 0; } int __assuan_socketpair (assuan_context_t ctx, int namespace, int style, int protocol, assuan_fd_t filedes[2]) { gpg_err_set_errno (ENOSYS); return -1; } assuan_fd_t __assuan_socket (assuan_context_t ctx, int domain, int type, int proto) { assuan_fd_t res; res = SOCKET2HANDLE (socket (domain, type, proto)); if (res == SOCKET2HANDLE (INVALID_SOCKET)) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; } int __assuan_connect (assuan_context_t ctx, assuan_fd_t sock, struct sockaddr *addr, socklen_t length) { int res; res = connect (HANDLE2SOCKET (sock), addr, length); if (res < 0) gpg_err_set_errno (_assuan_sock_wsa2errno (WSAGetLastError ())); return res; } /* The default system hooks for assuan contexts. */ struct assuan_system_hooks _assuan_system_hooks = { 0, __assuan_usleep, __assuan_pipe, __assuan_close, __assuan_read, __assuan_write, __assuan_recvmsg, __assuan_sendmsg, __assuan_spawn, __assuan_waitpid, __assuan_socketpair, __assuan_socket, __assuan_connect };