scd,pcsc: Use HANDLE for context and card.
[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  * Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik
4  *
5  * This file is part of GnuPG.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This file is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, see <https://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27
28 #include "../common/util.h"
29 #include "../common/status.h"
30 #include "../common/i18n.h"
31 #include "../common/sysutils.h"
32 #include "../common/init.h"
33 #include "../common/asshelp.h"
34 #include "../common/userids.h"
35 #include "../common/ccparray.h"
36 #include "../common/exectool.h"
37 #include "../common/mbox-util.h"
38 #include "../common/name-value.h"
39 #include "call-dirmngr.h"
40 #include "mime-maker.h"
41 #include "send-mail.h"
42 #include "gpg-wks.h"
43
44
45 /* Constants to identify the commands and options. */
46 enum cmd_and_opt_values
47   {
48     aNull = 0,
49
50     oQuiet      = 'q',
51     oVerbose    = 'v',
52     oOutput     = 'o',
53     oDirectory  = 'C',
54
55     oDebug      = 500,
56
57     aSupported,
58     aCheck,
59     aCreate,
60     aReceive,
61     aRead,
62     aInstallKey,
63     aRemoveKey,
64     aPrintWKDHash,
65     aPrintWKDURL,
66
67     oGpgProgram,
68     oSend,
69     oFakeSubmissionAddr,
70     oStatusFD,
71     oWithColons,
72
73     oDummy
74   };
75
76
77 /* The list of commands and options. */
78 static ARGPARSE_OPTS opts[] = {
79   ARGPARSE_group (300, ("@Commands:\n ")),
80
81   ARGPARSE_c (aSupported, "supported",
82               ("check whether provider supports WKS")),
83   ARGPARSE_c (aCheck, "check",
84               ("check whether a key is available")),
85   ARGPARSE_c (aCreate,   "create",
86               ("create a publication request")),
87   ARGPARSE_c (aReceive,   "receive",
88               ("receive a MIME confirmation request")),
89   ARGPARSE_c (aRead,      "read",
90               ("receive a plain text confirmation request")),
91   ARGPARSE_c (aInstallKey, "install-key",
92               "install a key into a directory"),
93   ARGPARSE_c (aRemoveKey, "remove-key",
94               "remove a key from a directory"),
95   ARGPARSE_c (aPrintWKDHash, "print-wkd-hash",
96               "Print the WKD identifier for the given user ids"),
97   ARGPARSE_c (aPrintWKDURL, "print-wkd-url",
98               "Print the WKD URL for the given user id"),
99
100   ARGPARSE_group (301, ("@\nOptions:\n ")),
101
102   ARGPARSE_s_n (oVerbose, "verbose", ("verbose")),
103   ARGPARSE_s_n (oQuiet, "quiet",  ("be somewhat more quiet")),
104   ARGPARSE_s_s (oDebug, "debug", "@"),
105   ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
106   ARGPARSE_s_n (oSend, "send", "send the mail using sendmail"),
107   ARGPARSE_s_s (oOutput, "output", "|FILE|write the mail to FILE"),
108   ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")),
109   ARGPARSE_s_n (oWithColons, "with-colons", "@"),
110   ARGPARSE_s_s (oDirectory, "directory", "@"),
111
112   ARGPARSE_s_s (oFakeSubmissionAddr, "fake-submission-addr", "@"),
113
114   ARGPARSE_end ()
115 };
116
117
118 /* The list of supported debug flags.  */
119 static struct debug_flags_s debug_flags [] =
120   {
121     { DBG_MIME_VALUE   , "mime"    },
122     { DBG_PARSER_VALUE , "parser"  },
123     { DBG_CRYPTO_VALUE , "crypto"  },
124     { DBG_MEMORY_VALUE , "memory"  },
125     { DBG_MEMSTAT_VALUE, "memstat" },
126     { DBG_IPC_VALUE    , "ipc"     },
127     { DBG_EXTPROG_VALUE, "extprog" },
128     { 0, NULL }
129   };
130
131
132
133 /* Value of the option --fake-submission-addr.  */
134 const char *fake_submission_addr;
135
136
137 static void wrong_args (const char *text) GPGRT_ATTR_NORETURN;
138 static gpg_error_t proc_userid_from_stdin (gpg_error_t (*func)(const char *),
139                                            const char *text);
140 static gpg_error_t command_supported (char *userid);
141 static gpg_error_t command_check (char *userid);
142 static gpg_error_t command_send (const char *fingerprint, const char *userid);
143 static gpg_error_t encrypt_response (estream_t *r_output, estream_t input,
144                                      const char *addrspec,
145                                      const char *fingerprint);
146 static gpg_error_t read_confirmation_request (estream_t msg);
147 static gpg_error_t command_receive_cb (void *opaque,
148                                        const char *mediatype, estream_t fp,
149                                        unsigned int flags);
150
151
152 \f
153 /* Print usage information and provide strings for help. */
154 static const char *
155 my_strusage( int level )
156 {
157   const char *p;
158
159   switch (level)
160     {
161     case 11: p = "gpg-wks-client"; break;
162     case 12: p = "@GNUPG@"; break;
163     case 13: p = VERSION; break;
164     case 17: p = PRINTABLE_OS_NAME; break;
165     case 19: p = ("Please report bugs to <@EMAIL@>.\n"); break;
166
167     case 1:
168     case 40:
169       p = ("Usage: gpg-wks-client [command] [options] [args] (-h for help)");
170       break;
171     case 41:
172       p = ("Syntax: gpg-wks-client [command] [options] [args]\n"
173            "Client for the Web Key Service\n");
174       break;
175
176     default: p = NULL; break;
177     }
178   return p;
179 }
180
181
182 static void
183 wrong_args (const char *text)
184 {
185   es_fprintf (es_stderr, _("usage: %s [options] %s\n"), strusage (11), text);
186   exit (2);
187 }
188
189
190 \f
191 /* Command line parsing.  */
192 static enum cmd_and_opt_values
193 parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
194 {
195   enum cmd_and_opt_values cmd = 0;
196   int no_more_options = 0;
197
198   while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
199     {
200       switch (pargs->r_opt)
201         {
202         case oQuiet:     opt.quiet = 1; break;
203         case oVerbose:   opt.verbose++; break;
204         case oDebug:
205           if (parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags))
206             {
207               pargs->r_opt = ARGPARSE_INVALID_ARG;
208               pargs->err = ARGPARSE_PRINT_ERROR;
209             }
210           break;
211
212         case oGpgProgram:
213           opt.gpg_program = pargs->r.ret_str;
214           break;
215         case oDirectory:
216           opt.directory = pargs->r.ret_str;
217           break;
218         case oSend:
219           opt.use_sendmail = 1;
220           break;
221         case oOutput:
222           opt.output = pargs->r.ret_str;
223           break;
224         case oFakeSubmissionAddr:
225           fake_submission_addr = pargs->r.ret_str;
226           break;
227         case oStatusFD:
228           wks_set_status_fd (translate_sys2libc_fd_int (pargs->r.ret_int, 1));
229           break;
230         case oWithColons:
231           opt.with_colons = 1;
232           break;
233
234         case aSupported:
235         case aCreate:
236         case aReceive:
237         case aRead:
238         case aCheck:
239         case aInstallKey:
240         case aRemoveKey:
241         case aPrintWKDHash:
242         case aPrintWKDURL:
243           cmd = pargs->r_opt;
244           break;
245
246         default: pargs->err = 2; break;
247         }
248     }
249
250   return cmd;
251 }
252
253
254 \f
255 /* gpg-wks-client main. */
256 int
257 main (int argc, char **argv)
258 {
259   gpg_error_t err, delayed_err;
260   ARGPARSE_ARGS pargs;
261   enum cmd_and_opt_values cmd;
262
263   gnupg_reopen_std ("gpg-wks-client");
264   set_strusage (my_strusage);
265   log_set_prefix ("gpg-wks-client", GPGRT_LOG_WITH_PREFIX);
266
267   /* Make sure that our subsystems are ready.  */
268   i18n_init();
269   init_common_subsystems (&argc, &argv);
270
271   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
272   setup_libassuan_logging (&opt.debug, NULL);
273
274   /* Parse the command line. */
275   pargs.argc  = &argc;
276   pargs.argv  = &argv;
277   pargs.flags = ARGPARSE_FLAG_KEEP;
278   cmd = parse_arguments (&pargs, opts);
279
280   if (log_get_errorcount (0))
281     exit (2);
282
283   /* Print a warning if an argument looks like an option.  */
284   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
285     {
286       int i;
287
288       for (i=0; i < argc; i++)
289         if (argv[i][0] == '-' && argv[i][1] == '-')
290           log_info (("NOTE: '%s' is not considered an option\n"), argv[i]);
291     }
292
293   /* Set defaults for non given options.  */
294   if (!opt.gpg_program)
295     opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
296
297   if (!opt.directory)
298     opt.directory = "openpgpkey";
299
300   /* Tell call-dirmngr what options we want.  */
301   set_dirmngr_options (opt.verbose, (opt.debug & DBG_IPC_VALUE), 1);
302
303
304   /* Check that the top directory exists.  */
305   if (cmd == aInstallKey || cmd == aRemoveKey)
306     {
307       struct stat sb;
308
309       if (stat (opt.directory, &sb))
310         {
311           err = gpg_error_from_syserror ();
312           log_error ("error accessing directory '%s': %s\n",
313                      opt.directory, gpg_strerror (err));
314           goto leave;
315         }
316       if (!S_ISDIR(sb.st_mode))
317         {
318           log_error ("error accessing directory '%s': %s\n",
319                      opt.directory, "not a directory");
320           err = gpg_error (GPG_ERR_ENOENT);
321           goto leave;
322         }
323     }
324
325   /* Run the selected command.  */
326   switch (cmd)
327     {
328     case aSupported:
329       if (opt.with_colons)
330         {
331           for (; argc; argc--, argv++)
332             command_supported (*argv);
333           err = 0;
334         }
335       else
336         {
337           if (argc != 1)
338             wrong_args ("--supported DOMAIN");
339           err = command_supported (argv[0]);
340           if (err && gpg_err_code (err) != GPG_ERR_FALSE)
341             log_error ("checking support failed: %s\n", gpg_strerror (err));
342         }
343       break;
344
345     case aCreate:
346       if (argc != 2)
347         wrong_args ("--create FINGERPRINT USER-ID");
348       err = command_send (argv[0], argv[1]);
349       if (err)
350         log_error ("creating request failed: %s\n", gpg_strerror (err));
351       break;
352
353     case aReceive:
354       if (argc)
355         wrong_args ("--receive < MIME-DATA");
356       err = wks_receive (es_stdin, command_receive_cb, NULL);
357       if (err)
358         log_error ("processing mail failed: %s\n", gpg_strerror (err));
359       break;
360
361     case aRead:
362       if (argc)
363         wrong_args ("--read < WKS-DATA");
364       err = read_confirmation_request (es_stdin);
365       if (err)
366         log_error ("processing mail failed: %s\n", gpg_strerror (err));
367       break;
368
369     case aCheck:
370       if (argc != 1)
371         wrong_args ("--check USER-ID");
372       err = command_check (argv[0]);
373       break;
374
375     case aInstallKey:
376       if (!argc)
377         err = wks_cmd_install_key (NULL, NULL);
378       else if (argc == 2)
379         err = wks_cmd_install_key (*argv, argv[1]);
380       else
381         wrong_args ("--install-key [FILE|FINGERPRINT USER-ID]");
382       break;
383
384     case aRemoveKey:
385       if (argc != 1)
386         wrong_args ("--remove-key USER-ID");
387       err = wks_cmd_remove_key (*argv);
388       break;
389
390     case aPrintWKDHash:
391     case aPrintWKDURL:
392       if (!argc)
393         {
394           if (cmd == aPrintWKDHash)
395             err = proc_userid_from_stdin (wks_cmd_print_wkd_hash,
396                                           "printing WKD hash");
397           else
398             err = proc_userid_from_stdin (wks_cmd_print_wkd_url,
399                                           "printing WKD URL");
400         }
401       else
402         {
403           for (err = delayed_err = 0; !err && argc; argc--, argv++)
404             {
405               if (cmd == aPrintWKDHash)
406                 err = wks_cmd_print_wkd_hash (*argv);
407               else
408                 err = wks_cmd_print_wkd_url (*argv);
409               if (gpg_err_code (err) == GPG_ERR_INV_USER_ID)
410                 {
411                   /* Diagnostic already printed.  */
412                   delayed_err = err;
413                   err = 0;
414                 }
415               else if (err)
416                 log_error ("printing hash failed: %s\n", gpg_strerror (err));
417             }
418           if (!err)
419             err = delayed_err;
420         }
421       break;
422
423     default:
424       usage (1);
425       err = 0;
426       break;
427     }
428
429  leave:
430   if (err)
431     wks_write_status (STATUS_FAILURE, "- %u", err);
432   else if (log_get_errorcount (0))
433     wks_write_status (STATUS_FAILURE, "- %u", GPG_ERR_GENERAL);
434   else
435     wks_write_status (STATUS_SUCCESS, NULL);
436   return (err || log_get_errorcount (0))? 1:0;
437 }
438
439
440 /* Read user ids from stdin and call FUNC for each user id.  TEXT is
441  * used for error messages.  */
442 static gpg_error_t
443 proc_userid_from_stdin (gpg_error_t (*func)(const char *), const char *text)
444 {
445   gpg_error_t err = 0;
446   gpg_error_t delayed_err = 0;
447   char line[2048];
448   size_t n = 0;
449
450   /* If we are on a terminal disable buffering to get direct response.  */
451   if (gnupg_isatty (es_fileno (es_stdin))
452       && gnupg_isatty (es_fileno (es_stdout)))
453     {
454       es_setvbuf (es_stdin, NULL, _IONBF, 0);
455       es_setvbuf (es_stdout, NULL, _IOLBF, 0);
456     }
457
458   while (es_fgets (line, sizeof line - 1, es_stdin))
459     {
460       n = strlen (line);
461       if (!n || line[n-1] != '\n')
462         {
463           err = gpg_error (*line? GPG_ERR_LINE_TOO_LONG
464                            : GPG_ERR_INCOMPLETE_LINE);
465           log_error ("error reading stdin: %s\n", gpg_strerror (err));
466           break;
467         }
468       trim_spaces (line);
469       err = func (line);
470       if (gpg_err_code (err) == GPG_ERR_INV_USER_ID)
471         {
472           delayed_err = err;
473           err = 0;
474         }
475       else if (err)
476         log_error ("%s failed: %s\n", text, gpg_strerror (err));
477     }
478   if (es_ferror (es_stdin))
479     {
480       err = gpg_error_from_syserror ();
481       log_error ("error reading stdin: %s\n", gpg_strerror (err));
482       goto leave;
483     }
484
485  leave:
486   if (!err)
487     err = delayed_err;
488   return err;
489 }
490
491
492
493 \f
494 /* Add the user id UID to the key identified by FINGERPRINT.  */
495 static gpg_error_t
496 add_user_id (const char *fingerprint, const char *uid)
497 {
498   gpg_error_t err;
499   ccparray_t ccp;
500   const char **argv = NULL;
501
502   ccparray_init (&ccp, 0);
503
504   ccparray_put (&ccp, "--no-options");
505   if (!opt.verbose)
506     ccparray_put (&ccp, "--quiet");
507   else if (opt.verbose > 1)
508     ccparray_put (&ccp, "--verbose");
509   ccparray_put (&ccp, "--batch");
510   ccparray_put (&ccp, "--always-trust");
511   ccparray_put (&ccp, "--quick-add-uid");
512   ccparray_put (&ccp, fingerprint);
513   ccparray_put (&ccp, uid);
514
515   ccparray_put (&ccp, NULL);
516   argv = ccparray_get (&ccp, NULL);
517   if (!argv)
518     {
519       err = gpg_error_from_syserror ();
520       goto leave;
521     }
522   err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
523                                 NULL, NULL,
524                                 NULL, NULL);
525   if (err)
526     {
527       log_error ("adding user id failed: %s\n", gpg_strerror (err));
528       goto leave;
529     }
530
531  leave:
532   xfree (argv);
533   return err;
534 }
535
536
537 \f
538 struct decrypt_stream_parm_s
539 {
540   char *fpr;
541   char *mainfpr;
542   int  otrust;
543 };
544
545 static void
546 decrypt_stream_status_cb (void *opaque, const char *keyword, char *args)
547 {
548   struct decrypt_stream_parm_s *decinfo = opaque;
549
550   if (DBG_CRYPTO)
551     log_debug ("gpg status: %s %s\n", keyword, args);
552   if (!strcmp (keyword, "DECRYPTION_KEY") && !decinfo->fpr)
553     {
554       char *fields[3];
555
556       if (split_fields (args, fields, DIM (fields)) >= 3)
557         {
558           decinfo->fpr = xstrdup (fields[0]);
559           decinfo->mainfpr = xstrdup (fields[1]);
560           decinfo->otrust = *fields[2];
561         }
562     }
563 }
564
565 /* Decrypt the INPUT stream to a new stream which is stored at success
566  * at R_OUTPUT.  */
567 static gpg_error_t
568 decrypt_stream (estream_t *r_output, struct decrypt_stream_parm_s *decinfo,
569                 estream_t input)
570 {
571   gpg_error_t err;
572   ccparray_t ccp;
573   const char **argv;
574   estream_t output;
575
576   *r_output = NULL;
577   memset (decinfo, 0, sizeof *decinfo);
578
579   output = es_fopenmem (0, "w+b");
580   if (!output)
581     {
582       err = gpg_error_from_syserror ();
583       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
584       return err;
585     }
586
587   ccparray_init (&ccp, 0);
588
589   ccparray_put (&ccp, "--no-options");
590   /* We limit the output to 64 KiB to avoid DoS using compression
591    * tricks.  A regular client will anyway only send a minimal key;
592    * that is one w/o key signatures and attribute packets.  */
593   ccparray_put (&ccp, "--max-output=0x10000");
594   if (!opt.verbose)
595     ccparray_put (&ccp, "--quiet");
596   else if (opt.verbose > 1)
597     ccparray_put (&ccp, "--verbose");
598   ccparray_put (&ccp, "--batch");
599   ccparray_put (&ccp, "--status-fd=2");
600   ccparray_put (&ccp, "--decrypt");
601   ccparray_put (&ccp, "--");
602
603   ccparray_put (&ccp, NULL);
604   argv = ccparray_get (&ccp, NULL);
605   if (!argv)
606     {
607       err = gpg_error_from_syserror ();
608       goto leave;
609     }
610   err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
611                                 NULL, output,
612                                 decrypt_stream_status_cb, decinfo);
613   if (!err && (!decinfo->fpr || !decinfo->mainfpr || !decinfo->otrust))
614     err = gpg_error (GPG_ERR_INV_ENGINE);
615   if (err)
616     {
617       log_error ("decryption failed: %s\n", gpg_strerror (err));
618       goto leave;
619     }
620   else if (opt.verbose)
621     log_info ("decryption succeeded\n");
622
623   es_rewind (output);
624   *r_output = output;
625   output = NULL;
626
627  leave:
628   if (err)
629     {
630       xfree (decinfo->fpr);
631       xfree (decinfo->mainfpr);
632       memset (decinfo, 0, sizeof *decinfo);
633     }
634   es_fclose (output);
635   xfree (argv);
636   return err;
637 }
638
639
640 /* Return the submission address for the address or just the domain in
641  * ADDRSPEC.  The submission address is stored as a malloced string at
642  * R_SUBMISSION_ADDRESS.  At R_POLICY the policy flags of the domain
643  * are stored.  The caller needs to free them with wks_free_policy.
644  * The function returns an error code on failure to find a submission
645  * address or policy file.  Note: The function may store NULL at
646  * R_SUBMISSION_ADDRESS but return success to indicate that the web
647  * key directory is supported but not the web key service.  As per WKD
648  * specs a policy file is always required and will thus be return on
649  * success.  */
650 static gpg_error_t
651 get_policy_and_sa (const char *addrspec, int silent,
652                    policy_flags_t *r_policy, char **r_submission_address)
653 {
654   gpg_error_t err;
655   estream_t mbuf = NULL;
656   const char *domain;
657   const char *s;
658   policy_flags_t policy = NULL;
659   char *submission_to = NULL;
660
661   *r_submission_address = NULL;
662   *r_policy = NULL;
663
664   domain = strchr (addrspec, '@');
665   if (domain)
666     domain++;
667
668   if (opt.with_colons)
669     {
670       s = domain? domain : addrspec;
671       es_write_sanitized (es_stdout, s, strlen (s), ":", NULL);
672       es_putc (':', es_stdout);
673     }
674
675   /* We first try to get the submission address from the policy file
676    * (this is the new method).  If both are available we check that
677    * they match and print a warning if not.  In the latter case we
678    * keep on using the one from the submission-address file.    */
679   err = wkd_get_policy_flags (addrspec, &mbuf);
680   if (err && gpg_err_code (err) != GPG_ERR_NO_DATA
681       && gpg_err_code (err) != GPG_ERR_NO_NAME)
682     {
683       if (!opt.with_colons)
684         log_error ("error reading policy flags for '%s': %s\n",
685                    domain, gpg_strerror (err));
686       goto leave;
687     }
688   if (!mbuf)
689     {
690       if (!opt.with_colons)
691         log_error ("provider for '%s' does NOT support the Web Key Directory\n",
692                    addrspec);
693       err = gpg_error (GPG_ERR_FALSE);
694       goto leave;
695     }
696
697   policy = xtrycalloc (1, sizeof *policy);
698   if (!policy)
699     err = gpg_error_from_syserror ();
700   else
701     err = wks_parse_policy (policy, mbuf, 1);
702   es_fclose (mbuf);
703   mbuf = NULL;
704   if (err)
705     goto leave;
706
707   err = wkd_get_submission_address (addrspec, &submission_to);
708   if (err && !policy->submission_address)
709     {
710       if (!silent && !opt.with_colons)
711         log_error (_("error looking up submission address for domain '%s'"
712                      ": %s\n"), domain, gpg_strerror (err));
713       if (!silent && gpg_err_code (err) == GPG_ERR_NO_DATA && !opt.with_colons)
714         log_error (_("this domain probably doesn't support WKS.\n"));
715       goto leave;
716     }
717
718   if (submission_to && policy->submission_address
719       && ascii_strcasecmp (submission_to, policy->submission_address))
720     log_info ("Warning: different submission addresses (sa=%s, po=%s)\n",
721               submission_to, policy->submission_address);
722
723   if (!submission_to && policy->submission_address)
724     {
725       submission_to = xtrystrdup (policy->submission_address);
726       if (!submission_to)
727         {
728           err = gpg_error_from_syserror ();
729           goto leave;
730         }
731     }
732
733  leave:
734   *r_submission_address = submission_to;
735   submission_to = NULL;
736   *r_policy = policy;
737   policy = NULL;
738
739   if (opt.with_colons)
740     {
741       if (*r_policy && !*r_submission_address)
742         es_fprintf (es_stdout, "1:0::");
743       else if (*r_policy && *r_submission_address)
744         es_fprintf (es_stdout, "1:1::");
745       else if (err && !(gpg_err_code (err) == GPG_ERR_FALSE
746                         || gpg_err_code (err) == GPG_ERR_NO_DATA
747                         || gpg_err_code (err) == GPG_ERR_UNKNOWN_HOST))
748         es_fprintf (es_stdout, "0:0:%d:", err);
749       else
750         es_fprintf (es_stdout, "0:0::");
751       if (*r_policy)
752         {
753           es_fprintf (es_stdout, "%u:%u:%u:",
754                       (*r_policy)->protocol_version,
755                       (*r_policy)->auth_submit,
756                       (*r_policy)->mailbox_only);
757         }
758       es_putc ('\n', es_stdout);
759     }
760
761   xfree (submission_to);
762   wks_free_policy (policy);
763   xfree (policy);
764   es_fclose (mbuf);
765   return err;
766 }
767
768
769 \f
770 /* Check whether the  provider supports the WKS protocol.  */
771 static gpg_error_t
772 command_supported (char *userid)
773 {
774   gpg_error_t err;
775   char *addrspec = NULL;
776   char *submission_to = NULL;
777   policy_flags_t policy = NULL;
778
779   if (!strchr (userid, '@'))
780     {
781       char *tmp = xstrconcat ("foo@", userid, NULL);
782       addrspec = mailbox_from_userid (tmp, 0);
783       xfree (tmp);
784     }
785   else
786     addrspec = mailbox_from_userid (userid, 0);
787   if (!addrspec)
788     {
789       log_error (_("\"%s\" is not a proper mail address\n"), userid);
790       err = gpg_error (GPG_ERR_INV_USER_ID);
791       goto leave;
792     }
793
794   /* Get the submission address.  */
795   err = get_policy_and_sa (addrspec, 1, &policy, &submission_to);
796   if (err || !submission_to)
797     {
798       if (!submission_to
799           || gpg_err_code (err) == GPG_ERR_FALSE
800           || gpg_err_code (err) == GPG_ERR_NO_DATA
801           || gpg_err_code (err) == GPG_ERR_UNKNOWN_HOST
802           )
803         {
804           /* FALSE is returned if we already figured out that even the
805            * Web Key Directory is not supported and thus printed an
806            * error message.  */
807           if (opt.verbose && gpg_err_code (err) != GPG_ERR_FALSE
808               && !opt.with_colons)
809             {
810               if (gpg_err_code (err) == GPG_ERR_NO_DATA)
811                 log_info ("provider for '%s' does NOT support WKS\n",
812                           addrspec);
813               else
814                 log_info ("provider for '%s' does NOT support WKS (%s)\n",
815                           addrspec, gpg_strerror (err));
816             }
817           err = gpg_error (GPG_ERR_FALSE);
818           if (!opt.with_colons)
819             log_inc_errorcount ();
820         }
821       goto leave;
822     }
823
824   if (opt.verbose && !opt.with_colons)
825     log_info ("provider for '%s' supports WKS\n", addrspec);
826
827  leave:
828   wks_free_policy (policy);
829   xfree (policy);
830   xfree (submission_to);
831   xfree (addrspec);
832   return err;
833 }
834
835
836 \f
837 /* Check whether the key for USERID is available in the WKD.  */
838 static gpg_error_t
839 command_check (char *userid)
840 {
841   gpg_error_t err;
842   char *addrspec = NULL;
843   estream_t key = NULL;
844   char *fpr = NULL;
845   uidinfo_list_t mboxes = NULL;
846   uidinfo_list_t sl;
847   int found = 0;
848
849   addrspec = mailbox_from_userid (userid, 0);
850   if (!addrspec)
851     {
852       log_error (_("\"%s\" is not a proper mail address\n"), userid);
853       err = gpg_error (GPG_ERR_INV_USER_ID);
854       goto leave;
855     }
856
857   /* Get the submission address.  */
858   err = wkd_get_key (addrspec, &key);
859   switch (gpg_err_code (err))
860     {
861     case 0:
862       if (opt.verbose)
863         log_info ("public key for '%s' found via WKD\n", addrspec);
864       /* Fixme: Check that the key contains the user id.  */
865       break;
866
867     case GPG_ERR_NO_DATA: /* No such key.  */
868       if (opt.verbose)
869         log_info ("public key for '%s' NOT found via WKD\n", addrspec);
870       err = gpg_error (GPG_ERR_NO_PUBKEY);
871       log_inc_errorcount ();
872       break;
873
874     case GPG_ERR_UNKNOWN_HOST:
875       if (opt.verbose)
876         log_info ("error looking up '%s' via WKD: %s\n",
877                   addrspec, gpg_strerror (err));
878       err = gpg_error (GPG_ERR_NOT_SUPPORTED);
879       break;
880
881     default:
882       log_error ("error looking up '%s' via WKD: %s\n",
883                  addrspec, gpg_strerror (err));
884       break;
885     }
886
887   if (err)
888     goto leave;
889
890   /* Look closer at the key.  */
891   err = wks_list_key (key, &fpr, &mboxes);
892   if (err)
893     {
894       log_error ("error parsing key: %s\n", gpg_strerror (err));
895       err = gpg_error (GPG_ERR_NO_PUBKEY);
896       goto leave;
897     }
898
899   if (opt.verbose)
900     log_info ("fingerprint: %s\n", fpr);
901
902   for (sl = mboxes; sl; sl = sl->next)
903     {
904       if (sl->mbox && !strcmp (sl->mbox, addrspec))
905         found = 1;
906       if (opt.verbose)
907         {
908           log_info ("    user-id: %s\n", sl->uid);
909           log_info ("    created: %s\n", asctimestamp (sl->created));
910           if (sl->mbox)
911             log_info ("  addr-spec: %s\n", sl->mbox);
912         }
913     }
914   if (!found)
915     {
916       log_error ("public key for '%s' has no user id with the mail address\n",
917                  addrspec);
918       err = gpg_error (GPG_ERR_CERT_REVOKED);
919     }
920
921  leave:
922   xfree (fpr);
923   free_uidinfo_list (mboxes);
924   es_fclose (key);
925   xfree (addrspec);
926   return err;
927 }
928
929
930 \f
931 /* Locate the key by fingerprint and userid and send a publication
932  * request.  */
933 static gpg_error_t
934 command_send (const char *fingerprint, const char *userid)
935 {
936   gpg_error_t err;
937   KEYDB_SEARCH_DESC desc;
938   char *addrspec = NULL;
939   estream_t key = NULL;
940   estream_t keyenc = NULL;
941   char *submission_to = NULL;
942   mime_maker_t mime = NULL;
943   policy_flags_t policy = NULL;
944   int no_encrypt = 0;
945   int posteo_hack = 0;
946   const char *domain;
947   uidinfo_list_t uidlist = NULL;
948   uidinfo_list_t uid, thisuid;
949   time_t thistime;
950
951   if (classify_user_id (fingerprint, &desc, 1)
952       || desc.mode != KEYDB_SEARCH_MODE_FPR)
953     {
954       log_error (_("\"%s\" is not a fingerprint\n"), fingerprint);
955       err = gpg_error (GPG_ERR_INV_NAME);
956       goto leave;
957     }
958
959   addrspec = mailbox_from_userid (userid, 0);
960   if (!addrspec)
961     {
962       log_error (_("\"%s\" is not a proper mail address\n"), userid);
963       err = gpg_error (GPG_ERR_INV_USER_ID);
964       goto leave;
965     }
966   err = wks_get_key (&key, fingerprint, addrspec, 0);
967   if (err)
968     goto leave;
969
970   domain = strchr (addrspec, '@');
971   log_assert (domain);
972   domain++;
973
974   /* Get the submission address.  */
975   if (fake_submission_addr)
976     {
977       policy = xcalloc (1, sizeof *policy);
978       submission_to = xstrdup (fake_submission_addr);
979       err = 0;
980     }
981   else
982     {
983       err = get_policy_and_sa (addrspec, 0, &policy, &submission_to);
984       if (err)
985         goto leave;
986       if (!submission_to)
987         {
988           log_error (_("this domain probably doesn't support WKS.\n"));
989           err = gpg_error (GPG_ERR_NO_DATA);
990           goto leave;
991         }
992     }
993
994   log_info ("submitting request to '%s'\n", submission_to);
995
996   if (policy->auth_submit)
997     log_info ("no confirmation required for '%s'\n", addrspec);
998
999   /* In case the key has several uids with the same addr-spec we will
1000    * use the newest one.  */
1001   err = wks_list_key (key, NULL, &uidlist);
1002   if (err)
1003     {
1004       log_error ("error parsing key: %s\n",gpg_strerror (err));
1005       err = gpg_error (GPG_ERR_NO_PUBKEY);
1006       goto leave;
1007     }
1008   thistime = 0;
1009   thisuid = NULL;
1010   for (uid = uidlist; uid; uid = uid->next)
1011     {
1012       if (!uid->mbox)
1013         continue; /* Should not happen anyway.  */
1014       if (policy->mailbox_only && ascii_strcasecmp (uid->uid, uid->mbox))
1015         continue; /* UID has more than just the mailbox.  */
1016       if (uid->created > thistime)
1017         {
1018           thistime = uid->created;
1019           thisuid = uid;
1020         }
1021     }
1022   if (!thisuid)
1023     thisuid = uidlist;  /* This is the case for a missing timestamp.  */
1024   if (opt.verbose)
1025     log_info ("submitting key with user id '%s'\n", thisuid->uid);
1026
1027   /* If we have more than one user id we need to filter the key to
1028    * include only THISUID.  */
1029   if (uidlist->next)
1030     {
1031       estream_t newkey;
1032
1033       es_rewind (key);
1034       err = wks_filter_uid (&newkey, key, thisuid->uid, 0);
1035       if (err)
1036         {
1037           log_error ("error filtering key: %s\n", gpg_strerror (err));
1038           err = gpg_error (GPG_ERR_NO_PUBKEY);
1039           goto leave;
1040         }
1041       es_fclose (key);
1042       key = newkey;
1043     }
1044
1045   if (policy->mailbox_only
1046       && (!thisuid->mbox || ascii_strcasecmp (thisuid->uid, thisuid->mbox)))
1047     {
1048       log_info ("Warning: policy requires 'mailbox-only'"
1049                 " - adding user id '%s'\n", addrspec);
1050       err = add_user_id (fingerprint, addrspec);
1051       if (err)
1052         goto leave;
1053
1054       /* Need to get the key again.  This time we request filtering
1055        * for the full user id, so that we do not need check and filter
1056        * the key again.  */
1057       es_fclose (key);
1058       key = NULL;
1059       err = wks_get_key (&key, fingerprint, addrspec, 1);
1060       if (err)
1061         goto leave;
1062     }
1063
1064   /* Hack to support posteo but let them disable this by setting the
1065    * new policy-version flag.  */
1066   if (policy->protocol_version < 3
1067       && !ascii_strcasecmp (domain, "posteo.de"))
1068     {
1069       log_info ("Warning: Using draft-1 method for domain '%s'\n", domain);
1070       no_encrypt = 1;
1071       posteo_hack = 1;
1072     }
1073
1074   /* Encrypt the key part.  */
1075   if (!no_encrypt)
1076     {
1077       es_rewind (key);
1078       err = encrypt_response (&keyenc, key, submission_to, fingerprint);
1079       if (err)
1080         goto leave;
1081       es_fclose (key);
1082       key = NULL;
1083     }
1084
1085   /* Send the key.  */
1086   err = mime_maker_new (&mime, NULL);
1087   if (err)
1088     goto leave;
1089   err = mime_maker_add_header (mime, "From", addrspec);
1090   if (err)
1091     goto leave;
1092   err = mime_maker_add_header (mime, "To", submission_to);
1093   if (err)
1094     goto leave;
1095   err = mime_maker_add_header (mime, "Subject", "Key publishing request");
1096   if (err)
1097     goto leave;
1098
1099   /* Tell server which draft we support.  */
1100   err = mime_maker_add_header (mime, "Wks-Draft-Version",
1101                                  STR2(WKS_DRAFT_VERSION));
1102   if (err)
1103     goto leave;
1104
1105   if (no_encrypt)
1106     {
1107       void *data;
1108       size_t datalen, n;
1109
1110       if (posteo_hack)
1111         {
1112           /* Needs a multipart/mixed with one(!) attachment.  It does
1113            * not grok a non-multipart mail.  */
1114           err = mime_maker_add_header (mime, "Content-Type", "multipart/mixed");
1115           if (err)
1116             goto leave;
1117           err = mime_maker_add_container (mime);
1118           if (err)
1119             goto leave;
1120         }
1121
1122       err = mime_maker_add_header (mime, "Content-type",
1123                                    "application/pgp-keys");
1124       if (err)
1125         goto leave;
1126
1127       if (es_fclose_snatch (key, &data, &datalen))
1128         {
1129           err = gpg_error_from_syserror ();
1130           goto leave;
1131         }
1132       key = NULL;
1133       /* We need to skip over the first line which has a content-type
1134        * header not needed here.  */
1135       for (n=0; n < datalen ; n++)
1136         if (((const char *)data)[n] == '\n')
1137           {
1138             n++;
1139             break;
1140           }
1141
1142       err = mime_maker_add_body_data (mime, (char*)data + n, datalen - n);
1143       xfree (data);
1144       if (err)
1145         goto leave;
1146     }
1147   else
1148     {
1149       err = mime_maker_add_header (mime, "Content-Type",
1150                                    "multipart/encrypted; "
1151                                    "protocol=\"application/pgp-encrypted\"");
1152       if (err)
1153         goto leave;
1154       err = mime_maker_add_container (mime);
1155       if (err)
1156         goto leave;
1157
1158       err = mime_maker_add_header (mime, "Content-Type",
1159                                    "application/pgp-encrypted");
1160       if (err)
1161         goto leave;
1162       err = mime_maker_add_body (mime, "Version: 1\n");
1163       if (err)
1164         goto leave;
1165       err = mime_maker_add_header (mime, "Content-Type",
1166                                    "application/octet-stream");
1167       if (err)
1168         goto leave;
1169
1170       err = mime_maker_add_stream (mime, &keyenc);
1171       if (err)
1172         goto leave;
1173     }
1174
1175   err = wks_send_mime (mime);
1176
1177  leave:
1178   mime_maker_release (mime);
1179   xfree (submission_to);
1180   free_uidinfo_list (uidlist);
1181   es_fclose (keyenc);
1182   es_fclose (key);
1183   wks_free_policy (policy);
1184   xfree (policy);
1185   xfree (addrspec);
1186   return err;
1187 }
1188
1189
1190 \f
1191 static void
1192 encrypt_response_status_cb (void *opaque, const char *keyword, char *args)
1193 {
1194   gpg_error_t *failure = opaque;
1195   char *fields[2];
1196
1197   if (DBG_CRYPTO)
1198     log_debug ("gpg status: %s %s\n", keyword, args);
1199
1200   if (!strcmp (keyword, "FAILURE"))
1201     {
1202       if (split_fields (args, fields, DIM (fields)) >= 2
1203           && !strcmp (fields[0], "encrypt"))
1204         *failure = strtoul (fields[1], NULL, 10);
1205     }
1206
1207 }
1208
1209
1210 /* Encrypt the INPUT stream to a new stream which is stored at success
1211  * at R_OUTPUT.  Encryption is done for ADDRSPEC and for FINGERPRINT
1212  * (so that the sent message may later be inspected by the user).  We
1213  * currently retrieve that key from the WKD, DANE, or from "local".
1214  * "local" is last to prefer the latest key version but use a local
1215  * copy in case we are working offline.  It might be useful for the
1216  * server to send the fingerprint of its encryption key - or even the
1217  * entire key back.  */
1218 static gpg_error_t
1219 encrypt_response (estream_t *r_output, estream_t input, const char *addrspec,
1220                   const char *fingerprint)
1221 {
1222   gpg_error_t err;
1223   ccparray_t ccp;
1224   const char **argv;
1225   estream_t output;
1226   gpg_error_t gpg_err = 0;
1227
1228   *r_output = NULL;
1229
1230   output = es_fopenmem (0, "w+b");
1231   if (!output)
1232     {
1233       err = gpg_error_from_syserror ();
1234       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
1235       return err;
1236     }
1237
1238   ccparray_init (&ccp, 0);
1239
1240   ccparray_put (&ccp, "--no-options");
1241   if (!opt.verbose)
1242     ccparray_put (&ccp, "--quiet");
1243   else if (opt.verbose > 1)
1244     ccparray_put (&ccp, "--verbose");
1245   ccparray_put (&ccp, "--batch");
1246   ccparray_put (&ccp, "--status-fd=2");
1247   ccparray_put (&ccp, "--always-trust");
1248   ccparray_put (&ccp, "--armor");
1249   ccparray_put (&ccp, "-z0");  /* No compression for improved robustness.  */
1250   if (fake_submission_addr)
1251     ccparray_put (&ccp, "--auto-key-locate=clear,local");
1252   else
1253     ccparray_put (&ccp, "--auto-key-locate=clear,wkd,dane,local");
1254   ccparray_put (&ccp, "--recipient");
1255   ccparray_put (&ccp, addrspec);
1256   ccparray_put (&ccp, "--recipient");
1257   ccparray_put (&ccp, fingerprint);
1258   ccparray_put (&ccp, "--encrypt");
1259   ccparray_put (&ccp, "--");
1260
1261   ccparray_put (&ccp, NULL);
1262   argv = ccparray_get (&ccp, NULL);
1263   if (!argv)
1264     {
1265       err = gpg_error_from_syserror ();
1266       goto leave;
1267     }
1268   err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
1269                                 NULL, output,
1270                                 encrypt_response_status_cb, &gpg_err);
1271   if (err)
1272     {
1273       if (gpg_err)
1274         err = gpg_err;
1275       log_error ("encryption failed: %s\n", gpg_strerror (err));
1276       goto leave;
1277     }
1278
1279   es_rewind (output);
1280   *r_output = output;
1281   output = NULL;
1282
1283  leave:
1284   es_fclose (output);
1285   xfree (argv);
1286   return err;
1287 }
1288
1289
1290 static gpg_error_t
1291 send_confirmation_response (const char *sender, const char *address,
1292                             const char *nonce, int encrypt,
1293                             const char *fingerprint)
1294 {
1295   gpg_error_t err;
1296   estream_t body = NULL;
1297   estream_t bodyenc = NULL;
1298   mime_maker_t mime = NULL;
1299
1300   body = es_fopenmem (0, "w+b");
1301   if (!body)
1302     {
1303       err = gpg_error_from_syserror ();
1304       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
1305       return err;
1306     }
1307
1308   /* It is fine to use 8 bit encoding because that is encrypted and
1309    * only our client will see it.  */
1310   if (encrypt)
1311     {
1312       es_fputs ("Content-Type: application/vnd.gnupg.wks\n"
1313                 "Content-Transfer-Encoding: 8bit\n"
1314                 "\n",
1315                 body);
1316     }
1317
1318   es_fprintf (body, ("type: confirmation-response\n"
1319                      "sender: %s\n"
1320                      "address: %s\n"
1321                      "nonce: %s\n"),
1322               sender,
1323               address,
1324               nonce);
1325
1326   es_rewind (body);
1327   if (encrypt)
1328     {
1329       err = encrypt_response (&bodyenc, body, sender, fingerprint);
1330       if (err)
1331         goto leave;
1332       es_fclose (body);
1333       body = NULL;
1334     }
1335
1336   err = mime_maker_new (&mime, NULL);
1337   if (err)
1338     goto leave;
1339   err = mime_maker_add_header (mime, "From", address);
1340   if (err)
1341     goto leave;
1342   err = mime_maker_add_header (mime, "To", sender);
1343   if (err)
1344     goto leave;
1345   err = mime_maker_add_header (mime, "Subject", "Key publication confirmation");
1346   if (err)
1347     goto leave;
1348   err = mime_maker_add_header (mime, "Wks-Draft-Version",
1349                                STR2(WKS_DRAFT_VERSION));
1350   if (err)
1351     goto leave;
1352
1353   if (encrypt)
1354     {
1355       err = mime_maker_add_header (mime, "Content-Type",
1356                                    "multipart/encrypted; "
1357                                    "protocol=\"application/pgp-encrypted\"");
1358       if (err)
1359         goto leave;
1360       err = mime_maker_add_container (mime);
1361       if (err)
1362         goto leave;
1363
1364       err = mime_maker_add_header (mime, "Content-Type",
1365                                    "application/pgp-encrypted");
1366       if (err)
1367         goto leave;
1368       err = mime_maker_add_body (mime, "Version: 1\n");
1369       if (err)
1370         goto leave;
1371       err = mime_maker_add_header (mime, "Content-Type",
1372                                    "application/octet-stream");
1373       if (err)
1374         goto leave;
1375
1376       err = mime_maker_add_stream (mime, &bodyenc);
1377       if (err)
1378         goto leave;
1379     }
1380   else
1381     {
1382       err = mime_maker_add_header (mime, "Content-Type",
1383                                    "application/vnd.gnupg.wks");
1384       if (err)
1385         goto leave;
1386       err = mime_maker_add_stream (mime, &body);
1387       if (err)
1388         goto leave;
1389     }
1390
1391   err = wks_send_mime (mime);
1392
1393  leave:
1394   mime_maker_release (mime);
1395   es_fclose (bodyenc);
1396   es_fclose (body);
1397   return err;
1398 }
1399
1400
1401 /* Reply to a confirmation request.  The MSG has already been
1402  * decrypted and we only need to send the nonce back.  MAINFPR is
1403  * either NULL or the primary key fingerprint of the key used to
1404  * decrypt the request.  */
1405 static gpg_error_t
1406 process_confirmation_request (estream_t msg, const char *mainfpr)
1407 {
1408   gpg_error_t err;
1409   nvc_t nvc;
1410   nve_t item;
1411   const char *value, *sender, *address, *fingerprint, *nonce;
1412
1413   err = nvc_parse (&nvc, NULL, msg);
1414   if (err)
1415     {
1416       log_error ("parsing the WKS message failed: %s\n", gpg_strerror (err));
1417       goto leave;
1418     }
1419
1420   if (DBG_MIME)
1421     {
1422       log_debug ("request follows:\n");
1423       nvc_write (nvc, log_get_stream ());
1424     }
1425
1426   /* Check that this is a confirmation request.  */
1427   if (!((item = nvc_lookup (nvc, "type:")) && (value = nve_value (item))
1428         && !strcmp (value, "confirmation-request")))
1429     {
1430       if (item && value)
1431         log_error ("received unexpected wks message '%s'\n", value);
1432       else
1433         log_error ("received invalid wks message: %s\n", "'type' missing");
1434       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
1435       goto leave;
1436     }
1437
1438   /* Get the fingerprint.  */
1439   if (!((item = nvc_lookup (nvc, "fingerprint:"))
1440         && (value = nve_value (item))
1441         && strlen (value) >= 40))
1442     {
1443       log_error ("received invalid wks message: %s\n",
1444                  "'fingerprint' missing or invalid");
1445       err = gpg_error (GPG_ERR_INV_DATA);
1446       goto leave;
1447     }
1448   fingerprint = value;
1449
1450   /* Check that the fingerprint matches the key used to decrypt the
1451    * message.  In --read mode or with the old format we don't have the
1452    * decryption key; thus we can't bail out.  */
1453   if (!mainfpr || ascii_strcasecmp (mainfpr, fingerprint))
1454     {
1455       log_info ("target fingerprint: %s\n", fingerprint);
1456       log_info ("but decrypted with: %s\n", mainfpr);
1457       log_error ("confirmation request not decrypted with target key\n");
1458       if (mainfpr)
1459         {
1460           err = gpg_error (GPG_ERR_INV_DATA);
1461           goto leave;
1462         }
1463     }
1464
1465   /* Get the address.  */
1466   if (!((item = nvc_lookup (nvc, "address:")) && (value = nve_value (item))
1467         && is_valid_mailbox (value)))
1468     {
1469       log_error ("received invalid wks message: %s\n",
1470                  "'address' missing or invalid");
1471       err = gpg_error (GPG_ERR_INV_DATA);
1472       goto leave;
1473     }
1474   address = value;
1475   /* FIXME: Check that the "address" matches the User ID we want to
1476    * publish.  */
1477
1478   /* Get the sender.  */
1479   if (!((item = nvc_lookup (nvc, "sender:")) && (value = nve_value (item))
1480         && is_valid_mailbox (value)))
1481     {
1482       log_error ("received invalid wks message: %s\n",
1483                  "'sender' missing or invalid");
1484       err = gpg_error (GPG_ERR_INV_DATA);
1485       goto leave;
1486     }
1487   sender = value;
1488   /* FIXME: Check that the "sender" matches the From: address.  */
1489
1490   /* Get the nonce.  */
1491   if (!((item = nvc_lookup (nvc, "nonce:")) && (value = nve_value (item))
1492         && strlen (value) > 16))
1493     {
1494       log_error ("received invalid wks message: %s\n",
1495                  "'nonce' missing or too short");
1496       err = gpg_error (GPG_ERR_INV_DATA);
1497       goto leave;
1498     }
1499   nonce = value;
1500
1501   /* Send the confirmation.  If no key was found, try again without
1502    * encryption.  */
1503   err = send_confirmation_response (sender, address, nonce, 1, fingerprint);
1504   if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
1505     {
1506       log_info ("no encryption key found - sending response in the clear\n");
1507       err = send_confirmation_response (sender, address, nonce, 0, NULL);
1508     }
1509
1510  leave:
1511   nvc_release (nvc);
1512   return err;
1513 }
1514
1515
1516 /* Read a confirmation request and decrypt it if needed.  This
1517  * function may not be used with a mail or MIME message but only with
1518  * the actual encrypted or plaintext WKS data.  */
1519 static gpg_error_t
1520 read_confirmation_request (estream_t msg)
1521 {
1522   gpg_error_t err;
1523   int c;
1524   estream_t plaintext = NULL;
1525
1526   /* We take a really simple approach to check whether MSG is
1527    * encrypted: We know that an encrypted message is always armored
1528    * and thus starts with a few dashes.  It is even sufficient to
1529    * check for a single dash, because that can never be a proper first
1530    * WKS data octet.  We need to skip leading spaces, though. */
1531   while ((c = es_fgetc (msg)) == ' ' || c == '\t' || c == '\r' || c == '\n')
1532     ;
1533   if (c == EOF)
1534     {
1535       log_error ("can't process an empty message\n");
1536       return gpg_error (GPG_ERR_INV_DATA);
1537     }
1538   if (es_ungetc (c, msg) != c)
1539     {
1540       log_error ("error ungetting octet from message\n");
1541       return gpg_error (GPG_ERR_INTERNAL);
1542     }
1543
1544   if (c != '-')
1545     err = process_confirmation_request (msg, NULL);
1546   else
1547     {
1548       struct decrypt_stream_parm_s decinfo;
1549
1550       err = decrypt_stream (&plaintext, &decinfo, msg);
1551       if (err)
1552         log_error ("decryption failed: %s\n", gpg_strerror (err));
1553       else if (decinfo.otrust != 'u')
1554         {
1555           err = gpg_error (GPG_ERR_WRONG_SECKEY);
1556           log_error ("key used to decrypt the confirmation request"
1557                      " was not generated by us\n");
1558         }
1559       else
1560         err = process_confirmation_request (plaintext, decinfo.mainfpr);
1561       xfree (decinfo.fpr);
1562       xfree (decinfo.mainfpr);
1563     }
1564
1565   es_fclose (plaintext);
1566   return err;
1567 }
1568
1569
1570 /* Called from the MIME receiver to process the plain text data in MSG.  */
1571 static gpg_error_t
1572 command_receive_cb (void *opaque, const char *mediatype,
1573                     estream_t msg, unsigned int flags)
1574 {
1575   gpg_error_t err;
1576
1577   (void)opaque;
1578   (void)flags;
1579
1580   if (!strcmp (mediatype, "application/vnd.gnupg.wks"))
1581     err = read_confirmation_request (msg);
1582   else
1583     {
1584       log_info ("ignoring unexpected message of type '%s'\n", mediatype);
1585       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
1586     }
1587
1588   return err;
1589 }