Line data Source code
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 "util.h"
26 : #include "ccparray.h"
27 : #include "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 0 : decrypt_data_status_cb (void *opaque, const char *keyword, char *args)
63 : {
64 0 : receive_ctx_t ctx = opaque;
65 : (void)ctx;
66 0 : if (DBG_CRYPTO)
67 0 : log_debug ("gpg status: %s %s\n", keyword, args);
68 0 : }
69 :
70 :
71 : /* Decrypt the collected data. */
72 : static void
73 0 : 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 0 : es_rewind (ctx->encrypted);
81 :
82 0 : if (!ctx->plaintext)
83 0 : ctx->plaintext = es_fopenmem (0, "w+b");
84 0 : if (!ctx->plaintext)
85 : {
86 0 : err = gpg_error_from_syserror ();
87 0 : log_error ("error allocating space for plaintext: %s\n",
88 : gpg_strerror (err));
89 0 : return;
90 : }
91 :
92 0 : ccparray_init (&ccp, 0);
93 :
94 0 : 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 0 : ccparray_put (&ccp, "--max-output=0xf0000"); /*FIXME: Change s/F/1/ */
99 0 : ccparray_put (&ccp, "--batch");
100 0 : if (opt.verbose)
101 0 : ccparray_put (&ccp, "--verbose");
102 0 : ccparray_put (&ccp, "--always-trust");
103 0 : ccparray_put (&ccp, "--decrypt");
104 0 : ccparray_put (&ccp, "--");
105 :
106 0 : ccparray_put (&ccp, NULL);
107 0 : argv = ccparray_get (&ccp, NULL);
108 0 : if (!argv)
109 : {
110 0 : err = gpg_error_from_syserror ();
111 0 : goto leave;
112 : }
113 0 : err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->encrypted,
114 : NULL, ctx->plaintext,
115 : decrypt_data_status_cb, ctx);
116 0 : if (err)
117 : {
118 0 : log_error ("decryption failed: %s\n", gpg_strerror (err));
119 0 : goto leave;
120 : }
121 :
122 0 : if (DBG_CRYPTO)
123 : {
124 0 : es_rewind (ctx->plaintext);
125 0 : log_debug ("plaintext: '");
126 0 : while ((c = es_getc (ctx->plaintext)) != EOF)
127 0 : log_printf ("%c", c);
128 0 : log_printf ("'\n");
129 : }
130 0 : es_rewind (ctx->plaintext);
131 :
132 : leave:
133 0 : xfree (argv);
134 : }
135 :
136 :
137 : static void
138 0 : verify_signature_status_cb (void *opaque, const char *keyword, char *args)
139 : {
140 0 : receive_ctx_t ctx = opaque;
141 : (void)ctx;
142 0 : if (DBG_CRYPTO)
143 0 : log_debug ("gpg status: %s %s\n", keyword, args);
144 0 : }
145 :
146 : /* Verify the signed data. */
147 : static void
148 0 : verify_signature (receive_ctx_t ctx)
149 : {
150 : gpg_error_t err;
151 : ccparray_t ccp;
152 : const char **argv;
153 :
154 0 : log_assert (ctx->signeddata);
155 0 : log_assert (ctx->signature);
156 0 : es_rewind (ctx->signeddata);
157 0 : es_rewind (ctx->signature);
158 :
159 0 : ccparray_init (&ccp, 0);
160 :
161 0 : ccparray_put (&ccp, "--no-options");
162 0 : ccparray_put (&ccp, "--batch");
163 0 : if (opt.verbose)
164 0 : ccparray_put (&ccp, "--verbose");
165 0 : ccparray_put (&ccp, "--enable-special-filenames");
166 0 : ccparray_put (&ccp, "--status-fd=2");
167 0 : ccparray_put (&ccp, "--always-trust"); /* To avoid trustdb checks. */
168 0 : ccparray_put (&ccp, "--verify");
169 0 : ccparray_put (&ccp, "--");
170 0 : ccparray_put (&ccp, "-&@INEXTRA@");
171 0 : ccparray_put (&ccp, "-");
172 :
173 0 : ccparray_put (&ccp, NULL);
174 0 : argv = ccparray_get (&ccp, NULL);
175 0 : if (!argv)
176 : {
177 0 : err = gpg_error_from_syserror ();
178 0 : goto leave;
179 : }
180 0 : err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->signeddata,
181 : ctx->signature, NULL,
182 : verify_signature_status_cb, ctx);
183 0 : if (err)
184 : {
185 0 : log_error ("verification failed: %s\n", gpg_strerror (err));
186 0 : goto leave;
187 : }
188 :
189 0 : log_debug ("Fixme: Verification result is not used\n");
190 :
191 : leave:
192 0 : xfree (argv);
193 0 : }
194 :
195 :
196 : static gpg_error_t
197 0 : collect_encrypted (void *cookie, const char *data)
198 : {
199 0 : receive_ctx_t ctx = cookie;
200 :
201 0 : if (!ctx->encrypted)
202 0 : if (!(ctx->encrypted = es_fopenmem (MAX_ENCRYPTED, "w+b,samethread")))
203 0 : return gpg_error_from_syserror ();
204 0 : if (data)
205 0 : es_fputs (data, ctx->encrypted);
206 :
207 0 : if (es_ferror (ctx->encrypted))
208 0 : return gpg_error_from_syserror ();
209 :
210 0 : if (!data)
211 : {
212 0 : decrypt_data (ctx);
213 : }
214 :
215 0 : return 0;
216 : }
217 :
218 :
219 : static gpg_error_t
220 0 : collect_signeddata (void *cookie, const char *data)
221 : {
222 0 : receive_ctx_t ctx = cookie;
223 :
224 0 : if (!ctx->signeddata)
225 0 : if (!(ctx->signeddata = es_fopenmem (MAX_SIGNEDDATA, "w+b,samethread")))
226 0 : return gpg_error_from_syserror ();
227 0 : if (data)
228 0 : es_fputs (data, ctx->signeddata);
229 :
230 0 : if (es_ferror (ctx->signeddata))
231 0 : return gpg_error_from_syserror ();
232 0 : return 0;
233 : }
234 :
235 : static gpg_error_t
236 0 : collect_signature (void *cookie, const char *data)
237 : {
238 0 : receive_ctx_t ctx = cookie;
239 :
240 0 : if (!ctx->signature)
241 0 : if (!(ctx->signature = es_fopenmem (MAX_SIGNATURE, "w+b,samethread")))
242 0 : return gpg_error_from_syserror ();
243 0 : if (data)
244 0 : es_fputs (data, ctx->signature);
245 :
246 0 : if (es_ferror (ctx->signature))
247 0 : return gpg_error_from_syserror ();
248 :
249 0 : if (!data)
250 : {
251 0 : verify_signature (ctx);
252 : }
253 :
254 0 : return 0;
255 : }
256 :
257 :
258 : static gpg_error_t
259 0 : new_part (void *cookie, const char *mediatype, const char *mediasubtype)
260 : {
261 0 : receive_ctx_t ctx = cookie;
262 0 : gpg_error_t err = 0;
263 :
264 0 : ctx->collect_key_data = 0;
265 0 : ctx->collect_wkd_data = 0;
266 :
267 0 : if (!strcmp (mediatype, "application")
268 0 : && !strcmp (mediasubtype, "pgp-keys"))
269 : {
270 0 : log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
271 0 : if (ctx->key_data)
272 : {
273 0 : log_error ("we already got a key - ignoring this part\n");
274 0 : err = gpg_error (GPG_ERR_FALSE);
275 : }
276 : else
277 : {
278 0 : rfc822parse_t msg = mime_parser_rfc822parser (ctx->parser);
279 0 : if (msg)
280 : {
281 : char *value;
282 : size_t valueoff;
283 :
284 0 : value = rfc822parse_get_field (msg, "Wks-Draft-Version",
285 : -1, &valueoff);
286 0 : if (value)
287 : {
288 0 : if (atoi(value+valueoff) >= 2 )
289 0 : ctx->draft_version_2 = 1;
290 0 : free (value);
291 : }
292 : }
293 :
294 0 : ctx->key_data = es_fopenmem (0, "w+b");
295 0 : if (!ctx->key_data)
296 : {
297 0 : err = gpg_error_from_syserror ();
298 0 : log_error ("error allocating space for key: %s\n",
299 : gpg_strerror (err));
300 : }
301 : else
302 : {
303 0 : ctx->collect_key_data = 1;
304 0 : err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
305 : }
306 : }
307 : }
308 0 : else if (!strcmp (mediatype, "application")
309 0 : && !strcmp (mediasubtype, "vnd.gnupg.wks"))
310 : {
311 0 : log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
312 0 : if (ctx->wkd_data)
313 : {
314 0 : log_error ("we already got a wkd part - ignoring this part\n");
315 0 : err = gpg_error (GPG_ERR_FALSE);
316 : }
317 : else
318 : {
319 0 : ctx->wkd_data = es_fopenmem (0, "w+b");
320 0 : if (!ctx->wkd_data)
321 : {
322 0 : err = gpg_error_from_syserror ();
323 0 : log_error ("error allocating space for key: %s\n",
324 : gpg_strerror (err));
325 : }
326 : else
327 : {
328 0 : ctx->collect_wkd_data = 1;
329 0 : err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
330 : }
331 : }
332 : }
333 0 : else if (!strcmp (mediatype, "multipart")
334 0 : && !strcmp (mediasubtype, "mixed"))
335 : {
336 0 : ctx->multipart_mixed_seen = 1;
337 : }
338 0 : else if (!strcmp (mediatype, "text"))
339 : {
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 0 : if (!ctx->multipart_mixed_seen)
344 0 : err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
345 : }
346 : else
347 : {
348 0 : log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
349 0 : err = gpg_error (GPG_ERR_FALSE); /* We do not want the part. */
350 : }
351 :
352 0 : return err;
353 : }
354 :
355 :
356 : static gpg_error_t
357 0 : part_data (void *cookie, const void *data, size_t datalen)
358 : {
359 0 : receive_ctx_t ctx = cookie;
360 :
361 0 : if (data)
362 : {
363 0 : if (DBG_MIME)
364 0 : log_debug ("part_data: '%.*s'\n", (int)datalen, (const char*)data);
365 0 : if (ctx->collect_key_data)
366 : {
367 0 : if (es_write (ctx->key_data, data, datalen, NULL)
368 0 : || es_fputs ("\n", ctx->key_data))
369 0 : return gpg_error_from_syserror ();
370 : }
371 0 : if (ctx->collect_wkd_data)
372 : {
373 0 : if (es_write (ctx->wkd_data, data, datalen, NULL)
374 0 : || es_fputs ("\n", ctx->wkd_data))
375 0 : return gpg_error_from_syserror ();
376 : }
377 : }
378 : else
379 : {
380 0 : if (DBG_MIME)
381 0 : log_debug ("part_data: finished\n");
382 0 : ctx->collect_key_data = 0;
383 0 : ctx->collect_wkd_data = 0;
384 : }
385 0 : return 0;
386 : }
387 :
388 :
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
391 : * decrypted data. */
392 : gpg_error_t
393 0 : wks_receive (estream_t fp,
394 : gpg_error_t (*result_cb)(void *opaque,
395 : const char *mediatype,
396 : estream_t data,
397 : unsigned int flags),
398 : void *cb_data)
399 : {
400 : gpg_error_t err;
401 : receive_ctx_t ctx;
402 : mime_parser_t parser;
403 0 : estream_t plaintext = NULL;
404 : int c;
405 0 : unsigned int flags = 0;
406 :
407 0 : ctx = xtrycalloc (1, sizeof *ctx);
408 0 : if (!ctx)
409 0 : return gpg_error_from_syserror ();
410 :
411 0 : err = mime_parser_new (&parser, ctx);
412 0 : if (err)
413 0 : goto leave;
414 0 : if (DBG_PARSER)
415 0 : mime_parser_set_verbose (parser, 1);
416 0 : mime_parser_set_new_part (parser, new_part);
417 0 : mime_parser_set_part_data (parser, part_data);
418 0 : mime_parser_set_collect_encrypted (parser, collect_encrypted);
419 0 : mime_parser_set_collect_signeddata (parser, collect_signeddata);
420 0 : mime_parser_set_collect_signature (parser, collect_signature);
421 :
422 0 : ctx->parser = parser;
423 :
424 0 : err = mime_parser_parse (parser, fp);
425 0 : if (err)
426 0 : goto leave;
427 :
428 0 : if (ctx->key_data)
429 0 : log_info ("key data found\n");
430 0 : if (ctx->wkd_data)
431 0 : log_info ("wkd data found\n");
432 0 : if (ctx->draft_version_2)
433 : {
434 0 : log_info ("draft version 2 requested\n");
435 0 : flags |= WKS_RECEIVE_DRAFT2;
436 : }
437 :
438 0 : if (ctx->plaintext)
439 : {
440 0 : if (opt.verbose)
441 0 : log_info ("parsing decrypted message\n");
442 0 : plaintext = ctx->plaintext;
443 0 : ctx->plaintext = NULL;
444 0 : if (ctx->encrypted)
445 0 : es_rewind (ctx->encrypted);
446 0 : if (ctx->signeddata)
447 0 : es_rewind (ctx->signeddata);
448 0 : if (ctx->signature)
449 0 : es_rewind (ctx->signature);
450 0 : err = mime_parser_parse (parser, plaintext);
451 0 : if (err)
452 0 : return err;
453 : }
454 :
455 0 : if (!ctx->key_data && !ctx->wkd_data)
456 : {
457 0 : log_error ("no suitable data found in the message\n");
458 0 : err = gpg_error (GPG_ERR_NO_DATA);
459 0 : goto leave;
460 : }
461 :
462 0 : if (ctx->key_data)
463 : {
464 0 : if (DBG_MIME)
465 : {
466 0 : es_rewind (ctx->key_data);
467 0 : log_debug ("Key: '");
468 0 : log_printf ("\n");
469 0 : while ((c = es_getc (ctx->key_data)) != EOF)
470 0 : log_printf ("%c", c);
471 0 : log_printf ("'\n");
472 : }
473 0 : if (result_cb)
474 : {
475 0 : es_rewind (ctx->key_data);
476 0 : err = result_cb (cb_data, "application/pgp-keys",
477 : ctx->key_data, flags);
478 0 : if (err)
479 0 : goto leave;
480 : }
481 : }
482 0 : if (ctx->wkd_data)
483 : {
484 0 : if (DBG_MIME)
485 : {
486 0 : es_rewind (ctx->wkd_data);
487 0 : log_debug ("WKD: '");
488 0 : log_printf ("\n");
489 0 : while ((c = es_getc (ctx->wkd_data)) != EOF)
490 0 : log_printf ("%c", c);
491 0 : log_printf ("'\n");
492 : }
493 0 : if (result_cb)
494 : {
495 0 : es_rewind (ctx->wkd_data);
496 0 : err = result_cb (cb_data, "application/vnd.gnupg.wks",
497 : ctx->wkd_data, flags);
498 0 : if (err)
499 0 : goto leave;
500 : }
501 : }
502 :
503 :
504 : leave:
505 0 : es_fclose (plaintext);
506 0 : mime_parser_release (parser);
507 0 : ctx->parser = NULL;
508 0 : es_fclose (ctx->encrypted);
509 0 : es_fclose (ctx->plaintext);
510 0 : es_fclose (ctx->signeddata);
511 0 : es_fclose (ctx->signature);
512 0 : es_fclose (ctx->key_data);
513 0 : es_fclose (ctx->wkd_data);
514 0 : xfree (ctx);
515 0 : return err;
516 : }
|