OpenSSL, NSS and GnuTLS-built libcurls.
Daniel Stenberg (6 Jun 2008)
+- Axel Tillequin and Arnaud Ebalard added support for CURLOPT_CRLFILE, for
+ OpenSSL, NSS and GnuTLS-built libcurls.
+
- Added CURLINFO_PRIMARY_IP as a new information retrievable with
curl_easy_getinfo. It returns a pointer to a string with the most recently
used IP address. Modified test case 500 to also verify this feature. The
Public curl releases: 106
Command line options: 126
- curl_easy_setopt() options: 150
+ curl_easy_setopt() options: 151
Public functions in libcurl: 58
Public web site mirrors: 37
Known libcurl bindings: 36
This release includes the following changes:
o Added CURLINFO_PRIMARY_IP
+ o Added CURLOPT_CRLFILE
This release includes the following bugfixes:
This release would not have looked like this without help, code, reports and
advice from friends like these:
- Lenny Rachitsky
+ Lenny Rachitsky, Axel Tillequin, Arnaud Ebalard
Thanks! (and sorry if I forgot to mention someone)
140 - Arnaud Ebalard and Axel Tillequin's CRL support and issuer check patches
-141 - The sponsored feature CURLINFO_PRIMARY_IP that returns the IP address
- as a string for the most recently used connection.
-
144 - Help apps use 64bit/LFS libcurl!
145 -
indicate an accessible path. The \fICURLOPT_CAPATH\fP function apparently
does not work in Windows due to some limitation in openssl. This option is
OpenSSL-specific and does nothing if libcurl is built to use GnuTLS.
+.IP CURLOPT_CRLFILE
+Pass a char * to a zero terminated string naming a file with the concatenation
+of CRL (in PEM format) to use in the certificate validation that occurs during
+the SSL exchange.
+
+When curl is built to use NSS or GnuTLS, there is no way to influence the use
+of CRL passed to help in the verification process. When libcurl is built with
+OpenSSL support, X509_V_FLAG_CRL_CHECK and X509_V_FLAG_CRL_CHECK_ALL are both
+set, requiring CRL check against all the elements of the certificate chain if
+a CRL file is passed.
+
+This option makes sense only when used in combination with the
+\fICURLOPT_SSL_VERIFYPEER\fP option.
+
+A specific error code (CURLE_SSL_CRL_BADFILE) is defined with the option. It
+is returned when the SSL exchange fails because the CRL file cannot be loaded.
+Note that a failure in certificate verification due to a revocation information
+found in the CRL does not trigger this specific error.
.IP CURLOPT_RANDOM_FILE
Pass a char * to a zero terminated file name. The file will be used to read
from to seed the random engine for SSL. The more random the specified file is,
Socket is not ready for send/recv wait till it's ready and try again. This
return code is only returned from \fIcurl_easy_recv(3)\fP and
\fIcurl_easy_send(3)\fP (Added in 7.18.2)
+.IP "CURLE_SSL_CRL_BADFILE (82)"
+Failed to load CRL file (Added in 7.18.3)
.IP "CURLE_OBSOLETE*"
These error codes will never be returned. They used to be used in an old libcurl
version and are currently unused.
CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL
connection */
CURLE_AGAIN, /* 81 - socket is not ready for send/recv,
- wait till it's ready and try again */
+ wait till it's ready and try again (Added
+ in 7.18.2) */
+ CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or
+ wrong format (Added in 7.18.3) */
CURL_LAST /* never use! */
} CURLcode;
CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167),
CINIT(SEEKDATA, OBJECTPOINT, 168),
+ /* CRL file */
+ CINIT(CRLFILE, OBJECTPOINT, 169),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
rc, data->set.ssl.CAfile);
}
+ if(data->set.ssl.CRLfile) {
+ /* set the CRL list file */
+ rc = gnutls_certificate_set_x509_crl_file(conn->ssl[sockindex].cred,
+ data->set.ssl.CRLfile,
+ GNUTLS_X509_FMT_PEM);
+ if(rc < 0) {
+ failf(data, "error reading crl file %s (%s)\n",
+ data->set.ssl.CRLfile, gnutls_strerror(rc));
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ else
+ infof(data, "found %d CRL in %s\n",
+ rc, data->set.ssl.CRLfile);
+ }
+
/* Initialize TLS session as a client */
rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT);
if(rc) {
#include <sslproto.h>
#include <prtypes.h>
#include <pk11pub.h>
+#include <prio.h>
+#include <secitem.h>
+#include <secport.h>
#include "memory.h"
#include "easyif.h" /* for Curl_convert_from_utf8 prototype */
return 1;
}
+static int nss_load_crl(char* crlfilename, PRBool ascii)
+{
+ PRFileDesc *infile;
+ PRStatus prstat;
+ PRFileInfo info;
+ PRInt32 nb;
+ int rv;
+ SECItem crlDER;
+ CERTSignedCrl *crl=NULL;
+ PK11SlotInfo *slot=NULL;
+
+ infile = PR_Open(crlfilename,PR_RDONLY,0);
+ if (!infile) {
+ return 0;
+ }
+ crlDER.data = NULL;
+ prstat = PR_GetOpenFileInfo(infile,&info);
+ if (prstat!=PR_SUCCESS) return 0;
+ if (ascii) {
+ SECItem filedata;
+ char *asc,*body;
+ filedata.data = NULL;
+ if (!SECITEM_AllocItem(NULL,&filedata,info.size)) return 0;
+ nb = PR_Read(infile,filedata.data,info.size);
+ if (nb!=info.size) return 0;
+ asc = (char*)filedata.data;
+ if (!asc) {
+ return 0;
+ }
+ if ((body=strstr(asc,"-----BEGIN")) != NULL) {
+ char *trailer=NULL;
+ asc = body;
+ body = PORT_Strchr(asc,'\n');
+ if (!body) body = PORT_Strchr(asc,'\r');
+ if (body) trailer = strstr(++body,"-----END");
+ if (trailer!=NULL) *trailer='\0';
+ else return 0;
+ }
+ else {
+ body = asc;
+ }
+ rv = ATOB_ConvertAsciiToItem(&crlDER,body);
+ PORT_Free(filedata.data);
+ if (rv) return 0;
+ }
+ else {
+ if (!SECITEM_AllocItem(NULL,&crlDER,info.size)) return 0;
+ nb = PR_Read(infile,crlDER.data,info.size);
+ if (nb!=info.size) return 0;
+ }
+
+ slot = PK11_GetInternalKeySlot();
+ crl = PK11_ImportCRL(slot,&crlDER,
+ NULL,SEC_CRL_TYPE,
+ NULL,CRL_IMPORT_DEFAULT_OPTIONS,
+ NULL,(CRL_DECODE_DEFAULT_OPTIONS|
+ CRL_DECODE_DONT_COPY_DER));
+ if (slot) PK11_FreeSlot(slot);
+ if (!crl) return 0;
+ SEC_DestroyCrl(crl);
+ return 1;
+}
+
static int nss_load_key(struct connectdata *conn, char *key_file)
{
#ifdef HAVE_PK11_CREATEGENERICOBJECT
data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
data->set.ssl.CApath ? data->set.ssl.CApath : "none");
+ if (data->set.ssl.CRLfile) {
+ int rc = nss_load_crl(data->set.ssl.CRLfile, PR_FALSE);
+ if (!rc) {
+ curlerr = CURLE_SSL_CRL_BADFILE;
+ goto error;
+ }
+ infof(data,
+ " CRLfile: %s\n",
+ data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none");
+ }
+
if(data->set.str[STRING_CERT]) {
char *n;
char *nickname;
struct SessionHandle *data = conn->data;
SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
void *ssl_sessionid=NULL;
+ X509_LOOKUP *lookup=NULL;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
"none");
}
+
+ if (data->set.str[STRING_SSL_CRLFILE]) {
+ /* tell SSL where to find CRL file that is used to check certificate
+ * revocation */
+ lookup=X509_STORE_add_lookup(connssl->ctx->cert_store,X509_LOOKUP_file());
+ if ( !lookup ||
+ (X509_load_crl_file(lookup,data->set.str[STRING_SSL_CRLFILE],
+ X509_FILETYPE_PEM)!=1) ) {
+ failf(data,"error loading CRL file :\n"
+ " CRLfile: %s\n",
+ data->set.str[STRING_SSL_CRLFILE]?
+ data->set.str[STRING_SSL_CRLFILE]: "none");
+ return CURLE_SSL_CRL_BADFILE;
+ }
+ else {
+ /* Everything is fine. */
+ infof(data, "successfully load CRL file:\n");
+ X509_STORE_set_flags(connssl->ctx->cert_store,
+ X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
+ }
+ infof(data,
+ " CRLfile: %s\n", data->set.str[STRING_SSL_CRLFILE] ?
+ data->set.str[STRING_SSL_CRLFILE]: "none");
+ }
+
/* SSL always tries to verify the peer, this only says whether it should
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
case CURLE_SSL_SHUTDOWN_FAILED:
return "Failed to shut down the SSL connection";
+ case CURLE_SSL_CRL_BADFILE:
+ return "Failed to load CRL file (path? access rights?, format?)";
+
case CURLE_SEND_FAIL_REWIND:
return "Send failed since rewinding of the data stream failed";
result = setstropt(&data->set.str[STRING_SSL_CAPATH],
va_arg(param, char *));
break;
+ case CURLOPT_CRLFILE:
+ /*
+ * Set CRL file info for SSL connection. Specify file name of the CRL
+ * to check certificates revocation
+ */
+ result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
+ va_arg(param, char *));
+ break;
case CURLOPT_TELNETOPTIONS:
/*
* Set a linked list of telnet options
*/
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.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];
2: CN must match hostname */
char *CApath; /* certificate dir (doesn't work on windows) */
char *CAfile; /* cerficate to verify peer against */
+ char *CRLfile; /* CRL to check cerficate revocation */
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 */
STRING_USERAGENT, /* User-Agent string */
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 */
/* -- end of strings -- */
STRING_LAST /* not used, just an end-of-list marker */