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