+ SSL_CTX* const ctx;
+
+ public:
+ Context(SSL_CTX* context)
+ : ctx(context)
+ {
+ SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
+ SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, OnVerify);
+
+ const unsigned char session_id[] = "inspircd";
+ SSL_CTX_set_session_id_context(ctx, session_id, sizeof(session_id) - 1);
+ }
+
+ ~Context()
+ {
+ SSL_CTX_free(ctx);
+ }
+
+ bool SetDH(DHParams& dh)
+ {
+ return (SSL_CTX_set_tmp_dh(ctx, dh.get()) >= 0);
+ }
+
+ bool SetCiphers(const std::string& ciphers)
+ {
+ return SSL_CTX_set_cipher_list(ctx, ciphers.c_str());
+ }
+
+ bool SetCerts(const std::string& filename)
+ {
+ return SSL_CTX_use_certificate_chain_file(ctx, filename.c_str());
+ }
+
+ bool SetPrivateKey(const std::string& filename)
+ {
+ return SSL_CTX_use_PrivateKey_file(ctx, filename.c_str(), SSL_FILETYPE_PEM);
+ }
+
+ bool SetCA(const std::string& filename)
+ {
+ return SSL_CTX_load_verify_locations(ctx, filename.c_str(), 0);
+ }
+
+ SSL* CreateSession()
+ {
+ return SSL_new(ctx);
+ }
+ };
+
+ class Profile : public refcountbase
+ {
+ /** Name of this profile
+ */
+ const std::string name;
+
+ /** DH parameters in use
+ */
+ DHParams dh;
+
+ /** OpenSSL makes us have two contexts, one for servers and one for clients
+ */
+ Context ctx;
+ Context clictx;
+
+ /** Digest to use when generating fingerprints
+ */
+ const EVP_MD* digest;
+
+ /** Last error, set by error_callback()
+ */
+ std::string lasterr;
+
+ static int error_callback(const char* str, size_t len, void* u)
+ {
+ Profile* profile = reinterpret_cast<Profile*>(u);
+ profile->lasterr = std::string(str, len - 1);
+ return 0;
+ }
+
+ public:
+ Profile(const std::string& profilename, ConfigTag* tag)
+ : name(profilename)
+ , dh(ServerInstance->Config->Paths.PrependConfig(tag->getString("dhfile", "dh.pem")))
+ , ctx(SSL_CTX_new(SSLv23_server_method()))
+ , clictx(SSL_CTX_new(SSLv23_client_method()))
+ {
+ if ((!ctx.SetDH(dh)) || (!clictx.SetDH(dh)))
+ throw Exception("Couldn't set DH parameters");
+
+ std::string hash = tag->getString("hash", "md5");
+ digest = EVP_get_digestbyname(hash.c_str());
+ if (digest == NULL)
+ throw Exception("Unknown hash type " + hash);
+
+ std::string ciphers = tag->getString("ciphers");
+ if (!ciphers.empty())
+ {
+ if ((!ctx.SetCiphers(ciphers)) || (!clictx.SetCiphers(ciphers)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't set cipher list to \"" + ciphers + "\" " + lasterr);
+ }
+ }
+
+ /* Load our keys and certificates
+ * NOTE: OpenSSL's error logging API sucks, don't blame us for this clusterfuck.
+ */
+ std::string filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("certfile", "cert.pem"));
+ if ((!ctx.SetCerts(filename)) || (!clictx.SetCerts(filename)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't read certificate file: " + lasterr);
+ }
+
+ filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("keyfile", "key.pem"));
+ if ((!ctx.SetPrivateKey(filename)) || (!clictx.SetPrivateKey(filename)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ throw Exception("Can't read key file: " + lasterr);
+ }
+
+ // Load the CAs we trust
+ filename = ServerInstance->Config->Paths.PrependConfig(tag->getString("cafile", "ca.pem"));
+ if ((!ctx.SetCA(filename)) || (!clictx.SetCA(filename)))
+ {
+ ERR_print_errors_cb(error_callback, this);
+ ServerInstance->Logs->Log(MODNAME, LOG_DEFAULT, "Can't read CA list from %s. This is only a problem if you want to verify client certificates, otherwise it's safe to ignore this message. Error: %s", filename.c_str(), lasterr.c_str());
+ }
+ }
+
+ const std::string& GetName() const { return name; }
+ SSL* CreateServerSession() { return ctx.CreateSession(); }
+ SSL* CreateClientSession() { return clictx.CreateSession(); }
+ const EVP_MD* GetDigest() { return digest; }
+ };
+}