diff --git a/CHANGES b/CHANGES index 4d6fc8c9d..0a9c12d47 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,16 @@ Changelog +Daniel (18 June 2004) +- Gisle Vanem's patch that provides more details from the SSL layers (if you + use an OpenSSL version that supports it). It also introduces two new types + of data that can be sent to the debug callback: CURLINFO_SSL_DATA_IN and + CURLINFO_SSL_DATA_OUT. + +- With David Byron's test server I could repeat his problem and make sure that + POSTing over HTTPS:// with NTLM works fine now. There was a general problem + with multi-pass authentication with non-GET operations with CONNECT. + Daniel (16 June 2004) - Modified to keep the upload byte counter in an curl_off_t, not an int as before. 32bits is not enough. This is most likely the bug Jean-Louis Lemaire diff --git a/include/curl/curl.h b/include/curl/curl.h index 409256246..ef7129856 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -173,6 +173,8 @@ typedef enum { CURLINFO_HEADER_OUT, /* 2 */ CURLINFO_DATA_IN, /* 3 */ CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ CURLINFO_END } curl_infotype; diff --git a/lib/sendf.c b/lib/sendf.c index a4fbb5182..b1b33a8fc 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -204,7 +204,8 @@ CURLcode Curl_sendf(curl_socket_t sockfd, struct connectdata *conn, break; if(data->set.verbose) - Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written, conn->host.dispname); + Curl_debug(data, CURLINFO_DATA_OUT, sptr, bytes_written, + conn->host.dispname); if((size_t)bytes_written != write_len) { /* if not all was written at once, we must advance the pointer, decrease @@ -444,7 +445,7 @@ static int showit(struct SessionHandle *data, curl_infotype type, char *ptr, size_t size) { static const char * const s_infotype[CURLINFO_END] = { - "* ", "< ", "> ", "{ ", "} " }; + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; if(data->set.fdebug) return (*data->set.fdebug)(data, type, ptr, size, diff --git a/lib/ssluse.c b/lib/ssluse.c index abe299811..544385305 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -47,6 +47,9 @@ #include "connect.h" /* Curl_ourerrno() proto */ #include "strequal.h" +#define _MPRINTF_REPLACE /* use the internal *printf() functions */ +#include + #ifdef USE_SSLEAY #include #include @@ -56,6 +59,10 @@ /* The last #include file should be: */ #include "memdebug.h" +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + #if OPENSSL_VERSION_NUMBER >= 0x0090581fL #define HAVE_SSL_GET1_SESSION 1 #else @@ -859,6 +866,7 @@ static CURLcode verifyhost(struct connectdata *conn, /* Is this a wildcard match? */ else if((altptr[0] == '*') && (domainlen == altlen-1) && + domain && curl_strnequal(domain, altptr+1, domainlen)) matched = TRUE; break; @@ -938,6 +946,115 @@ static CURLcode verifyhost(struct connectdata *conn, } #endif +/* The SSL_CTRL_SET_MSG_CALLBACK doesn't exist in ancient OpenSSL versions + and thus this cannot be done there. */ +#ifdef SSL_CTRL_SET_MSG_CALLBACK + +static const char *ssl_msg_type(int ssl_ver, int msg) +{ + if (ssl_ver == SSL2_VERSION_MAJOR) { + switch (msg) { + case SSL2_MT_ERROR: + return "Error"; + case SSL2_MT_CLIENT_HELLO: + return "Client hello"; + case SSL2_MT_CLIENT_MASTER_KEY: + return "Client key"; + case SSL2_MT_CLIENT_FINISHED: + return "Client finished"; + case SSL2_MT_SERVER_HELLO: + return "Server hello"; + case SSL2_MT_SERVER_VERIFY: + return "Server verify"; + case SSL2_MT_SERVER_FINISHED: + return "Server finished"; + case SSL2_MT_REQUEST_CERTIFICATE: + return "Request CERT"; + case SSL2_MT_CLIENT_CERTIFICATE: + return "Client CERT"; + } + } + else if (ssl_ver == SSL3_VERSION_MAJOR) { + switch (msg) { + case SSL3_MT_HELLO_REQUEST: + return "Hello request"; + case SSL3_MT_CLIENT_HELLO: + return "Client hello"; + case SSL3_MT_SERVER_HELLO: + return "Server hello"; + case SSL3_MT_CERTIFICATE: + return "CERT"; + case SSL3_MT_SERVER_KEY_EXCHANGE: + return "Server key exchange"; + case SSL3_MT_CLIENT_KEY_EXCHANGE: + return "Client key exchange"; + case SSL3_MT_CERTIFICATE_REQUEST: + return "Request CERT"; + case SSL3_MT_SERVER_DONE: + return "Server finished"; + case SSL3_MT_CERTIFICATE_VERIFY: + return "CERT verify"; + case SSL3_MT_FINISHED: + return "Finished"; + } + } + return "Unknown"; +} + +static const char *tls_rt_type(int type) +{ + return ( + type == SSL3_RT_CHANGE_CIPHER_SPEC ? "TLS change cipher, " : + type == SSL3_RT_ALERT ? "TLS alert, " : + type == SSL3_RT_HANDSHAKE ? "TLS handshake, " : + type == SSL3_RT_APPLICATION_DATA ? "TLS app data, " : + "TLS Unknown, "); +} + + +/* + * Our callback from the SSL/TLS layers. + */ +static void ssl_tls_trace(int direction, int ssl_ver, int content_type, + const void *buf, size_t len, const SSL *ssl, + struct connectdata *conn) +{ + struct SessionHandle *data = conn->data; + const char *msg_name, *tls_rt_name; + char ssl_buf[1024]; + int ver, msg_type, txt_len; + + if (!conn || !conn->data || !conn->data->set.fdebug || + (direction != 0 && direction != 1)) + return; + + data = conn->data; + ssl_ver >>= 8; + ver = (ssl_ver == SSL2_VERSION_MAJOR ? '2' : + ssl_ver == SSL3_VERSION_MAJOR ? '3' : '?'); + + /* SSLv2 doesn't seem to have TLS record-type headers, so OpenSSL + * always pass-up content-type as 0. But the interesting message-tupe + * is at 'buf[0]'. + */ + if (ssl_ver == SSL3_VERSION_MAJOR && content_type != 0) + tls_rt_name = tls_rt_type(content_type); + else + tls_rt_name = ""; + + msg_type = *(char*)buf; + msg_name = ssl_msg_type(ssl_ver, msg_type); + + txt_len = 1 + sprintf(ssl_buf, "SSLv%c, %s%s (%d):\n", + ver, tls_rt_name, msg_name, msg_type); + Curl_debug(data, CURLINFO_TEXT, ssl_buf, txt_len, NULL); + + Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT : + CURLINFO_SSL_DATA_IN, buf, len, NULL); + (void) ssl; +} +#endif + /* ====================================================== */ CURLcode Curl_SSLConnect(struct connectdata *conn, @@ -991,6 +1108,14 @@ Curl_SSLConnect(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } +#ifdef SSL_CTRL_SET_MSG_CALLBACK + if (data->set.fdebug) { + SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK, + ssl_tls_trace); + SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, conn); + } +#endif + /* OpenSSL contains code to work-around lots of bugs and flaws in various SSL-implementations. SSL_CTX_set_options() is used to enabled those work-arounds. The man page for this option states that SSL_OP_ALL enables @@ -1001,6 +1126,16 @@ Curl_SSLConnect(struct connectdata *conn, */ SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL); +#if 0 + /* + * Not sure it's needed to tell SSL_connect() that socket is + * non-blocking. It doesn't seem to care, but just return with + * SSL_ERROR_WANT_x. + */ + if (data->state.used_interface == Curl_if_multi) + SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL); +#endif + if(data->set.cert) { if(!cert_stuff(conn, connssl->ctx, @@ -1105,10 +1240,6 @@ Curl_SSLConnect(struct connectdata *conn, /* 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)) @@ -1150,6 +1281,8 @@ Curl_SSLConnect(struct connectdata *conn, unsigned long errdetail; char error_buffer[120]; /* OpenSSL documents that this must be at least 120 bytes long. */ + CURLcode rc; + const char *cert_problem = NULL; errdetail = ERR_get_error(); /* Gets the earliest error code from the thread's error queue and removes the @@ -1161,16 +1294,34 @@ Curl_SSLConnect(struct connectdata *conn, SSL routines: SSL2_SET_CERTIFICATE: certificate verify failed */ + /* fall-through */ 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; + cert_problem = "SSL certificate problem, verify that the CA cert is" + " OK. Details:\n"; + rc = CURLE_SSL_CACERT; + break; default: + rc = CURLE_SSL_CONNECT_ERROR; + break; + } + /* detail is already set to the SSL error above */ + + /* If we e.g. use SSLv2 request-method and the server doesn't like us + * (RST connection etc.), OpenSSL gives no explanation whatsoever and + * the SO_ERROR is also lost. + */ + if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) { + failf(data, "Unknown SSL protocol error in connection to %s:%d ", + conn->host.name, conn->port); + return rc; + } + /* Could be a CERT problem */ + #ifdef HAVE_ERR_ERROR_STRING_N /* OpenSSL 0.9.6 and later has a function named ERRO_error_string_n() that takes the size of the buffer as a @@ -1179,10 +1330,8 @@ Curl_SSLConnect(struct connectdata *conn, #else ERR_error_string(errdetail, error_buffer); #endif - - failf(data, "SSL: %s", error_buffer); - return CURLE_SSL_CONNECT_ERROR; - } + failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer); + return rc; } } else @@ -1278,18 +1427,18 @@ Curl_SSLConnect(struct connectdata *conn, /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */ - data->set.ssl.certverifyresult=SSL_get_verify_result(connssl->handle); + err = 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 and we return earlyer if verifypeer is set? */ - failf(data, "SSL certificate verify result: %d", - data->set.ssl.certverifyresult); + failf(data, "SSL certificate verify result: %s (%d)", + X509_verify_cert_error_string(err), err); retcode = CURLE_SSL_PEER_CERTIFICATE; } else - infof(data, "SSL certificate verify result: %d, continuing anyway.\n", - data->set.ssl.certverifyresult); + infof(data, "SSL certificate verify result: %s (%d), continuing anyway.\n", + X509_verify_cert_error_string(err), err); } else infof(data, "SSL certificate verify ok.\n"); diff --git a/lib/url.c b/lib/url.c index f83e49435..e165b3730 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1892,7 +1892,7 @@ static int handleSock5Proxy(const char *proxy_name, } #else failf(conn->data, - "%s:%d has an internal error an needs to be fixed to work", + "%s:%d has an internal error and needs to be fixed to work", __FILE__, __LINE__); #endif } diff --git a/src/main.c b/src/main.c index 9551cfe26..c759fcc63 100644 --- a/src/main.c +++ b/src/main.c @@ -2572,7 +2572,6 @@ int my_trace(CURL *handle, curl_infotype type, struct Configurable *config = (struct Configurable *)userp; FILE *output=config->errors; const char *text; - (void)handle; /* prevent compiler warning */ if(!config->trace_stream) { @@ -2606,6 +2605,12 @@ int my_trace(CURL *handle, curl_infotype type, case CURLINFO_DATA_IN: text = "<= Recv data"; break; + case CURLINFO_SSL_DATA_IN: + text = "<= Recv SSL data"; + break; + case CURLINFO_SSL_DATA_OUT: + text = "<= Send SSL data"; + break; } dump(text, output, data, size, config->trace_ascii);