aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/gtls.c53
-rw-r--r--lib/nss.c69
-rw-r--r--lib/ssluse.c42
-rw-r--r--lib/strerror.c3
-rw-r--r--lib/url.c9
-rw-r--r--lib/urldata.h2
6 files changed, 170 insertions, 8 deletions
diff --git a/lib/gtls.c b/lib/gtls.c
index e9e410243..6e762f161 100644
--- a/lib/gtls.c
+++ b/lib/gtls.c
@@ -143,6 +143,32 @@ static void showtime(struct SessionHandle *data,
infof(data, "%s", data->state.buffer);
}
+static gnutls_datum load_file (const char *file)
+{
+ FILE *f;
+ gnutls_datum loaded_file = { NULL, 0 };
+ long filelen;
+ void *ptr;
+
+ if (!(f = fopen(file, "r"))
+ || fseek(f, 0, SEEK_END) != 0
+ || (filelen = ftell(f)) < 0
+ || fseek(f, 0, SEEK_SET) != 0
+ || !(ptr = malloc((size_t)filelen))
+ || fread(ptr, 1, (size_t)filelen, f) < (size_t)filelen) {
+ return loaded_file;
+ }
+
+ loaded_file.data = ptr;
+ loaded_file.size = (unsigned int)filelen;
+ return loaded_file;
+}
+
+static void unload_file(gnutls_datum data) {
+ free(data.data);
+}
+
+
/* this function does a BLOCKING SSL/TLS (re-)handshake */
static CURLcode handshake(struct connectdata *conn,
gnutls_session session,
@@ -221,7 +247,8 @@ Curl_gtls_connect(struct connectdata *conn,
unsigned int cert_list_size;
const gnutls_datum *chainp;
unsigned int verify_status;
- gnutls_x509_crt x509_cert;
+ gnutls_x509_crt x509_cert,x509_issuer;
+ gnutls_datum issuerp;
char certbuf[256]; /* big enough? */
size_t size;
unsigned int algo;
@@ -375,7 +402,9 @@ Curl_gtls_connect(struct connectdata *conn,
chainp = gnutls_certificate_get_peers(session, &cert_list_size);
if(!chainp) {
- if(data->set.ssl.verifypeer) {
+ if(data->set.ssl.verifypeer ||
+ data->set.ssl.verifyhost ||
+ data->set.ssl.issuercert) {
failf(data, "failed to get server cert");
return CURLE_PEER_FAILED_VERIFICATION;
}
@@ -399,8 +428,9 @@ Curl_gtls_connect(struct connectdata *conn,
/* verify_status is a bitmask of gnutls_certificate_status bits */
if(verify_status & GNUTLS_CERT_INVALID) {
if(data->set.ssl.verifypeer) {
- failf(data, "server certificate verification failed. CAfile: %s",
- data->set.ssl.CAfile?data->set.ssl.CAfile:"none");
+ failf(data, "server certificate verification failed. CAfile: %s "
+ "CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
+ data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
return CURLE_SSL_CACERT;
}
else
@@ -419,6 +449,21 @@ Curl_gtls_connect(struct connectdata *conn,
gnutls_x509_crt_t format */
gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
+ if (data->set.ssl.issuercert) {
+ gnutls_x509_crt_init(&x509_issuer);
+ 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);
+ unload_file(issuerp);
+ if (rc <= 0) {
+ failf(data, "server certificate issuer check failed (IssuerCert: %s)",
+ data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ infof(data,"\t server certificate issuer check OK (Issuer Cert: %s)\n",
+ data->set.ssl.issuercert?data->set.ssl.issuercert:"none");
+ }
+
size=sizeof(certbuf);
rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME,
0, /* the first and only one */
diff --git a/lib/nss.c b/lib/nss.c
index a5fc795f8..5e01c4448 100644
--- a/lib/nss.c
+++ b/lib/nss.c
@@ -723,6 +723,43 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
/**
*
+ * Check that the Peer certificate's issuer certificate matches the one found
+ * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the
+ * issuer check, so we provide comments that mimic the OpenSSL
+ * X509_check_issued function (in x509v3/v3_purp.c)
+ */
+static SECStatus check_issuer_cert(struct connectdata *conn, PRFileDesc *sock, char* issuer_nickname)
+{
+ CERTCertificate *cert,*cert_issuer,*issuer;
+ SECStatus res=SECSuccess;
+ void *proto_win = NULL;
+
+ /*
+ PRArenaPool *tmpArena = NULL;
+ CERTAuthKeyID *authorityKeyID = NULL;
+ SECITEM *caname = NULL;
+ */
+
+ cert = SSL_PeerCertificate(sock);
+ cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);
+
+ proto_win = SSL_RevealPinArg(sock);
+ issuer = NULL;
+ issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);
+
+ if ((!cert_issuer) || (!issuer))
+ res = SECFailure;
+ else if (CERT_CompareCerts(cert_issuer,issuer)==PR_FALSE)
+ res = SECFailure;
+
+ CERT_DestroyCertificate(cert);
+ CERT_DestroyCertificate(issuer);
+ CERT_DestroyCertificate(cert_issuer);
+ return res;
+}
+
+/**
+ *
* Callback to pick the SSL client certificate.
*/
static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
@@ -853,7 +890,7 @@ int Curl_nss_close_all(struct SessionHandle *data)
return 0;
}
-CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
+CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
{
PRInt32 err;
PRFileDesc *model = NULL;
@@ -1046,6 +1083,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
}
else {
strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX);
+ nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */
}
if(nss_Init_Tokens(conn) != SECSuccess) {
free(nickname);
@@ -1061,7 +1099,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
connssl->client_nickname = strdup(nickname);
if(SSL_GetClientAuthDataHook(model,
(SSLGetClientAuthData) SelectClientCert,
- (void *)connssl->client_nickname) !=
+ (void *)connssl) !=
SECSuccess) {
curlerr = CURLE_SSL_CERTPROBLEM;
goto error;
@@ -1074,6 +1112,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
else
connssl->client_nickname = NULL;
+
/* Import our model socket onto the existing file descriptor */
connssl->handle = PR_ImportTCPSocket(sockfd);
connssl->handle = SSL_ImportFD(model, connssl->handle);
@@ -1099,6 +1138,32 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
display_conn_info(conn, connssl->handle);
+ if (data->set.str[STRING_SSL_ISSUERCERT]) {
+ char *n;
+ char *nickname;
+ nickname = (char *)malloc(PATH_MAX);
+ if(is_file(data->set.str[STRING_SSL_ISSUERCERT])) {
+ n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/');
+ if (n) {
+ n++; /* skip last slash */
+ snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n);
+ }
+ }
+ else {
+ strncpy(nickname, data->set.str[STRING_SSL_ISSUERCERT], PATH_MAX);
+ nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */
+ }
+ if (check_issuer_cert(conn,connssl->handle,nickname)==SECFailure) {
+ infof(data,"SSL certificate issuer check failed\n");
+ free(nickname);
+ curlerr = CURLE_SSL_ISSUER_ERROR;
+ goto error;
+ }
+ else {
+ infof("SSL certificate issuer check ok\n");
+ }
+ }
+
return CURLE_OK;
error:
diff --git a/lib/ssluse.c b/lib/ssluse.c
index f14ad344e..4fff13d78 100644
--- a/lib/ssluse.c
+++ b/lib/ssluse.c
@@ -1629,6 +1629,9 @@ static CURLcode servercert(struct connectdata *conn,
long lerr;
ASN1_TIME *certdate;
struct SessionHandle *data = conn->data;
+ X509 *issuer;
+ FILE *fp;
+
connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
if(!connssl->server_cert) {
if(strict)
@@ -1678,6 +1681,41 @@ static CURLcode servercert(struct connectdata *conn,
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
+ /* e.g. match issuer name with provided issuer certificate */
+ if (data->set.str[STRING_SSL_ISSUERCERT]) {
+ if (! (fp=fopen(data->set.str[STRING_SSL_ISSUERCERT],"r"))) {
+ if (strict)
+ failf(data, "SSL: Unable to open issuer cert (%s)\n",
+ data->set.str[STRING_SSL_ISSUERCERT]);
+ X509_free(connssl->server_cert);
+ connssl->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ issuer = PEM_read_X509(fp,NULL,NULL,NULL);
+ if (!issuer) {
+ if (strict)
+ failf(data, "SSL: Unable to read issuer cert (%s)\n",
+ data->set.str[STRING_SSL_ISSUERCERT]);
+ X509_free(connssl->server_cert);
+ X509_free(issuer);
+ fclose(fp);
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ fclose(fp);
+ if (X509_check_issued(issuer,connssl->server_cert) != X509_V_OK) {
+ if (strict)
+ failf(data, "SSL: Certificate issuer check failed (%s)\n",
+ data->set.str[STRING_SSL_ISSUERCERT]);
+ X509_free(connssl->server_cert);
+ X509_free(issuer);
+ connssl->server_cert = NULL;
+ return CURLE_SSL_ISSUER_ERROR;
+ }
+ infof(data, "\t SSL certificate issuer check ok (%s)\n",
+ data->set.str[STRING_SSL_ISSUERCERT]);
+ X509_free(issuer);
+ }
+
lerr = data->set.ssl.certverifyresult=
SSL_get_verify_result(connssl->handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
@@ -1690,12 +1728,12 @@ static CURLcode servercert(struct connectdata *conn,
retcode = CURLE_PEER_FAILED_VERIFICATION;
}
else
- infof(data, "SSL certificate verify result: %s (%ld),"
+ infof(data, "\t SSL certificate verify result: %s (%ld),"
" continuing anyway.\n",
X509_verify_cert_error_string(lerr), lerr);
}
else
- infof(data, "SSL certificate verify ok.\n");
+ infof(data, "\t SSL certificate verify ok.\n");
}
X509_free(connssl->server_cert);
diff --git a/lib/strerror.c b/lib/strerror.c
index 03b01582c..032e2c747 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -225,6 +225,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_SSL_CRL_BADFILE:
return "Failed to load CRL file (path? access rights?, format?)";
+ case CURLE_SSL_ISSUER_ERROR:
+ return "Issuer check against peer certificate failed";
+
case CURLE_SEND_FAIL_REWIND:
return "Send failed since rewinding of the data stream failed";
diff --git a/lib/url.c b/lib/url.c
index 0e71cede0..a7f1a0c1a 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1819,6 +1819,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
va_arg(param, char *));
break;
+ case CURLOPT_ISSUERCERT:
+ /*
+ * Set Issuer certificate file
+ * to check certificates issuer
+ */
+ result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
+ va_arg(param, char *));
+ break;
case CURLOPT_TELNETOPTIONS:
/*
* Set a linked list of telnet options
@@ -3960,6 +3968,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
data->set.ssl.CApath = data->set.str[STRING_SSL_CAPATH];
data->set.ssl.CAfile = data->set.str[STRING_SSL_CAFILE];
data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
+ data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
data->set.ssl.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
data->set.ssl.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
data->set.ssl.cipher_list = data->set.str[STRING_SSL_CIPHER_LIST];
diff --git a/lib/urldata.h b/lib/urldata.h
index def598b41..2e0f69204 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -213,6 +213,7 @@ struct ssl_config_data {
char *CApath; /* certificate dir (doesn't work on windows) */
char *CAfile; /* cerficate to verify peer against */
char *CRLfile; /* CRL to check cerficate revocation */
+ char *issuercert; /* optional issuer cerficate filename */
char *random_file; /* path to file containing "random" data */
char *egdsocket; /* path to file containing the EGD daemon socket */
char *cipher_list; /* list of ciphers to use */
@@ -1319,6 +1320,7 @@ enum dupstring {
STRING_USERPWD, /* <user:password>, if used */
STRING_SSH_HOST_PUBLIC_KEY_MD5, /* md5 of host public key in ascii hex */
STRING_SSL_CRLFILE, /* crl file to check certificate */
+ STRING_SSL_ISSUERCERT, /* issuer cert file to check certificate */
/* -- end of strings -- */
STRING_LAST /* not used, just an end-of-list marker */