1 /* wks-receive.c - Receive a WKS mail
2 * Copyright (C) 2016 g10 Code GmbH
4 * This file is part of GnuPG.
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.
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.
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/>.
29 #include "rfc822parse.h"
30 #include "mime-parser.h"
33 /* Limit of acceptable signed data. */
34 #define MAX_SIGNEDDATA 10000
36 /* Limit of acceptable signature. */
37 #define MAX_SIGNATURE 10000
39 /* Limit of acceptable encrypted data. */
40 #define MAX_ENCRYPTED 100000
42 /* Data for a received object. */
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;
57 typedef struct receive_ctx_s *receive_ctx_t;
62 decrypt_data_status_cb (void *opaque, const char *keyword, char *args)
64 receive_ctx_t ctx = opaque;
67 log_debug ("gpg status: %s %s\n", keyword, args);
71 /* Decrypt the collected data. */
73 decrypt_data (receive_ctx_t ctx)
80 es_rewind (ctx->encrypted);
83 ctx->plaintext = es_fopenmem (0, "w+b");
86 err = gpg_error_from_syserror ();
87 log_error ("error allocating space for plaintext: %s\n",
92 ccparray_init (&ccp, 0);
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");
101 ccparray_put (&ccp, "--verbose");
102 ccparray_put (&ccp, "--always-trust");
103 ccparray_put (&ccp, "--decrypt");
104 ccparray_put (&ccp, "--");
106 ccparray_put (&ccp, NULL);
107 argv = ccparray_get (&ccp, NULL);
110 err = gpg_error_from_syserror ();
113 err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->encrypted,
114 NULL, ctx->plaintext,
115 decrypt_data_status_cb, ctx);
118 log_error ("decryption failed: %s\n", gpg_strerror (err));
124 es_rewind (ctx->plaintext);
125 log_debug ("plaintext: '");
126 while ((c = es_getc (ctx->plaintext)) != EOF)
127 log_printf ("%c", c);
130 es_rewind (ctx->plaintext);
138 verify_signature_status_cb (void *opaque, const char *keyword, char *args)
140 receive_ctx_t ctx = opaque;
143 log_debug ("gpg status: %s %s\n", keyword, args);
146 /* Verify the signed data. */
148 verify_signature (receive_ctx_t ctx)
154 log_assert (ctx->signeddata);
155 log_assert (ctx->signature);
156 es_rewind (ctx->signeddata);
157 es_rewind (ctx->signature);
159 ccparray_init (&ccp, 0);
161 ccparray_put (&ccp, "--no-options");
162 ccparray_put (&ccp, "--batch");
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, "-");
173 ccparray_put (&ccp, NULL);
174 argv = ccparray_get (&ccp, NULL);
177 err = gpg_error_from_syserror ();
180 err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->signeddata,
181 ctx->signature, NULL,
182 verify_signature_status_cb, ctx);
185 log_error ("verification failed: %s\n", gpg_strerror (err));
189 log_debug ("Fixme: Verification result is not used\n");
197 collect_encrypted (void *cookie, const char *data)
199 receive_ctx_t ctx = cookie;
202 if (!(ctx->encrypted = es_fopenmem (MAX_ENCRYPTED, "w+b,samethread")))
203 return gpg_error_from_syserror ();
205 es_fputs (data, ctx->encrypted);
207 if (es_ferror (ctx->encrypted))
208 return gpg_error_from_syserror ();
220 collect_signeddata (void *cookie, const char *data)
222 receive_ctx_t ctx = cookie;
224 if (!ctx->signeddata)
225 if (!(ctx->signeddata = es_fopenmem (MAX_SIGNEDDATA, "w+b,samethread")))
226 return gpg_error_from_syserror ();
228 es_fputs (data, ctx->signeddata);
230 if (es_ferror (ctx->signeddata))
231 return gpg_error_from_syserror ();
236 collect_signature (void *cookie, const char *data)
238 receive_ctx_t ctx = cookie;
241 if (!(ctx->signature = es_fopenmem (MAX_SIGNATURE, "w+b,samethread")))
242 return gpg_error_from_syserror ();
244 es_fputs (data, ctx->signature);
246 if (es_ferror (ctx->signature))
247 return gpg_error_from_syserror ();
251 verify_signature (ctx);
259 new_part (void *cookie, const char *mediatype, const char *mediasubtype)
261 receive_ctx_t ctx = cookie;
264 ctx->collect_key_data = 0;
265 ctx->collect_wkd_data = 0;
267 if (!strcmp (mediatype, "application")
268 && !strcmp (mediasubtype, "pgp-keys"))
270 log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
273 log_error ("we already got a key - ignoring this part\n");
274 err = gpg_error (GPG_ERR_FALSE);
278 rfc822parse_t msg = mime_parser_rfc822parser (ctx->parser);
284 value = rfc822parse_get_field (msg, "Wks-Draft-Version",
288 if (atoi(value+valueoff) >= 2 )
289 ctx->draft_version_2 = 1;
294 ctx->key_data = es_fopenmem (0, "w+b");
297 err = gpg_error_from_syserror ();
298 log_error ("error allocating space for key: %s\n",
303 ctx->collect_key_data = 1;
304 err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
308 else if (!strcmp (mediatype, "application")
309 && !strcmp (mediasubtype, "vnd.gnupg.wks"))
311 log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
314 log_error ("we already got a wkd part - ignoring this part\n");
315 err = gpg_error (GPG_ERR_FALSE);
319 ctx->wkd_data = es_fopenmem (0, "w+b");
322 err = gpg_error_from_syserror ();
323 log_error ("error allocating space for key: %s\n",
328 ctx->collect_wkd_data = 1;
329 err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
333 else if (!strcmp (mediatype, "multipart")
334 && !strcmp (mediasubtype, "mixed"))
336 ctx->multipart_mixed_seen = 1;
338 else if (!strcmp (mediatype, "text"))
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);
348 log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
349 err = gpg_error (GPG_ERR_FALSE); /* We do not want the part. */
357 part_data (void *cookie, const void *data, size_t datalen)
359 receive_ctx_t ctx = cookie;
364 log_debug ("part_data: '%.*s'\n", (int)datalen, (const char*)data);
365 if (ctx->collect_key_data)
367 if (es_write (ctx->key_data, data, datalen, NULL)
368 || es_fputs ("\n", ctx->key_data))
369 return gpg_error_from_syserror ();
371 if (ctx->collect_wkd_data)
373 if (es_write (ctx->wkd_data, data, datalen, NULL)
374 || es_fputs ("\n", ctx->wkd_data))
375 return gpg_error_from_syserror ();
381 log_debug ("part_data: finished\n");
382 ctx->collect_key_data = 0;
383 ctx->collect_wkd_data = 0;
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
393 wks_receive (estream_t fp,
394 gpg_error_t (*result_cb)(void *opaque,
395 const char *mediatype,
402 mime_parser_t parser;
403 estream_t plaintext = NULL;
405 unsigned int flags = 0;
407 ctx = xtrycalloc (1, sizeof *ctx);
409 return gpg_error_from_syserror ();
411 err = mime_parser_new (&parser, ctx);
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);
422 ctx->parser = parser;
424 err = mime_parser_parse (parser, fp);
429 log_info ("key data found\n");
431 log_info ("wkd data found\n");
432 if (ctx->draft_version_2)
434 log_info ("draft version 2 requested\n");
435 flags |= WKS_RECEIVE_DRAFT2;
441 log_info ("parsing decrypted message\n");
442 plaintext = ctx->plaintext;
443 ctx->plaintext = NULL;
445 es_rewind (ctx->encrypted);
447 es_rewind (ctx->signeddata);
449 es_rewind (ctx->signature);
450 err = mime_parser_parse (parser, plaintext);
455 if (!ctx->key_data && !ctx->wkd_data)
457 log_error ("no suitable data found in the message\n");
458 err = gpg_error (GPG_ERR_NO_DATA);
466 es_rewind (ctx->key_data);
467 log_debug ("Key: '");
469 while ((c = es_getc (ctx->key_data)) != EOF)
470 log_printf ("%c", c);
475 es_rewind (ctx->key_data);
476 err = result_cb (cb_data, "application/pgp-keys",
477 ctx->key_data, flags);
486 es_rewind (ctx->wkd_data);
487 log_debug ("WKD: '");
489 while ((c = es_getc (ctx->wkd_data)) != EOF)
490 log_printf ("%c", c);
495 es_rewind (ctx->wkd_data);
496 err = result_cb (cb_data, "application/vnd.gnupg.wks",
497 ctx->wkd_data, flags);
505 es_fclose (plaintext);
506 mime_parser_release (parser);
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);