tools: Add options to gpg-wks-server.
[gnupg.git] / tools / gpg-wks-server.c
1 /* gpg-wks-server.c - A server for the Web Key Service protocols.
2  * Copyright (C) 2016 Werner Koch
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 /* The Web Key Service I-D defines an update protocol to stpre a
21  * public key in the Web Key Directory.  The current specification is
22  * draft-koch-openpgp-webkey-service-01.txt.
23  */
24
25 #include <config.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #ifdef HAVE_STAT
31 # include <sys/stat.h>
32 #endif
33
34 #include "util.h"
35 #include "init.h"
36 #include "sysutils.h"
37 #include "ccparray.h"
38 #include "exectool.h"
39 #include "zb32.h"
40 #include "mbox-util.h"
41 #include "name-value.h"
42 #include "mime-maker.h"
43 #include "gpg-wks.h"
44
45
46 /* Constants to identify the commands and options. */
47 enum cmd_and_opt_values
48   {
49     aNull = 0,
50
51     oQuiet      = 'q',
52     oVerbose    = 'v',
53
54     oDebug      = 500,
55
56     aReceive,
57     aCron,
58
59     oGpgProgram,
60     oFrom,
61     oHeader,
62
63     oDummy
64   };
65
66
67 /* The list of commands and options. */
68 static ARGPARSE_OPTS opts[] = {
69   ARGPARSE_group (300, ("@Commands:\n ")),
70
71   ARGPARSE_c (aReceive,   "receive",
72               ("receive a submission or confirmation")),
73   ARGPARSE_c (aCron,      "cron",
74               ("run regular jobs")),
75
76   ARGPARSE_group (301, ("@\nOptions:\n ")),
77
78   ARGPARSE_s_n (oVerbose, "verbose", ("verbose")),
79   ARGPARSE_s_n (oQuiet, "quiet",  ("be somewhat more quiet")),
80   ARGPARSE_s_s (oDebug, "debug", "@"),
81   ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
82   ARGPARSE_s_s (oFrom, "from" , "|ADDR|use ADDR as the default sender"),
83   ARGPARSE_s_s (oHeader, "header" ,
84                 "|NAME=VALUE|add \"NAME: VALUE\" as header to all mails"),
85
86   ARGPARSE_end ()
87 };
88
89
90 /* The list of supported debug flags.  */
91 static struct debug_flags_s debug_flags [] =
92   {
93     { DBG_CRYPTO_VALUE , "crypto"  },
94     { DBG_MEMORY_VALUE , "memory"  },
95     { DBG_MEMSTAT_VALUE, "memstat" },
96     { DBG_IPC_VALUE    , "ipc"     },
97     { DBG_EXTPROG_VALUE, "extprog" },
98     { 0, NULL }
99   };
100
101
102 /* State for processing a message.  */
103 struct server_ctx_s
104 {
105   char *fpr;
106   strlist_t mboxes;  /* List of addr-specs taken from the UIDs.  */
107 };
108 typedef struct server_ctx_s *server_ctx_t;
109
110
111
112 static gpg_error_t command_receive_cb (void *opaque,
113                                        const char *mediatype, estream_t fp);
114
115
116 \f
117 /* Print usage information and and provide strings for help. */
118 static const char *
119 my_strusage( int level )
120 {
121   const char *p;
122
123   switch (level)
124     {
125     case 11: p = "gpg-wks-server (@GNUPG@)";
126       break;
127     case 13: p = VERSION; break;
128     case 17: p = PRINTABLE_OS_NAME; break;
129     case 19: p = ("Please report bugs to <@EMAIL@>.\n"); break;
130
131     case 1:
132     case 40:
133       p = ("Usage: gpg-wks-server command [options] (-h for help)");
134       break;
135     case 41:
136       p = ("Syntax: gpg-wks-server command [options]\n"
137            "Server for the Web Key Service protocol\n");
138       break;
139
140     default: p = NULL; break;
141     }
142   return p;
143 }
144
145
146 static void
147 wrong_args (const char *text)
148 {
149   es_fprintf (es_stderr, "usage: %s [options] %s\n", strusage (11), text);
150   exit (2);
151 }
152
153
154 \f
155 /* Command line parsing.  */
156 static enum cmd_and_opt_values
157 parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
158 {
159   enum cmd_and_opt_values cmd = 0;
160   int no_more_options = 0;
161
162   while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
163     {
164       switch (pargs->r_opt)
165         {
166         case oQuiet:     opt.quiet = 1; break;
167         case oVerbose:   opt.verbose++; break;
168         case oDebug:
169           if (parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags))
170             {
171               pargs->r_opt = ARGPARSE_INVALID_ARG;
172               pargs->err = ARGPARSE_PRINT_ERROR;
173             }
174           break;
175
176         case oGpgProgram:
177           opt.gpg_program = pargs->r.ret_str;
178           break;
179         case oFrom:
180           opt.default_from = pargs->r.ret_str;
181           break;
182         case oHeader:
183           append_to_strlist (&opt.extra_headers, pargs->r.ret_str);
184           break;
185
186         case aReceive:
187         case aCron:
188           cmd = pargs->r_opt;
189           break;
190
191         default: pargs->err = 2; break;
192         }
193     }
194
195   return cmd;
196 }
197
198
199 \f
200 /* gpg-wks-server main. */
201 int
202 main (int argc, char **argv)
203 {
204   gpg_error_t err;
205   ARGPARSE_ARGS pargs;
206   enum cmd_and_opt_values cmd;
207
208   gnupg_reopen_std ("gpg-wks-server");
209   set_strusage (my_strusage);
210   log_set_prefix ("gpg-wks-server", GPGRT_LOG_WITH_PREFIX);
211
212   /* Make sure that our subsystems are ready.  */
213   init_common_subsystems (&argc, &argv);
214
215   /* Parse the command line. */
216   pargs.argc  = &argc;
217   pargs.argv  = &argv;
218   pargs.flags = ARGPARSE_FLAG_KEEP;
219   cmd = parse_arguments (&pargs, opts);
220
221   if (log_get_errorcount (0))
222     exit (2);
223
224   /* Print a warning if an argument looks like an option.  */
225   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
226     {
227       int i;
228
229       for (i=0; i < argc; i++)
230         if (argv[i][0] == '-' && argv[i][1] == '-')
231           log_info (("NOTE: '%s' is not considered an option\n"), argv[i]);
232     }
233
234   /* Set defaults for non given options.  */
235   if (!opt.gpg_program)
236     opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
237
238   if (!opt.directory)
239     opt.directory = "/var/lib/gnupg/wks";
240
241   /* Check for syntax errors in the --header option to avoid later
242    * error messages with a not easy to find cause */
243   if (opt.extra_headers)
244     {
245       strlist_t sl;
246
247       for (sl = opt.extra_headers; sl; sl = sl->next)
248         {
249           err = mime_maker_add_header (NULL, sl->d, NULL);
250           if (err)
251             log_error ("syntax error in \"--header %s\": %s\n",
252                        sl->d, gpg_strerror (err));
253         }
254     }
255
256   if (log_get_errorcount (0))
257     exit (2);
258
259
260   /* Check that we have a working directory.  */
261 #if defined(HAVE_STAT)
262   {
263     struct stat sb;
264
265     if (stat (opt.directory, &sb))
266       {
267         err = gpg_error_from_syserror ();
268         log_error ("error accessing directory '%s': %s\n",
269                    opt.directory, gpg_strerror (err));
270         exit (2);
271       }
272     if (!S_ISDIR(sb.st_mode))
273       {
274         log_error ("error accessing directory '%s': %s\n",
275                    opt.directory, "not a directory");
276         exit (2);
277       }
278     if (sb.st_uid != getuid())
279       {
280         log_error ("directory '%s' not owned by user\n", opt.directory);
281         exit (2);
282       }
283     if ((sb.st_mode & S_IRWXO))
284       {
285         log_error ("directory '%s' has too relaxed permissions\n",
286                    opt.directory);
287         exit (2);
288       }
289   }
290 #else /*!HAVE_STAT*/
291   log_fatal ("program build w/o stat() call\n");
292 #endif /*!HAVE_STAT*/
293
294   /* Run the selected command.  */
295   switch (cmd)
296     {
297     case aReceive:
298       if (argc)
299         wrong_args ("--receive");
300       err = wks_receive (es_stdin, command_receive_cb, NULL);
301       if (err)
302         log_error ("processing mail failed: %s\n", gpg_strerror (err));
303       break;
304
305     case aCron:
306       if (argc)
307         wrong_args ("--cron");
308       break;
309
310     default:
311       usage (1);
312       break;
313     }
314
315   return log_get_errorcount (0)? 1:0;
316 }
317
318
319 \f
320 static void
321 list_key_status_cb (void *opaque, const char *keyword, char *args)
322 {
323   server_ctx_t ctx = opaque;
324   (void)ctx;
325   if (opt.debug)
326     log_debug ("%s: %s\n", keyword, args);
327 }
328
329
330 static gpg_error_t
331 list_key (server_ctx_t ctx, estream_t key)
332 {
333   gpg_error_t err;
334   ccparray_t ccp;
335   const char **argv;
336   estream_t listing;
337   char *line = NULL;
338   size_t length_of_line = 0;
339   size_t  maxlen;
340   ssize_t len;
341   char **fields = NULL;
342   int nfields;
343   int lnr;
344   char *mbox = NULL;
345
346   /* We store our results in the context - clear it first.  */
347   xfree (ctx->fpr);
348   ctx->fpr = NULL;
349   free_strlist (ctx->mboxes);
350   ctx->mboxes = NULL;
351
352   /* Open a memory stream.  */
353   listing = es_fopenmem (0, "w+b");
354   if (!listing)
355     {
356       err = gpg_error_from_syserror ();
357       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
358       return err;
359     }
360
361   ccparray_init (&ccp, 0);
362
363   ccparray_put (&ccp, "--no-options");
364   if (!opt.verbose)
365     ccparray_put (&ccp, "--quiet");
366   else if (opt.verbose > 1)
367     ccparray_put (&ccp, "--verbose");
368   ccparray_put (&ccp, "--batch");
369   ccparray_put (&ccp, "--status-fd=2");
370   ccparray_put (&ccp, "--always-trust");
371   ccparray_put (&ccp, "--with-colons");
372   ccparray_put (&ccp, "--dry-run");
373   ccparray_put (&ccp, "--import-options=import-minimal,import-show");
374   ccparray_put (&ccp, "--import");
375
376   ccparray_put (&ccp, NULL);
377   argv = ccparray_get (&ccp, NULL);
378   if (!argv)
379     {
380       err = gpg_error_from_syserror ();
381       goto leave;
382     }
383   err = gnupg_exec_tool_stream (opt.gpg_program, argv, key,
384                                 NULL, listing,
385                                 list_key_status_cb, ctx);
386   if (err)
387     {
388       log_error ("import failed: %s\n", gpg_strerror (err));
389       goto leave;
390     }
391
392   es_rewind (listing);
393   lnr = 0;
394   maxlen = 2048; /* Set limit.  */
395   while ((len = es_read_line (listing, &line, &length_of_line, &maxlen)) > 0)
396     {
397       lnr++;
398       if (!maxlen)
399         {
400           log_error ("received line too long\n");
401           err = gpg_error (GPG_ERR_LINE_TOO_LONG);
402           goto leave;
403         }
404       /* Strip newline and carriage return, if present.  */
405       while (len > 0
406              && (line[len - 1] == '\n' || line[len - 1] == '\r'))
407         line[--len] = '\0';
408       /* log_debug ("line '%s'\n", line); */
409
410       xfree (fields);
411       fields = strtokenize (line, ":");
412       if (!fields)
413         {
414           err = gpg_error_from_syserror ();
415           log_error ("strtokenize failed: %s\n", gpg_strerror (err));
416           goto leave;
417         }
418       for (nfields = 0; fields[nfields]; nfields++)
419         ;
420       if (!nfields)
421         {
422           err = gpg_error (GPG_ERR_INV_ENGINE);
423           goto leave;
424         }
425       if (!strcmp (fields[0], "sec"))
426         {
427           /* gpg may return "sec" as the first record - but we do not
428            * accept secret keys.  */
429           err = gpg_error (GPG_ERR_NO_PUBKEY);
430           goto leave;
431         }
432       if (lnr == 1 && strcmp (fields[0], "pub"))
433         {
434           /* First record is not a public key.  */
435           err = gpg_error (GPG_ERR_INV_ENGINE);
436           goto leave;
437         }
438       if (lnr > 1 && !strcmp (fields[0], "pub"))
439         {
440           /* More than one public key.  */
441           err = gpg_error (GPG_ERR_TOO_MANY);
442           goto leave;
443         }
444       if (!strcmp (fields[0], "sub") || !strcmp (fields[0], "ssb"))
445         break; /* We can stop parsing here.  */
446
447       if (!strcmp (fields[0], "fpr") && nfields > 9 && !ctx->fpr)
448         {
449           ctx->fpr = xtrystrdup (fields[9]);
450           if (!ctx->fpr)
451             {
452               err = gpg_error_from_syserror ();
453               goto leave;
454             }
455         }
456       else if (!strcmp (fields[0], "uid") && nfields > 9)
457         {
458           /* Fixme: Unescape fields[9] */
459           xfree (mbox);
460           mbox = mailbox_from_userid (fields[9]);
461           if (mbox && !append_to_strlist_try (&ctx->mboxes, mbox))
462             {
463               err = gpg_error_from_syserror ();
464               goto leave;
465             }
466         }
467     }
468   if (len < 0 || es_ferror (listing))
469     log_error ("error reading memory stream\n");
470
471  leave:
472   xfree (mbox);
473   xfree (fields);
474   es_free (line);
475   xfree (argv);
476   es_fclose (listing);
477   return err;
478 }
479
480
481 static void
482 encrypt_stream_status_cb (void *opaque, const char *keyword, char *args)
483 {
484   (void)opaque;
485
486   if (opt.debug)
487     log_debug ("%s: %s\n", keyword, args);
488 }
489
490
491 /* Encrypt the INPUT stream to a new stream which is stored at success
492  * at R_OUTPUT.  Encryption is done for the key with FINGERPRINT.  */
493 static gpg_error_t
494 encrypt_stream (estream_t *r_output, estream_t input, const char *fingerprint)
495 {
496   gpg_error_t err;
497   ccparray_t ccp;
498   const char **argv;
499   estream_t output;
500
501   *r_output = NULL;
502
503   output = es_fopenmem (0, "w+b");
504   if (!output)
505     {
506       err = gpg_error_from_syserror ();
507       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
508       return err;
509     }
510
511   ccparray_init (&ccp, 0);
512
513   ccparray_put (&ccp, "--no-options");
514   if (!opt.verbose)
515     ccparray_put (&ccp, "--quiet");
516   else if (opt.verbose > 1)
517     ccparray_put (&ccp, "--verbose");
518   ccparray_put (&ccp, "--batch");
519   ccparray_put (&ccp, "--status-fd=2");
520   ccparray_put (&ccp, "--always-trust");
521   ccparray_put (&ccp, "--armor");
522   ccparray_put (&ccp, "--recipient");
523   ccparray_put (&ccp, fingerprint);
524   ccparray_put (&ccp, "--encrypt");
525   ccparray_put (&ccp, "--");
526
527   ccparray_put (&ccp, NULL);
528   argv = ccparray_get (&ccp, NULL);
529   if (!argv)
530     {
531       err = gpg_error_from_syserror ();
532       goto leave;
533     }
534   err = gnupg_exec_tool_stream (opt.gpg_program, argv, input,
535                                 NULL, output,
536                                 encrypt_stream_status_cb, NULL);
537   if (err)
538     {
539       log_error ("encryption failed: %s\n", gpg_strerror (err));
540       goto leave;
541     }
542
543   es_rewind (output);
544   *r_output = output;
545   output = NULL;
546
547  leave:
548   es_fclose (output);
549   xfree (argv);
550   return err;
551 }
552
553
554 /* Get the submission address for address MBOX.  Caller must free the
555  * value.  If no address can be found NULL is returned.  */
556 static char *
557 get_submission_address (const char *mbox)
558 {
559   gpg_error_t err;
560   const char *domain;
561   char *fname, *line, *p;
562   size_t n;
563   estream_t fp;
564
565   domain = strchr (mbox, '@');
566   if (!domain)
567     return NULL;
568   domain++;
569
570   fname = make_filename_try (opt.directory, domain, "submission-address", NULL);
571   if (!fname)
572     {
573       err = gpg_error_from_syserror ();
574       log_error ("make_filename failed in %s: %s\n",
575                  __func__, gpg_strerror (err));
576       return NULL;
577     }
578
579   fp = es_fopen (fname, "r");
580   if (!fp)
581     {
582       err = gpg_error_from_syserror ();
583       if (gpg_err_code (err) == GPG_ERR_ENOENT)
584         log_info ("Note: no specific submission address configured"
585                   " for domain '%s'\n", domain);
586       else
587         log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
588       xfree (fname);
589       return NULL;
590     }
591
592   line = NULL;
593   n = 0;
594   if (es_getline (&line, &n, fp) < 0)
595     {
596       err = gpg_error_from_syserror ();
597       log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
598       xfree (line);
599       es_fclose (fp);
600       xfree (fname);
601       return NULL;
602     }
603   es_fclose (fp);
604   xfree (fname);
605
606   p = strchr (line, '\n');
607   if (p)
608     *p = 0;
609   trim_spaces (line);
610   if (!is_valid_mailbox (line))
611     {
612       log_error ("invalid submission address for domain '%s' detected\n",
613                  domain);
614       xfree (line);
615       return NULL;
616     }
617
618   return line;
619 }
620
621
622 /* We store the key under the name of the nonce we will then send to
623  * the user.  On success the nonce is stored at R_NONCE.  */
624 static gpg_error_t
625 store_key_as_pending (const char *dir, estream_t key, char **r_nonce)
626 {
627   gpg_error_t err;
628   char *dname = NULL;
629   char *fname = NULL;
630   char *nonce = NULL;
631   estream_t outfp = NULL;
632   char buffer[1024];
633   size_t nbytes, nwritten;
634
635   *r_nonce = NULL;
636
637   dname = make_filename_try (dir, "pending", NULL);
638   if (!dname)
639     {
640       err = gpg_error_from_syserror ();
641       goto leave;
642     }
643
644   if (!gnupg_mkdir (dname, "-rwx"))
645     log_info ("directory '%s' created\n", dname);
646
647   /* Create the nonce.  We use 20 bytes so that we don't waste a
648    * character in our zBase-32 encoding.  Using the gcrypt's nonce
649    * function is faster than using the strong random function; this is
650    * Good Enough for our purpose.  */
651   log_assert (sizeof buffer > 20);
652   gcry_create_nonce (buffer, 20);
653   nonce = zb32_encode (buffer, 8 * 20);
654   memset (buffer, 0, 20);  /* Not actually needed but it does not harm. */
655   if (!nonce)
656     {
657       err = gpg_error_from_syserror ();
658       goto leave;
659     }
660
661   fname = strconcat (dname, "/", nonce, NULL);
662   if (!fname)
663     {
664       err = gpg_error_from_syserror ();
665       goto leave;
666     }
667
668   /* With 128 bits of random we can expect that no other file exists
669    * under this name.  We use "x" to detect internal errors.  */
670   outfp = es_fopen (fname, "wbx,mode=-rw");
671   if (!outfp)
672     {
673       err = gpg_error_from_syserror ();
674       log_error ("error creating '%s': %s\n", fname, gpg_strerror (err));
675       goto leave;
676     }
677   es_rewind (key);
678   for (;;)
679     {
680       if (es_read (key, buffer, sizeof buffer, &nbytes))
681         {
682           err = gpg_error_from_syserror ();
683           log_error ("error reading '%s': %s\n",
684                      es_fname_get (key), gpg_strerror (err));
685           break;
686         }
687
688       if (!nbytes)
689         {
690           err = 0;
691           goto leave; /* Ready.  */
692         }
693       if (es_write (outfp, buffer, nbytes, &nwritten))
694         {
695           err = gpg_error_from_syserror ();
696           log_error ("error writing '%s': %s\n", fname, gpg_strerror (err));
697           goto leave;
698         }
699       else if (nwritten != nbytes)
700         {
701           err = gpg_error (GPG_ERR_EIO);
702           log_error ("error writing '%s': %s\n", fname, "short write");
703           goto leave;
704         }
705     }
706
707  leave:
708   if (err)
709     {
710       es_fclose (outfp);
711       gnupg_remove (fname);
712     }
713   else if (es_fclose (outfp))
714     {
715       err = gpg_error_from_syserror ();
716       log_error ("error closing '%s': %s\n", fname, gpg_strerror (err));
717     }
718
719   if (!err)
720     *r_nonce = nonce;
721   else
722     xfree (nonce);
723
724   xfree (fname);
725   xfree (dname);
726   return err;
727 }
728
729
730 /* Send a confirmation rewqyest.  DIR is the directory used for the
731  * address MBOX.  NONCE is the nonce we want to see in the response to
732  * this mail.  */
733 static gpg_error_t
734 send_confirmation_request (server_ctx_t ctx,
735                            const char *mbox, const char *nonce)
736 {
737   gpg_error_t err;
738   estream_t body = NULL;
739   estream_t bodyenc = NULL;
740   mime_maker_t mime = NULL;
741   char *from_buffer = NULL;
742   const char *from;
743   strlist_t sl;
744
745   from = from_buffer = get_submission_address (mbox);
746   if (!from)
747     {
748       from = opt.default_from;
749       if (!from)
750         {
751           log_error ("no sender address found for '%s'\n", mbox);
752           err = gpg_error (GPG_ERR_CONFIGURATION);
753           goto leave;
754         }
755       log_info ("Note: using default sender address '%s'\n", from);
756     }
757
758   body = es_fopenmem (0, "w+b");
759   if (!body)
760     {
761       err = gpg_error_from_syserror ();
762       log_error ("error allocating memory buffer: %s\n", gpg_strerror (err));
763       goto leave;
764     }
765   /* It is fine to use 8 bit encosind because that is encrypted and
766    * only our client will see it.  */
767   es_fputs ("Content-Type: application/vnd.gnupg.wks\n"
768             "Content-Transfer-Encoding: 8bit\n"
769             "\n",
770             body);
771
772   es_fprintf (body, ("type: confirmation-request\n"
773                      "sender: %s\n"
774                      "address: %s\n"
775                      "fingerprint: %s\n"
776                      "nonce: %s\n"),
777               from,
778               mbox,
779               ctx->fpr,
780               nonce);
781
782   es_rewind (body);
783   err = encrypt_stream (&bodyenc, body, ctx->fpr);
784   if (err)
785     goto leave;
786   es_fclose (body);
787   body = NULL;
788
789
790   err = mime_maker_new (&mime, NULL);
791   if (err)
792     goto leave;
793   err = mime_maker_add_header (mime, "From", from);
794   if (err)
795     goto leave;
796   err = mime_maker_add_header (mime, "To", mbox);
797   if (err)
798     goto leave;
799   err = mime_maker_add_header (mime, "Subject", "Confirm your key publication");
800   if (err)
801     goto leave;
802   for (sl = opt.extra_headers; sl; sl = sl->next)
803     {
804       err = mime_maker_add_header (mime, sl->d, NULL);
805       if (err)
806         goto leave;
807     }
808
809   err = mime_maker_add_header (mime, "Content-Type",
810                                "multipart/encrypted; "
811                                "protocol=\"application/pgp-encrypted\"");
812   if (err)
813     goto leave;
814   err = mime_maker_add_container (mime, "multipart/encrypted");
815   if (err)
816     goto leave;
817
818   err = mime_maker_add_header (mime, "Content-Type",
819                                "application/pgp-encrypted");
820   if (err)
821     goto leave;
822   err = mime_maker_add_body (mime, "Version: 1\n");
823   if (err)
824     goto leave;
825   err = mime_maker_add_header (mime, "Content-Type",
826                                "application/octet-stream");
827   if (err)
828     goto leave;
829
830   err = mime_maker_add_stream (mime, &bodyenc);
831   if (err)
832     goto leave;
833
834   err = mime_maker_make (mime, es_stdout);
835
836  leave:
837   mime_maker_release (mime);
838   xfree (bodyenc);
839   xfree (body);
840   xfree (from_buffer);
841   return err;
842 }
843
844
845 /* Store the key given by KEY into the pending directory and send a
846  * confirmation requests.  */
847 static gpg_error_t
848 process_new_key (server_ctx_t ctx, estream_t key)
849 {
850   gpg_error_t err;
851   strlist_t sl;
852   const char *s;
853   char *dname = NULL;
854   char *nonce = NULL;
855
856   /* First figure out the user id from the key.  */
857   err = list_key (ctx, key);
858   if (err)
859     goto leave;
860   if (!ctx->fpr)
861     {
862       log_error ("error parsing key (no fingerprint)\n");
863       err = gpg_error (GPG_ERR_NO_PUBKEY);
864       goto leave;
865     }
866   log_info ("fingerprint: %s\n", ctx->fpr);
867   for (sl = ctx->mboxes; sl; sl = sl->next)
868     {
869       log_info ("  addr-spec: %s\n", sl->d);
870     }
871
872   /* Walk over all user ids and send confirmation requests for those
873    * we support.  */
874   for (sl = ctx->mboxes; sl; sl = sl->next)
875     {
876       s = strchr (sl->d, '@');
877       log_assert (s && s[1]);
878       xfree (dname);
879       dname = make_filename_try (opt.directory, s+1, NULL);
880       if (!dname)
881         {
882           err = gpg_error_from_syserror ();
883           goto leave;
884         }
885       /* Fixme: check for proper directory permissions.  */
886       if (access (dname, W_OK))
887         {
888           log_info ("skipping address '%s': Domain not configured\n", sl->d);
889           continue;
890         }
891       log_info ("storing address '%s'\n", sl->d);
892
893       xfree (nonce);
894       err = store_key_as_pending (dname, key, &nonce);
895       if (err)
896         goto leave;
897
898       err = send_confirmation_request (ctx, sl->d, nonce);
899       if (err)
900         goto leave;
901     }
902
903  leave:
904   if (nonce)
905     wipememory (nonce, strlen (nonce));
906   xfree (nonce);
907   xfree (dname);
908   return err;
909 }
910
911
912 \f
913 /* Check that we have send a request with NONCE and publish the key.  */
914 static gpg_error_t
915 check_and_publish (server_ctx_t ctx, const char *address, const char *nonce)
916 {
917   gpg_error_t err;
918   char *fname = NULL;
919   char *fnewname = NULL;
920   estream_t key = NULL;
921   char *hash = NULL;
922   const char *domain;
923   const char *s;
924   strlist_t sl;
925
926   domain = strchr (address, '@');
927   log_assert (domain && domain[1]);
928   domain++;
929   fname = make_filename_try (opt.directory, domain, "pending", nonce, NULL);
930   if (!fname)
931     {
932       err = gpg_error_from_syserror ();
933       goto leave;
934     }
935
936   /* Try to open the file with the key.  */
937   key = es_fopen (fname, "rb");
938   if (!key)
939     {
940       err = gpg_error_from_syserror ();
941       if (gpg_err_code (err) == GPG_ERR_ENOENT)
942         {
943           log_info ("no pending request for '%s'\n", address);
944           err = gpg_error (GPG_ERR_NOT_FOUND);
945         }
946       else
947         log_error ("error reading '%s': %s\n", fname, gpg_strerror (err));
948       goto leave;
949     }
950
951   /* We need to get the fingerprint from the key.  */
952   err = list_key (ctx, key);
953   if (err)
954     goto leave;
955   if (!ctx->fpr)
956     {
957       log_error ("error parsing key (no fingerprint)\n");
958       err = gpg_error (GPG_ERR_NO_PUBKEY);
959       goto leave;
960     }
961   log_info ("fingerprint: %s\n", ctx->fpr);
962   for (sl = ctx->mboxes; sl; sl = sl->next)
963     log_info ("  addr-spec: %s\n", sl->d);
964
965   /* Check that the key has 'address' as a user id.  We use
966    * case-insensitive matching because the client is expected to
967    * return the address verbatim.  */
968   for (sl = ctx->mboxes; sl; sl = sl->next)
969     if (!strcmp (sl->d, address))
970       break;
971   if (!sl)
972     {
973       log_error ("error publishing key: '%s' is not a user ID of %s\n",
974                  address, ctx->fpr);
975       err = gpg_error (GPG_ERR_NO_PUBKEY);
976       goto leave;
977     }
978
979
980   /* Hash user ID and create filename.  */
981   s = strchr (address, '@');
982   log_assert (s);
983   {
984     char sha1buf[20];
985     gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, address, s - address);
986     hash = zb32_encode (sha1buf, 8*20);
987   }
988   if (!hash)
989     {
990       err = gpg_error_from_syserror ();
991       goto leave;
992     }
993
994   {
995     /*FIXME: This is a hack to make installation easier.  It is better
996      * to let --cron create the required directories.  */
997     fnewname = make_filename_try (opt.directory, domain, "hu", NULL);
998     if (!fnewname)
999       {
1000         err = gpg_error_from_syserror ();
1001         goto leave;
1002     }
1003     if (!gnupg_mkdir (fnewname, "-rwxr-xr-x"))
1004       log_info ("directory '%s' created\n", fname);
1005     xfree (fnewname);
1006   }
1007   fnewname = make_filename_try (opt.directory, domain, "hu", hash, NULL);
1008   if (!fnewname)
1009     {
1010       err = gpg_error_from_syserror ();
1011       goto leave;
1012     }
1013
1014   /* Publish.  */
1015   if (rename (fname, fnewname))
1016     {
1017       err = gpg_error_from_syserror ();
1018       log_error ("renaming '%s' to '%s' failed: %s\n",
1019                  fname, fnewname, gpg_strerror (err));
1020       goto leave;
1021     }
1022
1023   log_info ("key %s published for '%s'\n", ctx->fpr, address);
1024
1025  leave:
1026   es_fclose (key);
1027   xfree (hash);
1028   xfree (fnewname);
1029   xfree (fname);
1030   return err;
1031 }
1032
1033
1034 /* Process a confirmation response in MSG.  */
1035 static gpg_error_t
1036 process_confirmation_response (server_ctx_t ctx, estream_t msg)
1037 {
1038   gpg_error_t err;
1039   nvc_t nvc;
1040   nve_t item;
1041   const char *value, *sender, *address, *nonce;
1042
1043   err = nvc_parse (&nvc, NULL, msg);
1044   if (err)
1045     {
1046       log_error ("parsing the WKS message failed: %s\n", gpg_strerror (err));
1047       goto leave;
1048     }
1049
1050   if (opt.debug)
1051     {
1052       log_debug ("response follows:\n");
1053       nvc_write (nvc, log_get_stream ());
1054     }
1055
1056   /* Check that this is a confirmation response.  */
1057   if (!((item = nvc_lookup (nvc, "type:")) && (value = nve_value (item))
1058         && !strcmp (value, "confirmation-response")))
1059     {
1060       if (item && value)
1061         log_error ("received unexpected wks message '%s'\n", value);
1062       else
1063         log_error ("received invalid wks message: %s\n", "'type' missing");
1064       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
1065       goto leave;
1066     }
1067
1068   /* Get the sender.  */
1069   if (!((item = nvc_lookup (nvc, "sender:")) && (value = nve_value (item))
1070         && is_valid_mailbox (value)))
1071     {
1072       log_error ("received invalid wks message: %s\n",
1073                  "'sender' missing or invalid");
1074       err = gpg_error (GPG_ERR_INV_DATA);
1075       goto leave;
1076     }
1077   sender = value;
1078   (void)sender;
1079   /* FIXME: Do we really need the sender?.  */
1080
1081   /* Get the address.  */
1082   if (!((item = nvc_lookup (nvc, "address:")) && (value = nve_value (item))
1083         && is_valid_mailbox (value)))
1084     {
1085       log_error ("received invalid wks message: %s\n",
1086                  "'address' missing or invalid");
1087       err = gpg_error (GPG_ERR_INV_DATA);
1088       goto leave;
1089     }
1090   address = value;
1091
1092   /* Get the nonce.  */
1093   if (!((item = nvc_lookup (nvc, "nonce:")) && (value = nve_value (item))
1094         && strlen (value) > 16))
1095     {
1096       log_error ("received invalid wks message: %s\n",
1097                  "'nonce' missing or too short");
1098       err = gpg_error (GPG_ERR_INV_DATA);
1099       goto leave;
1100     }
1101   nonce = value;
1102
1103   err = check_and_publish (ctx, address, nonce);
1104
1105
1106  leave:
1107   nvc_release (nvc);
1108   return err;
1109 }
1110
1111
1112 \f
1113 /* Called from the MIME receiver to process the plain text data in MSG .  */
1114 static gpg_error_t
1115 command_receive_cb (void *opaque, const char *mediatype, estream_t msg)
1116 {
1117   gpg_error_t err;
1118   struct server_ctx_s ctx;
1119
1120   memset (&ctx, 0, sizeof ctx);
1121
1122   (void)opaque;
1123
1124   if (!strcmp (mediatype, "application/pgp-keys"))
1125     err = process_new_key (&ctx, msg);
1126   else if (!strcmp (mediatype, "application/vnd.gnupg.wks"))
1127     err = process_confirmation_response (&ctx, msg);
1128   else
1129     {
1130       log_info ("ignoring unexpected message of type '%s'\n", mediatype);
1131       err = gpg_error (GPG_ERR_UNEXPECTED_MSG);
1132     }
1133
1134   xfree (ctx.fpr);
1135   free_strlist (ctx.mboxes);
1136
1137   return err;
1138 }