Line data Source code
1 : /* ks-engine-hkp.c - HKP keyserver engine
2 : * Copyright (C) 2011, 2012 Free Software Foundation, Inc.
3 : * Copyright (C) 2011, 2012, 2014 Werner Koch
4 : *
5 : * This file is part of GnuPG.
6 : *
7 : * GnuPG is free software; you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License as published by
9 : * the Free Software Foundation; either version 3 of the License, or
10 : * (at your option) any later version.
11 : *
12 : * GnuPG is distributed in the hope that it will be useful,
13 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : * GNU General Public License for more details.
16 : *
17 : * You should have received a copy of the GNU General Public License
18 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <config.h>
22 :
23 : #include <stdio.h>
24 : #include <stdlib.h>
25 : #include <string.h>
26 : #include <assert.h>
27 : #ifdef HAVE_W32_SYSTEM
28 : # ifdef HAVE_WINSOCK2_H
29 : # include <winsock2.h>
30 : # endif
31 : # include <windows.h>
32 : #else /*!HAVE_W32_SYSTEM*/
33 : # include <sys/types.h>
34 : # include <sys/socket.h>
35 : # include <netdb.h>
36 : #endif /*!HAVE_W32_SYSTEM*/
37 :
38 : #include "dirmngr.h"
39 : #include "misc.h"
40 : #include "userids.h"
41 : #include "dns-stuff.h"
42 : #include "ks-engine.h"
43 :
44 : /* Substitutes for missing Mingw macro. The EAI_SYSTEM mechanism
45 : seems not to be available (probably because there is only one set
46 : of error codes anyway). For now we use WSAEINVAL. */
47 : #ifndef EAI_OVERFLOW
48 : # define EAI_OVERFLOW EAI_FAIL
49 : #endif
50 : #ifdef HAVE_W32_SYSTEM
51 : # ifndef EAI_SYSTEM
52 : # define EAI_SYSTEM WSAEINVAL
53 : # endif
54 : #endif
55 :
56 :
57 : /* Number of seconds after a host is marked as resurrected. */
58 : #define RESURRECT_INTERVAL (3600*3) /* 3 hours */
59 :
60 : /* To match the behaviour of our old gpgkeys helper code we escape
61 : more characters than actually needed. */
62 : #define EXTRA_ESCAPE_CHARS "@!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
63 :
64 : /* How many redirections do we allow. */
65 : #define MAX_REDIRECTS 2
66 :
67 : /* Number of retries done for a dead host etc. */
68 : #define SEND_REQUEST_RETRIES 3
69 :
70 : /* Objects used to maintain information about hosts. */
71 : struct hostinfo_s;
72 : typedef struct hostinfo_s *hostinfo_t;
73 : struct hostinfo_s
74 : {
75 : time_t lastfail; /* Time we tried to connect and failed. */
76 : time_t lastused; /* Time of last use. */
77 : int *pool; /* A -1 terminated array with indices into
78 : HOSTTABLE or NULL if NAME is not a pool
79 : name. */
80 : int poolidx; /* Index into POOL with the used host. -1 if not set. */
81 : unsigned int v4:1; /* Host supports AF_INET. */
82 : unsigned int v6:1; /* Host supports AF_INET6. */
83 : unsigned int onion:1;/* NAME is an onion (Tor HS) address. */
84 : unsigned int dead:1; /* Host is currently unresponsive. */
85 : time_t died_at; /* The time the host was marked dead. If this is
86 : 0 the host has been manually marked dead. */
87 : char *cname; /* Canonical name of the host. Only set if this
88 : is a pool. */
89 : char *v4addr; /* A string with the v4 IP address of the host.
90 : NULL if NAME has a numeric IP address or no v4
91 : address is available. */
92 : char *v6addr; /* A string with the v6 IP address of the host.
93 : NULL if NAME has a numeric IP address or no v6
94 : address is available. */
95 : unsigned short port; /* The port used by the host, 0 if unknown. */
96 : char name[1]; /* The hostname. */
97 : };
98 :
99 :
100 : /* An array of hostinfo_t for all hosts requested by the caller or
101 : resolved from a pool name and its allocated size.*/
102 : static hostinfo_t *hosttable;
103 : static int hosttable_size;
104 :
105 : /* The number of host slots we initially allocate for HOSTTABLE. */
106 : #define INITIAL_HOSTTABLE_SIZE 10
107 :
108 :
109 : /* Create a new hostinfo object, fill in NAME and put it into
110 : HOSTTABLE. Return the index into hosttable on success or -1 on
111 : error. */
112 : static int
113 0 : create_new_hostinfo (const char *name)
114 : {
115 : hostinfo_t hi, *newtable;
116 : int newsize;
117 : int idx, rc;
118 :
119 0 : hi = xtrymalloc (sizeof *hi + strlen (name));
120 0 : if (!hi)
121 0 : return -1;
122 0 : strcpy (hi->name, name);
123 0 : hi->pool = NULL;
124 0 : hi->poolidx = -1;
125 0 : hi->lastused = (time_t)(-1);
126 0 : hi->lastfail = (time_t)(-1);
127 0 : hi->v4 = 0;
128 0 : hi->v6 = 0;
129 0 : hi->onion = 0;
130 0 : hi->dead = 0;
131 0 : hi->died_at = 0;
132 0 : hi->cname = NULL;
133 0 : hi->v4addr = NULL;
134 0 : hi->v6addr = NULL;
135 0 : hi->port = 0;
136 :
137 : /* Add it to the hosttable. */
138 0 : for (idx=0; idx < hosttable_size; idx++)
139 0 : if (!hosttable[idx])
140 : {
141 0 : hosttable[idx] = hi;
142 0 : return idx;
143 : }
144 : /* Need to extend the hosttable. */
145 0 : newsize = hosttable_size + INITIAL_HOSTTABLE_SIZE;
146 0 : newtable = xtryrealloc (hosttable, newsize * sizeof *hosttable);
147 0 : if (!newtable)
148 : {
149 0 : xfree (hi);
150 0 : return -1;
151 : }
152 0 : hosttable = newtable;
153 0 : idx = hosttable_size;
154 0 : hosttable_size = newsize;
155 0 : rc = idx;
156 0 : hosttable[idx++] = hi;
157 0 : while (idx < hosttable_size)
158 0 : hosttable[idx++] = NULL;
159 :
160 0 : return rc;
161 : }
162 :
163 :
164 : /* Find the host NAME in our table. Return the index into the
165 : hosttable or -1 if not found. */
166 : static int
167 0 : find_hostinfo (const char *name)
168 : {
169 : int idx;
170 :
171 0 : for (idx=0; idx < hosttable_size; idx++)
172 0 : if (hosttable[idx] && !ascii_strcasecmp (hosttable[idx]->name, name))
173 0 : return idx;
174 0 : return -1;
175 : }
176 :
177 :
178 : static int
179 0 : sort_hostpool (const void *xa, const void *xb)
180 : {
181 0 : int a = *(int *)xa;
182 0 : int b = *(int *)xb;
183 :
184 0 : assert (a >= 0 && a < hosttable_size);
185 0 : assert (b >= 0 && b < hosttable_size);
186 0 : assert (hosttable[a]);
187 0 : assert (hosttable[b]);
188 :
189 0 : return ascii_strcasecmp (hosttable[a]->name, hosttable[b]->name);
190 : }
191 :
192 :
193 : /* Return true if the host with the hosttable index TBLIDX is in POOL. */
194 : static int
195 0 : host_in_pool_p (int *pool, int tblidx)
196 : {
197 : int i, pidx;
198 :
199 0 : for (i=0; (pidx = pool[i]) != -1; i++)
200 0 : if (pidx == tblidx && hosttable[pidx])
201 0 : return 1;
202 0 : return 0;
203 : }
204 :
205 :
206 : /* Select a random host. Consult TABLE which indices into the global
207 : hosttable. Returns index into TABLE or -1 if no host could be
208 : selected. */
209 : static int
210 0 : select_random_host (int *table)
211 : {
212 : int *tbl;
213 : size_t tblsize;
214 : int pidx, idx;
215 :
216 : /* We create a new table so that we randomly select only from
217 : currently alive hosts. */
218 0 : for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
219 0 : if (hosttable[pidx] && !hosttable[pidx]->dead)
220 0 : tblsize++;
221 0 : if (!tblsize)
222 0 : return -1; /* No hosts. */
223 :
224 0 : tbl = xtrymalloc (tblsize * sizeof *tbl);
225 0 : if (!tbl)
226 0 : return -1;
227 0 : for (idx=0, tblsize=0; (pidx = table[idx]) != -1; idx++)
228 0 : if (hosttable[pidx] && !hosttable[pidx]->dead)
229 0 : tbl[tblsize++] = pidx;
230 :
231 0 : if (tblsize == 1) /* Save a get_uint_nonce. */
232 0 : pidx = tbl[0];
233 : else
234 0 : pidx = tbl[get_uint_nonce () % tblsize];
235 :
236 0 : xfree (tbl);
237 0 : return pidx;
238 : }
239 :
240 :
241 : /* Figure out if a set of DNS records looks like a pool. */
242 : static int
243 0 : arecords_is_pool (dns_addrinfo_t aibuf)
244 : {
245 : dns_addrinfo_t ai;
246 : int n_v6, n_v4;
247 :
248 0 : n_v6 = n_v4 = 0;
249 0 : for (ai = aibuf; ai; ai = ai->next)
250 : {
251 0 : if (ai->family == AF_INET6)
252 0 : n_v6++;
253 0 : else if (ai->family == AF_INET)
254 0 : n_v4++;
255 : }
256 :
257 0 : return n_v6 > 1 || n_v4 > 1;
258 : }
259 :
260 :
261 : /* Add the host AI under the NAME into the HOSTTABLE. If PORT is not
262 : zero, it specifies which port to use to talk to the host. If NAME
263 : specifies a pool (as indicated by IS_POOL), update the given
264 : reference table accordingly. */
265 : static void
266 0 : add_host (const char *name, int is_pool,
267 : const dns_addrinfo_t ai, unsigned short port,
268 : int *reftbl, size_t reftblsize, int *refidx)
269 : {
270 : gpg_error_t tmperr;
271 : char *tmphost;
272 : int idx, tmpidx;
273 0 : int is_numeric = 0;
274 : int i;
275 :
276 0 : idx = find_hostinfo (name);
277 :
278 0 : if (!is_pool && !is_ip_address (name))
279 : {
280 : /* This is a hostname but not a pool. Use the name
281 : as given without going through resolve_dns_addr. */
282 0 : tmphost = xtrystrdup (name);
283 0 : if (!tmphost)
284 0 : tmperr = gpg_error_from_syserror ();
285 : else
286 0 : tmperr = 0;
287 : }
288 : else
289 : {
290 0 : tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
291 : DNS_WITHBRACKET, &tmphost);
292 0 : if (tmphost && is_ip_address (tmphost))
293 0 : is_numeric = 1;
294 : }
295 :
296 0 : if (tmperr)
297 : {
298 0 : log_info ("resolve_dns_addr failed while checking '%s': %s\n",
299 : name, gpg_strerror (tmperr));
300 : }
301 0 : else if ((*refidx) + 1 >= reftblsize)
302 : {
303 0 : log_error ("resolve_dns_addr for '%s': '%s'"
304 : " [index table full - ignored]\n", name, tmphost);
305 : }
306 : else
307 : {
308 0 : if (!is_pool && is_ip_address (name))
309 : /* Update the original entry. */
310 0 : tmpidx = idx;
311 : else
312 0 : tmpidx = find_hostinfo (tmphost);
313 0 : log_info ("resolve_dns_addr for '%s': '%s'%s\n",
314 : name, tmphost,
315 : tmpidx == -1? "" : " [already known]");
316 :
317 0 : if (tmpidx == -1) /* Create a new entry. */
318 0 : tmpidx = create_new_hostinfo (tmphost);
319 :
320 0 : if (tmpidx == -1)
321 : {
322 0 : log_error ("map_host for '%s' problem: %s - '%s'"
323 : " [ignored]\n",
324 0 : name, strerror (errno), tmphost);
325 : }
326 : else /* Set or update the entry. */
327 : {
328 0 : char *ipaddr = NULL;
329 :
330 0 : if (port)
331 0 : hosttable[tmpidx]->port = port;
332 :
333 0 : if (!is_numeric)
334 : {
335 0 : xfree (tmphost);
336 0 : tmperr = resolve_dns_addr (ai->addr, ai->addrlen,
337 : (DNS_NUMERICHOST
338 : | DNS_WITHBRACKET),
339 : &tmphost);
340 0 : if (tmperr)
341 0 : log_info ("resolve_dns_addr failed: %s\n",
342 : gpg_strerror (tmperr));
343 : else
344 : {
345 0 : ipaddr = tmphost;
346 0 : tmphost = NULL;
347 : }
348 : }
349 :
350 0 : if (ai->family == AF_INET6)
351 : {
352 0 : hosttable[tmpidx]->v6 = 1;
353 0 : xfree (hosttable[tmpidx]->v6addr);
354 0 : hosttable[tmpidx]->v6addr = ipaddr;
355 : }
356 0 : else if (ai->family == AF_INET)
357 : {
358 0 : hosttable[tmpidx]->v4 = 1;
359 0 : xfree (hosttable[tmpidx]->v4addr);
360 0 : hosttable[tmpidx]->v4addr = ipaddr;
361 : }
362 : else
363 0 : BUG ();
364 :
365 0 : for (i=0; i < *refidx; i++)
366 0 : if (reftbl[i] == tmpidx)
367 0 : break;
368 0 : if (!(i < *refidx) && tmpidx != idx)
369 0 : reftbl[(*refidx)++] = tmpidx;
370 : }
371 : }
372 0 : xfree (tmphost);
373 0 : }
374 :
375 :
376 : /* Map the host name NAME to the actual to be used host name. This
377 : allows us to manage round robin DNS names. We use our own strategy
378 : to choose one of the hosts. For example we skip those hosts which
379 : failed for some time and we stick to one host for a time
380 : independent of DNS retry times. If FORCE_RESELECT is true a new
381 : host is always selected. The selected host is stored as a malloced
382 : string at R_HOST; on error NULL is stored. If we know the port
383 : used by the selected host, a string representation is written to
384 : R_PORTSTR, otherwise it is left untouched. If R_HTTPFLAGS is not
385 : NULL it will receive flags which are to be passed to http_open. If
386 : R_POOLNAME is not NULL a malloced name of the pool is stored or
387 : NULL if it is not a pool. */
388 : static gpg_error_t
389 0 : map_host (ctrl_t ctrl, const char *name, int force_reselect,
390 : char **r_host, char *r_portstr,
391 : unsigned int *r_httpflags, char **r_poolname)
392 : {
393 0 : gpg_error_t err = 0;
394 : hostinfo_t hi;
395 : int idx;
396 :
397 0 : *r_host = NULL;
398 0 : if (r_httpflags)
399 0 : *r_httpflags = 0;
400 0 : if (r_poolname)
401 0 : *r_poolname = NULL;
402 :
403 : /* No hostname means localhost. */
404 0 : if (!name || !*name)
405 : {
406 0 : *r_host = xtrystrdup ("localhost");
407 0 : return *r_host? 0 : gpg_error_from_syserror ();
408 : }
409 :
410 : /* See whether the host is in our table. */
411 0 : idx = find_hostinfo (name);
412 0 : if (idx == -1 && is_onion_address (name))
413 : {
414 0 : idx = create_new_hostinfo (name);
415 0 : if (idx == -1)
416 0 : return gpg_error_from_syserror ();
417 0 : hi = hosttable[idx];
418 0 : hi->onion = 1;
419 : }
420 0 : else if (idx == -1)
421 : {
422 : /* We never saw this host. Allocate a new entry. */
423 : dns_addrinfo_t aibuf, ai;
424 : int *reftbl;
425 : size_t reftblsize;
426 : int refidx;
427 0 : int is_pool = 0;
428 : char *cname;
429 : #ifdef USE_DNS_SRV
430 : char *srvrecord;
431 : struct srventry *srvs;
432 : int srvscount;
433 : #endif /* USE_DNS_SRV */
434 :
435 0 : reftblsize = 100;
436 0 : reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
437 0 : if (!reftbl)
438 0 : return gpg_error_from_syserror ();
439 0 : refidx = 0;
440 :
441 0 : idx = create_new_hostinfo (name);
442 0 : if (idx == -1)
443 : {
444 0 : err = gpg_error_from_syserror ();
445 0 : xfree (reftbl);
446 0 : return err;
447 : }
448 0 : hi = hosttable[idx];
449 :
450 : #ifdef USE_DNS_SRV
451 : /* Check for SRV records. */
452 0 : srvrecord = xtryasprintf ("_hkp._tcp.%s", name);
453 0 : if (srvrecord == NULL)
454 : {
455 0 : err = gpg_error_from_syserror ();
456 0 : xfree (reftbl);
457 0 : return err;
458 : }
459 :
460 0 : srvscount = getsrv (srvrecord, &srvs);
461 0 : xfree (srvrecord);
462 0 : if (srvscount < 0)
463 : {
464 0 : err = gpg_error_from_syserror ();
465 0 : xfree (reftbl);
466 0 : return err;
467 : }
468 :
469 0 : if (srvscount > 0)
470 : {
471 : int i;
472 0 : is_pool = srvscount > 1;
473 :
474 0 : for (i = 0; i < srvscount; i++)
475 : {
476 0 : err = resolve_dns_name (srvs[i].target, 0,
477 : AF_UNSPEC, SOCK_STREAM,
478 : &ai, &cname);
479 0 : if (err)
480 0 : continue;
481 0 : dirmngr_tick (ctrl);
482 0 : add_host (name, is_pool, ai, srvs[i].port,
483 : reftbl, reftblsize, &refidx);
484 : }
485 :
486 0 : xfree (srvs);
487 : }
488 : #endif /* USE_DNS_SRV */
489 :
490 : /* Find all A records for this entry and put them into the pool
491 : list - if any. */
492 0 : err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname);
493 0 : if (err)
494 : {
495 0 : log_error ("resolving '%s' failed: %s\n", name, gpg_strerror (err));
496 0 : err = 0;
497 : }
498 : else
499 : {
500 : /* First figure out whether this is a pool. For a pool we
501 : use a different strategy than for a plain server: We use
502 : the canonical name of the pool as the virtual host along
503 : with the IP addresses. If it is not a pool, we use the
504 : specified name. */
505 0 : if (! is_pool)
506 0 : is_pool = arecords_is_pool (aibuf);
507 0 : if (is_pool && cname)
508 : {
509 0 : hi->cname = cname;
510 0 : cname = NULL;
511 : }
512 :
513 0 : for (ai = aibuf; ai; ai = ai->next)
514 : {
515 0 : if (ai->family != AF_INET && ai->family != AF_INET6)
516 0 : continue;
517 0 : dirmngr_tick (ctrl);
518 :
519 0 : add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx);
520 : }
521 : }
522 0 : reftbl[refidx] = -1;
523 0 : xfree (cname);
524 0 : free_dns_addrinfo (aibuf);
525 :
526 0 : if (refidx && is_pool)
527 : {
528 0 : assert (!hi->pool);
529 0 : hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl);
530 0 : if (!hi->pool)
531 : {
532 0 : err = gpg_error_from_syserror ();
533 0 : log_error ("shrinking index table in map_host failed: %s\n",
534 : gpg_strerror (err));
535 0 : xfree (reftbl);
536 0 : return err;
537 : }
538 0 : qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool);
539 : }
540 : else
541 0 : xfree (reftbl);
542 : }
543 :
544 0 : hi = hosttable[idx];
545 0 : if (hi->pool)
546 : {
547 : /* Deal with the pool name before selecting a host. */
548 0 : if (r_poolname)
549 : {
550 0 : *r_poolname = xtrystrdup (hi->cname? hi->cname : hi->name);
551 0 : if (!*r_poolname)
552 0 : return gpg_error_from_syserror ();
553 : }
554 :
555 : /* If the currently selected host is now marked dead, force a
556 : re-selection . */
557 0 : if (force_reselect)
558 0 : hi->poolidx = -1;
559 0 : else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
560 0 : && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
561 0 : hi->poolidx = -1;
562 :
563 : /* Select a host if needed. */
564 0 : if (hi->poolidx == -1)
565 : {
566 0 : hi->poolidx = select_random_host (hi->pool);
567 0 : if (hi->poolidx == -1)
568 : {
569 0 : log_error ("no alive host found in pool '%s'\n", name);
570 0 : if (r_poolname)
571 : {
572 0 : xfree (*r_poolname);
573 0 : *r_poolname = NULL;
574 : }
575 0 : return gpg_error (GPG_ERR_NO_KEYSERVER);
576 : }
577 : }
578 :
579 0 : assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
580 0 : hi = hosttable[hi->poolidx];
581 0 : assert (hi);
582 : }
583 :
584 0 : if (hi->dead)
585 : {
586 0 : log_error ("host '%s' marked as dead\n", hi->name);
587 0 : if (r_poolname)
588 : {
589 0 : xfree (*r_poolname);
590 0 : *r_poolname = NULL;
591 : }
592 0 : return gpg_error (GPG_ERR_NO_KEYSERVER);
593 : }
594 :
595 0 : if (r_httpflags)
596 : {
597 : /* If the hosttable does not indicate that a certain host
598 : supports IPv<N>, we explicit set the corresponding http
599 : flags. The reason for this is that a host might be listed in
600 : a pool as not v6 only but actually support v6 when later
601 : the name is resolved by our http layer. */
602 0 : if (!hi->v4)
603 0 : *r_httpflags |= HTTP_FLAG_IGNORE_IPv4;
604 0 : if (!hi->v6)
605 0 : *r_httpflags |= HTTP_FLAG_IGNORE_IPv6;
606 :
607 : /* Note that we do not set the HTTP_FLAG_FORCE_TOR for onion
608 : addresses because the http module detects this itself. This
609 : also allows us to use an onion address without Tor mode being
610 : enabled. */
611 : }
612 :
613 0 : *r_host = xtrystrdup (hi->name);
614 0 : if (!*r_host)
615 : {
616 0 : err = gpg_error_from_syserror ();
617 0 : if (r_poolname)
618 : {
619 0 : xfree (*r_poolname);
620 0 : *r_poolname = NULL;
621 : }
622 0 : return err;
623 : }
624 0 : if (hi->port)
625 0 : snprintf (r_portstr, 6 /* five digits and the sentinel */,
626 0 : "%hu", hi->port);
627 0 : return 0;
628 : }
629 :
630 :
631 : /* Mark the host NAME as dead. NAME may be given as an URL. Returns
632 : true if a host was really marked as dead or was already marked dead
633 : (e.g. by a concurrent session). */
634 : static int
635 0 : mark_host_dead (const char *name)
636 : {
637 : const char *host;
638 0 : char *host_buffer = NULL;
639 0 : parsed_uri_t parsed_uri = NULL;
640 0 : int done = 0;
641 :
642 0 : if (name && *name && !http_parse_uri (&parsed_uri, name, 1))
643 : {
644 0 : if (parsed_uri->v6lit)
645 : {
646 0 : host_buffer = strconcat ("[", parsed_uri->host, "]", NULL);
647 0 : if (!host_buffer)
648 0 : log_error ("out of core in mark_host_dead");
649 0 : host = host_buffer;
650 : }
651 : else
652 0 : host = parsed_uri->host;
653 : }
654 : else
655 0 : host = name;
656 :
657 0 : if (host && *host && strcmp (host, "localhost"))
658 : {
659 : hostinfo_t hi;
660 : int idx;
661 :
662 0 : idx = find_hostinfo (host);
663 0 : if (idx != -1)
664 : {
665 0 : hi = hosttable[idx];
666 0 : log_info ("marking host '%s' as dead%s\n",
667 0 : hi->name, hi->dead? " (again)":"");
668 0 : hi->dead = 1;
669 0 : hi->died_at = gnupg_get_time ();
670 0 : if (!hi->died_at)
671 0 : hi->died_at = 1;
672 0 : done = 1;
673 : }
674 : }
675 :
676 0 : http_release_parsed_uri (parsed_uri);
677 0 : xfree (host_buffer);
678 0 : return done;
679 : }
680 :
681 :
682 : /* Mark a host in the hosttable as dead or - if ALIVE is true - as
683 : alive. */
684 : gpg_error_t
685 0 : ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
686 : {
687 0 : gpg_error_t err = 0;
688 : hostinfo_t hi, hi2;
689 : int idx, idx2, idx3, n;
690 :
691 0 : if (!name || !*name || !strcmp (name, "localhost"))
692 0 : return 0;
693 :
694 0 : idx = find_hostinfo (name);
695 0 : if (idx == -1)
696 0 : return gpg_error (GPG_ERR_NOT_FOUND);
697 :
698 0 : hi = hosttable[idx];
699 0 : if (alive && hi->dead)
700 : {
701 0 : hi->dead = 0;
702 0 : err = ks_printf_help (ctrl, "marking '%s' as alive", name);
703 : }
704 0 : else if (!alive && !hi->dead)
705 : {
706 0 : hi->dead = 1;
707 0 : hi->died_at = 0; /* Manually set dead. */
708 0 : err = ks_printf_help (ctrl, "marking '%s' as dead", name);
709 : }
710 :
711 : /* If the host is a pool mark all member hosts. */
712 0 : if (!err && hi->pool)
713 : {
714 0 : for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++)
715 : {
716 0 : assert (n >= 0 && n < hosttable_size);
717 :
718 0 : if (!alive)
719 : {
720 : /* Do not mark a host from a pool dead if it is also a
721 : member in another pool. */
722 0 : for (idx3=0; idx3 < hosttable_size; idx3++)
723 : {
724 0 : if (hosttable[idx3]
725 0 : && hosttable[idx3]->pool
726 0 : && idx3 != idx
727 0 : && host_in_pool_p (hosttable[idx3]->pool, n))
728 0 : break;
729 : }
730 0 : if (idx3 < hosttable_size)
731 0 : continue; /* Host is also a member of another pool. */
732 : }
733 :
734 0 : hi2 = hosttable[n];
735 0 : if (!hi2)
736 : ;
737 0 : else if (alive && hi2->dead)
738 : {
739 0 : hi2->dead = 0;
740 0 : err = ks_printf_help (ctrl, "marking '%s' as alive",
741 0 : hi2->name);
742 : }
743 0 : else if (!alive && !hi2->dead)
744 : {
745 0 : hi2->dead = 1;
746 0 : hi2->died_at = 0; /* Manually set dead. */
747 0 : err = ks_printf_help (ctrl, "marking '%s' as dead",
748 0 : hi2->name);
749 : }
750 : }
751 : }
752 :
753 0 : return err;
754 : }
755 :
756 :
757 : /* Debug function to print the entire hosttable. */
758 : gpg_error_t
759 0 : ks_hkp_print_hosttable (ctrl_t ctrl)
760 : {
761 : gpg_error_t err;
762 : int idx, idx2;
763 : hostinfo_t hi;
764 : membuf_t mb;
765 : time_t curtime;
766 : char *p, *died;
767 : const char *diedstr;
768 :
769 0 : err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):");
770 0 : if (err)
771 0 : return err;
772 :
773 0 : curtime = gnupg_get_time ();
774 0 : for (idx=0; idx < hosttable_size; idx++)
775 0 : if ((hi=hosttable[idx]))
776 : {
777 0 : if (hi->dead && hi->died_at)
778 : {
779 0 : died = elapsed_time_string (hi->died_at, curtime);
780 0 : diedstr = died? died : "error";
781 : }
782 : else
783 0 : diedstr = died = NULL;
784 0 : err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s%s\n",
785 : idx,
786 0 : hi->onion? "O" : hi->v6? "6":" ",
787 0 : hi->v4? "4":" ",
788 0 : hi->dead? "d":" ",
789 0 : hi->name,
790 0 : hi->v6addr? " v6=":"",
791 0 : hi->v6addr? hi->v6addr:"",
792 0 : hi->v4addr? " v4=":"",
793 0 : hi->v4addr? hi->v4addr:"",
794 : diedstr? " (":"",
795 : diedstr? diedstr:"",
796 : diedstr? ")":"" );
797 0 : xfree (died);
798 0 : if (err)
799 0 : return err;
800 :
801 0 : if (hi->cname)
802 0 : err = ks_printf_help (ctrl, " . %s", hi->cname);
803 0 : if (err)
804 0 : return err;
805 :
806 0 : if (hi->pool)
807 : {
808 0 : init_membuf (&mb, 256);
809 0 : put_membuf_printf (&mb, " . -->");
810 0 : for (idx2=0; hi->pool[idx2] != -1; idx2++)
811 : {
812 0 : put_membuf_printf (&mb, " %d", hi->pool[idx2]);
813 0 : if (hi->poolidx == hi->pool[idx2])
814 0 : put_membuf_printf (&mb, "*");
815 : }
816 0 : put_membuf( &mb, "", 1);
817 0 : p = get_membuf (&mb, NULL);
818 0 : if (!p)
819 0 : return gpg_error_from_syserror ();
820 0 : err = ks_print_help (ctrl, p);
821 0 : xfree (p);
822 0 : if (err)
823 0 : return err;
824 : }
825 : }
826 0 : return 0;
827 : }
828 :
829 :
830 :
831 : /* Print a help output for the schemata supported by this module. */
832 : gpg_error_t
833 0 : ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri)
834 : {
835 0 : const char const data[] =
836 : "Handler for HKP URLs:\n"
837 : " hkp://\n"
838 : #if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
839 : " hkps://\n"
840 : #endif
841 : "Supported methods: search, get, put\n";
842 : gpg_error_t err;
843 :
844 : #if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
845 0 : const char data2[] = " hkp\n hkps";
846 : #else
847 : const char data2[] = " hkp";
848 : #endif
849 :
850 0 : if (!uri)
851 0 : err = ks_print_help (ctrl, data2);
852 0 : else if (uri->is_http && (!strcmp (uri->scheme, "hkp")
853 0 : || !strcmp (uri->scheme, "hkps")))
854 0 : err = ks_print_help (ctrl, data);
855 : else
856 0 : err = 0;
857 :
858 0 : return err;
859 : }
860 :
861 :
862 : /* Build the remote part of the URL from SCHEME, HOST and an optional
863 : PORT. Returns an allocated string at R_HOSTPORT or NULL on failure
864 : If R_POOLNAME is not NULL it receives a malloced string with the
865 : poolname. */
866 : static gpg_error_t
867 0 : make_host_part (ctrl_t ctrl,
868 : const char *scheme, const char *host, unsigned short port,
869 : int force_reselect,
870 : char **r_hostport, unsigned int *r_httpflags, char **r_poolname)
871 : {
872 : gpg_error_t err;
873 : char portstr[10];
874 : char *hostname;
875 :
876 0 : *r_hostport = NULL;
877 :
878 0 : portstr[0] = 0;
879 0 : err = map_host (ctrl, host, force_reselect,
880 : &hostname, portstr, r_httpflags, r_poolname);
881 0 : if (err)
882 0 : return err;
883 :
884 : /* Map scheme and port. */
885 0 : if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https"))
886 : {
887 0 : scheme = "https";
888 0 : if (! *portstr)
889 0 : strcpy (portstr, "443");
890 : }
891 : else /* HKP or HTTP. */
892 : {
893 0 : scheme = "http";
894 0 : if (! *portstr)
895 0 : strcpy (portstr, "11371");
896 : }
897 0 : if (port)
898 0 : snprintf (portstr, sizeof portstr, "%hu", port);
899 : else
900 : {
901 : /*fixme_do_srv_lookup ()*/
902 : }
903 :
904 0 : *r_hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL);
905 0 : xfree (hostname);
906 0 : if (!*r_hostport)
907 : {
908 0 : if (r_poolname)
909 : {
910 0 : xfree (*r_poolname);
911 0 : *r_poolname = NULL;
912 : }
913 0 : return gpg_error_from_syserror ();
914 : }
915 0 : return 0;
916 : }
917 :
918 :
919 : /* Resolve all known keyserver names and update the hosttable. This
920 : is mainly useful for debugging because the resolving is anyway done
921 : on demand. */
922 : gpg_error_t
923 0 : ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri)
924 : {
925 : gpg_error_t err;
926 0 : char *hostport = NULL;
927 :
928 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, 1,
929 : &hostport, NULL, NULL);
930 0 : if (err)
931 : {
932 0 : err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s",
933 0 : uri->scheme, uri->host, uri->port,
934 : gpg_strerror (err));
935 : }
936 : else
937 : {
938 0 : err = ks_printf_help (ctrl, "%s", hostport);
939 0 : xfree (hostport);
940 : }
941 0 : return err;
942 : }
943 :
944 :
945 : /* Housekeeping function called from the housekeeping thread. It is
946 : used to mark dead hosts alive so that they may be tried again after
947 : some time. */
948 : void
949 0 : ks_hkp_housekeeping (time_t curtime)
950 : {
951 : int idx;
952 : hostinfo_t hi;
953 :
954 0 : for (idx=0; idx < hosttable_size; idx++)
955 : {
956 0 : hi = hosttable[idx];
957 0 : if (!hi)
958 0 : continue;
959 0 : if (!hi->dead)
960 0 : continue;
961 0 : if (!hi->died_at)
962 0 : continue; /* Do not resurrect manually shot hosts. */
963 0 : if (hi->died_at + RESURRECT_INTERVAL <= curtime
964 0 : || hi->died_at > curtime)
965 : {
966 0 : hi->dead = 0;
967 0 : log_info ("resurrected host '%s'", hi->name);
968 : }
969 : }
970 0 : }
971 :
972 :
973 : /* Send an HTTP request. On success returns an estream object at
974 : R_FP. HOSTPORTSTR is only used for diagnostics. If HTTPHOST is
975 : not NULL it will be used as HTTP "Host" header. If POST_CB is not
976 : NULL a post request is used and that callback is called to allow
977 : writing the post data. If R_HTTP_STATUS is not NULL, the http
978 : status code will be stored there. */
979 : static gpg_error_t
980 0 : send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
981 : const char *httphost, unsigned int httpflags,
982 : gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
983 : estream_t *r_fp, unsigned int *r_http_status)
984 : {
985 : gpg_error_t err;
986 0 : http_session_t session = NULL;
987 0 : http_t http = NULL;
988 0 : int redirects_left = MAX_REDIRECTS;
989 0 : estream_t fp = NULL;
990 0 : char *request_buffer = NULL;
991 :
992 0 : *r_fp = NULL;
993 :
994 0 : err = http_session_new (&session, NULL, httphost, HTTP_FLAG_TRUST_DEF);
995 0 : if (err)
996 0 : goto leave;
997 0 : http_session_set_log_cb (session, cert_log_cb);
998 :
999 : once_more:
1000 0 : err = http_open (&http,
1001 : post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
1002 : request,
1003 : httphost,
1004 : /* fixme: AUTH */ NULL,
1005 : (httpflags
1006 0 : |(opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)
1007 0 : |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)),
1008 0 : ctrl->http_proxy,
1009 : session,
1010 : NULL,
1011 : /*FIXME curl->srvtag*/NULL);
1012 0 : if (!err)
1013 : {
1014 0 : fp = http_get_write_ptr (http);
1015 : /* Avoid caches to get the most recent copy of the key. We set
1016 : both the Pragma and Cache-Control versions of the header, so
1017 : we're good with both HTTP 1.0 and 1.1. */
1018 0 : es_fputs ("Pragma: no-cache\r\n"
1019 : "Cache-Control: no-cache\r\n", fp);
1020 0 : if (post_cb)
1021 0 : err = post_cb (post_cb_value, http);
1022 0 : if (!err)
1023 : {
1024 0 : http_start_data (http);
1025 0 : if (es_ferror (fp))
1026 0 : err = gpg_error_from_syserror ();
1027 : }
1028 : }
1029 0 : if (err)
1030 : {
1031 : /* Fixme: After a redirection we show the old host name. */
1032 0 : log_error (_("error connecting to '%s': %s\n"),
1033 : hostportstr, gpg_strerror (err));
1034 0 : goto leave;
1035 : }
1036 :
1037 : /* Wait for the response. */
1038 0 : dirmngr_tick (ctrl);
1039 0 : err = http_wait_response (http);
1040 0 : if (err)
1041 : {
1042 0 : log_error (_("error reading HTTP response for '%s': %s\n"),
1043 : hostportstr, gpg_strerror (err));
1044 0 : goto leave;
1045 : }
1046 :
1047 0 : if (http_get_tls_info (http, NULL))
1048 : {
1049 : /* Update the httpflags so that a redirect won't fallback to an
1050 : unencrypted connection. */
1051 0 : httpflags |= HTTP_FLAG_FORCE_TLS;
1052 : }
1053 :
1054 0 : if (r_http_status)
1055 0 : *r_http_status = http_get_status_code (http);
1056 :
1057 0 : switch (http_get_status_code (http))
1058 : {
1059 : case 200:
1060 0 : err = 0;
1061 0 : break; /* Success. */
1062 :
1063 : case 301:
1064 : case 302:
1065 : case 307:
1066 : {
1067 0 : const char *s = http_get_header (http, "Location");
1068 :
1069 0 : log_info (_("URL '%s' redirected to '%s' (%u)\n"),
1070 : request, s?s:"[none]", http_get_status_code (http));
1071 0 : if (s && *s && redirects_left-- )
1072 : {
1073 0 : xfree (request_buffer);
1074 0 : request_buffer = xtrystrdup (s);
1075 0 : if (request_buffer)
1076 : {
1077 0 : request = request_buffer;
1078 0 : http_close (http, 0);
1079 0 : http = NULL;
1080 0 : goto once_more;
1081 : }
1082 0 : err = gpg_error_from_syserror ();
1083 : }
1084 : else
1085 0 : err = gpg_error (GPG_ERR_NO_DATA);
1086 0 : log_error (_("too many redirections\n"));
1087 : }
1088 0 : goto leave;
1089 :
1090 : case 501:
1091 0 : err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1092 0 : goto leave;
1093 :
1094 : default:
1095 0 : log_error (_("error accessing '%s': http status %u\n"),
1096 : request, http_get_status_code (http));
1097 0 : err = gpg_error (GPG_ERR_NO_DATA);
1098 0 : goto leave;
1099 : }
1100 :
1101 : /* FIXME: We should register a permanent redirection and whether a
1102 : host has ever used TLS so that future calls will always use
1103 : TLS. */
1104 :
1105 0 : fp = http_get_read_ptr (http);
1106 0 : if (!fp)
1107 : {
1108 0 : err = gpg_error (GPG_ERR_BUG);
1109 0 : goto leave;
1110 : }
1111 :
1112 : /* Return the read stream and close the HTTP context. */
1113 0 : *r_fp = fp;
1114 0 : http_close (http, 1);
1115 0 : http = NULL;
1116 :
1117 : leave:
1118 0 : http_close (http, 0);
1119 0 : http_session_release (session);
1120 0 : xfree (request_buffer);
1121 0 : return err;
1122 : }
1123 :
1124 :
1125 : /* Helper to evaluate the error code ERR form a send_request() call
1126 : with REQUEST. The function returns true if the caller shall try
1127 : again. TRIES_LEFT points to a variable to track the number of
1128 : retries; this function decrements it and won't return true if it is
1129 : down to zero. */
1130 : static int
1131 0 : handle_send_request_error (gpg_error_t err, const char *request,
1132 : unsigned int *tries_left)
1133 : {
1134 0 : int retry = 0;
1135 :
1136 0 : switch (gpg_err_code (err))
1137 : {
1138 : case GPG_ERR_ECONNREFUSED:
1139 : case GPG_ERR_ENETUNREACH:
1140 : case GPG_ERR_UNKNOWN_HOST:
1141 : case GPG_ERR_NETWORK:
1142 0 : if (mark_host_dead (request) && *tries_left)
1143 0 : retry = 1;
1144 0 : break;
1145 :
1146 : case GPG_ERR_ETIMEDOUT:
1147 0 : if (*tries_left)
1148 : {
1149 0 : log_info ("selecting a different host due to a timeout\n");
1150 0 : retry = 1;
1151 : }
1152 :
1153 : default:
1154 0 : break;
1155 : }
1156 :
1157 0 : if (*tries_left)
1158 0 : --*tries_left;
1159 :
1160 0 : return retry;
1161 : }
1162 :
1163 :
1164 : /* Search the keyserver identified by URI for keys matching PATTERN.
1165 : On success R_FP has an open stream to read the data. If
1166 : R_HTTP_STATUS is not NULL, the http status code will be stored
1167 : there. */
1168 : gpg_error_t
1169 0 : ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
1170 : estream_t *r_fp, unsigned int *r_http_status)
1171 : {
1172 : gpg_error_t err;
1173 : KEYDB_SEARCH_DESC desc;
1174 : char fprbuf[2+40+1];
1175 0 : char *hostport = NULL;
1176 0 : char *request = NULL;
1177 0 : estream_t fp = NULL;
1178 : int reselect;
1179 : unsigned int httpflags;
1180 0 : char *httphost = NULL;
1181 0 : unsigned int tries = SEND_REQUEST_RETRIES;
1182 :
1183 0 : *r_fp = NULL;
1184 :
1185 : /* Remove search type indicator and adjust PATTERN accordingly.
1186 : Note that HKP keyservers like the 0x to be present when searching
1187 : by keyid. We need to re-format the fingerprint and keyids so to
1188 : remove the gpg specific force-use-of-this-key flag ("!"). */
1189 0 : err = classify_user_id (pattern, &desc, 1);
1190 0 : if (err)
1191 0 : return err;
1192 0 : switch (desc.mode)
1193 : {
1194 : case KEYDB_SEARCH_MODE_EXACT:
1195 : case KEYDB_SEARCH_MODE_SUBSTR:
1196 : case KEYDB_SEARCH_MODE_MAIL:
1197 : case KEYDB_SEARCH_MODE_MAILSUB:
1198 0 : pattern = desc.u.name;
1199 0 : break;
1200 : case KEYDB_SEARCH_MODE_SHORT_KID:
1201 0 : snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1202 0 : pattern = fprbuf;
1203 0 : break;
1204 : case KEYDB_SEARCH_MODE_LONG_KID:
1205 0 : snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
1206 0 : (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1207 0 : pattern = fprbuf;
1208 0 : break;
1209 : case KEYDB_SEARCH_MODE_FPR16:
1210 0 : fprbuf[0] = '0';
1211 0 : fprbuf[1] = 'x';
1212 0 : bin2hex (desc.u.fpr, 16, fprbuf+2);
1213 0 : pattern = fprbuf;
1214 0 : break;
1215 : case KEYDB_SEARCH_MODE_FPR20:
1216 : case KEYDB_SEARCH_MODE_FPR:
1217 0 : fprbuf[0] = '0';
1218 0 : fprbuf[1] = 'x';
1219 0 : bin2hex (desc.u.fpr, 20, fprbuf+2);
1220 0 : pattern = fprbuf;
1221 0 : break;
1222 : default:
1223 0 : return gpg_error (GPG_ERR_INV_USER_ID);
1224 : }
1225 :
1226 : /* Build the request string. */
1227 0 : reselect = 0;
1228 : again:
1229 : {
1230 : char *searchkey;
1231 :
1232 0 : xfree (hostport); hostport = NULL;
1233 0 : xfree (httphost); httphost = NULL;
1234 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1235 : &hostport, &httpflags, &httphost);
1236 0 : if (err)
1237 0 : goto leave;
1238 :
1239 0 : searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
1240 0 : if (!searchkey)
1241 : {
1242 0 : err = gpg_error_from_syserror ();
1243 0 : goto leave;
1244 : }
1245 :
1246 0 : xfree (request);
1247 0 : request = strconcat (hostport,
1248 : "/pks/lookup?op=index&options=mr&search=",
1249 : searchkey,
1250 : NULL);
1251 0 : xfree (searchkey);
1252 0 : if (!request)
1253 : {
1254 0 : err = gpg_error_from_syserror ();
1255 0 : goto leave;
1256 : }
1257 : }
1258 :
1259 : /* Send the request. */
1260 0 : err = send_request (ctrl, request, hostport, httphost, httpflags,
1261 : NULL, NULL, &fp, r_http_status);
1262 0 : if (handle_send_request_error (err, request, &tries))
1263 : {
1264 0 : reselect = 1;
1265 0 : goto again;
1266 : }
1267 0 : if (err)
1268 0 : goto leave;
1269 :
1270 0 : err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1271 0 : if (err)
1272 0 : goto leave;
1273 :
1274 : /* Peek at the response. */
1275 : {
1276 0 : int c = es_getc (fp);
1277 0 : if (c == -1)
1278 : {
1279 0 : err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
1280 0 : log_error ("error reading response: %s\n", gpg_strerror (err));
1281 0 : goto leave;
1282 : }
1283 0 : if (c == '<')
1284 : {
1285 : /* The document begins with a '<': Assume a HTML response,
1286 : which we don't support. */
1287 0 : err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
1288 0 : goto leave;
1289 : }
1290 0 : es_ungetc (c, fp);
1291 : }
1292 :
1293 : /* Return the read stream. */
1294 0 : *r_fp = fp;
1295 0 : fp = NULL;
1296 :
1297 : leave:
1298 0 : es_fclose (fp);
1299 0 : xfree (request);
1300 0 : xfree (hostport);
1301 0 : xfree (httphost);
1302 0 : return err;
1303 : }
1304 :
1305 :
1306 : /* Get the key described key the KEYSPEC string from the keyserver
1307 : identified by URI. On success R_FP has an open stream to read the
1308 : data. The data will be provided in a format GnuPG can import
1309 : (either a binary OpenPGP message or an armored one). */
1310 : gpg_error_t
1311 0 : ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
1312 : {
1313 : gpg_error_t err;
1314 : KEYDB_SEARCH_DESC desc;
1315 : char kidbuf[2+40+1];
1316 0 : const char *exactname = NULL;
1317 0 : char *searchkey = NULL;
1318 0 : char *hostport = NULL;
1319 0 : char *request = NULL;
1320 0 : estream_t fp = NULL;
1321 : int reselect;
1322 0 : char *httphost = NULL;
1323 : unsigned int httpflags;
1324 0 : unsigned int tries = SEND_REQUEST_RETRIES;
1325 :
1326 0 : *r_fp = NULL;
1327 :
1328 : /* Remove search type indicator and adjust PATTERN accordingly.
1329 : Note that HKP keyservers like the 0x to be present when searching
1330 : by keyid. We need to re-format the fingerprint and keyids so to
1331 : remove the gpg specific force-use-of-this-key flag ("!"). */
1332 0 : err = classify_user_id (keyspec, &desc, 1);
1333 0 : if (err)
1334 0 : return err;
1335 0 : switch (desc.mode)
1336 : {
1337 : case KEYDB_SEARCH_MODE_SHORT_KID:
1338 0 : snprintf (kidbuf, sizeof kidbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1339 0 : break;
1340 : case KEYDB_SEARCH_MODE_LONG_KID:
1341 0 : snprintf (kidbuf, sizeof kidbuf, "0x%08lX%08lX",
1342 0 : (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1343 0 : break;
1344 : case KEYDB_SEARCH_MODE_FPR20:
1345 : case KEYDB_SEARCH_MODE_FPR:
1346 : /* This is a v4 fingerprint. */
1347 0 : kidbuf[0] = '0';
1348 0 : kidbuf[1] = 'x';
1349 0 : bin2hex (desc.u.fpr, 20, kidbuf+2);
1350 0 : break;
1351 :
1352 : case KEYDB_SEARCH_MODE_EXACT:
1353 0 : exactname = desc.u.name;
1354 0 : break;
1355 :
1356 : case KEYDB_SEARCH_MODE_FPR16:
1357 0 : log_error ("HKP keyservers do not support v3 fingerprints\n");
1358 : default:
1359 0 : return gpg_error (GPG_ERR_INV_USER_ID);
1360 : }
1361 :
1362 0 : searchkey = http_escape_string (exactname? exactname : kidbuf,
1363 : EXTRA_ESCAPE_CHARS);
1364 0 : if (!searchkey)
1365 : {
1366 0 : err = gpg_error_from_syserror ();
1367 0 : goto leave;
1368 : }
1369 :
1370 0 : reselect = 0;
1371 : again:
1372 : /* Build the request string. */
1373 0 : xfree (hostport); hostport = NULL;
1374 0 : xfree (httphost); httphost = NULL;
1375 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1376 : &hostport, &httpflags, &httphost);
1377 0 : if (err)
1378 0 : goto leave;
1379 :
1380 0 : xfree (request);
1381 0 : request = strconcat (hostport,
1382 : "/pks/lookup?op=get&options=mr&search=",
1383 : searchkey,
1384 : exactname? "&exact=on":"",
1385 : NULL);
1386 0 : if (!request)
1387 : {
1388 0 : err = gpg_error_from_syserror ();
1389 0 : goto leave;
1390 : }
1391 :
1392 : /* Send the request. */
1393 0 : err = send_request (ctrl, request, hostport, httphost, httpflags,
1394 : NULL, NULL, &fp, NULL);
1395 0 : if (handle_send_request_error (err, request, &tries))
1396 : {
1397 0 : reselect = 1;
1398 0 : goto again;
1399 : }
1400 0 : if (err)
1401 0 : goto leave;
1402 :
1403 0 : err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1404 0 : if (err)
1405 0 : goto leave;
1406 :
1407 : /* Return the read stream and close the HTTP context. */
1408 0 : *r_fp = fp;
1409 0 : fp = NULL;
1410 :
1411 : leave:
1412 0 : es_fclose (fp);
1413 0 : xfree (request);
1414 0 : xfree (hostport);
1415 0 : xfree (httphost);
1416 0 : xfree (searchkey);
1417 0 : return err;
1418 : }
1419 :
1420 :
1421 :
1422 :
1423 : /* Callback parameters for put_post_cb. */
1424 : struct put_post_parm_s
1425 : {
1426 : char *datastring;
1427 : };
1428 :
1429 :
1430 : /* Helper for ks_hkp_put. */
1431 : static gpg_error_t
1432 0 : put_post_cb (void *opaque, http_t http)
1433 : {
1434 0 : struct put_post_parm_s *parm = opaque;
1435 0 : gpg_error_t err = 0;
1436 : estream_t fp;
1437 : size_t len;
1438 :
1439 0 : fp = http_get_write_ptr (http);
1440 0 : len = strlen (parm->datastring);
1441 :
1442 0 : es_fprintf (fp,
1443 : "Content-Type: application/x-www-form-urlencoded\r\n"
1444 : "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
1445 0 : http_start_data (http);
1446 0 : if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
1447 0 : err = gpg_error_from_syserror ();
1448 0 : return err;
1449 : }
1450 :
1451 :
1452 : /* Send the key in {DATA,DATALEN} to the keyserver identified by URI. */
1453 : gpg_error_t
1454 0 : ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
1455 : {
1456 : gpg_error_t err;
1457 0 : char *hostport = NULL;
1458 0 : char *request = NULL;
1459 0 : estream_t fp = NULL;
1460 : struct put_post_parm_s parm;
1461 0 : char *armored = NULL;
1462 : int reselect;
1463 0 : char *httphost = NULL;
1464 : unsigned int httpflags;
1465 0 : unsigned int tries = SEND_REQUEST_RETRIES;
1466 :
1467 0 : parm.datastring = NULL;
1468 :
1469 0 : err = armor_data (&armored, data, datalen);
1470 0 : if (err)
1471 0 : goto leave;
1472 :
1473 0 : parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
1474 0 : if (!parm.datastring)
1475 : {
1476 0 : err = gpg_error_from_syserror ();
1477 0 : goto leave;
1478 : }
1479 0 : xfree (armored);
1480 0 : armored = NULL;
1481 :
1482 : /* Build the request string. */
1483 0 : reselect = 0;
1484 : again:
1485 0 : xfree (hostport); hostport = NULL;
1486 0 : xfree (httphost); httphost = NULL;
1487 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1488 : &hostport, &httpflags, &httphost);
1489 0 : if (err)
1490 0 : goto leave;
1491 :
1492 0 : xfree (request);
1493 0 : request = strconcat (hostport, "/pks/add", NULL);
1494 0 : if (!request)
1495 : {
1496 0 : err = gpg_error_from_syserror ();
1497 0 : goto leave;
1498 : }
1499 :
1500 : /* Send the request. */
1501 0 : err = send_request (ctrl, request, hostport, httphost, 0,
1502 : put_post_cb, &parm, &fp, NULL);
1503 0 : if (handle_send_request_error (err, request, &tries))
1504 : {
1505 0 : reselect = 1;
1506 0 : goto again;
1507 : }
1508 0 : if (err)
1509 0 : goto leave;
1510 :
1511 : leave:
1512 0 : es_fclose (fp);
1513 0 : xfree (parm.datastring);
1514 0 : xfree (armored);
1515 0 : xfree (request);
1516 0 : xfree (hostport);
1517 0 : xfree (httphost);
1518 0 : return err;
1519 : }
|