1 /* call-gpg.c - Communication with the GPG
2 * Copyright (C) 2009 Free Software Foundation, Inc.
3 *
4 * This file is part of GnuPG.
5 *
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21
22 #include <assert.h>
23 #include <assuan.h>
24 #include <errno.h>
25 #include <npth.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <time.h>
30
31 #include "call-gpg.h"
32 #include "exechelp.h"
33 #include "i18n.h"
34 #include "logging.h"
35 #include "membuf.h"
36 #include "strlist.h"
37 #include "util.h"
38
39
40 static GPGRT_INLINE gpg_error_t
my_error_from_syserror(void)41 my_error_from_syserror (void)
42 {
43 return gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
44 }
45
46 static GPGRT_INLINE gpg_error_t
my_error_from_errno(int e)47 my_error_from_errno (int e)
48 {
49 return gpg_err_make (default_errsource, gpg_err_code_from_errno (e));
50 }
51
52
53 /* Fire up a new GPG. Handle the server's initial greeting. Returns
54 0 on success and stores the assuan context at R_CTX. */
55 static gpg_error_t
start_gpg(ctrl_t ctrl,const char * gpg_program,strlist_t gpg_arguments,int input_fd,int output_fd,assuan_context_t * r_ctx)56 start_gpg (ctrl_t ctrl, const char *gpg_program, strlist_t gpg_arguments,
57 int input_fd, int output_fd, assuan_context_t *r_ctx)
58 {
59 gpg_error_t err;
60 assuan_context_t ctx = NULL;
61 const char *pgmname;
62 const char **argv;
63 assuan_fd_t no_close_list[5];
64 int i;
65 char line[ASSUAN_LINELENGTH];
66
67 (void)ctrl;
68
69 *r_ctx = NULL;
70
71 err = assuan_new (&ctx);
72 if (err)
73 {
74 log_error ("can't allocate assuan context: %s\n", gpg_strerror (err));
75 return err;
76 }
77
78 /* The first time we are used, initialize the gpg_program variable. */
79 if ( !gpg_program || !*gpg_program )
80 gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
81
82 /* Compute argv[0]. */
83 if ( !(pgmname = strrchr (gpg_program, '/')))
84 pgmname = gpg_program;
85 else
86 pgmname++;
87
88 if (fflush (NULL))
89 {
90 err = my_error_from_syserror ();
91 log_error ("error flushing pending output: %s\n", gpg_strerror (err));
92 return err;
93 }
94
95 argv = xtrycalloc (strlist_length (gpg_arguments) + 3, sizeof *argv);
96 if (argv == NULL)
97 {
98 err = my_error_from_syserror ();
99 return err;
100 }
101 i = 0;
102 argv[i++] = pgmname;
103 argv[i++] = "--server";
104 for (; gpg_arguments; gpg_arguments = gpg_arguments->next)
105 argv[i++] = gpg_arguments->d;
106 argv[i++] = NULL;
107
108 i = 0;
109 if (log_get_fd () != -1)
110 no_close_list[i++] = assuan_fd_from_posix_fd (log_get_fd ());
111 no_close_list[i++] = assuan_fd_from_posix_fd (fileno (stderr));
112 if (input_fd != -1)
113 no_close_list[i++] = assuan_fd_from_posix_fd (input_fd);
114 if (output_fd != -1)
115 no_close_list[i++] = assuan_fd_from_posix_fd (output_fd);
116 no_close_list[i] = ASSUAN_INVALID_FD;
117
118 /* Connect to GPG and perform initial handshaking. */
119 err = assuan_pipe_connect (ctx, gpg_program, argv, no_close_list,
120 NULL, NULL, 0);
121 if (err)
122 {
123 assuan_release (ctx);
124 log_error ("can't connect to GPG: %s\n", gpg_strerror (err));
125 return gpg_error (GPG_ERR_NO_ENGINE);
126 }
127
128 if (input_fd != -1)
129 {
130 snprintf (line, sizeof line, "INPUT FD=%d", input_fd);
131 err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
132 if (err)
133 {
134 assuan_release (ctx);
135 log_error ("error sending INPUT command: %s\n", gpg_strerror (err));
136 return err;
137 }
138 }
139
140 if (output_fd != -1)
141 {
142 snprintf (line, sizeof line, "OUTPUT FD=%d", output_fd);
143 err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
144 if (err)
145 {
146 assuan_release (ctx);
147 log_error ("error sending OUTPUT command: %s\n", gpg_strerror (err));
148 return err;
149 }
150 }
151
152 *r_ctx = ctx;
153 return 0;
154 }
155
156
157 /* Release the assuan context created by start_gpg. */
158 static void
release_gpg(assuan_context_t ctx)159 release_gpg (assuan_context_t ctx)
160 {
161 assuan_release (ctx);
162 }
163
164
165
166 /* The data passed to the writer_thread. */
167 struct writer_thread_parms
168 {
169 int fd;
170 const void *data;
171 size_t datalen;
172 estream_t stream;
173 gpg_error_t *err_addr;
174 };
175
176
177 /* The thread started by start_writer. */
178 static void *
writer_thread_main(void * arg)179 writer_thread_main (void *arg)
180 {
181 gpg_error_t err = 0;
182 struct writer_thread_parms *parm = arg;
183 char _buffer[4096];
184 char *buffer;
185 size_t length;
186
187 if (parm->stream)
188 {
189 buffer = _buffer;
190 err = es_read (parm->stream, buffer, sizeof _buffer, &length);
191 if (err)
192 {
193 log_error ("reading stream failed: %s\n", gpg_strerror (err));
194 goto leave;
195 }
196 }
197 else
198 {
199 buffer = (char *) parm->data;
200 length = parm->datalen;
201 }
202
203 while (length)
204 {
205 ssize_t nwritten;
206
207 nwritten = npth_write (parm->fd, buffer, length < 4096? length:4096);
208 if (nwritten < 0)
209 {
210 if (errno == EINTR)
211 continue;
212 err = my_error_from_syserror ();
213 break; /* Write error. */
214 }
215 length -= nwritten;
216
217 if (parm->stream)
218 {
219 if (length == 0)
220 {
221 err = es_read (parm->stream, buffer, sizeof _buffer, &length);
222 if (err)
223 {
224 log_error ("reading stream failed: %s\n",
225 gpg_strerror (err));
226 break;
227 }
228 if (length == 0)
229 /* We're done. */
230 break;
231 }
232 }
233 else
234 buffer += nwritten;
235 }
236
237 leave:
238 *parm->err_addr = err;
239 if (close (parm->fd))
240 log_error ("closing writer fd %d failed: %s\n", parm->fd, strerror (errno));
241 xfree (parm);
242 return NULL;
243 }
244
245
246 /* Fire up a thread to send (DATA,DATALEN) to the file descriptor FD.
247 On success the thread receives the ownership over FD. The thread
248 ID is stored at R_TID. WRITER_ERR is the address of an gpg_error_t
249 variable to receive a possible write error after the thread has
250 finished. */
251 static gpg_error_t
start_writer(int fd,const void * data,size_t datalen,estream_t stream,npth_t * r_thread,gpg_error_t * err_addr)252 start_writer (int fd, const void *data, size_t datalen, estream_t stream,
253 npth_t *r_thread, gpg_error_t *err_addr)
254 {
255 gpg_error_t err;
256 struct writer_thread_parms *parm;
257 npth_attr_t tattr;
258 npth_t thread;
259 int ret;
260
261 memset (r_thread, '\0', sizeof (*r_thread));
262 *err_addr = 0;
263
264 parm = xtrymalloc (sizeof *parm);
265 if (!parm)
266 return my_error_from_syserror ();
267 parm->fd = fd;
268 parm->data = data;
269 parm->datalen = datalen;
270 parm->stream = stream;
271 parm->err_addr = err_addr;
272
273 npth_attr_init (&tattr);
274 npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
275
276 ret = npth_create (&thread, &tattr, writer_thread_main, parm);
277 if (ret)
278 {
279 err = my_error_from_errno (ret);
280 log_error ("error spawning writer thread: %s\n", gpg_strerror (err));
281 }
282 else
283 {
284 npth_setname_np (thread, "fd-writer");
285 err = 0;
286 *r_thread = thread;
287 }
288 npth_attr_destroy (&tattr);
289
290 return err;
291 }
292
293
294
295 /* The data passed to the reader_thread. */
296 struct reader_thread_parms
297 {
298 int fd;
299 membuf_t *mb;
300 estream_t stream;
301 gpg_error_t *err_addr;
302 };
303
304
305 /* The thread started by start_reader. */
306 static void *
reader_thread_main(void * arg)307 reader_thread_main (void *arg)
308 {
309 gpg_error_t err = 0;
310 struct reader_thread_parms *parm = arg;
311 char buffer[4096];
312 int nread;
313
314 while ( (nread = npth_read (parm->fd, buffer, sizeof buffer)) )
315 {
316 if (nread < 0)
317 {
318 if (errno == EINTR)
319 continue;
320 err = my_error_from_syserror ();
321 break; /* Read error. */
322 }
323
324 if (parm->stream)
325 {
326 const char *p = buffer;
327 size_t nwritten;
328 while (nread)
329 {
330 err = es_write (parm->stream, p, nread, &nwritten);
331 if (err)
332 {
333 log_error ("writing stream failed: %s\n",
334 gpg_strerror (err));
335 goto leave;
336 }
337 nread -= nwritten;
338 p += nwritten;
339 }
340 }
341 else
342 put_membuf (parm->mb, buffer, nread);
343 }
344
345 leave:
346 *parm->err_addr = err;
347 if (close (parm->fd))
348 log_error ("closing reader fd %d failed: %s\n", parm->fd, strerror (errno));
349 xfree (parm);
350 return NULL;
351 }
352
353
354 /* Fire up a thread to receive data from the file descriptor FD. On
355 success the thread receives the ownership over FD. The thread ID
356 is stored at R_TID. After the thread has finished an error from
357 the thread will be stored at ERR_ADDR. */
358 static gpg_error_t
start_reader(int fd,membuf_t * mb,estream_t stream,npth_t * r_thread,gpg_error_t * err_addr)359 start_reader (int fd, membuf_t *mb, estream_t stream,
360 npth_t *r_thread, gpg_error_t *err_addr)
361 {
362 gpg_error_t err;
363 struct reader_thread_parms *parm;
364 npth_attr_t tattr;
365 npth_t thread;
366 int ret;
367
368 memset (r_thread, '\0', sizeof (*r_thread));
369 *err_addr = 0;
370
371 parm = xtrymalloc (sizeof *parm);
372 if (!parm)
373 return my_error_from_syserror ();
374 parm->fd = fd;
375 parm->mb = mb;
376 parm->stream = stream;
377 parm->err_addr = err_addr;
378
379 npth_attr_init (&tattr);
380 npth_attr_setdetachstate (&tattr, NPTH_CREATE_JOINABLE);
381
382 ret = npth_create (&thread, &tattr, reader_thread_main, parm);
383 if (ret)
384 {
385 err = my_error_from_errno (ret);
386 log_error ("error spawning reader thread: %s\n", gpg_strerror (err));
387 }
388 else
389 {
390 npth_setname_np (thread, "fd-reader");
391 err = 0;
392 *r_thread = thread;
393 }
394 npth_attr_destroy (&tattr);
395
396 return err;
397 }
398
399
400
401
402 /* Call GPG to encrypt a block of data.
403
404
405 */
406 static gpg_error_t
_gpg_encrypt(ctrl_t ctrl,const char * gpg_program,strlist_t gpg_arguments,const void * plain,size_t plainlen,estream_t plain_stream,strlist_t keys,membuf_t * reader_mb,estream_t cipher_stream)407 _gpg_encrypt (ctrl_t ctrl,
408 const char *gpg_program,
409 strlist_t gpg_arguments,
410 const void *plain, size_t plainlen,
411 estream_t plain_stream,
412 strlist_t keys,
413 membuf_t *reader_mb,
414 estream_t cipher_stream)
415 {
416 gpg_error_t err;
417 assuan_context_t ctx = NULL;
418 int outbound_fds[2] = { -1, -1 };
419 int inbound_fds[2] = { -1, -1 };
420 npth_t writer_thread = (npth_t)0;
421 npth_t reader_thread = (npth_t)0;
422 gpg_error_t writer_err, reader_err;
423 char line[ASSUAN_LINELENGTH];
424 strlist_t sl;
425 int ret;
426
427 /* Make sure that either the stream interface xor the buffer
428 interface is used. */
429 assert ((plain == NULL) != (plain_stream == NULL));
430 assert ((reader_mb == NULL) != (cipher_stream == NULL));
431
432 /* Create two pipes. */
433 err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0);
434 if (!err)
435 err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0);
436 if (err)
437 {
438 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
439 goto leave;
440 }
441
442 /* Start GPG and send the INPUT and OUTPUT commands. */
443 err = start_gpg (ctrl, gpg_program, gpg_arguments,
444 outbound_fds[0], inbound_fds[1], &ctx);
445 if (err)
446 goto leave;
447 close (outbound_fds[0]); outbound_fds[0] = -1;
448 close (inbound_fds[1]); inbound_fds[1] = -1;
449
450 /* Start a writer thread to feed the INPUT command of the server. */
451 err = start_writer (outbound_fds[1], plain, plainlen, plain_stream,
452 &writer_thread, &writer_err);
453 if (err)
454 return err;
455 outbound_fds[1] = -1; /* The thread owns the FD now. */
456
457 /* Start a reader thread to eat from the OUTPUT command of the
458 server. */
459 err = start_reader (inbound_fds[0], reader_mb, cipher_stream,
460 &reader_thread, &reader_err);
461 if (err)
462 return err;
463 outbound_fds[0] = -1; /* The thread owns the FD now. */
464
465 /* Run the encryption. */
466 for (sl = keys; sl; sl = sl->next)
467 {
468 snprintf (line, sizeof line, "RECIPIENT -- %s", sl->d);
469 err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
470 if (err)
471 {
472 log_error ("the engine's RECIPIENT command failed: %s <%s>\n",
473 gpg_strerror (err), gpg_strsource (err));
474 goto leave;
475 }
476 }
477
478 err = assuan_transact (ctx, "ENCRYPT", NULL, NULL, NULL, NULL, NULL, NULL);
479 if (err)
480 {
481 log_error ("the engine's ENCRYPT command failed: %s <%s>\n",
482 gpg_strerror (err), gpg_strsource (err));
483 goto leave;
484 }
485
486 /* Wait for reader and return the data. */
487 ret = npth_join (reader_thread, NULL);
488 if (ret)
489 {
490 err = my_error_from_errno (ret);
491 log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err));
492 goto leave;
493 }
494 /* FIXME: Not really valid, as npth_t is an opaque type. */
495 memset (&reader_thread, '\0', sizeof (reader_thread));
496 if (reader_err)
497 {
498 err = reader_err;
499 log_error ("read error in reader thread: %s\n", gpg_strerror (err));
500 goto leave;
501 }
502
503 /* Wait for the writer to catch a writer error. */
504 ret = npth_join (writer_thread, NULL);
505 if (ret)
506 {
507 err = my_error_from_errno (ret);
508 log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err));
509 goto leave;
510 }
511 memset (&writer_thread, '\0', sizeof (writer_thread));
512 if (writer_err)
513 {
514 err = writer_err;
515 log_error ("write error in writer thread: %s\n", gpg_strerror (err));
516 goto leave;
517 }
518
519 leave:
520 /* FIXME: Not valid, as npth_t is an opaque type. */
521 if (reader_thread)
522 npth_detach (reader_thread);
523 if (writer_thread)
524 npth_detach (writer_thread);
525 if (outbound_fds[0] != -1)
526 close (outbound_fds[0]);
527 if (outbound_fds[1] != -1)
528 close (outbound_fds[1]);
529 if (inbound_fds[0] != -1)
530 close (inbound_fds[0]);
531 if (inbound_fds[1] != -1)
532 close (inbound_fds[1]);
533 release_gpg (ctx);
534 return err;
535 }
536
537 gpg_error_t
gpg_encrypt_blob(ctrl_t ctrl,const char * gpg_program,strlist_t gpg_arguments,const void * plain,size_t plainlen,strlist_t keys,void ** r_ciph,size_t * r_ciphlen)538 gpg_encrypt_blob (ctrl_t ctrl,
539 const char *gpg_program,
540 strlist_t gpg_arguments,
541 const void *plain, size_t plainlen,
542 strlist_t keys,
543 void **r_ciph, size_t *r_ciphlen)
544 {
545 gpg_error_t err;
546 membuf_t reader_mb;
547
548 *r_ciph = NULL;
549 *r_ciphlen = 0;
550
551 /* Init the memory buffer to receive the encrypted stuff. */
552 init_membuf (&reader_mb, 4096);
553
554 err = _gpg_encrypt (ctrl, gpg_program, gpg_arguments,
555 plain, plainlen, NULL,
556 keys,
557 &reader_mb, NULL);
558
559 if (! err)
560 {
561 /* Return the data. */
562 *r_ciph = get_membuf (&reader_mb, r_ciphlen);
563 if (!*r_ciph)
564 {
565 err = my_error_from_syserror ();
566 log_error ("error while storing the data in the reader thread: %s\n",
567 gpg_strerror (err));
568 }
569 }
570
571 xfree (get_membuf (&reader_mb, NULL));
572 return err;
573 }
574
575 gpg_error_t
gpg_encrypt_stream(ctrl_t ctrl,const char * gpg_program,strlist_t gpg_arguments,estream_t plain_stream,strlist_t keys,estream_t cipher_stream)576 gpg_encrypt_stream (ctrl_t ctrl,
577 const char *gpg_program,
578 strlist_t gpg_arguments,
579 estream_t plain_stream,
580 strlist_t keys,
581 estream_t cipher_stream)
582 {
583 return _gpg_encrypt (ctrl, gpg_program, gpg_arguments,
584 NULL, 0, plain_stream,
585 keys,
586 NULL, cipher_stream);
587 }
588
589 /* Call GPG to decrypt a block of data.
590
591
592 */
593 static gpg_error_t
_gpg_decrypt(ctrl_t ctrl,const char * gpg_program,strlist_t gpg_arguments,const void * ciph,size_t ciphlen,estream_t cipher_stream,membuf_t * reader_mb,estream_t plain_stream)594 _gpg_decrypt (ctrl_t ctrl,
595 const char *gpg_program,
596 strlist_t gpg_arguments,
597 const void *ciph, size_t ciphlen,
598 estream_t cipher_stream,
599 membuf_t *reader_mb,
600 estream_t plain_stream)
601 {
602 gpg_error_t err;
603 assuan_context_t ctx = NULL;
604 int outbound_fds[2] = { -1, -1 };
605 int inbound_fds[2] = { -1, -1 };
606 npth_t writer_thread = (npth_t)0;
607 npth_t reader_thread = (npth_t)0;
608 gpg_error_t writer_err, reader_err;
609 int ret;
610
611 /* Make sure that either the stream interface xor the buffer
612 interface is used. */
613 assert ((ciph == NULL) != (cipher_stream == NULL));
614 assert ((reader_mb == NULL) != (plain_stream == NULL));
615
616 /* Create two pipes. */
617 err = gnupg_create_outbound_pipe (outbound_fds, NULL, 0);
618 if (!err)
619 err = gnupg_create_inbound_pipe (inbound_fds, NULL, 0);
620 if (err)
621 {
622 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
623 goto leave;
624 }
625
626 /* Start GPG and send the INPUT and OUTPUT commands. */
627 err = start_gpg (ctrl, gpg_program, gpg_arguments,
628 outbound_fds[0], inbound_fds[1], &ctx);
629 if (err)
630 goto leave;
631 close (outbound_fds[0]); outbound_fds[0] = -1;
632 close (inbound_fds[1]); inbound_fds[1] = -1;
633
634 /* Start a writer thread to feed the INPUT command of the server. */
635 err = start_writer (outbound_fds[1], ciph, ciphlen, cipher_stream,
636 &writer_thread, &writer_err);
637 if (err)
638 return err;
639 outbound_fds[1] = -1; /* The thread owns the FD now. */
640
641 /* Start a reader thread to eat from the OUTPUT command of the
642 server. */
643 err = start_reader (inbound_fds[0], reader_mb, plain_stream,
644 &reader_thread, &reader_err);
645 if (err)
646 return err;
647 outbound_fds[0] = -1; /* The thread owns the FD now. */
648
649 /* Run the decryption. */
650 err = assuan_transact (ctx, "DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL);
651 if (err)
652 {
653 log_error ("the engine's DECRYPT command failed: %s <%s>\n",
654 gpg_strerror (err), gpg_strsource (err));
655 goto leave;
656 }
657
658 /* Wait for reader and return the data. */
659 ret = npth_join (reader_thread, NULL);
660 if (ret)
661 {
662 err = my_error_from_errno (ret);
663 log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err));
664 goto leave;
665 }
666 memset (&reader_thread, '\0', sizeof (reader_thread));
667 if (reader_err)
668 {
669 err = reader_err;
670 log_error ("read error in reader thread: %s\n", gpg_strerror (err));
671 goto leave;
672 }
673
674 /* Wait for the writer to catch a writer error. */
675 ret = npth_join (writer_thread, NULL);
676 if (ret)
677 {
678 err = my_error_from_errno (ret);
679 log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err));
680 goto leave;
681 }
682 memset (&writer_thread, '\0', sizeof (writer_thread));
683 if (writer_err)
684 {
685 err = writer_err;
686 log_error ("write error in writer thread: %s\n", gpg_strerror (err));
687 goto leave;
688 }
689
690 leave:
691 if (reader_thread)
692 npth_detach (reader_thread);
693 if (writer_thread)
694 npth_detach (writer_thread);
695 if (outbound_fds[0] != -1)
696 close (outbound_fds[0]);
697 if (outbound_fds[1] != -1)
698 close (outbound_fds[1]);
699 if (inbound_fds[0] != -1)
700 close (inbound_fds[0]);
701 if (inbound_fds[1] != -1)
702 close (inbound_fds[1]);
703 release_gpg (ctx);
704 return err;
705 }
706
707 gpg_error_t
gpg_decrypt_blob(ctrl_t ctrl,const char * gpg_program,strlist_t gpg_arguments,const void * ciph,size_t ciphlen,void ** r_plain,size_t * r_plainlen)708 gpg_decrypt_blob (ctrl_t ctrl,
709 const char *gpg_program,
710 strlist_t gpg_arguments,
711 const void *ciph, size_t ciphlen,
712 void **r_plain, size_t *r_plainlen)
713 {
714 gpg_error_t err;
715 membuf_t reader_mb;
716
717 *r_plain = NULL;
718 *r_plainlen = 0;
719
720 /* Init the memory buffer to receive the encrypted stuff. */
721 init_membuf_secure (&reader_mb, 1024);
722
723 err = _gpg_decrypt (ctrl, gpg_program, gpg_arguments,
724 ciph, ciphlen, NULL,
725 &reader_mb, NULL);
726
727 if (! err)
728 {
729 /* Return the data. */
730 *r_plain = get_membuf (&reader_mb, r_plainlen);
731 if (!*r_plain)
732 {
733 err = my_error_from_syserror ();
734 log_error ("error while storing the data in the reader thread: %s\n",
735 gpg_strerror (err));
736 }
737 }
738
739 xfree (get_membuf (&reader_mb, NULL));
740 return err;
741 }
742
743 gpg_error_t
gpg_decrypt_stream(ctrl_t ctrl,const char * gpg_program,strlist_t gpg_arguments,estream_t cipher_stream,estream_t plain_stream)744 gpg_decrypt_stream (ctrl_t ctrl,
745 const char *gpg_program,
746 strlist_t gpg_arguments,
747 estream_t cipher_stream,
748 estream_t plain_stream)
749 {
750 return _gpg_decrypt (ctrl, gpg_program, gpg_arguments,
751 NULL, 0, cipher_stream,
752 NULL, plain_stream);
753 }
754