Status CmdAuthenticate::_authenticateX509(const UserName& user, const BSONObj& cmdObj) { if (!getSSLManager()) { return Status(ErrorCodes::ProtocolError, "SSL support is required for the MONGODB-X509 mechanism."); } if(user.getDB() != "$external") { return Status(ErrorCodes::ProtocolError, "X.509 authentication must always use the $external database."); } ClientBasic *client = ClientBasic::getCurrent(); AuthorizationSession* authorizationSession = client->getAuthorizationSession(); std::string subjectName = client->port()->getX509SubjectName(); if (user.getUser() != subjectName) { return Status(ErrorCodes::AuthenticationFailed, "There is no x.509 client certificate matching the user."); } else { std::string srvSubjectName = getSSLManager()->getServerSubjectName(); size_t srvClusterIdPos = srvSubjectName.find(",OU="); size_t peerClusterIdPos = subjectName.find(",OU="); std::string srvClusterId = srvClusterIdPos != std::string::npos ? srvSubjectName.substr(srvClusterIdPos) : ""; std::string peerClusterId = peerClusterIdPos != std::string::npos ? subjectName.substr(peerClusterIdPos) : ""; // Handle internal cluster member auth, only applies to server-server connections int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); if (srvClusterId == peerClusterId && !srvClusterId.empty()) { if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_undefined || clusterAuthMode == ServerGlobalParams::ClusterAuthMode_keyFile) { return Status(ErrorCodes::AuthenticationFailed, "The provided certificate " "can only be used for cluster authentication, not client " "authentication. The current configuration does not allow " "x.509 cluster authentication, check the --clusterAuthMode flag"); } authorizationSession->grantInternalAuthorization(); } // Handle normal client authentication, only applies to client-server connections else { if (_isX509AuthDisabled) { return Status(ErrorCodes::BadValue, _x509AuthenticationDisabledMessage); } Status status = authorizationSession->addAndAuthorizeUser(user); if (!status.isOK()) { return status; } } return Status::OK(); } }
void NetworkInterfaceASIO::_authenticate(AsyncOp* op) { // There is currently no way for NetworkInterfaceASIO's users to run a command // without going through _authenticate(). Callers may want to run certain commands, // such as ismasters, pre-auth. We may want to offer this choice in the future. // This check is sufficient to see if auth is enabled on the system, // and avoids creating dependencies on deeper, less accessible auth code. if (!isInternalAuthSet()) { return _runConnectionHook(op); } // We will only have a valid clientName if SSL is enabled. std::string clientName; #ifdef MONGO_CONFIG_SSL if (getSSLManager()) { clientName = getSSLManager()->getSSLConfiguration().clientSubjectName; } #endif // authenticateClient will use this to run auth-related commands over our connection. auto runCommandHook = [this, op](executor::RemoteCommandRequest request, auth::AuthCompletionHandler handler) { // SERVER-14170: Set the metadataHook to nullptr explicitly as we cannot write metadata // here. auto beginStatus = op->beginCommand(request); if (!beginStatus.isOK()) { return handler(beginStatus); } auto callAuthCompletionHandler = [this, op, handler]() { auto authResponse = op->command()->response(op, op->operationProtocol(), now(), nullptr); handler(authResponse); }; _asyncRunCommand(op, [this, op, callAuthCompletionHandler](std::error_code ec, size_t bytes) { _validateAndRun(op, ec, callAuthCompletionHandler); }); }; // This will be called when authentication has completed. auto authHook = [this, op](auth::AuthResponse response) { if (!response.isOK()) return _completeOperation(op, response); return _runConnectionHook(op); }; auto params = getInternalUserAuthParams(); auth::authenticateClient( params, op->request().target.host(), clientName, runCommandHook, authHook); }
SSLConnection::SSLConnection(SSL_CTX* context, Socket* sock) : socket(sock) { // This just ensures that SSL multithreading support is set up for this thread, // if it's not already. SSLThreadInfo::get(); ssl = SSL_new(context); std::string sslErr = NULL != getSSLManager() ? getSSLManager()->getSSLErrorMessage(ERR_get_error()) : ""; massert(15861, "Error creating new SSL object " + sslErr, ssl); BIO_new_bio_pair(&internalBIO, BUFFER_SIZE, &networkBIO, BUFFER_SIZE); SSL_set_bio(ssl, internalBIO, internalBIO); }
Listener::Listener(const string& name, const string &ip, int port, bool logConnect ) : _port(port), _name(name), _ip(ip), _setupSocketsSuccessful(false), _logConnect(logConnect), _elapsedTime(0) { #ifdef MONGO_SSL _ssl = getSSLManager(); #endif }
void AsyncSecureStream::_handleHandshake(std::error_code ec, const std::string& hostName) { auto certStatus = getSSLManager()->parseAndValidatePeerCertificate(_stream.native_handle(), hostName); if (!certStatus.isOK()) { warning() << certStatus.getStatus(); } _userHandler(make_error_code(certStatus.getStatus().code())); }
void AsyncSecureStream::_handleHandshake(std::error_code ec, const std::string& hostName) { auto certStatus = getSSLManager()->parseAndValidatePeerCertificate(_stream.native_handle(), hostName); if (!certStatus.isOK()) { warning() << certStatus.getStatus(); return _userHandler( // TODO: fix handling of std::error_code w.r.t. codes used by Status std::error_code(certStatus.getStatus().code(), std::generic_category())); } _userHandler(std::error_code()); }
bool initializeServerGlobalState() { Listener::globalTicketHolder.resize(serverGlobalParams.maxConns).transitional_ignore(); #ifndef _WIN32 if (!serverGlobalParams.noUnixSocket && !fs::is_directory(serverGlobalParams.socket)) { cout << serverGlobalParams.socket << " must be a directory" << endl; return false; } #endif if (!serverGlobalParams.pidFile.empty()) { if (!writePidFile(serverGlobalParams.pidFile)) { // error message logged in writePidFile return false; } } int clusterAuthMode = serverGlobalParams.clusterAuthMode.load(); if (!serverGlobalParams.keyFile.empty() && clusterAuthMode != ServerGlobalParams::ClusterAuthMode_x509) { if (!setUpSecurityKey(serverGlobalParams.keyFile)) { // error message printed in setUpPrivateKey return false; } } // Auto-enable auth unless we are in mixed auth/no-auth or clusterAuthMode was not provided. // clusterAuthMode defaults to "keyFile" if a --keyFile parameter is provided. if (clusterAuthMode != ServerGlobalParams::ClusterAuthMode_undefined && !serverGlobalParams.transitionToAuth) { getGlobalAuthorizationManager()->setAuthEnabled(true); } #ifdef MONGO_CONFIG_SSL if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509 || clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509) { setInternalUserAuthParams( BSON(saslCommandMechanismFieldName << "MONGODB-X509" << saslCommandUserDBFieldName << "$external" << saslCommandUserFieldName << getSSLManager()->getSSLConfiguration().clientSubjectName)); } #endif return true; }
std::unique_ptr<NetworkInterface> makeNetworkInterface( std::unique_ptr<NetworkConnectionHook> hook, std::unique_ptr<rpc::EgressMetadataHook> metadataHook) { NetworkInterfaceASIO::Options options{}; options.networkConnectionHook = std::move(hook); options.metadataHook = std::move(metadataHook); options.timerFactory = stdx::make_unique<AsyncTimerFactoryASIO>(); #ifdef MONGO_CONFIG_SSL if (SSLManagerInterface* manager = getSSLManager()) { options.streamFactory = stdx::make_unique<AsyncSecureStreamFactory>(manager); } #endif if (!options.streamFactory) options.streamFactory = stdx::make_unique<AsyncStreamFactory>(); return stdx::make_unique<NetworkInterfaceASIO>(std::move(options)); }
void BackgroundJob::jobBody() { const string threadName = name(); if (!threadName.empty()) { setThreadName(threadName.c_str()); } LOG(1) << "BackgroundJob starting: " << threadName << endl; try { run(); } catch (const std::exception& e) { error() << "backgroundjob " << threadName << " exception: " << e.what(); throw; } // We must cache this value so that we can use it after we leave the following scope. const bool selfDelete = _selfDelete; #ifdef MONGO_CONFIG_SSL // TODO(sverch): Allow people who use the BackgroundJob to also specify cleanup tasks. // Currently the networking code depends on this class and this class depends on the // networking code because of this ad hoc cleanup. SSLManagerInterface* manager = getSSLManager(); if (manager) manager->cleanupThreadLocals(); #endif { // It is illegal to access any state owned by this BackgroundJob after leaving this // scope, with the exception of the call to 'delete this' below. std::unique_lock<std::mutex> l( _status->mutex ); _status->state = Done; _status->done.notify_all(); } if( selfDelete ) delete this; }
bool CmdAuthenticate::authenticateX509(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result) { if(dbname != "$external") { errmsg = "X.509 authentication must always use the $external database."; result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed); return false; } std::string user = cmdObj.getStringField("user"); ClientBasic *client = ClientBasic::getCurrent(); AuthorizationSession* authorizationSession = client->getAuthorizationSession(); StringData subjectName = client->port()->getX509SubjectName(); if (user != subjectName) { errmsg = "There is no x.509 client certificate matching the user."; result.append(saslCommandCodeFieldName, ErrorCodes::AuthenticationFailed); return false; } else { StringData srvSubjectName = getSSLManager()->getSubjectName(); StringData srvClusterId = srvSubjectName.substr(0, srvSubjectName.find("/CN")+1); StringData peerClusterId = subjectName.substr(0, subjectName.find("/CN")+1); // Handle internal cluster member if (srvClusterId == peerClusterId) { authorizationSession->grantInternalAuthorization(UserName(user, "$external")); } // Handle normal client authentication else { Principal* principal = new Principal(UserName(user, "$external")); principal->setImplicitPrivilegeAcquisition(true); authorizationSession->addAuthorizedPrincipal(principal); } result.append( "dbname" , dbname ); result.append( "user" , user ); return true; } }
Status CmdAuthenticate::_authenticateX509(const UserName& user, const BSONObj& cmdObj) { if(user.getDB() != "$external") { return Status(ErrorCodes::ProtocolError, "X.509 authentication must always use the $external database."); } ClientBasic *client = ClientBasic::getCurrent(); AuthorizationSession* authorizationSession = client->getAuthorizationSession(); StringData subjectName = client->port()->getX509SubjectName(); if (user.getUser() != subjectName) { return Status(ErrorCodes::AuthenticationFailed, "There is no x.509 client certificate matching the user."); } else { StringData srvSubjectName = getSSLManager()->getServerSubjectName(); StringData srvClusterId = srvSubjectName.substr(srvSubjectName.find(",OU=")); StringData peerClusterId = subjectName.substr(subjectName.find(",OU=")); fassert(17002, !srvClusterId.empty() && srvClusterId != srvSubjectName); // Handle internal cluster member auth, only applies to server-server connections if (srvClusterId == peerClusterId) { if (cmdLine.clusterAuthMode.empty() || cmdLine.clusterAuthMode == "keyfile") { return Status(ErrorCodes::AuthenticationFailed, "X509 authentication is not allowed for cluster authentication"); } authorizationSession->grantInternalAuthorization(user); } // Handle normal client authentication, only applies to client-server connections else { Principal* principal = new Principal(user); authorizationSession->addAndAuthorizePrincipal(principal); } return Status::OK(); } }
/** * Handles incoming messages from a given socket. * * Terminating conditions: * 1. Assertions while handling the request. * 2. Socket is closed. * 3. Server is shutting down (based on inShutdown) * * @param arg this method is in charge of cleaning up the arg object. * * @return NULL */ static void* handleIncomingMsg(void* arg) { TicketHolderReleaser connTicketReleaser( &Listener::globalTicketHolder ); scoped_ptr<HandleIncomingMsgParam> himArg(static_cast<HandleIncomingMsgParam*>(arg)); MessagingPort* inPort = himArg->inPort; MessageHandler* handler = himArg->handler; { string threadName = "conn"; if ( inPort->connectionId() > 0 ) threadName = str::stream() << threadName << inPort->connectionId(); setThreadName( threadName.c_str() ); } verify( inPort ); inPort->psock->setLogLevel(1); scoped_ptr<MessagingPort> p( inPort ); string otherSide; Message m; try { LastError * le = new LastError(); lastError.reset( le ); // lastError now has ownership otherSide = p->psock->remoteString(); #ifdef MONGO_SSL std::string x509SubjectName = p->psock->doSSLHandshake(); inPort->setX509SubjectName(x509SubjectName); #endif handler->connected( p.get() ); while ( ! inShutdown() ) { m.reset(); p->psock->clearCounters(); if ( ! p->recv(m) ) { if( !cmdLine.quiet ){ int conns = Listener::globalTicketHolder.used()-1; const char* word = (conns == 1 ? " connection" : " connections"); log() << "end connection " << otherSide << " (" << conns << word << " now open)" << endl; } p->shutdown(); break; } handler->process( m , p.get() , le ); networkCounter.hit( p->psock->getBytesIn() , p->psock->getBytesOut() ); } } catch ( AssertionException& e ) { log() << "AssertionException handling request, closing client connection: " << e << endl; p->shutdown(); } catch ( SocketException& e ) { log() << "SocketException handling request, closing client connection: " << e << endl; p->shutdown(); } catch ( const DBException& e ) { // must be right above std::exception to avoid catching subclasses log() << "DBException handling request, closing client connection: " << e << endl; p->shutdown(); } catch ( std::exception &e ) { error() << "Uncaught std::exception: " << e.what() << ", terminating" << endl; dbexit( EXIT_UNCAUGHT ); } catch ( ... ) { error() << "Uncaught exception, terminating" << endl; dbexit( EXIT_UNCAUGHT ); } // Normal disconnect path. #ifdef MONGO_SSL SSLManagerInterface* manager = getSSLManager(); if (manager) manager->cleanupThreadLocals(); #endif handler->disconnected( p.get() ); return NULL; }
void ASIOSSLContext::init(SSLManagerInterface::ConnectionDirection direction) { if (_mode != SSLParams::SSLMode_disabled) { uassertStatusOK(getSSLManager()->initSSLContext( _context->native_handle(), getSSLGlobalParams(), direction)); } }
int HttpClient::_go( const char * command , string url , const char * body , Result * result ) { bool ssl = false; if ( url.find( "https://" ) == 0 ) { ssl = true; url = url.substr( 8 ); } else { uassert( 10271 , "invalid url" , url.find( "http://" ) == 0 ); url = url.substr( 7 ); } string host , path; if ( url.find( "/" ) == string::npos ) { host = url; path = "/"; } else { host = url.substr( 0 , url.find( "/" ) ); path = url.substr( url.find( "/" ) ); } HD( "host [" << host << "]" ); HD( "path [" << path << "]" ); string server = host; int port = ssl ? 443 : 80; string::size_type idx = host.find( ":" ); if ( idx != string::npos ) { server = host.substr( 0 , idx ); string t = host.substr( idx + 1 ); port = atoi( t.c_str() ); } HD( "server [" << server << "]" ); HD( "port [" << port << "]" ); string req; { stringstream ss; ss << command << " " << path << " HTTP/1.1\r\n"; ss << "Host: " << host << "\r\n"; ss << "Connection: Close\r\n"; ss << "User-Agent: mongodb http client\r\n"; if ( body ) { ss << "Content-Length: " << strlen( body ) << "\r\n"; } ss << "\r\n"; if ( body ) { ss << body; } req = ss.str(); } SockAddr addr( server.c_str() , port ); uassert( 15000 , "server socket addr is invalid" , addr.isValid() ); HD( "addr: " << addr.toString() ); Socket sock; if ( ! sock.connect( addr ) ) return -1; if ( ssl ) { #ifdef MONGO_SSL // pointer to global singleton instance SSLManagerInterface* mgr = getSSLManager(); sock.secure(mgr, ""); #else uasserted( 15862 , "no ssl support" ); #endif } { const char * out = req.c_str(); int toSend = req.size(); sock.send( out , toSend, "_go" ); } char buf[4097]; int got = sock.unsafe_recv( buf , 4096 ); buf[got] = 0; int rc; char version[32]; verify( sscanf( buf , "%s %d" , version , &rc ) == 2 ); HD( "rc: " << rc ); StringBuilder sb; if ( result ) sb << buf; // SERVER-8864, unsafe_recv will throw when recv returns 0 indicating closed socket. try { while ( ( got = sock.unsafe_recv( buf , 4096 ) ) > 0) { buf[got] = 0; if ( result ) sb << buf; } } catch (const SocketException&) {} if ( result ) { result->_init( rc , sb.str() ); } return rc; }
return it->second; } #endif const auto getTLSVersionCounts = ServiceContext::declareDecoration<TLSVersionCounts>(); } // namespace TLSVersionCounts& TLSVersionCounts::get(ServiceContext* serviceContext) { return getTLSVersionCounts(serviceContext); } MONGO_INITIALIZER_WITH_PREREQUISITES(SSLManagerLogger, ("SSLManager", "GlobalLogManager")) (InitializerContext*) { if (!isSSLServer || (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled)) { const auto& config = getSSLManager()->getSSLConfiguration(); if (!config.clientSubjectName.empty()) { LOG(1) << "Client Certificate Name: " << config.clientSubjectName; } if (!config.serverSubjectName.empty()) { LOG(1) << "Server Certificate Name: " << config.serverSubjectName; LOG(1) << "Server Certificate Expiration: " << config.serverCertificateExpirationDate; } } return Status::OK(); } StatusWith<std::string> SSLX509Name::getOID(StringData oid) const { for (const auto& rdn : _entries) { for (const auto& entry : rdn) {
Status TransportLayerASIO::setup() { std::vector<std::string> listenAddrs; if (_listenerOptions.ipList.empty()) { listenAddrs = {"127.0.0.1"}; if (_listenerOptions.enableIPv6) { listenAddrs.emplace_back("::1"); } } else { boost::split( listenAddrs, _listenerOptions.ipList, boost::is_any_of(","), boost::token_compress_on); } #ifndef _WIN32 if (_listenerOptions.useUnixSockets) { listenAddrs.emplace_back(makeUnixSockPath(_listenerOptions.port)); } #endif for (auto& ip : listenAddrs) { std::error_code ec; if (ip.empty()) { warning() << "Skipping empty bind address"; continue; } SockAddr addr(StringData(ip), _listenerOptions.port, _listenerOptions.enableIPv6 ? AF_UNSPEC : AF_INET); asio::generic::stream_protocol::endpoint endpoint(addr.raw(), addr.addressSize); #ifndef _WIN32 if (addr.getType() == AF_UNIX) { if (::unlink(ip.c_str()) == -1 && errno != ENOENT) { error() << "Failed to unlink socket file " << ip << " " << errnoWithDescription(errno); fassertFailedNoTrace(40486); } } #endif if (addr.getType() == AF_INET6 && !_listenerOptions.enableIPv6) { error() << "Specified ipv6 bind address, but ipv6 is disabled"; fassertFailedNoTrace(40488); } GenericAcceptor acceptor(*_ioContext); acceptor.open(endpoint.protocol()); acceptor.set_option(GenericAcceptor::reuse_address(true)); acceptor.bind(endpoint, ec); if (ec) { return errorCodeToStatus(ec); } #ifndef _WIN32 if (addr.getType() == AF_UNIX) { if (::chmod(ip.c_str(), serverGlobalParams.unixSocketPermissions) == -1) { error() << "Failed to chmod socket file " << ip << " " << errnoWithDescription(errno); fassertFailedNoTrace(40487); } } #endif _acceptors.emplace_back(std::move(acceptor)); } invariant(!_acceptors.empty()); #ifdef MONGO_CONFIG_SSL const auto& sslParams = getSSLGlobalParams(); _sslMode = static_cast<SSLParams::SSLModes>(sslParams.sslMode.load()); if (_sslMode != SSLParams::SSLMode_disabled) { _sslContext = stdx::make_unique<asio::ssl::context>(asio::ssl::context::sslv23); const auto sslManager = getSSLManager(); sslManager ->initSSLContext(_sslContext->native_handle(), sslParams, SSLManagerInterface::ConnectionDirection::kOutgoing) .transitional_ignore(); } #endif return Status::OK(); }