Loading lib/gtls.c +152 −60 Original line number Diff line number Diff line Loading @@ -182,55 +182,77 @@ static void unload_file(gnutls_datum data) { } /* this function does a BLOCKING SSL/TLS (re-)handshake */ /* this function does a SSL/TLS (re-)handshake */ static CURLcode handshake(struct connectdata *conn, gnutls_session session, int sockindex, bool duringconnect) bool duringconnect, bool nonblocking) { struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; gnutls_session session = conn->ssl[sockindex].session; curl_socket_t sockfd = conn->sock[sockindex]; long timeout_ms; int rc; if(!gtls_inited) Curl_gtls_init(); do { rc = gnutls_handshake(session); int what; if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { long timeout_ms = Curl_timeleft(conn, NULL, duringconnect); while(1) { /* check allowed time left */ timeout_ms = Curl_timeleft(conn, NULL, duringconnect); if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } rc = Curl_socket_ready(conn->sock[sockindex], conn->sock[sockindex], (int)timeout_ms); if(rc > 0) /* reabable or writable, go loop*/ continue; else if(0 == rc) { /* if ssl is expecting something, check if it's available. */ if(connssl->connecting_state == ssl_connect_2_reading || connssl->connecting_state == ssl_connect_2_writing) { curl_socket_t writefd = ssl_connect_2_writing== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; curl_socket_t readfd = ssl_connect_2_reading== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_ready(readfd, writefd, nonblocking?0:(int)timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); return CURLE_SSL_CONNECT_ERROR; } else if(0 == what) { if(nonblocking) { 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); return CURLE_SSL_CONNECT_ERROR; } /* socket is readable or writable */ } else break; } while(1); if(rc < 0) { failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } rc = gnutls_handshake(session); if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { connssl->connecting_state = gnutls_record_get_direction(session)? ssl_connect_2_writing:ssl_connect_2_reading; if(nonblocking) { return CURLE_OK; } } else if (rc < 0) { failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc)); } else { /* Reset our connect state machine */ connssl->connecting_state = ssl_connect_1; return CURLE_OK; } } } static gnutls_x509_crt_fmt do_file_type(const char *type) { Loading @@ -243,31 +265,14 @@ static gnutls_x509_crt_fmt do_file_type(const char *type) return -1; } /* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. */ CURLcode Curl_gtls_connect(struct connectdata *conn, static CURLcode gtls_connect_step1(struct connectdata *conn, int sockindex) { static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; struct SessionHandle *data = conn->data; gnutls_session session; int rc; unsigned int cert_list_size; const gnutls_datum *chainp; unsigned int verify_status; gnutls_x509_crt x509_cert,x509_issuer; gnutls_datum issuerp; char certbuf[256]; /* big enough? */ size_t size; unsigned int algo; unsigned int bits; time_t certclock; const char *ptr; void *ssl_sessionid; size_t ssl_idsize; bool sni = TRUE; /* default is SNI enabled */ Loading Loading @@ -411,10 +416,29 @@ Curl_gtls_connect(struct connectdata *conn, infof (data, "SSL re-using session ID\n"); } rc = handshake(conn, session, sockindex, TRUE); if(rc) /* handshake() sets its own error message with failf() */ return rc; return CURLE_OK; } static CURLcode gtls_connect_step3(struct connectdata *conn, int sockindex) { unsigned int cert_list_size; const gnutls_datum *chainp; unsigned int verify_status; gnutls_x509_crt x509_cert,x509_issuer; gnutls_datum issuerp; char certbuf[256]; /* big enough? */ size_t size; unsigned int algo; unsigned int bits; time_t certclock; const char *ptr; struct SessionHandle *data = conn->data; gnutls_session session = conn->ssl[sockindex].session; int rc; int incache; void *ssl_sessionid; /* This function will return the peer's raw certificate (chain) as sent by the peer. These certificates are in raw format (DER encoded for Loading Loading @@ -623,20 +647,88 @@ Curl_gtls_connect(struct connectdata *conn, /* extract session ID to the allocated buffer */ gnutls_session_get_data(session, connect_sessionid, &connect_idsize); if(ssl_sessionid) incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)); if (incache) { /* there was one before in the cache, so instead of risking that the previous one was rejected, we just kill that and store the new */ Curl_ssl_delsessionid(conn, ssl_sessionid); } /* store this session id */ return Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize); } } return CURLE_OK; } /* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. */ /* We use connssl->connecting_state to keep track of the connection status; there are three states: 'ssl_connect_1' (not started yet or complete), 'ssl_connect_2_reading' (waiting for data from server), and 'ssl_connect_2_writing' (waiting to be able to write). */ static CURLcode gtls_connect_common(struct connectdata *conn, int sockindex, bool nonblocking, bool *done) { int rc; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; /* Initiate the connection, if not already done */ if(ssl_connect_1==connssl->connecting_state) { rc = gtls_connect_step1 (conn, sockindex); if(rc) return rc; } rc = handshake(conn, sockindex, TRUE, nonblocking); if(rc) /* handshake() sets its own error message with failf() */ return rc; /* Finish connecting once the handshake is done */ if(ssl_connect_1==connssl->connecting_state) { rc = gtls_connect_step3(conn, sockindex); if(rc) return rc; } *done = ssl_connect_1==connssl->connecting_state; return CURLE_OK; } CURLcode Curl_gtls_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done) { return gtls_connect_common(conn, sockindex, TRUE, done); } CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex) { CURLcode retcode; bool done = FALSE; retcode = gtls_connect_common(conn, sockindex, FALSE, &done); if(retcode) return retcode; DEBUGASSERT(done); return CURLE_OK; } /* for documentation see Curl_ssl_send() in sslgen.h */ ssize_t Curl_gtls_send(struct connectdata *conn, Loading Loading @@ -769,7 +861,7 @@ ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ if(ret == GNUTLS_E_REHANDSHAKE) { /* BLOCKING call, this is bad but a work-around for now. Fixing this "the proper way" takes a whole lot of work. */ CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE); CURLcode rc = handshake(conn, num, FALSE, FALSE); if(rc) /* handshake() writes error message on its own */ *curlcode = rc; Loading lib/gtls.h +4 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ int Curl_gtls_init(void); int Curl_gtls_cleanup(void); CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex); CURLcode Curl_gtls_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done); /* tell GnuTLS to close down all open information regarding connections (and thus session ID caching etc) */ Loading @@ -52,6 +55,7 @@ int Curl_gtls_seed(struct SessionHandle *data); #define curlssl_init Curl_gtls_init #define curlssl_cleanup Curl_gtls_cleanup #define curlssl_connect Curl_gtls_connect #define curlssl_connect_nonblocking Curl_gtls_connect_nonblocking #define curlssl_session_free(x) Curl_gtls_session_free(x) #define curlssl_close_all Curl_gtls_close_all #define curlssl_close Curl_gtls_close Loading lib/http.c +3 −15 Original line number Diff line number Diff line Loading @@ -1817,9 +1817,9 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done) } #endif #ifdef USE_SSLEAY /* This function is OpenSSL-specific. It should be made to query the generic SSL layer instead. */ #if defined(USE_SSLEAY) || defined(USE_GNUTLS) /* This function is for OpenSSL and GnuTLS only. It should be made to query the generic SSL layer instead. */ static int https_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks) Loading @@ -1844,17 +1844,6 @@ static int https_getsock(struct connectdata *conn, return CURLE_OK; } #else #ifdef USE_GNUTLS static int https_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks) { (void)conn; (void)socks; (void)numsocks; return GETSOCK_BLANK; } #else #ifdef USE_NSS static int https_getsock(struct connectdata *conn, curl_socket_t *socks, Loading @@ -1879,7 +1868,6 @@ static int https_getsock(struct connectdata *conn, #endif #endif #endif #endif /* * Curl_http_done() gets called from Curl_done() after a single HTTP request Loading lib/urldata.h +1 −0 Original line number Diff line number Diff line Loading @@ -231,6 +231,7 @@ struct ssl_connect_data { #ifdef USE_GNUTLS gnutls_session session; gnutls_certificate_credentials cred; ssl_connect_state connecting_state; #endif /* USE_GNUTLS */ #ifdef USE_NSS PRFileDesc *handle; Loading Loading
lib/gtls.c +152 −60 Original line number Diff line number Diff line Loading @@ -182,55 +182,77 @@ static void unload_file(gnutls_datum data) { } /* this function does a BLOCKING SSL/TLS (re-)handshake */ /* this function does a SSL/TLS (re-)handshake */ static CURLcode handshake(struct connectdata *conn, gnutls_session session, int sockindex, bool duringconnect) bool duringconnect, bool nonblocking) { struct SessionHandle *data = conn->data; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; gnutls_session session = conn->ssl[sockindex].session; curl_socket_t sockfd = conn->sock[sockindex]; long timeout_ms; int rc; if(!gtls_inited) Curl_gtls_init(); do { rc = gnutls_handshake(session); int what; if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { long timeout_ms = Curl_timeleft(conn, NULL, duringconnect); while(1) { /* check allowed time left */ timeout_ms = Curl_timeleft(conn, NULL, duringconnect); if(timeout_ms < 0) { /* a precaution, no need to continue if time already is up */ /* no need to continue if time already is up */ failf(data, "SSL connection timeout"); return CURLE_OPERATION_TIMEDOUT; } rc = Curl_socket_ready(conn->sock[sockindex], conn->sock[sockindex], (int)timeout_ms); if(rc > 0) /* reabable or writable, go loop*/ continue; else if(0 == rc) { /* if ssl is expecting something, check if it's available. */ if(connssl->connecting_state == ssl_connect_2_reading || connssl->connecting_state == ssl_connect_2_writing) { curl_socket_t writefd = ssl_connect_2_writing== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; curl_socket_t readfd = ssl_connect_2_reading== connssl->connecting_state?sockfd:CURL_SOCKET_BAD; what = Curl_socket_ready(readfd, writefd, nonblocking?0:(int)timeout_ms); if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); return CURLE_SSL_CONNECT_ERROR; } else if(0 == what) { if(nonblocking) { 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); return CURLE_SSL_CONNECT_ERROR; } /* socket is readable or writable */ } else break; } while(1); if(rc < 0) { failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc)); return CURLE_SSL_CONNECT_ERROR; } rc = gnutls_handshake(session); if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { connssl->connecting_state = gnutls_record_get_direction(session)? ssl_connect_2_writing:ssl_connect_2_reading; if(nonblocking) { return CURLE_OK; } } else if (rc < 0) { failf(data, "gnutls_handshake() failed: %s", gnutls_strerror(rc)); } else { /* Reset our connect state machine */ connssl->connecting_state = ssl_connect_1; return CURLE_OK; } } } static gnutls_x509_crt_fmt do_file_type(const char *type) { Loading @@ -243,31 +265,14 @@ static gnutls_x509_crt_fmt do_file_type(const char *type) return -1; } /* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. */ CURLcode Curl_gtls_connect(struct connectdata *conn, static CURLcode gtls_connect_step1(struct connectdata *conn, int sockindex) { static const int cert_type_priority[] = { GNUTLS_CRT_X509, 0 }; struct SessionHandle *data = conn->data; gnutls_session session; int rc; unsigned int cert_list_size; const gnutls_datum *chainp; unsigned int verify_status; gnutls_x509_crt x509_cert,x509_issuer; gnutls_datum issuerp; char certbuf[256]; /* big enough? */ size_t size; unsigned int algo; unsigned int bits; time_t certclock; const char *ptr; void *ssl_sessionid; size_t ssl_idsize; bool sni = TRUE; /* default is SNI enabled */ Loading Loading @@ -411,10 +416,29 @@ Curl_gtls_connect(struct connectdata *conn, infof (data, "SSL re-using session ID\n"); } rc = handshake(conn, session, sockindex, TRUE); if(rc) /* handshake() sets its own error message with failf() */ return rc; return CURLE_OK; } static CURLcode gtls_connect_step3(struct connectdata *conn, int sockindex) { unsigned int cert_list_size; const gnutls_datum *chainp; unsigned int verify_status; gnutls_x509_crt x509_cert,x509_issuer; gnutls_datum issuerp; char certbuf[256]; /* big enough? */ size_t size; unsigned int algo; unsigned int bits; time_t certclock; const char *ptr; struct SessionHandle *data = conn->data; gnutls_session session = conn->ssl[sockindex].session; int rc; int incache; void *ssl_sessionid; /* This function will return the peer's raw certificate (chain) as sent by the peer. These certificates are in raw format (DER encoded for Loading Loading @@ -623,20 +647,88 @@ Curl_gtls_connect(struct connectdata *conn, /* extract session ID to the allocated buffer */ gnutls_session_get_data(session, connect_sessionid, &connect_idsize); if(ssl_sessionid) incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)); if (incache) { /* there was one before in the cache, so instead of risking that the previous one was rejected, we just kill that and store the new */ Curl_ssl_delsessionid(conn, ssl_sessionid); } /* store this session id */ return Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize); } } return CURLE_OK; } /* * This function is called after the TCP connect has completed. Setup the TLS * layer and do all necessary magic. */ /* We use connssl->connecting_state to keep track of the connection status; there are three states: 'ssl_connect_1' (not started yet or complete), 'ssl_connect_2_reading' (waiting for data from server), and 'ssl_connect_2_writing' (waiting to be able to write). */ static CURLcode gtls_connect_common(struct connectdata *conn, int sockindex, bool nonblocking, bool *done) { int rc; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; /* Initiate the connection, if not already done */ if(ssl_connect_1==connssl->connecting_state) { rc = gtls_connect_step1 (conn, sockindex); if(rc) return rc; } rc = handshake(conn, sockindex, TRUE, nonblocking); if(rc) /* handshake() sets its own error message with failf() */ return rc; /* Finish connecting once the handshake is done */ if(ssl_connect_1==connssl->connecting_state) { rc = gtls_connect_step3(conn, sockindex); if(rc) return rc; } *done = ssl_connect_1==connssl->connecting_state; return CURLE_OK; } CURLcode Curl_gtls_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done) { return gtls_connect_common(conn, sockindex, TRUE, done); } CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex) { CURLcode retcode; bool done = FALSE; retcode = gtls_connect_common(conn, sockindex, FALSE, &done); if(retcode) return retcode; DEBUGASSERT(done); return CURLE_OK; } /* for documentation see Curl_ssl_send() in sslgen.h */ ssize_t Curl_gtls_send(struct connectdata *conn, Loading Loading @@ -769,7 +861,7 @@ ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ if(ret == GNUTLS_E_REHANDSHAKE) { /* BLOCKING call, this is bad but a work-around for now. Fixing this "the proper way" takes a whole lot of work. */ CURLcode rc = handshake(conn, conn->ssl[num].session, num, FALSE); CURLcode rc = handshake(conn, num, FALSE, FALSE); if(rc) /* handshake() writes error message on its own */ *curlcode = rc; Loading
lib/gtls.h +4 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,9 @@ int Curl_gtls_init(void); int Curl_gtls_cleanup(void); CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex); CURLcode Curl_gtls_connect_nonblocking(struct connectdata *conn, int sockindex, bool *done); /* tell GnuTLS to close down all open information regarding connections (and thus session ID caching etc) */ Loading @@ -52,6 +55,7 @@ int Curl_gtls_seed(struct SessionHandle *data); #define curlssl_init Curl_gtls_init #define curlssl_cleanup Curl_gtls_cleanup #define curlssl_connect Curl_gtls_connect #define curlssl_connect_nonblocking Curl_gtls_connect_nonblocking #define curlssl_session_free(x) Curl_gtls_session_free(x) #define curlssl_close_all Curl_gtls_close_all #define curlssl_close Curl_gtls_close Loading
lib/http.c +3 −15 Original line number Diff line number Diff line Loading @@ -1817,9 +1817,9 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done) } #endif #ifdef USE_SSLEAY /* This function is OpenSSL-specific. It should be made to query the generic SSL layer instead. */ #if defined(USE_SSLEAY) || defined(USE_GNUTLS) /* This function is for OpenSSL and GnuTLS only. It should be made to query the generic SSL layer instead. */ static int https_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks) Loading @@ -1844,17 +1844,6 @@ static int https_getsock(struct connectdata *conn, return CURLE_OK; } #else #ifdef USE_GNUTLS static int https_getsock(struct connectdata *conn, curl_socket_t *socks, int numsocks) { (void)conn; (void)socks; (void)numsocks; return GETSOCK_BLANK; } #else #ifdef USE_NSS static int https_getsock(struct connectdata *conn, curl_socket_t *socks, Loading @@ -1879,7 +1868,6 @@ static int https_getsock(struct connectdata *conn, #endif #endif #endif #endif /* * Curl_http_done() gets called from Curl_done() after a single HTTP request Loading
lib/urldata.h +1 −0 Original line number Diff line number Diff line Loading @@ -231,6 +231,7 @@ struct ssl_connect_data { #ifdef USE_GNUTLS gnutls_session session; gnutls_certificate_credentials cred; ssl_connect_state connecting_state; #endif /* USE_GNUTLS */ #ifdef USE_NSS PRFileDesc *handle; Loading