summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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;
+}