/* Copyright 2013 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl #include "mongo/platform/basic.h" #include "mongo/util/net/ssl_options.h" #include #include "mongo/base/status.h" #include "mongo/db/server_options.h" #include "mongo/util/log.h" #include "mongo/util/text.h" #include "mongo/util/options_parser/startup_options.h" namespace mongo { using std::string; Status addSSLServerOptions(moe::OptionSection* options) { options->addOptionChaining("net.ssl.sslOnNormalPorts", "sslOnNormalPorts", moe::Switch, "use ssl on configured ports") .setSources(moe::SourceAllLegacy) .incompatibleWith("net.ssl.mode"); options->addOptionChaining("net.ssl.mode", "sslMode", moe::String, "set the SSL operation mode (disabled|allowSSL|preferSSL|requireSSL)"); options->addOptionChaining("net.ssl.PEMKeyFile", "sslPEMKeyFile", moe::String, "PEM file for ssl"); options->addOptionChaining("net.ssl.PEMKeyPassword", "sslPEMKeyPassword", moe::String, "PEM file password") .setImplicit(moe::Value(std::string(""))); options->addOptionChaining("net.ssl.clusterFile", "sslClusterFile", moe::String, "Key file for internal SSL authentication"); options->addOptionChaining("net.ssl.clusterPassword", "sslClusterPassword", moe::String, "Internal authentication key file password") .setImplicit(moe::Value(std::string(""))); options->addOptionChaining("net.ssl.CAFile", "sslCAFile", moe::String, "Certificate Authority file for SSL"); options->addOptionChaining("net.ssl.CRLFile", "sslCRLFile", moe::String, "Certificate Revocation List file for SSL"); options->addOptionChaining("net.ssl.sslCipherConfig", "sslCipherConfig", moe::String, "OpenSSL cipher configuration string") .hidden(); options->addOptionChaining("net.ssl.disabledProtocols", "sslDisabledProtocols", moe::String, "Comma separated list of disabled protocols") .hidden(); options->addOptionChaining("net.ssl.weakCertificateValidation", "sslWeakCertificateValidation", moe::Switch, "allow client to connect without " "presenting a certificate"); // Alias for --sslWeakCertificateValidation. options->addOptionChaining("net.ssl.allowConnectionsWithoutCertificates", "sslAllowConnectionsWithoutCertificates", moe::Switch, "allow client to connect without presenting a certificate"); options->addOptionChaining("net.ssl.allowInvalidHostnames", "sslAllowInvalidHostnames", moe::Switch, "Allow server certificates to provide non-matching hostnames"); options->addOptionChaining("net.ssl.allowInvalidCertificates", "sslAllowInvalidCertificates", moe::Switch, "allow connections to servers with invalid certificates"); options->addOptionChaining("net.ssl.FIPSMode", "sslFIPSMode", moe::Switch, "activate FIPS 140-2 mode at startup"); return Status::OK(); } Status addSSLClientOptions(moe::OptionSection* options) { options->addOptionChaining("ssl", "ssl", moe::Switch, "use SSL for all connections"); options->addOptionChaining("ssl.CAFile", "sslCAFile", moe::String, "Certificate Authority file for SSL") .requires("ssl"); options->addOptionChaining("ssl.PEMKeyFile", "sslPEMKeyFile", moe::String, "PEM certificate/key file for SSL") .requires("ssl"); options->addOptionChaining("ssl.PEMKeyPassword", "sslPEMKeyPassword", moe::String, "password for key in PEM file for SSL") .requires("ssl"); options->addOptionChaining("ssl.CRLFile", "sslCRLFile", moe::String, "Certificate Revocation List file for SSL") .requires("ssl") .requires("ssl.CAFile"); options->addOptionChaining("net.ssl.disabledProtocols", "sslDisabledProtocols", moe::String, "Comma separated list of disabled protocols") .requires("ssl") .hidden(); options->addOptionChaining("net.ssl.allowInvalidHostnames", "sslAllowInvalidHostnames", moe::Switch, "allow connections to servers with non-matching hostnames") .requires("ssl"); options->addOptionChaining("ssl.allowInvalidCertificates", "sslAllowInvalidCertificates", moe::Switch, "allow connections to servers with invalid certificates") .requires("ssl"); options->addOptionChaining("ssl.FIPSMode", "sslFIPSMode", moe::Switch, "activate FIPS 140-2 mode at startup") .requires("ssl"); return Status::OK(); } Status validateSSLServerOptions(const moe::Environment& params) { #ifdef _WIN32 if (params.count("install") || params.count("reinstall")) { if (params.count("net.ssl.PEMKeyFile") && !boost::filesystem::path(params["net.ssl.PEMKeyFile"].as()).is_absolute()) { return Status(ErrorCodes::BadValue, "PEMKeyFile requires an absolute file path with Windows services"); } if (params.count("net.ssl.clusterFile") && !boost::filesystem::path( params["net.ssl.clusterFile"].as()).is_absolute()) { return Status(ErrorCodes::BadValue, "clusterFile requires an absolute file path with Windows services"); } if (params.count("net.ssl.CAFile") && !boost::filesystem::path(params["net.ssl.CAFile"].as()).is_absolute()) { return Status(ErrorCodes::BadValue, "CAFile requires an absolute file path with Windows services"); } if (params.count("net.ssl.CRLFile") && !boost::filesystem::path(params["net.ssl.CRLFile"].as()).is_absolute()) { return Status(ErrorCodes::BadValue, "CRLFile requires an absolute file path with Windows services"); } } #endif return Status::OK(); } Status canonicalizeSSLServerOptions(moe::Environment* params) { if (params->count("net.ssl.sslOnNormalPorts") && (*params)["net.ssl.sslOnNormalPorts"].as() == true) { Status ret = params->set("net.ssl.mode", moe::Value(std::string("requireSSL"))); if (!ret.isOK()) { return ret; } ret = params->remove("net.ssl.sslOnNormalPorts"); if (!ret.isOK()) { return ret; } } return Status::OK(); } Status storeSSLServerOptions(const moe::Environment& params) { if (params.count("net.ssl.mode")) { std::string sslModeParam = params["net.ssl.mode"].as(); if (sslModeParam == "disabled") { sslGlobalParams.sslMode.store(SSLParams::SSLMode_disabled); } else if (sslModeParam == "allowSSL") { sslGlobalParams.sslMode.store(SSLParams::SSLMode_allowSSL); } else if (sslModeParam == "preferSSL") { sslGlobalParams.sslMode.store(SSLParams::SSLMode_preferSSL); } else if (sslModeParam == "requireSSL") { sslGlobalParams.sslMode.store(SSLParams::SSLMode_requireSSL); } else { return Status(ErrorCodes::BadValue, "unsupported value for sslMode " + sslModeParam ); } } if (params.count("net.ssl.PEMKeyFile")) { sslGlobalParams.sslPEMKeyFile = boost::filesystem::absolute( params["net.ssl.PEMKeyFile"].as()).generic_string(); } if (params.count("net.ssl.PEMKeyPassword")) { sslGlobalParams.sslPEMKeyPassword = params["net.ssl.PEMKeyPassword"].as(); } if (params.count("net.ssl.clusterFile")) { sslGlobalParams.sslClusterFile = boost::filesystem::absolute( params["net.ssl.clusterFile"].as()).generic_string(); } if (params.count("net.ssl.clusterPassword")) { sslGlobalParams.sslClusterPassword = params["net.ssl.clusterPassword"].as(); } if (params.count("net.ssl.CAFile")) { sslGlobalParams.sslCAFile = boost::filesystem::absolute( params["net.ssl.CAFile"].as()).generic_string(); } if (params.count("net.ssl.CRLFile")) { sslGlobalParams.sslCRLFile = boost::filesystem::absolute( params["net.ssl.CRLFile"].as()).generic_string(); } if (params.count("net.ssl.sslCipherConfig")) { sslGlobalParams.sslCipherConfig = params["net.ssl.sslCipherConfig"].as(); } if (params.count("net.ssl.disabledProtocols")) { std::vector tokens = StringSplitter::split( params["net.ssl.disabledProtocols"].as(), ","); const std::map validConfigs { {"noTLS1_0", SSLParams::Protocols::TLS1_0}, {"noTLS1_1", SSLParams::Protocols::TLS1_1}, {"noTLS1_2", SSLParams::Protocols::TLS1_2} }; for (const std::string& token : tokens) { auto mappedToken = validConfigs.find(token); if (mappedToken != validConfigs.end()) { sslGlobalParams.sslDisabledProtocols.push_back(mappedToken->second); } else { return Status(ErrorCodes::BadValue, "Unrecognized disabledProtocols '" + token +"'"); } } } if (params.count("net.ssl.weakCertificateValidation")) { sslGlobalParams.sslWeakCertificateValidation = params["net.ssl.weakCertificateValidation"].as(); } else if (params.count("net.ssl.allowConnectionsWithoutCertificates")) { sslGlobalParams.sslWeakCertificateValidation = params["net.ssl.allowConnectionsWithoutCertificates"].as(); } if (params.count("net.ssl.allowInvalidHostnames")) { sslGlobalParams.sslAllowInvalidHostnames = params["net.ssl.allowInvalidHostnames"].as(); } if (params.count("net.ssl.allowInvalidCertificates")) { sslGlobalParams.sslAllowInvalidCertificates = params["net.ssl.allowInvalidCertificates"].as(); } if (params.count("net.ssl.FIPSMode")) { sslGlobalParams.sslFIPSMode = params["net.ssl.FIPSMode"].as(); } int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); if (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled) { if (sslGlobalParams.sslPEMKeyFile.size() == 0) { return Status(ErrorCodes::BadValue, "need sslPEMKeyFile when SSL is enabled"); } if (sslGlobalParams.sslWeakCertificateValidation && sslGlobalParams.sslCAFile.empty()) { return Status(ErrorCodes::BadValue, "need sslCAFile with sslWeakCertificateValidation"); } if (!sslGlobalParams.sslCRLFile.empty() && sslGlobalParams.sslCAFile.empty()) { return Status(ErrorCodes::BadValue, "need sslCAFile with sslCRLFile"); } std::string sslCANotFoundError("No SSL certificate validation can be performed since" " no CA file has been provided; please specify an" " sslCAFile parameter"); if (sslGlobalParams.sslCAFile.empty()) { if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509) { return Status(ErrorCodes::BadValue, sslCANotFoundError); } warning() << sslCANotFoundError; } } else if (sslGlobalParams.sslPEMKeyFile.size() || sslGlobalParams.sslPEMKeyPassword.size() || sslGlobalParams.sslClusterFile.size() || sslGlobalParams.sslClusterPassword.size() || sslGlobalParams.sslCAFile.size() || sslGlobalParams.sslCRLFile.size() || sslGlobalParams.sslCipherConfig.size() || sslGlobalParams.sslDisabledProtocols.size() || sslGlobalParams.sslWeakCertificateValidation || sslGlobalParams.sslFIPSMode) { return Status(ErrorCodes::BadValue, "need to enable SSL via the sslMode flag when " "using SSL configuration parameters"); } if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendKeyFile || clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509 || clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509) { if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_disabled) { return Status(ErrorCodes::BadValue, "need to enable SSL via the sslMode flag"); } } if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_allowSSL) { if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509 || clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509) { return Status(ErrorCodes::BadValue, "cannot have x.509 cluster authentication in allowSSL mode"); } } return Status::OK(); } Status storeSSLClientOptions(const moe::Environment& params) { if (params.count("ssl") && params["ssl"].as() == true) { sslGlobalParams.sslMode.store(SSLParams::SSLMode_requireSSL); } if (params.count("ssl.PEMKeyFile")) { sslGlobalParams.sslPEMKeyFile = params["ssl.PEMKeyFile"].as(); } if (params.count("ssl.PEMKeyPassword")) { sslGlobalParams.sslPEMKeyPassword = params["ssl.PEMKeyPassword"].as(); } if (params.count("ssl.CAFile")) { sslGlobalParams.sslCAFile = params["ssl.CAFile"].as(); } if (params.count("ssl.CRLFile")) { sslGlobalParams.sslCRLFile = params["ssl.CRLFile"].as(); } if (params.count("net.ssl.allowInvalidHostnames")) { sslGlobalParams.sslAllowInvalidHostnames = params["net.ssl.allowInvalidHostnames"].as(); } if (params.count("ssl.allowInvalidCertificates")) { sslGlobalParams.sslAllowInvalidCertificates = true; } if (params.count("ssl.FIPSMode")) { sslGlobalParams.sslFIPSMode = true; } return Status::OK(); } Status validateSSLMongoShellOptions(const moe::Environment& params) { // Users must specify either a CAFile or allowInvalidCertificates if ssl=true. if (params.count("ssl") && params["ssl"].as() == true && !params.count("ssl.CAFile") && !params.count("ssl.allowInvalidCertificates")) { return Status(ErrorCodes::BadValue, "need to either provide sslCAFile or specify sslAllowInvalidCertificates"); } return Status::OK(); } } // namespace mongo