Report ERR_SECURE_CONNECT_FAIL details to the user via a new error detail API. Currently, the ERR_SECURE_CONNECT_FAIL response contains no usable error information. Moreover, there is no interface to pass SSL error information to the response generation code. This patch adds an interface to allow Squid error responses to contain detailed information about SSL certificate verification failure. For example, the error message may contain the following text: "Server Certificate Verification Failed: Certificate Common Name (www.lufthansa.com) does not match the host name you are connecting to (www.lufthansa.de)." Change details: -------------------- - errorpage.cc/.h: The error page now supports the '%D' formating code to display the detail string passed by modules. The detail strings passed by modules can contain error page formating codes. Currently only SSL detail errors messages are supported. - A new class Ssl::ErrorDetail defined in ssl/ErrorDetail.[cc,h] The Ssl::ErrorDetail objects passed to the SSL verification callback functions (sl_verify_cb callback function defined in support.cc) and filled with error detail data (error_no and a pointer to the X509 Certificate) in the case of an error and passed back to the forward.cc code. - The Ssl::ErrorDetail class internally uses (hard coded) templates and formating codes to allow supporting multiple languages and adding easily new features Other changes: ------------------- - errorpage.cc/.h: The BuildContent method split to BuildContent and ConvertText method. The second method does the real conversion from a given text template to output. It is used now to allow formating the detail strings passed with %D. - sslparseErrorString moved to ssl/ErrorDetail.cc file and renamed to Ssl::parseErrorString - sslFindErrorString moved to ssl/ErrorDetail.cc file and renamed to Ssl::getErrorName - The ssl_error_t typedef definition moved from ssl/support.h to ssl/ErrorDetail.h and renamed to Ssl::error_t === modified file 'src/acl/SslErrorData.cc' --- src/acl/SslErrorData.cc 2009-05-20 09:28:36 +0000 +++ src/acl/SslErrorData.cc 2010-11-23 10:08:08 +0000 @@ -5,72 +5,72 @@ #include "squid.h" #include "acl/SslErrorData.h" #include "acl/Checklist.h" #include "wordlist.h" ACLSslErrorData::ACLSslErrorData() : values (NULL) {} ACLSslErrorData::ACLSslErrorData(ACLSslErrorData const &old) : values (NULL) { assert (!old.values); } ACLSslErrorData::~ACLSslErrorData() { if (values) delete values; } bool -ACLSslErrorData::match(ssl_error_t toFind) +ACLSslErrorData::match(Ssl::error_t toFind) { return values->findAndTune (toFind); } /* explicit instantiation required for some systems */ /** \cond AUTODOCS-IGNORE */ // AYJ: 2009-05-20 : Removing. clashes with template instantiation for other ACLs. -// template cbdata_type CbDataList::CBDATA_CbDataList; +// template cbdata_type CbDataList::CBDATA_CbDataList; /** \endcond */ wordlist * ACLSslErrorData::dump() { wordlist *W = NULL; - CbDataList *data = values; + CbDataList *data = values; while (data != NULL) { - wordlistAdd(&W, sslFindErrorString(data->element)); + wordlistAdd(&W, Ssl::getErrorName(data->element)); data = data->next; } return W; } void ACLSslErrorData::parse() { - CbDataList **Tail; + CbDataList **Tail; char *t = NULL; for (Tail = &values; *Tail; Tail = &((*Tail)->next)); while ((t = strtokFile())) { - CbDataList *q = new CbDataList(sslParseErrorString(t)); + CbDataList *q = new CbDataList(Ssl::parseErrorString(t)); *(Tail) = q; Tail = &q->next; } } bool ACLSslErrorData::empty() const { return values == NULL; } -ACLData * +ACLData * ACLSslErrorData::clone() const { /* Splay trees don't clone yet. */ assert (!values); return new ACLSslErrorData(*this); } === modified file 'src/acl/SslErrorData.h' --- src/acl/SslErrorData.h 2010-11-18 08:01:53 +0000 +++ src/acl/SslErrorData.h 2010-11-23 10:07:38 +0000 @@ -1,34 +1,35 @@ /* * $Id$ */ #ifndef SQUID_ACLSSL_ERRORDATA_H #define SQUID_ACLSSL_ERRORDATA_H #include "acl/Acl.h" #include "acl/Data.h" #include "CbDataList.h" #include "ssl/support.h" +#include "ssl/ErrorDetail.h" -class ACLSslErrorData : public ACLData +class ACLSslErrorData : public ACLData { public: MEMPROXY_CLASS(ACLSslErrorData); ACLSslErrorData(); ACLSslErrorData(ACLSslErrorData const &); ACLSslErrorData &operator= (ACLSslErrorData const &); virtual ~ACLSslErrorData(); - bool match(ssl_error_t); + bool match(Ssl::error_t); wordlist *dump(); void parse(); bool empty() const; - virtual ACLData *clone() const; + virtual ACLData *clone() const; - CbDataList *values; + CbDataList *values; }; MEMPROXY_CLASS_INLINE(ACLSslErrorData); #endif /* SQUID_ACLSSL_ERRORDATA_H */ === modified file 'src/errorpage.cc' --- src/errorpage.cc 2010-11-01 05:44:28 +0000 +++ src/errorpage.cc 2010-11-29 22:28:57 +0000 @@ -494,40 +494,41 @@ errorStateFree(err); } void errorStateFree(ErrorState * err) { HTTPMSGUNLOCK(err->request); safe_free(err->redirect_url); safe_free(err->url); safe_free(err->request_hdrs); wordlistDestroy(&err->ftp.server_msg); safe_free(err->ftp.request); safe_free(err->ftp.reply); err->auth_user_request = NULL; safe_free(err->err_msg); #if USE_ERR_LOCALES if (err->err_language != Config.errorDefaultLanguage) #endif safe_free(err->err_language); + delete err->detail; cbdataFree(err); } int ErrorState::Dump(MemBuf * mb) { MemBuf str; const char *p = NULL; /* takes priority over mb if set */ char ntoabuf[MAX_IPSTRLEN]; str.reset(); /* email subject line */ str.Printf("CacheErrorInfo - %s", errorPageName(type)); mb->Printf("?subject=%s", rfc1738_escape_part(str.buf)); str.reset(); /* email body */ str.Printf("CacheHost: %s\r\n", getMyHostname()); /* - Err Msgs */ str.Printf("ErrPage: %s\r\n", errorPageName(type)); @@ -583,69 +584,82 @@ /* - FTP stuff */ if (ftp.request) { str.Printf("FTP Request: %s\r\n", ftp.request); str.Printf("FTP Reply: %s\r\n", ftp.reply); str.Printf("FTP Msg: "); wordlistCat(ftp.server_msg, &str); str.Printf("\r\n"); } str.Printf("\r\n"); mb->Printf("&body=%s", rfc1738_escape_part(str.buf)); str.clean(); return 0; } /// \ingroup ErrorPageInternal #define CVT_BUF_SZ 512 const char * -ErrorState::Convert(char token, bool building_deny_info_url) +ErrorState::Convert(char token, bool building_deny_info_url, bool allowRecursion) { static MemBuf mb; const char *p = NULL; /* takes priority over mb if set */ int do_quote = 1; int no_urlescape = 0; /* if true then item is NOT to be further URL-encoded */ char ntoabuf[MAX_IPSTRLEN]; mb.reset(); switch (token) { case 'a': if (request && request->auth_user_request != NULL) p = request->auth_user_request->username(); if (!p) p = "-"; break; case 'B': if (building_deny_info_url) break; p = request ? ftpUrlWith2f(request) : "[no URL]"; break; case 'c': if (building_deny_info_url) break; p = errorPageName(type); break; + case 'D': + if (!allowRecursion) + p = "%D"; // if recursion is not allowed, do not convert + else if (detail) { + const String &errDetail = detail->toString(); + MemBuf *detail_mb = ConvertText(errDetail.termedBuf(), false); + mb.append(detail_mb->content(), detail_mb->contentSize()); + delete detail_mb; + do_quote = 0; + } else + mb.Printf("[No Error Detail]"); + break; + case 'e': mb.Printf("%d", xerrno); break; case 'E': if (xerrno) mb.Printf("(%d) %s", xerrno, strerror(xerrno)); else mb.Printf("[No Error]"); break; case 'f': if (building_deny_info_url) break; /* FTP REQUEST LINE */ if (ftp.request) p = ftp.request; else p = "nothing"; break; @@ -879,41 +893,41 @@ debugs(4, 3, "errorConvert: %%" << token << " --> '" << p << "'" ); if (do_quote) p = html_quote(p); if (building_deny_info_url && !no_urlescape) p = rfc1738_escape_part(p); return p; } void ErrorState::DenyInfoLocation(const char *name, HttpRequest *aRequest, MemBuf &result) { char const *m = name; char const *p = m; char const *t; while ((p = strchr(m, '%'))) { result.append(m, p - m); /* copy */ - t = Convert(*++p, true); /* convert */ + t = Convert(*++p, true, true); /* convert */ result.Printf("%s", t); /* copy */ m = p + 1; /* advance */ } if (*m) result.Printf("%s", m); /* copy tail */ assert((size_t)result.contentSize() == strlen(result.content())); } HttpReply * ErrorState::BuildHttpReply() { HttpReply *rep = new HttpReply; const char *name = errorPageName(page_id); /* no LMT for error pages; error pages expire immediately */ if (strchr(name, ':')) { /* Redirection */ rep->setHeaders(HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, -1); @@ -957,44 +971,41 @@ rep->header.putStr(HDR_CONTENT_LANGUAGE, err_language); } else #endif /* USE_ERROR_LOCALES */ { /* default templates are in English */ /* language is known unless error_directory override used */ if (!Config.errorDirectory) rep->header.putStr(HDR_CONTENT_LANGUAGE, "en"); } httpBodySet(&rep->body, content); /* do not memBufClean() or delete the content, it was absorbed by httpBody */ } return rep; } MemBuf * ErrorState::BuildContent() { - MemBuf *content = new MemBuf; const char *m = NULL; - const char *p; - const char *t; assert(page_id > ERR_NONE && page_id < error_page_count); #if USE_ERR_LOCALES String hdr; char dir[256]; int l = 0; /** error_directory option in squid.conf overrides translations. * Custom errors are always found either in error_directory or the templates directory. * Otherwise locate the Accept-Language header */ if (!Config.errorDirectory && page_id < ERR_MAX && request && request->header.getList(HDR_ACCEPT_LANGUAGE, &hdr) ) { size_t pos = 0; // current parsing position in header string char *reset = NULL; // where to reset the p pointer for each new tag file char *dt = NULL; /* prep the directory path string to prevent snprintf ... */ l = strlen(DEFAULT_SQUID_ERROR_DIR); @@ -1077,37 +1088,45 @@ // IFF we terminated the tag on whitespace or ';' we need to skip to the next ',' or end of header. while (pos < hdr.size() && hdr[pos] != ',') pos++; if (hdr[pos] == ',') pos++; } } #endif /* USE_ERR_LOCALES */ /** \par * If client-specific error templates are not enabled or available. * fall back to the old style squid.conf settings. */ if (!m) { m = error_text[page_id]; #if USE_ERR_LOCALES if (!Config.errorDirectory) err_language = Config.errorDefaultLanguage; #endif debugs(4, 2, HERE << "No existing error page language negotiated for " << errorPageName(page_id) << ". Using default error file."); } + return ConvertText(m, true); +} + +MemBuf *ErrorState::ConvertText(const char *text, bool allowRecursion) +{ + MemBuf *content = new MemBuf; + const char *p; + const char *m = text; assert(m); content->init(); while ((p = strchr(m, '%'))) { content->append(m, p - m); /* copy */ - t = Convert(*++p, false); /* convert */ + const char *t = Convert(*++p, false, allowRecursion); /* convert */ content->Printf("%s", t); /* copy */ m = p + 1; /* advance */ } if (*m) content->Printf("%s", m); /* copy tail */ assert((size_t)content->contentSize() == strlen(content->content())); return content; } === modified file 'src/errorpage.h' --- src/errorpage.h 2010-10-21 08:13:41 +0000 +++ src/errorpage.h 2010-11-29 22:52:15 +0000 @@ -21,51 +21,53 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * * Copyright (c) 2003, Robert Collins */ #ifndef SQUID_ERRORPAGE_H #define SQUID_ERRORPAGE_H #include "squid.h" #include "auth/UserRequest.h" #include "cbdata.h" #include "ip/Address.h" +#include "ssl/ErrorDetail.h" /** \defgroup ErrorPageAPI Error Pages API \ingroup Components \section ErrorPageStringCodes Error Page % codes for text insertion. * \verbatim a - User identity x B - URL with FTP %2f hack x c - Squid error code x d - seconds elapsed since request received x + D - Error details x e - errno x E - strerror() x f - FTP request line x F - FTP reply line x g - FTP server message x h - cache hostname x H - server host name x i - client IP address x I - server IP address x l - HREF link for CSS stylesheet inclusion x L - HREF link for more info/contact x M - Request Method x m - Error message returned by auth helper x o - Message returned external acl helper x p - URL port # x P - Protocol x R - Full HTTP Request x S - squid signature from ERR_SIGNATURE x s - caching proxy software with version x t - local time x @@ -82,55 +84,64 @@ class HttpReply; class MemBuf; /// \ingroup ErrorPageAPI class ErrorState { public: /** * Allocates and initializes an error response */ HttpReply *BuildHttpReply(void); private: /** * Locates error page template to be used for this error * and constructs the HTML page content from it. */ MemBuf *BuildContent(void); /** + * Convert the given template string into textual output + * + * \param text The string to be converted + * \param allowRecursion Whether to convert codes which output may contain codes + */ + MemBuf *ConvertText(const char *text, bool allowRecursion); + + /** * Generates the Location: header value for a deny_info error page * to be used for this error. */ void DenyInfoLocation(const char *name, HttpRequest *request, MemBuf &result); /** * Map the Error page and deny_info template % codes into textual output. * * Several of the codes produce blocks of non-URL compatible results. * When processing the deny_info location URL they will be skipped. * * \param token The token following % which need to be converted * \param building_deny_info_url Perform special deny_info actions, such as URL-encoding and token skipping. + * \ allowRecursion True if the codes which do recursions should converted */ - const char *Convert(char token, bool building_deny_info_url); + const char *Convert(char token, bool building_deny_info_url, bool allowRecursion); /** * CacheManager / Debug dump of the ErrorState object. * Writes output into the given MemBuf. \retval 0 successful completion. */ int Dump(MemBuf * mb); public: err_type type; int page_id; char *err_language; http_status httpStatus; AuthUserRequest::Pointer auth_user_request; HttpRequest *request; char *url; int xerrno; u_short port; String dnsError; ///< DNS lookup error message time_t ttl; @@ -138,40 +149,41 @@ Ip::Address src_addr; char *redirect_url; ERCB *callback; void *callback_data; struct { unsigned int flag_cbdata:1; } flags; struct { wordlist *server_msg; char *request; char *reply; char *cwd_msg; MemBuf *listing; } ftp; char *request_hdrs; char *err_msg; /* Preformatted error message from the cache */ + Ssl::ErrorDetail *detail; private: CBDATA_CLASS2(ErrorState); }; /** \ingroup ErrorPageAPI * * This function finds the error messages formats, and stores * them in error_text[] * \par Global effects: * error_text[] - is modified */ SQUIDCEXTERN void errorInitialize(void); /// \ingroup ErrorPageAPI SQUIDCEXTERN void errorClean(void); /** \ingroup ErrorPageAPI === modified file 'src/forward.cc' --- src/forward.cc 2010-10-28 18:52:59 +0000 +++ src/forward.cc 2010-11-30 10:06:20 +0000 @@ -34,40 +34,41 @@ #include "squid.h" #include "forward.h" #include "acl/FilledChecklist.h" #include "acl/Gadgets.h" #include "CacheManager.h" #include "event.h" #include "errorpage.h" #include "fde.h" #include "hier_code.h" #include "HttpReply.h" #include "HttpRequest.h" #include "ip/QosConfig.h" #include "MemObject.h" #include "pconn.h" #include "SquidTime.h" #include "Store.h" #include "icmp/net_db.h" #include "ip/Intercept.h" #include "ip/tools.h" #include "mgr/Registration.h" +#include "ssl/ErrorDetail.h" static PSC fwdStartCompleteWrapper; static PF fwdServerClosedWrapper; #if USE_SSL static PF fwdNegotiateSSLWrapper; #endif static PF fwdConnectTimeoutWrapper; static EVH fwdConnectStartWrapper; static CNCB fwdConnectDoneWrapper; static OBJH fwdStats; static void fwdServerFree(FwdServer * fs); #define MAX_FWD_STATS_IDX 9 static int FwdReplyCodes[MAX_FWD_STATS_IDX + 1][HTTP_INVALID_HEADER + 1]; #if WIP_FWD_LOG static void fwdLog(FwdState * fwdState); static Logfile *logfile = NULL; #endif @@ -593,40 +594,48 @@ commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0); return; case SSL_ERROR_WANT_WRITE: commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0); return; default: debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd << ": " << ERR_error_string(ERR_get_error(), NULL) << " (" << ssl_error << "/" << ret << "/" << errno << ")"); ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL); #ifdef EPROTO anErr->xerrno = EPROTO; #else anErr->xerrno = EACCES; #endif + Ssl::ErrorDetail *errFromFailure = (Ssl::ErrorDetail *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail); + if (errFromFailure != NULL){ + // The errFromFailure is attached to the ssl object + // and will be released when ssl object destroyed. + // Copy errFromFailure to a new Ssl::ErrorDetail object + anErr->detail = new Ssl::ErrorDetail(*errFromFailure); + } + fail(anErr); if (fs->_peer) { peerConnectFailed(fs->_peer); fs->_peer->stats.conn_open--; } comm_close(fd); return; } } if (fs->_peer && !SSL_session_reused(ssl)) { if (fs->_peer->sslSession) SSL_SESSION_free(fs->_peer->sslSession); fs->_peer->sslSession = SSL_get1_session(ssl); } dispatch(); === modified file 'src/globals.h' --- src/globals.h 2010-10-23 08:50:56 +0000 +++ src/globals.h 2010-11-29 19:11:15 +0000 @@ -149,36 +149,37 @@ extern unsigned int WIN32_Socks_initialized; /* 0 */ #endif #ifdef _SQUID_WIN32_ extern unsigned int WIN32_OS_version; /* 0 */ extern char *WIN32_OS_string; /* NULL */ extern char *WIN32_Service_name; /* NULL */ extern char *WIN32_Command_Line; /* NULL */ extern char *WIN32_Service_Command_Line; /* NULL */ extern unsigned int WIN32_run_mode; /* _WIN_SQUID_RUN_MODE_INTERACTIVE */ #endif #if HAVE_SBRK extern void *sbrk_start; /* 0 */ #endif extern int ssl_ex_index_server; /* -1 */ extern int ssl_ctx_ex_index_dont_verify_domain; /* -1 */ extern int ssl_ex_index_cert_error_check; /* -1 */ + extern int ssl_ex_index_ssl_error_detail; /* -1 */ extern const char *external_acl_message; /* NULL */ extern int opt_send_signal; /* -1 */ extern int opt_no_daemon; /* 0 */ extern int opt_parse_cfg_only; /* 0 */ /// current Squid process number (e.g., 4). /// Zero for SMP-unaware code and in no-SMP mode. extern int KidIdentifier; /* 0 */ #ifdef __cplusplus } #endif #endif /* SQUID_GLOBALS_H */ === added file 'src/ssl/ErrorDetail.cc' --- src/ssl/ErrorDetail.cc 1970-01-01 00:00:00 +0000 +++ src/ssl/ErrorDetail.cc 2010-11-30 09:59:41 +0000 @@ -0,0 +1,258 @@ +#include "squid.h" +#include "ssl/ErrorDetail.h" + +struct SslErrorDetailEntry { + Ssl::error_t value; + const char *name; + const char *detail; +}; + +// TODO: optimize by replacing with std::map or similar +static SslErrorDetailEntry TheSslDetailMap[] = { + { SQUID_X509_V_ERR_DOMAIN_MISMATCH, + "SQUID_X509_V_ERR_DOMAIN_MISMATCH", + "%err_name: The hostname you are connecting to (%H), does not match any of the Certificate valid names: %ssl_cn"}, + { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT, + "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT", + "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name" }, + { X509_V_ERR_CERT_NOT_YET_VALID, + "X509_V_ERR_CERT_NOT_YET_VALID", + "%err_name: SSL Certficate is not valid before: %ssl_notbefore" }, + { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD, + "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD", + "%err_name: SSL Certificate has invalid start date (the 'not before' field): %subject" }, + { X509_V_ERR_CERT_HAS_EXPIRED, + "X509_V_ERR_CERT_HAS_EXPIRED", + "%err_name: SSL Certificate expired on %ssl_notafter" }, + { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD, + "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD", + "%err_name: SSL Certificate has invalid expiration date (the 'not after' field): %ssl_subject" }, + {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, + "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT", + "%err_name: Self-signed SSL Certificate: %ssl_subject"}, + { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY, + "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY", + "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name" }, + { SSL_ERROR_NONE, "SSL_ERROR_NONE", "%err_name: No error" }, + {SSL_ERROR_NONE, NULL, NULL } +}; + +Ssl::error_t +Ssl::parseErrorString(const char *name) +{ + assert(name); + + for (int i = 0; TheSslDetailMap[i].name; ++i) { + if (strcmp(name, TheSslDetailMap[i].name) == 0) + return TheSslDetailMap[i].value; + } + + if (xisdigit(*name)) { + const long int value = strtol(name, NULL, 0); + if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX) + return value; + fatalf("Too small or too bug SSL error code '%s'", name); + } + + fatalf("Unknown SSL error name '%s'", name); + return SSL_ERROR_SSL; // not reached +} + +const char * +Ssl::getErrorName(Ssl::error_t value) +{ + + for (int i = 0; TheSslDetailMap[i].name; ++i) { + if (TheSslDetailMap[i].value == value) + return TheSslDetailMap[i].name; + } + + return NULL; +} + +static const char *getErrorDetail(Ssl::error_t value) +{ + for (int i = 0; TheSslDetailMap[i].name; ++i) { + if (TheSslDetailMap[i].value == value) + return TheSslDetailMap[i].detail; + } + + return NULL; +} + +Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = +{ + {"ssl_subject", &Ssl::ErrorDetail::subject}, + {"ssl_ca_name", &Ssl::ErrorDetail::ca_name}, + {"ssl_cn", &Ssl::ErrorDetail::cn}, + {"ssl_notbefore", &Ssl::ErrorDetail::notbefore}, + {"ssl_notafter", &Ssl::ErrorDetail::notafter}, + {"err_name", &Ssl::ErrorDetail::err_code}, + {NULL,NULL} +}; + +/** + * The subject of the current certification in text form + */ +const char *Ssl::ErrorDetail::subject() const +{ + if (!peer_cert) + return "[Not available]"; + + static char tmpBuffer[256]; // A temporary buffer + X509_NAME_oneline(X509_get_subject_name(peer_cert.get()), tmpBuffer, + sizeof(tmpBuffer)); + return tmpBuffer; +} + +// helper function to be used with Ssl::matchX509CommonNames +static int copy_cn(void *check_data, ASN1_STRING *cn_data) +{ + String *str = (String *)check_data; + if (!str) // no data? abort + return 0; + if (str->defined()) + str->append(", "); + str->append((const char *)cn_data->data, cn_data->length); + return 1; +} + +/** + * The list with certificates cn and alternate names + */ +const char *Ssl::ErrorDetail::cn() const +{ + if (!peer_cert) + return "[Not available]"; + + static String tmpStr; ///< A temporary string buffer + tmpStr.clean(); + Ssl::matchX509CommonNames(peer_cert.get(), &tmpStr, copy_cn); + return tmpStr.termedBuf(); +} + +/** + * The issuer name + */ +const char *Ssl::ErrorDetail::ca_name() const +{ + if (!peer_cert) + return "[Not available]"; + + static char tmpBuffer[256]; // A temporary buffer + X509_NAME_oneline(X509_get_issuer_name(peer_cert.get()), tmpBuffer, sizeof(tmpBuffer)); + return tmpBuffer; +} + +/** + * The certificate "not before" field + */ +const char *Ssl::ErrorDetail::notbefore() const +{ + if (!peer_cert) + return "[Not available]"; + + static char tmpBuffer[256]; // A temporary buffer + ASN1_UTCTIME * tm = X509_get_notBefore(peer_cert.get()); + Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer)); + return tmpBuffer; +} + +/** + * The certificate "not after" field + */ +const char *Ssl::ErrorDetail::notafter() const +{ + if (!peer_cert) + return "[Not available]"; + + static char tmpBuffer[256]; // A temporary buffer + ASN1_UTCTIME * tm = X509_get_notAfter(peer_cert.get()); + Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer)); + return tmpBuffer; +} + +/** + * The string representation of the error_no + */ +const char *Ssl::ErrorDetail::err_code() const +{ + const char *err = getErrorName(error_no); + if (!err) + return "[Not available]"; + return err; +} + +/** + * It converts the code to a string value. Currently the following + * formating codes are supported: + * %err_name: The name of the SSL error + * %ssl_cn: The comma-separated list of common and alternate names + * %ssl_subject: The certificate subject + * %ssl_ca_name: The certificate issuer name + * %ssl_notbefore: The certificate "not before" field + * %ssl_notafter: The certificate "not after" field + \retval the length of the code (the number of characters will be replaced by value) +*/ +int Ssl::ErrorDetail::convert(const char *code, const char **value) const +{ + *value = "-"; + for (int i=0; ErrorFormatingCodes[i].code!=NULL; i++) { + const int len = strlen(ErrorFormatingCodes[i].code); + if (strncmp(code,ErrorFormatingCodes[i].code, len)==0) { + ErrorDetail::fmt_action_t action = ErrorFormatingCodes[i].fmt_action; + *value = (this->*action)(); + return len; + } + } + return 0; +} + +/** + * It uses the convert method to build the string errDetailStr using + * a template message for the current SSL error. The template messages + * can also contain normal error pages formating codes. + * Currently the error template messages are hard-coded + */ +void Ssl::ErrorDetail::buildDetail() const +{ + char const *s = getErrorDetail(error_no); + char const *p; + char const *t; + int code_len = 0; + while ((p = strchr(s, '%'))) { + errDetailStr.append(s, p - s); + code_len = convert(++p, &t); + if (code_len) + errDetailStr.append(t); + else + errDetailStr.append("%"); + s = p + code_len; + } + errDetailStr.append(s, strlen(s)); +} + +const String &Ssl::ErrorDetail::toString() const +{ + if (!errDetailStr.defined()) + buildDetail(); + return errDetailStr; +} + +/* We may do not want to use X509_dup but instead + internal SSL locking: + CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509); + peer_cert.reset(cert); +*/ +Ssl::ErrorDetail::ErrorDetail( error_t err_no, X509 *cert): error_no (err_no) +{ + peer_cert.reset(X509_dup(cert)); +} + +Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail) +{ + error_no = anErrDetail.error_no; + if (anErrDetail.peer_cert.get()) { + peer_cert.reset(X509_dup(anErrDetail.peer_cert.get())); + } +} === added file 'src/ssl/ErrorDetail.h' --- src/ssl/ErrorDetail.h 1970-01-01 00:00:00 +0000 +++ src/ssl/ErrorDetail.h 2010-11-29 22:28:37 +0000 @@ -0,0 +1,74 @@ +#ifndef _SQUID_SSL_ERROR_DETAIL_H +#define _SQUID_SSL_ERROR_DETAIL_H + +#include "err_detail_type.h" +#include "ssl/support.h" +#include "ssl/gadgets.h" + +#if HAVE_OPENSSL_SSL_H +#include +#endif + +// Custom SSL errors; assumes all official errors are positive +#define SQUID_X509_V_ERR_DOMAIN_MISMATCH -1 +// All SSL errors range: from smallest (negative) custom to largest SSL error +#define SQUID_SSL_ERROR_MIN SQUID_X509_V_ERR_DOMAIN_MISMATCH +#define SQUID_SSL_ERROR_MAX INT_MAX + +namespace Ssl +{ + /// Squid defined error code (<0), an error code returned by SSL X509 api, or SSL_ERROR_NONE + typedef int error_t; + +/** + \ingroup ServerProtocolSSLAPI + * The error_t representation of the error described by "name". + */ +error_t parseErrorString(const char *name); + +/** + \ingroup ServerProtocolSSLAPI + * The string representation of the SSL error "value" + */ +const char *getErrorName(error_t value); + +/** + \ingroup ServerProtocolSSLAPI + * Used to pass SSL error details to the error pages returned to the + * end user. + */ +class ErrorDetail { +public: + ErrorDetail(error_t err_no, X509 *cert); + ErrorDetail(ErrorDetail const &); + const String &toString() const; ///< An error detail string to embed in squid error pages + +private: + typedef const char * (ErrorDetail::*fmt_action_t)() const; + /** + * Holds a formating code and its conversion method + */ + class err_frm_code { + public: + const char *code; ///< The formating code + fmt_action_t fmt_action; ///< A pointer to the conversion method + }; + static err_frm_code ErrorFormatingCodes[]; ///< The supported formating codes + + const char *subject() const; + const char *ca_name() const; + const char *cn() const; + const char *notbefore() const; + const char *notafter() const; + const char *err_code() const; + + int convert(const char *code, const char **value) const; + void buildDetail() const; + + mutable String errDetailStr; ///< Caches the error detail message + error_t error_no; ///< The error code + X509_Pointer peer_cert; ///< A pointer to the peer certificate +}; + +}//namespace Ssl +#endif === modified file 'src/ssl/Makefile.am' --- src/ssl/Makefile.am 2010-11-19 01:12:35 +0000 +++ src/ssl/Makefile.am 2010-11-29 23:33:56 +0000 @@ -3,38 +3,40 @@ noinst_LTLIBRARIES = libsslsquid.la libsslutil.la EXTRA_PROGRAMS = \ ssl_crtd if USE_SSL_CRTD SSL_CRTD = ssl_crtd SSL_CRTD_SOURCE = \ helper.cc \ helper.h else SSL_CRTD = SSL_CRTD_SOURCE = endif libsslsquid_la_SOURCES = \ context_storage.cc \ context_storage.h \ Config.cc \ - Config.h + Config.h \ + ErrorDetail.cc \ + ErrorDetail.h \ + support.cc \ + support.h libsslutil_la_SOURCES = \ - support.cc \ - support.h \ gadgets.cc \ gadgets.h \ crtd_message.cc \ crtd_message.h \ $(SSL_CRTD_SOURCE) libexec_PROGRAMS = \ $(SSL_CRTD) if USE_SSL_CRTD ssl_crtd_SOURCES = ssl_crtd.cc certificate_db.cc certificate_db.h ssl_crtd_LDADD = $(SSLLIB) -lsslutil $(COMPAT_LIB) endif === modified file 'src/ssl/support.cc' --- src/ssl/support.cc 2010-11-18 08:16:57 +0000 +++ src/ssl/support.cc 2010-11-30 09:52:15 +0000 @@ -25,40 +25,41 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" /* MS Visual Studio Projects are monolithic, so we need the following * #if to exclude the SSL code from compile process when not needed. */ #if USE_SSL #include "fde.h" #include "acl/FilledChecklist.h" +#include "ssl/ErrorDetail.h" #include "ssl/gadgets.h" /** \defgroup ServerProtocolSSLInternal Server-Side SSL Internals \ingroup ServerProtocolSSLAPI */ /// \ingroup ServerProtocolSSLInternal static int ssl_ask_password_cb(char *buf, int size, int rwflag, void *userdata) { FILE *in; int len = 0; char cmdline[1024]; snprintf(cmdline, sizeof(cmdline), "\"%s\" \"%s\"", Config.Program.ssl_password, (const char *)userdata); in = popen(cmdline, "r"); if (fgets(buf, size, in)) @@ -119,160 +120,192 @@ default: debugs(83, 1, "ssl_temp_rsa_cb: Unexpected key length " << keylen); return NULL; } if (rsa == NULL) { debugs(83, 1, "ssl_temp_rsa_cb: Failed to generate key " << keylen); return NULL; } if (newkey) { if (do_debug(83, 5)) PEM_write_RSAPrivateKey(debug_log, rsa, NULL, NULL, 0, NULL, NULL); debugs(83, 1, "Generated ephemeral RSA key of length " << keylen); } return rsa; } +int Ssl::asn1timeToString(ASN1_TIME *tm, char *buf, int len) +{ + BIO *bio; + int write = 0; + bio = BIO_new(BIO_s_mem()); + if (bio) { + if (ASN1_TIME_print(bio, tm)) + write = BIO_read(bio, buf, len-1); + BIO_free(bio); + } + buf[write]='\0'; + return write; +} + +int Ssl::matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data, ASN1_STRING *cn_data)) +{ + assert(peer_cert); + + X509_NAME *name = X509_get_subject_name(peer_cert); + + for (int i = X509_NAME_get_index_by_NID(name, NID_commonName, -1); i >= 0; i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) { + + ASN1_STRING *cn_data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); + + if ( (*check_func)(check_data, cn_data) == 0) + return 1; + } + + STACK_OF(GENERAL_NAME) * altnames; + altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL); + + if (altnames) { + int numalts = sk_GENERAL_NAME_num(altnames); + for (int i = 0; i < numalts; i++) { + const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); + if (check->type != GEN_DNS) { + continue; + } + ASN1_STRING *cn_data = check->d.dNSName; + + if ( (*check_func)(check_data, cn_data) == 0) + return 1; + } + sk_GENERAL_NAME_pop_free(altnames, GENERAL_NAME_free); + } + return 0; +} + +static int check_domain( void *check_data, ASN1_STRING *cn_data) +{ + char cn[1024]; + const char *server = (const char *)check_data; + + if (cn_data->length > (int)sizeof(cn) - 1) { + return 1; //if does not fit our buffer just ignore + } + memcpy(cn, cn_data->data, cn_data->length); + cn[cn_data->length] = '\0'; + debugs(83, 4, "Verifying server domain " << server << " to certificate name/subjectAltName " << cn); + return matchDomainName(server, cn[0] == '*' ? cn + 1 : cn); +} + /// \ingroup ServerProtocolSSLInternal static int ssl_verify_cb(int ok, X509_STORE_CTX * ctx) { char buffer[256]; SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl); const char *server = (const char *)SSL_get_ex_data(ssl, ssl_ex_index_server); void *dont_verify_domain = SSL_CTX_get_ex_data(sslctx, ssl_ctx_ex_index_dont_verify_domain); ACLChecklist *check = (ACLChecklist*)SSL_get_ex_data(ssl, ssl_ex_index_cert_error_check); X509 *peer_cert = ctx->cert; + Ssl::error_t error_no = SSL_ERROR_NONE; X509_NAME_oneline(X509_get_subject_name(peer_cert), buffer, sizeof(buffer)); if (ok) { debugs(83, 5, "SSL Certificate signature OK: " << buffer); - if (server) { - int i; - int found = 0; - char cn[1024]; - - STACK_OF(GENERAL_NAME) * altnames; - altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL); - if (altnames) { - int numalts = sk_GENERAL_NAME_num(altnames); - debugs(83, 3, "Verifying server domain " << server << " to certificate subjectAltName"); - for (i = 0; i < numalts; i++) { - const GENERAL_NAME *check = sk_GENERAL_NAME_value(altnames, i); - if (check->type != GEN_DNS) { - continue; - } - ASN1_STRING *data = check->d.dNSName; - if (data->length > (int)sizeof(cn) - 1) { - continue; - } - memcpy(cn, data->data, data->length); - cn[data->length] = '\0'; - debugs(83, 4, "Verifying server domain " << server << " to certificate name " << cn); - if (matchDomainName(server, cn[0] == '*' ? cn + 1 : cn) == 0) { - found = 1; - break; - } - } - } - - X509_NAME *name = X509_get_subject_name(peer_cert); - debugs(83, 3, "Verifying server domain " << server << " to certificate dn " << buffer); - - for (i = X509_NAME_get_index_by_NID(name, NID_commonName, -1); i >= 0; i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) { - ASN1_STRING *data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, i)); - - if (data->length > (int)sizeof(cn) - 1) - continue; - - memcpy(cn, data->data, data->length); - - cn[data->length] = '\0'; - - debugs(83, 4, "Verifying server domain " << server << " to certificate cn " << cn); - - if (matchDomainName(server, cn[0] == '*' ? cn + 1 : cn) == 0) { - found = 1; - break; - } - } + if (server) { + int found = Ssl::matchX509CommonNames(peer_cert, (void *)server, check_domain); if (!found) { debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server); ok = 0; + error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH; + if (check) Filled(check)->ssl_error = SQUID_X509_V_ERR_DOMAIN_MISMATCH; } } } else { + error_no = ctx->error; switch (ctx->error) { + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: debugs(83, 5, "SSL Certficate error: CA not known: " << buffer); break; case X509_V_ERR_CERT_NOT_YET_VALID: debugs(83, 5, "SSL Certficate not yet valid: " << buffer); break; case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: debugs(83, 5, "SSL Certificate has illegal \'not before\' field: " << buffer); break; case X509_V_ERR_CERT_HAS_EXPIRED: debugs(83, 5, "SSL Certificate expired: " << buffer); break; case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: debugs(83, 5, "SSL Certificate has invalid \'not after\' field: " << buffer); break; + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + debugs(83, 5, "SSL Certificate is self signed: " << buffer); + break; + default: debugs(83, 1, "SSL unknown certificate error " << ctx->error << " in " << buffer); break; } if (check) Filled(check)->ssl_error = ctx->error; } if (!ok && check) { if (check->fastCheck()) { debugs(83, 3, "bypassing SSL error " << ctx->error << " in " << buffer); ok = 1; } else { debugs(83, 5, "confirming SSL error " << ctx->error); } } if (!dont_verify_domain && server) {} + if (error_no != SSL_ERROR_NONE && !SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_detail) ) { + Ssl::ErrorDetail *errDetail = new Ssl::ErrorDetail(error_no, peer_cert); + if(!SSL_set_ex_data(ssl, ssl_ex_index_ssl_error_detail, errDetail)) { + debugs(83, 2, "Failed to set Ssl::ErrorDetail in ssl_verify_cb: Certificate " << buffer); + delete errDetail; + } + } + return ok; } /// \ingroup ServerProtocolSSLInternal static struct ssl_option { const char *name; long value; } ssl_options[] = { #if SSL_OP_MICROSOFT_SESS_ID_BUG { "MICROSOFT_SESS_ID_BUG", SSL_OP_MICROSOFT_SESS_ID_BUG }, #endif #if SSL_OP_NETSCAPE_CHALLENGE_BUG { "NETSCAPE_CHALLENGE_BUG", SSL_OP_NETSCAPE_CHALLENGE_BUG }, @@ -508,146 +541,107 @@ #if X509_V_FLAG_CRL_CHECK else if (strcmp(flag, "VERIFY_CRL") == 0) fl |= SSL_FLAG_VERIFY_CRL; else if (strcmp(flag, "VERIFY_CRL_ALL") == 0) fl |= SSL_FLAG_VERIFY_CRL_ALL; #endif else fatalf("Unknown ssl flag '%s'", flag); flag = strtok(NULL, ":,"); } safe_free(tmp); return fl; } -struct SslErrorMapEntry { - const char *name; - ssl_error_t value; -}; - -static SslErrorMapEntry TheSslErrorMap[] = { - { "SQUID_X509_V_ERR_DOMAIN_MISMATCH", SQUID_X509_V_ERR_DOMAIN_MISMATCH }, - { "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT", X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT }, - { "X509_V_ERR_CERT_NOT_YET_VALID", X509_V_ERR_CERT_NOT_YET_VALID }, - { "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD", X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD }, - { "X509_V_ERR_CERT_HAS_EXPIRED", X509_V_ERR_CERT_HAS_EXPIRED }, - { "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD", X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD }, - { "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY", X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY }, - { "SSL_ERROR_NONE", SSL_ERROR_NONE }, - { NULL, SSL_ERROR_NONE } -}; - -ssl_error_t -sslParseErrorString(const char *name) -{ - assert(name); - - for (int i = 0; TheSslErrorMap[i].name; ++i) { - if (strcmp(name, TheSslErrorMap[i].name) == 0) - return TheSslErrorMap[i].value; - } - - if (xisdigit(*name)) { - const long int value = strtol(name, NULL, 0); - if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX) - return value; - fatalf("Too small or too bug SSL error code '%s'", name); - } - - fatalf("Unknown SSL error name '%s'", name); - return SSL_ERROR_SSL; // not reached -} - -const char * -sslFindErrorString(ssl_error_t value) -{ - for (int i = 0; TheSslErrorMap[i].name; ++i) { - if (TheSslErrorMap[i].value == value) - return TheSslErrorMap[i].name; - } - - return NULL; -} - // "dup" function for SSL_get_ex_new_index("cert_err_check") static int ssl_dupAclChecklist(CRYPTO_EX_DATA *, CRYPTO_EX_DATA *, void *, int, long, void *) { // We do not support duplication of ACLCheckLists. // If duplication is needed, we can count copies with cbdata. assert(false); return 0; } // "free" function for SSL_get_ex_new_index("cert_err_check") static void ssl_freeAclChecklist(void *, void *ptr, CRYPTO_EX_DATA *, int, long, void *) { delete static_cast(ptr); // may be NULL } +// "free" function for SSL_get_ex_new_index("ssl_error_detail") +static void +ssl_free_ErrorDetail(void *, void *ptr, CRYPTO_EX_DATA *, + int, long, void *) +{ + Ssl::ErrorDetail *errDetail = static_cast (ptr); + delete errDetail; +} + /// \ingroup ServerProtocolSSLInternal static void ssl_initialize(void) { static int ssl_initialized = 0; if (!ssl_initialized) { ssl_initialized = 1; SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); #if HAVE_OPENSSL_ENGINE_H if (Config.SSL.ssl_engine) { ENGINE *e; if (!(e = ENGINE_by_id(Config.SSL.ssl_engine))) { fatalf("Unable to find SSL engine '%s'\n", Config.SSL.ssl_engine); } if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) { int ssl_error = ERR_get_error(); fatalf("Failed to initialise SSL engine: %s\n", ERR_error_string(ssl_error, NULL)); } } #else if (Config.SSL.ssl_engine) { fatalf("Your OpenSSL has no SSL engine support\n"); } #endif } ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, NULL); ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL); ssl_ex_index_cert_error_check = SSL_get_ex_new_index(0, (void *) "cert_error_check", NULL, &ssl_dupAclChecklist, &ssl_freeAclChecklist); + ssl_ex_index_ssl_error_detail = SSL_get_ex_new_index(0, (void *) "ssl_error_detail", NULL, NULL, &ssl_free_ErrorDetail); } /// \ingroup ServerProtocolSSLInternal static int ssl_load_crl(SSL_CTX *sslContext, const char *CRLfile) { X509_STORE *st = SSL_CTX_get_cert_store(sslContext); X509_CRL *crl; BIO *in = BIO_new_file(CRLfile, "r"); int count = 0; if (!in) { debugs(83, 2, "WARNING: Failed to open CRL file '" << CRLfile << "'"); return 0; } while ((crl = PEM_read_bio_X509_CRL(in,NULL,NULL,NULL))) { if (!X509_STORE_add_crl(st, crl)) debugs(83, 2, "WARNING: Failed to add CRL from file '" << CRLfile << "'"); else === modified file 'src/ssl/support.h' --- src/ssl/support.h 2010-11-21 04:40:05 +0000 +++ src/ssl/support.h 2010-11-29 23:33:56 +0000 @@ -72,73 +72,84 @@ /// \ingroup ServerProtocolSSLAPI const char *sslGetUserEmail(SSL *ssl); /// \ingroup ServerProtocolSSLAPI typedef char const *SSLGETATTRIBUTE(SSL *, const char *); /// \ingroup ServerProtocolSSLAPI SSLGETATTRIBUTE sslGetUserAttribute; /// \ingroup ServerProtocolSSLAPI SSLGETATTRIBUTE sslGetCAAttribute; /// \ingroup ServerProtocolSSLAPI const char *sslGetUserCertificatePEM(SSL *ssl); /// \ingroup ServerProtocolSSLAPI const char *sslGetUserCertificateChainPEM(SSL *ssl); -typedef int ssl_error_t; -ssl_error_t sslParseErrorString(const char *name); -const char *sslFindErrorString(ssl_error_t value); - namespace Ssl { /** \ingroup ServerProtocolSSLAPI * Decide on the kind of certificate and generate a CA- or self-signed one */ SSL_CTX *generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey); /** \ingroup ServerProtocolSSLAPI * Check date of certificate signature. If there is out of date error fucntion * returns false, true otherwise. */ bool verifySslCertificateDate(SSL_CTX * sslContext); /** \ingroup ServerProtocolSSLAPI * Read private key and certificate from memory and generate SSL context * using their. */ SSL_CTX * generateSslContextUsingPkeyAndCertFromMemory(const char * data); -} //namespace Ssl +/** + \ingroup ServerProtocolSSLAPI + * Iterates over the X509 common and alternate names and to see if matches with given data + * using the check_func. + \param peer_cert The X509 cert to check + \param check_data The data with which the X509 CNs compared + \param check_func The function used to match X509 CNs. The CN data passed as ASN1_STRING data + \return 1 if any of the certificate CN matches, 0 if none matches. + */ +int matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data, ASN1_STRING *cn_data)); -// Custom SSL errors; assumes all official errors are positive -#define SQUID_X509_V_ERR_DOMAIN_MISMATCH -1 -// All SSL errors range: from smallest (negative) custom to largest SSL error -#define SQUID_SSL_ERROR_MIN SQUID_X509_V_ERR_DOMAIN_MISMATCH -#define SQUID_SSL_ERROR_MAX INT_MAX +/** + \ingroup ServerProtocolSSLAPI + * Convert a given ASN1_TIME to a string form. + \param tm the time in ASN1_TIME form + \param buf the buffer to write the output + \param len write at most len bytes + \return The number of bytes written + */ +int asn1timeToString(ASN1_TIME *tm, char *buf, int len); + +} //namespace Ssl #ifdef _SQUID_MSWIN_ #ifdef __cplusplus /** \cond AUTODOCS-IGNORE */ namespace Squid { /** \endcond */ /// \ingroup ServerProtocolSSLAPI inline int SSL_set_fd(SSL *ssl, int fd) { return ::SSL_set_fd(ssl, _get_osfhandle(fd)); } /// \ingroup ServerProtocolSSLAPI #define SSL_set_fd(ssl,fd) Squid::SSL_set_fd(ssl,fd)