From bfb1bb119241d85bb9b400881328496e12a39aed Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Mon, 5 Aug 2019 18:11:13 +0100 Subject: Migrate SSL certificate storage to the browser window * Fetchers now provide the certificates before headers * This is propagated all the way to the browser window * When a query occurs, we retrieve it from there and fire the query with those stored certificates. * The serial number is a bignum, store it as hex. Signed-off-by: Daniel Silverstone --- content/content.c | 3 + content/content.h | 7 ++ content/fetch.h | 8 +- content/fetchers/curl.c | 251 +++++++++++++++++++++++++----------------------- content/hlcache.c | 12 +++ content/llcache.c | 21 ++-- content/llcache.h | 5 + 7 files changed, 177 insertions(+), 130 deletions(-) (limited to 'content') diff --git a/content/content.c b/content/content.c index bb69c766d..dc9805448 100644 --- a/content/content.c +++ b/content/content.c @@ -142,6 +142,9 @@ nserror content_llcache_callback(llcache_handle *llcache, nserror error = NSERROR_OK; switch (event->type) { + case LLCACHE_EVENT_GOT_CERTS: + /* Will never happen: handled in hlcache */ + break; case LLCACHE_EVENT_HAD_HEADERS: /* Will never happen: handled in hlcache */ break; diff --git a/content/content.h b/content/content.h index 4fdcaf2c0..6e298e045 100644 --- a/content/content.h +++ b/content/content.h @@ -44,6 +44,7 @@ struct object_params; struct rect; struct redraw_context; struct llcache_query_msg; +struct ssl_cert_info; /** Status of a content */ typedef enum { @@ -59,6 +60,7 @@ typedef enum { /** Used in callbacks to indicate what has occurred. */ typedef enum { CONTENT_MSG_LOG, /**< Content wishes to log something */ + CONTENT_MSG_SSL_CERTS, /**< Content is from SSL and this is its chain */ CONTENT_MSG_QUERY, /**< Something under the content has a query */ CONTENT_MSG_QUERY_FINISHED, /**< Something under the content finished its query */ CONTENT_MSG_LOADING, /**< fetching or converting */ @@ -107,6 +109,11 @@ union content_msg_data { size_t msglen; /**< The length of that message */ browser_window_console_flags flags; /**< The flags of the logging */ } log; + /** CONTENT_MSG_SSL_CERTS - The certificate chain from the underlying fetch */ + struct { + const struct ssl_cert_info *certs; /**< The chain */ + size_t num; /**< The number of certs in the chain */ + } certs; /** CONTENT_MSG_QUERY - Query from underlying object somewhere */ const struct llcache_query_msg *query_msg; /** CONTENT_MSG_QUERY_FINISHED - Query from underlying object finished */ diff --git a/content/fetch.h b/content/fetch.h index 0b4b52a2f..174e07bf6 100644 --- a/content/fetch.h +++ b/content/fetch.h @@ -42,6 +42,7 @@ typedef enum { FETCH_ERROR, FETCH_REDIRECT, FETCH_NOTMODIFIED, + FETCH_CERTS, FETCH_AUTH, FETCH_CERT_ERR, FETCH_SSL_ERR @@ -70,7 +71,7 @@ typedef struct fetch_msg { struct { const struct ssl_cert_info *certs; size_t num_certs; - } cert_err; + } certs; } data; } fetch_msg; @@ -95,12 +96,15 @@ struct ssl_cert_info { char not_before[32]; /**< Valid from date */ char not_after[32]; /**< Valid to date */ int sig_type; /**< Signature type */ - long serial; /**< Serial number */ + char serialnum[64]; /**< Serial number */ char issuer[256]; /**< Issuer details */ char subject[256]; /**< Subject details */ int cert_type; /**< Certificate type */ }; +/** maximum number of X509 certificates in chain for TLS connection */ +#define MAX_SSL_CERTS 10 + typedef void (*fetch_callback)(const fetch_msg *msg, void *p); /** diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c index 0ab0b6c24..fdfe53d30 100644 --- a/content/fetchers/curl.c +++ b/content/fetchers/curl.c @@ -64,9 +64,6 @@ /** maximum number of progress notifications per second */ #define UPDATES_PER_SECOND 2 -/** maximum number of X509 certificates in chain for TLS connection */ -#define MAX_CERTS 10 - /* the ciphersuites we are willing to use */ #define CIPHER_LIST \ /* disable everything */ \ @@ -109,7 +106,7 @@ struct curl_fetch_info { struct curl_httppost *post_multipart; /**< Multipart post data, or 0. */ uint64_t last_progress_update; /**< Time of last progress update */ int cert_depth; /**< deepest certificate in use */ - struct cert_info cert_data[MAX_CERTS]; /**< HTTPS certificate data */ + struct cert_info cert_data[MAX_SSL_CERTS]; /**< HTTPS certificate data */ }; /** curl handle cache entry */ @@ -445,6 +442,129 @@ failed: } +/** + * Report the certificate information in the fetch to the users + */ +static void +fetch_curl_report_certs_upstream(struct curl_fetch_info *f) +{ + int depth; + BIO *mem; + BUF_MEM *buf; + const ASN1_INTEGER *asn1_num; + BIGNUM *bignum; + struct ssl_cert_info ssl_certs[MAX_SSL_CERTS]; + fetch_msg msg; + struct cert_info *certs = f->cert_data; + memset(ssl_certs, 0, sizeof(ssl_certs)); + + for (depth = 0; depth <= f->cert_depth; depth++) { + assert(certs[depth].cert != NULL); + + /* get certificate version */ + ssl_certs[depth].version = X509_get_version(certs[depth].cert); + + /* not before date */ + mem = BIO_new(BIO_s_mem()); + ASN1_TIME_print(mem, X509_get_notBefore(certs[depth].cert)); + BIO_get_mem_ptr(mem, &buf); + (void) BIO_set_close(mem, BIO_NOCLOSE); + BIO_free(mem); + memcpy(ssl_certs[depth].not_before, + buf->data, + min(sizeof(ssl_certs[depth].not_before) - 1, + (unsigned)buf->length)); + ssl_certs[depth].not_before[min(sizeof(ssl_certs[depth].not_before) - 1, + (unsigned)buf->length)] = 0; + BUF_MEM_free(buf); + + /* not after date */ + mem = BIO_new(BIO_s_mem()); + ASN1_TIME_print(mem, + X509_get_notAfter(certs[depth].cert)); + BIO_get_mem_ptr(mem, &buf); + (void) BIO_set_close(mem, BIO_NOCLOSE); + BIO_free(mem); + memcpy(ssl_certs[depth].not_after, + buf->data, + min(sizeof(ssl_certs[depth].not_after) - 1, + (unsigned)buf->length)); + ssl_certs[depth].not_after[min(sizeof(ssl_certs[depth].not_after) - 1, + (unsigned)buf->length)] = 0; + BUF_MEM_free(buf); + + /* signature type */ + ssl_certs[depth].sig_type = + X509_get_signature_type(certs[depth].cert); + + /* serial number */ + asn1_num = X509_get0_serialNumber(certs[depth].cert); + if (asn1_num != NULL) { + bignum = ASN1_INTEGER_to_BN(asn1_num, NULL); + if (bignum != NULL) { + char *tmp = BN_bn2hex(bignum); + if (tmp != NULL) { + strncpy(ssl_certs[depth].serialnum, + tmp, + sizeof(ssl_certs[depth].serialnum)); + ssl_certs[depth].serialnum[sizeof(ssl_certs[depth].serialnum)-1] = '\0'; + OPENSSL_free(tmp); + } + BN_free(bignum); + bignum = NULL; + } + } + + /* issuer name */ + mem = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(mem, + X509_get_issuer_name(certs[depth].cert), + 0, XN_FLAG_SEP_CPLUS_SPC | + XN_FLAG_DN_REV | XN_FLAG_FN_NONE); + BIO_get_mem_ptr(mem, &buf); + (void) BIO_set_close(mem, BIO_NOCLOSE); + BIO_free(mem); + memcpy(ssl_certs[depth].issuer, + buf->data, + min(sizeof(ssl_certs[depth].issuer) - 1, + (unsigned) buf->length)); + ssl_certs[depth].issuer[min(sizeof(ssl_certs[depth].issuer) - 1, + (unsigned) buf->length)] = 0; + BUF_MEM_free(buf); + + /* subject */ + mem = BIO_new(BIO_s_mem()); + X509_NAME_print_ex(mem, + X509_get_subject_name(certs[depth].cert), + 0, + XN_FLAG_SEP_CPLUS_SPC | + XN_FLAG_DN_REV | + XN_FLAG_FN_NONE); + BIO_get_mem_ptr(mem, &buf); + (void) BIO_set_close(mem, BIO_NOCLOSE); + BIO_free(mem); + memcpy(ssl_certs[depth].subject, + buf->data, + min(sizeof(ssl_certs[depth].subject) - 1, + (unsigned)buf->length)); + ssl_certs[depth].subject[min(sizeof(ssl_certs[depth].subject) - 1, + (unsigned) buf->length)] = 0; + BUF_MEM_free(buf); + + /* type of certificate */ + ssl_certs[depth].cert_type = + X509_certificate_type(certs[depth].cert, + X509_get_pubkey(certs[depth].cert)); + } + + msg.type = FETCH_CERTS; + msg.data.certs.certs = ssl_certs; + msg.data.certs.num_certs = depth; + + fetch_send_callback(&msg, f->fetch_handle); +} + + /** * OpenSSL Certificate verification callback * @@ -479,7 +599,7 @@ fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx) } /* certificate chain is excessively deep so fail verification */ - if (depth >= MAX_CERTS) { + if (depth >= MAX_SSL_CERTS) { X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG); return 0; @@ -524,6 +644,7 @@ fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx) */ static int fetch_curl_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *parm) { + struct curl_fetch_info *f = (struct curl_fetch_info *) parm; int ok; /* Store fetch struct in context for verify callback */ @@ -534,6 +655,8 @@ static int fetch_curl_cert_verify_callback(X509_STORE_CTX *x509_ctx, void *parm) ok = X509_verify_cert(x509_ctx); } + fetch_curl_report_certs_upstream(f); + return ok; } @@ -886,7 +1009,7 @@ static void fetch_curl_free(void *vf) curl_formfree(f->post_multipart); } - for (i = 0; i < MAX_CERTS && f->cert_data[i].cert; i++) { + for (i = 0; i < MAX_SSL_CERTS && f->cert_data[i].cert; i++) { ns_X509_free(f->cert_data[i].cert); } @@ -955,114 +1078,6 @@ static bool fetch_curl_process_headers(struct curl_fetch_info *f) return false; } -/** - * setup callback to allow the user to examine certificates which have - * failed to validate during fetch. - */ -static void -curl_start_cert_validate(struct curl_fetch_info *f, - struct cert_info *certs) -{ - int depth; - BIO *mem; - BUF_MEM *buf; - struct ssl_cert_info ssl_certs[MAX_CERTS]; - fetch_msg msg; - - for (depth = 0; depth <= f->cert_depth; depth++) { - assert(certs[depth].cert != NULL); - - /* get certificate version */ - ssl_certs[depth].version = X509_get_version(certs[depth].cert); - - /* not before date */ - mem = BIO_new(BIO_s_mem()); - ASN1_TIME_print(mem, X509_get_notBefore(certs[depth].cert)); - BIO_get_mem_ptr(mem, &buf); - (void) BIO_set_close(mem, BIO_NOCLOSE); - BIO_free(mem); - memcpy(ssl_certs[depth].not_before, - buf->data, - min(sizeof(ssl_certs[depth].not_before) - 1, - (unsigned)buf->length)); - ssl_certs[depth].not_before[min(sizeof(ssl_certs[depth].not_before) - 1, - (unsigned)buf->length)] = 0; - BUF_MEM_free(buf); - - /* not after date */ - mem = BIO_new(BIO_s_mem()); - ASN1_TIME_print(mem, - X509_get_notAfter(certs[depth].cert)); - BIO_get_mem_ptr(mem, &buf); - (void) BIO_set_close(mem, BIO_NOCLOSE); - BIO_free(mem); - memcpy(ssl_certs[depth].not_after, - buf->data, - min(sizeof(ssl_certs[depth].not_after) - 1, - (unsigned)buf->length)); - ssl_certs[depth].not_after[min(sizeof(ssl_certs[depth].not_after) - 1, - (unsigned)buf->length)] = 0; - BUF_MEM_free(buf); - - /* signature type */ - ssl_certs[depth].sig_type = - X509_get_signature_type(certs[depth].cert); - - /* serial number */ - ssl_certs[depth].serial = - ASN1_INTEGER_get( - X509_get_serialNumber(certs[depth].cert)); - - /* issuer name */ - mem = BIO_new(BIO_s_mem()); - X509_NAME_print_ex(mem, - X509_get_issuer_name(certs[depth].cert), - 0, XN_FLAG_SEP_CPLUS_SPC | - XN_FLAG_DN_REV | XN_FLAG_FN_NONE); - BIO_get_mem_ptr(mem, &buf); - (void) BIO_set_close(mem, BIO_NOCLOSE); - BIO_free(mem); - memcpy(ssl_certs[depth].issuer, - buf->data, - min(sizeof(ssl_certs[depth].issuer) - 1, - (unsigned) buf->length)); - ssl_certs[depth].issuer[min(sizeof(ssl_certs[depth].issuer) - 1, - (unsigned) buf->length)] = 0; - BUF_MEM_free(buf); - - /* subject */ - mem = BIO_new(BIO_s_mem()); - X509_NAME_print_ex(mem, - X509_get_subject_name(certs[depth].cert), - 0, - XN_FLAG_SEP_CPLUS_SPC | - XN_FLAG_DN_REV | - XN_FLAG_FN_NONE); - BIO_get_mem_ptr(mem, &buf); - (void) BIO_set_close(mem, BIO_NOCLOSE); - BIO_free(mem); - memcpy(ssl_certs[depth].subject, - buf->data, - min(sizeof(ssl_certs[depth].subject) - 1, - (unsigned)buf->length)); - ssl_certs[depth].subject[min(sizeof(ssl_certs[depth].subject) - 1, - (unsigned) buf->length)] = 0; - BUF_MEM_free(buf); - - /* type of certificate */ - ssl_certs[depth].cert_type = - X509_certificate_type(certs[depth].cert, - X509_get_pubkey(certs[depth].cert)); - - /* and clean up */ - ns_X509_free(certs[depth].cert); - } - - msg.type = FETCH_CERT_ERR; - msg.data.cert_err.certs = ssl_certs; - msg.data.cert_err.num_certs = depth; - fetch_send_callback(&msg, f->fetch_handle); -} /** * Handle a completed fetch (CURLMSG_DONE from curl_multi_info_read()). @@ -1079,7 +1094,6 @@ static void fetch_curl_done(CURL *curl_handle, CURLcode result) struct curl_fetch_info *f; char **_hideous_hack = (char **) (void *) &f; CURLcode code; - struct cert_info certs[MAX_CERTS]; /* find the structure associated with this fetch */ /* For some reason, cURL thinks CURLINFO_PRIVATE should be a string?! */ @@ -1127,9 +1141,6 @@ static void fetch_curl_done(CURL *curl_handle, CURLcode result) /* CURLE_SSL_PEER_CERTIFICATE renamed to * CURLE_PEER_FAILED_VERIFICATION */ - memset(certs, 0, sizeof(certs)); - memcpy(certs, f->cert_data, sizeof(certs)); - memset(f->cert_data, 0, sizeof(f->cert_data)); cert = true; } else { NSLOG(netsurf, INFO, "Unknown cURL response code %d", result); @@ -1146,7 +1157,9 @@ static void fetch_curl_done(CURL *curl_handle, CURLcode result) fetch_send_callback(&msg, f->fetch_handle); } else if (cert) { /* user needs to validate certificate with issue */ - curl_start_cert_validate(f, certs); + fetch_msg msg; + msg.type = FETCH_CERT_ERR; + fetch_send_callback(&msg, f->fetch_handle); } else if (error) { fetch_msg msg; switch (result) { diff --git a/content/hlcache.c b/content/hlcache.c index ca0e47907..d2b612d05 100644 --- a/content/hlcache.c +++ b/content/hlcache.c @@ -433,6 +433,18 @@ static nserror hlcache_llcache_callback(llcache_handle *handle, assert(ctx->llcache == handle); switch (event->type) { + case LLCACHE_EVENT_GOT_CERTS: + /* Pass them on upward */ + if (ctx->handle->cb != NULL) { + hlcache_event hlevent; + + hlevent.type = CONTENT_MSG_SSL_CERTS; + hlevent.data.certs.certs = event->data.certs.certs; + hlevent.data.certs.num = event->data.certs.num; + + ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw); + } + break; case LLCACHE_EVENT_HAD_HEADERS: error = mimesniff_compute_effective_type(llcache_handle_get_header(handle, "Content-Type"), NULL, 0, ctx->flags & HLCACHE_RETRIEVE_SNIFF_TYPE, diff --git a/content/llcache.c b/content/llcache.c index f3f4b68b0..b0f22b7fa 100644 --- a/content/llcache.c +++ b/content/llcache.c @@ -2360,12 +2360,9 @@ static nserror llcache_fetch_auth(llcache_object *object, const char *realm) * Handle a TLS certificate verification failure * * \param object Object being fetched - * \param certs Certificate chain - * \param num Number of certificates in chain * \return NSERROR_OK on success, appropriate error otherwise */ -static nserror llcache_fetch_cert_error(llcache_object *object, - const struct ssl_cert_info *certs, size_t num) +static nserror llcache_fetch_cert_error(llcache_object *object) { nserror error = NSERROR_OK; @@ -2386,8 +2383,6 @@ static nserror llcache_fetch_cert_error(llcache_object *object, /* Emit query for TLS */ query.type = LLCACHE_QUERY_SSL; query.url = object->url; - query.data.ssl.certs = certs; - query.data.ssl.num = num; /* Construct the query event */ event.type = LLCACHE_EVENT_QUERY; @@ -2880,7 +2875,17 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) error = llcache_send_event_to_users(object, &event); break; + case FETCH_CERTS: + /* Certificate information from the fetch */ + /** \todo CERTS - Should we persist this on the object and + * then catch up new users etc? + */ + event.type = LLCACHE_EVENT_GOT_CERTS; + event.data.certs.certs = msg->data.certs.certs; + event.data.certs.num = msg->data.certs.num_certs; + error = llcache_send_event_to_users(object, &event); + break; /* Events requiring action */ case FETCH_AUTH: /* Need Authentication */ @@ -2902,9 +2907,7 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p) object->candidate = NULL; } - error = llcache_fetch_cert_error(object, - msg->data.cert_err.certs, - msg->data.cert_err.num_certs); + error = llcache_fetch_cert_error(object); break; case FETCH_SSL_ERR: /* TLS connection setup failed */ diff --git a/content/llcache.h b/content/llcache.h index dd2dadc72..b2577a6c1 100644 --- a/content/llcache.h +++ b/content/llcache.h @@ -103,6 +103,7 @@ typedef nserror (*llcache_query_response)(bool proceed, void *cbpw); /** Low-level cache event types */ typedef enum { + LLCACHE_EVENT_GOT_CERTS, /**< SSL certificates arrived */ LLCACHE_EVENT_HAD_HEADERS, /**< Received all headers */ LLCACHE_EVENT_HAD_DATA, /**< Received some data */ LLCACHE_EVENT_DONE, /**< Finished fetching data */ @@ -143,6 +144,10 @@ typedef struct { nsurl *from; /**< Redirect origin */ nsurl *to; /**< Redirect target */ } redirect; /**< Fetch URL redirect occured */ + struct { + const struct ssl_cert_info *certs; /**< The chain */ + size_t num; /**< Number of certs in chain */ + } certs; llcache_query_msg query;/**< Query event */ } data; /**< Event data */ } llcache_event; -- cgit v1.2.3