1
/* gpgkeys_kdns.c - Fetch a key via the GnuPG specific KDNS scheme.
2
* Copyright (C) 2008 Free Software Foundation, Inc.
4
* This file is part of GnuPG.
6
* GnuPG is free software; you can redistribute it and/or modify it
7
* 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.
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.
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/>.
32
# ifndef HAVE_ADNS_FREE
33
# define adns_free free
37
#define INCLUDED_BY_MAIN_MODULE 1
39
#include "keyserver.h"
43
#define PGM "gpgkeys_kdns"
45
/* getopt(3) requires declarion of some global variables. */
49
/* Convenience variables usually intialized withn std{in,out,err}. */
50
static FILE *input, *output, *console;
52
/* Standard keyserver module options. */
53
static struct ks_options *opt;
55
/* The flags we pass to adns_init: Do not allow any environment
56
variables and for now enable debugging. */
57
#define MY_ADNS_INITFLAGS (adns_if_noenv)
60
/* ADNS has no support for CERT yes. */
61
#define my_adns_r_cert 37
63
/* The root of the KDNS tree. */
64
static const char *kdns_root;
66
/* The replacement string for the at sign. */
67
static const char *kdns_at_repl;
69
/* Flag indicating that a TCP conenction should be used. */
70
static int kdns_usevc;
74
/* Retrieve one key. ADDRESS should be an RFC-2822 addr-spec. */
76
get_key (adns_state adns_ctx, char *address)
78
int ret = KEYSERVER_INTERNAL_ERROR;
81
adns_answer *answer = NULL;
82
const unsigned char *data;
84
struct b64state b64state;
87
domain = strrchr (address, '@');
88
if (!domain || domain == address || !domain[1])
90
fprintf (console, PGM": invalid mail address `%s'\n", address);
91
ret = KEYSERVER_GENERAL_ERROR;
94
name = xtrymalloc (strlen (address) + strlen (kdns_at_repl)
95
+ 1 + strlen (kdns_root) + 1);
98
memcpy (name, address, domain - address);
99
p = stpcpy (name + (domain-address), ".");
101
p = stpcpy (stpcpy (p, kdns_at_repl), ".");
102
p = stpcpy (p, domain+1);
104
strcpy (stpcpy (p, "."), kdns_root);
106
fprintf (output,"NAME %s BEGIN\n", address);
107
if (opt->verbose > 2)
108
fprintf(console, PGM": looking up `%s'\n", name);
110
if ( adns_synchronous (adns_ctx, name, (adns_r_unknown | my_adns_r_cert),
111
adns_qf_quoteok_query|(kdns_usevc?adns_qf_usevc:0),
114
fprintf (console, PGM": DNS query failed: %s\n", strerror (errno));
115
ret = KEYSERVER_KEY_NOT_FOUND;
118
if (answer->status != adns_s_ok)
120
fprintf (console, PGM": DNS query returned: %s (%s)\n",
121
adns_strerror (answer->status),
122
adns_errabbrev (answer->status));
123
ret = KEYSERVER_KEY_NOT_FOUND;
126
datalen = answer->rrs.byteblock->len;
127
data = answer->rrs.byteblock->data;
129
if ( opt->debug > 1 )
133
fprintf (console, "got %d bytes of data:", datalen);
134
for (i=0; i < datalen; i++)
137
fprintf (console, "\n%08x ", i);
138
fprintf (console, "%02x", data[i]);
140
putc ('\n', console);
144
fprintf (console, PGM": error: truncated CERT record\n");
145
ret = KEYSERVER_KEY_NOT_FOUND;
149
switch ( ((data[0]<<8)|data[1]) )
151
case 3: /* CERT type is PGP. */
152
/* (key tag and algorithm fields are ignored for this CERT type). */
157
/* Gpg checks for a minium length of 11, thus we do the same. */
158
fprintf (console, PGM": error: OpenPGP data to short\n");
159
ret = KEYSERVER_KEY_NOT_FOUND;
162
if (b64enc_start (&b64state, output, "PGP PUBLIC KEY BLOCK")
163
|| b64enc_write (&b64state, data, datalen)
164
|| b64enc_finish (&b64state))
165
goto leave; /* Oops, base64 encoder failed. */
169
fprintf (console, PGM": CERT type %d ignored\n", (data[0] <<8|data[1]));
170
ret = KEYSERVER_KEY_NOT_FOUND;
174
ret = 0; /* All fine. */
178
fprintf (output, "\nNAME %s FAILED %d\n", address, ret);
180
fprintf (output, "\nNAME %s END\n", address);
187
/* Print some help. */
191
fputs (PGM" (GnuPG) " VERSION"\n\n", fp);
194
" -o\toutput to this file\n"
196
fputs ("This keyserver helper accepts URLs of the form:\n"
197
" kdns://[NAMESERVER]/[ROOT][?at=STRING]\n"
199
" NAMESERVER used for queries (default: system standard)\n"
200
" ROOT a DNS name appended to the query (default: none)\n"
201
" STRING a string to replace the '@' (default: \".\")\n"
202
"If a long answer is expected add the parameter \"usevc=1\".\n"
204
fputs ("Example: A query for \"hacker@gnupg.org\" with\n"
205
" kdns://10.0.0.1/example.net?at=_key&usevc=1\n"
206
"setup as --auto-key-lookup does a CERT record query\n"
207
"with type PGP on the nameserver 10.0.0.1 for\n"
208
" hacker._key_.gnupg.org.example.net\n"
214
main (int argc, char *argv[])
217
int ret = KEYSERVER_INTERNAL_ERROR;
219
struct keylist *keylist = NULL;
220
struct keylist **keylist_tail = &keylist;
221
struct keylist *akey;
223
adns_state adns_ctx = NULL;
224
adns_initflags my_adns_initflags = MY_ADNS_INITFLAGS;
227
/* The defaults for the KDNS name mangling. */
233
/* Kludge to implement standard GNU options. */
234
if (argc > 1 && !strcmp (argv[1], "--version"))
236
fputs (PGM" (GnuPG) " VERSION"\n", stdout);
239
else if (argc > 1 && !strcmp (argv[1], "--help"))
245
while ( (arg = getopt (argc, argv, "hVo:")) != -1 )
250
printf ("%d\n%s\n", KEYSERVER_PROTO_VERSION, VERSION);
254
output = fopen (optarg,"w");
257
fprintf (console, PGM": cannot open output file `%s': %s\n",
258
optarg, strerror(errno) );
259
return KEYSERVER_INTERNAL_ERROR;
272
input = fopen (argv[optind], "r");
275
fprintf (console, PGM": cannot open input file `%s': %s\n",
276
argv[optind], strerror(errno) );
277
return KEYSERVER_INTERNAL_ERROR;
287
opt = init_ks_options();
289
return KEYSERVER_NO_MEMORY;
291
/* Get the command and info block */
292
while ( fgets(line,MAX_LINE,input) )
299
err = parse_ks_options (line, opt);
309
if (opt->timeout && register_timeout() == -1 )
311
fprintf (console, PGM": unable to register timeout handler\n");
312
return KEYSERVER_INTERNAL_ERROR;
317
fprintf (console, PGM": HOST=%s\n", opt->host? opt->host:"(none)");
318
fprintf (console, PGM": PATH=%s\n", opt->path? opt->path:"(none)");
320
if (opt->path && *opt->path == '/')
324
kdns_root = opt->path+1;
325
p = strchr (opt->path+1, '?');
331
pend = strchr (p, '&');
334
if (!strncmp (p, "at=", 3))
336
else if (!strncmp (p, "usevc=", 6))
337
kdns_usevc = !!atoi (p+6);
342
if (strchr (kdns_root, '/'))
344
fprintf (console, PGM": invalid character in KDNS root\n");
345
return KEYSERVER_GENERAL_ERROR;
347
if (!strcmp (kdns_at_repl, "."))
352
fprintf (console, PGM": kdns_root=%s\n", kdns_root);
353
fprintf (console, PGM": kdns_at=%s\n", kdns_at_repl);
354
fprintf (console, PGM": kdns_usevc=%d\n", kdns_usevc);
358
my_adns_initflags |= adns_if_debug;
363
snprintf (cfgtext, sizeof cfgtext, "nameserver %s\n", opt->host);
364
tmprc = adns_init_strcfg (&adns_ctx, my_adns_initflags, console,cfgtext);
367
tmprc = adns_init (&adns_ctx, my_adns_initflags, console);
370
fprintf (console, PGM": error initializing ADNS: %s\n",
375
if (opt->action == KS_GETNAME)
377
while ( fgets (line,MAX_LINE,input) )
379
if (line[0]=='\n' || !line[0] )
381
line[strlen(line)-1] = 0; /* Trim the trailing LF. */
383
akey = xtrymalloc (sizeof *akey);
387
PGM": out of memory while building key list\n");
388
ret = KEYSERVER_NO_MEMORY;
391
assert (sizeof (akey->str) > strlen(line));
392
strcpy (akey->str, line);
394
*keylist_tail = akey;
395
keylist_tail = &akey->next;
401
PGM": this keyserver type only supports "
402
"key retrieval by name\n");
406
/* Send the response */
407
fprintf (output, "VERSION %d\n", KEYSERVER_PROTO_VERSION);
408
fprintf (output, "PROGRAM %s\n\n", VERSION);
410
if (opt->verbose > 1)
413
fprintf (console, "User:\t\t%s\n", opt->opaque);
414
fprintf (console, "Command:\tGET\n");
417
for (akey = keylist; akey; akey = akey->next)
419
set_timeout (opt->timeout);
420
if ( get_key (adns_ctx, akey->str) )
429
adns_finish (adns_ctx);
432
akey = keylist->next;
438
if (output != stdout)
442
free_ks_options (opt);