curl-library
[PATCH] add CURLINFO_SSL_VERIFYRESULT_STR providing a human-readable error string
From: Kamil Dudka <kdudka_at_redhat.com>
Date: Fri, 27 Apr 2012 16:08:41 +0200
Date: Fri, 27 Apr 2012 16:08:41 +0200
Bug: http://lists.baseurl.org/pipermail/yum-devel/2012-January/009002.html
--- RELEASE-NOTES | 1 + docs/libcurl/curl_easy_getinfo.3 | 5 ++++ include/curl/curl.h | 3 +- lib/getinfo.c | 3 ++ lib/nss.c | 14 +++++++++++- lib/ssluse.c | 39 +++++++++++++++++++++++++++++++------ lib/url.c | 6 +++++ lib/urldata.h | 2 + 8 files changed, 63 insertions(+), 10 deletions(-) diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 0c92dfc..ca08648 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -11,6 +11,7 @@ This release includes the following changes: o nss: the minimal supported version of NSS bumped to 3.12.x o nss: human-readable names are now provided for NSS errors if available + o added CURLINFO_SSL_VERIFYRESULT_STR providing a human-readable error string o This release includes the following bugfixes: diff --git a/docs/libcurl/curl_easy_getinfo.3 b/docs/libcurl/curl_easy_getinfo.3 index 0968136..4ad045c 100644 --- a/docs/libcurl/curl_easy_getinfo.3 +++ b/docs/libcurl/curl_easy_getinfo.3 @@ -127,6 +127,11 @@ than one request if FOLLOWLOCATION is true. Pass a pointer to a long to receive the result of the certification verification that was requested (using the CURLOPT_SSL_VERIFYPEER option to \fIcurl_easy_setopt(3)\fP). +.IP CURLINFO_SSL_VERIFYRESULT_STR +Pass a pointer to a char pointer to receive a human-redable error string +corresponding to the error code returned by \fICURLINFO_SSL_VERIFYRESULT\fP +(if available). NOTE: this option works only if libcurl is built with OpenSSL +or NSS support. (Added in 7.25.1) .IP CURLINFO_SSL_ENGINES Pass the address of a 'struct curl_slist *' to receive a linked-list of OpenSSL crypto-engines supported. Note that engines are normally implemented diff --git a/include/curl/curl.h b/include/curl/curl.h index 2cad282..c0a1db5 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1996,9 +1996,10 @@ typedef enum { CURLINFO_PRIMARY_PORT = CURLINFO_LONG + 40, CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, + CURLINFO_SSL_VERIFYRESULT_STR = CURLINFO_STRING + 43, /* Fill in new entries below here! */ - CURLINFO_LASTONE = 42 + CURLINFO_LASTONE = 43 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as diff --git a/lib/getinfo.c b/lib/getinfo.c index cd6feee..033ac26 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -175,6 +175,9 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) case CURLINFO_SSL_VERIFYRESULT: *param_longp = data->set.ssl.certverifyresult; break; + case CURLINFO_SSL_VERIFYRESULT_STR: + *param_charp = data->set.ssl.certverifyresult_str; + break; case CURLINFO_CONTENT_LENGTH_DOWNLOAD: *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN)? (double)data->progress.size_dl:-1; diff --git a/lib/nss.c b/lib/nss.c index 6b93893..6ae8e0e 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -612,6 +612,14 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); } +/* assign verification result as error code together with a text description */ +static void assign_certverifyresult(struct SessionHandle *data, + PRErrorCode err) +{ + data->set.ssl.certverifyresult = err; + data->set.ssl.certverifyresult_str = PR_ErrorToString(err, PR_LANGUAGE_EN); +} + static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) { SECStatus result = SECFailure; @@ -620,7 +628,8 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) CERTCertificate *cert = NULL; char *subject, *subject_cn, *issuer; - conn->data->set.ssl.certverifyresult=err; + assign_certverifyresult(conn->data, err); + cert = SSL_PeerCertificate(sock); subject = CERT_NameToAscii(&cert->subject); subject_cn = CERT_GetCommonName(&cert->subject); @@ -1340,7 +1349,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess) goto error; - data->set.ssl.certverifyresult=0; /* not checked yet */ + assign_certverifyresult(data, 0); /* not checked yet */ + if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) != SECSuccess) { goto error; diff --git a/lib/ssluse.c b/lib/ssluse.c index a55ad3c..b69be47 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -1380,6 +1380,16 @@ static const char *tls_rt_type(int type) "TLS Unknown, "); } +/* + * Free the previously allocated certverifyresult_str and allocate a new one + */ +static void assign_certverifyresult_str(struct SessionHandle *data, + const char *str) +{ + Curl_safefree(data->set.ssl.certverifyresult_str); + if(str) + data->set.ssl.certverifyresult_str = strdup(str); +} /* * Our callback from the SSL/TLS layers. @@ -1804,6 +1814,7 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) CURLcode rc; const char *cert_problem = NULL; long lerr; + const char *error_str; connssl->connecting_state = ssl_connect_2; /* the connection failed, we're not waiting for @@ -1827,11 +1838,17 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) certificate verify failed */ rc = CURLE_SSL_CACERT; + /* store verification result as number */ lerr = SSL_get_verify_result(connssl->handle); + data->set.ssl.certverifyresult = lerr; + + /* store verification result as text */ + error_str = X509_verify_cert_error_string(lerr); + assign_certverifyresult_str(data, error_str); + if(lerr != X509_V_OK) { snprintf(error_buffer, sizeof(error_buffer), - "SSL certificate problem: %s", - X509_verify_cert_error_string(lerr)); + "SSL certificate problem: %s", error_str); } else cert_problem = "SSL certificate problem, verify that the CA cert is" @@ -2280,6 +2297,7 @@ static CURLcode servercert(struct connectdata *conn, CURLcode retcode = CURLE_OK; int rc; long lerr; + const char *error_str; ASN1_TIME *certdate; struct SessionHandle *data = conn->data; X509 *issuer; @@ -2291,6 +2309,7 @@ static CURLcode servercert(struct connectdata *conn, (void)get_cert_chain(conn, connssl); data->set.ssl.certverifyresult = !X509_V_OK; + assign_certverifyresult_str(data, NULL); connssl->server_cert = SSL_get_peer_certificate(connssl->handle); if(!connssl->server_cert) { @@ -2377,21 +2396,27 @@ static CURLcode servercert(struct connectdata *conn, X509_free(issuer); } - lerr = data->set.ssl.certverifyresult= - SSL_get_verify_result(connssl->handle); - if(data->set.ssl.certverifyresult != X509_V_OK) { + /* store verification result as number */ + lerr = SSL_get_verify_result(connssl->handle); + data->set.ssl.certverifyresult = lerr; + + /* store verification result as text */ + error_str = X509_verify_cert_error_string(lerr); + assign_certverifyresult_str(data, error_str); + + if(lerr != X509_V_OK) { if(data->set.ssl.verifypeer) { /* We probably never reach this, because SSL_connect() will fail and we return earlier if verifypeer is set? */ if(strict) failf(data, "SSL certificate verify result: %s (%ld)", - X509_verify_cert_error_string(lerr), lerr); + error_str, lerr); retcode = CURLE_PEER_FAILED_VERIFICATION; } else infof(data, "\t SSL certificate verify result: %s (%ld)," " continuing anyway.\n", - X509_verify_cert_error_string(lerr), lerr); + error_str, lerr); } else infof(data, "\t SSL certificate verify ok.\n"); diff --git a/lib/url.c b/lib/url.c index b78c200..e16beaa 100644 --- a/lib/url.c +++ b/lib/url.c @@ -280,6 +280,12 @@ void Curl_freeset(struct SessionHandle * data) data->change.referer_alloc = FALSE; } data->change.referer = NULL; + +#ifdef USE_SSLEAY + /* we have to duplicate the result of X509_verify_cert_error_string() but + * the duplicated string cannot be freed in Curl_ossl_close() */ + Curl_safefree(data->set.ssl.certverifyresult_str); +#endif } static CURLcode setstropt(char **charp, char * s) diff --git a/lib/urldata.h b/lib/urldata.h index 20519cf..4ff7933 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -287,6 +287,8 @@ struct ssl_connect_data { struct ssl_config_data { long version; /* what version the client wants to use */ long certverifyresult; /* result from the certificate verification */ + const char *certverifyresult_str; + /* text describing certverifyresult or NULL */ long verifypeer; /* set TRUE if this is desired */ long verifyhost; /* 0: no verify 1: check that CN exists -- 1.7.1 ------------------------------------------------------------------- List admin: http://cool.haxx.se/list/listinfo/curl-library Etiquette: http://curl.haxx.se/mail/etiquette.htmlReceived on 2012-04-27