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
5 * This file is part of GnuPG.
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.
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.
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/>.
26 #include "../common/util.h"
27 #include "../common/ccparray.h"
28 #include "../common/exectool.h"
30 #include "rfc822parse.h"
31 #include "mime-parser.h"
34 /* Limit of acceptable signed data. */
35 #define MAX_SIGNEDDATA 10000
37 /* Limit of acceptable signature. */
38 #define MAX_SIGNATURE 10000
40 /* Limit of acceptable encrypted data. */
41 #define MAX_ENCRYPTED 100000
43 /* Data for a received object. */
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;
58 typedef struct receive_ctx_s *receive_ctx_t;
63 decrypt_data_status_cb (void *opaque, const char *keyword, char *args)
65 receive_ctx_t ctx = opaque;
68 log_debug ("gpg status: %s %s\n", keyword, args);
72 /* Decrypt the collected data. */
74 decrypt_data (receive_ctx_t ctx)
81 es_rewind (ctx->encrypted);
84 ctx->plaintext = es_fopenmem (0, "w+b");
87 err = gpg_error_from_syserror ();
88 log_error ("error allocating space for plaintext: %s\n",
93 ccparray_init (&ccp, 0);
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");
102 ccparray_put (&ccp, "--verbose");
103 ccparray_put (&ccp, "--always-trust");
104 ccparray_put (&ccp, "--decrypt");
105 ccparray_put (&ccp, "--");
107 ccparray_put (&ccp, NULL);
108 argv = ccparray_get (&ccp, NULL);
111 err = gpg_error_from_syserror ();
114 err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->encrypted,
115 NULL, ctx->plaintext,
116 decrypt_data_status_cb, ctx);
119 log_error ("decryption failed: %s\n", gpg_strerror (err));
125 es_rewind (ctx->plaintext);
126 log_debug ("plaintext: '");
127 while ((c = es_getc (ctx->plaintext)) != EOF)
128 log_printf ("%c", c);
131 es_rewind (ctx->plaintext);
139 verify_signature_status_cb (void *opaque, const char *keyword, char *args)
141 receive_ctx_t ctx = opaque;
144 log_debug ("gpg status: %s %s\n", keyword, args);
147 /* Verify the signed data. */
149 verify_signature (receive_ctx_t ctx)
155 log_assert (ctx->signeddata);
156 log_assert (ctx->signature);
157 es_rewind (ctx->signeddata);
158 es_rewind (ctx->signature);
160 ccparray_init (&ccp, 0);
162 ccparray_put (&ccp, "--no-options");
163 ccparray_put (&ccp, "--batch");
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, "-");
174 ccparray_put (&ccp, NULL);
175 argv = ccparray_get (&ccp, NULL);
178 err = gpg_error_from_syserror ();
181 err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->signeddata,
182 ctx->signature, NULL,
183 verify_signature_status_cb, ctx);
186 log_error ("verification failed: %s\n", gpg_strerror (err));
190 log_debug ("Fixme: Verification result is not used\n");
198 collect_encrypted (void *cookie, const char *data)
200 receive_ctx_t ctx = cookie;
203 if (!(ctx->encrypted = es_fopenmem (MAX_ENCRYPTED, "w+b,samethread")))
204 return gpg_error_from_syserror ();
206 es_fputs (data, ctx->encrypted);
208 if (es_ferror (ctx->encrypted))
209 return gpg_error_from_syserror ();
221 collect_signeddata (void *cookie, const char *data)
223 receive_ctx_t ctx = cookie;
225 if (!ctx->signeddata)
226 if (!(ctx->signeddata = es_fopenmem (MAX_SIGNEDDATA, "w+b,samethread")))
227 return gpg_error_from_syserror ();
229 es_fputs (data, ctx->signeddata);
231 if (es_ferror (ctx->signeddata))
232 return gpg_error_from_syserror ();
237 collect_signature (void *cookie, const char *data)
239 receive_ctx_t ctx = cookie;
242 if (!(ctx->signature = es_fopenmem (MAX_SIGNATURE, "w+b,samethread")))
243 return gpg_error_from_syserror ();
245 es_fputs (data, ctx->signature);
247 if (es_ferror (ctx->signature))
248 return gpg_error_from_syserror ();
252 verify_signature (ctx);
259 /* The callback for the transition from header to body. We use it to
260 * look at some header values. */
262 t2body (void *cookie, int level)
264 receive_ctx_t ctx = cookie;
269 log_info ("t2body for level %d\n", level);
272 /* This is the outermost header. */
273 msg = mime_parser_rfc822parser (ctx->parser);
276 value = rfc822parse_get_field (msg, "Wks-Draft-Version",
280 if (atoi(value+valueoff) >= 2 )
281 ctx->draft_version_2 = 1;
292 new_part (void *cookie, const char *mediatype, const char *mediasubtype)
294 receive_ctx_t ctx = cookie;
297 ctx->collect_key_data = 0;
298 ctx->collect_wkd_data = 0;
300 if (!strcmp (mediatype, "application")
301 && !strcmp (mediasubtype, "pgp-keys"))
303 log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
306 log_error ("we already got a key - ignoring this part\n");
307 err = gpg_error (GPG_ERR_FALSE);
311 ctx->key_data = es_fopenmem (0, "w+b");
314 err = gpg_error_from_syserror ();
315 log_error ("error allocating space for key: %s\n",
320 ctx->collect_key_data = 1;
321 err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
325 else if (!strcmp (mediatype, "application")
326 && !strcmp (mediasubtype, "vnd.gnupg.wks"))
328 log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
331 log_error ("we already got a wkd part - ignoring this part\n");
332 err = gpg_error (GPG_ERR_FALSE);
336 ctx->wkd_data = es_fopenmem (0, "w+b");
339 err = gpg_error_from_syserror ();
340 log_error ("error allocating space for key: %s\n",
345 ctx->collect_wkd_data = 1;
346 err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
350 else if (!strcmp (mediatype, "multipart")
351 && !strcmp (mediasubtype, "mixed"))
353 ctx->multipart_mixed_seen = 1;
355 else if (!strcmp (mediatype, "text"))
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);
365 log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
366 err = gpg_error (GPG_ERR_FALSE); /* We do not want the part. */
374 part_data (void *cookie, const void *data, size_t datalen)
376 receive_ctx_t ctx = cookie;
381 log_debug ("part_data: '%.*s'\n", (int)datalen, (const char*)data);
382 if (ctx->collect_key_data)
384 if (es_write (ctx->key_data, data, datalen, NULL)
385 || es_fputs ("\n", ctx->key_data))
386 return gpg_error_from_syserror ();
388 if (ctx->collect_wkd_data)
390 if (es_write (ctx->wkd_data, data, datalen, NULL)
391 || es_fputs ("\n", ctx->wkd_data))
392 return gpg_error_from_syserror ();
398 log_debug ("part_data: finished\n");
399 ctx->collect_key_data = 0;
400 ctx->collect_wkd_data = 0;
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
410 wks_receive (estream_t fp,
411 gpg_error_t (*result_cb)(void *opaque,
412 const char *mediatype,
419 mime_parser_t parser;
420 estream_t plaintext = NULL;
422 unsigned int flags = 0;
424 ctx = xtrycalloc (1, sizeof *ctx);
426 return gpg_error_from_syserror ();
428 err = mime_parser_new (&parser, ctx);
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);
440 ctx->parser = parser;
442 err = mime_parser_parse (parser, fp);
447 log_info ("key data found\n");
449 log_info ("wkd data found\n");
450 if (ctx->draft_version_2)
452 log_info ("draft version 2 requested\n");
453 flags |= WKS_RECEIVE_DRAFT2;
459 log_info ("parsing decrypted message\n");
460 plaintext = ctx->plaintext;
461 ctx->plaintext = NULL;
463 es_rewind (ctx->encrypted);
465 es_rewind (ctx->signeddata);
467 es_rewind (ctx->signature);
468 err = mime_parser_parse (parser, plaintext);
473 if (!ctx->key_data && !ctx->wkd_data)
475 log_error ("no suitable data found in the message\n");
476 err = gpg_error (GPG_ERR_NO_DATA);
484 es_rewind (ctx->key_data);
485 log_debug ("Key: '");
487 while ((c = es_getc (ctx->key_data)) != EOF)
488 log_printf ("%c", c);
493 es_rewind (ctx->key_data);
494 err = result_cb (cb_data, "application/pgp-keys",
495 ctx->key_data, flags);
504 es_rewind (ctx->wkd_data);
505 log_debug ("WKD: '");
507 while ((c = es_getc (ctx->wkd_data)) != EOF)
508 log_printf ("%c", c);
513 es_rewind (ctx->wkd_data);
514 err = result_cb (cb_data, "application/vnd.gnupg.wks",
515 ctx->wkd_data, flags);
523 es_fclose (plaintext);
524 mime_parser_release (parser);
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);