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