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