From 3fe8251dfbb533803e25cc38365114b28c5a1c85 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 6 Jun 2008 18:40:21 +0000 Subject: [PATCH] - Axel Tillequin and Arnaud Ebalard added support for CURLOPT_CRLFILE, for OpenSSL, NSS and GnuTLS-built libcurls. --- CHANGES | 3 ++ RELEASE-NOTES | 5 +-- TODO-RELEASE | 3 -- docs/libcurl/curl_easy_setopt.3 | 18 ++++++++++ docs/libcurl/libcurl-errors.3 | 2 ++ include/curl/curl.h | 8 ++++- lib/gtls.c | 15 ++++++++ lib/nss.c | 77 +++++++++++++++++++++++++++++++++++++++++ lib/ssluse.c | 26 ++++++++++++++ lib/strerror.c | 3 ++ lib/url.c | 9 +++++ lib/urldata.h | 2 ++ 12 files changed, 165 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index 3726eff..4145bbe 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,9 @@ 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 diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 573acce..9f7157e 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -2,7 +2,7 @@ Curl and libcurl 7.18.3 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 @@ -11,6 +11,7 @@ Curl and libcurl 7.18.3 This release includes the following changes: o Added CURLINFO_PRIMARY_IP + o Added CURLOPT_CRLFILE This release includes the following bugfixes: @@ -31,6 +32,6 @@ New curl mirrors: 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) diff --git a/TODO-RELEASE b/TODO-RELEASE index 7edbc48..661894c 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -5,9 +5,6 @@ To be addressed before 7.18.3 (planned release: August 2008) 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 - diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index d378edb..ee425a2 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -1452,6 +1452,24 @@ in combination with the \fICURLOPT_SSL_VERIFYPEER\fP option. If 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, diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors.3 index 28e6f82..994489b 100644 --- a/docs/libcurl/libcurl-errors.3 +++ b/docs/libcurl/libcurl-errors.3 @@ -212,6 +212,8 @@ Failed to shut down the SSL connection 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. diff --git a/include/curl/curl.h b/include/curl/curl.h index b42f0b3..a67fd22 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -452,7 +452,10 @@ typedef enum { 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; @@ -1200,6 +1203,9 @@ typedef enum { CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), CINIT(SEEKDATA, OBJECTPOINT, 168), + /* CRL file */ + CINIT(CRLFILE, OBJECTPOINT, 169), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/gtls.c b/lib/gtls.c index 80100b4..e9e4102 100644 --- a/lib/gtls.c +++ b/lib/gtls.c @@ -271,6 +271,21 @@ Curl_gtls_connect(struct connectdata *conn, 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) { diff --git a/lib/nss.c b/lib/nss.c index 20f8770..a5fc795 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -59,6 +59,9 @@ #include #include #include +#include +#include +#include #include "memory.h" #include "easyif.h" /* for Curl_convert_from_utf8 prototype */ @@ -362,6 +365,69 @@ done: 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 @@ -955,6 +1021,17 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) 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; diff --git a/lib/ssluse.c b/lib/ssluse.c index b0d7fd0..f14ad34 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -1293,6 +1293,7 @@ ossl_connect_step1(struct connectdata *conn, 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 @@ -1429,6 +1430,31 @@ ossl_connect_step1(struct connectdata *conn, 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 diff --git a/lib/strerror.c b/lib/strerror.c index fe5a76d..03b0158 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -222,6 +222,9 @@ curl_easy_strerror(CURLcode error) 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"; diff --git a/lib/url.c b/lib/url.c index 9067923..0e71ced 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1811,6 +1811,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, 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 @@ -3951,6 +3959,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.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 1f9d3eb..def598b 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -212,6 +212,7 @@ struct ssl_config_data { 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 */ @@ -1317,6 +1318,7 @@ enum dupstring { STRING_USERAGENT, /* User-Agent string */ STRING_USERPWD, /* , 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 */ -- 2.7.4