Support SSL verification through new about: handler

In doing this, also propagate why the certificates were bad
so that the page can display a reason.  We will need FatMessages
for all these.

Signed-off-by: Daniel Silverstone <dsilvers@digital-scurf.org>
This commit is contained in:
Daniel Silverstone 2019-08-06 13:15:23 +01:00
parent 75349e79d8
commit 1cf1ec55bc
12 changed files with 294 additions and 43 deletions

View File

@ -28,6 +28,7 @@
#include "utils/config.h"
#include "utils/nsurl.h"
#include "utils/inet.h"
#include "netsurf/ssl_certs.h"
struct content;
struct fetch;
@ -88,23 +89,6 @@ struct fetch_multipart_data {
bool file; /**< Item is a file */
};
/**
* 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 */
};
/** maximum number of X509 certificates in chain for TLS connection */
#define MAX_SSL_CERTS 10
typedef void (*fetch_callback)(const fetch_msg *msg, void *p);
/**

View File

@ -555,6 +555,49 @@ fetch_curl_report_certs_upstream(struct curl_fetch_info *f)
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;
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;
break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
/* fallthrough */
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
/* fallthrough */
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
/* fallthrough */
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
ssl_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;
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;
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
ssl_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;
break;
case X509_V_ERR_CERT_REVOKED:
ssl_certs[depth].err = SSL_CERT_ERR_REVOKED;
break;
default:
ssl_certs[depth].err = SSL_CERT_ERR_UNKNOWN;
break;
}
}
msg.type = FETCH_CERTS;

View File

@ -844,23 +844,45 @@ browser_window_content_done(struct browser_window *bw)
* Handle query responses from SSL requests
*/
static nserror
browser_window__handle_query_response(bool proceed, void *pw)
browser_window__handle_ssl_query_response(bool proceed, void *pw)
{
struct browser_window *bw = (struct browser_window *)pw;
nserror res = NSERROR_OK;
if (proceed) {
/* We want to restart the request, with the loading
* context
*/
res = browser_window__navigate_internal(bw, &bw->loading_parameters);
if (res != NSERROR_OK) {
NSLOG(netsurf, WARNING, "Unable to navigate after query proceeds");
}
/* If we're in the process of loading, stop the load */
if (bw->loading_content != NULL) {
/* We had a loading content (maybe auth page?) */
browser_window_stop(bw);
browser_window_remove_caret(bw, false);
browser_window_destroy_children(bw);
}
return res;
if (!proceed) {
/* We're processing a "back to safety", do a rough-and-ready
* nav to the old 'current' parameters, with any post data
* stripped away
*/
if (bw->current_parameters.post_urlenc != NULL) {
free(bw->current_parameters.post_urlenc);
bw->current_parameters.post_urlenc = NULL;
}
if (bw->current_parameters.post_multipart != NULL) {
fetch_multipart_data_destroy(bw->current_parameters.post_multipart);
bw->current_parameters.post_multipart = NULL;
}
bw->current_parameters.flags &= ~BW_NAVIGATE_HISTORY;
bw->internal_nav = false;
return browser_window__navigate_internal(bw, &bw->current_parameters);
}
/* We're processing a "proceed" attempt from the form */
/* First, we permit the SSL */
urldb_set_cert_permissions(bw->loading_parameters.url, true);
/* And then we navigate to the original loading parameters */
bw->internal_nav = false;
return browser_window__navigate_internal(bw, &bw->loading_parameters);
}
/**
@ -1073,6 +1095,70 @@ out:
return err;
}
/**
* Handle a certificate verification request (BAD_CERTS) during a fetch
*/
static nserror
browser_window__handle_bad_certs(struct browser_window *bw,
nsurl *url)
{
struct browser_fetch_parameters params;
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;
memset(&params, 0, sizeof(params));
err = nsurl_create("about:query/ssl", &params.url);
if (err != NSERROR_OK) {
goto out;
}
err = fetch_multipart_data_new_kv(&params.post_multipart,
"siteurl",
nsurl_access(url));
if (err != NSERROR_OK) {
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;
}
}
err = fetch_multipart_data_new_kv(&params.post_multipart,
"reason",
reason);
if (err != NSERROR_OK) {
goto out;
}
/* Now we issue the fetch */
bw->internal_nav = true;
err = browser_window__navigate_internal(bw, &params);
if (err != NSERROR_OK) {
goto out;
}
err = guit->misc->cert_verify(url,
bw->loading_ssl_info.certs,
bw->loading_ssl_info.num,
browser_window__handle_ssl_query_response,
bw);
if (err == NSERROR_NOT_IMPLEMENTED) {
err = NSERROR_OK;
}
out:
browser_window__free_fetch_parameters(&params);
return err;
}
/**
* Handle errors during content fetch
*/
@ -1129,14 +1215,7 @@ browser_window__handle_error(struct browser_window *bw,
res = browser_window__handle_login(bw, message, url);
break;
case NSERROR_BAD_CERTS:
res = guit->misc->cert_verify(url,
bw->loading_ssl_info.certs,
bw->loading_ssl_info.num,
browser_window__handle_query_response,
bw);
if (res != NSERROR_OK) {
NSLOG(netsurf, DEBUG, "Unable to start GUI callback for SSL certs");
}
res = browser_window__handle_bad_certs(bw, url);
break;
default:
break;
@ -2986,6 +3065,8 @@ browser_window_navigate(struct browser_window *bw,
if (scheme == corestring_lwc_about) {
if (path == corestring_lwc_query_auth) {
is_internal = true;
} else if (path == corestring_lwc_query_ssl) {
is_internal = true;
}
}
lwc_string_unref(scheme);
@ -3331,6 +3412,32 @@ browser_window__navigate_internal_query_auth(struct browser_window *bw,
}
/**
* Internal navigation handler for the SSL/privacy query page.
*
* If the parameters indicate we're processing a *response* from the handler
* then we deal with that, otherwise we pass it on to the about: handler
*/
static nserror
browser_window__navigate_internal_query_ssl(struct browser_window *bw,
struct browser_fetch_parameters *params)
{
bool is_proceed = false, is_back = false;
assert(params->post_multipart != NULL);
is_proceed = fetch_multipart_data_find(params->post_multipart, "proceed") != NULL;
is_back = fetch_multipart_data_find(params->post_multipart, "back") != NULL;
if (!(is_proceed || is_back)) {
/* This is a request, so pass it on */
return browser_window__navigate_internal_real(bw, params);
}
return browser_window__handle_ssl_query_response(is_proceed, bw);
}
nserror
browser_window__navigate_internal(struct browser_window *bw,
struct browser_fetch_parameters *params)
@ -3356,6 +3463,10 @@ browser_window__navigate_internal(struct browser_window *bw,
lwc_string_unref(path);
return browser_window__navigate_internal_query_auth(bw, params);
}
if (path == corestring_lwc_query_ssl) {
lwc_string_unref(path);
return browser_window__navigate_internal_query_ssl(bw, params);
}
lwc_string_unref(path);
/* Fall through to a normal about: fetch */

View File

@ -6086,7 +6086,6 @@ static struct gui_misc_table amiga_misc_table = {
.quit = gui_quit,
.launch_url = gui_launch_url,
.cert_verify = ami_cert_verify,
};
/** Normal entry point from OS */

View File

@ -1107,7 +1107,6 @@ static struct gui_misc_table atari_misc_table = {
.warning = atari_warn_user,
.quit = gui_quit,
.cert_verify = gui_cert_verify,
};
/* #define WITH_DBG_LOGFILE 1 */

View File

@ -1072,7 +1072,6 @@ static struct gui_misc_table nsgtk_misc_table = {
.quit = gui_quit,
.launch_url = gui_launch_url,
.cert_verify = gtk_cert_verify,
.pdf_password = nsgtk_pdf_password,
};

View File

@ -2431,7 +2431,6 @@ static struct gui_misc_table riscos_misc_table = {
.quit = gui_quit,
.launch_url = gui_launch_url,
.cert_verify = gui_cert_verify,
};

View File

@ -312,8 +312,6 @@ static nserror nsw32_messages_init(char **respaths)
static struct gui_misc_table win32_misc_table = {
.schedule = win32_schedule,
.warning = win32_warning,
.cert_verify = nsw32_cert_verify,
};
/**

View File

@ -0,0 +1,61 @@
/*
* Copyright 2019 Daniel Silverstone <dsilvers@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
*
* SSL related types and values
*/
#ifndef NETSURF_SSL_CERTS_H_
#define NETSURF_SSL_CERTS_H_
/**
* ssl certificate error status
*/
typedef enum {
SSL_CERT_ERR_OK, /**< Nothing wrong with this certificate */
SSL_CERT_ERR_UNKNOWN, /**< Unknown error */
SSL_CERT_ERR_BAD_ISSUER, /**< Bad issuer */
SSL_CERT_ERR_BAD_SIG, /**< Bad signature on this certificate */
SSL_CERT_ERR_TOO_YOUNG, /**< This certificate is not yet valid */
SSL_CERT_ERR_TOO_OLD, /**< This certificate is no longer valid */
SSL_CERT_ERR_SELF_SIGNED, /**< This certificate (or the chain) is self signed */
SSL_CERT_ERR_CHAIN_SELF_SIGNED, /**< This certificate chain is self signed */
SSL_CERT_ERR_REVOKED, /**< This certificate has been revoked */
} ssl_cert_err;
/**
* 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 */
};
/** maximum number of X509 certificates in chain for TLS connection */
#define MAX_SSL_CERTS 10
#endif /* NETSURF_SSL_CERTS_H_ */

View File

@ -147,6 +147,7 @@ CORESTRING_LWC_VALUE(max_age, "max-age");
CORESTRING_LWC_VALUE(no_cache, "no-cache");
CORESTRING_LWC_VALUE(no_store, "no-store");
CORESTRING_LWC_VALUE(query_auth, "query/auth");
CORESTRING_LWC_VALUE(query_ssl, "query/ssl");
/* mime types */
CORESTRING_LWC_VALUE(multipart_form_data, "multipart/form-data");

View File

@ -343,6 +343,54 @@ const char *messages_get_errorcode(nserror code)
return messages_get_ctx("Unknown", messages_hash);
}
/* exported function documented in utils/messages.h */
const char *messages_get_sslcode(ssl_cert_err code)
{
switch (code) {
case SSL_CERT_ERR_OK:
/* Nothing wrong with this certificate */
return messages_get_ctx("SSLCertErrOk", messages_hash);
case SSL_CERT_ERR_UNKNOWN:
/* Unknown error */
return messages_get_ctx("SSLCertErrUnknown", messages_hash);
case SSL_CERT_ERR_BAD_ISSUER:
/* Bad issuer */
return messages_get_ctx("SSLCertErrBadIssuer", messages_hash);
case SSL_CERT_ERR_BAD_SIG:
/* Bad signature on this certificate */
return messages_get_ctx("SSLCertErrBadSig", messages_hash);
case SSL_CERT_ERR_TOO_YOUNG:
/* This certificate is not yet valid */
return messages_get_ctx("SSLCertErrTooYoung", messages_hash);
case SSL_CERT_ERR_TOO_OLD:
/* This certificate is no longer valid */
return messages_get_ctx("SSLCertErrTooOld", messages_hash);
case SSL_CERT_ERR_SELF_SIGNED:
/* This certificate is self signed */
return messages_get_ctx("SSLCertErrSelfSigned", messages_hash);
case SSL_CERT_ERR_CHAIN_SELF_SIGNED:
/* This certificate chain is self signed */
return messages_get_ctx("SSLCertErrChainSelfSigned", messages_hash);
case SSL_CERT_ERR_REVOKED:
/* This certificate has been revoked */
return messages_get_ctx("SSLCertErrRevoked", messages_hash);
}
/* The switch has no default, so the compiler should tell us when we
* forget to add messages for new error codes. As such, we should
* never get here.
*/
assert(0);
return messages_get_ctx("Unknown", messages_hash);
}
/* exported function documented in utils/messages.h */
void messages_destroy(void)

View File

@ -36,6 +36,7 @@
#include <stdint.h>
#include "utils/errors.h"
#include "netsurf/ssl_certs.h"
/**
* Read keys and values from messages file into the standard Messages hash.
@ -78,6 +79,14 @@ const char *messages_get(const char *key);
*/
const char *messages_get_errorcode(nserror code);
/**
* lookup of a message by SSL error code from the standard Messages hash.
*
* \param code ssl error code
* \return message text
*/
const char *messages_get_sslcode(ssl_cert_err code);
/**
* Formatted message from a key in the global message hash.
*