diff options
author | Werner Koch <wk@gnupg.org> | 2008-04-07 21:31:12 +0200 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2008-04-07 21:31:12 +0200 |
commit | 86f35a55d01f1ef7aec2b303a722ff5733148aa4 (patch) | |
tree | 99cb8163a8db0deba06608fec178ea9ad602e5c0 /keyserver/gpgkeys_kdns.c | |
parent | Fixed last yat2m change. (diff) | |
download | gnupg2-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.c | 435 |
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; +} |