summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2020-02-23 16:06:52 +0000
committerVincent Sanders <vince@kyllikki.org>2020-02-23 16:23:50 +0000
commit0c34d06494afe217ace7460c66df800d457dd2e8 (patch)
tree17cc135e3513e7235421502e2e58a2f44ff1a137
parent214478fc15a2b9ee67e19e08cf27657c0157037a (diff)
downloadnetsurf-0c34d06494afe217ace7460c66df800d457dd2e8.tar.gz
netsurf-0c34d06494afe217ace7460c66df800d457dd2e8.tar.bz2
Keep the complete certificate chain from a fetch
Instead of extracting information from the X509 certificate chain in the fetcher the entire chain is propagated in Distinguished Encoding Rules (DER) format. This allows all the information contained in a certificate chain to be retained which can subsequently be presented to the user
-rw-r--r--content/content.h7
-rw-r--r--content/fetch.h5
-rw-r--r--content/fetchers/curl.c177
-rw-r--r--content/hlcache.c9
-rw-r--r--content/llcache.c285
-rw-r--r--content/llcache.h35
-rw-r--r--desktop/browser_private.h15
-rw-r--r--desktop/browser_window.c46
-rw-r--r--desktop/gui_factory.c3
-rw-r--r--desktop/sslcert_viewer.c184
-rw-r--r--desktop/sslcert_viewer.h14
-rw-r--r--frontends/gtk/page_info.c14
-rw-r--r--include/netsurf/browser_window.h8
-rw-r--r--include/netsurf/misc.h5
-rw-r--r--include/netsurf/ssl_certs.h71
-rw-r--r--utils/Makefile1
-rw-r--r--utils/ssl_certs.c138
17 files changed, 590 insertions, 427 deletions
diff --git a/content/content.h b/content/content.h
index 144a698c1..f8f8d32f1 100644
--- a/content/content.h
+++ b/content/content.h
@@ -44,7 +44,7 @@ struct object_params;
struct rect;
struct redraw_context;
struct llcache_query_msg;
-struct ssl_cert_info;
+struct cert_chain;
/** Status of a content */
typedef enum {
@@ -118,10 +118,7 @@ union content_msg_data {
* 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;
+ const struct cert_chain *chain;
/**
* CONTENT_MSG_ERROR - Error from content or underlying fetch
diff --git a/content/fetch.h b/content/fetch.h
index 9a81ece95..9e80b2685 100644
--- a/content/fetch.h
+++ b/content/fetch.h
@@ -75,10 +75,7 @@ typedef struct fetch_msg {
const char *realm;
} auth;
- struct {
- const struct ssl_cert_info *certs;
- size_t num_certs;
- } certs;
+ const struct cert_chain *chain;
} data;
} fetch_msg;
diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c
index 0be33ae16..83e92d808 100644
--- a/content/fetchers/curl.c
+++ b/content/fetchers/curl.c
@@ -149,7 +149,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_SSL_CERTS]; /**< HTTPS certificate data */
+ struct cert_info cert_data[MAX_CERT_DEPTH]; /**< HTTPS certificate data */
};
/** curl handle cache entry */
@@ -471,128 +471,37 @@ failed:
static void
fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
{
- int depth;
+ size_t depth;
BIO *mem;
- BUF_MEM *buf;
- const ASN1_INTEGER *asn1_num;
- BIGNUM *bignum;
- struct ssl_cert_info ssl_certs[MAX_SSL_CERTS];
+ BUF_MEM *buf[MAX_CERT_DEPTH];
+ struct cert_chain chain;
fetch_msg msg;
- struct cert_info *certs = f->cert_data;
- memset(ssl_certs, 0, sizeof(ssl_certs));
+ struct cert_info *certs;
+
+ memset(&chain, 0, sizeof(chain));
+
+ certs = f->cert_data;
+ chain.depth = f->cert_depth + 1; /* 0 indexed certificate depth */
- for (depth = 0; depth <= f->cert_depth; depth++) {
+ for (depth = 0; depth < chain.depth; depth++) {
if (certs[depth].cert == NULL) {
/* This certificate is missing, skip it */
- ssl_certs[depth].err = SSL_CERT_ERR_CERT_MISSING;
+ chain.certs[depth].err = SSL_CERT_ERR_CERT_MISSING;
continue;
}
- /* 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_get_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));
-
/* error code (if any) */
switch (certs[depth].err) {
case X509_V_OK:
- ssl_certs[depth].err = SSL_CERT_ERR_OK;
+ chain.certs[depth].err = SSL_CERT_ERR_OK;
break;
+
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
/* fallthrough */
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
- ssl_certs[depth].err = SSL_CERT_ERR_BAD_ISSUER;
+ chain.certs[depth].err = SSL_CERT_ERR_BAD_ISSUER;
break;
+
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
/* fallthrough */
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
@@ -600,41 +509,66 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
/* fallthrough */
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
- ssl_certs[depth].err = SSL_CERT_ERR_BAD_SIG;
+ chain.certs[depth].err = SSL_CERT_ERR_BAD_SIG;
break;
+
case X509_V_ERR_CERT_NOT_YET_VALID:
/* fallthrough */
case X509_V_ERR_CRL_NOT_YET_VALID:
- ssl_certs[depth].err = SSL_CERT_ERR_TOO_YOUNG;
+ chain.certs[depth].err = SSL_CERT_ERR_TOO_YOUNG;
break;
+
case X509_V_ERR_CERT_HAS_EXPIRED:
/* fallthrough */
case X509_V_ERR_CRL_HAS_EXPIRED:
- ssl_certs[depth].err = SSL_CERT_ERR_TOO_OLD;
+ chain.certs[depth].err = SSL_CERT_ERR_TOO_OLD;
break;
+
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
- ssl_certs[depth].err = SSL_CERT_ERR_SELF_SIGNED;
+ chain.certs[depth].err = SSL_CERT_ERR_SELF_SIGNED;
break;
+
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
- ssl_certs[depth].err = SSL_CERT_ERR_CHAIN_SELF_SIGNED;
+ chain.certs[depth].err = SSL_CERT_ERR_CHAIN_SELF_SIGNED;
break;
+
case X509_V_ERR_CERT_REVOKED:
- ssl_certs[depth].err = SSL_CERT_ERR_REVOKED;
+ chain.certs[depth].err = SSL_CERT_ERR_REVOKED;
break;
+
case X509_V_ERR_HOSTNAME_MISMATCH:
- ssl_certs[depth].err = SSL_CERT_ERR_HOSTNAME_MISMATCH;
+ chain.certs[depth].err = SSL_CERT_ERR_HOSTNAME_MISMATCH;
break;
+
default:
- ssl_certs[depth].err = SSL_CERT_ERR_UNKNOWN;
+ chain.certs[depth].err = SSL_CERT_ERR_UNKNOWN;
break;
}
+
+ /*
+ * get certificate in Distinguished Encoding Rules (DER) format.
+ */
+ mem = BIO_new(BIO_s_mem());
+ i2d_X509_bio(mem, certs[depth].cert);
+ BIO_get_mem_ptr(mem, &buf[depth]);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+
+ chain.certs[depth].der = (uint8_t *)buf[depth]->data;
+ chain.certs[depth].der_length = buf[depth]->length;
}
msg.type = FETCH_CERTS;
- msg.data.certs.certs = ssl_certs;
- msg.data.certs.num_certs = depth;
+ msg.data.chain = &chain;
fetch_send_callback(&msg, f->fetch_handle);
+
+ /* release the openssl memory buffer */
+ for (depth = 0; depth < chain.depth; depth++) {
+ if (buf[depth] != NULL) {
+ BUF_MEM_free(buf[depth]);
+ }
+ }
}
@@ -667,7 +601,7 @@ fetch_curl_verify_callback(int verify_ok, X509_STORE_CTX *x509_ctx)
fetch = X509_STORE_CTX_get_app_data(x509_ctx);
/* certificate chain is excessively deep so fail verification */
- if (depth >= MAX_SSL_CERTS) {
+ if (depth >= MAX_CERT_DEPTH) {
X509_STORE_CTX_set_error(x509_ctx,
X509_V_ERR_CERT_CHAIN_TOO_LONG);
return 0;
@@ -1098,8 +1032,11 @@ static void fetch_curl_free(void *vf)
curl_formfree(f->post_multipart);
}
- for (i = 0; i < MAX_SSL_CERTS && f->cert_data[i].cert; i++) {
- ns_X509_free(f->cert_data[i].cert);
+ /* free certificate data */
+ for (i = 0; i < MAX_CERT_DEPTH; i++) {
+ if (f->cert_data[i].cert != NULL) {
+ ns_X509_free(f->cert_data[i].cert);
+ }
}
free(f);
diff --git a/content/hlcache.c b/content/hlcache.c
index ec011ecdc..23dbc5706 100644
--- a/content/hlcache.c
+++ b/content/hlcache.c
@@ -423,8 +423,10 @@ static nserror hlcache_migrate_ctx(hlcache_retrieval_ctx *ctx,
* \param pw Pointer to client-specific data
* \return NSERROR_OK on success, appropriate error otherwise
*/
-static nserror hlcache_llcache_callback(llcache_handle *handle,
- const llcache_event *event, void *pw)
+static nserror
+hlcache_llcache_callback(llcache_handle *handle,
+ const llcache_event *event,
+ void *pw)
{
hlcache_retrieval_ctx *ctx = pw;
lwc_string *effective_type = NULL;
@@ -439,8 +441,7 @@ static nserror hlcache_llcache_callback(llcache_handle *handle,
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;
+ hlevent.data.chain = event->data.chain;
ctx->handle->cb(ctx->handle, &hlevent, ctx->handle->pw);
}
diff --git a/content/llcache.c b/content/llcache.c
index 8a71f1874..3a75bf971 100644
--- a/content/llcache.c
+++ b/content/llcache.c
@@ -36,6 +36,7 @@
#include <string.h>
#include <strings.h>
#include <nsutils/time.h>
+#include <nsutils/base64.h>
#include "netsurf/inttypes.h"
#include "utils/config.h"
@@ -175,8 +176,7 @@ struct llcache_object {
size_t source_len; /**< Byte length of source data */
size_t source_alloc; /**< Allocated size of source buffer */
- size_t ssl_cert_count; /**< The number of SSL certificates stored */
- struct ssl_cert_info *ssl_certs; /**< SSL certificate information if count is non-zero */
+ struct cert_chain *chain; /**< Certificate chain from the fetch */
llcache_store_state store_state; /**< where the data for the object is stored */
@@ -969,11 +969,7 @@ static nserror llcache_object_destroy(llcache_object *object)
NSLOG(llcache, DEBUG, "Destroying object %p, %s", object,
nsurl_access(object->url));
- if (object->ssl_cert_count != 0) {
- free(object->ssl_certs);
- object->ssl_certs = NULL;
- object->ssl_cert_count = 0;
- }
+ cert_chain_free(object->chain);
if (object->source_data != NULL) {
if (object->store_state == LLCACHE_STATE_DISC) {
@@ -1240,6 +1236,13 @@ llcache_serialise_metadata(llcache_object *object,
char *op;
unsigned int hloop;
int use;
+ size_t cert_chain_depth;
+
+ if (object->chain != NULL) {
+ cert_chain_depth = object->chain->depth;
+ } else {
+ cert_chain_depth = 0;
+ }
allocsize = 10 + 1; /* object length */
@@ -1251,22 +1254,19 @@ llcache_serialise_metadata(llcache_object *object,
allocsize += 10 + 1; /* space for number of header entries */
- allocsize += 10 + 1; /* space for number of SSL certificates */
-
- allocsize += nsurl_length(object->url) + 1;
-
for (hloop = 0 ; hloop < object->num_headers ; hloop++) {
allocsize += strlen(object->headers[hloop].name) + 1;
allocsize += strlen(object->headers[hloop].value) + 1;
}
- for (hloop = 0; hloop < object->ssl_cert_count; hloop++) {
- allocsize += (10 + 1) * 4; /* version, sig_type, cert_type, err */
- allocsize += strlen(object->ssl_certs[hloop].not_before) + 1;
- allocsize += strlen(object->ssl_certs[hloop].not_after) + 1;
- allocsize += strlen(object->ssl_certs[hloop].serialnum) + 1;
- allocsize += strlen(object->ssl_certs[hloop].issuer) + 1;
- allocsize += strlen(object->ssl_certs[hloop].subject) + 1;
+ allocsize += nsurl_length(object->url) + 1;
+
+ /* space for number of DER formatted certificates */
+ allocsize += 10 + 1;
+
+ for (hloop = 0; hloop < cert_chain_depth; hloop++) {
+ allocsize += 10 + 1; /* error status */
+ allocsize += 4 * ((object->chain->certs[hloop].der_length + 2) / 3);
}
data = malloc(allocsize);
@@ -1351,8 +1351,8 @@ llcache_serialise_metadata(llcache_object *object,
datasize -= use;
}
- /* number of ssl certificates */
- use = snprintf(op, datasize, "%" PRIsizet, object->ssl_cert_count);
+ /* number of DER formatted ssl certificates */
+ use = snprintf(op, datasize, "%" PRIsizet, cert_chain_depth);
if (use < 0) {
goto operror;
}
@@ -1363,80 +1363,13 @@ llcache_serialise_metadata(llcache_object *object,
datasize -= use;
/* SSL certificates */
- for (hloop = 0; hloop < object->ssl_cert_count; hloop++) {
- struct ssl_cert_info *cert = &(object->ssl_certs[hloop]);
- /* Certificate version */
- use = snprintf(op, datasize, "%ld", cert->version);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* not_before */
- use = snprintf(op, datasize, "%s", cert->not_before);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* not_after */
- use = snprintf(op, datasize, "%s", cert->not_after);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* Signature type */
- use = snprintf(op, datasize, "%d", cert->sig_type);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* serialnum */
- use = snprintf(op, datasize, "%s", cert->serialnum);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* issuer */
- use = snprintf(op, datasize, "%s", cert->issuer);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* subject */
- use = snprintf(op, datasize, "%s", cert->subject);
- if (use < 0) {
- goto operror;
- }
- use++; /* does not count the null */
- if (use > datasize)
- goto overflow;
- op += use;
- datasize -= use;
- /* Certificate type */
- use = snprintf(op, datasize, "%d", cert->cert_type);
+ for (hloop = 0; hloop < cert_chain_depth; hloop++) {
+ size_t output_length;
+ nsuerror res;
+
+ /* Certificate error code */
+ use = snprintf(op, datasize, "%d",
+ (int)(object->chain->certs[hloop].err));
if (use < 0) {
goto operror;
}
@@ -1445,14 +1378,26 @@ llcache_serialise_metadata(llcache_object *object,
goto overflow;
op += use;
datasize -= use;
- /* Certificate error code */
- use = snprintf(op, datasize, "%d", (int)(cert->err));
- if (use < 0) {
- goto operror;
+
+ /* DER certificate data in base64 encoding */
+ if (object->chain->certs[hloop].der != NULL) {
+ output_length = datasize;
+ res = nsu_base64_encode(
+ object->chain->certs[hloop].der,
+ object->chain->certs[hloop].der_length,
+ (uint8_t *)op,
+ &output_length);
+ if (res != NSUERROR_OK) {
+ goto operror;
+ }
+ use = output_length;
+ } else {
+ use = 0;
}
- use++; /* does not count the null */
+ use++; /* allow for null */
if (use > datasize)
goto overflow;
+ *(op + output_length) = 0;
op += use;
datasize -= use;
}
@@ -1510,7 +1455,7 @@ llcache_process_metadata(llcache_object *object)
size_t num_headers;
size_t hloop;
size_t ssl_cert_count = 0;
- struct ssl_cert_info *ssl_certs = NULL;
+ struct cert_chain *chain = NULL;
NSLOG(llcache, INFO, "Retrieving metadata");
@@ -1642,7 +1587,7 @@ llcache_process_metadata(llcache_object *object)
goto skip_ssl_certificates;
}
- /* Next line is the number of SSL certificates*/
+ /* Next line is the number of DER base64 encoded certificates */
line++;
ln += lnsize + 1;
lnsize = strlen(ln);
@@ -1657,72 +1602,20 @@ llcache_process_metadata(llcache_object *object)
goto skip_ssl_certificates;
}
- ssl_certs = calloc(sizeof(struct ssl_cert_info), ssl_cert_count);
- if (ssl_certs == NULL) {
- res = NSERROR_NOMEM;
+ if (ssl_cert_count > MAX_CERT_DEPTH) {
+ res = NSERROR_INVALID;
+ goto format_error;
+ }
+
+ res = cert_chain_alloc(ssl_cert_count, &chain);
+ if (res != NSERROR_OK) {
goto format_error;
}
for (hloop = 0; hloop < ssl_cert_count; hloop++) {
- struct ssl_cert_info *cert = &ssl_certs[hloop];
int errcode;
- /* Certificate version */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%ld", &cert->version) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
- /* Not before */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->not_before, ln, lnsize);
- /* Not after */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->not_after, ln, lnsize);
- /* Signature type */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%d", &cert->sig_type) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
- /* Serial Number */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->serialnum, ln, lnsize);
- /* issuer */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->issuer, ln, lnsize);
- /* subject */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- memcpy(&cert->subject, ln, lnsize);
- /* Certificate type */
- line++;
- ln += lnsize + 1;
- lnsize = strlen(ln);
- remaining -= lnsize + 1;
- if ((lnsize < 1) || (sscanf(ln, "%d", &cert->cert_type) != 1)) {
- res = NSERROR_INVALID;
- goto format_error;
- }
+ nsuerror nsures;
+
/* Certificate error code */
line++;
ln += lnsize + 1;
@@ -1735,9 +1628,25 @@ llcache_process_metadata(llcache_object *object)
if (errcode < SSL_CERT_ERR_OK ||
errcode > SSL_CERT_ERR_MAX_KNOWN) {
/* Error with the cert code, assume UNKNOWN */
- cert->err = SSL_CERT_ERR_UNKNOWN;
+ chain->certs[hloop].err = SSL_CERT_ERR_UNKNOWN;
} else {
- cert->err = (ssl_cert_err)errcode;
+ chain->certs[hloop].err = (ssl_cert_err)errcode;
+ }
+
+ /* base64 encoded DER certificate data */
+ line++;
+ ln += lnsize + 1;
+ lnsize = strlen(ln);
+ remaining -= lnsize + 1;
+ if (lnsize > 0) {
+ nsures = nsu_base64_decode_alloc((const uint8_t *)ln,
+ lnsize,
+ &chain->certs[hloop].der,
+ &chain->certs[hloop].der_length);
+ if (nsures != NSUERROR_OK) {
+ res = NSERROR_NOMEM;
+ goto format_error;
+ }
}
}
@@ -1754,8 +1663,7 @@ skip_ssl_certificates:
object->cache.res_time = response_time;
object->cache.fin_time = completion_time;
- object->ssl_cert_count = ssl_cert_count;
- object->ssl_certs = ssl_certs;
+ object->chain = chain;
/* object stored in backing store */
object->store_state = LLCACHE_STATE_DISC;
@@ -1768,9 +1676,7 @@ format_error:
line, res);
guit->llcache->release(object->url, BACKING_STORE_META);
- if (ssl_certs != NULL) {
- free(ssl_certs);
- }
+ cert_chain_free(chain);
return res;
}
@@ -3173,21 +3079,15 @@ static void llcache_fetch_callback(const fetch_msg *msg, void *p)
case FETCH_CERTS:
/* Certificate information from the fetch */
- /* Persist the data onto our object */
- object->ssl_certs = calloc(sizeof(struct ssl_cert_info),
- msg->data.certs.num_certs);
- if (object->ssl_certs != NULL) {
- object->ssl_cert_count = msg->data.certs.num_certs;
- memcpy(object->ssl_certs, msg->data.certs.certs,
- sizeof(struct ssl_cert_info) * object->ssl_cert_count);
- }
-
- /* Now pass on the event */
- event.type = LLCACHE_EVENT_GOT_CERTS;
- event.data.certs.certs = msg->data.certs.certs;
- event.data.certs.num = msg->data.certs.num_certs;
+ /* Persist the chain onto our object */
+ error = cert_chain_dup(msg->data.chain, &object->chain);
+ if (error != NSERROR_OK) {
+ /* Now pass on the event */
+ event.type = LLCACHE_EVENT_GOT_CERTS;
+ event.data.chain = msg->data.chain;
- error = llcache_send_event_to_users(object, &event);
+ error = llcache_send_event_to_users(object, &event);
+ }
break;
/* Events requiring action */
@@ -3388,10 +3288,9 @@ static nserror llcache_object_notify_users(llcache_object *object)
handle->state = LLCACHE_FETCH_HEADERS;
/* Emit any certificate data we hold */
- if (object->ssl_cert_count > 0) {
+ if (object->chain != NULL) {
event.type = LLCACHE_EVENT_GOT_CERTS;
- event.data.certs.certs = object->ssl_certs;
- event.data.certs.num = object->ssl_cert_count;
+ event.data.chain = object->chain;
error = handle->cb(handle, &event, handle->pw);
} else {
error = NSERROR_OK;
@@ -3613,16 +3512,12 @@ llcache_object_snapshot(llcache_object *object, llcache_object **snapshot)
}
}
- if (object->ssl_cert_count != 0) {
- newobj->ssl_certs = calloc(sizeof(struct ssl_cert_info),
- object->ssl_cert_count);
- if (newobj->ssl_certs == NULL) {
+ if (object->chain != NULL) {
+ error = cert_chain_dup(object->chain, &newobj->chain);
+ if (error != NSERROR_OK) {
llcache_object_destroy(newobj);
- return NSERROR_NOMEM;
+ return error;
}
- memcpy(newobj->ssl_certs, object->ssl_certs,
- sizeof(struct ssl_cert_info) * object->ssl_cert_count);
- newobj->ssl_cert_count = object->ssl_cert_count;
}
newobj->fetch.state = LLCACHE_FETCH_COMPLETE;
@@ -3662,7 +3557,7 @@ total_object_size(llcache_object *object)
}
}
- tot += object->ssl_cert_count * sizeof(struct ssl_cert_info);
+ tot += cert_chain_size(object->chain);
return tot;
}
diff --git a/content/llcache.h b/content/llcache.h
index 8d2411e0a..514272f29 100644
--- a/content/llcache.h
+++ b/content/llcache.h
@@ -30,7 +30,7 @@
#include "utils/errors.h"
#include "utils/nsurl.h"
-struct ssl_cert_info;
+struct cert_chain;
struct fetch_multipart_data;
/** Handle for low-level cache object */
@@ -83,26 +83,23 @@ typedef enum {
* and must be copied if it is desirable to retain.
*/
typedef struct {
- llcache_event_type type; /**< Type of event */
+ llcache_event_type type; /**< Type of event */
union {
struct {
- const uint8_t *buf; /**< Buffer of data */
- size_t len; /**< Length of buffer, in bytes */
- } data; /**< Received data */
+ const uint8_t *buf; /**< Buffer of data */
+ size_t len; /**< Byte length of buffer */
+ } data; /**< Received data */
struct {
- nserror code; /**< The error code */
- const char *msg; /**< Error message */
+ nserror code; /**< The error code */
+ const char *msg; /**< Error message */
} error;
- const char *progress_msg; /**< Progress message */
+ const char *progress_msg; /**< Progress message */
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;
- } data; /**< Event data */
+ nsurl *from; /**< Redirect origin */
+ nsurl *to; /**< Redirect target */
+ } redirect; /**< Fetch URL redirect occured */
+ const struct cert_chain *chain; /**< Certificate chain */
+ } data; /**< Event data */
} llcache_event;
/**
@@ -171,17 +168,17 @@ struct llcache_parameters {
size_t hysteresis; /**< The hysteresis around the target size */
/** The minimum lifetime to consider sending objects to backing store.*/
- int minimum_lifetime;
+ int minimum_lifetime;
/** The minimum bandwidth to allow the backing store to
* use in bytes/second
*/
- size_t minimum_bandwidth;
+ size_t minimum_bandwidth;
/** The maximum bandwidth to allow the backing store to use in
* bytes/second
*/
- size_t maximum_bandwidth;
+ size_t maximum_bandwidth;
/** The time quantum over which to calculate the bandwidth values
*/
diff --git a/desktop/browser_private.h b/desktop/browser_private.h
index 6e45052d7..41b8fefd4 100644
--- a/desktop/browser_private.h
+++ b/desktop/browser_private.h
@@ -89,13 +89,6 @@ struct browser_fetch_parameters {
bool parent_quirks; /**< Optional parent quirks */
};
-/**
- * The SSL context for a fetch, as provided by the fetchers
- */
-struct browser_ssl_info {
- struct ssl_cert_info certs[MAX_SSL_CERTS]; /**< The certificate chain */
- size_t num; /**< The number of certificates in the chain */
-};
/**
* Browser window data.
@@ -113,9 +106,9 @@ struct browser_window {
struct browser_fetch_parameters current_parameters;
/**
- * The SSL information for the current content
+ * The certificate chain for the current content
*/
- struct browser_ssl_info current_ssl_info;
+ struct cert_chain *current_cert_chain;
/**
* Content handle of page in process of being loaded or NULL
@@ -129,9 +122,9 @@ struct browser_window {
struct browser_fetch_parameters loading_parameters;
/**
- * The SSL information for the loading content
+ * The certificate chain for the loading content
*/
- struct browser_ssl_info loading_ssl_info;
+ struct cert_chain *loading_cert_chain;
/**
* Favicon
diff --git a/desktop/browser_window.c b/desktop/browser_window.c
index 06f41ddf3..6defe0182 100644
--- a/desktop/browser_window.c
+++ b/desktop/browser_window.c
@@ -743,9 +743,10 @@ static nserror browser_window_content_ready(struct browser_window *bw)
browser_window__free_fetch_parameters(&bw->current_parameters);
bw->current_parameters = bw->loading_parameters;
memset(&bw->loading_parameters, 0, sizeof(bw->loading_parameters));
- /* Transfer the SSL info */
- bw->current_ssl_info = bw->loading_ssl_info;
- bw->loading_ssl_info.num = 0;
+ /* Transfer the certificate chain */
+ cert_chain_free(bw->current_cert_chain);
+ bw->current_cert_chain = bw->loading_cert_chain;
+ bw->loading_cert_chain = NULL;
}
/* Format the new content to the correct dimensions */
@@ -1136,7 +1137,7 @@ browser_window__handle_bad_certs(struct browser_window *bw,
nserror err;
/* Initially we don't know WHY the SSL cert was bad */
const char *reason = messages_get_sslcode(SSL_CERT_ERR_UNKNOWN);
- size_t n;
+ size_t depth;
memset(&params, 0, sizeof(params));
@@ -1151,12 +1152,14 @@ browser_window__handle_bad_certs(struct browser_window *bw,
goto out;
}
- for (n = 0; n < bw->loading_ssl_info.num; ++n) {
- size_t idx = bw->loading_ssl_info.num - (n + 1);
- ssl_cert_err err = bw->loading_ssl_info.certs[idx].err;
- if (err != SSL_CERT_ERR_OK) {
- reason = messages_get_sslcode(err);
- break;
+ if (bw->loading_cert_chain != NULL) {
+ for (depth = 0; depth < bw->loading_cert_chain->depth; ++depth) {
+ size_t idx = bw->loading_cert_chain->depth - (depth + 1);
+ ssl_cert_err err = bw->loading_cert_chain->certs[idx].err;
+ if (err != SSL_CERT_ERR_OK) {
+ reason = messages_get_sslcode(err);
+ break;
+ }
}
}
@@ -1175,8 +1178,7 @@ browser_window__handle_bad_certs(struct browser_window *bw,
}
err = guit->misc->cert_verify(url,
- bw->loading_ssl_info.certs,
- bw->loading_ssl_info.num,
+ bw->loading_cert_chain,
browser_window__handle_ssl_query_response,
bw);
@@ -1352,11 +1354,8 @@ browser_window_callback(hlcache_handle *c, const hlcache_event *event, void *pw)
switch (event->type) {
case CONTENT_MSG_SSL_CERTS:
/* SSL certificate information has arrived, store it */
- assert(event->data.certs.num < MAX_SSL_CERTS);
- memcpy(&bw->loading_ssl_info.certs[0],
- event->data.certs.certs,
- sizeof(struct ssl_cert_info) * event->data.certs.num);
- bw->loading_ssl_info.num = event->data.certs.num;
+ cert_chain_free(bw->loading_cert_chain);
+ cert_chain_dup(event->data.chain, &bw->loading_cert_chain);
break;
case CONTENT_MSG_LOG:
@@ -3431,7 +3430,8 @@ navigate_internal_real(struct browser_window *bw,
fetch_is_post = (params->post_urlenc != NULL || params->post_multipart != NULL);
/* Clear SSL info for load */
- bw->loading_ssl_info.num = 0;
+ cert_chain_free(bw->loading_cert_chain);
+ bw->loading_cert_chain = NULL;
/* Set up retrieval parameters */
if (!(params->flags & BW_NAVIGATE_UNVERIFIABLE)) {
@@ -4707,17 +4707,17 @@ browser_window_page_info_state browser_window_get_page_info_state(
}
/* Exported interface, documented in browser_window.h */
-nserror browser_window_get_ssl_chain(struct browser_window *bw, size_t *num,
- struct ssl_cert_info **chain)
+nserror
+browser_window_get_ssl_chain(struct browser_window *bw,
+ struct cert_chain **chain)
{
assert(bw != NULL);
- if (bw->current_ssl_info.num == 0) {
+ if (bw->current_cert_chain == NULL) {
return NSERROR_NOT_FOUND;
}
- *num = bw->current_ssl_info.num;
- *chain = &(bw->current_ssl_info.certs[0]);
+ *chain = bw->current_cert_chain;
return NSERROR_OK;
}
diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c
index 8b52e5469..7ef457a7a 100644
--- a/desktop/gui_factory.c
+++ b/desktop/gui_factory.c
@@ -639,8 +639,7 @@ static nserror gui_default_launch_url(struct nsurl *url)
static nserror gui_default_cert_verify(nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw)
{
diff --git a/desktop/sslcert_viewer.c b/desktop/sslcert_viewer.c
index 4d8725757..ec0fd3431 100644
--- a/desktop/sslcert_viewer.c
+++ b/desktop/sslcert_viewer.c
@@ -52,6 +52,21 @@ enum sslcert_viewer_field {
typedef nserror (*response_cb)(bool proceed, void *pw);
/**
+ * ssl certificate information for certificate error message
+ */
+struct ssl_cert_info {
+ long version; /**< Certificate version */
+ char not_before[32]; /**< Valid from date */
+ char not_after[32]; /**< Valid to date */
+ int sig_type; /**< Signature type */
+ char serialnum[64]; /**< Serial number */
+ char issuer[256]; /**< Issuer details */
+ char subject[256]; /**< Subject details */
+ int cert_type; /**< Certificate type */
+ ssl_cert_err err; /**< Whatever is wrong with this certificate */
+};
+
+/**
* ssl certificate verification context.
*/
struct sslcert_session_data {
@@ -473,38 +488,183 @@ nserror sslcert_viewer_fini(struct sslcert_session_data *ssl_d)
return err;
}
+#ifdef WITH_OPENSSL
+
+#include <openssl/ssl.h>
+#include <openssl/x509v3.h>
+
+static nserror
+der_to_certinfo(const uint8_t *der,
+ size_t der_length,
+ struct ssl_cert_info *info)
+{
+ BIO *mem;
+ BUF_MEM *buf;
+ const ASN1_INTEGER *asn1_num;
+ BIGNUM *bignum;
+ X509 *cert; /**< Pointer to certificate */
+
+ if (der == NULL) {
+ return NSERROR_OK;
+ }
+
+ cert = d2i_X509(NULL, &der, der_length);
+ if (cert == NULL) {
+ return NSERROR_INVALID;
+ }
+
+ /* get certificate version */
+ info->version = X509_get_version(cert);
+
+ /* not before date */
+ mem = BIO_new(BIO_s_mem());
+ ASN1_TIME_print(mem, X509_get_notBefore(cert));
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->not_before,
+ buf->data,
+ min(sizeof(info->not_before) - 1, (unsigned)buf->length));
+ info->not_before[min(sizeof(info->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(cert));
+ BIO_get_mem_ptr(mem, &buf);
+ (void) BIO_set_close(mem, BIO_NOCLOSE);
+ BIO_free(mem);
+ memcpy(info->not_after,
+ buf->data,
+ min(sizeof(info->not_after) - 1, (unsigned)buf->length));
+ info->not_after[min(sizeof(info->not_after) - 1, (unsigned)buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* signature type */
+ info->sig_type = X509_get_signature_type(cert);
+
+ /* serial number */
+ asn1_num = X509_get_serialNumber(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(info->serialnum,
+ tmp,
+ sizeof(info->serialnum));
+ info->serialnum[sizeof(info->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(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(info->issuer,
+ buf->data,
+ min(sizeof(info->issuer) - 1, (unsigned) buf->length));
+ info->issuer[min(sizeof(info->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(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(info->subject,
+ buf->data,
+ min(sizeof(info->subject) - 1, (unsigned)buf->length));
+ info->subject[min(sizeof(info->subject) - 1, (unsigned) buf->length)] = 0;
+ BUF_MEM_free(buf);
+
+ /* type of certificate */
+ info->cert_type = X509_certificate_type(cert, X509_get_pubkey(cert));
+
+ X509_free(cert);
+
+ return NSERROR_OK;
+}
+#else
+static nserror
+der_to_certinfo(uint8_t *der, size_t der_length, struct ssl_cert_info *info)
+{
+ return NSERROR_NOT_IMPLEMENTED;
+}
+#endif
+
+/* copy certificate data */
+static nserror
+convert_chain_to_cert_info(const struct cert_chain *chain,
+ struct ssl_cert_info **cert_info_out)
+{
+ struct ssl_cert_info *certs;
+ size_t depth;
+ nserror res;
+
+ certs = calloc(chain->depth, sizeof(struct ssl_cert_info));
+ if (certs == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ for (depth = 0; depth < chain->depth;depth++) {
+ res = der_to_certinfo(chain->certs[depth].der,
+ chain->certs[depth].der_length,
+ certs + depth);
+ if (res != NSERROR_OK) {
+ free(certs);
+ return res;
+ }
+ certs[depth].err = chain->certs[depth].err;
+ }
+
+ *cert_info_out = certs;
+ return NSERROR_OK;
+}
/* Exported interface, documented in sslcert_viewer.h */
nserror
-sslcert_viewer_create_session_data(unsigned long num,
- struct nsurl *url,
+sslcert_viewer_create_session_data(struct nsurl *url,
nserror (*cb)(bool proceed, void *pw),
void *cbpw,
- const struct ssl_cert_info *certs,
+ const struct cert_chain *chain,
struct sslcert_session_data **ssl_d)
{
struct sslcert_session_data *data;
-
+ nserror res;
assert(url != NULL);
- assert(certs != NULL);
+ assert(chain != NULL);
data = malloc(sizeof(struct sslcert_session_data));
if (data == NULL) {
*ssl_d = NULL;
return NSERROR_NOMEM;
}
-
- /* copy certificate data */
- data->certs = malloc(num * sizeof(struct ssl_cert_info));
- if (data->certs == NULL) {
+ res = convert_chain_to_cert_info(chain, &data->certs);
+ if (res != NSERROR_OK) {
free(data);
*ssl_d = NULL;
- return NSERROR_NOMEM;
+ return res;
}
- memcpy(data->certs, certs, num * sizeof(struct ssl_cert_info));
data->url = nsurl_ref(url);
- data->num = num;
+ data->num = chain->depth;
data->cb = cb;
data->cbpw = cbpw;
diff --git a/desktop/sslcert_viewer.h b/desktop/sslcert_viewer.h
index 6955e0167..854284083 100644
--- a/desktop/sslcert_viewer.h
+++ b/desktop/sslcert_viewer.h
@@ -32,16 +32,15 @@ struct redraw_context;
struct core_window_callback_table;
struct rect;
struct nsurl;
-struct ssl_cert_info;
+struct cert_chain;
/**
* Create ssl certificate viewer session data.
*
- * \param num The number of certificates in the chain
* \param url Address of the page we're inspecting certificates of
* \param cb Low level cache callback
* \param cbpw Low level cache private data
- * \param certs The SSL certificates
+ * \param chain The SSL certificate chain
* \param ssl_d Updated to SSL certificate session data
* \return NSERROR_OK on success, appropriate error otherwise
*
@@ -49,8 +48,10 @@ struct ssl_cert_info;
* sslcert_viewer_fini destroys the session data.
*/
nserror sslcert_viewer_create_session_data(
- unsigned long num, struct nsurl *url, nserror (*cb)(bool proceed, void *pw),
- void *cbpw, const struct ssl_cert_info *certs,
+ struct nsurl *url,
+ nserror (*cb)(bool proceed, void *pw),
+ void *cbpw,
+ const struct cert_chain *chain,
struct sslcert_session_data **ssl_d);
@@ -65,7 +66,8 @@ nserror sslcert_viewer_create_session_data(
* \return NSERROR_OK on success, appropriate error otherwise
*/
nserror sslcert_viewer_init(struct core_window_callback_table *cw_t,
- void *core_window_handle, struct sslcert_session_data *ssl_d);
+ void *core_window_handle,
+ struct sslcert_session_data *ssl_d);
/**
diff --git a/frontends/gtk/page_info.c b/frontends/gtk/page_info.c
index adc2dfa66..1401fdca9 100644
--- a/frontends/gtk/page_info.c
+++ b/frontends/gtk/page_info.c
@@ -52,6 +52,7 @@ struct nsgtk_crtvrfy_window {
struct sslcert_session_data *ssl_data;
};
+
/**
* destroy a previously created certificate view
*/
@@ -178,11 +179,14 @@ nserror nsgtk_page_info(struct browser_window *bw)
struct nsgtk_crtvrfy_window *ncwin;
nserror res;
- size_t num;
- struct ssl_cert_info *chain;
+ struct cert_chain *chain;
struct nsurl *url;
- browser_window_get_ssl_chain(bw, &num, &chain);
+ res = browser_window_get_ssl_chain(bw, &chain);
+ if (res != NSERROR_OK) {
+ NSLOG(netsurf, WARNING, "Unable to get certificate chain");
+ return NSERROR_INVALID;
+ }
url = browser_window_access_url(bw);
ncwin = malloc(sizeof(struct nsgtk_crtvrfy_window));
@@ -245,8 +249,8 @@ nserror nsgtk_page_info(struct browser_window *bw)
}
/* initialise certificate viewing interface */
- res = sslcert_viewer_create_session_data(num, url, dummy_cb, NULL, chain,
- &ncwin->ssl_data);
+ res = sslcert_viewer_create_session_data(
+ url, dummy_cb, NULL, chain, &ncwin->ssl_data);
if (res != NSERROR_OK) {
g_object_unref(G_OBJECT(ncwin->dlg));
free(ncwin);
diff --git a/include/netsurf/browser_window.h b/include/netsurf/browser_window.h
index 7b2f652e6..e8faa1877 100644
--- a/include/netsurf/browser_window.h
+++ b/include/netsurf/browser_window.h
@@ -42,7 +42,7 @@ struct form_control;
struct nsurl;
struct rect;
struct redraw_context;
-struct ssl_cert_info;
+struct cert_chain;
enum content_debug;
/**
@@ -784,11 +784,9 @@ browser_window_page_info_state browser_window_get_page_info_state(
* If there is no chain available, this will return NSERROR_NOT_FOUND
*
* \param bw The browser window
- * \param num Pointer to be filled out with chain length
- * \param chain Pointer to be filled out with chain base
+ * \param chain Pointer to be filled out with certificate chain
* \return Whether or not the chain is available
*/
-nserror browser_window_get_ssl_chain(struct browser_window *bw, size_t *num,
- struct ssl_cert_info **chain);
+nserror browser_window_get_ssl_chain(struct browser_window *bw, struct cert_chain **chain);
#endif
diff --git a/include/netsurf/misc.h b/include/netsurf/misc.h
index 8a7953192..cc0b78dbb 100644
--- a/include/netsurf/misc.h
+++ b/include/netsurf/misc.h
@@ -27,7 +27,7 @@
struct form_control;
struct gui_window;
-struct ssl_cert_info;
+struct cert_chain;
struct nsurl;
/**
@@ -81,8 +81,7 @@ struct gui_misc_table {
* \return NSERROR_OK on sucess else error and cb never called
*/
nserror (*cert_verify)(struct nsurl *url,
- const struct ssl_cert_info *certs,
- unsigned long num,
+ const struct cert_chain *chain,
nserror (*cb)(bool proceed, void *pw),
void *cbpw);
diff --git a/include/netsurf/ssl_certs.h b/include/netsurf/ssl_certs.h
index 0444678a8..1aaf485a7 100644
--- a/include/netsurf/ssl_certs.h
+++ b/include/netsurf/ssl_certs.h
@@ -48,22 +48,67 @@ typedef enum {
/** Always the max known ssl certificate error type */
#define SSL_CERT_ERR_MAX_KNOWN SSL_CERT_ERR_HOSTNAME_MISMATCH
+/** maximum number of X509 certificates in chain for TLS connection */
+#define MAX_CERT_DEPTH 10
+
/**
- * ssl certificate information for certificate error message
+ * X509 certificate chain
*/
-struct ssl_cert_info {
- long version; /**< Certificate version */
- char not_before[32]; /**< Valid from date */
- char not_after[32]; /**< Valid to date */
- int sig_type; /**< Signature type */
- char serialnum[64]; /**< Serial number */
- char issuer[256]; /**< Issuer details */
- char subject[256]; /**< Subject details */
- int cert_type; /**< Certificate type */
- ssl_cert_err err; /**< Whatever is wrong with this certificate */
+struct cert_chain {
+ /**
+ * the number of certificates in the chain
+ * */
+ size_t depth;
+ struct {
+ /**
+ * Whatever is wrong with this certificate
+ */
+ ssl_cert_err err;
+
+ /**
+ * data in Distinguished Encoding Rules (DER) format
+ */
+ uint8_t *der;
+
+ /**
+ * DER length
+ */
+ size_t der_length;
+ } certs[MAX_CERT_DEPTH];
};
-/** maximum number of X509 certificates in chain for TLS connection */
-#define MAX_SSL_CERTS 10
+/**
+ * create new certificate chain
+ *
+ * \param dpth the depth to set in the new chain.
+ * \param chain_out A pointer to recive the new chain.
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ */
+nserror cert_chain_alloc(size_t depth, struct cert_chain **chain_out);
+
+/**
+ * duplicate a certificate chain
+ *
+ * \param src The certificate chain to copy from
+ * \param dst_out A pointer to recive the duplicated chain
+ * \return NSERROR_OK on success or NSERROR_NOMEM on memory exhaustion
+ */
+nserror cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out);
+
+/**
+ * free a certificate chain
+ *
+ * \param chain The certificate chain to free
+ * \return NSERROR_OK on success
+ */
+nserror cert_chain_free(struct cert_chain *chain);
+
+/**
+ * total number of data bytes in a chain
+ *
+ * \param chain The chain to size
+ * \return the number of bytes used by the chain
+ */
+size_t cert_chain_size(const struct cert_chain *chain);
#endif /* NETSURF_SSL_CERTS_H_ */
diff --git a/utils/Makefile b/utils/Makefile
index 2f59501c2..e0fbd8e20 100644
--- a/utils/Makefile
+++ b/utils/Makefile
@@ -13,6 +13,7 @@ S_UTILS := \
messages.c \
nsoption.c \
punycode.c \
+ ssl_certs.c \
talloc.c \
time.c \
url.c \
diff --git a/utils/ssl_certs.c b/utils/ssl_certs.c
new file mode 100644
index 000000000..7154561aa
--- /dev/null
+++ b/utils/ssl_certs.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2020 Vincent Sanders <vince@netsurf-browser.org>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ *
+ * NetSurf is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * NetSurf is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * \file
+ * helpers for X509 certificate chains
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+
+#include "utils/errors.h"
+#include "utils/log.h"
+
+#include "netsurf/ssl_certs.h"
+
+/*
+ * create new certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_alloc(size_t depth, struct cert_chain **chain_out)
+{
+ struct cert_chain* chain;
+
+ chain = calloc(1, sizeof(struct cert_chain));
+ if (chain == NULL) {
+ return NSERROR_NOMEM;
+ }
+
+ chain->depth = depth;
+
+ *chain_out = chain;
+
+ return NSERROR_OK;
+}
+
+
+/*
+ * duplicate certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror
+cert_chain_dup(const struct cert_chain *src, struct cert_chain **dst_out)
+{
+ struct cert_chain* dst;
+ size_t depth;
+ nserror res;
+
+ res = cert_chain_alloc(src->depth, &dst);
+ if (res != NSERROR_OK) {
+ return res;
+ }
+
+ for (depth = 0; depth < src->depth; depth++) {
+ dst->certs[depth].err = src->certs[depth].err;
+ dst->certs[depth].der_length = src->certs[depth].der_length;
+ if (src->certs[depth].der != NULL) {
+ dst->certs[depth].der = malloc(src->certs[depth].der_length);
+ if (dst->certs[depth].der == NULL) {
+ cert_chain_free(dst);
+ return NSERROR_NOMEM;
+ }
+ memcpy(dst->certs[depth].der,
+ src->certs[depth].der,
+ src->certs[depth].der_length);
+ }
+
+ }
+
+ *dst_out = dst;
+ return NSERROR_OK;
+}
+
+
+/*
+ * free certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+nserror cert_chain_free(struct cert_chain* chain)
+{
+ size_t depth;
+
+ if (chain != NULL) {
+ for (depth = 0; depth < chain->depth; depth++) {
+ if (chain->certs[depth].der != NULL) {
+ free(chain->certs[depth].der);
+ }
+ }
+
+ free(chain);
+ }
+
+ return NSERROR_OK;
+}
+
+
+/*
+ * calculate storage used of certificate chain
+ *
+ * exported interface documented in netsurf/ssl_certs.h
+ */
+size_t cert_chain_size(const struct cert_chain *chain)
+{
+ size_t size = 0;
+ size_t depth;
+
+ if (chain != NULL) {
+ size += sizeof(struct cert_chain);
+
+ for (depth = 0; depth < chain->depth; depth++) {
+ if (chain->certs[depth].der != NULL) {
+ size += chain->certs[depth].der_length;
+ }
+ }
+ }
+
+ return size;
+}