diff options
-rw-r--r-- | content/content.h | 7 | ||||
-rw-r--r-- | content/fetch.h | 5 | ||||
-rw-r--r-- | content/fetchers/curl.c | 177 | ||||
-rw-r--r-- | content/hlcache.c | 9 | ||||
-rw-r--r-- | content/llcache.c | 285 | ||||
-rw-r--r-- | content/llcache.h | 35 | ||||
-rw-r--r-- | desktop/browser_private.h | 15 | ||||
-rw-r--r-- | desktop/browser_window.c | 46 | ||||
-rw-r--r-- | desktop/gui_factory.c | 3 | ||||
-rw-r--r-- | desktop/sslcert_viewer.c | 184 | ||||
-rw-r--r-- | desktop/sslcert_viewer.h | 14 | ||||
-rw-r--r-- | frontends/gtk/page_info.c | 14 | ||||
-rw-r--r-- | include/netsurf/browser_window.h | 8 | ||||
-rw-r--r-- | include/netsurf/misc.h | 5 | ||||
-rw-r--r-- | include/netsurf/ssl_certs.h | 71 | ||||
-rw-r--r-- | utils/Makefile | 1 | ||||
-rw-r--r-- | utils/ssl_certs.c | 138 |
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(¶ms, 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; +} |