chiark / gitweb /
agent: Allow threads to interrupt main select loop with SIGCONT.
[gnupg2.git] / tools / wks-receive.c
1 /* wks-receive.c - Receive a WKS mail
2  * Copyright (C) 2016 g10 Code GmbH
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 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "util.h"
26 #include "ccparray.h"
27 #include "exectool.h"
28 #include "gpg-wks.h"
29 #include "rfc822parse.h"
30 #include "mime-parser.h"
31
32
33 /* Limit of acceptable signed data.  */
34 #define MAX_SIGNEDDATA 10000
35
36 /* Limit of acceptable signature.  */
37 #define MAX_SIGNATURE 10000
38
39 /* Limit of acceptable encrypted data.  */
40 #define MAX_ENCRYPTED 100000
41
42 /* Data for a received object.  */
43 struct receive_ctx_s
44 {
45   mime_parser_t parser;
46   estream_t encrypted;
47   estream_t plaintext;
48   estream_t signeddata;
49   estream_t signature;
50   estream_t key_data;
51   estream_t wkd_data;
52   unsigned int collect_key_data:1;
53   unsigned int collect_wkd_data:1;
54   unsigned int draft_version_2:1;  /* This is a draft version 2 request.  */
55   unsigned int multipart_mixed_seen:1;
56 };
57 typedef struct receive_ctx_s *receive_ctx_t;
58
59
60
61 static void
62 decrypt_data_status_cb (void *opaque, const char *keyword, char *args)
63 {
64   receive_ctx_t ctx = opaque;
65   (void)ctx;
66   if (DBG_CRYPTO)
67     log_debug ("gpg status: %s %s\n", keyword, args);
68 }
69
70
71 /* Decrypt the collected data.  */
72 static void
73 decrypt_data (receive_ctx_t ctx)
74 {
75   gpg_error_t err;
76   ccparray_t ccp;
77   const char **argv;
78   int c;
79
80   es_rewind (ctx->encrypted);
81
82   if (!ctx->plaintext)
83     ctx->plaintext = es_fopenmem (0, "w+b");
84   if (!ctx->plaintext)
85     {
86       err = gpg_error_from_syserror ();
87       log_error ("error allocating space for plaintext: %s\n",
88                  gpg_strerror (err));
89       return;
90     }
91
92   ccparray_init (&ccp, 0);
93
94   ccparray_put (&ccp, "--no-options");
95   /* We limit the output to 64 KiB to avoid DoS using compression
96    * tricks.  A regular client will anyway only send a minimal key;
97    * that is one w/o key signatures and attribute packets.  */
98   ccparray_put (&ccp, "--max-output=0xf0000"); /*FIXME: Change s/F/1/ */
99   ccparray_put (&ccp, "--batch");
100   if (opt.verbose)
101     ccparray_put (&ccp, "--verbose");
102   ccparray_put (&ccp, "--always-trust");
103   ccparray_put (&ccp, "--decrypt");
104   ccparray_put (&ccp, "--");
105
106   ccparray_put (&ccp, NULL);
107   argv = ccparray_get (&ccp, NULL);
108   if (!argv)
109     {
110       err = gpg_error_from_syserror ();
111       goto leave;
112     }
113   err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->encrypted,
114                                 NULL, ctx->plaintext,
115                                 decrypt_data_status_cb, ctx);
116   if (err)
117     {
118       log_error ("decryption failed: %s\n", gpg_strerror (err));
119       goto leave;
120     }
121
122   if (DBG_CRYPTO)
123     {
124       es_rewind (ctx->plaintext);
125       log_debug ("plaintext: '");
126       while ((c = es_getc (ctx->plaintext)) != EOF)
127         log_printf ("%c", c);
128       log_printf ("'\n");
129     }
130   es_rewind (ctx->plaintext);
131
132  leave:
133   xfree (argv);
134 }
135
136
137 static void
138 verify_signature_status_cb (void *opaque, const char *keyword, char *args)
139 {
140   receive_ctx_t ctx = opaque;
141   (void)ctx;
142   if (DBG_CRYPTO)
143     log_debug ("gpg status: %s %s\n", keyword, args);
144 }
145
146 /* Verify the signed data.  */
147 static void
148 verify_signature (receive_ctx_t ctx)
149 {
150   gpg_error_t err;
151   ccparray_t ccp;
152   const char **argv;
153
154   log_assert (ctx->signeddata);
155   log_assert (ctx->signature);
156   es_rewind (ctx->signeddata);
157   es_rewind (ctx->signature);
158
159   ccparray_init (&ccp, 0);
160
161   ccparray_put (&ccp, "--no-options");
162   ccparray_put (&ccp, "--batch");
163   if (opt.verbose)
164     ccparray_put (&ccp, "--verbose");
165   ccparray_put (&ccp, "--enable-special-filenames");
166   ccparray_put (&ccp, "--status-fd=2");
167   ccparray_put (&ccp, "--always-trust"); /* To avoid trustdb checks.  */
168   ccparray_put (&ccp, "--verify");
169   ccparray_put (&ccp, "--");
170   ccparray_put (&ccp, "-&@INEXTRA@");
171   ccparray_put (&ccp, "-");
172
173   ccparray_put (&ccp, NULL);
174   argv = ccparray_get (&ccp, NULL);
175   if (!argv)
176     {
177       err = gpg_error_from_syserror ();
178       goto leave;
179     }
180   err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->signeddata,
181                                 ctx->signature, NULL,
182                                 verify_signature_status_cb, ctx);
183   if (err)
184     {
185       log_error ("verification failed: %s\n", gpg_strerror (err));
186       goto leave;
187     }
188
189   log_debug ("Fixme: Verification result is not used\n");
190
191  leave:
192   xfree (argv);
193 }
194
195
196 static gpg_error_t
197 collect_encrypted (void *cookie, const char *data)
198 {
199   receive_ctx_t ctx = cookie;
200
201   if (!ctx->encrypted)
202     if (!(ctx->encrypted = es_fopenmem (MAX_ENCRYPTED, "w+b,samethread")))
203       return gpg_error_from_syserror ();
204   if (data)
205     es_fputs (data, ctx->encrypted);
206
207   if (es_ferror (ctx->encrypted))
208     return gpg_error_from_syserror ();
209
210   if (!data)
211     {
212       decrypt_data (ctx);
213     }
214
215   return 0;
216 }
217
218
219 static gpg_error_t
220 collect_signeddata (void *cookie, const char *data)
221 {
222   receive_ctx_t ctx = cookie;
223
224   if (!ctx->signeddata)
225     if (!(ctx->signeddata = es_fopenmem (MAX_SIGNEDDATA, "w+b,samethread")))
226       return gpg_error_from_syserror ();
227   if (data)
228     es_fputs (data, ctx->signeddata);
229
230   if (es_ferror (ctx->signeddata))
231     return gpg_error_from_syserror ();
232   return 0;
233 }
234
235 static gpg_error_t
236 collect_signature (void *cookie, const char *data)
237 {
238   receive_ctx_t ctx = cookie;
239
240   if (!ctx->signature)
241     if (!(ctx->signature = es_fopenmem (MAX_SIGNATURE, "w+b,samethread")))
242       return gpg_error_from_syserror ();
243   if (data)
244     es_fputs (data, ctx->signature);
245
246   if (es_ferror (ctx->signature))
247     return gpg_error_from_syserror ();
248
249   if (!data)
250     {
251       verify_signature (ctx);
252     }
253
254   return 0;
255 }
256
257
258 static gpg_error_t
259 new_part (void *cookie, const char *mediatype, const char *mediasubtype)
260 {
261   receive_ctx_t ctx = cookie;
262   gpg_error_t err = 0;
263
264   ctx->collect_key_data = 0;
265   ctx->collect_wkd_data = 0;
266
267   if (!strcmp (mediatype, "application")
268       && !strcmp (mediasubtype, "pgp-keys"))
269     {
270       log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
271       if (ctx->key_data)
272         {
273           log_error ("we already got a key - ignoring this part\n");
274           err = gpg_error (GPG_ERR_FALSE);
275         }
276       else
277         {
278           rfc822parse_t msg = mime_parser_rfc822parser (ctx->parser);
279           if (msg)
280             {
281               char *value;
282               size_t valueoff;
283
284               value = rfc822parse_get_field (msg, "Wks-Draft-Version",
285                                              -1, &valueoff);
286               if (value)
287                 {
288                   if (atoi(value+valueoff) >= 2 )
289                     ctx->draft_version_2 = 1;
290                   free (value);
291                 }
292             }
293
294           ctx->key_data = es_fopenmem (0, "w+b");
295           if (!ctx->key_data)
296             {
297               err = gpg_error_from_syserror ();
298               log_error ("error allocating space for key: %s\n",
299                          gpg_strerror (err));
300             }
301           else
302             {
303               ctx->collect_key_data = 1;
304               err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded.  */
305             }
306         }
307     }
308   else if (!strcmp (mediatype, "application")
309            && !strcmp (mediasubtype, "vnd.gnupg.wks"))
310     {
311       log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
312       if (ctx->wkd_data)
313         {
314           log_error ("we already got a wkd part - ignoring this part\n");
315           err = gpg_error (GPG_ERR_FALSE);
316         }
317       else
318         {
319           ctx->wkd_data = es_fopenmem (0, "w+b");
320           if (!ctx->wkd_data)
321             {
322               err = gpg_error_from_syserror ();
323               log_error ("error allocating space for key: %s\n",
324                          gpg_strerror (err));
325             }
326           else
327             {
328               ctx->collect_wkd_data = 1;
329               err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded.  */
330             }
331         }
332     }
333   else if (!strcmp (mediatype, "multipart")
334            && !strcmp (mediasubtype, "mixed"))
335     {
336       ctx->multipart_mixed_seen = 1;
337     }
338   else if (!strcmp (mediatype, "text"))
339     {
340       /* Check that we receive a text part only after a
341        * application/mixed.  This is actually a too simple test and we
342        * should eventually employ a strict MIME structure check.  */
343       if (!ctx->multipart_mixed_seen)
344         err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
345     }
346   else
347     {
348       log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
349       err = gpg_error (GPG_ERR_FALSE); /* We do not want the part.  */
350     }
351
352   return err;
353 }
354
355
356 static gpg_error_t
357 part_data (void *cookie, const void *data, size_t datalen)
358 {
359   receive_ctx_t ctx = cookie;
360
361   if (data)
362     {
363       if (DBG_MIME)
364         log_debug ("part_data: '%.*s'\n", (int)datalen, (const char*)data);
365       if (ctx->collect_key_data)
366         {
367           if (es_write (ctx->key_data, data, datalen, NULL)
368               || es_fputs ("\n", ctx->key_data))
369             return gpg_error_from_syserror ();
370         }
371       if (ctx->collect_wkd_data)
372         {
373           if (es_write (ctx->wkd_data, data, datalen, NULL)
374               || es_fputs ("\n", ctx->wkd_data))
375             return gpg_error_from_syserror ();
376         }
377     }
378   else
379     {
380       if (DBG_MIME)
381         log_debug ("part_data: finished\n");
382       ctx->collect_key_data = 0;
383       ctx->collect_wkd_data = 0;
384     }
385   return 0;
386 }
387
388
389 /* Receive a WKS mail from FP and process it accordingly.  On success
390  * the RESULT_CB is called with the mediatype and a stream with the
391  * decrypted data. */
392 gpg_error_t
393 wks_receive (estream_t fp,
394              gpg_error_t (*result_cb)(void *opaque,
395                                       const char *mediatype,
396                                       estream_t data,
397                                       unsigned int flags),
398              void *cb_data)
399 {
400   gpg_error_t err;
401   receive_ctx_t ctx;
402   mime_parser_t parser;
403   estream_t plaintext = NULL;
404   int c;
405   unsigned int flags = 0;
406
407   ctx = xtrycalloc (1, sizeof *ctx);
408   if (!ctx)
409     return gpg_error_from_syserror ();
410
411   err = mime_parser_new (&parser, ctx);
412   if (err)
413     goto leave;
414   if (DBG_PARSER)
415     mime_parser_set_verbose (parser, 1);
416   mime_parser_set_new_part (parser, new_part);
417   mime_parser_set_part_data (parser, part_data);
418   mime_parser_set_collect_encrypted (parser, collect_encrypted);
419   mime_parser_set_collect_signeddata (parser, collect_signeddata);
420   mime_parser_set_collect_signature (parser, collect_signature);
421
422   ctx->parser = parser;
423
424   err = mime_parser_parse (parser, fp);
425   if (err)
426     goto leave;
427
428   if (ctx->key_data)
429     log_info ("key data found\n");
430   if (ctx->wkd_data)
431     log_info ("wkd data found\n");
432   if (ctx->draft_version_2)
433     {
434       log_info ("draft version 2 requested\n");
435       flags |= WKS_RECEIVE_DRAFT2;
436     }
437
438   if (ctx->plaintext)
439     {
440       if (opt.verbose)
441         log_info ("parsing decrypted message\n");
442       plaintext = ctx->plaintext;
443       ctx->plaintext = NULL;
444       if (ctx->encrypted)
445         es_rewind (ctx->encrypted);
446       if (ctx->signeddata)
447         es_rewind (ctx->signeddata);
448       if (ctx->signature)
449         es_rewind (ctx->signature);
450       err = mime_parser_parse (parser, plaintext);
451       if (err)
452         return err;
453     }
454
455   if (!ctx->key_data && !ctx->wkd_data)
456     {
457       log_error ("no suitable data found in the message\n");
458       err = gpg_error (GPG_ERR_NO_DATA);
459       goto leave;
460     }
461
462   if (ctx->key_data)
463     {
464       if (DBG_MIME)
465         {
466           es_rewind (ctx->key_data);
467           log_debug ("Key: '");
468           log_printf ("\n");
469           while ((c = es_getc (ctx->key_data)) != EOF)
470             log_printf ("%c", c);
471           log_printf ("'\n");
472         }
473       if (result_cb)
474         {
475           es_rewind (ctx->key_data);
476           err = result_cb (cb_data, "application/pgp-keys",
477                            ctx->key_data, flags);
478           if (err)
479             goto leave;
480         }
481     }
482   if (ctx->wkd_data)
483     {
484       if (DBG_MIME)
485         {
486           es_rewind (ctx->wkd_data);
487           log_debug ("WKD: '");
488           log_printf ("\n");
489           while ((c = es_getc (ctx->wkd_data)) != EOF)
490             log_printf ("%c", c);
491           log_printf ("'\n");
492         }
493       if (result_cb)
494         {
495           es_rewind (ctx->wkd_data);
496           err = result_cb (cb_data, "application/vnd.gnupg.wks",
497                            ctx->wkd_data, flags);
498           if (err)
499             goto leave;
500         }
501     }
502
503
504  leave:
505   es_fclose (plaintext);
506   mime_parser_release (parser);
507   ctx->parser = NULL;
508   es_fclose (ctx->encrypted);
509   es_fclose (ctx->plaintext);
510   es_fclose (ctx->signeddata);
511   es_fclose (ctx->signature);
512   es_fclose (ctx->key_data);
513   es_fclose (ctx->wkd_data);
514   xfree (ctx);
515   return err;
516 }