Line data Source code
1 : /* exectool.c - Utility functions to execute a helper tool
2 : * Copyright (C) 2015 Werner Koch
3 : * Copyright (C) 2016 g10 Code GmbH
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * This file is free software; you can redistribute it and/or modify
8 : * it under the terms of either
9 : *
10 : * - the GNU Lesser General Public License as published by the Free
11 : * Software Foundation; either version 3 of the License, or (at
12 : * your option) any later version.
13 : *
14 : * or
15 : *
16 : * - the GNU General Public License as published by the Free
17 : * Software Foundation; either version 2 of the License, or (at
18 : * your option) any later version.
19 : *
20 : * or both in parallel, as here.
21 : *
22 : * This file is distributed in the hope that it will be useful,
23 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 : * GNU General Public License for more details.
26 : *
27 : * You should have received a copy of the GNU General Public License
28 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
29 : */
30 :
31 : #include <config.h>
32 : #include <stdio.h>
33 : #include <stdlib.h>
34 : #include <string.h>
35 : #include <stdarg.h>
36 : #include <errno.h>
37 : #include <assert.h>
38 : #include <gpg-error.h>
39 :
40 : #include <assuan.h>
41 : #include "i18n.h"
42 : #include "logging.h"
43 : #include "membuf.h"
44 : #include "mischelp.h"
45 : #include "exechelp.h"
46 : #include "sysutils.h"
47 : #include "util.h"
48 : #include "exectool.h"
49 :
50 : typedef struct
51 : {
52 : const char *pgmname;
53 : exec_tool_status_cb_t status_cb;
54 : void *status_cb_value;
55 : int cont;
56 : size_t used;
57 : size_t buffer_size;
58 : char *buffer;
59 : } read_and_log_buffer_t;
60 :
61 :
62 : static inline gpg_error_t
63 0 : my_error_from_syserror (void)
64 : {
65 0 : return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
66 : }
67 :
68 :
69 : static void
70 131 : read_and_log_stderr (read_and_log_buffer_t *state, es_poll_t *fderr)
71 : {
72 : gpg_error_t err;
73 : int c;
74 :
75 131 : if (!fderr)
76 : {
77 : /* Flush internal buffer. */
78 77 : if (state->used)
79 : {
80 : const char *pname;
81 : int len;
82 :
83 54 : state->buffer[state->used] = 0;
84 54 : state->used = 0;
85 :
86 54 : pname = strrchr (state->pgmname, '/');
87 54 : if (pname && pname != state->pgmname && pname[1])
88 54 : pname++;
89 : else
90 0 : pname = state->pgmname;
91 54 : len = strlen (pname);
92 :
93 54 : if (state->status_cb
94 0 : && !strncmp (state->buffer, "[GNUPG:] ", 9)
95 0 : && state->buffer[9] >= 'A' && state->buffer[9] <= 'Z')
96 0 : {
97 : char *rest;
98 :
99 0 : rest = strchr (state->buffer + 9, ' ');
100 0 : if (!rest)
101 : {
102 : /* Set REST to an empty string. */
103 0 : rest = state->buffer + strlen (state->buffer);
104 : }
105 : else
106 : {
107 0 : *rest++ = 0;
108 0 : trim_spaces (rest);
109 : }
110 0 : state->status_cb (state->status_cb_value,
111 0 : state->buffer + 9, rest);
112 : }
113 54 : else if (!state->cont
114 54 : && !strncmp (state->buffer, pname, len)
115 48 : && strlen (state->buffer) > strlen (pname)
116 48 : && state->buffer[len] == ':' )
117 : {
118 : /* PGMNAME plus colon is identical to the start of
119 : the output: print only the output. */
120 48 : log_info ("%s\n", state->buffer);
121 : }
122 : else
123 12 : log_info ("%s%c %s\n",
124 6 : pname, state->cont? '+':':', state->buffer);
125 : }
126 77 : state->cont = 0;
127 208 : return;
128 : }
129 : for (;;)
130 : {
131 2730 : c = es_fgetc (fderr->stream);
132 2730 : if (c == EOF)
133 : {
134 54 : if (es_feof (fderr->stream))
135 : {
136 23 : fderr->ignore = 1; /* Not anymore needed. */
137 : }
138 31 : else if (es_ferror (fderr->stream))
139 : {
140 0 : err = my_error_from_syserror ();
141 0 : log_error ("error reading stderr of '%s': %s\n",
142 : state->pgmname, gpg_strerror (err));
143 0 : fderr->ignore = 1; /* Disable. */
144 : }
145 :
146 54 : break;
147 : }
148 2676 : else if (c == '\n')
149 : {
150 54 : read_and_log_stderr (state, NULL);
151 : }
152 : else
153 : {
154 2622 : if (state->used >= state->buffer_size - 1)
155 : {
156 0 : if (state->status_cb)
157 : {
158 : /* A status callback requires that we have a full
159 : * line. Thus we need to enlarget the buffer in
160 : * this case. */
161 : char *newbuffer;
162 0 : size_t newsize = state->buffer_size + 256;
163 :
164 0 : newbuffer = xtrymalloc (newsize);
165 0 : if (!newbuffer)
166 : {
167 0 : log_error ("error allocating memory for status cb: %s\n",
168 : gpg_strerror (my_error_from_syserror ()));
169 : /* We better disable the status CB in this case. */
170 0 : state->status_cb = NULL;
171 0 : read_and_log_stderr (state, NULL);
172 0 : state->cont = 1;
173 : }
174 : else
175 : {
176 0 : memcpy (newbuffer, state->buffer, state->used);
177 0 : xfree (state->buffer);
178 0 : state->buffer = newbuffer;
179 0 : state->buffer_size = newsize;
180 : }
181 : }
182 : else
183 : {
184 0 : read_and_log_stderr (state, NULL);
185 0 : state->cont = 1;
186 : }
187 : }
188 2622 : state->buffer[state->used++] = c;
189 : }
190 2676 : }
191 : }
192 :
193 :
194 :
195 : /* A buffer to copy from one stream to another. */
196 : struct copy_buffer
197 : {
198 : char buffer[4096];
199 : char *writep;
200 : size_t nread;
201 : };
202 :
203 :
204 : /* Initialize a copy buffer. */
205 : static void
206 69 : copy_buffer_init (struct copy_buffer *c)
207 : {
208 69 : c->writep = c->buffer;
209 69 : c->nread = 0;
210 69 : }
211 :
212 :
213 : /* Securely wipe a copy buffer. */
214 : static void
215 69 : copy_buffer_shred (struct copy_buffer *c)
216 : {
217 69 : if (c == NULL)
218 69 : return;
219 69 : wipememory (c->buffer, sizeof c->buffer);
220 69 : c->writep = NULL;
221 69 : c->nread = ~0U;
222 : }
223 :
224 :
225 : /* Copy data from SOURCE to SINK using copy buffer C. */
226 : static gpg_error_t
227 1202 : copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
228 : {
229 : gpg_error_t err;
230 1202 : size_t nwritten = 0;
231 :
232 1202 : if (c->nread == 0)
233 : {
234 1188 : c->writep = c->buffer;
235 1188 : err = es_read (source, c->buffer, sizeof c->buffer, &c->nread);
236 1188 : if (err)
237 : {
238 14 : if (errno == EAGAIN)
239 14 : return 0; /* We will just retry next time. */
240 :
241 0 : return my_error_from_syserror ();
242 : }
243 :
244 1174 : assert (c->nread <= sizeof c->buffer);
245 : }
246 :
247 1188 : if (c->nread == 0)
248 29 : return 0; /* Done copying. */
249 :
250 :
251 1159 : err = sink? es_write (sink, c->writep, c->nread, &nwritten) : 0;
252 1159 : if (err)
253 : {
254 0 : if (errno == EAGAIN)
255 0 : return 0; /* We will just retry next time. */
256 :
257 0 : return my_error_from_syserror ();
258 : }
259 :
260 1159 : assert (nwritten <= c->nread);
261 1159 : c->writep += nwritten;
262 1159 : c->nread -= nwritten;
263 1159 : assert (c->writep - c->buffer <= sizeof c->buffer);
264 :
265 1159 : if (sink && es_fflush (sink) && errno != EAGAIN)
266 0 : err = my_error_from_syserror ();
267 :
268 1159 : return err;
269 : }
270 :
271 :
272 : /* Flush the remaining data to SINK. */
273 : static gpg_error_t
274 46 : copy_buffer_flush (struct copy_buffer *c, estream_t sink)
275 : {
276 : gpg_error_t err;
277 :
278 92 : while (c->nread > 0)
279 : {
280 0 : err = copy_buffer_do_copy (c, NULL, sink);
281 0 : if (err)
282 0 : return err;
283 : }
284 :
285 46 : return 0;
286 : }
287 :
288 :
289 :
290 : /* Run the program PGMNAME with the command line arguments given in
291 : * the NULL terminates array ARGV. If INPUT is not NULL it will be
292 : * fed to stdin of the process. stderr is logged using log_info and
293 : * the process' stdout is written to OUTPUT. If OUTPUT is NULL the
294 : * output is discarded. If INEXTRA is given, an additional input
295 : * stream will be passed to the child; to tell the child about this
296 : * ARGV is scanned and the first occurrence of an argument
297 : * "-&@INEXTRA@" is replaced by the concatenation of "-&" and the
298 : * child's file descriptor of the pipe created for the INEXTRA stream.
299 : *
300 : * On error a diagnostic is printed and an error code returned. */
301 : gpg_error_t
302 23 : gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
303 : estream_t input, estream_t inextra,
304 : estream_t output,
305 : exec_tool_status_cb_t status_cb,
306 : void *status_cb_value)
307 : {
308 : gpg_error_t err;
309 23 : pid_t pid = (pid_t) -1;
310 23 : estream_t infp = NULL;
311 23 : estream_t extrafp = NULL;
312 23 : estream_t outfp = NULL, errfp = NULL;
313 : es_poll_t fds[4];
314 : int exceptclose[2];
315 23 : int extrapipe[2] = {-1, -1};
316 : char extrafdbuf[20];
317 23 : const char *argsave = NULL;
318 : int argsaveidx;
319 : int count;
320 : read_and_log_buffer_t fderrstate;
321 23 : struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL;
322 :
323 23 : memset (fds, 0, sizeof fds);
324 23 : memset (&fderrstate, 0, sizeof fderrstate);
325 :
326 23 : cpbuf_in = xtrymalloc (sizeof *cpbuf_in);
327 23 : if (cpbuf_in == NULL)
328 : {
329 0 : err = my_error_from_syserror ();
330 0 : goto leave;
331 : }
332 23 : copy_buffer_init (cpbuf_in);
333 :
334 23 : cpbuf_out = xtrymalloc (sizeof *cpbuf_out);
335 23 : if (cpbuf_out == NULL)
336 : {
337 0 : err = my_error_from_syserror ();
338 0 : goto leave;
339 : }
340 23 : copy_buffer_init (cpbuf_out);
341 :
342 23 : cpbuf_extra = xtrymalloc (sizeof *cpbuf_extra);
343 23 : if (cpbuf_extra == NULL)
344 : {
345 0 : err = my_error_from_syserror ();
346 0 : goto leave;
347 : }
348 23 : copy_buffer_init (cpbuf_extra);
349 :
350 23 : fderrstate.pgmname = pgmname;
351 23 : fderrstate.status_cb = status_cb;
352 23 : fderrstate.status_cb_value = status_cb_value;
353 23 : fderrstate.buffer_size = 256;
354 23 : fderrstate.buffer = xtrymalloc (fderrstate.buffer_size);
355 23 : if (!fderrstate.buffer)
356 : {
357 0 : err = my_error_from_syserror ();
358 0 : goto leave;
359 : }
360 :
361 23 : if (inextra)
362 : {
363 0 : err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1);
364 0 : if (err)
365 : {
366 0 : log_error ("error running outbound pipe for extra fp: %s\n",
367 : gpg_strerror (err));
368 0 : goto leave;
369 : }
370 0 : exceptclose[0] = extrapipe[0]; /* Do not close in child. */
371 0 : exceptclose[1] = -1;
372 : /* Now find the argument marker and replace by the pipe's fd.
373 : Yeah, that is an ugly non-thread safe hack but it safes us to
374 : create a copy of the array. */
375 0 : snprintf (extrafdbuf, sizeof extrafdbuf, "-&%d", extrapipe[0]);
376 0 : for (argsaveidx=0; argv[argsaveidx]; argsaveidx++)
377 0 : if (!strcmp (argv[argsaveidx], "-&@INEXTRA@"))
378 : {
379 0 : argsave = argv[argsaveidx];
380 0 : argv[argsaveidx] = extrafdbuf;
381 0 : break;
382 : }
383 : }
384 : else
385 23 : exceptclose[0] = -1;
386 :
387 23 : err = gnupg_spawn_process (pgmname, argv,
388 : exceptclose, NULL, GNUPG_SPAWN_NONBLOCK,
389 : input? &infp : NULL,
390 : &outfp, &errfp, &pid);
391 23 : if (extrapipe[0] != -1)
392 0 : close (extrapipe[0]);
393 23 : if (argsave)
394 0 : argv[argsaveidx] = argsave;
395 23 : if (err)
396 : {
397 0 : log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
398 0 : goto leave;
399 : }
400 :
401 23 : fds[0].stream = infp;
402 23 : fds[0].want_write = 1;
403 23 : if (!input)
404 0 : fds[0].ignore = 1;
405 23 : fds[1].stream = outfp;
406 23 : fds[1].want_read = 1;
407 23 : fds[2].stream = errfp;
408 23 : fds[2].want_read = 1;
409 23 : fds[3].stream = extrafp;
410 23 : fds[3].want_write = 1;
411 23 : if (!inextra)
412 23 : fds[3].ignore = 1;
413 :
414 : /* Now read as long as we have something to poll. We continue
415 : reading even after EOF or error on stdout so that we get the
416 : other error messages or remaining outut. */
417 1262 : while (! (fds[1].ignore && fds[2].ignore))
418 : {
419 1216 : count = es_poll (fds, DIM(fds), -1);
420 1216 : if (count == -1)
421 : {
422 0 : err = my_error_from_syserror ();
423 0 : log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
424 0 : goto leave;
425 : }
426 1216 : if (!count)
427 : {
428 0 : log_debug ("unexpected timeout while polling '%s'\n", pgmname);
429 0 : break;
430 : }
431 :
432 1216 : if (fds[0].got_write)
433 : {
434 575 : err = copy_buffer_do_copy (cpbuf_in, input, fds[0].stream);
435 575 : if (err)
436 : {
437 0 : log_error ("error feeding data to '%s': %s\n",
438 : pgmname, gpg_strerror (err));
439 0 : goto leave;
440 : }
441 :
442 575 : if (es_feof (input))
443 : {
444 23 : err = copy_buffer_flush (cpbuf_in, fds[0].stream);
445 23 : if (err)
446 : {
447 0 : log_error ("error feeding data to '%s': %s\n",
448 : pgmname, gpg_strerror (err));
449 0 : goto leave;
450 : }
451 :
452 23 : fds[0].ignore = 1; /* ready. */
453 23 : es_fclose (infp); infp = NULL;
454 : }
455 : }
456 :
457 1216 : if (fds[3].got_write)
458 : {
459 0 : log_assert (inextra);
460 0 : err = copy_buffer_do_copy (cpbuf_extra, inextra, fds[3].stream);
461 0 : if (err)
462 : {
463 0 : log_error ("error feeding data to '%s': %s\n",
464 : pgmname, gpg_strerror (err));
465 0 : goto leave;
466 : }
467 :
468 0 : if (es_feof (inextra))
469 : {
470 0 : err = copy_buffer_flush (cpbuf_extra, fds[3].stream);
471 0 : if (err)
472 : {
473 0 : log_error ("error feeding data to '%s': %s\n",
474 : pgmname, gpg_strerror (err));
475 0 : goto leave;
476 : }
477 :
478 0 : fds[3].ignore = 1; /* ready. */
479 0 : es_fclose (extrafp); extrafp = NULL;
480 : }
481 : }
482 :
483 1216 : if (fds[1].got_read)
484 : {
485 627 : err = copy_buffer_do_copy (cpbuf_out, fds[1].stream, output);
486 627 : if (err)
487 : {
488 0 : log_error ("error reading data from '%s': %s\n",
489 : pgmname, gpg_strerror (err));
490 0 : goto leave;
491 : }
492 :
493 627 : if (es_feof (fds[1].stream))
494 : {
495 23 : err = copy_buffer_flush (cpbuf_out, output);
496 23 : if (err)
497 : {
498 0 : log_error ("error reading data from '%s': %s\n",
499 : pgmname, gpg_strerror (err));
500 0 : goto leave;
501 : }
502 :
503 23 : fds[1].ignore = 1; /* ready. */
504 : }
505 : }
506 :
507 1216 : if (fds[2].got_read)
508 54 : read_and_log_stderr (&fderrstate, fds + 2);
509 : }
510 :
511 23 : read_and_log_stderr (&fderrstate, NULL); /* Flush. */
512 23 : es_fclose (infp); infp = NULL;
513 23 : es_fclose (extrafp); extrafp = NULL;
514 23 : es_fclose (outfp); outfp = NULL;
515 23 : es_fclose (errfp); errfp = NULL;
516 :
517 23 : err = gnupg_wait_process (pgmname, pid, 1, NULL);
518 23 : pid = (pid_t)(-1);
519 :
520 : leave:
521 23 : if (err && pid != (pid_t) -1)
522 0 : gnupg_kill_process (pid);
523 :
524 23 : es_fclose (infp);
525 23 : es_fclose (extrafp);
526 23 : es_fclose (outfp);
527 23 : es_fclose (errfp);
528 23 : if (pid != (pid_t)(-1))
529 0 : gnupg_wait_process (pgmname, pid, 1, NULL);
530 23 : gnupg_release_process (pid);
531 :
532 23 : copy_buffer_shred (cpbuf_in);
533 23 : xfree (cpbuf_in);
534 23 : copy_buffer_shred (cpbuf_out);
535 23 : xfree (cpbuf_out);
536 23 : copy_buffer_shred (cpbuf_extra);
537 23 : xfree (cpbuf_extra);
538 23 : xfree (fderrstate.buffer);
539 23 : return err;
540 : }
541 :
542 :
543 : /* A dummy free function to pass to 'es_mopen'. */
544 : static void
545 5 : nop_free (void *ptr)
546 : {
547 : (void) ptr;
548 5 : }
549 :
550 : /* Run the program PGMNAME with the command line arguments given in
551 : the NULL terminates array ARGV. If INPUT_STRING is not NULL it
552 : will be fed to stdin of the process. stderr is logged using
553 : log_info and the process' stdout is returned in a newly malloced
554 : buffer RESULT with the length stored at RESULTLEN if not given as
555 : NULL. A hidden Nul is appended to the output. On error NULL is
556 : stored at RESULT, a diagnostic is printed, and an error code
557 : returned. */
558 : gpg_error_t
559 5 : gnupg_exec_tool (const char *pgmname, const char *argv[],
560 : const char *input_string,
561 : char **result, size_t *resultlen)
562 : {
563 : gpg_error_t err;
564 5 : estream_t input = NULL;
565 : estream_t output;
566 : size_t len;
567 : size_t nread;
568 :
569 5 : *result = NULL;
570 5 : if (resultlen)
571 5 : *resultlen = 0;
572 :
573 5 : if (input_string)
574 : {
575 5 : len = strlen (input_string);
576 5 : input = es_mopen ((char *) input_string, len, len,
577 : 0 /* don't grow */, NULL, nop_free, "rb");
578 5 : if (! input)
579 0 : return my_error_from_syserror ();
580 : }
581 :
582 5 : output = es_fopenmem (0, "wb");
583 5 : if (! output)
584 : {
585 0 : err = my_error_from_syserror ();
586 0 : goto leave;
587 : }
588 :
589 5 : err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL);
590 5 : if (err)
591 1 : goto leave;
592 :
593 4 : len = es_ftello (output);
594 4 : err = es_fseek (output, 0, SEEK_SET);
595 4 : if (err)
596 0 : goto leave;
597 :
598 4 : *result = xtrymalloc (len + 1);
599 4 : if (!*result)
600 : {
601 0 : err = my_error_from_syserror ();
602 0 : goto leave;
603 : }
604 :
605 4 : if (len)
606 : {
607 3 : err = es_read (output, *result, len, &nread);
608 3 : if (err)
609 0 : goto leave;
610 3 : if (nread != len)
611 0 : log_fatal ("%s: short read from memstream\n", __func__);
612 : }
613 4 : (*result)[len] = 0;
614 :
615 4 : if (resultlen)
616 4 : *resultlen = len;
617 :
618 : leave:
619 5 : es_fclose (input);
620 5 : es_fclose (output);
621 5 : if (err)
622 : {
623 1 : xfree (*result);
624 1 : *result = NULL;
625 : }
626 5 : return err;
627 : }
|