49a15179b8c9c2e6b21193b54478d86967c76b47
[gnupg.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 "../common/util.h"
26 #include "../common/ccparray.h"
27 #include "../common/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 /* The callback for the transition from header to body.  We use it to
259  * look at some header values.  */
260 static gpg_error_t
261 t2body (void *cookie, int level)
262 {
263   receive_ctx_t ctx = cookie;
264   rfc822parse_t msg;
265   char *value;
266   size_t valueoff;
267
268   log_info ("t2body for level %d\n", level);
269   if (!level)
270     {
271       /* This is the outermost header.  */
272       msg = mime_parser_rfc822parser (ctx->parser);
273       if (msg)
274         {
275           value = rfc822parse_get_field (msg, "Wks-Draft-Version",
276                                          -1, &valueoff);
277           if (value)
278             {
279               if (atoi(value+valueoff) >= 2 )
280                 ctx->draft_version_2 = 1;
281               free (value);
282             }
283         }
284     }
285
286   return 0;
287 }
288
289
290 static gpg_error_t
291 new_part (void *cookie, const char *mediatype, const char *mediasubtype)
292 {
293   receive_ctx_t ctx = cookie;
294   gpg_error_t err = 0;
295
296   ctx->collect_key_data = 0;
297   ctx->collect_wkd_data = 0;
298
299   if (!strcmp (mediatype, "application")
300       && !strcmp (mediasubtype, "pgp-keys"))
301     {
302       log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
303       if (ctx->key_data)
304         {
305           log_error ("we already got a key - ignoring this part\n");
306           err = gpg_error (GPG_ERR_FALSE);
307         }
308       else
309         {
310           ctx->key_data = es_fopenmem (0, "w+b");
311           if (!ctx->key_data)
312             {
313               err = gpg_error_from_syserror ();
314               log_error ("error allocating space for key: %s\n",
315                          gpg_strerror (err));
316             }
317           else
318             {
319               ctx->collect_key_data = 1;
320               err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded.  */
321             }
322         }
323     }
324   else if (!strcmp (mediatype, "application")
325            && !strcmp (mediasubtype, "vnd.gnupg.wks"))
326     {
327       log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
328       if (ctx->wkd_data)
329         {
330           log_error ("we already got a wkd part - ignoring this part\n");
331           err = gpg_error (GPG_ERR_FALSE);
332         }
333       else
334         {
335           ctx->wkd_data = es_fopenmem (0, "w+b");
336           if (!ctx->wkd_data)
337             {
338               err = gpg_error_from_syserror ();
339               log_error ("error allocating space for key: %s\n",
340                          gpg_strerror (err));
341             }
342           else
343             {
344               ctx->collect_wkd_data = 1;
345               err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded.  */
346             }
347         }
348     }
349   else if (!strcmp (mediatype, "multipart")
350            && !strcmp (mediasubtype, "mixed"))
351     {
352       ctx->multipart_mixed_seen = 1;
353     }
354   else if (!strcmp (mediatype, "text"))
355     {
356       /* Check that we receive a text part only after a
357        * application/mixed.  This is actually a too simple test and we
358        * should eventually employ a strict MIME structure check.  */
359       if (!ctx->multipart_mixed_seen)
360         err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
361     }
362   else
363     {
364       log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
365       err = gpg_error (GPG_ERR_FALSE); /* We do not want the part.  */
366     }
367
368   return err;
369 }
370
371
372 static gpg_error_t
373 part_data (void *cookie, const void *data, size_t datalen)
374 {
375   receive_ctx_t ctx = cookie;
376
377   if (data)
378     {
379       if (DBG_MIME)
380         log_debug ("part_data: '%.*s'\n", (int)datalen, (const char*)data);
381       if (ctx->collect_key_data)
382         {
383           if (es_write (ctx->key_data, data, datalen, NULL)
384               || es_fputs ("\n", ctx->key_data))
385             return gpg_error_from_syserror ();
386         }
387       if (ctx->collect_wkd_data)
388         {
389           if (es_write (ctx->wkd_data, data, datalen, NULL)
390               || es_fputs ("\n", ctx->wkd_data))
391             return gpg_error_from_syserror ();
392         }
393     }
394   else
395     {
396       if (DBG_MIME)
397         log_debug ("part_data: finished\n");
398       ctx->collect_key_data = 0;
399       ctx->collect_wkd_data = 0;
400     }
401   return 0;
402 }
403
404
405 /* Receive a WKS mail from FP and process it accordingly.  On success
406  * the RESULT_CB is called with the mediatype and a stream with the
407  * decrypted data. */
408 gpg_error_t
409 wks_receive (estream_t fp,
410              gpg_error_t (*result_cb)(void *opaque,
411                                       const char *mediatype,
412                                       estream_t data,
413                                       unsigned int flags),
414              void *cb_data)
415 {
416   gpg_error_t err;
417   receive_ctx_t ctx;
418   mime_parser_t parser;
419   estream_t plaintext = NULL;
420   int c;
421   unsigned int flags = 0;
422
423   ctx = xtrycalloc (1, sizeof *ctx);
424   if (!ctx)
425     return gpg_error_from_syserror ();
426
427   err = mime_parser_new (&parser, ctx);
428   if (err)
429     goto leave;
430   if (DBG_PARSER)
431     mime_parser_set_verbose (parser, 1);
432   mime_parser_set_t2body (parser, t2body);
433   mime_parser_set_new_part (parser, new_part);
434   mime_parser_set_part_data (parser, part_data);
435   mime_parser_set_collect_encrypted (parser, collect_encrypted);
436   mime_parser_set_collect_signeddata (parser, collect_signeddata);
437   mime_parser_set_collect_signature (parser, collect_signature);
438
439   ctx->parser = parser;
440
441   err = mime_parser_parse (parser, fp);
442   if (err)
443     goto leave;
444
445   if (ctx->key_data)
446     log_info ("key data found\n");
447   if (ctx->wkd_data)
448     log_info ("wkd data found\n");
449   if (ctx->draft_version_2)
450     {
451       log_info ("draft version 2 requested\n");
452       flags |= WKS_RECEIVE_DRAFT2;
453     }
454
455   if (ctx->plaintext)
456     {
457       if (opt.verbose)
458         log_info ("parsing decrypted message\n");
459       plaintext = ctx->plaintext;
460       ctx->plaintext = NULL;
461       if (ctx->encrypted)
462         es_rewind (ctx->encrypted);
463       if (ctx->signeddata)
464         es_rewind (ctx->signeddata);
465       if (ctx->signature)
466         es_rewind (ctx->signature);
467       err = mime_parser_parse (parser, plaintext);
468       if (err)
469         return err;
470     }
471
472   if (!ctx->key_data && !ctx->wkd_data)
473     {
474       log_error ("no suitable data found in the message\n");
475       err = gpg_error (GPG_ERR_NO_DATA);
476       goto leave;
477     }
478
479   if (ctx->key_data)
480     {
481       if (DBG_MIME)
482         {
483           es_rewind (ctx->key_data);
484           log_debug ("Key: '");
485           log_printf ("\n");
486           while ((c = es_getc (ctx->key_data)) != EOF)
487             log_printf ("%c", c);
488           log_printf ("'\n");
489         }
490       if (result_cb)
491         {
492           es_rewind (ctx->key_data);
493           err = result_cb (cb_data, "application/pgp-keys",
494                            ctx->key_data, flags);
495           if (err)
496             goto leave;
497         }
498     }
499   if (ctx->wkd_data)
500     {
501       if (DBG_MIME)
502         {
503           es_rewind (ctx->wkd_data);
504           log_debug ("WKD: '");
505           log_printf ("\n");
506           while ((c = es_getc (ctx->wkd_data)) != EOF)
507             log_printf ("%c", c);
508           log_printf ("'\n");
509         }
510       if (result_cb)
511         {
512           es_rewind (ctx->wkd_data);
513           err = result_cb (cb_data, "application/vnd.gnupg.wks",
514                            ctx->wkd_data, flags);
515           if (err)
516             goto leave;
517         }
518     }
519
520
521  leave:
522   es_fclose (plaintext);
523   mime_parser_release (parser);
524   ctx->parser = NULL;
525   es_fclose (ctx->encrypted);
526   es_fclose (ctx->plaintext);
527   es_fclose (ctx->signeddata);
528   es_fclose (ctx->signature);
529   es_fclose (ctx->key_data);
530   es_fclose (ctx->wkd_data);
531   xfree (ctx);
532   return err;
533 }