Commit 357ff4d1 authored by Patrick Monnerat's avatar Patrick Monnerat
Browse files

Factorize pinned public key code into generic file handling and backend specific

parent 265b9a2e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -299,7 +299,7 @@ curl_easy_strerror(CURLcode error)
    return "The max connection limit is reached";

  case CURLE_SSL_PINNEDPUBKEYNOTMATCH:
    return "SSL public key does not matched pinned public key";
    return "SSL public key does not match pinned public key";

    /* error codes not used by current libcurl */
  case CURLE_OBSOLETE20:
+23 −57
Original line number Diff line number Diff line
@@ -678,22 +678,22 @@ gtls_connect_step1(struct connectdata *conn,
  return CURLE_OK;
}

static int pkp_pin_peer_pubkey(gnutls_x509_crt_t cert, char *pinnedpubkey)
static CURLcode pkp_pin_peer_pubkey(gnutls_x509_crt_t cert,
                                    const char *pinnedpubkey)
{
  /* Scratch */
  FILE* fp = NULL;
  size_t len1 = 0, len2 = 0;
  unsigned char *buff1 = NULL, *buff2 = NULL;
  long size = 0;
  unsigned char *buff1 = NULL;

  gnutls_pubkey_t key = NULL;

  /* Result is returned to caller */
  int ret = 0, result = FALSE;
  int ret = 0;
  CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;

  /* if a path wasn't specified, don't pin */
  if(NULL == pinnedpubkey) return TRUE;
  if(NULL == cert) return FALSE;
  if(NULL == pinnedpubkey) return CURLE_OK;
  if(NULL == cert) return result;

  do {
    /* Begin Gyrations to get the public key     */
@@ -719,57 +719,14 @@ static int pkp_pin_peer_pubkey(gnutls_x509_crt_t cert, char *pinnedpubkey)

    /* End Gyrations */

    fp = fopen(pinnedpubkey, "r");

    if(NULL == fp)
      break; /* failed */

    /* Seek to eof to determine the file's size */
    ret = fseek(fp, 0, SEEK_END);
    if(0 != ret)
      break; /* failed */

    /* Fetch the file's size */
    size = ftell(fp);

    /*
     * if the size of our certificate doesn't match the size of
     * the file, they can't be the same, don't bother reading it
     */
    if(size > 0 && len2 != (size_t)size)
      break; /* failed */

    /* Rewind to beginning to perform the read */
    ret = fseek(fp, 0, SEEK_SET);
    if(0 != ret)
      break; /* failed */

    /* http://www.openssl.org/docs/crypto/buffer.html */
    buff2 = malloc(len2);
    if(NULL == buff2)
      break; /* failed */

    /* Returns number of elements read, which should be 1 */
    ret = (int)fread(buff2, (size_t)len2, 1, fp);
    if(1 != ret)
      break; /* failed */

    /* The one good exit point */
    result = (0 == memcmp(buff1, buff2, (size_t)len2));

    result = Curl_pin_peer_pubkey(pinnedpubkey, buff1, len1);
  } while(0);

  if(NULL != fp)
    fclose(fp);

  if(NULL != key)
    gnutls_pubkey_deinit(key);

  if(NULL != buff2)
    free(buff2);

  if(NULL != buff1)
    free(buff1);
  Curl_safefree(buff1);

  return result;
}
@@ -876,10 +833,12 @@ gtls_connect_step3(struct connectdata *conn,
    issuerp = load_file(data->set.ssl.issuercert);
    gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
    rc = gnutls_x509_crt_check_issuer(x509_cert,x509_issuer);
    gnutls_x509_crt_deinit(x509_issuer);
    unload_file(issuerp);
    if(rc <= 0) {
      failf(data, "server certificate issuer check failed (IssuerCert: %s)",
            data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_SSL_ISSUER_ERROR;
    }
    infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
@@ -965,6 +924,7 @@ gtls_connect_step3(struct connectdata *conn,
  if(certclock == (time_t)-1) {
    if(data->set.ssl.verifypeer) {
      failf(data, "server cert expiration date verify failed");
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_SSL_CONNECT_ERROR;
    }
    else
@@ -974,6 +934,7 @@ gtls_connect_step3(struct connectdata *conn,
    if(certclock < time(NULL)) {
      if(data->set.ssl.verifypeer) {
        failf(data, "server certificate expiration date has passed.");
        gnutls_x509_crt_deinit(x509_cert);
        return CURLE_PEER_FAILED_VERIFICATION;
      }
      else
@@ -988,6 +949,7 @@ gtls_connect_step3(struct connectdata *conn,
  if(certclock == (time_t)-1) {
    if(data->set.ssl.verifypeer) {
      failf(data, "server cert activation date verify failed");
      gnutls_x509_crt_deinit(x509_cert);
      return CURLE_SSL_CONNECT_ERROR;
    }
    else
@@ -997,6 +959,7 @@ gtls_connect_step3(struct connectdata *conn,
    if(certclock > time(NULL)) {
      if(data->set.ssl.verifypeer) {
        failf(data, "server certificate not activated yet.");
        gnutls_x509_crt_deinit(x509_cert);
        return CURLE_PEER_FAILED_VERIFICATION;
      }
      else
@@ -1006,11 +969,14 @@ gtls_connect_step3(struct connectdata *conn,
      infof(data, "\t server certificate activation date OK\n");
  }

  if(data->set.str[STRING_SSL_PINNEDPUBLICKEY] != NULL &&
      TRUE != pkp_pin_peer_pubkey(x509_cert,
      data->set.str[STRING_SSL_PINNEDPUBLICKEY])) {
    failf(data, "SSL: public key does not matched pinned public key!");
    return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
  ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
  if(ptr) {
    result = pkp_pin_peer_pubkey(x509_cert, ptr);
    if(result != CURLE_OK) {
      failf(data, "SSL: public key does not match pinned public key!");
      gnutls_x509_crt_deinit(x509_cert);
      return result;
    }
  }

  /* Show:
+12 −56
Original line number Diff line number Diff line
@@ -2366,20 +2366,18 @@ static CURLcode get_cert_chain(struct connectdata *conn,
 * Heavily modified from:
 * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
 */
static int pkp_pin_peer_pubkey(X509* cert, char *pinnedpubkey)
static CURLcode pkp_pin_peer_pubkey(X509* cert, const char *pinnedpubkey)
{
  /* Scratch */
  FILE* fp = NULL;
  int len1 = 0, len2 = 0;
  unsigned char *buff1 = NULL, *buff2 = NULL, *temp = NULL;
  long size = 0;
  unsigned char *buff1 = NULL, *temp = NULL;

  /* Result is returned to caller */
  int ret = 0, result = FALSE;
  CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;

  /* if a path wasn't specified, don't pin */
  if(NULL == pinnedpubkey) return TRUE;
  if(NULL == cert) return FALSE;
  if(NULL == pinnedpubkey) return CURLE_OK;
  if(NULL == cert) return result;

  do {
    /* Begin Gyrations to get the subjectPublicKeyInfo     */
@@ -2409,54 +2407,11 @@ static int pkp_pin_peer_pubkey(X509* cert, char *pinnedpubkey)

    /* End Gyrations */

    /* See the warning above!!! */
    fp = fopen(pinnedpubkey, "r");

    if(NULL == fp)
      break; /* failed */

    /* Seek to eof to determine the file's size */
    ret = fseek(fp, 0, SEEK_END);
    if(0 != ret)
      break; /* failed */

    /* Fetch the file's size */
    size = ftell(fp);

    /*
     * if the size of our certificate doesn't match the size of
     * the file, they can't be the same, don't bother reading it
     */
    if(len2 != size)
      break; /* failed */

    /* Rewind to beginning to perform the read */
    ret = fseek(fp, 0, SEEK_SET);
    if(0 != ret)
      break; /* failed */

    /* http://www.openssl.org/docs/crypto/buffer.html */
    buff2 = OPENSSL_malloc(len2);
    if(NULL == buff2)
      break; /* failed */

    /* Returns number of elements read, which should be 1 */
    ret = (int)fread(buff2, (size_t)len2, 1, fp);
    if(1 != ret)
      break; /* failed */

    /* The one good exit point */
    result = (0 == memcmp(buff1, buff2, (size_t)len2));

    result = Curl_pin_peer_pubkey(pinnedpubkey, buff1, len1);
  } while(0);

  if(NULL != fp)
    fclose(fp);

  /* http://www.openssl.org/docs/crypto/buffer.html */
  if(NULL != buff2)
    OPENSSL_free(buff2);

  if(NULL != buff1)
    OPENSSL_free(buff1);

@@ -2483,6 +2438,7 @@ static CURLcode servercert(struct connectdata *conn,
  X509 *issuer;
  FILE *fp;
  char *buffer = data->state.buffer;
  const char *ptr;

  if(data->set.ssl.certinfo)
    /* we've been asked to gather certificate info! */
@@ -2586,11 +2542,11 @@ static CURLcode servercert(struct connectdata *conn,
      infof(data, "\t SSL certificate verify ok.\n");
  }

  if(data->set.str[STRING_SSL_PINNEDPUBLICKEY] != NULL &&
      TRUE != pkp_pin_peer_pubkey(connssl->server_cert,
      data->set.str[STRING_SSL_PINNEDPUBLICKEY])) {
    failf(data, "SSL: public key does not matched pinned public key!");
    return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
  ptr = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
  if(retcode == CURLE_OK && ptr) {
    retcode = pkp_pin_peer_pubkey(connssl->server_cert, ptr);
    if(retcode != CURLE_OK)
      failf(data, "SSL: public key does not match pinned public key!");
  }

  X509_free(connssl->server_cert);
+56 −0
Original line number Diff line number Diff line
@@ -682,6 +682,62 @@ int Curl_ssl_random(struct SessionHandle *data,
  return curlssl_random(data, entropy, length);
}

/*
 * Generic pinned public key check.
 */

CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
                              const unsigned char *pubkey, size_t pubkeylen)
{
  FILE *fp = NULL;
  unsigned char *buf = NULL;
  long size = 0;
  CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;

  /* if a path wasn't specified, don't pin */
  if(!pinnedpubkey)
    return CURLE_OK;
  if(!pubkey || !pubkeylen)
    return result;
  fp = fopen(pinnedpubkey, "rb");
  if(!fp)
    return result;

  do {
    /* Determine the file's size */
    if(fseek(fp, 0, SEEK_END))
      break;
    size = ftell(fp);
    if(fseek(fp, 0, SEEK_SET))
      break;

    /*
     * if the size of our certificate doesn't match the size of
     * the file, they can't be the same, don't bother reading it
     */
    if((long) pubkeylen != size)
      break;

    /* Allocate buffer for the pinned key. */
    buf = malloc(pubkeylen);
    if(!buf)
      break;

    /* Returns number of elements read, which should be 1 */
    if((int) fread(buf, pubkeylen, 1, fp) != 1)
      break;

    /* The one good exit point */
    if(!memcmp(pubkey, buf, pubkeylen))
      result = CURLE_OK;
  } while(0);

  Curl_safefree(buf);
  fclose(fp);

  return result;
}

void Curl_ssl_md5sum(unsigned char *tmp, /* input */
                     size_t tmplen,
                     unsigned char *md5sum, /* output */
+3 −0
Original line number Diff line number Diff line
@@ -108,6 +108,9 @@ void Curl_ssl_md5sum(unsigned char *tmp, /* input */
                     size_t tmplen,
                     unsigned char *md5sum, /* output */
                     size_t md5len);
/* Check pinned public key. */
CURLcode Curl_pin_peer_pubkey(const char *pinnedpubkey,
                              const unsigned char *pubkey, size_t pubkeylen);

#define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */