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 <https://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 : char *srvrecord;
430 : struct srventry *srvs;
431 : int srvscount;
432 :
433 0 : reftblsize = 100;
434 0 : reftbl = xtrymalloc (reftblsize * sizeof *reftbl);
435 0 : if (!reftbl)
436 0 : return gpg_error_from_syserror ();
437 0 : refidx = 0;
438 :
439 0 : idx = create_new_hostinfo (name);
440 0 : if (idx == -1)
441 : {
442 0 : err = gpg_error_from_syserror ();
443 0 : xfree (reftbl);
444 0 : return err;
445 : }
446 0 : hi = hosttable[idx];
447 :
448 0 : if (!is_ip_address (name))
449 : {
450 : /* Check for SRV records. */
451 0 : srvrecord = xtryasprintf ("_hkp._tcp.%s", name);
452 0 : if (srvrecord == NULL)
453 : {
454 0 : err = gpg_error_from_syserror ();
455 0 : xfree (reftbl);
456 0 : return err;
457 : }
458 :
459 0 : srvscount = getsrv (srvrecord, &srvs);
460 0 : xfree (srvrecord);
461 0 : if (srvscount < 0)
462 : {
463 0 : err = gpg_error_from_syserror ();
464 0 : xfree (reftbl);
465 0 : return err;
466 : }
467 :
468 0 : if (srvscount > 0)
469 : {
470 : int i;
471 0 : is_pool = srvscount > 1;
472 :
473 0 : for (i = 0; i < srvscount; i++)
474 : {
475 0 : err = resolve_dns_name (srvs[i].target, 0,
476 : AF_UNSPEC, SOCK_STREAM,
477 : &ai, &cname);
478 0 : if (err)
479 0 : continue;
480 0 : dirmngr_tick (ctrl);
481 0 : add_host (name, is_pool, ai, srvs[i].port,
482 : reftbl, reftblsize, &refidx);
483 : }
484 :
485 0 : xfree (srvs);
486 : }
487 : }
488 :
489 : /* Find all A records for this entry and put them into the pool
490 : list - if any. */
491 0 : err = resolve_dns_name (name, 0, 0, SOCK_STREAM, &aibuf, &cname);
492 0 : if (err)
493 : {
494 0 : log_error ("resolving '%s' failed: %s\n", name, gpg_strerror (err));
495 0 : err = 0;
496 : }
497 : else
498 : {
499 : /* First figure out whether this is a pool. For a pool we
500 : use a different strategy than for a plain server: We use
501 : the canonical name of the pool as the virtual host along
502 : with the IP addresses. If it is not a pool, we use the
503 : specified name. */
504 0 : if (! is_pool)
505 0 : is_pool = arecords_is_pool (aibuf);
506 0 : if (is_pool && cname)
507 : {
508 0 : hi->cname = cname;
509 0 : cname = NULL;
510 : }
511 :
512 0 : for (ai = aibuf; ai; ai = ai->next)
513 : {
514 0 : if (ai->family != AF_INET && ai->family != AF_INET6)
515 0 : continue;
516 0 : dirmngr_tick (ctrl);
517 :
518 0 : add_host (name, is_pool, ai, 0, reftbl, reftblsize, &refidx);
519 : }
520 : }
521 0 : reftbl[refidx] = -1;
522 0 : xfree (cname);
523 0 : free_dns_addrinfo (aibuf);
524 :
525 0 : if (refidx && is_pool)
526 : {
527 0 : assert (!hi->pool);
528 0 : hi->pool = xtryrealloc (reftbl, (refidx+1) * sizeof *reftbl);
529 0 : if (!hi->pool)
530 : {
531 0 : err = gpg_error_from_syserror ();
532 0 : log_error ("shrinking index table in map_host failed: %s\n",
533 : gpg_strerror (err));
534 0 : xfree (reftbl);
535 0 : return err;
536 : }
537 0 : qsort (hi->pool, refidx, sizeof *reftbl, sort_hostpool);
538 : }
539 : else
540 0 : xfree (reftbl);
541 : }
542 :
543 0 : hi = hosttable[idx];
544 0 : if (hi->pool)
545 : {
546 : /* Deal with the pool name before selecting a host. */
547 0 : if (r_poolname)
548 : {
549 0 : *r_poolname = xtrystrdup (hi->cname? hi->cname : hi->name);
550 0 : if (!*r_poolname)
551 0 : return gpg_error_from_syserror ();
552 : }
553 :
554 : /* If the currently selected host is now marked dead, force a
555 : re-selection . */
556 0 : if (force_reselect)
557 0 : hi->poolidx = -1;
558 0 : else if (hi->poolidx >= 0 && hi->poolidx < hosttable_size
559 0 : && hosttable[hi->poolidx] && hosttable[hi->poolidx]->dead)
560 0 : hi->poolidx = -1;
561 :
562 : /* Select a host if needed. */
563 0 : if (hi->poolidx == -1)
564 : {
565 0 : hi->poolidx = select_random_host (hi->pool);
566 0 : if (hi->poolidx == -1)
567 : {
568 0 : log_error ("no alive host found in pool '%s'\n", name);
569 0 : if (r_poolname)
570 : {
571 0 : xfree (*r_poolname);
572 0 : *r_poolname = NULL;
573 : }
574 0 : return gpg_error (GPG_ERR_NO_KEYSERVER);
575 : }
576 : }
577 :
578 0 : assert (hi->poolidx >= 0 && hi->poolidx < hosttable_size);
579 0 : hi = hosttable[hi->poolidx];
580 0 : assert (hi);
581 : }
582 :
583 0 : if (hi->dead)
584 : {
585 0 : log_error ("host '%s' marked as dead\n", hi->name);
586 0 : if (r_poolname)
587 : {
588 0 : xfree (*r_poolname);
589 0 : *r_poolname = NULL;
590 : }
591 0 : return gpg_error (GPG_ERR_NO_KEYSERVER);
592 : }
593 :
594 0 : if (r_httpflags)
595 : {
596 : /* If the hosttable does not indicate that a certain host
597 : supports IPv<N>, we explicit set the corresponding http
598 : flags. The reason for this is that a host might be listed in
599 : a pool as not v6 only but actually support v6 when later
600 : the name is resolved by our http layer. */
601 0 : if (!hi->v4)
602 0 : *r_httpflags |= HTTP_FLAG_IGNORE_IPv4;
603 0 : if (!hi->v6)
604 0 : *r_httpflags |= HTTP_FLAG_IGNORE_IPv6;
605 :
606 : /* Note that we do not set the HTTP_FLAG_FORCE_TOR for onion
607 : addresses because the http module detects this itself. This
608 : also allows us to use an onion address without Tor mode being
609 : enabled. */
610 : }
611 :
612 0 : *r_host = xtrystrdup (hi->name);
613 0 : if (!*r_host)
614 : {
615 0 : err = gpg_error_from_syserror ();
616 0 : if (r_poolname)
617 : {
618 0 : xfree (*r_poolname);
619 0 : *r_poolname = NULL;
620 : }
621 0 : return err;
622 : }
623 0 : if (hi->port)
624 0 : snprintf (r_portstr, 6 /* five digits and the sentinel */,
625 0 : "%hu", hi->port);
626 0 : return 0;
627 : }
628 :
629 :
630 : /* Mark the host NAME as dead. NAME may be given as an URL. Returns
631 : true if a host was really marked as dead or was already marked dead
632 : (e.g. by a concurrent session). */
633 : static int
634 0 : mark_host_dead (const char *name)
635 : {
636 : const char *host;
637 0 : char *host_buffer = NULL;
638 0 : parsed_uri_t parsed_uri = NULL;
639 0 : int done = 0;
640 :
641 0 : if (name && *name && !http_parse_uri (&parsed_uri, name, 1))
642 : {
643 0 : if (parsed_uri->v6lit)
644 : {
645 0 : host_buffer = strconcat ("[", parsed_uri->host, "]", NULL);
646 0 : if (!host_buffer)
647 0 : log_error ("out of core in mark_host_dead");
648 0 : host = host_buffer;
649 : }
650 : else
651 0 : host = parsed_uri->host;
652 : }
653 : else
654 0 : host = name;
655 :
656 0 : if (host && *host && strcmp (host, "localhost"))
657 : {
658 : hostinfo_t hi;
659 : int idx;
660 :
661 0 : idx = find_hostinfo (host);
662 0 : if (idx != -1)
663 : {
664 0 : hi = hosttable[idx];
665 0 : log_info ("marking host '%s' as dead%s\n",
666 0 : hi->name, hi->dead? " (again)":"");
667 0 : hi->dead = 1;
668 0 : hi->died_at = gnupg_get_time ();
669 0 : if (!hi->died_at)
670 0 : hi->died_at = 1;
671 0 : done = 1;
672 : }
673 : }
674 :
675 0 : http_release_parsed_uri (parsed_uri);
676 0 : xfree (host_buffer);
677 0 : return done;
678 : }
679 :
680 :
681 : /* Mark a host in the hosttable as dead or - if ALIVE is true - as
682 : alive. */
683 : gpg_error_t
684 0 : ks_hkp_mark_host (ctrl_t ctrl, const char *name, int alive)
685 : {
686 0 : gpg_error_t err = 0;
687 : hostinfo_t hi, hi2;
688 : int idx, idx2, idx3, n;
689 :
690 0 : if (!name || !*name || !strcmp (name, "localhost"))
691 0 : return 0;
692 :
693 0 : idx = find_hostinfo (name);
694 0 : if (idx == -1)
695 0 : return gpg_error (GPG_ERR_NOT_FOUND);
696 :
697 0 : hi = hosttable[idx];
698 0 : if (alive && hi->dead)
699 : {
700 0 : hi->dead = 0;
701 0 : err = ks_printf_help (ctrl, "marking '%s' as alive", name);
702 : }
703 0 : else if (!alive && !hi->dead)
704 : {
705 0 : hi->dead = 1;
706 0 : hi->died_at = 0; /* Manually set dead. */
707 0 : err = ks_printf_help (ctrl, "marking '%s' as dead", name);
708 : }
709 :
710 : /* If the host is a pool mark all member hosts. */
711 0 : if (!err && hi->pool)
712 : {
713 0 : for (idx2=0; !err && (n=hi->pool[idx2]) != -1; idx2++)
714 : {
715 0 : assert (n >= 0 && n < hosttable_size);
716 :
717 0 : if (!alive)
718 : {
719 : /* Do not mark a host from a pool dead if it is also a
720 : member in another pool. */
721 0 : for (idx3=0; idx3 < hosttable_size; idx3++)
722 : {
723 0 : if (hosttable[idx3]
724 0 : && hosttable[idx3]->pool
725 0 : && idx3 != idx
726 0 : && host_in_pool_p (hosttable[idx3]->pool, n))
727 0 : break;
728 : }
729 0 : if (idx3 < hosttable_size)
730 0 : continue; /* Host is also a member of another pool. */
731 : }
732 :
733 0 : hi2 = hosttable[n];
734 0 : if (!hi2)
735 : ;
736 0 : else if (alive && hi2->dead)
737 : {
738 0 : hi2->dead = 0;
739 0 : err = ks_printf_help (ctrl, "marking '%s' as alive",
740 0 : hi2->name);
741 : }
742 0 : else if (!alive && !hi2->dead)
743 : {
744 0 : hi2->dead = 1;
745 0 : hi2->died_at = 0; /* Manually set dead. */
746 0 : err = ks_printf_help (ctrl, "marking '%s' as dead",
747 0 : hi2->name);
748 : }
749 : }
750 : }
751 :
752 0 : return err;
753 : }
754 :
755 :
756 : /* Debug function to print the entire hosttable. */
757 : gpg_error_t
758 0 : ks_hkp_print_hosttable (ctrl_t ctrl)
759 : {
760 : gpg_error_t err;
761 : int idx, idx2;
762 : hostinfo_t hi;
763 : membuf_t mb;
764 : time_t curtime;
765 : char *p, *died;
766 : const char *diedstr;
767 :
768 0 : err = ks_print_help (ctrl, "hosttable (idx, ipv6, ipv4, dead, name, time):");
769 0 : if (err)
770 0 : return err;
771 :
772 0 : curtime = gnupg_get_time ();
773 0 : for (idx=0; idx < hosttable_size; idx++)
774 0 : if ((hi=hosttable[idx]))
775 : {
776 0 : if (hi->dead && hi->died_at)
777 : {
778 0 : died = elapsed_time_string (hi->died_at, curtime);
779 0 : diedstr = died? died : "error";
780 : }
781 : else
782 0 : diedstr = died = NULL;
783 0 : err = ks_printf_help (ctrl, "%3d %s %s %s %s%s%s%s%s%s%s%s\n",
784 : idx,
785 0 : hi->onion? "O" : hi->v6? "6":" ",
786 0 : hi->v4? "4":" ",
787 0 : hi->dead? "d":" ",
788 0 : hi->name,
789 0 : hi->v6addr? " v6=":"",
790 0 : hi->v6addr? hi->v6addr:"",
791 0 : hi->v4addr? " v4=":"",
792 0 : hi->v4addr? hi->v4addr:"",
793 : diedstr? " (":"",
794 : diedstr? diedstr:"",
795 : diedstr? ")":"" );
796 0 : xfree (died);
797 0 : if (err)
798 0 : return err;
799 :
800 0 : if (hi->cname)
801 0 : err = ks_printf_help (ctrl, " . %s", hi->cname);
802 0 : if (err)
803 0 : return err;
804 :
805 0 : if (hi->pool)
806 : {
807 0 : init_membuf (&mb, 256);
808 0 : put_membuf_printf (&mb, " . -->");
809 0 : for (idx2=0; hi->pool[idx2] != -1; idx2++)
810 : {
811 0 : put_membuf_printf (&mb, " %d", hi->pool[idx2]);
812 0 : if (hi->poolidx == hi->pool[idx2])
813 0 : put_membuf_printf (&mb, "*");
814 : }
815 0 : put_membuf( &mb, "", 1);
816 0 : p = get_membuf (&mb, NULL);
817 0 : if (!p)
818 0 : return gpg_error_from_syserror ();
819 0 : err = ks_print_help (ctrl, p);
820 0 : xfree (p);
821 0 : if (err)
822 0 : return err;
823 : }
824 : }
825 0 : return 0;
826 : }
827 :
828 :
829 :
830 : /* Print a help output for the schemata supported by this module. */
831 : gpg_error_t
832 0 : ks_hkp_help (ctrl_t ctrl, parsed_uri_t uri)
833 : {
834 0 : const char const data[] =
835 : "Handler for HKP URLs:\n"
836 : " hkp://\n"
837 : #if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
838 : " hkps://\n"
839 : #endif
840 : "Supported methods: search, get, put\n";
841 : gpg_error_t err;
842 :
843 : #if HTTP_USE_GNUTLS || HTTP_USE_NTBTLS
844 0 : const char data2[] = " hkp\n hkps";
845 : #else
846 : const char data2[] = " hkp";
847 : #endif
848 :
849 0 : if (!uri)
850 0 : err = ks_print_help (ctrl, data2);
851 0 : else if (uri->is_http && (!strcmp (uri->scheme, "hkp")
852 0 : || !strcmp (uri->scheme, "hkps")))
853 0 : err = ks_print_help (ctrl, data);
854 : else
855 0 : err = 0;
856 :
857 0 : return err;
858 : }
859 :
860 :
861 : /* Build the remote part of the URL from SCHEME, HOST and an optional
862 : PORT. Returns an allocated string at R_HOSTPORT or NULL on failure
863 : If R_POOLNAME is not NULL it receives a malloced string with the
864 : poolname. */
865 : static gpg_error_t
866 0 : make_host_part (ctrl_t ctrl,
867 : const char *scheme, const char *host, unsigned short port,
868 : int force_reselect,
869 : char **r_hostport, unsigned int *r_httpflags, char **r_poolname)
870 : {
871 : gpg_error_t err;
872 : char portstr[10];
873 : char *hostname;
874 :
875 0 : *r_hostport = NULL;
876 :
877 0 : portstr[0] = 0;
878 0 : err = map_host (ctrl, host, force_reselect,
879 : &hostname, portstr, r_httpflags, r_poolname);
880 0 : if (err)
881 0 : return err;
882 :
883 : /* Map scheme and port. */
884 0 : if (!strcmp (scheme, "hkps") || !strcmp (scheme,"https"))
885 : {
886 0 : scheme = "https";
887 0 : if (! *portstr)
888 0 : strcpy (portstr, "443");
889 : }
890 : else /* HKP or HTTP. */
891 : {
892 0 : scheme = "http";
893 0 : if (! *portstr)
894 0 : strcpy (portstr, "11371");
895 : }
896 0 : if (port)
897 0 : snprintf (portstr, sizeof portstr, "%hu", port);
898 : else
899 : {
900 : /*fixme_do_srv_lookup ()*/
901 : }
902 :
903 0 : *r_hostport = strconcat (scheme, "://", hostname, ":", portstr, NULL);
904 0 : xfree (hostname);
905 0 : if (!*r_hostport)
906 : {
907 0 : if (r_poolname)
908 : {
909 0 : xfree (*r_poolname);
910 0 : *r_poolname = NULL;
911 : }
912 0 : return gpg_error_from_syserror ();
913 : }
914 0 : return 0;
915 : }
916 :
917 :
918 : /* Resolve all known keyserver names and update the hosttable. This
919 : is mainly useful for debugging because the resolving is anyway done
920 : on demand. */
921 : gpg_error_t
922 0 : ks_hkp_resolve (ctrl_t ctrl, parsed_uri_t uri)
923 : {
924 : gpg_error_t err;
925 0 : char *hostport = NULL;
926 :
927 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, 1,
928 : &hostport, NULL, NULL);
929 0 : if (err)
930 : {
931 0 : err = ks_printf_help (ctrl, "%s://%s:%hu: resolve failed: %s",
932 0 : uri->scheme, uri->host, uri->port,
933 : gpg_strerror (err));
934 : }
935 : else
936 : {
937 0 : err = ks_printf_help (ctrl, "%s", hostport);
938 0 : xfree (hostport);
939 : }
940 0 : return err;
941 : }
942 :
943 :
944 : /* Housekeeping function called from the housekeeping thread. It is
945 : used to mark dead hosts alive so that they may be tried again after
946 : some time. */
947 : void
948 0 : ks_hkp_housekeeping (time_t curtime)
949 : {
950 : int idx;
951 : hostinfo_t hi;
952 :
953 0 : for (idx=0; idx < hosttable_size; idx++)
954 : {
955 0 : hi = hosttable[idx];
956 0 : if (!hi)
957 0 : continue;
958 0 : if (!hi->dead)
959 0 : continue;
960 0 : if (!hi->died_at)
961 0 : continue; /* Do not resurrect manually shot hosts. */
962 0 : if (hi->died_at + RESURRECT_INTERVAL <= curtime
963 0 : || hi->died_at > curtime)
964 : {
965 0 : hi->dead = 0;
966 0 : log_info ("resurrected host '%s'", hi->name);
967 : }
968 : }
969 0 : }
970 :
971 :
972 : /* Send an HTTP request. On success returns an estream object at
973 : R_FP. HOSTPORTSTR is only used for diagnostics. If HTTPHOST is
974 : not NULL it will be used as HTTP "Host" header. If POST_CB is not
975 : NULL a post request is used and that callback is called to allow
976 : writing the post data. If R_HTTP_STATUS is not NULL, the http
977 : status code will be stored there. */
978 : static gpg_error_t
979 0 : send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
980 : const char *httphost, unsigned int httpflags,
981 : gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
982 : estream_t *r_fp, unsigned int *r_http_status)
983 : {
984 : gpg_error_t err;
985 0 : http_session_t session = NULL;
986 0 : http_t http = NULL;
987 0 : int redirects_left = MAX_REDIRECTS;
988 0 : estream_t fp = NULL;
989 0 : char *request_buffer = NULL;
990 :
991 0 : *r_fp = NULL;
992 :
993 0 : err = http_session_new (&session, NULL, httphost, HTTP_FLAG_TRUST_DEF);
994 0 : if (err)
995 0 : goto leave;
996 0 : http_session_set_log_cb (session, cert_log_cb);
997 :
998 : once_more:
999 0 : err = http_open (&http,
1000 : post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
1001 : request,
1002 : httphost,
1003 : /* fixme: AUTH */ NULL,
1004 : (httpflags
1005 0 : |(opt.honor_http_proxy? HTTP_FLAG_TRY_PROXY:0)
1006 0 : |(opt.use_tor? HTTP_FLAG_FORCE_TOR:0)),
1007 0 : ctrl->http_proxy,
1008 : session,
1009 : NULL,
1010 : /*FIXME curl->srvtag*/NULL);
1011 0 : if (!err)
1012 : {
1013 0 : fp = http_get_write_ptr (http);
1014 : /* Avoid caches to get the most recent copy of the key. We set
1015 : both the Pragma and Cache-Control versions of the header, so
1016 : we're good with both HTTP 1.0 and 1.1. */
1017 0 : es_fputs ("Pragma: no-cache\r\n"
1018 : "Cache-Control: no-cache\r\n", fp);
1019 0 : if (post_cb)
1020 0 : err = post_cb (post_cb_value, http);
1021 0 : if (!err)
1022 : {
1023 0 : http_start_data (http);
1024 0 : if (es_ferror (fp))
1025 0 : err = gpg_error_from_syserror ();
1026 : }
1027 : }
1028 0 : if (err)
1029 : {
1030 : /* Fixme: After a redirection we show the old host name. */
1031 0 : log_error (_("error connecting to '%s': %s\n"),
1032 : hostportstr, gpg_strerror (err));
1033 0 : goto leave;
1034 : }
1035 :
1036 : /* Wait for the response. */
1037 0 : dirmngr_tick (ctrl);
1038 0 : err = http_wait_response (http);
1039 0 : if (err)
1040 : {
1041 0 : log_error (_("error reading HTTP response for '%s': %s\n"),
1042 : hostportstr, gpg_strerror (err));
1043 0 : goto leave;
1044 : }
1045 :
1046 0 : if (http_get_tls_info (http, NULL))
1047 : {
1048 : /* Update the httpflags so that a redirect won't fallback to an
1049 : unencrypted connection. */
1050 0 : httpflags |= HTTP_FLAG_FORCE_TLS;
1051 : }
1052 :
1053 0 : if (r_http_status)
1054 0 : *r_http_status = http_get_status_code (http);
1055 :
1056 0 : switch (http_get_status_code (http))
1057 : {
1058 : case 200:
1059 0 : err = 0;
1060 0 : break; /* Success. */
1061 :
1062 : case 301:
1063 : case 302:
1064 : case 307:
1065 : {
1066 0 : const char *s = http_get_header (http, "Location");
1067 :
1068 0 : log_info (_("URL '%s' redirected to '%s' (%u)\n"),
1069 : request, s?s:"[none]", http_get_status_code (http));
1070 0 : if (s && *s && redirects_left-- )
1071 : {
1072 0 : xfree (request_buffer);
1073 0 : request_buffer = xtrystrdup (s);
1074 0 : if (request_buffer)
1075 : {
1076 0 : request = request_buffer;
1077 0 : http_close (http, 0);
1078 0 : http = NULL;
1079 0 : goto once_more;
1080 : }
1081 0 : err = gpg_error_from_syserror ();
1082 : }
1083 : else
1084 0 : err = gpg_error (GPG_ERR_NO_DATA);
1085 0 : log_error (_("too many redirections\n"));
1086 : }
1087 0 : goto leave;
1088 :
1089 : case 501:
1090 0 : err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
1091 0 : goto leave;
1092 :
1093 : default:
1094 0 : log_error (_("error accessing '%s': http status %u\n"),
1095 : request, http_get_status_code (http));
1096 0 : err = gpg_error (GPG_ERR_NO_DATA);
1097 0 : goto leave;
1098 : }
1099 :
1100 : /* FIXME: We should register a permanent redirection and whether a
1101 : host has ever used TLS so that future calls will always use
1102 : TLS. */
1103 :
1104 0 : fp = http_get_read_ptr (http);
1105 0 : if (!fp)
1106 : {
1107 0 : err = gpg_error (GPG_ERR_BUG);
1108 0 : goto leave;
1109 : }
1110 :
1111 : /* Return the read stream and close the HTTP context. */
1112 0 : *r_fp = fp;
1113 0 : http_close (http, 1);
1114 0 : http = NULL;
1115 :
1116 : leave:
1117 0 : http_close (http, 0);
1118 0 : http_session_release (session);
1119 0 : xfree (request_buffer);
1120 0 : return err;
1121 : }
1122 :
1123 :
1124 : /* Helper to evaluate the error code ERR form a send_request() call
1125 : with REQUEST. The function returns true if the caller shall try
1126 : again. TRIES_LEFT points to a variable to track the number of
1127 : retries; this function decrements it and won't return true if it is
1128 : down to zero. */
1129 : static int
1130 0 : handle_send_request_error (gpg_error_t err, const char *request,
1131 : unsigned int *tries_left)
1132 : {
1133 0 : int retry = 0;
1134 :
1135 0 : switch (gpg_err_code (err))
1136 : {
1137 : case GPG_ERR_ECONNREFUSED:
1138 : case GPG_ERR_ENETUNREACH:
1139 : case GPG_ERR_UNKNOWN_HOST:
1140 : case GPG_ERR_NETWORK:
1141 0 : if (mark_host_dead (request) && *tries_left)
1142 0 : retry = 1;
1143 0 : break;
1144 :
1145 : case GPG_ERR_ETIMEDOUT:
1146 0 : if (*tries_left)
1147 : {
1148 0 : log_info ("selecting a different host due to a timeout\n");
1149 0 : retry = 1;
1150 : }
1151 :
1152 : default:
1153 0 : break;
1154 : }
1155 :
1156 0 : if (*tries_left)
1157 0 : --*tries_left;
1158 :
1159 0 : return retry;
1160 : }
1161 :
1162 :
1163 : /* Search the keyserver identified by URI for keys matching PATTERN.
1164 : On success R_FP has an open stream to read the data. If
1165 : R_HTTP_STATUS is not NULL, the http status code will be stored
1166 : there. */
1167 : gpg_error_t
1168 0 : ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
1169 : estream_t *r_fp, unsigned int *r_http_status)
1170 : {
1171 : gpg_error_t err;
1172 : KEYDB_SEARCH_DESC desc;
1173 : char fprbuf[2+40+1];
1174 0 : char *hostport = NULL;
1175 0 : char *request = NULL;
1176 0 : estream_t fp = NULL;
1177 : int reselect;
1178 : unsigned int httpflags;
1179 0 : char *httphost = NULL;
1180 0 : unsigned int tries = SEND_REQUEST_RETRIES;
1181 :
1182 0 : *r_fp = NULL;
1183 :
1184 : /* Remove search type indicator and adjust PATTERN accordingly.
1185 : Note that HKP keyservers like the 0x to be present when searching
1186 : by keyid. We need to re-format the fingerprint and keyids so to
1187 : remove the gpg specific force-use-of-this-key flag ("!"). */
1188 0 : err = classify_user_id (pattern, &desc, 1);
1189 0 : if (err)
1190 0 : return err;
1191 0 : switch (desc.mode)
1192 : {
1193 : case KEYDB_SEARCH_MODE_EXACT:
1194 : case KEYDB_SEARCH_MODE_SUBSTR:
1195 : case KEYDB_SEARCH_MODE_MAIL:
1196 : case KEYDB_SEARCH_MODE_MAILSUB:
1197 0 : pattern = desc.u.name;
1198 0 : break;
1199 : case KEYDB_SEARCH_MODE_SHORT_KID:
1200 0 : snprintf (fprbuf, sizeof fprbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1201 0 : pattern = fprbuf;
1202 0 : break;
1203 : case KEYDB_SEARCH_MODE_LONG_KID:
1204 0 : snprintf (fprbuf, sizeof fprbuf, "0x%08lX%08lX",
1205 0 : (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1206 0 : pattern = fprbuf;
1207 0 : break;
1208 : case KEYDB_SEARCH_MODE_FPR16:
1209 0 : fprbuf[0] = '0';
1210 0 : fprbuf[1] = 'x';
1211 0 : bin2hex (desc.u.fpr, 16, fprbuf+2);
1212 0 : pattern = fprbuf;
1213 0 : break;
1214 : case KEYDB_SEARCH_MODE_FPR20:
1215 : case KEYDB_SEARCH_MODE_FPR:
1216 0 : fprbuf[0] = '0';
1217 0 : fprbuf[1] = 'x';
1218 0 : bin2hex (desc.u.fpr, 20, fprbuf+2);
1219 0 : pattern = fprbuf;
1220 0 : break;
1221 : default:
1222 0 : return gpg_error (GPG_ERR_INV_USER_ID);
1223 : }
1224 :
1225 : /* Build the request string. */
1226 0 : reselect = 0;
1227 : again:
1228 : {
1229 : char *searchkey;
1230 :
1231 0 : xfree (hostport); hostport = NULL;
1232 0 : xfree (httphost); httphost = NULL;
1233 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1234 : &hostport, &httpflags, &httphost);
1235 0 : if (err)
1236 0 : goto leave;
1237 :
1238 0 : searchkey = http_escape_string (pattern, EXTRA_ESCAPE_CHARS);
1239 0 : if (!searchkey)
1240 : {
1241 0 : err = gpg_error_from_syserror ();
1242 0 : goto leave;
1243 : }
1244 :
1245 0 : xfree (request);
1246 0 : request = strconcat (hostport,
1247 : "/pks/lookup?op=index&options=mr&search=",
1248 : searchkey,
1249 : NULL);
1250 0 : xfree (searchkey);
1251 0 : if (!request)
1252 : {
1253 0 : err = gpg_error_from_syserror ();
1254 0 : goto leave;
1255 : }
1256 : }
1257 :
1258 : /* Send the request. */
1259 0 : err = send_request (ctrl, request, hostport, httphost, httpflags,
1260 : NULL, NULL, &fp, r_http_status);
1261 0 : if (handle_send_request_error (err, request, &tries))
1262 : {
1263 0 : reselect = 1;
1264 0 : goto again;
1265 : }
1266 0 : if (err)
1267 0 : goto leave;
1268 :
1269 0 : err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1270 0 : if (err)
1271 0 : goto leave;
1272 :
1273 : /* Peek at the response. */
1274 : {
1275 0 : int c = es_getc (fp);
1276 0 : if (c == -1)
1277 : {
1278 0 : err = es_ferror (fp)?gpg_error_from_syserror ():gpg_error (GPG_ERR_EOF);
1279 0 : log_error ("error reading response: %s\n", gpg_strerror (err));
1280 0 : goto leave;
1281 : }
1282 0 : if (c == '<')
1283 : {
1284 : /* The document begins with a '<': Assume a HTML response,
1285 : which we don't support. */
1286 0 : err = gpg_error (GPG_ERR_UNSUPPORTED_ENCODING);
1287 0 : goto leave;
1288 : }
1289 0 : es_ungetc (c, fp);
1290 : }
1291 :
1292 : /* Return the read stream. */
1293 0 : *r_fp = fp;
1294 0 : fp = NULL;
1295 :
1296 : leave:
1297 0 : es_fclose (fp);
1298 0 : xfree (request);
1299 0 : xfree (hostport);
1300 0 : xfree (httphost);
1301 0 : return err;
1302 : }
1303 :
1304 :
1305 : /* Get the key described key the KEYSPEC string from the keyserver
1306 : identified by URI. On success R_FP has an open stream to read the
1307 : data. The data will be provided in a format GnuPG can import
1308 : (either a binary OpenPGP message or an armored one). */
1309 : gpg_error_t
1310 0 : ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
1311 : {
1312 : gpg_error_t err;
1313 : KEYDB_SEARCH_DESC desc;
1314 : char kidbuf[2+40+1];
1315 0 : const char *exactname = NULL;
1316 0 : char *searchkey = NULL;
1317 0 : char *hostport = NULL;
1318 0 : char *request = NULL;
1319 0 : estream_t fp = NULL;
1320 : int reselect;
1321 0 : char *httphost = NULL;
1322 : unsigned int httpflags;
1323 0 : unsigned int tries = SEND_REQUEST_RETRIES;
1324 :
1325 0 : *r_fp = NULL;
1326 :
1327 : /* Remove search type indicator and adjust PATTERN accordingly.
1328 : Note that HKP keyservers like the 0x to be present when searching
1329 : by keyid. We need to re-format the fingerprint and keyids so to
1330 : remove the gpg specific force-use-of-this-key flag ("!"). */
1331 0 : err = classify_user_id (keyspec, &desc, 1);
1332 0 : if (err)
1333 0 : return err;
1334 0 : switch (desc.mode)
1335 : {
1336 : case KEYDB_SEARCH_MODE_SHORT_KID:
1337 0 : snprintf (kidbuf, sizeof kidbuf, "0x%08lX", (ulong)desc.u.kid[1]);
1338 0 : break;
1339 : case KEYDB_SEARCH_MODE_LONG_KID:
1340 0 : snprintf (kidbuf, sizeof kidbuf, "0x%08lX%08lX",
1341 0 : (ulong)desc.u.kid[0], (ulong)desc.u.kid[1]);
1342 0 : break;
1343 : case KEYDB_SEARCH_MODE_FPR20:
1344 : case KEYDB_SEARCH_MODE_FPR:
1345 : /* This is a v4 fingerprint. */
1346 0 : kidbuf[0] = '0';
1347 0 : kidbuf[1] = 'x';
1348 0 : bin2hex (desc.u.fpr, 20, kidbuf+2);
1349 0 : break;
1350 :
1351 : case KEYDB_SEARCH_MODE_EXACT:
1352 0 : exactname = desc.u.name;
1353 0 : break;
1354 :
1355 : case KEYDB_SEARCH_MODE_FPR16:
1356 0 : log_error ("HKP keyservers do not support v3 fingerprints\n");
1357 : default:
1358 0 : return gpg_error (GPG_ERR_INV_USER_ID);
1359 : }
1360 :
1361 0 : searchkey = http_escape_string (exactname? exactname : kidbuf,
1362 : EXTRA_ESCAPE_CHARS);
1363 0 : if (!searchkey)
1364 : {
1365 0 : err = gpg_error_from_syserror ();
1366 0 : goto leave;
1367 : }
1368 :
1369 0 : reselect = 0;
1370 : again:
1371 : /* Build the request string. */
1372 0 : xfree (hostport); hostport = NULL;
1373 0 : xfree (httphost); httphost = NULL;
1374 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1375 : &hostport, &httpflags, &httphost);
1376 0 : if (err)
1377 0 : goto leave;
1378 :
1379 0 : xfree (request);
1380 0 : request = strconcat (hostport,
1381 : "/pks/lookup?op=get&options=mr&search=",
1382 : searchkey,
1383 : exactname? "&exact=on":"",
1384 : NULL);
1385 0 : if (!request)
1386 : {
1387 0 : err = gpg_error_from_syserror ();
1388 0 : goto leave;
1389 : }
1390 :
1391 : /* Send the request. */
1392 0 : err = send_request (ctrl, request, hostport, httphost, httpflags,
1393 : NULL, NULL, &fp, NULL);
1394 0 : if (handle_send_request_error (err, request, &tries))
1395 : {
1396 0 : reselect = 1;
1397 0 : goto again;
1398 : }
1399 0 : if (err)
1400 0 : goto leave;
1401 :
1402 0 : err = dirmngr_status (ctrl, "SOURCE", hostport, NULL);
1403 0 : if (err)
1404 0 : goto leave;
1405 :
1406 : /* Return the read stream and close the HTTP context. */
1407 0 : *r_fp = fp;
1408 0 : fp = NULL;
1409 :
1410 : leave:
1411 0 : es_fclose (fp);
1412 0 : xfree (request);
1413 0 : xfree (hostport);
1414 0 : xfree (httphost);
1415 0 : xfree (searchkey);
1416 0 : return err;
1417 : }
1418 :
1419 :
1420 :
1421 :
1422 : /* Callback parameters for put_post_cb. */
1423 : struct put_post_parm_s
1424 : {
1425 : char *datastring;
1426 : };
1427 :
1428 :
1429 : /* Helper for ks_hkp_put. */
1430 : static gpg_error_t
1431 0 : put_post_cb (void *opaque, http_t http)
1432 : {
1433 0 : struct put_post_parm_s *parm = opaque;
1434 0 : gpg_error_t err = 0;
1435 : estream_t fp;
1436 : size_t len;
1437 :
1438 0 : fp = http_get_write_ptr (http);
1439 0 : len = strlen (parm->datastring);
1440 :
1441 0 : es_fprintf (fp,
1442 : "Content-Type: application/x-www-form-urlencoded\r\n"
1443 : "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
1444 0 : http_start_data (http);
1445 0 : if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
1446 0 : err = gpg_error_from_syserror ();
1447 0 : return err;
1448 : }
1449 :
1450 :
1451 : /* Send the key in {DATA,DATALEN} to the keyserver identified by URI. */
1452 : gpg_error_t
1453 0 : ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
1454 : {
1455 : gpg_error_t err;
1456 0 : char *hostport = NULL;
1457 0 : char *request = NULL;
1458 0 : estream_t fp = NULL;
1459 : struct put_post_parm_s parm;
1460 0 : char *armored = NULL;
1461 : int reselect;
1462 0 : char *httphost = NULL;
1463 : unsigned int httpflags;
1464 0 : unsigned int tries = SEND_REQUEST_RETRIES;
1465 :
1466 0 : parm.datastring = NULL;
1467 :
1468 0 : err = armor_data (&armored, data, datalen);
1469 0 : if (err)
1470 0 : goto leave;
1471 :
1472 0 : parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
1473 0 : if (!parm.datastring)
1474 : {
1475 0 : err = gpg_error_from_syserror ();
1476 0 : goto leave;
1477 : }
1478 0 : xfree (armored);
1479 0 : armored = NULL;
1480 :
1481 : /* Build the request string. */
1482 0 : reselect = 0;
1483 : again:
1484 0 : xfree (hostport); hostport = NULL;
1485 0 : xfree (httphost); httphost = NULL;
1486 0 : err = make_host_part (ctrl, uri->scheme, uri->host, uri->port, reselect,
1487 : &hostport, &httpflags, &httphost);
1488 0 : if (err)
1489 0 : goto leave;
1490 :
1491 0 : xfree (request);
1492 0 : request = strconcat (hostport, "/pks/add", NULL);
1493 0 : if (!request)
1494 : {
1495 0 : err = gpg_error_from_syserror ();
1496 0 : goto leave;
1497 : }
1498 :
1499 : /* Send the request. */
1500 0 : err = send_request (ctrl, request, hostport, httphost, 0,
1501 : put_post_cb, &parm, &fp, NULL);
1502 0 : if (handle_send_request_error (err, request, &tries))
1503 : {
1504 0 : reselect = 1;
1505 0 : goto again;
1506 : }
1507 0 : if (err)
1508 0 : goto leave;
1509 :
1510 : leave:
1511 0 : es_fclose (fp);
1512 0 : xfree (parm.datastring);
1513 0 : xfree (armored);
1514 0 : xfree (request);
1515 0 : xfree (hostport);
1516 0 : xfree (httphost);
1517 0 : return err;
1518 : }
|