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