diff --git a/doc/newsfragments/dont-use-openssl-cli.bugfix b/doc/newsfragments/dont-use-openssl-cli.bugfix new file mode 100644 index 00000000..316d6abf --- /dev/null +++ b/doc/newsfragments/dont-use-openssl-cli.bugfix @@ -0,0 +1 @@ +Barrier no longer uses openssl CLI tool for any operations and hooks into the openssl library directly. diff --git a/res/openssl/barrier.conf b/res/openssl/barrier.conf deleted file mode 100644 index a29abfd5..00000000 --- a/res/openssl/barrier.conf +++ /dev/null @@ -1,65 +0,0 @@ -# -# Barrier OpenSSL configuration file. -# Used for generation of certificate requests. -# - -dir = . - -[ca] -default_ca = CA_default - -[CA_default] -serial = $dir/serial -database = $dir/certindex.txt -new_certs_dir = $dir/certs -certificate = $dir/cacert.pem -private_key = $dir/private/cakey.pem -default_days = 365 -default_md = md5 -preserve = no -email_in_dn = no -nameopt = default_ca -certopt = default_ca -policy = policy_match - -[policy_match] -countryName = match -stateOrProvinceName = match -organizationName = match -organizationalUnitName = optional -commonName = supplied -emailAddress = optional - -[req] -default_bits = 2048 # Size of keys -default_keyfile = key.pem # name of generated keys -default_md = md5 # message digest algorithm -string_mask = nombstr # permitted characters -distinguished_name = req_distinguished_name -req_extensions = v3_req - -[req_distinguished_name] -0.organizationName = Organization Name (company) -organizationalUnitName = Organizational Unit Name (department, division) -emailAddress = Email Address -emailAddress_max = 40 -localityName = Locality Name (city, district) -stateOrProvinceName = State or Province Name (full name) -countryName = Country Name (2 letter code) -countryName_min = 2 -countryName_max = 2 -commonName = Common Name (hostname, IP, or your name) -commonName_max = 64 -0.organizationName_default = My Company -localityName_default = My Town -stateOrProvinceName_default = State or Providence -countryName_default = US - -[v3_ca] -basicConstraints = CA:TRUE -subjectKeyIdentifier = hash -authorityKeyIdentifier = keyid:always,issuer:always - -[v3_req] -basicConstraints = CA:FALSE -subjectKeyIdentifier = hash diff --git a/src/gui/src/SslCertificate.cpp b/src/gui/src/SslCertificate.cpp index 99af4d44..ac70d01a 100644 --- a/src/gui/src/SslCertificate.cpp +++ b/src/gui/src/SslCertificate.cpp @@ -30,16 +30,8 @@ #include #include -static const char kCertificateLifetime[] = "365"; -static const char kCertificateSubjectInfo[] = "/CN=Barrier"; static const char kCertificateFilename[] = "Barrier.pem"; static const char kSslDir[] = "SSL"; -static const char kUnixOpenSslCommand[] = "openssl"; - -#if defined(Q_OS_WIN) -static const char kWinOpenSslBinary[] = "openssl.exe"; -static const char kConfigFile[] = "barrier.conf"; -#endif SslCertificate::SslCertificate(QObject *parent) : QObject(parent) @@ -50,93 +42,21 @@ SslCertificate::SslCertificate(QObject *parent) : } } -std::pair SslCertificate::runTool(const QStringList& args) -{ - QString program; -#if defined(Q_OS_WIN) - program = QCoreApplication::applicationDirPath(); - program.append("\\").append(kWinOpenSslBinary); -#else - program = kUnixOpenSslCommand; -#endif - - - QStringList environment; -#if defined(Q_OS_WIN) - environment << QString("OPENSSL_CONF=%1\\%2") - .arg(QCoreApplication::applicationDirPath()) - .arg(kConfigFile); -#endif - - QProcess process; - process.setEnvironment(environment); - process.start(program, args); - - bool success = process.waitForStarted(); - std::string output; - - QString standardError; - if (success && process.waitForFinished()) - { - output = process.readAllStandardOutput().trimmed().toStdString(); - standardError = process.readAllStandardError().trimmed(); - } - - int code = process.exitCode(); - if (!success || code != 0) - { - emit error( - QString("SSL tool failed: %1\n\nCode: %2\nError: %3") - .arg(program) - .arg(process.exitCode()) - .arg(standardError.isEmpty() ? "Unknown" : standardError)); - return {false, output}; - } - - return {true, output}; -} - void SslCertificate::generateCertificate() { auto filename = QString::fromStdString(getCertificatePath()); QFile file(filename); if (!file.exists() || !isCertificateValid(filename)) { - QStringList arguments; - - // self signed certificate - arguments.append("req"); - arguments.append("-x509"); - arguments.append("-nodes"); - - // valid duration - arguments.append("-days"); - arguments.append(kCertificateLifetime); - - // subject information - arguments.append("-subj"); - - QString subInfo(kCertificateSubjectInfo); - arguments.append(subInfo); - - // private key - arguments.append("-newkey"); - arguments.append("rsa:2048"); - QDir sslDir(QString::fromStdString(getCertificateDirectory())); if (!sslDir.exists()) { sslDir.mkpath("."); } - // key output filename - arguments.append("-keyout"); - arguments.append(filename); - - // certificate output filename - arguments.append("-out"); - arguments.append(filename); - - if (!runTool(arguments).first) { + try { + barrier::generate_pem_self_signed_cert(filename.toStdString()); + } catch (const std::exception& e) { + emit error(QString("SSL tool failed: %1").arg(e.what())); return; } diff --git a/src/lib/net/SecureUtils.cpp b/src/lib/net/SecureUtils.cpp index 97c9cf9a..4b081f66 100644 --- a/src/lib/net/SecureUtils.cpp +++ b/src/lib/net/SecureUtils.cpp @@ -20,6 +20,7 @@ #include "base/finally.h" #include "io/fstream.h" +#include #include #include #include @@ -97,4 +98,48 @@ std::vector get_pem_file_cert_fingerprint(const std::string& path, return get_ssl_cert_fingerprint(cert, type); } +void generate_pem_self_signed_cert(const std::string& path) +{ + auto expiration_days = 365; + + auto* private_key = EVP_PKEY_new(); + if (!private_key) { + throw std::runtime_error("Could not allocate private key for certificate"); + } + auto private_key_free = finally([private_key](){ EVP_PKEY_free(private_key); }); + + auto* rsa = RSA_generate_key(2048, RSA_F4, nullptr, nullptr); + if (!rsa) { + throw std::runtime_error("Failed to generate RSA key"); + } + EVP_PKEY_assign_RSA(private_key, rsa); + + auto* cert = X509_new(); + if (!cert) { + throw std::runtime_error("Could not allocate certificate"); + } + auto cert_free = finally([cert]() { X509_free(cert); }); + + ASN1_INTEGER_set(X509_get_serialNumber(cert), 1); + X509_gmtime_adj(X509_get_notBefore(cert), 0); + X509_gmtime_adj(X509_get_notAfter(cert), expiration_days * 24 * 3600); + X509_set_pubkey(cert, private_key); + + auto* name = X509_get_subject_name(cert); + X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, + reinterpret_cast("Barrier"), -1, -1, 0); + X509_set_issuer_name(cert, name); + + X509_sign(cert, private_key, EVP_sha256()); + + auto fp = fopen_utf8_path(path.c_str(), "r"); + if (!fp) { + throw std::runtime_error("Could not open certificate output path"); + } + auto file_close = finally([fp]() { std::fclose(fp); }); + + PEM_write_PrivateKey(fp, private_key, nullptr, nullptr, 0, nullptr, nullptr); + PEM_write_X509(fp, cert); +} + } // namespace barrier diff --git a/src/lib/net/SecureUtils.h b/src/lib/net/SecureUtils.h index 6a6343c8..df450136 100644 --- a/src/lib/net/SecureUtils.h +++ b/src/lib/net/SecureUtils.h @@ -38,6 +38,8 @@ std::vector get_ssl_cert_fingerprint(X509* cert, FingerprintType t std::vector get_pem_file_cert_fingerprint(const std::string& path, FingerprintType type); +void generate_pem_self_signed_cert(const std::string& path); + } // namespace barrier #endif // BARRIER_LIB_NET_SECUREUTILS_H