*
*/
-#include "squid-old.h"
+#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 "fde.h"
+#include "globals.h"
+#include "protos.h"
#include "ssl/ErrorDetail.h"
#include "ssl/support.h"
#include "ssl/gadgets.h"
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+const char *Ssl::BumpModeStr[] = {
+ "none",
+ "client-first",
+ "server-first",
+ NULL
+};
+
/**
\defgroup ServerProtocolSSLInternal Server-Side SSL Internals
\ingroup ServerProtocolSSLAPI
len = strlen(buf);
while (len > 0 && (buf[len - 1] == '\n' || buf[len - 1] == '\r'))
-
- len--;
+ --len;
buf[len] = '\0';
break;
default:
- debugs(83, 1, "ssl_temp_rsa_cb: Unexpected key length " << keylen);
+ debugs(83, DBG_IMPORTANT, "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);
+ debugs(83, DBG_IMPORTANT, "ssl_temp_rsa_cb: Failed to generate key " << keylen);
return NULL;
}
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);
+ debugs(83, DBG_IMPORTANT, "Generated ephemeral RSA key of length " << keylen);
}
return rsa;
return matchDomainName(server, cn[0] == '*' ? cn + 1 : cn);
}
+bool Ssl::checkX509ServerValidity(X509 *cert, const char *server)
+{
+ return matchX509CommonNames(cert, (void *)server, check_domain);
+}
+
/// \ingroup ServerProtocolSSLInternal
static int
ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
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 *peeked_cert = (X509 *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_peeked_cert);
X509 *peer_cert = ctx->cert;
X509_NAME_oneline(X509_get_subject_name(peer_cert), buffer,
debugs(83, 5, "SSL Certificate signature OK: " << buffer);
if (server) {
- int found = Ssl::matchX509CommonNames(peer_cert, (void *)server, check_domain);
-
- if (!found) {
+ if (!Ssl::checkX509ServerValidity(peer_cert, server)) {
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 (ok && peeked_cert) {
+ // Check whether the already peeked certificate matches the new one.
+ if (X509_cmp(peer_cert, peeked_cert) != 0) {
+ debugs(83, 2, "SQUID_X509_V_ERR_CERT_CHANGE: Certificate " << buffer << " does not match peeked certificate");
+ ok = 0;
+ error_no = SQUID_X509_V_ERR_CERT_CHANGE;
+ }
+ }
+
if (!ok) {
+ Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors));
+ if (!errs) {
+ errs = new Ssl::Errors(error_no);
+ if (!SSL_set_ex_data(ssl, ssl_ex_index_ssl_errors, (void *)errs)) {
+ debugs(83, 2, "Failed to set ssl error_no in ssl_verify_cb: Certificate " << buffer);
+ delete errs;
+ errs = NULL;
+ }
+ } else // remember another error number
+ errs->push_back_unique(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);
+ assert(!filledCheck->sslErrors);
+ filledCheck->sslErrors = new Ssl::Errors(error_no);
if (check->fastCheck() == ACCESS_ALLOWED) {
debugs(83, 3, "bypassing SSL error " << error_no << " in " << buffer);
ok = 1;
} else {
debugs(83, 5, "confirming SSL error " << error_no);
}
+ delete filledCheck->sslErrors;
+ filledCheck->sslErrors = NULL;
}
}
}
Ssl::ErrorDetail *errDetail =
- new Ssl::ErrorDetail(error_no, broken_cert);
+ new Ssl::ErrorDetail(error_no, peer_cert, broken_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;
}
+static void
+ssl_free_SslErrors(void *, void *ptr, CRYPTO_EX_DATA *,
+ int, long, void *)
+{
+ Ssl::Errors *errs = static_cast <Ssl::Errors*>(ptr);
+ delete errs;
+}
+
+// "free" function for X509 certificates
+static void
+ssl_free_X509(void *, void *ptr, CRYPTO_EX_DATA *,
+ int, long, void *)
+{
+ X509 *cert = static_cast <X509 *>(ptr);
+ X509_free(cert);
+}
+
/// \ingroup ServerProtocolSSLInternal
static void
ssl_initialize(void)
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);
+ 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_errors = SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors);
}
/// \ingroup ServerProtocolSSLInternal
if (!CAfile)
CAfile = clientCA;
+ if (!certfile) {
+ debugs(83, DBG_CRITICAL, "ERROR: No certificate file");
+ return NULL;
+ }
+
switch (version) {
case 2:
if (sslContext == NULL) {
ssl_error = ERR_get_error();
- fatalf("Failed to allocate SSL context: %s\n",
- ERR_error_string(ssl_error, NULL));
+ debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate SSL context: " << ERR_error_string(ssl_error, NULL));
+ return NULL;
}
SSL_CTX_set_options(sslContext, ssl_parse_options(options));
if (!SSL_CTX_set_cipher_list(sslContext, cipher)) {
ssl_error = ERR_get_error();
- fatalf("Failed to set SSL cipher suite '%s': %s\n",
- cipher, ERR_error_string(ssl_error, NULL));
+ debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << cipher << "': " << ERR_error_string(ssl_error, NULL));
+ SSL_CTX_free(sslContext);
+ return NULL;
}
}
}
if (certfile) {
- debugs(83, 1, "Using certificate in " << certfile);
+ debugs(83, DBG_IMPORTANT, "Using certificate in " << certfile);
if (!SSL_CTX_use_certificate_chain_file(sslContext, certfile)) {
ssl_error = ERR_get_error();
certfile, ERR_error_string(ssl_error, NULL));
}
- debugs(83, 1, "Using private key in " << keyfile);
+ debugs(83, DBG_IMPORTANT, "Using private key in " << keyfile);
ssl_ask_password(sslContext, keyfile);
if (!SSL_CTX_use_PrivateKey_file(sslContext, keyfile, SSL_FILETYPE_PEM)) {
nid = OBJ_txt2nid((char *) attribute_name);
if (nid == 0) {
- debugs(83, 1, "WARNING: Unknown SSL attribute name '" << attribute_name << "'");
+ debugs(83, DBG_IMPORTANT, "WARNING: Unknown SSL attribute name '" << attribute_name << "'");
return NULL;
}
PEM_write_bio_X509(mem, cert);
-
len = BIO_get_mem_data(mem, &ptr);
str = (char *)xmalloc(len + 1);
return createSSLContext(cert, pkey);
}
-SSL_CTX * Ssl::generateSslContext(char const *host, Ssl::X509_Pointer const & signedX509, Ssl::EVP_PKEY_Pointer const & signedPkey)
+SSL_CTX * Ssl::generateSslContext(CertificateProperties const &properties)
{
Ssl::X509_Pointer cert;
Ssl::EVP_PKEY_Pointer pkey;
- if (!generateSslCertificateAndPrivateKey(host, signedX509, signedPkey, cert, pkey, NULL)) {
+ if (!generateSslCertificate(cert, pkey, properties))
return NULL;
- }
+
if (!cert)
return NULL;
return createSSLContext(cert, pkey);
}
-bool Ssl::verifySslCertificateDate(SSL_CTX * sslContext)
+bool Ssl::verifySslCertificate(SSL_CTX * sslContext, CertificateProperties const &properties)
{
// Temporary ssl for getting X509 certificate from SSL_CTX.
Ssl::SSL_Pointer ssl(SSL_new(sslContext));
ASN1_TIME * time_notBefore = X509_get_notBefore(cert);
ASN1_TIME * time_notAfter = X509_get_notAfter(cert);
bool ret = (X509_cmp_current_time(time_notBefore) < 0 && X509_cmp_current_time(time_notAfter) > 0);
- return ret;
+ if (!ret)
+ return false;
+
+ return certificateMatchesProperties(cert, properties);
}
bool
}
}
+bool Ssl::generateUntrustedCert(X509_Pointer &untrustedCert, EVP_PKEY_Pointer &untrustedPkey, X509_Pointer const &cert, EVP_PKEY_Pointer const & pkey)
+{
+ // Generate the self-signed certificate, using a hard-coded subject prefix
+ Ssl::CertificateProperties certProperties;
+ if (const char *cn = CommonHostName(cert.get())) {
+ certProperties.commonName = "Not trusted by \"";
+ certProperties.commonName += cn;
+ certProperties.commonName += "\"";
+ } else if (const char *org = getOrganization(cert.get())) {
+ certProperties.commonName = "Not trusted by \"";
+ certProperties.commonName += org;
+ certProperties.commonName += "\"";
+ } else
+ certProperties.commonName = "Not trusted";
+ certProperties.setCommonName = true;
+ // O, OU, and other CA subject fields will be mimicked
+ // Expiration date and other common properties will be mimicked
+ certProperties.signAlgorithm = Ssl::algSignSelf;
+ certProperties.signWithPkey.resetAndLock(pkey.get());
+ certProperties.mimicCert.resetAndLock(cert.get());
+ return Ssl::generateSslCertificate(untrustedCert, untrustedPkey, certProperties);
+}
+
#endif /* USE_SSL */