From e70671769c65826efc07a9da8a75a694e9619140 Mon Sep 17 00:00:00 2001
From: Christian Grothoff <christian@grothoff.org>
Date: Wed, 18 Sep 2013 22:00:55 +0200
Subject: [PATCH] Adding support for CURLINFO_CERTINFO when compiled with
 GnuTLS.

This change exposes the server's x509 certificate chain to
the client via the CURLINFO_CERTINFO mechanism, which
previously was documented to only work for OpenSSL.  However,
the format in which the certificate is returned maybe
slightly different: as implemented, GnuTLS provides more
information in a human-readable fashion.  The OpenSSL
format as generated from the code seemed also rather
ad-hoc, so I'm not sure if this is OK or not.  I would
prefer if both mechanisms were changed to output some
standard format that can be easily processed later (DER
would be best), but for that a new option (like
CURLINFO_CERTINFO_PEM) should probably be introduced;
this change is less invasive and should improve
compatibility between the OpenSSL and GnuTLS-based
variants of libcurl.

Note that I did not update the documentation itself.
---
 lib/gtls.c |   29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/lib/gtls.c b/lib/gtls.c
index 700e46a..6f5536b 100644
--- a/lib/gtls.c
+++ b/lib/gtls.c
@@ -51,6 +51,7 @@
 #include "connect.h" /* for the connect timeout */
 #include "select.h"
 #include "rawstr.h"
+#include "slist.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -605,6 +606,34 @@ gtls_connect_step3(struct connectdata *conn,
     infof(data, "\t common name: WARNING couldn't obtain\n");
   }
 
+  if(0 == Curl_ssl_init_certinfo (data, cert_list_size)) {
+    unsigned int i;
+
+    for(i=0;i<cert_list_size;i++) {
+      gnutls_x509_crt_t cert;
+      gnutls_datum_t dn;
+
+      if(GNUTLS_E_SUCCESS == gnutls_x509_crt_init (&cert)) {
+        if((GNUTLS_E_SUCCESS ==
+            gnutls_x509_crt_import (cert, &chainp[i],
+                                    GNUTLS_X509_FMT_DER)) &&
+           (GNUTLS_E_SUCCESS ==
+            gnutls_x509_crt_print (cert,
+                                   GNUTLS_CRT_PRINT_FULL,
+                                   &dn))) {
+          char *output;
+          struct curl_certinfo * ci = &data->info.certs;
+
+          output = curl_maprintf ("%.*s", dn.size, dn.data);
+          gnutls_free (dn.data);
+          if(NULL != output)
+            ci->certinfo[i] = Curl_slist_append_nodup (NULL, output);
+        }
+        gnutls_x509_crt_deinit (cert);
+      }
+    }
+  }
+
   if(data->set.ssl.verifypeer) {
     /* This function will try to verify the peer's certificate and return its
        status (trusted, invalid etc.). The value of status should be one or
-- 
1.7.10.4

