LCOV - code coverage report
Current view: top level - tools - wks-receive.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 252 0.0 %
Date: 2016-11-29 15:00:56 Functions: 0 10 0.0 %

          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             : }

Generated by: LCOV version 1.11