#include #include #include #include #include #include "SSLManager.h" namespace fs = std::experimental::filesystem; /* openssl req -x509 -nodes -out default_certificate.pem -newkey rsa:2048 -keyout default_privatekey.pem -days 3650 -config <( cat <<-EOF [req] default_bits = 2048 prompt = no default_md = sha256 distinguished_name = dn [ dn ] C=DE O=TeaSpeak OU=Web Server emailAddress=contact@teaspeak.de CN = web.teaspeak.de EOF ) */ /* static std::string SSL_DEFAULT_KEY = R"( -----BEGIN PRIVATE KEY----- MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrnuiIrA/uK/VI yUDFIc8aClGNzlWvCL18LmhCdE0Mlp0X6aYS/lergGJZark8dNbARNcsU41535L/ 0IrqhQr3zsaZqK4sQ5xKtRpUXjknFPMRIcfx7efA/8687yxJ90IJbov7EOT2cVF+ nzE4DNPFyih6G42wMeMTfnuy60fABblWZikeYNM+/YtFcB1uTiJhPWid0URo85d+ e0ifrO7y3EszGd1Hl27oroyFAeGRP5MyU3cj0ZUBjxEdEnJXmAQnHM4cO6tC9E62 775J4G+abJy6TdyxUUK9LhtfTpMnLYpGhMzJwslrfqQ9almY2K7X0xzBYxRNRXsA B6sBI1llAgMBAAECggEBAJ7pic/j4uxa78jx8XOYFri6DUINaPGmWi5+mjPOlPmv DM9znj/AG1XGj0rUs6jzV1a5Z7S3uSy8hNUzOS5m+vzzDpqBwqViBXp3r2WnyawS je+zI/00mX/wXnI71Pq4ZQFux1c3EYvQ6fEhXuXTmtRumIRYtx4LU4RdfhTyH4IB Rqsb6tPyO0gVPdTL5V3O74bSs0k2QVkm4U/UKiuDJeY5Upy/MX0y+ObEzkxCL/e5 o7jB0DkvCu5wjHWWoA/hduvBXLelbPqxXSuz6YGGPiOaM131Zmk1JIkmeANX1T8b raWg4yV7oiOprAyx2ioU6Q55w+AYfq2q+noJwEOOrYECgYEA0fJ+pfywksV2eXBu N2TkruTYsMGE7bSvM8E6ABkYutUz4bC8ytHszs6e3vMhojO6aHqp389vFrPHSu23 1Uqk/eVBbRSHzbvRXl35OzNzeSfmJKkzbG8OnLnukoHzDJUPrhFUHBR8KHnuIycv 87Lj+wmpIaAfaFCct8sKbgnB3e0CgYEA0UQ0GLS6BnwfwdKJVubaIc6P1wtr7vam UVFntusx3v8AI1qiQ6EzQ73NX9eh3L5sKVcrMtBLur3G/Ferp135J6V2+ZYc1nUA 67x/9wquo42SxlRCfx00zeCCGU0Q56/UEND626So7E6k1sgKnWXq5uuQYZU0wpbi InD1oo+bulkCgYA2wd2AY2CWV0QoNke40OrIJs3RhBese9S6VepPvjvx9st6UMNc ztXJtqA/HACosn8q4ttNkWey7x7KjyfETJytz855qcIlyZe42h+37hpu/hYLd8n+ vRR9kg0ETzpaDMKzLrfWPw2G7Q5MQttB32WQwxtGtuGaLnRBh4Zn3smenQKBgANF DYtVR5LSXaypnXu+H6pnj9fMVeNl9zNOElDJW/4f/eCPifmEi0iDrrHQrLbGQupi ckpY9tX0ISfQNt5mmX4FF9bOgaTYLyt/xoAVqqTjkWeH6YIS8sBEwcOjcKAuHyIk IcdMy1bl4613crMC5Ki3BYqAylJACUiAe1YO6GABAoGBALk+VuDvY8IxcSTOZXxe hGG/TZlHz/xfvuOC0ngsfX+C7Q98KDh72SzBDk4wSqWTLTLPn4jZArmD+gVagNz9 GSb4gpxinfXnb1px0BjR7YoVxJnk8FEjxe7PPSeYWt5e0Liyl0LPBm6xK0LXppyr N9G+1ojrRslgE5HGdZ9Axi54 -----END PRIVATE KEY----- )"; static std::string SSL_DEFAULT_CERT = R"( -----BEGIN CERTIFICATE----- MIIDYjCCAkoCCQCagl242EEilDANBgkqhkiG9w0BAQsFADBzMQswCQYDVQQGEwJE RTERMA8GA1UECgwIVGVhU3BlYWsxEzARBgNVBAsMCldlYiBTZXJ2ZXIxIjAgBgkq hkiG9w0BCQEWE2NvbnRhY3RAdGVhc3BlYWsuZGUxGDAWBgNVBAMMD3dlYi50ZWFz cGVhay5kZTAeFw0xODAzMjIxNTM4MzVaFw0yODAzMTkxNTM4MzVaMHMxCzAJBgNV BAYTAkRFMREwDwYDVQQKDAhUZWFTcGVhazETMBEGA1UECwwKV2ViIFNlcnZlcjEi MCAGCSqGSIb3DQEJARYTY29udGFjdEB0ZWFzcGVhay5kZTEYMBYGA1UEAwwPd2Vi LnRlYXNwZWFrLmRlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAq57o iKwP7iv1SMlAxSHPGgpRjc5Vrwi9fC5oQnRNDJadF+mmEv5Xq4BiWWq5PHTWwETX LFONed+S/9CK6oUK987GmaiuLEOcSrUaVF45JxTzESHH8e3nwP/OvO8sSfdCCW6L +xDk9nFRfp8xOAzTxcooehuNsDHjE357sutHwAW5VmYpHmDTPv2LRXAdbk4iYT1o ndFEaPOXfntIn6zu8txLMxndR5du6K6MhQHhkT+TMlN3I9GVAY8RHRJyV5gEJxzO HDurQvROtu++SeBvmmycuk3csVFCvS4bX06TJy2KRoTMycLJa36kPWpZmNiu19Mc wWMUTUV7AAerASNZZQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCGNR1J3ynUVwuR D7RrxfsVYiW/Wx6/i+MQCsk0R+lrsjlkPIUr8iIQu762QszWFaubdh8jqXVu5psT utQk5RoZ5XrKUcE2av9G4o6Grj3BcsT3JXQcHtIpuDQnJDFoRe950YSVmZKbpvL/ STN46EjSSiDUpv1qqXeVr9CEyCZftj4esJ6RvJwYeKBG8HXoNzMYK32N6JWGbZFu U76TSNwcjXNId43V4OVFZ/ReaD8Nvzq10GwgKb2HshQtdIvOVNfAAk/mX4e+5I1k 9zTEm+IBljBFvNzAKQRxUiiUDTjazKVt51ToJhRVBGXtPmVvhKacWWC3tbHdWisG 5vm7hxLQ -----END CERTIFICATE----- )"; */ using namespace ts; using namespace ts::ssl; using namespace std; SSLManager::SSLManager() = default; SSLManager::~SSLManager() = default; bool SSLManager::initialize() { SSL_load_error_strings(); OpenSSL_add_ssl_algorithms(); return true; } static auto ERR_TO_STRING = [](const char* err, size_t length, void* ptrTarget){ auto target = (string*) ptrTarget; target->resize(length); memcpy((void *) target->data(), err, length); return 0; }; #define SSL_ERROR(message) \ do { \ ERR_print_errors_cb(ERR_TO_STRING, &error); \ error = (message) + error; \ return nullptr; \ } while(false) std::shared_ptr SSLManager::initializeContext(const std::string &key, std::string &privateKey, std::string &certificate, std::string &error, bool raw, const std::shared_ptr& generator) { auto load = this->loadContext(privateKey, certificate, error, raw, generator); if(!load) return nullptr; this->contexts[key] = load; return load; } std::shared_ptr SSLManager::initializeSSLKey(const std::string &key, const std::string &rsaKey, std::string &error, bool raw) { auto load = this->loadSSL(rsaKey, error, raw); if(!load) return nullptr; this->rsa[key] = load; return load; } EVP_PKEY* SSLGenerator::generateKey() { auto key = std::unique_ptr(EVP_PKEY_new(), ::EVP_PKEY_free); auto rsa = RSA_new(); auto e = std::unique_ptr(BN_new(), ::BN_free); BN_set_word(e.get(), RSA_F4); if(!RSA_generate_key_ex(rsa, 2048, e.get(), nullptr)) return nullptr; EVP_PKEY_assign_RSA(key.get(), rsa); return key.release(); } X509* SSLGenerator::generateCertificate(EVP_PKEY* key) { auto cert = X509_new(); X509_set_pubkey(cert, key); ASN1_INTEGER_set(X509_get_serialNumber(cert), 3); X509_gmtime_adj(X509_get_notBefore(cert), 0); X509_gmtime_adj(X509_get_notAfter(cert), 31536000L); X509_NAME* name = nullptr; name = X509_get_subject_name(cert); for(const auto& subject : this->subjects) X509_NAME_add_entry_by_txt(name, subject.first.c_str(), MBSTRING_ASC, (unsigned char *) subject.second.c_str(), subject.second.length(), -1, 0); X509_set_subject_name(cert, name); name = X509_get_issuer_name(cert); for(const auto& subject : this->issues) X509_NAME_add_entry_by_txt(name, subject.first.c_str(), MBSTRING_ASC, (unsigned char *) subject.second.c_str(), subject.second.length(), -1, 0); X509_set_issuer_name(cert, name); X509_sign(cert, key, EVP_sha512()); return cert; } //TODO passwords std::shared_ptr SSLManager::loadContext(std::string &rawKey, std::string &rawCert, std::string &error, bool rawData, const shared_ptr& generator) { std::shared_ptr certBio = nullptr; std::shared_ptr keyBio = nullptr; std::shared_ptr cert = nullptr; std::shared_ptr key = nullptr; bool flagCertModified = false; bool flagKeyModified = false; std::shared_ptr context = nullptr; std::shared_ptr result = nullptr; if(rawData) { certBio = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); BIO_write(certBio.get(), rawCert.c_str(), rawCert.length()); keyBio = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); BIO_write(keyBio.get(), rawKey.c_str(), rawKey.length()); } else { auto keyPath = fs::u8path(rawKey); auto certPath = fs::u8path(rawCert); if(!fs::exists(keyPath)) { if(!generator) { error = "Missing key file"; return nullptr; } try { if(keyPath.has_parent_path()) fs::create_directories(keyPath.parent_path()); } catch (fs::filesystem_error& error) { logError("Could not create key directory: " + string(error.what())); } { std::ofstream { keyPath }; } } if(!fs::exists(certPath)) { if(!generator) { error = "Missing certificate file"; return nullptr; } try { if(certPath.has_parent_path()) fs::create_directories(certPath.parent_path()); } catch (fs::filesystem_error& error) { logError("Could not create certificate directory: " + string(error.what())); } { std::ofstream { certPath }; } } auto mode = generator ? "rw" : "r"; certBio = shared_ptr(BIO_new_file(rawCert.c_str(), mode), ::BIO_free); if(!certBio) SSL_ERROR("Could not load certificate: "); keyBio = shared_ptr(BIO_new_file(rawKey.c_str(), mode), ::BIO_free); if(!keyBio) SSL_ERROR("Could not load key: "); } cert = shared_ptr(PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr), ::X509_free); if(!cert && !generator) SSL_ERROR("Could not read certificate: "); key = shared_ptr(PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); if(!key && !generator) SSL_ERROR("Could not read key: "); if(!key) { key = shared_ptr(generator->generateKey(), ::EVP_PKEY_free); flagKeyModified = true; } if(!cert) { cert = shared_ptr(generator->generateCertificate(key.get()), ::X509_free); flagCertModified = true; } //Create context context = shared_ptr(SSL_CTX_new(SSLv23_server_method()), ::SSL_CTX_free); if (!context) SSL_ERROR("Could not create context: "); if (SSL_CTX_use_PrivateKey(context.get(), key.get()) <= 0) SSL_ERROR("Could not use private key: "); if (SSL_CTX_use_certificate(context.get(), cert.get()) <= 0) SSL_ERROR("Could not use certificate: "); result = std::make_shared(); result->context = context; result->certificate = cert; result->privateKey = key; if(flagCertModified) { if(!rawData) { certBio = shared_ptr(BIO_new_file(rawCert.c_str(), "w"), ::BIO_free); if(PEM_write_bio_X509(certBio.get(), cert.get()) != 1) SSL_ERROR("Could not write new certificate: "); flagCertModified = false; } else { assert(false); //TODO implement with membuf /* void* ptr = nullptr; auto length = BIO_get_mem_data(certBio, &ptr); if(!ptr) SSL_ERROR("Could not get cert bio mem pointer: "); if(length <= 0) SSL_ERROR("Could not get cert bio mem length (" + to_string(length) + "): "); rawCert.reserve(length); memcpy((void*) rawCert.data(), ptr, length); */ } } if(flagKeyModified) { if(!rawData) { keyBio = shared_ptr(BIO_new_file(rawKey.c_str(), "w"), ::BIO_free); if(PEM_write_bio_PrivateKey(keyBio.get(), key.get(), nullptr, nullptr, 0, nullptr, nullptr) != 1) SSL_ERROR("Could not write new key: "); flagKeyModified = false; } else { assert(false); //TODO implement with membuf /* void* ptr = nullptr; auto length = BIO_get_mem_data(certBio, &ptr); if(!ptr) SSL_ERROR("Could not get cert bio mem pointer: "); if(length <= 0) SSL_ERROR("Could not get cert bio mem length (" + to_string(length) + "): "); rawCert.reserve(length); memcpy((void*) rawCert.data(), ptr, length); */ } } return result; } std::shared_ptr SSLManager::loadSSL(const std::string &rawKey, std::string &error, bool rawData, bool readPublic) { std::shared_ptr keyBio = nullptr; std::shared_ptr key = nullptr; std::shared_ptr result = make_shared(); // SSL_CTX_set_ecdh_auto(ctx, 1); if(rawData) { keyBio = shared_ptr(BIO_new(BIO_s_mem()), ::BIO_free); BIO_write(keyBio.get(), rawKey.c_str(), rawKey.length()); } else { auto keyPath = fs::u8path(rawKey); if(!fs::exists(keyPath)) { try { if(keyPath.has_parent_path()) fs::create_directories(keyPath.parent_path()); } catch (fs::filesystem_error& error) { logError("Could not create key directory: " + string(error.what())); } { std::ofstream { keyPath }; } } keyBio = shared_ptr(BIO_new_file(rawKey.c_str(), "r"), ::BIO_free); if(!keyBio) SSL_ERROR("Could not load key: "); } if(readPublic) key = shared_ptr(PEM_read_bio_PUBKEY(keyBio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); else key = shared_ptr(PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr), ::EVP_PKEY_free); result->contains_private = !readPublic; if(!key) { if(readPublic) { SSL_ERROR("Could not read key!"); } else return this->loadSSL(rawKey, error, rawData, true); } result->key = key; return result; } bool SSLManager::verifySign(const std::shared_ptr &key, const std::string &message, const std::string &sign) { assert(key); auto hash = digest::sha256(message); return RSA_verify(NID_sha256, (u_char*) hash.data(), hash.length(), (u_char*) sign.data(), sign.length(), EVP_PKEY_get1_RSA(key->key.get())) == 1; } std::shared_ptr SSLManager::web_ssl_options() { lock_guard lock(this->_web_options_lock); if(this->_web_options || this->_web_disabled) return this->_web_options; this->_web_options = make_shared(); this->_web_options->type = pipes::SSL::SERVER; this->_web_options->context_method = TLS_method(); this->_web_options->free_unused_keypairs = false; /* we dont want our keys get removed */ string web_prefix = "web_"; for(auto& context : this->contexts) { auto name = context.first; if(name.length() < web_prefix.length()) continue; if(name.substr(0, web_prefix.length()) != web_prefix) continue; auto servername = context.first.substr(web_prefix.length()); if(servername == "default") { this->_web_options->default_keypair({context.second->privateKey, context.second->certificate}); } else { this->_web_options->servername_keys[servername] = {context.second->privateKey, context.second->certificate}; } } return this->_web_options; }