1 /* assuan-support.c - Assuan wrappers
2  * Copyright (C) 2009 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, see <https://gnu.org/licenses/>.
18  * SPDX-License-Identifier: LGPL-2.1-or-later
19  */
20 
21 
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 #include <assert.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 
30 #include "assuan.h"
31 
32 #include "gpgme.h"
33 #include "ath.h"
34 #include "priv-io.h"
35 #include "debug.h"
36 
37 
38 struct assuan_malloc_hooks _gpgme_assuan_malloc_hooks =
39   {
40     malloc,
41     realloc,
42     free
43   };
44 
45 
46 int
_gpgme_assuan_log_cb(assuan_context_t ctx,void * hook,unsigned int cat,const char * msg)47 _gpgme_assuan_log_cb (assuan_context_t ctx, void *hook,
48 		      unsigned int cat, const char *msg)
49 {
50   (void)ctx;
51   (void)hook;
52   (void)cat;
53 
54   if (msg == NULL)
55     return 1;
56 
57   _gpgme_debug (NULL, DEBUG_ASSUAN, -1, NULL, NULL, NULL, "%s", msg);
58   return 0;
59 }
60 
61 
62 static void
my_usleep(assuan_context_t ctx,unsigned int usec)63 my_usleep (assuan_context_t ctx, unsigned int usec)
64 {
65   /* FIXME: Add to ath.  */
66   __assuan_usleep (ctx, usec);
67 }
68 
69 
70 /* Create a pipe with an inheritable end.  */
71 static int
my_pipe(assuan_context_t ctx,assuan_fd_t fds[2],int inherit_idx)72 my_pipe (assuan_context_t ctx, assuan_fd_t fds[2], int inherit_idx)
73 {
74   int res;
75   int gfds[2];
76 
77   (void)ctx;
78 
79   res = _gpgme_io_pipe (gfds, inherit_idx);
80 
81   /* For now... */
82   fds[0] = (assuan_fd_t) gfds[0];
83   fds[1] = (assuan_fd_t) gfds[1];
84 
85   return res;
86 }
87 
88 
89 /* Close the given file descriptor, created with _assuan_pipe or one
90    of the socket functions.  */
91 static int
my_close(assuan_context_t ctx,assuan_fd_t fd)92 my_close (assuan_context_t ctx, assuan_fd_t fd)
93 {
94   (void)ctx;
95   return _gpgme_io_close ((int) fd);
96 }
97 
98 
99 static gpgme_ssize_t
my_read(assuan_context_t ctx,assuan_fd_t fd,void * buffer,size_t size)100 my_read (assuan_context_t ctx, assuan_fd_t fd, void *buffer, size_t size)
101 {
102   (void)ctx;
103   return _gpgme_io_read ((int) fd, buffer, size);
104 }
105 
106 
107 static gpgme_ssize_t
my_write(assuan_context_t ctx,assuan_fd_t fd,const void * buffer,size_t size)108 my_write (assuan_context_t ctx, assuan_fd_t fd, const void *buffer, size_t size)
109 {
110   (void)ctx;
111   return _gpgme_io_write ((int) fd, buffer, size);
112 }
113 
114 
115 static int
my_recvmsg(assuan_context_t ctx,assuan_fd_t fd,assuan_msghdr_t msg,int flags)116 my_recvmsg (assuan_context_t ctx, assuan_fd_t fd, assuan_msghdr_t msg,
117 	    int flags)
118 {
119   (void)ctx;
120 #ifdef HAVE_W32_SYSTEM
121   (void)fd;
122   (void)msg;
123   (void)flags;
124   gpg_err_set_errno (ENOSYS);
125   return -1;
126 #else
127   return _gpgme_io_recvmsg ((int) fd, msg, flags);
128 #endif
129 }
130 
131 
132 
133 static int
my_sendmsg(assuan_context_t ctx,assuan_fd_t fd,const assuan_msghdr_t msg,int flags)134 my_sendmsg (assuan_context_t ctx, assuan_fd_t fd, const assuan_msghdr_t msg,
135 	    int flags)
136 {
137   (void)ctx;
138 #ifdef HAVE_W32_SYSTEM
139   (void)fd;
140   (void)msg;
141   (void)flags;
142   gpg_err_set_errno (ENOSYS);
143   return -1;
144 #else
145   return _gpgme_io_sendmsg ((int) fd, msg, flags);
146 #endif
147 }
148 
149 
150 /* If NAME is NULL, don't exec, just fork.  FD_CHILD_LIST is modified
151    to reflect the value of the FD in the peer process (on
152    Windows).  */
153 static int
my_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)154 my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
155 	  const char **argv,
156 	  assuan_fd_t fd_in, assuan_fd_t fd_out,
157 	  assuan_fd_t *fd_child_list,
158 	  void (*atfork) (void *opaque, int reserved),
159 	  void *atforkvalue, unsigned int flags)
160 {
161   int err = 0;
162   struct spawn_fd_item_s *fd_items;
163   int i;
164 
165   (void)ctx;
166   (void)flags;
167 
168   assert (name);
169 
170   if (! name)
171     {
172       gpg_err_set_errno (ENOSYS);
173       return -1;
174     }
175 
176   i = 0;
177   if (fd_child_list)
178     {
179       while (fd_child_list[i] != ASSUAN_INVALID_FD)
180 	i++;
181     }
182   /* fd_in, fd_out, terminator */
183   i += 3;
184   fd_items = calloc (i, sizeof (struct spawn_fd_item_s));
185   if (! fd_items)
186     return -1;
187   i = 0;
188   if (fd_child_list)
189     {
190       while (fd_child_list[i] != ASSUAN_INVALID_FD)
191 	{
192 	  fd_items[i].fd = (int) fd_child_list[i];
193 	  fd_items[i].dup_to = -1;
194 	  i++;
195 	}
196     }
197   if (fd_in != ASSUAN_INVALID_FD)
198     {
199       fd_items[i].fd = (int) fd_in;
200       fd_items[i].dup_to = 0;
201       i++;
202     }
203   if (fd_out != ASSUAN_INVALID_FD)
204     {
205       fd_items[i].fd = (int) fd_out;
206       fd_items[i].dup_to = 1;
207       i++;
208     }
209   fd_items[i].fd = -1;
210   fd_items[i].dup_to = -1;
211 
212 #ifdef HAVE_W32_SYSTEM
213   /* Fix up a potential logger fd so that on windows the fd
214    * translation can work through gpgme-w32spawn.
215    *
216    * We do this here as a hack because we would
217    * otherwise have to change assuan_api and the current
218    * plan in 2019 is to change away from this to gpgrt
219    * based IPC. */
220   if (argv)
221     {
222       int loc = 0;
223       while (argv[loc])
224         {
225           if (!strcmp ("--logger-fd", argv[loc]))
226             {
227               long logger_fd = -1;
228               char *tail;
229               int k = 0;
230               loc++;
231               if (!argv[loc])
232                 {
233                   err = GPG_ERR_INV_ARG;
234                   break;
235                 }
236               logger_fd = strtol (argv[loc], &tail, 10);
237               if (tail == argv[loc] || logger_fd < 0)
238                 {
239                   err = GPG_ERR_INV_ARG;
240                   break;
241                 }
242               while (fd_items[k++].fd != -1)
243                 {
244                   if (fd_items[k].fd == logger_fd)
245                     {
246                       fd_items[k].arg_loc = loc;
247                       break;
248                     }
249                 }
250               break;
251             }
252           loc++;
253         }
254     }
255 #endif
256 
257   if (!err)
258     {
259       err = _gpgme_io_spawn (name, (char*const*)argv,
260                              (IOSPAWN_FLAG_NOCLOSE | IOSPAWN_FLAG_DETACHED),
261                              fd_items, atfork, atforkvalue, r_pid);
262     }
263   if (!err)
264     {
265       i = 0;
266 
267       if (fd_child_list)
268 	{
269 	  while (fd_child_list[i] != ASSUAN_INVALID_FD)
270 	    {
271 	      fd_child_list[i] = (assuan_fd_t) fd_items[i].peer_name;
272 	      i++;
273 	    }
274 	}
275     }
276   free (fd_items);
277   return err;
278 }
279 
280 
281 /* If action is 0, like waitpid.  If action is 1, just release the PID?  */
282 static pid_t
my_waitpid(assuan_context_t ctx,pid_t pid,int nowait,int * status,int options)283 my_waitpid (assuan_context_t ctx, pid_t pid,
284 	    int nowait, int *status, int options)
285 {
286   (void)ctx;
287 #ifdef HAVE_W32_SYSTEM
288   (void)nowait;
289   (void)status;
290   (void)options;
291   (void)pid;  /* Just a number without a kernel object.  */
292 #else
293   /* We can't just release the PID, a waitpid is mandatory.  But
294      NOWAIT in POSIX systems just means the caller already did the
295      waitpid for this child.  */
296   if (! nowait)
297     return _gpgme_ath_waitpid (pid, status, options);
298 #endif
299   return 0;
300 }
301 
302 
303 
304 
305 static int
my_socketpair(assuan_context_t ctx,int namespace,int style,int protocol,assuan_fd_t filedes[2])306 my_socketpair (assuan_context_t ctx, int namespace, int style,
307 	       int protocol, assuan_fd_t filedes[2])
308 {
309 #ifdef HAVE_W32_SYSTEM
310   (void)ctx;
311   (void)namespace;
312   (void)style;
313   (void)protocol;
314   (void)filedes;
315   gpg_err_set_errno (ENOSYS);
316   return -1;
317 #else
318   /* FIXME: Debug output missing.  */
319   return __assuan_socketpair (ctx, namespace, style, protocol, filedes);
320 #endif
321 }
322 
323 
324 static int
my_socket(assuan_context_t ctx,int namespace,int style,int protocol)325 my_socket (assuan_context_t ctx, int namespace, int style, int protocol)
326 {
327   (void)ctx;
328   return _gpgme_io_socket (namespace, style, protocol);
329 }
330 
331 
332 static int
my_connect(assuan_context_t ctx,int sock,struct sockaddr * addr,socklen_t length)333 my_connect (assuan_context_t ctx, int sock, struct sockaddr *addr,
334 	    socklen_t length)
335 {
336   (void)ctx;
337   return _gpgme_io_connect (sock, addr, length);
338 }
339 
340 
341 /* Note for Windows: Ignore the incompatible pointer type warning for
342    my_read and my_write.  Mingw has been changed to use int for
343    ssize_t on 32 bit systems while we use long.  For 64 bit we use
344    int64_t while mingw uses __int64_t.  It doe not matter at all
345    because under Windows long and int are both 32 bit even on 64
346    bit.  */
347 struct assuan_system_hooks _gpgme_assuan_system_hooks =
348   {
349     ASSUAN_SYSTEM_HOOKS_VERSION,
350     my_usleep,
351     my_pipe,
352     my_close,
353     my_read,
354     my_write,
355     my_recvmsg,
356     my_sendmsg,
357     my_spawn,
358     my_waitpid,
359     my_socketpair,
360     my_socket,
361     my_connect
362   };
363