andre@998: From c57d951c3bda8b1ca66cac45dfd6270fa34b01d3 Mon Sep 17 00:00:00 2001 andre@998: From: Andre Heinecke andre@998: Date: Mon, 1 Sep 2014 16:55:40 +0200 andre@998: Subject: [PATCH 2/3] Add CURLOPT_PEERCERT option to pin a peer cert andre@998: andre@998: Only implemented for a specific usecase with polarssl andre@998: --- andre@998: include/curl/curl.h | 3 +++ andre@998: include/curl/typecheck-gcc.h | 1 + andre@998: lib/url.c | 8 ++++++++ andre@998: lib/urldata.h | 1 + andre@998: lib/vtls/polarssl.c | 42 ++++++++++++++++++++++++++++++++++++++++-- andre@998: 5 files changed, 53 insertions(+), 2 deletions(-) andre@998: andre@998: diff --git a/include/curl/curl.h b/include/curl/curl.h andre@998: index d40b2db..20a9d82 100644 andre@998: --- a/include/curl/curl.h andre@998: +++ b/include/curl/curl.h andre@998: @@ -1611,6 +1611,9 @@ typedef enum { andre@998: /* Pass in a bitmask of "header options" */ andre@998: CINIT(HEADEROPT, LONG, 229), andre@998: andre@998: + /* Peer certificate */ andre@998: + CINIT(PEERCERT, OBJECTPOINT, 230), andre@998: + andre@998: CURLOPT_LASTENTRY /* the last unused */ andre@998: } CURLoption; andre@998: andre@998: diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h andre@998: index 69d41a2..241529d 100644 andre@998: --- a/include/curl/typecheck-gcc.h andre@998: +++ b/include/curl/typecheck-gcc.h andre@998: @@ -258,6 +258,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, andre@998: (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ andre@998: (option) == CURLOPT_CRLFILE || \ andre@998: (option) == CURLOPT_ISSUERCERT || \ andre@998: + (option) == CURLOPT_PEERCERT || \ andre@998: (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ andre@998: (option) == CURLOPT_SSH_KNOWNHOSTS || \ andre@998: (option) == CURLOPT_MAIL_FROM || \ andre@998: diff --git a/lib/url.c b/lib/url.c andre@998: index 89c3fd5..b089cdf 100644 andre@998: --- a/lib/url.c andre@998: +++ b/lib/url.c andre@998: @@ -2015,6 +2015,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, andre@998: result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT], andre@998: va_arg(param, char *)); andre@998: break; andre@998: + case CURLOPT_PEERCERT: andre@998: + /* andre@998: + * Set peer certificate file andre@998: + * to check peer certificate against andre@998: + */ andre@998: + result = setstropt(&data->set.str[STRING_SSL_PEERCERT], andre@998: + va_arg(param, char *)); andre@998: + break; andre@998: case CURLOPT_TELNETOPTIONS: andre@998: /* andre@998: * Set a linked list of telnet options andre@998: diff --git a/lib/urldata.h b/lib/urldata.h andre@998: index 8594c2f..a6dc1ae 100644 andre@998: --- a/lib/urldata.h andre@998: +++ b/lib/urldata.h andre@998: @@ -1391,6 +1391,7 @@ enum dupstring { andre@998: STRING_USERAGENT, /* User-Agent string */ andre@998: STRING_SSL_CRLFILE, /* crl file to check certificate */ andre@998: STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */ andre@998: + STRING_SSL_PEERCERT, /* issuer cert file to check certificate */ andre@998: STRING_USERNAME, /* , if used */ andre@998: STRING_PASSWORD, /* , if used */ andre@998: STRING_OPTIONS, /* , if used */ andre@998: diff --git a/lib/vtls/polarssl.c b/lib/vtls/polarssl.c andre@998: index e18cadf..2c40e36 100644 andre@998: --- a/lib/vtls/polarssl.c andre@998: +++ b/lib/vtls/polarssl.c andre@998: @@ -360,6 +360,7 @@ polarssl_connect_step2(struct connectdata *conn, andre@998: #ifdef HAS_ALPN andre@998: const char* next_protocol; andre@998: #endif andre@998: + const x509_crt *peer_cert = NULL; andre@998: andre@998: char errorbuf[128]; andre@998: memset(errorbuf, 0, sizeof(errorbuf)); andre@998: @@ -419,12 +420,49 @@ polarssl_connect_step2(struct connectdata *conn, andre@998: return CURLE_PEER_FAILED_VERIFICATION; andre@998: } andre@998: andre@998: - if(ssl_get_peer_cert(&(connssl->ssl))) { andre@998: + peer_cert = ssl_get_peer_cert(&(connssl->ssl)); andre@998: + if(peer_cert) { andre@998: + if(data->set.str[STRING_SSL_PEERCERT]) { andre@998: + x509_crt pinned_cert; andre@998: + unsigned int i; andre@998: + andre@998: + /* Handle pinned certificate */ andre@998: + x509_crt_init(&pinned_cert); andre@998: + ret = x509_crt_parse_file(&pinned_cert, andre@998: + data->set.str[STRING_SSL_PEERCERT]); andre@998: + andre@998: + if(ret) { andre@998: +#ifdef POLARSSL_ERROR_C andre@998: + error_strerror(ret, errorbuf, sizeof(errorbuf)); andre@998: +#endif /* POLARSSL_ERROR_C */ andre@998: + failf(data, "Error reading peer cert file %s - PolarSSL: (-0x%04X) %s", andre@998: + data->set.str[STRING_SSL_PEERCERT], -ret, errorbuf); andre@998: + andre@998: + x509_crt_free(&pinned_cert); andre@998: + return CURLE_PEER_FAILED_VERIFICATION; andre@998: + } andre@998: + andre@998: + if (peer_cert->raw.len == 0 || andre@998: + peer_cert->raw.len != pinned_cert.raw.len) { andre@998: + failf(data, "Error validating peer certificate. Size does " andre@998: + "not match the certificate set with PEERCERT option.\n"); andre@998: + x509_crt_free(&pinned_cert); andre@998: + return CURLE_PEER_FAILED_VERIFICATION; andre@998: + } andre@998: + for (i = 0; i < peer_cert->raw.len; i++) { andre@998: + if (peer_cert->raw.p[i] != pinned_cert.raw.p[i]) { andre@998: + failf(data, "Error validating peer certificate. Does " andre@998: + "not match the certificate set with PEERCERT option.\n"); andre@998: + return CURLE_PEER_FAILED_VERIFICATION; andre@998: + } andre@998: + } andre@998: + } andre@998: + andre@998: /* If the session was resumed, there will be no peer certs */ andre@998: memset(buffer, 0, sizeof(buffer)); andre@998: andre@998: if(x509_crt_info(buffer, sizeof(buffer), (char *)"* ", andre@998: - ssl_get_peer_cert(&(connssl->ssl))) != -1) andre@998: + peer_cert) != -1) andre@998: infof(data, "Dumping cert info:\n%s\n", buffer); andre@998: } andre@998: andre@998: -- andre@998: 1.9.1 andre@998: