diff -u3 -Hb -r CVS-latest/include/curl/curl.h include/curl/curl.h --- CVS-latest/include/curl/curl.h Wed Jun 09 10:21:11 2004 +++ include/curl/curl.h Mon Jun 14 10:33:03 2004 @@ -173,6 +173,8 @@ 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; @@ -181,6 +183,7 @@ curl_infotype type, /* what kind of data */ char *data, /* points to the data */ size_t size, /* size of the data pointed to */ + const char *extra, /* extra info may be NULL (with CURLINFO_SSL_DATA_x) */ void *userptr); /* whatever the user please */ /* All possible error codes from all sorts of curl functions. Future versions diff -u3 -Hb -r CVS-latest/lib/sendf.c lib/sendf.c --- CVS-latest/lib/sendf.c Mon Jun 07 12:28:14 2004 +++ lib/sendf.c Mon Jun 14 17:19:20 2004 @@ -441,13 +441,13 @@ /* return 0 on success */ static int showit(struct SessionHandle *data, curl_infotype type, - char *ptr, size_t size) + char *ptr, size_t size, const char *extra) { static const char * const s_infotype[CURLINFO_END] = { - "* ", "< ", "> ", "{ ", "} " }; + "* ", "< ", "> ", "{ ", "} ", "{ ", "} " }; if(data->set.fdebug) - return (*data->set.fdebug)(data, type, ptr, size, + return (*data->set.fdebug)(data, type, ptr, size, extra, data->set.debugdata); switch(type) { @@ -485,11 +485,11 @@ if(t) { snprintf(buffer, sizeof(buffer), "[Data %s %s]", t, host); - rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer)); + rc = showit(data, CURLINFO_TEXT, buffer, strlen(buffer), NULL); if(rc) return rc; } } - rc = showit(data, type, ptr, size); + rc = showit(data, type, ptr, size, host); return rc; } diff -u3 -Hb -r CVS-latest/lib/ssluse.c lib/ssluse.c --- CVS-latest/lib/ssluse.c Sun Jun 13 10:33:26 2004 +++ lib/ssluse.c Tue Jun 15 17:52:33 2004 @@ -859,6 +859,7 @@ /* Is this a wildcard match? */ else if((altptr[0] == '*') && (domainlen == altlen-1) && + domain && curl_strnequal(domain, altptr+1, domainlen)) matched = TRUE; break; @@ -938,6 +939,114 @@ } #endif +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 = (char*) buf; /* Why doesn't Curl_debug() take a const? */ + char ssl_info[256]; + int ver, msg_type; + + 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 'ssl_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 = ssl_buf[0]; + msg_name = ssl_msg_type(ssl_ver, msg_type); + + if (direction == 1) { + snprintf(ssl_info, sizeof(ssl_info), "SSLv%c, %s%s (%d)", + ver, tls_rt_name, msg_name, msg_type); + Curl_debug(data, CURLINFO_SSL_DATA_OUT, ssl_buf, len, ssl_info); + } + else { + snprintf(ssl_info, sizeof(ssl_info), "SSLv%c, %s%s (%d)", + ver, tls_rt_name, msg_name, msg_type); + Curl_debug(data, CURLINFO_SSL_DATA_IN, ssl_buf, len, ssl_info); + } + (void) ssl; +} + /* ====================================================== */ CURLcode Curl_SSLConnect(struct connectdata *conn, @@ -991,6 +1100,11 @@ return CURLE_OUT_OF_MEMORY; } + 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); + } + /* 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 +1115,16 @@ */ 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, @@ -1150,6 +1274,8 @@ 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 +1287,34 @@ 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 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 +1323,8 @@ #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 +1420,18 @@ /* 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 -u3 -Hb -r CVS-latest/lib/url.c lib/url.c --- CVS-latest/lib/url.c Thu Jun 10 13:06:21 2004 +++ lib/url.c Thu Jun 10 19:03:43 2004 @@ -1892,7 +1892,7 @@ } #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 -u3 -Hb -r CVS-latest/src/main.c src/main.c --- CVS-latest/src/main.c Tue Jun 08 23:56:30 2004 +++ src/main.c Mon Jun 14 18:35:31 2004 @@ -2567,11 +2567,12 @@ static int my_trace(CURL *handle, curl_infotype type, unsigned char *data, size_t size, - void *userp) + const char *ssl_info, void *userp) { struct Configurable *config = (struct Configurable *)userp; FILE *output=config->errors; const char *text; + char extra[256]; (void)handle; /* prevent compiler warning */ @@ -2606,6 +2607,14 @@ case CURLINFO_DATA_IN: text = "<= Recv data"; break; + case CURLINFO_SSL_DATA_OUT: + snprintf(extra, sizeof(extra), "=> Send data, %s", ssl_info ? ssl_info : ""); + text = extra; + break; + case CURLINFO_SSL_DATA_IN: + snprintf(extra, sizeof(extra), "<= Recv data, %s", ssl_info ? ssl_info : ""); + text = extra; + break; } dump(text, output, data, size, config->trace_ascii);