1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-22 08:08:50 -05:00

Neil Dunbar provided a patch that now makes libcurl check SSL

subjectAltNames when matching certs. This is apparently detailed in RFC2818
  as the right thing to do. I had to add configure checks for inet_pton() and
  our own (strictly speaking, code from BIND written by Paul Vixie) provided
  code for the function for platforms that miss it.
This commit is contained in:
Daniel Stenberg 2003-10-07 21:46:47 +00:00
parent f52534522c
commit 6494889e3b
4 changed files with 454 additions and 80 deletions

View File

@ -78,7 +78,7 @@ memdebug.h inet_ntoa_r.h http_chunks.c http_chunks.h strtok.c strtok.h \
connect.c connect.h llist.c llist.h hash.c hash.h multi.c \ connect.c connect.h llist.c llist.h hash.c hash.h multi.c \
content_encoding.c content_encoding.h share.c share.h http_digest.c \ content_encoding.c content_encoding.h share.c share.h http_digest.c \
md5.c md5.h http_digest.h http_negotiate.c http_negotiate.h \ md5.c md5.h http_digest.h http_negotiate.c http_negotiate.h \
http_ntlm.c http_ntlm.h ca-bundle.h http_ntlm.c http_ntlm.h ca-bundle.h inet_pton.c inet_pton.h
noinst_HEADERS = setup.h transfer.h noinst_HEADERS = setup.h transfer.h

226
lib/inet_pton.c Normal file
View File

@ -0,0 +1,226 @@
/* This is from the BIND 4.9.4 release, modified to compile by itself */
/* Copyright (c) 1996 by Internet Software Consortium.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
*/
#include "setup.h"
#ifndef HAVE_INET_PTON
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
#define IN6ADDRSZ 16
#define INADDRSZ 4
#define INT16SZ 2
#ifndef AF_INET6
#define AF_INET6 AF_MAX+1 /* just to let this compile */
#endif
/*
* WARNING: Don't even consider trying to compile this on a system where
* sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
*/
static int inet_pton4(const char *src, u_char *dst);
static int inet_pton6(const char *src, u_char *dst);
/* int
* inet_pton(af, src, dst)
* convert from presentation format (which usually means ASCII printable)
* to network format (which is usually some kind of binary format).
* return:
* 1 if the address was valid for the specified address family
* 0 if the address wasn't valid (`dst' is untouched in this case)
* -1 if some other error occurred (`dst' is untouched in this case, too)
* author:
* Paul Vixie, 1996.
*/
int
Curl_inet_pton(af, src, dst)
int af;
const char *src;
void *dst;
{
switch (af) {
case AF_INET:
return (inet_pton4(src, dst));
case AF_INET6:
return (inet_pton6(src, dst));
default:
errno = EAFNOSUPPORT;
return (-1);
}
/* NOTREACHED */
}
/* int
* inet_pton4(src, dst)
* like inet_aton() but without all the hexadecimal and shorthand.
* return:
* 1 if `src' is a valid dotted quad, else 0.
* notice:
* does not touch `dst' unless it's returning 1.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton4(src, dst)
const char *src;
u_char *dst;
{
static const char digits[] = "0123456789";
int saw_digit, octets, ch;
u_char tmp[INADDRSZ], *tp;
saw_digit = 0;
octets = 0;
*(tp = tmp) = 0;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr(digits, ch)) != NULL) {
u_int new = *tp * 10 + (pch - digits);
if (new > 255)
return (0);
*tp = new;
if (! saw_digit) {
if (++octets > 4)
return (0);
saw_digit = 1;
}
} else if (ch == '.' && saw_digit) {
if (octets == 4)
return (0);
*++tp = 0;
saw_digit = 0;
} else
return (0);
}
if (octets < 4)
return (0);
/* bcopy(tmp, dst, INADDRSZ); */
memcpy(dst, tmp, INADDRSZ);
return (1);
}
/* int
* inet_pton6(src, dst)
* convert presentation level address to network order binary form.
* return:
* 1 if `src' is a valid [RFC1884 2.2] address, else 0.
* notice:
* (1) does not touch `dst' unless it's returning 1.
* (2) :: in a full address is silently ignored.
* credit:
* inspired by Mark Andrews.
* author:
* Paul Vixie, 1996.
*/
static int
inet_pton6(src, dst)
const char *src;
u_char *dst;
{
static const char xdigits_l[] = "0123456789abcdef",
xdigits_u[] = "0123456789ABCDEF";
u_char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
const char *xdigits, *curtok;
int ch, saw_xdigit;
u_int val;
memset((tp = tmp), 0, IN6ADDRSZ);
endp = tp + IN6ADDRSZ;
colonp = NULL;
/* Leading :: requires some special handling. */
if (*src == ':')
if (*++src != ':')
return (0);
curtok = src;
saw_xdigit = 0;
val = 0;
while ((ch = *src++) != '\0') {
const char *pch;
if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
pch = strchr((xdigits = xdigits_u), ch);
if (pch != NULL) {
val <<= 4;
val |= (pch - xdigits);
if (val > 0xffff)
return (0);
saw_xdigit = 1;
continue;
}
if (ch == ':') {
curtok = src;
if (!saw_xdigit) {
if (colonp)
return (0);
colonp = tp;
continue;
}
if (tp + INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
saw_xdigit = 0;
val = 0;
continue;
}
if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
inet_pton4(curtok, tp) > 0) {
tp += INADDRSZ;
saw_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
return (0);
}
if (saw_xdigit) {
if (tp + INT16SZ > endp)
return (0);
*tp++ = (u_char) (val >> 8) & 0xff;
*tp++ = (u_char) val & 0xff;
}
if (colonp != NULL) {
/*
* Since some memmove()'s erroneously fail to handle
* overlapping regions, we'll do the shift by hand.
*/
const int n = tp - colonp;
int i;
for (i = 1; i <= n; i++) {
endp[- i] = colonp[n - i];
colonp[n - i] = 0;
}
tp = endp;
}
if (tp != endp)
return (0);
/* bcopy(tmp, dst, IN6ADDRSZ); */
memcpy(dst, tmp, IN6ADDRSZ);
return (1);
}
#endif /* HAVE_INET_PTON */

34
lib/inet_pton.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __INET_PTON_H
#define __INET_PTON_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2003, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#ifdef HAVE_INET_PTON
#define Curl_inet_pton(x,y,z) inet_pton(x,y,z)
#else
int Curl_inet_pton(int, const char *, void *);
#endif
#endif /* __INET_PTON_H */

View File

@ -30,6 +30,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h>
#ifdef HAVE_SYS_TYPES_H #ifdef HAVE_SYS_TYPES_H
#include <sys/types.h> #include <sys/types.h>
#endif #endif
@ -41,9 +42,11 @@
#include "sendf.h" #include "sendf.h"
#include "formdata.h" /* for the boundary function */ #include "formdata.h" /* for the boundary function */
#include "url.h" /* for the ssl config check function */ #include "url.h" /* for the ssl config check function */
#include "inet_pton.h"
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/x509v3.h>
/* The last #include file should be: */ /* The last #include file should be: */
#ifdef CURLDEBUG #ifdef CURLDEBUG
@ -191,7 +194,7 @@ int random_the_seed(struct SessionHandle *data)
/* generates a default path for the random seed file */ /* generates a default path for the random seed file */
buf[0]=0; /* blank it first */ buf[0]=0; /* blank it first */
RAND_file_name(buf, BUFSIZE); RAND_file_name(buf, BUFSIZE);
if ( buf[0] ) { if(buf[0]) {
/* we got a file name to try */ /* we got a file name to try */
nread += RAND_load_file(buf, 16384); nread += RAND_load_file(buf, 16384);
if(seed_enough(nread)) if(seed_enough(nread))
@ -207,13 +210,13 @@ int random_the_seed(struct SessionHandle *data)
#endif #endif
static int do_file_type(const char *type) static int do_file_type(const char *type)
{ {
if (!type || !type[0]) if(!type || !type[0])
return SSL_FILETYPE_PEM; return SSL_FILETYPE_PEM;
if (curl_strequal(type, "PEM")) if(curl_strequal(type, "PEM"))
return SSL_FILETYPE_PEM; return SSL_FILETYPE_PEM;
if (curl_strequal(type, "DER")) if(curl_strequal(type, "DER"))
return SSL_FILETYPE_ASN1; return SSL_FILETYPE_ASN1;
if (curl_strequal(type, "ENG")) if(curl_strequal(type, "ENG"))
return SSL_FILETYPE_ENGINE; return SSL_FILETYPE_ENGINE;
return -1; return -1;
} }
@ -228,7 +231,7 @@ int cert_stuff(struct connectdata *conn,
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
int file_type; int file_type;
if (cert_file != NULL) { if(cert_file != NULL) {
SSL *ssl; SSL *ssl;
X509 *x509; X509 *x509;
@ -255,7 +258,7 @@ int cert_stuff(struct connectdata *conn,
switch(file_type) { switch(file_type) {
case SSL_FILETYPE_PEM: case SSL_FILETYPE_PEM:
/* SSL_CTX_use_certificate_chain_file() only works on PEM files */ /* SSL_CTX_use_certificate_chain_file() only works on PEM files */
if (SSL_CTX_use_certificate_chain_file(conn->ssl.ctx, if(SSL_CTX_use_certificate_chain_file(conn->ssl.ctx,
cert_file) != 1) { cert_file) != 1) {
failf(data, "unable to set certificate file (wrong password?)"); failf(data, "unable to set certificate file (wrong password?)");
return 0; return 0;
@ -266,7 +269,7 @@ int cert_stuff(struct connectdata *conn,
/* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but /* SSL_CTX_use_certificate_file() works with either PEM or ASN1, but
we use the case above for PEM so this can only be performed with we use the case above for PEM so this can only be performed with
ASN1 files. */ ASN1 files. */
if (SSL_CTX_use_certificate_file(conn->ssl.ctx, if(SSL_CTX_use_certificate_file(conn->ssl.ctx,
cert_file, cert_file,
file_type) != 1) { file_type) != 1) {
failf(data, "unable to set certificate file (wrong password?)"); failf(data, "unable to set certificate file (wrong password?)");
@ -286,11 +289,11 @@ int cert_stuff(struct connectdata *conn,
switch(file_type) { switch(file_type) {
case SSL_FILETYPE_PEM: case SSL_FILETYPE_PEM:
if (key_file == NULL) if(key_file == NULL)
/* cert & key can only be in PEM case in the same file */ /* cert & key can only be in PEM case in the same file */
key_file=cert_file; key_file=cert_file;
case SSL_FILETYPE_ASN1: case SSL_FILETYPE_ASN1:
if (SSL_CTX_use_PrivateKey_file(conn->ssl.ctx, if(SSL_CTX_use_PrivateKey_file(conn->ssl.ctx,
key_file, key_file,
file_type) != 1) { file_type) != 1) {
failf(data, "unable to set private key file: '%s' type %s\n", failf(data, "unable to set private key file: '%s' type %s\n",
@ -302,11 +305,11 @@ int cert_stuff(struct connectdata *conn,
#ifdef HAVE_OPENSSL_ENGINE_H #ifdef HAVE_OPENSSL_ENGINE_H
{ /* XXXX still needs some work */ { /* XXXX still needs some work */
EVP_PKEY *priv_key = NULL; EVP_PKEY *priv_key = NULL;
if (conn && conn->data && conn->data->engine) { if(conn && conn->data && conn->data->engine) {
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS #ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
UI_METHOD *ui_method = UI_OpenSSL(); UI_METHOD *ui_method = UI_OpenSSL();
#endif #endif
if (!key_file || !key_file[0]) { if(!key_file || !key_file[0]) {
failf(data, "no key set to load from crypto engine\n"); failf(data, "no key set to load from crypto engine\n");
return 0; return 0;
} }
@ -315,11 +318,11 @@ int cert_stuff(struct connectdata *conn,
ui_method, ui_method,
#endif #endif
data->set.key_passwd); data->set.key_passwd);
if (!priv_key) { if(!priv_key) {
failf(data, "failed to load private key from crypto engine\n"); failf(data, "failed to load private key from crypto engine\n");
return 0; return 0;
} }
if (SSL_CTX_use_PrivateKey(conn->ssl.ctx, priv_key) != 1) { if(SSL_CTX_use_PrivateKey(conn->ssl.ctx, priv_key) != 1) {
failf(data, "unable to set private key\n"); failf(data, "unable to set private key\n");
EVP_PKEY_free(priv_key); EVP_PKEY_free(priv_key);
return 0; return 0;
@ -346,7 +349,7 @@ int cert_stuff(struct connectdata *conn,
/* This version was provided by Evan Jordan and is supposed to not /* This version was provided by Evan Jordan and is supposed to not
leak memory as the previous version: */ leak memory as the previous version: */
if (x509 != NULL) { if(x509 != NULL) {
EVP_PKEY *pktmp = X509_get_pubkey(x509); EVP_PKEY *pktmp = X509_get_pubkey(x509);
EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl)); EVP_PKEY_copy_parameters(pktmp,SSL_get_privatekey(ssl));
EVP_PKEY_free(pktmp); EVP_PKEY_free(pktmp);
@ -360,7 +363,7 @@ int cert_stuff(struct connectdata *conn,
/* Now we know that a key and cert have been set against /* Now we know that a key and cert have been set against
* the SSL context */ * the SSL context */
if (!SSL_CTX_check_private_key(conn->ssl.ctx)) { if(!SSL_CTX_check_private_key(conn->ssl.ctx)) {
failf(data, "Private key does not match the certificate public key"); failf(data, "Private key does not match the certificate public key");
return(0); return(0);
} }
@ -457,7 +460,7 @@ void Curl_SSL_cleanup(void)
*/ */
void Curl_SSL_Close(struct connectdata *conn) void Curl_SSL_Close(struct connectdata *conn)
{ {
if (conn->ssl.use) { if(conn->ssl.use) {
/* /*
ERR_remove_state() frees the error queue associated with ERR_remove_state() frees the error queue associated with
thread pid. If pid == 0, the current thread will have its thread pid. If pid == 0, the current thread will have its
@ -583,7 +586,7 @@ int Curl_SSL_Close_All(struct SessionHandle *data)
free(data->state.session); free(data->state.session);
} }
#ifdef HAVE_OPENSSL_ENGINE_H #ifdef HAVE_OPENSSL_ENGINE_H
if (data->engine) if(data->engine)
{ {
ENGINE_free(data->engine); ENGINE_free(data->engine);
data->engine = NULL; data->engine = NULL;
@ -669,28 +672,28 @@ static int Curl_ASN1_UTCTIME_output(struct connectdata *conn,
i=tm->length; i=tm->length;
asn1_string=(char *)tm->data; asn1_string=(char *)tm->data;
if (i < 10) if(i < 10)
return 1; return 1;
if (asn1_string[i-1] == 'Z') if(asn1_string[i-1] == 'Z')
gmt=TRUE; gmt=TRUE;
for (i=0; i<10; i++) for (i=0; i<10; i++)
if ((asn1_string[i] > '9') || (asn1_string[i] < '0')) if((asn1_string[i] > '9') || (asn1_string[i] < '0'))
return 2; return 2;
year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0'); year= (asn1_string[0]-'0')*10+(asn1_string[1]-'0');
if (year < 50) if(year < 50)
year+=100; year+=100;
month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0'); month= (asn1_string[2]-'0')*10+(asn1_string[3]-'0');
if ((month > 12) || (month < 1)) if((month > 12) || (month < 1))
return 3; return 3;
day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0'); day= (asn1_string[4]-'0')*10+(asn1_string[5]-'0');
hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0'); hour= (asn1_string[6]-'0')*10+(asn1_string[7]-'0');
minute= (asn1_string[8]-'0')*10+(asn1_string[9]-'0'); minute= (asn1_string[8]-'0')*10+(asn1_string[9]-'0');
if ( (asn1_string[10] >= '0') && (asn1_string[10] <= '9') && if((asn1_string[10] >= '0') && (asn1_string[10] <= '9') &&
(asn1_string[11] >= '0') && (asn1_string[11] <= '9')) (asn1_string[11] >= '0') && (asn1_string[11] <= '9'))
second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0'); second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0');
infof(data, infof(data,
@ -742,6 +745,148 @@ cert_hostcheck(const char *certname, const char *hostname)
} }
#endif #endif
static CURLcode verifyhost(struct connectdata *conn)
{
char peer_CN[257];
int ntype = 3; /* 1 = IPv6, 2 = IPv4, 3=DNS */
int i;
int altmatch = 0;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
struct in_addr addr;
#endif
char *ptr;
struct SessionHandle *data = conn->data;
#ifdef ENABLE_IPV6
if(conn->hostname[0] == '[' && strchr(conn->hostname, ']')) {
char *n2 = strdup(conn->hostname+1);
*strchr(n2, ']') = '\0';
if(Curl_inet_pton(AF_INET6, n2, &addr))
ntype = 1;
free(n2);
}
else
#endif
{
if((ptr = strrchr(conn->hostname, '.')) &&
isdigit((unsigned char)ptr[1])) {
if(Curl_inet_pton(AF_INET, conn->hostname, &addr))
ntype = 2;
}
}
i = X509_get_ext_by_NID(conn->ssl.server_cert, NID_subject_alt_name, -1);
if(i >= 0) {
X509_EXTENSION *ex;
STACK_OF(GENERAL_NAME) *alt;
ex = X509_get_ext(conn->ssl.server_cert, i);
alt = X509V3_EXT_d2i(ex);
if(alt) {
int n, len1 = 0, len2 = 0;
char *domain = NULL;
GENERAL_NAME *gn;
if(ntype == 3) {
len1 = strlen(conn->hostname);
domain = strchr(conn->hostname, '.');
if(domain) {
len2 = len1 - (domain-conn->hostname);
}
}
n = sk_GENERAL_NAME_num(alt);
for (i=0; i<n; i++) {
char *sn;
int sl;
gn = sk_GENERAL_NAME_value(alt, i);
if(gn->type == GEN_DNS) {
if(ntype != 3)
continue;
sn = (char *) ASN1_STRING_data(gn->d.ia5);
sl = ASN1_STRING_length(gn->d.ia5);
/* Is this an exact match? */
if((len1 == sl) && curl_strnequal(conn->hostname, sn, len1))
break;
/* Is this a wildcard match? */
if((*sn == '*') && domain && (len2 == sl-1) &&
curl_strnequal(domain, sn+1, len2))
break;
}
else if(gn->type == GEN_IPADD) {
if(ntype == 3)
continue;
sn = (char *) ASN1_STRING_data(gn->d.ia5);
sl = ASN1_STRING_length(gn->d.ia5);
#ifdef ENABLE_IPv6
if(ntype == 1 && sl != sizeof(struct in6_addr))
continue;
else
#endif
if(ntype == 2 && sl != sizeof(struct in_addr))
continue;
if(!memcmp(sn, &addr, sl))
break;
}
}
GENERAL_NAMES_free(alt);
if(i < n) { /* got a match in altnames */
altmatch = 1;
infof(data, "\t subjectAltName: %s matched\n", conn->hostname);
}
}
}
if(!altmatch) {
bool obtain=FALSE;
if(X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert),
NID_commonName,
peer_CN,
sizeof(peer_CN)) < 0) {
if(data->set.ssl.verifyhost > 1) {
failf(data,
"SSL: unable to obtain common name from peer certificate");
X509_free(conn->ssl.server_cert);
return CURLE_SSL_PEER_CERTIFICATE;
}
else {
/* Consider verifyhost == 1 as an "OK" for a missing CN field, but we
output a note about the situation */
infof(data, "\t common name: WARNING couldn't obtain\n");
}
}
else
obtain = TRUE;
if(obtain) {
if(!cert_hostcheck(peer_CN, conn->hostname)) {
if(data->set.ssl.verifyhost > 1) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'", peer_CN, conn->hostname);
X509_free(conn->ssl.server_cert);
return CURLE_SSL_PEER_CERTIFICATE;
}
else
infof(data, "\t common name: %s (does not match '%s')\n",
peer_CN, conn->hostname);
}
else
infof(data, "\t common name: %s (matched)\n", peer_CN);
}
}
return CURLE_OK;
}
/* ====================================================== */ /* ====================================================== */
CURLcode CURLcode
Curl_SSLConnect(struct connectdata *conn) Curl_SSLConnect(struct connectdata *conn)
@ -803,19 +948,19 @@ Curl_SSLConnect(struct connectdata *conn)
SSL_CTX_set_options(conn->ssl.ctx, SSL_OP_ALL); SSL_CTX_set_options(conn->ssl.ctx, SSL_OP_ALL);
if(data->set.cert) { if(data->set.cert) {
if (!cert_stuff(conn, if(!cert_stuff(conn,
data->set.cert, data->set.cert,
data->set.cert_type, data->set.cert_type,
data->set.key, data->set.key,
data->set.key_type)) { data->set.key_type)) {
/* failf() is already done in cert_stuff() */ /* failf() is already done in cert_stuff() */
return CURLE_SSL_CERTPROBLEM; return CURLE_SSL_CERTPROBLEM;
} }
} }
if(data->set.ssl.cipher_list) { if(data->set.ssl.cipher_list) {
if (!SSL_CTX_set_cipher_list(conn->ssl.ctx, if(!SSL_CTX_set_cipher_list(conn->ssl.ctx,
data->set.ssl.cipher_list)) { data->set.ssl.cipher_list)) {
failf(data, "failed setting cipher list"); failf(data, "failed setting cipher list");
return CURLE_SSL_CIPHER; return CURLE_SSL_CIPHER;
} }
@ -826,10 +971,10 @@ Curl_SSLConnect(struct connectdata *conn)
SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT| SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
SSL_VERIFY_CLIENT_ONCE, SSL_VERIFY_CLIENT_ONCE,
cert_verify_callback); cert_verify_callback);
if ((data->set.ssl.CAfile || data->set.ssl.CApath) && if((data->set.ssl.CAfile || data->set.ssl.CApath) &&
!SSL_CTX_load_verify_locations(conn->ssl.ctx, !SSL_CTX_load_verify_locations(conn->ssl.ctx,
data->set.ssl.CAfile, data->set.ssl.CAfile,
data->set.ssl.CApath)) { data->set.ssl.CApath)) {
failf(data,"error setting certificate verify locations"); failf(data,"error setting certificate verify locations");
return CURLE_SSL_CACERT; return CURLE_SSL_CACERT;
} }
@ -838,10 +983,10 @@ Curl_SSLConnect(struct connectdata *conn)
SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback); SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback);
/* give application a chance to interfere with SSL set up. */ /* give application a chance to interfere with SSL set up. */
if (data->set.ssl.fsslctx) { if(data->set.ssl.fsslctx) {
retcode = (*data->set.ssl.fsslctx)(data, conn->ssl.ctx, retcode = (*data->set.ssl.fsslctx)(data, conn->ssl.ctx,
data->set.ssl.fsslctxp); data->set.ssl.fsslctxp);
if (retcode) { if(retcode) {
failf(data,"error signaled by ssl ctx callback"); failf(data,"error signaled by ssl ctx callback");
return retcode; return retcode;
} }
@ -996,15 +1141,15 @@ Curl_SSLConnect(struct connectdata *conn)
* attack * attack
*/ */
conn->ssl.server_cert = SSL_get_peer_certificate (conn->ssl.handle); conn->ssl.server_cert = SSL_get_peer_certificate(conn->ssl.handle);
if(!conn->ssl.server_cert) { if(!conn->ssl.server_cert) {
failf(data, "SSL: couldn't get peer certificate!"); failf(data, "SSL: couldn't get peer certificate!");
return CURLE_SSL_PEER_CERTIFICATE; return CURLE_SSL_PEER_CERTIFICATE;
} }
infof (data, "Server certificate:\n"); infof (data, "Server certificate:\n");
str = X509_NAME_oneline (X509_get_subject_name (conn->ssl.server_cert), str = X509_NAME_oneline(X509_get_subject_name(conn->ssl.server_cert),
NULL, 0); NULL, 0);
if(!str) { if(!str) {
failf(data, "SSL: couldn't get X509-subject!"); failf(data, "SSL: couldn't get X509-subject!");
X509_free(conn->ssl.server_cert); X509_free(conn->ssl.server_cert);
@ -1019,45 +1164,14 @@ Curl_SSLConnect(struct connectdata *conn)
certdate = X509_get_notAfter(conn->ssl.server_cert); certdate = X509_get_notAfter(conn->ssl.server_cert);
Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate); Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);
if (data->set.ssl.verifyhost) { if(data->set.ssl.verifyhost) {
char peer_CN[257]; retcode = verifyhost(conn);
if (X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert), if(retcode)
NID_commonName, return retcode;
peer_CN,
sizeof(peer_CN)) < 0) {
/* Failed to get the CN field from the server's certificate */
if (data->set.ssl.verifyhost > 1) {
failf(data, "SSL: unable to obtain common name from peer certificate");
X509_free(conn->ssl.server_cert);
return CURLE_SSL_PEER_CERTIFICATE;
}
else
/* Consider verifyhost == 1 as an "OK" for a missing CN field, but we
output a note about the situation */
infof(data, "\t common name: WARNING couldn't obtain\n");
}
else {
/* Compare the CN field with the remote host name */
if (!cert_hostcheck(peer_CN, conn->hostname)) {
if (data->set.ssl.verifyhost > 1) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'",
peer_CN, conn->hostname);
X509_free(conn->ssl.server_cert);
return CURLE_SSL_PEER_CERTIFICATE;
}
else
infof(data,
"\t common name: %s (does not match '%s')\n",
peer_CN, conn->hostname);
}
else
infof(data, "\t common name: %s (matched)\n", peer_CN);
}
} }
str = X509_NAME_oneline (X509_get_issuer_name (conn->ssl.server_cert), str = X509_NAME_oneline(X509_get_issuer_name(conn->ssl.server_cert),
NULL, 0); NULL, 0);
if(!str) { if(!str) {
failf(data, "SSL: couldn't get X509-issuer name!"); failf(data, "SSL: couldn't get X509-issuer name!");
X509_free(conn->ssl.server_cert); X509_free(conn->ssl.server_cert);
@ -1071,7 +1185,7 @@ Curl_SSLConnect(struct connectdata *conn)
if(data->set.ssl.verifypeer) { if(data->set.ssl.verifypeer) {
data->set.ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle); data->set.ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle);
if (data->set.ssl.certverifyresult != X509_V_OK) { if(data->set.ssl.certverifyresult != X509_V_OK) {
failf(data, "SSL certificate verify result: %d", failf(data, "SSL certificate verify result: %d",
data->set.ssl.certverifyresult); data->set.ssl.certverifyresult);
retcode = CURLE_SSL_PEER_CERTIFICATE; retcode = CURLE_SSL_PEER_CERTIFICATE;