commit 2bf0d76b2c24aefdc17dd35f07a5dc3c9535bf16
parent 15657b98a1190c78879b0dcee69aab4d22554451
Author: ukai <ukai>
Date: Wed, 26 Dec 2001 12:18:06 +0000
[w3m-dev 02743] RFC2818 server identity check
From: Fumitoshi UKAI <ukai@debian.or.jp>
Diffstat:
M | ChangeLog | | | 14 | ++++++++++++++ |
M | NEWS | | | 1 | + |
M | istream.c | | | 83 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | istream.h | | | 2 | ++ |
M | url.c | | | 64 | ++++++++++++++++++++++++++++++++++++++++++++++++++++------------ |
5 files changed, 152 insertions(+), 12 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,3 +1,17 @@
+2001-12-26 Fumitoshi UKAI <ukai@debian.or.jp>
+
+ * [w3m-dev 02743] RFC2818 server identity check
+ * NEWS: RFC2818 server identity check
+ * istream.c (ssl_check_cert_ident): added
+ * istream.h (ssl_check_cert_ident): ditto
+ * istream.h: #include <x509v3.h>
+ * url.c (free_ssl_ctx): ssl_ctx = NULL
+ * url.c (openSSLHandle): arg hostname to check cert id
+ * url.c (openSSLHandle): check SSL_get_verify_result
+ if ssl_verify_server
+ * url.c (openSSLHandle): check server identity by ssl_check_cert_ident
+ * url.c (openURL): openSSLHandle with pu->host
+
2001-12-26 Hironori Sakamoto <hsaka@mth.biglobe.ne.jp>
* [w3m-dev 02715] bugfix in scripts/multipart/multipart.cgi.in
diff --git a/NEWS b/NEWS
@@ -1,5 +1,6 @@
w3m 0.2.4? 0.3?
+* RFC2818 server identity check
* incremental search (C-s, C-r)
* linux/ia64 support (?)
diff --git a/istream.c b/istream.c
@@ -378,6 +378,89 @@ ssl_get_certificate(InputStream stream)
BIO_free_all(bp);
return s;
}
+
+Str
+ssl_check_cert_ident(SSL *handle, char *hostname)
+{
+ X509 *x;
+ int i;
+ Str ret = Strnew_charp("SSL error: failed to check certificate chain");
+ /*
+ * All we need to do here is check that the CN matches.
+ *
+ * From RFC2818 3.1 Server Identity:
+ * If a subjectAltName extension of type dNSName is present, that MUST
+ * be used as the identity. Otherwise, the (most specific) Common Name
+ * field in the Subject field of the certificate MUST be used. Although
+ * the use of the Common Name is existing practice, it is deprecated and
+ * Certification Authorities are encouraged to use the dNSName instead.
+ */
+ x = SSL_get_peer_certificate(handle);
+ if (!x) {
+ ret = Strnew_charp("Unable to get peer certificate");
+ return ret;
+ }
+
+ i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
+ if (i >= 0) {
+ X509_EXTENSION *ex;
+ STACK_OF(GENERAL_NAME) *alt;
+
+ ex = X509_get_ext(x, i);
+ alt = X509V3_EXT_d2i(ex);
+ if (alt) {
+ int n, len1, len2 = 0;
+ char *domain;
+ GENERAL_NAME *gn;
+ X509V3_EXT_METHOD *method;
+
+ len1 = strlen(hostname);
+ n = sk_GENERAL_NAME_num(alt);
+ domain = strchr(hostname, '.');
+ if (domain)
+ len2 = len1 - (domain - hostname);
+ for (i = 0; i < n; i++) {
+ gn = sk_GENERAL_NAME_value(alt, i);
+ if (gn->type == GEN_DNS) {
+ char *sn = ASN1_STRING_data(gn->d.ia5);
+ int sl = ASN1_STRING_length(gn->d.ia5);
+
+ /* Is this an exact match? */
+ if ((len1 == sl) && !strncasecmp(hostname, sn, len1))
+ break;
+
+ /* Is this a wildcard match? */
+ if ((*sn == '*') && domain && (len2 == sl-1) &&
+ !strncasecmp(domain, sn+1, len2))
+ break;
+ }
+ }
+ method = X509V3_EXT_get(ex);
+ method->ext_free(alt);
+ if (i < n) /* Found a match */
+ ret = NULL;
+ }
+ }
+
+ if (ret != NULL) {
+ X509_NAME *xn;
+ char buf[2048];
+
+ xn = X509_get_subject_name(x);
+
+ if (X509_NAME_get_text_by_NID(xn, NID_commonName,
+ buf, sizeof(buf)) == -1) {
+ ret = Strnew_charp("Unable to get common name from peer cert");
+ return ret;
+ }
+ else if (strcasecmp(hostname, buf))
+ ret = Sprintf("Bad cert ident %s from %s", buf, hostname);
+ else
+ ret = NULL;
+ }
+ X509_free(x);
+ return ret;
+}
#endif
/* Raw level input stream functions */
diff --git a/istream.h b/istream.h
@@ -6,6 +6,7 @@
#ifdef USE_SSL
#include <bio.h>
#include <x509.h>
+#include <x509v3.h>
#include <ssl.h>
#endif
#include "Str.h"
@@ -126,6 +127,7 @@ extern int ISfileno(InputStream stream);
extern int ISeos(InputStream stream);
#ifdef USE_SSL
extern Str ssl_get_certificate(InputStream stream);
+extern Str ssl_check_cert_ident(SSL *handle, char *hostname);
#endif
#define IST_BASIC 0
diff --git a/url.c b/url.c
@@ -239,6 +239,7 @@ free_ssl_ctx()
{
if (ssl_ctx != NULL)
SSL_CTX_free(ssl_ctx);
+ ssl_ctx = NULL;
}
#if SSLEAY_VERSION_NUMBER >= 0x00905100
@@ -272,10 +273,12 @@ init_PRNG()
#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */
static SSL *
-openSSLHandle(int sock)
+openSSLHandle(int sock, char *hostname)
{
SSL *handle;
- char *emsg;
+ Str emsg;
+ Str amsg = NULL;
+ char *ans;
static char *old_ssl_forbid_method = NULL;
#ifdef USE_SSL_VERIFY
static int old_ssl_verify_server = -1;
@@ -288,7 +291,6 @@ openSSLHandle(int sock)
ssl_path_modified = 1;
#else
free_ssl_ctx();
- ssl_ctx = NULL;
#endif
}
#ifdef USE_SSL_VERIFY
@@ -298,7 +300,6 @@ openSSLHandle(int sock)
}
if (ssl_path_modified) {
free_ssl_ctx();
- ssl_ctx = NULL;
ssl_path_modified = 0;
}
#endif /* defined(USE_SSL_VERIFY) */
@@ -350,9 +351,6 @@ openSSLHandle(int sock)
#endif /* defined(USE_SSL_VERIFY) */
SSL_CTX_set_default_verify_paths(ssl_ctx);
#endif /* SSLEAY_VERSION_NUMBER >= 0x0800 */
-/*** by inu
- atexit(free_ssl_ctx);
-*/
}
handle = SSL_new(ssl_ctx);
SSL_set_fd(handle, sock);
@@ -361,11 +359,53 @@ openSSLHandle(int sock)
#endif /* SSLEAY_VERSION_NUMBER >= 0x00905100 */
if (SSL_connect(handle) <= 0)
goto eend;
+#ifdef USE_SSL_VERIFY
+ /* check the cert chain.
+ * The chain length is automatically checked by OpenSSL when we
+ * set the verify depth in the ctx.
+ */
+ if (ssl_verify_server && (SSL_get_verify_result(handle) != X509_V_OK)) {
+ emsg = Strnew_charp("Accept SSL session "
+ "which certificate doesn't verify (y/n)?");
+ term_raw();
+ ans = inputChar(emsg->ptr);
+ if (tolower(*ans) == 'y') {
+ amsg = Strnew_charp("Accept unsecure SSL session: "
+ "unverified certificate");
+ } else {
+ char *e = "This SSL session was rejected "
+ "to prevent security violation";
+ disp_err_message(e, FALSE);
+ free_ssl_ctx();
+ return NULL;
+ }
+ }
+#endif
+
+ emsg = ssl_check_cert_ident(handle, hostname);
+ if (emsg != NULL) {
+ if (emsg->length > COLS - 16)
+ Strshrink(emsg, emsg->length - (COLS - 16));
+ term_raw();
+ Strcat_charp(emsg, ": accept(y/n)?");
+ ans = inputChar(emsg->ptr);
+ if (tolower(*ans) == 'y') {
+ amsg = Strnew_charp("Accept unsecure SSL session:"
+ "certificate ident mismatch");
+ } else {
+ char *e = "This SSL session was rejected "
+ "to prevent security violation";
+ disp_err_message(e, FALSE);
+ free_ssl_ctx();
+ return NULL;
+ }
+ }
+ if (amsg)
+ disp_err_message(amsg->ptr, FALSE);
return handle;
eend:
- emsg =
- Sprintf("SSL error: %s", ERR_error_string(ERR_get_error(), NULL))->ptr;
- disp_err_message(emsg, FALSE);
+ emsg = Sprintf("SSL error: %s", ERR_error_string(ERR_get_error(), NULL));
+ disp_err_message(emsg->ptr, FALSE);
return NULL;
}
@@ -1522,7 +1562,7 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,
#ifdef USE_SSL
if (pu->scheme == SCM_HTTPS && *status == HTST_CONNECT) {
sock = ssl_socket_of(ouf->stream);
- if (!(sslh = openSSLHandle(sock))) {
+ if (!(sslh = openSSLHandle(sock, pu->host))) {
*status = HTST_MISSING;
return uf;
}
@@ -1576,7 +1616,7 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,
}
#ifdef USE_SSL
if (pu->scheme == SCM_HTTPS) {
- if (!(sslh = openSSLHandle(sock))) {
+ if (!(sslh = openSSLHandle(sock, pu->host))) {
*status = HTST_MISSING;
return uf;
}