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