LCOV - code coverage report
Current view: top level - tools - gpg-wks-client.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 479 0.0 %
Date: 2016-12-01 18:37:21 Functions: 0 16 0.0 %

          Line data    Source code
       1             : /* gpg-wks-client.c - A client for the Web Key Service protocols.
       2             :  * Copyright (C) 2016 Werner Koch
       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 "i18n.h"
      27             : #include "sysutils.h"
      28             : #include "init.h"
      29             : #include "asshelp.h"
      30             : #include "userids.h"
      31             : #include "ccparray.h"
      32             : #include "exectool.h"
      33             : #include "mbox-util.h"
      34             : #include "name-value.h"
      35             : #include "call-dirmngr.h"
      36             : #include "mime-maker.h"
      37             : #include "send-mail.h"
      38             : #include "gpg-wks.h"
      39             : 
      40             : 
      41             : /* Constants to identify the commands and options. */
      42             : enum cmd_and_opt_values
      43             :   {
      44             :     aNull = 0,
      45             : 
      46             :     oQuiet      = 'q',
      47             :     oVerbose    = 'v',
      48             :     oOutput     = 'o',
      49             : 
      50             :     oDebug      = 500,
      51             : 
      52             :     aSupported,
      53             :     aCreate,
      54             :     aReceive,
      55             :     aRead,
      56             : 
      57             :     oGpgProgram,
      58             :     oSend,
      59             :     oFakeSubmissionAddr,
      60             : 
      61             :     oDummy
      62             :   };
      63             : 
      64             : 
      65             : /* The list of commands and options. */
      66             : static ARGPARSE_OPTS opts[] = {
      67             :   ARGPARSE_group (300, ("@Commands:\n ")),
      68             : 
      69             :   ARGPARSE_c (aSupported, "supported",
      70             :               ("check whether provider supports WKS")),
      71             :   ARGPARSE_c (aCreate,   "create",
      72             :               ("create a publication request")),
      73             :   ARGPARSE_c (aReceive,   "receive",
      74             :               ("receive a MIME confirmation request")),
      75             :   ARGPARSE_c (aRead,      "read",
      76             :               ("receive a plain text confirmation request")),
      77             : 
      78             :   ARGPARSE_group (301, ("@\nOptions:\n ")),
      79             : 
      80             :   ARGPARSE_s_n (oVerbose, "verbose", ("verbose")),
      81             :   ARGPARSE_s_n (oQuiet, "quiet",  ("be somewhat more quiet")),
      82             :   ARGPARSE_s_s (oDebug, "debug", "@"),
      83             :   ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
      84             :   ARGPARSE_s_n (oSend, "send", "send the mail using sendmail"),
      85             :   ARGPARSE_s_s (oOutput, "output", "|FILE|write the mail to FILE"),
      86             : 
      87             :   ARGPARSE_s_s (oFakeSubmissionAddr, "fake-submission-addr", "@"),
      88             : 
      89             :   ARGPARSE_end ()
      90             : };
      91             : 
      92             : 
      93             : /* The list of supported debug flags.  */
      94             : static struct debug_flags_s debug_flags [] =
      95             :   {
      96             :     { DBG_MIME_VALUE   , "mime"    },
      97             :     { DBG_PARSER_VALUE , "parser"  },
      98             :     { DBG_CRYPTO_VALUE , "crypto"  },
      99             :     { DBG_MEMORY_VALUE , "memory"  },
     100             :     { DBG_MEMSTAT_VALUE, "memstat" },
     101             :     { DBG_IPC_VALUE    , "ipc"     },
     102             :     { DBG_EXTPROG_VALUE, "extprog" },
     103             :     { 0, NULL }
     104             :   };
     105             : 
     106             : 
     107             : 
     108             : /* Value of the option --fake-submission-addr.  */
     109             : const char *fake_submission_addr;
     110             : 
     111             : 
     112             : static void wrong_args (const char *text) GPGRT_ATTR_NORETURN;
     113             : static gpg_error_t command_supported (char *userid);
     114             : static gpg_error_t command_send (const char *fingerprint, char *userid);
     115             : static gpg_error_t encrypt_response (estream_t *r_output, estream_t input,
     116             :                                      const char *addrspec,
     117             :                                      const char *fingerprint);
     118             : static gpg_error_t read_confirmation_request (estream_t msg);
     119             : static gpg_error_t command_receive_cb (void *opaque,
     120             :                                        const char *mediatype, estream_t fp,
     121             :                                        unsigned int flags);
     122             : 
     123             : 
     124             : 
     125             : /* Print usage information and and provide strings for help. */
     126             : static const char *
     127           0 : my_strusage( int level )
     128             : {
     129             :   const char *p;
     130             : 
     131           0 :   switch (level)
     132             :     {
     133           0 :     case 11: p = "gpg-wks-client (@GNUPG@)";
     134           0 :       break;
     135           0 :     case 13: p = VERSION; break;
     136           0 :     case 17: p = PRINTABLE_OS_NAME; break;
     137           0 :     case 19: p = ("Please report bugs to <@EMAIL@>.\n"); break;
     138             : 
     139             :     case 1:
     140             :     case 40:
     141           0 :       p = ("Usage: gpg-wks-client [command] [options] [args] (-h for help)");
     142           0 :       break;
     143             :     case 41:
     144           0 :       p = ("Syntax: gpg-wks-client [command] [options] [args]\n"
     145             :            "Client for the Web Key Service\n");
     146           0 :       break;
     147             : 
     148           0 :     default: p = NULL; break;
     149             :     }
     150           0 :   return p;
     151             : }
     152             : 
     153             : 
     154             : static void
     155           0 : wrong_args (const char *text)
     156             : {
     157           0 :   es_fprintf (es_stderr, _("usage: %s [options] %s\n"), strusage (11), text);
     158           0 :   exit (2);
     159             : }
     160             : 
     161             : 
     162             : 
     163             : /* Command line parsing.  */
     164             : static enum cmd_and_opt_values
     165           0 : parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
     166             : {
     167           0 :   enum cmd_and_opt_values cmd = 0;
     168           0 :   int no_more_options = 0;
     169             : 
     170           0 :   while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
     171             :     {
     172           0 :       switch (pargs->r_opt)
     173             :         {
     174           0 :         case oQuiet:     opt.quiet = 1; break;
     175           0 :         case oVerbose:   opt.verbose++; break;
     176             :         case oDebug:
     177           0 :           if (parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags))
     178             :             {
     179           0 :               pargs->r_opt = ARGPARSE_INVALID_ARG;
     180           0 :               pargs->err = ARGPARSE_PRINT_ERROR;
     181             :             }
     182           0 :           break;
     183             : 
     184             :         case oGpgProgram:
     185           0 :           opt.gpg_program = pargs->r.ret_str;
     186           0 :           break;
     187             :         case oSend:
     188           0 :           opt.use_sendmail = 1;
     189           0 :           break;
     190             :         case oOutput:
     191           0 :           opt.output = pargs->r.ret_str;
     192           0 :           break;
     193             :         case oFakeSubmissionAddr:
     194           0 :           fake_submission_addr = pargs->r.ret_str;
     195           0 :           break;
     196             : 
     197             :         case aSupported:
     198             :         case aCreate:
     199             :         case aReceive:
     200             :         case aRead:
     201           0 :           cmd = pargs->r_opt;
     202           0 :           break;
     203             : 
     204           0 :         default: pargs->err = 2; break;
     205             :         }
     206             :     }
     207             : 
     208           0 :   return cmd;
     209             : }
     210             : 
     211             : 
     212             : 
     213             : /* gpg-wks-client main. */
     214             : int
     215           0 : main (int argc, char **argv)
     216             : {
     217             :   gpg_error_t err;
     218             :   ARGPARSE_ARGS pargs;
     219             :   enum cmd_and_opt_values cmd;
     220             : 
     221           0 :   gnupg_reopen_std ("gpg-wks-client");
     222           0 :   set_strusage (my_strusage);
     223           0 :   log_set_prefix ("gpg-wks-client", GPGRT_LOG_WITH_PREFIX);
     224             : 
     225             :   /* Make sure that our subsystems are ready.  */
     226           0 :   i18n_init();
     227           0 :   init_common_subsystems (&argc, &argv);
     228             : 
     229           0 :   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
     230           0 :   setup_libassuan_logging (&opt.debug, NULL);
     231             : 
     232             :   /* Parse the command line. */
     233           0 :   pargs.argc  = &argc;
     234           0 :   pargs.argv  = &argv;
     235           0 :   pargs.flags = ARGPARSE_FLAG_KEEP;
     236           0 :   cmd = parse_arguments (&pargs, opts);
     237             : 
     238           0 :   if (log_get_errorcount (0))
     239           0 :     exit (2);
     240             : 
     241             :   /* Print a warning if an argument looks like an option.  */
     242           0 :   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
     243             :     {
     244             :       int i;
     245             : 
     246           0 :       for (i=0; i < argc; i++)
     247           0 :         if (argv[i][0] == '-' && argv[i][1] == '-')
     248           0 :           log_info (("NOTE: '%s' is not considered an option\n"), argv[i]);
     249             :     }
     250             : 
     251             :   /* Set defaults for non given options.  */
     252           0 :   if (!opt.gpg_program)
     253           0 :     opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
     254             : 
     255             :   /* Tell call-dirmngr what options we want.  */
     256           0 :   set_dirmngr_options (opt.verbose, (opt.debug & DBG_IPC_VALUE), 1);
     257             : 
     258             :   /* Run the selected command.  */
     259           0 :   switch (cmd)
     260             :     {
     261             :     case aSupported:
     262           0 :       if (argc != 1)
     263           0 :         wrong_args ("--supported USER-ID");
     264           0 :       err = command_supported (argv[0]);
     265           0 :       if (err && gpg_err_code (err) != GPG_ERR_FALSE)
     266           0 :         log_error ("checking support failed: %s\n", gpg_strerror (err));
     267           0 :       break;
     268             : 
     269             :     case aCreate:
     270           0 :       if (argc != 2)
     271           0 :         wrong_args ("--create FINGERPRINT USER-ID");
     272           0 :       err = command_send (argv[0], argv[1]);
     273           0 :       if (err)
     274           0 :         log_error ("creating request failed: %s\n", gpg_strerror (err));
     275           0 :       break;
     276             : 
     277             :     case aReceive:
     278           0 :       if (argc)
     279           0 :         wrong_args ("--receive < MIME-DATA");
     280           0 :       err = wks_receive (es_stdin, command_receive_cb, NULL);
     281           0 :       if (err)
     282           0 :         log_error ("processing mail failed: %s\n", gpg_strerror (err));
     283           0 :       break;
     284             : 
     285             :     case aRead:
     286           0 :       if (argc)
     287           0 :         wrong_args ("--read < WKS-DATA");
     288           0 :       err = read_confirmation_request (es_stdin);
     289           0 :       if (err)
     290           0 :         log_error ("processing mail failed: %s\n", gpg_strerror (err));
     291           0 :       break;
     292             : 
     293             :     default:
     294           0 :       usage (1);
     295           0 :       break;
     296             :     }
     297             : 
     298           0 :   return log_get_errorcount (0)? 1:0;
     299             : }
     300             : 
     301             : 
     302             : 
     303             : struct get_key_status_parm_s
     304             : {
     305             :   const char *fpr;
     306             :   int found;
     307             :   int count;
     308             : };
     309             : 
     310             : static void
     311           0 : get_key_status_cb (void *opaque, const char *keyword, char *args)
     312             : {
     313           0 :   struct get_key_status_parm_s *parm = opaque;
     314             : 
     315             :   /*log_debug ("%s: %s\n", keyword, args);*/
     316           0 :   if (!strcmp (keyword, "EXPORTED"))
     317             :     {
     318           0 :       parm->count++;
     319           0 :       if (!ascii_strcasecmp (args, parm->fpr))
     320           0 :         parm->found = 1;
     321             :     }
     322           0 : }
     323             : 
     324             : 
     325             : /* Get a key by fingerprint from gpg's keyring and make sure that the
     326             :  * mail address ADDRSPEC is included in the key.  The key is returned
     327             :  * as a new memory stream at R_KEY.
     328             :  *
     329             :  * Fixme: After we have implemented import and export filters for gpg
     330             :  * this function shall only return a key with just this user id.  */
     331             : static gpg_error_t
     332           0 : get_key (estream_t *r_key, const char *fingerprint, const char *addrspec)
     333             : {
     334             :   gpg_error_t err;
     335             :   ccparray_t ccp;
     336           0 :   const char **argv = NULL;
     337           0 :   estream_t key = NULL;
     338             :   struct get_key_status_parm_s parm;
     339           0 :   char *filterexp = NULL;
     340             : 
     341           0 :   memset (&parm, 0, sizeof parm);
     342             : 
     343           0 :   *r_key = NULL;
     344             : 
     345           0 :   key = es_fopenmem (0, "w+b");
     346           0 :   if (!key)
     347             :     {
     348           0 :       err = gpg_error_from_syserror ();
     349           0 :       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
     350           0 :       goto leave;
     351             :     }
     352             :   /* Prefix the key with the MIME content type.  */
     353           0 :   es_fputs ("Content-Type: application/pgp-keys\n"
     354             :             "\n", key);
     355             : 
     356           0 :   filterexp = es_bsprintf ("keep-uid=mbox = %s", addrspec);
     357           0 :   if (!filterexp)
     358             :     {
     359           0 :       err = gpg_error_from_syserror ();
     360           0 :       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
     361           0 :       goto leave;
     362             :     }
     363             : 
     364           0 :   ccparray_init (&ccp, 0);
     365             : 
     366           0 :   ccparray_put (&ccp, "--no-options");
     367           0 :   if (!opt.verbose)
     368           0 :     ccparray_put (&ccp, "--quiet");
     369           0 :   else if (opt.verbose > 1)
     370           0 :     ccparray_put (&ccp, "--verbose");
     371           0 :   ccparray_put (&ccp, "--batch");
     372           0 :   ccparray_put (&ccp, "--status-fd=2");
     373           0 :   ccparray_put (&ccp, "--always-trust");
     374           0 :   ccparray_put (&ccp, "--armor");
     375           0 :   ccparray_put (&ccp, "--export-options=export-minimal");
     376           0 :   ccparray_put (&ccp, "--export-filter");
     377           0 :   ccparray_put (&ccp, filterexp);
     378           0 :   ccparray_put (&ccp, "--export");
     379           0 :   ccparray_put (&ccp, "--");
     380           0 :   ccparray_put (&ccp, fingerprint);
     381             : 
     382           0 :   ccparray_put (&ccp, NULL);
     383           0 :   argv = ccparray_get (&ccp, NULL);
     384           0 :   if (!argv)
     385             :     {
     386           0 :       err = gpg_error_from_syserror ();
     387           0 :       goto leave;
     388             :     }
     389           0 :   parm.fpr = fingerprint;
     390           0 :   err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
     391             :                                 NULL, key,
     392             :                                 get_key_status_cb, &parm);
     393           0 :   if (!err && parm.count > 1)
     394           0 :     err = gpg_error (GPG_ERR_TOO_MANY);
     395           0 :   else if (!err && !parm.found)
     396           0 :     err = gpg_error (GPG_ERR_NOT_FOUND);
     397           0 :   if (err)
     398             :     {
     399           0 :       log_error ("export failed: %s\n", gpg_strerror (err));
     400           0 :       goto leave;
     401             :     }
     402             : 
     403           0 :   es_rewind (key);
     404           0 :   *r_key = key;
     405           0 :   key = NULL;
     406             : 
     407             :  leave:
     408           0 :   es_fclose (key);
     409           0 :   xfree (argv);
     410           0 :   xfree (filterexp);
     411           0 :   return err;
     412             : }
     413             : 
     414             : 
     415             : 
     416             : static void
     417           0 : decrypt_stream_status_cb (void *opaque, const char *keyword, char *args)
     418             : {
     419             :   (void)opaque;
     420             : 
     421           0 :   if (DBG_CRYPTO)
     422           0 :     log_debug ("gpg status: %s %s\n", keyword, args);
     423           0 : }
     424             : 
     425             : 
     426             : /* Decrypt the INPUT stream to a new stream which is stored at success
     427             :  * at R_OUTPUT.  */
     428             : static gpg_error_t
     429           0 : decrypt_stream (estream_t *r_output, estream_t input)
     430             : {
     431             :   gpg_error_t err;
     432             :   ccparray_t ccp;
     433             :   const char **argv;
     434             :   estream_t output;
     435             : 
     436           0 :   *r_output = NULL;
     437             : 
     438           0 :   output = es_fopenmem (0, "w+b");
     439           0 :   if (!output)
     440             :     {
     441           0 :       err = gpg_error_from_syserror ();
     442           0 :       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
     443           0 :       return err;
     444             :     }
     445             : 
     446           0 :   ccparray_init (&ccp, 0);
     447             : 
     448           0 :   ccparray_put (&ccp, "--no-options");
     449             :   /* We limit the output to 64 KiB to avoid DoS using compression
     450             :    * tricks.  A regular client will anyway only send a minimal key;
     451             :    * that is one w/o key signatures and attribute packets.  */
     452           0 :   ccparray_put (&ccp, "--max-output=0x10000");
     453           0 :   if (!opt.verbose)
     454           0 :     ccparray_put (&ccp, "--quiet");
     455           0 :   else if (opt.verbose > 1)
     456           0 :     ccparray_put (&ccp, "--verbose");
     457           0 :   ccparray_put (&ccp, "--batch");
     458           0 :   ccparray_put (&ccp, "--status-fd=2");
     459           0 :   ccparray_put (&ccp, "--decrypt");
     460           0 :   ccparray_put (&ccp, "--");
     461             : 
     462           0 :   ccparray_put (&ccp, NULL);
     463           0 :   argv = ccparray_get (&ccp, NULL);
     464           0 :   if (!argv)
     465             :     {
     466           0 :       err = gpg_error_from_syserror ();
     467           0 :       goto leave;
     468             :     }
     469           0 :   err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
     470             :                                 NULL, output,
     471             :                                 decrypt_stream_status_cb, NULL);
     472           0 :   if (err)
     473             :     {
     474           0 :       log_error ("decryption failed: %s\n", gpg_strerror (err));
     475           0 :       goto leave;
     476             :     }
     477           0 :   else if (opt.verbose)
     478           0 :     log_info ("decryption succeeded\n");
     479             : 
     480           0 :   es_rewind (output);
     481           0 :   *r_output = output;
     482           0 :   output = NULL;
     483             : 
     484             :  leave:
     485           0 :   es_fclose (output);
     486           0 :   xfree (argv);
     487           0 :   return err;
     488             : }
     489             : 
     490             : 
     491             : 
     492             : 
     493             : /* Check whether the  provider supports the WKS protocol.  */
     494             : static gpg_error_t
     495           0 : command_supported (char *userid)
     496             : {
     497             :   gpg_error_t err;
     498           0 :   char *addrspec = NULL;
     499           0 :   char *submission_to = NULL;
     500             : 
     501           0 :   addrspec = mailbox_from_userid (userid);
     502           0 :   if (!addrspec)
     503             :     {
     504           0 :       log_error (_("\"%s\" is not a proper mail address\n"), userid);
     505           0 :       err = gpg_error (GPG_ERR_INV_USER_ID);
     506           0 :       goto leave;
     507             :     }
     508             : 
     509             :   /* Get the submission address.  */
     510           0 :   err = wkd_get_submission_address (addrspec, &submission_to);
     511           0 :   if (err)
     512             :     {
     513           0 :       if (gpg_err_code (err) == GPG_ERR_NO_DATA
     514           0 :           || gpg_err_code (err) == GPG_ERR_UNKNOWN_HOST)
     515             :         {
     516           0 :           if (opt.verbose)
     517           0 :             log_info ("provider for '%s' does NOT support WKS (%s)\n",
     518             :                       addrspec, gpg_strerror (err));
     519           0 :           err = gpg_error (GPG_ERR_FALSE);
     520           0 :           log_inc_errorcount ();
     521             :         }
     522           0 :       goto leave;
     523             :     }
     524           0 :   if (opt.verbose)
     525           0 :     log_info ("provider for '%s' supports WKS\n", addrspec);
     526             : 
     527             :  leave:
     528           0 :   xfree (submission_to);
     529           0 :   xfree (addrspec);
     530           0 :   return err;
     531             : }
     532             : 
     533             : 
     534             : 
     535             : /* Locate the key by fingerprint and userid and send a publication
     536             :  * request.  */
     537             : static gpg_error_t
     538           0 : command_send (const char *fingerprint, char *userid)
     539             : {
     540             :   gpg_error_t err;
     541             :   KEYDB_SEARCH_DESC desc;
     542           0 :   char *addrspec = NULL;
     543           0 :   estream_t key = NULL;
     544           0 :   estream_t keyenc = NULL;
     545           0 :   char *submission_to = NULL;
     546           0 :   mime_maker_t mime = NULL;
     547             :   struct policy_flags_s policy;
     548             : 
     549           0 :   memset (&policy, 0, sizeof policy);
     550             : 
     551           0 :   if (classify_user_id (fingerprint, &desc, 1)
     552           0 :       || !(desc.mode == KEYDB_SEARCH_MODE_FPR
     553           0 :            || desc.mode == KEYDB_SEARCH_MODE_FPR20))
     554             :     {
     555           0 :       log_error (_("\"%s\" is not a fingerprint\n"), fingerprint);
     556           0 :       err = gpg_error (GPG_ERR_INV_NAME);
     557           0 :       goto leave;
     558             :     }
     559           0 :   addrspec = mailbox_from_userid (userid);
     560           0 :   if (!addrspec)
     561             :     {
     562           0 :       log_error (_("\"%s\" is not a proper mail address\n"), userid);
     563           0 :       err = gpg_error (GPG_ERR_INV_USER_ID);
     564           0 :       goto leave;
     565             :     }
     566           0 :   err = get_key (&key, fingerprint, addrspec);
     567           0 :   if (err)
     568           0 :     goto leave;
     569             : 
     570             :   /* Get the submission address.  */
     571           0 :   if (fake_submission_addr)
     572             :     {
     573           0 :       submission_to = xstrdup (fake_submission_addr);
     574           0 :       err = 0;
     575             :     }
     576             :   else
     577           0 :     err = wkd_get_submission_address (addrspec, &submission_to);
     578           0 :   if (err)
     579           0 :     goto leave;
     580           0 :   log_info ("submitting request to '%s'\n", submission_to);
     581             : 
     582             :   /* Get the policy flags.  */
     583           0 :   if (!fake_submission_addr)
     584             :     {
     585             :       estream_t mbuf;
     586             : 
     587           0 :       err = wkd_get_policy_flags (addrspec, &mbuf);
     588           0 :       if (err)
     589             :         {
     590           0 :           log_error ("error reading policy flags for '%s': %s\n",
     591             :                      submission_to, gpg_strerror (err));
     592           0 :           goto leave;
     593             :       }
     594           0 :       if (mbuf)
     595             :         {
     596           0 :           err = wks_parse_policy (&policy, mbuf, 1);
     597           0 :           es_fclose (mbuf);
     598           0 :           if (err)
     599           0 :             goto leave;
     600             :         }
     601             :     }
     602             : 
     603           0 :   if (policy.auth_submit)
     604           0 :     log_info ("no confirmation required for '%s'\n", addrspec);
     605             : 
     606             :   /* Encrypt the key part.  */
     607           0 :   es_rewind (key);
     608           0 :   err = encrypt_response (&keyenc, key, submission_to, fingerprint);
     609           0 :   if (err)
     610           0 :     goto leave;
     611           0 :   es_fclose (key);
     612           0 :   key = NULL;
     613             : 
     614             : 
     615             :   /* Send the key.  */
     616           0 :   err = mime_maker_new (&mime, NULL);
     617           0 :   if (err)
     618           0 :     goto leave;
     619           0 :   err = mime_maker_add_header (mime, "From", addrspec);
     620           0 :   if (err)
     621           0 :     goto leave;
     622           0 :   err = mime_maker_add_header (mime, "To", submission_to);
     623           0 :   if (err)
     624           0 :     goto leave;
     625           0 :   err = mime_maker_add_header (mime, "Subject", "Key publishing request");
     626           0 :   if (err)
     627           0 :     goto leave;
     628             : 
     629             :   /* Tell server that we support draft version 3.  */
     630           0 :   err = mime_maker_add_header (mime, "Wks-Draft-Version", "3");
     631           0 :   if (err)
     632           0 :     goto leave;
     633             : 
     634           0 :   err = mime_maker_add_header (mime, "Content-Type",
     635             :                                "multipart/encrypted; "
     636             :                                "protocol=\"application/pgp-encrypted\"");
     637           0 :   if (err)
     638           0 :     goto leave;
     639           0 :   err = mime_maker_add_container (mime);
     640           0 :   if (err)
     641           0 :     goto leave;
     642             : 
     643           0 :   err = mime_maker_add_header (mime, "Content-Type",
     644             :                                "application/pgp-encrypted");
     645           0 :   if (err)
     646           0 :     goto leave;
     647           0 :   err = mime_maker_add_body (mime, "Version: 1\n");
     648           0 :   if (err)
     649           0 :     goto leave;
     650           0 :   err = mime_maker_add_header (mime, "Content-Type",
     651             :                                "application/octet-stream");
     652           0 :   if (err)
     653           0 :     goto leave;
     654             : 
     655           0 :   err = mime_maker_add_stream (mime, &keyenc);
     656           0 :   if (err)
     657           0 :     goto leave;
     658             : 
     659           0 :   err = wks_send_mime (mime);
     660             : 
     661             :  leave:
     662           0 :   mime_maker_release (mime);
     663           0 :   xfree (submission_to);
     664           0 :   es_fclose (keyenc);
     665           0 :   es_fclose (key);
     666           0 :   xfree (addrspec);
     667           0 :   return err;
     668             : }
     669             : 
     670             : 
     671             : 
     672             : static void
     673           0 : encrypt_response_status_cb (void *opaque, const char *keyword, char *args)
     674             : {
     675           0 :   gpg_error_t *failure = opaque;
     676             :   char *fields[2];
     677             : 
     678           0 :   if (DBG_CRYPTO)
     679           0 :     log_debug ("gpg status: %s %s\n", keyword, args);
     680             : 
     681           0 :   if (!strcmp (keyword, "FAILURE"))
     682             :     {
     683           0 :       if (split_fields (args, fields, DIM (fields)) >= 2
     684           0 :           && !strcmp (fields[0], "encrypt"))
     685           0 :         *failure = strtoul (fields[1], NULL, 10);
     686             :     }
     687             : 
     688           0 : }
     689             : 
     690             : 
     691             : /* Encrypt the INPUT stream to a new stream which is stored at success
     692             :  * at R_OUTPUT.  Encryption is done for ADDRSPEC and for FINGERPRINT
     693             :  * (so that the sent message may later be inspected by the user).  We
     694             :  * currently retrieve that key from the WKD, DANE, or from "local".
     695             :  * "local" is last to prefer the latest key version but use a local
     696             :  * copy in case we are working offline.  It might be useful for the
     697             :  * server to send the fingerprint of its encryption key - or even the
     698             :  * entire key back.  */
     699             : static gpg_error_t
     700           0 : encrypt_response (estream_t *r_output, estream_t input, const char *addrspec,
     701             :                   const char *fingerprint)
     702             : {
     703             :   gpg_error_t err;
     704             :   ccparray_t ccp;
     705             :   const char **argv;
     706             :   estream_t output;
     707           0 :   gpg_error_t gpg_err = 0;
     708             : 
     709           0 :   *r_output = NULL;
     710             : 
     711           0 :   output = es_fopenmem (0, "w+b");
     712           0 :   if (!output)
     713             :     {
     714           0 :       err = gpg_error_from_syserror ();
     715           0 :       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
     716           0 :       return err;
     717             :     }
     718             : 
     719           0 :   ccparray_init (&ccp, 0);
     720             : 
     721           0 :   ccparray_put (&ccp, "--no-options");
     722           0 :   if (!opt.verbose)
     723           0 :     ccparray_put (&ccp, "--quiet");
     724           0 :   else if (opt.verbose > 1)
     725           0 :     ccparray_put (&ccp, "--verbose");
     726           0 :   ccparray_put (&ccp, "--batch");
     727           0 :   ccparray_put (&ccp, "--status-fd=2");
     728           0 :   ccparray_put (&ccp, "--always-trust");
     729           0 :   ccparray_put (&ccp, "--armor");
     730           0 :   if (fake_submission_addr)
     731           0 :     ccparray_put (&ccp, "--auto-key-locate=clear,local");
     732             :   else
     733           0 :     ccparray_put (&ccp, "--auto-key-locate=clear,wkd,dane,local");
     734           0 :   ccparray_put (&ccp, "--recipient");
     735           0 :   ccparray_put (&ccp, addrspec);
     736           0 :   ccparray_put (&ccp, "--recipient");
     737           0 :   ccparray_put (&ccp, fingerprint);
     738           0 :   ccparray_put (&ccp, "--encrypt");
     739           0 :   ccparray_put (&ccp, "--");
     740             : 
     741           0 :   ccparray_put (&ccp, NULL);
     742           0 :   argv = ccparray_get (&ccp, NULL);
     743           0 :   if (!argv)
     744             :     {
     745           0 :       err = gpg_error_from_syserror ();
     746           0 :       goto leave;
     747             :     }
     748           0 :   err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
     749             :                                 NULL, output,
     750             :                                 encrypt_response_status_cb, &gpg_err);
     751           0 :   if (err)
     752             :     {
     753           0 :       if (gpg_err)
     754           0 :         err = gpg_err;
     755           0 :       log_error ("encryption failed: %s\n", gpg_strerror (err));
     756           0 :       goto leave;
     757             :     }
     758             : 
     759           0 :   es_rewind (output);
     760           0 :   *r_output = output;
     761           0 :   output = NULL;
     762             : 
     763             :  leave:
     764           0 :   es_fclose (output);
     765           0 :   xfree (argv);
     766           0 :   return err;
     767             : }
     768             : 
     769             : 
     770             : static gpg_error_t
     771           0 : send_confirmation_response (const char *sender, const char *address,
     772             :                             const char *nonce, int encrypt,
     773             :                             const char *fingerprint)
     774             : {
     775             :   gpg_error_t err;
     776           0 :   estream_t body = NULL;
     777           0 :   estream_t bodyenc = NULL;
     778           0 :   mime_maker_t mime = NULL;
     779             : 
     780           0 :   body = es_fopenmem (0, "w+b");
     781           0 :   if (!body)
     782             :     {
     783           0 :       err = gpg_error_from_syserror ();
     784           0 :       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
     785           0 :       return err;
     786             :     }
     787             : 
     788             :   /* It is fine to use 8 bit encoding because that is encrypted and
     789             :    * only our client will see it.  */
     790           0 :   if (encrypt)
     791             :     {
     792           0 :       es_fputs ("Content-Type: application/vnd.gnupg.wks\n"
     793             :                 "Content-Transfer-Encoding: 8bit\n"
     794             :                 "\n",
     795             :                 body);
     796             :     }
     797             : 
     798           0 :   es_fprintf (body, ("type: confirmation-response\n"
     799             :                      "sender: %s\n"
     800             :                      "address: %s\n"
     801             :                      "nonce: %s\n"),
     802             :               sender,
     803             :               address,
     804             :               nonce);
     805             : 
     806           0 :   es_rewind (body);
     807           0 :   if (encrypt)
     808             :     {
     809           0 :       err = encrypt_response (&bodyenc, body, sender, fingerprint);
     810           0 :       if (err)
     811           0 :         goto leave;
     812           0 :       es_fclose (body);
     813           0 :       body = NULL;
     814             :     }
     815             : 
     816           0 :   err = mime_maker_new (&mime, NULL);
     817           0 :   if (err)
     818           0 :     goto leave;
     819           0 :   err = mime_maker_add_header (mime, "From", address);
     820           0 :   if (err)
     821           0 :     goto leave;
     822           0 :   err = mime_maker_add_header (mime, "To", sender);
     823           0 :   if (err)
     824           0 :     goto leave;
     825           0 :   err = mime_maker_add_header (mime, "Subject", "Key publication confirmation");
     826           0 :   if (err)
     827           0 :     goto leave;
     828             : 
     829           0 :   if (encrypt)
     830             :     {
     831           0 :       err = mime_maker_add_header (mime, "Content-Type",
     832             :                                    "multipart/encrypted; "
     833             :                                    "protocol=\"application/pgp-encrypted\"");
     834           0 :       if (err)
     835           0 :         goto leave;
     836           0 :       err = mime_maker_add_container (mime);
     837           0 :       if (err)
     838           0 :         goto leave;
     839             : 
     840           0 :       err = mime_maker_add_header (mime, "Content-Type",
     841             :                                    "application/pgp-encrypted");
     842           0 :       if (err)
     843           0 :         goto leave;
     844           0 :       err = mime_maker_add_body (mime, "Version: 1\n");
     845           0 :       if (err)
     846           0 :         goto leave;
     847           0 :       err = mime_maker_add_header (mime, "Content-Type",
     848             :                                    "application/octet-stream");
     849           0 :       if (err)
     850           0 :         goto leave;
     851             : 
     852           0 :       err = mime_maker_add_stream (mime, &bodyenc);
     853           0 :       if (err)
     854           0 :         goto leave;
     855             :     }
     856             :   else
     857             :     {
     858           0 :       err = mime_maker_add_header (mime, "Content-Type",
     859             :                                    "application/vnd.gnupg.wks");
     860           0 :       if (err)
     861           0 :         goto leave;
     862           0 :       err = mime_maker_add_stream (mime, &body);
     863           0 :       if (err)
     864           0 :         goto leave;
     865             :     }
     866             : 
     867           0 :   err = wks_send_mime (mime);
     868             : 
     869             :  leave:
     870           0 :   mime_maker_release (mime);
     871           0 :   es_fclose (bodyenc);
     872           0 :   es_fclose (body);
     873           0 :   return err;
     874             : }
     875             : 
     876             : 
     877             : /* Reply to a confirmation request.  The MSG has already been
     878             :  * decrypted and we only need to send the nonce back.  */
     879             : static gpg_error_t
     880           0 : process_confirmation_request (estream_t msg)
     881             : {
     882             :   gpg_error_t err;
     883             :   nvc_t nvc;
     884             :   nve_t item;
     885             :   const char *value, *sender, *address, *fingerprint, *nonce;
     886             : 
     887           0 :   err = nvc_parse (&nvc, NULL, msg);
     888           0 :   if (err)
     889             :     {
     890           0 :       log_error ("parsing the WKS message failed: %s\n", gpg_strerror (err));
     891           0 :       goto leave;
     892             :     }
     893             : 
     894           0 :   if (DBG_MIME)
     895             :     {
     896           0 :       log_debug ("request follows:\n");
     897           0 :       nvc_write (nvc, log_get_stream ());
     898             :     }
     899             : 
     900             :   /* Check that this is a confirmation request.  */
     901           0 :   if (!((item = nvc_lookup (nvc, "type:")) && (value = nve_value (item))
     902           0 :         && !strcmp (value, "confirmation-request")))
     903             :     {
     904           0 :       if (item && value)
     905           0 :         log_error ("received unexpected wks message '%s'\n", value);
     906             :       else
     907           0 :         log_error ("received invalid wks message: %s\n", "'type' missing");
     908           0 :       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
     909           0 :       goto leave;
     910             :     }
     911             : 
     912             :   /* Get the fingerprint.  */
     913           0 :   if (!((item = nvc_lookup (nvc, "fingerprint:"))
     914           0 :         && (value = nve_value (item))
     915           0 :         && strlen (value) >= 40))
     916             :     {
     917           0 :       log_error ("received invalid wks message: %s\n",
     918             :                  "'fingerprint' missing or invalid");
     919           0 :       err = gpg_error (GPG_ERR_INV_DATA);
     920           0 :       goto leave;
     921             :     }
     922           0 :   fingerprint = value;
     923             : 
     924             :   /* FIXME: Check that the fingerprint matches the key used to decrypt the
     925             :    * message.  */
     926             : 
     927             :   /* Get the address.  */
     928           0 :   if (!((item = nvc_lookup (nvc, "address:")) && (value = nve_value (item))
     929           0 :         && is_valid_mailbox (value)))
     930             :     {
     931           0 :       log_error ("received invalid wks message: %s\n",
     932             :                  "'address' missing or invalid");
     933           0 :       err = gpg_error (GPG_ERR_INV_DATA);
     934           0 :       goto leave;
     935             :     }
     936           0 :   address = value;
     937             :   /* FIXME: Check that the "address" matches the User ID we want to
     938             :    * publish.  Also get the "fingerprint" and compare that to our to
     939             :    * be published key.  Further we should make sure that we actually
     940             :    * decrypted using that fingerprint (which is a bit problematic if
     941             :    * --read is used). */
     942             : 
     943             :   /* Get the sender.  */
     944           0 :   if (!((item = nvc_lookup (nvc, "sender:")) && (value = nve_value (item))
     945           0 :         && is_valid_mailbox (value)))
     946             :     {
     947           0 :       log_error ("received invalid wks message: %s\n",
     948             :                  "'sender' missing or invalid");
     949           0 :       err = gpg_error (GPG_ERR_INV_DATA);
     950           0 :       goto leave;
     951             :     }
     952           0 :   sender = value;
     953             :   /* FIXME: Check that the "sender" matches the From: address.  */
     954             : 
     955             :   /* Get the nonce.  */
     956           0 :   if (!((item = nvc_lookup (nvc, "nonce:")) && (value = nve_value (item))
     957           0 :         && strlen (value) > 16))
     958             :     {
     959           0 :       log_error ("received invalid wks message: %s\n",
     960             :                  "'nonce' missing or too short");
     961           0 :       err = gpg_error (GPG_ERR_INV_DATA);
     962           0 :       goto leave;
     963             :     }
     964           0 :   nonce = value;
     965             : 
     966             :   /* Send the confirmation.  If no key was found, try again without
     967             :    * encryption.  */
     968           0 :   err = send_confirmation_response (sender, address, nonce, 1, fingerprint);
     969           0 :   if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
     970             :     {
     971           0 :       log_info ("no encryption key found - sending response in the clear\n");
     972           0 :       err = send_confirmation_response (sender, address, nonce, 0, NULL);
     973             :     }
     974             : 
     975             :  leave:
     976           0 :   nvc_release (nvc);
     977           0 :   return err;
     978             : }
     979             : 
     980             : 
     981             : /* Read a confirmation request and decrypt it if needed.  This
     982             :  * function may not be used with a mail or MIME message but only with
     983             :  * the actual encrypted or plaintext WKS data.  */
     984             : static gpg_error_t
     985           0 : read_confirmation_request (estream_t msg)
     986             : {
     987             :   gpg_error_t err;
     988             :   int c;
     989           0 :   estream_t plaintext = NULL;
     990             : 
     991             :   /* We take a really simple approach to check whether MSG is
     992             :    * encrypted: We know that an encrypted message is always armored
     993             :    * and thus starts with a few dashes.  It is even sufficient to
     994             :    * check for a single dash, because that can never be a proper first
     995             :    * WKS data octet.  We need to skip leading spaces, though. */
     996           0 :   while ((c = es_fgetc (msg)) == ' ' || c == '\t' || c == '\r' || c == '\n')
     997             :     ;
     998           0 :   if (c == EOF)
     999             :     {
    1000           0 :       log_error ("can't process an empty message\n");
    1001           0 :       return gpg_error (GPG_ERR_INV_DATA);
    1002             :     }
    1003           0 :   if (es_ungetc (c, msg) != c)
    1004             :     {
    1005           0 :       log_error ("error ungetting octet from message\n");
    1006           0 :       return gpg_error (GPG_ERR_INTERNAL);
    1007             :     }
    1008             : 
    1009           0 :   if (c != '-')
    1010           0 :     err = process_confirmation_request (msg);
    1011             :   else
    1012             :     {
    1013           0 :       err = decrypt_stream (&plaintext, msg);
    1014           0 :       if (err)
    1015           0 :         log_error ("decryption failed: %s\n", gpg_strerror (err));
    1016             :       else
    1017           0 :         err = process_confirmation_request (plaintext);
    1018             :     }
    1019             : 
    1020           0 :   es_fclose (plaintext);
    1021           0 :   return err;
    1022             : }
    1023             : 
    1024             : 
    1025             : /* Called from the MIME receiver to process the plain text data in MSG.  */
    1026             : static gpg_error_t
    1027           0 : command_receive_cb (void *opaque, const char *mediatype,
    1028             :                     estream_t msg, unsigned int flags)
    1029             : {
    1030             :   gpg_error_t err;
    1031             : 
    1032             :   (void)opaque;
    1033             :   (void)flags;
    1034             : 
    1035           0 :   if (!strcmp (mediatype, "application/vnd.gnupg.wks"))
    1036           0 :     err = read_confirmation_request (msg);
    1037             :   else
    1038             :     {
    1039           0 :       log_info ("ignoring unexpected message of type '%s'\n", mediatype);
    1040           0 :       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
    1041             :     }
    1042             : 
    1043           0 :   return err;
    1044             : }

Generated by: LCOV version 1.11