From e5c7feec5151299975fe03184cc322ea51fb45c2 Mon Sep 17 00:00:00 2001
From: Andre Heinecke <aheinecke@intevation.de>
Date: Fri, 12 Sep 2014 13:01:07 +0200
Subject: [PATCH 2/2] Add CURLOPT_PEERCERT option to pin a peer cert

    This is only implemented for a specific usecase with polarssl
---
 include/curl/curl.h          |  3 +++
 include/curl/typecheck-gcc.h |  1 +
 lib/url.c                    |  8 ++++++++
 lib/urldata.h                |  1 +
 lib/vtls/polarssl.c          | 41 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 54 insertions(+)

diff --git a/include/curl/curl.h b/include/curl/curl.h
index d40b2db..20a9d82 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1611,6 +1611,9 @@ typedef enum {
   /* Pass in a bitmask of "header options" */
   CINIT(HEADEROPT, LONG, 229),
 
+  /* Peer certificate */
+  CINIT(PEERCERT, OBJECTPOINT, 230),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h
index 69d41a2..241529d 100644
--- a/include/curl/typecheck-gcc.h
+++ b/include/curl/typecheck-gcc.h
@@ -258,6 +258,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
    (option) == CURLOPT_SSH_PRIVATE_KEYFILE ||                                 \
    (option) == CURLOPT_CRLFILE ||                                             \
    (option) == CURLOPT_ISSUERCERT ||                                          \
+   (option) == CURLOPT_PEERCERT ||                                            \
    (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE ||                               \
    (option) == CURLOPT_SSH_KNOWNHOSTS ||                                      \
    (option) == CURLOPT_MAIL_FROM ||                                           \
diff --git a/lib/url.c b/lib/url.c
index 67126ab3..5721ee2 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -2015,6 +2015,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
     result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
                        va_arg(param, char *));
     break;
+  case CURLOPT_PEERCERT:
+    /*
+     * Set peer certificate file
+     * to check peer certificate against
+     */
+    result = setstropt(&data->set.str[STRING_SSL_PEERCERT],
+                       va_arg(param, char *));
+    break;
   case CURLOPT_TELNETOPTIONS:
     /*
      * Set a linked list of telnet options
diff --git a/lib/urldata.h b/lib/urldata.h
index 8594c2f..a6dc1ae 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1391,6 +1391,7 @@ enum dupstring {
   STRING_USERAGENT,       /* User-Agent string */
   STRING_SSL_CRLFILE,     /* crl file to check certificate */
   STRING_SSL_ISSUERCERT,  /* issuer cert file to check certificate */
+  STRING_SSL_PEERCERT,  /* issuer cert file to check certificate */
   STRING_USERNAME,        /* <username>, if used */
   STRING_PASSWORD,        /* <password>, if used */
   STRING_OPTIONS,         /* <options>, if used */
diff --git a/lib/vtls/polarssl.c b/lib/vtls/polarssl.c
index 08dc4c6..8f34901 100644
--- a/lib/vtls/polarssl.c
+++ b/lib/vtls/polarssl.c
@@ -403,6 +403,44 @@ polarssl_connect_step1(struct connectdata *conn,
   return CURLE_OK;
 }
 
+static int
+pinned_verify(void *pinned_cert_file_name, x509_crt *crt,
+              int depth, int *flags)
+{
+  x509_crt pinned_cert;
+  x509_crt *leaf = crt;
+  unsigned int i;
+  int ret;
+
+  if (pinned_cert_file_name == NULL || crt == NULL) {
+    *flags |= BADCERT_NOT_TRUSTED;
+    return *flags;
+  }
+
+  x509_crt_init(&pinned_cert);
+  ret = x509_crt_parse_file(&pinned_cert, pinned_cert_file_name);
+
+  if(ret) {
+    x509_crt_free(&pinned_cert);
+    *flags |= BADCERT_NOT_TRUSTED;
+    return *flags;
+  }
+
+  while (leaf->next) {
+    leaf = leaf->next;
+  }
+
+  ret = memcmp(pinned_cert.raw.p, leaf->raw.p, pinned_cert.raw.len);
+  x509_crt_free(&pinned_cert);
+  if (ret == 0) {
+    *flags = 0;
+    return 0;
+  }
+
+  *flags |= BADCERT_NOT_TRUSTED;
+  return *flags;
+}
+
 static CURLcode
 polarssl_connect_step2(struct connectdata *conn,
                      int sockindex)
@@ -422,6 +460,9 @@ polarssl_connect_step2(struct connectdata *conn,
   conn->recv[sockindex] = polarssl_recv;
   conn->send[sockindex] = polarssl_send;
 
+  if(data->set.str[STRING_SSL_PEERCERT])
+    ssl_set_verify (&connssl->ssl, pinned_verify, data->set.str[STRING_SSL_PEERCERT]);
+
   for(;;) {
     if(!(ret = ssl_handshake(&connssl->ssl)))
       break;
-- 
1.9.1

