From f67501a12a885082daa7cc9da922402d5d2e109c Mon Sep 17 00:00:00 2001 From: Fumitoshi UKAI Date: Wed, 26 Dec 2001 12:18:06 +0000 Subject: [w3m-dev 02743] RFC2818 server identity check From: Fumitoshi UKAI --- ChangeLog | 16 +++++++++++- NEWS | 1 + istream.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- istream.h | 4 ++- url.c | 66 +++++++++++++++++++++++++++++++++++++++---------- 5 files changed, 156 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index e72b7db..c6b4287 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +2001-12-26 Fumitoshi UKAI + + * [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 + * 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 * [w3m-dev 02715] bugfix in scripts/multipart/multipart.cgi.in @@ -1599,4 +1613,4 @@ * release-0-2-1 * import w3m-0.2.1 -$Id: ChangeLog,v 1.178 2001/12/26 01:57:19 ukai Exp $ +$Id: ChangeLog,v 1.179 2001/12/26 12:18:06 ukai Exp $ diff --git a/NEWS b/NEWS index c41b0d0..9de240b 100644 --- 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 index d866160..1e11e8f 100644 --- a/istream.c +++ b/istream.c @@ -1,4 +1,4 @@ -/* $Id: istream.c,v 1.6 2001/11/24 02:01:26 ukai Exp $ */ +/* $Id: istream.c,v 1.7 2001/12/26 12:18:06 ukai Exp $ */ #include "fm.h" #include "istream.h" #include @@ -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 index b7138fa..20a0fcd 100644 --- a/istream.h +++ b/istream.h @@ -1,4 +1,4 @@ -/* $Id: istream.h,v 1.4 2001/11/24 02:01:26 ukai Exp $ */ +/* $Id: istream.h,v 1.5 2001/12/26 12:18:06 ukai Exp $ */ #ifndef IO_STREAM_H #define IO_STREAM_H @@ -6,6 +6,7 @@ #ifdef USE_SSL #include #include +#include #include #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 index 5c02fc2..03d03cd 100644 --- a/url.c +++ b/url.c @@ -1,4 +1,4 @@ -/* $Id: url.c,v 1.22 2001/12/07 07:58:07 ukai Exp $ */ +/* $Id: url.c,v 1.23 2001/12/26 12:18:06 ukai Exp $ */ #include "fm.h" #include #include @@ -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; } -- cgit v1.2.3