diff options
Diffstat (limited to 'dirmngr')
-rw-r--r-- | dirmngr/certcache.c | 36 | ||||
-rw-r--r-- | dirmngr/dns.c | 80 | ||||
-rw-r--r-- | dirmngr/domaininfo.c | 125 | ||||
-rw-r--r-- | dirmngr/http.c | 23 | ||||
-rw-r--r-- | dirmngr/ks-engine-hkp.c | 51 | ||||
-rw-r--r-- | dirmngr/ks-engine-http.c | 4 | ||||
-rw-r--r-- | dirmngr/ocsp.c | 109 |
7 files changed, 331 insertions, 97 deletions
diff --git a/dirmngr/certcache.c b/dirmngr/certcache.c index adb005ec8..5486997b6 100644 --- a/dirmngr/certcache.c +++ b/dirmngr/certcache.c @@ -1471,6 +1471,9 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) { ksba_cert_ref (ci->cert); release_cache_lock (); + if (DBG_LOOKUP) + log_debug ("%s: certificate found in the cache" + " via ocsp_certs\n", __func__); return ci->cert; /* We use this certificate. */ } release_cache_lock (); @@ -1478,7 +1481,7 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n"); } - /* No check whether the certificate is cached. */ + /* Now check whether the certificate is cached. */ for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++) { if (!keyid) @@ -1487,6 +1490,9 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) && !cmp_simple_canon_sexp (keyid, subj)) { xfree (subj); + if (DBG_LOOKUP) + log_debug ("%s: certificate found in the cache" + " via subject DN\n", __func__); break; /* Found matching cert. */ } xfree (subj); @@ -1495,6 +1501,34 @@ find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) if (cert) return cert; /* Done. */ + /* If we do not have a subject DN but have a keyid, try to locate it + * by keyid. */ + if (!subject_dn && keyid) + { + int i; + cert_item_t ci; + ksba_sexp_t ski; + + acquire_cache_read_lock (); + for (i=0; i < 256; i++) + for (ci=cert_cache[i]; ci; ci = ci->next) + if (ci->cert && !ksba_cert_get_subj_key_id (ci->cert, NULL, &ski)) + { + if (!cmp_simple_canon_sexp (keyid, ski)) + { + ksba_free (ski); + ksba_cert_ref (ci->cert); + release_cache_lock (); + if (DBG_LOOKUP) + log_debug ("%s: certificate found in the cache" + " via ski\n", __func__); + return ci->cert; + } + ksba_free (ski); + } + release_cache_lock (); + } + if (DBG_LOOKUP) log_debug ("find_cert_bysubject: certificate not in cache\n"); diff --git a/dirmngr/dns.c b/dirmngr/dns.c index fa5e5283d..142e8d2c1 100644 --- a/dirmngr/dns.c +++ b/dirmngr/dns.c @@ -2217,8 +2217,8 @@ static void dns_p_dump3(struct dns_packet *P, struct dns_rr_i *I, FILE *fp) { void dns_p_dump(struct dns_packet *P, FILE *fp) { - struct dns_rr_i _I = { 0 }; - dns_p_dump3(P, &_I, fp); + struct dns_rr_i I_instance = { 0 }; + dns_p_dump3(P, &I_instance, fp); } /* dns_p_dump() */ @@ -5275,8 +5275,8 @@ error: struct dns_packet *dns_hosts_query(struct dns_hosts *hosts, struct dns_packet *Q, int *error_) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - struct dns_packet *P = dns_p_init(&_P.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + struct dns_packet *P = dns_p_init(&P_instance.p, 512); struct dns_packet *A = 0; struct dns_rr rr; struct dns_hosts_entry *ent; @@ -6837,7 +6837,7 @@ unsigned dns_hints_grep(struct sockaddr **sa, socklen_t *sa_len, unsigned lim, s struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q, int *error_) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; struct dns_packet *A, *P; struct dns_rr rr; char zone[DNS_D_MAXNAME + 1]; @@ -6846,11 +6846,11 @@ struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q struct sockaddr *sa; socklen_t slen; int error; - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = DNS_S_QUESTION; + I_instance.section = DNS_S_QUESTION; - if (!dns_rr_grep(&rr, 1, &_I, Q, &error)) + if (!dns_rr_grep(&rr, 1, &I_instance, Q, &error)) goto error; if (!(zlen = dns_d_expand(zone, sizeof zone, rr.dn.p, Q, &error))) @@ -6858,7 +6858,7 @@ struct dns_packet *dns_hints_query(struct dns_hints *hints, struct dns_packet *Q else if (zlen >= sizeof zone) goto toolong; - P = dns_p_init(&_P.p, 512); + P = dns_p_init(&P_instance.p, 512); dns_header(P)->qr = 1; if ((error = dns_rr_copy(P, &rr, Q))) @@ -8463,8 +8463,8 @@ error: static struct dns_packet *dns_res_glue(struct dns_resolver *R, struct dns_packet *Q) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - struct dns_packet *P = dns_p_init(&_P.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + struct dns_packet *P = dns_p_init(&P_instance.p, 512); char qname[DNS_D_MAXNAME + 1]; size_t qlen; enum dns_type qtype; @@ -8537,20 +8537,20 @@ static int dns_res_nameserv_cmp(struct dns_rr *a, struct dns_rr *b, struct dns_r int cmp, error; if (!(error = dns_ns_parse(&ns, a, P))) { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = (DNS_S_ALL & ~DNS_S_QD); - _I.name = ns.host; - _I.type = DNS_T_A; - glued[0] = !!dns_rr_grep(&x, 1, &_I, P, &error); + I_instance.section = (DNS_S_ALL & ~DNS_S_QD); + I_instance.name = ns.host; + I_instance.type = DNS_T_A; + glued[0] = !!dns_rr_grep(&x, 1, &I_instance, P, &error); } if (!(error = dns_ns_parse(&ns, b, P))) { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = (DNS_S_ALL & ~DNS_S_QD); - _I.name = ns.host; - _I.type = DNS_T_A; - glued[1] = !!dns_rr_grep(&y, 1, &_I, P, &error); + I_instance.section = (DNS_S_ALL & ~DNS_S_QD); + I_instance.name = ns.host; + I_instance.type = DNS_T_A; + glued[1] = !!dns_rr_grep(&y, 1, &I_instance, P, &error); } if ((cmp = glued[1] - glued[0])) { return cmp; @@ -9916,13 +9916,13 @@ exec: return dns_ai_setent(ent, &any, rr.type, ai); case DNS_AI_S_SUBMIT_G: { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; - _I.section = DNS_S_QD; - _I.name = ai->g.name; - _I.type = ai->g.type; + I_instance.section = DNS_S_QD; + I_instance.name = ai->g.name; + I_instance.type = ai->g.type; /* skip if already queried */ - if (dns_rr_grep(&rr, 1, &_I, ai->glue, &error)) + if (dns_rr_grep(&rr, 1, &I_instance, ai->glue, &error)) dns_ai_goto(DNS_AI_S_FOREACH_I); /* skip if we recursed (CNAME chains should have been handled in the resolver) */ if (++ai->g_depth > 1) @@ -10598,7 +10598,7 @@ static struct dns_trace *trace(const char *mode) { static void print_packet(struct dns_packet *P, FILE *fp) { - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; I.sort = MAIN.sort; dns_p_dump3(P, &I, fp); @@ -10608,10 +10608,10 @@ static void print_packet(struct dns_packet *P, FILE *fp) { static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _Q = { 0 }; - struct dns_packet *P = dns_p_init(&_P.p, 512); - struct dns_packet *Q = dns_p_init(&_Q.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } Q_instance = { 0 }; + struct dns_packet *P = dns_p_init(&P_instance.p, 512); + struct dns_packet *Q = dns_p_init(&Q_instance.p, 512); enum dns_section section; struct dns_rr rr; int error; @@ -10655,7 +10655,7 @@ static int parse_packet(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { const char *dn = "ns8.yahoo.com"; char *_name = dns_d_init(_p, sizeof _p, dn, strlen (dn), DNS_D_ANCHOR); struct dns_rr rrset[32]; - struct dns_rr_i _I = { 0 }; + struct dns_rr_i I_instance = { 0 }; struct dns_rr_i *rri = &I; unsigned rrcount = dns_rr_grep(rrset, lengthof(rrset), rri, Q, &error); @@ -10818,8 +10818,8 @@ static int show_hosts(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { static int query_hosts(int argc, char *argv[]) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _Q = { 0 }; - struct dns_packet *Q = dns_p_init(&_Q.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } Q_instance = { 0 }; + struct dns_packet *Q = dns_p_init(&Q_instance.p, 512); struct dns_packet *A; char qname[DNS_D_MAXNAME + 1]; size_t qlen; @@ -10937,8 +10937,8 @@ static int dump_random(int argc, char *argv[]) { static int send_query(int argc, char *argv[]) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _Q = { 0 }; - struct dns_packet *A, *Q = dns_p_init(&_Q.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } Q_instance = { 0 }; + struct dns_packet *A, *Q = dns_p_init(&Q_instance.p, 512); char host[INET6_ADDRSTRLEN + 1]; struct sockaddr_storage ss; struct dns_socket *so; @@ -11033,10 +11033,10 @@ static int show_hints(int argc, char *argv[]) { if (0 == strcmp(how, "plain")) { dns_hints_dump(hints, stdout); } else { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; struct dns_packet *query, *answer; - query = dns_p_init(&_P.p, 512); + query = dns_p_init(&P_instance.p, 512); if ((error = dns_p_push(query, DNS_S_QUESTION, who, strlen(who), DNS_T_A, DNS_C_IN, 0, 0))) panic("%s: %s", who, dns_strerror(error)); @@ -11199,8 +11199,8 @@ static int echo_port(int argc DNS_NOTUSED, char *argv[] DNS_NOTUSED) { panic("127.0.0.1:5353: %s", dns_strerror(errno)); for (;;) { - union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } _P = { 0 }; - struct dns_packet *pkt = dns_p_init(&_P.p, 512); + union { unsigned char b[dns_p_calcsize((512))]; struct dns_packet p; } P_instance = { 0 }; + struct dns_packet *pkt = dns_p_init(&P_instance.p, 512); struct sockaddr_storage ss; socklen_t slen = sizeof ss; ssize_t count; diff --git a/dirmngr/domaininfo.c b/dirmngr/domaininfo.c index f6263b06d..b41aef366 100644 --- a/dirmngr/domaininfo.c +++ b/dirmngr/domaininfo.c @@ -47,6 +47,7 @@ struct domaininfo_s unsigned int wkd_not_found:1; /* A WKD query failed. */ unsigned int wkd_supported:1; /* One WKD entry was found. */ unsigned int wkd_not_supported:1; /* Definitely does not support WKD. */ + unsigned int keepmark:1; /* Private to insert_or_update(). */ char name[1]; }; typedef struct domaininfo_s *domaininfo_t; @@ -143,7 +144,10 @@ insert_or_update (const char *domain, { domaininfo_t di; domaininfo_t di_new; - domaininfo_t di_cut; + domaininfo_t drop = NULL; + domaininfo_t drop_extra = NULL; + int nkept = 0; + int ndropped = 0; u32 hash; int count; @@ -162,7 +166,6 @@ insert_or_update (const char *domain, /* Need to do another lookup because the malloc is a system call and * thus the hash array may have been changed by another thread. */ - di_cut = NULL; for (count=0, di = domainbuckets[hash]; di; di = di->next, count++) if (!strcmp (di->name, domain)) { @@ -172,16 +175,89 @@ insert_or_update (const char *domain, } /* Before we insert we need to check whether the chain gets too long. */ - di_cut = NULL; if (count >= MAX_DOMAINBUCKET_LEN) { - for (count=0, di = domainbuckets[hash]; di; di = di->next, count++) - if (count >= MAX_DOMAINBUCKET_LEN/2) - { - di_cut = di->next; - di->next = NULL; - break; - } + domaininfo_t bucket; + domaininfo_t *array; + int narray, idx; + domaininfo_t keep = NULL; + + /* Unlink from the global list before doing a syscall. */ + bucket = domainbuckets[hash]; + domainbuckets[hash] = NULL; + + array = xtrycalloc (count, sizeof *array); + if (!array) + { + /* That's bad; give up the entire bucket. */ + log_error ("domaininfo: error allocating helper array: %s\n", + gpg_strerror (gpg_err_code_from_syserror ())); + drop_extra = bucket; + goto leave; + } + narray = 0; + + /* Move all items into an array for easier processing. */ + for (di = bucket; di; di = di->next) + array[narray++] = di; + log_assert (narray == count); + + /* Mark all item in the array which are flagged to support wkd + * but not more than half of the maximum. This way we will at + * the end drop half of the items. */ + count = 0; + for (idx=0; idx < narray; idx++) + { + di = array[idx]; + di->keepmark = 0; /* Clear flag here on the first pass. */ + if (di->wkd_supported && count < MAX_DOMAINBUCKET_LEN/2) + { + di->keepmark = 1; + count++; + } + } + /* Now mark those which are marked as not found. */ + /* FIXME: we should use an LRU algorithm here. */ + for (idx=0; idx < narray; idx++) + { + di = array[idx]; + if (!di->keepmark + && di->wkd_not_supported && count < MAX_DOMAINBUCKET_LEN/2) + { + di->keepmark = 1; + count++; + } + } + + /* Build a bucket list and a second list for later freeing the + * items (we can't do it directly because a free is a system + * call and we want to avoid locks in this module. Note that + * the kept items will be reversed order which does not matter. */ + for (idx=0; idx < narray; idx++) + { + di = array[idx]; + if (di->keepmark) + { + di->next = keep; + keep = di; + nkept++; + } + else + { + di->next = drop; + drop = di; + ndropped++; + } + } + + /* In case another thread added new stuff to the domain list we + * simply drop them instead all. It would also be possible to + * append them to our list but then we can't guarantee that a + * bucket list is almost all of the time limited to + * MAX_DOMAINBUCKET_LEN. Not sure whether this is really a + * sensible strategy. */ + drop_extra = domainbuckets[hash]; + domainbuckets[hash] = keep; } /* Insert */ @@ -190,17 +266,28 @@ insert_or_update (const char *domain, di->next = domainbuckets[hash]; domainbuckets[hash] = di; - /* Remove the rest of the cutted chain. */ - while (di_cut) + if (opt.verbose && (nkept || ndropped)) + log_info ("domaininfo: bucket=%lu kept=%d purged=%d\n", + (unsigned long)hash, nkept, ndropped); + + leave: + /* Remove the dropped items. */ + while (drop) + { + di = drop->next; + xfree (drop); + drop = di; + } + while (drop_extra) { - di = di_cut->next; - xfree (di_cut); - di_cut = di; + di = drop_extra->next; + xfree (drop_extra); + drop_extra = di; } } -/* Helper for domaininfo_set_no_name. */ +/* Helper for domaininfo_set_no_name. May not do any syscalls. */ static void set_no_name_cb (domaininfo_t di, int insert_mode) { @@ -224,7 +311,7 @@ domaininfo_set_no_name (const char *domain) } -/* Helper for domaininfo_set_wkd_supported. */ +/* Helper for domaininfo_set_wkd_supported. May not do any syscalls. */ static void set_wkd_supported_cb (domaininfo_t di, int insert_mode) { @@ -245,7 +332,7 @@ domaininfo_set_wkd_supported (const char *domain) } -/* Helper for domaininfo_set_wkd_not_supported. */ +/* Helper for domaininfo_set_wkd_not_supported. May not do any syscalls. */ static void set_wkd_not_supported_cb (domaininfo_t di, int insert_mode) { @@ -265,7 +352,7 @@ domaininfo_set_wkd_not_supported (const char *domain) -/* Helper for domaininfo_set_wkd_not_found. */ +/* Helper for domaininfo_set_wkd_not_found. May not do any syscalls. */ static void set_wkd_not_found_cb (domaininfo_t di, int insert_mode) { diff --git a/dirmngr/http.c b/dirmngr/http.c index d6856fe05..81b7ba897 100644 --- a/dirmngr/http.c +++ b/dirmngr/http.c @@ -3536,8 +3536,13 @@ same_host_p (parsed_uri_t a, parsed_uri_t b) { "protonmail.com", "api.protonmail.com" }, { NULL, "api.protonmail.ch" }, { "protonmail.ch", "api.protonmail.com" }, - { NULL, "api.protonmail.ch" } + { NULL, "api.protonmail.ch" }, + { "pm.me", "api.protonmail.ch" } }; + static const char *subdomains[] = + { + "openpgpkey." + }; int i; const char *from; @@ -3559,6 +3564,22 @@ same_host_p (parsed_uri_t a, parsed_uri_t b) return 1; } + /* Also consider hosts the same if they differ only in a subdomain; + * in both direction. This allows to have redirection between the + * WKD advanced and direct lookup methods. */ + for (i=0; i < DIM (subdomains); i++) + { + const char *subdom = subdomains[i]; + size_t subdomlen = strlen (subdom); + + if (!ascii_strncasecmp (a->host, subdom, subdomlen) + && !ascii_strcasecmp (a->host + subdomlen, b->host)) + return 1; + if (!ascii_strncasecmp (b->host, subdom, subdomlen) + && !ascii_strcasecmp (b->host + subdomlen, a->host)) + return 1; + } + return 0; } diff --git a/dirmngr/ks-engine-hkp.c b/dirmngr/ks-engine-hkp.c index 4d660b87e..f8814ecd0 100644 --- a/dirmngr/ks-engine-hkp.c +++ b/dirmngr/ks-engine-hkp.c @@ -68,6 +68,10 @@ /* Number of retries done for a dead host etc. */ #define SEND_REQUEST_RETRIES 3 +/* Number of retries done in case of transient errors. */ +#define SEND_REQUEST_EXTRA_RETRIES 5 + + enum ks_protocol { KS_PROTOCOL_HKP, KS_PROTOCOL_HKPS, KS_PROTOCOL_MAX }; /* Objects used to maintain information about hosts. */ @@ -1217,6 +1221,7 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, /* FIXME: I am not sure whey we allow a downgrade for hkp requests. * Needs at least an explanation here.. */ + once_more: err = http_session_new (&session, httphost, ((ctrl->http_no_crl? HTTP_FLAG_NO_CRL : 0) | HTTP_FLAG_TRUST_DEF), @@ -1226,7 +1231,6 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, http_session_set_log_cb (session, cert_log_cb); http_session_set_timeout (session, ctrl->timeout); - once_more: err = http_open (ctrl, &http, post_cb? HTTP_REQ_POST : HTTP_REQ_GET, request, @@ -1306,6 +1310,8 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, request = request_buffer; http_close (http, 0); http = NULL; + http_session_release (session); + session = NULL; } goto once_more; @@ -1313,6 +1319,10 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; + case 413: /* Payload too large */ + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + default: log_error (_("error accessing '%s': http status %u\n"), request, http_get_status_code (http)); @@ -1349,10 +1359,12 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr, with REQUEST. The function returns true if the caller shall try again. TRIES_LEFT points to a variable to track the number of retries; this function decrements it and won't return true if it is - down to zero. */ + down to zero. EXTRA_TRIES_LEFT does the same but only for + transient http status codes. */ static int handle_send_request_error (ctrl_t ctrl, gpg_error_t err, const char *request, - unsigned int http_status, unsigned int *tries_left) + unsigned int http_status, unsigned int *tries_left, + unsigned int *extra_tries_left) { int retry = 0; @@ -1408,9 +1420,12 @@ handle_send_request_error (ctrl_t ctrl, gpg_error_t err, const char *request, case 503: /* Service Unavailable */ case 504: /* Gateway Timeout */ - log_info ("selecting a different host due to a %u (%s)", - http_status, http_status2string (http_status)); - retry = 1; + if (*extra_tries_left) + { + log_info ("selecting a different host due to a %u (%s)", + http_status, http_status2string (http_status)); + retry = 2; + } break; } } @@ -1420,8 +1435,16 @@ handle_send_request_error (ctrl_t ctrl, gpg_error_t err, const char *request, break; } - if (*tries_left) - --*tries_left; + if (retry == 2) + { + if (*extra_tries_left) + --*extra_tries_left; + } + else + { + if (*tries_left) + --*tries_left; + } return retry; } @@ -1446,6 +1469,7 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, char *httphost = NULL; unsigned int http_status; unsigned int tries = SEND_REQUEST_RETRIES; + unsigned int extra_tries = SEND_REQUEST_EXTRA_RETRIES; *r_fp = NULL; @@ -1521,7 +1545,8 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern, /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, httpflags, NULL, NULL, &fp, &http_status); - if (handle_send_request_error (ctrl, err, request, http_status, &tries)) + if (handle_send_request_error (ctrl, err, request, http_status, + &tries, &extra_tries)) { reselect = 1; goto again; @@ -1591,6 +1616,7 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) unsigned int httpflags; unsigned int http_status; unsigned int tries = SEND_REQUEST_RETRIES; + unsigned int extra_tries = SEND_REQUEST_EXTRA_RETRIES; *r_fp = NULL; @@ -1664,7 +1690,8 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp) /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, httpflags, NULL, NULL, &fp, &http_status); - if (handle_send_request_error (ctrl, err, request, http_status, &tries)) + if (handle_send_request_error (ctrl, err, request, http_status, + &tries, &extra_tries)) { reselect = 1; goto again; @@ -1740,6 +1767,7 @@ ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) unsigned int httpflags; unsigned int http_status; unsigned int tries = SEND_REQUEST_RETRIES; + unsigned int extra_tries = SEND_REQUEST_EXTRA_RETRIES; parm.datastring = NULL; @@ -1778,7 +1806,8 @@ ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen) /* Send the request. */ err = send_request (ctrl, request, hostport, httphost, 0, put_post_cb, &parm, &fp, &http_status); - if (handle_send_request_error (ctrl, err, request, http_status, &tries)) + if (handle_send_request_error (ctrl, err, request, http_status, + &tries, &extra_tries)) { reselect = 1; goto again; diff --git a/dirmngr/ks-engine-http.c b/dirmngr/ks-engine-http.c index 0f3e2db4a..a84a3a1ea 100644 --- a/dirmngr/ks-engine-http.c +++ b/dirmngr/ks-engine-http.c @@ -174,6 +174,10 @@ ks_http_fetch (ctrl_t ctrl, const char *url, unsigned int flags, } goto once_more; + case 413: /* Payload too large */ + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + default: log_error (_("error accessing '%s': http status %u\n"), url, http_get_status_code (http)); diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c index 79c252d87..e19779c59 100644 --- a/dirmngr/ocsp.c +++ b/dirmngr/ocsp.c @@ -116,10 +116,15 @@ read_response (estream_t fp, unsigned char **r_buffer, size_t *r_buflen) /* Construct an OCSP request, send it to the configured OCSP responder and parse the response. On success the OCSP context may be used to - further process the response. */ + further process the response. The signature value and the + production date are returned at R_SIGVAL and R_PRODUCED_AT; they + may be NULL or an empty string if not available. A new hash + context is returned at R_MD. */ static gpg_error_t -do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, - const char *url, ksba_cert_t cert, ksba_cert_t issuer_cert) +do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, + const char *url, ksba_cert_t cert, ksba_cert_t issuer_cert, + ksba_sexp_t *r_sigval, ksba_isotime_t r_produced_at, + gcry_md_hd_t *r_md) { gpg_error_t err; unsigned char *request, *response; @@ -132,6 +137,10 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, (void)ctrl; + *r_sigval = NULL; + *r_produced_at = 0; + *r_md = NULL; + if (dirmngr_use_tor ()) { /* For now we do not allow OCSP via Tor due to possible privacy @@ -238,6 +247,10 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, } break; + case 413: /* Payload too large */ + err = gpg_error (GPG_ERR_TOO_LARGE); + break; + default: log_error (_("error accessing '%s': http status %u\n"), url, http_get_status_code (http)); @@ -259,6 +272,7 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, xfree (free_this); return err; } + /* log_printhex (response, responselen, "ocsp response"); */ err = ksba_ocsp_parse_response (ocsp, response, responselen, &response_status); @@ -286,11 +300,34 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, } if (response_status == KSBA_OCSP_RSPSTATUS_SUCCESS) { + int hash_algo; + if (opt.verbose) log_info (_("OCSP responder at '%s' status: %s\n"), url, t); + /* Get the signature value now because we can all this fucntion + * only once. */ + *r_sigval = ksba_ocsp_get_sig_val (ocsp, r_produced_at); + + hash_algo = hash_algo_from_sigval (*r_sigval); + if (!hash_algo) + { + if (opt.verbose) + log_info ("ocsp: using SHA-256 as fallback hash algo.\n"); + hash_algo = GCRY_MD_SHA256; + } + err = gcry_md_open (r_md, hash_algo, 0); + if (err) + { + log_error (_("failed to establish a hashing context for OCSP: %s\n"), + gpg_strerror (err)); + goto leave; + } + if (DBG_HASHING) + gcry_md_debug (*r_md, "ocsp"); + err = ksba_ocsp_hash_response (ocsp, response, responselen, - HASH_FNC, md); + HASH_FNC, *r_md); if (err) log_error (_("hashing the OCSP response for '%s' failed: %s\n"), url, gpg_strerror (err)); @@ -301,8 +338,17 @@ do_ocsp_request (ctrl_t ctrl, ksba_ocsp_t ocsp, gcry_md_hd_t md, err = gpg_error (GPG_ERR_GENERAL); } + leave: xfree (response); xfree (free_this); + if (err) + { + xfree (*r_sigval); + *r_sigval = NULL; + *r_produced_at = 0; + gcry_md_close (*r_md); + *r_md = NULL; + } return err; } @@ -387,7 +433,7 @@ check_signature_core (ctrl_t ctrl, ksba_cert_t cert, gcry_sexp_t s_sig, /* We simply ignore all errors. */ gcry_sexp_release (s_pkey); - return -1; + return err; } @@ -406,18 +452,27 @@ check_signature (ctrl_t ctrl, int algo, cert_idx; gcry_sexp_t s_hash; ksba_cert_t cert; + const char *s; /* Create a suitable S-expression with the hash value of our response. */ gcry_md_final (md); algo = gcry_md_get_algo (md); - if (algo != GCRY_MD_SHA1 ) + s = gcry_md_algo_name (algo); + if (algo && s && strlen (s) < 16) { - log_error (_("only SHA-1 is supported for OCSP responses\n")); - return gpg_error (GPG_ERR_DIGEST_ALGO); + char hashalgostr[16+1]; + int i; + + for (i=0; s[i]; i++) + hashalgostr[i] = ascii_tolower (s[i]); + hashalgostr[i] = 0; + err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", + hashalgostr, + (int)gcry_md_get_algo_dlen (algo), + gcry_md_read (md, algo)); } - err = gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash sha1 %b))", - gcry_md_get_algo_dlen (algo), - gcry_md_read (md, algo)); + else + err = gpg_error (GPG_ERR_DIGEST_ALGO); if (err) { log_error (_("creating S-expression failed: %s\n"), gcry_strerror (err)); @@ -461,6 +516,7 @@ check_signature (ctrl_t ctrl, { cert_ref_t cref; + /* dump_cert ("from ocsp response", cert); */ cref = xtrymalloc (sizeof *cref); if (!cref) log_error (_("allocating list item failed: %s\n"), @@ -496,8 +552,6 @@ check_signature (ctrl_t ctrl, } log_printf ("not found\n"); } - ksba_free (name); - ksba_free (keyid); if (cert) { @@ -506,10 +560,24 @@ check_signature (ctrl_t ctrl, ksba_cert_release (cert); if (!err) { + ksba_free (name); + ksba_free (keyid); gcry_sexp_release (s_hash); return 0; /* Successfully verified the signature. */ } + log_error ("responder certificate "); + if (name) + log_printf ("'/%s' ", name); + if (keyid) + { + log_printf ("{"); + dump_serial (keyid); + log_printf ("} "); + } + log_printf ("did not verify: %s\n", gpg_strerror (err)); } + ksba_free (name); + ksba_free (keyid); } gcry_sexp_release (s_hash); @@ -584,8 +652,6 @@ ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, goto leave; } - - /* Figure out the OCSP responder to use. 1. Try to get the reponder from the certificate. We do only take http and https style URIs into account. @@ -642,14 +708,8 @@ ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, } /* Ask the OCSP responder. */ - err = gcry_md_open (&md, GCRY_MD_SHA1, 0); - if (err) - { - log_error (_("failed to establish a hashing context for OCSP: %s\n"), - gpg_strerror (err)); - goto leave; - } - err = do_ocsp_request (ctrl, ocsp, md, url, cert, issuer_cert); + err = do_ocsp_request (ctrl, ocsp, url, cert, issuer_cert, + &sigval, produced_at, &md); if (err) goto leave; @@ -681,8 +741,7 @@ ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr, } /* We got a useful answer, check that the answer has a valid signature. */ - sigval = ksba_ocsp_get_sig_val (ocsp, produced_at); - if (!sigval || !*produced_at) + if (!sigval || !*produced_at || !md) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; |