cURL / Mailing Lists / curl-library / Single Mail

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

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.html
Received on 2012-04-27