From 473322ec66a0969c3c59e8006f9ac72768b91adf Mon Sep 17 00:00:00 2001
From: Patrick Monnerat <pm@datasphere.ch>
Date: Tue, 14 Oct 2014 14:58:26 +0200
Subject: [PATCH] Implement pinned public key in GSKit backend

---
 lib/vtls/gskit.c | 20 +++++++++++++++++++-
 lib/x509asn1.c   | 15 ++++++++++++---
 lib/x509asn1.h   |  4 +++-
 3 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/lib/vtls/gskit.c b/lib/vtls/gskit.c
index 0f8b08f2ce..ae878c7bcc 100644
--- a/lib/vtls/gskit.c
+++ b/lib/vtls/gskit.c
@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -804,6 +804,7 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
   const gsk_cert_data_elem *p;
   const char *cert = (const char *) NULL;
   const char *certend;
+  const char *ptr;
   int i;
   CURLcode cc;
 
@@ -857,6 +858,23 @@ static CURLcode gskit_connect_step3(struct connectdata *conn, int sockindex)
     }
   }
 
+  /* Check pinned public key. */
+  ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+  if(cc == CURLE_OK && ptr) {
+    curl_X509certificate x509;
+    curl_asn1Element *p;
+
+    if(!cert)
+      return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+    Curl_parseX509(&x509, cert, certend);
+    p = &x509.subjectPublicKeyInfo;
+    cc = Curl_pin_peer_pubkey(ptr, p->header, p->end - p->header);
+    if(cc != CURLE_OK) {
+      failf(data, "SSL: public key does not match pinned public key!");
+      return cc;
+    }
+  }
+
   connssl->connecting_state = ssl_connect_done;
   return CURLE_OK;
 }
diff --git a/lib/x509asn1.c b/lib/x509asn1.c
index 31ea5de003..e100e07f46 100644
--- a/lib/x509asn1.c
+++ b/lib/x509asn1.c
@@ -122,6 +122,7 @@ const char * Curl_getASN1Element(curl_asn1Element * elem,
     return (const char *) NULL;
 
   /* Process header byte. */
+  elem->header = beg;
   b = (unsigned char) *beg++;
   elem->constructed = (b & 0x20) != 0;
   elem->class = (b >> 6) & 3;
@@ -682,6 +683,7 @@ void Curl_parseX509(curl_X509certificate * cert,
      Syntax is assumed to have already been checked by the SSL backend.
      See RFC 5280. */
 
+  cert->certificate.header = NULL;
   cert->certificate.beg = beg;
   cert->certificate.end = end;
 
@@ -701,6 +703,7 @@ void Curl_parseX509(curl_X509certificate * cert,
   beg = tbsCertificate.beg;
   end = tbsCertificate.end;
   /* Get optional version, get serialNumber. */
+  cert->version.header = NULL;
   cert->version.beg = &defaultVersion;
   cert->version.end = &defaultVersion + sizeof defaultVersion;;
   beg = Curl_getASN1Element(&elem, beg, end);
@@ -720,15 +723,19 @@ void Curl_parseX509(curl_X509certificate * cert,
   /* Get subject. */
   beg = Curl_getASN1Element(&cert->subject, beg, end);
   /* Get subjectPublicKeyAlgorithm and subjectPublicKey. */
-  beg = Curl_getASN1Element(&elem, beg, end);
+  beg = Curl_getASN1Element(&cert->subjectPublicKeyInfo, beg, end);
   ccp = Curl_getASN1Element(&cert->subjectPublicKeyAlgorithm,
-                            elem.beg, elem.end);
-  Curl_getASN1Element(&cert->subjectPublicKey, ccp, elem.end);
+                            cert->subjectPublicKeyInfo.beg,
+                            cert->subjectPublicKeyInfo.end);
+  Curl_getASN1Element(&cert->subjectPublicKey, ccp,
+                      cert->subjectPublicKeyInfo.end);
   /* Get optional issuerUiqueID, subjectUniqueID and extensions. */
   cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0;
   cert->extensions.tag = elem.tag = 0;
+  cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL;
   cert->issuerUniqueID.beg = cert->issuerUniqueID.end = "";
   cert->subjectUniqueID.beg = cert->subjectUniqueID.end = "";
+  cert->extensions.header = NULL;
   cert->extensions.beg = cert->extensions.end = "";
   if(beg < end)
     beg = Curl_getASN1Element(&elem, beg, end);
@@ -771,6 +778,7 @@ static const char * dumpAlgo(curl_asn1Element * param,
   /* Get algorithm parameters and return algorithm name. */
 
   beg = Curl_getASN1Element(&oid, beg, end);
+  param->header = NULL;
   param->tag = 0;
   param->beg = param->end = end;
   if(beg < end)
@@ -1140,6 +1148,7 @@ CURLcode Curl_verifyhost(struct connectdata * conn,
   }
 
   /* Process subject. */
+  name.header = NULL;
   name.beg = name.end = "";
   q = cert.subject.beg;
   /* we have to look to the last occurrence of a commonName in the
diff --git a/lib/x509asn1.h b/lib/x509asn1.h
index 274d728b71..075c424f3c 100644
--- a/lib/x509asn1.h
+++ b/lib/x509asn1.h
@@ -76,8 +76,9 @@
 
 /* ASN.1 parsed element. */
 typedef struct {
+  const char *  header;         /* Pointer to header byte. */
   const char *  beg;            /* Pointer to element data. */
-  const char *  end;            /* Pointer to 1st byte after element data. */
+  const char *  end;            /* Pointer to 1st byte after element. */
   unsigned char class;          /* ASN.1 element class. */
   unsigned char tag;            /* ASN.1 element tag. */
   bool          constructed;    /* Element is constructed. */
@@ -102,6 +103,7 @@ typedef struct {
   curl_asn1Element      notBefore;
   curl_asn1Element      notAfter;
   curl_asn1Element      subject;
+  curl_asn1Element      subjectPublicKeyInfo;
   curl_asn1Element      subjectPublicKeyAlgorithm;
   curl_asn1Element      subjectPublicKey;
   curl_asn1Element      issuerUniqueID;
-- 
GitLab