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:
parent
75349e79d8
commit
1cf1ec55bc
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(¶ms, 0, sizeof(params));
|
||||
|
||||
err = nsurl_create("about:query/ssl", ¶ms.url);
|
||||
if (err != NSERROR_OK) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = fetch_multipart_data_new_kv(¶ms.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(¶ms.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, ¶ms);
|
||||
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(¶ms);
|
||||
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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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_ */
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue