1 2 /** 3 * Copyright (C) 2018-present MongoDB, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the Server Side Public License, version 1, 7 * as published by MongoDB, Inc. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * Server Side Public License for more details. 13 * 14 * You should have received a copy of the Server Side Public License 15 * along with this program. If not, see 16 * <http://www.mongodb.com/licensing/server-side-public-license>. 17 * 18 * As a special exception, the copyright holders give permission to link the 19 * code of portions of this program with the OpenSSL library under certain 20 * conditions as described in each individual source file and distribute 21 * linked combinations including the program with the OpenSSL library. You 22 * must comply with the Server Side Public License in all respects for 23 * all of the code used other than as permitted herein. If you modify file(s) 24 * with this exception, you may extend this exception to your version of the 25 * file(s), but you are not obligated to do so. If you do not wish to do so, 26 * delete this exception statement from your version. If you delete this 27 * exception statement from all source files in the program, then also delete 28 * it in the license file. 29 */ 30 31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kCommand 32 33 #include "mongo/platform/basic.h" 34 35 #include <limits> 36 37 #include "mongo/base/init.h" 38 #include "mongo/base/status.h" 39 #include "mongo/bson/util/bson_extract.h" 40 #include "mongo/db/audit.h" 41 #include "mongo/db/auth/authorization_session.h" 42 #include "mongo/db/client.h" 43 #include "mongo/db/commands.h" 44 #include "mongo/db/operation_context.h" 45 #include "mongo/db/service_context.h" 46 #include "mongo/util/log.h" 47 #include "mongo/util/mongoutils/str.h" 48 49 namespace mongo { 50 51 class KillOpCommand : public BasicCommand { 52 public: KillOpCommand()53 KillOpCommand() : BasicCommand("killOp") {} 54 55 supportsWriteConcern(const BSONObj & cmd) const56 virtual bool supportsWriteConcern(const BSONObj& cmd) const override { 57 return false; 58 } 59 slaveOk() const60 bool slaveOk() const final { 61 return true; 62 } 63 adminOnly() const64 bool adminOnly() const final { 65 return true; 66 } 67 parseOpId(const BSONObj & cmdObj)68 static long long parseOpId(const BSONObj& cmdObj) { 69 long long op; 70 uassertStatusOK(bsonExtractIntegerField(cmdObj, "op", &op)); 71 72 // Internally opid is an unsigned 32-bit int, but as BSON only has signed integer types, 73 // we wrap values exceeding 2,147,483,647 to negative numbers. The following undoes this 74 // transformation, so users can use killOp on the (negative) opid they received. 75 if (op >= std::numeric_limits<int>::min() && op < 0) 76 op += 1ull << 32; 77 78 uassert(26823, 79 str::stream() << "invalid op : " << op, 80 (op >= 0) && (op <= std::numeric_limits<unsigned int>::max())); 81 82 83 return op; 84 } 85 _findOp(Client * client,unsigned int opId)86 static StatusWith<std::tuple<stdx::unique_lock<Client>, OperationContext*>> _findOp( 87 Client* client, unsigned int opId) { 88 AuthorizationSession* authzSession = AuthorizationSession::get(client); 89 90 for (ServiceContext::LockedClientsCursor cursor(client->getServiceContext()); 91 Client* opClient = cursor.next();) { 92 stdx::unique_lock<Client> lk(*opClient); 93 94 OperationContext* opCtx = opClient->getOperationContext(); 95 if (opCtx && opCtx->getOpID() == opId) { 96 if (authzSession->isAuthorizedForActionsOnResource( 97 ResourcePattern::forClusterResource(), ActionType::killop) || 98 authzSession->isCoauthorizedWithClient(opClient, lk)) { 99 return {std::make_tuple(std::move(lk), opCtx)}; 100 } 101 break; 102 } 103 } 104 105 return Status(ErrorCodes::NoSuchKey, str::stream() << "Could not access opID: " << opId); 106 } 107 checkAuthForCommand(Client * client,const std::string & dbname,const BSONObj & cmdObj)108 Status checkAuthForCommand(Client* client, 109 const std::string& dbname, 110 const BSONObj& cmdObj) final { 111 AuthorizationSession* authzSession = AuthorizationSession::get(client); 112 113 if (authzSession->isAuthorizedForActionsOnResource(ResourcePattern::forClusterResource(), 114 ActionType::killop)) { 115 // If we have administrative permission to run killop, we don't need to traverse the 116 // Client list to figure out if we own the operation which will be terminated. 117 return Status::OK(); 118 } 119 120 bool isAuthenticated = 121 AuthorizationSession::get(client)->getAuthenticatedUserNames().more(); 122 if (isAuthenticated) { 123 long long opId = parseOpId(cmdObj); 124 auto swLkAndOp = _findOp(client, opId); 125 if (swLkAndOp.isOK()) { 126 // We were able to find the Operation, and we were authorized to interact with it. 127 return Status::OK(); 128 } 129 } 130 return Status(ErrorCodes::Unauthorized, "Unauthorized"); 131 } 132 run(OperationContext * opCtx,const std::string & db,const BSONObj & cmdObj,BSONObjBuilder & result)133 bool run(OperationContext* opCtx, 134 const std::string& db, 135 const BSONObj& cmdObj, 136 BSONObjBuilder& result) final { 137 long long opId = parseOpId(cmdObj); 138 139 log() << "going to kill op: " << opId; 140 result.append("info", "attempting to kill op"); 141 auto swLkAndOp = _findOp(opCtx->getClient(), opId); 142 if (swLkAndOp.isOK()) { 143 stdx::unique_lock<Client> lk; 144 OperationContext* opCtxToKill; 145 std::tie(lk, opCtxToKill) = std::move(swLkAndOp.getValue()); 146 opCtx->getServiceContext()->killOperation(opCtxToKill); 147 } 148 149 return true; 150 } 151 } killOpCmd; 152 153 } // namespace mongo 154