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 <https://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 134 : 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 134 : 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 211 : return;
128 : }
129 : for (;;)
130 : {
131 2871 : c = es_fgetc (fderr->stream);
132 2871 : if (c == EOF)
133 : {
134 57 : if (es_feof (fderr->stream))
135 : {
136 23 : fderr->ignore = 1; /* Not anymore needed. */
137 : }
138 34 : 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 57 : break;
147 : }
148 2814 : else if (c == '\n')
149 : {
150 54 : read_and_log_stderr (state, NULL);
151 : }
152 : else
153 : {
154 2760 : 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 2760 : state->buffer[state->used++] = c;
189 : }
190 2814 : }
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 1210 : copy_buffer_do_copy (struct copy_buffer *c, estream_t source, estream_t sink)
228 : {
229 : gpg_error_t err;
230 1210 : size_t nwritten = 0;
231 :
232 1210 : if (c->nread == 0)
233 : {
234 1192 : c->writep = c->buffer;
235 1192 : err = es_read (source, c->buffer, sizeof c->buffer, &c->nread);
236 1192 : if (err)
237 : {
238 18 : if (errno == EAGAIN)
239 18 : 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 1192 : if (c->nread == 0)
248 31 : return 0; /* Done copying. */
249 :
250 :
251 1161 : nwritten = 0;
252 1161 : err = sink? es_write (sink, c->writep, c->nread, &nwritten) : 0;
253 :
254 1161 : assert (nwritten <= c->nread);
255 1161 : c->writep += nwritten;
256 1161 : c->nread -= nwritten;
257 1161 : assert (c->writep - c->buffer <= sizeof c->buffer);
258 :
259 1161 : if (err)
260 : {
261 0 : if (errno == EAGAIN)
262 0 : return 0; /* We will just retry next time. */
263 :
264 0 : return my_error_from_syserror ();
265 : }
266 :
267 1161 : if (sink && es_fflush (sink) && errno != EAGAIN)
268 0 : err = my_error_from_syserror ();
269 :
270 1161 : return err;
271 : }
272 :
273 :
274 : /* Flush the remaining data to SINK. */
275 : static gpg_error_t
276 46 : copy_buffer_flush (struct copy_buffer *c, estream_t sink)
277 : {
278 : gpg_error_t err;
279 :
280 92 : while (c->nread > 0)
281 : {
282 0 : err = copy_buffer_do_copy (c, NULL, sink);
283 0 : if (err)
284 0 : return err;
285 : }
286 :
287 46 : return 0;
288 : }
289 :
290 :
291 :
292 : /* Run the program PGMNAME with the command line arguments given in
293 : * the NULL terminates array ARGV. If INPUT is not NULL it will be
294 : * fed to stdin of the process. stderr is logged using log_info and
295 : * the process' stdout is written to OUTPUT. If OUTPUT is NULL the
296 : * output is discarded. If INEXTRA is given, an additional input
297 : * stream will be passed to the child; to tell the child about this
298 : * ARGV is scanned and the first occurrence of an argument
299 : * "-&@INEXTRA@" is replaced by the concatenation of "-&" and the
300 : * child's file descriptor of the pipe created for the INEXTRA stream.
301 : *
302 : * On error a diagnostic is printed and an error code returned. */
303 : gpg_error_t
304 23 : gnupg_exec_tool_stream (const char *pgmname, const char *argv[],
305 : estream_t input, estream_t inextra,
306 : estream_t output,
307 : exec_tool_status_cb_t status_cb,
308 : void *status_cb_value)
309 : {
310 : gpg_error_t err;
311 23 : pid_t pid = (pid_t) -1;
312 23 : estream_t infp = NULL;
313 23 : estream_t extrafp = NULL;
314 23 : estream_t outfp = NULL, errfp = NULL;
315 : es_poll_t fds[4];
316 : int exceptclose[2];
317 23 : int extrapipe[2] = {-1, -1};
318 : char extrafdbuf[20];
319 23 : const char *argsave = NULL;
320 : int argsaveidx;
321 : int count;
322 : read_and_log_buffer_t fderrstate;
323 23 : struct copy_buffer *cpbuf_in = NULL, *cpbuf_out = NULL, *cpbuf_extra = NULL;
324 :
325 23 : memset (fds, 0, sizeof fds);
326 23 : memset (&fderrstate, 0, sizeof fderrstate);
327 :
328 23 : cpbuf_in = xtrymalloc (sizeof *cpbuf_in);
329 23 : if (cpbuf_in == NULL)
330 : {
331 0 : err = my_error_from_syserror ();
332 0 : goto leave;
333 : }
334 23 : copy_buffer_init (cpbuf_in);
335 :
336 23 : cpbuf_out = xtrymalloc (sizeof *cpbuf_out);
337 23 : if (cpbuf_out == NULL)
338 : {
339 0 : err = my_error_from_syserror ();
340 0 : goto leave;
341 : }
342 23 : copy_buffer_init (cpbuf_out);
343 :
344 23 : cpbuf_extra = xtrymalloc (sizeof *cpbuf_extra);
345 23 : if (cpbuf_extra == NULL)
346 : {
347 0 : err = my_error_from_syserror ();
348 0 : goto leave;
349 : }
350 23 : copy_buffer_init (cpbuf_extra);
351 :
352 23 : fderrstate.pgmname = pgmname;
353 23 : fderrstate.status_cb = status_cb;
354 23 : fderrstate.status_cb_value = status_cb_value;
355 23 : fderrstate.buffer_size = 256;
356 23 : fderrstate.buffer = xtrymalloc (fderrstate.buffer_size);
357 23 : if (!fderrstate.buffer)
358 : {
359 0 : err = my_error_from_syserror ();
360 0 : goto leave;
361 : }
362 :
363 23 : if (inextra)
364 : {
365 0 : err = gnupg_create_outbound_pipe (extrapipe, &extrafp, 1);
366 0 : if (err)
367 : {
368 0 : log_error ("error running outbound pipe for extra fp: %s\n",
369 : gpg_strerror (err));
370 0 : goto leave;
371 : }
372 0 : exceptclose[0] = extrapipe[0]; /* Do not close in child. */
373 0 : exceptclose[1] = -1;
374 : /* Now find the argument marker and replace by the pipe's fd.
375 : Yeah, that is an ugly non-thread safe hack but it safes us to
376 : create a copy of the array. */
377 0 : snprintf (extrafdbuf, sizeof extrafdbuf, "-&%d", extrapipe[0]);
378 0 : for (argsaveidx=0; argv[argsaveidx]; argsaveidx++)
379 0 : if (!strcmp (argv[argsaveidx], "-&@INEXTRA@"))
380 : {
381 0 : argsave = argv[argsaveidx];
382 0 : argv[argsaveidx] = extrafdbuf;
383 0 : break;
384 : }
385 : }
386 : else
387 23 : exceptclose[0] = -1;
388 :
389 23 : err = gnupg_spawn_process (pgmname, argv,
390 : exceptclose, NULL, GNUPG_SPAWN_NONBLOCK,
391 : input? &infp : NULL,
392 : &outfp, &errfp, &pid);
393 23 : if (extrapipe[0] != -1)
394 0 : close (extrapipe[0]);
395 23 : if (argsave)
396 0 : argv[argsaveidx] = argsave;
397 23 : if (err)
398 : {
399 0 : log_error ("error running '%s': %s\n", pgmname, gpg_strerror (err));
400 0 : goto leave;
401 : }
402 :
403 23 : fds[0].stream = infp;
404 23 : fds[0].want_write = 1;
405 23 : if (!input)
406 0 : fds[0].ignore = 1;
407 23 : fds[1].stream = outfp;
408 23 : fds[1].want_read = 1;
409 23 : fds[2].stream = errfp;
410 23 : fds[2].want_read = 1;
411 23 : fds[3].stream = extrafp;
412 23 : fds[3].want_write = 1;
413 23 : if (!inextra)
414 23 : fds[3].ignore = 1;
415 :
416 : /* Now read as long as we have something to poll. We continue
417 : reading even after EOF or error on stdout so that we get the
418 : other error messages or remaining outut. */
419 1290 : while (! (fds[1].ignore && fds[2].ignore))
420 : {
421 1244 : count = es_poll (fds, DIM(fds), -1);
422 1244 : if (count == -1)
423 : {
424 0 : err = my_error_from_syserror ();
425 0 : log_error ("error polling '%s': %s\n", pgmname, gpg_strerror (err));
426 0 : goto leave;
427 : }
428 1244 : if (!count)
429 : {
430 0 : log_debug ("unexpected timeout while polling '%s'\n", pgmname);
431 0 : break;
432 : }
433 :
434 1244 : if (fds[0].got_write)
435 : {
436 575 : err = copy_buffer_do_copy (cpbuf_in, input, fds[0].stream);
437 575 : if (err)
438 : {
439 0 : log_error ("error feeding data to '%s': %s\n",
440 : pgmname, gpg_strerror (err));
441 0 : goto leave;
442 : }
443 :
444 575 : if (es_feof (input))
445 : {
446 23 : err = copy_buffer_flush (cpbuf_in, fds[0].stream);
447 23 : if (err)
448 : {
449 0 : log_error ("error feeding data to '%s': %s\n",
450 : pgmname, gpg_strerror (err));
451 0 : goto leave;
452 : }
453 :
454 23 : fds[0].ignore = 1; /* ready. */
455 23 : es_fclose (infp); infp = NULL;
456 : }
457 : }
458 :
459 1244 : if (fds[3].got_write)
460 : {
461 0 : log_assert (inextra);
462 0 : err = copy_buffer_do_copy (cpbuf_extra, inextra, fds[3].stream);
463 0 : if (err)
464 : {
465 0 : log_error ("error feeding data to '%s': %s\n",
466 : pgmname, gpg_strerror (err));
467 0 : goto leave;
468 : }
469 :
470 0 : if (es_feof (inextra))
471 : {
472 0 : err = copy_buffer_flush (cpbuf_extra, fds[3].stream);
473 0 : if (err)
474 : {
475 0 : log_error ("error feeding data to '%s': %s\n",
476 : pgmname, gpg_strerror (err));
477 0 : goto leave;
478 : }
479 :
480 0 : fds[3].ignore = 1; /* ready. */
481 0 : es_fclose (extrafp); extrafp = NULL;
482 : }
483 : }
484 :
485 1244 : if (fds[1].got_read)
486 : {
487 635 : err = copy_buffer_do_copy (cpbuf_out, fds[1].stream, output);
488 635 : if (err)
489 : {
490 0 : log_error ("error reading data from '%s': %s\n",
491 : pgmname, gpg_strerror (err));
492 0 : goto leave;
493 : }
494 :
495 635 : if (es_feof (fds[1].stream))
496 : {
497 23 : err = copy_buffer_flush (cpbuf_out, output);
498 23 : if (err)
499 : {
500 0 : log_error ("error reading data from '%s': %s\n",
501 : pgmname, gpg_strerror (err));
502 0 : goto leave;
503 : }
504 :
505 23 : fds[1].ignore = 1; /* ready. */
506 : }
507 : }
508 :
509 1244 : if (fds[2].got_read)
510 57 : read_and_log_stderr (&fderrstate, fds + 2);
511 : }
512 :
513 23 : read_and_log_stderr (&fderrstate, NULL); /* Flush. */
514 23 : es_fclose (infp); infp = NULL;
515 23 : es_fclose (extrafp); extrafp = NULL;
516 23 : es_fclose (outfp); outfp = NULL;
517 23 : es_fclose (errfp); errfp = NULL;
518 :
519 23 : err = gnupg_wait_process (pgmname, pid, 1, NULL);
520 23 : pid = (pid_t)(-1);
521 :
522 : leave:
523 23 : if (err && pid != (pid_t) -1)
524 0 : gnupg_kill_process (pid);
525 :
526 23 : es_fclose (infp);
527 23 : es_fclose (extrafp);
528 23 : es_fclose (outfp);
529 23 : es_fclose (errfp);
530 23 : if (pid != (pid_t)(-1))
531 0 : gnupg_wait_process (pgmname, pid, 1, NULL);
532 23 : gnupg_release_process (pid);
533 :
534 23 : copy_buffer_shred (cpbuf_in);
535 23 : xfree (cpbuf_in);
536 23 : copy_buffer_shred (cpbuf_out);
537 23 : xfree (cpbuf_out);
538 23 : copy_buffer_shred (cpbuf_extra);
539 23 : xfree (cpbuf_extra);
540 23 : xfree (fderrstate.buffer);
541 23 : return err;
542 : }
543 :
544 :
545 : /* A dummy free function to pass to 'es_mopen'. */
546 : static void
547 5 : nop_free (void *ptr)
548 : {
549 : (void) ptr;
550 5 : }
551 :
552 : /* Run the program PGMNAME with the command line arguments given in
553 : the NULL terminates array ARGV. If INPUT_STRING is not NULL it
554 : will be fed to stdin of the process. stderr is logged using
555 : log_info and the process' stdout is returned in a newly malloced
556 : buffer RESULT with the length stored at RESULTLEN if not given as
557 : NULL. A hidden Nul is appended to the output. On error NULL is
558 : stored at RESULT, a diagnostic is printed, and an error code
559 : returned. */
560 : gpg_error_t
561 5 : gnupg_exec_tool (const char *pgmname, const char *argv[],
562 : const char *input_string,
563 : char **result, size_t *resultlen)
564 : {
565 : gpg_error_t err;
566 5 : estream_t input = NULL;
567 : estream_t output;
568 : size_t len;
569 : size_t nread;
570 :
571 5 : *result = NULL;
572 5 : if (resultlen)
573 5 : *resultlen = 0;
574 :
575 5 : if (input_string)
576 : {
577 5 : len = strlen (input_string);
578 5 : input = es_mopen ((char *) input_string, len, len,
579 : 0 /* don't grow */, NULL, nop_free, "rb");
580 5 : if (! input)
581 0 : return my_error_from_syserror ();
582 : }
583 :
584 5 : output = es_fopenmem (0, "wb");
585 5 : if (! output)
586 : {
587 0 : err = my_error_from_syserror ();
588 0 : goto leave;
589 : }
590 :
591 5 : err = gnupg_exec_tool_stream (pgmname, argv, input, NULL, output, NULL, NULL);
592 5 : if (err)
593 1 : goto leave;
594 :
595 4 : len = es_ftello (output);
596 4 : err = es_fseek (output, 0, SEEK_SET);
597 4 : if (err)
598 0 : goto leave;
599 :
600 4 : *result = xtrymalloc (len + 1);
601 4 : if (!*result)
602 : {
603 0 : err = my_error_from_syserror ();
604 0 : goto leave;
605 : }
606 :
607 4 : if (len)
608 : {
609 3 : err = es_read (output, *result, len, &nread);
610 3 : if (err)
611 0 : goto leave;
612 3 : if (nread != len)
613 0 : log_fatal ("%s: short read from memstream\n", __func__);
614 : }
615 4 : (*result)[len] = 0;
616 :
617 4 : if (resultlen)
618 4 : *resultlen = len;
619 :
620 : leave:
621 5 : es_fclose (input);
622 5 : es_fclose (output);
623 5 : if (err)
624 : {
625 1 : xfree (*result);
626 1 : *result = NULL;
627 : }
628 5 : return err;
629 : }
|