This option control how generated fake SSL certificates are signed.
Syntax:
sslproxy_cert_sign <signing algorithm> acl ...
where <signing algorithm> can be one of the signTrusted, signUntrusted or
signSelf
Default signing algorithm if the sslproxy_cert_sign is not configured is
signSelf, if the server certificate is self signed, signUntrusted if the server
certificate is untrusted (ERR_INVALID_CA, ERR_SELF_SIGNED_CERT_IN_CHAIN,
ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE, ERR_UNABLE_TO_GET_ISSUER_CERT,
ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY and ERR_CERT_UNTRUSTED errors) and
signTrusted if the server certificate is trusted.
Notes:
- The signing algorithm passed as parameter to the ssl_crtd daemon
- A self signed certificate generated on squid startup to be used as
signing certificate for untrusted certificates, with CN =
"Squid CA for Untrusted Certificates"
- The configured certificates with http(s)_port squid.conf option chained to
the client SSL connection only when signTrusted signing algorithm selected.
When the signing algorithm is signSelf or signUntrusted no other certificate
send to the client browser.
- A small bug fixed which did not allow the sslproxy_cert_adapt option
to be enabled in squid if the icap client is not enabled
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_sign(sslproxy_cert_sign **cert_sign);
+static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign);
+static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign);
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);
cfg->oldest_service_failure = 0;
cfg->service_failure_limit = 0;
}
+#endif
#if USE_SSL
static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt)
safe_free(ca);
}
}
-#endif
+
+static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
+{
+ char *al;
+ sslproxy_cert_sign *cs = (sslproxy_cert_sign *) xcalloc(1, sizeof(sslproxy_cert_sign));
+ if ((al = strtok(NULL, w_space)) == NULL) {
+ self_destruct();
+ return;
+ }
+
+ if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignTrusted]) == 0)
+ cs->alg = Ssl::algSignTrusted;
+ else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted]) == 0)
+ cs->alg = Ssl::algSignUntrusted;
+ else if (strcmp(al, Ssl::CertSignAlgorithmStr[Ssl::algSignSelf]) == 0)
+ cs->alg = Ssl::algSignSelf;
+ else {
+ debugs(3, DBG_CRITICAL, "FATAL: sslproxy_cert_sign: unknown cert signing algorithm: " << al);
+ self_destruct();
+ return;
+ }
+
+ aclParseAclList(LegacyParser, &cs->aclList);
+
+ while(*cert_sign)
+ cert_sign = &(*cert_sign)->next;
+
+ *cert_sign = cs;
+}
+
+static void dump_sslproxy_cert_sign(StoreEntry *entry, const char *name, sslproxy_cert_sign *cert_sign)
+{
+ sslproxy_cert_sign *cs;
+ for (cs = cert_sign; cs != NULL; cs = cs->next) {
+ storeAppendPrintf(entry, "%s ", name);
+ storeAppendPrintf(entry, "%s ", Ssl::certSignAlgorithm(cs->alg));
+ if (cs->aclList)
+ dump_acl_list(entry, cs->aclList);
+ storeAppendPrintf(entry, "\n");
+ }
+}
+
+static void free_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign)
+{
+ while(*cert_sign) {
+ sslproxy_cert_sign *cs = *cert_sign;
+ *cert_sign = cs->next;
+
+ if (cs->aclList)
+ aclDestroyAclList(&cs->aclList);
+
+ safe_free(cs);
+ }
+}
#endif
wccp2_service
wccp2_service_info
wordlist
+sslproxy_cert_sign acl
sslproxy_cert_adapt acl
Default setting: sslproxy_cert_error deny all
DOC_END
+NAME: sslproxy_cert_sign
+IFDEF: USE_SSL
+DEFAULT: none
+TYPE: sslproxy_cert_sign
+LOC: Config.ssl_client.cert_sign
+DOC_START
+
+ sslproxy_cert_sign <adaptation algorithm> acl ...
+
+ The following certificate signing algorithms supported:
+ signTrusted
+ the current signing algorithm using a configured CA certificate
+ that is usually placed in and trusted by end-user browsers
+ signUntrusted
+ sign to guarantee an X509_V_ERR_CERT_UNTRUSTED browser error
+ signSelf
+ sign using a self-signed certificate with the right CN to
+ generate a X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT error in the
+ browser.
+
+ When the acl(s) match, the corresponding signing algorithm is used to
+ generate the certificate. Otherwise, the default signing algorithm used
+DOC_END
+
NAME: sslproxy_cert_adapt
IFDEF: USE_SSL
DEFAULT: none
getSslContextDone(NULL);
}
-void ConnStateData::buildSslCertAdaptParams(Ssl::CrtdMessage::BodyParams &certAdaptParams)
+void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties, Ssl::CrtdMessage::BodyParams &certAdaptParams)
{
// Build a key to use for storing/retrieving certificates to cache
sslBumpCertKey = sslCommonName.defined() ? sslCommonName : sslConnectHostOrIp;
+ certProperties.commonName = sslBumpCertKey.termedBuf();
// fake certificate adaptation requires bump-server-first mode
if (!bumpServerFirstErrorEntry())
return;
+ if (X509 *mimicCert = bumpServerCert.get())
+ certProperties.mimicCert.resetAndLock(mimicCert);
+
HttpRequest *fakeRequest = new HttpRequest();
fakeRequest->SetHost(sslConnectHostOrIp.termedBuf());
fakeRequest->port = clientConnection->local.GetPort();
// if not param defined for Common Name adaptation use hostname from
// the CONNECT request
- if (!param && ca->alg == Ssl::algSetCommonName)
- param = sslConnectHostOrIp.termedBuf();
+ if (ca->alg == Ssl::algSetCommonName) {
+ if (!param)
+ param = sslConnectHostOrIp.termedBuf();
+ certProperties.commonName = param;
+ certProperties.setCommonName = true;
+ }
+ else if(ca->alg == Ssl::algSetValidAfter)
+ certProperties.setValidAfter = true;
+ else if(ca->alg == Ssl::algSetValidBefore)
+ certProperties.setValidBefore = true;
assert(alg && param);
debugs(33, 5, HERE << "Matches certificate adaptation aglorithm: " <<
sslBumpCertKey.append("=");
sslBumpCertKey.append(param);
}
- }
+ }
+
+ certProperties.signAlgorithm = Ssl::algSignEnd;
+ for (sslproxy_cert_sign *sg = Config.ssl_client.cert_sign; sg != NULL; sg = sg->next) {
+ if (sg->aclList && checklist.fastCheck(sg->aclList) == ACCESS_ALLOWED) {
+ const char *sgAlg = Ssl::CertSignAlgorithmStr[sg->alg];
+ certProperties.signAlgorithm = (Ssl::CertSignAlgorithm)sg->alg;
+ certAdaptParams.insert( std::make_pair(Ssl::CrtdMessage::param_Sign, sgAlg));
+ sslBumpCertKey.append("+Sign=");
+ sslBumpCertKey.append(sgAlg);
+ break;
+ }
+ }
+
+ if (certProperties.signAlgorithm == Ssl::algSignEnd) {
+ // Use the default algorithm
+ //Temporary code....
+ // TODO: implement the following using acls:
+ Ssl::ssl_error_t selfSignErrors[] = {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, 0};
+ Ssl::ssl_error_t unTrustedErrors[] = {X509_V_ERR_INVALID_CA,
+ X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
+ X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
+ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
+ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
+ X509_V_ERR_CERT_UNTRUSTED,0};
+ for (int i = 0; selfSignErrors[i] != 0; i++) {
+ if (bumpSslErrorNoList->find(selfSignErrors[i])) {
+ certProperties.signAlgorithm = Ssl::algSignSelf;
+ const char *sgAlg = Ssl::CertSignAlgorithmStr[Ssl::algSignSelf];
+ sslBumpCertKey.append("+Sign=");
+ sslBumpCertKey.append(sgAlg);
+ certAdaptParams.insert( std::make_pair(Ssl::CrtdMessage::param_Sign, sgAlg));
+ break;
+ }
+ }
+ if (certProperties.signAlgorithm == Ssl::algSignEnd) {
+ for (int i = 0; unTrustedErrors[i] != 0; i++) {
+ if (bumpSslErrorNoList->find(selfSignErrors[i])) {
+ certProperties.signAlgorithm = Ssl::algSignUntrusted;
+ const char *sgAlg = Ssl::CertSignAlgorithmStr[Ssl::algSignUntrusted];
+ sslBumpCertKey.append("+Sign=");
+ sslBumpCertKey.append(sgAlg);
+ certAdaptParams.insert( std::make_pair(Ssl::CrtdMessage::param_Sign, sgAlg));
+ break;
+ }
+ }
+ }
+ if (certProperties.signAlgorithm == Ssl::algSignEnd)
+ certProperties.signAlgorithm = Ssl::algSignTrusted;
+ //End of Temporary code....
+ }
+
+ if (certProperties.signAlgorithm == Ssl::algSignUntrusted) {
+ assert(Ssl::SquidCaCert.get() && Ssl::SquidCaCertKey.get());
+ certProperties.signWithX509.resetAndLock(Ssl::SquidCaCert.get());
+ certProperties.signWithPkey.resetAndLock(Ssl::SquidCaCertKey.get());
+ }
+ else {
+ if (port->signingCert.get())
+ certProperties.signWithX509.resetAndLock(port->signingCert.get());
+
+ if (port->signPkey.get())
+ certProperties.signWithPkey.resetAndLock(port->signPkey.get());
+ }
+ signAlgorithm = certProperties.signAlgorithm;
}
void
{
if (port->generateHostCertificates) {
Ssl::CrtdMessage::BodyParams certAdaptParams;
- buildSslCertAdaptParams(certAdaptParams);
+ Ssl::CertificateProperties certProperties;
+ buildSslCertGenerationParams(certProperties, certAdaptParams);
assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
- debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache");
+ 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(sslBumpCertKey.termedBuf());
if (dynCtx) {
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.");
+ debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " 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));
+ map.insert(std::make_pair(Ssl::CrtdMessage::param_host, certProperties.commonName));
/*Append parameters for cert adaptation*/
map.insert(certAdaptParams.begin(), certAdaptParams.end());
std::string bufferToWrite;
- Ssl::writeCertAndPrivateKeyToMemory(port->signingCert, port->signPkey, bufferToWrite);
- if (bumpServerCert.get()) {
- Ssl::appendCertToMemory(bumpServerCert, bufferToWrite);
+ Ssl::writeCertAndPrivateKeyToMemory(certProperties.signWithX509, certProperties.signWithPkey, bufferToWrite);
+ if (certProperties.mimicCert.get()) {
+ Ssl::appendCertToMemory(certProperties.mimicCert, bufferToWrite);
debugs(33, 5, HERE << "Append Mimic Certificate to body request: " << bufferToWrite);
}
request_message.composeBody(map, bufferToWrite);
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, certAdaptParams);
+ debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName);
+ dynCtx = Ssl::generateSslContext(certProperties);
getSslContextDone(dynCtx, true);
return;
#endif //USE_SSL_CRTD
// Try to add generated ssl context to storage.
if (port->generateHostCertificates && isNew) {
- Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
+ if (signAlgorithm == Ssl::algSignTrusted)
+ Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
+ //else it is self-signed or untrusted do not attrach any certificate
Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
void setBumpSslErrorList(Ssl::Errors *errNoList) {bumpSslErrorNoList = cbdataReference(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);
+ void buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties, Ssl::CrtdMessage::BodyParams &certAdaptParams);
bool serveDelayedError(ClientSocketContext *context);
#else
bool switchedToHttps() const { return false; }
StoreEntry *bumpErrorEntry;
Ssl::X509_Pointer bumpServerCert;
Ssl::Errors *bumpSslErrorNoList; ///< The list of SSL certificate errors which ignored
+ Ssl::CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
#endif
AsyncCall::Pointer reader; ///< set when we are reading
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]);
+const std::string Ssl::CrtdMessage::param_Sign("Sign");
static const std::string param_SetValidBefore;
/// Parameter name for passing SetCommonName cert adaptation variable
static const std::string param_SetCommonName;
+ /// Parameter name for passing signing algorithm
+ static const std::string param_Sign;
private:
enum ParseState {
BEFORE_CODE,
#include <openssl/x509v3.h>
#endif
-/**
- \ingroup ServerProtocolSSLInternal
- * Add CN to subject in request.
- */
-static bool addCnToRequest(Ssl::X509_REQ_Pointer & request, char const * cn)
-{
- // 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_NAME *name = X509_REQ_get_subject_name(request.get());
- if (!name)
- return false;
-
- // The second argument of the X509_NAME_add_entry_by_txt declared as
- // "char *" on some OS. Use cn_name to avoid compile warnings.
- static char cn_name[3] = "CN";
- if (!X509_NAME_add_entry_by_txt(name, cn_name, MBSTRING_ASC, (unsigned char *)cn, -1, -1, 0))
- return false;
-
- return true;
-}
-
-/**
- \ingroup ServerProtocolSSLInternal
- * Make request on sign using private key and hostname.
- */
-static bool makeRequest(Ssl::X509_REQ_Pointer & request, Ssl::EVP_PKEY_Pointer const & pkey, char const * host)
-{
- if (!X509_REQ_set_version(request.get(), 0L))
- return false;
-
- if (!addCnToRequest(request, host))
- return false;
-
- if (!X509_REQ_set_pubkey(request.get(), pkey.get()))
- return false;
- return true;
-}
-
EVP_PKEY * Ssl::createSslPrivateKey()
{
Ssl::EVP_PKEY_Pointer pkey(EVP_PKEY_new());
return pkey.release();
}
-X509_REQ * Ssl::createNewX509Request(Ssl::EVP_PKEY_Pointer const & pkey, const char * hostname)
-{
- Ssl::X509_REQ_Pointer request(X509_REQ_new());
-
- if (!request)
- return NULL;
-
- if (!makeRequest(request, pkey, hostname))
- return NULL;
- return request.release();
-}
-
/**
\ingroup ServerProtocolSSLInternal
* Set serial random serial number or set random serial number.
return true;
}
-X509 * Ssl::signRequest(Ssl::X509_REQ_Pointer const & request, Ssl::X509_Pointer const & x509, Ssl::EVP_PKEY_Pointer const & pkey, ASN1_TIME * timeNotAfter, BIGNUM const * serial)
-{
- Ssl::X509_Pointer cert(X509_new());
- if (!cert)
- return NULL;
-
- if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial))
- return NULL;
-
- if (!X509_set_issuer_name(cert.get(), x509.get() ? X509_get_subject_name(x509.get()) : X509_REQ_get_subject_name(request.get())))
- return NULL;
-
- if (!X509_gmtime_adj(X509_get_notBefore(cert.get()), (-2)*24*60*60))
- return NULL;
-
- if (timeNotAfter) {
- if (!X509_set_notAfter(cert.get(), timeNotAfter))
- return NULL;
- } else if (!X509_gmtime_adj(X509_get_notAfter(cert.get()), 60*60*24*356*3))
- return NULL;
-
- if (!X509_set_subject_name(cert.get(), X509_REQ_get_subject_name(request.get())))
- return NULL;
-
- Ssl::EVP_PKEY_Pointer tmppkey(X509_REQ_get_pubkey(request.get()));
-
- if (!tmppkey || !X509_set_pubkey(cert.get(), tmppkey.get()))
- return NULL;
-
- if (!X509_sign(cert.get(), pkey.get(), EVP_sha1()))
- return NULL;
-
- return cert.release();
-}
-
bool Ssl::writeCertAndPrivateKeyToMemory(Ssl::X509_Pointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, std::string & bufferToWrite)
{
bufferToWrite.clear();
return true;
}
-bool Ssl::generateSslCertificateAndPrivateKey(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey, Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, BIGNUM const * serial)
-{
- pkey.reset(createSslPrivateKey());
- if (!pkey)
- return false;
-
- Ssl::X509_REQ_Pointer request(createNewX509Request(pkey, host));
- if (!request)
- return false;
-
- if (signedX509.get() && signedPkey.get())
- cert.reset(signRequest(request, signedX509, signedPkey, X509_get_notAfter(signedX509.get()), serial));
- else
- cert.reset(signRequest(request, signedX509, pkey, NULL, serial));
-
- if (!cert)
- return false;
-
- return true;
-}
-
// Replace certs common name with the given
static bool replaceCommonName(Ssl::X509_Pointer & cert, const char *cn)
{
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);
+ if (loc >=0) {
+ 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::CertSignAlgorithmStr[] = {
+ "signTrusted",
+ "signUntrusted",
+ "signSelf",
+ NULL
+};
+
const char *Ssl::CertAdaptAlgorithmStr[] = {
"setValidAfter",
"setValidBefore",
NULL
};
-static bool mimicCertificate(Ssl::X509_Pointer & cert, Ssl::X509_Pointer const & caCert, Ssl::X509_Pointer const &certToMimic, Ssl::CrtdMessage::BodyParams const &exceptions)
+Ssl::CertificateProperties::CertificateProperties(): serial(NULL),
+ setValidAfter(false),
+ setValidBefore(false),
+ setCommonName(false),
+ signAlgorithm(Ssl::algSignEnd)
+{}
+
+static bool buildCertificate(Ssl::X509_Pointer & cert, Ssl::CertificateProperties const &properties)
{
// 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_NAME *name = X509_get_subject_name(certToMimic.get());
- if (!name)
- return false;
- // X509_set_subject_name will call X509_dup for name
- X509_set_subject_name(cert.get(), name);
+ if (properties.mimicCert.get()) {
+ X509_NAME *name = X509_get_subject_name(properties.mimicCert.get());
+ if (!name)
+ return false;
+ // 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))
+ if (properties.setCommonName || !properties.mimicCert.get()) {
+ // In this case the CN of the certificate given by the user
+ if (!replaceCommonName(cert, properties.commonName.c_str()))
return false;
}
// Currently there is not any way in openssl tollkit to compare two ASN1_TIME
// objects.
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 (!properties.setValidBefore && properties.mimicCert.get())
+ aTime = X509_get_notBefore(properties.mimicCert.get());
+ if (!aTime && properties.signWithX509.get())
+ aTime = X509_get_notBefore(properties.signWithX509.get());
if (aTime) {
if (!X509_set_notBefore(cert.get(), aTime))
return false;
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 (!properties.setValidAfter && properties.mimicCert.get())
+ aTime = X509_get_notAfter(properties.mimicCert.get());
+ if (!aTime && properties.signWithX509.get())
+ aTime = X509_get_notAfter(properties.signWithX509.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);
- if (alStr) {
- X509_alias_set1(cert.get(), alStr, alLen);
- }
- // Add subjectAltName extension used to support multiple hostnames with one certificate
- int pos=X509_get_ext_by_NID (certToMimic.get(), OBJ_sn2nid("subjectAltName"), -1);
- X509_EXTENSION *ext=X509_get_ext(certToMimic.get(), pos);
- if (ext)
- X509_add_ext(cert.get(), ext, -1);
+ if (properties.mimicCert.get()) {
+ unsigned char *alStr;
+ int alLen;
+ alStr = X509_alias_get0(properties.mimicCert.get(), &alLen);
+ if (alStr) {
+ X509_alias_set1(cert.get(), alStr, alLen);
+ }
+
+ // Add subjectAltName extension used to support multiple hostnames with one certificate
+ int pos=X509_get_ext_by_NID (properties.mimicCert.get(), OBJ_sn2nid("subjectAltName"), -1);
+ X509_EXTENSION *ext=X509_get_ext(properties.mimicCert.get(), pos);
+ if (ext)
+ X509_add_ext(cert.get(), ext, -1);
+ }
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, Ssl::CrtdMessage::BodyParams const &mimicExceptions)
+bool Ssl::generateSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties)
{
- if (!certToMimic.get())
- return false;
-
+ Ssl::EVP_PKEY_Pointer pkey;
pkey.reset(createSslPrivateKey());
if (!pkey)
return false;
// Set pub key and serial given by the caller
if (!X509_set_pubkey(cert.get(), pkey.get()))
return false;
- if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial))
+ if (!setSerialNumber(X509_get_serialNumber(cert.get()), properties.serial.get()))
return false;
- // inherit properties from certToMimic
- if (!mimicCertificate(cert, signedX509, certToMimic, mimicExceptions))
+ // Fill the certificate with the required properties
+ if (!buildCertificate(cert, properties))
return false;
+ int ret = 0;
// Set issuer name, from CA or our subject name for self signed cert
- if (!X509_set_issuer_name(cert.get(), signedX509.get() ? X509_get_subject_name(signedX509.get()) : X509_get_subject_name(cert.get())))
+ if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithX509.get())
+ ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(properties.signWithX509.get()));
+ else // Self signed certificate, set issuer to self
+ ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(cert.get()));
+ if (!ret)
return false;
/*Now sign the request */
- int ret = 0;
- if (signedPkey.get())
- ret = X509_sign(cert.get(), signedPkey.get(), EVP_sha1());
+ if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithPkey.get())
+ ret = X509_sign(cert.get(), properties.signWithPkey.get(), EVP_sha1());
else //else sign with self key (self signed request)
ret = X509_sign(cert.get(), pkey.get(), EVP_sha1());
return false;
certToStore.reset(cert.release());
+ pkeyToStore.reset(pkey.release());
return true;
}
because they are used by ssl_crtd.
*/
+/**
+ \ingroup SslCrtdSslAPI
+ * Add SSL locking (a.k.a. reference counting) to TidyPointer
+ */
+template <typename T, void (*DeAllocator)(T *t), int lock>
+class LockingPointer: public TidyPointer<T, DeAllocator>
+{
+public:
+ typedef TidyPointer<T, DeAllocator> Parent;
+
+ LockingPointer(T *t = NULL): Parent(t) {
+ }
+
+ void resetAndLock(T *t) {
+ if (t != this->get()) {
+ reset(t);
+ if (t)
+ CRYPTO_add(&t->references, 1, lock);
+ }
+ }
+};
+
// Macro to be used to define the C++ equivalent function of an extern "C"
// function. The C++ function suffixed with the _cpp extension
#define CtoCpp1(function, argument) \
* TidyPointer typedefs for common SSL objects
*/
CtoCpp1(X509_free, X509 *)
-typedef TidyPointer<X509, X509_free_cpp> X509_Pointer;
+typedef LockingPointer<X509, X509_free_cpp, CRYPTO_LOCK_X509> X509_Pointer;
CtoCpp1(sk_X509_free, STACK_OF(X509) *)
typedef TidyPointer<STACK_OF(X509), sk_X509_free_cpp> X509_STACK_Pointer;
CtoCpp1(EVP_PKEY_free, EVP_PKEY *)
-typedef TidyPointer<EVP_PKEY, EVP_PKEY_free_cpp> EVP_PKEY_Pointer;
+typedef LockingPointer<EVP_PKEY, EVP_PKEY_free_cpp, CRYPTO_LOCK_EVP_PKEY> EVP_PKEY_Pointer;
CtoCpp1(BN_free, BIGNUM *)
typedef TidyPointer<BIGNUM, BN_free_cpp> BIGNUM_Pointer;
*/
EVP_PKEY * createSslPrivateKey();
-/**
- \ingroup SslCrtdSslAPI
- * Create request on certificate for a host.
- */
-X509_REQ * createNewX509Request(EVP_PKEY_Pointer const & pkey, const char * hostname);
-
/**
\ingroup SslCrtdSslAPI
* Write private key and SSL certificate to memory.
*/
bool readCertFromMemory(X509_Pointer & cert, char const * bufferToRead);
+/**
+ \ingroup SslCrtdSslAPI
+ * Supported certificate signing algorithms
+ */
+enum CertSignAlgorithm {algSignTrusted = 0, algSignUntrusted, algSignSelf, algSignEnd};
+
/**
\ingroup SslCrtdSslAPI
- * Sign SSL request.
- * \param x509 if this param equals NULL, returning certificate will be selfsigned.
- * \return X509 Signed certificate.
+ * Short names for certificate signing algorithms
*/
-X509 * signRequest(X509_REQ_Pointer const & request, X509_Pointer const & x509, EVP_PKEY_Pointer const & pkey, ASN1_TIME * timeNotAfter, BIGNUM const * serial);
+
+extern const char *CertSignAlgorithmStr[];
/**
\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.
+ * Return the short name of the signing algorithm "sg"
+ */
+inline const char *certSignAlgorithm(int sg)
+{
+ if (sg >=0 && sg < Ssl::algSignEnd)
+ return Ssl::CertSignAlgorithmStr[sg];
+
+ return NULL;
+}
+
+/**
+ \ingroup SslCrtdSslAPI
+ * Return the id of the signing algorithm "sg"
*/
-bool generateSslCertificateAndPrivateKey(char const *host, X509_Pointer const & signedX509, EVP_PKEY_Pointer const & signedPkey, X509_Pointer & cert, EVP_PKEY_Pointer & pkey, BIGNUM const* serial);
+inline CertSignAlgorithm certSignAlgorithmId(const char *sg)
+{
+ for (int i = 0; i < algSignEnd && Ssl::CertSignAlgorithmStr[i] != NULL; i++)
+ if (strcmp(Ssl::CertSignAlgorithmStr[i], sg) == 0)
+ return (CertSignAlgorithm)i;
+
+ return algSignEnd;
+}
/**
\ingroup SslCrtdSslAPI
* Supported certificate adaptation algorithms
*/
-enum CertAdaptAlgorithm {algSetValidAfter = 0, algSetValidBefore, algSetCommonName, algEnd};
+enum CertAdaptAlgorithm {algSetValidAfter = 0, algSetValidBefore, algSetCommonName, algSetEnd};
/**
\ingroup SslCrtdSslAPI
*/
inline const char *sslCertAdaptAlgoritm(int alg)
{
- if (alg >=0 && alg < Ssl::algEnd)
+ if (alg >=0 && alg < Ssl::algSetEnd)
return Ssl::CertAdaptAlgorithmStr[alg];
return NULL;
}
+/**
+ \ingroup SslCrtdSslAPI
+ * Simple struct to pass certificate generation parameters to generateSslCertificate function.
+ */
+class CertificateProperties {
+public:
+ CertificateProperties();
+ X509_Pointer mimicCert; ///< Certificate to mimic
+ X509_Pointer signWithX509; ///< Certificate to sign the generated request
+ EVP_PKEY_Pointer signWithPkey; ///< The key of the signing certificate
+ BIGNUM_Pointer serial; ///< Use this serial for generated certificate
+ bool setValidAfter; ///< Do not mimic "Not Valid After" field
+ bool setValidBefore; ///< Do not mimic "Not Valid Before" field
+ bool setCommonName; ///< Replace the CN field of the mimicing subject with the given
+ std::string commonName; ///< A CN to use for the generated certificate
+ CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
+private:
+ CertificateProperties(CertificateProperties &);
+ CertificateProperties &operator =(CertificateProperties const &);
+};
+
/**
\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, CrtdMessage::BodyParams const & mimicExceptions);
+bool generateSslCertificate(X509_Pointer & cert, EVP_PKEY_Pointer & pkey, CertificateProperties const &properties);
/**
\ingroup SslCrtdSslAPI
static bool proccessNewRequest(Ssl::CrtdMessage const & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
{
Ssl::CrtdMessage::BodyParams map;
+ Ssl::CertificateProperties certProperties;
std::string body_part;
request_message.parseBody(map, body_part);
Ssl::CrtdMessage::BodyParams::iterator i = map.find(Ssl::CrtdMessage::param_host);
if (i == map.end())
throw std::runtime_error("Cannot find \"" + Ssl::CrtdMessage::param_host + "\" parameter in request message.");
- std::string host = i->second;
+ certProperties.commonName = i->second;
Ssl::CertificateDb db(db_path, max_db_size, fs_block_size);
Ssl::X509_Pointer cert;
Ssl::EVP_PKEY_Pointer pkey;
- Ssl::X509_Pointer certToMimic;
const char *s;
std::string cert_subject;
if ((s = strstr(body_part.c_str(), CERT_BEGIN_STR))) {
s += strlen(CERT_BEGIN_STR);
if ((s = strstr(s, CERT_BEGIN_STR))) {
- Ssl::readCertFromMemory(certToMimic, s);
- if (certToMimic.get()) {
+ Ssl::readCertFromMemory(certProperties.mimicCert, s);
+ if (certProperties.mimicCert.get()) {
char buf[1024];
- cert_subject = X509_NAME_oneline(X509_get_subject_name(certToMimic.get()), buf, sizeof(buf));
+ cert_subject = X509_NAME_oneline(X509_get_subject_name(certProperties.mimicCert.get()), buf, sizeof(buf));
}
}
}
if (cert_subject.empty())
- cert_subject = "/CN=" + host;
+ cert_subject = "/CN=" + certProperties.commonName;
i = map.find(Ssl::CrtdMessage::param_SetValidAfter);
- if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0)
+ if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0) {
cert_subject.append("+SetValidAfter=on");
+ certProperties.setValidAfter = true;
+ }
i = map.find(Ssl::CrtdMessage::param_SetValidBefore);
- if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0)
+ if (i != map.end() && strcasecmp(i->second.c_str(), "on") == 0) {
cert_subject.append("+SetValidBefore=on");
+ certProperties.setValidBefore = true;
+ }
i = map.find(Ssl::CrtdMessage::param_SetCommonName);
if (i != map.end()) {
cert_subject.append("+SetCommonName=");
cert_subject.append(i->second);
+ // use this as Common Name instead of the hostname
+ // defined with host or Common Name from mimic cert
+ certProperties.commonName = i->second;
+ certProperties.setCommonName = true;
}
+ i = map.find(Ssl::CrtdMessage::param_Sign);
+ if (i != map.end()) {
+ cert_subject.append("+Sign=");
+ cert_subject.append(i->second);
+ if ((certProperties.signAlgorithm = Ssl::certSignAlgorithmId(i->second.c_str())) == Ssl::algSignEnd)
+ throw std::runtime_error("Wrong signing algoritm:" + i->second);
+ }
+ else
+ certProperties.signAlgorithm = Ssl::algSignTrusted;
+
db.find(cert_subject, cert, pkey);
- if (cert.get() && certToMimic.get()) {
- if (!Ssl::ssl_match_certificates(cert.get(), certToMimic.get())) {
+ if (cert.get() && certProperties.mimicCert.get()) {
+ if (!Ssl::ssl_match_certificates(cert.get(), certProperties.mimicCert.get())) {
// The certificate changed (renewed or other reason).
// Generete a new one with the updated fields.
cert.reset(NULL);
}
if (!cert || !pkey) {
- Ssl::X509_Pointer certToSign;
- Ssl::EVP_PKEY_Pointer pkeyToSign;
- Ssl::readCertAndPrivateKeyFromMemory(certToSign, pkeyToSign, body_part.c_str());
-
- Ssl::BIGNUM_Pointer serial(db.getCurrentSerialNumber());
-
- if (certToMimic.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 (certProperties.signAlgorithm != Ssl::algSignSelf) {
+ if (!Ssl::readCertAndPrivateKeyFromMemory(certProperties.signWithX509, certProperties.signWithPkey, body_part.c_str()))
+ throw std::runtime_error("Broken signing certificate!");
+ } /*else Squid did not send certificate to sign the generated certificate*/
+
+ certProperties.serial.reset(db.getCurrentSerialNumber());
+
+ if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
+ throw std::runtime_error("Cannot create ssl certificate or private key.");
if (!db.addCertAndPrivateKey(cert, pkey, cert_subject) && db.IsEnabledDiskStore())
throw std::runtime_error("Cannot add certificate to db.");
#include "ssl/support.h"
#include "ssl/gadgets.h"
+Ssl::X509_Pointer Ssl::SquidCaCert;
+Ssl::EVP_PKEY_Pointer Ssl::SquidCaCertKey;
+
/**
\defgroup ServerProtocolSSLInternal Server-Side SSL Internals
\ingroup ServerProtocolSSLAPI
}
+ // Generate the self-signed Ssl::SquidCaCert, using the "SquidLocalCa" as CN
+ Ssl::CertificateProperties certProperties;
+ certProperties.commonName = "Squid CA for Untrusted Certificates";
+ certProperties.signAlgorithm = Ssl::algSignSelf;
+ bool ret = Ssl::generateSslCertificate(Ssl::SquidCaCert, Ssl::SquidCaCertKey, certProperties);
+ assert(ret);
+
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);
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::CrtdMessage::BodyParams const &mimicExceptions)
+SSL_CTX * Ssl::generateSslContext(CertificateProperties const &properties)
{
Ssl::X509_Pointer cert;
Ssl::EVP_PKEY_Pointer pkey;
- if (mimicCert .get()) {
- if (!generateSslCertificate(mimicCert, signedX509, signedPkey, cert, pkey, NULL, mimicExceptions))
- return NULL;
- }
- else if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) {
+ if (!generateSslCertificate(cert, pkey, properties))
return NULL;
- }
if (!cert)
return NULL;
namespace Ssl
{
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ * A temporary self-signed certificate generated on squid start up, to be
+ * used to sign the generated untrusted certificates.
+*/
+extern X509_Pointer SquidCaCert;
+
+/**
+ \ingroup ServerProtocolSSLAPI
+ * The key of the SquidCaCert certificate.
+*/
+extern EVP_PKEY_Pointer SquidCaCertKey;
+
/**
\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, CrtdMessage::BodyParams const & mimicExceptions);
+SSL_CTX * generateSslContext(CertificateProperties const &properties);
/**
\ingroup ServerProtocolSSLAPI
char *flags;
acl_access *cert_error;
SSL_CTX *sslContext;
+ sslproxy_cert_sign *cert_sign;
sslproxy_cert_adapt *cert_adapt;
} ssl_client;
#endif
};
#if USE_SSL
+struct _sslproxy_cert_sign {
+ int alg;
+ ACLList *aclList;
+ sslproxy_cert_sign *next;
+};
+
struct _sslproxy_cert_adapt {
int alg;
char *param;
typedef struct _customlog customlog;
#if USE_SSL
+typedef struct _sslproxy_cert_sign sslproxy_cert_sign;
+
typedef struct _sslproxy_cert_adapt sslproxy_cert_adapt;
#endif