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