summaryrefslogtreecommitdiffstats
path: root/keyserver/gpgkeys_kdns.c
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2008-04-07 21:31:12 +0200
committerWerner Koch <wk@gnupg.org>2008-04-07 21:31:12 +0200
commit86f35a55d01f1ef7aec2b303a722ff5733148aa4 (patch)
tree99cb8163a8db0deba06608fec178ea9ad602e5c0 /keyserver/gpgkeys_kdns.c
parentFixed last yat2m change. (diff)
downloadgnupg2-86f35a55d01f1ef7aec2b303a722ff5733148aa4.tar.xz
gnupg2-86f35a55d01f1ef7aec2b303a722ff5733148aa4.zip
Minor cleanups.
Implemented key helper kdns
Diffstat (limited to 'keyserver/gpgkeys_kdns.c')
-rw-r--r--keyserver/gpgkeys_kdns.c435
1 files changed, 435 insertions, 0 deletions
diff --git a/keyserver/gpgkeys_kdns.c b/keyserver/gpgkeys_kdns.c
new file mode 100644
index 000000000..522f1c823
--- /dev/null
+++ b/keyserver/gpgkeys_kdns.c
@@ -0,0 +1,435 @@
+/* gpgkeys_kdns.c - Fetch a key via the GnuPG specific KDNS scheme.
+ * Copyright (C) 2008 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 <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#ifdef HAVE_GETOPT_H
+# include <getopt.h>
+#endif
+#include <assert.h>
+#ifdef HAVE_ADNS_H
+# include <adns.h>
+#endif
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#include "util.h"
+#include "keyserver.h"
+#include "ksutil.h"
+
+/* Our own name. */
+#define PGM "gpgkeys_kdns"
+
+/* getopt(3) requires declarion of some global variables. */
+extern char *optarg;
+extern int optind;
+
+/* Convenience variables usually intialized withn std{in,out,err}. */
+static FILE *input, *output, *console;
+
+/* Standard keyserver module options. */
+static struct ks_options *opt;
+
+/* The flags we pass to adns_init: Do not allow any environment
+ variables and for now enable debugging. */
+#define MY_ADNS_INITFLAGS (adns_if_noenv)
+
+
+/* ADNS has no support for CERT yes. */
+#define my_adns_r_cert 37
+
+/* The root of the KDNS tree. */
+static const char *kdns_root;
+
+/* The replacement string for the at sign. */
+static const char *kdns_at_repl;
+
+
+
+
+
+/* Retrieve one key. ADDRESS should be an RFC-2822 addr-spec. */
+static int
+get_key (adns_state adns_ctx, char *address)
+{
+ int ret = KEYSERVER_INTERNAL_ERROR;
+ const char *domain;
+ char *name = NULL;
+ adns_answer *answer = NULL;
+ const unsigned char *data;
+ int datalen;
+ struct b64state b64state;
+ char *p;
+
+ domain = strrchr (address, '@');
+ if (!domain || domain == address || !domain[1])
+ {
+ fprintf (console, PGM": invalid mail address `%s'\n", address);
+ ret = KEYSERVER_GENERAL_ERROR;
+ goto leave;
+ }
+ name = xtrymalloc (strlen (address) + strlen (kdns_at_repl)
+ + 1 + strlen (kdns_root) + 1);
+ if (!name)
+ goto leave;
+ memcpy (name, address, domain - address);
+ p = stpcpy (name + (domain-address), ".");
+ if (*kdns_at_repl)
+ p = stpcpy (stpcpy (p, kdns_at_repl), ".");
+ p = stpcpy (p, domain+1);
+ if (*kdns_root)
+ strcpy (stpcpy (p, "."), kdns_root);
+
+ fprintf (output,"NAME %s BEGIN\n", address);
+ if (opt->verbose > 2)
+ fprintf(console, PGM": looking up `%s'\n", name);
+
+
+ if ( adns_synchronous (adns_ctx, name, (adns_r_unknown | my_adns_r_cert),
+ adns_qf_quoteok_query,
+ &answer) )
+ {
+ fprintf (console, PGM": DNS query failed: %s\n", strerror (errno));
+ ret = KEYSERVER_KEY_NOT_FOUND;
+ goto leave;
+ }
+ if (answer->status != adns_s_ok)
+ {
+ fprintf (console, PGM": DNS query returned: %s (%s)\n",
+ adns_strerror (answer->status),
+ adns_errabbrev (answer->status));
+ ret = KEYSERVER_KEY_NOT_FOUND;
+ goto leave;
+ }
+ datalen = answer->rrs.byteblock->len;
+ data = answer->rrs.byteblock->data;
+
+ if ( opt->debug > 1 )
+ {
+ int i;
+
+ fprintf (console, "got %d bytes of data:", datalen);
+ for (i=0; i < datalen; i++)
+ {
+ if (!(i % 32))
+ fprintf (console, "\n%08x ", i);
+ fprintf (console, "%02x", data[i]);
+ }
+ putc ('\n', console);
+ }
+ if ( datalen < 5 )
+ {
+ fprintf (console, PGM": error: truncated CERT record\n");
+ ret = KEYSERVER_KEY_NOT_FOUND;
+ goto leave;
+ }
+
+ switch ( ((data[0]<<8)|data[1]) )
+ {
+ case 3: /* CERT type is PGP. */
+ /* (key tag and algorithm fields are ignored for this CERT type). */
+ data += 5;
+ datalen -= 5;
+ if ( datalen < 11 )
+ {
+ /* Gpg checks for a minium length of 11, thus we do the same. */
+ fprintf (console, PGM": error: OpenPGP data to short\n");
+ ret = KEYSERVER_KEY_NOT_FOUND;
+ goto leave;
+ }
+ if (b64enc_start (&b64state, output, "PGP PUBLIC KEY BLOCK")
+ || b64enc_write (&b64state, data, datalen)
+ || b64enc_finish (&b64state))
+ goto leave; /* Oops, base64 encoder failed. */
+ break;
+
+ default:
+ fprintf (console, PGM": CERT type %d ignored\n", (data[0] <<8|data[1]));
+ ret = KEYSERVER_KEY_NOT_FOUND;
+ goto leave;
+ }
+
+ ret = 0; /* All fine. */
+
+ leave:
+ if (ret)
+ fprintf (output, "\nNAME %s FAILED %d\n", address, ret);
+ else
+ fprintf (output, "\nNAME %s END\n", address);
+ free (answer); /* (Right, this is free and not xfree.) */
+ xfree (name);
+ return ret;
+}
+
+
+/* Print some help. */
+static void
+show_help (FILE *fp)
+{
+ fputs (PGM" (GnuPG) " VERSION"\n\n", fp);
+ fputs (" -h\thelp\n"
+ " -V\tversion\n"
+ " -o\toutput to this file\n"
+ "\n", fp);
+ fputs ("This keyserver helper accepts URLs of the form:\n"
+ " kdns://[NAMESERVER]/[ROOT][?at=[STRING]]\n"
+ "with\n"
+ " NAMESERVER used for queries (default: system standard)\n"
+ " ROOT a DNS name appended to the query (default: none)\n"
+ " STRING A string to replace the '@' (default: \".\")\n"
+ "\n", fp);
+ fputs ("Example: A query for \"hacker@gnupg.org\" with\n"
+ " kdns://10.0.0.1/example.net?at=_key?\n"
+ "setup as --auto-key-lookup does a CERT record query\n"
+ "with type PGP on the nameserver 10.0.0.1 for\n"
+ " hacker._key_.gnupg.org.example.net\n"
+ "\n", fp);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int arg;
+ int ret = KEYSERVER_INTERNAL_ERROR;
+ char line[MAX_LINE];
+ struct keylist *keylist = NULL;
+ struct keylist **keylist_tail = &keylist;
+ struct keylist *akey;
+ int failed = 0;
+ adns_state adns_ctx = NULL;
+ adns_initflags my_adns_initflags = MY_ADNS_INITFLAGS;
+ int tmprc;
+
+ /* The defaults for the KDNS name mangling. */
+ kdns_root = "";
+ kdns_at_repl = "";
+
+ console = stderr;
+
+ /* Kludge to implement standard GNU options. */
+ if (argc > 1 && !strcmp (argv[1], "--version"))
+ {
+ fputs (PGM" (GnuPG) " VERSION"\n", stdout);
+ return 0;
+ }
+ else if (argc > 1 && !strcmp (argv[1], "--help"))
+ {
+ show_help (stdout);
+ return 0;
+ }
+
+ while ( (arg = getopt (argc, argv, "hVo:")) != -1 )
+ {
+ switch(arg)
+ {
+ case 'V':
+ printf ("%d\n%s\n", KEYSERVER_PROTO_VERSION, VERSION);
+ return KEYSERVER_OK;
+
+ case 'o':
+ output = fopen (optarg,"w");
+ if (!output)
+ {
+ fprintf (console, PGM": cannot open output file `%s': %s\n",
+ optarg, strerror(errno) );
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+ break;
+
+ case 'h':
+ default:
+ show_help (console);
+ return KEYSERVER_OK;
+ }
+ }
+
+ if (argc > optind)
+ {
+ input = fopen (argv[optind], "r");
+ if (!input)
+ {
+ fprintf (console, PGM": cannot open input file `%s': %s\n",
+ argv[optind], strerror(errno) );
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+ }
+
+ if (!input)
+ input = stdin;
+
+ if (!output)
+ output = stdout;
+
+ opt = init_ks_options();
+ if(!opt)
+ return KEYSERVER_NO_MEMORY;
+
+ /* Get the command and info block */
+ while ( fgets(line,MAX_LINE,input) )
+ {
+ int err;
+
+ if(line[0]=='\n')
+ break;
+
+ err = parse_ks_options (line, opt);
+ if (err > 0)
+ {
+ ret = err;
+ goto leave;
+ }
+ else if (!err)
+ continue;
+ }
+
+ if (opt->timeout && register_timeout() == -1 )
+ {
+ fprintf (console, PGM": unable to register timeout handler\n");
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+
+ fprintf (console, PGM": HOST=%s\n", opt->host? opt->host:"(none)");
+ fprintf (console, PGM": PATH=%s\n", opt->path? opt->path:"(none)");
+ if (opt->path && *opt->path == '/')
+ {
+ char *p, *pend;
+
+ kdns_root = opt->path+1;
+ p = strchr (opt->path+1, '?');
+ if (p)
+ {
+ *p++ = 0;
+ do
+ {
+ pend = strchr (p, '&');
+ if (pend)
+ *pend++ = 0;
+ if (!strncmp (p, "at=", 3))
+ {
+ /* Found. */
+ kdns_at_repl = p+3;
+ break;
+ }
+ }
+ while ((p = pend));
+ }
+ }
+ if (strchr (kdns_root, '/'))
+ {
+ fprintf (console, PGM": invalid character in KDNS root\n");
+ return KEYSERVER_GENERAL_ERROR;
+ }
+ if (!strcmp (kdns_at_repl, "."))
+ kdns_at_repl = "";
+ fprintf (console, PGM": kdns_root=%s\n", kdns_root);
+ fprintf (console, PGM": kdns_at=%s\n", kdns_at_repl);
+
+
+ if (opt->debug)
+ my_adns_initflags |= adns_if_debug;
+ if (opt->host)
+ {
+ char cfgtext[200];
+
+ snprintf (cfgtext, sizeof cfgtext, "nameserver %s\n", opt->host);
+ tmprc = adns_init_strcfg (&adns_ctx, my_adns_initflags, console,cfgtext);
+ }
+ else
+ tmprc = adns_init (&adns_ctx, my_adns_initflags, console);
+ if (tmprc)
+ {
+ fprintf (console, PGM": error initializing ADNS: %s\n",
+ strerror (errno));
+ goto leave;
+ }
+
+ if (opt->action == KS_GETNAME)
+ {
+ while ( fgets (line,MAX_LINE,input) )
+ {
+ if (line[0]=='\n' || !line[0] )
+ break;
+ line[strlen(line)-1] = 0; /* Trim the trailing LF. */
+
+ akey = xtrymalloc (sizeof *akey);
+ if (!akey)
+ {
+ fprintf (console,
+ PGM": out of memory while building key list\n");
+ ret = KEYSERVER_NO_MEMORY;
+ goto leave;
+ }
+ assert (sizeof (akey->str) > strlen(line));
+ strcpy (akey->str, line);
+ akey->next = NULL;
+ *keylist_tail = akey;
+ keylist_tail = &akey->next;
+ }
+ }
+ else
+ {
+ fprintf (console,
+ PGM": this keyserver type only supports "
+ "key retrieval by name\n");
+ goto leave;
+ }
+
+ /* Send the response */
+ fprintf (output, "VERSION %d\n", KEYSERVER_PROTO_VERSION);
+ fprintf (output, "PROGRAM %s\n\n", VERSION);
+
+ if (opt->verbose > 1)
+ {
+ if (opt->opaque)
+ fprintf (console, "User:\t\t%s\n", opt->opaque);
+ fprintf (console, "Command:\tGET\n");
+ }
+
+ for (akey = keylist; akey; akey = akey->next)
+ {
+ set_timeout (opt->timeout);
+ if ( get_key (adns_ctx, akey->str) )
+ failed++;
+ }
+ if (!failed)
+ ret = KEYSERVER_OK;
+
+
+ leave:
+ if (adns_ctx)
+ adns_finish (adns_ctx);
+ while (keylist)
+ {
+ akey = keylist->next;
+ xfree (keylist);
+ keylist = akey;
+ }
+ if (input != stdin)
+ fclose (input);
+ if (output != stdout)
+ fclose (output);
+ kdns_root = "";
+ kdns_at_repl = ".";
+ free_ks_options (opt);
+ return ret;
+}