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