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);
}
Example #3
0
    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);
    }
Example #4
0
    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));
}
Example #9
0
    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();
        }
    }
Example #12
0
        /**
         * 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;
        }
Example #13
0
void ASIOSSLContext::init(SSLManagerInterface::ConnectionDirection direction) {
    if (_mode != SSLParams::SSLMode_disabled) {
        uassertStatusOK(getSSLManager()->initSSLContext(
                            _context->native_handle(), getSSLGlobalParams(), direction));
    }
}
Example #14
0
    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;
    }
Example #15
0
    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) {
Example #16
0
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();
}