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