SettleStderr();
SettleSyslog();
+
+ debugs(84, 2, "starting " << name << " with PID " << getpid());
}
void
security_file_certgen_LDADD = \
$(top_builddir)/src/ssl/libsslutil.la \
+ $(top_builddir)/src/sbuf/libsbuf.la \
+ $(top_builddir)/src/debug/libdebug.la \
+ $(top_builddir)/src/comm/libminimal.la \
+ $(top_builddir)/src/mem/libminimal.la \
+ $(top_builddir)/src/base/libbase.la \
$(top_builddir)/src/time/libtime.la \
$(SSLLIB) \
$(COMPAT_LIB)
#include "squid.h"
#include "base/HardFun.h"
+#include "base/TextException.h"
+#include "sbuf/Stream.h"
#include "security/cert_generators/file/certificate_db.h"
#include <cerrno>
fd = open(filename.c_str(), O_RDWR);
if (fd == -1)
#endif
- throw std::runtime_error("Failed to open file " + filename);
+ throw TextException(ToSBuf("Failed to open file ", filename), Here());
#if _SQUID_WINDOWS_
if (!LockFile(hFile, 0, 0, 1, 0))
#else
if (flock(fd, LOCK_EX) != 0)
#endif
- throw std::runtime_error("Failed to get a lock of " + filename);
+ throw TextException(ToSBuf("Failed to get a lock of ", filename), Here());
}
void Ssl::Lock::unlock()
}
#endif
else
- throw std::runtime_error("Lock is already unlocked for " + filename);
+ throw TextException(ToSBuf("Lock is already unlocked for ", filename), Here());
}
Ssl::Lock::~Lock()
unlock();
}
-Ssl::Locker::Locker(Lock &aLock, const char *aFileName, int aLineNo):
- weLocked(false), lock(aLock), fileName(aFileName), lineNo(aLineNo)
+Ssl::Locker::Locker(Lock &aLock, const SourceLocation &aCaller):
+ weLocked(false),
+ lock(aLock),
+ caller(aCaller)
{
if (!lock.locked()) {
lock.lock();
Ssl::Locker::~Locker()
{
- (void)lineNo; // lineNo is unused but some find it helpful in debugging
if (weLocked)
lock.unlock();
}
bool
Ssl::CertificateDb::find(std::string const &key, const Security::CertPointer &expectedOrig, Security::CertPointer &cert, Security::PrivateKeyPointer &pkey)
{
- const Locker locker(dbLock, Here);
+ const Locker locker(dbLock, Here());
load();
return pure_find(key, expectedOrig, cert, pkey);
}
bool Ssl::CertificateDb::purgeCert(std::string const & key) {
- const Locker locker(dbLock, Here);
+ const Locker locker(dbLock, Here());
load();
if (!db)
return false;
bool
Ssl::CertificateDb::addCertAndPrivateKey(std::string const &useKey, const Security::CertPointer &cert, const Security::PrivateKeyPointer &pkey, const Security::CertPointer &orig)
{
- const Locker locker(dbLock, Here);
+ const Locker locker(dbLock, Here());
load();
if (!db || !cert || !pkey)
return false;
void
Ssl::CertificateDb::Create(std::string const & db_path) {
if (db_path == "")
- throw std::runtime_error("Path to db is empty");
+ throw TextException("Path to db is empty", Here());
std::string db_full(db_path + "/" + db_file);
std::string cert_full(db_path + "/" + cert_dir);
std::string size_full(db_path + "/" + size_file);
if (mkdir(db_path.c_str(), 0777))
- throw std::runtime_error("Cannot create " + db_path);
+ throw TextException(ToSBuf("Cannot create ", db_path), Here());
if (mkdir(cert_full.c_str(), 0777))
- throw std::runtime_error("Cannot create " + cert_full);
+ throw TextException(ToSBuf("Cannot create ", cert_full), Here());
std::ofstream size(size_full.c_str());
if (size)
size << 0;
else
- throw std::runtime_error("Cannot open " + size_full + " to open");
+ throw TextException(ToSBuf("Cannot open ", size_full, " to open"), Here());
std::ofstream db(db_full.c_str());
if (!db)
- throw std::runtime_error("Cannot open " + db_full + " to open");
+ throw TextException(ToSBuf("Cannot open ", db_full, " to open"), Here());
}
void
void Ssl::CertificateDb::writeSize(size_t db_size) {
std::ofstream ofstr(size_full.c_str());
if (!ofstr)
- throw std::runtime_error("cannot write \"" + size_full + "\" file");
+ throw TextException(ToSBuf("cannot write \"", size_full, "\" file"), Here());
ofstr << db_size;
}
// Load db from file.
Ssl::BIO_Pointer in(BIO_new(BIO_s_file()));
if (!in || BIO_read_filename(in.get(), db_full.c_str()) <= 0)
- throw std::runtime_error("Uninitialized SSL certificate database directory: " + db_path + ". To initialize, run \"security_file_certgen -c -s " + db_path + "\".");
+ throw TextException(ToSBuf("Uninitialized SSL certificate database directory: ", db_path, ". To initialize, run \"security_file_certgen -c -s ", db_path, "\"."), Here());
bool corrupt = false;
Ssl::TXT_DB_Pointer temp_db(TXT_DB_read(in.get(), cnlNumber));
corrupt = true;
if (corrupt)
- throw std::runtime_error("The SSL certificate database " + db_path + " is corrupted. Please rebuild");
+ throw TextException(ToSBuf("The SSL certificate database ", db_path, " is corrupted. Please rebuild"), Here());
db.reset(temp_db.release());
}
void Ssl::CertificateDb::save() {
if (!db)
- throw std::runtime_error("The certificates database is not loaded");;
+ throw TextException("The certificates database is not loaded", Here());
// To save the db to file, create a new BIO with BIO file methods.
Ssl::BIO_Pointer out(BIO_new(BIO_s_file()));
if (!out || !BIO_write_filename(out.get(), const_cast<char *>(db_full.c_str())))
- throw std::runtime_error("Failed to initialize " + db_full + " file for writing");;
+ throw TextException(ToSBuf("Failed to initialize ", db_full, " file for writing"), Here());
if (TXT_DB_write(out.get(), db.get()) < 0)
- throw std::runtime_error("Failed to write " + db_full + " file");
+ throw TextException(ToSBuf("Failed to write ", db_full, " file"), Here());
}
// Normally defined in defines.h file
subSize(filename);
int ret = remove(filename.c_str());
if (ret < 0 && errno != ENOENT)
- throw std::runtime_error("Failed to remove certificate file " + filename + " from db");
+ throw TextException(ToSBuf("Failed to remove certificate file ", filename, " from db"), Here());
}
bool Ssl::CertificateDb::deleteInvalidCertificate() {
#ifndef SQUID_SSL_CERTIFICATE_DB_H
#define SQUID_SSL_CERTIFICATE_DB_H
+#include "base/Here.h"
#include "ssl/gadgets.h"
#include <string>
{
public:
/// locks the lock if the lock was unlocked
- Locker(Lock &lock, const char *aFileName, int lineNo);
+ Locker(Lock &, const SourceLocation &);
/// unlocks the lock if it was locked by us
~Locker();
private:
bool weLocked; ///< whether we locked the lock
Lock &lock; ///< the lock we are operating on
- const std::string fileName; ///< where the lock was needed
- const int lineNo; ///< where the lock was needed
-};
-/// convenience macro to pass source code location to Locker and others
-#define Here __FILE__, __LINE__
+ /// where the lock was needed (currently not reported anywhere)
+ const SourceLocation caller;
+};
/**
* Database class for storing SSL certificates and their private keys.
*/
#include "squid.h"
+#include "base/TextException.h"
+#include "debug/Stream.h"
#include "helper/protocol_defines.h"
+#include "sbuf/Stream.h"
#include "security/cert_generators/file/certificate_db.h"
#include "ssl/crtd_message.h"
#include "time/gadgets.h"
if (!strncasecmp(unit, B_GBYTES_STR, strlen(B_GBYTES_STR)))
return 1 << 30;
- std::cerr << "WARNING: Unknown bytes unit '" << unit << "'" << std::endl;
-
- return 0;
+ throw TextException(ToSBuf("Unknown bytes unit: ", unit), Here());
}
-/// Parse uninterrapted string of bytes value. It looks like "4MB".
-static bool parseBytesOptionValue(size_t * bptr, char const * value)
+/// Parse the number of bytes given as <integer><unit> value (e.g., 4MB).
+/// \param name the name of the option being parsed
+static size_t
+parseBytesOptionValue(const char * const name, const char * const value)
{
// Find number from string beginning.
char const * number_begin = value;
++number_end;
}
+ if (number_end <= number_begin)
+ throw TextException(ToSBuf("expecting a decimal number at the beginning of ", name, " value but got: ", value), Here());
+
std::string number(number_begin, number_end - number_begin);
std::istringstream in(number);
- int d = 0;
- if (!(in >> d))
- return false;
+ size_t base = 0;
+ if (!(in >> base) || !in.eof())
+ throw TextException(ToSBuf("unsupported integer part of ", name, " value: ", number), Here());
- int m;
- if ((m = parseBytesUnits(number_end)) == 0) {
- return false;
- }
+ const auto multiplier = parseBytesUnits(number_end);
+ static_assert(std::is_unsigned<decltype(multiplier * base)>::value, "no signed overflows");
+ const auto product = multiplier * base;
+ if (base && multiplier != product / base)
+ throw TextException(ToSBuf(name, " size too large: ", value), Here());
- *bptr = static_cast<size_t>(m * d);
- if (static_cast<long>(*bptr * 2) != m * d * 2)
- return false;
-
- return true;
+ return product;
}
/// Print help using response code.
static bool processNewRequest(Ssl::CrtdMessage & request_message, std::string const & db_path, size_t max_db_size, size_t fs_block_size)
{
Ssl::CertificateProperties certProperties;
- std::string error;
- if (!request_message.parseRequest(certProperties, error))
- throw std::runtime_error("Error while parsing the crtd request: " + error);
+ request_message.parseRequest(certProperties);
// TODO: create a DB object only once, instead re-allocating here on every call.
std::unique_ptr<Ssl::CertificateDb> db;
if (db)
db->find(certKey, certProperties.mimicCert, cert, pkey);
- } catch (std::runtime_error &err) {
+ } catch (...) {
dbFailed = true;
- error = err.what();
+ debugs(83, DBG_IMPORTANT, "ERROR: Database search failure: " << CurrentException <<
+ Debug::Extra << "database location: " << db_path);
}
if (!cert || !pkey) {
if (!Ssl::generateSslCertificate(cert, pkey, certProperties))
- throw std::runtime_error("Cannot create ssl certificate or private key.");
+ throw TextException("Cannot create ssl certificate or private key.", Here());
try {
/* XXX: this !dbFailed condition prevents the helper fixing DB issues
object lifecycle and formally altering the helper behaviour.
*/
if (!dbFailed && db && !db->addCertAndPrivateKey(certKey, cert, pkey, certProperties.mimicCert))
- throw std::runtime_error("Cannot add certificate to db.");
+ throw TextException("Cannot add certificate to db.", Here());
- } catch (const std::runtime_error &err) {
+ } catch (...) {
dbFailed = true;
- error = err.what();
+ debugs(83, DBG_IMPORTANT, "ERROR: Database update failure: " << CurrentException <<
+ Debug::Extra << "database location: " << db_path);
}
}
- if (dbFailed)
- std::cerr << "security_file_certgen helper database '" << db_path << "' failed: " << error << std::endl;
-
std::string bufferToWrite;
if (!Ssl::writeCertAndPrivateKeyToMemory(cert, pkey, bufferToWrite))
- throw std::runtime_error("Cannot write ssl certificate or/and private key to memory.");
+ throw TextException("Cannot write ssl certificate or/and private key to memory.", Here());
Ssl::CrtdMessage response_message(Ssl::CrtdMessage::REPLY);
response_message.setCode("OK");
int main(int argc, char *argv[])
{
try {
+ Debug::NameThisHelper("sslcrtd_program");
+
size_t max_db_size = 0;
size_t fs_block_size = 0;
int8_t c;
debug_enabled = 1;
break;
case 'b':
- if (!parseBytesOptionValue(&fs_block_size, optarg)) {
- throw std::runtime_error("Error when parsing -b options value");
- }
+ fs_block_size = parseBytesOptionValue("-b", optarg);
break;
case 's':
db_path = optarg;
case 'M':
// use of -M without -s is probably an admin mistake, so make it an error
if (db_path.empty()) {
- throw std::runtime_error("Error -M option requires an -s parameter be set first.");
- }
- if (!parseBytesOptionValue(&max_db_size, optarg)) {
- throw std::runtime_error("Error when parsing -M options value");
+ throw TextException("Error -M option requires an -s parameter be set first.", Here());
}
+ max_db_size = parseBytesOptionValue("-M", optarg);
break;
case 'v':
std::cout << "security_file_certgen version " << VERSION << std::endl;
// when -s is used, -M is required
if (!db_path.empty() && max_db_size == 0)
- throw std::runtime_error("security_file_certgen -s requires an -M parameter");
+ throw TextException("security_file_certgen -s requires an -M parameter", Here());
if (create_new_db) {
// when -c is used, -s is required (implying also -M, which is checked above)
if (db_path.empty())
- throw std::runtime_error("security_file_certgen is missing the required parameter. There should be -s and -M parameters when -c is used.");
+ throw TextException("security_file_certgen is missing the required parameter. There should be -s and -M parameters when -c is used.", Here());
std::cout << "Initialization SSL db..." << std::endl;
Ssl::CertificateDb::Create(db_path);
}
if (parse_result == Ssl::CrtdMessage::ERROR) {
- throw std::runtime_error("Cannot parse request message.");
+ throw TextException("Cannot parse request message.", Here());
} else if (request_message.getCode() == Ssl::CrtdMessage::code_new_certificate) {
processNewRequest(request_message, db_path, max_db_size, fs_block_size);
} else {
- throw std::runtime_error("Unknown request code: \"" + request_message.getCode() + "\".");
+ throw TextException(ToSBuf("Unknown request code: \"", request_message.getCode(), "\"."), Here());
}
std::cout.flush();
}
- } catch (std::runtime_error & error) {
- std::cerr << argv[0] << ": " << error.what() << std::endl;
+ } catch (...) {
+ debugs(83, DBG_CRITICAL, "FATAL: Cannot generate certificates: " << CurrentException);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
*/
#include "squid.h"
+#include "base/TextException.h"
+#include "sbuf/Stream.h"
#include "ssl/crtd_message.h"
#include "ssl/gadgets.h"
body += '\n' + other_part;
}
-bool Ssl::CrtdMessage::parseRequest(Ssl::CertificateProperties &certProperties, std::string &error)
+void
+Ssl::CrtdMessage::parseRequest(CertificateProperties &certProperties)
{
Ssl::CrtdMessage::BodyParams map;
std::string certs_part;
parseBody(map, certs_part);
Ssl::CrtdMessage::BodyParams::iterator i = map.find(Ssl::CrtdMessage::param_host);
if (i == map.end()) {
- error = "Cannot find \"host\" parameter in request message";
- return false;
+ throw TextException("Cannot find \"host\" parameter in request message", Here());
}
certProperties.commonName = i->second;
i = map.find(Ssl::CrtdMessage::param_Sign);
if (i != map.end()) {
if ((certProperties.signAlgorithm = Ssl::certSignAlgorithmId(i->second.c_str())) == Ssl::algSignEnd) {
- error = "Wrong signing algorithm: ";
- error += i->second;
- return false;
+ throw TextException(ToSBuf("Wrong signing algorithm: ", i->second), Here());
}
} else
certProperties.signAlgorithm = Ssl::algSignTrusted;
i = map.find(Ssl::CrtdMessage::param_SignHash);
const char *signHashName = i != map.end() ? i->second.c_str() : SQUID_SSL_SIGN_HASH_IF_NONE;
if (!(certProperties.signHash = EVP_get_digestbyname(signHashName))) {
- error = "Wrong signing hash: ";
- error += signHashName;
- return false;
+ throw TextException(ToSBuf("Wrong signing hash: ", signHashName), Here());
}
if (!Ssl::readCertAndPrivateKeyFromMemory(certProperties.signWithX509, certProperties.signWithPkey, certs_part.c_str())) {
- error = "Broken signing certificate!";
- return false;
+ throw TextException("Broken signing certificate!", Here());
}
static const std::string CERT_BEGIN_STR("-----BEGIN CERTIFICATE");
if ((pos= certs_part.find(CERT_BEGIN_STR, pos)) != std::string::npos)
Ssl::readCertFromMemory(certProperties.mimicCert, certs_part.c_str() + pos);
}
- return true;
}
void Ssl::CrtdMessage::composeRequest(Ssl::CertificateProperties const &certProperties)
std::string certsPart;
if (!Ssl::writeCertAndPrivateKeyToMemory(certProperties.signWithX509, certProperties.signWithPkey, certsPart))
- throw std::runtime_error("Ssl::writeCertAndPrivateKeyToMemory()");
+ throw TextException("Ssl::writeCertAndPrivateKeyToMemory()", Here());
if (certProperties.mimicCert.get()) {
if (!Ssl::appendCertToMemory(certProperties.mimicCert, certsPart))
- throw std::runtime_error("Ssl::appendCertToMemory()");
+ throw TextException("Ssl::appendCertToMemory()", Here());
}
body += "\n" + certsPart;
}
void composeBody(BodyParams const & map, std::string const & other_part);
/// orchestrates entire request parsing
- bool parseRequest(Ssl::CertificateProperties &, std::string &error);
+ void parseRequest(CertificateProperties &);
void composeRequest(Ssl::CertificateProperties const &); // throws
/// String code for "new_certificate" messages