Newer
Older
Daniel Stenberg
committed
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
print_pubkey_BN(dsa, p, i);
print_pubkey_BN(dsa, q, i);
print_pubkey_BN(dsa, g, i);
print_pubkey_BN(dsa, priv_key, i);
print_pubkey_BN(dsa, pub_key, i);
break;
case EVP_PKEY_DH:
print_pubkey_BN(dh, p, i);
print_pubkey_BN(dh, g, i);
print_pubkey_BN(dh, priv_key, i);
print_pubkey_BN(dh, pub_key, i);
break;
#if 0
case EVP_PKEY_EC: /* symbol not present in OpenSSL 0.9.6 */
/* left TODO */
break;
#endif
}
}
X509V3_ext(data, i, cinf->extensions);
X509_signature(data, i, x->signature);
dumpcert(data, x, i);
}
return CURLE_OK;
}
Daniel Stenberg
committed
/*
* Get the server cert, verify it and show it etc, only call failf() if the
* 'strict' argument is TRUE as otherwise all this is for informational
* purposes only!
*
* We check certificates to authenticate the server; otherwise we risk
* man-in-the-middle attack.
*/
static CURLcode servercert(struct connectdata *conn,
struct ssl_connect_data *connssl,
bool strict)
Daniel Stenberg
committed
{
CURLcode retcode = CURLE_OK;
Daniel Stenberg
committed
int rc;
Daniel Stenberg
committed
long lerr;
ASN1_TIME *certdate;
struct SessionHandle *data = conn->data;
Daniel Stenberg
committed
X509 *issuer;
FILE *fp;
Daniel Stenberg
committed
char buffer[256];
if(data->set.ssl.certinfo)
/* we've been asked to gather certificate info! */
(void)get_cert_chain(conn, connssl);
Daniel Stenberg
committed
Daniel Stenberg
committed
data->set.ssl.certverifyresult = !X509_V_OK;
Daniel Stenberg
committed
connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
if(!connssl->server_cert) {
Daniel Stenberg
committed
if(strict)
failf(data, "SSL: couldn't get peer certificate!");
return CURLE_PEER_FAILED_VERIFICATION;
}
infof (data, "Server certificate:\n");
Daniel Stenberg
committed
Daniel Stenberg
committed
rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
buffer, sizeof(buffer));
if(rc) {
Daniel Stenberg
committed
if(strict)
failf(data, "SSL: couldn't get X509-subject!");
Daniel Stenberg
committed
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
Daniel Stenberg
committed
return CURLE_SSL_CONNECT_ERROR;
Daniel Stenberg
committed
infof(data, "\t subject: %s\n", buffer);
Daniel Stenberg
committed
certdate = X509_get_notBefore(connssl->server_cert);
Daniel Stenberg
committed
asn1_output(certdate, buffer, sizeof(buffer));
infof(data, "\t start date: %s\n", buffer);
Daniel Stenberg
committed
certdate = X509_get_notAfter(connssl->server_cert);
Daniel Stenberg
committed
asn1_output(certdate, buffer, sizeof(buffer));
infof(data, "\t expire date: %s\n", buffer);
if(data->set.ssl.verifyhost) {
Daniel Stenberg
committed
retcode = verifyhost(conn, connssl->server_cert);
if(retcode) {
Daniel Stenberg
committed
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return retcode;
}
Daniel Stenberg
committed
}
Daniel Stenberg
committed
rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert),
buffer, sizeof(buffer));
if(rc) {
Daniel Stenberg
committed
if(strict)
failf(data, "SSL: couldn't get X509-issuer name!");
retcode = CURLE_SSL_CONNECT_ERROR;
else {
Daniel Stenberg
committed
infof(data, "\t issuer: %s\n", buffer);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
Daniel Stenberg
committed
Daniel Stenberg
committed
/* e.g. match issuer name with provided issuer certificate */
if (data->set.str[STRING_SSL_ISSUERCERT]) {
if (! (fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r"))) {
if (strict)
Daniel Stenberg
committed
failf(data, "SSL: Unable to open issuer cert (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
Daniel Stenberg
committed
}
issuer = PEM_read_X509(fp,NULL,ZERO_NULL,NULL);
Daniel Stenberg
committed
if (!issuer) {
if (strict)
Daniel Stenberg
committed
failf(data, "SSL: Unable to read issuer cert (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
fclose(fp);
return CURLE_SSL_ISSUER_ERROR;
Daniel Stenberg
committed
}
fclose(fp);
if (X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
if (strict)
Daniel Stenberg
committed
failf(data, "SSL: Certificate issuer check failed (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
X509_free(connssl->server_cert);
X509_free(issuer);
connssl->server_cert = NULL;
Daniel Stenberg
committed
return CURLE_SSL_ISSUER_ERROR;
}
infof(data, "\t SSL certificate issuer check ok (%s)\n",
Daniel Stenberg
committed
data->set.str[STRING_SSL_ISSUERCERT]);
Daniel Stenberg
committed
X509_free(issuer);
}
Daniel Stenberg
committed
lerr = data->set.ssl.certverifyresult=
SSL_get_verify_result(connssl->handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
if(data->set.ssl.verifypeer) {
/* We probably never reach this, because SSL_connect() will fail
Daniel Stenberg
committed
and we return earlier if verifypeer is set? */
Daniel Stenberg
committed
if(strict)
failf(data, "SSL certificate verify result: %s (%ld)",
X509_verify_cert_error_string(lerr), lerr);
retcode = CURLE_PEER_FAILED_VERIFICATION;
}
else
Daniel Stenberg
committed
infof(data, "\t SSL certificate verify result: %s (%ld),"
Daniel Stenberg
committed
" continuing anyway.\n",
X509_verify_cert_error_string(lerr), lerr);
Daniel Stenberg
committed
}
else
Daniel Stenberg
committed
infof(data, "\t SSL certificate verify ok.\n");
Daniel Stenberg
committed
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
Daniel Stenberg
committed
connssl->connecting_state = ssl_connect_done;
Daniel Stenberg
committed
return retcode;
}
static CURLcode
ossl_connect_step3(struct connectdata *conn,
int sockindex)
Daniel Stenberg
committed
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
{
CURLcode retcode = CURLE_OK;
void *ssl_sessionid=NULL;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
if(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
/* Since this is not a cached session ID, then we want to stach this one
in the cache! */
SSL_SESSION *our_ssl_sessionid;
#ifdef HAVE_SSL_GET1_SESSION
our_ssl_sessionid = SSL_get1_session(connssl->handle);
/* SSL_get1_session() will increment the reference
count and the session will stay in memory until explicitly freed with
SSL_SESSION_free(3), regardless of its state.
This function was introduced in openssl 0.9.5a. */
#else
our_ssl_sessionid = SSL_get_session(connssl->handle);
/* if SSL_get1_session() is unavailable, use SSL_get_session().
This is an inferior option because the session can be flushed
at any time by openssl. It is included only so curl compiles
under versions of openssl < 0.9.5a.
WARNING: How curl behaves if it's session is flushed is
untested.
*/
#endif
retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
0 /* unknown size */);
if(retcode) {
failf(data, "failed to store ssl session");
return retcode;
}
}
/*
* We check certificates to authenticate the server; otherwise we risk
* man-in-the-middle attack; NEVERTHELESS, if we're told explicitly not to
* verify the peer ignore faults and failures from the server cert
* operations.
*/
if(!data->set.ssl.verifypeer)
(void)servercert(conn, connssl, FALSE);
else
retcode = servercert(conn, connssl, TRUE);
if(CURLE_OK == retcode)
connssl->connecting_state = ssl_connect_done;
Daniel Stenberg
committed
return retcode;
Daniel Stenberg
committed
Daniel Stenberg
committed
static CURLcode
ossl_connect_common(struct connectdata *conn,
int sockindex,
bool nonblocking,
bool *done)
Daniel Stenberg
committed
{
CURLcode retcode;
struct SessionHandle *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
long timeout_ms;
Daniel Stenberg
committed
if(ssl_connect_1==connssl->connecting_state) {
/* Find out how much more time we're allowed */
timeout_ms = Curl_timeleft(conn, NULL, TRUE);
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
retcode = ossl_connect_step1(conn, sockindex);
Daniel Stenberg
committed
if(retcode)
Daniel Stenberg
committed
return retcode;
}
timeout_ms = 0;
Daniel Stenberg
committed
while(ssl_connect_2 == connssl->connecting_state ||
ssl_connect_2_reading == connssl->connecting_state ||
ssl_connect_2_writing == connssl->connecting_state) {
Daniel Stenberg
committed
/* check allowed time left */
timeout_ms = Curl_timeleft(conn, NULL, TRUE);
if(timeout_ms < 0) {
/* no need to continue if time already is up */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
Daniel Stenberg
committed
/* if ssl is expecting something, check if it's available. */
Daniel Stenberg
committed
if(connssl->connecting_state == ssl_connect_2_reading
Daniel Stenberg
committed
|| connssl->connecting_state == ssl_connect_2_writing) {
Daniel Stenberg
committed
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
Daniel Stenberg
committed
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
while(1) {
Daniel Stenberg
committed
int what = Curl_socket_ready(readfd, writefd,
nonblocking?0:(int)timeout_ms);
Daniel Stenberg
committed
if(what > 0)
Daniel Stenberg
committed
/* readable or writable, go loop in the outer loop */
Daniel Stenberg
committed
break;
else if(0 == what) {
Daniel Stenberg
committed
if(nonblocking) {
Daniel Stenberg
committed
*done = FALSE;
return CURLE_OK;
}
else {
/* timeout */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEDOUT;
}
}
else {
/* anything that gets here is fatally bad */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
Daniel Stenberg
committed
return CURLE_SSL_CONNECT_ERROR;
}
} /* while()-loop for the select() */
}
/* get the timeout from step2 to avoid computing it twice. */
retcode = ossl_connect_step2(conn, sockindex);
Daniel Stenberg
committed
if(retcode)
Daniel Stenberg
committed
return retcode;
} /* repeat step2 until all transactions are done. */
Daniel Stenberg
committed
if(ssl_connect_3==connssl->connecting_state) {
retcode = ossl_connect_step3(conn, sockindex);
Daniel Stenberg
committed
if(retcode)
Daniel Stenberg
committed
return retcode;
}
Daniel Stenberg
committed
if(ssl_connect_done==connssl->connecting_state) {
Daniel Stenberg
committed
connssl->state = ssl_connection_complete;
Daniel Stenberg
committed
*done = TRUE;
}
Daniel Stenberg
committed
else
Daniel Stenberg
committed
*done = FALSE;
Daniel Stenberg
committed
/* Reset our connect state machine */
connssl->connecting_state = ssl_connect_1;
Daniel Stenberg
committed
return CURLE_OK;
}
CURLcode
Curl_ossl_connect_nonblocking(struct connectdata *conn,
int sockindex,
bool *done)
{
return ossl_connect_common(conn, sockindex, TRUE, done);
Daniel Stenberg
committed
}
CURLcode
Curl_ossl_connect(struct connectdata *conn,
int sockindex)
{
CURLcode retcode;
bool done = FALSE;
retcode = ossl_connect_common(conn, sockindex, FALSE, &done);
Daniel Stenberg
committed
if(retcode)
Daniel Stenberg
committed
return retcode;
DEBUGASSERT(done);
Daniel Stenberg
committed
return CURLE_OK;
}
Daniel Stenberg
committed
bool Curl_ossl_data_pending(const struct connectdata *conn,
int connindex)
{
if(conn->ssl[connindex].handle)
/* SSL is in use */
return (bool)(0 != SSL_pending(conn->ssl[connindex].handle));
else
return FALSE;
}
Daniel Stenberg
committed
/* return number of sent (non-SSL) bytes */
Daniel Stenberg
committed
ssize_t Curl_ossl_send(struct connectdata *conn,
int sockindex,
const void *mem,
Daniel Stenberg
committed
size_t len)
Daniel Stenberg
committed
{
/* SSL_write() is said to return 'int' while write() and send() returns
'size_t' */
int err;
char error_buffer[120]; /* OpenSSL documents that this must be at least 120
bytes long. */
unsigned long sslerror;
int rc = SSL_write(conn->ssl[sockindex].handle, mem, (int)len);
if(rc < 0) {
err = SSL_get_error(conn->ssl[sockindex].handle, rc);
switch(err) {
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* The operation did not complete; the same TLS/SSL I/O function
should be called again later. This is basicly an EWOULDBLOCK
equivalent. */
return 0;
case SSL_ERROR_SYSCALL:
Daniel Stenberg
committed
failf(conn->data, "SSL_write() returned SYSCALL, errno = %d",
SOCKERRNO);
Daniel Stenberg
committed
return -1;
case SSL_ERROR_SSL:
/* A failure in the SSL library occurred, usually a protocol error.
The OpenSSL error queue contains more information on the error. */
sslerror = ERR_get_error();
Daniel Stenberg
committed
failf(conn->data, "SSL_write() error: %s",
Daniel Stenberg
committed
ERR_error_string(sslerror, error_buffer));
return -1;
}
/* a true error */
Daniel Stenberg
committed
failf(conn->data, "SSL_write() return error %d", err);
Daniel Stenberg
committed
return -1;
}
Daniel Stenberg
committed
return (ssize_t)rc; /* number of bytes */
Daniel Stenberg
committed
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
}
/*
* If the read would block we return -1 and set 'wouldblock' to TRUE.
* Otherwise we return the amount of data read. Other errors should return -1
* and set 'wouldblock' to FALSE.
*/
ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */
int num, /* socketindex */
char *buf, /* store read data here */
size_t buffersize, /* max amount to read */
bool *wouldblock)
{
char error_buffer[120]; /* OpenSSL documents that this must be at
least 120 bytes long. */
unsigned long sslerror;
ssize_t nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf,
(int)buffersize);
*wouldblock = FALSE;
if(nread < 0) {
/* failed SSL_read */
int err = SSL_get_error(conn->ssl[num].handle, (int)nread);
switch(err) {
case SSL_ERROR_NONE: /* this is not an error */
case SSL_ERROR_ZERO_RETURN: /* no more data */
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke SSL_read() */
*wouldblock = TRUE;
return -1; /* basically EWOULDBLOCK */
default:
/* openssl/ssl.h says "look at error stack/return value/errno" */
sslerror = ERR_get_error();
failf(conn->data, "SSL read: %s, errno %d",
ERR_error_string(sslerror, error_buffer),
SOCKERRNO);
Daniel Stenberg
committed
return -1;
}
}
return nread;
}
size_t Curl_ossl_version(char *buffer, size_t size)
{
Daniel Stenberg
committed
#ifdef YASSL_VERSION
/* yassl provides an OpenSSL API compatiblity layer so it looks identical
to OpenSSL in all other aspects */
return snprintf(buffer, size, "yassl/%s", YASSL_VERSION);
Daniel Stenberg
committed
#else /* YASSL_VERSION */
Daniel Stenberg
committed
#if(SSLEAY_VERSION_NUMBER >= 0x905000)
Daniel Stenberg
committed
{
char sub[2];
unsigned long ssleay_value;
sub[1]='\0';
ssleay_value=SSLeay();
if(ssleay_value < 0x906000) {
ssleay_value=SSLEAY_VERSION_NUMBER;
sub[0]='\0';
}
else {
if(ssleay_value&0xff0) {
Daniel Stenberg
committed
}
else
sub[0]='\0';
}
return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx%s",
Daniel Stenberg
committed
(ssleay_value>>28)&0xf,
(ssleay_value>>20)&0xff,
(ssleay_value>>12)&0xff,
sub);
}
#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
Daniel Stenberg
committed
#if(SSLEAY_VERSION_NUMBER >= 0x900000)
return snprintf(buffer, size, "OpenSSL/%lx.%lx.%lx",
Daniel Stenberg
committed
(SSLEAY_VERSION_NUMBER>>28)&0xff,
(SSLEAY_VERSION_NUMBER>>20)&0xff,
(SSLEAY_VERSION_NUMBER>>12)&0xf);
#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
{
char sub[2];
sub[1]='\0';
if(SSLEAY_VERSION_NUMBER&0x0f) {
sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1;
}
else
sub[0]='\0';
return snprintf(buffer, size, "SSL/%x.%x.%x%s",
Daniel Stenberg
committed
(SSLEAY_VERSION_NUMBER>>12)&0xff,
(SSLEAY_VERSION_NUMBER>>8)&0xf,
(SSLEAY_VERSION_NUMBER>>4)&0xf, sub);
}
#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */
#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */
Daniel Stenberg
committed
#endif /* YASSL_VERSION */
Daniel Stenberg
committed
}
#endif /* USE_SSLEAY */