1 /* gpg-wks-server.c - A server for the Web Key Service protocols.
2  * Copyright (C) 2016, 2018 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  * SPDX-License-Identifier: LGPL-2.1-or-later
20  */
21 
22 /* The Web Key Service I-D defines an update protocol to store a
23  * public key in the Web Key Directory.  The current specification is
24  * draft-koch-openpgp-webkey-service-05.txt.
25  */
26 
27 #include <config.h>
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <dirent.h>
36 
37 #define INCLUDED_BY_MAIN_MODULE 1
38 #include "../common/util.h"
39 #include "../common/init.h"
40 #include "../common/sysutils.h"
41 #include "../common/userids.h"
42 #include "../common/ccparray.h"
43 #include "../common/exectool.h"
44 #include "../common/zb32.h"
45 #include "../common/mbox-util.h"
46 #include "../common/name-value.h"
47 #include "mime-maker.h"
48 #include "send-mail.h"
49 #include "gpg-wks.h"
50 
51 
52 /* The time we wait for a confirmation response.  */
53 #define PENDING_TTL (86400 * 3)  /* 3 days.  */
54 
55 
56 /* Constants to identify the commands and options. */
57 enum cmd_and_opt_values
58   {
59     aNull = 0,
60 
61     oQuiet      = 'q',
62     oVerbose	= 'v',
63     oOutput     = 'o',
64     oDirectory  = 'C',
65 
66     oDebug      = 500,
67 
68     aReceive,
69     aCron,
70     aListDomains,
71     aInstallKey,
72     aRevokeKey,
73     aRemoveKey,
74     aCheck,
75 
76     oGpgProgram,
77     oSend,
78     oFrom,
79     oHeader,
80     oWithDir,
81     oWithFile,
82 
83     oDummy
84   };
85 
86 
87 /* The list of commands and options. */
88 static gpgrt_opt_t opts[] = {
89   ARGPARSE_group (300, ("@Commands:\n ")),
90 
91   ARGPARSE_c (aReceive,   "receive",
92               ("receive a submission or confirmation")),
93   ARGPARSE_c (aCron,      "cron",
94               ("run regular jobs")),
95   ARGPARSE_c (aListDomains, "list-domains",
96               ("list configured domains")),
97   ARGPARSE_c (aCheck, "check",
98               ("check whether a key is installed")),
99   ARGPARSE_c (aCheck, "check-key", "@"),
100   ARGPARSE_c (aInstallKey, "install-key",
101               "install a key from FILE into the WKD"),
102   ARGPARSE_c (aRemoveKey, "remove-key",
103               "remove a key from the WKD"),
104   ARGPARSE_c (aRevokeKey, "revoke-key",
105               "mark a key as revoked"),
106 
107   ARGPARSE_group (301, ("@\nOptions:\n ")),
108 
109   ARGPARSE_s_n (oVerbose, "verbose", ("verbose")),
110   ARGPARSE_s_n (oQuiet,	"quiet",  ("be somewhat more quiet")),
111   ARGPARSE_s_s (oDebug, "debug", "@"),
112   ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
113   ARGPARSE_s_n (oSend, "send", "send the mail using sendmail"),
114   ARGPARSE_s_s (oOutput, "output", "|FILE|write the mail to FILE"),
115   ARGPARSE_s_s (oDirectory, "directory", "|DIR|use DIR as top directory"),
116   ARGPARSE_s_s (oFrom, "from", "|ADDR|use ADDR as the default sender"),
117   ARGPARSE_s_s (oHeader, "header" ,
118                 "|NAME=VALUE|add \"NAME: VALUE\" as header to all mails"),
119   ARGPARSE_s_n (oWithDir, "with-dir", "@"),
120   ARGPARSE_s_n (oWithFile, "with-file", "@"),
121 
122   ARGPARSE_end ()
123 };
124 
125 
126 /* The list of supported debug flags.  */
127 static struct debug_flags_s debug_flags [] =
128   {
129     { DBG_MIME_VALUE   , "mime"    },
130     { DBG_PARSER_VALUE , "parser"  },
131     { DBG_CRYPTO_VALUE , "crypto"  },
132     { DBG_MEMORY_VALUE , "memory"  },
133     { DBG_MEMSTAT_VALUE, "memstat" },
134     { DBG_IPC_VALUE    , "ipc"     },
135     { DBG_EXTPROG_VALUE, "extprog" },
136     { 0, NULL }
137   };
138 
139 
140 /* State for processing a message.  */
141 struct server_ctx_s
142 {
143   char *fpr;
144   uidinfo_list_t mboxes;  /* List with addr-specs taken from the UIDs.  */
145   unsigned int draft_version_2:1; /* Client supports the draft 2.  */
146 };
147 typedef struct server_ctx_s *server_ctx_t;
148 
149 
150 /* Flag for --with-dir.  */
151 static int opt_with_dir;
152 /* Flag for --with-file.  */
153 static int opt_with_file;
154 
155 
156 /* Prototypes.  */
157 static gpg_error_t get_domain_list (strlist_t *r_list);
158 
159 static gpg_error_t command_receive_cb (void *opaque,
160                                        const char *mediatype, estream_t fp,
161                                        unsigned int flags);
162 static gpg_error_t command_list_domains (void);
163 static gpg_error_t command_revoke_key (const char *mailaddr);
164 static gpg_error_t command_check_key (const char *mailaddr);
165 static gpg_error_t command_cron (void);
166 
167 
168 
169 /* Print usage information and provide strings for help. */
170 static const char *
my_strusage(int level)171 my_strusage( int level )
172 {
173   const char *p;
174 
175   switch (level)
176     {
177     case  9: p = "LGPL-2.1-or-later"; break;
178     case 11: p = "gpg-wks-server"; break;
179     case 12: p = "@GNUPG@"; break;
180     case 13: p = VERSION; break;
181     case 14: p = GNUPG_DEF_COPYRIGHT_LINE; break;
182     case 17: p = PRINTABLE_OS_NAME; break;
183     case 19: p = ("Please report bugs to <@EMAIL@>.\n"); break;
184 
185     case 1:
186     case 40:
187       p = ("Usage: gpg-wks-server command [options] (-h for help)");
188       break;
189     case 41:
190       p = ("Syntax: gpg-wks-server command [options]\n"
191            "Server for the Web Key Service protocol\n");
192       break;
193 
194     default: p = NULL; break;
195     }
196   return p;
197 }
198 
199 
200 static void
wrong_args(const char * text)201 wrong_args (const char *text)
202 {
203   es_fprintf (es_stderr, "usage: %s [options] %s\n", gpgrt_strusage (11), text);
204   exit (2);
205 }
206 
207 
208 
209 /* Command line parsing.  */
210 static enum cmd_and_opt_values
parse_arguments(gpgrt_argparse_t * pargs,gpgrt_opt_t * popts)211 parse_arguments (gpgrt_argparse_t *pargs, gpgrt_opt_t *popts)
212 {
213   enum cmd_and_opt_values cmd = 0;
214   int no_more_options = 0;
215 
216   while (!no_more_options && gpgrt_argparse (NULL, pargs, popts))
217     {
218       switch (pargs->r_opt)
219         {
220 	case oQuiet:     opt.quiet = 1; break;
221         case oVerbose:   opt.verbose++; break;
222         case oDebug:
223           if (parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags))
224             {
225               pargs->r_opt = ARGPARSE_INVALID_ARG;
226               pargs->err = ARGPARSE_PRINT_ERROR;
227             }
228           break;
229 
230         case oGpgProgram:
231           opt.gpg_program = pargs->r.ret_str;
232           break;
233         case oDirectory:
234           opt.directory = pargs->r.ret_str;
235           break;
236         case oFrom:
237           opt.default_from = pargs->r.ret_str;
238           break;
239         case oHeader:
240           append_to_strlist (&opt.extra_headers, pargs->r.ret_str);
241           break;
242         case oSend:
243           opt.use_sendmail = 1;
244           break;
245         case oOutput:
246           opt.output = pargs->r.ret_str;
247           break;
248         case oWithDir:
249           opt_with_dir = 1;
250           break;
251         case oWithFile:
252           opt_with_file = 1;
253           break;
254 
255 	case aReceive:
256         case aCron:
257         case aListDomains:
258         case aCheck:
259         case aInstallKey:
260         case aRemoveKey:
261         case aRevokeKey:
262           cmd = pargs->r_opt;
263           break;
264 
265         default: pargs->err = ARGPARSE_PRINT_ERROR; break;
266 	}
267     }
268 
269   return cmd;
270 }
271 
272 
273 
274 /* gpg-wks-server main. */
275 int
main(int argc,char ** argv)276 main (int argc, char **argv)
277 {
278   gpg_error_t err, firsterr;
279   gpgrt_argparse_t pargs;
280   enum cmd_and_opt_values cmd;
281 
282   gnupg_reopen_std ("gpg-wks-server");
283   gpgrt_set_strusage (my_strusage);
284   log_set_prefix ("gpg-wks-server", GPGRT_LOG_WITH_PREFIX);
285 
286   /* Make sure that our subsystems are ready.  */
287   init_common_subsystems (&argc, &argv);
288 
289   /* Parse the command line. */
290   pargs.argc  = &argc;
291   pargs.argv  = &argv;
292   pargs.flags = ARGPARSE_FLAG_KEEP;
293   cmd = parse_arguments (&pargs, opts);
294   gpgrt_argparse (NULL, &pargs, NULL);
295 
296   if (log_get_errorcount (0))
297     exit (2);
298 
299   /* Print a warning if an argument looks like an option.  */
300   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
301     {
302       int i;
303 
304       for (i=0; i < argc; i++)
305         if (argv[i][0] == '-' && argv[i][1] == '-')
306           log_info (("NOTE: '%s' is not considered an option\n"), argv[i]);
307     }
308 
309   /* Set defaults for non given options.  */
310   if (!opt.gpg_program)
311     opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
312 
313   if (!opt.directory)
314     opt.directory = "/var/lib/gnupg/wks";
315 
316   /* Check for syntax errors in the --header option to avoid later
317    * error messages with a not easy to find cause */
318   if (opt.extra_headers)
319     {
320       strlist_t sl;
321 
322       for (sl = opt.extra_headers; sl; sl = sl->next)
323         {
324           err = mime_maker_add_header (NULL, sl->d, NULL);
325           if (err)
326             log_error ("syntax error in \"--header %s\": %s\n",
327                        sl->d, gpg_strerror (err));
328         }
329     }
330 
331   if (log_get_errorcount (0))
332     exit (2);
333 
334 
335   /* Check that we have a working directory.  */
336 #if defined(HAVE_STAT)
337   {
338     struct stat sb;
339 
340     if (gnupg_stat (opt.directory, &sb))
341       {
342         err = gpg_error_from_syserror ();
343         log_error ("error accessing directory '%s': %s\n",
344                    opt.directory, gpg_strerror (err));
345         exit (2);
346       }
347     if (!S_ISDIR(sb.st_mode))
348       {
349         log_error ("error accessing directory '%s': %s\n",
350                    opt.directory, "not a directory");
351         exit (2);
352       }
353     if (sb.st_uid != getuid())
354       {
355         log_error ("directory '%s' not owned by user\n", opt.directory);
356         exit (2);
357       }
358     if ((sb.st_mode & (S_IROTH|S_IWOTH)))
359       {
360         log_error ("directory '%s' has too relaxed permissions\n",
361                    opt.directory);
362         log_info ("Fix by running: chmod o-rw '%s'\n", opt.directory);
363         exit (2);
364       }
365   }
366 #else /*!HAVE_STAT*/
367   log_fatal ("program build w/o stat() call\n");
368 #endif /*!HAVE_STAT*/
369 
370   /* Run the selected command.  */
371   switch (cmd)
372     {
373     case aReceive:
374       if (argc)
375         wrong_args ("--receive");
376       err = wks_receive (es_stdin, command_receive_cb, NULL);
377       break;
378 
379     case aCron:
380       if (argc)
381         wrong_args ("--cron");
382       err = command_cron ();
383       break;
384 
385     case aListDomains:
386       err = command_list_domains ();
387       break;
388 
389     case aInstallKey:
390       if (!argc)
391         err = wks_cmd_install_key (NULL, NULL);
392       else if (argc == 2)
393         err = wks_cmd_install_key (*argv, argv[1]);
394       else
395         wrong_args ("--install-key [FILE|FINGERPRINT USER-ID]");
396       break;
397 
398     case aRemoveKey:
399       if (argc != 1)
400         wrong_args ("--remove-key USER-ID");
401       err = wks_cmd_remove_key (*argv);
402       break;
403 
404     case aRevokeKey:
405       if (argc != 1)
406         wrong_args ("--revoke-key USER-ID");
407       err = command_revoke_key (*argv);
408       break;
409 
410     case aCheck:
411       if (!argc)
412         wrong_args ("--check USER-IDs");
413       firsterr = 0;
414       for (; argc; argc--, argv++)
415         {
416           err = command_check_key (*argv);
417           if (!firsterr)
418             firsterr = err;
419         }
420       err = firsterr;
421       break;
422 
423     default:
424       gpgrt_usage (1);
425       err = gpg_error (GPG_ERR_BUG);
426       break;
427     }
428 
429   if (err)
430     log_error ("command failed: %s\n", gpg_strerror (err));
431   return log_get_errorcount (0)? 1:0;
432 }
433 
434 
435 /* Take the key in KEYFILE and write it to OUTFILE in binary encoding.
436  * If ADDRSPEC is given only matching user IDs are included in the
437  * output.  */
438 static gpg_error_t
copy_key_as_binary(const char * keyfile,const char * outfile,const char * addrspec)439 copy_key_as_binary (const char *keyfile, const char *outfile,
440                     const char *addrspec)
441 {
442   gpg_error_t err;
443   ccparray_t ccp;
444   const char **argv = NULL;
445   char *filterexp = NULL;
446 
447   if (addrspec)
448     {
449       filterexp = es_bsprintf ("keep-uid=mbox = %s", addrspec);
450       if (!filterexp)
451         {
452           err = gpg_error_from_syserror ();
453           log_error ("error allocating memory buffer: %s\n",
454                      gpg_strerror (err));
455           goto leave;
456         }
457     }
458 
459   ccparray_init (&ccp, 0);
460 
461   ccparray_put (&ccp, "--no-options");
462   if (!opt.verbose)
463     ccparray_put (&ccp, "--quiet");
464   else if (opt.verbose > 1)
465     ccparray_put (&ccp, "--verbose");
466   ccparray_put (&ccp, "--batch");
467   ccparray_put (&ccp, "--yes");
468   ccparray_put (&ccp, "--always-trust");
469   ccparray_put (&ccp, "--no-keyring");
470   ccparray_put (&ccp, "--output");
471   ccparray_put (&ccp, outfile);
472   ccparray_put (&ccp, "--import-options=import-export");
473   if (filterexp)
474     {
475       ccparray_put (&ccp, "--import-filter");
476       ccparray_put (&ccp, filterexp);
477     }
478   ccparray_put (&ccp, "--import");
479   ccparray_put (&ccp, "--");
480   ccparray_put (&ccp, keyfile);
481 
482   ccparray_put (&ccp, NULL);
483   argv = ccparray_get (&ccp, NULL);
484   if (!argv)
485     {
486       err = gpg_error_from_syserror ();
487       goto leave;
488     }
489   err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
490                                 NULL, NULL, NULL, NULL);
491   if (err)
492     {
493       log_error ("%s failed: %s\n", __func__, gpg_strerror (err));
494       goto leave;
495     }
496 
497  leave:
498   xfree (filterexp);
499   xfree (argv);
500   return err;
501 }
502 
503 
504 /* Take the key in KEYFILE and write it to DANEFILE using the DANE
505  * output format. */
506 static gpg_error_t
copy_key_as_dane(const char * keyfile,const char * danefile)507 copy_key_as_dane (const char *keyfile, const char *danefile)
508 {
509   gpg_error_t err;
510   ccparray_t ccp;
511   const char **argv;
512 
513   ccparray_init (&ccp, 0);
514 
515   ccparray_put (&ccp, "--no-options");
516   if (!opt.verbose)
517     ccparray_put (&ccp, "--quiet");
518   else if (opt.verbose > 1)
519     ccparray_put (&ccp, "--verbose");
520   ccparray_put (&ccp, "--batch");
521   ccparray_put (&ccp, "--yes");
522   ccparray_put (&ccp, "--always-trust");
523   ccparray_put (&ccp, "--no-keyring");
524   ccparray_put (&ccp, "--output");
525   ccparray_put (&ccp, danefile);
526   ccparray_put (&ccp, "--export-options=export-dane");
527   ccparray_put (&ccp, "--import-options=import-export");
528   ccparray_put (&ccp, "--import");
529   ccparray_put (&ccp, "--");
530   ccparray_put (&ccp, keyfile);
531 
532   ccparray_put (&ccp, NULL);
533   argv = ccparray_get (&ccp, NULL);
534   if (!argv)
535     {
536       err = gpg_error_from_syserror ();
537       goto leave;
538     }
539   err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL,
540                                 NULL, NULL, NULL, NULL);
541   if (err)
542     {
543       log_error ("%s failed: %s\n", __func__, gpg_strerror (err));
544       goto leave;
545     }
546 
547  leave:
548   xfree (argv);
549   return err;
550 }
551 
552 
553 static void
encrypt_stream_status_cb(void * opaque,const char * keyword,char * args)554 encrypt_stream_status_cb (void *opaque, const char *keyword, char *args)
555 {
556   (void)opaque;
557 
558   if (DBG_CRYPTO)
559     log_debug ("gpg status: %s %s\n", keyword, args);
560 }
561 
562 
563 /* Encrypt the INPUT stream to a new stream which is stored at success
564  * at R_OUTPUT.  Encryption is done for the key in file KEYFIL.  */
565 static gpg_error_t
encrypt_stream(estream_t * r_output,estream_t input,const char * keyfile)566 encrypt_stream (estream_t *r_output, estream_t input, const char *keyfile)
567 {
568   gpg_error_t err;
569   ccparray_t ccp;
570   const char **argv;
571   estream_t output;
572 
573   *r_output = NULL;
574 
575   output = es_fopenmem (0, "w+b");
576   if (!output)
577     {
578       err = gpg_error_from_syserror ();
579       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
580       return err;
581     }
582 
583   ccparray_init (&ccp, 0);
584 
585   ccparray_put (&ccp, "--no-options");
586   if (!opt.verbose)
587     ccparray_put (&ccp, "--quiet");
588   else if (opt.verbose > 1)
589     ccparray_put (&ccp, "--verbose");
590   ccparray_put (&ccp, "--batch");
591   ccparray_put (&ccp, "--status-fd=2");
592   ccparray_put (&ccp, "--always-trust");
593   ccparray_put (&ccp, "--no-keyring");
594   ccparray_put (&ccp, "--armor");
595   ccparray_put (&ccp, "-z0");  /* No compression for improved robustness.  */
596   ccparray_put (&ccp, "--recipient-file");
597   ccparray_put (&ccp, keyfile);
598   ccparray_put (&ccp, "--encrypt");
599   ccparray_put (&ccp, "--");
600 
601   ccparray_put (&ccp, NULL);
602   argv = ccparray_get (&ccp, NULL);
603   if (!argv)
604     {
605       err = gpg_error_from_syserror ();
606       goto leave;
607     }
608   err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
609                                 NULL, output,
610                                 encrypt_stream_status_cb, NULL);
611   if (err)
612     {
613       log_error ("encryption failed: %s\n", gpg_strerror (err));
614       goto leave;
615     }
616 
617   es_rewind (output);
618   *r_output = output;
619   output = NULL;
620 
621  leave:
622   es_fclose (output);
623   xfree (argv);
624   return err;
625 }
626 
627 
628 static void
sign_stream_status_cb(void * opaque,const char * keyword,char * args)629 sign_stream_status_cb (void *opaque, const char *keyword, char *args)
630 {
631   (void)opaque;
632 
633   if (DBG_CRYPTO)
634     log_debug ("gpg status: %s %s\n", keyword, args);
635 }
636 
637 /* Sign the INPUT stream to a new stream which is stored at success at
638  * R_OUTPUT.  A detached signature is created using the key specified
639  * by USERID.  */
640 static gpg_error_t
sign_stream(estream_t * r_output,estream_t input,const char * userid)641 sign_stream (estream_t *r_output, estream_t input, const char *userid)
642 {
643   gpg_error_t err;
644   ccparray_t ccp;
645   const char **argv;
646   estream_t output;
647 
648   *r_output = NULL;
649 
650   output = es_fopenmem (0, "w+b");
651   if (!output)
652     {
653       err = gpg_error_from_syserror ();
654       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
655       return err;
656     }
657 
658   ccparray_init (&ccp, 0);
659 
660   ccparray_put (&ccp, "--no-options");
661   if (!opt.verbose)
662     ccparray_put (&ccp, "--quiet");
663   else if (opt.verbose > 1)
664     ccparray_put (&ccp, "--verbose");
665   ccparray_put (&ccp, "--batch");
666   ccparray_put (&ccp, "--status-fd=2");
667   ccparray_put (&ccp, "--armor");
668   ccparray_put (&ccp, "--local-user");
669   ccparray_put (&ccp, userid);
670   ccparray_put (&ccp, "--detach-sign");
671   ccparray_put (&ccp, "--");
672 
673   ccparray_put (&ccp, NULL);
674   argv = ccparray_get (&ccp, NULL);
675   if (!argv)
676     {
677       err = gpg_error_from_syserror ();
678       goto leave;
679     }
680   err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
681                                 NULL, output,
682                                 sign_stream_status_cb, NULL);
683   if (err)
684     {
685       log_error ("signing failed: %s\n", gpg_strerror (err));
686       goto leave;
687     }
688 
689   es_rewind (output);
690   *r_output = output;
691   output = NULL;
692 
693  leave:
694   es_fclose (output);
695   xfree (argv);
696   return err;
697 }
698 
699 
700 /* Get the submission address for address MBOX.  Caller must free the
701  * value.  If no address can be found NULL is returned.  */
702 static char *
get_submission_address(const char * mbox)703 get_submission_address (const char *mbox)
704 {
705   gpg_error_t err;
706   const char *domain;
707   char *fname, *line, *p;
708   size_t n;
709   estream_t fp;
710 
711   domain = strchr (mbox, '@');
712   if (!domain)
713     return NULL;
714   domain++;
715 
716   fname = make_filename_try (opt.directory, domain, "submission-address", NULL);
717   if (!fname)
718     {
719       err = gpg_error_from_syserror ();
720       log_error ("make_filename failed in %s: %s\n",
721                  __func__, gpg_strerror (err));
722       return NULL;
723     }
724 
725   fp = es_fopen (fname, "r");
726   if (!fp)
727     {
728       err = gpg_error_from_syserror ();
729       if (gpg_err_code (err) == GPG_ERR_ENOENT)
730         log_info ("Note: no specific submission address configured"
731                   " for domain '%s'\n", domain);
732       else
733         log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
734       xfree (fname);
735       return NULL;
736     }
737 
738   line = NULL;
739   n = 0;
740   if (es_getline (&line, &n, fp) < 0)
741     {
742       err = gpg_error_from_syserror ();
743       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
744       xfree (line);
745       es_fclose (fp);
746       xfree (fname);
747       return NULL;
748     }
749   es_fclose (fp);
750   xfree (fname);
751 
752   p = strchr (line, '\n');
753   if (p)
754     *p = 0;
755   trim_spaces (line);
756   if (!is_valid_mailbox (line))
757     {
758       log_error ("invalid submission address for domain '%s' detected\n",
759                  domain);
760       xfree (line);
761       return NULL;
762     }
763 
764   return line;
765 }
766 
767 
768 /* Get the policy flags for address MBOX and store them in POLICY.  */
769 static gpg_error_t
get_policy_flags(policy_flags_t policy,const char * mbox)770 get_policy_flags (policy_flags_t policy, const char *mbox)
771 {
772   gpg_error_t err;
773   const char *domain;
774   char *fname;
775   estream_t fp;
776 
777   memset (policy, 0, sizeof *policy);
778 
779   domain = strchr (mbox, '@');
780   if (!domain)
781     return gpg_error (GPG_ERR_INV_USER_ID);
782   domain++;
783 
784   fname = make_filename_try (opt.directory, domain, "policy", NULL);
785   if (!fname)
786     {
787       err = gpg_error_from_syserror ();
788       log_error ("make_filename failed in %s: %s\n",
789                  __func__, gpg_strerror (err));
790       return err;
791     }
792 
793   fp = es_fopen (fname, "r");
794   if (!fp)
795     {
796       err = gpg_error_from_syserror ();
797       if (gpg_err_code (err) == GPG_ERR_ENOENT)
798         err = 0;
799       else
800         log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
801       xfree (fname);
802       return err;
803     }
804 
805   err = wks_parse_policy (policy, fp, 0);
806   es_fclose (fp);
807   xfree (fname);
808   return err;
809 }
810 
811 
812 /* We store the key under the name of the nonce we will then send to
813  * the user.  On success the nonce is stored at R_NONCE and the file
814  * name at R_FNAME.  */
815 static gpg_error_t
store_key_as_pending(const char * dir,estream_t key,char ** r_nonce,char ** r_fname)816 store_key_as_pending (const char *dir, estream_t key,
817                       char **r_nonce, char **r_fname)
818 {
819   gpg_error_t err;
820   char *dname = NULL;
821   char *fname = NULL;
822   char *nonce = NULL;
823   estream_t outfp = NULL;
824   char buffer[1024];
825   size_t nbytes, nwritten;
826 
827   *r_nonce = NULL;
828   *r_fname = NULL;
829 
830   dname = make_filename_try (dir, "pending", NULL);
831   if (!dname)
832     {
833       err = gpg_error_from_syserror ();
834       goto leave;
835     }
836 
837   /* Create the nonce.  We use 20 bytes so that we don't waste a
838    * character in our zBase-32 encoding.  Using the gcrypt's nonce
839    * function is faster than using the strong random function; this is
840    * Good Enough for our purpose.  */
841   log_assert (sizeof buffer > 20);
842   gcry_create_nonce (buffer, 20);
843   nonce = zb32_encode (buffer, 8 * 20);
844   memset (buffer, 0, 20);  /* Not actually needed but it does not harm. */
845   if (!nonce)
846     {
847       err = gpg_error_from_syserror ();
848       goto leave;
849     }
850 
851   fname = strconcat (dname, "/", nonce, NULL);
852   if (!fname)
853     {
854       err = gpg_error_from_syserror ();
855       goto leave;
856     }
857 
858   /* With 128 bits of random we can expect that no other file exists
859    * under this name.  We use "x" to detect internal errors.  */
860   outfp = es_fopen (fname, "wbx,mode=-rw");
861   if (!outfp)
862     {
863       err = gpg_error_from_syserror ();
864       log_error ("error creating '%s': %s\n", fname, gpg_strerror (err));
865       goto leave;
866     }
867   es_rewind (key);
868   for (;;)
869     {
870       if (es_read (key, buffer, sizeof buffer, &nbytes))
871         {
872           err = gpg_error_from_syserror ();
873           log_error ("error reading '%s': %s\n",
874                      es_fname_get (key), gpg_strerror (err));
875           break;
876         }
877 
878       if (!nbytes)
879         {
880           err = 0;
881           goto leave; /* Ready.  */
882         }
883       if (es_write (outfp, buffer, nbytes, &nwritten))
884         {
885           err = gpg_error_from_syserror ();
886           log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
887           goto leave;
888         }
889       else if (nwritten != nbytes)
890         {
891           err = gpg_error (GPG_ERR_EIO);
892           log_error ("error writing '%s': %s\n", fname, "short write");
893           goto leave;
894         }
895     }
896 
897  leave:
898   if (err)
899     {
900       es_fclose (outfp);
901       gnupg_remove (fname);
902     }
903   else if (es_fclose (outfp))
904     {
905       err = gpg_error_from_syserror ();
906       log_error ("error closing '%s': %s\n", fname, gpg_strerror (err));
907     }
908 
909   if (!err)
910     {
911       *r_nonce = nonce;
912       *r_fname = fname;
913     }
914   else
915     {
916       xfree (nonce);
917       xfree (fname);
918     }
919   xfree (dname);
920   return err;
921 }
922 
923 
924 /* Send a confirmation request.  DIR is the directory used for the
925  * address MBOX.  NONCE is the nonce we want to see in the response to
926  * this mail.  FNAME the name of the file with the key.  */
927 static gpg_error_t
send_confirmation_request(server_ctx_t ctx,const char * mbox,const char * nonce,const char * keyfile)928 send_confirmation_request (server_ctx_t ctx,
929                            const char *mbox, const char *nonce,
930                            const char *keyfile)
931 {
932   gpg_error_t err;
933   estream_t body = NULL;
934   estream_t bodyenc = NULL;
935   estream_t signeddata = NULL;
936   estream_t signature = NULL;
937   mime_maker_t mime = NULL;
938   char *from_buffer = NULL;
939   const char *from;
940   strlist_t sl;
941 
942   from = from_buffer = get_submission_address (mbox);
943   if (!from)
944     {
945       from = opt.default_from;
946       if (!from)
947         {
948           log_error ("no sender address found for '%s'\n", mbox);
949           err = gpg_error (GPG_ERR_CONFIGURATION);
950           goto leave;
951         }
952       log_info ("Note: using default sender address '%s'\n", from);
953     }
954 
955   body = es_fopenmem (0, "w+b");
956   if (!body)
957     {
958       err = gpg_error_from_syserror ();
959       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
960       goto leave;
961     }
962 
963   if (!ctx->draft_version_2)
964     {
965       /* It is fine to use 8 bit encoding because that is encrypted and
966        * only our client will see it.  */
967       es_fputs ("Content-Type: application/vnd.gnupg.wks\n"
968                 "Content-Transfer-Encoding: 8bit\n"
969                 "\n",
970                 body);
971     }
972 
973   es_fprintf (body, ("type: confirmation-request\n"
974                      "sender: %s\n"
975                      "address: %s\n"
976                      "fingerprint: %s\n"
977                      "nonce: %s\n"),
978               from,
979               mbox,
980               ctx->fpr,
981               nonce);
982 
983   es_rewind (body);
984   err = encrypt_stream (&bodyenc, body, keyfile);
985   if (err)
986     goto leave;
987   es_fclose (body);
988   body = NULL;
989 
990 
991   err = mime_maker_new (&mime, NULL);
992   if (err)
993     goto leave;
994   err = mime_maker_add_header (mime, "From", from);
995   if (err)
996     goto leave;
997   err = mime_maker_add_header (mime, "To", mbox);
998   if (err)
999     goto leave;
1000   err = mime_maker_add_header (mime, "Subject", "Confirm your key publication");
1001   if (err)
1002     goto leave;
1003 
1004   err = mime_maker_add_header (mime, "Wks-Draft-Version",
1005                                STR2(WKS_DRAFT_VERSION));
1006   if (err)
1007     goto leave;
1008 
1009   /* Help Enigmail to identify messages.  Note that this is in no way
1010    * secured.  */
1011   err = mime_maker_add_header (mime, "WKS-Phase", "confirm");
1012   if (err)
1013     goto leave;
1014 
1015   for (sl = opt.extra_headers; sl; sl = sl->next)
1016     {
1017       err = mime_maker_add_header (mime, sl->d, NULL);
1018       if (err)
1019         goto leave;
1020     }
1021 
1022   if (!ctx->draft_version_2)
1023     {
1024       err = mime_maker_add_header (mime, "Content-Type",
1025                                    "multipart/encrypted; "
1026                                    "protocol=\"application/pgp-encrypted\"");
1027       if (err)
1028         goto leave;
1029       err = mime_maker_add_container (mime);
1030       if (err)
1031         goto leave;
1032 
1033       err = mime_maker_add_header (mime, "Content-Type",
1034                                    "application/pgp-encrypted");
1035       if (err)
1036         goto leave;
1037       err = mime_maker_add_body (mime, "Version: 1\n");
1038       if (err)
1039         goto leave;
1040       err = mime_maker_add_header (mime, "Content-Type",
1041                                    "application/octet-stream");
1042       if (err)
1043         goto leave;
1044 
1045       err = mime_maker_add_stream (mime, &bodyenc);
1046       if (err)
1047         goto leave;
1048 
1049     }
1050   else
1051     {
1052       unsigned int partid;
1053 
1054       /* FIXME: Add micalg.  */
1055       err = mime_maker_add_header (mime, "Content-Type",
1056                                    "multipart/signed; "
1057                                    "protocol=\"application/pgp-signature\"");
1058       if (err)
1059         goto leave;
1060       err = mime_maker_add_container (mime);
1061       if (err)
1062         goto leave;
1063 
1064       err = mime_maker_add_header (mime, "Content-Type", "multipart/mixed");
1065       if (err)
1066         goto leave;
1067 
1068       err = mime_maker_add_container (mime);
1069       if (err)
1070         goto leave;
1071       partid = mime_maker_get_partid (mime);
1072 
1073       err = mime_maker_add_header (mime, "Content-Type", "text/plain");
1074       if (err)
1075         goto leave;
1076 
1077       err = mime_maker_add_body
1078         (mime,
1079          "This message has been send to confirm your request\n"
1080          "to publish your key.  If you did not request a key\n"
1081          "publication, simply ignore this message.\n"
1082          "\n"
1083          "Most mail software can handle this kind of message\n"
1084          "automatically and thus you would not have seen this\n"
1085          "message.  It seems that your client does not fully\n"
1086          "support this service.  The web page\n"
1087          "\n"
1088          "       https://gnupg.org/faq/wkd.html\n"
1089          "\n"
1090          "explains how you can process this message anyway in\n"
1091          "a few manual steps.\n");
1092       if (err)
1093         goto leave;
1094 
1095       err = mime_maker_add_header (mime, "Content-Type",
1096                                    "application/vnd.gnupg.wks");
1097       if (err)
1098         goto leave;
1099 
1100       err = mime_maker_add_stream (mime, &bodyenc);
1101       if (err)
1102         goto leave;
1103 
1104       err = mime_maker_end_container (mime);
1105       if (err)
1106         goto leave;
1107 
1108       /* mime_maker_dump_tree (mime); */
1109       err = mime_maker_get_part (mime, partid, &signeddata);
1110       if (err)
1111         goto leave;
1112 
1113       err = sign_stream (&signature, signeddata, from);
1114       if (err)
1115         goto leave;
1116 
1117       err = mime_maker_add_header (mime, "Content-Type",
1118                                    "application/pgp-signature");
1119       if (err)
1120         goto leave;
1121 
1122       err = mime_maker_add_stream (mime, &signature);
1123       if (err)
1124         goto leave;
1125     }
1126 
1127   err = wks_send_mime (mime);
1128 
1129  leave:
1130   mime_maker_release (mime);
1131   es_fclose (signature);
1132   es_fclose (signeddata);
1133   es_fclose (bodyenc);
1134   es_fclose (body);
1135   xfree (from_buffer);
1136   return err;
1137 }
1138 
1139 
1140 /* Store the key given by KEY into the pending directory and send a
1141  * confirmation requests.  */
1142 static gpg_error_t
process_new_key(server_ctx_t ctx,estream_t key)1143 process_new_key (server_ctx_t ctx, estream_t key)
1144 {
1145   gpg_error_t err;
1146   uidinfo_list_t sl;
1147   const char *s;
1148   char *dname = NULL;
1149   char *nonce = NULL;
1150   char *fname = NULL;
1151   struct policy_flags_s policybuf;
1152 
1153   memset (&policybuf, 0, sizeof policybuf);
1154 
1155   /* First figure out the user id from the key.  */
1156   xfree (ctx->fpr);
1157   free_uidinfo_list (ctx->mboxes);
1158   err = wks_list_key (key, &ctx->fpr, &ctx->mboxes);
1159   if (err)
1160     goto leave;
1161   log_assert (ctx->fpr);
1162   log_info ("fingerprint: %s\n", ctx->fpr);
1163   for (sl = ctx->mboxes; sl; sl = sl->next)
1164     {
1165       if (sl->mbox)
1166         log_info ("  addr-spec: %s\n", sl->mbox);
1167     }
1168 
1169   /* Walk over all user ids and send confirmation requests for those
1170    * we support.  */
1171   for (sl = ctx->mboxes; sl; sl = sl->next)
1172     {
1173       if (!sl->mbox)
1174         continue;
1175       s = strchr (sl->mbox, '@');
1176       log_assert (s && s[1]);
1177       xfree (dname);
1178       dname = make_filename_try (opt.directory, s+1, NULL);
1179       if (!dname)
1180         {
1181           err = gpg_error_from_syserror ();
1182           goto leave;
1183         }
1184 
1185       if (gnupg_access (dname, W_OK))
1186         {
1187           log_info ("skipping address '%s': Domain not configured\n", sl->mbox);
1188           continue;
1189         }
1190       if (get_policy_flags (&policybuf, sl->mbox))
1191         {
1192           log_info ("skipping address '%s': Bad policy flags\n", sl->mbox);
1193           continue;
1194         }
1195 
1196       if (policybuf.auth_submit)
1197         {
1198           /* Bypass the confirmation stuff and publish the key as is.  */
1199           log_info ("publishing address '%s'\n", sl->mbox);
1200           /* FIXME: We need to make sure that we do this only for the
1201            * address in the mail.  */
1202           log_debug ("auth-submit not yet working!\n");
1203         }
1204       else
1205         {
1206           log_info ("storing address '%s'\n", sl->mbox);
1207 
1208           xfree (nonce);
1209           xfree (fname);
1210           err = store_key_as_pending (dname, key, &nonce, &fname);
1211           if (err)
1212             goto leave;
1213 
1214           err = send_confirmation_request (ctx, sl->mbox, nonce, fname);
1215           if (err)
1216             goto leave;
1217         }
1218     }
1219 
1220  leave:
1221   if (nonce)
1222     wipememory (nonce, strlen (nonce));
1223   xfree (nonce);
1224   xfree (fname);
1225   xfree (dname);
1226   wks_free_policy (&policybuf);
1227   return err;
1228 }
1229 
1230 
1231 
1232 /* Send a message to tell the user at MBOX that their key has been
1233  * published.  FNAME the name of the file with the key.  */
1234 static gpg_error_t
send_congratulation_message(const char * mbox,const char * keyfile)1235 send_congratulation_message (const char *mbox, const char *keyfile)
1236 {
1237   gpg_error_t err;
1238   estream_t body = NULL;
1239   estream_t bodyenc = NULL;
1240   mime_maker_t mime = NULL;
1241   char *from_buffer = NULL;
1242   const char *from;
1243   strlist_t sl;
1244 
1245   from = from_buffer = get_submission_address (mbox);
1246   if (!from)
1247     {
1248       from = opt.default_from;
1249       if (!from)
1250         {
1251           log_error ("no sender address found for '%s'\n", mbox);
1252           err = gpg_error (GPG_ERR_CONFIGURATION);
1253           goto leave;
1254         }
1255       log_info ("Note: using default sender address '%s'\n", from);
1256     }
1257 
1258   body = es_fopenmem (0, "w+b");
1259   if (!body)
1260     {
1261       err = gpg_error_from_syserror ();
1262       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
1263       goto leave;
1264     }
1265   /* It is fine to use 8 bit encoding because that is encrypted and
1266    * only our client will see it.  */
1267   es_fputs ("Content-Type: text/plain; charset=utf-8\n"
1268             "Content-Transfer-Encoding: 8bit\n"
1269             "\n",
1270             body);
1271 
1272   es_fprintf (body,
1273               "Hello!\n\n"
1274               "The key for your address '%s' has been published\n"
1275               "and can now be retrieved from the Web Key Directory.\n"
1276               "\n"
1277               "For more information on this system see:\n"
1278               "\n"
1279               "  https://gnupg.org/faq/wkd.html\n"
1280               "\n"
1281               "Best regards\n"
1282               "\n"
1283               "  Gnu Key Publisher\n\n\n"
1284               "-- \n"
1285               "The GnuPG Project welcomes donations: %s\n",
1286               mbox, "https://gnupg.org/donate");
1287 
1288   es_rewind (body);
1289   err = encrypt_stream (&bodyenc, body, keyfile);
1290   if (err)
1291     goto leave;
1292   es_fclose (body);
1293   body = NULL;
1294 
1295   err = mime_maker_new (&mime, NULL);
1296   if (err)
1297     goto leave;
1298   err = mime_maker_add_header (mime, "From", from);
1299   if (err)
1300     goto leave;
1301   err = mime_maker_add_header (mime, "To", mbox);
1302   if (err)
1303     goto leave;
1304   err = mime_maker_add_header (mime, "Subject", "Your key has been published");
1305   if (err)
1306     goto leave;
1307   err = mime_maker_add_header (mime, "Wks-Draft-Version",
1308                                STR2(WKS_DRAFT_VERSION));
1309   if (err)
1310     goto leave;
1311   err = mime_maker_add_header (mime, "WKS-Phase", "done");
1312   if (err)
1313     goto leave;
1314   for (sl = opt.extra_headers; sl; sl = sl->next)
1315     {
1316       err = mime_maker_add_header (mime, sl->d, NULL);
1317       if (err)
1318         goto leave;
1319     }
1320 
1321   err = mime_maker_add_header (mime, "Content-Type",
1322                                "multipart/encrypted; "
1323                                "protocol=\"application/pgp-encrypted\"");
1324   if (err)
1325     goto leave;
1326   err = mime_maker_add_container (mime);
1327   if (err)
1328     goto leave;
1329 
1330   err = mime_maker_add_header (mime, "Content-Type",
1331                                "application/pgp-encrypted");
1332   if (err)
1333     goto leave;
1334   err = mime_maker_add_body (mime, "Version: 1\n");
1335   if (err)
1336     goto leave;
1337   err = mime_maker_add_header (mime, "Content-Type",
1338                                "application/octet-stream");
1339   if (err)
1340     goto leave;
1341 
1342   err = mime_maker_add_stream (mime, &bodyenc);
1343   if (err)
1344     goto leave;
1345 
1346   err = wks_send_mime (mime);
1347 
1348  leave:
1349   mime_maker_release (mime);
1350   es_fclose (bodyenc);
1351   es_fclose (body);
1352   xfree (from_buffer);
1353   return err;
1354 }
1355 
1356 
1357 /* Check that we have send a request with NONCE and publish the key.  */
1358 static gpg_error_t
check_and_publish(server_ctx_t ctx,const char * address,const char * nonce)1359 check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
1360 {
1361   gpg_error_t err;
1362   char *fname = NULL;
1363   char *fnewname = NULL;
1364   estream_t key = NULL;
1365   char *hash = NULL;
1366   const char *domain;
1367   const char *s;
1368   uidinfo_list_t sl;
1369   char shaxbuf[32]; /* Used for SHA-1 and SHA-256 */
1370 
1371   /* FIXME: There is a bug in name-value.c which adds white space for
1372    * the last pair and thus we strip the nonce here until this has
1373    * been fixed.  */
1374   char *nonce2 = xstrdup (nonce);
1375   trim_trailing_spaces (nonce2);
1376   nonce = nonce2;
1377 
1378 
1379   domain = strchr (address, '@');
1380   log_assert (domain && domain[1]);
1381   domain++;
1382   fname = make_filename_try (opt.directory, domain, "pending", nonce, NULL);
1383   if (!fname)
1384     {
1385       err = gpg_error_from_syserror ();
1386       goto leave;
1387     }
1388 
1389   /* Try to open the file with the key.  */
1390   key = es_fopen (fname, "rb");
1391   if (!key)
1392     {
1393       err = gpg_error_from_syserror ();
1394       if (gpg_err_code (err) == GPG_ERR_ENOENT)
1395         {
1396           log_info ("no pending request for '%s'\n", address);
1397           err = gpg_error (GPG_ERR_NOT_FOUND);
1398         }
1399       else
1400         log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
1401       goto leave;
1402     }
1403 
1404   /* We need to get the fingerprint from the key.  */
1405   xfree (ctx->fpr);
1406   free_uidinfo_list (ctx->mboxes);
1407   err = wks_list_key (key, &ctx->fpr, &ctx->mboxes);
1408   if (err)
1409     goto leave;
1410   log_assert (ctx->fpr);
1411   log_info ("fingerprint: %s\n", ctx->fpr);
1412   for (sl = ctx->mboxes; sl; sl = sl->next)
1413     if (sl->mbox)
1414       log_info ("  addr-spec: %s\n", sl->mbox);
1415 
1416   /* Check that the key has 'address' as a user id.  We use
1417    * case-insensitive matching because the client is expected to
1418    * return the address verbatim.  */
1419   for (sl = ctx->mboxes; sl; sl = sl->next)
1420     if (sl->mbox && !strcmp (sl->mbox, address))
1421       break;
1422   if (!sl)
1423     {
1424       log_error ("error publishing key: '%s' is not a user ID of %s\n",
1425                  address, ctx->fpr);
1426       err = gpg_error (GPG_ERR_NO_PUBKEY);
1427       goto leave;
1428     }
1429 
1430   /* Hash user ID and create filename.  */
1431   err = wks_compute_hu_fname (&fnewname, address);
1432   if (err)
1433     goto leave;
1434 
1435   /* Publish.  */
1436   err = copy_key_as_binary (fname, fnewname, address);
1437   if (err)
1438     {
1439       err = gpg_error_from_syserror ();
1440       log_error ("copying '%s' to '%s' failed: %s\n",
1441                  fname, fnewname, gpg_strerror (err));
1442       goto leave;
1443     }
1444 
1445   /* Make sure it is world readable.  */
1446   if (gnupg_chmod (fnewname, "-rwxr--r--"))
1447     log_error ("can't set permissions of '%s': %s\n",
1448                fnewname, gpg_strerror (gpg_err_code_from_syserror()));
1449 
1450   log_info ("key %s published for '%s'\n", ctx->fpr, address);
1451   send_congratulation_message (address, fnewname);
1452 
1453   /* Try to publish as DANE record if the DANE directory exists.  */
1454   xfree (fname);
1455   fname = fnewname;
1456   fnewname = make_filename_try (opt.directory, domain, "dane", NULL);
1457   if (!fnewname)
1458     {
1459       err = gpg_error_from_syserror ();
1460       goto leave;
1461     }
1462   if (!gnupg_access (fnewname, W_OK))
1463     {
1464       /* Yes, we have a dane directory.  */
1465       s = strchr (address, '@');
1466       log_assert (s);
1467       gcry_md_hash_buffer (GCRY_MD_SHA256, shaxbuf, address, s - address);
1468       xfree (hash);
1469       hash = bin2hex (shaxbuf, 28, NULL);
1470       if (!hash)
1471         {
1472           err = gpg_error_from_syserror ();
1473           goto leave;
1474         }
1475       xfree (fnewname);
1476       fnewname = make_filename_try (opt.directory, domain, "dane", hash, NULL);
1477       if (!fnewname)
1478         {
1479           err = gpg_error_from_syserror ();
1480           goto leave;
1481         }
1482       err = copy_key_as_dane (fname, fnewname);
1483       if (err)
1484         goto leave;
1485       log_info ("key %s published for '%s' (DANE record)\n", ctx->fpr, address);
1486     }
1487 
1488  leave:
1489   es_fclose (key);
1490   xfree (hash);
1491   xfree (fnewname);
1492   xfree (fname);
1493   xfree (nonce2);
1494   return err;
1495 }
1496 
1497 
1498 /* Process a confirmation response in MSG.  */
1499 static gpg_error_t
process_confirmation_response(server_ctx_t ctx,estream_t msg)1500 process_confirmation_response (server_ctx_t ctx, estream_t msg)
1501 {
1502   gpg_error_t err;
1503   nvc_t nvc;
1504   nve_t item;
1505   const char *value, *sender, *address, *nonce;
1506 
1507   err = nvc_parse (&nvc, NULL, msg);
1508   if (err)
1509     {
1510       log_error ("parsing the WKS message failed: %s\n", gpg_strerror (err));
1511       goto leave;
1512     }
1513 
1514   if (opt.debug)
1515     {
1516       log_debug ("response follows:\n");
1517       nvc_write (nvc, log_get_stream ());
1518     }
1519 
1520   /* Check that this is a confirmation response.  */
1521   if (!((item = nvc_lookup (nvc, "type:")) && (value = nve_value (item))
1522         && !strcmp (value, "confirmation-response")))
1523     {
1524       if (item && value)
1525         log_error ("received unexpected wks message '%s'\n", value);
1526       else
1527         log_error ("received invalid wks message: %s\n", "'type' missing");
1528       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
1529       goto leave;
1530     }
1531 
1532   /* Get the sender.  */
1533   if (!((item = nvc_lookup (nvc, "sender:")) && (value = nve_value (item))
1534         && is_valid_mailbox (value)))
1535     {
1536       log_error ("received invalid wks message: %s\n",
1537                  "'sender' missing or invalid");
1538       err = gpg_error (GPG_ERR_INV_DATA);
1539       goto leave;
1540     }
1541   sender = value;
1542   (void)sender;
1543   /* FIXME: Do we really need the sender?.  */
1544 
1545   /* Get the address.  */
1546   if (!((item = nvc_lookup (nvc, "address:")) && (value = nve_value (item))
1547         && is_valid_mailbox (value)))
1548     {
1549       log_error ("received invalid wks message: %s\n",
1550                  "'address' missing or invalid");
1551       err = gpg_error (GPG_ERR_INV_DATA);
1552       goto leave;
1553     }
1554   address = value;
1555 
1556   /* Get the nonce.  */
1557   if (!((item = nvc_lookup (nvc, "nonce:")) && (value = nve_value (item))
1558         && strlen (value) > 16))
1559     {
1560       log_error ("received invalid wks message: %s\n",
1561                  "'nonce' missing or too short");
1562       err = gpg_error (GPG_ERR_INV_DATA);
1563       goto leave;
1564     }
1565   nonce = value;
1566 
1567   err = check_and_publish (ctx, address, nonce);
1568 
1569 
1570  leave:
1571   nvc_release (nvc);
1572   return err;
1573 }
1574 
1575 
1576 
1577 /* Called from the MIME receiver to process the plain text data in MSG .  */
1578 static gpg_error_t
command_receive_cb(void * opaque,const char * mediatype,estream_t msg,unsigned int flags)1579 command_receive_cb (void *opaque, const char *mediatype,
1580                     estream_t msg, unsigned int flags)
1581 {
1582   gpg_error_t err;
1583   struct server_ctx_s ctx;
1584 
1585   (void)opaque;
1586 
1587   memset (&ctx, 0, sizeof ctx);
1588   if ((flags & WKS_RECEIVE_DRAFT2))
1589     ctx.draft_version_2 = 1;
1590 
1591   if (!strcmp (mediatype, "application/pgp-keys"))
1592     err = process_new_key (&ctx, msg);
1593   else if (!strcmp (mediatype, "application/vnd.gnupg.wks"))
1594     err = process_confirmation_response (&ctx, msg);
1595   else
1596     {
1597       log_info ("ignoring unexpected message of type '%s'\n", mediatype);
1598       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
1599     }
1600 
1601   xfree (ctx.fpr);
1602   free_uidinfo_list (ctx.mboxes);
1603 
1604   return err;
1605 }
1606 
1607 
1608 
1609 /* Return a list of all configured domains.  Each list element is the
1610  * top directory for the domain.  To figure out the actual domain
1611  * name strrchr(name, '/') can be used.  */
1612 static gpg_error_t
get_domain_list(strlist_t * r_list)1613 get_domain_list (strlist_t *r_list)
1614 {
1615   gpg_error_t err;
1616   gnupg_dir_t dir = NULL;
1617   char *fname = NULL;
1618   gnupg_dirent_t dentry;
1619   struct stat sb;
1620   strlist_t list = NULL;
1621 
1622   *r_list = NULL;
1623 
1624   dir = gnupg_opendir (opt.directory);
1625   if (!dir)
1626     {
1627       err = gpg_error_from_syserror ();
1628       goto leave;
1629     }
1630 
1631   while ((dentry = gnupg_readdir (dir)))
1632     {
1633       if (*dentry->d_name == '.')
1634         continue;
1635       if (!strchr (dentry->d_name, '.'))
1636         continue; /* No dot - can't be a domain subdir.  */
1637 
1638       xfree (fname);
1639       fname = make_filename_try (opt.directory, dentry->d_name, NULL);
1640       if (!fname)
1641         {
1642           err = gpg_error_from_syserror ();
1643           log_error ("make_filename failed in %s: %s\n",
1644                      __func__, gpg_strerror (err));
1645           goto leave;
1646         }
1647 
1648       if (gnupg_stat (fname, &sb))
1649         {
1650           err = gpg_error_from_syserror ();
1651           log_error ("error accessing '%s': %s\n", fname, gpg_strerror (err));
1652           continue;
1653         }
1654       if (!S_ISDIR(sb.st_mode))
1655         continue;
1656 
1657       if (!add_to_strlist_try (&list, fname))
1658         {
1659           err = gpg_error_from_syserror ();
1660           log_error ("add_to_strlist failed in %s: %s\n",
1661                      __func__, gpg_strerror (err));
1662           goto leave;
1663         }
1664     }
1665   err = 0;
1666   *r_list = list;
1667   list = NULL;
1668 
1669  leave:
1670   free_strlist (list);
1671   gnupg_closedir (dir);
1672   xfree (fname);
1673   return err;
1674 }
1675 
1676 
1677 
1678 static gpg_error_t
expire_one_domain(const char * top_dirname,const char * domain)1679 expire_one_domain (const char *top_dirname, const char *domain)
1680 {
1681   gpg_error_t err;
1682   char *dirname;
1683   char *fname = NULL;
1684   gnupg_dir_t dir = NULL;
1685   gnupg_dirent_t dentry;
1686   struct stat sb;
1687   time_t now = gnupg_get_time ();
1688 
1689   dirname = make_filename_try (top_dirname, "pending", NULL);
1690   if (!dirname)
1691     {
1692       err = gpg_error_from_syserror ();
1693       log_error ("make_filename failed in %s: %s\n",
1694                  __func__, gpg_strerror (err));
1695       goto leave;
1696     }
1697 
1698   dir = gnupg_opendir (dirname);
1699   if (!dir)
1700     {
1701       err = gpg_error_from_syserror ();
1702       log_error (("can't access directory '%s': %s\n"),
1703                  dirname, gpg_strerror (err));
1704       goto leave;
1705     }
1706 
1707   while ((dentry = gnupg_readdir (dir)))
1708     {
1709       if (*dentry->d_name == '.')
1710         continue;
1711       xfree (fname);
1712       fname = make_filename_try (dirname, dentry->d_name, NULL);
1713       if (!fname)
1714         {
1715           err = gpg_error_from_syserror ();
1716           log_error ("make_filename failed in %s: %s\n",
1717                      __func__, gpg_strerror (err));
1718           goto leave;
1719         }
1720       if (strlen (dentry->d_name) != 32)
1721         {
1722           log_info ("garbage file '%s' ignored\n", fname);
1723           continue;
1724         }
1725       if (gnupg_stat (fname, &sb))
1726         {
1727           err = gpg_error_from_syserror ();
1728           log_error ("error accessing '%s': %s\n", fname, gpg_strerror (err));
1729           continue;
1730         }
1731       if (S_ISDIR(sb.st_mode))
1732         {
1733           log_info ("garbage directory '%s' ignored\n", fname);
1734           continue;
1735         }
1736       if (sb.st_mtime + PENDING_TTL < now)
1737         {
1738           if (opt.verbose)
1739             log_info ("domain %s: removing pending key '%s'\n",
1740                       domain, dentry->d_name);
1741           if (remove (fname))
1742             {
1743               err = gpg_error_from_syserror ();
1744               /* In case the file has just been renamed or another
1745                * processes is cleaning up, we don't print a diagnostic
1746                * for ENOENT.  */
1747               if (gpg_err_code (err) != GPG_ERR_ENOENT)
1748                 log_error ("error removing '%s': %s\n",
1749                            fname, gpg_strerror (err));
1750             }
1751         }
1752     }
1753   err = 0;
1754 
1755  leave:
1756   gnupg_closedir (dir);
1757   xfree (dirname);
1758   xfree (fname);
1759   return err;
1760 
1761 }
1762 
1763 
1764 /* Scan spool directories and expire too old pending keys.  */
1765 static gpg_error_t
expire_pending_confirmations(strlist_t domaindirs)1766 expire_pending_confirmations (strlist_t domaindirs)
1767 {
1768   gpg_error_t err = 0;
1769   strlist_t sl;
1770   const char *domain;
1771 
1772   for (sl = domaindirs; sl; sl = sl->next)
1773     {
1774       domain = strrchr (sl->d, '/');
1775       log_assert (domain);
1776       domain++;
1777 
1778       expire_one_domain (sl->d, domain);
1779     }
1780 
1781   return err;
1782 }
1783 
1784 
1785 /* List all configured domains.  */
1786 static gpg_error_t
command_list_domains(void)1787 command_list_domains (void)
1788 {
1789   static struct {
1790     const char *name;
1791     const char *perm;
1792   } requireddirs[] = {
1793     { "pending", "-rwx" },
1794     { "hu",      "-rwxr-xr-x" }
1795   };
1796   gpg_err_code_t ec;
1797   gpg_error_t err;
1798   strlist_t domaindirs;
1799   strlist_t sl;
1800   const char *domain;
1801   char *fname = NULL;
1802   int i;
1803   estream_t fp;
1804 
1805   err = get_domain_list (&domaindirs);
1806   if (err)
1807     {
1808       log_error ("error reading list of domains: %s\n", gpg_strerror (err));
1809       return err;
1810     }
1811 
1812   for (sl = domaindirs; sl; sl = sl->next)
1813     {
1814       domain = strrchr (sl->d, '/');
1815       log_assert (domain);
1816       domain++;
1817       if (opt_with_dir)
1818         es_printf ("%s %s\n", domain, sl->d);
1819       else
1820         es_printf ("%s\n", domain);
1821 
1822 
1823       /* Check that the required directories are there.  */
1824       for (i=0; i < DIM (requireddirs); i++)
1825         {
1826           xfree (fname);
1827           fname = make_filename_try (sl->d, requireddirs[i].name, NULL);
1828           if (!fname)
1829             {
1830               err = gpg_error_from_syserror ();
1831               goto leave;
1832             }
1833           if ((ec = gnupg_access (fname, W_OK)))
1834             {
1835               err = gpg_error (ec);
1836               if (gpg_err_code (err) == GPG_ERR_ENOENT)
1837                 {
1838                   if (gnupg_mkdir (fname, requireddirs[i].perm))
1839                     {
1840                       err = gpg_error_from_syserror ();
1841                       log_error ("domain %s: error creating subdir '%s': %s\n",
1842                                  domain, requireddirs[i].name,
1843                                  gpg_strerror (err));
1844                     }
1845                   else
1846                     log_info ("domain %s: subdir '%s' created\n",
1847                               domain, requireddirs[i].name);
1848                 }
1849               else if (err)
1850                 log_error ("domain %s: problem with subdir '%s': %s\n",
1851                            domain, requireddirs[i].name, gpg_strerror (err));
1852             }
1853         }
1854 
1855       /* Print a warning if the submission address is not configured.  */
1856       xfree (fname);
1857       fname = make_filename_try (sl->d, "submission-address", NULL);
1858       if (!fname)
1859         {
1860           err = gpg_error_from_syserror ();
1861           goto leave;
1862         }
1863       if ((ec = gnupg_access (fname, F_OK)))
1864         {
1865           err = gpg_error (ec);
1866           if (gpg_err_code (err) == GPG_ERR_ENOENT)
1867             log_error ("domain %s: submission address not configured\n",
1868                        domain);
1869           else
1870             log_error ("domain %s: problem with '%s': %s\n",
1871                        domain, fname, gpg_strerror (err));
1872         }
1873 
1874       /* Check the syntax of the optional policy file.  */
1875       xfree (fname);
1876       fname = make_filename_try (sl->d, "policy", NULL);
1877       if (!fname)
1878         {
1879           err = gpg_error_from_syserror ();
1880           goto leave;
1881         }
1882       fp = es_fopen (fname, "r");
1883       if (!fp)
1884         {
1885           err = gpg_error_from_syserror ();
1886           if (gpg_err_code (err) == GPG_ERR_ENOENT)
1887             {
1888               fp = es_fopen (fname, "w");
1889               if (!fp)
1890                 log_error ("domain %s: can't create policy file: %s\n",
1891                            domain, gpg_strerror (err));
1892               else
1893                 es_fclose (fp);
1894               fp = NULL;
1895             }
1896           else
1897             log_error ("domain %s: error in policy file: %s\n",
1898                        domain, gpg_strerror (err));
1899         }
1900       else
1901         {
1902           struct policy_flags_s policy;
1903           err = wks_parse_policy (&policy, fp, 0);
1904           es_fclose (fp);
1905           wks_free_policy (&policy);
1906         }
1907     }
1908   err = 0;
1909 
1910  leave:
1911   xfree (fname);
1912   free_strlist (domaindirs);
1913   return err;
1914 }
1915 
1916 
1917 /* Run regular maintenance jobs.  */
1918 static gpg_error_t
command_cron(void)1919 command_cron (void)
1920 {
1921   gpg_error_t err;
1922   strlist_t domaindirs;
1923 
1924   err = get_domain_list (&domaindirs);
1925   if (err)
1926     {
1927       log_error ("error reading list of domains: %s\n", gpg_strerror (err));
1928       return err;
1929     }
1930 
1931   err = expire_pending_confirmations (domaindirs);
1932 
1933   free_strlist (domaindirs);
1934   return err;
1935 }
1936 
1937 
1938 /* Check whether the key with USER_ID is installed.  */
1939 static gpg_error_t
command_check_key(const char * userid)1940 command_check_key (const char *userid)
1941 {
1942   gpg_err_code_t ec;
1943   gpg_error_t err;
1944   char *addrspec = NULL;
1945   char *fname = NULL;
1946 
1947   err = wks_fname_from_userid (userid, 0, &fname, &addrspec);
1948   if (err)
1949     goto leave;
1950 
1951   if ((ec = gnupg_access (fname, R_OK)))
1952     {
1953       err = gpg_error (ec);
1954       if (opt_with_file)
1955         es_printf ("%s n %s\n", addrspec, fname);
1956       if (gpg_err_code (err) == GPG_ERR_ENOENT)
1957         {
1958           if (!opt.quiet)
1959             log_info ("key for '%s' is NOT installed\n", addrspec);
1960           log_inc_errorcount ();
1961           err = 0;
1962         }
1963       else
1964         log_error ("error stating '%s': %s\n", fname, gpg_strerror (err));
1965       goto leave;
1966     }
1967 
1968   if (opt_with_file)
1969     es_printf ("%s i %s\n", addrspec, fname);
1970 
1971   if (opt.verbose)
1972     log_info ("key for '%s' is installed\n", addrspec);
1973   err = 0;
1974 
1975  leave:
1976   xfree (fname);
1977   xfree (addrspec);
1978   return err;
1979 }
1980 
1981 
1982 /* Revoke the key with mail address MAILADDR.  */
1983 static gpg_error_t
command_revoke_key(const char * mailaddr)1984 command_revoke_key (const char *mailaddr)
1985 {
1986   /* Remove should be different from removing but we have not yet
1987    * defined a suitable way to do this.  */
1988   return wks_cmd_remove_key (mailaddr);
1989 }
1990