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