* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2015, 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
#ifdef USE_GNUTLS
+#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
# define HAS_ALPN
# endif
# endif
+
+# if (GNUTLS_VERSION_NUMBER >= 0x03020d)
+# define HAS_OCSP
+# endif
+#endif
+
+#ifdef HAS_OCSP
+# include <gnutls/ocsp.h>
#endif
/*
gnutls_record_get_direction(session)?
ssl_connect_2_writing:ssl_connect_2_reading;
continue;
- if(nonblocking)
- return CURLE_OK;
}
else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
const char *strerr = NULL;
gnutls_alpn_set_protocols(session, protocols, protocols_size, 0);
infof(data, "ALPN, offering %s, %s\n", NGHTTP2_PROTO_VERSION_ID,
ALPN_HTTP_1_1);
+ connssl->asked_for_h2 = TRUE;
}
else {
infof(data, "SSL, can't negotiate HTTP/2.0 without ALPN\n");
/* lowat must be set to zero when using custom push and pull functions. */
gnutls_transport_set_lowat(session, 0);
+#ifdef HAS_OCSP
+ if(data->set.ssl.verifystatus) {
+ rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif
+
/* This might be a reconnect, so we check for a session ID in the cache
to speed up things */
return CURLE_OK;
}
+static CURLcode pkp_pin_peer_pubkey(gnutls_x509_crt_t cert,
+ const char *pinnedpubkey)
+{
+ /* Scratch */
+ size_t len1 = 0, len2 = 0;
+ unsigned char *buff1 = NULL;
+
+ gnutls_pubkey_t key = NULL;
+
+ /* Result is returned to caller */
+ int ret = 0;
+ CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ /* if a path wasn't specified, don't pin */
+ if(NULL == pinnedpubkey)
+ return CURLE_OK;
+
+ if(NULL == cert)
+ return result;
+
+ do {
+ /* Begin Gyrations to get the public key */
+ gnutls_pubkey_init(&key);
+
+ ret = gnutls_pubkey_import_x509(key, cert, 0);
+ if(ret < 0)
+ break; /* failed */
+
+ ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, NULL, &len1);
+ if(ret != GNUTLS_E_SHORT_MEMORY_BUFFER || len1 == 0)
+ break; /* failed */
+
+ buff1 = malloc(len1);
+ if(NULL == buff1)
+ break; /* failed */
+
+ len2 = len1;
+
+ ret = gnutls_pubkey_export(key, GNUTLS_X509_FMT_DER, buff1, &len2);
+ if(ret < 0 || len1 != len2)
+ break; /* failed */
+
+ /* End Gyrations */
+
+ /* The one good exit point */
+ result = Curl_pin_peer_pubkey(pinnedpubkey, buff1, len1);
+ } while(0);
+
+ if(NULL != key)
+ gnutls_pubkey_deinit(key);
+
+ Curl_safefree(buff1);
+
+ return result;
+}
+
static Curl_recv gtls_recv;
static Curl_send gtls_send;
struct SessionHandle *data = conn->data;
gnutls_session_t session = conn->ssl[sockindex].session;
int rc;
- int incache;
+ bool incache;
void *ssl_sessionid;
#ifdef HAS_ALPN
gnutls_datum_t proto;
else
infof(data, "\t server certificate verification SKIPPED\n");
+#ifdef HAS_OCSP
+ if(data->set.ssl.verifystatus) {
+ if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
+ if(verify_status & GNUTLS_CERT_REVOKED)
+ failf(data, "SSL server certificate was REVOKED\n");
+ else
+ failf(data, "SSL server certificate status verification FAILED");
+
+ return CURLE_SSL_INVALIDCERTSTATUS;
+ }
+ else
+ infof(data, "SSL server certificate status verification OK\n");
+ }
+ else
+ infof(data, "SSL server certificate status verification SKIPPED\n");
+#endif
+
/* initialize an X.509 certificate structure. */
gnutls_x509_crt_init(&x509_cert);
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",
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
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
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
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
infof(data, "\t server certificate activation date OK\n");
}
+ 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:
- ciphers used
conn->negnpn = NPN_HTTP1_1;
}
}
- else {
+ else if(connssl->asked_for_h2) {
infof(data, "ALPN, server did not agree to a protocol\n");
}
}
int sockindex)
{
- CURLcode retcode;
+ CURLcode result;
bool done = FALSE;
- retcode = gtls_connect_common(conn, sockindex, FALSE, &done);
- if(retcode)
- return retcode;
+ result = gtls_connect_common(conn, sockindex, FALSE, &done);
+ if(result)
+ return result;
DEBUGASSERT(done);
if(ret == GNUTLS_E_REHANDSHAKE) {
/* BLOCKING call, this is bad but a work-around for now. Fixing this "the
proper way" takes a whole lot of work. */
- CURLcode rc = handshake(conn, num, FALSE, FALSE);
- if(rc)
+ CURLcode result = handshake(conn, num, FALSE, FALSE);
+ if(result)
/* handshake() writes error message on its own */
- *curlcode = rc;
+ *curlcode = result;
else
*curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
return -1;
return snprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
}
-int Curl_gtls_seed(struct SessionHandle *data)
+#ifndef USE_GNUTLS_NETTLE
+static int Curl_gtls_seed(struct SessionHandle *data)
{
/* we have the "SSL is seeded" boolean static to prevent multiple
time-consuming seedings in vain */
static bool ssl_seeded = FALSE;
/* Quickly add a bit of entropy */
-#ifndef USE_GNUTLS_NETTLE
gcry_fast_random_poll();
-#endif
if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
data->set.str[STRING_SSL_EGDSOCKET]) {
}
return 0;
}
+#endif
-void Curl_gtls_random(struct SessionHandle *data,
- unsigned char *entropy,
- size_t length)
+/* data might be NULL! */
+int Curl_gtls_random(struct SessionHandle *data,
+ unsigned char *entropy,
+ size_t length)
{
#if defined(USE_GNUTLS_NETTLE)
(void)data;
gnutls_rnd(GNUTLS_RND_RANDOM, entropy, length);
#elif defined(USE_GNUTLS)
- Curl_gtls_seed(data); /* Initiate the seed if not already done */
+ if(data)
+ Curl_gtls_seed(data); /* Initiate the seed if not already done */
gcry_randomize(entropy, length, GCRY_STRONG_RANDOM);
#endif
+ return 0;
}
void Curl_gtls_md5sum(unsigned char *tmp, /* input */
#endif
}
+bool Curl_gtls_cert_status_request(void)
+{
+#ifdef HAS_OCSP
+ return TRUE;
+#else
+ return FALSE;
+#endif
+}
+
#endif /* USE_GNUTLS */