Newer
Older
/* Everything is fine. */
infof(data,"successfully set certificate verify locations:\n");
infof(data, " CAfile: %s\n",
data->set.ssl.CAfile ? data->set.ssl.CAfile : "none");
infof(data, " CApath: %s\n",
data->set.ssl.CApath ? data->set.ssl.CApath : "none");
Daniel Stenberg
committed
}
/* SSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
SSL_CTX_set_verify(conn->ssl.ctx,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
cert_verify_callback);
Daniel Stenberg
committed
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
retcode = (*data->set.ssl.fsslctx)(data, conn->ssl.ctx,
data->set.ssl.fsslctxp);
if(retcode) {
failf(data,"error signaled by ssl ctx callback");
return retcode;
}
}
/* Lets make an SSL structure */
conn->ssl.handle = SSL_new (conn->ssl.ctx);
SSL_set_connect_state (conn->ssl.handle);
conn->ssl.server_cert = 0x0;
if(!conn->bits.reuse) {
/* We're not re-using a connection, check if there's a cached ID we
can/should use here! */
if(!Get_SSL_Session(conn, &ssl_sessionid)) {
/* we got a session id, use it! */
SSL_set_session(conn->ssl.handle, ssl_sessionid);
/* Informational message */
infof (data, "SSL re-using session ID\n");
}
}
/* pass the raw socket into the SSL layers */
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
SSL_set_fd(conn->ssl.handle, conn->firstsocket);
do {
fd_set writefd;
fd_set readfd;
struct timeval interval;
long timeout_ms;
/* Find out if any timeout is set. If not, use 300 seconds.
Otherwise, figure out the most strict timeout of the two possible one
and then how much time that has elapsed to know how much time we
allow for the connect call */
if(data->set.timeout || data->set.connecttimeout) {
double has_passed;
/* Evaluate in milliseconds how much time that has passed */
has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);
#ifndef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/* get the most strict timeout of the ones converted to milliseconds */
if(data->set.timeout &&
(data->set.timeout>data->set.connecttimeout))
timeout_ms = data->set.timeout*1000;
else
timeout_ms = data->set.connecttimeout*1000;
/* subtract the passed time */
timeout_ms -= (long)has_passed;
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEOUTED;
}
}
else
/* no particular time-out has been set */
timeout_ms=300000; /* milliseconds, default to five minutes */
Daniel Stenberg
committed
FD_ZERO(&writefd);
FD_ZERO(&readfd);
err = SSL_connect(conn->ssl.handle);
/* 1 is fine
0 is "not successful but was shut down controlled"
<0 is "handshake was not successful, because a fatal error occurred" */
if(1 != err) {
int detail = SSL_get_error(conn->ssl.handle, err);
if(SSL_ERROR_WANT_READ == detail)
FD_SET(conn->firstsocket, &readfd);
else if(SSL_ERROR_WANT_WRITE == detail)
FD_SET(conn->firstsocket, &writefd);
else {
/* untreated error */
char error_buffer[120]; /* OpenSSL documents that this must be at least
120 bytes long. */
detail = ERR_get_error(); /* Gets the earliest error code from the
thread's error queue and removes the
entry. */
switch(detail) {
case 0x1407E086:
/* 1407E086:
SSL routines:
SSL2_SET_CERTIFICATE:
certificate verify failed */
case 0x14090086:
/* 14090086:
SSL routines:
SSL3_GET_SERVER_CERTIFICATE:
certificate verify failed */
failf(data,
"SSL certificate problem, verify that the CA cert is OK");
return CURLE_SSL_CACERT;
default:
/* detail is already set to the SSL error above */
failf(data, "SSL: %s", ERR_error_string(detail, error_buffer));
/* OpenSSL 0.9.6 and later has a function named
ERRO_error_string_n() that takes the size of the buffer as a third
argument, and we should possibly switch to using that one in the
future. */
return CURLE_SSL_CONNECT_ERROR;
}
Daniel Stenberg
committed
}
}
else
/* we have been connected fine, get out of the connect loop */
break;
interval.tv_sec = timeout_ms/1000;
timeout_ms -= interval.tv_sec*1000;
interval.tv_usec = timeout_ms*1000;
what = select(conn->firstsocket+1, &readfd, &writefd, NULL, &interval);
if(what > 0)
/* reabable or writable, go loop yourself */
continue;
else if(0 == what) {
/* timeout */
failf(data, "SSL connection timeout");
return CURLE_OPERATION_TIMEOUTED;
}
else
break; /* get out of loop */
} while(1);
/* Informational message */
infof (data, "SSL connection using %s\n",
SSL_get_cipher(conn->ssl.handle));
if(!ssl_sessionid) {
/* Since this is not a cached session ID, then we want to stach this one
in the cache! */
Store_SSL_Session(conn);
}
/* Get server's certificate (note: beware of dynamic allocation) - opt */
/* major serious hack alert -- we should check certificates
* to authenticate the server; otherwise we risk man-in-the-middle
* attack
*/
conn->ssl.server_cert = SSL_get_peer_certificate(conn->ssl.handle);
if(!conn->ssl.server_cert) {
failf(data, "SSL: couldn't get peer certificate!");
Daniel Stenberg
committed
return CURLE_SSL_PEER_CERTIFICATE;
}
infof (data, "Server certificate:\n");
str = X509_NAME_oneline(X509_get_subject_name(conn->ssl.server_cert),
NULL, 0);
if(!str) {
failf(data, "SSL: couldn't get X509-subject!");
Daniel Stenberg
committed
X509_free(conn->ssl.server_cert);
Daniel Stenberg
committed
return CURLE_SSL_CONNECT_ERROR;
}
infof(data, "\t subject: %s\n", str);
CRYPTO_free(str);
certdate = X509_get_notBefore(conn->ssl.server_cert);
Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate);
certdate = X509_get_notAfter(conn->ssl.server_cert);
Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);
if(data->set.ssl.verifyhost) {
retcode = verifyhost(conn);
if(retcode) {
X509_free(conn->ssl.server_cert);
return retcode;
}
Daniel Stenberg
committed
}
str = X509_NAME_oneline(X509_get_issuer_name(conn->ssl.server_cert),
NULL, 0);
if(!str) {
failf(data, "SSL: couldn't get X509-issuer name!");
retcode = CURLE_SSL_CONNECT_ERROR;
else {
infof(data, "\t issuer: %s\n", str);
CRYPTO_free(str);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
Daniel Stenberg
committed
data->set.ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
if(data->set.ssl.verifypeer) {
/* We probably never reach this, because SSL_connect() will fail
and we return earlyer if verifypeer is set? */
failf(data, "SSL certificate verify result: %d",
data->set.ssl.certverifyresult);
retcode = CURLE_SSL_PEER_CERTIFICATE;
}
else
infof(data, "SSL certificate verify result: %d, continuing anyway.\n",
data->set.ssl.certverifyresult);
Daniel Stenberg
committed
}
else
infof(data, "SSL certificate verify ok.\n");
X509_free(conn->ssl.server_cert);
/* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
(void) conn;
Daniel Stenberg
committed
return retcode;