bool replAuthenticate(DBClientBase* conn) { if (isInternalAuthSet()) return conn->authenticateInternalUser(); if (getGlobalAuthorizationManager()->isAuthEnabled()) return false; return true; }
void setInternalUserAuthParams(const BSONObj& authParamsIn) { if (!isInternalAuthSet()) { authParamsSet = true; } boost::mutex::scoped_lock lk(authParamMutex); authParams = authParamsIn.copy(); }
bool isSelf(const HostAndPort& hostAndPort) { // Fastpath: check if the host&port in question is bound to one // of the interfaces on this machine. // No need for ip match if the ports do not match if (hostAndPort.port() == serverGlobalParams.port) { std::vector<std::string> myAddrs = serverGlobalParams.bind_ip.empty() ? getBoundAddrs(IPv6Enabled()) : std::vector<std::string>(); if (!serverGlobalParams.bind_ip.empty()) { boost::split(myAddrs, serverGlobalParams.bind_ip, boost::is_any_of(", ")); } const std::vector<std::string> hostAddrs = getAddrsForHost(hostAndPort.host(), hostAndPort.port(), IPv6Enabled()); for (std::vector<std::string>::const_iterator i = myAddrs.begin(); i != myAddrs.end(); ++i) { for (std::vector<std::string>::const_iterator j = hostAddrs.begin(); j != hostAddrs.end(); ++j) { if (*i == *j) { return true; } } } } if (!Listener::getTimeTracker()) { // this ensures we are actually running a server // this may return true later, so may want to retry return false; } try { DBClientConnection conn; std::string errmsg; if (!conn.connect(hostAndPort, errmsg)) { return false; } if (getGlobalAuthorizationManager()->isAuthEnabled() && isInternalAuthSet()) { if (!authenticateInternalUser(&conn)) { return false; } } BSONObj out; bool ok = conn.simpleCommand("admin" , &out, "_isSelf"); bool me = ok && out["id"].type() == jstOID && instanceId == out["id"].OID(); return me; } catch (const std::exception& e) { warning() << "could't check isSelf (" << hostAndPort << ") " << e.what() << std::endl; } return false; }
bool SyncSourceFeedback::replAuthenticate() { if (!getGlobalAuthorizationManager()->isAuthEnabled()) return true; if (!isInternalAuthSet()) return false; return authenticateInternalUser(_connection.get()); }
bool setInternalUserAuthParams(BSONObj authParams) { if (!isInternalAuthSet()) { internalSecurity.authParams = authParams.copy(); authParamsSet = true; return true; } else { log() << "Internal auth params have already been set" << endl; return false; } }
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); }
bool authenticateInternalUser(DBClientWithCommands* conn){ if (!isInternalAuthSet()) { log() << "ERROR: No authentication params set for internal user" << endl; return false; } try { conn->auth(internalSecurity.authParams); return true; } catch(const UserException& ex) { log() << "can't authenticate to " << conn->toString() << " as internal user, error: " << ex.what() << endl; return false; } }
bool authenticateInternalUser(DBClientWithCommands* conn) { if (!isInternalAuthSet()) { if (!serverGlobalParams.quiet) { log() << "ERROR: No authentication parameters set for internal user"; } return false; } try { conn->auth(getInternalUserAuthParamsWithFallback()); return true; } catch(const UserException& ex) { if (!serverGlobalParams.quiet) { log() << "can't authenticate to " << conn->toString() << " as internal user, error: "<< ex.what(); } return false; } }
bool authenticateInternalUser(DBClientWithCommands* conn){ if (!isInternalAuthSet()) { log() << "ERROR: No authentication parameters set for internal user" << endl; return false; } try { BSONObj outgoingAuthParams; { boost::mutex::scoped_lock lk(authParamMutex); outgoingAuthParams = authParams.copy(); } conn->auth(outgoingAuthParams); return true; } catch(const UserException& ex) { log() << "can't authenticate to " << conn->toString() << " as internal user, error: " << ex.what() << endl; return false; } }
void setInternalUserAuthParams(const BSONObj& authParamsIn) { if (!isInternalAuthSet()) { authParamsSet = true; } boost::mutex::scoped_lock lk(authParamMutex); if (authParamsIn["mechanism"].String() != "SCRAM-SHA-1") { authParams = authParamsIn.copy(); return; } // Create authParams for legacy MONGODB-CR authentication for 2.6/2.8 mixed // mode if applicable. mmb::Document fallback(authParamsIn); fallback.root().findFirstChildNamed("mechanism").setValueString("MONGODB-CR"); mmb::Document doc(authParamsIn); mmb::Element fallbackEl = doc.makeElementObject("fallbackParams"); fallbackEl.setValueObject(fallback.getObject()); doc.root().pushBack(fallbackEl); authParams = doc.getObject().copy(); }
/* Generally replAuthenticate will only be called within system threads to fully authenticate * connections to other nodes in the cluster that will be used as part of internal operations. * If a user-initiated action results in needing to call replAuthenticate, you can call it * with skipAuthCheck set to false. Only do this if you are certain that the proper auth * checks have already run to ensure that the user is authorized to do everything that this * connection will be used for! */ bool replAuthenticate(DBClientBase *conn, bool skipAuthCheck) { if(!AuthorizationManager::isAuthEnabled()) { return true; } if (!skipAuthCheck && !cc().getAuthorizationSession()->hasInternalAuthorization()) { log() << "replauthenticate: requires internal authorization, failing" << endl; return false; } if (isInternalAuthSet()) { return authenticateInternalUser(conn); } BSONObj user; { Client::ReadContext ctxt("local."); if( !Helpers::findOne("local.system.users", userReplQuery, user) || // try the first user in local !Helpers::getSingleton("local.system.users", user) ) { log() << "replauthenticate: no user in local.system.users to use for authentication" << endl; return false; } } std::string u = user.getStringField("user"); std::string p = user.getStringField("pwd"); massert( 10392 , "bad user object? [1]", !u.empty()); massert( 10393 , "bad user object? [2]", !p.empty()); std::string err; if( !conn->auth("local", u.c_str(), p.c_str(), err, false) ) { log() << "replauthenticate: can't authenticate to master server, user:" << u << endl; return false; } return true; }
ConnectionPool::ConnectionList::iterator ConnectionPool::acquireConnection( const HostAndPort& target, Date_t now, Milliseconds timeout) { boost::unique_lock<boost::mutex> lk(_mutex); // Clean up connections on stale/unused hosts _cleanUpStaleHosts_inlock(now); for (HostConnectionMap::iterator hostConns; ((hostConns = _connections.find(target)) != _connections.end());) { // Clean up the requested host to remove stale/unused connections _cleanUpOlderThan_inlock(now, &hostConns->second); if (hostConns->second.empty()) { // prevent host from causing unnecessary cleanups _lastUsedHosts[hostConns->first] = kNeverTooStale; break; } _inUseConnections.splice(_inUseConnections.begin(), hostConns->second, hostConns->second.begin()); const ConnectionList::iterator candidate = _inUseConnections.begin(); lk.unlock(); try { if (candidate->conn->isStillConnected()) { // setSoTimeout takes a double representing the number of seconds for send and // receive timeouts. Thus, we must take count() and divide by // 1000.0 to get the number of seconds with a fractional part. candidate->conn->setSoTimeout(timeout.count() / 1000.0); return candidate; } } catch (...) { lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); throw; } lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); } // No idle connection in the pool; make a new one. lk.unlock(); std::auto_ptr<DBClientConnection> conn(new DBClientConnection); // setSoTimeout takes a double representing the number of seconds for send and receive // timeouts. Thus, we must take count() and divide by 1000.0 to get the number // of seconds with a fractional part. conn->setSoTimeout(timeout.count() / 1000.0); std::string errmsg; uassert(28640, str::stream() << "Failed attempt to connect to " << target.toString() << "; " << errmsg, conn->connect(target, errmsg)); conn->port().tag |= _messagingPortTags; if (getGlobalAuthorizationManager()->isAuthEnabled()) { uassert(ErrorCodes::AuthenticationFailed, "Missing credentials for authenticating as internal user", isInternalAuthSet()); conn->auth(getInternalUserAuthParamsWithFallback()); } lk.lock(); return _inUseConnections.insert(_inUseConnections.begin(), ConnectionInfo(conn.release(), now)); }
ConnectionPool::ConnectionList::iterator ConnectionPool::acquireConnection( const HostAndPort& target, Date_t now, Milliseconds timeout) { stdx::unique_lock<stdx::mutex> lk(_mutex); // Clean up connections on stale/unused hosts _cleanUpStaleHosts_inlock(now); for (HostConnectionMap::iterator hostConns; (hostConns = _connections.find(target)) != _connections.end();) { // Clean up the requested host to remove stale/unused connections _cleanUpOlderThan_inlock(now, &hostConns->second); if (hostConns->second.empty()) { // prevent host from causing unnecessary cleanups _lastUsedHosts[hostConns->first] = kNeverTooStale; break; } _inUseConnections.splice( _inUseConnections.begin(), hostConns->second, hostConns->second.begin()); const ConnectionList::iterator candidate = _inUseConnections.begin(); lk.unlock(); try { if (candidate->conn->isStillConnected()) { // setSoTimeout takes a double representing the number of seconds for send and // receive timeouts. Thus, we must take count() and divide by // 1000.0 to get the number of seconds with a fractional part. candidate->conn->setSoTimeout(timeout.count() / 1000.0); return candidate; } } catch (...) { lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); throw; } lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); } // No idle connection in the pool; make a new one. lk.unlock(); std::unique_ptr<DBClientConnection> conn(new DBClientConnection()); // setSoTimeout takes a double representing the number of seconds for send and receive // timeouts. Thus, we must take count() and divide by 1000.0 to get the number // of seconds with a fractional part. conn->setSoTimeout(timeout.count() / 1000.0); if (_hook) { uassertStatusOK( conn->connect(target, [this, &target](const executor::RemoteCommandResponse& isMasterReply) { return _hook->validateHost(target, isMasterReply); })); auto postConnectRequest = uassertStatusOK(_hook->makeRequest(target)); // We might not have a postConnectRequest if (postConnectRequest != boost::none) { auto start = Date_t::now(); auto reply = conn->runCommandWithMetadata(postConnectRequest->dbname, postConnectRequest->cmdObj.firstElementFieldName(), postConnectRequest->metadata, postConnectRequest->cmdObj); auto rcr = executor::RemoteCommandResponse(reply->getCommandReply().getOwned(), reply->getMetadata().getOwned(), Date_t::now() - start); uassertStatusOK(_hook->handleReply(target, std::move(rcr))); } } else { uassertStatusOK(conn->connect(target)); } conn->port().tag |= _messagingPortTags; if (getGlobalAuthorizationManager()->isAuthEnabled()) { uassert(ErrorCodes::AuthenticationFailed, "Missing credentials for authenticating as internal user", isInternalAuthSet()); conn->auth(getInternalUserAuthParamsWithFallback()); } lk.lock(); return _inUseConnections.insert(_inUseConnections.begin(), ConnectionInfo(conn.release(), now)); }
bool HostAndPort::isSelf() const { int _p = port(); int p = _p == -1 ? ServerGlobalParams::DefaultDBPort : _p; string host = str::stream() << this->host() << ":" << p; { // check cache for this host // debatably something _could_ change, but I'm not sure right now (erh 10/14/2010) scoped_lock lk( isSelfCommand._cacheLock ); map<string,bool>::const_iterator i = isSelfCommand._cache.find( host ); if ( i != isSelfCommand._cache.end() ) return i->second; } #if !defined(_WIN32) && !defined(__sunos__) // on linux and os x we can do a quick check for an ip match // no need for ip match if the ports do not match if (p == serverGlobalParams.port) { const vector<string> myaddrs = getMyAddrs(); const vector<string> addrs = getAllIPs(_host); for (vector<string>::const_iterator i=myaddrs.begin(), iend=myaddrs.end(); i!=iend; ++i) { for (vector<string>::const_iterator j=addrs.begin(), jend=addrs.end(); j!=jend; ++j) { string a = *i; string b = *j; if ( a == b || ( str::startsWith( a , "127." ) && str::startsWith( b , "127." ) ) // 127. is all loopback ) { // add to cache scoped_lock lk( isSelfCommand._cacheLock ); isSelfCommand._cache[host] = true; return true; } } } } #endif if ( ! Listener::getTimeTracker() ) { // this ensures we are actually running a server // this may return true later, so may want to retry return false; } try { isSelfCommand.init(); DBClientConnection conn; string errmsg; if ( ! conn.connect( host , errmsg ) ) { // should this go in the cache? return false; } if (getGlobalAuthorizationManager()->isAuthEnabled() && isInternalAuthSet()) { if (!authenticateInternalUser(&conn)) { return false; } } BSONObj out; bool ok = conn.simpleCommand( "admin" , &out , "_isSelf" ); bool me = ok && out["id"].type() == jstOID && isSelfCommand._id == out["id"].OID(); // add to cache scoped_lock lk( isSelfCommand._cacheLock ); isSelfCommand._cache[host] = me; return me; } catch ( std::exception& e ) { warning() << "could't check isSelf (" << host << ") " << e.what() << endl; } return false; }
bool isSelf(const HostAndPort& hostAndPort) { // Fastpath: check if the host&port in question is bound to one // of the interfaces on this machine. // No need for ip match if the ports do not match if (hostAndPort.port() == serverGlobalParams.port) { std::vector<std::string> myAddrs = serverGlobalParams.bind_ip.empty() ? getBoundAddrs(IPv6Enabled()) : std::vector<std::string>(); if (!serverGlobalParams.bind_ip.empty()) { boost::split(myAddrs, serverGlobalParams.bind_ip, boost::is_any_of(", ")); } const std::vector<std::string> hostAddrs = getAddrsForHost(hostAndPort.host(), hostAndPort.port(), IPv6Enabled()); for (std::vector<std::string>::const_iterator i = myAddrs.begin(); i != myAddrs.end(); ++i) { for (std::vector<std::string>::const_iterator j = hostAddrs.begin(); j != hostAddrs.end(); ++j) { if (*i == *j) { return true; } } } } // Ensure that the server is up and ready to accept incoming network requests. const Listener* listener = Listener::getTimeTracker(); if (!listener) { return false; } listener->waitUntilListening(); try { DBClientConnection conn; conn.setSoTimeout(30); // 30 second timeout // We need to avoid the isMaster call triggered by a normal connect, which would // cause a deadlock. 'isSelf' is called by the Replication Coordinator when validating // a replica set configuration document, but the 'isMaster' command requires a lock on the // replication coordinator to execute. As such we call we call 'connectSocketOnly', which // does not call 'isMaster'. if (!conn.connectSocketOnly(hostAndPort).isOK()) { return false; } if (getGlobalAuthorizationManager()->isAuthEnabled() && isInternalAuthSet()) { if (!conn.authenticateInternalUser()) { return false; } } BSONObj out; bool ok = conn.simpleCommand("admin", &out, "_isSelf"); bool me = ok && out["id"].type() == jstOID && instanceId == out["id"].OID(); return me; } catch (const std::exception& e) { warning() << "couldn't check isSelf (" << hostAndPort << ") " << e.what() << std::endl; } return false; }
ConnectionPool::ConnectionList::iterator ConnectionPool::acquireConnection( const HostAndPort& target, Date_t now, Milliseconds timeout) { stdx::unique_lock<stdx::mutex> lk(_mutex); // Clean up connections on stale/unused hosts _cleanUpStaleHosts_inlock(now); for (HostConnectionMap::iterator hostConns; (hostConns = _connections.find(target)) != _connections.end();) { // Clean up the requested host to remove stale/unused connections _cleanUpOlderThan_inlock(now, &hostConns->second); if (hostConns->second.empty()) { // prevent host from causing unnecessary cleanups _lastUsedHosts[hostConns->first] = kNeverTooStale; break; } _inUseConnections.splice( _inUseConnections.begin(), hostConns->second, hostConns->second.begin()); const ConnectionList::iterator candidate = _inUseConnections.begin(); lk.unlock(); try { if (candidate->conn->isStillConnected()) { // setSoTimeout takes a double representing the number of seconds for send and // receive timeouts. Thus, we must express 'timeout' in milliseconds and divide by // 1000.0 to get the number of seconds with a fractional part. candidate->conn->setSoTimeout(durationCount<Milliseconds>(timeout) / 1000.0); return candidate; } } catch (...) { lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); throw; } lk.lock(); _destroyConnection_inlock(&_inUseConnections, candidate); } // No idle connection in the pool; make a new one. lk.unlock(); std::unique_ptr<DBClientConnection> conn; if (_hook) { conn.reset(new DBClientConnection( false, // auto reconnect 0, // socket timeout {}, // MongoURI [this, target](const executor::RemoteCommandResponse& isMasterReply) { return _hook->validateHost(target, BSONObj(), isMasterReply); })); } else { conn.reset(new DBClientConnection()); } // setSoTimeout takes a double representing the number of seconds for send and receive // timeouts. Thus, we must express 'timeout' in milliseconds and divide by 1000.0 to get // the number of seconds with a fractional part. conn->setSoTimeout(durationCount<Milliseconds>(timeout) / 1000.0); uassertStatusOK(conn->connect(target, StringData())); conn->setTags(_messagingPortTags); if (isInternalAuthSet()) { conn->auth(getInternalUserAuthParams()); } if (_hook) { auto postConnectRequest = uassertStatusOK(_hook->makeRequest(target)); // We might not have a postConnectRequest if (postConnectRequest != boost::none) { auto start = Date_t::now(); auto reply = conn->runCommand(OpMsgRequest::fromDBAndBody(postConnectRequest->dbname, postConnectRequest->cmdObj, postConnectRequest->metadata)); auto rcr = executor::RemoteCommandResponse(reply->getCommandReply().getOwned(), Date_t::now() - start); uassertStatusOK(_hook->handleReply(target, std::move(rcr))); } } lk.lock(); return _inUseConnections.insert(_inUseConnections.begin(), ConnectionInfo(conn.release(), now)); }