summaryrefslogtreecommitdiffstats
path: root/tools/wks-receive.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2016-06-29 12:00:22 +0200
committerWerner Koch <wk@gnupg.org>2016-06-29 12:04:11 +0200
commit5d6c83deaa11327366b0038928200b9f9f85b426 (patch)
treeeb9ea558716455ebea601d56abe18077be387be5 /tools/wks-receive.c
parentbuild: Improve GNUPG_BUILD_PROGRAM macro. (diff)
downloadgnupg2-5d6c83deaa11327366b0038928200b9f9f85b426.tar.xz
gnupg2-5d6c83deaa11327366b0038928200b9f9f85b426.zip
tools: Add gpg-wks-client and gpg-wks-server.
* configure.ac: Add option --enable-wks-tools * tools/gpg-wks-client.c: New. * tools/gpg-wks-server.c: New. * tools/gpg-wks.h: new. * tools/wks-receive.c: New. * tools/call-dirmngr.c, tools/call-dirmngr.h: New. -- Note that this is just a starting point and not a finished implementation. Here is how to test the system using foo@test.gnupg.org as example. Prepare: mkdir /var/lib/gnupg/wks chmod o-rwx /var/lib/gnupg/wks mkdir /var/lib/gnupg/wks/test.gnupg.org Run the protocol: ./gpg-wks-client -v --send FPR USERID >x ./gpg-wks-server -v --receive <x >y ./gpg-wks-client --receive <y >z ./gpg-wks-server -v --receive <z You should also setup a cron job to rsync /var/lib/gnupg/wks/test.gnupg.org/hu/* to the webserver. Signed-off-by: Werner Koch <wk@gnupg.org>
Diffstat (limited to 'tools/wks-receive.c')
-rw-r--r--tools/wks-receive.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/tools/wks-receive.c b/tools/wks-receive.c
new file mode 100644
index 000000000..59141fcdc
--- /dev/null
+++ b/tools/wks-receive.c
@@ -0,0 +1,464 @@
+/* wks-receive.c - Receive a WKS mail
+ * Copyright (C) 2016 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+#include "ccparray.h"
+#include "exectool.h"
+#include "gpg-wks.h"
+#include "mime-parser.h"
+
+
+/* Limit of acceptable signed data. */
+#define MAX_SIGNEDDATA 10000
+
+/* Limit of acceptable signature. */
+#define MAX_SIGNATURE 10000
+
+/* Limit of acceptable encrypted data. */
+#define MAX_ENCRYPTED 100000
+
+/* Data for a received object. */
+struct receive_ctx_s
+{
+ estream_t encrypted;
+ estream_t plaintext;
+ estream_t signeddata;
+ estream_t signature;
+ estream_t key_data;
+ estream_t wkd_data;
+ unsigned int collect_key_data:1;
+ unsigned int collect_wkd_data:1;
+};
+typedef struct receive_ctx_s *receive_ctx_t;
+
+
+
+static void
+decrypt_data_status_cb (void *opaque, const char *keyword, char *args)
+{
+ receive_ctx_t ctx = opaque;
+ (void)ctx;
+ log_debug ("%s: %s\n", keyword, args);
+}
+
+
+/* Decrypt the collected data. */
+static void
+decrypt_data (receive_ctx_t ctx)
+{
+ gpg_error_t err;
+ ccparray_t ccp;
+ const char **argv;
+ int c;
+
+ es_rewind (ctx->encrypted);
+
+ if (!ctx->plaintext)
+ ctx->plaintext = es_fopenmem (0, "w+b");
+ if (!ctx->plaintext)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating space for plaintext: %s\n",
+ gpg_strerror (err));
+ return;
+ }
+
+ ccparray_init (&ccp, 0);
+
+ /* We limit the output to 64 KiB to avoid DoS using compression
+ * tricks. A regular client will anyway only send a minimal key;
+ * that is one w/o key signatures and attribute packets. */
+ ccparray_put (&ccp, "--max-output=0xf0000"); /*FIXME: Change s/F/1/ */
+ ccparray_put (&ccp, "--batch");
+ if (opt.verbose)
+ ccparray_put (&ccp, "--verbose");
+ ccparray_put (&ccp, "--always-trust");
+ ccparray_put (&ccp, "--decrypt");
+ ccparray_put (&ccp, "--");
+
+ ccparray_put (&ccp, NULL);
+ argv = ccparray_get (&ccp, NULL);
+ if (!argv)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->encrypted,
+ NULL, ctx->plaintext,
+ decrypt_data_status_cb, ctx);
+ if (err)
+ {
+ log_error ("decryption failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ if (opt.debug)
+ {
+ es_rewind (ctx->plaintext);
+ log_debug ("plaintext: '");
+ while ((c = es_getc (ctx->plaintext)) != EOF)
+ log_printf ("%c", c);
+ log_printf ("'\n");
+ }
+ es_rewind (ctx->plaintext);
+
+ leave:
+ xfree (argv);
+}
+
+
+static void
+verify_signature_status_cb (void *opaque, const char *keyword, char *args)
+{
+ receive_ctx_t ctx = opaque;
+ (void)ctx;
+ log_debug ("%s: %s\n", keyword, args);
+}
+
+/* Verify the signed data. */
+static void
+verify_signature (receive_ctx_t ctx)
+{
+ gpg_error_t err;
+ ccparray_t ccp;
+ const char **argv;
+
+ log_assert (ctx->signeddata);
+ log_assert (ctx->signature);
+ es_rewind (ctx->signeddata);
+ es_rewind (ctx->signature);
+
+ ccparray_init (&ccp, 0);
+
+ ccparray_put (&ccp, "--batch");
+ if (opt.verbose)
+ ccparray_put (&ccp, "--verbose");
+ ccparray_put (&ccp, "--enable-special-filenames");
+ ccparray_put (&ccp, "--status-fd=2");
+ ccparray_put (&ccp, "--verify");
+ ccparray_put (&ccp, "--");
+ ccparray_put (&ccp, "-&@INEXTRA@");
+ ccparray_put (&ccp, "-");
+
+ ccparray_put (&ccp, NULL);
+ argv = ccparray_get (&ccp, NULL);
+ if (!argv)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = gnupg_exec_tool_stream (opt.gpg_program, argv, ctx->signeddata,
+ ctx->signature, NULL,
+ verify_signature_status_cb, ctx);
+ if (err)
+ {
+ log_error ("verification failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ leave:
+ xfree (argv);
+}
+
+
+static gpg_error_t
+collect_encrypted (void *cookie, const char *data)
+{
+ receive_ctx_t ctx = cookie;
+
+ if (!ctx->encrypted)
+ if (!(ctx->encrypted = es_fopenmem (MAX_ENCRYPTED, "w+b,samethread")))
+ return gpg_error_from_syserror ();
+ if (data)
+ es_fputs (data, ctx->encrypted);
+
+ if (es_ferror (ctx->encrypted))
+ return gpg_error_from_syserror ();
+
+ if (!data)
+ {
+ decrypt_data (ctx);
+ }
+
+ return 0;
+}
+
+
+static gpg_error_t
+collect_signeddata (void *cookie, const char *data)
+{
+ receive_ctx_t ctx = cookie;
+
+ if (!ctx->signeddata)
+ if (!(ctx->signeddata = es_fopenmem (MAX_SIGNEDDATA, "w+b,samethread")))
+ return gpg_error_from_syserror ();
+ if (data)
+ es_fputs (data, ctx->signeddata);
+
+ if (es_ferror (ctx->signeddata))
+ return gpg_error_from_syserror ();
+ return 0;
+}
+
+static gpg_error_t
+collect_signature (void *cookie, const char *data)
+{
+ receive_ctx_t ctx = cookie;
+
+ if (!ctx->signature)
+ if (!(ctx->signature = es_fopenmem (MAX_SIGNATURE, "w+b,samethread")))
+ return gpg_error_from_syserror ();
+ if (data)
+ es_fputs (data, ctx->signature);
+
+ if (es_ferror (ctx->signature))
+ return gpg_error_from_syserror ();
+
+ if (!data)
+ {
+ verify_signature (ctx);
+ }
+
+ return 0;
+}
+
+
+static gpg_error_t
+new_part (void *cookie, const char *mediatype, const char *mediasubtype)
+{
+ receive_ctx_t ctx = cookie;
+ gpg_error_t err = 0;
+
+ ctx->collect_key_data = 0;
+ ctx->collect_wkd_data = 0;
+
+ if (!strcmp (mediatype, "application")
+ && !strcmp (mediasubtype, "pgp-keys"))
+ {
+ log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
+ if (ctx->key_data)
+ {
+ log_error ("we already got a key - ignoring this part\n");
+ err = gpg_error (GPG_ERR_FALSE);
+ }
+ else
+ {
+ ctx->key_data = es_fopenmem (0, "w+b");
+ if (!ctx->key_data)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating space for key: %s\n",
+ gpg_strerror (err));
+ }
+ else
+ {
+ ctx->collect_key_data = 1;
+ err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
+ }
+ }
+ }
+ else if (!strcmp (mediatype, "application")
+ && !strcmp (mediasubtype, "vnd.gnupg.wks"))
+ {
+ log_info ("new '%s/%s' message part\n", mediatype, mediasubtype);
+ if (ctx->wkd_data)
+ {
+ log_error ("we already got a wkd part - ignoring this part\n");
+ err = gpg_error (GPG_ERR_FALSE);
+ }
+ else
+ {
+ ctx->wkd_data = es_fopenmem (0, "w+b");
+ if (!ctx->wkd_data)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error allocating space for key: %s\n",
+ gpg_strerror (err));
+ }
+ else
+ {
+ ctx->collect_wkd_data = 1;
+ err = gpg_error (GPG_ERR_TRUE); /* We want the part decoded. */
+ }
+ }
+ }
+ else
+ {
+ log_error ("unexpected '%s/%s' message part\n", mediatype, mediasubtype);
+ err = gpg_error (GPG_ERR_FALSE); /* We do not want the part. */
+ }
+
+ return err;
+}
+
+
+static gpg_error_t
+part_data (void *cookie, const void *data, size_t datalen)
+{
+ receive_ctx_t ctx = cookie;
+
+ if (data)
+ {
+ if (opt.debug)
+ log_debug ("part_data: '%.*s'\n", (int)datalen, (const char*)data);
+ if (ctx->collect_key_data)
+ {
+ if (es_write (ctx->key_data, data, datalen, NULL)
+ || es_fputs ("\n", ctx->key_data))
+ return gpg_error_from_syserror ();
+ }
+ if (ctx->collect_wkd_data)
+ {
+ if (es_write (ctx->wkd_data, data, datalen, NULL)
+ || es_fputs ("\n", ctx->wkd_data))
+ return gpg_error_from_syserror ();
+ }
+ }
+ else
+ {
+ if (opt.debug)
+ log_debug ("part_data: finished\n");
+ ctx->collect_key_data = 0;
+ ctx->collect_wkd_data = 0;
+ }
+ return 0;
+}
+
+
+/* Receive a WKS mail from FP and process it accordingly. On success
+ * the RESULT_CB is called with the mediatype and a stream with the
+ * decrypted data. */
+gpg_error_t
+wks_receive (estream_t fp,
+ gpg_error_t (*result_cb)(void *opaque,
+ const char *mediatype,
+ estream_t data),
+ void *cb_data)
+{
+ gpg_error_t err;
+ receive_ctx_t ctx;
+ mime_parser_t parser;
+ estream_t plaintext = NULL;
+ int c;
+
+ ctx = xtrycalloc (1, sizeof *ctx);
+ if (!ctx)
+ return gpg_error_from_syserror ();
+
+ err = mime_parser_new (&parser, ctx);
+ if (err)
+ goto leave;
+ if (opt.verbose > 1 || opt.debug)
+ mime_parser_set_verbose (parser, opt.debug? 10: 1);
+ mime_parser_set_new_part (parser, new_part);
+ mime_parser_set_part_data (parser, part_data);
+ mime_parser_set_collect_encrypted (parser, collect_encrypted);
+ mime_parser_set_collect_signeddata (parser, collect_signeddata);
+ mime_parser_set_collect_signature (parser, collect_signature);
+
+ err = mime_parser_parse (parser, fp);
+ if (err)
+ goto leave;
+
+ if (ctx->key_data)
+ log_info ("key data found\n");
+ if (ctx->wkd_data)
+ log_info ("wkd data found\n");
+
+ if (ctx->plaintext)
+ {
+ if (opt.verbose)
+ log_info ("parsing decrypted message\n");
+ plaintext = ctx->plaintext;
+ ctx->plaintext = NULL;
+ if (ctx->encrypted)
+ es_rewind (ctx->encrypted);
+ if (ctx->signeddata)
+ es_rewind (ctx->signeddata);
+ if (ctx->signature)
+ es_rewind (ctx->signature);
+ err = mime_parser_parse (parser, plaintext);
+ if (err)
+ return err;
+ }
+
+ if (!ctx->key_data && !ctx->wkd_data)
+ {
+ log_error ("no suitable data found in the message\n");
+ err = gpg_error (GPG_ERR_NO_DATA);
+ goto leave;
+ }
+
+ if (ctx->key_data)
+ {
+ if (opt.debug)
+ {
+ es_rewind (ctx->key_data);
+ log_debug ("Key: '");
+ log_printf ("\n");
+ while ((c = es_getc (ctx->key_data)) != EOF)
+ log_printf ("%c", c);
+ log_printf ("'\n");
+ }
+ if (result_cb)
+ {
+ es_rewind (ctx->key_data);
+ err = result_cb (cb_data, "application/pgp-keys", ctx->key_data);
+ if (err)
+ goto leave;
+ }
+ }
+ if (ctx->wkd_data)
+ {
+ if (opt.debug)
+ {
+ es_rewind (ctx->wkd_data);
+ log_debug ("WKD: '");
+ log_printf ("\n");
+ while ((c = es_getc (ctx->wkd_data)) != EOF)
+ log_printf ("%c", c);
+ log_printf ("'\n");
+ }
+ if (result_cb)
+ {
+ es_rewind (ctx->wkd_data);
+ err = result_cb (cb_data, "application/vnd.gnupg.wks", ctx->wkd_data);
+ if (err)
+ goto leave;
+ }
+ }
+
+
+ leave:
+ es_fclose (plaintext);
+ mime_parser_release (parser);
+ es_fclose (ctx->encrypted);
+ es_fclose (ctx->plaintext);
+ es_fclose (ctx->signeddata);
+ es_fclose (ctx->signature);
+ es_fclose (ctx->key_data);
+ es_fclose (ctx->wkd_data);
+ xfree (ctx);
+ return err;
+}