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