diff options
Diffstat (limited to 'dirmngr/ks-engine-hkp.c')
-rw-r--r-- | dirmngr/ks-engine-hkp.c | 294 |
1 files changed, 211 insertions, 83 deletions
diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index 356f64348..662e9e4cb 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -37,6 +37,117 @@ #define MAX_REDIRECTS 2 +/* Send an HTTP request. On success returns an estream object at + R_FP. HOSTPORTSTR is only used for diagnostics. */ +static gpg_error_t +send_request (ctrl_t ctrl, const char *request, const char *hostportstr, + estream_t *r_fp) +{ + gpg_error_t err; + http_t http = NULL; + int redirects_left = MAX_REDIRECTS; + estream_t fp = NULL; + char *request_buffer = NULL; + + *r_fp = NULL; + once_more: + err = http_open (&http, HTTP_REQ_GET, request, + /* fixme: AUTH */ NULL, + 0, + /* fixme: proxy*/ NULL, + NULL, NULL, + /*FIXME curl->srvtag*/NULL); + if (!err) + { + fp = http_get_write_ptr (http); + /* Avoid caches to get the most recent copy of the key. We set + both the Pragma and Cache-Control versions of the header, so + we're good with both HTTP 1.0 and 1.1. */ + es_fputs ("Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n", fp); + http_start_data (http); + if (es_ferror (fp)) + err = gpg_error_from_syserror (); + } + if (err) + { + /* Fixme: After a redirection we show the old host name. */ + log_error (_("error connecting to `%s': %s\n"), + hostportstr, gpg_strerror (err)); + goto leave; + } + + /* Wait for the response. */ + dirmngr_tick (ctrl); + err = http_wait_response (http); + if (err) + { + log_error (_("error reading HTTP response for `%s': %s\n"), + hostportstr, gpg_strerror (err)); + goto leave; + } + + switch (http_get_status_code (http)) + { + case 200: + err = 0; + break; /* Success. */ + + case 301: + case 302: + { + const char *s = http_get_header (http, "Location"); + + log_info (_("URL `%s' redirected to `%s' (%u)\n"), + request, s?s:"[none]", http_get_status_code (http)); + if (s && *s && redirects_left-- ) + { + xfree (request_buffer); + request_buffer = xtrystrdup (s); + if (request_buffer) + { + request = request_buffer; + http_close (http, 0); + http = NULL; + goto once_more; + } + err = gpg_error_from_syserror (); + } + else + err = gpg_error (GPG_ERR_NO_DATA); + log_error (_("too many redirections\n")); + } + goto leave; + + default: + log_error (_("error accessing `%s': http status %u\n"), + request, http_get_status_code (http)); + err = gpg_error (GPG_ERR_NO_DATA); + goto leave; + } + + fp = http_get_read_ptr (http); + if (!fp) + { + err = gpg_error (GPG_ERR_BUG); + goto leave; + } + + /* Return the read stream and close the HTTP context. */ + *r_fp = fp; + fp = NULL; + http_close (http, 1); + http = NULL; + + leave: + es_fclose (fp); + http_close (http, 0); + xfree (request_buffer); + return err; +} + + + /* Search the keyserver identified by URI for keys matching PATTERN. On success R_FP has an open stream to read the data. */ gpg_error_t @@ -48,10 +159,8 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, char fprbuf[2+40+1]; const char *scheme; char portstr[10]; - http_t http = NULL; char *hostport = NULL; char *request = NULL; - int redirects_left = MAX_REDIRECTS; estream_t fp = NULL; *r_fp = NULL; @@ -142,87 +251,11 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, } /* Send the request. */ - once_more: - err = http_open (&http, HTTP_REQ_GET, request, - /* fixme: AUTH */ NULL, - 0, - /* fixme: proxy*/ NULL, - NULL, NULL, - /*FIXME curl->srvtag*/NULL); - if (!err) - { - fp = http_get_write_ptr (http); - /* Avoid caches to get the most recent copy of the key. We set - both the Pragma and Cache-Control versions of the header, so - we're good with both HTTP 1.0 and 1.1. */ - es_fputs ("Pragma: no-cache\r\n" - "Cache-Control: no-cache\r\n", fp); - http_start_data (http); - if (es_ferror (fp)) - err = gpg_error_from_syserror (); - } + err = send_request (ctrl, request, hostport, &fp); if (err) - { - /* Fixme: After a redirection we show the old host name. */ - log_error (_("error connecting to `%s': %s\n"), - hostport, gpg_strerror (err)); - goto leave; - } - - /* Wait for the response. */ - dirmngr_tick (ctrl); - err = http_wait_response (http); - if (err) - { - log_error (_("error reading HTTP response for `%s': %s\n"), - hostport, gpg_strerror (err)); - goto leave; - } - - switch (http_get_status_code (http)) - { - case 200: - break; /* Success. */ - - case 301: - case 302: - { - const char *s = http_get_header (http, "Location"); - - log_info (_("URL `%s' redirected to `%s' (%u)\n"), - request, s?s:"[none]", http_get_status_code (http)); - if (s && *s && redirects_left-- ) - { - xfree (request); - request = xtrystrdup (s); - if (request) - { - http_close (http, 0); - http = NULL; - goto once_more; - } - err = gpg_error_from_syserror (); - } - else - err = gpg_error (GPG_ERR_NO_DATA); - log_error (_("too many redirections\n")); - } - goto leave; - - default: - log_error (_("error accessing `%s': http status %u\n"), - request, http_get_status_code (http)); - err = gpg_error (GPG_ERR_NO_DATA); - goto leave; - } + goto leave; /* Start reading the response. */ - fp = http_get_read_ptr (http); - if (!fp) - { - err = gpg_error (GPG_ERR_BUG); - goto leave; - } { int c = es_getc (fp); if (c == -1) @@ -241,15 +274,110 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, es_ungetc (c, fp); } + /* Return the read stream. */ + *r_fp = fp; + fp = NULL; + + leave: + es_fclose (fp); + xfree (request); + xfree (hostport); + return err; +} + + +/* Get the key described key the KEYSPEC string from the keyserver + identified by URI. On success R_FP has an open stream to read the + data. */ +gpg_error_t +ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) +{ + gpg_error_t err; + KEYDB_SEARCH_DESC desc; + char kidbuf[8+1]; + const char *scheme; + char portstr[10]; + char *hostport = NULL; + char *request = NULL; + estream_t fp = NULL; + + *r_fp = NULL; + + /* Remove search type indicator and adjust PATTERN accordingly. + Note that HKP keyservers like the 0x to be present when searching + by keyid. We need to re-format the fingerprint and keyids so to + remove the gpg specific force-use-of-this-key flag ("!"). */ + err = classify_user_id (keyspec, &desc); + if (err) + return err; + switch (desc.mode) + { + case KEYDB_SEARCH_MODE_SHORT_KID: + case KEYDB_SEARCH_MODE_LONG_KID: + snprintf (kidbuf, sizeof kidbuf, "%08lX", (ulong)desc.u.kid[1]); + break; + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + /* This is a v4 fingerprint. Take the last 8 hex digits from + the fingerprint which is the expected short keyid. */ + bin2hex (desc.u.fpr+16, 4, kidbuf); + break; + + case KEYDB_SEARCH_MODE_FPR16: + log_error ("HKP keyserver do not support v3 fingerprints\n"); + default: + return gpg_error (GPG_ERR_INV_USER_ID); + } + + /* Map scheme and port. */ + if (!strcmp (uri->scheme,"hkps") || !strcmp (uri->scheme,"https")) + { + scheme = "https"; + strcpy (portstr, "443"); + } + else /* HKP or HTTP. */ + { + scheme = "http"; + strcpy (portstr, "11371"); + } + if (uri->port) + snprintf (portstr, sizeof portstr, "%hu", uri->port); + else + {} /*fixme_do_srv_lookup ()*/ + + /* Build the request string. */ + { + hostport = strconcat (scheme, "://", + *uri->host? uri->host: "localhost", + ":", portstr, NULL); + if (!hostport) + { + err = gpg_error_from_syserror (); + goto leave; + } + + request = strconcat (hostport, + "/pks/lookup?op=get&options=mr&search=0x", + kidbuf, + NULL); + if (!request) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + + /* Send the request. */ + err = send_request (ctrl, request, hostport, &fp); + if (err) + goto leave; + /* Return the read stream and close the HTTP context. */ *r_fp = fp; fp = NULL; - http_close (http, 1); - http = NULL; leave: es_fclose (fp); - http_close (http, 0); xfree (request); xfree (hostport); return err; |