aboutsummaryrefslogtreecommitdiffstats
path: root/dirmngr
diff options
context:
space:
mode:
Diffstat (limited to 'dirmngr')
-rw-r--r--dirmngr/certcache.c36
-rw-r--r--dirmngr/dns.c80
-rw-r--r--dirmngr/domaininfo.c125
-rw-r--r--dirmngr/http.c23
-rw-r--r--dirmngr/ks-engine-hkp.c51
-rw-r--r--dirmngr/ks-engine-http.c4
-rw-r--r--dirmngr/ocsp.c109
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;