1
Origin: r5104, r5107, r5108 and r5116
2
Description: perform certificate host validation (CVE-2010-1155). CVE-2010-1154
3
was also assigned to this issue for checking /0 in CN, but it never checked
4
the hostname until these commits. From the upstream svn log:
6
Check if an SSL certificate matches the hostname of the server we are
9
Use one SSL_CTX per connection, use default trusted CAs if nothing specified.
10
This allows useful use of -ssl_verify without -ssl_cafile/-ssl_capath, using
11
OpenSSL's default trusted CAs.
13
Call OpenSSL_add_all_algorithms(), may be needed to verify SHA256 certs with
14
certain versions of OpenSSL.
16
network-openssl: Show why a certificate failed validation.
18
diff -Nur irssi-0.8.14/src/core/network.h irssi-0.8.14.new/src/core/network.h
19
--- irssi-0.8.14/src/core/network.h 2009-07-21 13:48:05.000000000 -0500
20
+++ irssi-0.8.14.new/src/core/network.h 2010-04-14 13:25:51.050161002 -0500
22
/* Connect to socket */
23
GIOChannel *net_connect(const char *addr, int port, IPADDR *my_ip);
24
/* Connect to socket with ip address and SSL*/
25
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify);
26
+GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, const char* hostname, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify);
27
int irssi_ssl_handshake(GIOChannel *handle);
28
/* Connect to socket with ip address */
29
GIOChannel *net_connect_ip(IPADDR *ip, int port, IPADDR *my_ip);
30
diff -Nur irssi-0.8.14/src/core/network-openssl.c irssi-0.8.14.new/src/core/network-openssl.c
31
--- irssi-0.8.14/src/core/network-openssl.c 2009-07-21 13:48:05.000000000 -0500
32
+++ irssi-0.8.14.new/src/core/network-openssl.c 2010-04-14 13:26:18.761443736 -0500
35
#include <openssl/crypto.h>
36
#include <openssl/x509.h>
37
+#include <openssl/x509v3.h>
38
#include <openssl/pem.h>
39
#include <openssl/ssl.h>
40
#include <openssl/err.h>
44
unsigned int verify:1;
45
+ const char *hostname;
48
-static SSL_CTX *ssl_ctx = NULL;
49
+static int ssl_inited = FALSE;
51
static void irssi_ssl_free(GIOChannel *handle)
53
GIOSSLChannel *chan = (GIOSSLChannel *)handle;
54
g_io_channel_unref(chan->giochan);
56
- if (chan->ctx != ssl_ctx)
57
- SSL_CTX_free(chan->ctx);
58
+ SSL_CTX_free(chan->ctx);
62
-static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, X509 *cert)
63
+/* Checks if the given string has internal NUL characters. */
64
+static gboolean has_internal_nul(const char* str, int len) {
65
+ /* Remove trailing nul characters. They would give false alarms */
66
+ while (len > 0 && str[len-1] == 0)
68
+ return strlen(str) != len;
71
+/* tls_dns_name - Extract valid DNS name from subjectAltName value */
72
+static const char *tls_dns_name(const GENERAL_NAME * gn)
74
+ const char *dnsname;
76
+ /* We expect the OpenSSL library to construct GEN_DNS extension objects as
77
+ ASN1_IA5STRING values. Check we got the right union member. */
78
+ if (ASN1_STRING_type(gn->d.ia5) != V_ASN1_IA5STRING) {
79
+ g_warning("Invalid ASN1 value type in subjectAltName");
83
+ /* Safe to treat as an ASCII string possibly holding a DNS name */
84
+ dnsname = (char *) ASN1_STRING_data(gn->d.ia5);
86
+ if (has_internal_nul(dnsname, ASN1_STRING_length(gn->d.ia5))) {
87
+ g_warning("Internal NUL in subjectAltName");
94
+/* tls_text_name - extract certificate property value by name */
95
+static char *tls_text_name(X509_NAME *name, int nid)
98
+ X509_NAME_ENTRY *entry;
99
+ ASN1_STRING *entry_str;
101
+ unsigned char *utf8_value;
104
+ if (name == 0 || (pos = X509_NAME_get_index_by_NID(name, nid, -1)) < 0) {
108
+ entry = X509_NAME_get_entry(name, pos);
109
+ g_return_val_if_fail(entry != NULL, NULL);
110
+ entry_str = X509_NAME_ENTRY_get_data(entry);
111
+ g_return_val_if_fail(entry_str != NULL, NULL);
113
+ /* Convert everything into UTF-8. It's up to OpenSSL to do something
114
+ reasonable when converting ASCII formats that contain non-ASCII
116
+ if ((utf8_length = ASN1_STRING_to_UTF8(&utf8_value, entry_str)) < 0) {
117
+ g_warning("Error decoding ASN.1 type=%d", ASN1_STRING_type(entry_str));
121
+ if (has_internal_nul((char *)utf8_value, utf8_length)) {
122
+ g_warning("NUL character in hostname in certificate");
123
+ OPENSSL_free(utf8_value);
127
+ result = g_strdup((char *) utf8_value);
128
+ OPENSSL_free(utf8_value);
133
+/** check if a hostname in the certificate matches the hostname we used for the connection */
134
+static gboolean match_hostname(const char *cert_hostname, const char *hostname)
136
+ const char *hostname_left;
138
+ if (!strcasecmp(cert_hostname, hostname)) { /* exact match */
140
+ } else if (cert_hostname[0] == '*' && cert_hostname[1] == '.' && cert_hostname[2] != 0) { /* wildcard match */
141
+ /* The initial '*' matches exactly one hostname component */
142
+ hostname_left = strchr(hostname, '.');
143
+ if (hostname_left != NULL && ! strcasecmp(hostname_left + 1, cert_hostname + 2)) {
150
+/* based on verify_extract_name from tls_client.c in postfix */
151
+static gboolean irssi_ssl_verify_hostname(X509 *cert, const char *hostname)
153
+ int gen_index, gen_count;
154
+ gboolean matched = FALSE, has_dns_name = FALSE;
155
+ const char *cert_dns_name;
156
+ char *cert_subject_cn;
157
+ const GENERAL_NAME *gn;
158
+ STACK_OF(GENERAL_NAME) * gens;
160
+ /* Verify the dNSName(s) in the peer certificate against the hostname. */
161
+ gens = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0);
163
+ gen_count = sk_GENERAL_NAME_num(gens);
164
+ for (gen_index = 0; gen_index < gen_count && !matched; ++gen_index) {
165
+ gn = sk_GENERAL_NAME_value(gens, gen_index);
166
+ if (gn->type != GEN_DNS)
169
+ /* Even if we have an invalid DNS name, we still ultimately
170
+ ignore the CommonName, because subjectAltName:DNS is
171
+ present (though malformed). */
172
+ has_dns_name = TRUE;
173
+ cert_dns_name = tls_dns_name(gn);
174
+ if (cert_dns_name && *cert_dns_name) {
175
+ matched = match_hostname(cert_dns_name, hostname);
179
+ /* Free stack *and* member GENERAL_NAME objects */
180
+ sk_GENERAL_NAME_pop_free(gens, GENERAL_NAME_free);
183
+ if (has_dns_name) {
185
+ /* The CommonName in the issuer DN is obsolete when SubjectAltName is available. */
186
+ g_warning("None of the Subject Alt Names in the certificate match hostname '%s'", hostname);
189
+ } else { /* No subjectAltNames, look at CommonName */
190
+ cert_subject_cn = tls_text_name(X509_get_subject_name(cert), NID_commonName);
191
+ if (cert_subject_cn && *cert_subject_cn) {
192
+ matched = match_hostname(cert_subject_cn, hostname);
194
+ g_warning("SSL certificate common name '%s' doesn't match host name '%s'", cert_subject_cn, hostname);
197
+ g_warning("No subjectAltNames and no valid common name in certificate");
199
+ free(cert_subject_cn);
205
+static gboolean irssi_ssl_verify(SSL *ssl, SSL_CTX *ctx, const char* hostname, X509 *cert)
207
- if (SSL_get_verify_result(ssl) != X509_V_OK) {
210
+ result = SSL_get_verify_result(ssl);
211
+ if (result != X509_V_OK) {
212
unsigned char md[EVP_MAX_MD_SIZE];
216
- g_warning("Could not verify SSL servers certificate:");
217
+ g_warning("Could not verify SSL servers certificate: %s",
218
+ X509_verify_cert_error_string(result));
219
if ((str = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0)) == NULL)
220
g_warning(" Could not get subject-name from peer certificate");
226
+ } else if (! irssi_ssl_verify_hostname(cert, hostname)){
231
@@ -229,19 +378,14 @@
234
SSL_load_error_strings();
236
- ssl_ctx = SSL_CTX_new(SSLv23_client_method());
239
- g_error("Initialization of the SSL library failed");
242
+ OpenSSL_add_all_algorithms();
249
-static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *mycert, const char *mypkey, const char *cafile, const char *capath, gboolean verify)
250
+static GIOChannel *irssi_ssl_get_iochannel(GIOChannel *handle, const char *hostname, const char *mycert, const char *mypkey, const char *cafile, const char *capath, gboolean verify)
254
@@ -251,18 +395,20 @@
256
g_return_val_if_fail(handle != NULL, NULL);
258
- if(!ssl_ctx && !irssi_ssl_init())
259
+ if(!ssl_inited && !irssi_ssl_init())
262
if(!(fd = g_io_channel_unix_get_fd(handle)))
265
+ ctx = SSL_CTX_new(SSLv23_client_method());
267
+ g_error("Could not allocate memory for SSL context");
271
if (mycert && *mycert) {
272
char *scert = NULL, *spkey = NULL;
273
- if ((ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
274
- g_error("Could not allocate memory for SSL context");
277
scert = convert_home(mycert);
278
if (mypkey && *mypkey)
279
spkey = convert_home(mypkey);
281
if ((cafile && *cafile) || (capath && *capath)) {
282
char *scafile = NULL;
283
char *scapath = NULL;
284
- if (! ctx && (ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) {
285
- g_error("Could not allocate memory for SSL context");
288
if (cafile && *cafile)
289
scafile = convert_home(cafile);
290
if (capath && *capath)
291
@@ -297,14 +439,15 @@
296
+ if (!SSL_CTX_set_default_verify_paths(ctx))
297
+ g_warning("Could not load default certificates");
303
if(!(ssl = SSL_new(ctx)))
305
g_warning("Failed to allocate SSL structure");
312
g_warning("Failed to associate socket to SSL stream");
314
- if (ctx != ssl_ctx)
323
chan->verify = verify;
324
+ chan->hostname = hostname;
326
gchan = (GIOChannel *)chan;
327
gchan->funcs = &irssi_ssl_channel_funcs;
328
@@ -333,14 +476,14 @@
332
-GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify)
333
+GIOChannel *net_connect_ip_ssl(IPADDR *ip, int port, const char* hostname, IPADDR *my_ip, const char *cert, const char *pkey, const char *cafile, const char *capath, gboolean verify)
335
GIOChannel *handle, *ssl_handle;
337
handle = net_connect_ip(ip, port, my_ip);
340
- ssl_handle = irssi_ssl_get_iochannel(handle, cert, pkey, cafile, capath, verify);
341
+ ssl_handle = irssi_ssl_get_iochannel(handle, hostname, cert, pkey, cafile, capath, verify);
342
if (ssl_handle == NULL)
343
g_io_channel_unref(handle);
346
g_warning("SSL server supplied no certificate");
349
- ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, cert);
350
+ ret = !chan->verify || irssi_ssl_verify(chan->ssl, chan->ctx, chan->hostname, cert);
354
diff -Nur irssi-0.8.14/src/core/servers.c irssi-0.8.14.new/src/core/servers.c
355
--- irssi-0.8.14/src/core/servers.c 2009-07-21 13:48:05.000000000 -0500
356
+++ irssi-0.8.14.new/src/core/servers.c 2010-04-14 13:25:51.070161073 -0500
358
port = server->connrec->proxy != NULL ?
359
server->connrec->proxy_port : server->connrec->port;
360
handle = server->connrec->use_ssl ?
361
- net_connect_ip_ssl(ip, port, own_ip, server->connrec->ssl_cert, server->connrec->ssl_pkey,
362
+ net_connect_ip_ssl(ip, port, server->connrec->address, own_ip, server->connrec->ssl_cert, server->connrec->ssl_pkey,
363
server->connrec->ssl_cafile, server->connrec->ssl_capath, server->connrec->ssl_verify) :
364
net_connect_ip(ip, port, own_ip);