Пример #1
0
static Status _checkAuthorizationImpl(Command* c,
                                      OperationContext* opCtx,
                                      const OpMsgRequest& request) {
    namespace mmb = mutablebson;
    auto client = opCtx->getClient();
    auto dbname = request.getDatabase();
    if (c->adminOnly() && dbname != "admin") {
        return Status(ErrorCodes::Unauthorized,
                      str::stream() << c->getName()
                                    << " may only be run against the admin database.");
    }
    if (AuthorizationSession::get(client)->getAuthorizationManager().isAuthEnabled()) {
        Status status = c->checkAuthForRequest(opCtx, request);
        if (status == ErrorCodes::Unauthorized) {
            mmb::Document cmdToLog(request.body, mmb::Document::kInPlaceDisabled);
            c->redactForLogging(&cmdToLog);
            return Status(ErrorCodes::Unauthorized,
                          str::stream() << "not authorized on " << dbname << " to execute command "
                                        << cmdToLog.toString());
        }
        if (!status.isOK()) {
            return status;
        }
    } else if (c->adminOnly() && c->localHostOnlyIfNoAuth() &&
               !client->getIsLocalHostConnection()) {
        return Status(ErrorCodes::Unauthorized,
                      str::stream() << c->getName()
                                    << " must run from localhost when running db without auth");
    }
    return Status::OK();
}
Пример #2
0
void CommandInvocation::checkAuthorization(OperationContext* opCtx,
                                           const OpMsgRequest& request) const {
    // Always send an authorization event to audit log, even if OK.
    // Not using a scope guard because auditLogAuthEvent could conceivably throw.
    try {
        const Command* c = definition();
        if (checkAuthorizationImplPreParse(opCtx, c, request)) {
            // Blanket authorization: don't need to check anything else.
        } else {
            try {
                doCheckAuthorization(opCtx);
            } catch (const ExceptionFor<ErrorCodes::Unauthorized>&) {
                namespace mmb = mutablebson;
                mmb::Document cmdToLog(request.body, mmb::Document::kInPlaceDisabled);
                c->snipForLogging(&cmdToLog);
                auto dbname = request.getDatabase();
                uasserted(ErrorCodes::Unauthorized,
                          str::stream() << "not authorized on " << dbname << " to execute command "
                                        << redact(cmdToLog.getObject()));
            }
        }
    } catch (const DBException& e) {
        log(LogComponent::kAccessControl) << e.toStatus();
        CommandHelpers::auditLogAuthEvent(opCtx, this, request, e.code());
        throw;
    }
    CommandHelpers::auditLogAuthEvent(opCtx, this, request, ErrorCodes::OK);
}
Пример #3
0
rpc::UniqueReply MockRemoteDBServer::runCommand(InstanceID id, const OpMsgRequest& request) {
    checkIfUp(id);
    std::string cmdName = request.getCommandName().toString();

    BSONObj reply;
    {
        scoped_spinlock lk(_lock);

        uassert(ErrorCodes::IllegalOperation,
                str::stream() << "no reply for command: " << cmdName,
                _cmdMap.count(cmdName));

        reply = _cmdMap[cmdName]->next();
    }

    if (_delayMilliSec > 0) {
        mongo::sleepmillis(_delayMilliSec);
    }

    checkIfUp(id);

    {
        scoped_spinlock lk(_lock);
        _cmdCount++;
    }

    // We need to construct a reply message - it will always be read through a view so it
    // doesn't matter whether we use OpMsgReplyBuilder or LegacyReplyBuilder
    auto message = rpc::OpMsgReplyBuilder{}.setCommandReply(reply).done();
    auto replyView = stdx::make_unique<rpc::OpMsgReply>(&message);
    return rpc::UniqueReply(std::move(message), std::move(replyView));
}
Message opCommandRequestFromOpMsgRequest(const OpMsgRequest& request) {
    const auto commandName = request.getCommandName();

    BufBuilder builder;
    builder.skip(mongo::MsgData::MsgDataHeaderSize);  // Leave room for message header.
    builder.appendStr(request.getDatabase());
    builder.appendStr(commandName);

    // OP_COMMAND is only used when communicating with 3.4 nodes and they serialize their metadata
    // fields differently. In addition to field-level differences, some generic arguments are pulled
    // out to a metadata object, separate from the body. We do all down-conversion here so that the
    // rest of the code only has to deal with the current format.
    BSONObjBuilder metadataBuilder;  // Will be appended to the message after we finish the body.
    {
        BSONObjBuilder bodyBuilder(builder);
        for (auto elem : request.body) {
            const auto fieldName = elem.fieldNameStringData();
            if (fieldName == "$configServerState") {
                metadataBuilder.appendAs(elem, "configsvr");
            } else if (fieldName == "$readPreference") {
                BSONObjBuilder ssmBuilder(metadataBuilder.subobjStart("$ssm"));
                ssmBuilder.append(elem);
                ssmBuilder.append("$secondaryOk",
                                  uassertStatusOK(ReadPreferenceSetting::fromInnerBSON(elem))
                                      .canRunOnSecondary());
            } else if (fieldName == "$db") {
                // skip
            } else if (fieldGoesInMetadata(commandName, fieldName)) {
                metadataBuilder.append(elem);
            } else {
                bodyBuilder.append(elem);
            }
        }
        for (auto&& seq : request.sequences) {
            invariant(seq.name.find('.') == std::string::npos);  // Only support top-level for now.
            dassert(!bodyBuilder.asTempObj().hasField(seq.name));
            bodyBuilder.append(seq.name, seq.objs);
        }
    }
    metadataBuilder.obj().appendSelfToBufBuilder(builder);

    MsgData::View msg = builder.buf();
    msg.setLen(builder.len());
    msg.setOperation(dbCommand);
    return Message(builder.release());
}
Пример #5
0
Message messageFromOpMsgRequest(Protocol proto, const OpMsgRequest& request) {
    switch (proto) {
        case Protocol::kOpMsg:
            return request.serialize();
        case Protocol::kOpQuery:
            return legacyRequestFromOpMsgRequest(request);
        case Protocol::kOpCommandV1:
            return opCommandRequestFromOpMsgRequest(request);
        default:
            MONGO_UNREACHABLE;
    }
}
Пример #6
0
BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) {
    auto command = globalCommandRegistry()->findCommand(request.getCommandName());
    invariant(command);
    BSONObjBuilder out;
    try {
        bool ok = command->publicRun(opCtx, request, out);
        appendCommandStatus(out, ok);
    } catch (const StaleConfigException&) {
        // These exceptions are intended to be handled at a higher level.
        throw;
    } catch (const DBException& ex) {
        out.resetToEmpty();
        appendCommandStatus(out, ex.toStatus());
    }
    return out.obj();
}
Пример #7
0
void CommandHelpers::auditLogAuthEvent(OperationContext* opCtx,
                                       const CommandInvocation* invocation,
                                       const OpMsgRequest& request,
                                       ErrorCodes::Error err) {
    class Hook final : public audit::CommandInterface {
    public:
        explicit Hook(const CommandInvocation* invocation, const NamespaceString* nss)
            : _invocation(invocation), _nss(nss) {}

        void snipForLogging(mutablebson::Document* cmdObj) const override {
            if (_invocation) {
                _invocation->definition()->snipForLogging(cmdObj);
            }
        }

        StringData sensitiveFieldName() const override {
            if (_invocation) {
                return _invocation->definition()->sensitiveFieldName();
            }
            return StringData{};
        }

        StringData getName() const override {
            if (!_invocation) {
                return "Error"_sd;
            }
            return _invocation->definition()->getName();
        }

        NamespaceString ns() const override {
            return *_nss;
        }

        bool redactArgs() const override {
            return !_invocation;
        }

    private:
        const CommandInvocation* _invocation;
        const NamespaceString* _nss;
    };

    NamespaceString nss = invocation ? invocation->ns() : NamespaceString(request.getDatabase());
    audit::logCommandAuthzCheck(opCtx->getClient(), request, Hook(invocation, &nss), err);
}
Пример #8
0
    void waitForReadConcern(OperationContext* opCtx,
                            const CommandInvocation* invocation,
                            const OpMsgRequest& request) const override {
        Status rcStatus = mongo::waitForReadConcern(
            opCtx, repl::ReadConcernArgs::get(opCtx), invocation->allowsAfterClusterTime());

        if (!rcStatus.isOK()) {
            if (rcStatus == ErrorCodes::ExceededTimeLimit) {
                const int debugLevel =
                    serverGlobalParams.clusterRole == ClusterRole::ConfigServer ? 0 : 2;
                LOG(debugLevel) << "Command on database " << request.getDatabase()
                                << " timed out waiting for read concern to be satisfied. Command: "
                                << redact(ServiceEntryPointCommon::getRedactedCopyForLogging(
                                       invocation->definition(), request.body));
            }

            uassertStatusOK(rcStatus);
        }
    }
Пример #9
0
BSONObj CommandHelpers::runCommandDirectly(OperationContext* opCtx, const OpMsgRequest& request) {
    auto command = globalCommandRegistry()->findCommand(request.getCommandName());
    invariant(command);
    rpc::OpMsgReplyBuilder replyBuilder;
    std::unique_ptr<CommandInvocation> invocation;
    try {
        invocation = command->parse(opCtx, request);
        invocation->run(opCtx, &replyBuilder);
        auto body = replyBuilder.getBodyBuilder();
        CommandHelpers::extractOrAppendOk(body);
    } catch (const StaleConfigException&) {
        // These exceptions are intended to be handled at a higher level.
        throw;
    } catch (const DBException& ex) {
        if (ex.code() == ErrorCodes::Unauthorized) {
            CommandHelpers::auditLogAuthEvent(opCtx, invocation.get(), request, ex.code());
        }
        auto body = replyBuilder.getBodyBuilder();
        body.resetToEmpty();
        appendCommandStatusNoThrow(body, ex.toStatus());
    }
    return replyBuilder.releaseBody();
}
Пример #10
0
bool BasicCommand::enhancedRun(OperationContext* opCtx,
                               const OpMsgRequest& request,
                               BSONObjBuilder& result) {
    uassertNoDocumentSequences(request);
    return run(opCtx, request.getDatabase().toString(), request.body, result);
}
Пример #11
0
Status BasicCommand::checkAuthForRequest(OperationContext* opCtx,
                                         const OpMsgRequest& request) const {
    uassertNoDocumentSequences(request);
    return checkAuthForOperation(opCtx, request.getDatabase().toString(), request.body);
}
Пример #12
0
 Invocation(Command* cmd, const OpMsgRequest& request)
     : CommandInvocation(cmd),
       _request(uassertStatusOK(
           GetMoreRequest::parseFromBSON(request.getDatabase().toString(), request.body))) {}