#if USE_SSL
ACL::Prototype ACLSslError::RegistryProtoype(&ACLSslError::RegistryEntry_, "ssl_error");
-ACLStrategised<int> ACLSslError::RegistryEntry_(new ACLSslErrorData, ACLSslErrorStrategy::Instance(), "ssl_error");
+ACLStrategised<Ssl::Errors const &> ACLSslError::RegistryEntry_(new ACLSslErrorData, ACLSslErrorStrategy::Instance(), "ssl_error");
ACL::Prototype ACLCertificate::UserRegistryProtoype(&ACLCertificate::UserRegistryEntry_, "user_cert");
ACLStrategised<SSL *> ACLCertificate::UserRegistryEntry_(new ACLCertificateData (sslGetUserAttribute), ACLCertificateStrategy::Instance(), "user_cert");
ACL::Prototype ACLCertificate::CARegistryProtoype(&ACLCertificate::CARegistryEntry_, "ca_cert");
#endif
#if SQUID_SNMP
snmp_community(NULL),
-#endif
-#if USE_SSL
- ssl_error(0),
#endif
extacl_entry (NULL),
conn_(NULL),
#endif
#if SQUID_SNMP
snmp_community(NULL),
-#endif
-#if USE_SSL
- ssl_error(0),
#endif
extacl_entry (NULL),
conn_(NULL),
#if USE_AUTH
#include "auth/UserRequest.h"
#endif
+#if USE_SSL
+#include "ssl/support.h"
+#endif
class ExternalACLEntry;
class ConnStateData;
#endif
#if USE_SSL
- int ssl_error;
+ Ssl::Errors sslErrorList;
#endif
ExternalACLEntry *extacl_entry;
int
ACLSslErrorStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
{
- return data->match (checklist->ssl_error);
+ return data->match (checklist->sslErrorList);
}
ACLSslErrorStrategy *
#define SQUID_ACLSSL_ERROR_H
#include "acl/Strategy.h"
#include "acl/Strategised.h"
+#include "ssl/support.h"
-class ACLSslErrorStrategy : public ACLStrategy<int>
+class ACLSslErrorStrategy : public ACLStrategy<Ssl::Errors const&>
{
public:
private:
static ACL::Prototype RegistryProtoype;
- static ACLStrategised<int> RegistryEntry_;
+ static ACLStrategised<Ssl::Errors const&> RegistryEntry_;
};
#endif /* SQUID_ACLSSL_ERROR_H */
}
bool
-ACLSslErrorData::match(Ssl::ssl_error_t toFind)
+ACLSslErrorData::match(Ssl::Errors const &toFind)
{
- return values->findAndTune (toFind);
+ typedef std::vector<Ssl::ssl_error_t>::const_iterator SEsI;
+ for (SEsI it = toFind.begin() ; it != toFind.end(); it++ ) {
+ if (values->findAndTune (*it))
+ return true;
+ }
+ return false;
}
/* explicit instantiation required for some systems */
return values == NULL;
}
-ACLData<Ssl::ssl_error_t> *
+ACLSslErrorData *
ACLSslErrorData::clone() const
{
/* Splay trees don't clone yet. */
#include "CbDataList.h"
#include "ssl/support.h"
#include "ssl/ErrorDetail.h"
+#include <vector>
-class ACLSslErrorData : public ACLData<Ssl::ssl_error_t>
+class ACLSslErrorData : public ACLData<Ssl::Errors const&>
{
public:
ACLSslErrorData(ACLSslErrorData const &);
ACLSslErrorData &operator= (ACLSslErrorData const &);
virtual ~ACLSslErrorData();
- bool match(Ssl::ssl_error_t);
+ bool match(Ssl::Errors const &);
wordlist *dump();
void parse();
bool empty() const;
- virtual ACLData<Ssl::ssl_error_t> *clone() const;
+ virtual ACLSslErrorData *clone() const;
CbDataList<Ssl::ssl_error_t> *values;
};
static void parse_https_port_list(https_port_list **);
static void dump_https_port_list(StoreEntry *, const char *, const https_port_list *);
static void free_https_port_list(https_port_list **);
+static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
+static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt);
+static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt);
#if 0
static int check_null_https_port_list(const https_port_list *);
#endif
cfg->service_failure_limit = 0;
}
+#if USE_SSL
+static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
+{
+ char *al;
+ sslproxy_cert_adapt *ca = (sslproxy_cert_adapt *) xcalloc(1, sizeof(sslproxy_cert_adapt));
+ if ((al = strtok(NULL, w_space)) == NULL) {
+ self_destruct();
+ return;
+ }
+
+ const char *param;
+ if ( char *s = strchr(al, '{')) {
+ *s = '\0'; // terminate the al string
+ s++;
+ param = s;
+ s = strchr(s, '}');
+ if (!s) {
+ self_destruct();
+ return;
+ }
+ *s = '\0';
+ }
+ else
+ param = NULL;
+
+ if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidAfter]) == 0) {
+ ca->alg = Ssl::algSetValidAfter;
+ ca->param = strdup("on");
+ }
+ else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetValidBefore]) == 0) {
+ ca->alg = Ssl::algSetValidBefore;
+ ca->param = strdup("on");
+ }
+ else if (strcmp(al, Ssl::CertAdaptAlgorithmStr[Ssl::algSetCommonName]) == 0) {
+ ca->alg = Ssl::algSetCommonName;
+ if (param) {
+ ca->param = strdup(param);
+ }
+ } else {
+ debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_adapt: unknown cert adaptation algorithm: " << al);
+ self_destruct();
+ return;
+ }
+
+ aclParseAclList(LegacyParser, &ca->aclList);
+
+ while(*cert_adapt)
+ cert_adapt = &(*cert_adapt)->next;
+
+ *cert_adapt = ca;
+}
+
+static void dump_sslproxy_cert_adapt(StoreEntry *entry, const char *name, sslproxy_cert_adapt *cert_adapt)
+{
+ for (sslproxy_cert_adapt *ca = cert_adapt; ca != NULL; ca = ca->next) {
+ storeAppendPrintf(entry, "%s ", name);
+ storeAppendPrintf(entry, "%s{%s} ", Ssl::sslCertAdaptAlgoritm(ca->alg), ca->param);
+ if (ca->aclList)
+ dump_acl_list(entry, ca->aclList);
+ storeAppendPrintf(entry, "\n");
+ }
+}
+
+static void free_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
+{
+ while(*cert_adapt) {
+ sslproxy_cert_adapt *ca = *cert_adapt;
+ *cert_adapt = ca->next;
+ safe_free(ca->param);
+
+ if (ca->aclList)
+ aclDestroyAclList(&ca->aclList);
+
+ safe_free(ca);
+ }
+}
+#endif
+
#endif
wccp2_service
wccp2_service_info
wordlist
+sslproxy_cert_adapt acl
Default setting: sslproxy_cert_error deny all
DOC_END
+NAME: sslproxy_cert_adapt
+IFDEF: USE_SSL
+DEFAULT: none
+TYPE: sslproxy_cert_adapt
+LOC: Config.ssl_client.cert_adapt
+DOC_START
+
+ sslproxy_cert_adapt <adaptation algorithm> acl ...
+
+ The following certificate adaptation algorithms supported:
+ setValidAfter
+ sets the "Not After" property to the "Not After" propery of
+ the ca certificate used to sign generated certificates
+ setValidBefore
+ sets the "Not Before" property to the "Not Before" property of
+ the ca certificate used to sign generated certificates
+ setCommonName
+ sets certificate Subject.CN property to the
+ host name from specified as a CN parameter (setCommonName{CN})
+ or, if no explicit CN parameter was specified, extracted from
+ the CONNECT request. It is a misconfiguration to use setName
+ without an explicit parameter for intercepted or tproxied SSL
+ transactions.
+
+ When the acl(s) match, the corresponding adaptation algorithm is
+ applied to the fake/generated certificate. Otherwise, the
+ default mimicking action takes place.
+DOC_END
+
NAME: sslpassword_program
IFDEF: USE_SSL
DEFAULT: none
} else {
Ssl::CrtdMessage reply_message;
if (reply_message.parse(reply, strlen(reply)) != Ssl::CrtdMessage::OK) {
- debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslHostName << " is incorrect");
+ debugs(33, 5, HERE << "Reply from ssl_crtd for " << sslConnectHostOrIp << " is incorrect");
} else {
if (reply_message.getCode() != "OK") {
- debugs(33, 5, HERE << "Certificate for " << sslHostName << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
+ debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " cannot be generated. ssl_crtd response: " << reply_message.getBody());
} else {
- debugs(33, 5, HERE << "Certificate for " << sslHostName << " was successfully recieved from ssl_crtd");
+ debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str());
getSslContextDone(ctx, true);
return;
getSslContextDone(NULL);
}
+void ConnStateData::buildSslCertAdaptParams(Ssl::CrtdMessage::BodyParams &certAdaptParams)
+{
+ // Build a key to use for storing/retrieving certificates to cache
+ sslBumpCertKey = sslCommonName.defined() ? sslCommonName : sslConnectHostOrIp;
+
+ // fake certificate adaptation requires bump-server-first mode
+ if (!bumpServerFirstErrorEntry())
+ return;
+
+ HttpRequest *fakeRequest = new HttpRequest();
+ fakeRequest->SetHost(sslConnectHostOrIp.termedBuf());
+ fakeRequest->port = clientConnection->local.GetPort();
+ fakeRequest->protocol = AnyP::PROTO_HTTPS;
+
+ ACLFilledChecklist checklist(NULL, fakeRequest,
+ clientConnection != NULL ? clientConnection->rfc931 : dash_str);
+ checklist.conn(this);
+ checklist.src_addr = clientConnection->remote;
+ checklist.my_addr = clientConnection->local;
+ checklist.sslErrorList = bumpSslErrorNoList;
+
+ for (sslproxy_cert_adapt *ca = Config.ssl_client.cert_adapt; ca != NULL; ca = ca->next) {
+ if (ca->aclList && checklist.fastCheck(ca->aclList) == ACCESS_ALLOWED) {
+ const char *alg = Ssl::CertAdaptAlgorithmStr[ca->alg];
+ const char *param = ca->param;
+
+ // if already set ignore
+ if (certAdaptParams.find(alg) != certAdaptParams.end())
+ continue;
+
+ // if not param defined for Common Name adaptation use hostname from
+ // the CONNECT request
+ if (!param && ca->alg == Ssl::algSetCommonName)
+ param = sslConnectHostOrIp.termedBuf();
+
+ assert(alg && param);
+ debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
+ alg << " param: " << param);
+
+ // append to the certificate adaptation parameters
+ certAdaptParams.insert( std::make_pair(alg, param));
+
+ // And also build the key used to store certificate to cache.
+ sslBumpCertKey.append("+");
+ sslBumpCertKey.append(alg);
+ sslBumpCertKey.append("=");
+ sslBumpCertKey.append(param);
+ }
+ }
+}
+
void
ConnStateData::getSslContextStart()
{
- char const * host = sslHostName.termedBuf();
- if (port->generateHostCertificates && host && strcmp(host, "") != 0) {
- debugs(33, 5, HERE << "Finding SSL certificate for " << host << " in cache");
+ if (port->generateHostCertificates) {
+ Ssl::CrtdMessage::BodyParams certAdaptParams;
+ buildSslCertAdaptParams(certAdaptParams);
+ assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
+
+ debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache");
Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
- SSL_CTX * dynCtx = ssl_ctx_cache.find(host);
+ SSL_CTX * dynCtx = ssl_ctx_cache.find(sslBumpCertKey.termedBuf());
if (dynCtx) {
- debugs(33, 5, HERE << "SSL certificate for " << host << " have found in cache");
+ debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " have found in cache");
if (Ssl::verifySslCertificate(dynCtx, bumpServerCert.get())) {
- debugs(33, 5, HERE << "Cached SSL certificate for " << host << " is valid");
+ debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is valid");
getSslContextDone(dynCtx);
return;
} else {
- debugs(33, 5, HERE << "Cached SSL certificate for " << host << " is out of date. Delete this certificate from cache");
- ssl_ctx_cache.remove(host);
+ debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
+ ssl_ctx_cache.remove(sslBumpCertKey.termedBuf());
}
} else {
- debugs(33, 5, HERE << "SSL certificate for " << host << " haven't found in cache");
+ debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
}
+ char const * host = sslCommonName.defined() ? sslCommonName.termedBuf() : sslConnectHostOrIp.termedBuf();
#if USE_SSL_CRTD
debugs(33, 5, HERE << "Generating SSL certificate for " << host << " using ssl_crtd.");
Ssl::CrtdMessage request_message;
request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
Ssl::CrtdMessage::BodyParams map;
map.insert(std::make_pair(Ssl::CrtdMessage::param_host, host));
+ /*Append parameters for cert adaptation*/
+ map.insert(certAdaptParams.begin(), certAdaptParams.end());
std::string bufferToWrite;
Ssl::writeCertAndPrivateKeyToMemory(port->signingCert, port->signPkey, bufferToWrite);
if (bumpServerCert.get()) {
debugs(33, 5, HERE << "Append Mimic Certificate to body request: " << bufferToWrite);
}
request_message.composeBody(map, bufferToWrite);
+ debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
return;
#else
debugs(33, 5, HERE << "Generating SSL certificate for " << host);
- dynCtx = Ssl::generateSslContext(host, bumpServerCert, port->signingCert, port->signPkey);
+ dynCtx = Ssl::generateSslContext(host, bumpServerCert, port->signingCert, port->signPkey, certAdaptParams);
getSslContextDone(dynCtx, true);
return;
#endif //USE_SSL_CRTD
Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
- if (sslContext && sslHostName != "") {
- if (!ssl_ctx_cache.add(sslHostName.termedBuf(), sslContext)) {
+ assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
+ if (sslContext) {
+ if (!ssl_ctx_cache.add(sslBumpCertKey.termedBuf(), sslContext)) {
// If it is not in storage delete after using. Else storage deleted it.
fd_table[clientConnection->fd].dynamicSslContext = sslContext;
}
} else {
- debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslHostName);
+ debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslConnectHostOrIp);
}
}
{
assert(!switchedToHttps_);
- sslHostName = host;
+ sslConnectHostOrIp = host;
+ sslCommonName = host;
//HTTPMSGLOCK(currentobject->http->request);
assert(areAllContextsForThisConnection());
const bool alwaysBumpServerFirst = true;
if (alwaysBumpServerFirst) {
Must(!httpsPeeker.set());
- httpsPeeker = new Ssl::ServerPeeker(this, sslHostName.termedBuf(), port);
+ httpsPeeker = new Ssl::ServerPeeker(this, sslConnectHostOrIp.termedBuf(), port);
bumpErrorEntry = httpsPeeker->storeEntry();
Must(bumpErrorEntry);
bumpErrorEntry->lock();
return;
}
- // otherwise, use sslHostName
+ // otherwise, use sslConnectHostOrIp
getSslContextStart();
}
assert(ssl);
Ssl::X509_Pointer serverCert(SSL_get_peer_certificate(ssl));
assert(serverCert.get() != NULL);
- sslHostName = Ssl::CommonHostName(serverCert.get());
- assert(sslHostName.defined());
- debugs(33, 5, HERE << "found HTTPS server " << sslHostName << " at bumped " <<
+ sslCommonName = Ssl::CommonHostName(serverCert.get());
+ assert(sslCommonName.defined());
+ debugs(33, 5, HERE << "found HTTPS server CN " << sslCommonName << " at bumped " <<
*serverConnection);
pinConnection(serverConnection, NULL, NULL, false);
- debugs(33, 5, HERE << "bumped HTTPS server: " << sslHostName);
+ debugs(33, 5, HERE << "bumped HTTPS server: " << sslConnectHostOrIp);
} else
- debugs(33, 5, HERE << "Error while bumped HTTPS server: " << sslHostName);
+ debugs(33, 5, HERE << "Error while bumped HTTPS server: " << sslConnectHostOrIp);
if (httpsPeeker.valid())
httpsPeeker->noteHttpsPeeked(serverConnection);
StoreEntry *bumpServerFirstErrorEntry() const {return bumpErrorEntry;}
void setBumpServerCert(X509 *serverCert) {bumpServerCert.reset(serverCert);}
X509 *getBumpServerCert() {return bumpServerCert.get();}
+ void setBumpSslErrorList(Ssl::Errors &errNoList) {bumpSslErrorNoList = errNoList;}
+ /// Fill the certAdaptParams with the required data for certificate adaptation
+ /// and create the key for storing/retrieve the certificate to/from the cache
+ void buildSslCertAdaptParams(Ssl::CrtdMessage::BodyParams &certAdaptParams);
bool serveDelayedError(ClientSocketContext *context);
#else
bool switchedToHttps() const { return false; }
#if USE_SSL
bool switchedToHttps_;
- String sslHostName; ///< Host name for SSL certificate generation
+ /// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
+ String sslConnectHostOrIp; ///< The SSL server host name as passed in the CONNECT request
+ String sslCommonName; ///< CN name for SSL certificate generation
+ String sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
/// a job that connects to the HTTPS server to get its SSL certificate
CbcPointer<Ssl::ServerPeeker> httpsPeeker;
StoreEntry *bumpErrorEntry;
Ssl::X509_Pointer bumpServerCert;
+ Ssl::Errors bumpSslErrorNoList; ///< The list of SSL certificate errors which ignored
#endif
AsyncCall::Pointer reader; ///< set when we are reading
// Get the server certificate from ErrorDetail object and store it
// to connection manager
request->clientConnectionManager->setBumpServerCert(X509_dup(srvX509));
+
+ // if there is a list of ssl errors, pass it to connection manager
+ if (Ssl::Errors *errNoList = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_sslerrno)))
+ request->clientConnectionManager->setBumpSslErrorList(*errNoList);
}
HttpRequest *fakeRequest = NULL;
}
}
- if (request->clientConnectionManager.valid())
+ if (request->clientConnectionManager.valid()) {
request->clientConnectionManager->setBumpServerCert(SSL_get_peer_certificate(ssl));
+ if (Ssl::Errors *errNoList = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_sslerrno)))
+ request->clientConnectionManager->setBumpSslErrorList(*errNoList);
+ }
if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
if (serverConnection()->getPeer()->sslSession)
extern int ssl_ex_index_cert_error_check; /* -1 */
extern int ssl_ex_index_ssl_error_detail; /* -1 */
extern int ssl_ex_index_ssl_peeked_cert; /* -1 */
+ extern int ssl_ex_index_ssl_error_sslerrno; /* -1 */
extern const char *external_acl_message; /* NULL */
extern int opt_send_signal; /* -1 */
return pure_find(host_name, cert, pkey);
}
-bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey)
+bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, std::string const & useName)
{
const Locker locker(dbLock, Here);
load();
{
TidyPointer<char, tidyFree> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0));
- if (pure_find(subject.get(), cert, pkey))
+ if (pure_find(useName.empty() ? subject.get() : useName, cert, pkey))
return true;
}
// check db size.
ASN1_UTCTIME * tm = X509_get_notAfter(cert.get());
row.setValue(cnlExp_date, std::string(reinterpret_cast<char *>(tm->data), tm->length).c_str());
row.setValue(cnlFile, "unknown");
- {
+ if (!useName.empty())
+ row.setValue(cnlName, useName.c_str());
+ else {
TidyPointer<char, tidyFree> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0));
row.setValue(cnlName, subject.get());
}
/// Find certificate and private key for host name
bool find(std::string const & host_name, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
/// Save certificate to disk.
- bool addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey);
+ bool addCertAndPrivateKey(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, std::string const & useName);
/// Get a serial number to use for generating a new certificate.
BIGNUM * getCurrentSerialNumber();
/// Create and initialize a database under the db_path
*/
#include "config.h"
+#include "ssl/gadgets.h"
#include "ssl/crtd_message.h"
#if HAVE_CSTDLIB
#include <cstdlib>
const std::string Ssl::CrtdMessage::code_new_certificate("new_certificate");
const std::string Ssl::CrtdMessage::param_host("host");
+const std::string Ssl::CrtdMessage::param_SetValidAfter(Ssl::CertAdaptAlgorithmStr[algSetValidAfter]);
+const std::string Ssl::CrtdMessage::param_SetValidBefore(Ssl::CertAdaptAlgorithmStr[algSetValidBefore]);
+const std::string Ssl::CrtdMessage::param_SetCommonName(Ssl::CertAdaptAlgorithmStr[algSetCommonName]);
static const std::string code_new_certificate;
/// Parameter name for passing hostname
static const std::string param_host;
+ /// Parameter name for passing SetValidAfter cert adaptation variable
+ static const std::string param_SetValidAfter;
+ /// Parameter name for passing SetValidBefore cert adaptation variable
+ static const std::string param_SetValidBefore;
+ /// Parameter name for passing SetCommonName cert adaptation variable
+ static const std::string param_SetCommonName;
private:
enum ParseState {
BEFORE_CODE,
return true;
}
-static bool mimicCertificate(Ssl::X509_Pointer & cert, Ssl::X509_Pointer const & caCert, Ssl::X509_Pointer const &certToMimic)
+// Replace certs common name with the given
+static bool replaceCommonName(Ssl::X509_Pointer & cert, const char *cn)
+{
+ X509_NAME *name = X509_get_subject_name(cert.get());
+ if (!name)
+ return false;
+ // Remove the CN part:
+ int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
+ X509_NAME_ENTRY *tmp = X509_NAME_get_entry(name, loc);
+ X509_NAME_delete_entry(name, loc);
+ X509_NAME_ENTRY_free(tmp);
+
+ // Add a new CN
+ return X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
+ (unsigned char *)cn, -1, -1, 0);
+}
+
+const char *Ssl::CertAdaptAlgorithmStr[] = {
+ "setValidAfter",
+ "setValidBefore",
+ "setCommonName",
+ NULL
+};
+
+static bool mimicCertificate(Ssl::X509_Pointer & cert, Ssl::X509_Pointer const & caCert, Ssl::X509_Pointer const &certToMimic, Ssl::CrtdMessage::BodyParams const &exceptions)
{
// not an Ssl::X509_NAME_Pointer because X509_REQ_get_subject_name()
// returns a pointer to the existing subject name. Nothing to clean here.
// X509_set_subject_name will call X509_dup for name
X509_set_subject_name(cert.get(), name);
+ Ssl::CrtdMessage::BodyParams::const_iterator it;
+ it = exceptions.find(sslCertAdaptAlgoritm(Ssl::algSetCommonName));
+ if (it != exceptions.end()) {
+ // In this case the CN of the certificate given by the exceptions
+ // and should be replaced
+ const char *cn = it->second.c_str();
+ if (cn && !replaceCommonName(cert, cn))
+ return false;
+ }
// We should get caCert notBefore and notAfter fields and do not allow
// notBefore/notAfter values from certToMimic before/after notBefore/notAfter
// fields from caCert.
// Currently there is not any way in openssl tollkit to compare two ASN1_TIME
// objects.
- ASN1_TIME *aTime;
- if ((aTime = X509_get_notBefore(certToMimic.get())) || (aTime = X509_get_notBefore(caCert.get())) ) {
+ ASN1_TIME *aTime = NULL;
+ it = exceptions.find(sslCertAdaptAlgoritm(Ssl::algSetValidBefore));
+ if (it == exceptions.end() || strcasecmp(it->second.c_str(), "on") != 0)
+ aTime = X509_get_notBefore(certToMimic.get());
+ if (!aTime)
+ aTime = X509_get_notBefore(caCert.get());
+
+ if (aTime) {
if (!X509_set_notBefore(cert.get(), aTime))
return false;
}
else if (!X509_gmtime_adj(X509_get_notBefore(cert.get()), (-2)*24*60*60))
return false;
- if ((aTime = X509_get_notAfter(certToMimic.get())) || (aTime = X509_get_notAfter(caCert.get())) ) {
+ aTime = NULL;
+ it = exceptions.find(sslCertAdaptAlgoritm(Ssl::algSetValidAfter));
+ if (it == exceptions.end() || strcasecmp(it->second.c_str(), "on") != 0)
+ aTime = X509_get_notAfter(certToMimic.get());
+ if (!aTime)
+ aTime = X509_get_notAfter(caCert.get());
+ if (aTime) {
if (!X509_set_notAfter(cert.get(), aTime))
return NULL;
} else if (!X509_gmtime_adj(X509_get_notAfter(cert.get()), 60*60*24*356*3))
return NULL;
-
-
unsigned char *alStr;
int alLen;
alStr = X509_alias_get0(certToMimic.get(), &alLen);
return true;
}
-bool Ssl::generateSslCertificate(Ssl::X509_Pointer const &certToMimic, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkey, BIGNUM const * serial)
+bool Ssl::generateSslCertificate(Ssl::X509_Pointer const &certToMimic, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkey, BIGNUM const * serial, Ssl::CrtdMessage::BodyParams const &mimicExceptions)
{
if (!certToMimic.get())
return false;
return false;
// inherit properties from certToMimic
- if (!mimicCertificate(cert, signedX509, certToMimic))
+ if (!mimicCertificate(cert, signedX509, certToMimic, mimicExceptions))
return false;
// Set issuer name, from CA or our subject name for self signed cert
#define SQUID_SSL_GADGETS_H
#include "base/TidyPointer.h"
+#include "ssl/crtd_message.h"
#if HAVE_OPENSSL_SSL_H
#include <openssl/ssl.h>
*/
bool generateSslCertificateAndPrivateKey(char const *host, X509_Pointer const & signedX509, EVP_PKEY_Pointer const & signedPkey, X509_Pointer & cert, EVP_PKEY_Pointer & pkey, BIGNUM const* serial);
+/**
+ \ingroup SslCrtdSslAPI
+ * Supported certificate adaptation algorithms
+ */
+enum CertAdaptAlgorithm {algSetValidAfter = 0, algSetValidBefore, algSetCommonName, algEnd};
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Short names for certificate adaptation algorithms
+ */
+extern const char *CertAdaptAlgorithmStr[];
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Return the short name of the adaptation algorithm "alg"
+ */
+inline const char *sslCertAdaptAlgoritm(int alg)
+{
+ if (alg >=0 && alg < Ssl::algEnd)
+ return Ssl::CertAdaptAlgorithmStr[alg];
+
+ return NULL;
+}
+
/**
\ingroup SslCrtdSslAPI
* Decide on the kind of certificate and generate a CA- or self-signed one.
* Return generated certificate and private key in resultX509 and resultPkey
* variables.
*/
-bool generateSslCertificate(X509_Pointer const &certToMimic, X509_Pointer const & signedX509, EVP_PKEY_Pointer const & signedPkey, X509_Pointer & cert, EVP_PKEY_Pointer & pkey, BIGNUM const * serial);
+bool generateSslCertificate(X509_Pointer const &certToMimic, X509_Pointer const & signedX509, EVP_PKEY_Pointer const & signedPkey, X509_Pointer & cert, EVP_PKEY_Pointer & pkey, BIGNUM const * serial, CrtdMessage::BodyParams const & mimicExceptions);
/**
\ingroup SslCrtdSslAPI
if (cert_subject.empty())
cert_subject = "/CN=" + host;
+ i = map.find(Ssl::CrtdMessage::param_SetValidAfter);
+ if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0)
+ cert_subject.append("+SetValidAfter=on");
+
+ i = map.find(Ssl::CrtdMessage::param_SetValidBefore);
+ if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0)
+ cert_subject.append("+SetValidBefore=on");
+
+ i = map.find(Ssl::CrtdMessage::param_SetCommonName);
+ if (i != map.end()) {
+ cert_subject.append("+SetCommonName=");
+ cert_subject.append(i->second);
+ }
+
db.find(cert_subject, cert, pkey);
if (cert.get() && certToMimic.get()) {
Ssl::BIGNUM_Pointer serial(db.getCurrentSerialNumber());
if (certToMimic.get()) {
- Ssl::generateSslCertificate(certToMimic, certToSign, pkeyToSign, cert, pkey, serial.get());
+ Ssl::generateSslCertificate(certToMimic, certToSign, pkeyToSign, cert, pkey, serial.get(), map);
}
else
if (!Ssl::generateSslCertificateAndPrivateKey(host.c_str(), certToSign, pkeyToSign, cert, pkey, serial.get()))
throw std::runtime_error("Cannot create ssl certificate or private key.");
- if (!db.addCertAndPrivateKey(cert, pkey) && db.IsEnabledDiskStore())
+ if (!db.addCertAndPrivateKey(cert, pkey, cert_subject) && db.IsEnabledDiskStore())
throw std::runtime_error("Cannot add certificate to db.");
}
}
if (!ok) {
+ Ssl::Errors *errNoList = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_error_sslerrno));
+ if (!errNoList) {
+ errNoList = new Ssl::Errors;
+ if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_error_sslerrno, (void *)errNoList)) {
+ debugs(83, 2, "Failed to set ssl error_no in ssl_verify_cb: Certificate " << buffer);
+ delete errNoList;
+ errNoList = NULL;
+ }
+ }
+
+ if (errNoList) // Append the err no to the SSL errors lists.
+ errNoList->push_back(error_no);
+
if (const char *err_descr = Ssl::GetErrorDescr(error_no))
debugs(83, 5, err_descr << ": " << buffer);
else
debugs(83, DBG_IMPORTANT, "SSL unknown certificate error " << error_no << " in " << buffer);
if (check) {
- Filled(check)->ssl_error = error_no;
+ ACLFilledChecklist *filledCheck = Filled(check);
+ filledCheck->sslErrorList.clear();
+ filledCheck->sslErrorList.push_back(error_no);
if (check->fastCheck() == ACCESS_ALLOWED) {
debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
ok = 1;
delete errDetail;
}
+static void
+ssl_free_SslErrNoList(void *, void *ptr, CRYPTO_EX_DATA *,
+ int, long, void *)
+{
+ Ssl::Errors *errNo = static_cast <Ssl::Errors *>(ptr);
+ delete errNo;
+}
+
// "free" function for X509 certificates
static void
ssl_free_X509(void *, void *ptr, CRYPTO_EX_DATA *,
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);
ssl_ex_index_ssl_peeked_cert = SSL_get_ex_new_index(0, (void *) "ssl_peeked_cert", NULL, NULL, &ssl_free_X509);
+ ssl_ex_index_ssl_error_sslerrno = SSL_get_ex_new_index(0, (void *) "ssl_error_sslerrno", NULL, NULL, &ssl_free_SslErrNoList);
}
/// \ingroup ServerProtocolSSLInternal
return createSSLContext(cert, pkey);
}
-SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & mimicCert, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey)
+SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & mimicCert, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, Ssl::CrtdMessage::BodyParams const &mimicExceptions)
{
Ssl::X509_Pointer cert;
Ssl::EVP_PKEY_Pointer pkey;
if (mimicCert .get()) {
- if (!generateSslCertificate(mimicCert, signedX509, signedPkey, cert, pkey, NULL))
+ if (!generateSslCertificate(mimicCert, signedX509, signedPkey, cert, pkey, NULL, mimicExceptions))
return NULL;
}
else if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) {
{
/// Squid defined error code (<0), an error code returned by SSL X509 api, or SSL_ERROR_NONE
typedef int ssl_error_t;
+
+/// \ingroup ServerProtocolSSLAPI
+/// SSL error codes in the order they were encountered
+typedef std::vector<ssl_error_t> Errors;
} //namespace Ssl
/// \ingroup ServerProtocolSSLAPI
\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 & mimicCert, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey);
+ SSL_CTX *generateSslContext(char const *host, Ssl::X509_Pointer const & mimicCert, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, CrtdMessage::BodyParams const & mimicExceptions);
/**
\ingroup ServerProtocolSSLAPI
char *flags;
acl_access *cert_error;
SSL_CTX *sslContext;
+ sslproxy_cert_adapt *cert_adapt;
} ssl_client;
#endif
int zero_object_sz;
};
+#if USE_SSL
+struct _sslproxy_cert_adapt {
+ int alg;
+ char *param;
+ ACLList *aclList;
+ sslproxy_cert_adapt *next;
+};
+#endif
+
class Logfile;
#include "format/Format.h"
typedef struct _customlog customlog;
+#if USE_SSL
+typedef struct _sslproxy_cert_adapt sslproxy_cert_adapt;
+#endif
+
#if SQUID_SNMP
typedef variable_list *(oid_ParseFn) (variable_list *, snint *);