}
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 <int> instantiation for other ACLs.
-// template cbdata_type CbDataList<ssl_error_t>::CBDATA_CbDataList;
+// template cbdata_type CbDataList<Ssl::error_t>::CBDATA_CbDataList;
/** \endcond */
wordlist *
ACLSslErrorData::dump()
{
wordlist *W = NULL;
- CbDataList<ssl_error_t> *data = values;
+ CbDataList<Ssl::error_t> *data = values;
while (data != NULL) {
- wordlistAdd(&W, sslFindErrorString(data->element));
+ wordlistAdd(&W, Ssl::getErrorName(data->element));
data = data->next;
}
void
ACLSslErrorData::parse()
{
- CbDataList<ssl_error_t> **Tail;
+ CbDataList<Ssl::error_t> **Tail;
char *t = NULL;
for (Tail = &values; *Tail; Tail = &((*Tail)->next));
while ((t = strtokFile())) {
- CbDataList<ssl_error_t> *q = new CbDataList<ssl_error_t>(sslParseErrorString(t));
+ CbDataList<Ssl::error_t> *q = new CbDataList<Ssl::error_t>(Ssl::parseErrorString(t));
*(Tail) = q;
Tail = &q->next;
}
return values == NULL;
}
-ACLData<ssl_error_t> *
+ACLData<Ssl::error_t> *
ACLSslErrorData::clone() const
{
/* Splay trees don't clone yet. */
#include "acl/Data.h"
#include "CbDataList.h"
#include "ssl/support.h"
+#include "ssl/ErrorDetail.h"
-class ACLSslErrorData : public ACLData<ssl_error_t>
+class ACLSslErrorData : public ACLData<Ssl::error_t>
{
public:
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<ssl_error_t> *clone() const;
+ virtual ACLData<Ssl::error_t> *clone() const;
- CbDataList<ssl_error_t> *values;
+ CbDataList<Ssl::error_t> *values;
};
MEMPROXY_CLASS_INLINE(ACLSslErrorData);
if (err->err_language != Config.errorDefaultLanguage)
#endif
safe_free(err->err_language);
+#if USE_SSL
+ delete err->detail;
+#endif
cbdataFree(err);
}
#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 */
p = errorPageName(type);
break;
+ case 'D':
+ if (!allowRecursion)
+ p = "%D"; // if recursion is not allowed, do not convert
+#if USE_SSL
+ // currently only SSL error details implemented
+ 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
+#endif
+ mb.Printf("[No Error Detail]");
+ break;
+
case 'e':
mb.Printf("%d", xerrno);
break;
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 */
}
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);
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 */
}
#include "auth/UserRequest.h"
#include "cbdata.h"
#include "ip/Address.h"
+#if USE_SSL
+#include "ssl/ErrorDetail.h"
+#endif
/**
\defgroup ErrorPageAPI Error Pages API
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
*/
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.
*
* \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.
char *request_hdrs;
char *err_msg; /* Preformatted error message from the cache */
+#if USE_SSL
+ Ssl::ErrorDetail *detail;
+#endif
private:
CBDATA_CLASS2(ErrorState);
};
#include "mgr/Registration.h"
#if USE_SSL
#include "ssl/support.h"
+#include "ssl/ErrorDetail.h"
#endif
static PSC fwdStartCompleteWrapper;
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) {
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 */
--- /dev/null
+#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;
+
+ if (!s) //May be add a default detail string?
+ return;
+
+ 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()));
+ }
+}
--- /dev/null
+#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 <openssl/ssl.h>
+#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
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 \
#include "fde.h"
#include "acl/FilledChecklist.h"
+#include "ssl/ErrorDetail.h"
#include "ssl/support.h"
#include "ssl/gadgets.h"
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)
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;
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 (!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;
}
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 *,
delete static_cast<ACLChecklist *>(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 <Ssl::ErrorDetail *>(ptr);
+ delete errDetail;
+}
+
/// \ingroup ServerProtocolSSLInternal
static void
ssl_initialize(void)
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
/// \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
{
/**
*/
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_